Coverage Report

Created: 2026-04-27 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/zlog_5424_cli.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Copyright (C) 2021  David Lamparter for NetDEF, Inc.
4
 */
5
6
#include "zebra.h"
7
#include "zlog_5424.h"
8
9
#include <sys/types.h>
10
#include <pwd.h>
11
#include <grp.h>
12
13
#include "lib/command.h"
14
#include "lib/libfrr.h"
15
#include "lib/log_vty.h"
16
17
8
DEFINE_MTYPE_STATIC(LOG, LOG_5424_CONFIG, "extended syslog config");
18
8
DEFINE_MTYPE_STATIC(LOG, LOG_5424_DATA, "extended syslog config items");
19
8
20
8
static int target_cmp(const struct zlog_cfg_5424_user *a,
21
8
          const struct zlog_cfg_5424_user *b)
22
8
{
23
0
  return strcmp(a->name, b->name);
24
0
}
25
26
DECLARE_RBTREE_UNIQ(targets, struct zlog_cfg_5424_user, targets_item,
27
        target_cmp);
28
DEFINE_QOBJ_TYPE(zlog_cfg_5424_user);
29
30
static struct targets_head targets = INIT_RBTREE_UNIQ(targets);
31
static struct event_loop *log_5424_master;
32
33
static void clear_dst(struct zlog_cfg_5424_user *cfg);
34
35
struct log_option {
36
  const char *name;
37
  ptrdiff_t offs;
38
  bool dflt;
39
};
40
41
/* clang-format off */
42
static struct log_option log_opts[] = {
43
  { "code-location",  offsetof(struct zlog_cfg_5424, kw_location) },
44
  { "version",    offsetof(struct zlog_cfg_5424, kw_version) },
45
  { "unique-id",    offsetof(struct zlog_cfg_5424, kw_uid), true },
46
  { "error-category", offsetof(struct zlog_cfg_5424, kw_ec), true },
47
  { "format-args",  offsetof(struct zlog_cfg_5424, kw_args) },
48
  {},
49
};
50
51
0
#define DFLT_TS_FLAGS   (6 | ZLOG_TS_UTC)
52
0
#define DFLT_FACILITY   LOG_DAEMON
53
0
#define DFLT_PRIO_MIN   LOG_DEBUG
54
/* clang-format on */
55
56
enum unix_special {
57
  SPECIAL_NONE = 0,
58
  SPECIAL_SYSLOG,
59
  SPECIAL_JOURNALD,
60
};
61
62
static struct zlog_cfg_5424_user *log_5424_alloc(const char *name)
63
0
{
64
0
  struct zlog_cfg_5424_user *cfg;
65
66
0
  cfg = XCALLOC(MTYPE_LOG_5424_CONFIG, sizeof(*cfg));
67
0
  cfg->name = XSTRDUP(MTYPE_LOG_5424_DATA, name);
68
69
0
  cfg->cfg.master = log_5424_master;
70
0
  cfg->cfg.kw_location = true;
71
0
  cfg->cfg.kw_version = false;
72
0
  cfg->cfg.facility = DFLT_FACILITY;
73
0
  cfg->cfg.prio_min = DFLT_PRIO_MIN;
74
0
  cfg->cfg.ts_flags = DFLT_TS_FLAGS;
75
0
  clear_dst(cfg);
76
77
0
  for (struct log_option *opt = log_opts; opt->name; opt++) {
78
0
    bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
79
0
    *ptr = opt->dflt;
80
0
  }
81
82
0
  zlog_5424_init(&cfg->cfg);
83
84
0
  QOBJ_REG(cfg, zlog_cfg_5424_user);
85
0
  targets_add(&targets, cfg);
86
0
  return cfg;
87
0
}
88
89
static void log_5424_free(struct zlog_cfg_5424_user *cfg, bool keepopen)
90
0
{
91
0
  targets_del(&targets, cfg);
92
0
  QOBJ_UNREG(cfg);
93
94
0
  zlog_5424_fini(&cfg->cfg, keepopen);
95
0
  clear_dst(cfg);
96
97
0
  XFREE(MTYPE_LOG_5424_DATA, cfg->filename);
98
0
  XFREE(MTYPE_LOG_5424_DATA, cfg->name);
99
0
  XFREE(MTYPE_LOG_5424_CONFIG, cfg);
100
0
}
101
102
static void clear_dst(struct zlog_cfg_5424_user *cfg)
103
0
{
104
0
  XFREE(MTYPE_LOG_5424_DATA, cfg->filename);
105
0
  cfg->cfg.filename = cfg->filename;
106
107
0
  XFREE(MTYPE_LOG_5424_DATA, cfg->file_user);
108
0
  XFREE(MTYPE_LOG_5424_DATA, cfg->file_group);
109
0
  XFREE(MTYPE_LOG_5424_DATA, cfg->envvar);
110
111
0
  cfg->cfg.fd = -1;
112
0
  cfg->cfg.file_uid = -1;
113
0
  cfg->cfg.file_gid = -1;
114
0
  cfg->cfg.file_mode = LOGFILE_MASK & 0666;
115
0
  cfg->cfg.file_nocreate = false;
116
0
  cfg->cfg.dst = ZLOG_5424_DST_NONE;
117
0
}
118
119
static int reconf_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
120
0
{
121
0
  if (!cfg->reconf_dst && !cfg->reconf_meta && vty->type != VTY_FILE)
122
0
    vty_out(vty,
123
0
      "%% Changes will be applied when exiting this config block\n");
124
125
0
  cfg->reconf_dst = true;
126
0
  return CMD_SUCCESS;
127
0
}
128
129
static int reconf_meta(struct zlog_cfg_5424_user *cfg, struct vty *vty)
130
0
{
131
0
  if (!cfg->reconf_dst && !cfg->reconf_meta && vty->type != VTY_FILE)
132
0
    vty_out(vty,
133
0
      "%% Changes will be applied when exiting this config block\n");
134
135
0
  cfg->reconf_meta = true;
136
0
  return CMD_SUCCESS;
137
0
}
138
139
static int reconf_clear_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
140
0
{
141
0
  if (cfg->cfg.dst == ZLOG_5424_DST_NONE)
142
0
    return CMD_SUCCESS;
143
144
0
  clear_dst(cfg);
145
0
  return reconf_dst(cfg, vty);
146
0
}
147
148
#include "lib/zlog_5424_cli_clippy.c"
149
150
DEFPY_NOSH(log_5424_target,
151
     log_5424_target_cmd,
152
     "log extended-syslog EXTLOGNAME",
153
     "Logging control\n"
154
     "Extended RFC5424 syslog (including file targets)\n"
155
     "Name identifying this syslog target\n")
