zsh-workers
 help / color / mirror / code / Atom feed
* FIFOs
@ 2000-04-14 17:05 Peter Stephenson
  2000-04-17 10:03 ` FIFOs Andrej Borsenkow
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Peter Stephenson @ 2000-04-14 17:05 UTC (permalink / raw)
  To: Zsh hackers list

Bart wrote:
> For guaranteed correct operation, we should remove the PATH_DEV_FD code
> from getproc() in exec.c, or (perhaps better) change it to be used only
> if mkfifo() is absent or fails.

This is easy (apart from the `or fails', which I haven't attempted to
implement --- unless that simply means `or the configure test for it
fails').  

There are two issues here, however (without a patch, you'll have to #undef
PATH_DEV_FD in exec.c to see them).  The first isn't too bad.

% echo <(echo foo)

Here the parent shell can, with the wind in the right direction, get back
and delete the file named by the <(...) before the child has had a chance
to open it (let alone call the code to fill it).  There's no easy way to
synch this, since you end up with deadlock --- the child can't open the
fifo until there's a process reading it.  This has happened to me a few
times.  It looks pretty unlikely if you stare at the code --- the open is
only a few instructions later in the child while the host is doing all the
normal command processing first --- but if you think about the scheduling
of forked-off child processes on heavily loaded machines (in this case SMP)
maybe it's not so surprising.

One good reason not to worry about this is that if the process actually
opens the fifo, that's guaranteed not to happen, i.e.

% cat <(echo foo)

always works.


The second thing is a killer, at least without a rethink.  In the case
first shown, where the fifo is never opened, but this time does still
exist, the zsh just hangs on for ever waiting for it and sits around
uselessly in the process table.  The second remark above still applies, but
this time the failure is less benign.  Maybe somebody understands this
better.  Anyway, I haven't sent a patch because of that.

I suppose this a system issue, since it's not obvious to me why the read
doesn't just fail when the fifo is deleted, at which point there's no
chance of anyone ever reading from it (this is Solaris 2.6).  It would be
reasonably safe to arrange for a timeout, but it would have to be set up
specially since poll() and select() won't work if we haven't yet got an fd.

-- 
Peter Stephenson <pws@cambridgesiliconradio.com>
Cambridge Silicon Radio, Unit 300, Science Park, Milton Road,
Cambridge, CB4 0XL, UK                          Tel: +44 (0)1223 392070


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FIFOs
  2000-04-14 17:05 FIFOs Peter Stephenson
@ 2000-04-17 10:03 ` Andrej Borsenkow
  2000-04-17 10:47   ` PATCH: FIFOs Peter Stephenson
  2000-04-24  3:31 ` FIFOs Bart Schaefer
  2000-05-24 12:16 ` FIFOs again Andrej Borsenkow
  2 siblings, 1 reply; 10+ messages in thread
From: Andrej Borsenkow @ 2000-04-17 10:03 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

>
> There are two issues here, however (without a patch, you'll
> have to #undef
> PATH_DEV_FD in exec.c to see them).
>

[ Stuff omitted ]

O.K., if for whatever reason the FIFO is not possible, it nice to have
it mentioned in manual. To document current behaviour (passing
/dev/fd/... instead of FIFO) and mention, that it may not work with some
programs, a la sudo.

-andrej


^ permalink raw reply	[flat|nested] 10+ messages in thread

* PATCH: Re: FIFOs
  2000-04-17 10:03 ` FIFOs Andrej Borsenkow
@ 2000-04-17 10:47   ` Peter Stephenson
  0 siblings, 0 replies; 10+ messages in thread
From: Peter Stephenson @ 2000-04-17 10:47 UTC (permalink / raw)
  To: Zsh hackers list

> O.K., if for whatever reason the FIFO is not possible, it nice to have
> it mentioned in manual. To document current behaviour (passing
> /dev/fd/... instead of FIFO) and mention, that it may not work with some
> programs, a la sudo.

Index: Doc/Zsh/expn.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/expn.yo,v
retrieving revision 1.6
diff -u -r1.6 expn.yo
--- Doc/Zsh/expn.yo	2000/04/14 13:07:58	1.6
+++ Doc/Zsh/expn.yo	2000/04/17 10:44:33
@@ -278,13 +278,13 @@
 `tt(=LPAR())var(list)tt(RPAR())'
 is subject to process substitution.
 In the case of the tt(<) or tt(>) forms, the shell will run process
