blob: 9825c0d2ed4e0439636ae48dd57dda3f4471d5e0 [file] [log] [blame]
Willy Tarreau8f38bd02009-05-10 08:53:33 +02001/*
2 * Asynchronous signal delivery functions.
3 *
4 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <signal.h>
Willy Tarreaube8c7362009-07-23 13:40:20 +020014#include <string.h>
15
Willy Tarreau8f38bd02009-05-10 08:53:33 +020016#include <proto/signal.h>
17#include <proto/log.h>
18
19/* Principle : we keep an in-order list of the first occurrence of all received
20 * signals. All occurrences of a same signal are grouped though. The signal
21 * queue does not need to be deeper than the number of signals we can handle.
22 * The handlers will be called asynchronously with the signal number. They can
23 * check themselves the number of calls by checking the descriptor this signal.
24 */
25
26int signal_queue_len; /* length of signal queue, <= MAX_SIGNAL (1 entry per signal max) */
27int signal_queue[MAX_SIGNAL]; /* in-order queue of received signals */
28struct signal_descriptor signal_state[MAX_SIGNAL];
29sigset_t blocked_sig;
30
31void signal_init()
32{
33 signal_queue_len = 0;
34 memset(signal_queue, 0, sizeof(signal_queue));
35 memset(signal_state, 0, sizeof(signal_state));
36 sigfillset(&blocked_sig);
37}
38
39void signal_handler(int sig)
40{
41 if (sig < 0 || sig > MAX_SIGNAL || !signal_state[sig].handler) {
42 /* unhandled signal */
43 qfprintf(stderr, "Received unhandled signal %d. Signal has been disabled.\n", sig);
44 signal(sig, SIG_IGN);
45 return;
46 }
47
48 if (!signal_state[sig].count) {
49 /* signal was not queued yet */
50 if (signal_queue_len < MAX_SIGNAL)
51 signal_queue[signal_queue_len++] = sig;
52 else
53 qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig);
54 }
55 signal_state[sig].count++;
56 signal(sig, signal_handler); /* re-arm signal */
57}
58
59/* Register a handler for signal <sig>. Set it to NULL, SIG_DFL or SIG_IGN to
60 * remove the handler. The signal's queue is flushed and the signal is really
61 * registered (or unregistered) for the process. The interface is the same as
62 * for standard signal delivery, except that the handler does not need to rearm
63 * the signal itself (it can disable it however).
64 */
65void signal_register(int sig, void (*handler)(int))
66{
67 if (sig < 0 || sig > MAX_SIGNAL) {
68 qfprintf(stderr, "Failed to register signal %d : out of range [0..%d].\n", sig, MAX_SIGNAL);
69 return;
70 }
71
72 signal_state[sig].count = 0;
73 if (handler == NULL)
74 handler = SIG_IGN;
75
76 if (handler != SIG_IGN && handler != SIG_DFL) {
77 signal_state[sig].handler = handler;
78 signal(sig, signal_handler);
79 }
80 else {
81 signal_state[sig].handler = NULL;
82 signal(sig, handler);
83 }
84}
85
86/* Call handlers of all pending signals and clear counts and queue length. The
87 * handlers may unregister themselves by calling signal_register() while they
88 * are called, just like it is done with normal signal handlers.
89 * Note that it is more efficient to call the inline version which checks the
90 * queue length before getting here.
91 */
92void __signal_process_queue()
93{
94 int sig, cur_pos = 0;
95 struct signal_descriptor *desc;
96 sigset_t old_sig;
97
98 /* block signal delivery during processing */
99 sigprocmask(SIG_SETMASK, &blocked_sig, &old_sig);
100
101 for (cur_pos = 0; cur_pos < signal_queue_len; cur_pos++) {
102 sig = signal_queue[cur_pos];
103 desc = &signal_state[sig];
104 if (desc->count) {
105 if (desc->handler)
106 desc->handler(sig);
107 desc->count = 0;
108 }
109 }
110 signal_queue_len = 0;
111
112 /* restore signal delivery */
113 sigprocmask(SIG_SETMASK, &old_sig, NULL);
114}