156
0
{
157
0
  struct zlog_cfg_5424_user *cfg, ref;
158
159
0
  ref.name = (char *)extlogname;
160
0
  cfg = targets_find(&targets, &ref);
161
162
0
  if (!cfg)
163
0
    cfg = log_5424_alloc(extlogname);
164
165
0
  VTY_PUSH_CONTEXT(EXTLOG_NODE, cfg);
166
0
  return CMD_SUCCESS;
167
0
}
168
169
DEFPY(no_log_5424_target,
170
      no_log_5424_target_cmd,
171
      "no log extended-syslog EXTLOGNAME",
172
      NO_STR
173
      "Logging control\n"
174
      "Extended RFC5424 syslog (including file targets)\n"
175
      "Name identifying this syslog target\n")
176
0
{
177
0
  struct zlog_cfg_5424_user *cfg, ref;
178
179
0
  ref.name = (char *)extlogname;
180
0
  cfg = targets_find(&targets, &ref);
181
182
0
  if (!cfg) {
183
0
    vty_out(vty, "%% No extended syslog target named \"%s\"\n",
184
0
      extlogname);
185
0
    return CMD_WARNING;
186
0
  }
187
188
0
  log_5424_free(cfg, false);
189
0
  return CMD_SUCCESS;
190
0
}
191
192
/*    "format <rfc3164|rfc5424|local-syslogd|journald>$fmt" */
193
#define FORMAT_HELP                                                            \
194
  "Select log message formatting\n"                                      \
195
  "RFC3164 (legacy) syslog\n"                                            \
196
  "RFC5424 (modern) syslog, supports structured data (default)\n"        \
197
  "modified RFC3164 without hostname for local syslogd (/dev/log)\n"     \
198
  "journald (systemd log) native format\n"                               \
199
  /* end */
200
201
static enum zlog_5424_format log_5424_fmt(const char *fmt,
202
            enum zlog_5424_format dflt)
203
0
{
204
0
  if (!fmt)
205
0
    return dflt;
206
0
  else if (!strcmp(fmt, "rfc5424"))
207
0
    return ZLOG_FMT_5424;
208
0
  else if (!strcmp(fmt, "rfc3164"))
209
0
    return ZLOG_FMT_3164;
210
0
  else if (!strcmp(fmt, "local-syslogd"))
211
0
    return ZLOG_FMT_LOCAL;
212
0
  else if (!strcmp(fmt, "journald"))
213
0
    return ZLOG_FMT_JOURNALD;
214
215
0
  return dflt;
216
0
}
217
218
DEFPY(log_5424_destination_file,
219
      log_5424_destination_file_cmd,
220
      "[no] destination file$type PATH "
221
    "[create$create [{user WORD|group WORD|mode PERMS}]"
222
    "|no-create$nocreate] "
223
    "[format <rfc3164|rfc5424|local-syslogd|journald>$fmt]",
224
      NO_STR
225
      "Log destination setup\n"
226
      "Log to file\n"
227
      "Path to destination\n"
228
      "Create file if it does not exist\n"
229
      "Set file owner\n"
230
      "User name\n"
231
      "Set file group\n"
232
      "Group name\n"
233
      "Set permissions\n"
234
      "File permissions (octal)\n"
235
      "Do not create file if it does not exist\n"
236
      FORMAT_HELP)
