1

Note that this question is not duplicate of executing default signal handler. That question only asks for how to call default handler. While I want to call default handler while keeping my own for future signals. Apparently this must be done in a significantly different way since solutions provided in that other question don't work as explained below. Futhermore both most thumbed-up answers are plain wrong which I pointed out in comments there. (This: https://stackoverflow.com/a/6015916/422489 and this: https://stackoverflow.com/a/13290134/422489.)

Similar case with question Explicitly invoke SIG_DFL/SIG_IGN handlers on Linux.


Is it possible for a signal handler to somehow invoke previous handler without permanently resetting the handler?

Let us assume that I have some global variables:

struct sigaction new_action, old_action;

Then somewhere in my code I do something like this:

sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
new_action.sa_handler = &my_handler;
sigaction(SIGINT, &new_action, &old_action);

Now how would I implement my_handler so that it does its own staff and calls whatever was the previous handler (as saved in old_action)?

If we would assume just programmer provided handlers then we could do simply this:

void my_handler(int sig) {
    /* own staff */
    (*old_action)(sig);
    /* own staff */
}

(Possibly slightly more complex if SA_SIGINFO was taken int accont.) But this will obviously fail with SIG_IGN and SIG_DFL handlers as those are not actual function pointers but some “magic special values”.

SIG_IGN could be dealt with by just doing… nothing. But that still leaves SIG_DFL. And also I would hesitate to do some if checks for special values as they will not work on systems that introduce their own custom special values that I don’t know about (or future standards that will add new special values).

(And yes, I do know that SIG_DFL often just closes the process. But still this remains an interesting issue.)

Now it seems that I could switch handlers and use raise to reissue the signal. A simple approach like this:

void my_handler(int sig) {
    /* own staff */
    sigaction(sig, &old_action, NULL);
    raise(sig);
    sigaction(sig, &new_action, NULL);
    /* own staff */
}

But it will not work as expected. While a handler is executing its own signal gets blocked. Which means that raise will not deliver the signal but instead will just leave it until the signal will be unblocked again. Then we will change the handler back and upon exiting the handler the signal raised made will be delivered to current handler which is again my_handler. (I did simple tests and this seems to lead to obvious infinite recursion.)

There is a simple “workaround”:

void my_handler(int sig) {
    /* own staff */
    sigaction(sig, &old_action, NULL);
    raise(sig);
}

But it doesn’t work as well. Now the signal will be delivered to previous handler but we will not reset back to my_handler and there is no clear place in which we could do that.

I considered using the “infinite recursion” approach but explicitly unblock the signal after changing to old handler. Yet it seems unsafe. Can I be sure that raise will indeed deliver the signal? Or some other signal will get delivered instead and problem returns? Also it seems that I would no longer be sure if the signal was not raised by someone else somewhere else and I will end up with two deliveries to old handler.

Is there any way to safely invoke previous handler?

Community
  • 1
  • 1
Adam Badura
  • 5,069
  • 1
  • 35
  • 70
  • 1
    Related, and perhaps essentially a duplicate: http://stackoverflow.com/a/3234395/132382 – pilcrow Nov 11 '14 at 01:37
  • It seems to me that that question misses essential feature. It is not only about invoking previous handler (`SIG_DEF` in particular) but also about keeping your own handler installed. – Adam Badura Nov 11 '14 at 08:25
  • 1
    You can't do that. For SIG_IGN it's easy, as you said. For user-defined handler, you can chain the call. For SIG_DFL, you only choice is to simulate the current system's default behaviour for each signal as described in the man page. When the default behaviour is to terminate the process, you can uninstall your signal handler and return (it'll be triggered again by the kernel causing termination). If it's 'generating a core', you can use SIGSEGV behaviour in your signal handler to let the kernel built a core for you (and terminate the process) – xryl669 Jan 13 '17 at 14:38

0 Answers0