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