237
0
{
238
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
239
0
  enum zlog_5424_dst dst;
240
0
  bool reconf = true, warn_perm = false;
241
0
  char *prev_user, *prev_group;
242
0
  mode_t perm_val = LOGFILE_MASK & 0666;
243
0
  enum zlog_5424_format fmtv;
244
245
0
  if (no)
246
0
    return reconf_clear_dst(cfg, vty);
247
248
0
  fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
249
250
0
  if (perms) {
251
0
    char *errp = (char *)perms;
252
253
0
    perm_val = strtoul(perms, &errp, 8);
254
0
    if (*errp || errp == perms || perm_val == 0 ||
255
0
        (perm_val & ~0666)) {
256
0
      vty_out(vty, "%% Invalid permissions value \"%s\"\n",
257
0
        perms);
258
0
      return CMD_WARNING;
259
0
    }
260
0
  }
261
262
0
  dst = (strcmp(type, "fifo") == 0) ? ZLOG_5424_DST_FIFO
263
0
            : ZLOG_5424_DST_FILE;
264
265
0
  if (cfg->filename && !strcmp(path, cfg->filename) &&
266
0
      dst == cfg->cfg.dst && cfg->cfg.active && cfg->cfg.fmt == fmtv)
267
0
    reconf = false;
268
269
  /* keep for compare below */
270
0
  prev_user = cfg->file_user;
271
0
  prev_group = cfg->file_group;
272
0
  cfg->file_user = NULL;
273
0
  cfg->file_group = NULL;
274
275
0
  clear_dst(cfg);
276
277
0
  cfg->filename = XSTRDUP(MTYPE_LOG_5424_DATA, path);
278
0
  cfg->cfg.dst = dst;
279
0
  cfg->cfg.filename = cfg->filename;
280
0
  cfg->cfg.fmt = fmtv;
281
282
0
  if (nocreate)
283
0
    cfg->cfg.file_nocreate = true;
284
0
  else {
285
0
    if (user) {
286
0
      struct passwd *pwent;
287
288
0
      warn_perm |= (prev_user && strcmp(user, prev_user));
289
0
      cfg->file_user = XSTRDUP(MTYPE_LOG_5424_DATA, user);
290
291
0
      errno = 0;
292
0
      pwent = getpwnam(user);
293
0
      if (!pwent)
294
0
        vty_out(vty,
295
0
          "%% Could not look up user \"%s\" (%s), file owner will be left untouched!\n",
296
0
          user,
297
0
          errno ? safe_strerror(errno)
298
0
                : "No entry by this user name");
299
0
      else
300
0
        cfg->cfg.file_uid = pwent->pw_uid;
301
0
    }
302
0
    if (group) {
303
0
      struct group *grent;
304
305
0
      warn_perm |= (prev_group && strcmp(group, prev_group));
306
0
      cfg->file_group = XSTRDUP(MTYPE_LOG_5424_DATA, group);
307
308
0
      errno = 0;
309
0
      grent = getgrnam(group);
310
0
      if (!grent)
311
0
        vty_out(vty,
312
0
          "%% Could not look up group \"%s\" (%s), file group will be left untouched!\n",
313
0
          group,
314
0
          errno ? safe_strerror(errno)
315
0
                : "No entry by this group name");
316
0
      else
317
0
        cfg->cfg.file_gid = grent->gr_gid;
318
0
    }
319
0
  }
320
0
  XFREE(MTYPE_LOG_5424_DATA, prev_user);
321
0
  XFREE(MTYPE_LOG_5424_DATA, prev_group);
322
323
0
  if (cfg->cfg.file_uid != (uid_t)-1 || cfg->cfg.file_gid != (gid_t)-1) {
324
0
    struct stat st;
325
326
0
    if (stat(cfg->filename, &st) == 0) {
327
0
      warn_perm |= (st.st_uid != cfg->cfg.file_uid);
328
0
      warn_perm |= (st.st_gid != cfg->cfg.file_gid);
329
0
    }
330
0
  }
331
0
  if (warn_perm)
332
0
    vty_out(vty,
333
0
      "%% Warning: ownership and permission bits are only applied when creating\n"
334
0
      "%%          log files.  Use system tools to change existing files.\n"
335
0
      "%%          FRR may also be missing necessary privileges to set these.\n");
336
337
0
  if (reconf)
338
0
    return reconf_dst(cfg, vty);
339
340
0
  return CMD_SUCCESS;
341
0
}
342
343
/* FIFOs are for legacy /dev/log implementations;  using this is very much not
344
 * recommended since it can unexpectedly block in logging calls.  Also the fd
345
 * would need to be reopened when the process at the other end restarts.  None
346
 * of this is handled - use at your own caution.  It's _HIDDEN for a purpose.
347
 */
348
ALIAS_HIDDEN(log_5424_destination_file,
349
       log_5424_destination_fifo_cmd,
350
      "[no] destination fifo$type PATH "
351
    "[create$create [{owner WORD|group WORD|permissions PERMS}]"
352
    "|no-create$nocreate] "
353
    "[format <rfc3164|rfc5424|local-syslogd|journald>$fmt]",
354
      NO_STR
355
      "Log destination setup\n"
356
      "Log to filesystem FIFO\n"
357
      "Path to destination\n"
358
      "Create file if it does not exist\n"
359
      "Set file owner\n"
360
      "User name\n"
361
      "Set file group\n"
362
      "Group name\n"
363
      "Set permissions\n"
364
      "File permissions (octal)\n"
365
      "Do not create file if it does not exist\n"
366
      FORMAT_HELP)
