blob: b2ff42970717a224ed51708810f2b62c954687ca [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>
11#include <haproxy/ring.h>
12#include <haproxy/tools.h>
13#include <haproxy/version.h>
14
15/* A global buffer used to store all startup alerts/warnings. It will then be
16 * retrieve on the CLI. */
17static struct ring *startup_logs = NULL;
18
Amaury Denoyelle1833e432021-05-26 11:05:22 +020019/* A thread local buffer used to store all alerts/warnings. It can be used to
20 * retrieve them for CLI commands after startup.
21 */
22#define USER_MESSAGES_BUFSIZE 1024
23static THREAD_LOCAL struct buffer usermsgs_buf = BUF_NULL;
24
25/* Put msg in usermsgs_buf.
26 *
27 * The message should not be terminated by a newline because this function
28 * manually insert it.
29 *
30 * If there is not enough room in the buffer, the message is silently discarded.
31 * Do not forget to frequently clear the buffer.
32 */
33static void usermsgs_put(const struct ist *msg)
34{
35 /* Allocate the buffer if not already done. */
36 if (unlikely(b_is_null(&usermsgs_buf))) {
37 usermsgs_buf.area = malloc(USER_MESSAGES_BUFSIZE * sizeof(char));
38 usermsgs_buf.size = USER_MESSAGES_BUFSIZE;
39 }
40
41 if (likely(!b_is_null(&usermsgs_buf))) {
42 if (b_room(&usermsgs_buf) >= msg->len + 2) {
43 /* Insert the message + newline. */
44 b_putblk(&usermsgs_buf, msg->ptr, msg->len);
45 b_putchr(&usermsgs_buf, '\n');
46 /* Insert NUL outside of the buffer. */
47 *b_tail(&usermsgs_buf) = '\0';
48 }
49 }
50}
51
52/* Clear the messages log buffer. */
53void usermsgs_clr(void)
54{
55 if (likely(!b_is_null(&usermsgs_buf))) {
56 b_reset(&usermsgs_buf);
57 usermsgs_buf.area[0] = '\0';
58 }
59}
60
61/* Check if the user messages buffer is empty. */
62int usermsgs_empty(void)
63{
64 return !!(b_is_null(&usermsgs_buf) || !b_data(&usermsgs_buf));
65}
66
67/* Return the messages log buffer content. */
68const char *usermsgs_str(void)
69{
70 if (unlikely(b_is_null(&usermsgs_buf)))
71 return "";
72
73 return b_head(&usermsgs_buf);
74}
75
Amaury Denoyellece986e12021-06-04 11:20:32 +020076/* Generic function to display messages prefixed by a label */
77static void print_message(const char *label, const char *fmt, va_list argp)
78{
Amaury Denoyelle1833e432021-05-26 11:05:22 +020079 struct ist msg_ist = IST_NULL;
Amaury Denoyellece986e12021-06-04 11:20:32 +020080 char *head, *msg;
81 char prefix[11]; // '[' + 8 chars + ']' + 0.
82
83 *prefix = '[';
84 strncpy(prefix + 1, label, sizeof(prefix) - 2);
85 msg = prefix + strlen(prefix);
86 *msg++ = ']';
87 while (msg < prefix + sizeof(prefix) - 1)
88 *msg++ = ' ';
89 *msg = 0;
90
91 head = msg = NULL;
92 memprintf(&head, "%s (%u) : ", prefix, (uint)getpid());
93 memvprintf(&msg, fmt, argp);
94
Amaury Denoyelle1833e432021-05-26 11:05:22 +020095 /* trim the trailing '\n' */
96 msg_ist = ist(msg);
97 if (msg_ist.len > 0 && msg_ist.ptr[msg_ist.len - 1] == '\n')
98 msg_ist.len--;
99
Amaury Denoyellece986e12021-06-04 11:20:32 +0200100 if (global.mode & MODE_STARTING) {
101 if (unlikely(!startup_logs))
102 startup_logs = ring_new(STARTUP_LOG_SIZE);
103
104 if (likely(startup_logs)) {
105 struct ist m[2];
106
107 m[0] = ist(head);
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200108 m[1] = msg_ist;
109
Amaury Denoyellece986e12021-06-04 11:20:32 +0200110 ring_write(startup_logs, ~0, 0, 0, m, 2);
111 }
112 }
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200113 else {
114 usermsgs_put(&msg_ist);
115 }
Amaury Denoyellece986e12021-06-04 11:20:32 +0200116
117 fprintf(stderr, "%s%s", head, msg);
118 fflush(stderr);
119
120 free(head);
121 free(msg);
122}
123
124/*
125 * Displays the message on stderr with the date and pid. Overrides the quiet
126 * mode during startup.
127 */
128void ha_alert(const char *fmt, ...)
129{
130 va_list argp;
131
132 if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
133 if (!(warned & WARN_EXEC_PATH)) {
134 const char *path = get_exec_path();
135
136 warned |= WARN_EXEC_PATH;
137 ha_notice("haproxy version is %s\n", haproxy_version);
138 if (path)
139 ha_notice("path to executable is %s\n", path);
140 }
141 va_start(argp, fmt);
142 print_message("ALERT", fmt, argp);
143 va_end(argp);
144 }
145}
146
147/*
148 * Displays the message on stderr with the date and pid.
149 */
150void ha_warning(const char *fmt, ...)
151{
152 va_list argp;
153
154 warned |= WARN_ANY;
155
156 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
157 va_start(argp, fmt);
158 print_message("WARNING", fmt, argp);
159 va_end(argp);
160 }
161}
162
163/*
164 * Variant of _ha_diag_warning with va_list.
165 * Use it only if MODE_DIAG has been previously checked.
166 */
167void _ha_vdiag_warning(const char *fmt, va_list argp)
168{
169 print_message("DIAG", fmt, argp);
170}
171
172/*
173 * Output a diagnostic warning.
174 * Use it only if MODE_DIAG has been previously checked.
175 */
176void _ha_diag_warning(const char *fmt, ...)
177{
178 va_list argp;
179
180 va_start(argp, fmt);
181 _ha_vdiag_warning(fmt, argp);
182 va_end(argp);
183}
184
185/*
186 * Output a diagnostic warning. Do nothing of MODE_DIAG is not on.
187 */
188void ha_diag_warning(const char *fmt, ...)
189{
190 va_list argp;
191
192 if (global.mode & MODE_DIAG) {
193 va_start(argp, fmt);
194 _ha_vdiag_warning(fmt, argp);
195 va_end(argp);
196 }
197}
198
199/*
200 * Displays the message on stderr with the date and pid.
201 */
202void ha_notice(const char *fmt, ...)
203{
204 va_list argp;
205
206 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
207 va_start(argp, fmt);
208 print_message("NOTICE", fmt, argp);
209 va_end(argp);
210 }
211}
212
213/*
214 * Displays the message on <out> only if quiet mode is not set.
215 */
216void qfprintf(FILE *out, const char *fmt, ...)
217{
218 va_list argp;
219
220 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
221 va_start(argp, fmt);
222 vfprintf(out, fmt, argp);
223 fflush(out);
224 va_end(argp);
225 }
226}
227
228
229/* parse the "show startup-logs" command, returns 1 if a message is returned, otherwise zero */
230static int cli_parse_show_startup_logs(char **args, char *payload, struct appctx *appctx, void *private)
231{
232 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
233 return 1;
234
235 if (!startup_logs)
236 return cli_msg(appctx, LOG_INFO, "\n"); // nothing to print
237
238 return ring_attach_cli(startup_logs, appctx);
239}
240
241/* register cli keywords */
242static struct cli_kw_list cli_kws = {{ },{
243 { { "show", "startup-logs", NULL }, "show startup-logs : report logs emitted during HAProxy startup", cli_parse_show_startup_logs, NULL, NULL },
244 {{},}
245}};
246
247INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
248
249
250static void deinit_errors_buffers()
251{
252 ring_free(_HA_ATOMIC_XCHG(&startup_logs, NULL));
Amaury Denoyelle1833e432021-05-26 11:05:22 +0200253 ha_free(&usermsgs_buf.area);
Amaury Denoyellece986e12021-06-04 11:20:32 +0200254}
255
256REGISTER_PER_THREAD_FREE(deinit_errors_buffers);