blob: 1d30f632d12211b41f174fbf8a69e6486cb4be7a [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
61 sink->name = name;
62 sink->desc = desc;
63 sink->fmt = fmt;
64 sink->type = SINK_TYPE_NEW;
65 /* set defaults for syslog ones */
66 sink->syslog_facility = 0;
67 sink->syslog_minlvl = 0;
68 sink->maxlen = MAX_SYSLOG_LEN;
69 /* 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);
129 free(sink);
130 goto fail;
131 }
132
133 sink->type = SINK_TYPE_BUFFER;
134 end:
135 return sink;
136 fail:
137 return NULL;
138}
139
Willy Tarreau67b5a162019-08-11 16:38:56 +0200140/* tries to send <nmsg> message parts (up to 8, ignored above) from message
141 * array <msg> to sink <sink>. Formating according to the sink's preference is
Willy Tarreau8f240232019-08-27 16:41:06 +0200142 * done here. Lost messages are NOT accounted for. It is preferable to call
143 * sink_write() instead which will also try to emit the number of dropped
144 * messages when there are any. It returns >0 if it could write anything,
145 * <=0 otherwise.
Willy Tarreau67b5a162019-08-11 16:38:56 +0200146 */
Willy Tarreau8f240232019-08-27 16:41:06 +0200147ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg)
Willy Tarreau67b5a162019-08-11 16:38:56 +0200148{
Willy Tarreau67b5a162019-08-11 16:38:56 +0200149 char short_hdr[4];
Willy Tarreaua1426de2019-08-27 14:21:02 +0200150 struct ist pfx[4];
151 size_t npfx = 0;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200152
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200153 if (sink->fmt == SINK_FMT_SHORT || sink->fmt == SINK_FMT_TIMED) {
Willy Tarreau67b5a162019-08-11 16:38:56 +0200154 short_hdr[0] = '<';
155 short_hdr[1] = '0' + sink->syslog_minlvl;
156 short_hdr[2] = '>';
157
Willy Tarreaua1426de2019-08-27 14:21:02 +0200158 pfx[npfx].ptr = short_hdr;
159 pfx[npfx].len = 3;
160 npfx++;
161 }
Willy Tarreau67b5a162019-08-11 16:38:56 +0200162
Willy Tarreau53ba9d92019-09-26 08:03:58 +0200163 if (sink->fmt == SINK_FMT_ISO || sink->fmt == SINK_FMT_TIMED) {
164 pfx[npfx].ptr = timeofday_as_iso_us(1);
165 pfx[npfx].len = 27;
166 npfx++;
167 }
168
Willy Tarreau973e6622019-08-20 11:57:52 +0200169 if (sink->type == SINK_TYPE_FD) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200170 return fd_write_frag_line(sink->ctx.fd, sink->maxlen, pfx, npfx, msg, nmsg, 1);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200171 }
172 else if (sink->type == SINK_TYPE_BUFFER) {
Willy Tarreau8f240232019-08-27 16:41:06 +0200173 return ring_write(sink->ctx.ring, sink->maxlen, pfx, npfx, msg, nmsg);
Willy Tarreau973e6622019-08-20 11:57:52 +0200174 }
Willy Tarreau8f240232019-08-27 16:41:06 +0200175 return 0;
176}
Willy Tarreau67b5a162019-08-11 16:38:56 +0200177
Willy Tarreau8f240232019-08-27 16:41:06 +0200178/* Tries to emit a message indicating the number of dropped events. In case of
179 * success, the amount of drops is reduced by as much. It's supposed to be
180 * called under an exclusive lock on the sink to avoid multiple produces doing
181 * the same. On success, >0 is returned, otherwise <=0 on failure.
182 */
183int sink_announce_dropped(struct sink *sink)
184{
185 unsigned int dropped;
186 struct buffer msg;
187 struct ist msgvec[1];
188 char logbuf[64];
189
190 while (unlikely((dropped = sink->ctx.dropped) > 0)) {
191 chunk_init(&msg, logbuf, sizeof(logbuf));
192 chunk_printf(&msg, "%u event%s dropped", dropped, dropped > 1 ? "s" : "");
193 msgvec[0] = ist2(msg.area, msg.data);
194 if (__sink_write(sink, msgvec, 1) <= 0)
195 return 0;
196 /* success! */
197 HA_ATOMIC_SUB(&sink->ctx.dropped, dropped);
198 }
199 return 1;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200200}
201
Willy Tarreau9f830d72019-08-26 18:17:04 +0200202/* parse the "show events" command, returns 1 if a message is returned, otherwise zero */
203static int cli_parse_show_events(char **args, char *payload, struct appctx *appctx, void *private)
204{
205 struct sink *sink;
Willy Tarreau1d181e42019-08-30 11:17:01 +0200206 int arg;
Willy Tarreau9f830d72019-08-26 18:17:04 +0200207
208 args++; // make args[1] the 1st arg
209
210 if (!*args[1]) {
211 /* no arg => report the list of supported sink */
Willy Tarreau1d181e42019-08-30 11:17:01 +0200212 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 +0200213 list_for_each_entry(sink, &sink_list, sink_list) {
214 chunk_appendf(&trash, " %-10s : type=%s, %u dropped, %s\n",
215 sink->name,
216 sink->type == SINK_TYPE_NEW ? "init" :
217 sink->type == SINK_TYPE_FD ? "fd" :
218 sink->type == SINK_TYPE_BUFFER ? "buffer" : "?",
219 sink->ctx.dropped, sink->desc);
220 }
221
222 trash.area[trash.data] = 0;
223 return cli_msg(appctx, LOG_WARNING, trash.area);
224 }
225
226 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
227 return 1;
228
229 sink = sink_find(args[1]);
230 if (!sink)
231 return cli_err(appctx, "No such event sink");
232
233 if (sink->type != SINK_TYPE_BUFFER)
234 return cli_msg(appctx, LOG_NOTICE, "Nothing to report for this sink");
235
Willy Tarreau1d181e42019-08-30 11:17:01 +0200236 for (arg = 2; *args[arg]; arg++) {
237 if (strcmp(args[arg], "-w") == 0)
238 appctx->ctx.cli.i0 |= 1; // wait mode
239 else if (strcmp(args[arg], "-n") == 0)
240 appctx->ctx.cli.i0 |= 2; // seek to new
241 else if (strcmp(args[arg], "-nw") == 0 || strcmp(args[arg], "-wn") == 0)
242 appctx->ctx.cli.i0 |= 3; // seek to new + wait
243 else
244 return cli_err(appctx, "unknown option");
245 }
Willy Tarreau9f830d72019-08-26 18:17:04 +0200246 return ring_attach_cli(sink->ctx.ring, appctx);
247}
248
Willy Tarreau973e6622019-08-20 11:57:52 +0200249static void sink_init()
250{
251 sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
252 sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
Willy Tarreauf8340e32019-09-26 08:05:15 +0200253 sink_new_buf("buf0", "in-memory ring buffer", SINK_FMT_TIMED, 1048576);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200254}
255
256static void sink_deinit()
257{
258 struct sink *sink, *sb;
259
260 list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
261 if (sink->type == SINK_TYPE_BUFFER)
262 ring_free(sink->ctx.ring);
263 LIST_DEL(&sink->sink_list);
264 free(sink);
265 }
Willy Tarreau973e6622019-08-20 11:57:52 +0200266}
267
268INITCALL0(STG_REGISTER, sink_init);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200269REGISTER_POST_DEINIT(sink_deinit);
Willy Tarreau973e6622019-08-20 11:57:52 +0200270
Willy Tarreau9f830d72019-08-26 18:17:04 +0200271static struct cli_kw_list cli_kws = {{ },{
272 { { "show", "events", NULL }, "show events [<sink>] : show event sink state", cli_parse_show_events, cli_io_handler_show_ring, cli_io_release_show_ring },
273 {{},}
274}};
275
276INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
277
Willy Tarreau67b5a162019-08-11 16:38:56 +0200278/*
279 * Local variables:
280 * c-indent-level: 8
281 * c-basic-offset: 8
282 * End:
283 */