-4

I use the following code to catch Ctrl+C in my C program

Code

void sig_handler(int signo)
{
    if (signo == SIGINT) 
        exit(EXIT_SUCCESS);
}


void main ()
{
    ......

    if(signal(SIGINT, sig_handler)== SIG_ERR) 
    {
        printf(">>>>>>>>>>>>>>>>>>>>> SIG INT EROOR !!!! sigint=%d ID=%d \n",SIGINT, getpid());
    }
    else 
        printf(">>>>>>>>>>AFTER>>>>>>>>>>> SIG INT  sigint=%d PID=%d \n",SIGINT, getpid());

    char *buf = NULL;
    asprintf(&buf, "%d", getpid());
    write(fd, buf, strlen(buf));
    free(buf);
    uloop_run(); //entering main loop

    ubus_exit();
    uloop_done();

    xml_exit();
    config_exit();

    free(tmp);

    closelog();

    log_message(NAME, L_NOTICE, "exiting\n");

    return 0;
}

My purpose is to catch Ctrl + C but it seem the signal handler function i.e sig_handler() doesn't run.

I want to know how to fix it?

EsmaeelE
  • 2,331
  • 6
  • 22
  • 31
Anis_Stack
  • 3,306
  • 4
  • 30
  • 53

2 Answers2

7

As iharob answered, you should add the handler for the signal.

However, you should carefully read signal(7) and notice that it is not legal to call printf from inside a signal handler (since printf is not an async-signal-safe function). You should use write(2) instead of printf(3).

This restriction is significant and important. Don't forget that e.g. both printf and malloc could be interrupted at arbitrary moments, but they are not designed for that.

At the very least, call fflush(3) and/or end your printf format string with a \n; but that would still be undefined behavior (but you might be "unlucky" to have it do what you want most of the time).

BTW, it is recommended today to use sigaction(2) instead of the "obsolete" signal(2)

In practice, the recommended practice inside a signal handler would be most of the time to set some volatile sigatomic_t flag (to be tested outside the handler), or to call siglongjmp(3). If you insist on doing something else, be sure that you use (even indirectly) only async-signal-safe functions (and there are few of them, mostly the syscalls(2) ....). In particular, stdio(3) & malloc(3) should never be used from a signal handler (and that rules out most of the standard C functions, or most of library functions).

You may want to have some event loop around poll(2) (then you might be interested by the Linux specific signalfd(2)....); you should compile with all warnings and debug info (gcc -Wall -Wextra -g). Then use the gdb debugger (and also strace(1)) to debug your program.

Are you sure that the functions you are using (e.g. uloop_run, etc...) are not blocking or ignoring signals?. You should strace your program to find out!

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
4

You should add the handler to the signal with this function

sighandler_t signal(int signum, sighandler_t handler);

in your case

signal(SIGNINT, sig_handler);

One more thing, your main function must return int, so void main() is wrong, it should be int main().

The uloop_run function, from OpenWrt installs a signal handler for SIGINT so it's not possible to interrup it, and it overrides your signal handler.

That is the actual reason why your signal handler is never called.

The program wont handle the signal, until the uloop_run function exits, this is the uloop_run source with the relevant part

static void uloop_setup_signals(bool add)
{
    struct sigaction s;
    struct sigaction *act, *oldact;

    memset(&s, 0, sizeof(struct sigaction));

    if (add) {
        s.sa_handler = uloop_handle_sigint;
        s.sa_flags = 0;
        act = &s;
        oldact = &org_sighandler;
    } else {
        act = &org_sighandler;
        oldact = NULL;
    }

    sigaction(SIGINT, act, oldact);

    if (uloop_handle_sigchld) {
        if (add) {
            //act already points to s, so no need to update pointer
            s.sa_handler = uloop_sigchld;
            oldact = &org_sighandler_child;
        } else {
            act = &org_sighandler_child;
            oldact = NULL;
        }

        sigaction(SIGCHLD, act, oldact);
    }
}

void uloop_run(void)
{
    struct timeval tv;

    /*
    * Handlers are only updated for the first call to uloop_run() (and restored
    * when this call is done).
    */
    if (!uloop_recursive_count++)
        uloop_setup_signals(true);

    while(!uloop_cancelled)
    {
        uloop_gettime(&tv);
        uloop_gettime(&tv);
        uloop_run_events(uloop_get_next_timeout(&tv));
    }

    if (!--uloop_recursive_count)
        uloop_setup_signals(false);
}

as you can see, uloop_setup_signals(true); installs a new signal handler for SIGNINT and when the loop is finished uloop_setup_signals(false); is called restoring the previous signal handler.

So, this is the reason.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97