Search code examples
perlforkipc

IPC::Run - Detection of premature child exit and closed pipes


I would like to use IPC::Run to communicate with child via child's STDIN, STDOUT and STDERR (start, pump, finish). It seems to work.

I would like to know how to detect

  • premature child exit (e.g. caused by errors)
  • pipes closed by the child

Solution

  • The pump throws a die on errors, or writes its message to STDERR if "called after all harnessed activities have completed." See right before ROUTINES section and pump itself. The second case can come about if the child exited. So wrap the pump call in eval, and also convert warnings to die to catch both cases

    if ($talk_to_child) 
    {
        eval {
            local $SIG{__WARN__} = sub { die "pump WARNING: @_" };
            pump $harness;
        };
        if ($@) { 
            print $@;
            $talk_to_child = 0;
        }; 
    }
    # ... and eval {} for finish()
    

    But this alone won't cut it: when a parent tries to write to a child that exited it gets a SIGPIPE, which outright terminates the process. The same goes when a child closes streams and the parent attempts to write. So also install a signal handler for SIGPIPE

    $SIG{PIPE} = sub { 
        say "$_[0]: $!";
        $talk_to_child = 0;  # global
    };
    

    so that the parent survives the SIGPIPE. Consider local-izing the change to the global %SIG by doing local $SIG{PIPE} = ... instead, a good practice even just on general principle. On the other hand, there's good sense in globally handling a signal that can terminate you out of blue (even in cases where the handler may decide to exit).

    The eval is still needed even as $SIG{PIPE} is handled since pump throws, too.

    These together take care of all tests I came up with, practically as they stand. Still, some processing in the handler and in eval is needed to distinguish cases of interest if that is wanted.

    If this adds up to too much another way is to check before each call. See this post for one-line checks (wrapped in subs) of: (1) whether a child is running, using result, and (2) whether "there are open I/O channels or active processes", using pumpable.

    I think that you want both, and also throw in the SIGPIPE handler. That should cover it.

    I cannot be more specific here since the question doesn't provide specifics.