Coverage Report

Created: 2025-12-12 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/zlog_5424.c
Line
Count
Source
1
// SPDX-License-Identifier: ISC
2
/*
3
 * Copyright (c) 2015-21  David Lamparter, for NetDEF, Inc.
4
 */
5
6
/* when you work on this code, please install a fuzzer (e.g. AFL) and run
7
 * tests/lib/fuzz_zlog.c
8
 *
9
 * The most likely type of bug in this code is an off-by-one error in the
10
 * buffer management pieces, and this isn't easily covered by an unit test
11
 * or topotests.  Fuzzing is the best tool here, but the CI can't do that
12
 * since it's quite resource intensive.
13
 */
14
15
#include "zebra.h"
16
17
#include "zlog_5424.h"
18
19
#include <sys/un.h>
20
#include <syslog.h>
21
22
#include "memory.h"
23
#include "frrcu.h"
24
#include "printfrr.h"
25
#include "typerb.h"
26
#include "frr_pthread.h"
27
#include "command.h"
28
#include "monotime.h"
29
#include "frrevent.h"
30
31
#include "lib/version.h"
32
#include "lib/lib_errors.h"
33
34
8
DEFINE_MTYPE_STATIC(LOG, LOG_5424, "extended log target");
35
8
DEFINE_MTYPE_STATIC(LOG, LOG_5424_ROTATE, "extended log rotate helper");
36
8
37
8
/* the actual log target data structure
38
8
 *
39
8
 * remember this is RCU'd by the core zlog functions.  Changing anything
40
8
 * works by allocating a new struct, filling it, adding it, and removing the
41
8
 * old one.
42
8
 */
43
8
struct zlt_5424 {
44
8
  struct zlog_target zt;
45
8
46
8
  atomic_uint_fast32_t fd;
47
8
48
8
  enum zlog_5424_format fmt;
49
8
  uint32_t ts_flags;
50
8
  int facility;
51
8
52
8
  /* the various extra pieces to add... */
53
8
  bool kw_version : 1;
54
8
  bool kw_location : 1;
55
8
  bool kw_uid : 1;
56
8
  bool kw_ec : 1;
57
8
  bool kw_args : 1;
58
8
59
8
  /* some formats may or may not include the trailing \n */
60
8
  bool use_nl : 1;
61
8
62
8
  /* for DGRAM & SEQPACKET sockets, send 1 log message per packet, since
63
8
   * the socket preserves packet boundaries.  On Linux, this uses
64
8
   * sendmmsg() for efficiency, on other systems we need a syscall each.
65
8
   */
66
8
  bool packets : 1;
67
8
68
8
  /* for DGRAM, in order to not have to reconnect, we need to use
69
8
   * sendto()/sendmsg() with the destination given; otherwise we'll get
70
8
   * ENOTCONN.  (We do a connect(), which serves to verify the type of
71
8
   * socket, but if the receiver goes away, the kernel disconnects the
72
8
   * socket so writev() no longer works since the destination is now
73
8
   * unspecified.)
74
8
   */
75
8
  struct sockaddr_storage sa;
76
8
  socklen_t sa_len;
77
8
78
8
  /* these are both getting set, but current_err is cleared on success,
79
8
   * so we know whether the error is current or past.
80
8
   */
81
8
  int last_err, current_err;
82
8
  atomic_size_t lost_msgs;
83
8
  struct timeval last_err_ts;
84
8
85
8
  struct rcu_head_close head_close;
86
8
};
87
8
88
8
static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type);
89
8
90
8
/* rough header length estimate
91
8
 * ============================
92
8
 *
93
8
 *   ^ = might grow
94
8
 *
95
8
 *  49^ longest filename (pceplib/test/pcep_utils_double_linked_list_test.h)
96
8
 *   5^ highest line number (48530, bgpd/bgp_nb_config.c)
97
8
 *  65^ longest function name
98
8
 *      (lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_destroy)
99
8
 *  11  unique id ("XXXXX-XXXXX")
100
8
 *  10  EC ("4294967295" or "0xffffffff")
101
8
 *  35  ISO8601 TS at full length ("YYYY-MM-DD HH:MM:SS.NNNNNNNNN+ZZ:ZZ")
102
8
 * ---
103
8
 * 175
104
8
 *
105
8
 * rarely used (hopefully...):
106
8
 *  26^ FRR_VERSION ("10.10.10-dev-gffffffffffff")
107
8
 * ---
108
8
 * 201
109
8
 *
110
8
 * x16  highest number of format parameters currently
111
8
 *  40  estimate for hostname + 2*daemon + pid
112
8
 *
113
8
 * specific format overhead:
114
8
 *
115
8
 * RFC3164 - shorter than the others
116
8
 * RFC5424 - 175 + "<999>1 "=7 + 52 (location@50145) + 40 (host/...)
117
8
 *  rarely: + 65 + 26 (for [origin])
118
8
 *  args:  16 * (8 + per-arg (20?)) = ~448
119
8
 *
120
8
 * so without "args@", origin or (future) keywords, around 256 seems OK
121
8
 * with args@ and/or origin and/or keywords, 512 seems more reasonable
122
8
 *
123
8
 * but - note the code allocates this amount multiplied by the number of
124
8
 * messages in the incoming batch (minimum 3), this means short messages and
125
8
 * long messages smooth each other out.
126
8
 *
127
8
 * Since the code handles space-exceeded by grabbing a bunch of stack memory,
128
8
 * a reasonable middle ground estimate is desirable here, so ...
129
8
 * *drumroll*
130
8
 * let's go with 128 + args?128.  (remember the minimum 3 multiplier)
131
8
 *
132
8
 * low_space is the point where we don't try to fit another message in & just
133
8
 * submit what we have to the kernel.
134
8
 *
135
8
 * The zlog code only buffers debug & informational messages, so in production
136
8
 * usage most of the calls will be writing out only 1 message.  This makes
137
8
 * the min *3 multiplier quite useful.
138
8
 */
