Search code examples
linuxperlexecipcinter-process-communicat

Checking if a forked child has exec'ed yet in perl


I've got the following design in a perl script:

my $child_pid = fork;
if( ! $child_pid ){
    # do some stuff...
    exec( $user_specified_command );
else{
   # wait for child to exit
   waitpid( $child_pid, 0 );
}
# Continue with the script

I'm interested in getting an alert in the parent when the child execs, so that I can get some details about $user_specified_command (specifically, to use lsof to find out if stdout is redirected to a regular file). The result would be something like this:

my $child_pid = fork;
if( ! $child_pid ){
    # do some stuff...
    exec( $user_specified_command );
else{
   # wait until the child exec's
   wait_child_exec();

   # do some stuff...

   # wait for child to exit
   waitpid( $child_pid, 0 );
}
# Continue with the script

I could loop and grep ps output until the name changes, but it seems like exec is a serious enough event that there's a better way.


Solution

  • One general approach to this is to create a pipe in the parent which is inherited by the child, and have the parent block (or poll) the read end of the pipe.

    Assuming the child has FD_CLOEXEC or, better, a suitable value of $^F, the child's call to exec() will close the pipe's write end and generate an EOF for the parent:

    # Run a command in a child process, returning to the parent only after
    # the child process has called exec'd or, failing that, terminated.
    #
    # WARNING - this code not rigorously tested
    #
    sub spawn_patiently {
      my ($rd, $wr);
    
      return unless pipe($rd, $wr);
      # XXX This assumes $^F is less than fileno($wr)
      #     In practice, you'd want it to be less than fileno($rd), too
    
      my $pid = fork();
      return unless defined $pid;
    
      if (! $pid) {
        exec @_;
        die "exec: $!";
      }
    
      # parent - wait for child to exec
      close($wr);
      read($rd, my $dummy, 1);
    
      1;
    }