Question

I have a Bourne shell (/bin/sh) script (for portability) that wants to monitor another program. It should start the other program, then wait for it to exit. When the second program exits, it does some final work and exits itself. The catch is that the script needs to also respond to a signals (eg USR2) and do some work when those signals show up.

My naive implementation is:

#! /bin/sh
echo $$
trap 'echo Respond to USR2' USR2
/bin/sleep 120 &
pid=$!
wait $pid
echo $pid exited with $?
echo Doing final cleanup

This doesn't work. If I send the shell SIGUSR2, the trap fires as expected, but wait also finishes, returning 140. The /bin/sleep continues on its merry way. Typical output:

28849
Respond to USR2
28850 exited with 140
Doing final cleanup

This behaviour is consistent between dash and bash, the two Bourne shell derivatives I have convenient access to.

My current work around is to spin loop waiting for the child PID to disappear, probing with kill. Spin looping seems wasteful, and enlarges the window where my script might erroneously be waiting for the wrong process if PIDs are being rapidly reused.

#! /bin/sh
echo $$
trap 'echo Respond to USR2' USR2
/bin/sleep 15 &
pid=$!
while /bin/kill -0 $pid 2> /dev/null; do
    echo waiting...
    sleep 2
done
echo Doing final cleanup

Is there a better solution given my goal of simultaneously waiting for another process to exit and being able to respond to signals?

Was it helpful?

Solution

You could do:

while wait $pid; test $? -gt 128; do
    kill -0 $pid 2> /dev/null || break;
done

But note the following from the sh standard:

If the exit status of wait is greater than 128, there is no way for the application to know if the waited-for process exited with that value or was killed by a signal. Since most utilities exit with small values, there is seldom any ambiguity. Even in the ambiguous cases, most applications just need to know that the asynchronous job failed; it does not matter whether it detected an error and failed or was killed and did not complete its job normally.

In this case, the ambiguity is slightly different. You don't know if wait was interrupted by the signal or if the child was terminated by a signal.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top