]> git.proxmox.com Git - mirror_frr.git/blob - lib/zlog.c
Merge pull request #8907 from idryzhov/isis-batching
[mirror_frr.git] / lib / zlog.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 <unistd.h>
20 #include <sys/time.h>
21 #include <sys/mman.h>
22 #include <sys/types.h>
23 #include <time.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <pthread.h>
29
30 /* gettid() & co. */
31 #ifdef HAVE_PTHREAD_NP_H
32 #include <pthread_np.h>
33 #endif
34 #ifdef linux
35 #include <sys/syscall.h>
36 #endif
37 #ifdef __FreeBSD__
38 #include <sys/thr.h>
39 #endif
40 #ifdef __NetBSD__
41 #include <lwp.h>
42 #endif
43 #ifdef __DragonFly__
44 #include <sys/lwp.h>
45 #endif
46 #ifdef __APPLE__
47 #include <mach/mach_traps.h>
48 #endif
49
50 #include "memory.h"
51 #include "atomlist.h"
52 #include "printfrr.h"
53 #include "frrcu.h"
54 #include "zlog.h"
55 #include "libfrr_trace.h"
56
57 DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message");
58 DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer");
59
60 DEFINE_HOOK(zlog_init, (const char *progname, const char *protoname,
61 unsigned short instance, uid_t uid, gid_t gid),
62 (progname, protoname, instance, uid, gid));
63 DEFINE_KOOH(zlog_fini, (), ());
64 DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
65 (prefix, prio_min));
66
67 char zlog_prefix[128];
68 size_t zlog_prefixsz;
69 int zlog_tmpdirfd = -1;
70 int zlog_instance = -1;
71
72 static atomic_bool zlog_ec = true, zlog_xid = true;
73
74 /* these are kept around because logging is initialized (and directories
75 * & files created) before zprivs code switches to the FRR user; therefore
76 * we need to chown() things so we don't get permission errors later when
77 * trying to delete things on shutdown
78 */
79 static uid_t zlog_uid = -1;
80 static gid_t zlog_gid = -1;
81
82 DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head);
83 static struct zlog_targets_head zlog_targets;
84
85 /* Global setting for buffered vs immediate output. The default is
86 * per-pthread buffering.
87 */
88 static bool default_immediate;
89
90 /* cf. zlog.h for additional comments on this struct.
91 *
92 * Note: you MUST NOT pass the format string + va_list to non-FRR format
93 * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
94 * extended prinf() with additional formats (%pI4 and the like).
95 *
96 * Also remember to use va_copy() on args.
97 */
98
99 struct zlog_msg {
100 struct timespec ts;
101 int prio;
102
103 const char *fmt;
104 va_list args;
105 const struct xref_logmsg *xref;
106
107 char *stackbuf;
108 size_t stackbufsz;
109 char *text;
110 size_t textlen;
111 size_t hdrlen;
112
113 /* This is always ISO8601 with sub-second precision 9 here, it's
114 * converted for callers as needed. ts_dot points to the "."
115 * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
116 * local time offset.
117 *
118 * Valid if ZLOG_TS_ISO8601 is set.
119 * (0 if timestamp has not been formatted yet)
120 */
121 char ts_str[32], *ts_dot, ts_zonetail[8];
122 uint32_t ts_flags;
123
124 /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from
125 * the above, so just kept separately here.
126 */
127 uint32_t ts_3164_flags;
128 char ts_3164_str[16];
129
130 /* at the time of writing, 16 args was the actual maximum of arguments
131 * to a single zlog call. Particularly printing flag bitmasks seems
132 * to drive this. That said, the overhead of dynamically sizing this
133 * probably outweighs the value. If anything, a printfrr extension
134 * for printing flag bitmasks might be a good idea.
135 */
136 struct fmt_outpos argpos[24];
137 size_t n_argpos;
138 };
139
140 /* thread-local log message buffering
141 *
142 * This is strictly optional and set up by calling zlog_tls_buffer_init()
143 * on a particular thread.
144 *
145 * If in use, this will create a temporary file in /var/tmp which is used as
146 * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
147 * access doesn't require any syscalls, but in case of a crash the kernel
148 * knows to sync the memory back to disk. This way the user can still get the
149 * last log messages if there were any left unwritten in the buffer.
150 *
151 * Sizing this dynamically isn't particularly useful, so here's an 8k buffer
152 * with a message limit of 64 messages. Message metadata (e.g. priority,
153 * timestamp) aren't in the mmap region, so they're lost on crash, but we can
154 * live with that.
155 */
156
157 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
158 #define CAN_DO_TLS 1
159 #endif
160
161 #define TLS_LOG_BUF_SIZE 8192
162 #define TLS_LOG_MAXMSG 64
163
164 struct zlog_tls {
165 char *mmbuf;
166 size_t bufpos;
167 bool do_unlink;
168
169 size_t nmsgs;
170 struct zlog_msg msgs[TLS_LOG_MAXMSG];
171 struct zlog_msg *msgp[TLS_LOG_MAXMSG];
172 };
173
174 static inline void zlog_tls_free(void *arg);
175
176 /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
177 * available we'll use it here
178 */
179
180 #ifdef __OpenBSD__
181 static pthread_key_t zlog_tls_key;
182
183 static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
184 static void zlog_tls_key_init(void)
185 {
186 pthread_key_create(&zlog_tls_key, zlog_tls_free);
187 }
188
189 static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
190 static void zlog_tls_key_fini(void)
191 {
192 pthread_key_delete(zlog_tls_key);
193 }
194
195 static inline struct zlog_tls *zlog_tls_get(void)
196 {
197 return pthread_getspecific(zlog_tls_key);
198 }
199
200 static inline void zlog_tls_set(struct zlog_tls *val)
201 {
202 pthread_setspecific(zlog_tls_key, val);
203 }
204 #else
205 # ifndef thread_local
206 # define thread_local __thread
207 # endif
208
209 static thread_local struct zlog_tls *zlog_tls_var
210 __attribute__((tls_model("initial-exec")));
211
212 static inline struct zlog_tls *zlog_tls_get(void)
213 {
214 return zlog_tls_var;
215 }
216
217 static inline void zlog_tls_set(struct zlog_tls *val)
218 {
219 zlog_tls_var = val;
220 }
221 #endif
222
223 #ifdef CAN_DO_TLS
224 static intmax_t zlog_gettid(void)
225 {
226 #ifndef __OpenBSD__
227 /* accessing a TLS variable is much faster than a syscall */
228 static thread_local intmax_t cached_tid = -1;
229 if (cached_tid != -1)
230 return cached_tid;
231 #endif
232
233 long rv = -1;
234 #ifdef HAVE_PTHREAD_GETTHREADID_NP
235 rv = pthread_getthreadid_np();
236 #elif defined(linux)
237 rv = syscall(__NR_gettid);
238 #elif defined(__NetBSD__)
239 rv = _lwp_self();
240 #elif defined(__FreeBSD__)
241 thr_self(&rv);
242 #elif defined(__DragonFly__)
243 rv = lwp_gettid();
244 #elif defined(__OpenBSD__)
245 rv = getthrid();
246 #elif defined(__sun)
247 rv = pthread_self();
248 #elif defined(__APPLE__)
249 rv = mach_thread_self();
250 mach_port_deallocate(mach_task_self(), rv);
251 #endif
252
253 #ifndef __OpenBSD__
254 cached_tid = rv;
255 #endif
256 return rv;
257 }
258
259 void zlog_tls_buffer_init(void)
260 {
261 struct zlog_tls *zlog_tls;
262 char mmpath[MAXPATHLEN];
263 int mmfd;
264 size_t i;
265
266 zlog_tls = zlog_tls_get();
267
268 if (zlog_tls || zlog_tmpdirfd < 0)
269 return;
270
271 zlog_tls = XCALLOC(MTYPE_LOG_TLSBUF, sizeof(*zlog_tls));
272 for (i = 0; i < array_size(zlog_tls->msgp); i++)
273 zlog_tls->msgp[i] = &zlog_tls->msgs[i];
274
275 snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid());
276
277 mmfd = openat(zlog_tmpdirfd, mmpath,
278 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
279 if (mmfd < 0) {
280 zlog_err("failed to open thread log buffer \"%s\": %s",
281 mmpath, strerror(errno));
282 goto out_anon;
283 }
284 fchown(mmfd, zlog_uid, zlog_gid);
285
286 #ifdef HAVE_POSIX_FALLOCATE
287 if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0)
288 /* note next statement is under above if() */
289 #endif
290 if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
291 zlog_err("failed to allocate thread log buffer \"%s\": %s",
292 mmpath, strerror(errno));
293 goto out_anon_unlink;
294 }
295
296 zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
297 MAP_SHARED, mmfd, 0);
298 if (zlog_tls->mmbuf == MAP_FAILED) {
299 zlog_err("failed to mmap thread log buffer \"%s\": %s",
300 mmpath, strerror(errno));
301 goto out_anon_unlink;
302 }
303 zlog_tls->do_unlink = true;
304
305 close(mmfd);
306 zlog_tls_set(zlog_tls);
307 return;
308
309 out_anon_unlink:
310 unlinkat(zlog_tmpdirfd, mmpath, 0);
311 close(mmfd);
312 out_anon:
313
314 #ifndef MAP_ANONYMOUS
315 #define MAP_ANONYMOUS MAP_ANON
316 #endif
317 zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
318 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
319
320 if (!zlog_tls->mmbuf) {
321 zlog_err("failed to anonymous-mmap thread log buffer: %s",
322 strerror(errno));
323 XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
324 zlog_tls_set(NULL);
325 return;
326 }
327
328 zlog_tls_set(zlog_tls);
329 }
330
331 void zlog_tls_buffer_fini(void)
332 {
333 char mmpath[MAXPATHLEN];
334 struct zlog_tls *zlog_tls = zlog_tls_get();
335 bool do_unlink = zlog_tls ? zlog_tls->do_unlink : false;
336
337 zlog_tls_buffer_flush();
338
339 zlog_tls_free(zlog_tls);
340 zlog_tls_set(NULL);
341
342 snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid());
343 if (do_unlink && unlinkat(zlog_tmpdirfd, mmpath, 0))
344 zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno);
345 }
346
347 #else /* !CAN_DO_TLS */
348 void zlog_tls_buffer_init(void)
349 {
350 }
351
352 void zlog_tls_buffer_fini(void)
353 {
354 }
355 #endif
356
357 void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid)
358 {
359 #ifndef __OpenBSD__
360 static thread_local intmax_t cached_pid = -1;
361 if (cached_pid != -1)
362 *pid = cached_pid;
363 else
364 cached_pid = *pid = (intmax_t)getpid();
365 #else
366 *pid = (intmax_t)getpid();
367 #endif
368 #ifdef CAN_DO_TLS
369 *tid = zlog_gettid();
370 #else
371 *tid = *pid;
372 #endif
373 }
374
375 static inline void zlog_tls_free(void *arg)
376 {
377 struct zlog_tls *zlog_tls = arg;
378
379 if (!zlog_tls)
380 return;
381
382 munmap(zlog_tls->mmbuf, TLS_LOG_BUF_SIZE);
383 XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
384 }
385
386 void zlog_tls_buffer_flush(void)
387 {
388 struct zlog_target *zt;
389 struct zlog_tls *zlog_tls = zlog_tls_get();
390
391 if (!zlog_tls)
392 return;
393 if (!zlog_tls->nmsgs)
394 return;
395
396 rcu_read_lock();
397 frr_each (zlog_targets, &zlog_targets, zt) {
398 if (!zt->logfn)
399 continue;
400
401 zt->logfn(zt, zlog_tls->msgp, zlog_tls->nmsgs);
402 }
403 rcu_read_unlock();
404
405 zlog_tls->bufpos = 0;
406 zlog_tls->nmsgs = 0;
407 }
408
409
410 static void vzlog_notls(const struct xref_logmsg *xref, int prio,
411 const char *fmt, va_list ap)
412 {
413 struct zlog_target *zt;
414 struct zlog_msg stackmsg = {
415 .prio = prio & LOG_PRIMASK,
416 .fmt = fmt,
417 .xref = xref,
418 }, *msg = &stackmsg;
419 char stackbuf[512];
420
421 clock_gettime(CLOCK_REALTIME, &msg->ts);
422 va_copy(msg->args, ap);
423 msg->stackbuf = stackbuf;
424 msg->stackbufsz = sizeof(stackbuf);
425
426 rcu_read_lock();
427 frr_each (zlog_targets, &zlog_targets, zt) {
428 if (prio > zt->prio_min)
429 continue;
430 if (!zt->logfn)
431 continue;
432
433 zt->logfn(zt, &msg, 1);
434 }
435 rcu_read_unlock();
436
437 va_end(msg->args);
438 if (msg->text && msg->text != stackbuf)
439 XFREE(MTYPE_LOG_MESSAGE, msg->text);
440 }
441
442 static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref,
443 int prio, const char *fmt, va_list ap)
444 {
445 struct zlog_target *zt;
446 struct zlog_msg *msg;
447 char *buf;
448 bool ignoremsg = true;
449 bool immediate = default_immediate;
450
451 /* avoid further processing cost if no target wants this message */
452 rcu_read_lock();
453 frr_each (zlog_targets, &zlog_targets, zt) {
454 if (prio > zt->prio_min)
455 continue;
456 ignoremsg = false;
457 break;
458 }
459 rcu_read_unlock();
460
461 if (ignoremsg)
462 return;
463
464 msg = &zlog_tls->msgs[zlog_tls->nmsgs];
465 zlog_tls->nmsgs++;
466 if (zlog_tls->nmsgs == array_size(zlog_tls->msgs))
467 immediate = true;
468
469 memset(msg, 0, sizeof(*msg));
470 clock_gettime(CLOCK_REALTIME, &msg->ts);
471 va_copy(msg->args, ap);
472 msg->stackbuf = buf = zlog_tls->mmbuf + zlog_tls->bufpos;
473 msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1;
474 msg->fmt = fmt;
475 msg->prio = prio & LOG_PRIMASK;
476 msg->xref = xref;
477 if (msg->prio < LOG_INFO)
478 immediate = true;
479
480 if (!immediate) {
481 /* messages written later need to take the formatting cost
482 * immediately since we can't hold a reference on varargs
483 */
484 zlog_msg_text(msg, NULL);
485
486 if (msg->text != buf)
487 /* zlog_msg_text called malloc() on us :( */
488 immediate = true;
489 else {
490 zlog_tls->bufpos += msg->textlen + 1;
491 /* write a second \0 to mark current end position
492 * (in case of crash this signals end of unwritten log
493 * messages in mmap'd logbuf file)
494 */
495 zlog_tls->mmbuf[zlog_tls->bufpos] = '\0';
496
497 /* avoid malloc() for next message */
498 if (TLS_LOG_BUF_SIZE - zlog_tls->bufpos < 256)
499 immediate = true;
500 }
501 }
502
503 if (immediate)
504 zlog_tls_buffer_flush();
505
506 va_end(msg->args);
507 if (msg->text && msg->text != buf)
508 XFREE(MTYPE_LOG_MESSAGE, msg->text);
509 }
510
511 void vzlogx(const struct xref_logmsg *xref, int prio,
512 const char *fmt, va_list ap)
513 {
514 struct zlog_tls *zlog_tls = zlog_tls_get();
515
516 #ifdef HAVE_LTTNG
517 va_list copy;
518 va_copy(copy, ap);
519 char *msg = vasprintfrr(MTYPE_LOG_MESSAGE, fmt, copy);
520
521 switch (prio) {
522 case LOG_ERR:
523 frrtracelog(TRACE_ERR, msg);
524 break;
525 case LOG_WARNING:
526 frrtracelog(TRACE_WARNING, msg);
527 break;
528 case LOG_DEBUG:
529 frrtracelog(TRACE_DEBUG, msg);
530 break;
531 case LOG_NOTICE:
532 frrtracelog(TRACE_DEBUG, msg);
533 break;
534 case LOG_INFO:
535 default:
536 frrtracelog(TRACE_INFO, msg);
537 break;
538 }
539
540 va_end(copy);
541 XFREE(MTYPE_LOG_MESSAGE, msg);
542 #endif
543
544 if (zlog_tls)
545 vzlog_tls(zlog_tls, xref, prio, fmt, ap);
546 else
547 vzlog_notls(xref, prio, fmt, ap);
548 }
549
550 void zlog_sigsafe(const char *text, size_t len)
551 {
552 struct zlog_target *zt;
553 const char *end = text + len, *nlpos;
554
555 while (text < end) {
556 nlpos = memchr(text, '\n', end - text);
557 if (!nlpos)
558 nlpos = end;
559
560 frr_each (zlog_targets, &zlog_targets, zt) {
561 if (LOG_CRIT > zt->prio_min)
562 continue;
563 if (!zt->logfn_sigsafe)
564 continue;
565
566 zt->logfn_sigsafe(zt, text, nlpos - text);
567 }
568
569 if (nlpos == end)
570 break;
571 text = nlpos + 1;
572 }
573 }
574
575 void _zlog_assert_failed(const struct xref_assert *xref, const char *extra, ...)
576 {
577 va_list ap;
578 static bool assert_in_assert; /* "global-ish" variable, init to 0 */
579
580 if (assert_in_assert)
581 abort();
582 assert_in_assert = true;
583
584 if (extra) {
585 struct va_format vaf;
586
587 va_start(ap, extra);
588 vaf.fmt = extra;
589 vaf.va = &ap;
590
591 zlog(LOG_CRIT,
592 "%s:%d: %s(): assertion (%s) failed, extra info: %pVA",
593 xref->xref.file, xref->xref.line, xref->xref.func,
594 xref->expr, &vaf);
595
596 va_end(ap);
597 } else
598 zlog(LOG_CRIT, "%s:%d: %s(): assertion (%s) failed",
599 xref->xref.file, xref->xref.line, xref->xref.func,
600 xref->expr);
601
602 /* abort() prints backtrace & memstats in SIGABRT handler */
603 abort();
604 }
605
606 int zlog_msg_prio(struct zlog_msg *msg)
607 {
608 return msg->prio;
609 }
610
611 const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg)
612 {
613 return msg->xref;
614 }
615
616 const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
617 {
618 if (!msg->text) {
619 va_list args;
620 bool do_xid, do_ec;
621 size_t need = 0, hdrlen;
622 struct fbuf fb = {
623 .buf = msg->stackbuf,
624 .pos = msg->stackbuf,
625 .len = msg->stackbufsz,
626 };
627
628 do_ec = atomic_load_explicit(&zlog_ec, memory_order_relaxed);
629 do_xid = atomic_load_explicit(&zlog_xid, memory_order_relaxed);
630
631 if (msg->xref && do_xid && msg->xref->xref.xrefdata->uid[0]) {
632 need += bputch(&fb, '[');
633 need += bputs(&fb, msg->xref->xref.xrefdata->uid);
634 need += bputch(&fb, ']');
635 }
636 if (msg->xref && do_ec && msg->xref->ec)
637 need += bprintfrr(&fb, "[EC %u]", msg->xref->ec);
638 if (need)
639 need += bputch(&fb, ' ');
640
641 msg->hdrlen = hdrlen = need;
642 assert(hdrlen < msg->stackbufsz);
643
644 fb.outpos = msg->argpos;
645 fb.outpos_n = array_size(msg->argpos);
646 fb.outpos_i = 0;
647
648 va_copy(args, msg->args);
649 need += vbprintfrr(&fb, msg->fmt, args);
650 va_end(args);
651
652 msg->textlen = need;
653 need += bputch(&fb, '\n');
654
655 if (need <= msg->stackbufsz)
656 msg->text = msg->stackbuf;
657 else {
658 msg->text = XMALLOC(MTYPE_LOG_MESSAGE, need);
659
660 memcpy(msg->text, msg->stackbuf, hdrlen);
661
662 fb.buf = msg->text;
663 fb.len = need;
664 fb.pos = msg->text + hdrlen;
665 fb.outpos_i = 0;
666
667 va_copy(args, msg->args);
668 vbprintfrr(&fb, msg->fmt, args);
669 va_end(args);
670
671 bputch(&fb, '\n');
672 }
673
674 msg->n_argpos = fb.outpos_i;
675 }
676 if (textlen)
677 *textlen = msg->textlen;
678 return msg->text;
679 }
680
681 void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, size_t *n_argpos,
682 const struct fmt_outpos **argpos)
683 {
684 if (!msg->text)
685 zlog_msg_text(msg, NULL);
686
687 if (hdrlen)
688 *hdrlen = msg->hdrlen;
689 if (n_argpos)
690 *n_argpos = msg->n_argpos;
691 if (argpos)
692 *argpos = msg->argpos;
693 }
694
695 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
696 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
697
698 size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
699 {
700 size_t outsz = out ? (out->buf + out->len - out->pos) : 0;
701 size_t len1;
702
703 if (!(flags & ZLOG_TS_FORMAT))
704 return 0;
705
706 if (!(msg->ts_flags & ZLOG_TS_FORMAT) ||
707 ((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) {
708 struct tm tm;
709
710 if (flags & ZLOG_TS_UTC)
711 gmtime_r(&msg->ts.tv_sec, &tm);
712 else
713 localtime_r(&msg->ts.tv_sec, &tm);
714
715 strftime(msg->ts_str, sizeof(msg->ts_str),
716 "%Y-%m-%dT%H:%M:%S", &tm);
717
718 if (flags & ZLOG_TS_UTC) {
719 msg->ts_zonetail[0] = 'Z';
720 msg->ts_zonetail[1] = '\0';
721 } else
722 snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail),
723 "%+03d:%02d",
724 (int)(tm.tm_gmtoff / 3600),
725 (int)(labs(tm.tm_gmtoff) / 60) % 60);
726
727 msg->ts_dot = msg->ts_str + strlen(msg->ts_str);
728 snprintfrr(msg->ts_dot,
729 msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot,
730 ".%09lu", (unsigned long)msg->ts.tv_nsec);
731
732 msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC);
733 }
734
735 len1 = flags & ZLOG_TS_PREC;
736 len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0);
737
738 if (len1 > strlen(msg->ts_str))
739 len1 = strlen(msg->ts_str);
740
741 if (flags & ZLOG_TS_LEGACY) {
742 if (!out)
743 return len1;
744
745 if (len1 > outsz) {
746 memset(out->pos, 0, outsz);
747 out->pos += outsz;
748 return len1;
749 }
750
751 /* just swap out the formatting, faster than redoing it */
752 for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) {
753 switch (*p) {
754 case '-':
755 *out->pos++ = '/';
756 break;
757 case 'T':
758 *out->pos++ = ' ';
759 break;
760 default:
761 *out->pos++ = *p;
762 }
763 }
764 return len1;
765 } else {
766 size_t len2 = strlen(msg->ts_zonetail);
767
768 if (!out)
769 return len1 + len2;
770
771 if (len1 + len2 > outsz) {
772 memset(out->pos, 0, outsz);
773 out->pos += outsz;
774 return len1 + len2;
775 }
776
777 memcpy(out->pos, msg->ts_str, len1);
778 out->pos += len1;
779 memcpy(out->pos, msg->ts_zonetail, len2);
780 out->pos += len2;
781 return len1 + len2;
782 }
783 }
784
785 size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
786 {
787 flags &= ZLOG_TS_UTC;
788
789 if (!msg->ts_3164_str[0] || flags != msg->ts_3164_flags) {
790 /* these are "hardcoded" in RFC3164, so they're here too... */
791 static const char *const months[12] = {
792 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
793 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
794 };
795 struct tm tm;
796
797 /* RFC3164 explicitly asks for local time, but common usage
798 * also includes UTC.
799 */
800 if (flags & ZLOG_TS_UTC)
801 gmtime_r(&msg->ts.tv_sec, &tm);
802 else
803 localtime_r(&msg->ts.tv_sec, &tm);
804
805 snprintfrr(msg->ts_3164_str, sizeof(msg->ts_3164_str),
806 "%3s %2d %02d:%02d:%02d", months[tm.tm_mon],
807 tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
808
809 msg->ts_3164_flags = flags;
810 }
811 return bputs(out, msg->ts_3164_str);
812 }
813
814 void zlog_set_prefix_ec(bool enable)
815 {
816 atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed);
817 }
818
819 bool zlog_get_prefix_ec(void)
820 {
821 return atomic_load_explicit(&zlog_ec, memory_order_relaxed);
822 }
823
824 void zlog_set_prefix_xid(bool enable)
825 {
826 atomic_store_explicit(&zlog_xid, enable, memory_order_relaxed);
827 }
828
829 bool zlog_get_prefix_xid(void)
830 {
831 return atomic_load_explicit(&zlog_xid, memory_order_relaxed);
832 }
833
834 /* setup functions */
835
836 struct zlog_target *zlog_target_clone(struct memtype *mt,
837 struct zlog_target *oldzt, size_t size)
838 {
839 struct zlog_target *newzt;
840
841 newzt = XCALLOC(mt, size);
842 if (oldzt) {
843 newzt->prio_min = oldzt->prio_min;
844 newzt->logfn = oldzt->logfn;
845 newzt->logfn_sigsafe = oldzt->logfn_sigsafe;
846 }
847
848 return newzt;
849 }
850
851 struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
852 struct zlog_target *newzt)
853 {
854 if (newzt)
855 zlog_targets_add_tail(&zlog_targets, newzt);
856 if (oldzt)
857 zlog_targets_del(&zlog_targets, oldzt);
858 return oldzt;
859 }
860
861 /*
862 * Enable or disable 'immediate' output - default is to buffer
863 * each pthread's messages.
864 */
865 void zlog_set_immediate(bool set_p)
866 {
867 default_immediate = set_p;
868 }
869
870 /* common init */
871
872 #define TMPBASEDIR "/var/tmp/frr"
873
874 static char zlog_tmpdir[MAXPATHLEN];
875
876 void zlog_aux_init(const char *prefix, int prio_min)
877 {
878 if (prefix)
879 strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix));
880
881 hook_call(zlog_aux_init, prefix, prio_min);
882 }
883
884 void zlog_init(const char *progname, const char *protoname,
885 unsigned short instance, uid_t uid, gid_t gid)
886 {
887 zlog_uid = uid;
888 zlog_gid = gid;
889 zlog_instance = instance;
890
891 if (instance) {
892 snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
893 "/var/tmp/frr/%s-%d.%ld",
894 progname, instance, (long)getpid());
895
896 zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
897 "%s[%d]: ", protoname, instance);
898 } else {
899 snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
900 "/var/tmp/frr/%s.%ld",
901 progname, (long)getpid());
902
903 zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
904 "%s: ", protoname);
905 }
906
907 if (mkdir(TMPBASEDIR, 0700) != 0) {
908 if (errno != EEXIST) {
909 zlog_err("failed to mkdir \"%s\": %s",
910 TMPBASEDIR, strerror(errno));
911 goto out_warn;
912 }
913 }
914 chown(TMPBASEDIR, zlog_uid, zlog_gid);
915
916 if (mkdir(zlog_tmpdir, 0700) != 0) {
917 zlog_err("failed to mkdir \"%s\": %s",
918 zlog_tmpdir, strerror(errno));
919 goto out_warn;
920 }
921
922 #ifdef O_PATH
923 zlog_tmpdirfd = open(zlog_tmpdir,
924 O_PATH | O_RDONLY | O_CLOEXEC);
925 #else
926 zlog_tmpdirfd = open(zlog_tmpdir,
927 O_DIRECTORY | O_RDONLY | O_CLOEXEC);
928 #endif
929 if (zlog_tmpdirfd < 0) {
930 zlog_err("failed to open \"%s\": %s",
931 zlog_tmpdir, strerror(errno));
932 goto out_warn;
933 }
934
935 #ifdef AT_EMPTY_PATH
936 fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH);
937 #else
938 chown(zlog_tmpdir, zlog_uid, zlog_gid);
939 #endif
940
941 hook_call(zlog_init, progname, protoname, instance, uid, gid);
942 return;
943
944 out_warn:
945 zlog_err("crashlog and per-thread log buffering unavailable!");
946 hook_call(zlog_init, progname, protoname, instance, uid, gid);
947 }
948
949 void zlog_fini(void)
950 {
951 hook_call(zlog_fini);
952
953 if (zlog_tmpdirfd >= 0) {
954 close(zlog_tmpdirfd);
955 zlog_tmpdirfd = -1;
956
957 if (rmdir(zlog_tmpdir))
958 zlog_err("failed to rmdir \"%s\": %s",
959 zlog_tmpdir, strerror(errno));
960 }
961 }