I am trying to create a handler for the exit signal in c and my operating system is ubuntu.
I am using sigaction method to register my custom handler method.
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
Here's my code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void CustomHandler(int signo)
{
printf("Inside custom handler");
switch(signo)
{
case SIGFPE:
printf("ERROR: Illegal arithmatic operation.\n");
break;
}
exit(signo);
}
void newCustomHandler(int signo)
{
printf("Inside new custom handler");
switch(signo)
{
case SIGINT:
printf("ERROR: Illegal arithmatic operation.\n");
break;
}
exit(signo);
}
int main(void)
{
long value;
int i;
struct sigaction act = {CustomHandler};
struct sigaction newact = {newCustomHandler};
newact = act;
sigaction(SIGINT, &newact, NULL); //whats the difference between this
/*sigaction(SIGINT, &act, NULL); // and this?
sigaction(SIGINT, NULL, &newact);*/
for(i = 0; i < 5; i++)
{
printf("Value: ");
scanf("%ld", &value);
printf("Result = %ld\n", 2520 / value);
}
}
Now when I run the program and press Ctrl + c it displays Inside Inside custom handler.
I have read the documentation for sigaction and it says
If act is non-null, the new action for
signal signum is installed from act.
If oldact is non-null, the previous
action is saved in oldact.
why do I need to pass the second structure when I can directly assign the values like
newact = act
Thanks.
oldact is useful to reset the previous action handler:
sigaction(SIGINT, ©Interrupted, &previousHandler);
copy(something);
sigaction(SIGINT, &previousHandler, null);
This way, you can reset the previous signal handler even if you do not know what it was.
When you call sigaction, you replace the old handler with the new one; the oldact pointer is set to point to the handler information in effect before this replacement.
Perhaps, as is often the case, you want the new handlers to be in effect for only a limited region of the code: now that you know what the configuration was before you changed it, you can restore it by another call to sigaction, at the end of this region.
As Norman pointed out, you use oldact because
you want the new handlers to be in effect for only a limited region of the code
But I think one thing previous answers fail to explain very clearly (at least I think it's the only thing needs to be explained here) is oldact is what we read from sigaction.
As described in manual
If oldact is non-NULL, the previous action is saved
in oldact.
Alternatively, you can keep track of previous act, but if you think that's too much trouble, you can extract sigaction in this way.
One scenario one might find this useful, is when there is a default handler for one signal, and you want to use your customized handler for the first (or first few) time(s), and then the signal (if you receive it for many times), would be processed by the default handler, since it takes a lot of trouble to find or rebuild default handler you can put this at the buttom of your handler.
struct sigaction default_sa;
void your_handler(int signum) {
//code for your handler
//...
sigaction(signum,&default_sa,NULL);//use default handler
return ;
}
int main(){
//replace SIGNUM with real signal number
sigaction(SIGNUM,NULL,&default_sa);
struct sigaction sa;
sa.sa_handler = your_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGNUM, &sa, NULL);
//your rest of main function code
//...
}
Related
Consider the following code:
#include <signal.h>
#include <stdio.h>
void catch ()
{
printf("hi\n");
}
int main()
{
struct sigaction act;
act.sa_handler = catch;
sigaction(SIGINT, &act, NULL);
for(;;);
return 0;
}
When this program is run. The first time I press CTRL-C it prints "hi".
But the second time the program exits. what could be the reason for this?
What I wanted was that the program catch the signal each time it is raised.
If you don't use any SA_FLAG to explicitly define the behavior of "what to do after first catch of signal", it should be work.
Clear the contents of the sigaction, then initialize it.
memset(&act,0,sizeof(act)); // clear contents first
act.sa_handler = catch;
sigaction(SIGINT, &act, NULL);
See, sigaction(2).
In addition, do not use printf inside your signal handlers as Daniel pointed out. See signal-safety(7)
If you want to print something, or simply do something in your signal handler, you must use signal-safe functions. In your case, instead of using printf, you can use write() system call. See write(2).
By,
write(1,"hi\n",3); // 1 means standard out.
jmp_buf functjmp;
void sigsegv_handler(int sig) {
sio_printf("Caught sigsegv!\n");
siglongjmp(functjmp, 2);
return;
}
void foo(unsigned val) {
assert(0);
sio_printf("entered!\n");
}
int main() {
struct sigaction action;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
sigaddset(&action.sa_mask, SIGSEGV);
action.sa_flags = SA_RESTART; /* Restart syscalls if possible */
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
sigset_t prev_mask;
sigprocmask(SIG_BLOCK, NULL, &prev_mask);
if (sigsetjmp(functjmp, 0) == 0) {
foo(*(unsigned *)0x8);
} {
sigprocmask(SIG_BLOCK, &prev_mask, NULL);
sio_printf("jump handled!\n");
foo(*(unsigned *)0x8);
}
sio_fprintf(stderr, "how did it come here?!\n");
}
I've been debugging this code using gdb, and I cannot figure out why the program will not handle the second SIGSEGV signal with my own handler, assuming no other signals were received or sent by the program? Any sio prefixed functions are async safe variants of the stdio counterparts.
Currently, I surmise it has to do with something I'm missing in my conception about returning from the signal handler, which longjmp doesn't do at all.
Short answer: normally not possible to resume after SIGSEGV for C program. You might get more mileage with C++.
Long Answer: See discussions in Coming back to life after Segmentation Violation
Assuming OK to take the risk of undefined behavior:
It is possible to re-enable SEGV. The core issue is that during signal handler, the code explicitly blocks the SEGV signal from being triggered (with the sigaddset). In addition, the default behavior (of signal handlers) is that during signal handling, the same signal processing will be deferred until the signal handler returns. In the OP code, the signal handler never returns (because of the siglongjmp)
Both issues can be addressed by changing the original code.
// Make sure all attributes are NULL.
struct sigaction action = {} ;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
// Not Needed:: sigaddset(&action.sa_mask, SIGSEGV);
// Add SA_NODEFER to disable the deferred processing of SIGSEGV.
action.sa_flags = SA_RESTART | SA_NODEFER ; /* Restart syscalls if possible */
// rest of code here
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
...
I am trying to handle SIGSEGV signal from my code. I have written the following code in C under Fedora 15. My problem is that the signal handler is not getting called for the second segmentation fault. Can anybody kindly point out what i am doing wrong.
typedef struct _ST_DEMO
{
int m_iUnused;
_ST_DEMO()
{
m_iUnused = 0;
}
} ST_DEMO;
jmp_buf ex_buf__;
static void sig_hdl (int sig, siginfo_t *siginfo, void *context)
{
cout<<"Inside Signal Handler."<<endl;
longjmp(ex_buf__, 1);
}
int main (int argc, char *argv[])
{
ST_DEMO* pStDemo = 0;
struct sigaction act;
memset (&act, '\0', sizeof(act));
/* Use the sa_sigaction field because the handles has two additional parameters */
act.sa_sigaction = &sig_hdl;
/* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &act, NULL) < 0)
{
perror ("sigaction");
return 1;
}
if(!setjmp(ex_buf__))
{
cout<<"Before First Seg Fault."<<endl;
cout<<pStDemo->m_iUnused<<endl;
}
else
{
cout<<"After jump."<<endl;
}
cout<<"Before Second Seg Fault."<<endl;
cout<<pStDemo->m_iUnused<<endl;
while(1)
{
sleep(1);
}
return 0;
}
Your longjmp will cause you to jump to the location, but you will not have returned from the signal handler. This means that the signal is still blocked (this is the default behavior for signals, they are masked until you have returned from the signal handler).
You can fix this by indicating that you want the signal to occur again by clearing the signal mask in your handler before longjmp.
Use the SA_NODEFER flag in act.sa_flags to prevent it from being masked in the first place.
Use the siglongjmp/sigsetjmp functions, which saves the mask for you
Or
Call sigprocmask either before or after the longjmp to unmask it yourself.
A warning: This is a very dangerous thing to do (catch SIGSEGV, and then longjmp out of the signal handler) and it will be almost impossible to do anything useful with it.
If the memory access error occurs in any function that is not async signal safe and reentrant you will not be able to continue in any kind of sane way anyway.
But since there are multiple similar questions on the site I guess this is some kind of exercise.
Related question:
Catching Segmentation Violations and Getting on with Life
Also useful
Longjmp out of signal handler?
longjmp() from signal handler
So this is the problem im having right now, I've created 2 different programs (1 will be managing the other, while the other will be executed multiple times). The programs will be communicating back and forth via signals. My question is, is it possible (and how) to get the process id of the program sending the signal.
My programs use signal() to catch signals and kill() to send them.
Although signal() is in standard C library, this function is not portable, its behavior depending on the system. Better use sigaction() which is POSIX.1.
Here is an example of how to use sigaction with a handler void h(int sig) :
int mysignal (int sig, void (*h)(int), int options)
{
int r;
struct sigaction s;
s.sa_handler = h;
sigemptyset (&s.sa_mask);
s.sa_flags = options;
r = sigaction (sig, &s, NULL);
if (r < 0) perror (__func__);
return r;
}
options are described in man sigaction. A good choice is options=SA_RESTART.
To know the PID of the process which sent a signal, set options=SA_SIGINFO, and use a sa_sigaction callback instead of sa_handler; it will receive a siginfo_t struct, having a si_pid field. You can associate a data to the signal using sigqueue.
Generally speaking, using signals is a bad idea to communicate in a safe manner (when n signals are sent, only the first will have a chance to be delivered; there is no hook to associate other datas; the available user signals are only two). Better use pipes, named pipes or sockets.
Don't use signal(), it's obsolete. If you have it, use sigaction() instead, it provides an interface to get the sender's process ID.
To get the process id of the process that is sending the signal you need to include in your options SA_SIGINFO. If you do so the interface to the sigaction is slightly different. Here is an example of the proper handler to use and how to set itup. (I include SA_RESTART as an option just because it is generally a good idea, but it is not necessary)
// example of a handler which checks the signalling pid
void handler(int sig, siginfo_t* info, void* vp) {
if (info->si_pid != getpid()) {
// not from me (or my call to alarm)
return;
}
// from me. let me know alarm when off
alarmWentOff = 1;
}
Here is my general code for setting up a handler:
typedef void InfoHandler(int, siginfo_t *, void *);
InfoHandler*
SignalWithInfo(int signum, InfoHandler* handler)
{
struct sigaction action, old_action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_sigaction = handler;
sigemptyset(&action.sa_mask); /* block sigs of type being handled */
action.sa_flags = SA_RESTART|SA_SIGINFO; /* restart syscalls if possible */
if (sigaction(signum, &action, &old_action) < 0)
perror("Signal error");
return (old_action.sa_sigaction);
}
and finally, for this particular case:
SignalWithInfo(SIGALRM, handler);
It's changed now. The signal information shows not 1 pid, but easy digital number. That accords to the killall process. May be, it's possible, to enumerate the /proc/ directories, when you find the /proc/DIGIT, open the /proc/DIGIT/comm, read and close it. May be, acueried name will be "shutdown" or "reboot"
There is one function called test(), I want to call this function in every 30 seconds, Please find my implemented code snippet.
void init_sigaction(void) {
struct sigaction act;
act.sa_handler = test; //test()
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGPROF, &act, NULL);
}
void init_time(void) {
struct itimerval val;
val.it_value.tv_sec = 30; //Timer 30 Seconds
val.it_value.tv_usec = 0;
val.it_interval = val.it_value;
setitimer(ITIMER_PROF, &val, NULL);
}
int main()
{
/*Set the handler for the signal SIG to HANDLER */
signal(SIGINT, signal_handler);
init_sigaction();
init_time();
Some_other_function();
}
Now I am using some other function, and I want to pause sigaction timer until other function's execution. how can I implemented interrupt for pause?
Thanks,
From the manual page of setitimer:
A timer which is set to zero (it_value is zero or the timer expires and it_interval is zero) stops.
Call setitimer with zero times, and with a valid old_value argument to store the current values of the timer, so you can start it again later.
Edit:
How about something like this:
struct itimerval old_timer;
void pause_timer()
{
struct itimerval zero_timer = { 0 };
setitimer(ITIMER_PROF, &zero_time, &old_timer);
}
void resume_timer()
{
setitimer(ITIMER_PROF, &old_timer, NULL);
}
Note The code above is untested, and coded only by reading the manual page.
You could consider blocking some signals with e.g. the sigprocmask(2) system call.
However, I strongly recommend reading several times the signal(7) man page. Don't forget that a signal handler can happen any time (including at the worst possible time, e.g. during calls to fprintf or malloc...), so can only call directly or indirectly async-signal-safe functions; and a big lot of library functions are not in this small restricted set. A usual way is to set a volatile sig_atomic_t flag in the signal handler, and test for it outside.