vinyl-cache/bin/vinyltest/vtest2/src/vtc_syslog.c
0
/*-
1
 * Copyright (c) 2008-2010 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Frédéric Lécaille <flecaille@haproxy.com>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#include "config.h"
31
32
#include <sys/socket.h>
33
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
39
#include "vtc.h"
40
41
#include "vsa.h"
42
#include "vtcp.h"
43
#include "vudp.h"
44
#include "vre.h"
45
46
struct syslog_srv {
47
        unsigned                        magic;
48
#define SYSLOG_SRV_MAGIC                0xbf28a692
49
        char                            *name;
50
        struct vtclog                   *vl;
51
        VTAILQ_ENTRY(syslog_srv)        list;
52
        char                            run;
53
54
        int                             repeat;
55
        char                            *spec;
56
57
        int                             sock;
58
        char                            bind[256];
59
        int                             lvl;
60
61
        pthread_t                       tp;
62
        ssize_t                         rxbuf_left;
63
        size_t                          rxbuf_sz;
64
        char                            *rxbuf;
65
        vtim_dur                        timeout;
66
};
67
68
static pthread_mutex_t                  syslog_mtx;
69
70
static VTAILQ_HEAD(, syslog_srv)        syslogs =
71
    VTAILQ_HEAD_INITIALIZER(syslogs);
72
73
#define SYSLOGCMDS \
74
        CMD_SYSLOG(expect) \
75
        CMD_SYSLOG(recv)
76
77
#define CMD_SYSLOG(nm) static cmd_f cmd_syslog_##nm;
78
SYSLOGCMDS
79
#undef CMD_SYSLOG
80
81
static const struct cmds syslog_cmds[] = {
82
#define CMD_SYSLOG(n) { CMDS_MAGIC, #n, cmd_syslog_##n },
83
SYSLOGCMDS
84
#undef CMD_SYSLOG
85
        { CMDS_MAGIC, NULL, NULL }
86
};
87
88
static const char * const syslog_levels[] = {
89
        "emerg",
90
        "alert",
91
        "crit",
92
        "err",
93
        "warning",
94
        "notice",
95
        "info",
96
        "debug",
97
        NULL,
98
};
99
100
static int
101 10
get_syslog_level(struct vtclog *vl, const char *lvl)
102
{
103
        int i;
104
105 65
        for (i = 0; syslog_levels[i]; i++)
106 65
                if (!strcmp(lvl, syslog_levels[i]))
107 10
                        return (i);
108 0
        vtc_fatal(vl, "wrong syslog level '%s'\n", lvl);
109
}
110
111
/**********************************************************************
112
 * Allocate and initialize a syslog
113
 */
114
115
static struct syslog_srv *
116 5
syslog_new(const char *name, struct vtclog *vl)
117
{
118
        struct syslog_srv *s;
119
120 5
        VTC_CHECK_NAME(vl, name, "Syslog", 'S');
121 5
        ALLOC_OBJ(s, SYSLOG_SRV_MAGIC);
122 5
        AN(s);
123 5
        REPLACE(s->name, name);
124 5
        s->vl = vtc_logopen("%s", s->name);
125 5
        AN(s->vl);
126 5
        vtc_log_set_cmd(s->vl, syslog_cmds);
127
128 5
        bprintf(s->bind, "%s", default_listen_addr);
129 5
        s->repeat = 1;
130 5
        s->sock = -1;
131 5
        s->lvl = -1;
132 5
        s->timeout = vtc_maxdur * .5;           // XXX
133
134 5
        vl = vtc_logopen("%s", s->name);
135 5
        AN(vl);
136
137 5
        s->rxbuf_sz = s->rxbuf_left = 2048*1024;
138 5
        s->rxbuf = malloc(s->rxbuf_sz);         /* XXX */
139 5
        AN(s->rxbuf);
140
141 5
        PTOK(pthread_mutex_lock(&syslog_mtx));
142 5
        VTAILQ_INSERT_TAIL(&syslogs, s, list);
143 5
        PTOK(pthread_mutex_unlock(&syslog_mtx));
144 5
        return (s);
145
}
146
147
/**********************************************************************
148
 * Clean up a syslog
149
 */
