Shell – Starting an interactive shell as an asynchronous process (signal delivery)

shellshell-scriptsignals

Consider this short script. It sets up two signal handlers; one for USR1 and another for USR2. Then it starts an interactive shell session.

#!/bin/sh

sigusr1_handler () {
    variable=1
    printf "SIGUSR1: variable is now %d\n" "$variable"
}

sigusr2_handler () {
    variable=2
    printf "SIGUSR2: variable is now %d\n" "$variable"
}

variable=0

printf "At beginning, variable is %d\n" "$variable"

trap 'sigusr1_handler' USR1
trap 'sigusr2_handler' USR2

/bin/sh

printf "At end, variable is %d\n" "$variable"

Running it:

$ ./script.sh
At beginning, variable is 0
$ kill -s USR1 $PPID
$ kill -s USR2 $PPID
$ kill -s USR1 $PPID
$ exit
SIGUSR1: variable is now 1
SIGUSR2: variable is now 2
At end, variable is 2

The first thing I noticed is that the parent shell is not handling the signals until the child shell terminates. I found that this is documented by POSIX, so I'm at fault for not reading the documentation properly.

The second thing I noticed was that signal aren't queued up. That may be really basic knowledge, but it had escaped me. I assume that while the child process is executing, the generated signals are pending, and if a pending signal is generated again, it is simply ignored.

My issue is that I need to have the signals delivered to the signal handlers of the parent process while the interactive shell (the child process) is running. I need this because I want to make decisions based on the value $variable after the termination of the child process, and if signals are generated in the same order as in the interactive session above, $variable must have the value 1 at the end of the script.

So my naïve solution was to start the child process as an asynchronous background job:

/bin/sh &
wait

(I know I need to check the return value of wait in case of signal delivery, but I'll set that up later.)

This does not work. The child process exits immediately:

$ sh -x script.sh
+ variable=0
+ printf At beginning, variable is %d\n 0
At beginning, variable is 0
+ trap sigusr1_handler USR1
+ trap sigusr2_handler USR2
+ wait
+ /bin/sh
+ printf At end, variable is %d\n 0
At end, variable is 0

(It looks like the wait is executed before the shell in the trace output above, but I am doing it in the correct order in the script.)

Using /bin/sh -i & changes nothing. I made further attempts with /bin/sh -s and redirecting /dev/stdin into it, and combinations of these (-i -s), but to no avail.

So now my question is thus: How may I start an interactive shell from a script as an asynchronous foreground process (i.e. accepting input as any interactive shell) so that its parent process may have signals delivered as soon as they are generated? Or, is another design possible?

Best Answer

Satō Katsura's comment answers my question adequately:

It's system-dependent whether signals are queued or not. There is no guarantee signals are delivered in a certain order. Signals of the same type may be delivered in the same order they are generated, but that also is system-dependent (f.i. it's true on Linux). Use pipes for command channels if you need to send more than one kind of command.

Related Question