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(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"
#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