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;
70 int zlog_instance
= -1;
72 static atomic_bool zlog_ec
= true, zlog_xid
= true;
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
79 static uid_t zlog_uid
= -1;
80 static gid_t zlog_gid
= -1;
82 DECLARE_ATOMLIST(zlog_targets
, struct zlog_target
, head
);
83 static struct zlog_targets_head zlog_targets
;
85 /* Global setting for buffered vs immediate output. The default is
86 * per-pthread buffering.
88 static bool default_immediate
;
90 /* cf. zlog.h for additional comments on this struct.
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).
96 * Also remember to use va_copy() on args.
105 const struct xref_logmsg
*xref
;
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
118 * Valid if ZLOG_TS_ISO8601 is set.
119 * (0 if timestamp has not been formatted yet)
121 char ts_str
[32], *ts_dot
, ts_zonetail
[8];
124 /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from
125 * the above, so just kept separately here.
127 uint32_t ts_3164_flags
;
128 char ts_3164_str
[16];
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.
136 struct fmt_outpos argpos
[24];
140 /* thread-local log message buffering
142 * This is strictly optional and set up by calling zlog_tls_buffer_init()
143 * on a particular thread.
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.
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
157 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
161 #define TLS_LOG_BUF_SIZE 8192
162 #define TLS_LOG_MAXMSG 64
170 struct zlog_msg msgs
[TLS_LOG_MAXMSG
];
171 struct zlog_msg
*msgp
[TLS_LOG_MAXMSG
];
174 static inline void zlog_tls_free(void *arg
);
176 /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
177 * available we'll use it here
181 static pthread_key_t zlog_tls_key
;
183 static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
184 static void zlog_tls_key_init(void)
186 pthread_key_create(&zlog_tls_key
, zlog_tls_free
);
189 static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
190 static void zlog_tls_key_fini(void)
192 pthread_key_delete(zlog_tls_key
);
195 static inline struct zlog_tls
*zlog_tls_get(void)
197 return pthread_getspecific(zlog_tls_key
);
200 static inline void zlog_tls_set(struct zlog_tls
*val
)
202 pthread_setspecific(zlog_tls_key
, val
);
205 # ifndef thread_local
206 # define thread_local __thread
209 static thread_local
struct zlog_tls
*zlog_tls_var
210 __attribute__((tls_model("initial-exec")));
212 static inline struct zlog_tls
*zlog_tls_get(void)
217 static inline void zlog_tls_set(struct zlog_tls
*val
)
224 static intmax_t zlog_gettid(void)
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)
234 #ifdef HAVE_PTHREAD_GETTHREADID_NP
235 rv
= pthread_getthreadid_np();
237 rv
= syscall(__NR_gettid
);
238 #elif defined(__NetBSD__)
240 #elif defined(__FreeBSD__)
242 #elif defined(__DragonFly__)
244 #elif defined(__OpenBSD__)
248 #elif defined(__APPLE__)
249 rv
= mach_thread_self();
250 mach_port_deallocate(mach_task_self(), rv
);
259 void zlog_tls_buffer_init(void)
261 struct zlog_tls
*zlog_tls
;
262 char mmpath
[MAXPATHLEN
];
266 zlog_tls
= zlog_tls_get();
268 if (zlog_tls
|| zlog_tmpdirfd
< 0)
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
];
275 snprintfrr(mmpath
, sizeof(mmpath
), "logbuf.%jd", zlog_gettid());
277 mmfd
= openat(zlog_tmpdirfd
, mmpath
,
278 O_RDWR
| O_CREAT
| O_EXCL
| O_CLOEXEC
, 0600);
280 zlog_err("failed to open thread log buffer \"%s\": %s",
281 mmpath
, strerror(errno
));
284 fchown(mmfd
, zlog_uid
, zlog_gid
);
286 #ifdef HAVE_POSIX_FALLOCATE
287 if (posix_fallocate(mmfd
, 0, TLS_LOG_BUF_SIZE
) != 0)
288 /* note next statement is under above if() */
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
;
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
;
303 zlog_tls
->do_unlink
= true;
306 zlog_tls_set(zlog_tls
);
310 unlinkat(zlog_tmpdirfd
, mmpath
, 0);
314 #ifndef MAP_ANONYMOUS
315 #define MAP_ANONYMOUS MAP_ANON
317 zlog_tls
->mmbuf
= mmap(NULL
, TLS_LOG_BUF_SIZE
, PROT_READ
| PROT_WRITE
,
318 MAP_ANONYMOUS
| MAP_PRIVATE
, -1, 0);
320 if (!zlog_tls
->mmbuf
) {
321 zlog_err("failed to anonymous-mmap thread log buffer: %s",
323 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
328 zlog_tls_set(zlog_tls
);
331 void zlog_tls_buffer_fini(void)
333 char mmpath
[MAXPATHLEN
];
334 struct zlog_tls
*zlog_tls
= zlog_tls_get();
335 bool do_unlink
= zlog_tls
? zlog_tls
->do_unlink
: false;
337 zlog_tls_buffer_flush();
339 zlog_tls_free(zlog_tls
);
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
);
347 #else /* !CAN_DO_TLS */
348 void zlog_tls_buffer_init(void)
352 void zlog_tls_buffer_fini(void)
357 void zlog_msg_pid(struct zlog_msg
*msg
, intmax_t *pid
, intmax_t *tid
)
360 static thread_local
intmax_t cached_pid
= -1;
361 if (cached_pid
!= -1)
364 cached_pid
= *pid
= (intmax_t)getpid();
366 *pid
= (intmax_t)getpid();
369 *tid
= zlog_gettid();
375 static inline void zlog_tls_free(void *arg
)
377 struct zlog_tls
*zlog_tls
= arg
;
382 munmap(zlog_tls
->mmbuf
, TLS_LOG_BUF_SIZE
);
383 XFREE(MTYPE_LOG_TLSBUF
, zlog_tls
);
386 void zlog_tls_buffer_flush(void)
388 struct zlog_target
*zt
;
389 struct zlog_tls
*zlog_tls
= zlog_tls_get();
393 if (!zlog_tls
->nmsgs
)
397 frr_each (zlog_targets
, &zlog_targets
, zt
) {
401 zt
->logfn(zt
, zlog_tls
->msgp
, zlog_tls
->nmsgs
);
405 zlog_tls
->bufpos
= 0;
410 static void vzlog_notls(const struct xref_logmsg
*xref
, int prio
,
411 const char *fmt
, va_list ap
)
413 struct zlog_target
*zt
;
414 struct zlog_msg stackmsg
= {
415 .prio
= prio
& LOG_PRIMASK
,
421 clock_gettime(CLOCK_REALTIME
, &msg
->ts
);
422 va_copy(msg
->args
, ap
);
423 msg
->stackbuf
= stackbuf
;
424 msg
->stackbufsz
= sizeof(stackbuf
);
427 frr_each (zlog_targets
, &zlog_targets
, zt
) {
428 if (prio
> zt
->prio_min
)
433 zt
->logfn(zt
, &msg
, 1);
438 if (msg
->text
&& msg
->text
!= stackbuf
)
439 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
442 static void vzlog_tls(struct zlog_tls
*zlog_tls
, const struct xref_logmsg
*xref
,
443 int prio
, const char *fmt
, va_list ap
)
445 struct zlog_target
*zt
;
446 struct zlog_msg
*msg
;
448 bool ignoremsg
= true;
449 bool immediate
= default_immediate
;
451 /* avoid further processing cost if no target wants this message */
453 frr_each (zlog_targets
, &zlog_targets
, zt
) {
454 if (prio
> zt
->prio_min
)
464 msg
= &zlog_tls
->msgs
[zlog_tls
->nmsgs
];
466 if (zlog_tls
->nmsgs
== array_size(zlog_tls
->msgs
))
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;
475 msg
->prio
= prio
& LOG_PRIMASK
;
477 if (msg
->prio
< LOG_INFO
)
481 /* messages written later need to take the formatting cost
482 * immediately since we can't hold a reference on varargs
484 zlog_msg_text(msg
, NULL
);
486 if (msg
->text
!= buf
)
487 /* zlog_msg_text called malloc() on us :( */
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)
495 zlog_tls
->mmbuf
[zlog_tls
->bufpos
] = '\0';
497 /* avoid malloc() for next message */
498 if (TLS_LOG_BUF_SIZE
- zlog_tls
->bufpos
< 256)
504 zlog_tls_buffer_flush();
507 if (msg
->text
&& msg
->text
!= buf
)
508 XFREE(MTYPE_LOG_MESSAGE
, msg
->text
);
511 void vzlogx(const struct xref_logmsg
*xref
, int prio
,
512 const char *fmt
, va_list ap
)
514 struct zlog_tls
*zlog_tls
= zlog_tls_get();
519 char *msg
= vasprintfrr(MTYPE_LOG_MESSAGE
, fmt
, copy
);
523 frrtracelog(TRACE_ERR
, msg
);
526 frrtracelog(TRACE_WARNING
, msg
);
529 frrtracelog(TRACE_DEBUG
, msg
);
532 frrtracelog(TRACE_DEBUG
, msg
);
536 frrtracelog(TRACE_INFO
, msg
);
541 XFREE(MTYPE_LOG_MESSAGE
, msg
);
545 vzlog_tls(zlog_tls
, xref
, prio
, fmt
, ap
);
547 vzlog_notls(xref
, prio
, fmt
, ap
);
550 void zlog_sigsafe(const char *text
, size_t len
)
552 struct zlog_target
*zt
;
553 const char *end
= text
+ len
, *nlpos
;
556 nlpos
= memchr(text
, '\n', end
- text
);
560 frr_each (zlog_targets
, &zlog_targets
, zt
) {
561 if (LOG_CRIT
> zt
->prio_min
)
563 if (!zt
->logfn_sigsafe
)
566 zt
->logfn_sigsafe(zt
, text
, nlpos
- text
);
575 void _zlog_assert_failed(const struct xref_assert
*xref
, const char *extra
, ...)
578 static bool assert_in_assert
; /* "global-ish" variable, init to 0 */
580 if (assert_in_assert
)
582 assert_in_assert
= true;
585 struct va_format vaf
;
592 "%s:%d: %s(): assertion (%s) failed, extra info: %pVA",
593 xref
->xref
.file
, xref
->xref
.line
, xref
->xref
.func
,
598 zlog(LOG_CRIT
, "%s:%d: %s(): assertion (%s) failed",
599 xref
->xref
.file
, xref
->xref
.line
, xref
->xref
.func
,
602 /* abort() prints backtrace & memstats in SIGABRT handler */
606 int zlog_msg_prio(struct zlog_msg
*msg
)
611 const struct xref_logmsg
*zlog_msg_xref(struct zlog_msg
*msg
)
616 const char *zlog_msg_text(struct zlog_msg
*msg
, size_t *textlen
)
621 size_t need
= 0, hdrlen
;
623 .buf
= msg
->stackbuf
,
624 .pos
= msg
->stackbuf
,
625 .len
= msg
->stackbufsz
,
628 do_ec
= atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
629 do_xid
= atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
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
, ']');
636 if (msg
->xref
&& do_ec
&& msg
->xref
->ec
)
637 need
+= bprintfrr(&fb
, "[EC %u]", msg
->xref
->ec
);
639 need
+= bputch(&fb
, ' ');
641 msg
->hdrlen
= hdrlen
= need
;
642 assert(hdrlen
< msg
->stackbufsz
);
644 fb
.outpos
= msg
->argpos
;
645 fb
.outpos_n
= array_size(msg
->argpos
);
648 va_copy(args
, msg
->args
);
649 need
+= vbprintfrr(&fb
, msg
->fmt
, args
);
653 need
+= bputch(&fb
, '\n');
655 if (need
<= msg
->stackbufsz
)
656 msg
->text
= msg
->stackbuf
;
658 msg
->text
= XMALLOC(MTYPE_LOG_MESSAGE
, need
);
660 memcpy(msg
->text
, msg
->stackbuf
, hdrlen
);
664 fb
.pos
= msg
->text
+ hdrlen
;
667 va_copy(args
, msg
->args
);
668 vbprintfrr(&fb
, msg
->fmt
, args
);
674 msg
->n_argpos
= fb
.outpos_i
;
677 *textlen
= msg
->textlen
;
681 void zlog_msg_args(struct zlog_msg
*msg
, size_t *hdrlen
, size_t *n_argpos
,
682 const struct fmt_outpos
**argpos
)
685 zlog_msg_text(msg
, NULL
);
688 *hdrlen
= msg
->hdrlen
;
690 *n_argpos
= msg
->n_argpos
;
692 *argpos
= msg
->argpos
;
695 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
696 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
698 size_t zlog_msg_ts(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
700 size_t outsz
= out
? (out
->buf
+ out
->len
- out
->pos
) : 0;
703 if (!(flags
& ZLOG_TS_FORMAT
))
706 if (!(msg
->ts_flags
& ZLOG_TS_FORMAT
) ||
707 ((msg
->ts_flags
^ flags
) & ZLOG_TS_UTC
)) {
710 if (flags
& ZLOG_TS_UTC
)
711 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
713 localtime_r(&msg
->ts
.tv_sec
, &tm
);
715 strftime(msg
->ts_str
, sizeof(msg
->ts_str
),
716 "%Y-%m-%dT%H:%M:%S", &tm
);
718 if (flags
& ZLOG_TS_UTC
) {
719 msg
->ts_zonetail
[0] = 'Z';
720 msg
->ts_zonetail
[1] = '\0';
722 snprintfrr(msg
->ts_zonetail
, sizeof(msg
->ts_zonetail
),
724 (int)(tm
.tm_gmtoff
/ 3600),
725 (int)(labs(tm
.tm_gmtoff
) / 60) % 60);
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
);
732 msg
->ts_flags
= ZLOG_TS_ISO8601
| (flags
& ZLOG_TS_UTC
);
735 len1
= flags
& ZLOG_TS_PREC
;
736 len1
= (msg
->ts_dot
- msg
->ts_str
) + (len1
? len1
+ 1 : 0);
738 if (len1
> strlen(msg
->ts_str
))
739 len1
= strlen(msg
->ts_str
);
741 if (flags
& ZLOG_TS_LEGACY
) {
746 memset(out
->pos
, 0, outsz
);
751 /* just swap out the formatting, faster than redoing it */
752 for (char *p
= msg
->ts_str
; p
< msg
->ts_str
+ len1
; p
++) {
766 size_t len2
= strlen(msg
->ts_zonetail
);
771 if (len1
+ len2
> outsz
) {
772 memset(out
->pos
, 0, outsz
);
777 memcpy(out
->pos
, msg
->ts_str
, len1
);
779 memcpy(out
->pos
, msg
->ts_zonetail
, len2
);
785 size_t zlog_msg_ts_3164(struct zlog_msg
*msg
, struct fbuf
*out
, uint32_t flags
)
787 flags
&= ZLOG_TS_UTC
;
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",
797 /* RFC3164 explicitly asks for local time, but common usage
800 if (flags
& ZLOG_TS_UTC
)
801 gmtime_r(&msg
->ts
.tv_sec
, &tm
);
803 localtime_r(&msg
->ts
.tv_sec
, &tm
);
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
);
809 msg
->ts_3164_flags
= flags
;
811 return bputs(out
, msg
->ts_3164_str
);
814 void zlog_set_prefix_ec(bool enable
)
816 atomic_store_explicit(&zlog_ec
, enable
, memory_order_relaxed
);
819 bool zlog_get_prefix_ec(void)
821 return atomic_load_explicit(&zlog_ec
, memory_order_relaxed
);
824 void zlog_set_prefix_xid(bool enable
)
826 atomic_store_explicit(&zlog_xid
, enable
, memory_order_relaxed
);
829 bool zlog_get_prefix_xid(void)
831 return atomic_load_explicit(&zlog_xid
, memory_order_relaxed
);
834 /* setup functions */
836 struct zlog_target
*zlog_target_clone(struct memtype
*mt
,
837 struct zlog_target
*oldzt
, size_t size
)
839 struct zlog_target
*newzt
;
841 newzt
= XCALLOC(mt
, size
);
843 newzt
->prio_min
= oldzt
->prio_min
;
844 newzt
->logfn
= oldzt
->logfn
;
845 newzt
->logfn_sigsafe
= oldzt
->logfn_sigsafe
;
851 struct zlog_target
*zlog_target_replace(struct zlog_target
*oldzt
,
852 struct zlog_target
*newzt
)
855 zlog_targets_add_tail(&zlog_targets
, newzt
);
857 zlog_targets_del(&zlog_targets
, oldzt
);
862 * Enable or disable 'immediate' output - default is to buffer
863 * each pthread's messages.
865 void zlog_set_immediate(bool set_p
)
867 default_immediate
= set_p
;
872 #define TMPBASEDIR "/var/tmp/frr"
874 static char zlog_tmpdir
[MAXPATHLEN
];
876 void zlog_aux_init(const char *prefix
, int prio_min
)
879 strlcpy(zlog_prefix
, prefix
, sizeof(zlog_prefix
));
881 hook_call(zlog_aux_init
, prefix
, prio_min
);
884 void zlog_init(const char *progname
, const char *protoname
,
885 unsigned short instance
, uid_t uid
, gid_t gid
)
889 zlog_instance
= instance
;
892 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
),
893 "/var/tmp/frr/%s-%d.%ld",
894 progname
, instance
, (long)getpid());
896 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
897 "%s[%d]: ", protoname
, instance
);
899 snprintfrr(zlog_tmpdir
, sizeof(zlog_tmpdir
),
900 "/var/tmp/frr/%s.%ld",
901 progname
, (long)getpid());
903 zlog_prefixsz
= snprintfrr(zlog_prefix
, sizeof(zlog_prefix
),
907 if (mkdir(TMPBASEDIR
, 0700) != 0) {
908 if (errno
!= EEXIST
) {
909 zlog_err("failed to mkdir \"%s\": %s",
910 TMPBASEDIR
, strerror(errno
));
914 chown(TMPBASEDIR
, zlog_uid
, zlog_gid
);
916 if (mkdir(zlog_tmpdir
, 0700) != 0) {
917 zlog_err("failed to mkdir \"%s\": %s",
918 zlog_tmpdir
, strerror(errno
));
923 zlog_tmpdirfd
= open(zlog_tmpdir
,
924 O_PATH
| O_RDONLY
| O_CLOEXEC
);
926 zlog_tmpdirfd
= open(zlog_tmpdir
,
927 O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
);
929 if (zlog_tmpdirfd
< 0) {
930 zlog_err("failed to open \"%s\": %s",
931 zlog_tmpdir
, strerror(errno
));
936 fchownat(zlog_tmpdirfd
, "", zlog_uid
, zlog_gid
, AT_EMPTY_PATH
);
938 chown(zlog_tmpdir
, zlog_uid
, zlog_gid
);
941 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
945 zlog_err("crashlog and per-thread log buffering unavailable!");
946 hook_call(zlog_init
, progname
, protoname
, instance
, uid
, gid
);
951 hook_call(zlog_fini
);
953 if (zlog_tmpdirfd
>= 0) {
954 close(zlog_tmpdirfd
);
957 if (rmdir(zlog_tmpdir
))
958 zlog_err("failed to rmdir \"%s\": %s",
959 zlog_tmpdir
, strerror(errno
));