]> git.proxmox.com Git - mirror_frr.git/blame - lib/zlog_targets.c
Merge pull request #11076 from routingrocks/vrrp_master_ad_cli
[mirror_frr.git] / lib / zlog_targets.c
CommitLineData
0bdeb5e5
DL
1/*
2 * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "zebra.h"
18
19#include <sys/un.h>
20#include <syslog.h>
21
22#include "memory.h"
23#include "frrcu.h"
24#include "frr_pthread.h"
25#include "printfrr.h"
26#include "zlog.h"
27#include "zlog_targets.h"
28
767439c5
DL
29/* these allocations are intentionally left active even when doing full exit
30 * cleanup, in order to keep the logging subsystem fully functional until the
31 * absolute end.
32 */
33
bf8d3d6a 34DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem");
767439c5 35
bf8d3d6a
DL
36DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target");
37DEFINE_MTYPE_STATIC(LOG, LOG_FD_NAME, "log file name");
38DEFINE_MTYPE_STATIC(LOG, LOG_FD_ROTATE, "log file rotate helper");
39DEFINE_MTYPE_STATIC(LOG, LOG_SYSL, "syslog target");
0bdeb5e5
DL
40
41struct zlt_fd {
42 struct zlog_target zt;
43
44 atomic_uint_fast32_t fd;
45
46 char ts_subsec;
47 bool record_priority;
48
49 struct rcu_head_close head_close;
50};
51
52static const char * const prionames[] = {
53 [LOG_EMERG] = "emergencies: ",
54 [LOG_ALERT] = "alerts: ",
55 [LOG_CRIT] = "critical: ",
56 [LOG_ERR] = "errors: ",
57 [LOG_WARNING] = "warnings: ",
58 [LOG_NOTICE] = "notifications: ",
59 [LOG_INFO] = "informational: ",
60 [LOG_DEBUG] = "debugging: ",
61};
62
1c408628 63void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
0bdeb5e5
DL
64{
65 struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
66 int fd;
67 size_t i, textlen, iovpos = 0;
68 size_t niov = MIN(4 * nmsgs + 1, IOV_MAX);
69 struct iovec iov[niov];
70 /* "\nYYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ " = 37 chars */
71#define TS_LEN 40
72 char ts_buf[TS_LEN * nmsgs], *ts_pos = ts_buf;
73
74 fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
75
76 for (i = 0; i < nmsgs; i++) {
77 struct zlog_msg *msg = msgs[i];
78 int prio = zlog_msg_prio(msg);
79
db2baed1 80 if (prio <= zt->prio_min) {
f6caaa65
DL
81 struct fbuf fbuf = {
82 .buf = ts_buf,
83 .pos = ts_pos,
84 .len = sizeof(ts_buf),
85 };
8b94cb43
DL
86
87 iov[iovpos].iov_base = ts_pos;
f6caaa65
DL
88 zlog_msg_ts(msg, &fbuf,
89 ZLOG_TS_LEGACY | zte->ts_subsec);
90 ts_pos = fbuf.pos;
91
db2baed1
DL
92 *ts_pos++ = ' ';
93 iov[iovpos].iov_len =
94 ts_pos - (char *)iov[iovpos].iov_base;
0bdeb5e5 95
db2baed1 96 iovpos++;
0bdeb5e5 97
db2baed1
DL
98 if (zte->record_priority) {
99 iov[iovpos].iov_base = (char *)prionames[prio];
100 iov[iovpos].iov_len =
101 strlen(iov[iovpos].iov_base);
0bdeb5e5 102
db2baed1
DL
103 iovpos++;
104 }
0bdeb5e5 105
db2baed1
DL
106 iov[iovpos].iov_base = zlog_prefix;
107 iov[iovpos].iov_len = zlog_prefixsz;
0bdeb5e5 108
db2baed1 109 iovpos++;
0bdeb5e5 110
db2baed1
DL
111 iov[iovpos].iov_base =
112 (char *)zlog_msg_text(msg, &textlen);
8b94cb43 113 iov[iovpos].iov_len = textlen + 1;
0bdeb5e5 114
db2baed1
DL
115 iovpos++;
116 }
0bdeb5e5 117
db2baed1
DL
118 /* conditions that trigger writing:
119 * - out of space for more timestamps/headers
120 * - this being the last message in the batch
121 * - not enough remaining iov entries
122 */
123 if (iovpos > 0 && (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN
124 || i + 1 == nmsgs
125 || array_size(iov) - iovpos < 5)) {
0bdeb5e5
DL
126 writev(fd, iov, iovpos);
127
128 iovpos = 0;
129 ts_pos = ts_buf;
130 }
131 }
132
133 assert(iovpos == 0);
134}
135
136static void zlog_fd_sigsafe(struct zlog_target *zt, const char *text,
137 size_t len)
138{
139 struct zlt_fd *zte = container_of(zt, struct zlt_fd, zt);
140 struct iovec iov[4];
141 int fd;
142
143 iov[0].iov_base = (char *)prionames[LOG_CRIT];
144 iov[0].iov_len = zte->record_priority ? strlen(iov[0].iov_base) : 0;
145
146 iov[1].iov_base = zlog_prefix;
147 iov[1].iov_len = zlog_prefixsz;
148
149 iov[2].iov_base = (char *)text;
150 iov[2].iov_len = len;
151
152 iov[3].iov_base = (char *)"\n";
153 iov[3].iov_len = 1;
154
155 fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
156
157 writev(fd, iov, array_size(iov));
158}
159
160/*
161 * (re-)configuration
162 */
163
164void zlog_file_init(struct zlog_cfg_file *zcf)
165{
166 memset(zcf, 0, sizeof(*zcf));
167 zcf->prio_min = ZLOG_DISABLED;
168 zcf->fd = -1;
169 pthread_mutex_init(&zcf->cfg_mtx, NULL);
170}
171
172static void zlog_file_target_free(struct zlt_fd *zlt)
173{
174 if (!zlt)
175 return;
176
177 rcu_close(&zlt->head_close, zlt->fd);
178 rcu_free(MTYPE_LOG_FD, zlt, zt.rcu_head);
179}
180
181void zlog_file_fini(struct zlog_cfg_file *zcf)
182{
183 if (zcf->active) {
184 struct zlt_fd *ztf;
185 struct zlog_target *zt;
186
187 zt = zlog_target_replace(&zcf->active->zt, NULL);
188 ztf = container_of(zt, struct zlt_fd, zt);
189 zlog_file_target_free(ztf);
190 }
191 XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
192 pthread_mutex_destroy(&zcf->cfg_mtx);
193}
194
195static bool zlog_file_cycle(struct zlog_cfg_file *zcf)
196{
197 struct zlog_target *zt, *old;
198 struct zlt_fd *zlt = NULL;
199 int fd;
200 bool rv = true;
201
202 do {
203 if (zcf->prio_min == ZLOG_DISABLED)
204 break;
205
206 if (zcf->fd != -1)
207 fd = dup(zcf->fd);
208 else if (zcf->filename)
209 fd = open(zcf->filename,
210 O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC
211 | O_NOCTTY,
212 LOGFILE_MASK);
213 else
214 fd = -1;
215
216 if (fd < 0) {
217 rv = false;
218 break;
219 }
220
221 zt = zlog_target_clone(MTYPE_LOG_FD, &zcf->active->zt,
222 sizeof(*zlt));
223 zlt = container_of(zt, struct zlt_fd, zt);
224
225 zlt->fd = fd;
226 zlt->record_priority = zcf->record_priority;
227 zlt->ts_subsec = zcf->ts_subsec;
228
229 zlt->zt.prio_min = zcf->prio_min;
1c408628 230 zlt->zt.logfn = zcf->zlog_wrap ? zcf->zlog_wrap : zlog_fd;
0bdeb5e5
DL
231 zlt->zt.logfn_sigsafe = zlog_fd_sigsafe;
232 } while (0);
233
589b5e48
QY
234 old = zlog_target_replace(zcf->active ? &zcf->active->zt : NULL,
235 zlt ? &zlt->zt : NULL);
0bdeb5e5
DL
236 zcf->active = zlt;
237
589b5e48 238 zlog_file_target_free(container_of_null(old, struct zlt_fd, zt));
0bdeb5e5
DL
239
240 return rv;
241}
242
243void zlog_file_set_other(struct zlog_cfg_file *zcf)
244{
245 frr_with_mutex(&zcf->cfg_mtx) {
246 zlog_file_cycle(zcf);
247 }
248}
249
250bool zlog_file_set_filename(struct zlog_cfg_file *zcf, const char *filename)
251{
252 frr_with_mutex(&zcf->cfg_mtx) {
253 XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
254 zcf->filename = XSTRDUP(MTYPE_LOG_FD_NAME, filename);
255 zcf->fd = -1;
256
257 return zlog_file_cycle(zcf);
258 }
259 assert(0);
260}
261
262bool zlog_file_set_fd(struct zlog_cfg_file *zcf, int fd)
263{
264 frr_with_mutex(&zcf->cfg_mtx) {
265 if (zcf->fd == fd)
266 return true;
267
268 XFREE(MTYPE_LOG_FD_NAME, zcf->filename);
269 zcf->fd = fd;
270
271 return zlog_file_cycle(zcf);
272 }
273 assert(0);
274}
275
276struct rcu_close_rotate {
277 struct rcu_head_close head_close;
278 struct rcu_head head_self;
279};
280
281bool zlog_file_rotate(struct zlog_cfg_file *zcf)
282{
283 struct rcu_close_rotate *rcr;
284 int fd;
285
286 frr_with_mutex(&zcf->cfg_mtx) {
287 if (!zcf->active || !zcf->filename)
288 return true;
289
290 fd = open(zcf->filename,
291 O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC | O_NOCTTY,
292 LOGFILE_MASK);
293 if (fd < 0)
294 return false;
295
296 fd = atomic_exchange_explicit(&zcf->active->fd,
297 (uint_fast32_t)fd,
298 memory_order_relaxed);
299 }
300
301 rcr = XCALLOC(MTYPE_LOG_FD_ROTATE, sizeof(*rcr));
302 rcu_close(&rcr->head_close, fd);
303 rcu_free(MTYPE_LOG_FD_ROTATE, rcr, head_self);
304
305 return true;
306}
307
308/* fixed crash logging */
309
310static struct zlt_fd zlog_crashlog;
311
312static void zlog_crashlog_sigsafe(struct zlog_target *zt, const char *text,
313 size_t len)
314{
315 static int crashlog_fd = -1;
316
317 if (crashlog_fd == -1) {
318#ifdef HAVE_OPENAT
319 crashlog_fd = openat(zlog_tmpdirfd, "crashlog",
320 O_WRONLY | O_APPEND | O_CREAT,
321 LOGFILE_MASK);
322#endif
323 if (crashlog_fd < 0)
324 crashlog_fd = -2;
325 }
326
327 if (crashlog_fd == -2)
328 return;
329
330 zlog_crashlog.fd = crashlog_fd;
331 zlog_fd_sigsafe(&zlog_crashlog.zt, text, len);
332}
333
334/* this is used for assert failures (they don't need AS-Safe logging) */
335static void zlog_crashlog_plain(struct zlog_target *zt, struct zlog_msg *msgs[],
336 size_t nmsgs)
337{
338 size_t i, len;
339 const char *text;
340
341 for (i = 0; i < nmsgs; i++) {
342 if (zlog_msg_prio(msgs[i]) > zt->prio_min)
343 continue;
344
345 text = zlog_msg_text(msgs[i], &len);
346 zlog_crashlog_sigsafe(zt, text, len);
347 }
348}
349
350static void zlog_crashlog_init(void)
351{
352 zlog_crashlog.zt.prio_min = LOG_CRIT;
353 zlog_crashlog.zt.logfn = zlog_crashlog_plain;
354 zlog_crashlog.zt.logfn_sigsafe = zlog_crashlog_sigsafe;
355 zlog_crashlog.fd = -1;
356
357 zlog_target_replace(NULL, &zlog_crashlog.zt);
358}
359
360/* fixed logging for test/auxiliary programs */
361
362static struct zlt_fd zlog_aux_stdout;
363static bool zlog_is_aux;
364
365static int zlt_aux_init(const char *prefix, int prio_min)
366{
367 zlog_is_aux = true;
368
369 zlog_aux_stdout.zt.prio_min = prio_min;
370 zlog_aux_stdout.zt.logfn = zlog_fd;
371 zlog_aux_stdout.zt.logfn_sigsafe = zlog_fd_sigsafe;
372 zlog_aux_stdout.fd = STDOUT_FILENO;
373
374 zlog_target_replace(NULL, &zlog_aux_stdout.zt);
375 zlog_startup_end();
376 return 0;
377}
378
379static int zlt_init(const char *progname, const char *protoname,
380 unsigned short instance, uid_t uid, gid_t gid)
381{
382 openlog(progname, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
383 return 0;
384}
385
386static int zlt_fini(void)
387{
388 closelog();
389 return 0;
390}
391
392/* fixed startup logging to stderr */
393
394static struct zlt_fd zlog_startup_stderr;
395
396__attribute__((_CONSTRUCTOR(450))) static void zlog_startup_init(void)
397{
398 zlog_startup_stderr.zt.prio_min = LOG_WARNING;
399 zlog_startup_stderr.zt.logfn = zlog_fd;
400 zlog_startup_stderr.zt.logfn_sigsafe = zlog_fd_sigsafe;
401 zlog_startup_stderr.fd = STDERR_FILENO;
402
403 zlog_target_replace(NULL, &zlog_startup_stderr.zt);
404
405 hook_register(zlog_aux_init, zlt_aux_init);
406 hook_register(zlog_init, zlt_init);
407 hook_register(zlog_fini, zlt_fini);
408}
409
410void zlog_startup_end(void)
411{
412 static bool startup_ended = false;
413
414 if (startup_ended)
415 return;
416 startup_ended = true;
417
418 zlog_target_replace(&zlog_startup_stderr.zt, NULL);
419
420 if (zlog_is_aux)
421 return;
422
423 /* until here, crashlogs go to stderr */
424 zlog_crashlog_init();
425}
426
427/* syslog */
428
429struct zlt_syslog {
430 struct zlog_target zt;
431
432 int syslog_facility;
433};
434
435static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[],
436 size_t nmsgs)
437{
438 size_t i;
439 struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
8b94cb43
DL
440 const char *text;
441 size_t text_len;
0bdeb5e5
DL
442
443 for (i = 0; i < nmsgs; i++) {
444 if (zlog_msg_prio(msgs[i]) > zt->prio_min)
445 continue;
446
8b94cb43
DL
447 text = zlog_msg_text(msgs[i], &text_len);
448 syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%.*s",
449 (int)text_len, text);
0bdeb5e5
DL
450 }
451}
452
453#ifndef _PATH_LOG
454#define _PATH_LOG "/dev/log"
455#endif
456
457static void zlog_syslog_sigsafe(struct zlog_target *zt, const char *text,
458 size_t len)
459{
460 static int syslog_fd = -1;
461
462 char hdr[192];
463 size_t hdrlen;
464 struct iovec iov[2];
465
466 if (syslog_fd == -1) {
467 syslog_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
468 if (syslog_fd >= 0) {
469 struct sockaddr_un sa;
470 socklen_t salen = sizeof(sa);
471
472 sa.sun_family = AF_UNIX;
473 strlcpy(sa.sun_path, _PATH_LOG, sizeof(sa.sun_path));
474#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
475 salen = sa.sun_len = SUN_LEN(&sa);
476#endif
477 if (connect(syslog_fd, (struct sockaddr *)&sa, salen)) {
478 close(syslog_fd);
479 syslog_fd = -1;
480 }
481 }
482
483 /* /dev/log could be a fifo instead of a socket */
484 if (syslog_fd == -1) {
485 syslog_fd = open(_PATH_LOG, O_WRONLY | O_NOCTTY);
486 if (syslog_fd < 0)
487 /* give up ... */
488 syslog_fd = -2;
489 }
490 }
491
492 if (syslog_fd == -2)
493 return;
494
495 /* note zlog_prefix includes trailing ": ", need to cut off 2 chars */
496 hdrlen = snprintfrr(hdr, sizeof(hdr), "<%d>%.*s[%ld]: ", LOG_CRIT,
497 zlog_prefixsz > 2 ? (int)(zlog_prefixsz - 2) : 0,
498 zlog_prefix, (long)getpid());
499
500 iov[0].iov_base = hdr;
501 iov[0].iov_len = hdrlen;
502
503 iov[1].iov_base = (char *)text;
504 iov[1].iov_len = len;
505
506 writev(syslog_fd, iov, array_size(iov));
507}
508
509
510static pthread_mutex_t syslog_cfg_mutex = PTHREAD_MUTEX_INITIALIZER;
511static struct zlt_syslog *zlt_syslog;
512static int syslog_facility = LOG_DAEMON;
513static int syslog_prio_min = ZLOG_DISABLED;
514
515void zlog_syslog_set_facility(int facility)
516{
517 struct zlog_target *newztc;
518 struct zlt_syslog *newzt;
519
520 frr_with_mutex(&syslog_cfg_mutex) {
521 if (facility == syslog_facility)
522 return;
523 syslog_facility = facility;
524
525 if (syslog_prio_min == ZLOG_DISABLED)
526 return;
527
528 newztc = zlog_target_clone(MTYPE_LOG_SYSL, &zlt_syslog->zt,
529 sizeof(*newzt));
530 newzt = container_of(newztc, struct zlt_syslog, zt);
531 newzt->syslog_facility = syslog_facility;
532
533 zlog_target_free(MTYPE_LOG_SYSL,
534 zlog_target_replace(&zlt_syslog->zt,
535 &newzt->zt));
536
537 zlt_syslog = newzt;
538 }
539}
540
541int zlog_syslog_get_facility(void)
542{
543 frr_with_mutex(&syslog_cfg_mutex) {
544 return syslog_facility;
545 }
546 assert(0);
547}
548
549void zlog_syslog_set_prio_min(int prio_min)
550{
551 struct zlog_target *newztc;
552 struct zlt_syslog *newzt = NULL;
553
554 frr_with_mutex(&syslog_cfg_mutex) {
555 if (prio_min == syslog_prio_min)
556 return;
557 syslog_prio_min = prio_min;
558
559 if (syslog_prio_min != ZLOG_DISABLED) {
560 newztc = zlog_target_clone(MTYPE_LOG_SYSL,
561 &zlt_syslog->zt,
562 sizeof(*newzt));
563 newzt = container_of(newztc, struct zlt_syslog, zt);
564 newzt->zt.prio_min = prio_min;
565 newzt->zt.logfn = zlog_syslog;
566 newzt->zt.logfn_sigsafe = zlog_syslog_sigsafe;
567 newzt->syslog_facility = syslog_facility;
568 }
569
570 zlog_target_free(MTYPE_LOG_SYSL,
571 zlog_target_replace(&zlt_syslog->zt,
572 &newzt->zt));
573
574 zlt_syslog = newzt;
575 }
576}
577
578int zlog_syslog_get_prio_min(void)
579{
580 frr_with_mutex(&syslog_cfg_mutex) {
581 return syslog_prio_min;
582 }
583 assert(0);
584}