]>
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" | |
912d45a1 | 55 | #include "libfrr_trace.h" |
0bdeb5e5 DL |
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 | ||
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 | ||
139 | struct 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 | ||
148 | static 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__ | |
155 | static pthread_key_t zlog_tls_key; | |
156 | ||
157 | static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500))); | |
158 | static void zlog_tls_key_init(void) | |
159 | { | |
160 | pthread_key_create(&zlog_tls_key, zlog_tls_free); | |
161 | } | |
162 | ||
163 | static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500))); | |
164 | static void zlog_tls_key_fini(void) | |
165 | { | |
166 | pthread_key_delete(zlog_tls_key); | |
167 | } | |
168 | ||
169 | static inline struct zlog_tls *zlog_tls_get(void) | |
170 | { | |
171 | return pthread_getspecific(zlog_tls_key); | |
172 | } | |
173 | ||
174 | static 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 | ||
183 | static thread_local struct zlog_tls *zlog_tls_var | |
184 | __attribute__((tls_model("initial-exec"))); | |
185 | ||
186 | static inline struct zlog_tls *zlog_tls_get(void) | |
187 | { | |
188 | return zlog_tls_var; | |
189 | } | |
190 | ||
191 | static inline void zlog_tls_set(struct zlog_tls *val) | |
192 | { | |
193 | zlog_tls_var = val; | |
194 | } | |
195 | #endif | |
196 | ||
197 | #ifdef CAN_DO_TLS | |
198 | static 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 | ||
222 | void 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 | ||
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 | ||
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 | ||
488 | void 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 | ||
514 | int zlog_msg_prio(struct zlog_msg *msg) | |
515 | { | |
516 | return msg->prio; | |
517 | } | |
518 | ||
519 | const 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 | ||
538 | size_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 | ||
614 | struct 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 | ||
629 | struct 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 | ||
644 | static char zlog_tmpdir[MAXPATHLEN]; | |
645 | ||
646 | void 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 | ||
654 | void 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 | ||
713 | out_warn: | |
714 | zlog_err("crashlog and per-thread log buffering unavailable!"); | |
715 | hook_call(zlog_init, progname, protoname, instance, uid, gid); | |
716 | } | |
717 | ||
718 | void 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 | } |