]> git.proxmox.com Git - mirror_frr.git/blob - lib/zlog_5424_cli.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / lib / zlog_5424_cli.c
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 DEFINE_MTYPE_STATIC(LOG, LOG_5424_CONFIG, "extended syslog config");
18 DEFINE_MTYPE_STATIC(LOG, LOG_5424_DATA, "extended syslog config items");
19
20 static int target_cmp(const struct zlog_cfg_5424_user *a,
21 const struct zlog_cfg_5424_user *b)
22 {
23 return strcmp(a->name, b->name);
24 }
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 thread_master *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 #define DFLT_TS_FLAGS (6 | ZLOG_TS_UTC)
52 #define DFLT_FACILITY LOG_DAEMON
53 #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 {
64 struct zlog_cfg_5424_user *cfg;
65
66 cfg = XCALLOC(MTYPE_LOG_5424_CONFIG, sizeof(*cfg));
67 cfg->name = XSTRDUP(MTYPE_LOG_5424_DATA, name);
68
69 cfg->cfg.master = log_5424_master;
70 cfg->cfg.kw_location = true;
71 cfg->cfg.kw_version = false;
72 cfg->cfg.facility = DFLT_FACILITY;
73 cfg->cfg.prio_min = DFLT_PRIO_MIN;
74 cfg->cfg.ts_flags = DFLT_TS_FLAGS;
75 clear_dst(cfg);
76
77 for (struct log_option *opt = log_opts; opt->name; opt++) {
78 bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
79 *ptr = opt->dflt;
80 }
81
82 zlog_5424_init(&cfg->cfg);
83
84 QOBJ_REG(cfg, zlog_cfg_5424_user);
85 targets_add(&targets, cfg);
86 return cfg;
87 }
88
89 static void log_5424_free(struct zlog_cfg_5424_user *cfg, bool keepopen)
90 {
91 targets_del(&targets, cfg);
92 QOBJ_UNREG(cfg);
93
94 zlog_5424_fini(&cfg->cfg, keepopen);
95 clear_dst(cfg);
96
97 XFREE(MTYPE_LOG_5424_DATA, cfg->filename);
98 XFREE(MTYPE_LOG_5424_DATA, cfg->name);
99 XFREE(MTYPE_LOG_5424_CONFIG, cfg);
100 }
101
102 static void clear_dst(struct zlog_cfg_5424_user *cfg)
103 {
104 XFREE(MTYPE_LOG_5424_DATA, cfg->filename);
105 cfg->cfg.filename = cfg->filename;
106
107 XFREE(MTYPE_LOG_5424_DATA, cfg->file_user);
108 XFREE(MTYPE_LOG_5424_DATA, cfg->file_group);
109 XFREE(MTYPE_LOG_5424_DATA, cfg->envvar);
110
111 cfg->cfg.fd = -1;
112 cfg->cfg.file_uid = -1;
113 cfg->cfg.file_gid = -1;
114 cfg->cfg.file_mode = LOGFILE_MASK & 0666;
115 cfg->cfg.file_nocreate = false;
116 cfg->cfg.dst = ZLOG_5424_DST_NONE;
117 }
118
119 static int reconf_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
120 {
121 if (!cfg->reconf_dst && !cfg->reconf_meta && vty->type != VTY_FILE)
122 vty_out(vty,
123 "%% Changes will be applied when exiting this config block\n");
124
125 cfg->reconf_dst = true;
126 return CMD_SUCCESS;
127 }
128
129 static int reconf_meta(struct zlog_cfg_5424_user *cfg, struct vty *vty)
130 {
131 if (!cfg->reconf_dst && !cfg->reconf_meta && vty->type != VTY_FILE)
132 vty_out(vty,
133 "%% Changes will be applied when exiting this config block\n");
134
135 cfg->reconf_meta = true;
136 return CMD_SUCCESS;
137 }
138
139 static int reconf_clear_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
140 {
141 if (cfg->cfg.dst == ZLOG_5424_DST_NONE)
142 return CMD_SUCCESS;
143
144 clear_dst(cfg);
145 return reconf_dst(cfg, vty);
146 }
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 {
157 struct zlog_cfg_5424_user *cfg, ref;
158
159 ref.name = (char *)extlogname;
160 cfg = targets_find(&targets, &ref);
161
162 if (!cfg)
163 cfg = log_5424_alloc(extlogname);
164
165 VTY_PUSH_CONTEXT(EXTLOG_NODE, cfg);
166 return CMD_SUCCESS;
167 }
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 {
177 struct zlog_cfg_5424_user *cfg, ref;
178
179 ref.name = (char *)extlogname;
180 cfg = targets_find(&targets, &ref);
181
182 if (!cfg) {
183 vty_out(vty, "%% No extended syslog target named \"%s\"\n",
184 extlogname);
185 return CMD_WARNING;
186 }
187
188 log_5424_free(cfg, false);
189 return CMD_SUCCESS;
190 }
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 {
204 if (!fmt)
205 return dflt;
206 else if (!strcmp(fmt, "rfc5424"))
207 return ZLOG_FMT_5424;
208 else if (!strcmp(fmt, "rfc3164"))
209 return ZLOG_FMT_3164;
210 else if (!strcmp(fmt, "local-syslogd"))
211 return ZLOG_FMT_LOCAL;
212 else if (!strcmp(fmt, "journald"))
213 return ZLOG_FMT_JOURNALD;
214
215 return dflt;
216 }
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 {
238 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
239 enum zlog_5424_dst dst;
240 bool reconf = true, warn_perm = false;
241 char *prev_user, *prev_group;
242 mode_t perm_val = LOGFILE_MASK & 0666;
243 enum zlog_5424_format fmtv;
244
245 if (no)
246 return reconf_clear_dst(cfg, vty);
247
248 fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
249
250 if (perms) {
251 char *errp = (char *)perms;
252
253 perm_val = strtoul(perms, &errp, 8);
254 if (*errp || errp == perms || perm_val == 0 ||
255 (perm_val & ~0666)) {
256 vty_out(vty, "%% Invalid permissions value \"%s\"\n",
257 perms);
258 return CMD_WARNING;
259 }
260 }
261
262 dst = (strcmp(type, "fifo") == 0) ? ZLOG_5424_DST_FIFO
263 : ZLOG_5424_DST_FILE;
264
265 if (cfg->filename && !strcmp(path, cfg->filename) &&
266 dst == cfg->cfg.dst && cfg->cfg.active && cfg->cfg.fmt == fmtv)
267 reconf = false;
268
269 /* keep for compare below */
270 prev_user = cfg->file_user;
271 prev_group = cfg->file_group;
272 cfg->file_user = NULL;
273 cfg->file_group = NULL;
274
275 clear_dst(cfg);
276
277 cfg->filename = XSTRDUP(MTYPE_LOG_5424_DATA, path);
278 cfg->cfg.dst = dst;
279 cfg->cfg.filename = cfg->filename;
280 cfg->cfg.fmt = fmtv;
281
282 if (nocreate)
283 cfg->cfg.file_nocreate = true;
284 else {
285 if (user) {
286 struct passwd *pwent;
287
288 warn_perm |= (prev_user && strcmp(user, prev_user));
289 cfg->file_user = XSTRDUP(MTYPE_LOG_5424_DATA, user);
290
291 errno = 0;
292 pwent = getpwnam(user);
293 if (!pwent)
294 vty_out(vty,
295 "%% Could not look up user \"%s\" (%s), file owner will be left untouched!\n",
296 user,
297 errno ? safe_strerror(errno)
298 : "No entry by this user name");
299 else
300 cfg->cfg.file_uid = pwent->pw_uid;
301 }
302 if (group) {
303 struct group *grent;
304
305 warn_perm |= (prev_group && strcmp(group, prev_group));
306 cfg->file_group = XSTRDUP(MTYPE_LOG_5424_DATA, group);
307
308 errno = 0;
309 grent = getgrnam(group);
310 if (!grent)
311 vty_out(vty,
312 "%% Could not look up group \"%s\" (%s), file group will be left untouched!\n",
313 group,
314 errno ? safe_strerror(errno)
315 : "No entry by this group name");
316 else
317 cfg->cfg.file_gid = grent->gr_gid;
318 }
319 }
320 XFREE(MTYPE_LOG_5424_DATA, prev_user);
321 XFREE(MTYPE_LOG_5424_DATA, prev_group);
322
323 if (cfg->cfg.file_uid != (uid_t)-1 || cfg->cfg.file_gid != (gid_t)-1) {
324 struct stat st;
325
326 if (stat(cfg->filename, &st) == 0) {
327 warn_perm |= (st.st_uid != cfg->cfg.file_uid);
328 warn_perm |= (st.st_gid != cfg->cfg.file_gid);
329 }
330 }
331 if (warn_perm)
332 vty_out(vty,
333 "%% Warning: ownership and permission bits are only applied when creating\n"
334 "%% log files. Use system tools to change existing files.\n"
335 "%% FRR may also be missing necessary privileges to set these.\n");
336
337 if (reconf)
338 return reconf_dst(cfg, vty);
339
340 return CMD_SUCCESS;
341 }
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 {
371 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
372
373 if (no)
374 return reconf_clear_dst(cfg, vty);
375
376 cfg->unix_special = special;
377
378 if (cfg->cfg.dst == ZLOG_5424_DST_UNIX && cfg->filename &&
379 !strcmp(path, cfg->filename) && cfg->cfg.active &&
380 cfg->cfg.fmt == fmt)
381 return CMD_SUCCESS;
382
383 clear_dst(cfg);
384
385 cfg->filename = XSTRDUP(MTYPE_LOG_5424_DATA, path);
386 cfg->cfg.dst = ZLOG_5424_DST_UNIX;
387 cfg->cfg.filename = cfg->filename;
388 cfg->cfg.fmt = fmt;
389
390 cfg->cfg.reconn_backoff = 25;
391 cfg->cfg.reconn_backoff_cur = 25;
392 cfg->cfg.reconn_backoff_max = 10000;
393 return reconf_dst(cfg, vty);
394 }
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 {
406 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
407 enum zlog_5424_format fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
408
409 return dst_unix(vty, no, path, fmtv, SPECIAL_NONE);
410 }
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 {
419 return dst_unix(vty, no, "/run/systemd/journal/socket",
420 ZLOG_FMT_JOURNALD, SPECIAL_JOURNALD);
421 }
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 #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 {
439 int format = supp5424 ? ZLOG_FMT_5424 : ZLOG_FMT_DEV_LOG;
440
441 /* unfortunately, there is no way to detect 5424 support */
442 return dst_unix(vty, no, "/dev/log", format, SPECIAL_SYSLOG);
443 }
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 {
472 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
473 bool envvar_problem = false;
474 enum zlog_5424_format fmtv;
475
476 if (no)
477 return reconf_clear_dst(cfg, vty);
478
479 fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
480
481 if (envvar) {
482 char *envval;
483
484 envval = getenv(envvar);
485 if (!envval)
486 envvar_problem = true;
487 else {
488 char *errp = envval;
489
490 fd = strtoul(envval, &errp, 0);
491 if (errp == envval || *errp)
492 envvar_problem = true;
493 }
494
495 if (envvar_problem)
496 fd = -1;
497 } else if (fd1)
498 fd = 1;
499 else if (fd2)
500 fd = 2;
501
502 if (cfg->cfg.dst == ZLOG_5424_DST_FD && cfg->cfg.fd == fd &&
503 cfg->cfg.active && cfg->cfg.fmt == fmtv)
504 return CMD_SUCCESS;
505
506 clear_dst(cfg);
507
508 cfg->cfg.dst = ZLOG_5424_DST_FD;
509 cfg->cfg.fd = fd;
510 cfg->cfg.fmt = fmtv;
511 if (envvar)
512 cfg->envvar = XSTRDUP(MTYPE_LOG_5424_DATA, envvar);
513
514 if (envvar_problem)
515 vty_out(vty,
516 "%% environment variable \"%s\" not present or invalid.\n",
517 envvar);
518 if (!frr_is_startup_fd(fd))
519 vty_out(vty,
520 "%% file descriptor %d was not open when this process was started\n",
521 (int)fd);
522 if (envvar_problem || !frr_is_startup_fd(fd))
523 vty_out(vty,
524 "%% configuration will be saved but has no effect currently\n");
525
526 return reconf_dst(cfg, vty);
527 }
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 {
536 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
537
538 return reconf_clear_dst(cfg, vty);
539 }
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 {
550 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
551 int prio_min = log_level_match(levelarg);
552
553 if (prio_min == cfg->cfg.prio_min)
554 return CMD_SUCCESS;
555
556 cfg->cfg.prio_min = prio_min;
557 return reconf_meta(cfg, vty);
558 }
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 {
567 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
568 int facility = facility_match(facilityarg);
569
570 if (cfg->cfg.facility == facility)
571 return CMD_SUCCESS;
572
573 cfg->cfg.facility = facility;
574 return reconf_meta(cfg, vty);
575 }
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 {
588 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
589 bool val = !no, *ptr;
590 struct log_option *opt = log_opts;
591
592 while (opt->name && strcmp(opt->name, option))
593 opt++;
594 if (!opt->name)
595 return CMD_WARNING;
596
597 ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
598 if (*ptr == val)
599 return CMD_SUCCESS;
600
601 *ptr = val;
602 return reconf_meta(cfg, vty);
603 }
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 {
613 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
614 uint32_t ts_flags = cfg->cfg.ts_flags;
615
616 ts_flags &= ~ZLOG_TS_PREC;
617 if (no)
618 ts_flags |= DFLT_TS_FLAGS & ZLOG_TS_PREC;
619 else
620 ts_flags |= precision;
621
622 if (ts_flags == cfg->cfg.ts_flags)
623 return CMD_SUCCESS;
624
625 cfg->cfg.ts_flags = ts_flags;
626 return reconf_meta(cfg, vty);
627 }
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 {
636 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
637 uint32_t ts_flags = cfg->cfg.ts_flags;
638
639 ts_flags &= ~ZLOG_TS_UTC;
640 if (no)
641 ts_flags |= DFLT_TS_FLAGS & ZLOG_TS_UTC;
642 else
643 ts_flags |= (~DFLT_TS_FLAGS) & ZLOG_TS_UTC;
644
645 if (ts_flags == cfg->cfg.ts_flags)
646 return CMD_SUCCESS;
647
648 cfg->cfg.ts_flags = ts_flags;
649 return reconf_meta(cfg, vty);
650 }
651
652 static int log_5424_node_exit(struct vty *vty)
653 {
654 VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
655
656 if ((cfg->reconf_dst || cfg->reconf_meta) && vty->type != VTY_FILE)
657 vty_out(vty, "%% applying changes.\n");
658
659 if (cfg->reconf_dst)
660 zlog_5424_apply_dst(&cfg->cfg);
661 else if (cfg->reconf_meta)
662 zlog_5424_apply_meta(&cfg->cfg);
663
664 cfg->reconf_dst = cfg->reconf_meta = false;
665 return 1;
666 }
667
668 static int log_5424_config_write(struct vty *vty)
669 {
670 struct zlog_cfg_5424_user *cfg;
671
672 frr_each (targets, &targets, cfg) {
673 const char *fmt_str = "";
674
675 vty_out(vty, "log extended %s\n", cfg->name);
676
677 switch (cfg->cfg.fmt) {
678 case ZLOG_FMT_5424:
679 fmt_str = " format rfc5424";
680 break;
681 case ZLOG_FMT_3164:
682 fmt_str = " format rfc3164";
683 break;
684 case ZLOG_FMT_LOCAL:
685 fmt_str = " format local-syslogd";
686 break;
687 case ZLOG_FMT_JOURNALD:
688 fmt_str = " format journald";
689 break;
690 }
691
692 switch (cfg->cfg.dst) {
693 case ZLOG_5424_DST_NONE:
694 vty_out(vty, " ! no destination configured\n");
695 break;
696
697 case ZLOG_5424_DST_FD:
698 if (cfg->cfg.fmt == ZLOG_FMT_5424)
699 fmt_str = "";
700
701 if (cfg->envvar)
702 vty_out(vty, " destination fd envvar %s%s\n",
703 cfg->envvar, fmt_str);
704 else if (cfg->cfg.fd == 1)
705 vty_out(vty, " destination stdout%s\n",
706 fmt_str);
707 else if (cfg->cfg.fd == 2)
708 vty_out(vty, " destination stderr%s\n",
709 fmt_str);
710 else
711 vty_out(vty, " destination fd %d%s\n",
712 cfg->cfg.fd, fmt_str);
713 break;
714
715 case ZLOG_5424_DST_FILE:
716 case ZLOG_5424_DST_FIFO:
717 if (cfg->cfg.fmt == ZLOG_FMT_5424)
718 fmt_str = "";
719
720 vty_out(vty, " destination %s %s",
721 (cfg->cfg.dst == ZLOG_5424_DST_FIFO) ? "fifo"
722 : "file",
723 cfg->filename);
724
725 if (cfg->cfg.file_nocreate)
726 vty_out(vty, " no-create");
727 else if (cfg->file_user || cfg->file_group ||
728 cfg->cfg.file_mode != (LOGFILE_MASK & 0666)) {
729 vty_out(vty, " create");
730
731 if (cfg->file_user)
732 vty_out(vty, " user %s",
733 cfg->file_user);
734 if (cfg->file_group)
735 vty_out(vty, " group %s",
736 cfg->file_group);
737 if (cfg->cfg.file_mode != (LOGFILE_MASK & 0666))
738 vty_out(vty, " mode %04o",
739 cfg->cfg.file_mode);
740 }
741 vty_out(vty, "%s\n", fmt_str);
742 break;
743
744 case ZLOG_5424_DST_UNIX:
745 switch (cfg->unix_special) {
746 case SPECIAL_NONE:
747 vty_out(vty, " destination unix %s%s\n",
748 cfg->filename, fmt_str);
749 break;
750 case SPECIAL_SYSLOG:
751 if (cfg->cfg.fmt == ZLOG_FMT_DEV_LOG)
752 vty_out(vty, " destination syslog\n");
753 else
754 vty_out(vty,
755 " destination syslog supports-rfc5424\n");
756 break;
757 case SPECIAL_JOURNALD:
758 vty_out(vty, " destination journald\n");
759 break;
760 }
761 break;
762 }
763
764 if (cfg->cfg.prio_min != LOG_DEBUG)
765 vty_out(vty, " priority %s\n",
766 zlog_priority_str(cfg->cfg.prio_min));
767 if (cfg->cfg.facility != DFLT_FACILITY)
768 vty_out(vty, " facility %s\n",
769 facility_name(cfg->cfg.facility));
770
771 for (struct log_option *opt = log_opts; opt->name; opt++) {
772 bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
773
774 if (*ptr != opt->dflt)
775 vty_out(vty, " %sstructured-data %s\n",
776 *ptr ? "" : "no ", opt->name);
777 }
778
779 if ((cfg->cfg.ts_flags ^ DFLT_TS_FLAGS) & ZLOG_TS_PREC)
780 vty_out(vty, " timestamp precision %u\n",
781 cfg->cfg.ts_flags & ZLOG_TS_PREC);
782
783 if ((cfg->cfg.ts_flags ^ DFLT_TS_FLAGS) & ZLOG_TS_UTC) {
784 if (cfg->cfg.ts_flags & ZLOG_TS_UTC)
785 vty_out(vty, " no timestamp local-time\n");
786 else
787 vty_out(vty, " timestamp local-time\n");
788 }
789
790 vty_out(vty, "!\n");
791 }
792 return 0;
793 }
794
795 static int log_5424_show(struct vty *vty)
796 {
797 struct zlog_cfg_5424_user *cfg;
798
799 frr_each (targets, &targets, cfg) {
800 vty_out(vty, "\nExtended log target %pSQq\n", cfg->name);
801
802 switch (cfg->cfg.dst) {
803 case ZLOG_5424_DST_NONE:
804 vty_out(vty,
805 " Inactive (no destination configured)\n");
806 break;
807
808 case ZLOG_5424_DST_FD:
809 if (cfg->envvar)
810 vty_out(vty,
811 " logging to fd %d from environment variable %pSE\n",
812 cfg->cfg.fd, cfg->envvar);
813 else if (cfg->cfg.fd == 1)
814 vty_out(vty, " logging to stdout\n");
815 else if (cfg->cfg.fd == 2)
816 vty_out(vty, " logging to stderr\n");
817 else
818 vty_out(vty, " logging to fd %d\n",
819 cfg->cfg.fd);
820 break;
821
822 case ZLOG_5424_DST_FILE:
823 case ZLOG_5424_DST_FIFO:
824 case ZLOG_5424_DST_UNIX:
825 vty_out(vty, " logging to %s: %pSE\n",
826 (cfg->cfg.dst == ZLOG_5424_DST_FIFO) ? "fifo"
827 : (cfg->cfg.dst == ZLOG_5424_DST_UNIX)
828 ? "unix socket"
829 : "file",
830 cfg->filename);
831 break;
832 }
833
834 vty_out(vty, " log level: %s, facility: %s\n",
835 zlog_priority_str(cfg->cfg.prio_min),
836 facility_name(cfg->cfg.facility));
837
838 bool any_meta = false, first = true;
839
840 for (struct log_option *opt = log_opts; opt->name; opt++) {
841 bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
842
843 any_meta |= *ptr;
844 }
845
846 if (!any_meta)
847 continue;
848
849 switch (cfg->cfg.fmt) {
850 case ZLOG_FMT_5424:
851 case ZLOG_FMT_JOURNALD:
852 vty_out(vty, " structured data: ");
853
854 for (struct log_option *opt = log_opts; opt->name;
855 opt++) {
856 bool *ptr = (bool *)(((char *)&cfg->cfg) +
857 opt->offs);
858
859 if (*ptr) {
860 vty_out(vty, "%s%s", first ? "" : ", ",
861 opt->name);
862 first = false;
863 }
864 }
865 break;
866
867 case ZLOG_FMT_3164:
868 case ZLOG_FMT_LOCAL:
869 vty_out(vty,
870 " structured data is not supported by the selected format\n");
871 break;
872 }
873
874 vty_out(vty, "\n");
875
876 size_t lost_msgs;
877 int last_errno;
878 bool stale_errno;
879 struct timeval err_ts;
880 int64_t since;
881
882 zlog_5424_state(&cfg->cfg, &lost_msgs, &last_errno,
883 &stale_errno, &err_ts);
884 vty_out(vty, " number of lost messages: %zu\n", lost_msgs);
885
886 if (last_errno == 0)
887 since = 0;
888 else
889 since = monotime_since(&err_ts, NULL);
890 vty_out(vty,
891 " last error: %s (%lld.%06llds ago, currently %s)\n",
892 last_errno ? safe_strerror(last_errno) : "none",
893 since / 1000000LL, since % 1000000LL,
894 stale_errno ? "OK" : "erroring");
895 }
896 return 0;
897 }
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 {
911 struct zlog_cfg_5424_user *cfg;
912
913 frr_each (targets, &targets, cfg)
914 vector_set(comps, XSTRDUP(MTYPE_COMPLETION, cfg->name));
915 }
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 {
924 hook_register(zlog_cli_show, log_5424_show);
925
926 cmd_variable_handler_register(log_5424_var_handlers);
927
928 /* CLI commands. */
929 install_node(&extlog_node);
930 install_default(EXTLOG_NODE);
931
932 install_element(CONFIG_NODE, &log_5424_target_cmd);
933 install_element(CONFIG_NODE, &no_log_5424_target_cmd);
934
935 install_element(EXTLOG_NODE, &log_5424_destination_file_cmd);
936 install_element(EXTLOG_NODE, &log_5424_destination_fifo_cmd);
937 install_element(EXTLOG_NODE, &log_5424_destination_unix_cmd);
938 install_element(EXTLOG_NODE, &log_5424_destination_journald_cmd);
939 install_element(EXTLOG_NODE, &log_5424_destination_syslog_cmd);
940 install_element(EXTLOG_NODE, &log_5424_destination_fd_cmd);
941
942 install_element(EXTLOG_NODE, &log_5424_meta_cmd);
943 install_element(EXTLOG_NODE, &log_5424_prio_cmd);
944 install_element(EXTLOG_NODE, &log_5424_facility_cmd);
945 install_element(EXTLOG_NODE, &log_5424_ts_prec_cmd);
946 install_element(EXTLOG_NODE, &log_5424_ts_local_cmd);
947 }
948
949 /* hooks */
950
951 static int log_5424_early_init(struct thread_master *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 {
957 hook_register(frr_early_init, log_5424_early_init);
958 hook_register(zlog_rotate, log_5424_rotate);
959 hook_register(frr_fini, log_5424_fini);
960 }
961
962 static int log_5424_early_init(struct thread_master *master)
963 {
964 log_5424_master = master;
965
966 return 0;
967 }
968
969 static int log_5424_rotate(void)
970 {
971 struct zlog_cfg_5424_user *cfg;
972
973 frr_each (targets, &targets, cfg)
974 if (!zlog_5424_rotate(&cfg->cfg))
975 zlog_err(
976 "log rotation on extended log target %s failed",
977 cfg->name);
978
979 return 0;
980 }
981
982 static int log_5424_fini(void)
983 {
984 struct zlog_cfg_5424_user *cfg;
985
986 while ((cfg = targets_pop(&targets)))
987 log_5424_free(cfg, true);
988
989 log_5424_master = NULL;
990
991 return 0;
992 }