Can't catch signals after exec - c

I trying to make program reload itself after it receives signal. I have this code
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void signal_callback_handler(int signum){
printf("Caught signal %d\n",signum);
execv("./test", NULL); //reexec myself
}
int main()
{
signal(SIGINT, signal_callback_handler);
printf("Program STARTED\n");
while(1){
printf("Program processing stuff here.\n");
sleep(1);
}
return EXIT_SUCCESS;
}
The problem is that after exec program just ignore signal instead of calling signal handler.
Output:
Program STARTED
Program processing stuff here.
Program processing stuff here.
^CCaught signal 2
Program STARTED
Program processing stuff here.
^C^CProgram processing stuff here.
^C^C^C^C^CProgram processing stuff here.
^C^CProgram processing stuff here.
How to make signal handlers work after exec?

Signal masks are inherited across exec, and SIGINT is blocked during the invocation of your signal handler, which calls execve. Thus your re-exec'd image is started with SIGINT blocked.
If you strace the process, you'll see that your signal call becomes something like:
3143 rt_sigaction(SIGINT, {0xabcd, [INT], SA_RESTORER|SA_RESTART, 0xabcd}, {SIG_DFL, [], 0}, 8) = 0
^^^^^
|
+--- SIGINT is blocked during handler!
sigaction will give you finer control over the signal handler, and is recommended over signal.

Yor signal is blocked during execution of the signal handler, and the signal mask is inherited over exec. You need to reset it explicitly.
A potential problem here is that if you unblock inside the handler, and you have another signal pending, it will be delivered immediately, causing the handler to be executed again. This should be rare but it can happen.
Worse yet, if you unblock in the re-exec'd process and you have a signal pending, you may have it delivered to the re-exec'd process, potentially killing it. So set up a handler in the "child" first, then unblock.

Related

master error when multiple signal are sent

