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