1

In C, I want to catch the SIGINT signal and print out a message like "SIGINT received" by using sigaction and passing a new handler to it via

sa.sa_sigaction = handler;

I don't want to terminate the program.

If I run my program through the shell and generate the signal with Ctrl+c , the signal handler will catch the signal and print out my message.

Afterwards, it will peform the default action which is terminating the process.

What am I doing wrong?

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

static void handler(int sig, siginfo_t* si, void *unused){
    if(sig == SIGINT){
        printf("Signal %i received\n",si->si_signo);
    }
}

int main(int argc, char* argv[]){
    char s [256];


    struct sigaction sa;

    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGINT);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;

    if(sigaction(SIGINT, &sa, NULL) < 0 ){
        perror("sigaction");
    }

    fgets(s,sizeof(s), stdin);
    printf("%s", s);
    return 0;
}
fluter
  • 13,238
  • 8
  • 62
  • 100
Big Dude
  • 264
  • 1
  • 6
  • 25
  • What do you do after the sigaction and before returning from main? – rici Mar 17 '17 at 09:04
  • I've updated the code. I simply echo the user input. – Big Dude Mar 17 '17 at 09:11
  • So how do you know the sigint terminates your program? I think it just finishes because fgets returns. – rici Mar 17 '17 at 09:15
  • When I hit Ctrl+C, I'll receive the message "Signal 2 received". According to the manual of signal(7), 2 stands for SIGINT. 1) I empty the signal mask , this means that no signal should be blocked. 2) I add SIGINT to the signal mask, which means "block this signal"...but removing the line doesn't help either. 3) I pass my own handler to sa.sa_sigaction. – Big Dude Mar 17 '17 at 09:18
  • 2
    The problem is `SIGINT` will not only cause the handler to be called, it will also cause the `read` call be interrupted and returns an error, see my answer below. – fluter Mar 17 '17 at 09:31

1 Answers1

2

The problem is that fgets will call the read system call, and the syscall will return an error when interrupted by SIGINT, see man page of read:

EINTR The call was interrupted by a signal before any data was read; see signal(7).

So you should check the errno of fgets and if it was EINTR continue to call fgets. Try my updated program:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <errno.h>

static void handler(int sig, siginfo_t* si, void *unused){
    if(sig == SIGINT){
        printf("Signal %i received\n",si->si_signo);
    }
}

int main(int argc, char* argv[]){
    char s [256];


    struct sigaction sa = {0};

    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGINT);
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;

    if(sigaction(SIGINT, &sa, NULL) < 0 ){
        perror("sigaction");
    }

    char *p;
    do {
        p = fgets(s,sizeof(s), stdin);
    } while (!p && errno == EINTR);
    printf("%s\n", s);
    return 0;
}
fluter
  • 13,238
  • 8
  • 62
  • 100
  • You're right...I forgot about the read system call.....I just tried it with an infinite loop ( wihle(1)) and it worked.... Thanks a lot! – Big Dude Mar 17 '17 at 09:37