]>
Commit | Line | Data |
---|---|---|
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" | |
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))); | |
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))); | |
163 | static void zlog_tls_key_fini(void) | |
164 | { | |
165 | pthread_key_delete(zlog_tls_key); | |
166 | } | |
167 | ||
168 | static inline struct zlog_tls *zlog_tls_get(void) | |
169 | { | |
170 | return pthread_getspecific(zlog_tls_key); | |
171 | } | |
172 | ||
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 | ||
185 | static inline struct zlog_tls *zlog_tls_get(void) | |
186 | { | |
187 | return zlog_tls_var; | |
188 | } | |
189 | ||
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 | |
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 | ||
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 | fchown(mmfd, zlog_uid, zlog_gid); | |
242 | ||
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 | ||
249 | #ifdef HAVE_POSIX_FALLOCATE | |
250 | if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) < 0) { | |
251 | #else | |
252 | if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) { | |
253 | #endif | |
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 | ||
271 | out_anon_unlink: | |
272 | unlink(mmpath); | |
273 | close(mmfd); | |
274 | out_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 | ||
293 | void 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 */ | |
308 | void zlog_tls_buffer_init(void) | |
309 | { | |
310 | } | |
311 | ||
312 | void zlog_tls_buffer_fini(void) | |
313 | { | |
314 | } | |
315 | #endif | |
316 | ||
317 | static 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 | ||
328 | void 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 | ||
352 | static 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 | ||
382 | static 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 | ||
450 | void vzlog(int prio, const char *fmt, va_list ap) | |
451 | { | |
452 | struct zlog_tls *zlog_tls = zlog_tls_get(); | |
453 | ||
454 | if (zlog_tls) | |
455 | vzlog_tls(zlog_tls, prio, fmt, ap); | |
456 | else | |
457 | vzlog_notls(prio, fmt, ap); | |
458 | } | |
459 | ||
460 | void zlog_sigsafe(const char *text, size_t len) | |
461 | { | |
462 | struct zlog_target *zt; | |
463 | const char *end = text + len, *nlpos; | |
464 | ||
465 | while (text < end) { | |
466 | nlpos = memchr(text, '\n', end - text); | |
467 | if (!nlpos) | |
468 | nlpos = end; | |
469 | ||
470 | frr_each (zlog_targets, &zlog_targets, zt) { | |
471 | if (LOG_CRIT > zt->prio_min) | |
472 | continue; | |
473 | if (!zt->logfn_sigsafe) | |
474 | continue; | |
475 | ||
476 | zt->logfn_sigsafe(zt, text, nlpos - text); | |
477 | } | |
478 | ||
479 | if (nlpos == end) | |
480 | break; | |
481 | text = nlpos + 1; | |
482 | } | |
483 | } | |
484 | ||
485 | ||
486 | int zlog_msg_prio(struct zlog_msg *msg) | |
487 | { | |
488 | return msg->prio; | |
489 | } | |
490 | ||
491 | const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) | |
492 | { | |
493 | if (!msg->text) { | |
494 | va_list args; | |
495 | ||
496 | va_copy(args, msg->args); | |
497 | msg->text = vasnprintfrr(MTYPE_LOG_MESSAGE, msg->stackbuf, | |
498 | msg->stackbufsz, msg->fmt, args); | |
499 | msg->textlen = strlen(msg->text); | |
500 | va_end(args); | |
501 | } | |
502 | if (textlen) | |
503 | *textlen = msg->textlen; | |
504 | return msg->text; | |
505 | } | |
506 | ||
507 | #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY) | |
508 | #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC | |
509 | ||
510 | size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz, | |
511 | uint32_t flags) | |
512 | { | |
513 | size_t len1; | |
514 | ||
515 | if (!(flags & ZLOG_TS_FORMAT)) | |
516 | return 0; | |
517 | ||
518 | if (!(msg->ts_flags & ZLOG_TS_FORMAT) || | |
519 | ((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) { | |
520 | struct tm tm; | |
521 | ||
522 | if (flags & ZLOG_TS_UTC) | |
523 | gmtime_r(&msg->ts.tv_sec, &tm); | |
524 | else | |
525 | localtime_r(&msg->ts.tv_sec, &tm); | |
526 | ||
527 | strftime(msg->ts_str, sizeof(msg->ts_str), | |
528 | "%Y-%m-%dT%H:%M:%S", &tm); | |
529 | ||
530 | if (flags & ZLOG_TS_UTC) { | |
531 | msg->ts_zonetail[0] = 'Z'; | |
532 | msg->ts_zonetail[1] = '\0'; | |
533 | } else | |
534 | snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail), | |
535 | "%+03d:%02d", | |
536 | (int)(tm.tm_gmtoff / 3600), | |
537 | (int)(labs(tm.tm_gmtoff) / 60) % 60); | |
538 | ||
539 | msg->ts_dot = msg->ts_str + strlen(msg->ts_str); | |
540 | snprintfrr(msg->ts_dot, | |
541 | msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot, | |
542 | ".%09lu", (unsigned long)msg->ts.tv_nsec); | |
543 | ||
544 | msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC); | |
545 | } | |
546 | ||
547 | len1 = flags & ZLOG_TS_PREC; | |
548 | len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0); | |
549 | ||
550 | if (len1 > strlen(msg->ts_str)) | |
551 | len1 = strlen(msg->ts_str); | |
552 | ||
553 | if (flags & ZLOG_TS_LEGACY) { | |
554 | if (len1 + 1 > outsz) | |
555 | return 0; | |
556 | ||
557 | /* just swap out the formatting, faster than redoing it */ | |
558 | for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) { | |
559 | switch (*p) { | |
560 | case '-': | |
561 | *out++ = '/'; | |
562 | break; | |
563 | case 'T': | |
564 | *out++ = ' '; | |
565 | break; | |
566 | default: | |
567 | *out++ = *p; | |
568 | } | |
569 | } | |
570 | *out = '\0'; | |
571 | return len1; | |
572 | } else { | |
573 | size_t len2 = strlen(msg->ts_zonetail); | |
574 | ||
575 | if (len1 + len2 + 1 > outsz) | |
576 | return 0; | |
577 | memcpy(out, msg->ts_str, len1); | |
578 | memcpy(out + len1, msg->ts_zonetail, len2); | |
579 | out[len1 + len2] = '\0'; | |
580 | return len1 + len2; | |
581 | } | |
582 | } | |
583 | ||
584 | /* setup functions */ | |
585 | ||
586 | struct zlog_target *zlog_target_clone(struct memtype *mt, | |
587 | struct zlog_target *oldzt, size_t size) | |
588 | { | |
589 | struct zlog_target *newzt; | |
590 | ||
591 | newzt = XCALLOC(mt, size); | |
592 | if (oldzt) { | |
593 | newzt->prio_min = oldzt->prio_min; | |
594 | newzt->logfn = oldzt->logfn; | |
595 | newzt->logfn_sigsafe = oldzt->logfn_sigsafe; | |
596 | } | |
597 | ||
598 | return newzt; | |
599 | } | |
600 | ||
601 | struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, | |
602 | struct zlog_target *newzt) | |
603 | { | |
604 | if (newzt) | |
605 | zlog_targets_add_tail(&zlog_targets, newzt); | |
606 | if (oldzt) | |
607 | zlog_targets_del(&zlog_targets, oldzt); | |
608 | return oldzt; | |
609 | } | |
610 | ||
611 | ||
612 | /* common init */ | |
613 | ||
614 | #define TMPBASEDIR "/var/tmp/frr" | |
615 | ||
616 | static char zlog_tmpdir[MAXPATHLEN]; | |
617 | ||
618 | void zlog_aux_init(const char *prefix, int prio_min) | |
619 | { | |
620 | if (prefix) | |
621 | strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix)); | |
622 | ||
623 | hook_call(zlog_aux_init, prefix, prio_min); | |
624 | } | |
625 | ||
626 | void zlog_init(const char *progname, const char *protoname, | |
627 | unsigned short instance, uid_t uid, gid_t gid) | |
628 | { | |
629 | zlog_uid = uid; | |
630 | zlog_gid = gid; | |
631 | ||
632 | if (instance) { | |
633 | snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), | |
634 | "/var/tmp/frr/%s-%d.%ld", | |
635 | progname, instance, (long)getpid()); | |
636 | ||
637 | zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix), | |
638 | "%s[%d]: ", protoname, instance); | |
639 | } else { | |
640 | snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), | |
641 | "/var/tmp/frr/%s.%ld", | |
642 | progname, (long)getpid()); | |
643 | ||
644 | zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix), | |
645 | "%s: ", protoname); | |
646 | } | |
647 | ||
648 | if (mkdir(TMPBASEDIR, 0700) != 0) { | |
649 | if (errno != EEXIST) { | |
650 | zlog_err("failed to mkdir \"%s\": %s", | |
651 | TMPBASEDIR, strerror(errno)); | |
652 | goto out_warn; | |
653 | } | |
654 | } | |
655 | chown(TMPBASEDIR, zlog_uid, zlog_gid); | |
656 | ||
657 | if (mkdir(zlog_tmpdir, 0700) != 0) { | |
658 | zlog_err("failed to mkdir \"%s\": %s", | |
659 | zlog_tmpdir, strerror(errno)); | |
660 | goto out_warn; | |
661 | } | |
662 | ||
663 | #ifdef O_PATH | |
664 | zlog_tmpdirfd = open(zlog_tmpdir, | |
665 | O_PATH | O_RDONLY | O_CLOEXEC); | |
666 | #else | |
667 | zlog_tmpdirfd = open(zlog_tmpdir, | |
668 | O_DIRECTORY | O_RDONLY | O_CLOEXEC); | |
669 | #endif | |
670 | if (zlog_tmpdirfd < 0) { | |
671 | zlog_err("failed to open \"%s\": %s", | |
672 | zlog_tmpdir, strerror(errno)); | |
673 | goto out_warn; | |
674 | } | |
675 | ||
676 | #ifdef AT_EMPTY_PATH | |
677 | fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH); | |
678 | #else | |
679 | chown(zlog_tmpdir, zlog_uid, zlog_gid); | |
680 | #endif | |
681 | ||
682 | hook_call(zlog_init, progname, protoname, instance, uid, gid); | |
683 | return; | |
684 | ||
685 | out_warn: | |
686 | zlog_err("crashlog and per-thread log buffering unavailable!"); | |
687 | hook_call(zlog_init, progname, protoname, instance, uid, gid); | |
688 | } | |
689 | ||
690 | void zlog_fini(void) | |
691 | { | |
692 | hook_call(zlog_fini); | |
693 | ||
694 | if (zlog_tmpdirfd >= 0) { | |
695 | close(zlog_tmpdirfd); | |
696 | zlog_tmpdirfd = -1; | |
697 | ||
698 | if (rmdir(zlog_tmpdir)) | |
699 | zlog_err("failed to rmdir \"%s\": %s", | |
700 | zlog_tmpdir, strerror(errno)); | |
701 | } | |
702 | } |