Reputation: 360
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
Reputation: 241731
Linux's man 2 signal
explains what happens when you set a signal handler with signal
. There are two possibilities:
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:
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
orSIG_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 ofsignal()
.
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
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
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