367
368
static int dst_unix(struct vty *vty, const char *no, const char *path,
369
        enum zlog_5424_format fmt, enum unix_special special)
370
0
{
371
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
372
373
0
  if (no)
374
0
    return reconf_clear_dst(cfg, vty);
375
376
0
  cfg->unix_special = special;
377
378
0
  if (cfg->cfg.dst == ZLOG_5424_DST_UNIX && cfg->filename &&
379
0
      !strcmp(path, cfg->filename) && cfg->cfg.active &&
380
0
      cfg->cfg.fmt == fmt)
381
0
    return CMD_SUCCESS;
382
383
0
  clear_dst(cfg);
384
385
0
  cfg->filename = XSTRDUP(MTYPE_LOG_5424_DATA, path);
386
0
  cfg->cfg.dst = ZLOG_5424_DST_UNIX;
387
0
  cfg->cfg.filename = cfg->filename;
388
0
  cfg->cfg.fmt = fmt;
389
390
0
  cfg->cfg.reconn_backoff = 25;
391
0
  cfg->cfg.reconn_backoff_cur = 25;
392
0
  cfg->cfg.reconn_backoff_max = 10000;
393
0
  return reconf_dst(cfg, vty);
394
0
}
395
396
DEFPY(log_5424_destination_unix,
397
      log_5424_destination_unix_cmd,
398
      "[no] destination unix PATH "
399
     "[format <rfc3164|rfc5424|local-syslogd|journald>$fmt]",
400
      NO_STR
401
      "Log destination setup\n"
402
      "Log to unix socket\n"
403
      "Unix socket path\n"
404
      FORMAT_HELP)
405
0
{
406
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
407
0
  enum zlog_5424_format fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
408
409
0
  return dst_unix(vty, no, path, fmtv, SPECIAL_NONE);
410
0
}
411
412
DEFPY(log_5424_destination_journald,
413
      log_5424_destination_journald_cmd,
414
      "[no] destination journald",
415
      NO_STR
416
      "Log destination setup\n"
417
      "Log directly to systemd's journald\n")
418
0
{
419
0
  return dst_unix(vty, no, "/run/systemd/journal/socket",
420
0
      ZLOG_FMT_JOURNALD, SPECIAL_JOURNALD);
421
0
}
422
423
#if defined(__FreeBSD_version) && (__FreeBSD_version >= 1200061)
424
#define ZLOG_FMT_DEV_LOG  ZLOG_FMT_5424
425
#elif defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 500000000)
426
#define ZLOG_FMT_DEV_LOG  ZLOG_FMT_5424
427
#else
428
0
#define ZLOG_FMT_DEV_LOG  ZLOG_FMT_LOCAL
429
#endif
430
431
DEFPY(log_5424_destination_syslog,
432
      log_5424_destination_syslog_cmd,
433
      "[no] destination syslog [supports-rfc5424]$supp5424",
434
      NO_STR
435
      "Log destination setup\n"
436
      "Log directly to syslog\n"
437
      "Use RFC5424 format (please refer to documentation)\n")
438
0
{
439
0
  int format = supp5424 ? ZLOG_FMT_5424 : ZLOG_FMT_DEV_LOG;
440
441
  /* unfortunately, there is no way to detect 5424 support */
442
0
  return dst_unix(vty, no, "/dev/log", format, SPECIAL_SYSLOG);
443
0
}
444
445
/* could add something like
446
 *   "destination <udp|tcp>$proto <A.B.C.D|X:X::X:X> (1-65535)$port"
447
 * here, but there are 2 reasons not to do that:
448
 *
449
 *  - each FRR daemon would open its own connection, there's no system level
450
 *    aggregation.  That's the system's syslogd's job.  It likely also
451
 *    supports directing & filtering log messages with configurable rules.
452
 *  - we're likely not going to support DTLS or TLS for more secure logging;
453
 *    adding this would require a considerable amount of additional config
454
 *    and an entire TLS library to begin with.  A proper syslogd implements
455
 *    all of this, why reinvent the wheel?
456
 */
457
458
DEFPY(log_5424_destination_fd,
459
      log_5424_destination_fd_cmd,
460
      "[no] destination <fd <(0-63)$fd|envvar WORD>|stdout$fd1|stderr$fd2>"
461
     "[format <rfc3164|rfc5424|local-syslogd|journald>$fmt]",
462
      NO_STR
463
      "Log destination setup\n"
464
      "Log to pre-opened file descriptor\n"
465
      "File descriptor number (must be open at startup)\n"
466
      "Read file descriptor number from environment variable\n"
467
      "Environment variable name\n"
468
      "Log to standard output\n"
469
      "Log to standard error output\n"
470
      FORMAT_HELP)