139
8
140
8
static inline size_t zlog_5424_bufsz(struct zlt_5424 *zte, size_t nmsgs,
141
8
             size_t *low_space)
142
8
{
143
0
  size_t ret = 128;
144
145
0
  if (zte->kw_args)
146
0
    ret += 128;
147
0
  *low_space = ret;
148
0
  return ret * MAX(nmsgs, 3);
149
0
}
150
151
struct state {
152
  struct fbuf *fbuf;
153
  struct iovec *iov;
154
};
155
156
/* stack-based keyword support is likely to bump this to 3 or 4 */
157
0
#define IOV_PER_MSG 2
158
_Static_assert(IOV_MAX >= IOV_PER_MSG,
159
         "this code won't work with IOV_MAX < IOV_PER_MSG");
160
161
/* the following functions are quite similar, but trying to merge them just
162
 * makes a big mess.  check the others when touching one.
163
 *
164
 *    timestamp keywords  hostname
165
 * RFC5424  ISO8601   yes   yes
166
 * RFC3164  RFC3164   no    yes
167
 * local  RFC3164   no    no
168
 * journald ISO8601(unused) yes   (unused)
169
 */
170
171
static size_t zlog_5424_one(struct zlt_5424 *zte, struct zlog_msg *msg,
172
          struct state *state)
173
0
{
174
0
  size_t textlen;
175
0
  struct fbuf *fbuf = state->fbuf;
176
0
  char *orig_pos = fbuf->pos;
177
0
  size_t need = 0;
178
0
  int prio = zlog_msg_prio(msg);
179
0
  intmax_t pid, tid;
180
181
0
  zlog_msg_pid(msg, &pid, &tid);
182
183
0
  need += bprintfrr(fbuf, "<%d>1 ", prio | zte->facility);
184
0
  need += zlog_msg_ts(msg, fbuf, zte->ts_flags);
185
0
  need += bprintfrr(fbuf, " %s %s %jd %.*s ", cmd_hostname_get() ?: "-",
186
0
        zlog_progname, pid, (int)(zlog_prefixsz - 2),
187
0
        zlog_prefix);
188
189
0
  if (zte->kw_version)
190
0
    need += bprintfrr(
191
0
      fbuf,
192
0
      "[origin enterpriseId=\"50145\" software=\"FRRouting\" swVersion=\"%s\"]",
193
0
      FRR_VERSION);
194
195
0
  const struct xref_logmsg *xref;
196
0
  struct xrefdata *xrefdata;
197
198
0
  need += bprintfrr(fbuf, "[location@50145 tid=\"%jd\"", tid);
199
0
  if (zlog_instance > 0)
200
0
    need += bprintfrr(fbuf, " instance=\"%d\"", zlog_instance);
201
202
0
  xref = zlog_msg_xref(msg);
203
0
  xrefdata = xref ? xref->xref.xrefdata : NULL;
204
0
  if (xrefdata) {
205
0
    if (zte->kw_uid)
206
0
      need += bprintfrr(fbuf, " id=\"%s\"", xrefdata->uid);
207
0
    if (zte->kw_ec && prio <= LOG_WARNING)
208
0
      need += bprintfrr(fbuf, " ec=\"%u\"", xref->ec);
209
0
    if (zte->kw_location)
210
0
      need += bprintfrr(
211
0
        fbuf, " file=\"%s\" line=\"%d\" func=\"%s\"",
212
0
        xref->xref.file, xref->xref.line,
213
0
        xref->xref.func);
214
0
  }
215
0
  need += bputch(fbuf, ']');
216
217
0
  size_t hdrlen, n_argpos;
218
0
  const struct fmt_outpos *argpos;
219
0
  const char *text;
220
221
0
  text = zlog_msg_text(msg, &textlen);
222
0
  zlog_msg_args(msg, &hdrlen, &n_argpos, &argpos);
223
224
0
  if (zte->kw_args && n_argpos) {
225
0
    need += bputs(fbuf, "[args@50145");
226
227
0
    for (size_t i = 0; i < n_argpos; i++) {
228
0
      int len = argpos[i].off_end - argpos[i].off_start;
229
230
0
      need += bprintfrr(fbuf, " arg%zu=%*pSQsq", i + 1, len,
231
0
            text + argpos[i].off_start);
232
0
    }
233
234
0
    need += bputch(fbuf, ']');
235
0
  }
236
237
0
  need += bputch(fbuf, ' ');
238
239
0
  if (orig_pos + need > fbuf->buf + fbuf->len) {
240
    /* not enough space in the buffer for headers.  the loop in
241
     * zlog_5424() will flush other messages that are already in
242
     * the buffer, grab a bigger buffer if needed, and try again.
243
     */
244
0
    fbuf->pos = orig_pos;
245
0
    return need;
246
0
  }
247
248
  /* NB: zlog_5424 below assumes we use max. IOV_PER_MSG iovs here */
249
0
  state->iov->iov_base = orig_pos;
250
0
  state->iov->iov_len = fbuf->pos - orig_pos;
251
0
  state->iov++;
252
253
0
  state->iov->iov_base = (char *)text + hdrlen;
254
0
  state->iov->iov_len = textlen - hdrlen + zte->use_nl;
255
0
  state->iov++;
256
0
  return 0;
257
0
}
258
259
static size_t zlog_3164_one(struct zlt_5424 *zte, struct zlog_msg *msg,
260
          struct state *state)
