24

I have written an application where i have registered number of signal handler for different signals in linux . After process receives the signal the control is transferred to the signal handler i had registered. In this signal handler i do some work which i need to do, and then i would like to call the default signal hander i.e SIF_DFL or SIG_IGN . However, SIG_DFL and SIG_ING are both macros which expand to numeric values 0 and 1 respectively, which are invalid function addresses.

IS there any way i can call default actions i.e SIG_DFL or SIG_IGN ?

In order to achieve the effect of SIG_DFL or SIG_ING i call exit(1) and do nothing , respectively . But for signals like SIGSEGV i also would like to have core dump . In general i would want to my default behavior to be same as SIG_DFL and ignore behavior same SIG_IGN , the way Operating system would do .

KZcoding
  • 1,417
  • 4
  • 16
  • 26
app
  • 421
  • 1
  • 4
  • 7
  • possible duplicate of [Explicitly invoke SIG\_DFL/SIG\_IGN handlers on Linux](http://stackoverflow.com/questions/3147840/explicitly-invoke-sig-dfl-sig-ign-handlers-on-linux) – pilcrow Dec 21 '14 at 04:31

4 Answers4

16

The GNU C Library Reference Manual has a whole chapter explaining everything about signal handling.

You always get the previously set signal handler (a function pointer) when you install your own handler (see manpages for signal() or sigaction()).

previous_handler = signal(SIGINT, myhandler);

The general rule is, that you can always reset to the previous handler and raise() the signal again.

void myhandler(int sig) {
  /* own stuff .. */
  signal(sig, previous_handler);
  raise(sig);
  /* when it returns here .. set our signal handler again */
  signal(sig, myhandler);
}

There is one disadvantage of the general rule: Hardware exceptions which are mapped to signals are usually assigned to a certain instruction which caused the exception. So, when you raise a signal again, the associated instruction is not the same as originally. This can but should not harm other signal handlers.

Another disadvantage is, that each raised signal causes a lot of processing time. To prevent excessive use of raise() you can use the following alternatives:

  1. In case of SIG_DFL the function pointer points to address 0 (which is obviously no valid address). Thus, you have to reset the handler and raise() the signal again.

    if (previous_handler == SIG_DFL)
    {
      signal(sig, SIG_DFL);
      raise(sig);
      signal(sig, myhandler);
    } 
  2. SIG_IGN has value 1 (also an invalid address). Here you can just return (do nothing).

    else if (previous_handler == SIG_IGN)
    {
      return;
    } 
  3. Otherwise (neither SIG_IGN nor SIG_DFL) you have received a valid function pointer and you can call the handler directly,

    else
    {
      previous_handler(sig);
    }

Of course, you have to consider the different APIs as well (see manpages for signal() and sigaction()).

Gabriel Southern
  • 9,602
  • 12
  • 56
  • 95
homac
  • 391
  • 3
  • 7
  • 1
    I would strongy consider whether it is worth to optimize by doing a direct call to handler. This is not "future-proof". If a system comes that will intorduce yet another special handler (`SIG_FUN`) then above optimization will fail since it will try to actually call it as function while it is not a valid pointer... – Adam Badura Nov 10 '14 at 10:57
  • 12
    Also are you sure this will work? The GNU C Library Reference states (for example in 24.7.5) that delivery of a signal is blocked while a handler for that signal executes. So your `raise` will only send the signal but not invoke handler. Then you will reset back the handler. After your handler exists the signal from `raise` will be delivered but will find back your own handler. – Adam Badura Nov 10 '14 at 11:09
  • The link you provided is dead. – Koray Tugay Jun 03 '15 at 20:01
  • This should not be rated up so much. Not only will it not work as Adam Badura points out, restoring SIGINT and calling the default handler has the effect of terminating the process, which makes it a bit of a silly exercise to try to restore the previous handler when your process is (properly) going away since the user hit Ctrl+C or sent the signal via kill. – CubicleSoft May 14 '16 at 17:44
12

You can save the previous handler and then call it when the time is right.

Install handler. Make sure you save old handler

static struct sigaction new_sa, old_sa;

new_sa.sa_handler = my_handler;
sigemptyset(&new_handler.sa_mask);

if (sigaction(signo, &new_sa, &old_sa) == -1) {
    /* handle sigaction error */
}

In your new handler, call the old handler

(*old_sa.sa_handler)(signo)

You don't need to raise it again or do any messy stuff; simply call the old handler (of course, since you saved the sigaction you have acces to the old disposition and so on).

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • 1
    Wouldn't you also need to look at the `SA_SIGINFO` bit in `sa_flags`, and call either `sa_sigaction` or `sa_handler`? – Greg Hewgill Aug 04 '11 at 04:44
  • Today I'm learning more than I thought I needed to know about Linux signal handling (see http://stackoverflow.com/questions/6935988/how-to-provide-extend-on-write-functionality-for-memory-mapped-files-in-linux). – Greg Hewgill Aug 04 '11 at 08:48
  • 9
    It seems that this code will not work if previous handler was `SIG_IGN` or `SIG_DFL` which are often the most interesting cases. – Adam Badura Nov 10 '14 at 10:46
  • 1
    Could someone explain why this would not work for SIG_DFL or SIG_IGN? – HardcoreHenry Jun 13 '19 at 14:59
  • Might want to fix this typo: < new_sa.handler = my_handler > new_sa.sa_handler = my_handler – ByteMe95 Aug 15 '19 at 13:50
7

The usual approach is to reset the signal handler and then raise() the signal again:

Here's an example SIGINT handler:

void sigint_handler(int num)
{
    /* handle SIGINT */

    // call default handler
    signal(SIGINT, SIG_DFL);
    raise(SIGINT);
}
Philip
  • 5,795
  • 3
  • 33
  • 68
  • But how to only invoke the default handler without changing the handler? In your code `sigint_handler` will no longer be used as `SIGINT` handler. And there is no clear point where to set it back to `sigint_handler`. – Adam Badura Nov 10 '14 at 11:12
  • @AdamBadura - Well the default handler for SIGINT will terminate the process. The fact that the process will no longer be running after the default handler executes does kind of make signal handler restoration a bit of a moot point. – CubicleSoft May 14 '16 at 17:33
  • @CubicleSoft You are right in that case. But not in general when you don't know what default handler does. While the original question doesn't seem to be limited to `SIGINT`. – Adam Badura May 14 '16 at 21:41
  • Wouldn't raising the signal cause the current handler to be invoked again though? That seems to be the case when using sigaction. – hookenz Sep 04 '16 at 22:23
2

Given the signal handlers are implemented in kernel, the only way I see is to

  • reset the handler and
  • raise() the signal again
Jan Hudec
  • 73,652
  • 13
  • 125
  • 172