-var(list) asynchronously, connected to a named pipe (FIFO).
-The name of this pipe will become the argument to the command.
-If the form with tt(>)
-is selected then writing on this file will provide input for var(list).
-If tt(<) is used, then the file passed as an argument will
-be a named pipe connected to the output of the var(list) process.
-For example,
+var(list) asynchronously.  If the system supports the tt(/dev/fd)
+mechanism, the command argument is the name of the device file
+corresponding to a file descriptor; otherwise, if the system supports named
+pipes (FIFOs), the command argument will be a named pipe.  If the form with
+tt(>) is selected then writing on this special file will provide input for
+var(list).  If tt(<) is used, then the file passed as an argument will
+be connected to the output of the var(list) process.  For example,
 
 nofill(tt(paste <LPAR()cut -f1) var(file1)tt(RPAR() <LPAR()cut -f3) var(file2)tt(RPAR() |
 tee >LPAR())var(process1)tt(RPAR() >LPAR())var(process2)tt(RPAR() >/dev/null))
@@ -292,9 +292,19 @@
 cuts fields 1 and 3 from the files var(file1) and var(file2) respectively,
 pastes the results together, and sends it to the processes
 var(process1) and var(process2).
-Note that the file, which is passed as an argument to the command,
-is a system pipe, so programs that expect to lseek (see manref(lseek)(2))
-on the file will not work.
+
+Both the tt(/dev/fd) and the named pipe implementation have drawbacks.  In
+the former case, some programmes may automatically close the file
+descriptor in question before examining the file on the command line,
+particularly if this is necessary for security reasons such as when the
+programme is running setuid.  In the second case, the file passed as an
+argument to the command is a system pipe, so programs that expect to lseek
+(see manref(lseek)(2)) on the file will not work; furthermore, if the
+programme does not actually open the file the subshell attempting to read
+from or write to the pipe will (in a typical implementation, different
+operating systems may have different behaviour) block for ever and have to
+be killed explicitly.
+
 Also note that the previous example can be more compactly and
 efficiently written (provided the tt(MULTIOS) option is set) as:

-- 
Peter Stephenson <pws@cambridgesiliconradio.com>
Cambridge Silicon Radio, Unit 300, Science Park, Milton Road,
Cambridge, CB4 0XL, UK                          Tel: +44 (0)1223 392070


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FIFOs
  2000-04-14 17:05 FIFOs Peter Stephenson
  2000-04-17 10:03 ` FIFOs Andrej Borsenkow
@ 2000-04-24  3:31 ` Bart Schaefer
  2000-05-24 12:16 ` FIFOs again Andrej Borsenkow
  2 siblings, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2000-04-24  3:31 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

On Apr 14,  6:05pm, Peter Stephenson wrote:
} Subject: FIFOs
}
} % echo <(echo foo)
} 
} Here the parent shell can, with the wind in the right direction, get back
} and delete the file named by the <(...) before the child has had a chance
} to open it (let alone call the code to fill it).  There's no easy way to
} synch this, since you end up with deadlock --- the child can't open the
} fifo until there's a process reading it.

The obvious solution here is that zsh should fork a subshell to handle the
entire process inside the <(...), including actually removing the file at
the end.  That is, the parent creates the FIFO, then forks a subshell.  The
subshell runs "echo foo" and then removes the FIFO when that exits.

Meanwhile, the parent runs "echo nameofFIFOhere" and treats the subshell as
a backgrounded job.  It never touches the FIFO again.

} The second thing is a killer, at least without a rethink.  In the case
} first shown, where the fifo is never opened, but this time does still
} exist, the zsh just hangs on for ever waiting for it and sits around
} uselessly in the process table.

If the user asked for that, then that's what the user gets.  In the
scenario I just described, it's the subshell that sits around forever,
not the parent; so what?  It's not like the terminal is hung.

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FIFOs again
  2000-04-14 17:05 FIFOs Peter Stephenson
  2000-04-17 10:03 ` FIFOs Andrej Borsenkow
  2000-04-24  3:31 ` FIFOs Bart Schaefer
@ 2000-05-24 12:16 ` Andrej Borsenkow
  2000-05-24 12:43   ` Andrej Borsenkow
  2000-05-24 12:57   ` Peter Stephenson
  2 siblings, 2 replies; 10+ messages in thread
