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

I'm trying to figure out what signals are hitting a script of mine. I'd like to write a default signal handler that sends a note to syslog and then does whatever the signal would have done originally.

I tried this...

map { $SIG{$_} = \&handlesig } keys(%SIG); sub handlesig { my $sig = shift; syslog('info',"Got signal '$sig'"); local $SIG{$sig} = 'DEFAULT'; kill $sig, $$; }

But the local $SIG{$sig} = 'DEFAULT'; line doesn't seem to do anything. handlesig gets called recursively 50-odd times, and then my script dies.

Thanks!
-Pileofrogs

Replies are listed 'Best First'.
Re: Signal Question
by ikegami (Patriarch) on Nov 29, 2006 at 22:53 UTC
    I'm guessing the second signal is only handled after the handler returns from handling the first signal, at which point local $SIG{$sig} has already been unwound. That should be easy to verify by putting a print at the top of handlesig and another at the bottom.

      That makes sense to me. Trying to think of a solution, the first thing I came up with that would probably work is to write a signal reflector.

      sub spawn { my $pid; if( $^O =~ /Win32/ ) { $pid= system( 1, @_ ); } else { # do something more complex with fork() and exec() # since Perl doesn't provide a portable way to spawn } return $pid; } my $mirror= spawn( $^X, 'mirror.pl', $$ ); my @sigs; $SIG{$_}= \&logSig for keys(%SIG); $SIG{USR1}= \&restoreHandlers; sub logSig { my $sig = shift @_; push @sigs, $sig; syslog( 'info', "Got signal '$sig'" ); $SIG{$sig}= 'DEFAULT'; kill $sig, $mirror; } sub restoreHandlers { my $sig= shift @_; if( ! @sigs ) { syslog( 'info', "Got signal '$sig'" ); } else { $SIG{$_} = \&logsig for @sigs; @sigs= (); } }

      Then mirror.pl would be:

      my $caller= shift @ARGV; $SIG{$_} = \&reflectSig for keys(%SIG); sub reflectSig { my $sig= shift @_; kill $sig, $caller; kill 'USR1', $caller; }

      A bit complex, unfortunately. Of course, if you get sent the same signal twice, you might not log two lines; that is just how signals work.

      In the unlikely event that signals have priorities and when a process has received both a USR1 and some other signal it could decide to process the USR1 before the other (even though the other came in first), that could cause problems.

      - tye        

Re: Signal Question
by jbert (Priest) on Nov 30, 2006 at 09:01 UTC
    If you are on Linux, you can run your script under 'strace'. This will log signals received by the process as well as system calls made. If you don't want to see the syscalls, run strace -e trace=none perl yourscript.pl.

    If you're on Solaris, then 'truss' will probably do the same job for you.

    This is with a kill -CONT xxx being done in another window.

    $ strace -e trace=none sleep 50 --- SIGCONT (Continued) @ 0 (0) --- --- SIGCONT (Continued) @ 0 (0) --- --- SIGCONT (Continued) @ 0 (0) ---
Re: Signal Question
by superfrink (Curate) on Nov 30, 2006 at 05:08 UTC
    I do not have a complete answer but a while ago I wanted to figure out where some signals were coming from. I patched the Linux kernel to log signals sent from one process to another.

    If you feel like modifying the kernel (and you are using Linux) the code linked to below might help you get started.

    http://superfrink.net/code/sig2/siglog.html
Re: Signal Question
by zentara (Cardinal) on Nov 30, 2006 at 12:06 UTC
    I had this snippet laying around.
    #!/usr/bin/perl use warnings; use strict; sub handler_universal { print "\ngot a signal $_[0]\n"; $SIG{$_[0]} = \&handler_universal; } my @nami = keys(%SIG); #foreach( @nami){ print "$_\t$SIG{$_}\n"; } while(1){ for my $namesig (@nami) {$SIG{$namesig} = \&handler_universal;} }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum

      That was very useful, but why do you re-assign the SIG inside the handler?

      non-Perl: Andy Ford