0
votes

Consider the following bash script s:

#!/bin/bash
echo "Setting trap"
trap 'ctrl_c' INT
function ctrl_c() {
        echo "CTRL+C pressed"
        exit
}
sleep 1000

When calling it ./s, it will sleep. If you press Ctrl+C it prints the message and exits.

Now call it, open another terminal and kill the corresponding bash pid with -INT flag. It does not work or do anything. Why?

Kill without flags works but does not call the ctrl_c() function.

3
Send kill -INT to the process/PID you want to stop, here sleep. - Cyrus

3 Answers

2
votes

From the POSIX standard for the shell:

When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.

To verify this, we can run strace on the shell executing your script. Upon sending the SIGINT from another terminal, bash just notes that the signal has been received and returns to waiting for its child, the sleep command, to finish:

rt_sigaction(SIGINT, {0x808a550, [], 0}, {0x80a4600, [], 0}, 8) = 0
waitpid(-1, 0xbfb56324, 0)              = ? ERESTARTSYS
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]})                = -1 EINTR (Interrupted system call)
waitpid(-1, 

To make kill have the same effect as Ctrl-C, you should send SIGINT to the process group. The shell by default will put every process of a new command into its own pgrp:

$ ps -f -o pid,ppid,pgid,tty,comm -t pts/1
  PID  PPID  PGID TT       COMMAND
 3460  3447  3460 pts/1    bash
29087  3460 29087 pts/1     \_ foo.sh
29120 29087 29087 pts/1         \_ sleep

$ kill -2 -29087

And now the trap runs and the shell exits:

waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 29120
--- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=3556, si_uid=1000} ---
sigreturn({mask=[CHLD]})                = 29120
...
write(1, "CTRL+C pressed\n", 15)        = 15
...
exit_group(0)                           = ?
0
votes

When we use the ps aux command to find the PID values, we see that there are 2 processes, this program and sleep. Program process does not interrupt with -INT parameter as you say. However, when the -2 or -INT (same meaning) parameter is used for the sleep process, the process is interrupted as expected. The message prints and exits.I did not understand why the main process did not stop, but I wanted to show that the sleep process can be interrupted by this method. I hope it will be good for you.

0
votes

sleep is not a shell built-in:

$ which sleep
/bin/sleep

This means that when you run sleep it will run as a separate child process. When a child process is created various things are copied from parent to child (inherited) and one of these is the signal mask. Note: only the mask. So if you had set:

 trap '' INT

then sleep would have inherited that - it means ignore the signal. However, in your case you expect sleep (which is probably written in C) to execute a bash function. No can do - wrong language for a start. Imagine: it would have to execute bash to execute the bash function.

Caveat - details concerning signal handling can vary across platforms.

It is possible to make sleep a builtin, see here but that's probably more trouble than it is worth.