blob: 34bc97cddab5969d828133601423a683cd8021e1 [file] [log] [blame]
Willy Tarreau67b5a162019-08-11 16:38:56 +02001/*
2 * Event sink management
3 *
4 * Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation, version 2.1
9 * exclusively.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
Willy Tarreau67b5a162019-08-11 16:38:56 +020021#include <common/compat.h>
22#include <common/config.h>
23#include <common/ist.h>
24#include <common/mini-clist.h>
Willy Tarreau53ba9d92019-09-26 08:03:58 +020025#include <common/time.h>
Willy Tarreau9f830d72019-08-26 18:17:04 +020026#include <proto/cli.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020027#include <proto/log.h>
Willy Tarreau4ed23ca2019-08-23 15:47:49 +020028#include <proto/ring.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020029#include <proto/sink.h>
Willy Tarreau9f830d72019-08-26 18:17:04 +020030#include <proto/stream_interface.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020031
32struct list sink_list = LIST_HEAD_INIT(sink_list);
33
34struct sink *sink_find(const char *name)
35{
36 struct sink *sink;
37
38 list_for_each_entry(sink, &sink_list, sink_list)
39 if (strcmp(sink->name, name) == 0)
40 return sink;
41 return NULL;
42}
43
44/* creates a new sink and adds it to the list, it's still generic and not fully
45 * initialized. Returns NULL on allocation failure. If another one already
46 * exists with the same name, it will be returned. The caller can detect it as
47 * a newly created one has type SINK_TYPE_NEW.
48 */
Willy Tarreau973e6622019-08-20 11:57:52 +020049static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt fmt)
Willy Tarreau67b5a162019-08-11 16:38:56 +020050{
51 struct sink *sink;
52
53 sink = sink_find(name);
54 if (sink)
55 goto end;
56
57 sink = malloc(sizeof(*sink));
58 if (!sink)
59 goto end;
60
Emeric Brun957ec592020-05-11 14:41:31 +020061 sink->name = strdup(name);
62 sink->desc = strdup(desc);
Willy Tarreau67b5a162019-08-11 16:38:56 +020063 sink->fmt = fmt;
64 sink->type = SINK_TYPE_NEW;
Christopher Fauleta63a5c22019-11-15 15:10:12 +010065 sink->maxlen = BUFSIZE;
Willy Tarreau67b5a162019-08-11 16:38:56 +020066 /* address will be filled by the caller if needed */
Willy Tarreau973e6622019-08-20 11:57:52 +020067 sink->ctx.fd = -1;
Willy Tarreau67b5a162019-08-11 16:38:56 +020068 sink->ctx.dropped = 0;
69 HA_RWLOCK_INIT(&sink->ctx.lock);
70 LIST_ADDQ(&sink_list, &sink->sink_list);
71 end:
72 return sink;
73}
74
Willy Tarreau973e6622019-08-20 11:57:52 +020075/* creates a sink called <name> of type FD associated to fd <fd>, format <fmt>,
76 * and description <desc>. Returns NULL on allocation failure or conflict.
77 * Perfect duplicates are merged (same type, fd, and name).
78 */
79struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd)
80{
81 struct sink *sink;
82
83 sink = __sink_new(name, desc, fmt);
84 if (!sink || (sink->type == SINK_TYPE_FD && sink->ctx.fd == fd))
85 goto end;
86
87 if (sink->type != SINK_TYPE_NEW) {
88 sink = NULL;
89 goto end;
90 }
91
92 sink->type = SINK_TYPE_FD;
93 sink->ctx.fd = fd;
94 end:
95 return sink;
96}
97
Willy Tarreau4ed23ca2019-08-23 15:47:49 +020098/* creates a sink called <name> of type BUF of size <size>, format <fmt>,
99 * and description <desc>. Returns NULL on allocation failure or conflict.
100 * Perfect duplicates are merged (same type and name). If sizes differ, the
101 * largest one is kept.
102 */
103struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt, size_t size)
104{
105 struct sink *sink;
106
107 sink = __sink_new(name, desc, fmt);
108 if (!sink)
109 goto fail;
110
111 if (sink->type == SINK_TYPE_BUFFER) {
112 /* such a buffer already exists, we may have to resize it */
113 if (!ring_resize(sink->ctx.ring, size))
114 goto fail;
115 goto end;
116 }
117
118 if (sink->type != SINK_TYPE_NEW) {
119 /* already exists of another type */
120 goto fail;
121 }
122
123 sink->ctx.ring = ring_new(size);
124 if (!sink->ctx.ring) {
125 LIST_DEL(&sink->sink_list);
Emeric Brun957ec592020-05-11 14:41:31 +0200126 free(sink->name);
127 free(sink->desc);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200128 free(sink);
129 goto fail;
130 }
131
132 sink->type = SINK_TYPE_BUFFER;
133 end:
134 return sink;
135 fail:
136 return NULL;
137}
138
Willy Tarreau67b5a162019-08-11 16:38:56 +0200139/* tries to send <nmsg> message parts (up to 8, ignored above) from message
140 * array <msg> to sink <sink>. Formating according to the sink's preference is
Willy Tarreau8f240232019-08-27 16:41:06 +0200141 * done here. Lost messages are NOT accounted for. It is preferable to call
142 * sink_write() instead which will also try to emit the number of dropped
143 * messages when there are any. It returns >0 if it could write anything,
144 * <=0 otherwise.
Willy Tarreau67b5a162019-08-11 16:38:56 +0200145 */
Emeric Brunbd163812020-05-06 14:33:46 +0200146ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
147 int level, int facility, struct ist *tag,
148 struct ist *pid, struct ist *sd)
Willy Tarreau67b5a162019-08-11 16:38:56 +0200149{
Emeric Brunbd163812020-05-06 14:33:46 +0200150 int log_format;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200151 char short_hdr[4];
Emeric Brunbd163812020-05-06 14:33:46 +0200152 struct ist pfx[6];
Willy Tarreaua1426de2019-08-27 14:21:02 +0200153 size_t npfx = 0;
Emeric Brunbd163812020-05-06 14:33:46 +0200154 char *hdr_ptr;
155 int fac_level;
156
157 if (sink->fmt == SINK_FMT_RAW)
158 goto send;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200159
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200160 if (sink->fmt == SINK_FMT_SHORT || sink->fmt == SINK_FMT_TIMED) {
Willy Tarreau67b5a162019-08-11 16:38:56 +0200161 short_hdr[0] = '<';
Emeric Brunbd163812020-05-06 14:33:46 +0200162 short_hdr[1] = '0' + level;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200163 short_hdr[2] = '>';
164
Willy Tarreaua1426de2019-08-27 14:21:02 +0200165 pfx[npfx].ptr = short_hdr;
166 pfx[npfx].len = 3;
167 npfx++;
Emeric Brunbd163812020-05-06 14:33:46 +0200168 if (sink->fmt == SINK_FMT_SHORT)
169 goto send;
Willy Tarreaua1426de2019-08-27 14:21:02 +0200170 }
Willy Tarreau67b5a162019-08-11 16:38:56 +0200171
Emeric Brunbd163812020-05-06 14:33:46 +0200172
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200173 if (sink->fmt == SINK_FMT_ISO || sink->fmt == SINK_FMT_TIMED) {
174 pfx[npfx].ptr = timeofday_as_iso_us(1);
175 pfx[npfx].len = 27;
176 npfx++;
Emeric Brunbd163812020-05-06 14:33:46 +0200177 goto send;
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200178 }
Emeric Brunbd163812020-05-06 14:33:46 +0200179 else if (sink->fmt == SINK_FMT_RFC5424) {
180 pfx[npfx].ptr = logheader_rfc5424;
181 pfx[npfx].len = update_log_hdr_rfc5424(date.tv_sec) - pfx[npfx].ptr;
182 log_format = LOG_FORMAT_RFC5424;
183 }
184 else {
185 pfx[npfx].ptr = logheader;
186 pfx[npfx].len = update_log_hdr(date.tv_sec) - pfx[npfx].ptr;
187 log_format = LOG_FORMAT_RFC3164;
188 sd = NULL;
189 }
190
191 fac_level = (facility << 3) + level;
192 hdr_ptr = pfx[npfx].ptr + 3; /* last digit of the log level */
193 do {
194 *hdr_ptr = '0' + fac_level % 10;
195 fac_level /= 10;
196 hdr_ptr--;
197 } while (fac_level && hdr_ptr > pfx[npfx].ptr);
198 *hdr_ptr = '<';
199 pfx[npfx].len -= hdr_ptr - pfx[npfx].ptr;
200 pfx[npfx].ptr = hdr_ptr;
201 npfx++;
202
203 if (tag && tag->len) {
204 pfx[npfx].ptr = tag->ptr;
205 pfx[npfx].len = tag->len;
206 npfx++;
207 }
208 pfx[npfx].ptr = get_format_pid_sep1(log_format, &pfx[npfx].len);
209 if (pfx[npfx].len)
210 npfx++;
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200211
Emeric Brunbd163812020-05-06 14:33:46 +0200212 if (pid && pid->len) {
213 pfx[npfx].ptr = pid->ptr;
214 pfx[npfx].len = pid->len;
215 npfx++;
216 }
217
218 pfx[npfx].ptr = get_format_pid_sep2(log_format, &pfx[npfx].len);
219 if (pfx[npfx].len)
220 npfx++;
221
222 if (sd && sd->len) {
223 pfx[npfx].ptr = sd->ptr;
224 pfx[npfx].len = sd->len;
225 npfx++;
226 }
227
228send:
Willy Tarreau973e6622019-08-20 11:57:52 +0200229 if (sink->type == SINK_TYPE_FD) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200230 return fd_write_frag_line(sink->ctx.fd, sink->maxlen, pfx, npfx, msg, nmsg, 1);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200231 }
232 else if (sink->type == SINK_TYPE_BUFFER) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200233 return ring_write(sink->ctx.ring, sink->maxlen, pfx, npfx, msg, nmsg);
Willy Tarreau973e6622019-08-20 11:57:52 +0200234 }
Willy Tarreau8f240232019-08-27 16:41:06 +0200235 return 0;
236}
Willy Tarreau67b5a162019-08-11 16:38:56 +0200237
Willy Tarreau8f240232019-08-27 16:41:06 +0200238/* Tries to emit a message indicating the number of dropped events. In case of
239 * success, the amount of drops is reduced by as much. It's supposed to be
240 * called under an exclusive lock on the sink to avoid multiple produces doing
241 * the same. On success, >0 is returned, otherwise <=0 on failure.
242 */
Emeric Brunbd163812020-05-06 14:33:46 +0200243int sink_announce_dropped(struct sink *sink, int facility, struct ist *pid)
Willy Tarreau8f240232019-08-27 16:41:06 +0200244{
245 unsigned int dropped;
246 struct buffer msg;
247 struct ist msgvec[1];
248 char logbuf[64];
Emeric Brunbd163812020-05-06 14:33:46 +0200249 struct ist sd;
250 struct ist tag;
Willy Tarreau8f240232019-08-27 16:41:06 +0200251
252 while (unlikely((dropped = sink->ctx.dropped) > 0)) {
253 chunk_init(&msg, logbuf, sizeof(logbuf));
254 chunk_printf(&msg, "%u event%s dropped", dropped, dropped > 1 ? "s" : "");
255 msgvec[0] = ist2(msg.area, msg.data);
Emeric Brunbd163812020-05-06 14:33:46 +0200256
257 sd.ptr = default_rfc5424_sd_log_format;
258 sd.len = 2;
259 tag.ptr = global.log_tag.area;
260 tag.len = global.log_tag.data;
261 if (__sink_write(sink, msgvec, 1, LOG_NOTICE, facility, &tag, pid, &sd) <= 0)
Willy Tarreau8f240232019-08-27 16:41:06 +0200262 return 0;
263 /* success! */
264 HA_ATOMIC_SUB(&sink->ctx.dropped, dropped);
265 }
266 return 1;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200267}
268
Willy Tarreau9f830d72019-08-26 18:17:04 +0200269/* parse the "show events" command, returns 1 if a message is returned, otherwise zero */
270static int cli_parse_show_events(char **args, char *payload, struct appctx *appctx, void *private)
271{
272 struct sink *sink;
Willy Tarreau1d181e42019-08-30 11:17:01 +0200273 int arg;
Willy Tarreau9f830d72019-08-26 18:17:04 +0200274
275 args++; // make args[1] the 1st arg
276
277 if (!*args[1]) {
278 /* no arg => report the list of supported sink */
Willy Tarreau1d181e42019-08-30 11:17:01 +0200279 chunk_printf(&trash, "Supported events sinks are listed below. Add -w(wait), -n(new). Any key to stop\n");
Willy Tarreau9f830d72019-08-26 18:17:04 +0200280 list_for_each_entry(sink, &sink_list, sink_list) {
281 chunk_appendf(&trash, " %-10s : type=%s, %u dropped, %s\n",
282 sink->name,
283 sink->type == SINK_TYPE_NEW ? "init" :
284 sink->type == SINK_TYPE_FD ? "fd" :
285 sink->type == SINK_TYPE_BUFFER ? "buffer" : "?",
286 sink->ctx.dropped, sink->desc);
287 }
288
289 trash.area[trash.data] = 0;
290 return cli_msg(appctx, LOG_WARNING, trash.area);
291 }
292
293 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
294 return 1;
295
296 sink = sink_find(args[1]);
297 if (!sink)
298 return cli_err(appctx, "No such event sink");
299
300 if (sink->type != SINK_TYPE_BUFFER)
301 return cli_msg(appctx, LOG_NOTICE, "Nothing to report for this sink");
302
Willy Tarreau1d181e42019-08-30 11:17:01 +0200303 for (arg = 2; *args[arg]; arg++) {
304 if (strcmp(args[arg], "-w") == 0)
305 appctx->ctx.cli.i0 |= 1; // wait mode
306 else if (strcmp(args[arg], "-n") == 0)
307 appctx->ctx.cli.i0 |= 2; // seek to new
308 else if (strcmp(args[arg], "-nw") == 0 || strcmp(args[arg], "-wn") == 0)
309 appctx->ctx.cli.i0 |= 3; // seek to new + wait
310 else
311 return cli_err(appctx, "unknown option");
312 }
Willy Tarreau9f830d72019-08-26 18:17:04 +0200313 return ring_attach_cli(sink->ctx.ring, appctx);
314}
315
Willy Tarreau973e6622019-08-20 11:57:52 +0200316static void sink_init()
317{
318 sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
319 sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
Willy Tarreauf8340e32019-09-26 08:05:15 +0200320 sink_new_buf("buf0", "in-memory ring buffer", SINK_FMT_TIMED, 1048576);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200321}
322
323static void sink_deinit()
324{
325 struct sink *sink, *sb;
326
327 list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
328 if (sink->type == SINK_TYPE_BUFFER)
329 ring_free(sink->ctx.ring);
330 LIST_DEL(&sink->sink_list);
Emeric Brun957ec592020-05-11 14:41:31 +0200331 free(sink->name);
332 free(sink->desc);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200333 free(sink);
334 }
Willy Tarreau973e6622019-08-20 11:57:52 +0200335}
336
Emeric Brun957ec592020-05-11 14:41:31 +0200337/*
338 * Parse "ring" keyword and create corresponding sink buffer.
339 *
340 * The function returns 1 in success case, otherwise, it returns 0 and err is
341 * filled.
342 */
343
344int parse_sinkbuf(char **args, char **err)
345{
346 int myidx = 2;
347 size_t size = BUFSIZE;
348 size_t maxlen = size;
349 int format = SINK_FMT_RAW;
350 struct sink *sink = NULL;
351 char *desc, *name = args[1];
352 const char *inv;
353
354 if (!*name) {
355 memprintf(err, "missing sink name");
356 goto error;
357 }
358
359 inv = invalid_char(name);
360 if (inv) {
361 memprintf(err, "invalid sink name '%s' (character '%c' is not permitted)", name, *inv);
362 goto error;
363 }
364
365 desc = name;
366
367 while (*args[myidx]) {
368 if (!strcmp(args[myidx], "size")) {
369 myidx++;
370 size = atol(args[myidx]);
371 if (!size) {
372 memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name);
373 goto error;
374 }
375 }
376 else if (!strcmp(args[myidx], "maxlen")) {
377 myidx++;
378 maxlen = atol(args[myidx]);
379 if (!maxlen) {
380 memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name);
381 goto error;
382 }
383 }
384 else if (!strcmp(args[myidx], "maxlen")) {
385 myidx++;
386 maxlen = atol(args[myidx]);
387 if (!maxlen) {
388 memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name);
389 goto error;
390 }
391 }
392 else if (!strcmp(args[myidx], "desc")) {
393 myidx++;
394 desc = args[myidx];
395 }
396 else if (!strcmp(args[myidx],"format")) {
397 myidx++;
398 if (!strcmp(args[myidx], "raw")) {
399 format = SINK_FMT_RAW;
400 }
401 else if (!strcmp(args[myidx], "short")) {
402 format = SINK_FMT_SHORT;
403 }
404 else if (!strcmp(args[myidx], "iso")) {
405 format = SINK_FMT_ISO;
406 }
407 else if (!strcmp(args[myidx], "timed")) {
408 format = SINK_FMT_TIMED;
409 }
410 else if (!strcmp(args[myidx], "rfc3164")) {
411 format = SINK_FMT_RFC3164;
412 }
413 else if (!strcmp(args[myidx], "rfc5424")) {
414 format = SINK_FMT_RFC5424;
415 }
416 else {
417 memprintf(err, "unknown format '%s' for new sink buffer for ring '%s'", args[myidx], name);
418 goto error;
419 }
420 }
421 else {
422 memprintf(err, "unknown parameter '%s' for new sink buffer for ring '%s'", args[myidx], name);
423 goto error;
424 }
425 myidx++;
426 }
427
428 if (maxlen > size) {
429 memprintf(err, "maxlen '%lu' exceeds size of the new sink buffer for ring '%s'", maxlen, name);
430 goto error;
431 }
432
433 sink = sink_new_buf(name, desc, format, size);
434 if (!sink || sink->type != SINK_TYPE_BUFFER) {
435 memprintf(err, "failed to create a new sink buffer for ring '%s'", name);
436 goto error;
437 }
438
439 sink->maxlen = maxlen;
440
441 return 1;
442error:
443 if (sink) {
444 if (sink->type == SINK_TYPE_BUFFER)
445 ring_free(sink->ctx.ring);
446 LIST_DEL(&sink->sink_list);
447 free(sink->name);
448 free(sink->desc);
449 free(sink);
450 }
451 return 0;
452
453}
454
Willy Tarreau973e6622019-08-20 11:57:52 +0200455INITCALL0(STG_REGISTER, sink_init);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200456REGISTER_POST_DEINIT(sink_deinit);
Willy Tarreau973e6622019-08-20 11:57:52 +0200457
Willy Tarreau9f830d72019-08-26 18:17:04 +0200458static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaufcf94982019-11-15 15:07:21 +0100459 { { "show", "events", NULL }, "show events [<sink>] : show event sink state", cli_parse_show_events, NULL, NULL },
Willy Tarreau9f830d72019-08-26 18:17:04 +0200460 {{},}
461}};
462
463INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
464
Willy Tarreau67b5a162019-08-11 16:38:56 +0200465/*
466 * Local variables:
467 * c-indent-level: 8
468 * c-basic-offset: 8
469 * End:
470 */