261
0
{
262
0
  size_t textlen;
263
0
  struct fbuf *fbuf = state->fbuf;
264
0
  char *orig_pos = fbuf->pos;
265
0
  size_t need = 0;
266
0
  int prio = zlog_msg_prio(msg);
267
0
  intmax_t pid, tid;
268
269
0
  zlog_msg_pid(msg, &pid, &tid);
270
271
0
  need += bprintfrr(fbuf, "<%d>", prio | zte->facility);
272
0
  need += zlog_msg_ts_3164(msg, fbuf, zte->ts_flags);
273
0
  if (zte->fmt != ZLOG_FMT_LOCAL) {
274
0
    need += bputch(fbuf, ' ');
275
0
    need += bputs(fbuf, cmd_hostname_get() ?: "-");
276
0
  }
277
0
  need += bprintfrr(fbuf, " %s[%jd]: ", zlog_progname, pid);
278
279
0
  if (orig_pos + need > fbuf->buf + fbuf->len) {
280
    /* not enough space in the buffer for headers.  loop in
281
     * zlog_5424() will flush other messages that are already in
282
     * the buffer, grab a bigger buffer if needed, and try again.
283
     */
284
0
    fbuf->pos = orig_pos;
285
0
    return need;
286
0
  }
287
288
  /* NB: zlog_5424 below assumes we use max. IOV_PER_MSG iovs here */
289
0
  state->iov->iov_base = orig_pos;
290
0
  state->iov->iov_len = fbuf->pos - orig_pos;
291
0
  state->iov++;
292
293
0
  state->iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
294
0
  state->iov->iov_len = textlen + zte->use_nl;
295
0
  state->iov++;
296
0
  return 0;
297
0
}
298
299
static size_t zlog_journald_one(struct zlt_5424 *zte, struct zlog_msg *msg,
300
        struct state *state)
301
0
{
302
0
  size_t textlen;
303
0
  struct fbuf *fbuf = state->fbuf;
304
0
  char *orig_pos = fbuf->pos;
305
0
  size_t need = 0;
306
0
  int prio = zlog_msg_prio(msg);
307
0
  intmax_t pid, tid;
308
309
0
  zlog_msg_pid(msg, &pid, &tid);
310
311
0
  need += bprintfrr(fbuf,
312
0
        "PRIORITY=%d\n"
313
0
        "SYSLOG_FACILITY=%d\n"
314
0
        "TID=%jd\n"
315
0
        "FRR_DAEMON=%s\n"
316
0
        "SYSLOG_TIMESTAMP=",
317
0
        prio, zte->facility, tid, zlog_progname);
318
0
  need += zlog_msg_ts(msg, fbuf, zte->ts_flags);
319
0
  need += bputch(fbuf, '\n');
320
0
  if (zlog_instance > 0)
321
0
    need += bprintfrr(fbuf, "FRR_INSTANCE=%d\n", zlog_instance);
322
323
0
  const struct xref_logmsg *xref;
324
0
  struct xrefdata *xrefdata;
325
326
0
  xref = zlog_msg_xref(msg);
327
0
  xrefdata = xref ? xref->xref.xrefdata : NULL;
328
0
  if (xrefdata) {
329
0
    if (zte->kw_uid && xrefdata->uid[0])
330
0
      need += bprintfrr(fbuf, "FRR_ID=%s\n", xrefdata->uid);
331
0
    if (zte->kw_ec && prio <= LOG_WARNING)
332
0
      need += bprintfrr(fbuf, "FRR_EC=%d\n", xref->ec);
333
0
    if (zte->kw_location)
334
0
      need += bprintfrr(fbuf,
335
0
            "CODE_FILE=%s\n"
336
0
            "CODE_LINE=%d\n"
337
0
            "CODE_FUNC=%s\n",
338
0
            xref->xref.file, xref->xref.line,
339
0
            xref->xref.func);
340
0
  }
341
342
0
  size_t hdrlen, n_argpos;
343
0
  const struct fmt_outpos *argpos;
344
0
  const char *text;
345
346
0
  text = zlog_msg_text(msg, &textlen);
347
0
  zlog_msg_args(msg, &hdrlen, &n_argpos, &argpos);
348
349
0
  if (zte->kw_args && n_argpos) {
350
0
    for (size_t i = 0; i < n_argpos; i++) {
351
0
      int len = argpos[i].off_end - argpos[i].off_start;
352
353
      /* rather than escape the value, we could use
354
       * journald's binary encoding, but that seems a bit
355
       * excessive/unnecessary.  99% of things we print here
356
       * will just output 1:1 with %pSE.
357
       */
358
0
      need += bprintfrr(fbuf, "FRR_ARG%zu=%*pSE\n", i + 1,
359
0
            len, text + argpos[i].off_start);
360
0
    }
361
0
  }
362
363
0
  need += bputs(fbuf, "MESSAGE=");
364
365
0
  if (orig_pos + need > fbuf->buf + fbuf->len) {
366
    /* not enough space in the buffer for headers.  loop in
367
     * zlog_5424() will flush other messages that are already in
368
     * the buffer, grab a bigger buffer if needed, and try again.
369
     */
370
0
    fbuf->pos = orig_pos;
371
0
    return need;
372
0
  }
373
374
  /* NB: zlog_5424 below assumes we use max. IOV_PER_MSG iovs here */
375
0
  state->iov->iov_base = orig_pos;
376
0
  state->iov->iov_len = fbuf->pos - orig_pos;
377
0
  state->iov++;
378
379
0
  state->iov->iov_base = (char *)text + hdrlen;
380
0
  state->iov->iov_len = textlen - hdrlen + 1;
381
0
  state->iov++;
382
0
  return 0;
383
0
}
384
385
static size_t zlog_one(struct zlt_5424 *zte, struct zlog_msg *msg,
386
           struct state *state)