471
0
{
472
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
473
0
  bool envvar_problem = false;
474
0
  enum zlog_5424_format fmtv;
475
476
0
  if (no)
477
0
    return reconf_clear_dst(cfg, vty);
478
479
0
  fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
480
481
0
  if (envvar) {
482
0
    char *envval;
483
484
0
    envval = getenv(envvar);
485
0
    if (!envval)
486
0
      envvar_problem = true;
487
0
    else {
488
0
      char *errp = envval;
489
490
0
      fd = strtoul(envval, &errp, 0);
491
0
      if (errp == envval || *errp)
492
0
        envvar_problem = true;
493
0
    }
494
495
0
    if (envvar_problem)
496
0
      fd = -1;
497
0
  } else if (fd1)
498
0
    fd = 1;
499
0
  else if (fd2)
500
0
    fd = 2;
501
502
0
  if (cfg->cfg.dst == ZLOG_5424_DST_FD && cfg->cfg.fd == fd &&
503
0
      cfg->cfg.active && cfg->cfg.fmt == fmtv)
504
0
    return CMD_SUCCESS;
505
506
0
  clear_dst(cfg);
507
508
0
  cfg->cfg.dst = ZLOG_5424_DST_FD;
509
0
  cfg->cfg.fd = fd;
510
0
  cfg->cfg.fmt = fmtv;
511
0
  if (envvar)
512
0
    cfg->envvar = XSTRDUP(MTYPE_LOG_5424_DATA, envvar);
513
514
0
  if (envvar_problem)
515
0
    vty_out(vty,
516
0
      "%% environment variable \"%s\" not present or invalid.\n",
517
0
      envvar);
518
0
  if (!frr_is_startup_fd(fd))
519
0
    vty_out(vty,
520
0
      "%% file descriptor %d was not open when this process was started\n",
521
0
      (int)fd);
522
0
  if (envvar_problem || !frr_is_startup_fd(fd))
523
0
    vty_out(vty,
524
0
      "%% configuration will be saved but has no effect currently\n");
525
526
0
  return reconf_dst(cfg, vty);
527
0
}
528
529
DEFPY(log_5424_destination_none,
530
      log_5424_destination_none_cmd,
531
      "[no] destination [none]",
532
      NO_STR
533
      "Log destination setup\n"
534
      "Deconfigure destination\n")
535
0
{
536
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
537
538
0
  return reconf_clear_dst(cfg, vty);
539
0
}
540
541
/* end of destinations */
542
543
DEFPY(log_5424_prio,
544
      log_5424_prio_cmd,
545
      "[no] priority <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>$levelarg",
546
      NO_STR
547
      "Set minimum message priority to include for this target\n"
548
      LOG_LEVEL_DESC)
549
0
{
550
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
551
0
  int prio_min = log_level_match(levelarg);
552
553
0
  if (prio_min == cfg->cfg.prio_min)
554
0
    return CMD_SUCCESS;
555
556
0
  cfg->cfg.prio_min = prio_min;
557
0
  return reconf_meta(cfg, vty);
558
0
}
559
560
DEFPY(log_5424_facility,
561
      log_5424_facility_cmd,
562
      "[no] facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>$facilityarg",
563
      NO_STR
564
      "Set syslog facility to use\n"
565
      LOG_FACILITY_DESC)
566
0
{
567
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
568
0
  int facility = facility_match(facilityarg);
569
570
0
  if (cfg->cfg.facility == facility)
571
0
    return CMD_SUCCESS;
572
573
0
  cfg->cfg.facility = facility;
574
0
  return reconf_meta(cfg, vty);
575
0
}
576
577
DEFPY(log_5424_meta,
578
      log_5424_meta_cmd,
579
      "[no] structured-data <code-location|version|unique-id|error-category|format-args>$option",
580
      NO_STR
581
      "Select structured data (key/value pairs) to include in each message\n"
582
      "FRR source code location\n"
583
      "FRR version\n"
584
      "Unique message identifier (XXXXX-XXXXX)\n"
585
      "Error category (EC numeric)\n"
586
      "Individual formatted log message arguments\n")
587
0
{
588
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
589
0
  bool val = !no, *ptr;
590
0
  struct log_option *opt = log_opts;
591
592
0
  while (opt->name && strcmp(opt->name, option))
593
0
    opt++;
594
0
  if (!opt->name)
595
0
    return CMD_WARNING;
596
597
0
  ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
598
0
  if (*ptr == val)
599
0
    return CMD_SUCCESS;
600
601
0
  *ptr = val;
602
0
  return reconf_meta(cfg, vty);
603
0
}
604
605
DEFPY(log_5424_ts_prec,
606
      log_5424_ts_prec_cmd,
607
      "[no] timestamp precision (0-9)",
608
      NO_STR
609
      "Timestamp options\n"
610
      "Number of sub-second digits to include\n"
611
      "Number of sub-second digits to include\n")
