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