Coverage Report

Created: 2026-08-19 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/signal.c
Line
Count
Source
1
/*
2
 * Asynchronous signal delivery functions.
3
 *
4
 * Copyright 2000-2010 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>
14
#include <string.h>
15
16
#include <haproxy/errors.h>
17
#include <haproxy/signal.h>
18
#include <haproxy/task.h>
19
20
/* Principle : we keep an in-order list of the first occurrence of all received
21
 * signals. All occurrences of a same signal are grouped though. The signal
22
 * queue does not need to be deeper than the number of signals we can handle.
23
 * The handlers will be called asynchronously with the signal number. They can
24
 * check themselves the number of calls by checking the descriptor this signal.
25
 */
26
27
int signal_queue_len; /* length of signal queue, <= MAX_SIGNAL (1 entry per signal max) */
28
int signal_queue[MAX_SIGNAL];                     /* in-order queue of received signals */
29
struct signal_descriptor signal_state[MAX_SIGNAL];
30
sigset_t blocked_sig;
31
int signal_pending = 0; /* non-zero if t least one signal remains unprocessed */
32
33
DECLARE_STATIC_TYPED_POOL(pool_head_sig_handlers, "sig_handlers", struct sig_handler);
34
35
/* Common signal handler, used by all signals. Received signals are queued.
36
 * Signal number zero has a specific status, as it cannot be delivered by the
37
 * system, any function may call it to perform asynchronous signal delivery.
38
 */
39
void signal_handler(int sig)
40
0
{
41
  /* inform callees to be careful, we're in a signal handler! */
42
0
  _HA_ATOMIC_OR(&th_ctx->flags, TH_FL_IN_SIG_HANDLER);
43
44
0
  if (sig < 0 || sig >= MAX_SIGNAL) {
45
    /* unhandled signal */
46
0
    signal(sig, SIG_IGN);
47
0
    qfprintf(stderr, "Received unhandled signal %d. Signal has been disabled.\n", sig);
48
0
    _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_IN_SIG_HANDLER);
49
0
    return;
50
0
  }
51
52
0
  if (!signal_state[sig].count) {
53
    /* signal was not queued yet */
54
0
    if (signal_queue_len < MAX_SIGNAL)
55
0
      signal_queue[signal_queue_len++] = sig;
56
0
    else
57
0
      qfprintf(stderr, "Signal %d : signal queue is unexpectedly full.\n", sig);
58
0
  }
59
60
0
  signal_state[sig].count++;
61
0
  if (sig)
62
0
    signal(sig, signal_handler); /* re-arm signal */
63
64
  /* If the thread is TH_FL_SLEEPING we need to wake it */
65
0
  wake_thread(tid);
66
0
  _HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_IN_SIG_HANDLER);
67
0
}
68
69
/* Call handlers of all pending signals and clear counts and queue length. The
70
 * handlers may unregister themselves by calling signal_register() while they
71
 * are called, just like it is done with normal signal handlers.
72
 * Note that it is more efficient to call the inline version which checks the
73
 * queue length before getting here.
74
 */
