blob: da10c2f7f735bf0be31751be5d55d57857e7ca50 [file] [log] [blame]
William Lallemandeba6a542022-09-26 12:54:39 +02001#include <sys/mman.h>
2#include <sys/stat.h>
3#include <fcntl.h>
Amaury Denoyellece986e12021-06-04 11:20:32 +02004#include <stdarg.h>
5#include <stdio.h>
William Lallemandeba6a542022-09-26 12:54:39 +02006#include <stdlib.h>
Amaury Denoyellece986e12021-06-04 11:20:32 +02007#include <syslog.h>
8
9#include <haproxy/api.h>
10#include <haproxy/applet-t.h>
Amaury Denoyelle1833e432021-05-26 11:05:22 +020011#include <haproxy/buf.h>
Amaury Denoyellece986e12021-06-04 11:20:32 +020012#include <haproxy/cli.h>
13#include <haproxy/errors.h>
14#include <haproxy/global.h>
Amaury Denoyelle816281f2021-05-27 15:46:19 +020015#include <haproxy/obj_type.h>
Amaury Denoyellece986e12021-06-04 11:20:32 +020016#include <haproxy/ring.h>
17#include <haproxy/tools.h>
18#include <haproxy/version.h>
19
20/* A global buffer used to store all startup alerts/warnings. It will then be
21 * retrieve on the CLI. */
William Lallemandeba6a542022-09-26 12:54:39 +020022struct ring *startup_logs = NULL;
23#ifdef USE_SHM_OPEN
24static struct ring *shm_startup_logs = NULL;
25#endif
Amaury Denoyellece986e12021-06-04 11:20:32 +020026
Amaury Denoyelle1833e432021-05-26 11:05:22 +020027/* A thread local buffer used to store all alerts/warnings. It can be used to
28 * retrieve them for CLI commands after startup.
29 */
30#define USER_MESSAGES_BUFSIZE 1024
31static THREAD_LOCAL struct buffer usermsgs_buf = BUF_NULL;
32
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020033/* A thread local context used for stderr output via ha_alert/warning/notice/diag.
34 */
Amaury Denoyelle846830e2021-06-07 19:24:10 +020035#define USERMSGS_CTX_BUFSIZE PATH_MAX
36static THREAD_LOCAL struct usermsgs_ctx usermsgs_ctx = { .str = BUF_NULL, };
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020037
William Lallemandeba6a542022-09-26 12:54:39 +020038#ifdef USE_SHM_OPEN
39
40/* initialise an SHM for the startup logs and return its fd */
41static int startup_logs_new_shm()
42{
43 char *path = NULL;
44 int fd = -1;
45 int flags;
46
47 /* create a unique path per PID so we don't collide with another
48 process */
49 memprintf(&path, "/haproxy_startup_logs_%d", getpid());
50 fd = shm_open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
51 if (fd == -1)
52 goto error;
53 shm_unlink(path);
54 ha_free(&path);
55
56 if (ftruncate(fd, STARTUP_LOG_SIZE) == -1)
57 goto error;
58
59 flags = fcntl(fd, F_GETFD);
60 if (flags == -1)
61 goto error;
62 flags &= ~FD_CLOEXEC;
63 flags = fcntl(fd, F_SETFD, flags);
64 if (flags == -1)
65 goto error;
66
67 return fd;
68error:
69 if (fd != -1) {
70 close(fd);
71 fd = -1;
72 }
73 return fd;
74}
75
76/* mmap a startup-logs from a <fd>.
77 * if <new> is set to one, initialize the buffer.
78 * Returns the ring.
79 */
80static struct ring *startup_logs_from_fd(int fd, int new)
81{
82 char *area;
83 struct ring *r = NULL;
84
85 if (fd == -1)
86 goto error;
87
88 area = mmap(NULL, STARTUP_LOG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
89 if (area == MAP_FAILED || area == NULL)
90 goto error;
91
92 if (new)
93 r = ring_make_from_area(area, STARTUP_LOG_SIZE);
94 else
95 r = ring_cast_from_area(area);
96
97 if (r == NULL)
98 goto error;
99
100 shm_startup_logs = r; /* save the ptr so we can unmap later */
101
102 return r;
103error:
104 return NULL;
105}
106
107/*
Ilya Shipitsin4a689da2022-10-29 09:34:32 +0500108 * Use a shm across reexec of the master.
William Lallemandeba6a542022-09-26 12:54:39 +0200109 *
110 * During the startup of the master, a shm_open must be done and the FD saved
111 * into the HAPROXY_STARTUPLOGS_FD environment variable.
112 *
113 * When forking workers, the child must use a copy of the shm, not the shm itself.
114 *
115 * Once in wait mode, the shm must be copied and closed.
116 *
117 */
118void startup_logs_init()
119{
120 struct ring *r = NULL;
121 char *str_fd, *endptr;
122 int fd = -1;
123
124 str_fd = getenv("HAPROXY_STARTUPLOGS_FD");
125 if (str_fd) {
126 fd = strtol(str_fd, &endptr, 10);
127 if (*endptr != '\0')
128 goto error;
129 unsetenv("HAPROXY_STARTUPLOGS_FD");
130 }
131
132 /* during startup, or just after a reload.
133 * Note: the WAIT_ONLY env variable must be
134 * check in case of an early call */
135 if (!(global.mode & MODE_MWORKER_WAIT) &&
136 getenv("HAPROXY_MWORKER_WAIT_ONLY") == NULL) {
137 if (fd != -1)
138 close(fd);
139
140 fd = startup_logs_new_shm();
141 if (fd == -1)
142 goto error;
143
144 r = startup_logs_from_fd(fd, 1);
145 if (!r)
146 goto error;
147
Aurelien DARRAGONb28ded12023-04-05 16:18:40 +0200148 str_fd = NULL;
William Lallemandeba6a542022-09-26 12:54:39 +0200149 memprintf(&str_fd, "%d", fd);
150 setenv("HAPROXY_STARTUPLOGS_FD", str_fd, 1);
151 ha_free(&str_fd);
152
153 } else {
154 /* in wait mode, copy the shm to an allocated buffer */
155 struct ring *prev = NULL;
156
157 if (fd == -1)
158 goto error;
159
160 prev = startup_logs_from_fd(fd, 0);
161 if (!prev)
162 goto error;
163
164 r = startup_logs_dup(prev);
165 if (!r)
166 goto error;
167 startup_logs_free(prev);
168 close(fd);
169 }
170
171 startup_logs = r;
172
173 return;
174error:
175 if (fd != -1)
176 close(fd);
177 /* couldn't get a mmap to work */
178 startup_logs = ring_new(STARTUP_LOG_SIZE);
179
180}
181
182#else /* ! USE_SHM_OPEN */
183
184void startup_logs_init()
185{
186 startup_logs = ring_new(STARTUP_LOG_SIZE);
187}
188
189#endif
190
191/* free the startup logs, unmap if it was an shm */
192void startup_logs_free(struct ring *r)
193{
194#ifdef USE_SHM_OPEN
195 if (r == shm_startup_logs)
196 munmap(r, STARTUP_LOG_SIZE);
197 else
198#endif /* ! USE_SHM_OPEN */
199 ring_free(r);
200}
201
202/* duplicate a startup logs which was previously allocated in a shm */
203struct ring *startup_logs_dup(struct ring *src)
204{
205 struct ring *dst = NULL;
206
207 /* must use the size of the previous buffer */
208 dst = ring_new(b_size(&src->buf));
209 if (!dst)
210 goto error;
211
212 b_reset(&dst->buf);
213 b_ncat(&dst->buf, &src->buf, b_data(&src->buf));
214error:
215 return dst;
216}
217
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200218/* Put msg in usermsgs_buf.
219 *
220 * The message should not be terminated by a newline because this function
221 * manually insert it.
222 *
223 * If there is not enough room in the buffer, the message is silently discarded.
224 * Do not forget to frequently clear the buffer.
225 */
226static void usermsgs_put(const struct ist *msg)
227{
228 /* Allocate the buffer if not already done. */
229 if (unlikely(b_is_null(&usermsgs_buf))) {
230 usermsgs_buf.area = malloc(USER_MESSAGES_BUFSIZE * sizeof(char));
231 usermsgs_buf.size = USER_MESSAGES_BUFSIZE;
232 }
233
234 if (likely(!b_is_null(&usermsgs_buf))) {
235 if (b_room(&usermsgs_buf) >= msg->len + 2) {
236 /* Insert the message + newline. */
237 b_putblk(&usermsgs_buf, msg->ptr, msg->len);
238 b_putchr(&usermsgs_buf, '\n');
239 /* Insert NUL outside of the buffer. */
240 *b_tail(&usermsgs_buf) = '\0';
241 }
242 }
243}
244
Amaury Denoyelle6af81f82021-05-27 15:45:28 +0200245/* Clear the user messages log buffer.
246 *
247 * <prefix> will set the local-thread context appended to every output
248 * following this call. It can be NULL if not necessary.
249 */
250void usermsgs_clr(const char *prefix)
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200251{
252 if (likely(!b_is_null(&usermsgs_buf))) {
253 b_reset(&usermsgs_buf);
254 usermsgs_buf.area[0] = '\0';
255 }
Amaury Denoyelle6af81f82021-05-27 15:45:28 +0200256
257 usermsgs_ctx.prefix = prefix;
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200258}
259
260/* Check if the user messages buffer is empty. */
261int usermsgs_empty(void)
262{
263 return !!(b_is_null(&usermsgs_buf) || !b_data(&usermsgs_buf));
264}
265
266/* Return the messages log buffer content. */
267const char *usermsgs_str(void)
268{
269 if (unlikely(b_is_null(&usermsgs_buf)))
270 return "";
271
272 return b_head(&usermsgs_buf);
273}
274
Amaury Denoyelle6af81f82021-05-27 15:45:28 +0200275/* Set thread-local context infos to prefix forthcoming stderr output during
276 * configuration parsing.
277 *
278 * <file> and <line> specify the location of the parsed configuration.
279 *
280 * <obj> can be of various types. If not NULL, the string prefix generated will
281 * depend on its type.
282 */
283void set_usermsgs_ctx(const char *file, int line, enum obj_type *obj)
284{
285 usermsgs_ctx.file = file;
286 usermsgs_ctx.line = line;
287 usermsgs_ctx.obj = obj;
288}
289
290/* Set thread-local context infos to prefix forthcoming stderr output. It will
291 * be set as a complement to possibly already defined file/line.
292 *
293 * <obj> can be of various types. If not NULL, the string prefix generated will
294 * depend on its type.
295 */
296void register_parsing_obj(enum obj_type *obj)
297{
298 usermsgs_ctx.obj = obj;
299}
300
301/* Reset thread-local context infos for stderr output. */
302void reset_usermsgs_ctx(void)
303{
304 usermsgs_ctx.file = NULL;
305 usermsgs_ctx.line = 0;
306 usermsgs_ctx.obj = NULL;
307}
308
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200309static void generate_usermsgs_ctx_str(void)
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200310{
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200311 struct usermsgs_ctx *ctx = &usermsgs_ctx;
312 void *area;
313 int ret;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200314
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200315 if (unlikely(b_is_null(&ctx->str))) {
Tim Duesterhusec4a8752021-09-15 13:58:46 +0200316 area = calloc(USERMSGS_CTX_BUFSIZE, sizeof(*area));
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200317 if (area)
318 ctx->str = b_make(area, USERMSGS_CTX_BUFSIZE, 0, 0);
319 }
320
321 if (likely(!b_is_null(&ctx->str))) {
322 b_reset(&ctx->str);
323
324 if (ctx->prefix) {
325 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
326 "%s : ", ctx->prefix);
327 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
328 }
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200329
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200330 if (ctx->file) {
331 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
332 "[%s:%d] : ", ctx->file, ctx->line);
333 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
334 }
335
336 switch (obj_type(ctx->obj)) {
337 case OBJ_TYPE_SERVER:
338 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
339 "'server %s/%s' : ",
340 __objt_server(ctx->obj)->proxy->id,
341 __objt_server(ctx->obj)->id);
342 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
343 break;
344
345 case OBJ_TYPE_NONE:
346 default:
347 break;
348 }
Amaury Denoyelled0b237c2021-05-28 10:36:56 +0200349
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200350 if (!b_data(&ctx->str))
351 snprintf(b_tail(&ctx->str), b_room(&ctx->str), "%s", "");
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200352 }
353}
354
Amaury Denoyellece986e12021-06-04 11:20:32 +0200355/* Generic function to display messages prefixed by a label */
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200356static void print_message(int use_usermsgs_ctx, const char *label, const char *fmt, va_list argp)
Amaury Denoyellece986e12021-06-04 11:20:32 +0200357{
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200358 struct ist msg_ist = IST_NULL;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200359 char *head, *parsing_str, *msg;
Amaury Denoyellece986e12021-06-04 11:20:32 +0200360 char prefix[11]; // '[' + 8 chars + ']' + 0.
361
362 *prefix = '[';
363 strncpy(prefix + 1, label, sizeof(prefix) - 2);
364 msg = prefix + strlen(prefix);
365 *msg++ = ']';
366 while (msg < prefix + sizeof(prefix) - 1)
367 *msg++ = ' ';
368 *msg = 0;
369
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200370 head = parsing_str = msg = NULL;
Amaury Denoyellece986e12021-06-04 11:20:32 +0200371 memprintf(&head, "%s (%u) : ", prefix, (uint)getpid());
372 memvprintf(&msg, fmt, argp);
373
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200374 /* trim the trailing '\n' */
375 msg_ist = ist(msg);
376 if (msg_ist.len > 0 && msg_ist.ptr[msg_ist.len - 1] == '\n')
377 msg_ist.len--;
378
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200379 if (use_usermsgs_ctx) {
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200380 generate_usermsgs_ctx_str();
381 parsing_str = b_head(&usermsgs_ctx.str);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200382 reset_usermsgs_ctx();
383 }
384 else {
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200385 parsing_str = "";
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200386 }
387
Amaury Denoyellece986e12021-06-04 11:20:32 +0200388 if (global.mode & MODE_STARTING) {
389 if (unlikely(!startup_logs))
William Lallemandeba6a542022-09-26 12:54:39 +0200390 startup_logs_init();
Amaury Denoyellece986e12021-06-04 11:20:32 +0200391
392 if (likely(startup_logs)) {
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200393 struct ist m[3];
Amaury Denoyellece986e12021-06-04 11:20:32 +0200394
395 m[0] = ist(head);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200396 m[1] = ist(parsing_str);
397 m[2] = msg_ist;
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200398
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200399 ring_write(startup_logs, ~0, 0, 0, m, 3);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200400 }
401 }
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200402 else {
403 usermsgs_put(&msg_ist);
404 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200405
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200406 fprintf(stderr, "%s%s%s", head, parsing_str, msg);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200407 fflush(stderr);
408
409 free(head);
410 free(msg);
411}
412
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200413static void print_message_args(int use_usermsgs_ctx, const char *label, const char *fmt, ...)
414{
415 va_list argp;
416 va_start(argp, fmt);
417 print_message(use_usermsgs_ctx, label, fmt, argp);
418 va_end(argp);
419}
420
Amaury Denoyellece986e12021-06-04 11:20:32 +0200421/*
Aurelien DARRAGON88687f02023-04-04 22:04:35 +0200422 * Displays the message on stderr with the pid. Overrides the quiet
Amaury Denoyellece986e12021-06-04 11:20:32 +0200423 * mode during startup.
424 */
425void ha_alert(const char *fmt, ...)
426{
427 va_list argp;
428
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200429 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
430 !(global.mode & MODE_STARTING)) {
431 if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) {
Amaury Denoyellece986e12021-06-04 11:20:32 +0200432 const char *path = get_exec_path();
433
434 warned |= WARN_EXEC_PATH;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200435 print_message_args(0, "NOTICE", "haproxy version is %s\n", haproxy_version);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200436 if (path)
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200437 print_message_args(0, "NOTICE", "path to executable is %s\n", path);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200438 }
439 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200440 print_message(1, "ALERT", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200441 va_end(argp);
442 }
443}
444
445/*
Aurelien DARRAGON88687f02023-04-04 22:04:35 +0200446 * Displays the message on stderr with the pid.
Amaury Denoyellece986e12021-06-04 11:20:32 +0200447 */
448void ha_warning(const char *fmt, ...)
449{
450 va_list argp;
451
452 warned |= WARN_ANY;
453
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200454 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
455 !(global.mode & MODE_STARTING)) {
Amaury Denoyelleda3d6812021-05-28 09:57:10 +0200456 if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) {
457 const char *path = get_exec_path();
458
459 warned |= WARN_EXEC_PATH;
460 print_message_args(0, "NOTICE", "haproxy version is %s\n", haproxy_version);
461 if (path)
462 print_message_args(0, "NOTICE", "path to executable is %s\n", path);
463 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200464 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200465 print_message(1, "WARNING", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200466 va_end(argp);
467 }
468}
469
470/*
471 * Variant of _ha_diag_warning with va_list.
472 * Use it only if MODE_DIAG has been previously checked.
473 */
474void _ha_vdiag_warning(const char *fmt, va_list argp)
475{
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200476 print_message(1, "DIAG", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200477}
478
479/*
480 * Output a diagnostic warning.
481 * Use it only if MODE_DIAG has been previously checked.
482 */
483void _ha_diag_warning(const char *fmt, ...)
484{
485 va_list argp;
486
487 va_start(argp, fmt);
488 _ha_vdiag_warning(fmt, argp);
489 va_end(argp);
490}
491
492/*
493 * Output a diagnostic warning. Do nothing of MODE_DIAG is not on.
494 */
495void ha_diag_warning(const char *fmt, ...)
496{
497 va_list argp;
498
499 if (global.mode & MODE_DIAG) {
500 va_start(argp, fmt);
501 _ha_vdiag_warning(fmt, argp);
502 va_end(argp);
503 }
504}
505
506/*
Aurelien DARRAGON88687f02023-04-04 22:04:35 +0200507 * Displays the message on stderr with the pid.
Amaury Denoyellece986e12021-06-04 11:20:32 +0200508 */
509void ha_notice(const char *fmt, ...)
510{
511 va_list argp;
512
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200513 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
514 !(global.mode & MODE_STARTING)) {
Amaury Denoyellece986e12021-06-04 11:20:32 +0200515 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200516 print_message(1, "NOTICE", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200517 va_end(argp);
518 }
519}
520
521/*
522 * Displays the message on <out> only if quiet mode is not set.
523 */
524void qfprintf(FILE *out, const char *fmt, ...)
525{
526 va_list argp;
527
528 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
529 va_start(argp, fmt);
530 vfprintf(out, fmt, argp);
531 fflush(out);
532 va_end(argp);
533 }
534}
535
536
537/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
538static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
539{
540 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
541 return 1;
542
543 if (!startup_logs)
544 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
545
Willy Tarreaucba88382022-05-05 15:18:57 +0200546 return ring_attach_cli(startup_logs, appctx, 0);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200547}
548
549/* register cli keywords */
550static struct cli_kw_list cli_kws = {{ },{
William Lallemandeba6a542022-09-26 12:54:39 +0200551 { { "show", "startup-logs", NULL }, "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL, NULL, ACCESS_MASTER },
Amaury Denoyellece986e12021-06-04 11:20:32 +0200552 {{},}
553}};
554
555INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
556
557
558static void deinit_errors_buffers()
559{
560 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200561 ha_free(&usermsgs_buf.area);
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200562 ha_free(&usermsgs_ctx.str.area);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200563}
564
Willy Tarreau032e7002022-04-27 17:50:53 +0200565/* errors might be used in threads and even before forking, thus 2 deinit */
Amaury Denoyellece986e12021-06-04 11:20:32 +0200566REGISTER_PER_THREAD_FREE(deinit_errors_buffers);
Willy Tarreau032e7002022-04-27 17:50:53 +0200567REGISTER_POST_DEINIT(deinit_errors_buffers);