Reputation: 25645
On Linux, I would like to spawn off some servers as background services.
As soon as one of them terminates with an exit code != 0, they shall all to be SIGTERMed, and after 3 seconds grace period be KILLed if they don't want to go.
The same shall happen when Ctrl-C is pressed.
I want a rough equivalent of
set -e
server1 &
server2 &
server3 &
wait (+ kill on error)
(Unfortunately that code above does not have all the mentioned properties.)
It shall not be possible for children to steal/escape the Ctrl-C signals.
I accept solutions in Bash and Python (using subprocess
). For Python they may only use standard library functions (nothing I have to download modules with pip
first). If you have a cool way to do this in another language, please do not hesitate to show it.
Bonus points if it also works on Mac OS.
Upvotes: 2
Views: 258
Reputation: 212168
#!/bin/sh
# Avoid <() process substitutions for portability's sake
set -bm
trap clean SIGCHLD
trap 'kill 0' SIGINT
trap 'sleep 3; kill -9 0' SIGTERM
clean() {
while read spec pid extra; do
wait $pid || kill 0
done << EOF
$( jobs -l | grep 'Exit\|Done' )
EOF
}
server1 &
server2 &
server3 &
wait
Upvotes: 0
Reputation: 487725
It is technically possible to do this directly in bash, but I wouldn't, because it requires a lot of polling and will be inefficient and error-prone (basically you have to kill -0 $pid
to test if the process is still around). Here's a bit of bash code (from a script I have lying around) that shows how to record a backgrounded process pid:
start_tail() {
if [ x$TAIL_PID != x ]; then
echo "internal error: already running tail (pid $TAIL_PID)"
fi
tail -0f $1 &
TAIL_PID=$!
trap "kill $TAIL_PID" 0 1 2 3 15
}
stop_tail() {
if [ x$TAIL_PID != x ]; then
kill $TAIL_PID; trap 0 1 2 3 15
TAIL_PID=""
fi
}
and in this particular case you could probably make it work better (than the above, which is deliberately limited) using a "coprocess" and a fair bit of hacking.
It will be easier in Python, but don't use subprocess
; use direct OS calls (as @geekosaur described above). @jldupont's recommendation for http://supervisord.org/ looks like a good one (especially since it is written in Python).
Upvotes: 0
Reputation: 61369
Rather than write the solution for you, I will suggest where to look: setpgrp
, waitpid
, kill
. Note that "It shall not be possible for children to steal/escape the Ctrl-C signals." is neither possible (the only signals that cannot be evaded are SIGSTOP
and SIGKILL
) nor meaningful (you aren't delivering SIGINT
to them).
You can't do this properly in bash
because wait
waits for all specified (or, if none are specified, simply all) processes; you need "any" semantics, not "all".
Upvotes: 1