387
0
{
388
0
  switch (zte->fmt) {
389
0
  case ZLOG_FMT_5424:
390
0
    return zlog_5424_one(zte, msg, state);
391
0
  case ZLOG_FMT_3164:
392
0
  case ZLOG_FMT_LOCAL:
393
0
    return zlog_3164_one(zte, msg, state);
394
0
  case ZLOG_FMT_JOURNALD:
395
0
    return zlog_journald_one(zte, msg, state);
396
0
  }
397
0
  return 0;
398
0
}
399
400
static void zlog_5424_err(struct zlt_5424 *zte, size_t count)
401
0
{
402
0
  if (!count) {
403
0
    zte->current_err = 0;
404
0
    return;
405
0
  }
406
407
  /* only the counter is atomic because otherwise it'd be meaningless */
408
0
  atomic_fetch_add_explicit(&zte->lost_msgs, count, memory_order_relaxed);
409
410
  /* these are non-atomic and can provide wrong results when read, but
411
   * since they're only for debugging / display, that's OK.
412
   */
413
0
  zte->current_err = zte->last_err = errno;
414
0
  monotime(&zte->last_err_ts);
415
0
}
416
417
static void zlog_5424(struct zlog_target *zt, struct zlog_msg *msgs[],
418
          size_t nmsgs)
419
0
{
420
0
  size_t i;
421
0
  struct zlt_5424 *zte = container_of(zt, struct zlt_5424, zt);
422
0
  int fd, ret;
423
0
  size_t niov = MIN(IOV_PER_MSG * nmsgs, IOV_MAX);
424
0
  struct iovec iov[niov], *iov_last = iov + niov;
425
0
  struct mmsghdr mmsg[zte->packets ? nmsgs : 1], *mpos = mmsg;
426
0
  size_t count = 0;
427
428
  /* refer to size estimate at top of file */
429
0
  size_t low_space;
430
0
  char hdr_buf[zlog_5424_bufsz(zte, nmsgs, &low_space)];
431
0
  struct fbuf hdr_pos = {
432
0
    .buf = hdr_buf,
433
0
    .pos = hdr_buf,
434
0
    .len = sizeof(hdr_buf),
435
0
  };
436
0
  struct state state = {
437
0
    .fbuf = &hdr_pos,
438
0
    .iov = iov,
439
0
  };
440
441
0
  fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
442
443
0
  memset(mmsg, 0, sizeof(mmsg));
444
0
  if (zte->sa_len) {
445
0
    for (i = 0; i < array_size(mmsg); i++) {
446
0
      mmsg[i].msg_hdr.msg_name = (struct sockaddr *)&zte->sa;
447
0
      mmsg[i].msg_hdr.msg_namelen = zte->sa_len;
448
0
    }
449
0
  }
450
0
  mmsg[0].msg_hdr.msg_iov = iov;
451
452
0
  for (i = 0; i < nmsgs; i++) {
453
0
    int prio = zlog_msg_prio(msgs[i]);
454
0
    size_t need = 0;
455
456
0
    if (prio <= zte->zt.prio_min) {
457
0
      if (zte->packets)
458
0
        mpos->msg_hdr.msg_iov = state.iov;
459
460
0
      need = zlog_one(zte, msgs[i], &state);
461
462
0
      if (zte->packets) {
463
0
        mpos->msg_hdr.msg_iovlen =
464
0
          state.iov - mpos->msg_hdr.msg_iov;
465
0
        mpos++;
466
0
      }
467
0
      count++;
468
0
    }
469
470
    /* clang-format off */
471
0
    if ((need
472
0
         || (size_t)(hdr_pos.buf + hdr_pos.len - hdr_pos.pos)
473
0
        < low_space
474
0
         || i + 1 == nmsgs
475
0
         || state.iov + IOV_PER_MSG > iov_last)
476
0
        && state.iov > iov) {
477
      /* clang-format on */
478
479
0
      if (zte->packets) {
480
0
        struct mmsghdr *sendpos;
481
482
0
        for (sendpos = mmsg; sendpos < mpos;) {
483
0
          ret = sendmmsg(fd, sendpos,
484
0
                   mpos - sendpos, 0);
485
0
          if (ret <= 0)
486
0
            break;
487
0
          sendpos += ret;
488
0
        }
489
0
        zlog_5424_err(zte, mpos - sendpos);
490
0
        mpos = mmsg;
491
0
      } else {
492
0
        if (!zte->sa_len)
493
0
          ret = writev(fd, iov, state.iov - iov);
494
0
        else {
495
0
          mpos->msg_hdr.msg_iovlen =
496
0
            state.iov - iov;
497
0
          ret = sendmsg(fd, &mpos->msg_hdr, 0);
498
0
        }
499
500
0
        if (ret < 0)
501
0
          zlog_5424_err(zte, count);
502
0
        else
503
0
          zlog_5424_err(zte, 0);
504
0
      }
505
506
0
      count = 0;
507
0
      hdr_pos.pos = hdr_buf;
508
0
      state.iov = iov;
509
0
    }
510
511
    /* if need == 0, we just put a message (or nothing) in the
512
     * buffer and are continuing for more to batch in a single
513
     * writev()
514
     */
515
0
    if (need == 0)
516
0
      continue;
517
518
0
    if (need && need <= sizeof(hdr_buf)) {
519
      /* don't need to allocate, just try this msg
520
       * again without other msgs already using up
521
       * buffer space
522
       */
523
0
      i--;
524
0
      continue;
525
0
    }
526
527
    /* need > sizeof(hdr_buf), need to grab some memory.  Taking
528
     * it off the stack is fine here.
529
     */
530
0
    char buf2[need];
531
0
    struct fbuf fbuf2 = {
532
0
      .buf = buf2,
533
0
      .pos = buf2,
534
0
      .len = sizeof(buf2),
535
0
    };
536
537
0
    state.fbuf = &fbuf2;
538
0
    need = zlog_one(zte, msgs[i], &state);
539
0
    assert(need == 0);
540
541
0
    if (!zte->sa_len)
542
0
      ret = writev(fd, iov, state.iov - iov);
543
0
    else {
544
0
      mpos->msg_hdr.msg_iovlen = state.iov - iov;
545
0
      ret = sendmsg(fd, &mpos->msg_hdr, 0);
546
0
    }
547
548
0
    if (ret < 0)
549
0
      zlog_5424_err(zte, 1);
550
0
    else
551
0
      zlog_5424_err(zte, 0);
552
553
0
    count = 0;
554
0
    state.fbuf = &hdr_pos;
555
0
    state.iov = iov;
556
0
    mpos = mmsg;
557
0
  }
558
559
0
  assert(state.iov == iov);
560
0
}
561
562
/* strftime(), gmtime_r() and localtime_r() aren't AS-Safe (they access locale
563
 * data), but we need an AS-Safe timestamp below :(
564
 */
