blob: c65513a06334d8b7770495db06541b0bddb8661b [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
Emeric Brun99c453d2020-05-25 15:01:04 +020021#include <common/cfgparse.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020022#include <common/compat.h>
23#include <common/config.h>
24#include <common/ist.h>
25#include <common/mini-clist.h>
Willy Tarreau53ba9d92019-09-26 08:03:58 +020026#include <common/time.h>
Willy Tarreau9f830d72019-08-26 18:17:04 +020027#include <proto/cli.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020028#include <proto/log.h>
Willy Tarreau4ed23ca2019-08-23 15:47:49 +020029#include <proto/ring.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020030#include <proto/sink.h>
Willy Tarreau9f830d72019-08-26 18:17:04 +020031#include <proto/stream_interface.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020032
33struct list sink_list = LIST_HEAD_INIT(sink_list);
34
Emeric Brun99c453d2020-05-25 15:01:04 +020035struct sink *cfg_sink;
36
Willy Tarreau67b5a162019-08-11 16:38:56 +020037struct sink *sink_find(const char *name)
38{
39 struct sink *sink;
40
41 list_for_each_entry(sink, &sink_list, sink_list)
42 if (strcmp(sink->name, name) == 0)
43 return sink;
44 return NULL;
45}
46
47/* creates a new sink and adds it to the list, it's still generic and not fully
48 * initialized. Returns NULL on allocation failure. If another one already
49 * exists with the same name, it will be returned. The caller can detect it as
50 * a newly created one has type SINK_TYPE_NEW.
51 */
Willy Tarreau973e6622019-08-20 11:57:52 +020052static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt fmt)
Willy Tarreau67b5a162019-08-11 16:38:56 +020053{
54 struct sink *sink;
55
56 sink = sink_find(name);
57 if (sink)
58 goto end;
59
60 sink = malloc(sizeof(*sink));
61 if (!sink)
62 goto end;
63
Emeric Brun99c453d2020-05-25 15:01:04 +020064 sink->name = strdup(name);
65 sink->desc = strdup(desc);
Willy Tarreau67b5a162019-08-11 16:38:56 +020066 sink->fmt = fmt;
67 sink->type = SINK_TYPE_NEW;
Christopher Fauleta63a5c22019-11-15 15:10:12 +010068 sink->maxlen = BUFSIZE;
Willy Tarreau67b5a162019-08-11 16:38:56 +020069 /* address will be filled by the caller if needed */
Willy Tarreau973e6622019-08-20 11:57:52 +020070 sink->ctx.fd = -1;
Willy Tarreau67b5a162019-08-11 16:38:56 +020071 sink->ctx.dropped = 0;
72 HA_RWLOCK_INIT(&sink->ctx.lock);
73 LIST_ADDQ(&sink_list, &sink->sink_list);
74 end:
75 return sink;
76}
77
Willy Tarreau973e6622019-08-20 11:57:52 +020078/* creates a sink called <name> of type FD associated to fd <fd>, format <fmt>,
79 * and description <desc>. Returns NULL on allocation failure or conflict.
80 * Perfect duplicates are merged (same type, fd, and name).
81 */
82struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd)
83{
84 struct sink *sink;
85
86 sink = __sink_new(name, desc, fmt);
87 if (!sink || (sink->type == SINK_TYPE_FD && sink->ctx.fd == fd))
88 goto end;
89
90 if (sink->type != SINK_TYPE_NEW) {
91 sink = NULL;
92 goto end;
93 }
94
95 sink->type = SINK_TYPE_FD;
96 sink->ctx.fd = fd;
97 end:
98 return sink;
99}
100
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200101/* creates a sink called <name> of type BUF of size <size>, format <fmt>,
102 * and description <desc>. Returns NULL on allocation failure or conflict.
103 * Perfect duplicates are merged (same type and name). If sizes differ, the
104 * largest one is kept.
105 */
106struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt, size_t size)
107{
108 struct sink *sink;
109
110 sink = __sink_new(name, desc, fmt);
111 if (!sink)
112 goto fail;
113
114 if (sink->type == SINK_TYPE_BUFFER) {
115 /* such a buffer already exists, we may have to resize it */
116 if (!ring_resize(sink->ctx.ring, size))
117 goto fail;
118 goto end;
119 }
120
121 if (sink->type != SINK_TYPE_NEW) {
122 /* already exists of another type */
123 goto fail;
124 }
125
126 sink->ctx.ring = ring_new(size);
127 if (!sink->ctx.ring) {
128 LIST_DEL(&sink->sink_list);
Emeric Brun99c453d2020-05-25 15:01:04 +0200129 free(sink->name);
130 free(sink->desc);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200131 free(sink);
132 goto fail;
133 }
134
135 sink->type = SINK_TYPE_BUFFER;
136 end:
137 return sink;
138 fail:
139 return NULL;
140}
141
Willy Tarreau67b5a162019-08-11 16:38:56 +0200142/* tries to send <nmsg> message parts (up to 8, ignored above) from message
143 * array <msg> to sink <sink>. Formating according to the sink's preference is
Willy Tarreau8f240232019-08-27 16:41:06 +0200144 * done here. Lost messages are NOT accounted for. It is preferable to call
145 * sink_write() instead which will also try to emit the number of dropped
146 * messages when there are any. It returns >0 if it could write anything,
147 * <=0 otherwise.
Willy Tarreau67b5a162019-08-11 16:38:56 +0200148 */
Emeric Brunbd163812020-05-06 14:33:46 +0200149ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg,
150 int level, int facility, struct ist *tag,
151 struct ist *pid, struct ist *sd)
Willy Tarreau67b5a162019-08-11 16:38:56 +0200152{
Emeric Brunbd163812020-05-06 14:33:46 +0200153 int log_format;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200154 char short_hdr[4];
Emeric Brunbd163812020-05-06 14:33:46 +0200155 struct ist pfx[6];
Willy Tarreaua1426de2019-08-27 14:21:02 +0200156 size_t npfx = 0;
Emeric Brunbd163812020-05-06 14:33:46 +0200157 char *hdr_ptr;
158 int fac_level;
159
160 if (sink->fmt == SINK_FMT_RAW)
161 goto send;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200162
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200163 if (sink->fmt == SINK_FMT_SHORT || sink->fmt == SINK_FMT_TIMED) {
Willy Tarreau67b5a162019-08-11 16:38:56 +0200164 short_hdr[0] = '<';
Emeric Brunbd163812020-05-06 14:33:46 +0200165 short_hdr[1] = '0' + level;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200166 short_hdr[2] = '>';
167
Willy Tarreaua1426de2019-08-27 14:21:02 +0200168 pfx[npfx].ptr = short_hdr;
169 pfx[npfx].len = 3;
170 npfx++;
Emeric Brunbd163812020-05-06 14:33:46 +0200171 if (sink->fmt == SINK_FMT_SHORT)
172 goto send;
Willy Tarreaua1426de2019-08-27 14:21:02 +0200173 }
Willy Tarreau67b5a162019-08-11 16:38:56 +0200174
Emeric Brunbd163812020-05-06 14:33:46 +0200175
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200176 if (sink->fmt == SINK_FMT_ISO || sink->fmt == SINK_FMT_TIMED) {
177 pfx[npfx].ptr = timeofday_as_iso_us(1);
178 pfx[npfx].len = 27;
179 npfx++;
Emeric Brunbd163812020-05-06 14:33:46 +0200180 goto send;
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200181 }
Emeric Brunbd163812020-05-06 14:33:46 +0200182 else if (sink->fmt == SINK_FMT_RFC5424) {
183 pfx[npfx].ptr = logheader_rfc5424;
184 pfx[npfx].len = update_log_hdr_rfc5424(date.tv_sec) - pfx[npfx].ptr;
185 log_format = LOG_FORMAT_RFC5424;
186 }
187 else {
188 pfx[npfx].ptr = logheader;
189 pfx[npfx].len = update_log_hdr(date.tv_sec) - pfx[npfx].ptr;
190 log_format = LOG_FORMAT_RFC3164;
191 sd = NULL;
192 }
193
194 fac_level = (facility << 3) + level;
195 hdr_ptr = pfx[npfx].ptr + 3; /* last digit of the log level */
196 do {
197 *hdr_ptr = '0' + fac_level % 10;
198 fac_level /= 10;
199 hdr_ptr--;
200 } while (fac_level && hdr_ptr > pfx[npfx].ptr);
201 *hdr_ptr = '<';
202 pfx[npfx].len -= hdr_ptr - pfx[npfx].ptr;
203 pfx[npfx].ptr = hdr_ptr;
204 npfx++;
205
206 if (tag && tag->len) {
207 pfx[npfx].ptr = tag->ptr;
208 pfx[npfx].len = tag->len;
209 npfx++;
210 }
211 pfx[npfx].ptr = get_format_pid_sep1(log_format, &pfx[npfx].len);
212 if (pfx[npfx].len)
213 npfx++;
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200214
Emeric Brunbd163812020-05-06 14:33:46 +0200215 if (pid && pid->len) {
216 pfx[npfx].ptr = pid->ptr;
217 pfx[npfx].len = pid->len;
218 npfx++;
219 }
220
221 pfx[npfx].ptr = get_format_pid_sep2(log_format, &pfx[npfx].len);
222 if (pfx[npfx].len)
223 npfx++;
224
225 if (sd && sd->len) {
226 pfx[npfx].ptr = sd->ptr;
227 pfx[npfx].len = sd->len;
228 npfx++;
229 }
230
231send:
Willy Tarreau973e6622019-08-20 11:57:52 +0200232 if (sink->type == SINK_TYPE_FD) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200233 return fd_write_frag_line(sink->ctx.fd, sink->maxlen, pfx, npfx, msg, nmsg, 1);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200234 }
235 else if (sink->type == SINK_TYPE_BUFFER) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200236 return ring_write(sink->ctx.ring, sink->maxlen, pfx, npfx, msg, nmsg);
Willy Tarreau973e6622019-08-20 11:57:52 +0200237 }
Willy Tarreau8f240232019-08-27 16:41:06 +0200238 return 0;
239}
Willy Tarreau67b5a162019-08-11 16:38:56 +0200240
Willy Tarreau8f240232019-08-27 16:41:06 +0200241/* Tries to emit a message indicating the number of dropped events. In case of
242 * success, the amount of drops is reduced by as much. It's supposed to be
243 * called under an exclusive lock on the sink to avoid multiple produces doing
244 * the same. On success, >0 is returned, otherwise <=0 on failure.
245 */
Emeric Brunbd163812020-05-06 14:33:46 +0200246int sink_announce_dropped(struct sink *sink, int facility, struct ist *pid)
Willy Tarreau8f240232019-08-27 16:41:06 +0200247{
248 unsigned int dropped;
249 struct buffer msg;
250 struct ist msgvec[1];
251 char logbuf[64];
Emeric Brunbd163812020-05-06 14:33:46 +0200252 struct ist sd;
253 struct ist tag;
Willy Tarreau8f240232019-08-27 16:41:06 +0200254
255 while (unlikely((dropped = sink->ctx.dropped) > 0)) {
256 chunk_init(&msg, logbuf, sizeof(logbuf));
257 chunk_printf(&msg, "%u event%s dropped", dropped, dropped > 1 ? "s" : "");
258 msgvec[0] = ist2(msg.area, msg.data);
Emeric Brunbd163812020-05-06 14:33:46 +0200259
260 sd.ptr = default_rfc5424_sd_log_format;
261 sd.len = 2;
262 tag.ptr = global.log_tag.area;
263 tag.len = global.log_tag.data;
264 if (__sink_write(sink, msgvec, 1, LOG_NOTICE, facility, &tag, pid, &sd) <= 0)
Willy Tarreau8f240232019-08-27 16:41:06 +0200265 return 0;
266 /* success! */
267 HA_ATOMIC_SUB(&sink->ctx.dropped, dropped);
268 }
269 return 1;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200270}
271
Willy Tarreau9f830d72019-08-26 18:17:04 +0200272/* parse the "show events" command, returns 1 if a message is returned, otherwise zero */
273static int cli_parse_show_events(char **args, char *payload, struct appctx *appctx, void *private)
274{
275 struct sink *sink;
Willy Tarreau1d181e42019-08-30 11:17:01 +0200276 int arg;
Willy Tarreau9f830d72019-08-26 18:17:04 +0200277
278 args++; // make args[1] the 1st arg
279
280 if (!*args[1]) {
281 /* no arg => report the list of supported sink */
Willy Tarreau1d181e42019-08-30 11:17:01 +0200282 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 +0200283 list_for_each_entry(sink, &sink_list, sink_list) {
284 chunk_appendf(&trash, " %-10s : type=%s, %u dropped, %s\n",
285 sink->name,
286 sink->type == SINK_TYPE_NEW ? "init" :
287 sink->type == SINK_TYPE_FD ? "fd" :
288 sink->type == SINK_TYPE_BUFFER ? "buffer" : "?",
289 sink->ctx.dropped, sink->desc);
290 }
291
292 trash.area[trash.data] = 0;
293 return cli_msg(appctx, LOG_WARNING, trash.area);
294 }
295
296 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
297 return 1;
298
299 sink = sink_find(args[1]);
300 if (!sink)
301 return cli_err(appctx, "No such event sink");
302
303 if (sink->type != SINK_TYPE_BUFFER)
304 return cli_msg(appctx, LOG_NOTICE, "Nothing to report for this sink");
305
Willy Tarreau1d181e42019-08-30 11:17:01 +0200306 for (arg = 2; *args[arg]; arg++) {
307 if (strcmp(args[arg], "-w") == 0)
308 appctx->ctx.cli.i0 |= 1; // wait mode
309 else if (strcmp(args[arg], "-n") == 0)
310 appctx->ctx.cli.i0 |= 2; // seek to new
311 else if (strcmp(args[arg], "-nw") == 0 || strcmp(args[arg], "-wn") == 0)
312 appctx->ctx.cli.i0 |= 3; // seek to new + wait
313 else
314 return cli_err(appctx, "unknown option");
315 }
Willy Tarreau9f830d72019-08-26 18:17:04 +0200316 return ring_attach_cli(sink->ctx.ring, appctx);
317}
318
Emeric Brun99c453d2020-05-25 15:01:04 +0200319/*
320 * Parse "ring" section and create corresponding sink buffer.
321 *
322 * The function returns 0 in success case, otherwise, it returns error
323 * flags.
324 */
325int cfg_parse_ring(const char *file, int linenum, char **args, int kwm)
326{
327 int err_code = 0;
328 const char *inv;
329 size_t size = BUFSIZE;
330
331 if (strcmp(args[0], "ring") == 0) { /* new peers section */
332 if (!*args[1]) {
333 ha_alert("parsing [%s:%d] : missing ring name.\n", file, linenum);
334 err_code |= ERR_ALERT | ERR_FATAL;
335 goto err;
336 }
337
338 inv = invalid_char(args[1]);
339 if (inv) {
340 ha_alert("parsing [%s:%d] : invalid ring name '%s' (character '%c' is not permitted).\n", file, linenum, args[1], *inv);
341 err_code |= ERR_ALERT | ERR_FATAL;
342 goto err;
343 }
344
345 if (sink_find(args[1])) {
346 ha_alert("parsing [%s:%d] : sink named '%s' already exists.\n", file, linenum, args[1]);
347 err_code |= ERR_ALERT | ERR_FATAL;
348 goto err;
349 }
350
351 cfg_sink = sink_new_buf(args[1], args[1] , SINK_FMT_RAW, size);
352 if (!cfg_sink || cfg_sink->type != SINK_TYPE_BUFFER) {
353 ha_alert("parsing [%s:%d] : unable to create a new sink buffer for ring '%s'.\n", file, linenum, args[1]);
354 err_code |= ERR_ALERT | ERR_FATAL;
355 goto err;
356 }
357 }
358 else if (strcmp(args[0], "size") == 0) {
359 size = atol(args[1]);
360 if (!size) {
361 ha_alert("parsing [%s:%d] : invalid size '%s' for new sink buffer.\n", file, linenum, args[1]);
362 err_code |= ERR_ALERT | ERR_FATAL;
363 goto err;
364 }
365
366 if (!cfg_sink || (cfg_sink->type != SINK_TYPE_BUFFER)
367 || !ring_resize(cfg_sink->ctx.ring, size)) {
368 ha_alert("parsing [%s:%d] : fail to set sink buffer size '%s'.\n", file, linenum, args[1]);
369 err_code |= ERR_ALERT | ERR_FATAL;
370 goto err;
371 }
372 }
373 else if (strcmp(args[0],"format") == 0) {
374 if (!cfg_sink) {
375 ha_alert("parsing [%s:%d] : unable to set format '%s'.\n", file, linenum, args[1]);
376 err_code |= ERR_ALERT | ERR_FATAL;
377 goto err;
378 }
379
380 if (strcmp(args[1], "raw") == 0) {
381 cfg_sink->fmt = SINK_FMT_RAW;
382 }
383 else if (strcmp(args[1], "short") == 0) {
384 cfg_sink->fmt = SINK_FMT_SHORT;
385 }
386 else if (strcmp(args[1], "iso") == 0) {
387 cfg_sink->fmt = SINK_FMT_ISO;
388 }
389 else if (strcmp(args[1], "timed") == 0) {
390 cfg_sink->fmt = SINK_FMT_TIMED;
391 }
392 else if (strcmp(args[1], "rfc3164") == 0) {
393 cfg_sink->fmt = SINK_FMT_RFC3164;
394 }
395 else if (strcmp(args[1], "rfc5424") == 0) {
396 cfg_sink->fmt = SINK_FMT_RFC5424;
397 }
398 else {
399 ha_alert("parsing [%s:%d] : unknown format '%s'.\n", file, linenum, args[1]);
400 err_code |= ERR_ALERT | ERR_FATAL;
401 goto err;
402 }
403 }
404 else if (strcmp(args[0],"maxlen") == 0) {
405 if (!cfg_sink) {
406 ha_alert("parsing [%s:%d] : unable to set event max length '%s'.\n", file, linenum, args[1]);
407 err_code |= ERR_ALERT | ERR_FATAL;
408 goto err;
409 }
410
411 cfg_sink->maxlen = atol(args[1]);
412 if (!cfg_sink->maxlen) {
413 ha_alert("parsing [%s:%d] : invalid size '%s' for new sink buffer.\n", file, linenum, args[1]);
414 err_code |= ERR_ALERT | ERR_FATAL;
415 goto err;
416 }
417 }
418 else if (strcmp(args[0],"description") == 0) {
419 if (!cfg_sink) {
420 ha_alert("parsing [%s:%d] : unable to set description '%s'.\n", file, linenum, args[1]);
421 err_code |= ERR_ALERT | ERR_FATAL;
422 goto err;
423 }
424
425 if (!*args[1]) {
426 ha_alert("parsing [%s:%d] : missing ring description text.\n", file, linenum);
427 err_code |= ERR_ALERT | ERR_FATAL;
428 goto err;
429 }
430
431 free(cfg_sink->desc);
432
433 cfg_sink->desc = strdup(args[1]);
434 if (!cfg_sink->desc) {
435 ha_alert("parsing [%s:%d] : fail to set description '%s'.\n", file, linenum, args[1]);
436 err_code |= ERR_ALERT | ERR_FATAL;
437 goto err;
438 }
439 }
440
441err:
442 return err_code;
443}
444
445/*
446 * Post parsing "ring" section.
447 *
448 * The function returns 0 in success case, otherwise, it returns error
449 * flags.
450 */
451int cfg_post_parse_ring()
452{
453 int err_code = 0;
454
455 if (cfg_sink && (cfg_sink->type == SINK_TYPE_BUFFER)) {
456 if (cfg_sink->maxlen > b_size(&cfg_sink->ctx.ring->buf)) {
457 ha_warning("ring '%s' event max length '%u' exceeds size, forced to size '%lu'.\n",
458 cfg_sink->name, cfg_sink->maxlen, b_size(&cfg_sink->ctx.ring->buf));
459 cfg_sink->maxlen = b_size(&cfg_sink->ctx.ring->buf);
460 err_code |= ERR_ALERT;
461 }
462 }
463
464 cfg_sink = NULL;
465
466 return err_code;
467}
468
469/* resolve sink names at end of config. Returns 0 on success otherwise error
470 * flags.
471*/
472int post_sink_resolve()
473{
474 int err_code = 0;
475 struct logsrv *logsrv, *logb;
476 struct sink *sink;
477 struct proxy *px;
478
479 list_for_each_entry_safe(logsrv, logb, &global.logsrvs, list) {
480 if (logsrv->type == LOG_TARGET_BUFFER) {
481 sink = sink_find(logsrv->ring_name);
482 if (!sink || sink->type != SINK_TYPE_BUFFER) {
483 ha_alert("global log server uses unkown ring named '%s'.\n", logsrv->ring_name);
484 err_code |= ERR_ALERT | ERR_FATAL;
485 }
486 logsrv->sink = sink;
487 }
488 }
489
490 for (px = proxies_list; px; px = px->next) {
491 list_for_each_entry_safe(logsrv, logb, &px->logsrvs, list) {
492 if (logsrv->type == LOG_TARGET_BUFFER) {
493 sink = sink_find(logsrv->ring_name);
494 if (!sink || sink->type != SINK_TYPE_BUFFER) {
495 ha_alert("proxy '%s' log server uses unkown ring named '%s'.\n", px->id, logsrv->ring_name);
496 err_code |= ERR_ALERT | ERR_FATAL;
497 }
498 logsrv->sink = sink;
499 }
500 }
501 }
502 return err_code;
503}
504
505
Willy Tarreau973e6622019-08-20 11:57:52 +0200506static void sink_init()
507{
508 sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
509 sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
Willy Tarreauf8340e32019-09-26 08:05:15 +0200510 sink_new_buf("buf0", "in-memory ring buffer", SINK_FMT_TIMED, 1048576);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200511}
512
513static void sink_deinit()
514{
515 struct sink *sink, *sb;
516
517 list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
518 if (sink->type == SINK_TYPE_BUFFER)
519 ring_free(sink->ctx.ring);
520 LIST_DEL(&sink->sink_list);
Emeric Brun99c453d2020-05-25 15:01:04 +0200521 free(sink->name);
522 free(sink->desc);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200523 free(sink);
524 }
Willy Tarreau973e6622019-08-20 11:57:52 +0200525}
526
527INITCALL0(STG_REGISTER, sink_init);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200528REGISTER_POST_DEINIT(sink_deinit);
Willy Tarreau973e6622019-08-20 11:57:52 +0200529
Willy Tarreau9f830d72019-08-26 18:17:04 +0200530static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaufcf94982019-11-15 15:07:21 +0100531 { { "show", "events", NULL }, "show events [<sink>] : show event sink state", cli_parse_show_events, NULL, NULL },
Willy Tarreau9f830d72019-08-26 18:17:04 +0200532 {{},}
533}};
534
535INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
536
Emeric Brun99c453d2020-05-25 15:01:04 +0200537/* config parsers for this section */
538REGISTER_CONFIG_SECTION("ring", cfg_parse_ring, cfg_post_parse_ring);
539REGISTER_POST_CHECK(post_sink_resolve);
540
Willy Tarreau67b5a162019-08-11 16:38:56 +0200541/*
542 * Local variables:
543 * c-indent-level: 8
544 * c-basic-offset: 8
545 * End:
546 */