I/O system brokenness with named pipes

So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe. I have created the named pipe from Haskell no problem. But I can't use writeFile to write data to it. Worse, it returns: *** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address) which is completely false, as it *does* exist, and I can cat to it as expected. The call should block until everything is read on the remote end. I thought maybe writeFile is being weird, so I tried: openFile "/tmp/bakroller.zD0xHj/fifo" WriteMode Same thing. There is no logical reason I can see for this behavior. In fact, something must be going to *extra* effort to avoid writing to a named pipe, and I can't work out why. Named pipes are a standard, useful part of a system and shouldn't be ignored like this. Interestingly, readFile works fine on a named pipe. What's going on here? Am I going to have to resort to the System.Posix interface just to be able to write to a named pipe? -- John

jgoerzen:
So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe.
I have created the named pipe from Haskell no problem.
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
which is completely false, as it *does* exist, and I can cat to it as expected. The call should block until everything is read on the remote end.
I thought maybe writeFile is being weird, so I tried:
openFile "/tmp/bakroller.zD0xHj/fifo" WriteMode
Hmm, I can get this to work, but only if I have another process waiting on the pipe already: $ mkfifo /tmp/pipe $ tail -f /tmp/pipe $ ghc -e 'writeFile "/tmp/pipe" "test"' testtesttesttest^C However, if I don't have 'tail' waiting on the pipe, it fails.
There is no logical reason I can see for this behavior. In fact, something must be going to *extra* effort to avoid writing to a named pipe, and I can't work out why. Named pipes are a standard, useful part of a system and shouldn't be ignored like this.
Interestingly, readFile works fine on a named pipe.
What's going on here? Am I going to have to resort to the System.Posix interface just to be able to write to a named pipe?
Something fishy. -- Don

On Apr 11, 2008, at 6:33 , Don Stewart wrote:
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
Hmm, I can get this to work, but only if I have another process waiting on the pipe already:
Yep; that's one of the tricky parts of FIFOs. If you don't have the read side *always* open, it behaves very nonintuitively. (But with the read side always open, you will never receive an EOF.) This has nothing to do with Haskell; it's just the way FIFOs work. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Fri April 11 2008 7:53:12 am Brandon S. Allbery KF8NH wrote:
On Apr 11, 2008, at 6:33 , Don Stewart wrote:
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
Hmm, I can get this to work, but only if I have another process waiting on the pipe already:
Yep; that's one of the tricky parts of FIFOs. If you don't have the read side *always* open, it behaves very nonintuitively. (But with the read side always open, you will never receive an EOF.)
This has nothing to do with Haskell; it's just the way FIFOs work.
I wonder if we could document this behavior. I rarely use non-blocking I/O from C, and Haskell hides the fact that it's doing this, so the behavior is non-intuitive. Actually, better yet, I wonder if we could *fix* this behavior. Most programs can take a FIFO as arguments in a standard way, and it seems to me that this violates the principle of least surprise. Unfortunately, since we're talking about open here, we can't use select() or poll(). But I wonder if we couldn't use stat() to determine if something is a named pipe, and if so, enter a loop where we try to open it periodically? -- John

On Apr 11, 2008, at 6:15 AM, John Goerzen wrote:
I wonder if we could document this behavior. I rarely use non- blocking I/O from C, and Haskell hides the fact that it's doing this, so the behavior is non-intuitive.
I have run into this problem, with Network.Socket (socket). If I remember right, ktrace showed me what was happening. This isn't my favorite thing about Haskell. Is there even a means provided to set it back to blocking? I couldn't find one, had to write my own FFI. It is not news to me that there is an issue with the Haskell thread implementation here, but since any non-native library I/O will similarly be blocking, we have to be able to live with this anyway.
Actually, better yet, I wonder if we could *fix* this behavior. Most programs can take a FIFO as arguments in a standard way, and it seems to me that this violates the principle of least surprise.
Unfortunately, since we're talking about open here, we can't use select() or poll(). But I wonder if we couldn't use stat() to determine if something is a named pipe, and if so, enter a loop where we try to open it periodically?
I am having a little trouble following this. Somewhere in the thread, the subject of ReadWrite pipe behavior came up, apparently for whatever reason you get non-blocking I/O this way too. But as long as you don't do that, then all you need for normal named pipe I/O is to set the file descriptor back to blocking ... right? Is the loop to work around the Haskell non-blocking, or the ReadWrite non-blocking? Donn Cave, donn@avvanta.com

