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