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