From: Andrej Borsenkow @ 2000-05-24 12:16 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

As you intend to release 3.1.7, can we try to sort this out before?

First, current docs are a bit incorrect. Shell passes FIFO or /dev/fd/n,
which is in reality pipe (if I understand implementation correctly). So,
both are not seekable, while docs imply, that only FIFO case is not.

And more about FIFO case.
>
> % echo <(echo foo)
>
> Here the parent shell can, with the wind in the right
> direction, get back
> and delete the file named by the <(...) before the child has
> had a chance
> to open it (let alone call the code to fill it).
....
>
> One good reason not to worry about this is that if the
> process actually
> opens the fifo, that's guaranteed not to happen, i.e.
>
> % cat <(echo foo)
>
> always works.
>

Yes, I agree. This should not be an issue.

>
> The second thing is a killer, at least without a rethink.  In the case
> first shown, where the fifo is never opened, but this time does still
> exist, the zsh just hangs on for ever waiting for it and sits around
> uselessly in the process table.


Yes, I got the same. Real nasty. One possibility is "dummy open" in
parent. The child hangs because it tries to open FIFO without
counterpart. Parent can simply open it and then close when child exits
or we're done with current commmand (it currently have to delete FIFO
anyway).

-andrej


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FIFOs again
  2000-05-24 12:16 ` FIFOs again Andrej Borsenkow
@ 2000-05-24 12:43   ` Andrej Borsenkow
  2000-05-24 13:34     ` Peter Stephenson
  2000-05-24 12:57   ` Peter Stephenson
  1 sibling, 1 reply; 10+ messages in thread
From: Andrej Borsenkow @ 2000-05-24 12:43 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

> >
> > The second thing is a killer, at least without a rethink.
> In the case
> > first shown, where the fifo is never opened, but this time
> does still
> > exist, the zsh just hangs on for ever waiting for it and sits around
> > uselessly in the process table.
>
>
> Yes, I got the same. Real nasty. One possibility is "dummy open" in
> parent. The child hangs because it tries to open FIFO without
> counterpart. Parent can simply open it and then close when child exits
> or we're done with current commmand (it currently have to delete FIFO
> anyway).
>

Actually, it seems to be possible to immediately close it. The following
trivial change to getproc() (marked with >>>>>>) seems to do the trick
(no more hung processes).

