diff --git a/include/common/defaults.h b/include/common/defaults.h index 07cb75ff9..cdc6b7f02 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -166,4 +166,11 @@ #define STATS_VERSION_STRING " version " HAPROXY_VERSION ", released " HAPROXY_DATE #endif +/* Maximum signal queue size, and also number of different signals we can + * handle. + */ +#ifndef MAX_SIGNAL +#define MAX_SIGNAL 256 +#endif + #endif /* _COMMON_DEFAULTS_H */ diff --git a/include/proto/signal.h b/include/proto/signal.h new file mode 100644 index 000000000..273482744 --- /dev/null +++ b/include/proto/signal.h @@ -0,0 +1,29 @@ +/* + * Asynchronous signal delivery functions. + * + * Copyright 2000-2009 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include + +extern int signal_queue_len; +extern struct signal_descriptor signal_state[]; + +void signal_init(); +void signal_handler(int sig); +void signal_register(int sig, void (*handler)(int)); +void __signal_process_queue(); + +static inline void signal_process_queue() +{ + if (unlikely(signal_queue_len > 0)) + __signal_process_queue(); +} diff --git a/include/types/signal.h b/include/types/signal.h new file mode 100644 index 000000000..b863d23b5 --- /dev/null +++ b/include/types/signal.h @@ -0,0 +1,20 @@ +/* + * Asynchronous signal delivery functions descriptors. + * + * Copyright 2000-2009 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include + +struct signal_descriptor { + int count; /* number of times raised */ + void (*handler)(int sig); +}; diff --git a/src/signal.c b/src/signal.c new file mode 100644 index 000000000..7de17abfc --- /dev/null +++ b/src/signal.c @@ -0,0 +1,112 @@ +/* + * Asynchronous signal delivery functions. + * + * Copyright 2000-2009 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include + +/* Principle : we keep an in-order list of the first occurrence of all received + * signals. All occurrences of a same signal are grouped though. The signal + * queue does not need to be deeper than the number of signals we can handle. + * The handlers will be called asynchronously with the signal number. They can + * check themselves the number of calls by checking the descriptor this signal. + */ + +int signal_queue_len; /* length of signal queue, <= MAX_SIGNAL (1 entry per signal max) */ +int signal_queue[MAX_SIGNAL]; /* in-order queue of received signals */ +struct signal_descriptor signal_state[MAX_SIGNAL]; +sigset_t blocked_sig; + +void signal_init() +{ + signal_queue_len = 0; + memset(signal_queue, 0, sizeof(signal_queue)); + memset(signal_state, 0, sizeof(signal_state)); + sigfillset(&blocked_sig); +} + +void signal_handler(int sig) +{ + if (sig < 0 || sig > MAX_SIGNAL || !signal_state[sig].handler) { + /* unhandled signal */ + qfprintf(stderr, "Received unhandled signal %d. Signal has been disabled.\n", sig); + signal(sig, SIG_IGN); + return; + } + + if (!signal_state[sig].count) { + /* signal was not queued yet */ + if (signal_queue_len < MAX_SIGNAL) + signal_queue[signal_queue_len++] = sig; + else + qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig); + } + signal_state[sig].count++; + signal(sig, signal_handler); /* re-arm signal */ +} + +/* Register a handler for signal . Set it to NULL, SIG_DFL or SIG_IGN to + * remove the handler. The signal's queue is flushed and the signal is really + * registered (or unregistered) for the process. The interface is the same as + * for standard signal delivery, except that the handler does not need to rearm + * the signal itself (it can disable it however). + */ +void signal_register(int sig, void (*handler)(int)) +{ + if (sig < 0 || sig > MAX_SIGNAL) { + qfprintf(stderr, "Failed to register signal %d : out of range [0..%d].\n", sig, MAX_SIGNAL); + return; + } + + signal_state[sig].count = 0; + if (handler == NULL) + handler = SIG_IGN; + + if (handler != SIG_IGN && handler != SIG_DFL) { + signal_state[sig].handler = handler; + signal(sig, signal_handler); + } + else { + signal_state[sig].handler = NULL; + signal(sig, handler); + } +} + +/* Call handlers of all pending signals and clear counts and queue length. The + * handlers may unregister themselves by calling signal_register() while they + * are called, just like it is done with normal signal handlers. + * Note that it is more efficient to call the inline version which checks the + * queue length before getting here. + */ +void __signal_process_queue() +{ + int sig, cur_pos = 0; + struct signal_descriptor *desc; + sigset_t old_sig; + + /* block signal delivery during processing */ + sigprocmask(SIG_SETMASK, &blocked_sig, &old_sig); + + for (cur_pos = 0; cur_pos < signal_queue_len; cur_pos++) { + sig = signal_queue[cur_pos]; + desc = &signal_state[sig]; + if (desc->count) { + if (desc->handler) + desc->handler(sig); + desc->count = 0; + } + } + signal_queue_len = 0; + + /* restore signal delivery */ + sigprocmask(SIG_SETMASK, &old_sig, NULL); +}