Question

I am writing a bash wrapper for an application. This wrapper is responsible for changing the user, running the software and logging its output. I also want it to propagate the SIGINT signal.

Here is my code so far :

#!/bin/bash
set -e; set -u

function child_of {
    ps --ppid $1 -o "pid" --no-headers | head -n1
}

function handle_int {
    echo "Received SIGINT"
    kill -int $(child_of $SU_PID)
}

su myuser -p -c "bash /opt/loop.sh 2>&1 | tee -i >(logger -t mytag)" &
SU_PID=$!

trap "handle_int" SIGINT

wait $SU_PID
echo "This is the end."

My problem is that when I send a SIGINT to this wrapper, handle_int gets called but then the script is over, while I want it to continue to wait for $SU_PID.

Is there a way to catch the int signal, do something and then prevent the script from terminating ?

Was it helpful?

Solution

You have a gotcha: after Ctrl-C, "This is the end." is expected but it never comes because the script has exited prematurely. The reason is wait has (unexpectedly) returned non-zero while running under set -e.

According to "man bash":

If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes. When bash is waiting for an asynchronous command via the wait builtin, the reception of a signal for which a trap has been set will cause the wait builtin to return immediately with an exit status greater than 128, immediately after which the trap is executed.

You should wrap your wait call in set +e so that your program can continue running after handling a trapped signal while waiting for an asynchronous command.

Like this:

# wait function that handles trapped signal on asynchronous commands.
function safe_async_wait {
  set +e
  wait $1 # returns >128 on asynchronous commands
  set -e
}
#...
safe_async_wait $SU_PID
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top