150
151
static void
152 5
syslog_delete(struct syslog_srv *s)
153
{
154
155 5
        CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
156 5
        macro_undef(s->vl, s->name, "addr");
157 5
        macro_undef(s->vl, s->name, "port");
158 5
        macro_undef(s->vl, s->name, "sock");
159 5
        vtc_logclose(s->vl);
160 5
        free(s->name);
161 5
        free(s->rxbuf);
162
        /* XXX: MEMLEAK (?) (VSS ??) */
163 5
        FREE_OBJ(s);
164 5
}
165
166
static void
167 5
syslog_rx(const struct syslog_srv *s, int lvl)
168
{
169
        ssize_t ret;
170
171 5
        while (!vtc_error) {
172
                /* Pointers to syslog priority value (see <PRIVAL>, rfc5424). */
173
                char *prib, *prie, *end;
174
                unsigned int prival;
175
176 5
                VTCP_set_read_timeout(s->sock, s->timeout);
177
178 5
                ret = recv(s->sock, s->rxbuf, s->rxbuf_sz - 1, 0);
179 5
                if (ret < 0) {
180 0
                        if (errno == EINTR || errno == EAGAIN)
181 0
                                continue;
182
183 0
                        vtc_fatal(s->vl,
184
                            "%s: recv failed (fd: %d read: %s", __func__,
185 0
                            s->sock, strerror(errno));
186
                }
187 5
                if (ret == 0)
188 0
                        vtc_fatal(s->vl,
189
                            "syslog rx timeout (fd: %d %.3fs ret: %zd)",
190 0
                            s->sock, s->timeout, ret);
191
192 5
                s->rxbuf[ret] = '\0';
193 5
                vtc_dump(s->vl, 4, "syslog", s->rxbuf, ret);
194
195 5
                prib = s->rxbuf;
196 5
                if (*prib++ != '<')
197 0
                        vtc_fatal(s->vl, "syslog PRI, no '<'");
198 5
                prie = strchr(prib, '>');
199 5
                if (prie == NULL)
200 0
                        vtc_fatal(s->vl, "syslog PRI, no '>'");
201
202 5
                prival = strtoul(prib, &end, 10);
203 5
                if (end != prie)
204 0
                        vtc_fatal(s->vl, "syslog PRI, bad number");
205
206 5
                if (lvl >= 0 && lvl == (prival & 0x7))
207 5
                        return;
208
        }
209 5
}
210
211
/**********************************************************************
212
 * Syslog server bind
213
 */
214
215
static void
216 5
syslog_bind(struct syslog_srv *s)
217
{
218
        const char *err;
219
        char aaddr[VTCP_ADDRBUFSIZE];
220
        char aport[VTCP_PORTBUFSIZE];
221 5
        char buf[vsa_suckaddr_len];
222
        const struct suckaddr *sua;
223
224 5
        CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
225
226 5
        if (s->sock >= 0)
227 0
                VUDP_close(&s->sock);
228 5
        s->sock = VUDP_bind_on(s->bind, "0", &err);
229 5
        if (err != NULL)
230 0
                vtc_fatal(s->vl,
231
                    "Syslog server bind address (%s) cannot be resolved: %s",
232 0
                    s->bind, err);
233 5
        assert(s->sock > 0);
234 5
        sua = VSA_getsockname(s->sock, buf, sizeof buf);
235 5
        AN(sua);
236 5
        VTCP_name(sua, aaddr, sizeof aaddr, aport, sizeof aport);
237 5
        macro_def(s->vl, s->name, "addr", "%s", aaddr);
238 5
        macro_def(s->vl, s->name, "port", "%s", aport);
239 5
        if (VSA_Get_Proto(sua) == AF_INET)
240 5
                macro_def(s->vl, s->name, "sock", "%s:%s", aaddr, aport);
241
        else
242 0
                macro_def(s->vl, s->name, "sock", "[%s]:%s", aaddr, aport);
243
        /* Record the actual port, and reuse it on subsequent starts */
244 5
        bprintf(s->bind, "%s %s", aaddr, aport);
245 5
}
246
247
static void v_matchproto_(cmd_f)
248 5
cmd_syslog_expect(CMD_ARGS)
249
{
250
        struct syslog_srv *s;
251
        struct vsb vsb[1];
252
        vre_t *vre;
253
        int error, erroroffset, i, ret;
254
        char *cmp, *spec, errbuf[VRE_ERROR_LEN];
255
256 5
        (void)vl;
257 5
        CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC);
258 5
        AZ(strcmp(av[0], "expect"));
259 5
        av++;
260
261 5
        cmp = av[0];
262 5
        spec = av[1];
263 5
        AN(cmp);
264 5
        AN(spec);
265 5
        AZ(av[2]);
266
267 5
        assert(!strcmp(cmp, "~") || !strcmp(cmp, "!~"));
