From: Willy Tarreau Date: Fri, 27 Aug 2010 16:26:11 +0000 (+0200) Subject: [MEDIUM] signals: support redistribution of signal zero when stopping X-Git-Tag: v1.5-dev8~481 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d0807c3c603f661ef74b9cbb580aeffdbbd7d712;p=thirdparty%2Fhaproxy.git [MEDIUM] signals: support redistribution of signal zero when stopping Signal zero is never delivered by the system. However having a signal to which functions and tasks can subscribe to be notified of a stopping event is useful. So this patch does two things : 1) allow signal zero to be delivered from any function of signal handler 2) make soft_stop() deliver this signal so that tasks can be notified of a stopping condition. --- diff --git a/include/proto/signal.h b/include/proto/signal.h index 5b5a64aea3..ef43ef9baa 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 d9c3b6a4e9..0bbed6f662 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 2949ec7f9a..346d0b3e30 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -35,6 +35,7 @@ #include #include #include +#include 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 746ae9e0c3..128a3838fb 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;