]>
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; | |
131879fb | 97 | const struct xref_logmsg *xref; |
0bdeb5e5 DL |
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); | |
0bdeb5e5 DL |
243 | if (mmfd < 0) { |
244 | zlog_err("failed to open thread log buffer \"%s\": %s", | |
245 | mmpath, strerror(errno)); | |
246 | goto out_anon; | |
247 | } | |
41051697 | 248 | fchown(mmfd, zlog_uid, zlog_gid); |
0bdeb5e5 DL |
249 | |
250 | #ifdef HAVE_POSIX_FALLOCATE | |
6a3b431b DL |
251 | if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0) |
252 | /* note next statement is under above if() */ | |
0bdeb5e5 | 253 | #endif |
6a3b431b | 254 | if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) { |
0bdeb5e5 DL |
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 | ||
131879fb DL |
353 | static void vzlog_notls(const struct xref_logmsg *xref, int prio, |
354 | const char *fmt, va_list ap) | |
0bdeb5e5 DL |
355 | { |
356 | struct zlog_target *zt; | |
357 | struct zlog_msg stackmsg = { | |
358 | .prio = prio & LOG_PRIMASK, | |
359 | .fmt = fmt, | |
131879fb | 360 | .xref = xref, |
0bdeb5e5 DL |
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 | ||
131879fb DL |
385 | static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, |
386 | int prio, const char *fmt, va_list ap) | |
0bdeb5e5 DL |
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; | |
131879fb | 419 | msg->xref = xref; |
0bdeb5e5 DL |
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 | ||
131879fb DL |
454 | void vzlogx(const struct xref_logmsg *xref, int prio, |
455 | const char *fmt, va_list ap) | |
0bdeb5e5 DL |
456 | { |
457 | struct zlog_tls *zlog_tls = zlog_tls_get(); | |
458 | ||
1bd1ebaa QY |
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: | |
c7bb4f00 | 466 | frrtracelog(TRACE_ERR, msg); |
1bd1ebaa QY |
467 | break; |
468 | case LOG_WARNING: | |
c7bb4f00 | 469 | frrtracelog(TRACE_WARNING, msg); |
1bd1ebaa QY |
470 | break; |
471 | case LOG_DEBUG: | |
c7bb4f00 | 472 | frrtracelog(TRACE_DEBUG, msg); |
1bd1ebaa QY |
473 | break; |
474 | case LOG_NOTICE: | |
c7bb4f00 | 475 | frrtracelog(TRACE_DEBUG, msg); |
1bd1ebaa QY |
476 | break; |
477 | case LOG_INFO: | |
478 | default: | |
c7bb4f00 | 479 | frrtracelog(TRACE_INFO, msg); |
1bd1ebaa QY |
480 | break; |
481 | } | |
482 | ||
483 | va_end(copy); | |
484 | XFREE(MTYPE_LOG_MESSAGE, msg); | |
485 | #endif | |
486 | ||
0bdeb5e5 | 487 | if (zlog_tls) |
131879fb | 488 | vzlog_tls(zlog_tls, xref, prio, fmt, ap); |
0bdeb5e5 | 489 | else |
131879fb | 490 | vzlog_notls(xref, prio, fmt, ap); |
0bdeb5e5 DL |
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 | ||
131879fb DL |
524 | const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg) |
525 | { | |
526 | return msg->xref; | |
527 | } | |
528 | ||
0bdeb5e5 DL |
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 | } |