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 #pragma GCC diagnostic push
747 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
748 /* format-string checking is done further up the chain */
749 need
+= vbprintfrr(&fb
, msg
->fmt
, args
);
750 #pragma GCC diagnostic pop
754 need
+= bputch(&fb
, '\n');
756 if (need
<= msg
->stackbufsz
)
757 msg
->text
= msg
->stackbuf
;
759 msg
->text
= XMALLOC(MTYPE_LOG_MESSAGE
, need
);
761 memcpy(msg
->text
, msg
->stackbuf
, hdrlen
);
765 fb
.pos
= msg
->text
+ hdrlen
;
768 va_copy(args
, msg
->args
);
769 #pragma GCC diagnostic push
770 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
772 vbprintfrr(&fb
, msg
->fmt
, args
);
773 #pragma GCC diagnostic pop
779 msg
->n_argpos
= fb
.outpos_i
;
782 *textlen
= msg
->textlen
;
786 void zlog_msg_args(struct zlog_msg
*msg
, size_t *hdrlen
, size_t *n_argpos
,
787 const struct fmt_outpos
**argpos
)
790 zlog_msg_text(msg
, NULL
);
793 *hdrlen
= msg
->hdrlen
;
795 *n_argpos
= msg
->n_argpos
;
797 *argpos
= msg
->argpos
;
800 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
801 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
803 size_t zlog_msg_ts(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
805 size_t outsz
= out
? (out
->buf
+ out
->len
- out
->pos
) : 0;
808 if (!(flags
& ZLOG_TS_FORMAT
))
811 if (!(msg
->ts_flags
& ZLOG_TS_FORMAT
) ||
812 ((msg
->ts_flags
^ flags
) & ZLOG_TS_UTC
)) {
815 if (flags
& ZLOG_TS_UTC
)
816 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
818 localtime_r(&msg
->ts
.tv_sec
, &tm
);
820 strftime(msg
->ts_str
, sizeof(msg
->ts_str
),
821 "%Y-%m-%dT%H:%M:%S", &tm
);
823 if (flags
& ZLOG_TS_UTC
) {
824 msg
->ts_zonetail
[0] = 'Z';
825 msg
->ts_zonetail
[1] = '\0';
827 snprintfrr(msg
->ts_zonetail
, sizeof(msg
->ts_zonetail
),
829 (int)(tm
.tm_gmtoff
/ 3600),
830 (int)(labs(tm
.tm_gmtoff
) / 60) % 60);
832 msg
->ts_dot
= msg
->ts_str
+ strlen(msg
->ts_str
);
833 snprintfrr(msg
->ts_dot
,
834 msg
->ts_str
+ sizeof(msg
->ts_str
) - msg
->ts_dot
,
835 ".%09lu", (unsigned long)msg
->ts
.tv_nsec
);
837 msg
->ts_flags
= ZLOG_TS_ISO8601
| (flags
& ZLOG_TS_UTC
);
840 len1
= flags
& ZLOG_TS_PREC
;
841 len1
= (msg
->ts_dot
- msg
->ts_str
) + (len1
? len1
+ 1 : 0);
843 if (len1
> strlen(msg
->ts_str
))
844 len1
= strlen(msg
->ts_str
);
846 if (flags
& ZLOG_TS_LEGACY
) {
851 memset(out
->pos
, 0, outsz
);
856 /* just swap out the formatting, faster than redoing it */
857 for (char *p
= msg
->ts_str
; p
< msg
->ts_str
+ len1
; p
++) {
871 size_t len2
= strlen(msg
->ts_zonetail
);
876 if (len1
+ len2
> outsz
) {
877 memset(out
->pos
, 0, outsz
);
882 memcpy(out
->pos
, msg
->ts_str
, len1
);
884 memcpy(out
->pos
, msg
->ts_zonetail
, len2
);
890 size_t zlog_msg_ts_3164(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
892 flags
&= ZLOG_TS_UTC
;
894 if (!msg
->ts_3164_str
[0] || flags
!= msg
->ts_3164_flags
) {
895 /* these are "hardcoded" in RFC3164, so they're here too... */
896 static const char *const months
[12] = {
897 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
898 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
902 /* RFC3164 explicitly asks for local time, but common usage
905 if (flags
& ZLOG_TS_UTC
)
906 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
908 localtime_r(&msg
->ts
.tv_sec
, &tm
);
910 snprintfrr(msg
->ts_3164_str
, sizeof(msg
->ts_3164_str
),
911 "%3s %2d %02d:%02d:%02d", months
[tm
.tm_mon
],
912 tm
.tm_mday
, tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
);
914 msg
->ts_3164_flags
= flags
;
916 return bputs(out
, msg
->ts_3164_str
);
919 void zlog_msg_tsraw(struct zlog_msg
*msg
, struct timespec
*ts
)
921 memcpy(ts
, &msg
->ts
, sizeof(*ts
));
924 void zlog_set_prefix_ec(bool enable
)
926 atomic_store_explicit(&zlog_ec
, enable
, memory_order_relaxed
);
929 bool zlog_get_prefix_ec(void)
931 return atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
934 void zlog_set_prefix_xid(bool enable
)
936 atomic_store_explicit(&zlog_xid
, enable
, memory_order_relaxed
);
939 bool zlog_get_prefix_xid(void)
941 return atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
944 /* setup functions */
946 struct zlog_target
*zlog_target_clone(struct memtype
*mt
,
947 struct zlog_target
*oldzt
, size_t size
)
949 struct zlog_target
*newzt
;
951 newzt
= XCALLOC(mt
, size
);
953 newzt
->prio_min
= oldzt
->prio_min
;
954 newzt
->logfn
= oldzt
->logfn
;
955 newzt
->logfn_sigsafe
= oldzt
->logfn_sigsafe
;
961 struct zlog_target
*zlog_target_replace(struct zlog_target
*oldzt
,
962 struct zlog_target
*newzt
)
965 zlog_targets_add_tail(&zlog_targets
, newzt
);
967 zlog_targets_del(&zlog_targets
, oldzt
);
972 * Enable or disable 'immediate' output - default is to buffer
973 * each pthread's messages.
975 void zlog_set_immediate(bool set_p
)
977 default_immediate
= set_p
;
982 #define TMPBASEDIR "/var/tmp/frr"
984 static char zlog_tmpdir
[MAXPATHLEN
];
986 void zlog_aux_init(const char *prefix
, int prio_min
)
989 strlcpy(zlog_prefix
, prefix
, sizeof(zlog_prefix
));
991 hook_call(zlog_aux_init
, prefix
, prio_min
);
994 void zlog_init(const char *progname
, const char *protoname
,
995 unsigned short instance
, uid_t uid
, gid_t gid
)
999 zlog_instance
= instance
;
1002 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
), "%s/%s-%d.%ld",
1003 TMPBASEDIR
, progname
, instance
, (long)getpid());
1005 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
1006 "%s[%d]: ", protoname
, instance
);
1008 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
), "%s/%s.%ld",
1009 TMPBASEDIR
, progname
, (long)getpid());
1011 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
1015 if (mkdir(TMPBASEDIR
, 0700) != 0) {
1016 if (errno
!= EEXIST
) {
1017 zlog_err("failed to mkdir \"%s\": %s",
1018 TMPBASEDIR
, strerror(errno
));
1022 chown(TMPBASEDIR
, zlog_uid
, zlog_gid
);
1024 if (mkdir(zlog_tmpdir
, 0700) != 0) {
1025 zlog_err("failed to mkdir \"%s\": %s",
1026 zlog_tmpdir
, strerror(errno
));
1031 zlog_tmpdirfd
= open(zlog_tmpdir
,
1032 O_PATH
| O_RDONLY
| O_CLOEXEC
);
1034 zlog_tmpdirfd
= open(zlog_tmpdir
,
1035 O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
);
1037 if (zlog_tmpdirfd
< 0) {
1038 zlog_err("failed to open \"%s\": %s",
1039 zlog_tmpdir
, strerror(errno
));
1043 #ifdef AT_EMPTY_PATH
1044 fchownat(zlog_tmpdirfd
, "", zlog_uid
, zlog_gid
, AT_EMPTY_PATH
);
1046 chown(zlog_tmpdir
, zlog_uid
, zlog_gid
);
1049 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
1053 zlog_err("crashlog and per-thread log buffering unavailable!");
1054 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
1057 void zlog_fini(void)
1059 hook_call(zlog_fini
);
1061 if (zlog_tmpdirfd
>= 0) {
1062 close(zlog_tmpdirfd
);
1065 if (rmdir(zlog_tmpdir
))
1066 zlog_err("failed to rmdir \"%s\": %s",
1067 zlog_tmpdir
, strerror(errno
));