565
static void gmtime_assafe(time_t ts, struct tm *res)
566
0
{
567
0
  res->tm_sec = ts % 60;
568
0
  ts /= 60;
569
0
  res->tm_min = ts % 60;
570
0
  ts /= 60;
571
0
  res->tm_hour = ts % 24;
572
0
  ts /= 24;
573
574
0
  ts -= 11017; /* start on 2020-03-01, 11017 days since 1970-01-01 */
575
576
  /* 1461 days = 3 regular years + 1 leap year
577
   * this works until 2100, which isn't a leap year
578
   *
579
   * struct tm.tm_year starts at 1900.
580
   */
581
0
  res->tm_year = 2000 - 1900 + 4 * (ts / 1461);
582
0
  ts = ts % 1461;
583
584
0
  if (ts == 1460) {
585
0
    res->tm_year += 4;
586
0
    res->tm_mon = 1;
587
0
    res->tm_mday = 29;
588
0
    return;
589
0
  }
590
0
  res->tm_year += ts / 365;
591
0
  ts %= 365;
592
593
  /* note we're starting in march like the romans did... */
594
0
  if (ts >= 306) /* Jan 1 of next year */
595
0
    res->tm_year++;
596
597
0
  static time_t months[13] = {
598
0
    0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 365,
599
0
  };
600
0
  const size_t month_max = array_size(months) - 1;
601
602
0
  for (size_t i = 0; i < month_max; i++) {
603
0
    if (ts < months[i + 1]) {
604
0
      res->tm_mon = ((i + 2) % 12);
605
0
      res->tm_mday = 1 + ts - months[i];
606
0
      break;
607
0
    }
608
0
  }
609
0
}
610
611
/* one of the greatest advantages of this logging target:  unlike syslog(),
612
 * which is not AS-Safe, we can send crashlogs to syslog here.
613
 */
614
static void zlog_5424_sigsafe(struct zlog_target *zt, const char *text,
615
            size_t len)
