Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Portable way to catch signals and report problem to the user

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?

like image 391
Richard Corden Avatar asked Dec 09 '22 23:12

Richard Corden


2 Answers

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
like image 103
Richard Corden Avatar answered Dec 29 '22 00:12

Richard Corden


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.

like image 28
fizzer Avatar answered Dec 28 '22 23:12

fizzer