[dovecot-cvs]
dovecot/src/lib lib-signals.c, 1.8, 1.9 lib-signals.h, 1.3, 1.4
cras at dovecot.org
cras at dovecot.org
Sun Sep 25 14:07:40 EEST 2005
Update of /var/lib/cvs/dovecot/src/lib
In directory talvi:/tmp/cvs-serv8918/lib
Modified Files:
lib-signals.c lib-signals.h
Log Message:
Implemented new signal handling framework, which makes handling signals much
easier.
Index: lib-signals.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib/lib-signals.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -d -r1.8 -r1.9
--- lib-signals.c 9 Jun 2005 18:28:57 -0000 1.8
+++ lib-signals.c 25 Sep 2005 11:07:37 -0000 1.9
@@ -1,96 +1,196 @@
/* Copyright (c) 2001-2003 Timo Sirainen */
#include "lib.h"
+#include "ioloop.h"
+#include "fd-close-on-exec.h"
#include "lib-signals.h"
-#include <stdio.h>
#include <signal.h>
+#include <unistd.h>
-int lib_signal_kill;
-unsigned int lib_signal_hup_count;
-unsigned int lib_signal_usr1_count, lib_signal_usr2_count;
+#define MAX_SIGNAL_VALUE 31
-static void (*quit_handler) (int);
+struct signal_handler {
+ signal_handler_t *handler;
+ void *context;
-static void sig_counter(int signo)
+ int delayed;
+ struct signal_handler *next;
+};
+
+/* 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 struct signal_handler *signal_handlers[MAX_SIGNAL_VALUE+1];
+static int sig_pipe_fd[2];
+
+static struct io *io_sig;
+
+static void sig_handler(int signo)
{
-#ifndef HAVE_SIGACTION
- /* some systems may have changed the signal handler to default one */
- signal(signo, sig_counter);
-#endif
+ struct signal_handler *h;
+ int delayed_sent = FALSE;
- switch (signo) {
- case SIGHUP:
- lib_signal_hup_count++;
- break;
- case SIGUSR1:
- lib_signal_usr1_count++;
- break;
- case SIGUSR2:
- lib_signal_usr2_count++;
- break;
+ if (signo < 0 || signo > MAX_SIGNAL_VALUE)
+ return;
+
+ /* remember that we're inside a signal handler which might have been
+ called at any time. don't do anything that's unsafe. */
+ for (h = signal_handlers[signo]; h != NULL; h = h->next) {
+ if (!h->delayed)
+ h->handler(signo, h->context);
+ else if (!delayed_sent) {
+ int saved_errno = errno;
+ unsigned char signo_byte = signo;
+
+ if (write(sig_pipe_fd[1], &signo_byte, 1) != 1)
+ i_error("write(sigpipe) failed: %m");
+ delayed_sent = TRUE;
+ errno = saved_errno;
+ }
}
}
-static void sig_quit(int signo)
+static void signal_read(void *context __attr_unused__)
{
- /* if we get killed after this, just die instead of coming back here. */
- signal(SIGINT, SIG_DFL);
- signal(SIGTERM, SIG_DFL);
+ unsigned char signal_buf[512];
+ unsigned char signal_mask[MAX_SIGNAL_VALUE+1];
+ ssize_t i, ret;
+ int signo;
- lib_signal_kill = signo;
- quit_handler(signo);
+ ret = read(sig_pipe_fd[0], signal_buf, sizeof(signal_buf));
+ if (ret > 0) {
+ memset(signal_mask, 0, sizeof(signal_mask));
+
+ /* move them to mask first to avoid calling same handler
+ multiple times */
+ for (i = 0; i < ret; i++) {
+ signo = signal_buf[i];
+ if (signo > MAX_SIGNAL_VALUE) {
+ i_panic("sigpipe contains signal %d > %d",
+ signo, MAX_SIGNAL_VALUE);
+ }
+ signal_mask[signo] = 1;
+ }
+
+ /* call the delayed handlers */
+ for (signo = 0; signo < MAX_SIGNAL_VALUE; signo++) {
+ if (signal_mask[signo] > 0) {
+ struct signal_handler *h =
+ signal_handlers[signo];
+
+ for (; h != NULL; h = h->next) {
+ if (h->delayed)
+ h->handler(signo, h->context);
+ }
+ }
+ }
+ } else if (ret < 0) {
+ if (errno != EAGAIN)
+ i_fatal("read(sigpipe) failed: %m");
+ } else {
+ i_fatal("read(sigpipe) failed: EOF");
+ }
}
-void lib_init_signals(void (*sig_quit_handler) (int))
+void lib_signals_set_handler(int signo, int delayed,
+ signal_handler_t *handler, void *context)
{
-#ifdef HAVE_SIGACTION
- struct sigaction act;
-#endif
+ struct signal_handler *h;
- lib_signal_kill = 0;
- lib_signal_hup_count = 0;
- quit_handler = sig_quit_handler;
+ if (signo < 0 || signo > MAX_SIGNAL_VALUE) {
+ i_panic("Trying to set signal %d handler, but max is %d",
+ signo, MAX_SIGNAL_VALUE);
+ }
- /* signal() behaviour is a bit inconsistent between systems
- after the signal handler has been called. If the signal
- isn't ignored, or your handler doesn't kill the program,
- sigaction() should be used. */
-#ifdef HAVE_SIGACTION
- if (sigemptyset(&act.sa_mask) < 0)
- i_fatal("sigemptyset(): %m");
- act.sa_flags = 0;
- act.sa_handler = sig_counter;
- while (sigaction(SIGHUP, &act, NULL) < 0) {
- if (errno != EINTR)
- i_fatal("sigaction(): %m");
+ if (signal_handlers[signo] == NULL) {
+ /* first handler for this signal */
+ struct sigaction act;
+
+ if (sigemptyset(&act.sa_mask) < 0)
+ i_fatal("sigemptyset(): %m");
+ act.sa_flags = 0;
+ act.sa_handler = handler != NULL ? sig_handler : SIG_IGN;
+ if (sigaction(signo, &act, NULL) < 0)
+ i_fatal("sigaction(%d): %m", signo);
+
+ if (handler == NULL) {
+ /* we're ignoring the handler, just return */
+ return;
+ }
}
- while (sigaction(SIGUSR1, &act, NULL) < 0) {
- if (errno != EINTR)
- i_fatal("sigaction(): %m");
+ i_assert(sig_handler != NULL);
+
+ if (delayed && sig_pipe_fd[0] == -1) {
+ /* first delayed handler */
+ if (pipe(sig_pipe_fd) < 0)
+ i_fatal("pipe() failed: %m");
+ fd_close_on_exec(sig_pipe_fd[0], TRUE);
+ fd_close_on_exec(sig_pipe_fd[1], TRUE);
+ io_sig = io_add(sig_pipe_fd[0], IO_READ, signal_read, NULL);
}
- while (sigaction(SIGUSR2, &act, NULL) < 0) {
- if (errno != EINTR)
- i_fatal("sigaction(): %m");
+
+ h = i_new(struct signal_handler, 1);
+ h->handler = handler;
+ h->context = context;
+ h->delayed = delayed;
+
+ /* atomically set to signal_handlers[] list */
+ h->next = signal_handlers[signo];
+ signal_handlers[signo] = h;
+}
+
+void lib_signals_unset_handler(int signo, signal_handler_t *handler,
+ void *context)
+{
+ struct signal_handler *h, **p;
+
+ for (p = &signal_handlers[signo]; *p != NULL; p = &(*p)->next) {
+ if ((*p)->handler == handler && (*p)->context == context) {
+ h = *p;
+ *p = h->next;
+ i_free(h);
+ return;
+ }
}
- /* we want to just ignore SIGALRM, but to get it to abort syscalls
- with EINTR we can't just set it to SIG_IGN. sig_counter handler
- is good enough. */
- while (sigaction(SIGALRM, &act, NULL) < 0) {
- if (errno != EINTR)
- i_fatal("sigaction(): %m");
+ i_panic("lib_signals_unset_handler(%d, %p, %p): handler not found",
+ signo, (void *)handler, context);
+}
+
+void lib_signals_init(void)
+{
+ sig_pipe_fd[0] = sig_pipe_fd[1] = -1;
+ io_sig = NULL;
+
+ memset(signal_handlers, 0, sizeof(signal_handlers));
+}
+
+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;
+
+ while (handlers != NULL) {
+ h = handlers;
+ handlers = h->next;
+ i_free(h);
+ }
+ }
}
-#else
- signal(SIGHUP, sig_counter);
- signal(SIGUSR1, sig_counter);
- signal(SIGUSR2, sig_counter);
- signal(SIGALRM, sig_counter);
-#endif
- /* these signals should be called only once, so it's safe to use
- signal() */
- signal(SIGINT, sig_quit);
- signal(SIGTERM, sig_quit);
- signal(SIGPIPE, SIG_IGN);
+ if (io_sig != NULL)
+ io_remove(io_sig);
+ if (sig_pipe_fd[0] != -1) {
+ if (close(sig_pipe_fd[0]) < 0)
+ i_error("close(sigpipe) failed: %m");
+ if (close(sig_pipe_fd[1]) < 0)
+ i_error("close(sigpipe) failed: %m");
+ }
}
Index: lib-signals.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/lib/lib-signals.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- lib-signals.h 12 Nov 2004 14:35:39 -0000 1.3
+++ lib-signals.h 25 Sep 2005 11:07:37 -0000 1.4
@@ -1,10 +1,19 @@
#ifndef __LIB_SIGNALS_H
#define __LIB_SIGNALS_H
-extern int lib_signal_kill;
-extern unsigned int lib_signal_hup_count;
-extern unsigned int lib_signal_usr1_count, lib_signal_usr2_count;
+#include <signal.h>
-void lib_init_signals(void (*sig_quit_handler) (int));
+typedef void signal_handler_t(int signo, void *context);
+
+/* Set signal handler for specific signal. If delayed is TRUE, the handler
+ will be called later, ie. not as a real signal handler. If handler is NULL,
+ the signal is ignored. */
+void lib_signals_set_handler(int signo, int delayed,
+ signal_handler_t *handler, void *context);
+void lib_signals_unset_handler(int signo,
+ signal_handler_t *handler, void *context);
+
+void lib_signals_init(void);
+void lib_signals_deinit(void);
#endif
More information about the dovecot-cvs
mailing list