I got this issue:
I made a program in c, where the main process creates some child process, and these, after a while, are able to send a signal to the main process:
the signal is sent with this code:
kill(getppid(), SIGUSR1);
and the main process, in the while loop is waiting the SIGUSR1 message...
everything is fine, but if I increase the child number and automatically the possibility to have more signals in the same time, the program crash printing the message:
User defined signal 1
the main code is like this:
void signalHandler(int sig, siginfo_t* info, void* vp) {
if (sig == SIGUSR1) {
printf("SIGUSR1 has arrived\n");
} else if (sig == SIGUSR2) {
printf("SIGUSR2 has arrived\n");
}
}
int main(int argc, char const *argv[]) {
struct sigaction action, old_action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_sigaction = signalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART | SA_NODEFER;
while (1) {
sigaction(SIGUSR1, &action, &old_action);
sigaction(SIGUSR2, &action, &old_action);
}
}
I think the problem is that the signal is sent when the master is still working on the previous signal...but how can I do to fix this thing
thank you very much
It means that the child is sending the signal before the parent process was able to call sigaction() to configure the signal handler. When this happens, the default signal reaction to SIGUSR1 terminates the program:
SIGUSR1 P1990 Term User-defined signal 1
https://man7.org/linux/man-pages/man7/signal.7.html
However, there are many problems with your code. printf() is not safe to be called inside a signal handler (it's AS-Unsafe as defined by POSIX):
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_03
Also, using SA_NODEFER may create nested signals (another signal handler is called while some signal handler is running) but your program does not protect against a flood. Given enough children this will generate a stack overflow. Finally, the main program keeps running a non-stop infinite loop reconfiguring the signals, while it should have configured them only once outside the loop and blocked inside the loop (for example sigwait() or pselect()):
https://man7.org/linux/man-pages/man2/select.2.html
Finally, if you expect to run a large number of children that might flood the parent with signals, then it would be better to use the real time signal generation function (sigqueue()) rather than kill(). The difference is that with sigqueue(), all signals are queued and SA_NODEFER is not necessary to avoid discarding signals while some other signal handler is running:
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_02
Final conclusion: the code should be completely rewritten.

Sending Signals in C?

I read:
There are two signals that a process can’t ignore – SIGKILL =
terminate the receiving process – SIGSTOP = suspend the receiving
process
And some even claimed there is no way we can declare handlers for them
But In C I can write:
#include <signal.h>
#include <stdio.h>
void sigint_handler(int signum) {
printf("I'm ignoring you!\n");
}
int main() {
signal(SIGKILL,sigint_handler);
for(;;) { /*endless loop*/ } return 0;
}
Isn't this a contradiciton?
Side Question, When I write kill 123 in the terminal what signal will be sent I can't find this information anywhere in the internet?
Per POSIX-1.2017 General Information §2.4.3 Signal Actions
, a signal may have one of three different dispositions or "actions taken" when it is delivered to a process:
SIG_DFL: take the default or "normal" action.
SIG_IGN: ignore the signal (take no action).
user-defined: the signal is "caught" by a user-defined signal handler.
That said, the same section of POSIX also clarifies:
The system shall not allow the action for the signals SIGKILL or SIGSTOP to be set to SIG_IGN.
....
The system shall not allow a process to catch the signals SIGKILL and SIGSTOP.
If you checked the return code and errno of your signal() call, you'd almost certainly see it failing with EINVAL.

signal handling in linux for c

I am trying to understand how signals work in Linux from the sample program that I found online, but it has some parts which I don't really understand.
This is my sample program:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
void catcher(int sig) {
printf("catcher() has gained control\n");
}
int main(int argc, char *argv[]) {
struct sigaction sigact;
sigset_t sigset;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
sigaction(SIGUSR1, &sigact, NULL);
printf("before first kill()\n");
kill(getpid(), SIGUSR1);
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_SETMASK, &sigset, NULL);
printf("before second kill()\n");
kill(getpid(), SIGUSR1);
printf("after second kill()\n");
return 0;
}
Here is the sample output from my program:
before first kill()
catcher() has gained control
before second kill()
after second kill()
Can I know why the first line in the output is before first kill()? Why doesn't catcher() has gained control appear first?
From what I know, sa_handler consists of two types of signal, signal default and signal ignore.
How do we know which signal it will generate? Why would it trigger the function to print the catcher() has gained control if the signal ignore being generate?
Besides, what is the sa_mask function in this program? In my understanding, sa_mask will block the specified signal.
Can I know why the first line in the output is before first kill()?
Why doesn't catcher() has gained control appear first?
You installed a signal handler that catches SIGUSR1. Until SIGUSR1 is delivered to the process, normal program execution flow keeps happening. So, here:
printf("before first kill()\n");
kill(getpid(), SIGUSR1);
You only generate the signal after printing before first kill(). Why don't you expect this to appear before catcher() has gained control? In other words, when you call printf("before first kill()\n");, no signals have been raised yet, so you can only expect program execution to remain normal.
This line:
kill(getpid(), SIGUSR1);
Generates SIGUSR1. The operating system delivers the signal to the process at a convenient time. Because you installed a handler for SIGUSR1, your signal handler (catcher()) is invoked. You raise the signal after printing the first line, so it is expectable that the next line of output will come from the signal handler.
Note that printf(3) is not async-signal safe, so technically you can't call it from inside a signal handler, but it is usually ok for these toy examples.
From what I know, sa_handler consists of two types of signal, signal
default and signal ignore.
There's more to it than that. The sa_handler field of struct sigaction can have the values SIG_DFL, which corresponds to the default signal action (the default action is listed in man signal), and SIG_IGN, which means the signal is ignored (nothing happens when it is raised). But sa_handler can also be a pointer to a function that you want to be invoked every time the signal is delivered. This is what the code you showed is doing - it is saying: Hey, when SIGUSR1 is delivered, please call catcher().
How do we know which signal it will generate? Why would it trigger the
function to print the catcher() has gained control if the signal
ignore being generate?
You indicated a signal (SIGUSR1) when you called sigaction(2) to setup the handler. So, catcher() will be called when SIGUSR1 is delivered.
Besides, what is the sa_mask function in this program? In my
understanding, sa_mask will block the specified signal.
It's a signal mask that is atomically installed when the signal handler is entered, and uninstalled when the signal handler returns. By default, even if you pass it an empty mask, the signal being caught is always blocked upon entering the handler (unless the SA_NODEFER flag is set in the sa_flags field of struct sigaction). However, you might want to block other signals while the handler is executing - the way you do that is by indicating these signals in sa_mask.

Custom SIGINT signal handler - Program still terminates even after signal is caught

I am playing with the signal.h and unistd.h libraries, and I am having some issues. In the code below, when I send the SIGINT signal to my running program by calling CTRL-C, the signal is caught. However, when pressing CTRL-C again, the program terminates. As I understand it, the print statement "Received signal 2" should be printed every time I press CTRL-C.
Is my understanding of this signal incorrect, or is there a bug in my code?
Thanks for your input!
#include "handle_signals.h"
void sig_handler(int signum)
{
printf("\nReceived signal %d\n", signum);
}
int main()
{
signal(SIGINT, sig_handler);
while(1)
{
sleep(1);
}
return 0;
}
Terminal output:
xxx#ubuntu:~/Dropbox/xxx/handle_signals$ ./handle_signals
^C
Received signal 2
^C
xxx#ubuntu:~/Dropbox/xxx/handle_signals$
Edit: Here is the header I've included
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum);
Thanks for your responses. Reading through them now!
Don't use signal, use sigaction:
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.
http://man7.org/linux/man-pages/man2/signal.2.html
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal.
Linux implements the same semantics: the handler is reset when the signal is delivered.
The behaviour of signal upon receiving the first signal varies on different implementation. Typically, it requires reinstalling the handler after receiving the signal as handler is reset to its default action:
void sig_handler(int signum)
{
signal(SIGINT, sig_handler);
printf("\nReceived signal %d\n", signum);
}
which is one of the reasons you shouldn't use signal anymore and use sigaction. You can see a bare bone example of using sigaction here.

