Subject says it all.
I want to be able to defer to the default OS signal handler for certain signals after I am done with my cleanups.
E.g. if SIGINT is received I want to terminate the program after my custom cleanup routine and want to make use of the standard OS routine for that instead of callind _exit() or whatever.
I figure I can get a reference to the original signal handler somehow before overwriting it with sigaction(), but i don't know how to do that.
This is the code I have at the moment:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
volatile sig_atomic_t sigterm_flag = 0;
volatile sig_atomic_t sighup_flag = 0;
volatile sig_atomic_t sig_default_flag = 0;
void sigact(int signum, siginfo_t *siginfo, void *ucontext)
{
switch(signum)
{
case SIGTERM:
sigterm_flag = 1;
break;
case SIGHUP:
sighup_flag = 1;
break;
default:
sig_default_flag = 1;
break;
}
}
int main(void)
{
struct sigaction sa;
sa.sa_sigaction = sigact;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
while (1) {
if (sigterm_flag == 1) {
sigterm_flag = 0;
printf("SIGTERM caught.\n");
}
if (sighup_flag == 1) {
sighup_flag = 0;
printf("SIGHUP caught.\n");
}
if (sig_default_flag == 1) {
sig_default_flag = 0;
printf("Other signal caught.\n");
}
sleep(1);
}
return 0;
}
EDIT:
Challenge solved based on suggestion from tuxlike in answer https://stackoverflow.com/a/64595945/8359654
Here is the final code:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
volatile sig_atomic_t sigterm_flag = 0;
volatile sig_atomic_t sighup_flag = 0;
volatile sig_atomic_t sigint_flag = 0;
volatile sig_atomic_t sig_default_flag = 0;
void sigact(int signum, siginfo_t *siginfo, void *ucontext)
{
switch(signum)
{
case SIGTERM:
sigterm_flag = 1;
break;
case SIGHUP:
sighup_flag = 1;
break;
case SIGINT:
sigint_flag = 1;
break;
default:
sig_default_flag = 1;
break;
}
}
int main(int argc, char *argv[])
{
struct sigaction sa;
struct sigaction sa_default;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = sigact;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa_default.sa_mask);
sa_default.sa_handler = SIG_DFL;
sa_default.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
printf("Started %s as PID %ld\n", argv[0], (long int)getpid());
while (1) {
if (sigterm_flag == 1) {
sigterm_flag = 0;
printf("SIGTERM caught.\n");
}
if (sighup_flag == 1) {
sighup_flag = 0;
printf("SIGHUP caught.\n");
}
if (sigint_flag == 1) {
sigint_flag = 0;
printf("\nSIGINT caught.\n");
sigaction(SIGINT, &sa_default, NULL);
raise(SIGINT);
}
if (sig_default_flag == 1) {
sig_default_flag = 0;
printf("Other signal caught.\n");
}
sleep(1);
}
return 0;
}
(Note that you are missing sigemptyset(&sa.sa_mask);
from your signal action structure initialization. It is a common, recommended practice to initially do a memset(&sa, 0, sizeof sa);
to initialize all, even padding, to all-zeros, first.)
There is no userspace function for the default signal handler actions at all, the default handling happens in the kernel.
What you can do, is use sigaction()
to reset the default action (sa.sa_handler = SIG_DFL; sa.sa_flags = 0;
), then re-raise the signal using raise(signum)
, and return from the handler.
Both sigaction()
and raise()
are async-signal safe, and therefore perfectly acceptable to use in a signal handler.