pprindeville has asked for the wisdom of the Perl Monks concerning the following question:

I have a daemon written in Perl where I'm doing something like:
my $pipe; open($pipe, '-|', 'ip -o xfrm monitor') || die "couldn't start sub-com +mand"; my $pid = fork(); if (! defined $pid) { die "couldn't fork"; elsif ($pid > 0) { # write $pid to .pid file cleanup(); exit(0); ## problem happens HERE } ## now in sub-process close(STDIN); close(STDOUT); close(STDERR); ... while (my $line = <$pipe>) { ... } close($pipe); cleanup2(); exit(0);
and what seems to happen when the parent attempts to exit() after spawning the child, is that $pipe's destructor then wants to waitpid() on the process inside the pipe... even though there's a cloned copy of $pipe in the child process from the explicit fork(). How do I make the parent NOT call the destructor of $pipe? This is supposition, btw, as I've not had a chance to do an strace on a running instance or inspect the IO code that gets invoked in this case... Thanks

Replies are listed 'Best First'.
Re: What happens to a '-|' (pipe) file handle across a fork()? (misfeature)
by tye (Sage) on Nov 07, 2015 at 04:55 UTC

    Yeah, the change in Perl such that even the implicit closing of a popen-like file handle waits for the child to exit was a pretty sucky change in my book. For a very long time in Perl's history, only an explicit call to close would cause the wait. Super easy to get deadlock-like hangs with a modern Perl.

    - tye        

      Yeah, the change in Perl such that even the implicit closing of a popen-like file handle waits for the child to exit was a pretty sucky change in my book.
      Agreed. Sounds like a case of Perl trying to take care of too much for the programmer. Also sounds like there should be a method like:
      $pipe->nowait(1);
      to get around this.
      POSIX::close()?
Re: What happens to a '-|' (pipe) file handle across a fork()?
by shmem (Chancellor) on Nov 07, 2015 at 01:00 UTC
    elsif ($pid > 0) { # write $pid to .pid file cleanup(); exit(0); ## problem happens HERE }

    If $pid is defined here, you are in the parent process. The child doesn't get a PID from fork().

    ## now in sub-process

    Right; but the parent is gone. What are you trying to do? This is a daemon exiting after fork. Lousy parent, I'd say.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      Right; but the parent is gone. What are you trying to do? This is a daemon exiting after fork. Lousy parent, I'd say.
      That's exactly what a daemon parent is supposed to do: fork() a child into a detached state (see Paul Barry's "Programming the Network with Perl" for examples, i.e. p. 125-126). Otherwise, the process call it would also block forever. But more to the point, I'm referring to the other child, then child resulting from the open($pipe, '-|', 'ip -o xfrm monitor')—that child.
        That's exactly what a daemon parent is supposed to do: fork() a child into a detached state

        Yes of course, a daemon closes all unnecessary file descriptors and forks itself and terminates, to detach from any terminal or I/O lines whatsoever. But in my book, the daemon does this right up front, before doing any other tasks.

        But more to the point, I'm referring to the other child, then child resulting from the open($pipe, '-|', 'ip -o xfrm monitor')—that child.

        Well, that child is kid of the daemons parent, so it might get reaped if grandfather exits. Perhaps reordering would help:

        my $pipe; my $pid = fork(); if (! defined $pid) { die "couldn't fork"; elsif ($pid > 0) { # write $pid to .pid file cleanup(); exit(0); ## problem happens HERE } ## now in sub-process close(STDIN); close(STDOUT); close(STDERR); ... # open the pipe in the child only after daemonizing open($pipe, '-|', 'ip -o xfrm monitor') || die "couldn't start sub-com +mand"; while (my $line = <$pipe>) { ... } close($pipe); cleanup2(); exit(0);
        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'