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