]> git.proxmox.com Git - mirror_frr.git/blame - lib/zlog.c
lib: include `\n` in zlog_msg_text()
[mirror_frr.git] / lib / zlog.c
CommitLineData
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
57DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message");
58DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer");
0bdeb5e5
DL
59
60DEFINE_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));
63DEFINE_KOOH(zlog_fini, (), ());
0bdeb5e5 64DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
8451921b 65 (prefix, prio_min));
0bdeb5e5
DL
66
67char zlog_prefix[128];
68size_t zlog_prefixsz;
69int zlog_tmpdirfd = -1;
70
a3c67498
DL
71static 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 */
78static uid_t zlog_uid = -1;
79static gid_t zlog_gid = -1;
80
81DECLARE_ATOMLIST(zlog_targets, struct zlog_target, head);
82static 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 */
87static 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
98struct 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
163struct 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
173static 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__
180static pthread_key_t zlog_tls_key;
181
182static void zlog_tls_key_init(void) __attribute__((_CONSTRUCTOR(500)));
183static void zlog_tls_key_init(void)
184{
185 pthread_key_create(&zlog_tls_key, zlog_tls_free);
186}
187
188static void zlog_tls_key_fini(void) __attribute__((_DESTRUCTOR(500)));
189static void zlog_tls_key_fini(void)
190{
191 pthread_key_delete(zlog_tls_key);
192}
193
194static inline struct zlog_tls *zlog_tls_get(void)
195{
196 return pthread_getspecific(zlog_tls_key);
197}
198
199static 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
208static thread_local struct zlog_tls *zlog_tls_var
209 __attribute__((tls_model("initial-exec")));
210
211static inline struct zlog_tls *zlog_tls_get(void)
212{
213 return zlog_tls_var;
214}
215
216static inline void zlog_tls_set(struct zlog_tls *val)
217{
218 zlog_tls_var = val;
219}
220#endif
221
222#ifdef CAN_DO_TLS
223static 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
247void 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
297out_anon_unlink:
4b4935bb 298 unlinkat(zlog_tmpdirfd, mmpath, 0);
0bdeb5e5
DL
299 close(mmfd);
300out_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
319void 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 */
336void zlog_tls_buffer_init(void)
337{
338}
339
340void zlog_tls_buffer_fini(void)
341{
342}
343#endif
344
345static 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
356void 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
380static 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
412static 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
481void 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
520void 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
545void _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 = &ap;
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
576int zlog_msg_prio(struct zlog_msg *msg)
577{
578 return msg->prio;
579}
580
131879fb
DL
581const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg)
582{
583 return msg->xref;
584}
585
0bdeb5e5
DL
586const 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
651void 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 668size_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
755size_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
784void zlog_set_prefix_ec(bool enable)
785{
786 atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed);
787}
788
789bool zlog_get_prefix_ec(void)
790{
791 return atomic_load_explicit(&zlog_ec, memory_order_relaxed);
792}
793
794void zlog_set_prefix_xid(bool enable)
795{
796 atomic_store_explicit(&zlog_xid, enable, memory_order_relaxed);
797}
798
799bool zlog_get_prefix_xid(void)
800{
801 return atomic_load_explicit(&zlog_xid, memory_order_relaxed);
802}
803
0bdeb5e5
DL
804/* setup functions */
805
806struct 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
821struct 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 */
835void 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
844static char zlog_tmpdir[MAXPATHLEN];
845
846void 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
854void 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
913out_warn:
914 zlog_err("crashlog and per-thread log buffering unavailable!");
915 hook_call(zlog_init, progname, protoname, instance, uid, gid);
916}
917
918void 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}