Donn Cave wrote:
I have run into this problem, with Network.Socket (socket). If I remember right, ktrace showed me what was happening. This isn't my favorite thing about Haskell. Is there even a means provided to set it back to blocking?
There isn't a way right now to open a file using a blocking FD, however the IO library does support using blocking FDs (the std handles are left in blocking mode to avoid causing problems with pipes). We could certainly add an interface to let you open files in blocking mode - just submit a feature request via GHC's Trac and we'll try to get around to it (or better still, send us a patch... the code is in GHC.Handle.fdToHandle). Cheers, Simon

On Thu, 2008-04-10 at 20:34 -0500, John Goerzen wrote:
I have created the named pipe from Haskell no problem.
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
What's going on here? Am I going to have to resort to the System.Posix interface just to be able to write to a named pipe?
Named pipes have broken semantics for non-blocking IO, see google or the man pages on named pipes. GHC's standard Handle IO always sets file descriptor to non-blocking mode. That's the problem. That's why cat works, because it uses blocking IO. You would indeed need to use System.Posix to get a FD in blocking mode. Then you have to worry a bit about blocking other Haskell thread when you block on writing to the pipe. Duncan

On Friday 11 April 2008 05:39:54 am Duncan Coutts wrote:
On Thu, 2008-04-10 at 20:34 -0500, John Goerzen wrote:
I have created the named pipe from Haskell no problem.
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
What's going on here? Am I going to have to resort to the System.Posix interface just to be able to write to a named pipe?
Named pipes have broken semantics for non-blocking IO, see google or the man pages on named pipes. GHC's standard Handle IO always sets file descriptor to non-blocking mode. That's the problem. That's why cat works, because it uses blocking IO.
OK, I have referred to fifo(7) regarding this point. It seems I may need a loop trying over and over to open the FIFO in write mode. It also appears that ReadWriteMode appearing to work is Linux-specific, and this behavior is left undefined in POSIX. Does openFd do a non-blocking open? (Brief testing suggests it does) I'm somewhat confused about its semantics, especially since it does not appear to correspond directly to open(2). O_CREAT, for instance, is missing.
You would indeed need to use System.Posix to get a FD in blocking mode. Then you have to worry a bit about blocking other Haskell thread when you block on writing to the pipe.
Fortunately, in this particular case, I'm using forking instead of threading so this won't be a problem. Thanks for the help. -- John

On Apr 11, 2008, at 8:12 , John Goerzen wrote:
OK, I have referred to fifo(7) regarding this point. It seems I may need a loop trying over and over to open the FIFO in write mode. It also appears that ReadWriteMode appearing to work is Linux-specific, and this behavior is left undefined in POSIX.
Undefined in POSIX, normal for Unix (and Linux originally promised only POSIX semantics but was rejiggered some years back to behave like Unix in this regard, because Unix programs rely on these semantics and therefore failed on Linux). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Fri April 11 2008 8:02:07 am Brandon S. Allbery KF8NH wrote:
On Apr 11, 2008, at 8:12 , John Goerzen wrote:
OK, I have referred to fifo(7) regarding this point. It seems I may need a loop trying over and over to open the FIFO in write mode. It also appears that ReadWriteMode appearing to work is Linux-specific, and this behavior is left undefined in POSIX.
Undefined in POSIX, normal for Unix (and Linux originally promised only POSIX semantics but was rejiggered some years back to behave like Unix in this regard, because Unix programs rely on these semantics and therefore failed on Linux).
Hrm, can you define what OSs you mean when you say "Unix"?

On Apr 11, 2008, at 9:08 , John Goerzen wrote:
On Fri April 11 2008 8:02:07 am Brandon S. Allbery KF8NH wrote:
On Apr 11, 2008, at 8:12 , John Goerzen wrote:
OK, I have referred to fifo(7) regarding this point. It seems I may need a loop trying over and over to open the FIFO in write mode. It also appears that ReadWriteMode appearing to work is Linux-specific, and this behavior is left undefined in POSIX.
Undefined in POSIX, normal for Unix (and Linux originally promised only POSIX semantics but was rejiggered some years back to behave like Unix in this regard, because Unix programs rely on these semantics and therefore failed on Linux).
Hrm, can you define what OSs you mean when you say "Unix"?
SunOS/Solaris, and in general the descendants of 4.xBSD and AT&T System III/V. Unless this is a different issue from the usual FIFO only-works-as-expected-if-both-ends-always-open problem. That is, unless the first opener opens read/write, neither a prospective writer nor a prospective reader will be capable of opening it at all; depending on OS you will get ENXIO or EPIPE or (in some rare implementations) EAGAIN. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Fri, Apr 11, 2008 at 12:39:54PM +0200, Duncan Coutts wrote:
On Thu, 2008-04-10 at 20:34 -0500, John Goerzen wrote:
I have created the named pipe from Haskell no problem.
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
What's going on here? Am I going to have to resort to the System.Posix interface just to be able to write to a named pipe?
Named pipes have broken semantics for non-blocking IO, see google or the man pages on named pipes. GHC's standard Handle IO always sets file descriptor to non-blocking mode. That's the problem. That's why cat works, because it uses blocking IO.
You would indeed need to use System.Posix to get a FD in blocking mode. Then you have to worry a bit about blocking other Haskell thread when you block on writing to the pipe.
You could also work around this, at least on Linux, by opening the file in ReadMode before opening it in WriteMode. Hold the reading file handle open until you close the writing handle. Never read from the reading handle. In my testing, this worked well, but bear in mind that when the other readers close the pipe, further writes will "block" rather than produce a SIGPIPE. Matt

