Search code examples
cexecsigaction

Restarting process when receiving a signal with sigaction


I'm trying to make my process restart when it receives SIGUSR1. Since SIGINT is easier to test, I'm using it instead.

Here's the code:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void sig_handler(int signo){
  if (signo == SIGINT){
    char *args[] = { "./a", NULL };
    write(1, "Restarting...\n", 14);
    execv(args[0], args);
  }
}

int main(void) {
  printf("Starting...\n");

  struct sigaction saStruct;
  sigemptyset(&saStruct.sa_mask);
  sigaddset(&saStruct.sa_mask, SIGINT);
  saStruct.sa_flags = SA_NODEFER;
  saStruct.sa_handler = sig_handler;
  sigaction(SIGINT, &saStruct, NULL);

  while (1)
    sleep(1);
}

Unfortunately, this only works for the first time the signal is received. After that, it does nothing. I thought that the SA_NODEFER flag should make this work the way I wanted to, but it doesn't.

Also, when I try with SIGUSR1, it simply terminates the process.


Solution

  • The problem is here:

    sigaddset(&saStruct.sa_mask, SIGINT);
    

    The way NODEFER affects signals is:

    1. If NODEFER is set, other signals in sa_mask are still blocked.

    2. If NODEFER is set and the signal is in sa_mask, then the signal is still blocked.

    On the other hand (from Signals don't re-enable properly across execv()):

    When using signal() to register a signal handler, that signal number is blocked until the signal handler returns - in effect the kernel / libc blocks that signal number when the signal handler is invoked, and unblocks it after the signal handler returns. As you never return from the signal handler (instead you execl a new binary), SIGUSR1 stays blocked and so isn't caught the 2nd time.

    Just remove the line:

    sigaddset(&saStruct.sa_mask, SIGINT);
    

    and you are done.

    #define _XOPEN_SOURCE 700
    
    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    void sighandler(int signo)
    {
        if (signo == SIGUSR1)
        {
            char *args[] = {"./demo", NULL};
            char str[] = "Restarting...\n";
    
            write(1, str, sizeof(str) - 1);
            execv(args[0], args);
        }
    }
    
    int main(void)
    {
        printf("Starting...\n");
    
        struct sigaction act;
    
        act.sa_handler = sighandler;
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_NODEFER;
        sigaction(SIGUSR1, &act, 0);
    
        while (1)
        {
            sleep(1);
        }
    }