75
void __signal_process_queue()
76
0
{
77
0
  int sig, cur_pos = 0;
78
0
  struct signal_descriptor *desc;
79
0
  sigset_t old_sig;
80
81
  /* block signal delivery during processing */
82
0
  ha_sigmask(SIG_SETMASK, &blocked_sig, &old_sig);
83
84
  /* It is important that we scan the queue forwards so that we can
85
   * catch any signal that would have been queued by another signal
86
   * handler. That allows real signal handlers to redistribute signals
87
   * to tasks subscribed to signal zero.
88
   */
89
0
  for (cur_pos = 0; cur_pos < signal_queue_len; cur_pos++) {
90
0
    sig  = signal_queue[cur_pos];
91
0
    desc = &signal_state[sig];
92
0
    if (desc->count) {
93
0
      struct sig_handler *sh, *shb;
94
0
      list_for_each_entry_safe(sh, shb, &desc->handlers, list) {
95
0
        if ((sh->flags & SIG_F_TYPE_FCT) && sh->handler)
96
0
          ((void (*)(struct sig_handler *))sh->handler)(sh);
97
0
        else if ((sh->flags & SIG_F_TYPE_TASK) && sh->handler)
98
0
          task_wakeup(sh->handler, TASK_WOKEN_SIGNAL);
99
0
      }
100
0
      desc->count = 0;
101
0
    }
102
0
  }
103
0
  signal_queue_len = 0;
104
105
  /* restore signal delivery */
106
0
  ha_sigmask(SIG_SETMASK, &old_sig, NULL);
107
0
}
108
109
/* perform minimal initializations */
110
static void signal_init()
111
0
{
112
0
  int sig;
113
114
  /* Need to register the handler for SIGINT explicitly, as we can be
115
   * laucned within the subshell and at background:
116
   * $ (./haproxy -f env4.cfg &). According to POSIX standard
117
   * (2.11. Signals and Error Handling), we will inherit from the subshell
118
   * in this case SIG_IGN signal handler for SIGINT and SIGQUIT.
119
   */
120
0
  signal(SIGINT, SIG_DFL);
121
0
  signal_queue_len = 0;
122
0
  memset(signal_queue, 0, sizeof(signal_queue));
123
0
  memset(signal_state, 0, sizeof(signal_state));
124
125
0
  sigfillset(&blocked_sig);
126
0
  sigdelset(&blocked_sig, SIGPROF);
127
  /* man sigprocmask: If SIGBUS, SIGFPE, SIGILL, or SIGSEGV are
128
     generated while they are blocked, the result is undefined, unless
129
     the signal was generated by kill(2),
130
     sigqueue(3), or raise(3).
131
     Do not ignore WDTSIG or DEBUGSIG either, or it may deadlock the
132
     watchdog */
133
0
  sigdelset(&blocked_sig, SIGBUS);
134
0
  sigdelset(&blocked_sig, SIGFPE);
135
0
  sigdelset(&blocked_sig, SIGILL);
136
0
  sigdelset(&blocked_sig, SIGSEGV);
137
#ifdef DEBUGSIG
138
  sigdelset(&blocked_sig, DEBUGSIG);
139
#endif
140
#ifdef WDTSIG
141
  sigdelset(&blocked_sig, WDTSIG);
142
#endif
143
0
  for (sig = 0; sig < MAX_SIGNAL; sig++)
144
0
    LIST_INIT(&signal_state[sig].handlers);
145
0
}
146
147
/*
148
 * This function should be called to unblock all signals
149
 */
150
void haproxy_unblock_signals()
151
0
{
152
0
  sigset_t set;
153
154
  /* Ensure signals are not blocked. Some shells or service managers may
155
   * accidentally block all of our signals unfortunately, causing lots of
156
   * zombie processes to remain in the background during reloads.
157
   */
158
0
  sigemptyset(&set);
159
0
  ha_sigmask(SIG_SETMASK, &set, NULL);
160
0
}
161
162
/* releases all registered signal handlers */
163
void deinit_signals()
164
0
{
165
0
  int sig;
166
0
  struct sig_handler *sh, *shb;
167
168
0
  for (sig = 0; sig < MAX_SIGNAL; sig++) {
169
0
    if (sig != SIGPROF)
170
0
      signal(sig, SIG_DFL);
171
0
    list_for_each_entry_safe(sh, shb, &signal_state[sig].handlers, list) {
172
0
      LIST_DELETE(&sh->list);
173
0
      pool_free(pool_head_sig_handlers, sh);
174
0
    }
175
0
  }
176
0
}
177
178
/* Register a function and an integer argument on a signal. A pointer to the
179
 * newly allocated sig_handler is returned, or NULL in case of any error. The
180
 * caller is responsible for unregistering the function when not used anymore.
181
 * Note that passing a NULL as the function pointer enables interception of the
182
 * signal without processing, which is identical to SIG_IGN. If the signal is
183
 * zero (which the system cannot deliver), only internal functions will be able
184
 * to notify the registered functions.
185
 */