#ifndef PATH_DEV_FD
    if (!jobtab[thisjob].filelist)
 jobtab[thisjob].filelist = znewlinklist();
    zaddlinknode(jobtab[thisjob].filelist, ztrdup(pnam));

    if (zfork()) {
>>>>>> dummy = open(pnam, !out ? O_WRONLY | O_NOCTTY : O_RDONLY |
O_NOCTTY);
>>>>>> close (dummy);
#else
    mpipe(pipes);
    if (zfork()) {
 sprintf(pnam, "%s/%d", PATH_DEV_FD, pipes[!out]);
 zclose(pipes[out]);
 fdtable[pipes[!out]] = 2;
#endif
 return pnam;
    }

This looks safe - we do it only if fork() was successful and then child
will open FIFO in turn. BTW this is also a way to sync child and parent
to some extent. Oh, yes, and it should fix the first mentioned problem -
that parent removes FIFO too early.

-andrej


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FIFOs again
  2000-05-24 12:16 ` FIFOs again Andrej Borsenkow
  2000-05-24 12:43   ` Andrej Borsenkow
@ 2000-05-24 12:57   ` Peter Stephenson
  1 sibling, 0 replies; 10+ messages in thread
From: Peter Stephenson @ 2000-05-24 12:57 UTC (permalink / raw)
  To: Zsh hackers list

> First, current docs are a bit incorrect. Shell passes FIFO or /dev/fd/n,
> which is in reality pipe (if I understand implementation correctly). So,
> both are not seekable, while docs imply, that only FIFO case is not.

Ah, you mean because zsh supplies a pipe between the forked processes
even if the device file is a real live file descriptor.

Index: Doc/Zsh/expn.yo
===================================================================
RCS file: /cvsroot/zsh/zsh/Doc/Zsh/expn.yo,v
retrieving revision 1.17
diff -u -r1.17 expn.yo
--- Doc/Zsh/expn.yo	2000/05/22 15:01:35	1.17
+++ Doc/Zsh/expn.yo	2000/05/24 12:56:17
@@ -320,13 +320,13 @@
 the former case, some programmes may automatically close the file
 descriptor in question before examining the file on the command line,
 particularly if this is necessary for security reasons such as when the
-programme is running setuid.  In the second case, the file passed as an
-argument to the command is a system pipe, so programs that expect to lseek
-(see manref(lseek)(2)) on the file will not work; furthermore, if the
+programme is running setuid.  In the second case,  if the
 programme does not actually open the file the subshell attempting to read
 from or write to the pipe will (in a typical implementation, different
 operating systems may have different behaviour) block for ever and have to
-be killed explicitly.
+be killed explicitly.  In both cases, the shell actually supplies the
+information using a pipe, so that programmes that expect to lseek
+(see manref(lseek)(2)) on the file will not work.
 
 Also note that the previous example can be more compactly and
 efficiently written (provided the tt(MULTIOS) option is set) as:

-- 
Peter Stephenson <pws@cambridgesiliconradio.com>
Cambridge Silicon Radio, Unit 300, Science Park, Milton Road,
Cambridge, CB4 0XL, UK                          Tel: +44 (0)1223 392070


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FIFOs again
  2000-05-24 12:43   ` Andrej Borsenkow
@ 2000-05-24 13:34     ` Peter Stephenson
  2000-05-24 16:23       ` Andrej Borsenkow
  2000-05-25 15:47       ` Bart Schaefer
  0 siblings, 2 replies; 10+ messages in thread
From: Peter Stephenson @ 2000-05-24 13:34 UTC (permalink / raw)
  To: Zsh hackers list

Andrej wrote:
> > Yes, I got the same. Real nasty. One possibility is "dummy open" in
> > parent. The child hangs because it tries to open FIFO without
> > counterpart. Parent can simply open it and then close when child exits
> > or we're done with current commmand (it currently have to delete FIFO
> > anyway).
> >
> 
> Actually, it seems to be possible to immediately close it. The following
> trivial change to getproc() (marked with >>>>>>) seems to do the trick
> (no more hung processes).

I don't think this does the right thing.  Consider <(...).  The child
process successfully opens the pipe for writing when the parent opens it
for reading.  Now the parent closes it.  But meanwhile the child process
has been busy, and when it finds the file is closed again it either gets
SIGPIPE or a write failure on the fd and gives up --- unless the new
command has opened it in the meanwhile, but that's unlikely (and you need
it to be certain).  I tried with

  cat <(echo foo)

and confirmed that it's the cat which hangs forever, waiting for something
to open the other end of the pipe for writing.  At least that's what seems
to be happening with Solaris 2.6.

Keeping the process open in the parent might work, however (there's a
slight extra subtlety over your code that we need to check the fork
returned a positive value).  We would have to make quite sure of closing it
when we delete the file --- leaving a temporary file name around isn't a
big problem, but a file descriptor leak is.  Also, I'm not quite sure of
the issues behind having both the shell itself and the command which gets
the file name having fd's open to the same file, though I can't offhand see
a case where it would cause a problem since we never read from or write to
it.  But I don't think it's a good idea to rush into this at the last
minute before 3.1.7.  If someone can definitely answer those two points
(the first is internal, the second an OS matter) there's no reason why we
couldn't add this eventually.

-- 
Peter Stephenson <pws@cambridgesiliconradio.com>
Cambridge Silicon Radio, Unit 300, Science Park, Milton Road,
Cambridge, CB4 0XL, UK                          Tel: +44 (0)1223 392070


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: FIFOs again
  2000-05-24 13:34     ` Peter Stephenson
@ 2000-05-24 16:23       ` Andrej Borsenkow
  2000-05-25 15:47       ` Bart Schaefer
  1 sibling, 0 replies; 10+ messages in thread
From: Andrej Borsenkow @ 2000-05-24 16:23 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list


  I tried with
>
>   cat <(echo foo)
>
> and confirmed that it's the cat which hangs forever, waiting
> for something
> to open the other end of the pipe for writing.  At least
> that's what seems
> to be happening with Solaris 2.6.
>

Yes, you are right. The correct thing is - open FIFO for read AND write
and close it after command exited. (Opening just for read or write is
not enough and has potentially the same problems).

Probably, we could use the same structure as is used to keep track of
FIFO names for descriptors as well. It should cover the case of
background job as well.

Hmm ... and then again, all of them should be closed in next child ...
why do not we simply use

for(i=3;i++;i<MAX_FD) close(i);

in child? I just discovered that (probably, due to a bug) logon shells
keep one stray open file descriptor that is never closed by shell.

> Keeping the process open in the parent might work, however (there's a
> slight extra subtlety over your code that we need to check the fork
> returned a positive value).

Yes, of course. I think, there is no problem to have it opened as long
as we never actually use it.

-andrej


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: FIFOs again
  2000-05-24 13:34     ` Peter Stephenson
  2000-05-24 16:23       ` Andrej Borsenkow
@ 2000-05-25 15:47       ` Bart Schaefer
  1 sibling, 0 replies; 10+ messages in thread
From: Bart Schaefer @ 2000-05-25 15:47 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

On May 24,  2:34pm, Peter Stephenson wrote:
} Subject: Re: FIFOs again
}
} Andrej wrote:
} > > Yes, I got the same. Real nasty. One possibility is "dummy open" in
} > > parent. The child hangs because it tries to open FIFO without
} > > counterpart. Parent can simply open it and then close when child exits
} > > or we're done with current commmand (it currently have to delete FIFO
} > > anyway).
} > >
} > 
} > Actually, it seems to be possible to immediately close it. The following
} > trivial change to getproc() (marked with >>>>>>) seems to do the trick
} > (no more hung processes).
} 
} I don't think this does the right thing.  Consider <(...).

I don't think there's a simple fix to getproc() that will handle this.
Some work is going to have to be done only when both the child process
from inside the <(...) and the one supposedly reading it have exited.
That means recording that information somewhere and dealing with it in
handler() or in the job table code.

-- 
Bart Schaefer                                 Brass Lantern Enterprises
http://www.well.com/user/barts              http://www.brasslantern.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2000-05-25 15:48 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-04-14 17:05 FIFOs Peter Stephenson
2000-04-17 10:03 ` FIFOs Andrej Borsenkow
2000-04-17 10:47   ` PATCH: FIFOs Peter Stephenson
2000-04-24  3:31 ` FIFOs Bart Schaefer
2000-05-24 12:16 ` FIFOs again Andrej Borsenkow
2000-05-24 12:43   ` Andrej Borsenkow
2000-05-24 13:34     ` Peter Stephenson
2000-05-24 16:23       ` Andrej Borsenkow
2000-05-25 15:47       ` Bart Schaefer
2000-05-24 12:57   ` Peter Stephenson

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).