612
0
{
613
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
614
0
  uint32_t ts_flags = cfg->cfg.ts_flags;
615
616
0
  ts_flags &= ~ZLOG_TS_PREC;
617
0
  if (no)
618
0
    ts_flags |= DFLT_TS_FLAGS & ZLOG_TS_PREC;
619
0
  else
620
0
    ts_flags |= precision;
621
622
0
  if (ts_flags == cfg->cfg.ts_flags)
623
0
    return CMD_SUCCESS;
624
625
0
  cfg->cfg.ts_flags = ts_flags;
626
0
  return reconf_meta(cfg, vty);
627
0
}
628
629
DEFPY(log_5424_ts_local,
630
      log_5424_ts_local_cmd,
631
      "[no] timestamp local-time",
632
      NO_STR
633
      "Timestamp options\n"
634
      "Use local system time zone rather than UTC\n")
635
0
{
636
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
637
0
  uint32_t ts_flags = cfg->cfg.ts_flags;
638
639
0
  ts_flags &= ~ZLOG_TS_UTC;
640
0
  if (no)
641
0
    ts_flags |= DFLT_TS_FLAGS & ZLOG_TS_UTC;
642
0
  else
643
0
    ts_flags |= (~DFLT_TS_FLAGS) & ZLOG_TS_UTC;
644
645
0
  if (ts_flags == cfg->cfg.ts_flags)
646
0
    return CMD_SUCCESS;
647
648
0
  cfg->cfg.ts_flags = ts_flags;
649
0
  return reconf_meta(cfg, vty);
650
0
}
651
652
static int log_5424_node_exit(struct vty *vty)
653
0
{
654
0
  VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
655
656
0
  if ((cfg->reconf_dst || cfg->reconf_meta) && vty->type != VTY_FILE)
657
0
    vty_out(vty, "%% applying changes.\n");
658
659
0
  if (cfg->reconf_dst)
660
0
    zlog_5424_apply_dst(&cfg->cfg);
661
0
  else if (cfg->reconf_meta)
662
0
    zlog_5424_apply_meta(&cfg->cfg);
663
664
0
  cfg->reconf_dst = cfg->reconf_meta = false;
665
0
  return 1;
666
0
}
667
668
static int log_5424_config_write(struct vty *vty)
669
0
{
670
0
  struct zlog_cfg_5424_user *cfg;
671
672
0
  frr_each (targets, &targets, cfg) {
673
0
    const char *fmt_str = "";
674
675
0
    vty_out(vty, "log extended %s\n", cfg->name);
676
677
0
    switch (cfg->cfg.fmt) {
678
0
    case ZLOG_FMT_5424:
679
0
      fmt_str = " format rfc5424";
680
0
      break;
681
0
    case ZLOG_FMT_3164:
682
0
      fmt_str = " format rfc3164";
683
0
      break;
684
0
    case ZLOG_FMT_LOCAL:
685
0
      fmt_str = " format local-syslogd";
686
0
      break;
687
0
    case ZLOG_FMT_JOURNALD:
688
0
      fmt_str = " format journald";
689
0
      break;
690
0
    }
691
692
0
    switch (cfg->cfg.dst) {
693
0
    case ZLOG_5424_DST_NONE:
694
0
      vty_out(vty, " ! no destination configured\n");
695
0
      break;
696
697
0
    case ZLOG_5424_DST_FD:
698
0
      if (cfg->cfg.fmt == ZLOG_FMT_5424)
699
0
        fmt_str = "";
700
701
0
      if (cfg->envvar)
702
0
        vty_out(vty, " destination fd envvar %s%s\n",
703
0
          cfg->envvar, fmt_str);
704
0
      else if (cfg->cfg.fd == 1)
705
0
        vty_out(vty, " destination stdout%s\n",
706
0
          fmt_str);
707
0
      else if (cfg->cfg.fd == 2)
708
0
        vty_out(vty, " destination stderr%s\n",
709
0
          fmt_str);
710
0
      else
711
0
        vty_out(vty, " destination fd %d%s\n",
712
0
          cfg->cfg.fd, fmt_str);
713
0
      break;
714
715
0
    case ZLOG_5424_DST_FILE:
716
0
    case ZLOG_5424_DST_FIFO:
717
0
      if (cfg->cfg.fmt == ZLOG_FMT_5424)
718
0
        fmt_str = "";
719
720
0
      vty_out(vty, " destination %s %s",
721
0
        (cfg->cfg.dst == ZLOG_5424_DST_FIFO) ? "fifo"
722
0
                     : "file",
723
0
        cfg->filename);
724
725
0
      if (cfg->cfg.file_nocreate)
726
0
        vty_out(vty, " no-create");
727
0
      else if (cfg->file_user || cfg->file_group ||
728
0
         cfg->cfg.file_mode != (LOGFILE_MASK & 0666)) {
729
0
        vty_out(vty, " create");
730
731
0
        if (cfg->file_user)
732
0
          vty_out(vty, " user %s",
733
0
            cfg->file_user);
734
0
        if (cfg->file_group)
735
0
          vty_out(vty, " group %s",
736
0
            cfg->file_group);
737
0
        if (cfg->cfg.file_mode != (LOGFILE_MASK & 0666))
738
0
          vty_out(vty, " mode %04o",
739
0
            cfg->cfg.file_mode);
740
0
      }
741
0
      vty_out(vty, "%s\n", fmt_str);
742
0
      break;
743
744
0
    case ZLOG_5424_DST_UNIX:
745
0
      switch (cfg->unix_special) {
746
0
      case SPECIAL_NONE:
747
0
        vty_out(vty, " destination unix %s%s\n",
748
0
          cfg->filename, fmt_str);
749
0
        break;
750
0
      case SPECIAL_SYSLOG:
751
0
        if (cfg->cfg.fmt == ZLOG_FMT_DEV_LOG)
752
0
          vty_out(vty, " destination syslog\n");
753
0
        else
754
0
          vty_out(vty,
755
0
            " destination syslog supports-rfc5424\n");
756
0
        break;
757
0
      case SPECIAL_JOURNALD:
758
0
        vty_out(vty, " destination journald\n");
759
0
        break;
760
0
      }
761
0
      break;
762
0
    }
763
764
0
    if (cfg->cfg.prio_min != LOG_DEBUG)
765
0
      vty_out(vty, " priority %s\n",
766
0
        zlog_priority_str(cfg->cfg.prio_min));
767
0
    if (cfg->cfg.facility != DFLT_FACILITY)
768
0
      vty_out(vty, " facility %s\n",
769
0
        facility_name(cfg->cfg.facility));
770
771
0
    for (struct log_option *opt = log_opts; opt->name; opt++) {
772
0
      bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
773
774
0
      if (*ptr != opt->dflt)
775
0
        vty_out(vty, " %sstructured-data %s\n",
776
0
          *ptr ? "" : "no ", opt->name);
777
0
    }
778
779
0
    if ((cfg->cfg.ts_flags ^ DFLT_TS_FLAGS) & ZLOG_TS_PREC)
780
0
      vty_out(vty, " timestamp precision %u\n",
781
0
        cfg->cfg.ts_flags & ZLOG_TS_PREC);
782
783
0
    if ((cfg->cfg.ts_flags ^ DFLT_TS_FLAGS) & ZLOG_TS_UTC) {
784
0
      if (cfg->cfg.ts_flags & ZLOG_TS_UTC)
785
0
        vty_out(vty, " no timestamp local-time\n");
786
0
      else
787
0
        vty_out(vty, " timestamp local-time\n");
788
0
    }
789
790
0
    vty_out(vty, "!\n");
791
0
  }
792
0
  return 0;
793
0
}
794
795
static int log_5424_show(struct vty *vty)
796
0
{
797
0
  struct zlog_cfg_5424_user *cfg;
798
799
0
  frr_each (targets, &targets, cfg) {
800
0
    vty_out(vty, "\nExtended log target %pSQq\n", cfg->name);
801
802
0
    switch (cfg->cfg.dst) {
803
0
    case ZLOG_5424_DST_NONE:
804
0
      vty_out(vty,
805
0
        "  Inactive (no destination configured)\n");
806
0
      break;
807
808
0
    case ZLOG_5424_DST_FD:
809
0
      if (cfg->envvar)
810
0
        vty_out(vty,
811
0
          "  logging to fd %d from environment variable %pSE\n",
812
0
          cfg->cfg.fd, cfg->envvar);
813
0
      else if (cfg->cfg.fd == 1)
814
0
        vty_out(vty, "  logging to stdout\n");
815
0
      else if (cfg->cfg.fd == 2)
816
0
        vty_out(vty, "  logging to stderr\n");
817
0
      else
818
0
        vty_out(vty, "  logging to fd %d\n",
819
0
          cfg->cfg.fd);
820
0
      break;
821
822
0
    case ZLOG_5424_DST_FILE:
823
0
    case ZLOG_5424_DST_FIFO:
824
0
    case ZLOG_5424_DST_UNIX:
825
0
      vty_out(vty, "  logging to %s: %pSE\n",
826
0
        (cfg->cfg.dst == ZLOG_5424_DST_FIFO) ? "fifo"
827
0
        : (cfg->cfg.dst == ZLOG_5424_DST_UNIX)
828
0
          ? "unix socket"
829
0
          : "file",
830
0
        cfg->filename);
831
0
      break;
832
0
    }
833
834
0
    vty_out(vty, "  log level: %s, facility: %s\n",
835
0
      zlog_priority_str(cfg->cfg.prio_min),
836
0
      facility_name(cfg->cfg.facility));
837
838
0
    bool any_meta = false, first = true;
839
840
0
    for (struct log_option *opt = log_opts; opt->name; opt++) {
841
0
      bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
842
843
0
      any_meta |= *ptr;
844
0
    }
845
846
0
    if (!any_meta)
847
0
      continue;
848
849
0
    switch (cfg->cfg.fmt) {
850
0
    case ZLOG_FMT_5424:
851
0
    case ZLOG_FMT_JOURNALD:
852
0
      vty_out(vty, "  structured data: ");
853
854
0
      for (struct log_option *opt = log_opts; opt->name;
855
0
           opt++) {
856
0
        bool *ptr = (bool *)(((char *)&cfg->cfg) +
857
0
                 opt->offs);
858
859
0
        if (*ptr) {
860
0
          vty_out(vty, "%s%s", first ? "" : ", ",
861
0
            opt->name);
862
0
          first = false;
863
0
        }
864
0
      }
865
0
      break;
866
867
0
    case ZLOG_FMT_3164:
868
0
    case ZLOG_FMT_LOCAL:
869
0
      vty_out(vty,
870
0
        "  structured data is not supported by the selected format\n");
871
0
      break;
872
0
    }
873
874
0
    vty_out(vty, "\n");
875
876
0
    size_t lost_msgs;
877
0
    int last_errno;
878
0
    bool stale_errno;
879
0
    struct timeval err_ts;
880
0
    int64_t since;
881
882
0
    zlog_5424_state(&cfg->cfg, &lost_msgs, &last_errno,
883
0
        &stale_errno, &err_ts);
884
0
    vty_out(vty, "  number of lost messages: %zu\n", lost_msgs);
885
886
0
    if (last_errno == 0)
887
0
      since = 0;
888
0
    else
889
0
      since = monotime_since(&err_ts, NULL);
890
0
    vty_out(vty,
891
0
      "  last error: %s (%lld.%06llds ago, currently %s)\n",
892
0
      last_errno ? safe_strerror(last_errno) : "none",
893
0
      since / 1000000LL, since % 1000000LL,
894
0
      stale_errno ? "OK" : "erroring");
895
0
  }
896
0
  return 0;
897
0
}
898
899
static struct cmd_node extlog_node = {
900
  .name = "extended",
901
  .node = EXTLOG_NODE,
902
  .parent_node = CONFIG_NODE,
903
  .prompt = "%s(config-ext-log)# ",
904
905
  .config_write = log_5424_config_write,
906
  .node_exit = log_5424_node_exit,
907
};
908
909
static void log_5424_autocomplete(vector comps, struct cmd_token *token)
910
0
{
911
0
  struct zlog_cfg_5424_user *cfg;
912
913
0
  frr_each (targets, &targets, cfg)
914
0
    vector_set(comps, XSTRDUP(MTYPE_COMPLETION, cfg->name));
915
0
}
916
917
static const struct cmd_variable_handler log_5424_var_handlers[] = {
918
  {.tokenname = "EXTLOGNAME", .completions = log_5424_autocomplete},
919
  {.completions = NULL},
920
};
921
922
void log_5424_cmd_init(void)
923
4
{
924
4
  hook_register(zlog_cli_show, log_5424_show);
925
926
4
  cmd_variable_handler_register(log_5424_var_handlers);
927
928
  /* CLI commands. */
929
4
  install_node(&extlog_node);
930
4
  install_default(EXTLOG_NODE);
931
932
4
  install_element(CONFIG_NODE, &log_5424_target_cmd);
933
4
  install_element(CONFIG_NODE, &no_log_5424_target_cmd);
934
935
4
  install_element(EXTLOG_NODE, &log_5424_destination_file_cmd);
936
4
  install_element(EXTLOG_NODE, &log_5424_destination_fifo_cmd);
937
4
  install_element(EXTLOG_NODE, &log_5424_destination_unix_cmd);
938
4
  install_element(EXTLOG_NODE, &log_5424_destination_journald_cmd);
939
4
  install_element(EXTLOG_NODE, &log_5424_destination_syslog_cmd);
940
4
  install_element(EXTLOG_NODE, &log_5424_destination_fd_cmd);
941
942
4
  install_element(EXTLOG_NODE, &log_5424_meta_cmd);
943
4
  install_element(EXTLOG_NODE, &log_5424_prio_cmd);
944
4
  install_element(EXTLOG_NODE, &log_5424_facility_cmd);
945
4
  install_element(EXTLOG_NODE, &log_5424_ts_prec_cmd);
946
4
  install_element(EXTLOG_NODE, &log_5424_ts_local_cmd);
947
4
}
948
949
/* hooks */
950
951
static int log_5424_early_init(struct event_loop *master);
952
static int log_5424_rotate(void);
953
static int log_5424_fini(void);
954
955
__attribute__((_CONSTRUCTOR(475))) static void zlog_5424_startup_init(void)
956
8
{
957
8
  hook_register(frr_early_init, log_5424_early_init);
958
8
  hook_register(zlog_rotate, log_5424_rotate);
959
8
  hook_register(frr_fini, log_5424_fini);
960
8
}
961
962
static int log_5424_early_init(struct event_loop *master)
963
0
{
964
0
  log_5424_master = master;
965
966
0
  return 0;
967
0
}
968
969
static int log_5424_rotate(void)
970
0
{
971
0
  struct zlog_cfg_5424_user *cfg;
972
973
0
  frr_each (targets, &targets, cfg)
974
0
    if (!zlog_5424_rotate(&cfg->cfg))
975
0
      zlog_err(
976
0
        "log rotation on extended log target %s failed",
977
0
        cfg->name);
978
979
0
  return 0;
980
0
}
981
982
static int log_5424_fini(void)
983
0
{
984
0
  struct zlog_cfg_5424_user *cfg;
985
986
0
  while ((cfg = targets_pop(&targets)))
987
0
    log_5424_free(cfg, true);
988
989
0
  log_5424_master = NULL;
990
991
0
  return 0;
992
0
}