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