| /* |
| * Asynchronous signal delivery functions. |
| * |
| * Copyright 2000-2009 Willy Tarreau <w@1wt.eu> |
| * |
| * 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 <signal.h> |
| #include <string.h> |
| |
| #include <proto/signal.h> |
| #include <proto/log.h> |
| |
| /* 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 <sig>. 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); |
| } |