From: wpm@is.morgan.com (W. Phillip Moore)
Subject: signal traps in Bourne -- various and sundry answers
Organization: Informations Services Dept., Morgan Stanley Japan
Date: Thu, 24 Dec 1992 18:01:51 GMT


Steve> Is there a good way to tell a signal service routine to know which
Steve> signal invoked it?  The script below shows how I'm approaching this,
Steve> but surely there is a more elegant way... any suggestions?  

Try this on for size:

function SignalHandler
{
  case $1 in
    HUP|INT|QUIT|TERM)
      echo "Caught a SIG$1 -- doing down..." ;;
    KILL|STOP|CONT)
      echo "Hey, I can't catch a SIG$1, what's going on?" ;;
    *)
      echo "Who needs a SIG$1; happily ignoring..."
  esac  
}

for SIGNAL in `kill -l` ; do
  trap "SignalHandler $SIGNAL" $SIGNAL
done

This assumes that you are using a shell which doesn't have a built-in kill
which prints a numbered table of signals, such as ksh does.  Eg:

kill -l
 1) HUP                  12) SYS                  23) POLL
 2) INT                  13) PIPE                 24) XCPU
etc, etc

Will choke of course, but at least in SunOS:

/usr/bin/kill -l
HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG 
STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH LOST USR1 USR2

is what you're after.  It also assumes your shell will use symbolic names,
rather than integers, as I understand that some older brain-dead versions
do not.

While we're on the subject of signals and shells, here's something else I
find quite useful.  I have a generic wrapper script for use by our
developers which handles automatic restarting of user applications, logs
messages when applications crash, and some other neat stuff you probably
don't care about.  One thing I've provided is special functionailty when a
SIGUSR1 or SIGUSR2 is received, and in addition I trap on some other
signals (HUP,INT,TERM) as well, but I don't want to pass these signal traps
to the child process.

In the parent, I do:

    for SIGNAL in $( /usr/bin/kill -l ) ; do
      case $SIGNAL in
	HUP|INT|QUIT|TERM|USR1|USR2)
	   trap "SignalHandler $SIGNAL" $SIGNAL ;;
	PIPE|ALRM|WINCH)
	   trap - $SIGNAL ; trap "" $SIGNAL ;;
	*) ;;
      esac
    done

But before forking the child and waiting for it:

    (
      for SIGNAL in $( /usr/bin/kill -l ); do
        trap - $SIGNAL
        case $SIGNAL in
          USR1|USR2) trap "" $SIGNAL ;;
                  *) ;;
        esac
      done
      exec $FULLNAME ${1+"$@"}
    ) &

This restored the signal handler for the subshell to its default state,
except for USR1 and USR2 which I want the child to ignore.

If you are playing games with waiting for kids, you'll find you have to do
something ugly to simulate the ERRNO from wait.  In csh or perl (the latter
being much nicer for complicated games with subprocesses and signal
handling in particular), wait will set ERRNO to EINTR if you catch a signal
while waiting, but in ksh the variable ERRNO only gets set when a system
call fails.  Dropping out of wait when a signal is caught doesn't set
this.  Bummer.

Since wait will return 0, and you probably need to keep waiting for the
child anyway, I do something like this:

  child &
  CHILD_PID=$!

  while true ; do
    unset _SIGNAL_CAUGHT
    wait $CHILD_PID
    CHILD_STATUS=$?
    [[ -z $_SIGNAL_CAUGHT ]] && break
  done

In my SignalHandler function, I set the _SIGNAL_CAUGHT to True, so that I
can distinguish in the above loop whether or not I was interrupted or the
child exited.  

This is clearly a lot more than you asked for, but I hope people find it
useful.  

W. Phillip Moore                                        Phone: 81-3-3286-9359
Information Services Department                           FAX: 81-3-3286-9040
Morgan Stanley Japan Ltd.  Tokyo Branch             E-mail: wpm@is.morgan.com
        Ote Center Bldg. 3F, 1-1-3 Otemachi, Chiyoda-ku, Tokyo 100