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


I have 1 executable which runs several process in sequence. Since these processes takes a lot of time, I want the executable to work as a daemon and print to me it's current status when I'm sending a signal to it.

I've made all this work, but every time I send a signal to it to get the current status it kills the current process to give me the information. So do I need to set some parameters or something to make sure children can work unaffected by parents signal status?
This is my code in short:

$SIG{USR1} = \&status; sub status () { $SIG{USR1} = \&status; print "\n Status is: $status"; } foreach $path (@paths) { my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $path); $status = $path; waitpid($pid, 0); }

Any suggestions ?

Replies are listed 'Best First'.
Re: Signal to parent-process. Does it affect it's children?
by betterworld (Curate) on Sep 07, 2008 at 17:34 UTC

    I think the problem is that waitpid does not continue after the signal has been processed, however the child process is still running. I suggest putting a loop around waitpid. The return value of waitpid should give you a hint if the child actually exited or if a signal has interrupted the call.

    Update: By the way, I don't think that you need to set the signal handler again in status. Once it has been set, it gets executed every time a USR1 signal arrives.

      Seems like the interruption terminated the child. So far I haven't seen anything in the Poddoc manual.

      Resetting the signal handler depends on the system and whether its safe system calls or not. I'd rather be safe if I'm porting it to another system ..

        If you're ever thinking of porting to a Windows system, you should expect everything involving forking and external signals to break. That's because on the platform Perl emulates forking with multithreading, and that emulation may not work well with whatever emulation you find of Unix signals.
Re: Signal to parent-process. Does it affect it's children?
by shmem (Chancellor) on Sep 07, 2008 at 21:25 UTC
    I've made all this work, but every time I send a signal to it to get the current status it kills the current process

    Well, not for the code below on my box. I've added/changed some bits.

    use IPC::Open3; my $status; sub status () { print "Status is: $status\n"; $SIG{USR1} = \&status; } $SIG{USR1} = \&status; print "$$\n"; my @paths; my $c = 10; push @paths,"sleep ".$c++ for 0 .. 3; $c = 0; foreach $path (@paths) { print "loop ",++$c,"\n"; my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $path); $status = $path; waitpid($pid, 0); print "after waitpid\n"; }

    Running it and killing the process several times yields

    3297 loop 1 Status is: sleep 10 Status is: sleep 10 Status is: sleep 10 after waitpid loop 2 Status is: sleep 11 Status is: sleep 11 Status is: sleep 11

    so yes, waitpid is re-entrant (at least on my box) and sending USR1 doesn't advance the loop and re-open CMD_* (which would kill the child process implicitly). Did you notice the line

    print "Status is: $status\n";

    above? Do you have unbuffered I/O on STDOUT?

    update: yes, some signals get propagated to the children (USR1 isn't among them). Adding these bits to the script

    sub default { print "got SIG ".shift()."\n"} $SIG{$_} = \&default for keys %SIG; $SIG{USR1} = \&status;

    and invoking a perl script

    #!/usr/bin/perl # have to print to a terminal directly, # since default file handles are re-directed open O ,'>', '/dev/pts/1'; sub default { my $sig = shift; print O "child Status: got SIG$sig\n"; $SIG{$sig} = \&default; } $SIG{$_} = \&default for keys %SIG; sleep $_ for 1..shift;

    yields, killing the main script with <Ctrl>-C:

    3437 loop 1 child Status: got SIGINT got SIG INT got SIG CHLD after waitpid loop 2 ...

    You might want to wrap your call to open3() into a subroutine which resets $SIG{USR1} to 'IGNORE' using local. I suspect your problem being elsewhere. BTW, on which platform do you run?

      Hmmmm. I might be confused with functions being re-entrant and un-buffered I/O.

      What happens in my process is that I'm not having any waitpid but instead polling to check if a new line has been printed on the stream.
      Let me put the exact code since this might be something specific:

      $selector = IO::Select->new(); $selector->add(*CMD_ERR, *CMD_OUT); while(@ready = $selector->can_read) { foreach my $fh (@ready) { if(fileno($fh) == fileno(CMD_ERR)) { my $line = scalar <CMD_ERR>; print $line; } } }

      Basically if what your meaning by unbuffered is having lines on the stream which is not printed when the signal occur, then yes I very likely have.

      UPDATE: I've tried this on two systems. Opensolaris and SLC4, both 32 bit. My perl version is: v5.8.4

        Hi, maybe this is a stupid question but, how do you send the kill signal? E.g. kill -10 1234 sends the SIGUSR1 signal to process 1234 while kill -10 -1234 sends the signal to the whole process group (process 1234 plus its child processes). Just a thought...