blob: bd3c271f42c33d159612f490871383b7fd6bc1a1 [file] [log] [blame]
Amaury Denoyellece986e12021-06-04 11:20:32 +02001#include <stdarg.h>
2#include <stdio.h>
3#include <syslog.h>
4
5#include <haproxy/api.h>
6#include <haproxy/applet-t.h>
Amaury Denoyelle1833e432021-05-26 11:05:22 +02007#include <haproxy/buf.h>
Amaury Denoyellece986e12021-06-04 11:20:32 +02008#include <haproxy/cli.h>
9#include <haproxy/errors.h>
10#include <haproxy/global.h>
Amaury Denoyelle816281f2021-05-27 15:46:19 +020011#include <haproxy/obj_type.h>
Amaury Denoyellece986e12021-06-04 11:20:32 +020012#include <haproxy/ring.h>
13#include <haproxy/tools.h>
14#include <haproxy/version.h>
15
16/* A global buffer used to store all startup alerts/warnings. It will then be
17 * retrieve on the CLI. */
18static struct ring *startup_logs = NULL;
19
Amaury Denoyelle1833e432021-05-26 11:05:22 +020020/* A thread local buffer used to store all alerts/warnings. It can be used to
21 * retrieve them for CLI commands after startup.
22 */
23#define USER_MESSAGES_BUFSIZE 1024
24static THREAD_LOCAL struct buffer usermsgs_buf = BUF_NULL;
25
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020026/* A thread local context used for stderr output via ha_alert/warning/notice/diag.
27 */
Amaury Denoyelle846830e2021-06-07 19:24:10 +020028#define USERMSGS_CTX_BUFSIZE PATH_MAX
29static THREAD_LOCAL struct usermsgs_ctx usermsgs_ctx = { .str = BUF_NULL, };
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020030
Amaury Denoyelle1833e432021-05-26 11:05:22 +020031/* Put msg in usermsgs_buf.
32 *
33 * The message should not be terminated by a newline because this function
34 * manually insert it.
35 *
36 * If there is not enough room in the buffer, the message is silently discarded.
37 * Do not forget to frequently clear the buffer.
38 */
39static void usermsgs_put(const struct ist *msg)
40{
41 /* Allocate the buffer if not already done. */
42 if (unlikely(b_is_null(&usermsgs_buf))) {
43 usermsgs_buf.area = malloc(USER_MESSAGES_BUFSIZE * sizeof(char));
44 usermsgs_buf.size = USER_MESSAGES_BUFSIZE;
45 }
46
47 if (likely(!b_is_null(&usermsgs_buf))) {
48 if (b_room(&usermsgs_buf) >= msg->len + 2) {
49 /* Insert the message + newline. */
50 b_putblk(&usermsgs_buf, msg->ptr, msg->len);
51 b_putchr(&usermsgs_buf, '\n');
52 /* Insert NUL outside of the buffer. */
53 *b_tail(&usermsgs_buf) = '\0';
54 }
55 }
56}
57
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020058/* Clear the user messages log buffer.
59 *
60 * <prefix> will set the local-thread context appended to every output
61 * following this call. It can be NULL if not necessary.
62 */
63void usermsgs_clr(const char *prefix)
Amaury Denoyelle1833e432021-05-26 11:05:22 +020064{
65 if (likely(!b_is_null(&usermsgs_buf))) {
66 b_reset(&usermsgs_buf);
67 usermsgs_buf.area[0] = '\0';
68 }
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020069
70 usermsgs_ctx.prefix = prefix;
Amaury Denoyelle1833e432021-05-26 11:05:22 +020071}
72
73/* Check if the user messages buffer is empty. */
74int usermsgs_empty(void)
75{
76 return !!(b_is_null(&usermsgs_buf) || !b_data(&usermsgs_buf));
77}
78
79/* Return the messages log buffer content. */
80const char *usermsgs_str(void)
81{
82 if (unlikely(b_is_null(&usermsgs_buf)))
83 return "";
84
85 return b_head(&usermsgs_buf);
86}
87
Amaury Denoyelle6af81f82021-05-27 15:45:28 +020088/* Set thread-local context infos to prefix forthcoming stderr output during
89 * configuration parsing.
90 *
91 * <file> and <line> specify the location of the parsed configuration.
92 *
93 * <obj> can be of various types. If not NULL, the string prefix generated will
94 * depend on its type.
95 */
96void set_usermsgs_ctx(const char *file, int line, enum obj_type *obj)
97{
98 usermsgs_ctx.file = file;
99 usermsgs_ctx.line = line;
100 usermsgs_ctx.obj = obj;
101}
102
103/* Set thread-local context infos to prefix forthcoming stderr output. It will
104 * be set as a complement to possibly already defined file/line.
105 *
106 * <obj> can be of various types. If not NULL, the string prefix generated will
107 * depend on its type.
108 */
109void register_parsing_obj(enum obj_type *obj)
110{
111 usermsgs_ctx.obj = obj;
112}
113
114/* Reset thread-local context infos for stderr output. */
115void reset_usermsgs_ctx(void)
116{
117 usermsgs_ctx.file = NULL;
118 usermsgs_ctx.line = 0;
119 usermsgs_ctx.obj = NULL;
120}
121
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200122static void generate_usermsgs_ctx_str(void)
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200123{
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200124 struct usermsgs_ctx *ctx = &usermsgs_ctx;
125 void *area;
126 int ret;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200127
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200128 if (unlikely(b_is_null(&ctx->str))) {
Tim Duesterhusec4a8752021-09-15 13:58:46 +0200129 area = calloc(USERMSGS_CTX_BUFSIZE, sizeof(*area));
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200130 if (area)
131 ctx->str = b_make(area, USERMSGS_CTX_BUFSIZE, 0, 0);
132 }
133
134 if (likely(!b_is_null(&ctx->str))) {
135 b_reset(&ctx->str);
136
137 if (ctx->prefix) {
138 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
139 "%s : ", ctx->prefix);
140 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
141 }
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200142
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200143 if (ctx->file) {
144 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
145 "[%s:%d] : ", ctx->file, ctx->line);
146 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
147 }
148
149 switch (obj_type(ctx->obj)) {
150 case OBJ_TYPE_SERVER:
151 ret = snprintf(b_tail(&ctx->str), b_room(&ctx->str),
152 "'server %s/%s' : ",
153 __objt_server(ctx->obj)->proxy->id,
154 __objt_server(ctx->obj)->id);
155 b_add(&ctx->str, MIN(ret, b_room(&ctx->str)));
156 break;
157
158 case OBJ_TYPE_NONE:
159 default:
160 break;
161 }
Amaury Denoyelled0b237c2021-05-28 10:36:56 +0200162
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200163 if (!b_data(&ctx->str))
164 snprintf(b_tail(&ctx->str), b_room(&ctx->str), "%s", "");
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200165 }
166}
167
Amaury Denoyellece986e12021-06-04 11:20:32 +0200168/* Generic function to display messages prefixed by a label */
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200169static void print_message(int use_usermsgs_ctx, const char *label, const char *fmt, va_list argp)
Amaury Denoyellece986e12021-06-04 11:20:32 +0200170{
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200171 struct ist msg_ist = IST_NULL;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200172 char *head, *parsing_str, *msg;
Amaury Denoyellece986e12021-06-04 11:20:32 +0200173 char prefix[11]; // '[' + 8 chars + ']' + 0.
174
175 *prefix = '[';
176 strncpy(prefix + 1, label, sizeof(prefix) - 2);
177 msg = prefix + strlen(prefix);
178 *msg++ = ']';
179 while (msg < prefix + sizeof(prefix) - 1)
180 *msg++ = ' ';
181 *msg = 0;
182
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200183 head = parsing_str = msg = NULL;
Amaury Denoyellece986e12021-06-04 11:20:32 +0200184 memprintf(&head, "%s (%u) : ", prefix, (uint)getpid());
185 memvprintf(&msg, fmt, argp);
186
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200187 /* trim the trailing '\n' */
188 msg_ist = ist(msg);
189 if (msg_ist.len > 0 && msg_ist.ptr[msg_ist.len - 1] == '\n')
190 msg_ist.len--;
191
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200192 if (use_usermsgs_ctx) {
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200193 generate_usermsgs_ctx_str();
194 parsing_str = b_head(&usermsgs_ctx.str);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200195 reset_usermsgs_ctx();
196 }
197 else {
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200198 parsing_str = "";
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200199 }
200
Amaury Denoyellece986e12021-06-04 11:20:32 +0200201 if (global.mode & MODE_STARTING) {
202 if (unlikely(!startup_logs))
203 startup_logs = ring_new(STARTUP_LOG_SIZE);
204
205 if (likely(startup_logs)) {
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200206 struct ist m[3];
Amaury Denoyellece986e12021-06-04 11:20:32 +0200207
208 m[0] = ist(head);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200209 m[1] = ist(parsing_str);
210 m[2] = msg_ist;
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200211
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200212 ring_write(startup_logs, ~0, 0, 0, m, 3);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200213 }
214 }
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200215 else {
216 usermsgs_put(&msg_ist);
217 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200218
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200219 fprintf(stderr, "%s%s%s", head, parsing_str, msg);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200220 fflush(stderr);
221
222 free(head);
223 free(msg);
224}
225
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200226static void print_message_args(int use_usermsgs_ctx, const char *label, const char *fmt, ...)
227{
228 va_list argp;
229 va_start(argp, fmt);
230 print_message(use_usermsgs_ctx, label, fmt, argp);
231 va_end(argp);
232}
233
Amaury Denoyellece986e12021-06-04 11:20:32 +0200234/*
235 * Displays the message on stderr with the date and pid. Overrides the quiet
236 * mode during startup.
237 */
238void ha_alert(const char *fmt, ...)
239{
240 va_list argp;
241
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200242 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
243 !(global.mode & MODE_STARTING)) {
244 if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) {
Amaury Denoyellece986e12021-06-04 11:20:32 +0200245 const char *path = get_exec_path();
246
247 warned |= WARN_EXEC_PATH;
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200248 print_message_args(0, "NOTICE", "haproxy version is %s\n", haproxy_version);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200249 if (path)
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200250 print_message_args(0, "NOTICE", "path to executable is %s\n", path);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200251 }
252 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200253 print_message(1, "ALERT", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200254 va_end(argp);
255 }
256}
257
258/*
259 * Displays the message on stderr with the date and pid.
260 */
261void ha_warning(const char *fmt, ...)
262{
263 va_list argp;
264
265 warned |= WARN_ANY;
266
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200267 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
268 !(global.mode & MODE_STARTING)) {
Amaury Denoyelleda3d6812021-05-28 09:57:10 +0200269 if (!(warned & WARN_EXEC_PATH) && (global.mode & MODE_STARTING)) {
270 const char *path = get_exec_path();
271
272 warned |= WARN_EXEC_PATH;
273 print_message_args(0, "NOTICE", "haproxy version is %s\n", haproxy_version);
274 if (path)
275 print_message_args(0, "NOTICE", "path to executable is %s\n", path);
276 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200277 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200278 print_message(1, "WARNING", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200279 va_end(argp);
280 }
281}
282
283/*
284 * Variant of _ha_diag_warning with va_list.
285 * Use it only if MODE_DIAG has been previously checked.
286 */
287void _ha_vdiag_warning(const char *fmt, va_list argp)
288{
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200289 print_message(1, "DIAG", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200290}
291
292/*
293 * Output a diagnostic warning.
294 * Use it only if MODE_DIAG has been previously checked.
295 */
296void _ha_diag_warning(const char *fmt, ...)
297{
298 va_list argp;
299
300 va_start(argp, fmt);
301 _ha_vdiag_warning(fmt, argp);
302 va_end(argp);
303}
304
305/*
306 * Output a diagnostic warning. Do nothing of MODE_DIAG is not on.
307 */
308void ha_diag_warning(const char *fmt, ...)
309{
310 va_list argp;
311
312 if (global.mode & MODE_DIAG) {
313 va_start(argp, fmt);
314 _ha_vdiag_warning(fmt, argp);
315 va_end(argp);
316 }
317}
318
319/*
320 * Displays the message on stderr with the date and pid.
321 */
322void ha_notice(const char *fmt, ...)
323{
324 va_list argp;
325
Amaury Denoyelle0a1cdcc2021-05-26 11:05:45 +0200326 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE) ||
327 !(global.mode & MODE_STARTING)) {
Amaury Denoyellece986e12021-06-04 11:20:32 +0200328 va_start(argp, fmt);
Amaury Denoyelle816281f2021-05-27 15:46:19 +0200329 print_message(1, "NOTICE", fmt, argp);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200330 va_end(argp);
331 }
332}
333
334/*
335 * Displays the message on <out> only if quiet mode is not set.
336 */
337void qfprintf(FILE *out, const char *fmt, ...)
338{
339 va_list argp;
340
341 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
342 va_start(argp, fmt);
343 vfprintf(out, fmt, argp);
344 fflush(out);
345 va_end(argp);
346 }
347}
348
349
350/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
351static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
352{
353 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
354 return 1;
355
356 if (!startup_logs)
357 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
358
Willy Tarreaucba88382022-05-05 15:18:57 +0200359 return ring_attach_cli(startup_logs, appctx, 0);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200360}
361
362/* register cli keywords */
363static struct cli_kw_list cli_kws = {{ },{
364 { { "show", "startup-logs", NULL }, "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
365 {{},}
366}};
367
368INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
369
370
371static void deinit_errors_buffers()
372{
373 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200374 ha_free(&usermsgs_buf.area);
Amaury Denoyelle846830e2021-06-07 19:24:10 +0200375 ha_free(&usermsgs_ctx.str.area);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200376}
377
Willy Tarreau032e7002022-04-27 17:50:53 +0200378/* errors might be used in threads and even before forking, thus 2 deinit */
Amaury Denoyellece986e12021-06-04 11:20:32 +0200379REGISTER_PER_THREAD_FREE(deinit_errors_buffers);
Willy Tarreau032e7002022-04-27 17:50:53 +0200380REGISTER_POST_DEINIT(deinit_errors_buffers);