Signal handling in C - interrupt in interrupt

I was wondering if it is possible to be interrupted by a signal when my program is handling other signal at the same time, I tried to simulate it with:
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
void sig_output()
{
sigset_t set;
sigprocmask(0,NULL,&set);
printf("currently blocking:");
if (sigismember(&set,SIGUSR1))
printf("\nSIGUSR1");
if(sigismember(&set,SIGUSR2))
printf("\nSIGUSR2");
printf("\n");
return ;
}
void sig_handler(int sig)
{
raise(SIGUSR1);
printf("start\n");
if (sig==SIGUSR1)
printf("SIGUSR1\n");
else if (sig==SIGUSR2)
printf("SIGUSR2\n");
printf("end\n");
return ;
}
void other_sig_handler(int sig)
{
printf("start - other\n");
if (sig==SIGUSR1)
printf("SIGUSR1\n");
else if (sig==SIGUSR2)
printf("SIGUSR2\n");
printf("end - other\n");
return ;
}
int main()
{
sig_output();
struct sigaction a;
a.sa_handler=sig_handler;
a.sa_flags=0;
sigset_t set,old;
//blocking SIGUSR1,SIGUSR2
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
sigaddset(&set,SIGUSR2);
printf("blocking SIGUSR1, SIGUSR2\n");
sigprocmask(SIG_SETMASK,&set,&old);
sig_output();
//adding handles for SIGUSR1,SIGUSR2
sigemptyset(&(a.sa_mask));
sigaction(SIGUSR1,&a,NULL);
a.sa_handler=other_sig_handler;
sigaction(SIGUSR2,&a,NULL);
printf("poczatek wysylania \n");
raise(SIGUSR1);
raise(SIGUSR2);
raise(SIGUSR1);
printf("using sigsuspend\n");
sigsuspend(&old);
printf("end of program\n");
return 0;
}
and everytime I run this program I get
currently blocking:
blocking SIGUSR1, SIGUSR2
currently blocking:
SIGUSR1
SIGUSR2
raising
using sigsuspend
start - other
SIGUSR2
end - other
start
SIGUSR1
end
end of program
is it always like that?
Quoting the sigaction(2) manpage:
Signal routines normally execute with the signal that caused their
invocation blocked, but other signals may yet occur. A global signal mask
defines the set of signals currently blocked from delivery to a process.
The signal mask for a process is initialized from that of its parent
(normally empty). It may be changed with a sigprocmask(2) call, or when
a signal is delivered to the process.
You can control whether the signal is automatically blocked in its signal handler with the SA_NODEFER flag.
The order in which these particular pending signals are delivered is not, as far as I know, defined. However, signals are (mostly; there's an exception for SIGCLD, which is traditionally done by "cheating") "non-queueing", except for real-time signals. The non-queuing aspect means that if you have signal X blocked, and then raise it twice (as you do above for SIGUSR1), you only get it delivered once.
The only ordering documented on at least one system (MacOS) is:
If multiple signals are ready to be delivered at the same time, any signals that
could be caused by traps are delivered first.
(These are things like SIGSEGV and SIGBUS.) In general, you can control the order of delivery by use of the signal blocking masks: unblock any particular signal(s) at some point and those are the ones that can be delivered at that point.
If you do not set SA_NODEFER, the blocking mask at the entry to your handler will always block whatever signal your handler is handling, so that you won't have to worry about recursion.
The special case for SIGCLD comes from System V, which originally implemented this by resetting the handler to SIG_DFL on each SIGCLD delivery. (In fact, SysV did this with all signals, effectively implementing SA_RESETHAND whether you wanted it or not.) The default action was to discard the signal, as if the handler were SIG_IGN. This of course created race conditions when multiple child processes finished before the handler could do its thing. Instead of a block/unblock model, though, the SysV folks put in a hack: at the end of your SIGCLD handler, you would call signal(SIGCLD, handler); to fix up the handler. At that point, if there were any exited children that had not yet been wait-ed for, SysV would immediately generate a new SIGCLD, and your handler would be entered recursively. This made it look as though the signals were queued, without actually queueing them.
For more on Linux signals, see (eg) http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html.

Resources