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