]> git.proxmox.com Git - mirror_frr.git/blob - lib/zlog.c
Merge pull request #8267 from idryzhov/bgp-cli-fixes
[mirror_frr.git] / lib / zlog.c
1 /*
2 * Copyright (c) 2015-19 David Lamparter, for NetDEF, Inc.
3 *
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.
7 *
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.
15 */
16
17 #include "zebra.h"
18
19 #include <unistd.h>
20 #include <sys/time.h>
21 #include <sys/mman.h>
22 #include <sys/types.h>
23 #include <time.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <pthread.h>
29
30 /* gettid() & co. */
31 #ifdef HAVE_PTHREAD_NP_H
32 #include <pthread_np.h>
33 #endif
34 #ifdef linux
35 #include <sys/syscall.h>
36 #endif
37 #ifdef __FreeBSD__
38 #include <sys/thr.h>
39 #endif
40 #ifdef __NetBSD__
41 #include <lwp.h>
42 #endif
43 #ifdef __DragonFly__
44 #include <sys/lwp.h>
45 #endif
46 #ifdef __APPLE__
47 #include <mach/mach_traps.h>
48 #endif
49
50 #include "memory.h"
51 #include "atomlist.h"
52 #include "printfrr.h"
53 #include "frrcu.h"
54 #include "zlog.h"
55 #include "libfrr_trace.h"
56
57 DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message");
58 DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer");
59
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),
65 (prefix, prio_min));
66
67 char zlog_prefix[128];
68 size_t zlog_prefixsz;
69 int zlog_tmpdirfd = -1;
70
71 static atomic_bool zlog_ec = true, zlog_xid = true;
72
73 /* these are kept around because logging is initialized (and directories
74 * & files created) before zprivs code switches to the FRR user; therefore
75 * we need to chown() things so we don't get permission errors later when
76 * trying to delete things on shutdown
77 */
78 static uid_t zlog_uid = -1;
79 static gid_t zlog_gid = -1;
80
81 DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head);
82 static struct zlog_targets_head zlog_targets;
83
84 /* cf. zlog.h for additional comments on this struct.
85 *
86 * Note: you MUST NOT pass the format string + va_list to non-FRR format
87 * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an
88 * extended prinf() with additional formats (%pI4 and the like).
89 *
90 * Also remember to use va_copy() on args.
91 */
92
93 struct zlog_msg {
94 struct timespec ts;
95 int prio;
96
97 const char *fmt;
98 va_list args;
99 const struct xref_logmsg *xref;
100
101 char *stackbuf;
102 size_t stackbufsz;
103 char *text;
104 size_t textlen;
105
106 /* This is always ISO8601 with sub-second precision 9 here, it's
107 * converted for callers as needed. ts_dot points to the "."
108 * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the
109 * local time offset.
110 *
111 * Valid if ZLOG_TS_ISO8601 is set.
112 * (0 if timestamp has not been formatted yet)
113 */
114 uint32_t ts_flags;
115 char ts_str[32], *ts_dot, ts_zonetail[8];
116 };
117
118 /* thread-local log message buffering
119 *
120 * This is strictly optional and set up by calling zlog_tls_buffer_init()
121 * on a particular thread.
122 *
123 * If in use, this will create a temporary file in /var/tmp which is used as
124 * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer
125 * access doesn't require any syscalls, but in case of a crash the kernel
126 * knows to sync the memory back to disk. This way the user can still get the
127 * last log messages if there were any left unwritten in the buffer.
128 *
129 * Sizing this dynamically isn't particularly useful, so here's an 8k buffer
130 * with a message limit of 64 messages. Message metadata (e.g. priority,
131 * timestamp) aren't in the mmap region, so they're lost on crash, but we can
132 * live with that.
133 */
134
135 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
136 #define CAN_DO_TLS 1
137 #endif
138
139 #define TLS_LOG_BUF_SIZE 8192
140 #define TLS_LOG_MAXMSG 64
141
142 struct zlog_tls {
143 char *mmbuf;
144 size_t bufpos;
145
146 size_t nmsgs;
147 struct zlog_msg msgs[TLS_LOG_MAXMSG];
148 struct zlog_msg *msgp[TLS_LOG_MAXMSG];
149 };
150
151 static inline void zlog_tls_free(void *arg);
152
153 /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's
154 * available we'll use it here
155 */
156
157 #ifdef __OpenBSD__
158 static pthread_key_t zlog_tls_key;
159
160 static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
161 static void zlog_tls_key_init(void)
162 {
163 pthread_key_create(&zlog_tls_key, zlog_tls_free);
164 }
165
166 static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
167 static void zlog_tls_key_fini(void)
168 {
169 pthread_key_delete(zlog_tls_key);
170 }
171
172 static inline struct zlog_tls *zlog_tls_get(void)
173 {
174 return pthread_getspecific(zlog_tls_key);
175 }
176
177 static inline void zlog_tls_set(struct zlog_tls *val)
178 {
179 pthread_setspecific(zlog_tls_key, val);
180 }
181 #else
182 # ifndef thread_local
183 # define thread_local __thread
184 # endif
185
186 static thread_local struct zlog_tls *zlog_tls_var
187 __attribute__((tls_model("initial-exec")));
188
189 static inline struct zlog_tls *zlog_tls_get(void)
190 {
191 return zlog_tls_var;
192 }
193
194 static inline void zlog_tls_set(struct zlog_tls *val)
195 {
196 zlog_tls_var = val;
197 }
198 #endif
199
200 #ifdef CAN_DO_TLS
201 static long zlog_gettid(void)
202 {
203 long rv = -1;
204 #ifdef HAVE_PTHREAD_GETTHREADID_NP
205 rv = pthread_getthreadid_np();
206 #elif defined(linux)
207 rv = syscall(__NR_gettid);
208 #elif defined(__NetBSD__)
209 rv = _lwp_self();
210 #elif defined(__FreeBSD__)
211 thr_self(&rv);
212 #elif defined(__DragonFly__)
213 rv = lwp_gettid();
214 #elif defined(__OpenBSD__)
215 rv = getthrid();
216 #elif defined(__sun)
217 rv = pthread_self();
218 #elif defined(__APPLE__)
219 rv = mach_thread_self();
220 mach_port_deallocate(mach_task_self(), rv);
221 #endif
222 return rv;
223 }
224
225 void zlog_tls_buffer_init(void)
226 {
227 struct zlog_tls *zlog_tls;
228 char mmpath[MAXPATHLEN];
229 int mmfd;
230 size_t i;
231
232 zlog_tls = zlog_tls_get();
233
234 if (zlog_tls || zlog_tmpdirfd < 0)
235 return;
236
237 zlog_tls = XCALLOC(MTYPE_LOG_TLSBUF, sizeof(*zlog_tls));
238 for (i = 0; i < array_size(zlog_tls->msgp); i++)
239 zlog_tls->msgp[i] = &zlog_tls->msgs[i];
240
241 snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
242
243 mmfd = openat(zlog_tmpdirfd, mmpath,
244 O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
245 if (mmfd < 0) {
246 zlog_err("failed to open thread log buffer \"%s\": %s",
247 mmpath, strerror(errno));
248 goto out_anon;
249 }
250 fchown(mmfd, zlog_uid, zlog_gid);
251
252 #ifdef HAVE_POSIX_FALLOCATE
253 if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0)
254 /* note next statement is under above if() */
255 #endif
256 if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) {
257 zlog_err("failed to allocate thread log buffer \"%s\": %s",
258 mmpath, strerror(errno));
259 goto out_anon_unlink;
260 }
261
262 zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
263 MAP_SHARED, mmfd, 0);
264 if (zlog_tls->mmbuf == MAP_FAILED) {
265 zlog_err("failed to mmap thread log buffer \"%s\": %s",
266 mmpath, strerror(errno));
267 goto out_anon_unlink;
268 }
269
270 close(mmfd);
271 zlog_tls_set(zlog_tls);
272 return;
273
274 out_anon_unlink:
275 unlink(mmpath);
276 close(mmfd);
277 out_anon:
278
279 #ifndef MAP_ANONYMOUS
280 #define MAP_ANONYMOUS MAP_ANON
281 #endif
282 zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE,
283 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
284
285 if (!zlog_tls->mmbuf) {
286 zlog_err("failed to anonymous-mmap thread log buffer: %s",
287 strerror(errno));
288 XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
289 zlog_tls_set(NULL);
290 return;
291 }
292
293 zlog_tls_set(zlog_tls);
294 }
295
296 void zlog_tls_buffer_fini(void)
297 {
298 char mmpath[MAXPATHLEN];
299
300 zlog_tls_buffer_flush();
301
302 zlog_tls_free(zlog_tls_get());
303 zlog_tls_set(NULL);
304
305 snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
306 if (unlinkat(zlog_tmpdirfd, mmpath, 0))
307 zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno);
308 }
309
310 #else /* !CAN_DO_TLS */
311 void zlog_tls_buffer_init(void)
312 {
313 }
314
315 void zlog_tls_buffer_fini(void)
316 {
317 }
318 #endif
319
320 static inline void zlog_tls_free(void *arg)
321 {
322 struct zlog_tls *zlog_tls = arg;
323
324 if (!zlog_tls)
325 return;
326
327 munmap(zlog_tls->mmbuf, TLS_LOG_BUF_SIZE);
328 XFREE(MTYPE_LOG_TLSBUF, zlog_tls);
329 }
330
331 void zlog_tls_buffer_flush(void)
332 {
333 struct zlog_target *zt;
334 struct zlog_tls *zlog_tls = zlog_tls_get();
335
336 if (!zlog_tls)
337 return;
338 if (!zlog_tls->nmsgs)
339 return;
340
341 rcu_read_lock();
342 frr_each (zlog_targets, &zlog_targets, zt) {
343 if (!zt->logfn)
344 continue;
345
346 zt->logfn(zt, zlog_tls->msgp, zlog_tls->nmsgs);
347 }
348 rcu_read_unlock();
349
350 zlog_tls->bufpos = 0;
351 zlog_tls->nmsgs = 0;
352 }
353
354
355 static void vzlog_notls(const struct xref_logmsg *xref, int prio,
356 const char *fmt, va_list ap)
357 {
358 struct zlog_target *zt;
359 struct zlog_msg stackmsg = {
360 .prio = prio & LOG_PRIMASK,
361 .fmt = fmt,
362 .xref = xref,
363 }, *msg = &stackmsg;
364 char stackbuf[512];
365
366 clock_gettime(CLOCK_REALTIME, &msg->ts);
367 va_copy(msg->args, ap);
368 msg->stackbuf = stackbuf;
369 msg->stackbufsz = sizeof(stackbuf);
370
371 rcu_read_lock();
372 frr_each (zlog_targets, &zlog_targets, zt) {
373 if (prio > zt->prio_min)
374 continue;
375 if (!zt->logfn)
376 continue;
377
378 zt->logfn(zt, &msg, 1);
379 }
380 rcu_read_unlock();
381
382 va_end(msg->args);
383 if (msg->text && msg->text != stackbuf)
384 XFREE(MTYPE_LOG_MESSAGE, msg->text);
385 }
386
387 static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref,
388 int prio, const char *fmt, va_list ap)
389 {
390 struct zlog_target *zt;
391 struct zlog_msg *msg;
392 char *buf;
393 bool ignoremsg = true;
394 bool immediate = false;
395
396 /* avoid further processing cost if no target wants this message */
397 rcu_read_lock();
398 frr_each (zlog_targets, &zlog_targets, zt) {
399 if (prio > zt->prio_min)
400 continue;
401 ignoremsg = false;
402 break;
403 }
404 rcu_read_unlock();
405
406 if (ignoremsg)
407 return;
408
409 msg = &zlog_tls->msgs[zlog_tls->nmsgs];
410 zlog_tls->nmsgs++;
411 if (zlog_tls->nmsgs == array_size(zlog_tls->msgs))
412 immediate = true;
413
414 memset(msg, 0, sizeof(*msg));
415 clock_gettime(CLOCK_REALTIME, &msg->ts);
416 va_copy(msg->args, ap);
417 msg->stackbuf = buf = zlog_tls->mmbuf + zlog_tls->bufpos;
418 msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1;
419 msg->fmt = fmt;
420 msg->prio = prio & LOG_PRIMASK;
421 msg->xref = xref;
422 if (msg->prio < LOG_INFO)
423 immediate = true;
424
425 if (!immediate) {
426 /* messages written later need to take the formatting cost
427 * immediately since we can't hold a reference on varargs
428 */
429 zlog_msg_text(msg, NULL);
430
431 if (msg->text != buf)
432 /* zlog_msg_text called malloc() on us :( */
433 immediate = true;
434 else {
435 zlog_tls->bufpos += msg->textlen + 1;
436 /* write a second \0 to mark current end position
437 * (in case of crash this signals end of unwritten log
438 * messages in mmap'd logbuf file)
439 */
440 zlog_tls->mmbuf[zlog_tls->bufpos] = '\0';
441
442 /* avoid malloc() for next message */
443 if (TLS_LOG_BUF_SIZE - zlog_tls->bufpos < 256)
444 immediate = true;
445 }
446 }
447
448 if (immediate)
449 zlog_tls_buffer_flush();
450
451 va_end(msg->args);
452 if (msg->text && msg->text != buf)
453 XFREE(MTYPE_LOG_MESSAGE, msg->text);
454 }
455
456 void vzlogx(const struct xref_logmsg *xref, int prio,
457 const char *fmt, va_list ap)
458 {
459 struct zlog_tls *zlog_tls = zlog_tls_get();
460
461 #ifdef HAVE_LTTNG
462 va_list copy;
463 va_copy(copy, ap);
464 char *msg = vasprintfrr(MTYPE_LOG_MESSAGE, fmt, copy);
465
466 switch (prio) {
467 case LOG_ERR:
468 frrtracelog(TRACE_ERR, msg);
469 break;
470 case LOG_WARNING:
471 frrtracelog(TRACE_WARNING, msg);
472 break;
473 case LOG_DEBUG:
474 frrtracelog(TRACE_DEBUG, msg);
475 break;
476 case LOG_NOTICE:
477 frrtracelog(TRACE_DEBUG, msg);
478 break;
479 case LOG_INFO:
480 default:
481 frrtracelog(TRACE_INFO, msg);
482 break;
483 }
484
485 va_end(copy);
486 XFREE(MTYPE_LOG_MESSAGE, msg);
487 #endif
488
489 if (zlog_tls)
490 vzlog_tls(zlog_tls, xref, prio, fmt, ap);
491 else
492 vzlog_notls(xref, prio, fmt, ap);
493 }
494
495 void zlog_sigsafe(const char *text, size_t len)
496 {
497 struct zlog_target *zt;
498 const char *end = text + len, *nlpos;
499
500 while (text < end) {
501 nlpos = memchr(text, '\n', end - text);
502 if (!nlpos)
503 nlpos = end;
504
505 frr_each (zlog_targets, &zlog_targets, zt) {
506 if (LOG_CRIT > zt->prio_min)
507 continue;
508 if (!zt->logfn_sigsafe)
509 continue;
510
511 zt->logfn_sigsafe(zt, text, nlpos - text);
512 }
513
514 if (nlpos == end)
515 break;
516 text = nlpos + 1;
517 }
518 }
519
520
521 int zlog_msg_prio(struct zlog_msg *msg)
522 {
523 return msg->prio;
524 }
525
526 const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg)
527 {
528 return msg->xref;
529 }
530
531 const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
532 {
533 if (!msg->text) {
534 va_list args;
535 bool do_xid, do_ec;
536 size_t need = 0, hdrlen;
537 struct fbuf fb = {
538 .buf = msg->stackbuf,
539 .pos = msg->stackbuf,
540 .len = msg->stackbufsz,
541 };
542
543 do_ec = atomic_load_explicit(&zlog_ec, memory_order_relaxed);
544 do_xid = atomic_load_explicit(&zlog_xid, memory_order_relaxed);
545
546 if (msg->xref && do_xid && msg->xref->xref.xrefdata->uid[0]) {
547 need += bputch(&fb, '[');
548 need += bputs(&fb, msg->xref->xref.xrefdata->uid);
549 need += bputch(&fb, ']');
550 }
551 if (msg->xref && do_ec && msg->xref->ec)
552 need += bprintfrr(&fb, "[EC %u]", msg->xref->ec);
553 if (need)
554 need += bputch(&fb, ' ');
555
556 hdrlen = need;
557 assert(hdrlen < msg->stackbufsz);
558
559 va_copy(args, msg->args);
560 need += vbprintfrr(&fb, msg->fmt, args);
561 va_end(args);
562
563 msg->textlen = need;
564 need += bputch(&fb, '\0');
565
566 if (need <= msg->stackbufsz)
567 msg->text = msg->stackbuf;
568 else {
569 msg->text = XMALLOC(MTYPE_LOG_MESSAGE, need);
570
571 memcpy(msg->text, msg->stackbuf, hdrlen);
572
573 fb.buf = msg->text;
574 fb.len = need;
575 fb.pos = msg->text + hdrlen;
576
577 va_copy(args, msg->args);
578 vbprintfrr(&fb, msg->fmt, args);
579 va_end(args);
580
581 bputch(&fb, '\0');
582 }
583 }
584 if (textlen)
585 *textlen = msg->textlen;
586 return msg->text;
587 }
588
589 #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
590 #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
591
592 size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
593 uint32_t flags)
594 {
595 size_t len1;
596
597 if (!(flags & ZLOG_TS_FORMAT))
598 return 0;
599
600 if (!(msg->ts_flags & ZLOG_TS_FORMAT) ||
601 ((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) {
602 struct tm tm;
603
604 if (flags & ZLOG_TS_UTC)
605 gmtime_r(&msg->ts.tv_sec, &tm);
606 else
607 localtime_r(&msg->ts.tv_sec, &tm);
608
609 strftime(msg->ts_str, sizeof(msg->ts_str),
610 "%Y-%m-%dT%H:%M:%S", &tm);
611
612 if (flags & ZLOG_TS_UTC) {
613 msg->ts_zonetail[0] = 'Z';
614 msg->ts_zonetail[1] = '\0';
615 } else
616 snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail),
617 "%+03d:%02d",
618 (int)(tm.tm_gmtoff / 3600),
619 (int)(labs(tm.tm_gmtoff) / 60) % 60);
620
621 msg->ts_dot = msg->ts_str + strlen(msg->ts_str);
622 snprintfrr(msg->ts_dot,
623 msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot,
624 ".%09lu", (unsigned long)msg->ts.tv_nsec);
625
626 msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC);
627 }
628
629 len1 = flags & ZLOG_TS_PREC;
630 len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0);
631
632 if (len1 > strlen(msg->ts_str))
633 len1 = strlen(msg->ts_str);
634
635 if (flags & ZLOG_TS_LEGACY) {
636 if (len1 + 1 > outsz)
637 return 0;
638
639 /* just swap out the formatting, faster than redoing it */
640 for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) {
641 switch (*p) {
642 case '-':
643 *out++ = '/';
644 break;
645 case 'T':
646 *out++ = ' ';
647 break;
648 default:
649 *out++ = *p;
650 }
651 }
652 *out = '\0';
653 return len1;
654 } else {
655 size_t len2 = strlen(msg->ts_zonetail);
656
657 if (len1 + len2 + 1 > outsz)
658 return 0;
659 memcpy(out, msg->ts_str, len1);
660 memcpy(out + len1, msg->ts_zonetail, len2);
661 out[len1 + len2] = '\0';
662 return len1 + len2;
663 }
664 }
665
666 void zlog_set_prefix_ec(bool enable)
667 {
668 atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed);
669 }
670
671 bool zlog_get_prefix_ec(void)
672 {
673 return atomic_load_explicit(&zlog_ec, memory_order_relaxed);
674 }
675
676 void zlog_set_prefix_xid(bool enable)
677 {
678 atomic_store_explicit(&zlog_xid, enable, memory_order_relaxed);
679 }
680
681 bool zlog_get_prefix_xid(void)
682 {
683 return atomic_load_explicit(&zlog_xid, memory_order_relaxed);
684 }
685
686 /* setup functions */
687
688 struct zlog_target *zlog_target_clone(struct memtype *mt,
689 struct zlog_target *oldzt, size_t size)
690 {
691 struct zlog_target *newzt;
692
693 newzt = XCALLOC(mt, size);
694 if (oldzt) {
695 newzt->prio_min = oldzt->prio_min;
696 newzt->logfn = oldzt->logfn;
697 newzt->logfn_sigsafe = oldzt->logfn_sigsafe;
698 }
699
700 return newzt;
701 }
702
703 struct zlog_target *zlog_target_replace(struct zlog_target *oldzt,
704 struct zlog_target *newzt)
705 {
706 if (newzt)
707 zlog_targets_add_tail(&zlog_targets, newzt);
708 if (oldzt)
709 zlog_targets_del(&zlog_targets, oldzt);
710 return oldzt;
711 }
712
713
714 /* common init */
715
716 #define TMPBASEDIR "/var/tmp/frr"
717
718 static char zlog_tmpdir[MAXPATHLEN];
719
720 void zlog_aux_init(const char *prefix, int prio_min)
721 {
722 if (prefix)
723 strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix));
724
725 hook_call(zlog_aux_init, prefix, prio_min);
726 }
727
728 void zlog_init(const char *progname, const char *protoname,
729 unsigned short instance, uid_t uid, gid_t gid)
730 {
731 zlog_uid = uid;
732 zlog_gid = gid;
733
734 if (instance) {
735 snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
736 "/var/tmp/frr/%s-%d.%ld",
737 progname, instance, (long)getpid());
738
739 zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
740 "%s[%d]: ", protoname, instance);
741 } else {
742 snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
743 "/var/tmp/frr/%s.%ld",
744 progname, (long)getpid());
745
746 zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix),
747 "%s: ", protoname);
748 }
749
750 if (mkdir(TMPBASEDIR, 0700) != 0) {
751 if (errno != EEXIST) {
752 zlog_err("failed to mkdir \"%s\": %s",
753 TMPBASEDIR, strerror(errno));
754 goto out_warn;
755 }
756 }
757 chown(TMPBASEDIR, zlog_uid, zlog_gid);
758
759 if (mkdir(zlog_tmpdir, 0700) != 0) {
760 zlog_err("failed to mkdir \"%s\": %s",
761 zlog_tmpdir, strerror(errno));
762 goto out_warn;
763 }
764
765 #ifdef O_PATH
766 zlog_tmpdirfd = open(zlog_tmpdir,
767 O_PATH | O_RDONLY | O_CLOEXEC);
768 #else
769 zlog_tmpdirfd = open(zlog_tmpdir,
770 O_DIRECTORY | O_RDONLY | O_CLOEXEC);
771 #endif
772 if (zlog_tmpdirfd < 0) {
773 zlog_err("failed to open \"%s\": %s",
774 zlog_tmpdir, strerror(errno));
775 goto out_warn;
776 }
777
778 #ifdef AT_EMPTY_PATH
779 fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH);
780 #else
781 chown(zlog_tmpdir, zlog_uid, zlog_gid);
782 #endif
783
784 hook_call(zlog_init, progname, protoname, instance, uid, gid);
785 return;
786
787 out_warn:
788 zlog_err("crashlog and per-thread log buffering unavailable!");
789 hook_call(zlog_init, progname, protoname, instance, uid, gid);
790 }
791
792 void zlog_fini(void)
793 {
794 hook_call(zlog_fini);
795
796 if (zlog_tmpdirfd >= 0) {
797 close(zlog_tmpdirfd);
798 zlog_tmpdirfd = -1;
799
800 if (rmdir(zlog_tmpdir))
801 zlog_err("failed to rmdir \"%s\": %s",
802 zlog_tmpdir, strerror(errno));
803 }
804 }