616
0
{
617
0
  static const char *const months_3164[12] = {
618
0
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
619
0
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
620
0
  };
621
622
0
  struct zlt_5424 *zte = container_of(zt, struct zlt_5424, zt);
623
0
  struct iovec iov[3], *iovp = iov;
624
0
  char buf[256];
625
0
  struct fbuf fbuf = {
626
0
    .buf = buf,
627
0
    .pos = buf,
628
0
    .len = sizeof(buf),
629
0
  };
630
0
  int fd;
631
0
  intmax_t pid = (intmax_t)getpid();
632
0
  struct tm tm;
633
634
0
  switch (zte->fmt) {
635
0
  case ZLOG_FMT_5424:
636
0
    gmtime_assafe(time(NULL), &tm);
637
0
    bprintfrr(
638
0
      &fbuf,
639
0
      "<%d>1 %04u-%02u-%02uT%02u:%02u:%02uZ - %s %jd %.*s  ",
640
0
      zte->facility | LOG_CRIT, tm.tm_year + 1900,
641
0
      tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
642
0
      tm.tm_sec, zlog_progname, pid, (int)(zlog_prefixsz - 2),
643
0
      zlog_prefix);
644
0
    break;
645
646
0
  case ZLOG_FMT_3164:
647
0
  case ZLOG_FMT_LOCAL:
648
    /* this will unfortuantely be wrong by the timezone offset
649
     * if the user selected non-UTC.  But not much we can do
650
     * about that...
651
     */
652
0
    gmtime_assafe(time(NULL), &tm);
653
0
    bprintfrr(&fbuf, "<%d>%3s %2u %02u:%02u:%02u %s%s[%jd]: ",
654
0
        zte->facility | LOG_CRIT, months_3164[tm.tm_mon],
655
0
        tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
656
0
        (zte->fmt == ZLOG_FMT_LOCAL) ? "" : "- ",
657
0
        zlog_progname, pid);
658
0
    break;
659
660
0
  case ZLOG_FMT_JOURNALD:
661
0
    bprintfrr(&fbuf,
662
0
        "PRIORITY=%d\n"
663
0
        "SYSLOG_FACILITY=%d\n"
664
0
        "FRR_DAEMON=%s\n"
665
0
        "MESSAGE=",
666
0
        LOG_CRIT, zte->facility, zlog_progname);
667
0
    break;
668
0
  }
669
670
0
  iovp->iov_base = fbuf.buf;
671
0
  iovp->iov_len = fbuf.pos - fbuf.buf;
672
0
  iovp++;
673
674
0
  iovp->iov_base = (char *)text;
675
0
  iovp->iov_len = len;
676
0
  iovp++;
677
678
0
  if (zte->use_nl) {
679
0
    iovp->iov_base = (char *)"\n";
680
0
    iovp->iov_len = 1;
681
0
    iovp++;
682
0
  }
683
684
0
  fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
685
686
0
  if (!zte->sa_len)
687
0
    writev(fd, iov, iovp - iov);
688
0
  else {
689
0
    struct msghdr mh = {};
690
691
0
    mh.msg_name = (struct sockaddr *)&zte->sa;
692
0
    mh.msg_namelen = zte->sa_len;
693
0
    mh.msg_iov = iov;
694
0
    mh.msg_iovlen = iovp - iov;
695
0
    sendmsg(fd, &mh, 0);
696
0
  }
697
0
}
698
699
/* housekeeping & configuration */
700
701
void zlog_5424_init(struct zlog_cfg_5424 *zcf)
702
0
{
703
0
  pthread_mutex_init(&zcf->cfg_mtx, NULL);
704
0
}
705
706
static void zlog_5424_target_free(struct zlt_5424 *zlt)
707
0
{
708
0
  if (!zlt)
709
0
    return;
710
711
0
  rcu_close(&zlt->head_close, zlt->fd);
712
0
  rcu_free(MTYPE_LOG_5424, zlt, zt.rcu_head);
713
0
}
714
715
void zlog_5424_fini(struct zlog_cfg_5424 *zcf, bool keepopen)
716
{
717
  if (keepopen)
718
    zcf->active = NULL;
719
720
  if (zcf->active) {
721
    struct zlt_5424 *ztf;
722
    struct zlog_target *zt;
723
724
    zt = zlog_target_replace(&zcf->active->zt, NULL);
725
    ztf = container_of(zt, struct zlt_5424, zt);
726
    zlog_5424_target_free(ztf);
727
  }
728
  pthread_mutex_destroy(&zcf->cfg_mtx);
729
}
730
731
static void zlog_5424_cycle(struct zlog_cfg_5424 *zcf, int fd)
732
0
{
733
0
  struct zlog_target *old;
734
0
  struct zlt_5424 *zlt = NULL, *oldt;
735
736
0
  if (fd >= 0) {
737
0
    struct zlog_target *zt;
738
739
    /* all of this is swapped in by zlog_target_replace() below,
740
     * the old target is RCU-freed afterwards.
741
     */
742
0
    zt = zlog_target_clone(MTYPE_LOG_5424, &zcf->active->zt,
743
0
               sizeof(*zlt));
744
0
    zlt = container_of(zt, struct zlt_5424, zt);
745
746
0
    zlt->fd = fd;
747
0
    zlt->kw_version = zcf->kw_version;
748
0
    zlt->kw_location = zcf->kw_location;
749
0
    zlt->kw_uid = zcf->kw_uid;
750
0
    zlt->kw_ec = zcf->kw_ec;
751
0
    zlt->kw_args = zcf->kw_args;
752
0
    zlt->use_nl = true;
753
0
    zlt->facility = zcf->facility;
754
755
    /* DGRAM & SEQPACKET = 1 log message per packet */
756
0
    zlt->packets = (zcf->sock_type == SOCK_DGRAM) ||
757
0
             (zcf->sock_type == SOCK_SEQPACKET);
758
0
    zlt->sa = zcf->sa;
759
0
    zlt->sa_len = zcf->sa_len;
760
0
    zlt->fmt = zcf->fmt;
761
0
    zlt->zt.prio_min = zcf->prio_min;
762
0
    zlt->zt.logfn = zlog_5424;
763
0
    zlt->zt.logfn_sigsafe = zlog_5424_sigsafe;
764
765
0
    switch (zcf->fmt) {
766
0
    case ZLOG_FMT_5424:
767
0
    case ZLOG_FMT_JOURNALD:
768
0
      zlt->ts_flags = zcf->ts_flags;
769
0
      zlt->ts_flags &= ZLOG_TS_PREC | ZLOG_TS_UTC;
770
0
      zlt->ts_flags |= ZLOG_TS_ISO8601;
771
0
      break;
772
0
    case ZLOG_FMT_3164:
773
0
    case ZLOG_FMT_LOCAL:
774
0
      zlt->ts_flags = zcf->ts_flags & ZLOG_TS_UTC;
775
0
      if (zlt->packets)
776
0
        zlt->use_nl = false;
777
0
      break;
778
0
    }
779
0
  }
780
781
0
  old = zcf->active ? &zcf->active->zt : NULL;
782
0
  old = zlog_target_replace(old, &zlt->zt);
783
0
  zcf->active = zlt;
784
785
  /* oldt->fd == fd happens for zlog_5424_apply_meta() */
786
0
  oldt = container_of(old, struct zlt_5424, zt);
787
0
  if (oldt && oldt->fd != (unsigned int)fd)
788
0
    rcu_close(&oldt->head_close, oldt->fd);
789
0
  rcu_free(MTYPE_LOG_5424, oldt, zt.rcu_head);
790
0
}
791
792
static void zlog_5424_reconnect(struct event *t)
793
0
{
794
0
  struct zlog_cfg_5424 *zcf = EVENT_ARG(t);
795
0
  int fd = EVENT_FD(t);
796
0
  char dummy[256];
797
0
  ssize_t ret;
798
0
799
0
  if (zcf->active) {
800
0
    ret = read(fd, dummy, sizeof(dummy));
801
0
    if (ret > 0) {
802
0
      /* logger is sending us something?!?! */
803
0
      event_add_read(t->master, zlog_5424_reconnect, zcf, fd,
804
0
               &zcf->t_reconnect);
805
0
      return;
806
0
    }
807
0
808
0
    if (ret == 0)
809
0
      zlog_warn("logging socket %pSE closed by peer",
810
0
          zcf->filename);
811
0
    else
812
0
      zlog_warn("logging socket %pSE error: %m",
813
0
          zcf->filename);
814
0
  }
815
0
816
0
  /* do NOT close() anything here;  other threads may still be writing
817
0
   * and their messages need to be lost rather than end up on a random
818
0
   * other fd that got reassigned the same number, like a BGP session!
819
0
   */
820
0
  fd = zlog_5424_open(zcf, -1);
821
0
822
0
  frr_with_mutex (&zcf->cfg_mtx) {
823
0
    zlog_5424_cycle(zcf, fd);
824
0
  }
825
0
}
826
827
static int zlog_5424_unix(struct sockaddr_un *suna, int sock_type)
828
0
{
829
0
  int fd;
830
0
  int size = 1 * 1024 * 1024, prev_size;
831
0
  socklen_t opt_size;
832
0
  int save_errno;
833
834
0
  fd = socket(AF_UNIX, sock_type, 0);
835
0
  if (fd < 0)
836
0
    return -1;
837
838
0
  if (connect(fd, (struct sockaddr *)suna, sizeof(*suna))) {
839
    /* zlog_5424_open() will print the error for connect() */
840
0
    save_errno = errno;
841
0
    close(fd);
842
0
    errno = save_errno;
843
0
    return -1;
844
0
  }
845
846
0
  opt_size = sizeof(prev_size);
847
0
  if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &prev_size, &opt_size))
