9

I have a bash script that mounts and unmounts a device, which performing some read operations in between. Since the device is very slow, the script takes about 15 seconds to complete (the mount taking atleast 5-6 seconds). Since leaving this device mounted can cause other problems, I don't want this script to be interrupted.

Having said that, I can correctly handle SIGINT (Ctrl+c), but when I try to handle SIGTSTP (Ctrl+z), the script freezes. Which means the signal is trapped but the handler doesn't run.

#!/bin/sh
cleanup()
{
    # Don't worry about unmounting yet. Just checking if trap works.
    echo "Quitting..." > /dev/tty
    exit 0
}
trap 'cleanup' SIGTSTP
...

I manually have to send the KILL signal to the process. Any idea why this is happening and how I can fix it?

Jolta
  • 2,620
  • 1
  • 29
  • 42
Ram
  • 1,161
  • 1
  • 11
  • 34

1 Answers1

6

The shell does not execute the trap until the currently executing process terminates. (at least, that is the behavior of bash 3.00.15). If you send SIGINT via ^c, it is sent to all processes in the foreground process group; if the program currently executing receives it and terminates then bash can execute the trap. Similarly with SIGTSTP via ^z; bash receives the signal but does not execute the trap until the program that was being run terminates, which it does not do if it takes the default behavior and is suspended. Try replacing ... with a simple read f and note that the trap executes immediately.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • That makes so much sense. So it sounds like unless all the binaries you execute with the script also handle ^z the way you do, there is no way to properly handle ^z. Is that right? – Ram Oct 10 '12 at 17:42
  • You can run the job asynchronously under setsid and wait for it. eg: `setsid cmd & wait` instead of just `cmd`. – William Pursell Oct 10 '12 at 17:44
  • Seems to do it. Thanks for you help! But just curious, won't the process still remain stopped under a new session? – Ram Oct 10 '12 at 18:10
  • If the cmd is started under setsid, it does not receive the SIGTSTP generated by ^z. – William Pursell Oct 10 '12 at 18:11
  • Sorry, but I don't get it. If I replace SIGTSTP in the above script by SIGINT, the cleanup function is called as expected when ctrl-c is pressed. Also, if I do `pkill -20 test.sh`, it works as expected with SIGTSTP. So what is happening? – January Oct 10 '12 at 18:33
  • 1
    @January When ctrl-c is pressed, the currently running process and the script both receive SIGINT. The currently running process terminates, and then the script executes the trap. When you type ^z, SIGTSTP is sent to both the script and the current process, so the current process is suspended and the script hangs waiting for it. When you type `kill -20` to send SIGTSTP, *only* the script receives the signal. – William Pursell Oct 10 '12 at 18:47