I catch problems with this code:
void handler (int sig) {
// write error information with a stack trace to disk
}
void set_crash_signals () {
signal(SIGSEGV, handler);
signal(SIGABRT, handler);
signal(SIGFPE, handler);
}
Is there a way to get the assert message in the SIGABRT handler?
Is there some other trick I could use?
Related
I am learning concepts of signals in the C language and met a problem when building a program for practices.
In the codes below, I am trying to reset SIGINT each time after the user press "ctrl-c" and to record how many times the user press "ctrl-c".
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
void handler(int signo);
jmp_buf buf;
int int_counting = 1;
void handler(int signo)
{
signal(SIGINT, SIG_DFL);
int_counting++;
// just tried to add another "signal(SIGINT, handler)" here
// but saw the same result
longjmp(buf, 1);
}
int main()
{
if ((signal(SIGINT, handler) == SIG_ERR))
{
printf("Fail to catch the signal\n");
}
if (!setjmp(buf))
{
printf("Waiting for any signals ... \n");
}
else
{
if (!setjmp(buf)){} // to reset "setjmp" to zero
printf("Pressed 'ctrl-c' for %d times\n", int_counting);
printf("Waiting for another signal\n");
signal(SIGINT, handler);
}
while (int_counting <= 5)
{
sleep(1);
printf("Processing ...\n");
}
}
However, after the first signal no other signals can be sent to handler and the output looks like:
Could you anyone explains the reason?
Below are examples where it seems like the signal will not be masked.
// Examples for SIGALRM
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
int counting = 0;
void handler(int signo)
{
printf("%d\n", counting);
while (counting < 5)
{
signal(SIGALRM, handler);
printf("%d\n", beeps);
counting++
alarm(1);
}
}
void main(void)
{
if (signal(SIGALRM, handler) == SIG_ERR)
{
printf("cannot catch SIGALRM\n");
}
alarm(1);
while (counting < 5)
{
pause();
}
return;
}
// Example for SIGQUIT
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
jump_buf buf;
void handler(int signo)
{
signal(SIQQUIT, handler);
longjmp(buf, 1);
}
int main()
{
signal(SIQQUIT, handler);
if (!setjmp(buf))
{
printf("begin ...\n");
}
else
{
print("restart ...\n");
}
while (1)
{
sleep(1);
printf("waiting for sinals ...\n");
}
}
Although my original question is answered but if any further explanation about
why those signals will not be masked (or please tell me that is how they work in C), it would be greatly helpful.
You need to save the signal mask and therefore use siglongjmp() and sigsetjmp().
This works as expected:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
void handler(int signo);
sigjmp_buf buf;
int int_counting = 0;
void handler(int signo)
{
signal(SIGINT, SIG_DFL);
int_counting++;
// just tried to add another "signal(SIGINT, handler)" here
// but saw the same result
siglongjmp(buf, 1); // 1: "fake" return value
}
int main()
{
if ((signal(SIGINT, handler) == SIG_ERR))
{
printf("Fail to catch the signal\n");
}
if (!sigsetjmp(buf, 1)) // 1 (or any non-zero value): save sigmask
{
printf("Waiting for any signals ... \n");
}
else // this code is executed when the "fake" return value of sigsetjmp is non-zero
{
printf("Pressed 'ctrl-c' for %d times\n", int_counting);
printf("Waiting for another signal\n");
signal(SIGINT, handler);
}
while (int_counting <= 5)
{
sleep(1);
printf("Processing ...\n");
}
}
It is described in the man-page e.g. man setjmp:
sigsetjmp() and siglongjmp() also perform nonlocal gotos, but provide predictable handling of the process signal mask
SIGINT is masked (blocked) during execution of your signal handler, and remains masked when you longjmp out of it. That's why you don't see subsequent SIGINTs — the signal mask prevents their delivery.
The fix is three-fold.
First, use sigsetjmp(buf, 1) to save the calling mask and siglongjmp make the jump. That will restore the signal mask to its expected value at that point in execution.
Second, use sigaction rather than signal. sigaction will force you to explicitly choose behavior like masking out signals during handler execution. signal, on the other hand, does not mandate consistent behavior across platforms.
Third, don't use (sig)longjmp at all. You got into this trouble because you are issuing a non-local goto from within asynchronously executed user code (your handler). It's very easy to make mistakes when reasoning about that kind of code.
In my application I want to implement backtrace on segmentation fault according to this post:
How to generate a stacktrace when my gcc C++ app crashes
But I encountered a problem. My application uses DirectFB for graphics. After I initialize DirectFB by calling DirectFBCreate, the signal handler stops to be called.No matter where the signal handler is registered. Please compare main1, main2 and main3 functions in code bellow:
#include <stdio.h>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <directfb.h>
void handler(int sig) {
void *array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
// print out all the frames to stderr
fprintf(stderr, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1);
}
void baz() {
int *foo = (int*)-1; // make a bad pointer
printf("%d\n", *foo); // causes segfault
}
void bar() { baz(); }
void foo() { bar(); }
int main1(int argc, char **argv) {
signal(SIGSEGV, handler); // install our handler
// if the foo() function is called here,
// everything works as it should
foo();
IDirectFB *dfb = NULL;
DFBCHECK (DirectFBInit (&argc, &argv));
DFBCHECK (DirectFBCreate (&dfb));
}
int main2(int argc, char **argv) {
signal(SIGSEGV, handler); // install our handler
IDirectFB *dfb = NULL;
DFBCHECK (DirectFBInit (&argc, &argv));
DFBCHECK (DirectFBCreate (&dfb));
// but calling the foo() function after DirectFBCreate causes
// that the handler is not called
foo();
}
int main2(int argc, char **argv) {
IDirectFB *dfb = NULL;
DFBCHECK (DirectFBInit (&argc, &argv));
DFBCHECK (DirectFBCreate (&dfb));
signal(SIGSEGV, handler); // install our handler
// calling the foo() function after DirectFBCreate causes,
// that the handler is not called
// no matter the signal handler is registered after DirectFBCreate calling
foo();
}
I have also tried sigaction function instead of signal function, with the same result.
I have also tried using sigprocmask(SIG_SETMASK, &mask, NULL) to unblock the signal. But this also didn't help (which I expected).
Finally I found this post signal handler not working,
which seems to solve similar problem by disabling the library's signal handler by calling zsys_handler_set(NULL);. So I tried signal(SIGSEGV, NULL); and signal(SIGSEGV, SIG_DFL);. Again not succeeded. I didn't find any handler disabling function in DirectFB. Although I found [no-]sighandler argument in DirectFB config and used it, this didn't help neighter (which surprised me a lot).
My question is: If the DirectFB is able to steel my handler, how can I take it back?
I used strace as mentioned in the comment. I found out that the DirectFB doesn't call sigaction syscall but it blocks some signals, SIGSEGV included. Unblock the signal after DirectFB initialization was the soulution.
// DirectFb initialization
IDirectFB *dfb = NULL;
DFBCHECK (DirectFBInit (&argc, &argv));
DFBCHECK (DirectFBCreate (&dfb));
// Unblock the signal
sigset_t sa_mask;
sigemptyset(&sa_mask);
sigaddset(&sa_mask, SIGSEGV);
sigprocmask(SIG_UNBLOCK, &sa_mask, NULL);
// here is important to use SIG_UNBLOCK flag
// not SIG_SETMASK as I did in my question!!!
// Now this causes the handler call
foo();
1.I have written a piece of sample code and that will catch SIGALRM signal exit the main process.
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/signal.h>
static int sig_flag=0;
static void mysignal(int sig)
{
sig_flag=1;
}
void installsignal(int sig,void( *signalhandler)(int))
{
struct sigaction action={0};
action.sa_handler=signalhandler;
action.sa_flags=0;
sigemptyset(&action.sa_mask);
if(sigaction(sig,&action,NULL)<0)
{
printf("can not catch signal signum:%d\n",sig);
}
}
main()
{
installsignal(SIGALRM,mysignal);
if(sig_flag==1)
{
printf("\n Signal has been caought\n");
exit(0);
}
while(1)
{
printf("\nHello world\n");
sleep(1);
}
exit(0);
}
2.When i have changed my program as given below then its works:-
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/signal.h>
static int sig_flag=0;
static void mysignal(int sig)
{
sig_flag=1;
}
void installsignal(int sig,void( *signalhandler)(int))
{
struct sigaction action={0};
action.sa_handler=signalhandler;
action.sa_flags=0;
sigemptyset(&action.sa_mask);
if(sigaction(sig,&action,NULL)<0)
{
printf("can not catch signal signum:%d\n",sig);
}
}
main()
{
installsignal(SIGALRM,mysignal);
while(1)
{
printf("\nHello world\n");
if(sig_flag==1)
{
printf("\n Signal has been caought\n");
exit(0);
}
sleep(1);
}
exit(0);
}
Why first one sample code is not working
You got the control loop wrong. You never ask for the flag inside, so how would you want that you notice that the signal has been caught.
Besides that Basile is correct, only use sig_atomic_t for communication with a signal handler.
You should define
static volatile sig_atomic_t sig_flag=0;
Read about volatile variables and sig_atomic_t and read carefully signal(7)
Of course you need to test sig_flag inside your loop or use pause(2) or sigsuspend(2). Maybe you want an event loop using poll(2). Read Advanced Linux Programming, time(7), and about the Linux specific signalfd(2) and timerfd_create(2)....
I have two shared libraries linked to my test application. Both of the libraries have signal handlers for SIGINT.
Is it valid to have multiple signal handlers for same signal? Which order the handlers will execute when I generate a SIGINT signal?
As said by others, only one signal handler can be set, which is the last one. You would then have to manage calling two functions yourself. The sigaction function can return the previously installed signal handler which you can call yourself.
Something like this (untested code):
/* other signal handlers */
static void (*lib1_sighandler)(int) = NULL;
static void (*lib2_sighandler)(int) = NULL;
static void aggregate_handler(int signum)
{
/* your own cleanup */
if (lib1_sighandler)
lib1_sighandler(signum);
if (lib2_sighandler)
lib2_sighandler(signum);
}
... (later in main)
struct sigaction sa;
struct sigaction old;
lib1_init(...);
/* retrieve lib1's sig handler */
sigaction(SIGINT, NULL, &old);
lib1_sighandler = old.sa_handler;
lib2_init(...);
/* retrieve lib2's sig handler */
sigaction(SIGINT, NULL, &old);
lib2_sighandler = old.sa_handler;
/* set our own sig handler */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = aggregate_handler;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
Only one signal handler can be installed per signal. Only the latest installed handler will be active.
we can handle multiple signal with single signal handler but
but its is not possible to have multiple signal handler for same signal.
void sig_handler(int signo)
{
if (signo == SIGINT)
printf("received SIGINT 1\n");
}
void sig(int signo)
{
if (signo == SIGINT)
printf("received SIGINT 2\n");
}
int main(void)
{
if(signal(SIGINT, sig_handler) == SIG_ERR)
printf("\ncan't catch SIGINT\n");
if (signal(SIGINT, sig) == SIG_ERR)
printf("\ncan't catch SIGINT\n");
// A long long wait so that we can easily issue a signal to this process
while(1)
sleep(1);
return 0;
}
if u try to run this code u will find that the last assigned signal handler is set for that signal.
i think it is not possible to have a multiple signal handler for same signal.
As you could see in the man page for sigaction, the new signal handler replaces the old one and the old one is returned.
If you have two unused signals (say SIGUSR1 and SIGUSR2), assign those signals the two signal handlers for SIGINT. Then you may write your own Signal Handler for SIGINT and from that, you may raise the needed unused signal as per you want.
Shabaz hit the nail on the head. However, if you're looking for something all your libraries could use (provided you have access to the source code), you could do something along the following lines:
linked_list* sigint_handlers = NULL;
void sighand_init(sighand_config_t* config) {
struct sigaction action;
memset(&signalaction, 0, sizeof(signalaction));
action.sa_handler = &sighand_main;
// Order is important, in case we get a signal during start-up
sigint_handlers = linked_list_new();
sigaction(SIGINT, &action);
}
void sighand_main(int signum) {
if (signum == SIGINT) {
linked_list_node* node = linked_list_head(sigint_handlers);
while ((node = node->next) != NULL) {
node->object(signum);
}
if (sighand_config.exitonint) {
app_exit(0);
}
}
}
void sighand_add_int_handler(void (*handler)(int)) {
if (handler == NULL) return;
linked_list_add(sigint_handlers, handler);
}
void sighand_destroy() {
...
linked_list_destroy(signint_handlers);
...
}
Or, you could use this yourself, and after loading each library, get the handler and later call add_handler. Something along the lines of:
loadlibrary(lib1.so);
sigaction1 = signalget(SIGINT);
loadlibrary(lib2.so);
sigaction2 = signalget(SIGINT);
sighand_init(...);
sighand_add_int_handler(sigaction1.sa_handler);
sighand_add_int_handler(sigaction2.sa_handler);
Just some thoughts,
Anthony
So I have the following code segment as part of my program. Handler is the handler for the SIGALRM.
static void handler(void)
{
//DISABLE SIGALRM
sigset_t oldset;
disable_sigalrm(&oldset);
if(queue_length(&queue) == 0)
{
ucontext_t context;
getcontext(&context);
swapcontext(&context,&uctxt_main);
}
else
{
//...
//enable sigalarm
}
}
The program disable_sigalrm looks as follows:
void disable_sigalrm(sigset_t* oldset)
{
sigset_t sset;
//Empty sset
if(sigemptyset(&sset) == -1)
{
printf("Error in emptying the signal set.\n");
}
//Add SIGALRM to sset
if(sigaddset(&sset,SIGALRM) == -1)
{
printf("Error in adding SIGALRM to the specified signal set.\n");
}
//Block any occurence of SIGALRM
if(sigprocmask(SIG_BLOCK,&sset,oldset) == -1)
{
printf("Error in adding the specified signal; set to the signal mask.\n ");
}
}
Now when I run in the debugger even after the queue length reaches zero it keeps coming back to handler.
Since the SIGALRM is disabled, the only explanation that makes sense is if there are multiple signals waiting to reach the handler. Is it possible that multiple signals can wait to run to the handler? If so how would you disable them running the handler?