268
269 5
        vre = VRE_compile(spec, 0, &error, &erroroffset, 1);
270 5
        if (vre == NULL) {
271 0
                AN(VSB_init(vsb, errbuf, sizeof errbuf));
272 0
                AZ(VRE_error(vsb, error));
273 0
                AZ(VSB_finish(vsb));
274 0
                VSB_fini(vsb);
275 0
                vtc_fatal(s->vl, "REGEXP error: '%s' (@%d) (%s)",
276 0
                    errbuf, erroroffset, spec);
277
        }
278
279 5
        i = VRE_match(vre, s->rxbuf, 0, 0, NULL);
280
281 5
        VRE_free(&vre);
282
283 5
        ret = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!');
284 5
        if (!ret)
285 0
                vtc_fatal(s->vl, "EXPECT FAILED %s \"%s\"", cmp, spec);
286
        else
287 5
                vtc_log(s->vl, 4, "EXPECT MATCH %s \"%s\"", cmp, spec);
288 5
}
289
290
static void v_matchproto_(cmd_f)
291 5
cmd_syslog_recv(CMD_ARGS)
292
{
293
        int lvl;
294
        struct syslog_srv *s;
295
296 5
        CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC);
297 5
        (void)vl;
298 5
        AZ(strcmp(av[0], "recv"));
299 5
        av++;
300 5
        if (av[0] == NULL)
301 0
                lvl = s->lvl;
302
        else
303 5
                lvl = get_syslog_level(vl, av[0]);
304
305 5
        syslog_rx(s, lvl);
306 5
}
307
308
/**********************************************************************
309
 * Syslog server thread
310
 */
311
312
static void *
313 5
syslog_thread(void *priv)
314
{
315
        struct syslog_srv *s;
316
        int i;
317
318 5
        CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC);
319 5
        assert(s->sock >= 0);
320
321 5
        vtc_log(s->vl, 2, "Started on %s (level: %d)", s->bind, s->lvl);
322 10
        for (i = 0; i < s->repeat; i++) {
323 5
                if (s->repeat > 1)
324 0
                        vtc_log(s->vl, 3, "Iteration %d", i);
325 5
                parse_string(s->vl, s, s->spec);
326 5
                vtc_log(s->vl, 3, "shutting fd %d", s->sock);
327 5
        }
328 5
        VUDP_close(&s->sock);
329 5
        vtc_log(s->vl, 2, "Ending");
330 5
        return (NULL);
331
}
332
333
/**********************************************************************
334
 * Start the syslog thread
335
 */
336
337
static void
338 5
syslog_start(struct syslog_srv *s)
339
{
340 5
        CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
341 5
        vtc_log(s->vl, 2, "Starting syslog server");
342 5
        if (s->sock == -1)
343 0
                syslog_bind(s);
344 5
        vtc_log(s->vl, 1, "Bound on %s", s->bind);
345 5
        s->run = 1;
346 5
        PTOK(pthread_create(&s->tp, NULL, syslog_thread, s));
347 5
}
348
349
/**********************************************************************
350
 * Force stop the syslog thread
351
 */
352
353
static void
354 0
syslog_stop(struct syslog_srv *s)
355
{
356
        void *res;
357
358 0
        CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
359 0
        vtc_log(s->vl, 2, "Stopping for syslog server");
360 0
        (void)pthread_cancel(s->tp);
361 0
        PTOK(pthread_join(s->tp, &res));
362 0
        s->tp = 0;
363 0
        s->run = 0;
364 0
}
365
366
/**********************************************************************
367
 * Wait for syslog thread to stop
368
 */
369
370
static void
371 5
syslog_wait(struct syslog_srv *s)
372
{
373
        void *res;
374
375 5
        CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
376 5
        vtc_log(s->vl, 2, "Waiting for syslog server (%d)", s->sock);
377 5
        PTOK(pthread_join(s->tp, &res));
378 5
        if (res != NULL && !vtc_stop)
379 0
                vtc_fatal(s->vl, "Syslog server returned \"%p\"",
380 0
                    (char *)res);
381 5
        s->tp = 0;
382 5
        s->run = 0;
383 5
}
384
385
/* SECTION: syslog syslog
386
 *
387
 * Define and interact with syslog instances (for use with haproxy)
388
 *
389
 * To define a syslog server, you'll use this syntax::
390
 *
391
 *     syslog SNAME
392
 *
393
 * Arguments:
394
 *
395
 * SNAME
396
 *     Identify the syslog server with a string which must start with 'S'.
397
 *
398
 * \-level STRING
399
 *         Set the default syslog priority level used by any subsequent "recv"
400
 *         command.
401
 *         Any syslog dgram with a different level will be skipped by
402
 *         "recv" command. This default level value may be superseded
403
 *         by "recv" command if supplied as first argument: "recv <level>".
404
 *
405
 * \-start
406
 *         Start the syslog server thread in the background.
407
 *
408
 * \-repeat
409
 *         Instead of processing the specification only once, do it
410
 *         NUMBER times.
411
 *
412
 * \-bind
413
 *         Bind the syslog socket to a local address.
414
 *
415
 * \-wait
416
 *         Wait for that thread to terminate.
417
 *
418
 * \-stop
419
 *         Stop the syslog server thread.
420
 */