848
0
    return fd;
849
850
  /* setsockopt_so_sendbuf() logs on error; we don't really care that
851
   * much here.  Also, never shrink the buffer below the initial size.
852
   */
853
0
  while (size > prev_size &&
854
0
         setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) == -1)
855
0
    size /= 2;
856
857
0
  return fd;
858
0
}
859
860
static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type)
861
0
{
862
0
  int fd = -1;
863
0
  int flags = 0;
864
0
  int err;
865
0
  socklen_t optlen;
866
0
  bool do_chown = false;
867
0
  bool need_reconnect = false;
868
0
  static const int unix_types[] = {
869
0
    SOCK_STREAM,
870
0
    SOCK_SEQPACKET,
871
0
    SOCK_DGRAM,
872
0
  };
873
0
  struct sockaddr_un sa;
874
875
0
  zcf->sock_type = -1;
876
0
  zcf->sa_len = 0;
877
878
0
  switch (zcf->dst) {
879
0
  case ZLOG_5424_DST_NONE:
880
0
    break;
881
882
0
  case ZLOG_5424_DST_FD:
883
0
    fd = dup(zcf->fd);
884
885
0
    optlen = sizeof(sock_type);
886
0
    if (!getsockopt(fd, SOL_SOCKET, SO_TYPE, &sock_type, &optlen)) {
887
0
      zcf->sock_type = sock_type;
888
0
      need_reconnect = (zcf->sock_type != SOCK_DGRAM);
889
0
    }
890
0
    break;
891
892
0
  case ZLOG_5424_DST_FIFO:
893
0
    if (!zcf->filename)
894
0
      break;
895
896
0
    if (!zcf->file_nocreate) {
897
0
      frr_with_privs (lib_privs) {
898
0
        mode_t prevmask;
899
900
0
        prevmask = umask(0777 ^ zcf->file_mode);
901
0
        err = mkfifo(zcf->filename, 0666);
902
0
        umask(prevmask);
903
0
      }
904
0
      if (err == 0)
905
0
        do_chown = true;
906
0
      else if (errno != EEXIST)
907
0
        break;
908
0
    }
909
910
0
    flags = O_NONBLOCK;
911
    /* fallthru */
912
913
0
  case ZLOG_5424_DST_FILE:
914
0
    if (!zcf->filename)
915
0
      break;
916
917
0
    frr_with_privs (lib_privs) {
918
0
      fd = open(zcf->filename, flags | O_WRONLY | O_APPEND |
919
0
               O_CLOEXEC | O_NOCTTY);
920
0
    }
921
0
    if (fd >= 0)
922
0
      break;
923
0
    if (zcf->file_nocreate || flags) {
924
0
      flog_err_sys(EC_LIB_SYSTEM_CALL,
925
0
             "could not open log file %pSE: %m",
926
0
             zcf->filename);
927
0
      break;
928
0
    }
929
930
0
    frr_with_privs (lib_privs) {
931
0
      mode_t prevmask;
932
933
0
      prevmask = umask(0777 ^ zcf->file_mode);
934
0
      fd = open(zcf->filename,
935
0
          O_WRONLY | O_APPEND | O_CLOEXEC | O_NOCTTY |
936
0
            O_CREAT | O_EXCL,
937
0
          zcf->file_mode);
938
0
      umask(prevmask);
939
0
    }
940
0
    if (fd >= 0) {
941
0
      do_chown = true;
942
0
      break;
943
0
    }
944
945
0
    frr_with_privs (lib_privs) {
946
0
      fd = open(zcf->filename,
947
0
          O_WRONLY | O_APPEND | O_CLOEXEC | O_NOCTTY);
948
0
    }
949
0
    if (fd >= 0)
950
0
      break;
951
952
0
    flog_err_sys(EC_LIB_SYSTEM_CALL,
953
0
           "could not open or create log file %pSE: %m",
954
0
           zcf->filename);
955
0
    break;
956
957
0
  case ZLOG_5424_DST_UNIX:
958
0
    if (!zcf->filename)
959
0
      break;
960
961
0
    memset(&sa, 0, sizeof(sa));
962
0
    sa.sun_family = AF_UNIX;
963
0
    strlcpy(sa.sun_path, zcf->filename, sizeof(sa.sun_path));
964
965
    /* check if ZLOG_5424_DST_FD needs a touch when changing
966
     * something here.  the user can pass in a pre-opened unix
967
     * socket through a fd at startup.
968
     */
969
0
    frr_with_privs (lib_privs) {
970
0
      if (sock_type != -1)
971
0
        fd = zlog_5424_unix(&sa, sock_type);
972
0
      else {
973
0
        for (size_t i = 0; i < array_size(unix_types);
974
0
             i++) {
975
0
          fd = zlog_5424_unix(&sa, unix_types[i]);
976
0
          if (fd != -1) {
977
0
            zcf->sock_type = unix_types[i];
978
0
            break;
979
0
          }
980
0
        }
981
0
      }
982
0
    }
983
0
    if (fd == -1) {
984
0
      zcf->sock_type = -1;
985
986
0
      flog_err_sys(
987
0
        EC_LIB_SYSTEM_CALL,
988
0
        "could not connect to log unix path %pSE: %m",
989
0
        zcf->filename);
990
0
      need_reconnect = true;
991
0
    } else {
992
      /* datagram sockets are connectionless, restarting
993
       * the receiver may lose some packets but will resume
994
       * working afterwards without any action from us.
995
       */
996
0
      need_reconnect = (zcf->sock_type != SOCK_DGRAM);
997
0
    }
998
0
    break;
999
0
  }
1000
1001
  /* viable on both DST_FD and DST_UNIX path */
1002
0
  if (zcf->sock_type == SOCK_DGRAM) {
1003
0
    zcf->sa_len = sizeof(zcf->sa);
1004
0
    if (getpeername(fd, (struct sockaddr *)&zcf->sa,
1005
0
        &zcf->sa_len)) {
1006
0
      flog_err_sys(
1007
0
        EC_LIB_SYSTEM_CALL,
1008
0
        "could not get remote address for log socket.  logging may break if log receiver restarts.");
1009
0
      zcf->sa_len = 0;
1010
0
    }
1011
0
  }
1012
1013
0
  if (do_chown) {
1014
0
    uid_t uid = zcf->file_uid;
1015
0
    gid_t gid = zcf->file_gid;
1016
1017
0
    if (uid != (uid_t)-1 || gid != (gid_t)-1) {
1018
0
      frr_with_privs (lib_privs) {
1019
0
        err = fchown(fd, uid, gid);
1020
0
      }
1021
0
      if (err)
1022
0
        flog_err_sys(
1023
0
          EC_LIB_SYSTEM_CALL,
1024
0
          "failed to chown() log file %pSE: %m",
1025
0
          zcf->filename);
1026
0
    }
1027
0
  }
1028
1029
0
  if (need_reconnect) {
1030
0
    assert(zcf->master);
1031
1032
0
    if (fd != -1) {
1033
0
      event_add_read(zcf->master, zlog_5424_reconnect, zcf,
1034
0
               fd, &zcf->t_reconnect);
1035
0
      zcf->reconn_backoff_cur = zcf->reconn_backoff;
1036
1037
0
    } else {
1038
0
      event_add_timer_msec(zcf->master, zlog_5424_reconnect,
1039
0
               zcf, zcf->reconn_backoff_cur,
1040
0
               &zcf->t_reconnect);
1041
1042
0
      zcf->reconn_backoff_cur += zcf->reconn_backoff_cur / 2;
1043
0
      if (zcf->reconn_backoff_cur > zcf->reconn_backoff_max)
1044
0
        zcf->reconn_backoff_cur =
1045
0
          zcf->reconn_backoff_max;
1046
0
    }
1047
0
  }
1048
1049
0
  return fd;
1050
0
}
1051
1052
bool zlog_5424_apply_dst(struct zlog_cfg_5424 *zcf)
1053
0
{
1054
0
  int fd = -1;
1055
1056
0
  event_cancel(&zcf->t_reconnect);
1057
1058
0
  if (zcf->prio_min != ZLOG_DISABLED)
1059
0
    fd = zlog_5424_open(zcf, -1);
1060
1061
0
  frr_with_mutex (&zcf->cfg_mtx) {
1062
0
    zlog_5424_cycle(zcf, fd);
1063
0
  }
1064
0
  return fd != -1;
1065
0
}
1066
1067
1068
bool zlog_5424_apply_meta(struct zlog_cfg_5424 *zcf)
1069
0
{
1070
0
  frr_with_mutex (&zcf->cfg_mtx) {
1071
0
    if (zcf->active)
1072
0
      zlog_5424_cycle(zcf, zcf->active->fd);
1073
0
  }
1074
1075
0
  return true;
1076
0
}
1077
1078
void zlog_5424_state(struct zlog_cfg_5424 *zcf, size_t *lost_msgs,
1079
         int *last_errno, bool *stale_errno, struct timeval *err_ts)
