I'm supposed to write a C program which handles the first SIGINT with a custom handler, and then reset the default behaviour. My custom SIGINT handler should just print a msg. This is what I wrote:
#include <string.h>
#include <strdio.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
void handler(sig)
int sig;
{
printf("%d received\n",sig);
}
int main(){
signal(SIGINT, handler);
signal(SIGINT, SIG_DFL);
while(1);
exit(0);
}
If I launch it from a shell and then I enter Ctrl+C on the same tab, it works. If I try to send the SIGINT using kill -INT *process pid* it just terminates the program, no msg printed. Any idea why?
The signal function is not cumulative. You call it twice, so the last one is the good one, using the default behavior (SIG_DFL).
You have to just set your handler in main and in handler to set the new behavior (signal(SIGINT, SIG_DFL);) so that next signal will be default-treated (kill the process).
Note that signal may have different behavior on different unix systems, so you should have a look to sigaction which is the recommanded way to handle signals.
Possibly ecause signal(SIGINT, SIG_DFL); turns the handler off with respect to the kill. The man page has more info about if or when you need that line, and suggests using sigaction instead of signal for consistent behaviour across platforms:
struct sigaction sa;
sa.sa_handler = handler;
sigaction(SIGINT, &sa, NULL);
Related
Consider the simple example below which registers for a signal handler for SIGABRT, then calls abort(). When I run it, the program terminates before printing Done but after async-signal-safe printing in the trapped signal.
This implies that SIGABRT is not a blockable signal. This seems to be supported by this StackOverflow answer. However, I cannot find any corroborating evidence of that behavior in the signal man page, which clearly states that The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored but makes no similar mention for SIGABRT.
Can someone please enlighten me on this behavior?
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static struct sigaction old_sa;
static void my_handler(int signo)
{
const char x[] = "In Handler\n";
write(STDOUT_FILENO, x, strlen(x));
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = my_handler;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGABRT);
if (0 != sigaction(SIGABRT, &sa, &old_sa))
{
perror("sigaction");
exit(EXIT_FAILURE);
}
printf("Ready\n");
abort();
printf("Done\n");
exit(0);
}
Compiled with gcc ./try.c && ./a.out generates the following output
Ready
In Handler
Aborted
SIGABRT can be blocked. But the abort() function unblocks the signal before sending the signal.
This is specified in POSIX:
The abort() function shall override blocking or ignoring the SIGABRT signal.
You'll get the expected result if you use
kill(getpid(), SIGABRT);
instead of calling abort()
Your handler caught the signal. Then it returned. C 2018 7.22.4.1 2 says “The abort function causes abnormal program termination to occur, unless the signal SIGABRT is being caught and the signal handler does not return.” So, once your handler returns, the abort routine continues doing its thing, which is to terminate your program.
If instead of abort(); you use raise(SIGABRT); to raise the signal without calling the abort routine, then the signal handler will be called, will print, and will return, after which printf("Done\n"); will be executed.
static void AlarmHandler(int sig) ;
int i=0;
jmp_buf mark;
int main(int argc, char * argv[]){
setjmp(mark);
signal(SIGALRM, AlarmHandler);
alarm(2);
while(1);
return 0;
}
static void AlarmHandler(int sig) {
signal(SIGALRM, SIG_IGN);
printf("I am in AlarmHandler: %d \n",i);
i++;
longjmp(mark, 0);
}
When I run this code the program goes through the AlarmHandler only once and then it just stays trapped inside the while loop. Can someone explain why?
Your program might work as you expected on some POSIXy operating systems -- in fact, it does work as you expected on the computer I'm typing this on. However, it relies on a bunch of unspecified behavior relating to signals, and I think you've tripped over one of them: I think that on your computer, a signal is "blocked" — it can't be delivered again — while its handler is executing, and also, jumping out of the handler with longjmp does not unblock the signal. So you go around the loop once and then the second SIGALRM is never delivered because it's blocked. There are several other, related problems.
You can nail down all of the unspecified behavior and make the program reliable on all POSIXy operating systems, but you have to use different functions to set things up: sigsetjmp and sigaction. You should also get rid of the busy-waiting by using sigsuspend instead. A corrected program would look something like this:
#define _XOPEN_SOURCE 700
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <unistd.h>
static jmp_buf mark;
static void
handle_SIGALRM(int sig)
{
static int signal_count;
signal_count++;
printf("SIGALRM #%u\n", signal_count);
siglongjmp(mark, signal_count);
}
int
main(void)
{
sigset_t mask, omask;
sigemptyset(&mask);
sigaddset(&mask, SIGALRM);
if (sigprocmask(SIG_BLOCK, &mask, &omask)) {
perror("sigprocmask");
return 1;
}
struct sigaction sa;
sigfillset(&sa.sa_mask);
sa.sa_flags = 0; // DO interrupt blocking system calls
sa.sa_handler = handle_SIGALRM;
if (sigaction(SIGALRM, &sa, 0)) {
perror("sigaction");
return 1;
}
if (sigsetjmp(mark, 1) >= 4)
return 0;
alarm(1);
sigsuspend(&omask);
perror("shouldn't ever get here");
return 1;
}
I should probably say a few words about signal safety: In this program, it is safe to call printf and siglongjmp from the signal handler, because I have arranged for the SIGALRM only to be deliverable while the main thread of execution is blocked on sigsuspend. (That's what the call to sigprocmask up top does.) If you had anything to do in your main thread of execution besides sleep waiting for the signal to arrive, you would have to be much more careful about what you did in the signal handler, and I would advocate for using pselect and/or the self-pipe trick instead of jumping out of the handler, if at all possible.
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.
I'm trying to understand the following example code about signals:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
sig_atomic_t sigusr1_count = 0;
void handler(int signal_number) {
++sigusr1_count;
}
int main() {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &handler;
sigaction(SIGUSR1, &sa, NULL );
printf("SIGUSR1 was raised %d times\n", sigusr1_count);
return 0;
}
The output is:
SIGUSR1 was raised 0 times
Why was the signal not raised?
There are a couple of reasons. The first, is that nothing ever sends SIGUSR1 to your process. The other, more important one, is that you don't give any time for anyone to do so. Immediately after registering your signal handler, you print out how many times the signal was received in, most likely, less then a few milliseconds. You need to add something like sleep(10) before printing out the result.
Because there is nothing sending the SIGUSR1 signal to your application? Did you try running kill -USR1 pid to send the signal? Or kill(pid, SIGUSR1) from this or another application?
Where did you send the signal SIGUSR1? You should sens it after sigaction and before printf... Add a loop in your main function.
I was doing a little reading about sigaction() (sources are from my course notes) and I'm not sure I understand this text:
The signal mask is calculated and installed only for the duration of
the signal handler.
By default, the signal “sig” is also blocked when the signal occurs.
Once an action is installed for a specific signal using sigaction,
it remains installed until another action is explicitly requested.
Does this mean that the default signal mask is restored after returning form the signal handler?
Also, do I have to re-install the handler after using it, as if I was using signal()?
Also, there's this piece of code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void termination_handler(int signum) {
exit(7);
}
int main (void) {
struct sigaction new_action,old_action;
new_action.sa_handler = termination_handler;
sigemptyset(&new_action.sa_mask);
sigaddset(&new_action.sa_mask, SIGTERM);
new_action.sa_flags = 0;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT,&new_action,NULL);
}
sleep(10);
return 0;
}
So - how exactly will SIGTERM be handled? I can see that the installed handler is termination handler(), but then SIGTERM was added to the signal mask with no use of sigprocmask(). What does this mean? Thanks!
P.s. one last question: why the if statement in main()?
Let's try to understand what's happening with a modified version of your code :
#include <signal.h>
#include <stdio.h>
void termination_handler(int signum)
{
printf("Hello from handler\n");
sleep(1);
}
int main (void)
{
//Structs that will describe the old action and the new action
//associated to the SIGINT signal (Ctrl+c from keyboard).
struct sigaction new_action, old_action;
//Set the handler in the new_action struct
new_action.sa_handler = termination_handler;
//Set to empty the sa_mask. It means that no signal is blocked
// while the handler run.
sigemptyset(&new_action.sa_mask);
//Block the SEGTERM signal.
// It means that while the handler run, the SIGTERM signal is ignored
sigaddset(&new_action.sa_mask, SIGTERM);
//Remove any flag from sa_flag. See documentation for flags allowed
new_action.sa_flags = 0;
//Read the old signal associated to SIGINT (keyboard, see signal(7))
sigaction(SIGINT, NULL, &old_action);
//If the old handler wasn't SIG_IGN (it's a handler that just
// "ignore" the signal)
if (old_action.sa_handler != SIG_IGN)
{
//Replace the signal handler of SIGINT with the one described by new_action
sigaction(SIGINT,&new_action,NULL);
}
while(1)
{
printf("In the loop\n");
sleep(100);
}
return 0;
}
So, if you compile it and launch it, and press Ctrl+C, then you'll have the handler message executed, and then you get back immediately out of the main's sleep. You can do it as many time as you want, and the handler message and the inloop message are still displayed.
So, you give a function, and sigaction does everything needed to hook the signal with your handler.
Now, what about sigterm? If you increase the sleep time in termination_handler, you can type something like "pkill --signal SIGTERM ./a.out" after pressing Ctrl+C. Then, what happens? Nothing! The SIGTERM signal is blocked while termination_handler is running. But once you are back in the main, now the SIGTERM will kill the application.
(Remember, while you are testing this code, you can still kill applications by sending a SIGKILL signal.)
If you want to know more, and have more fun with signals, you have the signal manual and the sigaction manual which tell a lot more. Notice that you also have the detailed description of the sigaction structure.