421
422
void v_matchproto_(cmd_f)
423 5465
cmd_syslog(CMD_ARGS)
424
{
425
        struct syslog_srv *s;
426
427 5465
        (void)priv;
428
429 5465
        if (av == NULL) {
430
                /* Reset and free */
431 5455
                do {
432 5460
                        PTOK(pthread_mutex_lock(&syslog_mtx));
433 5460
                        s = VTAILQ_FIRST(&syslogs);
434 5460
                        CHECK_OBJ_ORNULL(s, SYSLOG_SRV_MAGIC);
435 5460
                        if (s != NULL)
436 5
                                VTAILQ_REMOVE(&syslogs, s, list);
437 5460
                        PTOK(pthread_mutex_unlock(&syslog_mtx));
438 5460
                        if (s != NULL) {
439 5
                                if (s->run) {
440 0
                                        (void)pthread_cancel(s->tp);
441 0
                                        syslog_wait(s);
442 0
                                }
443 5
                                if (s->sock >= 0)
444 0
                                        VUDP_close(&s->sock);
445 5
                                syslog_delete(s);
446 5
                        }
447 5460
                } while (s != NULL);
448 5455
                return;
449
        }
450
451 10
        AZ(strcmp(av[0], "syslog"));
452 10
        av++;
453
454 10
        PTOK(pthread_mutex_lock(&syslog_mtx));
455 10
        VTAILQ_FOREACH(s, &syslogs, list)
456 5
                if (!strcmp(s->name, av[0]))
457 5
                        break;
458 10
        PTOK(pthread_mutex_unlock(&syslog_mtx));
459 10
        if (s == NULL)
460 5
                s = syslog_new(av[0], vl);
461 10
        CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC);
462 10
        av++;
463
464 35
        for (; *av != NULL; av++) {
465 25
                if (vtc_error)
466 0
                        break;
467 25
                if (!strcmp(*av, "-wait")) {
468 5
                        if (!s->run)
469 0
                                vtc_fatal(s->vl, "Syslog server not -started");
470 5
                        syslog_wait(s);
471 5
                        continue;
472
                }
473
474 20
                if (!strcmp(*av, "-stop")) {
475 0
                        syslog_stop(s);
476 0
                        continue;
477
                }
478
479
                /*
480
                 * We do an implicit -wait if people muck about with a
481
                 * running syslog.
482
                 * This only works if the previous ->spec has completed
483
                 */
484 20
                if (s->run)
485 0
                        syslog_wait(s);
486
487 20
                AZ(s->run);
488 20
                if (!strcmp(*av, "-repeat")) {
489 0
                        AN(av[1]);
490 0
                        s->repeat = atoi(av[1]);
491 0
                        av++;
492 0
                        continue;
493
                }
494 20
                if (!strcmp(*av, "-bind")) {
495 5
                        AN(av[1]);
496 5
                        bprintf(s->bind, "%s", av[1]);
497 5
                        av++;
498 5
                        syslog_bind(s);
499 5
                        continue;
500
                }
501 15
                if (!strcmp(*av, "-level")) {
502 5
                        AN(av[1]);
503 5
                        s->lvl = get_syslog_level(vl, av[1]);
504 5
                        av++;
505 5
                        continue;
506
                }
507 10
                if (!strcmp(*av, "-start")) {
508 5
                        syslog_start(s);
509 5
                        continue;
510
                }
511 5
                if (**av == '-')
512 0
                        vtc_fatal(s->vl, "Unknown syslog argument: %s", *av);
513 5
                s->spec = *av;
514 5
        }
515 5465
}
516
517
void
518 5455
init_syslog(void)
519
{
520 5455
        PTOK(pthread_mutex_init(&syslog_mtx, NULL));
521 5455
}