blob: 8221adb0e0d40b44929c666a1599d9c143f2a207 [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
148 memprintf(&str_fd, "%d", fd);
149 setenv("HAPROXY_STARTUPLOGS_FD", str_fd, 1);
150 ha_free(&str_fd);
151
152 } else {
153 /* in wait mode, copy the shm to an allocated buffer */
154 struct ring *prev = NULL;
155
156 if (fd == -1)
157 goto error;
158
159 prev = startup_logs_from_fd(fd, 0);
160 if (!prev)
161 goto error;
162
163 r = startup_logs_dup(prev);
164 if (!r)
165 goto error;
166 startup_logs_free(prev);
167 close(fd);
168 }
169
170 startup_logs = r;
171
172 return;
173error:
174 if (fd != -1)
175 close(fd);
176 /* couldn't get a mmap to work */
177 startup_logs = ring_new(STARTUP_LOG_SIZE);
178
179}
180
181#else /* ! USE_SHM_OPEN */
182
183void startup_logs_init()
184{
185 startup_logs = ring_new(STARTUP_LOG_SIZE);
186}
187
188#endif
189
190/* free the startup logs, unmap if it was an shm */
191void startup_logs_free(struct ring *r)
192{
193#ifdef USE_SHM_OPEN
194 if (r == shm_startup_logs)
195 munmap(r, STARTUP_LOG_SIZE);
196 else
197#endif /* ! USE_SHM_OPEN */
198 ring_free(r);
199}
200
201/* duplicate a startup logs which was previously allocated in a shm */
202struct ring *startup_logs_dup(struct ring *src)
203{
204 struct ring *dst = NULL;
205
206 /* must use the size of the previous buffer */
207 dst = ring_new(b_size(&src->buf));
208 if (!dst)
209 goto error;
210
211 b_reset(&dst->buf);
212 b_ncat(&dst->buf, &src->buf, b_data(&src->buf));
213error:
214 return dst;
215}
216
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200217/* Put msg in usermsgs_buf.
218 *
219 * The message should not be terminated by a newline because this function
220 * manually insert it.
221 *
222 * If there is not enough room in the buffer, the message is silently discarded.
223 * Do not forget to frequently clear the buffer.
224 */
225static void usermsgs_put(const struct ist *msg)
226{
227 /* Allocate the buffer if not already done. */
228 if (unlikely(b_is_null(&usermsgs_buf))) {
229 usermsgs_buf.area = malloc(USER_MESSAGES_BUFSIZE * sizeof(char));
230 usermsgs_buf.size = USER_MESSAGES_BUFSIZE;
231 }
232
233 if (likely(!b_is_null(&usermsgs_buf))) {
234 if (b_room(&usermsgs_buf) >= msg->len + 2) {
235 /* Insert the message + newline. */
236 b_putblk(&usermsgs_buf, msg->ptr, msg->len);
237 b_putchr(&usermsgs_buf, '\n');
238 /* Insert NUL outside of the buffer. */
239 *b_tail(&usermsgs_buf) = '\0';
240 }
241 }
242}
243
Amaury Denoyelle6af81f82021-05-27 15:45:28 +0200244/* Clear the user messages log buffer.
245 *
246 * <prefix> will set the local-thread context appended to every output
247 * following this call. It can be NULL if not necessary.
248 */
249void usermsgs_clr(const char *prefix)
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200250{
251 if (likely(!b_is_null(&usermsgs_buf))) {
252 b_reset(&usermsgs_buf);
253 usermsgs_buf.area[0] = '\0';
254 }
Amaury Denoyelle6af81f82021-05-27 15:45:28 +0200255
256 usermsgs_ctx.prefix = prefix;
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200257}
258
259/* Check if the user messages buffer is empty. */
260int usermsgs_empty(void)
261{
262 return !!(b_is_null(&usermsgs_buf) || !b_data(&usermsgs_buf));
263}
264
265/* Return the messages log buffer content. */
266const char *usermsgs_str(void)
267{
268 if (unlikely(b_is_null(&usermsgs_buf)))
269 return "";
270
271 return b_head(&usermsgs_buf);
272}
273
Amaury Denoyelle6af81f82021-05-27 15:45:28 +0200274/* Set thread-local context infos to prefix forthcoming stderr output during
275 * configuration parsing.
276 *
277 * <file> and <line> specify the location of the parsed configuration.
278 *
279 * <obj> can be of various types. If not NULL, the string prefix generated will
280 * depend on its type.
281 */
282void set_usermsgs_ctx(const char *file, int line, enum obj_type *obj)
283{
284 usermsgs_ctx.file = file;
285 usermsgs_ctx.line = line;
286 usermsgs_ctx.obj = obj;
287}
288
289/* Set thread-local context infos to prefix forthcoming stderr output. It will
290 * be set as a complement to possibly already defined file/line.
291 *
292 * <obj> can be of various types. If not NULL, the string prefix generated will
293 * depend on its type.
294 */
295void register_parsing_obj(enum obj_type *obj)
296{
297 usermsgs_ctx.obj = obj;
298}
299
300/* Reset thread-local context infos for stderr output. */
301void reset_usermsgs_ctx(void)
302{
303 usermsgs_ctx.file = NULL;
304 usermsgs_ctx.line = 0;
305 usermsgs_ctx.obj = NULL;
306}
307
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200308static void generate_usermsgs_ctx_str(void)
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200309{
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200310 struct usermsgs_ctx *ctx = &usermsgs_ctx;
311 void *area;
312 int ret;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200313
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200314 if (unlikely(b_is_null(&ctx->str))) {
Tim Duesterhusec4a8752021-09-15 13:58:46 +0200315 area = calloc(USERMSGS_CTX_BUFSIZE, sizeof(*area));
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200316 if (area)
317 ctx->str = b_make(area, USERMSGS_CTX_BUFSIZE, 0, 0);
318 }
319
320 if (likely(!b_is_null(&ctx->str))) {
321 b_reset(&ctx->str);
322
323 if (ctx->prefix) {
324 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
325 "%s : ", ctx->prefix);
326 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
327 }
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200328
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200329 if (ctx->file) {
330 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
331 "[%s:%d] : ", ctx->file, ctx->line);
332 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
333 }
334
335 switch (obj_type(ctx->obj)) {
336 case OBJ_TYPE_SERVER:
337 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
338 "'server %s/%s' : ",
339 __objt_server(ctx->obj)->proxy->id,
340 __objt_server(ctx->obj)->id);
341 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
342 break;
343
344 case OBJ_TYPE_NONE:
345 default:
346 break;
347 }
Amaury Denoyelled0b237c2021-05-28 10:36:56 +0200348
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200349 if (!b_data(&ctx->str))
350 snprintf(b_tail(&ctx->str), b_room(&ctx->str), "%s", "");
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200351 }
352}
353
Amaury Denoyellece986e12021-06-04 11:20:32 +0200354/* Generic function to display messages prefixed by a label */
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200355static void print_message(int use_usermsgs_ctx, const char *label, const char *fmt, va_list argp)
Amaury Denoyellece986e12021-06-04 11:20:32 +0200356{
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200357 struct ist msg_ist = IST_NULL;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200358 char *head, *parsing_str, *msg;
Amaury Denoyellece986e12021-06-04 11:20:32 +0200359 char prefix[11]; // '[' + 8 chars + ']' + 0.
360
361 *prefix = '[';
362 strncpy(prefix + 1, label, sizeof(prefix) - 2);
363 msg = prefix + strlen(prefix);
364 *msg++ = ']';
365 while (msg < prefix + sizeof(prefix) - 1)
366 *msg++ = ' ';
367 *msg = 0;
368
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200369 head = parsing_str = msg = NULL;
Amaury Denoyellece986e12021-06-04 11:20:32 +0200370 memprintf(&head, "%s (%u) : ", prefix, (uint)getpid());
371 memvprintf(&msg, fmt, argp);
372
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200373 /* trim the trailing '\n' */
374 msg_ist = ist(msg);
375 if (msg_ist.len > 0 && msg_ist.ptr[msg_ist.len - 1] == '\n')
376 msg_ist.len--;
377
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200378 if (use_usermsgs_ctx) {
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200379 generate_usermsgs_ctx_str();
380 parsing_str = b_head(&usermsgs_ctx.str);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200381 reset_usermsgs_ctx();
382 }
383 else {
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200384 parsing_str = "";
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200385 }
386
Amaury Denoyellece986e12021-06-04 11:20:32 +0200387 if (global.mode & MODE_STARTING) {
388 if (unlikely(!startup_logs))
William Lallemandeba6a542022-09-26 12:54:39 +0200389 startup_logs_init();
Amaury Denoyellece986e12021-06-04 11:20:32 +0200390
391 if (likely(startup_logs)) {
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200392 struct ist m[3];
Amaury Denoyellece986e12021-06-04 11:20:32 +0200393
394 m[0] = ist(head);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200395 m[1] = ist(parsing_str);
396 m[2] = msg_ist;
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200397
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200398 ring_write(startup_logs, ~0, 0, 0, m, 3);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200399 }
400 }
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200401 else {
402 usermsgs_put(&msg_ist);
403 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200404
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200405 fprintf(stderr, "%s%s%s", head, parsing_str, msg);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200406 fflush(stderr);
407
408 free(head);
409 free(msg);
410}
411
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200412static void print_message_args(int use_usermsgs_ctx, const char *label, const char *fmt, ...)
413{
414 va_list argp;
415 va_start(argp, fmt);
416 print_message(use_usermsgs_ctx, label, fmt, argp);
417 va_end(argp);
418}
419
Amaury Denoyellece986e12021-06-04 11:20:32 +0200420/*
421 * Displays the message on stderr with the date and pid. Overrides the quiet
422 * mode during startup.
423 */
424void ha_alert(const char *fmt, ...)
425{
426 va_list argp;
427
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200428 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
429 !(global.mode & MODE_STARTING)) {
430 if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) {
Amaury Denoyellece986e12021-06-04 11:20:32 +0200431 const char *path = get_exec_path();
432
433 warned |= WARN_EXEC_PATH;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200434 print_message_args(0, "NOTICE", "haproxy version is %s\n", haproxy_version);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200435 if (path)
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200436 print_message_args(0, "NOTICE", "path to executable is %s\n", path);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200437 }
438 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200439 print_message(1, "ALERT", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200440 va_end(argp);
441 }
442}
443
444/*
445 * Displays the message on stderr with the date and pid.
446 */
447void ha_warning(const char *fmt, ...)
448{
449 va_list argp;
450
451 warned |= WARN_ANY;
452
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200453 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
454 !(global.mode & MODE_STARTING)) {
Amaury Denoyelleda3d6812021-05-28 09:57:10 +0200455 if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) {
456 const char *path = get_exec_path();
457
458 warned |= WARN_EXEC_PATH;
459 print_message_args(0, "NOTICE", "haproxy version is %s\n", haproxy_version);
460 if (path)
461 print_message_args(0, "NOTICE", "path to executable is %s\n", path);
462 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200463 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200464 print_message(1, "WARNING", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200465 va_end(argp);
466 }
467}
468
469/*
470 * Variant of _ha_diag_warning with va_list.
471 * Use it only if MODE_DIAG has been previously checked.
472 */
473void _ha_vdiag_warning(const char *fmt, va_list argp)
474{
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200475 print_message(1, "DIAG", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200476}
477
478/*
479 * Output a diagnostic warning.
480 * Use it only if MODE_DIAG has been previously checked.
481 */
482void _ha_diag_warning(const char *fmt, ...)
483{
484 va_list argp;
485
486 va_start(argp, fmt);
487 _ha_vdiag_warning(fmt, argp);
488 va_end(argp);
489}
490
491/*
492 * Output a diagnostic warning. Do nothing of MODE_DIAG is not on.
493 */
494void ha_diag_warning(const char *fmt, ...)
495{
496 va_list argp;
497
498 if (global.mode & MODE_DIAG) {
499 va_start(argp, fmt);
500 _ha_vdiag_warning(fmt, argp);
501 va_end(argp);
502 }
503}
504
505/*
506 * Displays the message on stderr with the date and pid.
507 */
508void ha_notice(const char *fmt, ...)
509{
510 va_list argp;
511
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200512 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
513 !(global.mode & MODE_STARTING)) {
Amaury Denoyellece986e12021-06-04 11:20:32 +0200514 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200515 print_message(1, "NOTICE", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200516 va_end(argp);
517 }
518}
519
520/*
521 * Displays the message on <out> only if quiet mode is not set.
522 */
523void qfprintf(FILE *out, const char *fmt, ...)
524{
525 va_list argp;
526
527 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
528 va_start(argp, fmt);
529 vfprintf(out, fmt, argp);
530 fflush(out);
531 va_end(argp);
532 }
533}
534
535
536/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
537static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
538{
539 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
540 return 1;
541
542 if (!startup_logs)
543 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
544
Willy Tarreaucba88382022-05-05 15:18:57 +0200545 return ring_attach_cli(startup_logs, appctx, 0);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200546}
547
548/* register cli keywords */
549static struct cli_kw_list cli_kws = {{ },{
William Lallemandeba6a542022-09-26 12:54:39 +0200550 { { "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 +0200551 {{},}
552}};
553
554INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
555
556
557static void deinit_errors_buffers()
558{
559 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200560 ha_free(&usermsgs_buf.area);
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200561 ha_free(&usermsgs_ctx.str.area);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200562}
563
Willy Tarreau032e7002022-04-27 17:50:53 +0200564/* errors might be used in threads and even before forking, thus 2 deinit */
Amaury Denoyellece986e12021-06-04 11:20:32 +0200565REGISTER_PER_THREAD_FREE(deinit_errors_buffers);
Willy Tarreau032e7002022-04-27 17:50:53 +0200566REGISTER_POST_DEINIT(deinit_errors_buffers);