Preventing propagation of SIGINT to Parent Process

Considering a scenario where a Parent program (could be a C++ program or a Shell Script) executes a Child shell script, when we hit Control+C (or whatever character is configured to be the INTR character) while the Child Shell Script is executing, a SIGINT is sent to all processes in the foreground process group. This includes the parent process. Source : POSIX.1-2008 XBD section 11.1.9 Is there a way to override this default behavior? That the CHILD Process alone handles the SIGNAL without it propagating to the parent? Reference : Stack Overflow Post - Parent Process not Completing when Child is Interrupted (TRAP INT)

asked Jun 28, 2013 at 5:27 243 2 2 silver badges 5 5 bronze badges

3 Answers 3

(Inspired by Gilles's answer)

With the ISIG flag set, the only way for the Child script to get SIGINT without its parent getting SIGINT is for it to be in its own process group. This can be accomplished with the set -m option.

If you turn on the -m option in the Child shell script, it will perform job control without being interactive. This will cause it to run stuff in a separate process group, preventing the parent from receiving the SIGINT when the INTR character is read.

-m This option shall be supported if the implementation supports the User Portability Utilities option. All jobs shall be run in their own process groups. Immediately before the shell issues a prompt after completion of the background job, a message reporting the exit status of the background job shall be written to standard error. If a foreground job stops, the shell shall write a message to standard error to that effect, formatted as described by the jobs utility. In addition, if a job changes status other than exiting (for example, if it stops for input or output or is stopped by a SIGSTOP signal), the shell shall write a similar message immediately prior to writing the next prompt. This option is enabled by default for interactive shells.

The -m option is similar to -i , but it doesn't alter the shell's behavior nearly as much as -i does.

#!/bin/sh trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT echo "PARENT: pid=$$" echo "PARENT: Spawning child. " ./Child echo "PARENT: child returned" echo "PARENT: exiting normally" 
#!/bin/sh -m # ^^ # notice the -m option above! trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT echo "CHILD: pid=$$" echo "CHILD: hit enter to exit" read foo echo "CHILD: exiting normally" 

This is what happens when you hit Control + C while Child is waiting for input:

$ ./Parent PARENT: pid=12233 PARENT: Spawning child. CHILD: pid=12234 CHILD: hit enter to exit ^CCHILD: caught SIGINT; exiting PARENT: child returned PARENT: exiting normally 

Notice how the parent's SIGINT handler is never executed.

Alternatively, if you'd rather modify Parent instead of Child , you can do this:

#!/bin/sh trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT echo "PARENT: pid=$$" echo "PARENT: Spawning child. " sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a shell script echo "PARENT: child returned" echo "PARENT: exiting normally" 
#!/bin/sh trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT echo "CHILD: pid=$$" echo "CHILD: hit enter to exit" read foo echo "CHILD: exiting normally" 

Alternative ideas

  1. Modify the other processes in the foreground process group to ignore SIGINT for the duration of Child . This doesn't address your question, but it may get you what you want.
  2. Modify Child to:
    1. Use stty -g to back up the current terminal settings.
    2. Run stty -isig to not generate signals with the INTR , QUIT , and SUSP characters.
    3. In the background, read the terminal input and send the signals yourself as appropriate (e.g., run kill -QUIT 0 when Control + \ is read, kill -INT $$ when Control + C is read). This is not trivial, and it may not be possible to get this to work smoothly if the Child script or anything it runs is meant to be interactive.
    4. Restore the terminal settings before exiting (ideally from a trap on EXIT ).
    #define _XOPEN_SOURCE 700 #include #include int main(int argc, char *argv[]) < // todo: add error checking void (*backup)(int); setpgid(0, 0); backup = signal(SIGTTOU, SIG_IGN); tcsetpgrp(0, getpid()); signal(SIGTTOU, backup); execvp(argv[1], argv + 1); return 1; >
    Example usage from Child :
    #!/bin/sh [ "$" = true ] || < # restart self after calling setpgid(0, 0) exec env DID_SETPGID=true setpgid "$0" "$@" # exec failed if control reached this point exit 1 >unset DID_SETPGID # do stuff here