diff --git a/include/proto/signal.h b/include/proto/signal.h index 5b5a64aea..ef43ef9ba 100644 --- a/include/proto/signal.h +++ b/include/proto/signal.h @@ -19,6 +19,7 @@ extern int signal_queue_len; extern struct signal_descriptor signal_state[]; extern struct pool_head *pool2_sig_handlers; +void signal_handler(int sig); void __signal_process_queue(); int signal_init(); void deinit_signals(); diff --git a/src/haproxy.c b/src/haproxy.c index d9c3b6a4e..0bbed6f66 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -244,7 +244,9 @@ void usage(char *name) /*********************************************************************/ /* - * upon SIGUSR1, let's have a soft stop. + * upon SIGUSR1, let's have a soft stop. Note that soft_stop() broadcasts + * a signal zero to all subscribers. This means that it's as easy as + * subscribing to signal 0 to get informed about an imminent shutdown. */ void sig_soft_stop(struct sig_handler *sh) { diff --git a/src/proxy.c b/src/proxy.c index 2949ec7f9..346d0b3e3 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -35,6 +35,7 @@ #include <proto/proto_tcp.h> #include <proto/proto_http.h> #include <proto/proxy.h> +#include <proto/signal.h> int listeners; /* # of proxy listeners, set by cfgparse, unset by maintain_proxies */ @@ -568,6 +569,8 @@ void soft_stop(void) } p = p->next; } + /* signal zero is used to broadcast the "stopping" event */ + signal_handler(0); } diff --git a/src/signal.c b/src/signal.c index 746ae9e0c..128a3838f 100644 --- a/src/signal.c +++ b/src/signal.c @@ -29,9 +29,13 @@ int signal_queue[MAX_SIGNAL]; /* in-order queue of received struct signal_descriptor signal_state[MAX_SIGNAL]; struct pool_head *pool2_sig_handlers = NULL; sigset_t blocked_sig; +int signal_pending = 0; /* non-zero if t least one signal remains unprocessed */ -/* Common signal handler, used by all signals. Received signals are queued. */ -static void signal_handler(int sig) +/* Common signal handler, used by all signals. Received signals are queued. + * Signal number zero has a specific status, as it cannot be delivered by the + * system, any function may call it to perform asynchronous signal delivery. + */ +void signal_handler(int sig) { if (sig < 0 || sig > MAX_SIGNAL) { /* unhandled signal */ @@ -47,8 +51,10 @@ static void signal_handler(int sig) else qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig); } + signal_state[sig].count++; - signal(sig, signal_handler); /* re-arm signal */ + if (sig) + signal(sig, signal_handler); /* re-arm signal */ } /* Call handlers of all pending signals and clear counts and queue length. The @@ -66,6 +72,11 @@ void __signal_process_queue() /* block signal delivery during processing */ sigprocmask(SIG_SETMASK, &blocked_sig, &old_sig); + /* It is important that we scan the queue forwards so that we can + * catch any signal that would have been queued by another signal + * handler. That allows real signal handlers to redistribute signals + * to tasks subscribed to signal zero. + */ for (cur_pos = 0; cur_pos < signal_queue_len; cur_pos++) { sig = signal_queue[cur_pos]; desc = &signal_state[sig]; @@ -121,7 +132,9 @@ void deinit_signals() * newly allocated sig_handler is returned, or NULL in case of any error. The * caller is responsible for unregistering the function when not used anymore. * Note that passing a NULL as the function pointer enables interception of the - * signal without processing, which is identical to SIG_IGN. + * signal without processing, which is identical to SIG_IGN. If the signal is + * zero (which the system cannot deliver), only internal functions will be able + * to notify the registered functions. */ struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler *), int arg) { @@ -130,7 +143,8 @@ struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler if (sig < 0 || sig > MAX_SIGNAL) return NULL; - signal(sig, signal_handler); + if (sig) + signal(sig, signal_handler); if (!fct) return NULL; @@ -150,7 +164,9 @@ struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler * allocated sig_handler is returned, or NULL in case of any error. The caller * is responsible for unregistering the task when not used anymore. Note that * passing a NULL as the task pointer enables interception of the signal - * without processing, which is identical to SIG_IGN. + * without processing, which is identical to SIG_IGN. If the signal is zero + * (which the system cannot deliver), only internal functions will be able to + * notify the registered functions. */ struct sig_handler *signal_register_task(int sig, struct task *task, int reason) { @@ -159,7 +175,8 @@ struct sig_handler *signal_register_task(int sig, struct task *task, int reason) if (sig < 0 || sig > MAX_SIGNAL) return NULL; - signal(sig, signal_handler); + if (sig) + signal(sig, signal_handler); if (!task) return NULL;