[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