bash behavior on sigterm

3

1

Have a script as below:

#!/bin/bash
#
# run this script.  don't run it if it's already running.
#

PIDFILE=/tmp/script.pid
LOGFILE=script.log


if [[ -f $PIDFILE ]]; then
    echo "$PIDFILE exists.  Not going to run..."
    exit 0
fi

sleep 10m >> $LOGFILE 2>&1 &
PID=$!
echo $PID > $PIDFILE

trap "echo Exiting...; rm $PIDFILE; exit $?" INT TERM EXIT KILL

wait $PID

Am invoking this script as below:

timeout 2m ./test_script

On timeout or ctrl+c, the script prints "Exiting" twice.

# timeout 2m ./test_script
^CExiting...
Exiting...
rm: cannot remove `/tmp/script.pid': No such file or directory

Below is the output of strace and more data:

# ps -ef | grep -v grep | egrep -i "sleep|time"
root      8571  4690  0 12:17 pts/0    00:00:00 timeout 2m ./test_script
root      8572  8571  0 12:17 pts/0    00:00:00 /bin/bash ./test_script
root      8573  8572  0 12:17 pts/0    00:00:00 sleep 10m
# cat /tmp/script.pid
8573
# strace -p 8571
Process 8571 attached - interrupt to quit
wait4(-1, 0x7fffc43fad4c, 0, NULL)      = ? ERESTARTSYS (To be restarted)
--- SIGINT (Interrupt) @ 0 (0) ---
kill(0, SIGINT)                         = 0
kill(0, SIGCONT)                        = 0
--- SIGCONT (Continued) @ 0 (0) ---
rt_sigreturn(0)                         = 61
--- SIGINT (Interrupt) @ 0 (0) ---
rt_sigreturn(0x2)                       = 61
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 8572
--- SIGCHLD (Child exited) @ 0 (0) ---
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
Process 8571 detached

Can someone kindly assist me with understanding the internals on why the script traps the signal twice to print "Exiting..." 2 times ?

cog_n1t1v3

Posted 2015-05-03T19:23:19.187

Reputation: 33

rm: cannot remove /tmp/script.pid: No such file or directory <-- you're executing echo "Exiting..." twice, are you sure you need all of INT TERM EXIT KILL - might it be that two of them get triggered - at the same time - when you hit CTRL-C? – Hannu – 2015-05-03T19:51:09.967

that is probably what is happening. two signals being handled almost simultaneously by trap. As far as the script needing to trap-on all the 4 signals specified, yes I need them all. As an alternative, I am simply using the timeout from within my script. This gets around the problem of double invocation when timeout completes – cog_n1t1v3 – 2015-05-03T21:51:57.117

You cannot catch KILL, so that one does not count. If I run your script using bash 4.1.5 however, I am seeing only one message for TERM (when it does exit) and no exit for INT. Perhaps the version of bash matters. – Thomas Dickey – 2015-05-04T10:31:58.840

Answers

1

If you replace your trap statement with these three lines:

trap "echo Exiting... INT;  exit $?" INT
trap "echo Exiting... TERM; exit $?" TERM
trap "echo Exiting... EXIT; exit $?" EXIT

you'll get the output

Exiting... TERM
Exiting... EXIT

from which we can deduce

  • The trap … TERM statement causes the shell to trap the SIGTERM signal.  The timeout command sends the process a SIGTERM (by default) when the timeout expires.  So the shell catches the signal and executes the specified command, including the echo, the rm (in your actual script), and the exit.
  • The trap … EXIT statement causes the shell to leave itself a sticky-note saying "remember to do this before I go home".  So, when the SIGTERM trap executes the exit command, the EXIT trap is executed.
  • When the EXIT trap executes the exit command, the script actually exits, rather than executing the EXIT trap and going off into the recursion inferno.

If you type Ctrl+C while the script is running, you will get the INT trap followed by the EXIT trap.  If you run the script without timeout, or with a timeout duration that is longer than the sleep time, you will get the EXIT trap only.

It's probably good enough to say

trap "exit $?" INT TERM
trap "echo Exiting...; rm $PIDFILE" EXIT

I believe that the EXIT trap doesn't need to execute exit, because you get into the EXIT trap by executing an exit command (including the implicit one at the end of the script), so, when you finish executing the EXIT trap (echo and rm), the shell has nothing left to do but exit.  The only question is what exit status the script exits with.  And, if you were saving some exit status value and doing rm $PIDFILE; exit $saved_status, that might be interesting.  But as long as you're talking about rm $PIDFILE; exit $?, the script is probably going to exit with the exit status of the rm; and that will probably happen by default if the rm is the last command you execute.

I did some quick tests that suggested that it's possible to leave off the

trap "exit" INT TERM

command, but I don't understand that.  YMMV.


P.S.  As Thomas said, trap … KILL is ineffective.

G-Man Says 'Reinstate Monica'

Posted 2015-05-03T19:23:19.187

Reputation: 6 509