4

I've a program, which installs a signal handler for SIGSEGV. In signal handler ( I try to catch crash ) I restart my application.

But when my application is resurrected it doesn't handle SIGSEGV anymore.

Here's an example:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

const char * app = 0;

void sig_handler(int signo)
{
    puts("sig_handler");

    const pid_t p = fork();

    if (p == 0)
    {
        printf("Running app %s\n", app);
        execl(app, 0);
    }

    exit(1);
}


int main(int argc, char** argv)
{
    app = argv[0];

    struct sigaction act;
    sigemptyset(&act.sa_mask);

    act.sa_handler = sig_handler;
    act.sa_flags = 0;

    const int status = sigaction(SIGSEGV, &act, 0) == 0;     
    printf("signaction = %d\n", status);

    sleep(5);

    int* a = 0;
    int b = *a;

    return 0;
}

what I get in output is:

./signals 
signaction = 1
sig_handler
Running app ./signals
signaction = 1

So I can see sighandler was set in right way, but resurrected app simply crashed silently.

What am I missing?

Michał Walenciak
  • 4,257
  • 4
  • 33
  • 61

1 Answers1

6

What you're missing is that, by default, when you handle a signal, any additional delivery of that signal is blocked until the handling function returns. Since you never return from your signal handler (you call execl() instead) then your second SIGSEGV isn't being delivered. It's waiting until your signal handler function returns, which it never will.

To get the results you seem to want, you have to change this default behavior. The easiest way to do that is to set the appropriate flag when you register the signal handler:

act.sa_flags = SA_NODEFER;

and you'll get the recursive behavior you seem to be looking for. Your other option is to unblock it with sigprocmask() before your execl() call.

Couple of other ancillary points:

  1. puts(), printf(), execl() and exit() are not async-safe, and shouldn't be called from a signal handler. execle() and _exit() would be OK.

  2. You're not calling execl() properly. The first argument should be the application name, so execl(app, app, (char *)0); would be correct. The cast to char *, which you omit, is required.

Crowman
  • 25,242
  • 5
  • 48
  • 56
  • Note that the [`execl()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html) specification says that pending signals are inherited by the child process. There are rules about signal handlers being reset to default, too. It's a dense read — there's a lot of information in the page. – Jonathan Leffler Mar 14 '16 at 07:42
  • @JonathanLeffler I was confused by `fork`s manual where you can find: `The child's set of pending signals is initially empty` – Michał Walenciak Mar 14 '16 at 16:59
  • @MichałWalenciak : it has to be initially empty, really, since it's a brand new process and nobody has sent it any signals yet. – Crowman Mar 14 '16 at 17:06
  • @MichałWalenciak: after a `fork()`, that's true; but you don't have a `fork()` in your code; you're simply using `execl()` to run the new executable, and (somewhat to my surprise) I don't see anything saying that pending signals are dropped when you execute a new program (or a new copy of your current program). I may have missed the relevant information. – Jonathan Leffler Mar 14 '16 at 17:15
  • @JonathanLeffler: I do have fork ;) – Michał Walenciak Mar 14 '16 at 18:57
  • @MichałWalenciak : The `fork()` man page states that the processes are identical except for the points listed. The process's signal mask is not on that list, so it is inherited, which explains the behavior that you're seeing. It's not the set of pending signals which are the issue, here, but the set of signals which are blocked. – Crowman Mar 14 '16 at 19:16
  • Hmm…so you do. I'd not looked for it in the signal handler; my apologies. Don't forget [How to avoid using `printf()` in a signal handler](http://stackoverflow.com/questions/16891019/) — but `fork()` is one of the functions that POSIX explicitly allows in a signal handler, along with some but not all of the `exec*()` family of functions, amongst others. (Curious omissions include functions such as `strlen()` — beware of the letter of the law!) – Jonathan Leffler Mar 14 '16 at 20:16