TAAPSogeking
TAAPSogeking

Reputation: 360

How to repeatedly restart a program via signal

I wanted a convenient way to restart a program. I thought I could just catch a signal (USR1 in the example) and call exec.

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

char* const* args;
void restart() {
    printf("restarting\n");
    execv(args[0], args);
}

int main(int argc, char* const argv[]) {
    printf("Starting\n");
    args = argv;
    signal(SIGUSR1, restart);
    raise(SIGUSR1); // could use pkill -SIGUSR1 file-name instead
    pause();
    printf("Terminated normally\n");
    return 0;
}

The above example kinda works. The output is

Starting
restarting
Starting

and then it hangs. Other signals can still be received.

I assume I'm just failing to clear the signal. The expected behavior is for program to keep restarting indefinitely.

Upvotes: 3

Views: 1336

Answers (3)

rici
rici

Reputation: 241731

Linux's man 2 signal explains what happens when you set a signal handler with signal. There are two possibilities:

  1. The disposition of the signal is reset to SIG_DFL, and then the handler is called. To handle this signal again, you need to re-establish the signal handler. This would have worked as you expected.

Or:

  1. The signal is blocked, and then the handler is called. When the handler returns, the signal is unblocked. If the handler doesn't return, the signal is still blocked, which is what was happening to you.

So one works with your code and the other doesn't. But which of the two applies? There is no reliable way of knowing. Posix allows both possibilities and both possibilities exist on different platforms. For this reason, the Linux manpage recommends:

The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN... [D]o not use it for [the purpose of establishing a signal handler].

POSIX.1 solved the portability mess by specifying sigaction(2), whichprovides explicit control of the semantics when a signal handler is invoked; use that interface instead of signal().

That's good advice. When you change to using sigaction, you'll probably want to go for the first option above, which requires:

sa.sa_flags = SA_RESETHAND | SA_NODEFER

That also comes from the Linux manpage, which is well worth reading in full. (Of course, the sigaction manpage is even more relevant.)

Upvotes: 1

cup
cup

Reputation: 8257

Not sure why this works - replace signal with sigset

First, define __USER_XOPEN_EXTENDED otherwise sigset is undefined

#define __USE_XOPEN_EXTENDED
#include <signal.h>

Change signal to sigset

...
sigset(SIGUSR1, restart);
...

It then keeps on restarting until it is killed. Perhaps someone could explain why sigset works and signal doesn't.

Upvotes: 1

pilcrow
pilcrow

Reputation: 58569

On your platform, SIGUSR1 is masked inside the signal handler, which is typical but not mandated behavior for signal (see, by contrast, SA_NODEFER and sigaction).

This mask is inherited across the execve, thus the SIGUSR1 is held pending in your second execution and never delivered.

Try something like this at the top of main(), and perhaps inside the handler, to see what's happening:

static int
is_blocked(int sig) {
  sigset_t ss;
  sigemptyset(&ss);
  (void)sigprocmask(SIG_BLOCK, NULL, &ss);
  return sigismember(&ss, sig);
}

Upvotes: 1

Related Questions