views:

732

answers:

5

If by some miracle a segfault occurs in our program, I want to catch the SIGSEGV and let the user (possibly a GUI client) know with a single return code that a serious problem has occurred. At the same time I would like to display information on the command line to show which signal was caught.

Today our signal handler looks as follows:

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

I can hear the screams of horror with the above, as I have read from this thread that it is evil to call a non-reentrant function from a signal handler.

Is there a portable way to handle the signal and provide information to users?

EDIT: Or at least portable within the POSIX framework?

A: 

Write a launcher program to run your program and report abnormal exit code to the user.

fizzer
+4  A: 

This table lists all of the functions that POSIX guarantees to be async-signal-safe and so can be called from a signal handler.

By using the 'write' command from this table, the following relatively "ugly" solution hopefully will do the trick:

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

EDIT: Building on windows.

After trying to build this one windows, it appears that 'STDERR_FILENO' is not defined. From the documentation however its value appears to be '2'.

#include <io.h>
#define STDIO_FILENO 2

EDIT: 'exit' should not be called from the signal handler either!

As pointed out by fizzer, calling _Exit in the above is a sledge hammer approach for signals such as HUP and TERM. Ideally, when these signals are caught a flag with "volatile sig_atomic_t" type can be used to notify the main program that it should exit.

The following I found useful in my searches.

  1. Introduction To Unix Signals Programming
  2. Extending Traditional Signals
Richard Corden
great answer, learned something new from it, too bad the original poster didn't come back to up vote/select an answer :)
Roman M
Thanks for your comment. BTW - This *is* the OPs answer, ie. me. I am still not 100% sure that this is correct, hence I will not yet mark it as answered.
Richard Corden
+1  A: 

Richard Corden, (I don't have the karma to comment, so a new post...). I'm confused - are you also the OP?

A few random comments:

  • SIGSEGV is synchronous, so you don't care about async-signal-safety.
  • This solution is clearly POSIX-only.
  • Handlers for async signals should return, so for those, prefer to set a volatile sig_atomic _t flag and test for it occasionally in the main program.

To the OP - if you're seeing SIGSEGV and you didn't raise it, you've already invoked undefined behaviour, so you can't portably assume anything about what your program will do.

fizzer
Yes - I'm the OP.
Richard Corden
Regarding the undefined behaviour - the goal is to exit ASAP and if we're lucky have a message produced on the STDERR highlighting this.
Richard Corden
The fact that SIGSEGV is synchronous is why you can't use functions that are not designed to be reentrant. What if the function that crashed was holding a lock, and the function called by the signal handler tries to acquire the same lock?
bk1e
A: 

FWIW, 2 is standard error on Windows also, but you're going to need some conditional compilation because their write() is called _write(). You'll also want

#ifdef SIGUSR1 /* or whatever */

etc around all references to signals not guaranteed to be defined by the C standard.

Also, as noted above, you don't want to handle SIGUSR1, SIGHUP, SIGINT, SIGQUIT and SIGTERM like this.

fizzer
+1  A: 

Richard, still not enough karma to comment, so a new answer I'm afraid. These are asynchronous signals; you have no idea when they are delivered, so possibly you will be in library code which needs to complete to stay consistent. Signal handlers for these signals are therefore required to return. If you call exit(), the library will do some work post-main(), including calling functions registered with atexit() and cleaning up the standard streams. This processing may fail if, say, your signal arrived in a standard library I/O function. Therefore in C90 you are not allowed to call exit(). I see now C99 relaxes the requirement by providing a new function _Exit() in stdlib.h. _Exit() may safely be called from a handler for an asynchronous signal. _Exit() will not call atexit() functions and may omit cleaning up the standard streams at the implementation's discretion.

To bk1e (commenter a few posts up) The fact that SIGSEGV is synchronous is why you can't use functions that are not designed to be reentrant. What if the function that crashed was holding a lock, and the function called by the signal handler tries to acquire the same lock?

This is a possibility, but it's not 'the fact that SIGSEGV is synchronous' which is the problem. Calling non-reentrant functions from the handler is much worse with asynchronous signals for two reasons:

  • asynchronous signal handlers are (generally) hoping to return and resume normal program execution. A handler for a synchronous signal is (generally) going to terminate anyway, so you've not lost much if you crash.
  • in a perverse sense, you have absolute control over when a synchronous signal is delivered - it happens as you execute your defective code, and at no other time. You have no control at all over when an async signal is delivered. Unless the OP's own I/O code is ifself the cause of the defect - e.g. outputting a bad char* - his error message has a reasonable chance of succeeding.
fizzer
Thanks for the detailed information.
Richard Corden