#include #include #include #include #include #include #include #include #include #include #include #include /* A global buffer used to store all startup alerts/warnings. It will then be * retrieve on the CLI. */ static struct ring *startup_logs = NULL; /* A thread local buffer used to store all alerts/warnings. It can be used to * retrieve them for CLI commands after startup. */ #define USER_MESSAGES_BUFSIZE 1024 static THREAD_LOCAL struct buffer usermsgs_buf = BUF_NULL; /* A thread local context used for stderr output via ha_alert/warning/notice/diag. */ static THREAD_LOCAL struct usermsgs_ctx usermsgs_ctx = { }; /* Put msg in usermsgs_buf. * * The message should not be terminated by a newline because this function * manually insert it. * * If there is not enough room in the buffer, the message is silently discarded. * Do not forget to frequently clear the buffer. */ static void usermsgs_put(const struct ist *msg) { /* Allocate the buffer if not already done. */ if (unlikely(b_is_null(&usermsgs_buf))) { usermsgs_buf.area = malloc(USER_MESSAGES_BUFSIZE * sizeof(char)); usermsgs_buf.size = USER_MESSAGES_BUFSIZE; } if (likely(!b_is_null(&usermsgs_buf))) { if (b_room(&usermsgs_buf) >= msg->len + 2) { /* Insert the message + newline. */ b_putblk(&usermsgs_buf, msg->ptr, msg->len); b_putchr(&usermsgs_buf, '\n'); /* Insert NUL outside of the buffer. */ *b_tail(&usermsgs_buf) = '\0'; } } } /* Clear the user messages log buffer. * * will set the local-thread context appended to every output * following this call. It can be NULL if not necessary. */ void usermsgs_clr(const char *prefix) { if (likely(!b_is_null(&usermsgs_buf))) { b_reset(&usermsgs_buf); usermsgs_buf.area[0] = '\0'; } usermsgs_ctx.prefix = prefix; } /* Check if the user messages buffer is empty. */ int usermsgs_empty(void) { return !!(b_is_null(&usermsgs_buf) || !b_data(&usermsgs_buf)); } /* Return the messages log buffer content. */ const char *usermsgs_str(void) { if (unlikely(b_is_null(&usermsgs_buf))) return ""; return b_head(&usermsgs_buf); } /* Set thread-local context infos to prefix forthcoming stderr output during * configuration parsing. * * and specify the location of the parsed configuration. * * can be of various types. If not NULL, the string prefix generated will * depend on its type. */ void set_usermsgs_ctx(const char *file, int line, enum obj_type *obj) { usermsgs_ctx.file = file; usermsgs_ctx.line = line; usermsgs_ctx.obj = obj; } /* Set thread-local context infos to prefix forthcoming stderr output. It will * be set as a complement to possibly already defined file/line. * * can be of various types. If not NULL, the string prefix generated will * depend on its type. */ void register_parsing_obj(enum obj_type *obj) { usermsgs_ctx.obj = obj; } /* Reset thread-local context infos for stderr output. */ void reset_usermsgs_ctx(void) { usermsgs_ctx.file = NULL; usermsgs_ctx.line = 0; usermsgs_ctx.obj = NULL; } /* Generic function to display messages prefixed by a label */ static void print_message(const char *label, const char *fmt, va_list argp) { struct ist msg_ist = IST_NULL; char *head, *msg; char prefix[11]; // '[' + 8 chars + ']' + 0. *prefix = '['; strncpy(prefix + 1, label, sizeof(prefix) - 2); msg = prefix + strlen(prefix); *msg++ = ']'; while (msg < prefix + sizeof(prefix) - 1) *msg++ = ' '; *msg = 0; head = msg = NULL; memprintf(&head, "%s (%u) : ", prefix, (uint)getpid()); memvprintf(&msg, fmt, argp); /* trim the trailing '\n' */ msg_ist = ist(msg); if (msg_ist.len > 0 && msg_ist.ptr[msg_ist.len - 1] == '\n') msg_ist.len--; if (global.mode & MODE_STARTING) { if (unlikely(!startup_logs)) startup_logs = ring_new(STARTUP_LOG_SIZE); if (likely(startup_logs)) { struct ist m[2]; m[0] = ist(head); m[1] = msg_ist; ring_write(startup_logs, ~0, 0, 0, m, 2); } } else { usermsgs_put(&msg_ist); } fprintf(stderr, "%s%s", head, msg); fflush(stderr); free(head); free(msg); } /* * Displays the message on stderr with the date and pid. Overrides the quiet * mode during startup. */ void ha_alert(const char *fmt, ...) { va_list argp; if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) || !(global.mode & MODE_STARTING)) { if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) { const char *path = get_exec_path(); warned |= WARN_EXEC_PATH; ha_notice("haproxy version is %s\n", haproxy_version); if (path) ha_notice("path to executable is %s\n", path); } va_start(argp, fmt); print_message("ALERT", fmt, argp); va_end(argp); } } /* * Displays the message on stderr with the date and pid. */ void ha_warning(const char *fmt, ...) { va_list argp; warned |= WARN_ANY; if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) || !(global.mode & MODE_STARTING)) { va_start(argp, fmt); print_message("WARNING", fmt, argp); va_end(argp); } } /* * Variant of _ha_diag_warning with va_list. * Use it only if MODE_DIAG has been previously checked. */ void _ha_vdiag_warning(const char *fmt, va_list argp) { print_message("DIAG", fmt, argp); } /* * Output a diagnostic warning. * Use it only if MODE_DIAG has been previously checked. */ void _ha_diag_warning(const char *fmt, ...) { va_list argp; va_start(argp, fmt); _ha_vdiag_warning(fmt, argp); va_end(argp); } /* * Output a diagnostic warning. Do nothing of MODE_DIAG is not on. */ void ha_diag_warning(const char *fmt, ...) { va_list argp; if (global.mode & MODE_DIAG) { va_start(argp, fmt); _ha_vdiag_warning(fmt, argp); va_end(argp); } } /* * Displays the message on stderr with the date and pid. */ void ha_notice(const char *fmt, ...) { va_list argp; if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) || !(global.mode & MODE_STARTING)) { va_start(argp, fmt); print_message("NOTICE", fmt, argp); va_end(argp); } } /* * Displays the message on only if quiet mode is not set. */ void qfprintf(FILE *out, const char *fmt, ...) { va_list argp; if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) { va_start(argp, fmt); vfprintf(out, fmt, argp); fflush(out); va_end(argp); } } /* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */ static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private) { if (!cli_has_level(appctx, ACCESS_LVL_OPER)) return 1; if (!startup_logs) return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print return ring_attach_cli(startup_logs, appctx); } /* register cli keywords */ static struct cli_kw_list cli_kws = {{ },{ { { "show", "startup-logs", NULL }, "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL }, {{},} }}; INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws); static void deinit_errors_buffers() { ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL)); ha_free(&usermsgs_buf.area); } REGISTER_PER_THREAD_FREE(deinit_errors_buffers);