Bug 95191

Summary: document behavior of open(2) when path names a fifo with no readers
Product: Documentation Reporter: Jason Vas Dias (jason.vas.dias)
Component: man-pagesAssignee: documentation_man-pages (documentation_man-pages)
Status: RESOLVED CODE_FIX    
Severity: normal CC: mtk.manpages
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: Subsystem:
Regression: No Bisected commit-id:
Attachments: illustrates problem - #1 working version that uses open
illustration #2: program that attempts to enable SIGIO on output fd 1 only - fails to recieve SIGIO
better version of t_sigio_write_no_open

Description Jason Vas Dias 2015-03-21 15:16:27 UTC
linux 3.13 open() , if the path refers to a fifo, and O_WRONLY is set in
its flags , but not O_NONBLOCK, then it does not return until a reader has
connected to the output end of the pipe.

This fact is not documented anywhere in the open(2) manual page - in fact,
it only mentions :

    ENXIO  O_NONBLOCK | O_WRONLY is set, the named file is a FIFO and no process 
           has the file open for reading.  Or, the file is a device special file 
           and no corresponding device exists.

You can easily verify this behaviour with bash :

  $ mkfifo /tmp/a.fifo
  $ strace -f bash -c 'echo 1 >/tmp/a.fifo' &
  ...
  open("/tmp/a.fifo", O_WRONLY|O_CREAT|O_TRUNC, 0666
  $ read res </tmp/a.fifo
) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
write(1, "1\n", 2)                      = 2
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Please add words to the effect :

  "If the path refers to a named pipe (FIFO), and O_NONBLOCK is not set,
   open() will block until a reader is connected to the write end of the pipe.
  "
to the open(2) manual page.
Comment 1 Jason Vas Dias 2015-03-21 20:18:43 UTC
I think the fcntl documentation is also particularly misleading when it suggests
that output file descriptors can be enabled to have SIGIO sent for them when 
only O_ASYNC and not O_NONBLOCK bits are set in the FD flags .
I cannot get the attached program to work if I do not open() the fifo file -
it will not do to simply fcntl(fd,F_SETFL,previous_flags|O_ASYNC) and 
fcntl(fd,F_SETOWN_EX,{ .type = F_OWNER_TID, .pid  = gettid() }) and
 sigio_sa = (struct sigaction)
      { .sa_sigaction = sigio_handler,
        .sa_flags = SA_NODEFER | SA_SIGINFO 
      };  
  if( sigaction(SIGIO, &sigio_sa, &sigio_prev_sa)
and then expect the process /  thread to receive SIGIO
where si->si_fd == 1 for stdout - the process never gets a SIGIO signal.
Is this a kernel bug ? if not why is this not documented ?
Comment 2 Jason Vas Dias 2015-03-21 20:21:19 UTC
Created attachment 171551 [details]
illustrates problem - #1 working version that uses open
Comment 3 Jason Vas Dias 2015-03-21 20:29:29 UTC
Created attachment 171561 [details]
illustration #2: program that attempts to enable SIGIO on output fd 1 only - fails to recieve SIGIO
Comment 4 Jason Vas Dias 2015-03-21 20:55:04 UTC
Should have added:
To test: 
  Save attachment #1 [details] as t_sigio_writer.c
  Save attachment #2 [details] as t_sigio_writer_no_open.c
  $ gcc -o t_sigio_writer t_sigio_writer.c
  $ mkfifo /tmp/a.fifo
  $ ./t_sigio_writer /tmp/a.fifo &
  $ read -d $'\004' res < /tmp/a.fifo && echo "RES: $res"
  RES:
  $
  $ gcc -o t_sigio_writer_no_open t_sigio_writer_no_open.c
  $ ./t_sigio_writer_no_open </tmp/a.fifo &
  $ read -d $'\004' res < /tmp/a.fifo && echo "RES: $res"
  ^C
 
An strace of 2nd process, which only does fcntl(1,F_SETFL,flags|O_ASYNC),
and does identical signal handling, that had to be killed,  shows it only
gets a SIGIO on FD 0, not on FD 1, after <CTRL+C> is pressed.
Comment 5 Jason Vas Dias 2015-03-21 20:56:38 UTC
Oops :
$ ./t_sigio_writer_no_open </tmp/a.fifo &
should have been:
$ ./t_sigio_writer_no_open >/tmp/a.fifo &
Comment 6 Jason Vas Dias 2015-03-21 21:16:24 UTC
Oops! Actually, all tests pass if compiled with '-pthread' .
Comment 7 Jason Vas Dias 2015-03-21 21:18:46 UTC
Created attachment 171581 [details]
better version of t_sigio_write_no_open
Comment 8 Michael Kerrisk 2015-03-22 15:10:37 UTC
Hello Jason,

(In reply to Jason Vas Dias from comment #0)
> linux 3.13 open() , if the path refers to a fifo, and O_WRONLY is set in
> its flags , but not O_NONBLOCK, then it does not return until a reader has
> connected to the output end of the pipe.
> 
> This fact is not documented anywhere in the open(2) manual page

[...]

> Please add words to the effect :
> 
>   "If the path refers to a named pipe (FIFO), and O_NONBLOCK is not set,
>    open() will block until a reader is connected to the write end of the
> pipe.
>   "
> to the open(2) manual page.

Well, there is already this text in the man page:

    O_NONBLOCK or O_NDELAY
        .... For the handling of FIFOs (named pipes), see also fifo(7).

(Were you aware of the fifo(7) page?)

But it's true that something could be said more prominently in 
open(2), I think. I added the following under NOTES:

   FIFOs
       Opening  the read or write end of a FIFO blocks until the other
       end is also opened (by another process or thread).  See fifo(7)
       for further details.

Thanks,

Michael
Comment 9 Michael Kerrisk 2015-03-22 15:15:08 UTC
(In reply to Jason Vas Dias from comment #1)
> I think the fcntl documentation is also particularly misleading when it
> suggests
> that output file descriptors can be enabled to have SIGIO sent for them when 
> only O_ASYNC and not O_NONBLOCK bits are set in the FD flags .

Hello Jason,

I don't understand what you mean here. But, also, I would really prefer a separate bug report, since this seems to be a separate issue.

I'm going to close this bug. Could you raise the above as a separate topics, and repost example programs. But please: make the example programs as simple as possible. I see extraneous stuff in the code you posted already, which just makes it harder for me to work out what's going on.

Thanks,

Michael
Comment 10 Jason Vas Dias 2015-03-23 19:47:14 UTC
Thanks for taking a look at this, Micheal. As you suggested, I opened
another bug: Bug #95331 - please take a look.