The POSIX spec says

The system() function shall ignore the SIGINT and SIGQUIT signals, and shall block the SIGCHLD signal, while waiting for the command to terminate. If this might cause the application to miss a signal that would have killed it, then the application should examine the return value from system() and take whatever action is appropriate to the application if the command terminated due to receipt of a signal.

This means that a program that starts a long-running sub-process will have SIGINT and SIGQUIT blocked for a long time. Here is a test program compiled on my Ubuntu 18.10 laptop:

$ cat > test_system.c <<< EOF
#include <stdlib.h>

int main() {
    system("sleep 86400");  // Sleep for 24 hours
}
EOF
$ gcc test_system.c -o test_system

If I start this test program running in the background...

$ ./test_system &
[1] 7489

..Then I can see that SIGINT(2) and SIGQUIT(3) are marked as ignored in the bitmask.

$ ps -H -o pid,pgrp,cmd,ignored
  PID  PGRP CMD                                  IGNORED
 6956  6956 -bash                       0000000000380004
 7489  7489   ./test_system             0000000000000006
 7491  7489     sh -c sleep 86400       0000000000000000
 7492  7489       sleep 86400           0000000000000000

Trying to kill test_system with SIGINT has no effect..

$ kill -SIGINT 7489

.. But sending SIGINT to the process group does kill it (this is expected, it means that every process in the process group receives the signal - sleep will exit and system will return).

   $ kill -SIGINT -7489
   [1]+  Done                    ./test_system

Questions

  1. What is the purpose of having SIGINT and SIGQUIT ignored since the process can still be killed via the process group (that's what happens when you do a ^C in the terminal).
  2. Bonus question: Why does POSIX demand that SIGCHLD should be blocked?
  3. Update If SIGINT and SIGQUIT are ignored to ensure we don't leave children behind, then why is there no handling for SIGTERM - it's the default signal sent by kill!

1 Answers

1
PSkocik On Best Solutions

SIGINT and SIGQUIT are terminal generated signals. By default, they're sent to the foreground process group when you press Ctrl+C or Ctrl+\ respectively.

I believe the idea for ignoring them while running a child via system is that the terminal should be as if it was temporarily owned by the child and Ctrl+C or Ctrl+\ should temporarily only affect the child and its descendants, not the parent.

SIGCHLD is blocked so that system's the SIGCHLD caused by the child terminating won't trigger a SIGCHLD handler if you have one, because such a SIGCHLD handler might reap the child started by system before system reaps it.