1 // SPDX-License-Identifier: ISC
3 * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
11 #include <sys/types.h>
20 #ifdef HAVE_PTHREAD_NP_H
21 #include <pthread_np.h>
24 #include <sys/syscall.h>
36 #include <mach/mach_traps.h>
40 #define UNW_LOCAL_ONLY
41 #include <libunwind.h>
50 #include "libfrr_trace.h"
53 DEFINE_MTYPE_STATIC(LIB
, LOG_MESSAGE
, "log message");
54 DEFINE_MTYPE_STATIC(LIB
, LOG_TLSBUF
, "log thread-local buffer");
56 DEFINE_HOOK(zlog_init
, (const char *progname
, const char *protoname
,
57 unsigned short instance
, uid_t uid
, gid_t gid
),
58 (progname
, protoname
, instance
, uid
, gid
));
59 DEFINE_KOOH(zlog_fini
, (), ());
60 DEFINE_HOOK(zlog_aux_init
, (const char *prefix
, int prio_min
),
63 char zlog_prefix
[128];
65 int zlog_tmpdirfd
= -1;
66 int zlog_instance
= -1;
68 static atomic_bool zlog_ec
= true, zlog_xid
= true;
70 /* these are kept around because logging is initialized (and directories
71 * & files created) before zprivs code switches to the FRR user; therefore
72 * we need to chown() things so we don't get permission errors later when
73 * trying to delete things on shutdown
75 static uid_t zlog_uid
= -1;
76 static gid_t zlog_gid
= -1;
78 DECLARE_ATOMLIST(zlog_targets
, struct zlog_target
, head
);
79 static struct zlog_targets_head zlog_targets
;
81 /* Global setting for buffered vs immediate output. The default is
82 * per-pthread buffering.
84 static bool default_immediate
;
86 /* cf. zlog.h for additional comments on this struct.
88 * Note: you MUST NOT pass the format string + va_list to non-FRR format
89 * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
90 * extended prinf() with additional formats (%pI4 and the like).
92 * Also remember to use va_copy() on args.
101 const struct xref_logmsg
*xref
;
109 /* This is always ISO8601 with sub-second precision 9 here, it's
110 * converted for callers as needed. ts_dot points to the "."
111 * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
114 * Valid if ZLOG_TS_ISO8601 is set.
115 * (0 if timestamp has not been formatted yet)
117 char ts_str
[32], *ts_dot
, ts_zonetail
[8];
120 /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from
121 * the above, so just kept separately here.
123 uint32_t ts_3164_flags
;
124 char ts_3164_str
[16];
126 /* at the time of writing, 16 args was the actual maximum of arguments
127 * to a single zlog call. Particularly printing flag bitmasks seems
128 * to drive this. That said, the overhead of dynamically sizing this
129 * probably outweighs the value. If anything, a printfrr extension
130 * for printing flag bitmasks might be a good idea.
132 struct fmt_outpos argpos
[24];
136 /* thread-local log message buffering
138 * This is strictly optional and set up by calling zlog_tls_buffer_init()
139 * on a particular thread.
141 * If in use, this will create a temporary file in /var/tmp which is used as
142 * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
143 * access doesn't require any syscalls, but in case of a crash the kernel
144 * knows to sync the memory back to disk. This way the user can still get the
145 * last log messages if there were any left unwritten in the buffer.
147 * Sizing this dynamically isn't particularly useful, so here's an 8k buffer
148 * with a message limit of 64 messages. Message metadata (e.g. priority,
149 * timestamp) aren't in the mmap region, so they're lost on crash, but we can
153 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
157 #define TLS_LOG_BUF_SIZE 8192
158 #define TLS_LOG_MAXMSG 64
166 struct zlog_msg msgs
[TLS_LOG_MAXMSG
];
167 struct zlog_msg
*msgp
[TLS_LOG_MAXMSG
];
170 static inline void zlog_tls_free(void *arg
);
172 /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
173 * available we'll use it here
177 static pthread_key_t zlog_tls_key
;
179 static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
180 static void zlog_tls_key_init(void)
182 pthread_key_create(&zlog_tls_key
, zlog_tls_free
);
185 static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
186 static void zlog_tls_key_fini(void)
188 pthread_key_delete(zlog_tls_key
);
191 static inline struct zlog_tls
*zlog_tls_get(void)
193 return pthread_getspecific(zlog_tls_key
);
196 static inline void zlog_tls_set(struct zlog_tls
*val
)
198 pthread_setspecific(zlog_tls_key
, val
);
201 # ifndef thread_local
202 # define thread_local __thread
205 static thread_local
struct zlog_tls
*zlog_tls_var
206 __attribute__((tls_model("initial-exec")));
208 static inline struct zlog_tls
*zlog_tls_get(void)
213 static inline void zlog_tls_set(struct zlog_tls
*val
)
220 static intmax_t zlog_gettid(void)
223 /* accessing a TLS variable is much faster than a syscall */
224 static thread_local
intmax_t cached_tid
= -1;
225 if (cached_tid
!= -1)
230 #ifdef HAVE_PTHREAD_GETTHREADID_NP
231 rv
= pthread_getthreadid_np();
233 rv
= syscall(__NR_gettid
);
234 #elif defined(__NetBSD__)
236 #elif defined(__FreeBSD__)
238 #elif defined(__DragonFly__)
240 #elif defined(__OpenBSD__)
244 #elif defined(__APPLE__)
245 rv
= mach_thread_self();
246 mach_port_deallocate(mach_task_self(), rv
);
255 void zlog_tls_buffer_init(void)
257 struct zlog_tls
*zlog_tls
;
258 char mmpath
[MAXPATHLEN
];
262 zlog_tls
= zlog_tls_get();
264 if (zlog_tls
|| zlog_tmpdirfd
< 0)
267 zlog_tls
= XCALLOC(MTYPE_LOG_TLSBUF
, sizeof(*zlog_tls
));
268 for (i
= 0; i
< array_size(zlog_tls
->msgp
); i
++)
269 zlog_tls
->msgp
[i
] = &zlog_tls
->msgs
[i
];
271 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%jd", zlog_gettid());
273 mmfd
= openat(zlog_tmpdirfd
, mmpath
,
274 O_RDWR
| O_CREAT
| O_EXCL
| O_CLOEXEC
, 0600);
276 zlog_err("failed to open thread log buffer \"%s\": %s",
277 mmpath
, strerror(errno
));
280 fchown(mmfd
, zlog_uid
, zlog_gid
);
282 #ifdef HAVE_POSIX_FALLOCATE
283 if (posix_fallocate(mmfd
, 0, TLS_LOG_BUF_SIZE
) != 0)
284 /* note next statement is under above if() */
286 if (ftruncate(mmfd
, TLS_LOG_BUF_SIZE
) < 0) {
287 zlog_err("failed to allocate thread log buffer \"%s\": %s",
288 mmpath
, strerror(errno
));
289 goto out_anon_unlink
;
292 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
293 MAP_SHARED
, mmfd
, 0);
294 if (zlog_tls
->mmbuf
== MAP_FAILED
) {
295 zlog_err("failed to mmap thread log buffer \"%s\": %s",
296 mmpath
, strerror(errno
));
297 goto out_anon_unlink
;
299 zlog_tls
->do_unlink
= true;
302 zlog_tls_set(zlog_tls
);
306 unlinkat(zlog_tmpdirfd
, mmpath
, 0);
310 #ifndef MAP_ANONYMOUS
311 #define MAP_ANONYMOUS MAP_ANON
313 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
314 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
316 if (!zlog_tls
->mmbuf
) {
317 zlog_err("failed to anonymous-mmap thread log buffer: %s",
319 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
324 zlog_tls_set(zlog_tls
);
327 void zlog_tls_buffer_fini(void)
329 char mmpath
[MAXPATHLEN
];
330 struct zlog_tls
*zlog_tls
= zlog_tls_get();
331 bool do_unlink
= zlog_tls
? zlog_tls
->do_unlink
: false;
333 zlog_tls_buffer_flush();
335 zlog_tls_free(zlog_tls
);
338 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%jd", zlog_gettid());
339 if (do_unlink
&& unlinkat(zlog_tmpdirfd
, mmpath
, 0))
340 zlog_err("unlink logbuf: %s (%d)", strerror(errno
), errno
);
343 #else /* !CAN_DO_TLS */
344 void zlog_tls_buffer_init(void)
348 void zlog_tls_buffer_fini(void)
353 void zlog_msg_pid(struct zlog_msg
*msg
, intmax_t *pid
, intmax_t *tid
)
356 static thread_local
intmax_t cached_pid
= -1;
357 if (cached_pid
!= -1)
360 cached_pid
= *pid
= (intmax_t)getpid();
362 *pid
= (intmax_t)getpid();
365 *tid
= zlog_gettid();
371 static inline void zlog_tls_free(void *arg
)
373 struct zlog_tls
*zlog_tls
= arg
;
378 munmap(zlog_tls
->mmbuf
, TLS_LOG_BUF_SIZE
);
379 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
382 void zlog_tls_buffer_flush(void)
384 struct zlog_target
*zt
;
385 struct zlog_tls
*zlog_tls
= zlog_tls_get();
389 if (!zlog_tls
->nmsgs
)
393 frr_each_safe (zlog_targets
, &zlog_targets
, zt
) {
397 zt
->logfn(zt
, zlog_tls
->msgp
, zlog_tls
->nmsgs
);
401 zlog_tls
->bufpos
= 0;
406 static void vzlog_notls(const struct xref_logmsg
*xref
, int prio
,
407 const char *fmt
, va_list ap
)
409 struct zlog_target
*zt
;
410 struct zlog_msg stackmsg
= {
411 .prio
= prio
& LOG_PRIMASK
,
417 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
418 va_copy(msg
->args
, ap
);
419 msg
->stackbuf
= stackbuf
;
420 msg
->stackbufsz
= sizeof(stackbuf
);
423 frr_each_safe (zlog_targets
, &zlog_targets
, zt
) {
424 if (prio
> zt
->prio_min
)
429 zt
->logfn(zt
, &msg
, 1);
434 if (msg
->text
&& msg
->text
!= stackbuf
)
435 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
438 static void vzlog_tls(struct zlog_tls
*zlog_tls
, const struct xref_logmsg
*xref
,
439 int prio
, const char *fmt
, va_list ap
)
441 struct zlog_target
*zt
;
442 struct zlog_msg
*msg
;
444 bool ignoremsg
= true;
445 bool immediate
= default_immediate
;
447 /* avoid further processing cost if no target wants this message */
449 frr_each (zlog_targets
, &zlog_targets
, zt
) {
450 if (prio
> zt
->prio_min
)
460 msg
= &zlog_tls
->msgs
[zlog_tls
->nmsgs
];
462 if (zlog_tls
->nmsgs
== array_size(zlog_tls
->msgs
))
465 memset(msg
, 0, sizeof(*msg
));
466 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
467 va_copy(msg
->args
, ap
);
468 msg
->stackbuf
= buf
= zlog_tls
->mmbuf
+ zlog_tls
->bufpos
;
469 msg
->stackbufsz
= TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
- 1;
471 msg
->prio
= prio
& LOG_PRIMASK
;
473 if (msg
->prio
< LOG_INFO
)
477 /* messages written later need to take the formatting cost
478 * immediately since we can't hold a reference on varargs
480 zlog_msg_text(msg
, NULL
);
482 if (msg
->text
!= buf
)
483 /* zlog_msg_text called malloc() on us :( */
486 zlog_tls
->bufpos
+= msg
->textlen
+ 1;
487 /* write a second \0 to mark current end position
488 * (in case of crash this signals end of unwritten log
489 * messages in mmap'd logbuf file)
491 zlog_tls
->mmbuf
[zlog_tls
->bufpos
] = '\0';
493 /* avoid malloc() for next message */
494 if (TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
< 256)
500 zlog_tls_buffer_flush();
503 if (msg
->text
&& msg
->text
!= buf
)
504 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
507 static void zlog_backtrace_msg(const struct xref_logmsg
*xref
, int prio
)
509 struct thread
*tc
= pthread_getspecific(thread_current
);
510 const char *uid
= xref
->xref
.xrefdata
->uid
;
511 bool found_thread
= false;
513 zlog(prio
, "| (%s) message in thread %jd, at %s(), %s:%d", uid
,
514 zlog_gettid(), xref
->xref
.func
, xref
->xref
.file
, xref
->xref
.line
);
516 #ifdef HAVE_LIBUNWIND
517 const char *threadfunc
= tc
? tc
->xref
->funcname
: NULL
;
518 bool found_caller
= false;
521 unw_word_t ip
, off
, sp
;
526 unw_init_local(&cursor
, &uc
);
527 while (unw_step(&cursor
) > 0) {
528 char buf
[96], name
[128] = "?";
529 bool is_thread
= false;
531 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
532 unw_get_reg(&cursor
, UNW_REG_SP
, &sp
);
534 if (unw_is_signal_frame(&cursor
))
535 zlog(prio
, "| (%s) ---- signal ----", uid
);
537 if (!unw_get_proc_name(&cursor
, buf
, sizeof(buf
), &off
)) {
538 if (!strcmp(buf
, xref
->xref
.func
))
540 if (threadfunc
&& !strcmp(buf
, threadfunc
))
541 found_thread
= is_thread
= true;
543 snprintf(name
, sizeof(name
), "%s+%#lx", buf
, (long)off
);
549 if (dladdr((void *)ip
, &dlinfo
))
550 zlog(prio
, "| (%s) %-36s %16lx+%08lx %16lx %s", uid
,
551 name
, (long)dlinfo
.dli_fbase
,
552 (long)ip
- (long)dlinfo
.dli_fbase
, (long)sp
,
555 zlog(prio
, "| (%s) %-36s %16lx %16lx", uid
, name
,
559 zlog(prio
, "| (%s) ^- scheduled from %s(), %s:%u", uid
,
560 tc
->xref
->xref
.func
, tc
->xref
->xref
.file
,
561 tc
->xref
->xref
.line
);
563 #elif defined(HAVE_GLIBC_BACKTRACE)
568 n_frames
= backtrace(frames
, array_size(frames
));
572 names
= backtrace_symbols(frames
, n_frames
);
574 for (i
= 0; i
< n_frames
; i
++) {
575 void *retaddr
= frames
[i
];
576 char *loc
= names
[i
];
578 zlog(prio
, "| (%s) %16lx %-36s", uid
, (long)retaddr
, loc
);
582 if (!found_thread
&& tc
)
583 zlog(prio
, "| (%s) scheduled from %s(), %s:%u", uid
,
584 tc
->xref
->xref
.func
, tc
->xref
->xref
.file
,
585 tc
->xref
->xref
.line
);
588 void vzlogx(const struct xref_logmsg
*xref
, int prio
,
589 const char *fmt
, va_list ap
)
591 struct zlog_tls
*zlog_tls
= zlog_tls_get();
596 char *msg
= vasprintfrr(MTYPE_LOG_MESSAGE
, fmt
, copy
);
600 frrtracelog(TRACE_ERR
, msg
);
603 frrtracelog(TRACE_WARNING
, msg
);
606 frrtracelog(TRACE_DEBUG
, msg
);
609 frrtracelog(TRACE_DEBUG
, msg
);
613 frrtracelog(TRACE_INFO
, msg
);
618 XFREE(MTYPE_LOG_MESSAGE
, msg
);
622 vzlog_tls(zlog_tls
, xref
, prio
, fmt
, ap
);
624 vzlog_notls(xref
, prio
, fmt
, ap
);
627 struct xrefdata_logmsg
*xrdl
;
629 xrdl
= container_of(xref
->xref
.xrefdata
, struct xrefdata_logmsg
,
631 if (xrdl
->fl_print_bt
)
632 zlog_backtrace_msg(xref
, prio
);
636 void zlog_sigsafe(const char *text
, size_t len
)
638 struct zlog_target
*zt
;
639 const char *end
= text
+ len
, *nlpos
;
642 nlpos
= memchr(text
, '\n', end
- text
);
646 frr_each (zlog_targets
, &zlog_targets
, zt
) {
647 if (LOG_CRIT
> zt
->prio_min
)
649 if (!zt
->logfn_sigsafe
)
652 zt
->logfn_sigsafe(zt
, text
, nlpos
- text
);
661 void _zlog_assert_failed(const struct xref_assert
*xref
, const char *extra
, ...)
664 static bool assert_in_assert
; /* "global-ish" variable, init to 0 */
666 if (assert_in_assert
)
668 assert_in_assert
= true;
671 struct va_format vaf
;
678 "%s:%d: %s(): assertion (%s) failed, extra info: %pVA",
679 xref
->xref
.file
, xref
->xref
.line
, xref
->xref
.func
,
684 zlog(LOG_CRIT
, "%s:%d: %s(): assertion (%s) failed",
685 xref
->xref
.file
, xref
->xref
.line
, xref
->xref
.func
,
688 /* abort() prints backtrace & memstats in SIGABRT handler */
692 int zlog_msg_prio(struct zlog_msg
*msg
)
697 const struct xref_logmsg
*zlog_msg_xref(struct zlog_msg
*msg
)
702 const char *zlog_msg_text(struct zlog_msg
*msg
, size_t *textlen
)
707 size_t need
= 0, hdrlen
;
709 .buf
= msg
->stackbuf
,
710 .pos
= msg
->stackbuf
,
711 .len
= msg
->stackbufsz
,
714 do_ec
= atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
715 do_xid
= atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
717 if (msg
->xref
&& do_xid
&& msg
->xref
->xref
.xrefdata
->uid
[0]) {
718 need
+= bputch(&fb
, '[');
719 need
+= bputs(&fb
, msg
->xref
->xref
.xrefdata
->uid
);
720 need
+= bputch(&fb
, ']');
722 if (msg
->xref
&& do_ec
&& msg
->xref
->ec
)
723 need
+= bprintfrr(&fb
, "[EC %u]", msg
->xref
->ec
);
725 need
+= bputch(&fb
, ' ');
727 msg
->hdrlen
= hdrlen
= need
;
728 assert(hdrlen
< msg
->stackbufsz
);
730 fb
.outpos
= msg
->argpos
;
731 fb
.outpos_n
= array_size(msg
->argpos
);
734 va_copy(args
, msg
->args
);
735 #pragma GCC diagnostic push
736 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
737 /* format-string checking is done further up the chain */
738 need
+= vbprintfrr(&fb
, msg
->fmt
, args
);
739 #pragma GCC diagnostic pop
743 need
+= bputch(&fb
, '\n');
745 if (need
<= msg
->stackbufsz
)
746 msg
->text
= msg
->stackbuf
;
748 msg
->text
= XMALLOC(MTYPE_LOG_MESSAGE
, need
);
750 memcpy(msg
->text
, msg
->stackbuf
, hdrlen
);
754 fb
.pos
= msg
->text
+ hdrlen
;
757 va_copy(args
, msg
->args
);
758 #pragma GCC diagnostic push
759 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
761 vbprintfrr(&fb
, msg
->fmt
, args
);
762 #pragma GCC diagnostic pop
768 msg
->n_argpos
= fb
.outpos_i
;
771 *textlen
= msg
->textlen
;
775 void zlog_msg_args(struct zlog_msg
*msg
, size_t *hdrlen
, size_t *n_argpos
,
776 const struct fmt_outpos
**argpos
)
779 zlog_msg_text(msg
, NULL
);
782 *hdrlen
= msg
->hdrlen
;
784 *n_argpos
= msg
->n_argpos
;
786 *argpos
= msg
->argpos
;
789 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
790 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
792 size_t zlog_msg_ts(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
794 size_t outsz
= out
? (out
->buf
+ out
->len
- out
->pos
) : 0;
797 if (!(flags
& ZLOG_TS_FORMAT
))
800 if (!(msg
->ts_flags
& ZLOG_TS_FORMAT
) ||
801 ((msg
->ts_flags
^ flags
) & ZLOG_TS_UTC
)) {
804 if (flags
& ZLOG_TS_UTC
)
805 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
807 localtime_r(&msg
->ts
.tv_sec
, &tm
);
809 strftime(msg
->ts_str
, sizeof(msg
->ts_str
),
810 "%Y-%m-%dT%H:%M:%S", &tm
);
812 if (flags
& ZLOG_TS_UTC
) {
813 msg
->ts_zonetail
[0] = 'Z';
814 msg
->ts_zonetail
[1] = '\0';
816 snprintfrr(msg
->ts_zonetail
, sizeof(msg
->ts_zonetail
),
818 (int)(tm
.tm_gmtoff
/ 3600),
819 (int)(labs(tm
.tm_gmtoff
) / 60) % 60);
821 msg
->ts_dot
= msg
->ts_str
+ strlen(msg
->ts_str
);
822 snprintfrr(msg
->ts_dot
,
823 msg
->ts_str
+ sizeof(msg
->ts_str
) - msg
->ts_dot
,
824 ".%09lu", (unsigned long)msg
->ts
.tv_nsec
);
826 msg
->ts_flags
= ZLOG_TS_ISO8601
| (flags
& ZLOG_TS_UTC
);
829 len1
= flags
& ZLOG_TS_PREC
;
830 len1
= (msg
->ts_dot
- msg
->ts_str
) + (len1
? len1
+ 1 : 0);
832 if (len1
> strlen(msg
->ts_str
))
833 len1
= strlen(msg
->ts_str
);
835 if (flags
& ZLOG_TS_LEGACY
) {
840 memset(out
->pos
, 0, outsz
);
845 /* just swap out the formatting, faster than redoing it */
846 for (char *p
= msg
->ts_str
; p
< msg
->ts_str
+ len1
; p
++) {
860 size_t len2
= strlen(msg
->ts_zonetail
);
865 if (len1
+ len2
> outsz
) {
866 memset(out
->pos
, 0, outsz
);
871 memcpy(out
->pos
, msg
->ts_str
, len1
);
873 memcpy(out
->pos
, msg
->ts_zonetail
, len2
);
879 size_t zlog_msg_ts_3164(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
881 flags
&= ZLOG_TS_UTC
;
883 if (!msg
->ts_3164_str
[0] || flags
!= msg
->ts_3164_flags
) {
884 /* these are "hardcoded" in RFC3164, so they're here too... */
885 static const char *const months
[12] = {
886 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
887 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
891 /* RFC3164 explicitly asks for local time, but common usage
894 if (flags
& ZLOG_TS_UTC
)
895 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
897 localtime_r(&msg
->ts
.tv_sec
, &tm
);
899 snprintfrr(msg
->ts_3164_str
, sizeof(msg
->ts_3164_str
),
900 "%3s %2d %02d:%02d:%02d", months
[tm
.tm_mon
],
901 tm
.tm_mday
, tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
903 msg
->ts_3164_flags
= flags
;
905 return bputs(out
, msg
->ts_3164_str
);
908 void zlog_msg_tsraw(struct zlog_msg
*msg
, struct timespec
*ts
)
910 memcpy(ts
, &msg
->ts
, sizeof(*ts
));
913 void zlog_set_prefix_ec(bool enable
)
915 atomic_store_explicit(&zlog_ec
, enable
, memory_order_relaxed
);
918 bool zlog_get_prefix_ec(void)
920 return atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
923 void zlog_set_prefix_xid(bool enable
)
925 atomic_store_explicit(&zlog_xid
, enable
, memory_order_relaxed
);
928 bool zlog_get_prefix_xid(void)
930 return atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
933 /* setup functions */
935 struct zlog_target
*zlog_target_clone(struct memtype
*mt
,
936 struct zlog_target
*oldzt
, size_t size
)
938 struct zlog_target
*newzt
;
940 newzt
= XCALLOC(mt
, size
);
942 newzt
->prio_min
= oldzt
->prio_min
;
943 newzt
->logfn
= oldzt
->logfn
;
944 newzt
->logfn_sigsafe
= oldzt
->logfn_sigsafe
;
950 struct zlog_target
*zlog_target_replace(struct zlog_target
*oldzt
,
951 struct zlog_target
*newzt
)
954 zlog_targets_add_tail(&zlog_targets
, newzt
);
956 zlog_targets_del(&zlog_targets
, oldzt
);
961 * Enable or disable 'immediate' output - default is to buffer
962 * each pthread's messages.
964 void zlog_set_immediate(bool set_p
)
966 default_immediate
= set_p
;
971 #define TMPBASEDIR "/var/tmp/frr"
973 static char zlog_tmpdir
[MAXPATHLEN
];
975 void zlog_aux_init(const char *prefix
, int prio_min
)
978 strlcpy(zlog_prefix
, prefix
, sizeof(zlog_prefix
));
980 hook_call(zlog_aux_init
, prefix
, prio_min
);
983 void zlog_init(const char *progname
, const char *protoname
,
984 unsigned short instance
, uid_t uid
, gid_t gid
)
988 zlog_instance
= instance
;
991 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
), "%s/%s-%d.%ld",
992 TMPBASEDIR
, progname
, instance
, (long)getpid());
994 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
995 "%s[%d]: ", protoname
, instance
);
997 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
), "%s/%s.%ld",
998 TMPBASEDIR
, progname
, (long)getpid());
1000 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
1004 if (mkdir(TMPBASEDIR
, 0700) != 0) {
1005 if (errno
!= EEXIST
) {
1006 zlog_err("failed to mkdir \"%s\": %s",
1007 TMPBASEDIR
, strerror(errno
));
1011 chown(TMPBASEDIR
, zlog_uid
, zlog_gid
);
1013 if (mkdir(zlog_tmpdir
, 0700) != 0) {
1014 zlog_err("failed to mkdir \"%s\": %s",
1015 zlog_tmpdir
, strerror(errno
));
1020 zlog_tmpdirfd
= open(zlog_tmpdir
,
1021 O_PATH
| O_RDONLY
| O_CLOEXEC
);
1023 zlog_tmpdirfd
= open(zlog_tmpdir
,
1024 O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
);
1026 if (zlog_tmpdirfd
< 0) {
1027 zlog_err("failed to open \"%s\": %s",
1028 zlog_tmpdir
, strerror(errno
));
1032 #ifdef AT_EMPTY_PATH
1033 fchownat(zlog_tmpdirfd
, "", zlog_uid
, zlog_gid
, AT_EMPTY_PATH
);
1035 chown(zlog_tmpdir
, zlog_uid
, zlog_gid
);
1038 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
1042 zlog_err("crashlog and per-thread log buffering unavailable!");
1043 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
1046 void zlog_fini(void)
1048 hook_call(zlog_fini
);
1050 if (zlog_tmpdirfd
>= 0) {
1051 close(zlog_tmpdirfd
);
1054 if (rmdir(zlog_tmpdir
))
1055 zlog_err("failed to rmdir \"%s\": %s",
1056 zlog_tmpdir
, strerror(errno
));