]>
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 | 56 | |
bf8d3d6a DL |
57 | DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message"); |
58 | DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer"); | |
0bdeb5e5 DL |
59 | |
60 | DEFINE_HOOK(zlog_init, (const char *progname, const char *protoname, | |
61 | unsigned short instance, uid_t uid, gid_t gid), | |
8451921b DL |
62 | (progname, protoname, instance, uid, gid)); |
63 | DEFINE_KOOH(zlog_fini, (), ()); | |
0bdeb5e5 | 64 | DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min), |
8451921b | 65 | (prefix, prio_min)); |
0bdeb5e5 DL |
66 | |
67 | char zlog_prefix[128]; | |
68 | size_t zlog_prefixsz; | |
69 | int zlog_tmpdirfd = -1; | |
70 | ||
a3c67498 DL |
71 | static atomic_bool zlog_ec = true, zlog_xid = true; |
72 | ||
0bdeb5e5 DL |
73 | /* these are kept around because logging is initialized (and directories |
74 | * & files created) before zprivs code switches to the FRR user; therefore | |
75 | * we need to chown() things so we don't get permission errors later when | |
76 | * trying to delete things on shutdown | |
77 | */ | |
78 | static uid_t zlog_uid = -1; | |
79 | static gid_t zlog_gid = -1; | |
80 | ||
81 | DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head); | |
82 | static struct zlog_targets_head zlog_targets; | |
83 | ||
42ddefe8 MS |
84 | /* Global setting for buffered vs immediate output. The default is |
85 | * per-pthread buffering. | |
86 | */ | |
87 | static bool default_immediate; | |
88 | ||
0bdeb5e5 DL |
89 | /* cf. zlog.h for additional comments on this struct. |
90 | * | |
91 | * Note: you MUST NOT pass the format string + va_list to non-FRR format | |
92 | * string functions (e.g. vsyslog, sd_journal_printv, ...) since FRR uses an | |
93 | * extended prinf() with additional formats (%pI4 and the like). | |
94 | * | |
95 | * Also remember to use va_copy() on args. | |
96 | */ | |
97 | ||
98 | struct zlog_msg { | |
99 | struct timespec ts; | |
100 | int prio; | |
101 | ||
102 | const char *fmt; | |
103 | va_list args; | |
131879fb | 104 | const struct xref_logmsg *xref; |
0bdeb5e5 DL |
105 | |
106 | char *stackbuf; | |
107 | size_t stackbufsz; | |
108 | char *text; | |
109 | size_t textlen; | |
e3daa82c | 110 | size_t hdrlen; |
0bdeb5e5 DL |
111 | |
112 | /* This is always ISO8601 with sub-second precision 9 here, it's | |
113 | * converted for callers as needed. ts_dot points to the "." | |
114 | * separating sub-seconds. ts_zonetail is "Z" or "+00:00" for the | |
115 | * local time offset. | |
116 | * | |
117 | * Valid if ZLOG_TS_ISO8601 is set. | |
118 | * (0 if timestamp has not been formatted yet) | |
119 | */ | |
0bdeb5e5 | 120 | char ts_str[32], *ts_dot, ts_zonetail[8]; |
243ff228 DL |
121 | uint32_t ts_flags; |
122 | ||
123 | /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from | |
124 | * the above, so just kept separately here. | |
125 | */ | |
126 | uint32_t ts_3164_flags; | |
127 | char ts_3164_str[16]; | |
e3daa82c DL |
128 | |
129 | /* at the time of writing, 16 args was the actual maximum of arguments | |
130 | * to a single zlog call. Particularly printing flag bitmasks seems | |
131 | * to drive this. That said, the overhead of dynamically sizing this | |
132 | * probably outweighs the value. If anything, a printfrr extension | |
133 | * for printing flag bitmasks might be a good idea. | |
134 | */ | |
135 | struct fmt_outpos argpos[24]; | |
136 | size_t n_argpos; | |
0bdeb5e5 DL |
137 | }; |
138 | ||
139 | /* thread-local log message buffering | |
140 | * | |
141 | * This is strictly optional and set up by calling zlog_tls_buffer_init() | |
142 | * on a particular thread. | |
143 | * | |
144 | * If in use, this will create a temporary file in /var/tmp which is used as | |
145 | * memory-mapped MAP_SHARED log message buffer. The idea there is that buffer | |
146 | * access doesn't require any syscalls, but in case of a crash the kernel | |
147 | * knows to sync the memory back to disk. This way the user can still get the | |
148 | * last log messages if there were any left unwritten in the buffer. | |
149 | * | |
150 | * Sizing this dynamically isn't particularly useful, so here's an 8k buffer | |
151 | * with a message limit of 64 messages. Message metadata (e.g. priority, | |
152 | * timestamp) aren't in the mmap region, so they're lost on crash, but we can | |
153 | * live with that. | |
154 | */ | |
155 | ||
156 | #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT) | |
157 | #define CAN_DO_TLS 1 | |
158 | #endif | |
159 | ||
160 | #define TLS_LOG_BUF_SIZE 8192 | |
161 | #define TLS_LOG_MAXMSG 64 | |
162 | ||
163 | struct zlog_tls { | |
164 | char *mmbuf; | |
165 | size_t bufpos; | |
4b4935bb | 166 | bool do_unlink; |
0bdeb5e5 DL |
167 | |
168 | size_t nmsgs; | |
169 | struct zlog_msg msgs[TLS_LOG_MAXMSG]; | |
170 | struct zlog_msg *msgp[TLS_LOG_MAXMSG]; | |
171 | }; | |
172 | ||
173 | static inline void zlog_tls_free(void *arg); | |
174 | ||
175 | /* proper ELF TLS is a bit faster than pthread_[gs]etspecific, so if it's | |
176 | * available we'll use it here | |
177 | */ | |
178 | ||
179 | #ifdef __OpenBSD__ | |
180 | static pthread_key_t zlog_tls_key; | |
181 | ||
182 | static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500))); | |
183 | static void zlog_tls_key_init(void) | |
184 | { | |
185 | pthread_key_create(&zlog_tls_key, zlog_tls_free); | |
186 | } | |
187 | ||
188 | static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500))); | |
189 | static void zlog_tls_key_fini(void) | |
190 | { | |
191 | pthread_key_delete(zlog_tls_key); | |
192 | } | |
193 | ||
194 | static inline struct zlog_tls *zlog_tls_get(void) | |
195 | { | |
196 | return pthread_getspecific(zlog_tls_key); | |
197 | } | |
198 | ||
199 | static inline void zlog_tls_set(struct zlog_tls *val) | |
200 | { | |
201 | pthread_setspecific(zlog_tls_key, val); | |
202 | } | |
203 | #else | |
204 | # ifndef thread_local | |
205 | # define thread_local __thread | |
206 | # endif | |
207 | ||
208 | static thread_local struct zlog_tls *zlog_tls_var | |
209 | __attribute__((tls_model("initial-exec"))); | |
210 | ||
211 | static inline struct zlog_tls *zlog_tls_get(void) | |
212 | { | |
213 | return zlog_tls_var; | |
214 | } | |
215 | ||
216 | static inline void zlog_tls_set(struct zlog_tls *val) | |
217 | { | |
218 | zlog_tls_var = val; | |
219 | } | |
220 | #endif | |
221 | ||
222 | #ifdef CAN_DO_TLS | |
223 | static long zlog_gettid(void) | |
224 | { | |
225 | long rv = -1; | |
226 | #ifdef HAVE_PTHREAD_GETTHREADID_NP | |
227 | rv = pthread_getthreadid_np(); | |
228 | #elif defined(linux) | |
229 | rv = syscall(__NR_gettid); | |
230 | #elif defined(__NetBSD__) | |
231 | rv = _lwp_self(); | |
232 | #elif defined(__FreeBSD__) | |
233 | thr_self(&rv); | |
234 | #elif defined(__DragonFly__) | |
235 | rv = lwp_gettid(); | |
236 | #elif defined(__OpenBSD__) | |
237 | rv = getthrid(); | |
238 | #elif defined(__sun) | |
239 | rv = pthread_self(); | |
240 | #elif defined(__APPLE__) | |
241 | rv = mach_thread_self(); | |
242 | mach_port_deallocate(mach_task_self(), rv); | |
243 | #endif | |
244 | return rv; | |
245 | } | |
246 | ||
247 | void zlog_tls_buffer_init(void) | |
248 | { | |
249 | struct zlog_tls *zlog_tls; | |
250 | char mmpath[MAXPATHLEN]; | |
251 | int mmfd; | |
252 | size_t i; | |
253 | ||
254 | zlog_tls = zlog_tls_get(); | |
255 | ||
256 | if (zlog_tls || zlog_tmpdirfd < 0) | |
257 | return; | |
258 | ||
259 | zlog_tls = XCALLOC(MTYPE_LOG_TLSBUF, sizeof(*zlog_tls)); | |
260 | for (i = 0; i < array_size(zlog_tls->msgp); i++) | |
261 | zlog_tls->msgp[i] = &zlog_tls->msgs[i]; | |
262 | ||
263 | snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid()); | |
264 | ||
265 | mmfd = openat(zlog_tmpdirfd, mmpath, | |
266 | O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600); | |
0bdeb5e5 DL |
267 | if (mmfd < 0) { |
268 | zlog_err("failed to open thread log buffer \"%s\": %s", | |
269 | mmpath, strerror(errno)); | |
270 | goto out_anon; | |
271 | } | |
41051697 | 272 | fchown(mmfd, zlog_uid, zlog_gid); |
0bdeb5e5 DL |
273 | |
274 | #ifdef HAVE_POSIX_FALLOCATE | |
6a3b431b DL |
275 | if (posix_fallocate(mmfd, 0, TLS_LOG_BUF_SIZE) != 0) |
276 | /* note next statement is under above if() */ | |
0bdeb5e5 | 277 | #endif |
6a3b431b | 278 | if (ftruncate(mmfd, TLS_LOG_BUF_SIZE) < 0) { |
0bdeb5e5 DL |
279 | zlog_err("failed to allocate thread log buffer \"%s\": %s", |
280 | mmpath, strerror(errno)); | |
281 | goto out_anon_unlink; | |
282 | } | |
283 | ||
284 | zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE, | |
285 | MAP_SHARED, mmfd, 0); | |
286 | if (zlog_tls->mmbuf == MAP_FAILED) { | |
287 | zlog_err("failed to mmap thread log buffer \"%s\": %s", | |
288 | mmpath, strerror(errno)); | |
289 | goto out_anon_unlink; | |
290 | } | |
4b4935bb | 291 | zlog_tls->do_unlink = true; |
0bdeb5e5 DL |
292 | |
293 | close(mmfd); | |
294 | zlog_tls_set(zlog_tls); | |
295 | return; | |
296 | ||
297 | out_anon_unlink: | |
4b4935bb | 298 | unlinkat(zlog_tmpdirfd, mmpath, 0); |
0bdeb5e5 DL |
299 | close(mmfd); |
300 | out_anon: | |
301 | ||
302 | #ifndef MAP_ANONYMOUS | |
303 | #define MAP_ANONYMOUS MAP_ANON | |
304 | #endif | |
305 | zlog_tls->mmbuf = mmap(NULL, TLS_LOG_BUF_SIZE, PROT_READ | PROT_WRITE, | |
306 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | |
307 | ||
308 | if (!zlog_tls->mmbuf) { | |
309 | zlog_err("failed to anonymous-mmap thread log buffer: %s", | |
310 | strerror(errno)); | |
311 | XFREE(MTYPE_LOG_TLSBUF, zlog_tls); | |
312 | zlog_tls_set(NULL); | |
313 | return; | |
314 | } | |
315 | ||
316 | zlog_tls_set(zlog_tls); | |
317 | } | |
318 | ||
319 | void zlog_tls_buffer_fini(void) | |
320 | { | |
321 | char mmpath[MAXPATHLEN]; | |
4b4935bb DL |
322 | struct zlog_tls *zlog_tls = zlog_tls_get(); |
323 | bool do_unlink = zlog_tls ? zlog_tls->do_unlink : false; | |
0bdeb5e5 DL |
324 | |
325 | zlog_tls_buffer_flush(); | |
326 | ||
4b4935bb | 327 | zlog_tls_free(zlog_tls); |
0bdeb5e5 DL |
328 | zlog_tls_set(NULL); |
329 | ||
330 | snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid()); | |
4b4935bb | 331 | if (do_unlink && unlinkat(zlog_tmpdirfd, mmpath, 0)) |
0bdeb5e5 DL |
332 | zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno); |
333 | } | |
334 | ||
335 | #else /* !CAN_DO_TLS */ | |
336 | void zlog_tls_buffer_init(void) | |
337 | { | |
338 | } | |
339 | ||
340 | void zlog_tls_buffer_fini(void) | |
341 | { | |
342 | } | |
343 | #endif | |
344 | ||
345 | static inline void zlog_tls_free(void *arg) | |
346 | { | |
347 | struct zlog_tls *zlog_tls = arg; | |
348 | ||
349 | if (!zlog_tls) | |
350 | return; | |
351 | ||
352 | munmap(zlog_tls->mmbuf, TLS_LOG_BUF_SIZE); | |
353 | XFREE(MTYPE_LOG_TLSBUF, zlog_tls); | |
354 | } | |
355 | ||
356 | void zlog_tls_buffer_flush(void) | |
357 | { | |
358 | struct zlog_target *zt; | |
359 | struct zlog_tls *zlog_tls = zlog_tls_get(); | |
360 | ||
361 | if (!zlog_tls) | |
362 | return; | |
363 | if (!zlog_tls->nmsgs) | |
364 | return; | |
365 | ||
366 | rcu_read_lock(); | |
367 | frr_each (zlog_targets, &zlog_targets, zt) { | |
368 | if (!zt->logfn) | |
369 | continue; | |
370 | ||
371 | zt->logfn(zt, zlog_tls->msgp, zlog_tls->nmsgs); | |
372 | } | |
373 | rcu_read_unlock(); | |
374 | ||
375 | zlog_tls->bufpos = 0; | |
376 | zlog_tls->nmsgs = 0; | |
377 | } | |
378 | ||
379 | ||
131879fb DL |
380 | static void vzlog_notls(const struct xref_logmsg *xref, int prio, |
381 | const char *fmt, va_list ap) | |
0bdeb5e5 DL |
382 | { |
383 | struct zlog_target *zt; | |
384 | struct zlog_msg stackmsg = { | |
385 | .prio = prio & LOG_PRIMASK, | |
386 | .fmt = fmt, | |
131879fb | 387 | .xref = xref, |
0bdeb5e5 DL |
388 | }, *msg = &stackmsg; |
389 | char stackbuf[512]; | |
390 | ||
391 | clock_gettime(CLOCK_REALTIME, &msg->ts); | |
392 | va_copy(msg->args, ap); | |
393 | msg->stackbuf = stackbuf; | |
394 | msg->stackbufsz = sizeof(stackbuf); | |
395 | ||
396 | rcu_read_lock(); | |
397 | frr_each (zlog_targets, &zlog_targets, zt) { | |
398 | if (prio > zt->prio_min) | |
399 | continue; | |
400 | if (!zt->logfn) | |
401 | continue; | |
402 | ||
403 | zt->logfn(zt, &msg, 1); | |
404 | } | |
405 | rcu_read_unlock(); | |
406 | ||
407 | va_end(msg->args); | |
408 | if (msg->text && msg->text != stackbuf) | |
409 | XFREE(MTYPE_LOG_MESSAGE, msg->text); | |
410 | } | |
411 | ||
131879fb DL |
412 | static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, |
413 | int prio, const char *fmt, va_list ap) | |
0bdeb5e5 DL |
414 | { |
415 | struct zlog_target *zt; | |
416 | struct zlog_msg *msg; | |
417 | char *buf; | |
418 | bool ignoremsg = true; | |
42ddefe8 | 419 | bool immediate = default_immediate; |
0bdeb5e5 DL |
420 | |
421 | /* avoid further processing cost if no target wants this message */ | |
422 | rcu_read_lock(); | |
423 | frr_each (zlog_targets, &zlog_targets, zt) { | |
424 | if (prio > zt->prio_min) | |
425 | continue; | |
426 | ignoremsg = false; | |
427 | break; | |
428 | } | |
429 | rcu_read_unlock(); | |
430 | ||
431 | if (ignoremsg) | |
432 | return; | |
433 | ||
434 | msg = &zlog_tls->msgs[zlog_tls->nmsgs]; | |
435 | zlog_tls->nmsgs++; | |
436 | if (zlog_tls->nmsgs == array_size(zlog_tls->msgs)) | |
437 | immediate = true; | |
438 | ||
439 | memset(msg, 0, sizeof(*msg)); | |
440 | clock_gettime(CLOCK_REALTIME, &msg->ts); | |
441 | va_copy(msg->args, ap); | |
442 | msg->stackbuf = buf = zlog_tls->mmbuf + zlog_tls->bufpos; | |
443 | msg->stackbufsz = TLS_LOG_BUF_SIZE - zlog_tls->bufpos - 1; | |
444 | msg->fmt = fmt; | |
445 | msg->prio = prio & LOG_PRIMASK; | |
131879fb | 446 | msg->xref = xref; |
0bdeb5e5 DL |
447 | if (msg->prio < LOG_INFO) |
448 | immediate = true; | |
449 | ||
450 | if (!immediate) { | |
451 | /* messages written later need to take the formatting cost | |
452 | * immediately since we can't hold a reference on varargs | |
453 | */ | |
454 | zlog_msg_text(msg, NULL); | |
455 | ||
456 | if (msg->text != buf) | |
457 | /* zlog_msg_text called malloc() on us :( */ | |
458 | immediate = true; | |
459 | else { | |
460 | zlog_tls->bufpos += msg->textlen + 1; | |
461 | /* write a second \0 to mark current end position | |
462 | * (in case of crash this signals end of unwritten log | |
463 | * messages in mmap'd logbuf file) | |
464 | */ | |
465 | zlog_tls->mmbuf[zlog_tls->bufpos] = '\0'; | |
466 | ||
467 | /* avoid malloc() for next message */ | |
468 | if (TLS_LOG_BUF_SIZE - zlog_tls->bufpos < 256) | |
469 | immediate = true; | |
470 | } | |
471 | } | |
472 | ||
473 | if (immediate) | |
474 | zlog_tls_buffer_flush(); | |
475 | ||
476 | va_end(msg->args); | |
477 | if (msg->text && msg->text != buf) | |
478 | XFREE(MTYPE_LOG_MESSAGE, msg->text); | |
479 | } | |
480 | ||
131879fb DL |
481 | void vzlogx(const struct xref_logmsg *xref, int prio, |
482 | const char *fmt, va_list ap) | |
0bdeb5e5 DL |
483 | { |
484 | struct zlog_tls *zlog_tls = zlog_tls_get(); | |
485 | ||
1bd1ebaa QY |
486 | #ifdef HAVE_LTTNG |
487 | va_list copy; | |
488 | va_copy(copy, ap); | |
489 | char *msg = vasprintfrr(MTYPE_LOG_MESSAGE, fmt, copy); | |
490 | ||
491 | switch (prio) { | |
492 | case LOG_ERR: | |
c7bb4f00 | 493 | frrtracelog(TRACE_ERR, msg); |
1bd1ebaa QY |
494 | break; |
495 | case LOG_WARNING: | |
c7bb4f00 | 496 | frrtracelog(TRACE_WARNING, msg); |
1bd1ebaa QY |
497 | break; |
498 | case LOG_DEBUG: | |
c7bb4f00 | 499 | frrtracelog(TRACE_DEBUG, msg); |
1bd1ebaa QY |
500 | break; |
501 | case LOG_NOTICE: | |
c7bb4f00 | 502 | frrtracelog(TRACE_DEBUG, msg); |
1bd1ebaa QY |
503 | break; |
504 | case LOG_INFO: | |
505 | default: | |
c7bb4f00 | 506 | frrtracelog(TRACE_INFO, msg); |
1bd1ebaa QY |
507 | break; |
508 | } | |
509 | ||
510 | va_end(copy); | |
511 | XFREE(MTYPE_LOG_MESSAGE, msg); | |
512 | #endif | |
513 | ||
0bdeb5e5 | 514 | if (zlog_tls) |
131879fb | 515 | vzlog_tls(zlog_tls, xref, prio, fmt, ap); |
0bdeb5e5 | 516 | else |
131879fb | 517 | vzlog_notls(xref, prio, fmt, ap); |
0bdeb5e5 DL |
518 | } |
519 | ||
520 | void zlog_sigsafe(const char *text, size_t len) | |
521 | { | |
522 | struct zlog_target *zt; | |
523 | const char *end = text + len, *nlpos; | |
524 | ||
525 | while (text < end) { | |
526 | nlpos = memchr(text, '\n', end - text); | |
527 | if (!nlpos) | |
528 | nlpos = end; | |
529 | ||
530 | frr_each (zlog_targets, &zlog_targets, zt) { | |
531 | if (LOG_CRIT > zt->prio_min) | |
532 | continue; | |
533 | if (!zt->logfn_sigsafe) | |
534 | continue; | |
535 | ||
536 | zt->logfn_sigsafe(zt, text, nlpos - text); | |
537 | } | |
538 | ||
539 | if (nlpos == end) | |
540 | break; | |
541 | text = nlpos + 1; | |
542 | } | |
543 | } | |
544 | ||
64dd7736 DL |
545 | void _zlog_assert_failed(const struct xref_assert *xref, const char *extra, ...) |
546 | { | |
547 | va_list ap; | |
548 | static bool assert_in_assert; /* "global-ish" variable, init to 0 */ | |
549 | ||
550 | if (assert_in_assert) | |
551 | abort(); | |
552 | assert_in_assert = true; | |
553 | ||
554 | if (extra) { | |
555 | struct va_format vaf; | |
556 | ||
557 | va_start(ap, extra); | |
558 | vaf.fmt = extra; | |
559 | vaf.va = ≈ | |
560 | ||
561 | zlog(LOG_CRIT, | |
562 | "%s:%d: %s(): assertion (%s) failed, extra info: %pVA", | |
563 | xref->xref.file, xref->xref.line, xref->xref.func, | |
564 | xref->expr, &vaf); | |
565 | ||
566 | va_end(ap); | |
567 | } else | |
568 | zlog(LOG_CRIT, "%s:%d: %s(): assertion (%s) failed", | |
569 | xref->xref.file, xref->xref.line, xref->xref.func, | |
570 | xref->expr); | |
571 | ||
572 | /* abort() prints backtrace & memstats in SIGABRT handler */ | |
573 | abort(); | |
574 | } | |
0bdeb5e5 DL |
575 | |
576 | int zlog_msg_prio(struct zlog_msg *msg) | |
577 | { | |
578 | return msg->prio; | |
579 | } | |
580 | ||
131879fb DL |
581 | const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg) |
582 | { | |
583 | return msg->xref; | |
584 | } | |
585 | ||
0bdeb5e5 DL |
586 | const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen) |
587 | { | |
588 | if (!msg->text) { | |
589 | va_list args; | |
a3c67498 DL |
590 | bool do_xid, do_ec; |
591 | size_t need = 0, hdrlen; | |
592 | struct fbuf fb = { | |
593 | .buf = msg->stackbuf, | |
594 | .pos = msg->stackbuf, | |
595 | .len = msg->stackbufsz, | |
596 | }; | |
597 | ||
598 | do_ec = atomic_load_explicit(&zlog_ec, memory_order_relaxed); | |
599 | do_xid = atomic_load_explicit(&zlog_xid, memory_order_relaxed); | |
600 | ||
601 | if (msg->xref && do_xid && msg->xref->xref.xrefdata->uid[0]) { | |
602 | need += bputch(&fb, '['); | |
603 | need += bputs(&fb, msg->xref->xref.xrefdata->uid); | |
604 | need += bputch(&fb, ']'); | |
605 | } | |
606 | if (msg->xref && do_ec && msg->xref->ec) | |
607 | need += bprintfrr(&fb, "[EC %u]", msg->xref->ec); | |
608 | if (need) | |
609 | need += bputch(&fb, ' '); | |
610 | ||
e3daa82c | 611 | msg->hdrlen = hdrlen = need; |
a3c67498 | 612 | assert(hdrlen < msg->stackbufsz); |
0bdeb5e5 | 613 | |
e3daa82c DL |
614 | fb.outpos = msg->argpos; |
615 | fb.outpos_n = array_size(msg->argpos); | |
616 | fb.outpos_i = 0; | |
617 | ||
0bdeb5e5 | 618 | va_copy(args, msg->args); |
a3c67498 | 619 | need += vbprintfrr(&fb, msg->fmt, args); |
0bdeb5e5 | 620 | va_end(args); |
a3c67498 DL |
621 | |
622 | msg->textlen = need; | |
8b94cb43 | 623 | need += bputch(&fb, '\n'); |
a3c67498 DL |
624 | |
625 | if (need <= msg->stackbufsz) | |
626 | msg->text = msg->stackbuf; | |
627 | else { | |
628 | msg->text = XMALLOC(MTYPE_LOG_MESSAGE, need); | |
629 | ||
630 | memcpy(msg->text, msg->stackbuf, hdrlen); | |
631 | ||
632 | fb.buf = msg->text; | |
633 | fb.len = need; | |
634 | fb.pos = msg->text + hdrlen; | |
e3daa82c | 635 | fb.outpos_i = 0; |
a3c67498 DL |
636 | |
637 | va_copy(args, msg->args); | |
638 | vbprintfrr(&fb, msg->fmt, args); | |
639 | va_end(args); | |
640 | ||
8b94cb43 | 641 | bputch(&fb, '\n'); |
a3c67498 | 642 | } |
e3daa82c DL |
643 | |
644 | msg->n_argpos = fb.outpos_i; | |
0bdeb5e5 DL |
645 | } |
646 | if (textlen) | |
647 | *textlen = msg->textlen; | |
648 | return msg->text; | |
649 | } | |
650 | ||
e3daa82c DL |
651 | void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, size_t *n_argpos, |
652 | const struct fmt_outpos **argpos) | |
653 | { | |
654 | if (!msg->text) | |
655 | zlog_msg_text(msg, NULL); | |
656 | ||
657 | if (hdrlen) | |
658 | *hdrlen = msg->hdrlen; | |
659 | if (n_argpos) | |
660 | *n_argpos = msg->n_argpos; | |
661 | if (argpos) | |
662 | *argpos = msg->argpos; | |
663 | } | |
664 | ||
0bdeb5e5 DL |
665 | #define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY) |
666 | #define ZLOG_TS_FLAGS ~ZLOG_TS_PREC | |
667 | ||
f6caaa65 | 668 | size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags) |
0bdeb5e5 | 669 | { |
f6caaa65 | 670 | size_t outsz = out ? (out->buf + out->len - out->pos) : 0; |
0bdeb5e5 DL |
671 | size_t len1; |
672 | ||
673 | if (!(flags & ZLOG_TS_FORMAT)) | |
674 | return 0; | |
675 | ||
676 | if (!(msg->ts_flags & ZLOG_TS_FORMAT) || | |
677 | ((msg->ts_flags ^ flags) & ZLOG_TS_UTC)) { | |
678 | struct tm tm; | |
679 | ||
680 | if (flags & ZLOG_TS_UTC) | |
681 | gmtime_r(&msg->ts.tv_sec, &tm); | |
682 | else | |
683 | localtime_r(&msg->ts.tv_sec, &tm); | |
684 | ||
685 | strftime(msg->ts_str, sizeof(msg->ts_str), | |
686 | "%Y-%m-%dT%H:%M:%S", &tm); | |
687 | ||
688 | if (flags & ZLOG_TS_UTC) { | |
689 | msg->ts_zonetail[0] = 'Z'; | |
690 | msg->ts_zonetail[1] = '\0'; | |
691 | } else | |
692 | snprintfrr(msg->ts_zonetail, sizeof(msg->ts_zonetail), | |
693 | "%+03d:%02d", | |
694 | (int)(tm.tm_gmtoff / 3600), | |
695 | (int)(labs(tm.tm_gmtoff) / 60) % 60); | |
696 | ||
697 | msg->ts_dot = msg->ts_str + strlen(msg->ts_str); | |
698 | snprintfrr(msg->ts_dot, | |
699 | msg->ts_str + sizeof(msg->ts_str) - msg->ts_dot, | |
700 | ".%09lu", (unsigned long)msg->ts.tv_nsec); | |
701 | ||
702 | msg->ts_flags = ZLOG_TS_ISO8601 | (flags & ZLOG_TS_UTC); | |
703 | } | |
704 | ||
705 | len1 = flags & ZLOG_TS_PREC; | |
706 | len1 = (msg->ts_dot - msg->ts_str) + (len1 ? len1 + 1 : 0); | |
707 | ||
708 | if (len1 > strlen(msg->ts_str)) | |
709 | len1 = strlen(msg->ts_str); | |
710 | ||
711 | if (flags & ZLOG_TS_LEGACY) { | |
f6caaa65 DL |
712 | if (!out) |
713 | return len1; | |
714 | ||
715 | if (len1 > outsz) { | |
716 | memset(out->pos, 0, outsz); | |
717 | out->pos += outsz; | |
718 | return len1; | |
719 | } | |
0bdeb5e5 DL |
720 | |
721 | /* just swap out the formatting, faster than redoing it */ | |
722 | for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) { | |
723 | switch (*p) { | |
724 | case '-': | |
f6caaa65 | 725 | *out->pos++ = '/'; |
0bdeb5e5 DL |
726 | break; |
727 | case 'T': | |
f6caaa65 | 728 | *out->pos++ = ' '; |
0bdeb5e5 DL |
729 | break; |
730 | default: | |
f6caaa65 | 731 | *out->pos++ = *p; |
0bdeb5e5 DL |
732 | } |
733 | } | |
0bdeb5e5 DL |
734 | return len1; |
735 | } else { | |
736 | size_t len2 = strlen(msg->ts_zonetail); | |
737 | ||
f6caaa65 DL |
738 | if (!out) |
739 | return len1 + len2; | |
740 | ||
741 | if (len1 + len2 > outsz) { | |
742 | memset(out->pos, 0, outsz); | |
743 | out->pos += outsz; | |
744 | return len1 + len2; | |
745 | } | |
746 | ||
747 | memcpy(out->pos, msg->ts_str, len1); | |
748 | out->pos += len1; | |
749 | memcpy(out->pos, msg->ts_zonetail, len2); | |
750 | out->pos += len2; | |
0bdeb5e5 DL |
751 | return len1 + len2; |
752 | } | |
753 | } | |
754 | ||
243ff228 DL |
755 | size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags) |
756 | { | |
757 | flags &= ZLOG_TS_UTC; | |
758 | ||
759 | if (!msg->ts_3164_str[0] || flags != msg->ts_3164_flags) { | |
760 | /* these are "hardcoded" in RFC3164, so they're here too... */ | |
761 | static const char *const months[12] = { | |
762 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | |
763 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", | |
764 | }; | |
765 | struct tm tm; | |
766 | ||
767 | /* RFC3164 explicitly asks for local time, but common usage | |
768 | * also includes UTC. | |
769 | */ | |
770 | if (flags & ZLOG_TS_UTC) | |
771 | gmtime_r(&msg->ts.tv_sec, &tm); | |
772 | else | |
773 | localtime_r(&msg->ts.tv_sec, &tm); | |
774 | ||
775 | snprintfrr(msg->ts_3164_str, sizeof(msg->ts_3164_str), | |
776 | "%3s %2d %02d:%02d:%02d", months[tm.tm_mon], | |
777 | tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); | |
778 | ||
779 | msg->ts_3164_flags = flags; | |
780 | } | |
781 | return bputs(out, msg->ts_3164_str); | |
782 | } | |
783 | ||
a3c67498 DL |
784 | void zlog_set_prefix_ec(bool enable) |
785 | { | |
786 | atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed); | |
787 | } | |
788 | ||
789 | bool zlog_get_prefix_ec(void) | |
790 | { | |
791 | return atomic_load_explicit(&zlog_ec, memory_order_relaxed); | |
792 | } | |
793 | ||
794 | void zlog_set_prefix_xid(bool enable) | |
795 | { | |
796 | atomic_store_explicit(&zlog_xid, enable, memory_order_relaxed); | |
797 | } | |
798 | ||
799 | bool zlog_get_prefix_xid(void) | |
800 | { | |
801 | return atomic_load_explicit(&zlog_xid, memory_order_relaxed); | |
802 | } | |
803 | ||
0bdeb5e5 DL |
804 | /* setup functions */ |
805 | ||
806 | struct zlog_target *zlog_target_clone(struct memtype *mt, | |
807 | struct zlog_target *oldzt, size_t size) | |
808 | { | |
809 | struct zlog_target *newzt; | |
810 | ||
811 | newzt = XCALLOC(mt, size); | |
812 | if (oldzt) { | |
813 | newzt->prio_min = oldzt->prio_min; | |
814 | newzt->logfn = oldzt->logfn; | |
815 | newzt->logfn_sigsafe = oldzt->logfn_sigsafe; | |
816 | } | |
817 | ||
818 | return newzt; | |
819 | } | |
820 | ||
821 | struct zlog_target *zlog_target_replace(struct zlog_target *oldzt, | |
822 | struct zlog_target *newzt) | |
823 | { | |
824 | if (newzt) | |
825 | zlog_targets_add_tail(&zlog_targets, newzt); | |
826 | if (oldzt) | |
827 | zlog_targets_del(&zlog_targets, oldzt); | |
828 | return oldzt; | |
829 | } | |
830 | ||
42ddefe8 MS |
831 | /* |
832 | * Enable or disable 'immediate' output - default is to buffer | |
833 | * each pthread's messages. | |
834 | */ | |
835 | void zlog_set_immediate(bool set_p) | |
836 | { | |
837 | default_immediate = set_p; | |
838 | } | |
0bdeb5e5 DL |
839 | |
840 | /* common init */ | |
841 | ||
842 | #define TMPBASEDIR "/var/tmp/frr" | |
843 | ||
844 | static char zlog_tmpdir[MAXPATHLEN]; | |
845 | ||
846 | void zlog_aux_init(const char *prefix, int prio_min) | |
847 | { | |
848 | if (prefix) | |
849 | strlcpy(zlog_prefix, prefix, sizeof(zlog_prefix)); | |
850 | ||
851 | hook_call(zlog_aux_init, prefix, prio_min); | |
852 | } | |
853 | ||
854 | void zlog_init(const char *progname, const char *protoname, | |
855 | unsigned short instance, uid_t uid, gid_t gid) | |
856 | { | |
857 | zlog_uid = uid; | |
858 | zlog_gid = gid; | |
859 | ||
860 | if (instance) { | |
861 | snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), | |
862 | "/var/tmp/frr/%s-%d.%ld", | |
863 | progname, instance, (long)getpid()); | |
864 | ||
865 | zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix), | |
866 | "%s[%d]: ", protoname, instance); | |
867 | } else { | |
868 | snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir), | |
869 | "/var/tmp/frr/%s.%ld", | |
870 | progname, (long)getpid()); | |
871 | ||
872 | zlog_prefixsz = snprintfrr(zlog_prefix, sizeof(zlog_prefix), | |
873 | "%s: ", protoname); | |
874 | } | |
875 | ||
876 | if (mkdir(TMPBASEDIR, 0700) != 0) { | |
877 | if (errno != EEXIST) { | |
878 | zlog_err("failed to mkdir \"%s\": %s", | |
879 | TMPBASEDIR, strerror(errno)); | |
880 | goto out_warn; | |
881 | } | |
882 | } | |
883 | chown(TMPBASEDIR, zlog_uid, zlog_gid); | |
884 | ||
885 | if (mkdir(zlog_tmpdir, 0700) != 0) { | |
886 | zlog_err("failed to mkdir \"%s\": %s", | |
887 | zlog_tmpdir, strerror(errno)); | |
888 | goto out_warn; | |
889 | } | |
890 | ||
891 | #ifdef O_PATH | |
892 | zlog_tmpdirfd = open(zlog_tmpdir, | |
893 | O_PATH | O_RDONLY | O_CLOEXEC); | |
894 | #else | |
895 | zlog_tmpdirfd = open(zlog_tmpdir, | |
896 | O_DIRECTORY | O_RDONLY | O_CLOEXEC); | |
897 | #endif | |
898 | if (zlog_tmpdirfd < 0) { | |
899 | zlog_err("failed to open \"%s\": %s", | |
900 | zlog_tmpdir, strerror(errno)); | |
901 | goto out_warn; | |
902 | } | |
903 | ||
904 | #ifdef AT_EMPTY_PATH | |
905 | fchownat(zlog_tmpdirfd, "", zlog_uid, zlog_gid, AT_EMPTY_PATH); | |
906 | #else | |
907 | chown(zlog_tmpdir, zlog_uid, zlog_gid); | |
908 | #endif | |
909 | ||
910 | hook_call(zlog_init, progname, protoname, instance, uid, gid); | |
911 | return; | |
912 | ||
913 | out_warn: | |
914 | zlog_err("crashlog and per-thread log buffering unavailable!"); | |
915 | hook_call(zlog_init, progname, protoname, instance, uid, gid); | |
916 | } | |
917 | ||
918 | void zlog_fini(void) | |
919 | { | |
920 | hook_call(zlog_fini); | |
921 | ||
922 | if (zlog_tmpdirfd >= 0) { | |
923 | close(zlog_tmpdirfd); | |
924 | zlog_tmpdirfd = -1; | |
925 | ||
926 | if (rmdir(zlog_tmpdir)) | |
927 | zlog_err("failed to rmdir \"%s\": %s", | |
928 | zlog_tmpdir, strerror(errno)); | |
929 | } | |
930 | } |