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>
55 #include "libfrr_trace.h"
57 DEFINE_MTYPE_STATIC(LIB
, LOG_MESSAGE
, "log message")
58 DEFINE_MTYPE_STATIC(LIB
, LOG_TLSBUF
, "log thread-local buffer")
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
),
67 char zlog_prefix
[128];
69 int zlog_tmpdirfd
= -1;
71 /* these are kept around because logging is initialized (and directories
72 * & files created) before zprivs code switches to the FRR user; therefore
73 * we need to chown() things so we don't get permission errors later when
74 * trying to delete things on shutdown
76 static uid_t zlog_uid
= -1;
77 static gid_t zlog_gid
= -1;
79 DECLARE_ATOMLIST(zlog_targets
, struct zlog_target
, head
);
80 static struct zlog_targets_head zlog_targets
;
82 /* cf. zlog.h for additional comments on this struct.
84 * Note: you MUST NOT pass the format string + va_list to non-FRR format
85 * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
86 * extended prinf() with additional formats (%pI4 and the like).
88 * Also remember to use va_copy() on args.
97 const struct xref_logmsg
*xref
;
104 /* This is always ISO8601 with sub-second precision 9 here, it's
105 * converted for callers as needed. ts_dot points to the "."
106 * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
109 * Valid if ZLOG_TS_ISO8601 is set.
110 * (0 if timestamp has not been formatted yet)
113 char ts_str
[32], *ts_dot
, ts_zonetail
[8];
116 /* thread-local log message buffering
118 * This is strictly optional and set up by calling zlog_tls_buffer_init()
119 * on a particular thread.
121 * If in use, this will create a temporary file in /var/tmp which is used as
122 * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
123 * access doesn't require any syscalls, but in case of a crash the kernel
124 * knows to sync the memory back to disk. This way the user can still get the
125 * last log messages if there were any left unwritten in the buffer.
127 * Sizing this dynamically isn't particularly useful, so here's an 8k buffer
128 * with a message limit of 64 messages. Message metadata (e.g. priority,
129 * timestamp) aren't in the mmap region, so they're lost on crash, but we can
133 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
137 #define TLS_LOG_BUF_SIZE 8192
138 #define TLS_LOG_MAXMSG 64
145 struct zlog_msg msgs
[TLS_LOG_MAXMSG
];
146 struct zlog_msg
*msgp
[TLS_LOG_MAXMSG
];
149 static inline void zlog_tls_free(void *arg
);
151 /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
152 * available we'll use it here
156 static pthread_key_t zlog_tls_key
;
158 static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
159 static void zlog_tls_key_init(void)
161 pthread_key_create(&zlog_tls_key
, zlog_tls_free
);
164 static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
165 static void zlog_tls_key_fini(void)
167 pthread_key_delete(zlog_tls_key
);
170 static inline struct zlog_tls
*zlog_tls_get(void)
172 return pthread_getspecific(zlog_tls_key
);
175 static inline void zlog_tls_set(struct zlog_tls
*val
)
177 pthread_setspecific(zlog_tls_key
, val
);
180 # ifndef thread_local
181 # define thread_local __thread
184 static thread_local
struct zlog_tls
*zlog_tls_var
185 __attribute__((tls_model("initial-exec")));
187 static inline struct zlog_tls
*zlog_tls_get(void)
192 static inline void zlog_tls_set(struct zlog_tls
*val
)
199 static long zlog_gettid(void)
202 #ifdef HAVE_PTHREAD_GETTHREADID_NP
203 rv
= pthread_getthreadid_np();
205 rv
= syscall(__NR_gettid
);
206 #elif defined(__NetBSD__)
208 #elif defined(__FreeBSD__)
210 #elif defined(__DragonFly__)
212 #elif defined(__OpenBSD__)
216 #elif defined(__APPLE__)
217 rv
= mach_thread_self();
218 mach_port_deallocate(mach_task_self(), rv
);
223 void zlog_tls_buffer_init(void)
225 struct zlog_tls
*zlog_tls
;
226 char mmpath
[MAXPATHLEN
];
230 zlog_tls
= zlog_tls_get();
232 if (zlog_tls
|| zlog_tmpdirfd
< 0)
235 zlog_tls
= XCALLOC(MTYPE_LOG_TLSBUF
, sizeof(*zlog_tls
));
236 for (i
= 0; i
< array_size(zlog_tls
->msgp
); i
++)
237 zlog_tls
->msgp
[i
] = &zlog_tls
->msgs
[i
];
239 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%ld", zlog_gettid());
241 mmfd
= openat(zlog_tmpdirfd
, mmpath
,
242 O_RDWR
| O_CREAT
| O_EXCL
| O_CLOEXEC
, 0600);
244 zlog_err("failed to open thread log buffer \"%s\": %s",
245 mmpath
, strerror(errno
));
248 fchown(mmfd
, zlog_uid
, zlog_gid
);
250 #ifdef HAVE_POSIX_FALLOCATE
251 if (posix_fallocate(mmfd
, 0, TLS_LOG_BUF_SIZE
) != 0)
252 /* note next statement is under above if() */
254 if (ftruncate(mmfd
, TLS_LOG_BUF_SIZE
) < 0) {
255 zlog_err("failed to allocate thread log buffer \"%s\": %s",
256 mmpath
, strerror(errno
));
257 goto out_anon_unlink
;
260 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
261 MAP_SHARED
, mmfd
, 0);
262 if (zlog_tls
->mmbuf
== MAP_FAILED
) {
263 zlog_err("failed to mmap thread log buffer \"%s\": %s",
264 mmpath
, strerror(errno
));
265 goto out_anon_unlink
;
269 zlog_tls_set(zlog_tls
);
277 #ifndef MAP_ANONYMOUS
278 #define MAP_ANONYMOUS MAP_ANON
280 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
281 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
283 if (!zlog_tls
->mmbuf
) {
284 zlog_err("failed to anonymous-mmap thread log buffer: %s",
286 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
291 zlog_tls_set(zlog_tls
);
294 void zlog_tls_buffer_fini(void)
296 char mmpath
[MAXPATHLEN
];
298 zlog_tls_buffer_flush();
300 zlog_tls_free(zlog_tls_get());
303 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%ld", zlog_gettid());
304 if (unlinkat(zlog_tmpdirfd
, mmpath
, 0))
305 zlog_err("unlink logbuf: %s (%d)", strerror(errno
), errno
);
308 #else /* !CAN_DO_TLS */
309 void zlog_tls_buffer_init(void)
313 void zlog_tls_buffer_fini(void)
318 static inline void zlog_tls_free(void *arg
)
320 struct zlog_tls
*zlog_tls
= arg
;
325 munmap(zlog_tls
->mmbuf
, TLS_LOG_BUF_SIZE
);
326 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
329 void zlog_tls_buffer_flush(void)
331 struct zlog_target
*zt
;
332 struct zlog_tls
*zlog_tls
= zlog_tls_get();
336 if (!zlog_tls
->nmsgs
)
340 frr_each (zlog_targets
, &zlog_targets
, zt
) {
344 zt
->logfn(zt
, zlog_tls
->msgp
, zlog_tls
->nmsgs
);
348 zlog_tls
->bufpos
= 0;
353 static void vzlog_notls(const struct xref_logmsg
*xref
, int prio
,
354 const char *fmt
, va_list ap
)
356 struct zlog_target
*zt
;
357 struct zlog_msg stackmsg
= {
358 .prio
= prio
& LOG_PRIMASK
,
364 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
365 va_copy(msg
->args
, ap
);
366 msg
->stackbuf
= stackbuf
;
367 msg
->stackbufsz
= sizeof(stackbuf
);
370 frr_each (zlog_targets
, &zlog_targets
, zt
) {
371 if (prio
> zt
->prio_min
)
376 zt
->logfn(zt
, &msg
, 1);
381 if (msg
->text
&& msg
->text
!= stackbuf
)
382 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
385 static void vzlog_tls(struct zlog_tls
*zlog_tls
, const struct xref_logmsg
*xref
,
386 int prio
, const char *fmt
, va_list ap
)
388 struct zlog_target
*zt
;
389 struct zlog_msg
*msg
;
391 bool ignoremsg
= true;
392 bool immediate
= false;
394 /* avoid further processing cost if no target wants this message */
396 frr_each (zlog_targets
, &zlog_targets
, zt
) {
397 if (prio
> zt
->prio_min
)
407 msg
= &zlog_tls
->msgs
[zlog_tls
->nmsgs
];
409 if (zlog_tls
->nmsgs
== array_size(zlog_tls
->msgs
))
412 memset(msg
, 0, sizeof(*msg
));
413 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
414 va_copy(msg
->args
, ap
);
415 msg
->stackbuf
= buf
= zlog_tls
->mmbuf
+ zlog_tls
->bufpos
;
416 msg
->stackbufsz
= TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
- 1;
418 msg
->prio
= prio
& LOG_PRIMASK
;
420 if (msg
->prio
< LOG_INFO
)
424 /* messages written later need to take the formatting cost
425 * immediately since we can't hold a reference on varargs
427 zlog_msg_text(msg
, NULL
);
429 if (msg
->text
!= buf
)
430 /* zlog_msg_text called malloc() on us :( */
433 zlog_tls
->bufpos
+= msg
->textlen
+ 1;
434 /* write a second \0 to mark current end position
435 * (in case of crash this signals end of unwritten log
436 * messages in mmap'd logbuf file)
438 zlog_tls
->mmbuf
[zlog_tls
->bufpos
] = '\0';
440 /* avoid malloc() for next message */
441 if (TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
< 256)
447 zlog_tls_buffer_flush();
450 if (msg
->text
&& msg
->text
!= buf
)
451 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
454 void vzlogx(const struct xref_logmsg
*xref
, int prio
,
455 const char *fmt
, va_list ap
)
457 struct zlog_tls
*zlog_tls
= zlog_tls_get();
462 char *msg
= vasprintfrr(MTYPE_LOG_MESSAGE
, fmt
, copy
);
466 frrtracelog(TRACE_ERR
, msg
);
469 frrtracelog(TRACE_WARNING
, msg
);
472 frrtracelog(TRACE_DEBUG
, msg
);
475 frrtracelog(TRACE_DEBUG
, msg
);
479 frrtracelog(TRACE_INFO
, msg
);
484 XFREE(MTYPE_LOG_MESSAGE
, msg
);
488 vzlog_tls(zlog_tls
, xref
, prio
, fmt
, ap
);
490 vzlog_notls(xref
, prio
, fmt
, ap
);
493 void zlog_sigsafe(const char *text
, size_t len
)
495 struct zlog_target
*zt
;
496 const char *end
= text
+ len
, *nlpos
;
499 nlpos
= memchr(text
, '\n', end
- text
);
503 frr_each (zlog_targets
, &zlog_targets
, zt
) {
504 if (LOG_CRIT
> zt
->prio_min
)
506 if (!zt
->logfn_sigsafe
)
509 zt
->logfn_sigsafe(zt
, text
, nlpos
- text
);
519 int zlog_msg_prio(struct zlog_msg
*msg
)
524 const struct xref_logmsg
*zlog_msg_xref(struct zlog_msg
*msg
)
529 const char *zlog_msg_text(struct zlog_msg
*msg
, size_t *textlen
)
534 va_copy(args
, msg
->args
);
535 msg
->text
= vasnprintfrr(MTYPE_LOG_MESSAGE
, msg
->stackbuf
,
536 msg
->stackbufsz
, msg
->fmt
, args
);
537 msg
->textlen
= strlen(msg
->text
);
541 *textlen
= msg
->textlen
;
545 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
546 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
548 size_t zlog_msg_ts(struct zlog_msg
*msg
, char *out
, size_t outsz
,
553 if (!(flags
& ZLOG_TS_FORMAT
))
556 if (!(msg
->ts_flags
& ZLOG_TS_FORMAT
) ||
557 ((msg
->ts_flags
^ flags
) & ZLOG_TS_UTC
)) {
560 if (flags
& ZLOG_TS_UTC
)
561 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
563 localtime_r(&msg
->ts
.tv_sec
, &tm
);
565 strftime(msg
->ts_str
, sizeof(msg
->ts_str
),
566 "%Y-%m-%dT%H:%M:%S", &tm
);
568 if (flags
& ZLOG_TS_UTC
) {
569 msg
->ts_zonetail
[0] = 'Z';
570 msg
->ts_zonetail
[1] = '\0';
572 snprintfrr(msg
->ts_zonetail
, sizeof(msg
->ts_zonetail
),
574 (int)(tm
.tm_gmtoff
/ 3600),
575 (int)(labs(tm
.tm_gmtoff
) / 60) % 60);
577 msg
->ts_dot
= msg
->ts_str
+ strlen(msg
->ts_str
);
578 snprintfrr(msg
->ts_dot
,
579 msg
->ts_str
+ sizeof(msg
->ts_str
) - msg
->ts_dot
,
580 ".%09lu", (unsigned long)msg
->ts
.tv_nsec
);
582 msg
->ts_flags
= ZLOG_TS_ISO8601
| (flags
& ZLOG_TS_UTC
);
585 len1
= flags
& ZLOG_TS_PREC
;
586 len1
= (msg
->ts_dot
- msg
->ts_str
) + (len1
? len1
+ 1 : 0);
588 if (len1
> strlen(msg
->ts_str
))
589 len1
= strlen(msg
->ts_str
);
591 if (flags
& ZLOG_TS_LEGACY
) {
592 if (len1
+ 1 > outsz
)
595 /* just swap out the formatting, faster than redoing it */
596 for (char *p
= msg
->ts_str
; p
< msg
->ts_str
+ len1
; p
++) {
611 size_t len2
= strlen(msg
->ts_zonetail
);
613 if (len1
+ len2
+ 1 > outsz
)
615 memcpy(out
, msg
->ts_str
, len1
);
616 memcpy(out
+ len1
, msg
->ts_zonetail
, len2
);
617 out
[len1
+ len2
] = '\0';
622 /* setup functions */
624 struct zlog_target
*zlog_target_clone(struct memtype
*mt
,
625 struct zlog_target
*oldzt
, size_t size
)
627 struct zlog_target
*newzt
;
629 newzt
= XCALLOC(mt
, size
);
631 newzt
->prio_min
= oldzt
->prio_min
;
632 newzt
->logfn
= oldzt
->logfn
;
633 newzt
->logfn_sigsafe
= oldzt
->logfn_sigsafe
;
639 struct zlog_target
*zlog_target_replace(struct zlog_target
*oldzt
,
640 struct zlog_target
*newzt
)
643 zlog_targets_add_tail(&zlog_targets
, newzt
);
645 zlog_targets_del(&zlog_targets
, oldzt
);
652 #define TMPBASEDIR "/var/tmp/frr"
654 static char zlog_tmpdir
[MAXPATHLEN
];
656 void zlog_aux_init(const char *prefix
, int prio_min
)
659 strlcpy(zlog_prefix
, prefix
, sizeof(zlog_prefix
));
661 hook_call(zlog_aux_init
, prefix
, prio_min
);
664 void zlog_init(const char *progname
, const char *protoname
,
665 unsigned short instance
, uid_t uid
, gid_t gid
)
671 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
),
672 "/var/tmp/frr/%s-%d.%ld",
673 progname
, instance
, (long)getpid());
675 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
676 "%s[%d]: ", protoname
, instance
);
678 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
),
679 "/var/tmp/frr/%s.%ld",
680 progname
, (long)getpid());
682 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
686 if (mkdir(TMPBASEDIR
, 0700) != 0) {
687 if (errno
!= EEXIST
) {
688 zlog_err("failed to mkdir \"%s\": %s",
689 TMPBASEDIR
, strerror(errno
));
693 chown(TMPBASEDIR
, zlog_uid
, zlog_gid
);
695 if (mkdir(zlog_tmpdir
, 0700) != 0) {
696 zlog_err("failed to mkdir \"%s\": %s",
697 zlog_tmpdir
, strerror(errno
));
702 zlog_tmpdirfd
= open(zlog_tmpdir
,
703 O_PATH
| O_RDONLY
| O_CLOEXEC
);
705 zlog_tmpdirfd
= open(zlog_tmpdir
,
706 O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
);
708 if (zlog_tmpdirfd
< 0) {
709 zlog_err("failed to open \"%s\": %s",
710 zlog_tmpdir
, strerror(errno
));
715 fchownat(zlog_tmpdirfd
, "", zlog_uid
, zlog_gid
, AT_EMPTY_PATH
);
717 chown(zlog_tmpdir
, zlog_uid
, zlog_gid
);
720 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
724 zlog_err("crashlog and per-thread log buffering unavailable!");
725 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
730 hook_call(zlog_fini
);
732 if (zlog_tmpdirfd
>= 0) {
733 close(zlog_tmpdirfd
);
736 if (rmdir(zlog_tmpdir
))
737 zlog_err("failed to rmdir \"%s\": %s",
738 zlog_tmpdir
, strerror(errno
));