[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 5b5a64a..ef43ef9 100644
--- a/include/proto/signal.h
+++ b/include/proto/signal.h
@@ -19,6 +19,7 @@
 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 d9c3b6a..0bbed6f 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -244,7 +244,9 @@
 /*********************************************************************/
 
 /*
- * 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 2949ec7..346d0b3 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 @@
 		}
 		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 746ae9e..128a383 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -29,9 +29,13 @@
 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 @@
 		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 @@
 	/* 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 @@
  * 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 @@
 	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 @@
  * 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 @@
 	if (sig < 0 || sig > MAX_SIGNAL)
 		return NULL;
 
-	signal(sig, signal_handler);
+	if (sig)
+		signal(sig, signal_handler);
 
 	if (!task)
 		return NULL;