blob: 8d2ce91830039ffb06a621815022a2d02e02c33d [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 Tarreau799e9ed2019-08-27 10:34:32 +020021#include <fcntl.h>
22#include <unistd.h>
Willy Tarreau67b5a162019-08-11 16:38:56 +020023#include <common/compat.h>
24#include <common/config.h>
25#include <common/ist.h>
26#include <common/mini-clist.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
35struct sink *sink_find(const char *name)
36{
37 struct sink *sink;
38
39 list_for_each_entry(sink, &sink_list, sink_list)
40 if (strcmp(sink->name, name) == 0)
41 return sink;
42 return NULL;
43}
44
45/* creates a new sink and adds it to the list, it's still generic and not fully
46 * initialized. Returns NULL on allocation failure. If another one already
47 * exists with the same name, it will be returned. The caller can detect it as
48 * a newly created one has type SINK_TYPE_NEW.
49 */
Willy Tarreau973e6622019-08-20 11:57:52 +020050static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt fmt)
Willy Tarreau67b5a162019-08-11 16:38:56 +020051{
52 struct sink *sink;
53
54 sink = sink_find(name);
55 if (sink)
56 goto end;
57
58 sink = malloc(sizeof(*sink));
59 if (!sink)
60 goto end;
61
62 sink->name = name;
63 sink->desc = desc;
64 sink->fmt = fmt;
65 sink->type = SINK_TYPE_NEW;
66 /* set defaults for syslog ones */
67 sink->syslog_facility = 0;
68 sink->syslog_minlvl = 0;
69 sink->maxlen = MAX_SYSLOG_LEN;
70 /* address will be filled by the caller if needed */
Willy Tarreau973e6622019-08-20 11:57:52 +020071 sink->ctx.fd = -1;
Willy Tarreau67b5a162019-08-11 16:38:56 +020072 sink->ctx.dropped = 0;
73 HA_RWLOCK_INIT(&sink->ctx.lock);
74 LIST_ADDQ(&sink_list, &sink->sink_list);
75 end:
76 return sink;
77}
78
Willy Tarreau973e6622019-08-20 11:57:52 +020079/* creates a sink called <name> of type FD associated to fd <fd>, format <fmt>,
80 * and description <desc>. Returns NULL on allocation failure or conflict.
81 * Perfect duplicates are merged (same type, fd, and name).
82 */
83struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd)
84{
85 struct sink *sink;
86
87 sink = __sink_new(name, desc, fmt);
88 if (!sink || (sink->type == SINK_TYPE_FD && sink->ctx.fd == fd))
89 goto end;
90
91 if (sink->type != SINK_TYPE_NEW) {
92 sink = NULL;
93 goto end;
94 }
95
Willy Tarreau799e9ed2019-08-27 10:34:32 +020096 /* FD not yet initialized to non-blocking mode.
97 * DON'T DO IT ON A TERMINAL!
98 */
99 if (!isatty(fd))
100 fcntl(fd, F_SETFL, O_NONBLOCK);
Willy Tarreau973e6622019-08-20 11:57:52 +0200101 sink->type = SINK_TYPE_FD;
102 sink->ctx.fd = fd;
103 end:
104 return sink;
105}
106
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200107/* creates a sink called <name> of type BUF of size <size>, format <fmt>,
108 * and description <desc>. Returns NULL on allocation failure or conflict.
109 * Perfect duplicates are merged (same type and name). If sizes differ, the
110 * largest one is kept.
111 */
112struct sink *sink_new_buf(const char *name, const char *desc, enum sink_fmt fmt, size_t size)
113{
114 struct sink *sink;
115
116 sink = __sink_new(name, desc, fmt);
117 if (!sink)
118 goto fail;
119
120 if (sink->type == SINK_TYPE_BUFFER) {
121 /* such a buffer already exists, we may have to resize it */
122 if (!ring_resize(sink->ctx.ring, size))
123 goto fail;
124 goto end;
125 }
126
127 if (sink->type != SINK_TYPE_NEW) {
128 /* already exists of another type */
129 goto fail;
130 }
131
132 sink->ctx.ring = ring_new(size);
133 if (!sink->ctx.ring) {
134 LIST_DEL(&sink->sink_list);
135 free(sink);
136 goto fail;
137 }
138
139 sink->type = SINK_TYPE_BUFFER;
140 end:
141 return sink;
142 fail:
143 return NULL;
144}
145
Willy Tarreau67b5a162019-08-11 16:38:56 +0200146/* tries to send <nmsg> message parts (up to 8, ignored above) from message
147 * array <msg> to sink <sink>. Formating according to the sink's preference is
Willy Tarreau8f240232019-08-27 16:41:06 +0200148 * done here. Lost messages are NOT accounted for. It is preferable to call
149 * sink_write() instead which will also try to emit the number of dropped
150 * messages when there are any. It returns >0 if it could write anything,
151 * <=0 otherwise.
Willy Tarreau67b5a162019-08-11 16:38:56 +0200152 */
Willy Tarreau8f240232019-08-27 16:41:06 +0200153ssize_t __sink_write(struct sink *sink, const struct ist msg[], size_t nmsg)
Willy Tarreau67b5a162019-08-11 16:38:56 +0200154{
Willy Tarreau67b5a162019-08-11 16:38:56 +0200155 char short_hdr[4];
Willy Tarreaua1426de2019-08-27 14:21:02 +0200156 struct ist pfx[4];
157 size_t npfx = 0;
Willy Tarreau67b5a162019-08-11 16:38:56 +0200158
159 if (sink->fmt == SINK_FMT_SHORT) {
160 short_hdr[0] = '<';
161 short_hdr[1] = '0' + sink->syslog_minlvl;
162 short_hdr[2] = '>';
163
Willy Tarreaua1426de2019-08-27 14:21:02 +0200164 pfx[npfx].ptr = short_hdr;
165 pfx[npfx].len = 3;
166 npfx++;
167 }
Willy Tarreau67b5a162019-08-11 16:38:56 +0200168
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;
206
207 args++; // make args[1] the 1st arg
208
209 if (!*args[1]) {
210 /* no arg => report the list of supported sink */
211 chunk_printf(&trash, "Supported events sinks:\n");
212 list_for_each_entry(sink, &sink_list, sink_list) {
213 chunk_appendf(&trash, " %-10s : type=%s, %u dropped, %s\n",
214 sink->name,
215 sink->type == SINK_TYPE_NEW ? "init" :
216 sink->type == SINK_TYPE_FD ? "fd" :
217 sink->type == SINK_TYPE_BUFFER ? "buffer" : "?",
218 sink->ctx.dropped, sink->desc);
219 }
220
221 trash.area[trash.data] = 0;
222 return cli_msg(appctx, LOG_WARNING, trash.area);
223 }
224
225 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
226 return 1;
227
228 sink = sink_find(args[1]);
229 if (!sink)
230 return cli_err(appctx, "No such event sink");
231
232 if (sink->type != SINK_TYPE_BUFFER)
233 return cli_msg(appctx, LOG_NOTICE, "Nothing to report for this sink");
234
235 return ring_attach_cli(sink->ctx.ring, appctx);
236}
237
Willy Tarreau973e6622019-08-20 11:57:52 +0200238static void sink_init()
239{
240 sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
241 sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200242 sink_new_buf("buf0", "in-memory ring buffer", SINK_FMT_RAW, 1048576);
243}
244
245static void sink_deinit()
246{
247 struct sink *sink, *sb;
248
249 list_for_each_entry_safe(sink, sb, &sink_list, sink_list) {
250 if (sink->type == SINK_TYPE_BUFFER)
251 ring_free(sink->ctx.ring);
252 LIST_DEL(&sink->sink_list);
253 free(sink);
254 }
Willy Tarreau973e6622019-08-20 11:57:52 +0200255}
256
257INITCALL0(STG_REGISTER, sink_init);
Willy Tarreau4ed23ca2019-08-23 15:47:49 +0200258REGISTER_POST_DEINIT(sink_deinit);
Willy Tarreau973e6622019-08-20 11:57:52 +0200259
Willy Tarreau9f830d72019-08-26 18:17:04 +0200260static struct cli_kw_list cli_kws = {{ },{
261 { { "show", "events", NULL }, "show events [<sink>] : show event sink state", cli_parse_show_events, cli_io_handler_show_ring, cli_io_release_show_ring },
262 {{},}
263}};
264
265INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
266
Willy Tarreau67b5a162019-08-11 16:38:56 +0200267/*
268 * Local variables:
269 * c-indent-level: 8
270 * c-basic-offset: 8
271 * End:
272 */