2 * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
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.
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.
22 #include <sys/types.h>
31 #ifdef HAVE_PTHREAD_NP_H
32 #include <pthread_np.h>
35 #include <sys/syscall.h>
47 #include <mach/mach_traps.h>
51 #define UNW_LOCAL_ONLY
52 #include <libunwind.h>
61 #include "libfrr_trace.h"
64 DEFINE_MTYPE_STATIC(LIB
, LOG_MESSAGE
, "log message");
65 DEFINE_MTYPE_STATIC(LIB
, LOG_TLSBUF
, "log thread-local buffer");
67 DEFINE_HOOK(zlog_init
, (const char *progname
, const char *protoname
,
68 unsigned short instance
, uid_t uid
, gid_t gid
),
69 (progname
, protoname
, instance
, uid
, gid
));
70 DEFINE_KOOH(zlog_fini
, (), ());
71 DEFINE_HOOK(zlog_aux_init
, (const char *prefix
, int prio_min
),
74 char zlog_prefix
[128];
76 int zlog_tmpdirfd
= -1;
77 int zlog_instance
= -1;
79 static atomic_bool zlog_ec
= true, zlog_xid
= true;
81 /* these are kept around because logging is initialized (and directories
82 * & files created) before zprivs code switches to the FRR user; therefore
83 * we need to chown() things so we don't get permission errors later when
84 * trying to delete things on shutdown
86 static uid_t zlog_uid
= -1;
87 static gid_t zlog_gid
= -1;
89 DECLARE_ATOMLIST(zlog_targets
, struct zlog_target
, head
);
90 static struct zlog_targets_head zlog_targets
;
92 /* Global setting for buffered vs immediate output. The default is
93 * per-pthread buffering.
95 static bool default_immediate
;
97 /* cf. zlog.h for additional comments on this struct.
99 * Note: you MUST NOT pass the format string + va_list to non-FRR format
100 * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
101 * extended prinf() with additional formats (%pI4 and the like).
103 * Also remember to use va_copy() on args.
112 const struct xref_logmsg
*xref
;
120 /* This is always ISO8601 with sub-second precision 9 here, it's
121 * converted for callers as needed. ts_dot points to the "."
122 * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
125 * Valid if ZLOG_TS_ISO8601 is set.
126 * (0 if timestamp has not been formatted yet)
128 char ts_str
[32], *ts_dot
, ts_zonetail
[8];
131 /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from
132 * the above, so just kept separately here.
134 uint32_t ts_3164_flags
;
135 char ts_3164_str
[16];
137 /* at the time of writing, 16 args was the actual maximum of arguments
138 * to a single zlog call. Particularly printing flag bitmasks seems
139 * to drive this. That said, the overhead of dynamically sizing this
140 * probably outweighs the value. If anything, a printfrr extension
141 * for printing flag bitmasks might be a good idea.
143 struct fmt_outpos argpos
[24];
147 /* thread-local log message buffering
149 * This is strictly optional and set up by calling zlog_tls_buffer_init()
150 * on a particular thread.
152 * If in use, this will create a temporary file in /var/tmp which is used as
153 * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
154 * access doesn't require any syscalls, but in case of a crash the kernel
155 * knows to sync the memory back to disk. This way the user can still get the
156 * last log messages if there were any left unwritten in the buffer.
158 * Sizing this dynamically isn't particularly useful, so here's an 8k buffer
159 * with a message limit of 64 messages. Message metadata (e.g. priority,
160 * timestamp) aren't in the mmap region, so they're lost on crash, but we can
164 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
168 #define TLS_LOG_BUF_SIZE 8192
169 #define TLS_LOG_MAXMSG 64
177 struct zlog_msg msgs
[TLS_LOG_MAXMSG
];
178 struct zlog_msg
*msgp
[TLS_LOG_MAXMSG
];
181 static inline void zlog_tls_free(void *arg
);
183 /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
184 * available we'll use it here
188 static pthread_key_t zlog_tls_key
;
190 static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
191 static void zlog_tls_key_init(void)
193 pthread_key_create(&zlog_tls_key
, zlog_tls_free
);
196 static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
197 static void zlog_tls_key_fini(void)
199 pthread_key_delete(zlog_tls_key
);
202 static inline struct zlog_tls
*zlog_tls_get(void)
204 return pthread_getspecific(zlog_tls_key
);
207 static inline void zlog_tls_set(struct zlog_tls
*val
)
209 pthread_setspecific(zlog_tls_key
, val
);
212 # ifndef thread_local
213 # define thread_local __thread
216 static thread_local
struct zlog_tls
*zlog_tls_var
217 __attribute__((tls_model("initial-exec")));
219 static inline struct zlog_tls
*zlog_tls_get(void)
224 static inline void zlog_tls_set(struct zlog_tls
*val
)
231 static intmax_t zlog_gettid(void)
234 /* accessing a TLS variable is much faster than a syscall */
235 static thread_local
intmax_t cached_tid
= -1;
236 if (cached_tid
!= -1)
241 #ifdef HAVE_PTHREAD_GETTHREADID_NP
242 rv
= pthread_getthreadid_np();
244 rv
= syscall(__NR_gettid
);
245 #elif defined(__NetBSD__)
247 #elif defined(__FreeBSD__)
249 #elif defined(__DragonFly__)
251 #elif defined(__OpenBSD__)
255 #elif defined(__APPLE__)
256 rv
= mach_thread_self();
257 mach_port_deallocate(mach_task_self(), rv
);
266 void zlog_tls_buffer_init(void)
268 struct zlog_tls
*zlog_tls
;
269 char mmpath
[MAXPATHLEN
];
273 zlog_tls
= zlog_tls_get();
275 if (zlog_tls
|| zlog_tmpdirfd
< 0)
278 zlog_tls
= XCALLOC(MTYPE_LOG_TLSBUF
, sizeof(*zlog_tls
));
279 for (i
= 0; i
< array_size(zlog_tls
->msgp
); i
++)
280 zlog_tls
->msgp
[i
] = &zlog_tls
->msgs
[i
];
282 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%jd", zlog_gettid());
284 mmfd
= openat(zlog_tmpdirfd
, mmpath
,
285 O_RDWR
| O_CREAT
| O_EXCL
| O_CLOEXEC
, 0600);
287 zlog_err("failed to open thread log buffer \"%s\": %s",
288 mmpath
, strerror(errno
));
291 fchown(mmfd
, zlog_uid
, zlog_gid
);
293 #ifdef HAVE_POSIX_FALLOCATE
294 if (posix_fallocate(mmfd
, 0, TLS_LOG_BUF_SIZE
) != 0)
295 /* note next statement is under above if() */
297 if (ftruncate(mmfd
, TLS_LOG_BUF_SIZE
) < 0) {
298 zlog_err("failed to allocate thread log buffer \"%s\": %s",
299 mmpath
, strerror(errno
));
300 goto out_anon_unlink
;
303 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
304 MAP_SHARED
, mmfd
, 0);
305 if (zlog_tls
->mmbuf
== MAP_FAILED
) {
306 zlog_err("failed to mmap thread log buffer \"%s\": %s",
307 mmpath
, strerror(errno
));
308 goto out_anon_unlink
;
310 zlog_tls
->do_unlink
= true;
313 zlog_tls_set(zlog_tls
);
317 unlinkat(zlog_tmpdirfd
, mmpath
, 0);
321 #ifndef MAP_ANONYMOUS
322 #define MAP_ANONYMOUS MAP_ANON
324 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
325 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
327 if (!zlog_tls
->mmbuf
) {
328 zlog_err("failed to anonymous-mmap thread log buffer: %s",
330 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
335 zlog_tls_set(zlog_tls
);
338 void zlog_tls_buffer_fini(void)
340 char mmpath
[MAXPATHLEN
];
341 struct zlog_tls
*zlog_tls
= zlog_tls_get();
342 bool do_unlink
= zlog_tls
? zlog_tls
->do_unlink
: false;
344 zlog_tls_buffer_flush();
346 zlog_tls_free(zlog_tls
);
349 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%jd", zlog_gettid());
350 if (do_unlink
&& unlinkat(zlog_tmpdirfd
, mmpath
, 0))
351 zlog_err("unlink logbuf: %s (%d)", strerror(errno
), errno
);
354 #else /* !CAN_DO_TLS */
355 void zlog_tls_buffer_init(void)
359 void zlog_tls_buffer_fini(void)
364 void zlog_msg_pid(struct zlog_msg
*msg
, intmax_t *pid
, intmax_t *tid
)
367 static thread_local
intmax_t cached_pid
= -1;
368 if (cached_pid
!= -1)
371 cached_pid
= *pid
= (intmax_t)getpid();
373 *pid
= (intmax_t)getpid();
376 *tid
= zlog_gettid();
382 static inline void zlog_tls_free(void *arg
)
384 struct zlog_tls
*zlog_tls
= arg
;
389 munmap(zlog_tls
->mmbuf
, TLS_LOG_BUF_SIZE
);
390 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
393 void zlog_tls_buffer_flush(void)
395 struct zlog_target
*zt
;
396 struct zlog_tls
*zlog_tls
= zlog_tls_get();
400 if (!zlog_tls
->nmsgs
)
404 frr_each_safe (zlog_targets
, &zlog_targets
, zt
) {
408 zt
->logfn(zt
, zlog_tls
->msgp
, zlog_tls
->nmsgs
);
412 zlog_tls
->bufpos
= 0;
417 static void vzlog_notls(const struct xref_logmsg
*xref
, int prio
,
418 const char *fmt
, va_list ap
)
420 struct zlog_target
*zt
;
421 struct zlog_msg stackmsg
= {
422 .prio
= prio
& LOG_PRIMASK
,
428 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
429 va_copy(msg
->args
, ap
);
430 msg
->stackbuf
= stackbuf
;
431 msg
->stackbufsz
= sizeof(stackbuf
);
434 frr_each_safe (zlog_targets
, &zlog_targets
, zt
) {
435 if (prio
> zt
->prio_min
)
440 zt
->logfn(zt
, &msg
, 1);
445 if (msg
->text
&& msg
->text
!= stackbuf
)
446 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
449 static void vzlog_tls(struct zlog_tls
*zlog_tls
, const struct xref_logmsg
*xref
,
450 int prio
, const char *fmt
, va_list ap
)
452 struct zlog_target
*zt
;
453 struct zlog_msg
*msg
;
455 bool ignoremsg
= true;
456 bool immediate
= default_immediate
;
458 /* avoid further processing cost if no target wants this message */
460 frr_each (zlog_targets
, &zlog_targets
, zt
) {
461 if (prio
> zt
->prio_min
)
471 msg
= &zlog_tls
->msgs
[zlog_tls
->nmsgs
];
473 if (zlog_tls
->nmsgs
== array_size(zlog_tls
->msgs
))
476 memset(msg
, 0, sizeof(*msg
));
477 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
478 va_copy(msg
->args
, ap
);
479 msg
->stackbuf
= buf
= zlog_tls
->mmbuf
+ zlog_tls
->bufpos
;
480 msg
->stackbufsz
= TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
- 1;
482 msg
->prio
= prio
& LOG_PRIMASK
;
484 if (msg
->prio
< LOG_INFO
)
488 /* messages written later need to take the formatting cost
489 * immediately since we can't hold a reference on varargs
491 zlog_msg_text(msg
, NULL
);
493 if (msg
->text
!= buf
)
494 /* zlog_msg_text called malloc() on us :( */
497 zlog_tls
->bufpos
+= msg
->textlen
+ 1;
498 /* write a second \0 to mark current end position
499 * (in case of crash this signals end of unwritten log
500 * messages in mmap'd logbuf file)
502 zlog_tls
->mmbuf
[zlog_tls
->bufpos
] = '\0';
504 /* avoid malloc() for next message */
505 if (TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
< 256)
511 zlog_tls_buffer_flush();
514 if (msg
->text
&& msg
->text
!= buf
)
515 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
518 static void zlog_backtrace_msg(const struct xref_logmsg
*xref
, int prio
)
520 struct thread
*tc
= pthread_getspecific(thread_current
);
521 const char *uid
= xref
->xref
.xrefdata
->uid
;
522 bool found_thread
= false;
524 zlog(prio
, "| (%s) message in thread %jd, at %s(), %s:%d", uid
,
525 zlog_gettid(), xref
->xref
.func
, xref
->xref
.file
, xref
->xref
.line
);
527 #ifdef HAVE_LIBUNWIND
528 const char *threadfunc
= tc
? tc
->xref
->funcname
: NULL
;
529 bool found_caller
= false;
532 unw_word_t ip
, off
, sp
;
537 unw_init_local(&cursor
, &uc
);
538 while (unw_step(&cursor
) > 0) {
539 char buf
[96], name
[128] = "?";
540 bool is_thread
= false;
542 unw_get_reg(&cursor
, UNW_REG_IP
, &ip
);
543 unw_get_reg(&cursor
, UNW_REG_SP
, &sp
);
545 if (unw_is_signal_frame(&cursor
))
546 zlog(prio
, "| (%s) ---- signal ----", uid
);
548 if (!unw_get_proc_name(&cursor
, buf
, sizeof(buf
), &off
)) {
549 if (!strcmp(buf
, xref
->xref
.func
))
551 if (threadfunc
&& !strcmp(buf
, threadfunc
))
552 found_thread
= is_thread
= true;
554 snprintf(name
, sizeof(name
), "%s+%#lx", buf
, (long)off
);
560 if (dladdr((void *)ip
, &dlinfo
))
561 zlog(prio
, "| (%s) %-36s %16lx+%08lx %16lx %s", uid
,
562 name
, (long)dlinfo
.dli_fbase
,
563 (long)ip
- (long)dlinfo
.dli_fbase
, (long)sp
,
566 zlog(prio
, "| (%s) %-36s %16lx %16lx", uid
, name
,
570 zlog(prio
, "| (%s) ^- scheduled from %s(), %s:%u", uid
,
571 tc
->xref
->xref
.func
, tc
->xref
->xref
.file
,
572 tc
->xref
->xref
.line
);
574 #elif defined(HAVE_GLIBC_BACKTRACE)
579 n_frames
= backtrace(frames
, array_size(frames
));
583 names
= backtrace_symbols(frames
, n_frames
);
585 for (i
= 0; i
< n_frames
; i
++) {
586 void *retaddr
= frames
[i
];
587 char *loc
= names
[i
];
589 zlog(prio
, "| (%s) %16lx %-36s", uid
, (long)retaddr
, loc
);
593 if (!found_thread
&& tc
)
594 zlog(prio
, "| (%s) scheduled from %s(), %s:%u", uid
,
595 tc
->xref
->xref
.func
, tc
->xref
->xref
.file
,
596 tc
->xref
->xref
.line
);
599 void vzlogx(const struct xref_logmsg
*xref
, int prio
,
600 const char *fmt
, va_list ap
)
602 struct zlog_tls
*zlog_tls
= zlog_tls_get();
607 char *msg
= vasprintfrr(MTYPE_LOG_MESSAGE
, fmt
, copy
);
611 frrtracelog(TRACE_ERR
, msg
);
614 frrtracelog(TRACE_WARNING
, msg
);
617 frrtracelog(TRACE_DEBUG
, msg
);
620 frrtracelog(TRACE_DEBUG
, msg
);
624 frrtracelog(TRACE_INFO
, msg
);
629 XFREE(MTYPE_LOG_MESSAGE
, msg
);
633 vzlog_tls(zlog_tls
, xref
, prio
, fmt
, ap
);
635 vzlog_notls(xref
, prio
, fmt
, ap
);
638 struct xrefdata_logmsg
*xrdl
;
640 xrdl
= container_of(xref
->xref
.xrefdata
, struct xrefdata_logmsg
,
642 if (xrdl
->fl_print_bt
)
643 zlog_backtrace_msg(xref
, prio
);
647 void zlog_sigsafe(const char *text
, size_t len
)
649 struct zlog_target
*zt
;
650 const char *end
= text
+ len
, *nlpos
;
653 nlpos
= memchr(text
, '\n', end
- text
);
657 frr_each (zlog_targets
, &zlog_targets
, zt
) {
658 if (LOG_CRIT
> zt
->prio_min
)
660 if (!zt
->logfn_sigsafe
)
663 zt
->logfn_sigsafe(zt
, text
, nlpos
- text
);
672 void _zlog_assert_failed(const struct xref_assert
*xref
, const char *extra
, ...)
675 static bool assert_in_assert
; /* "global-ish" variable, init to 0 */
677 if (assert_in_assert
)
679 assert_in_assert
= true;
682 struct va_format vaf
;
689 "%s:%d: %s(): assertion (%s) failed, extra info: %pVA",
690 xref
->xref
.file
, xref
->xref
.line
, xref
->xref
.func
,
695 zlog(LOG_CRIT
, "%s:%d: %s(): assertion (%s) failed",
696 xref
->xref
.file
, xref
->xref
.line
, xref
->xref
.func
,
699 /* abort() prints backtrace & memstats in SIGABRT handler */
703 int zlog_msg_prio(struct zlog_msg
*msg
)
708 const struct xref_logmsg
*zlog_msg_xref(struct zlog_msg
*msg
)
713 const char *zlog_msg_text(struct zlog_msg
*msg
, size_t *textlen
)
718 size_t need
= 0, hdrlen
;
720 .buf
= msg
->stackbuf
,
721 .pos
= msg
->stackbuf
,
722 .len
= msg
->stackbufsz
,
725 do_ec
= atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
726 do_xid
= atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
728 if (msg
->xref
&& do_xid
&& msg
->xref
->xref
.xrefdata
->uid
[0]) {
729 need
+= bputch(&fb
, '[');
730 need
+= bputs(&fb
, msg
->xref
->xref
.xrefdata
->uid
);
731 need
+= bputch(&fb
, ']');
733 if (msg
->xref
&& do_ec
&& msg
->xref
->ec
)
734 need
+= bprintfrr(&fb
, "[EC %u]", msg
->xref
->ec
);
736 need
+= bputch(&fb
, ' ');
738 msg
->hdrlen
= hdrlen
= need
;
739 assert(hdrlen
< msg
->stackbufsz
);
741 fb
.outpos
= msg
->argpos
;
742 fb
.outpos_n
= array_size(msg
->argpos
);
745 va_copy(args
, msg
->args
);
746 need
+= vbprintfrr(&fb
, msg
->fmt
, args
);
750 need
+= bputch(&fb
, '\n');
752 if (need
<= msg
->stackbufsz
)
753 msg
->text
= msg
->stackbuf
;
755 msg
->text
= XMALLOC(MTYPE_LOG_MESSAGE
, need
);
757 memcpy(msg
->text
, msg
->stackbuf
, hdrlen
);
761 fb
.pos
= msg
->text
+ hdrlen
;
764 va_copy(args
, msg
->args
);
765 vbprintfrr(&fb
, msg
->fmt
, args
);
771 msg
->n_argpos
= fb
.outpos_i
;
774 *textlen
= msg
->textlen
;
778 void zlog_msg_args(struct zlog_msg
*msg
, size_t *hdrlen
, size_t *n_argpos
,
779 const struct fmt_outpos
**argpos
)
782 zlog_msg_text(msg
, NULL
);
785 *hdrlen
= msg
->hdrlen
;
787 *n_argpos
= msg
->n_argpos
;
789 *argpos
= msg
->argpos
;
792 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
793 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
795 size_t zlog_msg_ts(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
797 size_t outsz
= out
? (out
->buf
+ out
->len
- out
->pos
) : 0;
800 if (!(flags
& ZLOG_TS_FORMAT
))
803 if (!(msg
->ts_flags
& ZLOG_TS_FORMAT
) ||
804 ((msg
->ts_flags
^ flags
) & ZLOG_TS_UTC
)) {
807 if (flags
& ZLOG_TS_UTC
)
808 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
810 localtime_r(&msg
->ts
.tv_sec
, &tm
);
812 strftime(msg
->ts_str
, sizeof(msg
->ts_str
),
813 "%Y-%m-%dT%H:%M:%S", &tm
);
815 if (flags
& ZLOG_TS_UTC
) {
816 msg
->ts_zonetail
[0] = 'Z';
817 msg
->ts_zonetail
[1] = '\0';
819 snprintfrr(msg
->ts_zonetail
, sizeof(msg
->ts_zonetail
),
821 (int)(tm
.tm_gmtoff
/ 3600),
822 (int)(labs(tm
.tm_gmtoff
) / 60) % 60);
824 msg
->ts_dot
= msg
->ts_str
+ strlen(msg
->ts_str
);
825 snprintfrr(msg
->ts_dot
,
826 msg
->ts_str
+ sizeof(msg
->ts_str
) - msg
->ts_dot
,
827 ".%09lu", (unsigned long)msg
->ts
.tv_nsec
);
829 msg
->ts_flags
= ZLOG_TS_ISO8601
| (flags
& ZLOG_TS_UTC
);
832 len1
= flags
& ZLOG_TS_PREC
;
833 len1
= (msg
->ts_dot
- msg
->ts_str
) + (len1
? len1
+ 1 : 0);
835 if (len1
> strlen(msg
->ts_str
))
836 len1
= strlen(msg
->ts_str
);
838 if (flags
& ZLOG_TS_LEGACY
) {
843 memset(out
->pos
, 0, outsz
);
848 /* just swap out the formatting, faster than redoing it */
849 for (char *p
= msg
->ts_str
; p
< msg
->ts_str
+ len1
; p
++) {
863 size_t len2
= strlen(msg
->ts_zonetail
);
868 if (len1
+ len2
> outsz
) {
869 memset(out
->pos
, 0, outsz
);
874 memcpy(out
->pos
, msg
->ts_str
, len1
);
876 memcpy(out
->pos
, msg
->ts_zonetail
, len2
);
882 size_t zlog_msg_ts_3164(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
884 flags
&= ZLOG_TS_UTC
;
886 if (!msg
->ts_3164_str
[0] || flags
!= msg
->ts_3164_flags
) {
887 /* these are "hardcoded" in RFC3164, so they're here too... */
888 static const char *const months
[12] = {
889 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
890 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
894 /* RFC3164 explicitly asks for local time, but common usage
897 if (flags
& ZLOG_TS_UTC
)
898 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
900 localtime_r(&msg
->ts
.tv_sec
, &tm
);
902 snprintfrr(msg
->ts_3164_str
, sizeof(msg
->ts_3164_str
),
903 "%3s %2d %02d:%02d:%02d", months
[tm
.tm_mon
],
904 tm
.tm_mday
, tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
906 msg
->ts_3164_flags
= flags
;
908 return bputs(out
, msg
->ts_3164_str
);
911 void zlog_msg_tsraw(struct zlog_msg
*msg
, struct timespec
*ts
)
913 memcpy(ts
, &msg
->ts
, sizeof(*ts
));
916 void zlog_set_prefix_ec(bool enable
)
918 atomic_store_explicit(&zlog_ec
, enable
, memory_order_relaxed
);
921 bool zlog_get_prefix_ec(void)
923 return atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
926 void zlog_set_prefix_xid(bool enable
)
928 atomic_store_explicit(&zlog_xid
, enable
, memory_order_relaxed
);
931 bool zlog_get_prefix_xid(void)
933 return atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
936 /* setup functions */
938 struct zlog_target
*zlog_target_clone(struct memtype
*mt
,
939 struct zlog_target
*oldzt
, size_t size
)
941 struct zlog_target
*newzt
;
943 newzt
= XCALLOC(mt
, size
);
945 newzt
->prio_min
= oldzt
->prio_min
;
946 newzt
->logfn
= oldzt
->logfn
;
947 newzt
->logfn_sigsafe
= oldzt
->logfn_sigsafe
;
953 struct zlog_target
*zlog_target_replace(struct zlog_target
*oldzt
,
954 struct zlog_target
*newzt
)
957 zlog_targets_add_tail(&zlog_targets
, newzt
);
959 zlog_targets_del(&zlog_targets
, oldzt
);
964 * Enable or disable 'immediate' output - default is to buffer
965 * each pthread's messages.
967 void zlog_set_immediate(bool set_p
)
969 default_immediate
= set_p
;
974 #define TMPBASEDIR "/var/tmp/frr"
976 static char zlog_tmpdir
[MAXPATHLEN
];
978 void zlog_aux_init(const char *prefix
, int prio_min
)
981 strlcpy(zlog_prefix
, prefix
, sizeof(zlog_prefix
));
983 hook_call(zlog_aux_init
, prefix
, prio_min
);
986 void zlog_init(const char *progname
, const char *protoname
,
987 unsigned short instance
, uid_t uid
, gid_t gid
)
991 zlog_instance
= instance
;
994 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
),
995 "/var/tmp/frr/%s-%d.%ld",
996 progname
, instance
, (long)getpid());
998 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
999 "%s[%d]: ", protoname
, instance
);
1001 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
),
1002 "/var/tmp/frr/%s.%ld",
1003 progname
, (long)getpid());
1005 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
1009 if (mkdir(TMPBASEDIR
, 0700) != 0) {
1010 if (errno
!= EEXIST
) {
1011 zlog_err("failed to mkdir \"%s\": %s",
1012 TMPBASEDIR
, strerror(errno
));
1016 chown(TMPBASEDIR
, zlog_uid
, zlog_gid
);
1018 if (mkdir(zlog_tmpdir
, 0700) != 0) {
1019 zlog_err("failed to mkdir \"%s\": %s",
1020 zlog_tmpdir
, strerror(errno
));
1025 zlog_tmpdirfd
= open(zlog_tmpdir
,
1026 O_PATH
| O_RDONLY
| O_CLOEXEC
);
1028 zlog_tmpdirfd
= open(zlog_tmpdir
,
1029 O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
);
1031 if (zlog_tmpdirfd
< 0) {
1032 zlog_err("failed to open \"%s\": %s",
1033 zlog_tmpdir
, strerror(errno
));
1037 #ifdef AT_EMPTY_PATH
1038 fchownat(zlog_tmpdirfd
, "", zlog_uid
, zlog_gid
, AT_EMPTY_PATH
);
1040 chown(zlog_tmpdir
, zlog_uid
, zlog_gid
);
1043 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
1047 zlog_err("crashlog and per-thread log buffering unavailable!");
1048 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
1051 void zlog_fini(void)
1053 hook_call(zlog_fini
);
1055 if (zlog_tmpdirfd
>= 0) {
1056 close(zlog_tmpdirfd
);
1059 if (rmdir(zlog_tmpdir
))
1060 zlog_err("failed to rmdir \"%s\": %s",
1061 zlog_tmpdir
, strerror(errno
));