186
struct sig_handler *signal_register_fct(int sig, void (*fct)(struct sig_handler *), int arg)
187
0
{
188
0
  struct sig_handler *sh;
189
190
0
  if (sig < 0 || sig >= MAX_SIGNAL)
191
0
    return NULL;
192
193
0
  if (sig)
194
0
    signal(sig, fct ? signal_handler : SIG_IGN);
195
196
0
  if (!fct)
197
0
    return NULL;
198
199
0
  sh = pool_alloc(pool_head_sig_handlers);
200
0
  if (!sh)
201
0
    return NULL;
202
203
0
  sh->handler = fct;
204
0
  sh->arg = arg;
205
0
  sh->flags = SIG_F_TYPE_FCT;
206
0
  LIST_APPEND(&signal_state[sig].handlers, &sh->list);
207
0
  return sh;
208
0
}
209
210
/* Register a task and a wake-up reason on a signal. A pointer to the newly
211
 * allocated sig_handler is returned, or NULL in case of any error. The caller
212
 * is responsible for unregistering the task when not used anymore. Note that
213
 * passing a NULL as the task pointer enables interception of the signal
214
 * without processing, which is identical to SIG_IGN. If the signal is zero
215
 * (which the system cannot deliver), only internal functions will be able to
216
 * notify the registered functions.
217
 */
218
struct sig_handler *signal_register_task(int sig, struct task *task, int reason)
219
0
{
220
0
  struct sig_handler *sh;
221
222
0
  if (sig < 0 || sig >= MAX_SIGNAL)
223
0
    return NULL;
224
225
0
  if (sig)
226
0
    signal(sig, signal_handler);
227
228
0
  if (!task)
229
0
    return NULL;
230
231
0
  sh = pool_alloc(pool_head_sig_handlers);
232
0
  if (!sh)
233
0
    return NULL;
234
235
0
  sh->handler = task;
236
0
  sh->arg = reason & ~TASK_WOKEN_ANY;
237
0
  sh->flags = SIG_F_TYPE_TASK;
238
0
  LIST_APPEND(&signal_state[sig].handlers, &sh->list);
239
0
  return sh;
240
0
}
241
242
/* Immediately unregister a handler so that no further signals may be delivered
243
 * to it. The struct is released so the caller may not reference it anymore.
244
 */
245
void signal_unregister_handler(struct sig_handler *handler)
246
0
{
247
0
  LIST_DELETE(&handler->list);
248
0
  pool_free(pool_head_sig_handlers, handler);
249
0
}
250
251
/* Immediately unregister a handler so that no further signals may be delivered
252
 * to it. The handler struct does not need to be known, only the function or
253
 * task pointer. This method is expensive because it scans all the list, so it
254
 * should only be used for rare cases (eg: exit). The struct is released so the
255
 * caller may not reference it anymore.
256
 */
257
void signal_unregister_target(int sig, void *target)
258
0
{
259
0
  struct sig_handler *sh, *shb;
260
261
0
  if (sig < 0 || sig >= MAX_SIGNAL)
262
0
    return;
263
264
0
  if (!target)
265
0
    return;
266
267
0
  list_for_each_entry_safe(sh, shb, &signal_state[sig].handlers, list) {
268
0
    if (sh->handler == target) {
269
0
      LIST_DELETE(&sh->list);
270
0
      pool_free(pool_head_sig_handlers, sh);
271
0
      break;
272
0
    }
273
0
  }
274
0
}
275
276
/*
277
 * Immedialtely unregister every handler assigned to a signal <sig>.
278
 * Once the handler list is empty, the signal is ignored with SIG_IGN.
279
 */
280
281
void signal_unregister(int sig)
282
0
{
283
0
  struct sig_handler *sh, *shb;
284
285
0
  if (sig < 0 || sig >= MAX_SIGNAL)
286
0
    return;
287
288
0
  list_for_each_entry_safe(sh, shb, &signal_state[sig].handlers, list) {
289
0
    LIST_DELETE(&sh->list);
290
0
    pool_free(pool_head_sig_handlers, sh);
291
0
  }
292
293
  signal(sig, SIG_IGN);
294
0
}
295
296
INITCALL0(STG_PREPARE, signal_init);