3

When you press Ctrl-C, foreground processes receive SIGINT:

$ bash -c 'sleep 100; echo program died'
^C
$ echo $?
130

However, if a program installs a SIGINT handler, parent program doesn't receive the signal. Why?

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>

void sig_int(int sig_num)
{
    exit(1);
}

static struct sigaction sigact = { .sa_handler=sig_int };

int main(int argc, char *argv[])
{
    sigaction(SIGINT,&sigact,NULL);
    sleep(100);
    return 0;
}

bash didn't die:

$ bash -c './a.out; echo program died'
^Cprogram died

Related to Bash not trapping interrupts during rsync/subshell exec statements , but all answers there are workarounds.

Community
  • 1
  • 1
basin
  • 3,949
  • 2
  • 27
  • 63
  • I think that this is related to this post http://unix.stackexchange.com/questions/149741/why-is-sigint-not-propagated-to-child-process-when-sent-to-its-parent-process – terence hill Nov 17 '15 at 20:51

1 Answers1

1

The shell ignore SIGINT if not sent directly from the terminal

This long post explain what is happening in details. Here I'll try to summarise the most important concepts and propose a working solution.


It turns out that the shell is programmed to ignore the SIGINT if it is not directly sent from the terminal (by hitting CTRL-C). If a subprocess intercepts it then it must exit by explicitly killing itself with SIGINT, quoting the post:

"If you don't catch SIGINT, the system automatically does the right thing for you: Your program exits and the calling program gets the right "I-exited-on-SIGINT" status after waiting for your exit.

But once you catch SIGINT, you have to take care of the proper way to exit after whatever cleanup you do in your SIGINT handler.

Decide whether the SIGINT is used for exit/abort purposes and hence a shellscript calling this program should discontinue. This is hopefully obvious. If you just need to do some cleanup on SIGINT, but then exit immediately, the answer is "yes".

If so, you have to tell the calling program about it by exiting with the "I-exited-on-SIGINT" status.

There is no other way of doing this than to kill yourself with a SIGINT signal. Do it by resetting the SIGINT handler to SIG_DFL, then send yourself the signal.

void sigint_handler(int sig)
{
  [do some cleanup]
  signal(SIGINT, SIG_DFL);
  kill(getpid(), SIGINT);
}

SIGINT Handler

Here is a working version of the handler that intercepts the signal and correctly kill itself (and thus it doesn't print 'program died'). OTOH, If you send a different signal the handler run the exit function and you will see again 'program died' printed on the screen.

void sig_int(int sig_num)
{
    if (sig_num == SIGINT) {
        printf("received SIGINT\n");
        signal(SIGINT, SIG_DFL);
        kill(getpid(), SIGINT);
    } else {
        exit(1);
    }
}

static struct sigaction sigact = { .sa_handler=sig_int };

int main(int argc, char *argv[])
{
    sigaction(SIGINT,&sigact,NULL);
    printf("go to sleep\n");
    sleep(3);
    printf("awaken\n");
    return 0;
}
terence hill
  • 3,354
  • 18
  • 31