1080
0
{
1081
0
  if (lost_msgs)
1082
0
    *lost_msgs =
1083
0
      zcf->active
1084
0
        ? atomic_load_explicit(&zcf->active->lost_msgs,
1085
0
                   memory_order_relaxed)
1086
0
        : 0;
1087
0
  if (last_errno)
1088
0
    *last_errno = zcf->active ? zcf->active->last_err : 0;
1089
0
  if (stale_errno)
1090
0
    *stale_errno = zcf->active ? !zcf->active->current_err : 0;
1091
0
  if (err_ts && zcf->active)
1092
0
    *err_ts = zcf->active->last_err_ts;
1093
0
}
1094
1095
struct rcu_close_rotate {
1096
  struct rcu_head_close head_close;
1097
  struct rcu_head head_self;
1098
};
1099
1100
bool zlog_5424_rotate(struct zlog_cfg_5424 *zcf)
1101
0
{
1102
0
  struct rcu_close_rotate *rcr;
1103
0
  int fd;
1104
1105
0
  frr_with_mutex (&zcf->cfg_mtx) {
1106
0
    if (!zcf->active)
1107
0
      return true;
1108
1109
0
    event_cancel(&zcf->t_reconnect);
1110
1111
    /* need to retain the socket type because it also influences
1112
     * other fields (packets) and we can't atomically swap these
1113
     * out.  But we really want the atomic swap so we neither lose
1114
     * nor duplicate log messages, particularly for file targets.
1115
     *
1116
     * (zlog_5424_apply_dst / zlog_target_replace will cause
1117
     * duplicate log messages if another thread logs something
1118
     * while we're right in the middle of swapping out the log
1119
     * target)
1120
     */
1121
0
    fd = zlog_5424_open(zcf, zcf->sock_type);
1122
0
    if (fd < 0)
1123
0
      return false;
1124
1125
0
    fd = atomic_exchange_explicit(&zcf->active->fd,
1126
0
                (uint_fast32_t)fd,
1127
0
                memory_order_relaxed);
1128
0
  }
1129
1130
0
  rcr = XCALLOC(MTYPE_LOG_5424_ROTATE, sizeof(*rcr));
1131
0
  rcu_close(&rcr->head_close, fd);
1132
0
  rcu_free(MTYPE_LOG_5424_ROTATE, rcr, head_self);
1133
1134
  return true;
1135
0
}