lib-signals.c revision f0cd1d0022590d0a0d84f57e362774c2e96e2ea8
/* Copyright (c) 2001-2012 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "ioloop.h"
#include "fd-close-on-exec.h"
#include "fd-set-nonblock.h"
#include "lib-signals.h"
#include <signal.h>
#include <unistd.h>
#define MAX_SIGNAL_VALUE 63
#define SIGNAL_IS_TERMINAL(signo) \
#if !defined(SA_SIGINFO) && !defined(SI_NOINFO)
/* without SA_SIGINFO we don't know what the real code is. we need SI_NOINFO
to make sure lib_signal_code_to_str() returns "". */
# define SI_NOINFO -1
#endif
struct signal_handler {
void *context;
enum libsig_flags flags;
struct signal_handler *next;
};
volatile unsigned int signal_term_counter = 0;
/* Remember that these are accessed inside signal handler which may be called
even while we're initializing/deinitializing. Try hard to keep everything
in consistent state. */
static bool signals_initialized = FALSE;
static bool have_pending_signals = FALSE;
{
/* common */
switch (sicode) {
#ifdef SI_NOINFO
case SI_NOINFO:
return "";
#endif
case SI_USER:
return "kill";
#ifdef SI_KERNEL
case SI_KERNEL:
return "kernel";
#endif
case SI_TIMER:
return "timer";
}
/* If SEGV_MAPERR is supported, the rest of them must be too.
FreeBSD 6 at least doesn't support these. */
#ifdef SEGV_MAPERR
switch (signo) {
case SIGSEGV:
switch (sicode) {
case SEGV_MAPERR:
return "address not mapped";
case SEGV_ACCERR:
return "invalid permissions";
}
break;
case SIGBUS:
switch (sicode) {
case BUS_ADRALN:
return "invalid address alignment";
#ifdef BUS_ADRERR /* for OSX 10.3 */
case BUS_ADRERR:
return "nonexistent physical address";
#endif
#ifdef BUS_OBJERR /* for OSX 10.3 */
case BUS_OBJERR:
return "object-specific hardware error";
#endif
}
}
#endif
}
#ifdef SA_SIGINFO
#else
static void sig_handler(int signo)
#endif
{
struct signal_handler *h;
char c = 0;
#if defined(SI_NOINFO) || !defined(SA_SIGINFO)
#ifndef SA_SIGINFO
#endif
/* Solaris can leave this to NULL */
}
#endif
return;
if (SIGNAL_IS_TERMINAL(signo))
/* remember that we're inside a signal handler which might have been
called at any time. don't do anything that's unsafe. we might also
get interrupted by another signal while inside this handler. */
if ((h->flags & LIBSIG_FLAG_DELAYED) == 0)
if (!have_pending_signals) {
int saved_errno = errno;
i_error("write(sigpipe) failed: %m");
errno = saved_errno;
}
}
}
}
#ifdef SA_SIGINFO
void *context ATTR_UNUSED)
#else
#endif
{
/* if we used SIG_IGN instead of this function,
the system call might be restarted */
}
static void ATTR_NULL(1)
{
struct signal_handler *h;
char buf[64];
int signo;
if (sigfillset(&fullset) < 0)
i_fatal("sigfillset() failed: %m");
i_fatal("sigprocmask() failed: %m");
/* typically we should read only a single byte, but if a signal
is sent while signal handler is running we might get more. */
if (ret > 0) {
} else if (ret < 0) {
i_fatal("read(sigpipe) failed: %m");
} else {
i_fatal("read(sigpipe) failed: EOF");
}
i_fatal("sigprocmask() failed: %m");
if (ret < 0)
return;
/* call the delayed handlers after signals are copied and unblocked */
continue;
if ((h->flags & LIBSIG_FLAG_DELAYED) != 0)
}
}
}
{
i_fatal("sigemptyset(): %m");
#ifdef SA_SIGINFO
#else
#endif
if ((flags & LIBSIG_FLAG_RESTART) != 0)
}
{
struct signal_handler *h;
i_panic("Trying to set signal %d handler, but max is %d",
}
/* first delayed handler */
if (pipe(sig_pipe_fd) < 0)
i_fatal("pipe() failed: %m");
if (signals_initialized) {
signal_read, (void *)NULL);
}
}
/* atomically set to signal_handlers[] list */
signal_handlers[signo] = h;
}
{
i_panic("Trying to ignore signal %d, but max is %d",
}
i_fatal("sigemptyset(): %m");
if (restart_syscalls) {
} else {
#ifdef SA_SIGINFO
#else
#endif
}
}
void *context)
{
struct signal_handler *h, **p;
h = *p;
*p = h->next;
i_free(h);
return;
}
}
i_panic("lib_signals_unset_handler(%d, %p, %p): handler not found",
}
void lib_signals_reset_ioloop(void)
{
signal_read, (void *)NULL);
}
}
void lib_signals_init(void)
{
int i;
/* add signals that were already registered */
for (i = 0; i < MAX_SIGNAL_VALUE; i++) {
if (signal_handlers[i] != NULL)
}
if (sig_pipe_fd[0] != -1) {
signal_read, (void *)NULL);
}
}
void lib_signals_deinit(void)
{
struct signal_handler *handlers, *h;
int i;
for (i = 0; i < MAX_SIGNAL_VALUE; i++) {
if (signal_handlers[i] != NULL) {
/* atomically remove from signal_handlers[] list */
handlers = signal_handlers[i];
signal_handlers[i] = NULL;
h = handlers;
i_free(h);
}
}
}
if (sig_pipe_fd[0] != -1) {
if (close(sig_pipe_fd[0]) < 0)
i_error("close(sigpipe) failed: %m");
i_error("close(sigpipe) failed: %m");
}
}