On Apr 10, 2008, at 21:34 , John Goerzen wrote:
So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe.
I have created the named pipe from Haskell no problem.
But I can't use writeFile to write data to it. Worse, it returns:
*** Exception: /tmp/bakroller.zD0xHj/fifo: openFile: does not exist (No such device or address)
The "does not exist" is misleading: ENXIO ("No such device or address"), unless it's being abused by the runtime, indicates something's wrong at the kernel level when trying to associate the FIFO. Using FIFOs properly is actually rather tricky and fidgety. Can you provide the actual code you're using? -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On 2008-04-15, Joe Buehler
John Goerzen wrote:
So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe.
How about /dev/stdin?
Only works on Linux. -- John

On Apr 16, 2008, at 11:16 , John Goerzen wrote:
On 2008-04-15, Joe Buehler
wrote: John Goerzen wrote:
So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe.
How about /dev/stdin?
Only works on Linux.
Hm. I see it even on our ancient Solaris 8 boxes. And I'm quite sure the *BSDs support it as well. Just how ancient / weird is this "POSIX" platform of yours? -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Wed April 16 2008 12:20:37 pm Brandon S. Allbery KF8NH wrote:
On Apr 16, 2008, at 11:16 , John Goerzen wrote:
On 2008-04-15, Joe Buehler
wrote: John Goerzen wrote:
So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe.
How about /dev/stdin?
Only works on Linux.
Hm. I see it even on our ancient Solaris 8 boxes. And I'm quite sure the *BSDs support it as well. Just how ancient / weird is this "POSIX" platform of yours?
OK, so I shouldn't have said "it only works on Linux", but as far as I can tell, it isn't standard (defined by POSIX). I don't presently have easy access to non-Linux machines, but I do want to be as portable as possible. -- John

You are insulting other Unixes. It works on Mac OS X, for example. On 16 Apr 2008, at 19:16, John Goerzen wrote:
On 2008-04-15, Joe Buehler
wrote: John Goerzen wrote:
So I have a need to write data to a POSIX named pipe (aka FIFO). Long story involving a command that doesn't have an option to read data from stdin, but can from a named pipe.
How about /dev/stdin?
Only works on Linux.
-- John
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Apr 16, 2008, at 13:23 , Miguel Mitrofanov wrote:
You are insulting other Unixes. It works on Mac OS X, for example.
Not just that, but IIRC Linux was late to the party: Solaris got / dev/fd/ and /dev/stdin before Linux got /proc/$$/fd/ (which gets symlinked to /dev/fd/). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Apr 16, 2008, at 10:25 AM, Brandon S. Allbery KF8NH wrote:
On Apr 16, 2008, at 13:23 , Miguel Mitrofanov wrote:
You are insulting other Unixes. It works on Mac OS X, for example.
Not just that, but IIRC Linux was late to the party: Solaris got / dev/fd/ and /dev/stdin before Linux got /proc/$$/fd/ (which gets symlinked to /dev/fd/).
Yeah, among people who don't know much about UNIX, Linux gets a lot of credit for things it borrowed. But /dev/stdin isn't a standard feature of UNIX, if you count AIX for example as a UNIX platform. And apparently you can compile Haskell programs on AIX - thanks to the original poster in this thread's work with GHC - so there you go. Donn

One question is whether the program is statically or dynamically linked, and if the latter, whether it is possible (as it is in many Unices) to slide your own open(2) definition in between the program and the system library. If it is, it's possible to slide in something that fakes /dev/stdin.
participants (10)
-
Brandon S. Allbery KF8NH
-
Don Stewart
-
Donn Cave
-
Duncan Coutts
-
Joe Buehler
-
John Goerzen
-
matth@mindspring.com
-
Miguel Mitrofanov
-
Richard A. O'Keefe
-
Simon Marlow