Line data Source code
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 48 : DEFINE_MTYPE_STATIC(LOG, LOG_5424_CONFIG, "extended syslog config");
31 48 : DEFINE_MTYPE_STATIC(LOG, LOG_5424_DATA, "extended syslog config items");
32 :
33 0 : static int target_cmp(const struct zlog_cfg_5424_user *a,
34 : const struct zlog_cfg_5424_user *b)
35 : {
36 0 : return strcmp(a->name, b->name);
37 : }
38 :
39 16 : 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 0 : static struct zlog_cfg_5424_user *log_5424_alloc(const char *name)
76 : {
77 0 : struct zlog_cfg_5424_user *cfg;
78 :
79 0 : cfg = XCALLOC(MTYPE_LOG_5424_CONFIG, sizeof(*cfg));
80 0 : cfg->name = XSTRDUP(MTYPE_LOG_5424_DATA, name);
81 :
82 0 : cfg->cfg.master = log_5424_master;
83 0 : cfg->cfg.kw_location = true;
84 0 : cfg->cfg.kw_version = false;
85 0 : cfg->cfg.facility = DFLT_FACILITY;
86 0 : cfg->cfg.prio_min = DFLT_PRIO_MIN;
87 0 : cfg->cfg.ts_flags = DFLT_TS_FLAGS;
88 0 : clear_dst(cfg);
89 :
90 0 : for (struct log_option *opt = log_opts; opt->name; opt++) {
91 0 : bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
92 0 : *ptr = opt->dflt;
93 : }
94 :
95 0 : zlog_5424_init(&cfg->cfg);
96 :
97 0 : QOBJ_REG(cfg, zlog_cfg_5424_user);
98 0 : targets_add(&targets, cfg);
99 0 : return cfg;
100 : }
101 :
102 0 : static void log_5424_free(struct zlog_cfg_5424_user *cfg, bool keepopen)
103 : {
104 0 : targets_del(&targets, cfg);
105 0 : QOBJ_UNREG(cfg);
106 :
107 0 : zlog_5424_fini(&cfg->cfg, keepopen);
108 0 : clear_dst(cfg);
109 :
110 0 : XFREE(MTYPE_LOG_5424_DATA, cfg->filename);
111 0 : XFREE(MTYPE_LOG_5424_DATA, cfg->name);
112 0 : XFREE(MTYPE_LOG_5424_CONFIG, cfg);
113 0 : }
114 :
115 0 : static void clear_dst(struct zlog_cfg_5424_user *cfg)
116 : {
117 0 : XFREE(MTYPE_LOG_5424_DATA, cfg->filename);
118 0 : cfg->cfg.filename = cfg->filename;
119 :
120 0 : XFREE(MTYPE_LOG_5424_DATA, cfg->file_user);
121 0 : XFREE(MTYPE_LOG_5424_DATA, cfg->file_group);
122 0 : XFREE(MTYPE_LOG_5424_DATA, cfg->envvar);
123 :
124 0 : cfg->cfg.fd = -1;
125 0 : cfg->cfg.file_uid = -1;
126 0 : cfg->cfg.file_gid = -1;
127 0 : cfg->cfg.file_mode = LOGFILE_MASK & 0666;
128 0 : cfg->cfg.file_nocreate = false;
129 0 : cfg->cfg.dst = ZLOG_5424_DST_NONE;
130 0 : }
131 :
132 0 : static int reconf_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
133 : {
134 0 : if (!cfg->reconf_dst && !cfg->reconf_meta && vty->type != VTY_FILE)
135 0 : vty_out(vty,
136 : "%% Changes will be applied when exiting this config block\n");
137 :
138 0 : cfg->reconf_dst = true;
139 0 : return CMD_SUCCESS;
140 : }
141 :
142 0 : static int reconf_meta(struct zlog_cfg_5424_user *cfg, struct vty *vty)
143 : {
144 0 : if (!cfg->reconf_dst && !cfg->reconf_meta && vty->type != VTY_FILE)
145 0 : vty_out(vty,
146 : "%% Changes will be applied when exiting this config block\n");
147 :
148 0 : cfg->reconf_meta = true;
149 0 : return CMD_SUCCESS;
150 : }
151 :
152 0 : static int reconf_clear_dst(struct zlog_cfg_5424_user *cfg, struct vty *vty)
153 : {
154 0 : if (cfg->cfg.dst == ZLOG_5424_DST_NONE)
155 : return CMD_SUCCESS;
156 :
157 0 : clear_dst(cfg);
158 0 : return reconf_dst(cfg, vty);
159 : }
160 :
161 : #include "lib/zlog_5424_cli_clippy.c"
162 :
163 0 : 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 0 : struct zlog_cfg_5424_user *cfg, ref;
171 :
172 0 : ref.name = (char *)extlogname;
173 0 : cfg = targets_find(&targets, &ref);
174 :
175 0 : if (!cfg)
176 0 : cfg = log_5424_alloc(extlogname);
177 :
178 0 : VTY_PUSH_CONTEXT(EXTLOG_NODE, cfg);
179 0 : return CMD_SUCCESS;
180 : }
181 :
182 0 : 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 0 : struct zlog_cfg_5424_user *cfg, ref;
191 :
192 0 : ref.name = (char *)extlogname;
193 0 : cfg = targets_find(&targets, &ref);
194 :
195 0 : if (!cfg) {
196 0 : vty_out(vty, "%% No extended syslog target named \"%s\"\n",
197 : extlogname);
198 0 : return CMD_WARNING;
199 : }
200 :
201 0 : log_5424_free(cfg, false);
202 0 : 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 0 : static enum zlog_5424_format log_5424_fmt(const char *fmt,
215 : enum zlog_5424_format dflt)
216 : {
217 0 : if (!fmt)
218 : return dflt;
219 0 : else if (!strcmp(fmt, "rfc5424"))
220 : return ZLOG_FMT_5424;
221 0 : else if (!strcmp(fmt, "rfc3164"))
222 : return ZLOG_FMT_3164;
223 0 : else if (!strcmp(fmt, "local-syslogd"))
224 : return ZLOG_FMT_LOCAL;
225 0 : else if (!strcmp(fmt, "journald"))
226 0 : return ZLOG_FMT_JOURNALD;
227 :
228 : return dflt;
229 : }
230 :
231 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
252 0 : enum zlog_5424_dst dst;
253 0 : bool reconf = true, warn_perm = false;
254 0 : char *prev_user, *prev_group;
255 0 : mode_t perm_val = LOGFILE_MASK & 0666;
256 0 : enum zlog_5424_format fmtv;
257 :
258 0 : if (no)
259 0 : return reconf_clear_dst(cfg, vty);
260 :
261 0 : fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
262 :
263 0 : if (perms) {
264 0 : char *errp = (char *)perms;
265 :
266 0 : perm_val = strtoul(perms, &errp, 8);
267 0 : if (*errp || errp == perms || perm_val == 0 ||
268 0 : (perm_val & ~0666)) {
269 0 : vty_out(vty, "%% Invalid permissions value \"%s\"\n",
270 : perms);
271 0 : return CMD_WARNING;
272 : }
273 : }
274 :
275 0 : dst = (strcmp(type, "fifo") == 0) ? ZLOG_5424_DST_FIFO
276 0 : : ZLOG_5424_DST_FILE;
277 :
278 0 : if (cfg->filename && !strcmp(path, cfg->filename) &&
279 0 : dst == cfg->cfg.dst && cfg->cfg.active && cfg->cfg.fmt == fmtv)
280 0 : reconf = false;
281 :
282 : /* keep for compare below */
283 0 : prev_user = cfg->file_user;
284 0 : prev_group = cfg->file_group;
285 0 : cfg->file_user = NULL;
286 0 : cfg->file_group = NULL;
287 :
288 0 : clear_dst(cfg);
289 :
290 0 : cfg->filename = XSTRDUP(MTYPE_LOG_5424_DATA, path);
291 0 : cfg->cfg.dst = dst;
292 0 : cfg->cfg.filename = cfg->filename;
293 0 : cfg->cfg.fmt = fmtv;
294 :
295 0 : if (nocreate)
296 0 : cfg->cfg.file_nocreate = true;
297 : else {
298 0 : if (user) {
299 0 : struct passwd *pwent;
300 :
301 0 : warn_perm |= (prev_user && strcmp(user, prev_user));
302 0 : cfg->file_user = XSTRDUP(MTYPE_LOG_5424_DATA, user);
303 :
304 0 : errno = 0;
305 0 : pwent = getpwnam(user);
306 0 : if (!pwent)
307 0 : vty_out(vty,
308 : "%% Could not look up user \"%s\" (%s), file owner will be left untouched!\n",
309 : user,
310 0 : errno ? safe_strerror(errno)
311 : : "No entry by this user name");
312 : else
313 0 : cfg->cfg.file_uid = pwent->pw_uid;
314 : }
315 0 : if (group) {
316 0 : struct group *grent;
317 :
318 0 : warn_perm |= (prev_group && strcmp(group, prev_group));
319 0 : cfg->file_group = XSTRDUP(MTYPE_LOG_5424_DATA, group);
320 :
321 0 : errno = 0;
322 0 : grent = getgrnam(group);
323 0 : if (!grent)
324 0 : vty_out(vty,
325 : "%% Could not look up group \"%s\" (%s), file group will be left untouched!\n",
326 : group,
327 0 : errno ? safe_strerror(errno)
328 : : "No entry by this group name");
329 : else
330 0 : cfg->cfg.file_gid = grent->gr_gid;
331 : }
332 : }
333 0 : XFREE(MTYPE_LOG_5424_DATA, prev_user);
334 0 : XFREE(MTYPE_LOG_5424_DATA, prev_group);
335 :
336 0 : if (cfg->cfg.file_uid != (uid_t)-1 || cfg->cfg.file_gid != (gid_t)-1) {
337 0 : struct stat st;
338 :
339 0 : if (stat(cfg->filename, &st) == 0) {
340 0 : warn_perm |= (st.st_uid != cfg->cfg.file_uid);
341 0 : warn_perm |= (st.st_gid != cfg->cfg.file_gid);
342 : }
343 : }
344 0 : if (warn_perm)
345 0 : 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 0 : if (reconf)
351 0 : 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 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
385 :
386 0 : if (no)
387 0 : return reconf_clear_dst(cfg, vty);
388 :
389 0 : cfg->unix_special = special;
390 :
391 0 : if (cfg->cfg.dst == ZLOG_5424_DST_UNIX && cfg->filename &&
392 0 : !strcmp(path, cfg->filename) && cfg->cfg.active &&
393 0 : cfg->cfg.fmt == fmt)
394 : return CMD_SUCCESS;
395 :
396 0 : clear_dst(cfg);
397 :
398 0 : cfg->filename = XSTRDUP(MTYPE_LOG_5424_DATA, path);
399 0 : cfg->cfg.dst = ZLOG_5424_DST_UNIX;
400 0 : cfg->cfg.filename = cfg->filename;
401 0 : cfg->cfg.fmt = fmt;
402 :
403 0 : cfg->cfg.reconn_backoff = 25;
404 0 : cfg->cfg.reconn_backoff_cur = 25;
405 0 : cfg->cfg.reconn_backoff_max = 10000;
406 0 : return reconf_dst(cfg, vty);
407 : }
408 :
409 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
420 0 : enum zlog_5424_format fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
421 :
422 0 : return dst_unix(vty, no, path, fmtv, SPECIAL_NONE);
423 : }
424 :
425 0 : 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 0 : 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 0 : 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 0 : int format = supp5424 ? ZLOG_FMT_5424 : ZLOG_FMT_DEV_LOG;
453 :
454 : /* unfortunately, there is no way to detect 5424 support */
455 0 : 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 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
486 0 : bool envvar_problem = false;
487 0 : enum zlog_5424_format fmtv;
488 :
489 0 : if (no)
490 0 : return reconf_clear_dst(cfg, vty);
491 :
492 0 : fmtv = log_5424_fmt(fmt, ZLOG_FMT_5424);
493 :
494 0 : if (envvar) {
495 0 : char *envval;
496 :
497 0 : envval = getenv(envvar);
498 0 : if (!envval)
499 : envvar_problem = true;
500 : else {
501 0 : char *errp = envval;
502 :
503 0 : fd = strtoul(envval, &errp, 0);
504 0 : if (errp == envval || *errp)
505 0 : envvar_problem = true;
506 : }
507 :
508 0 : if (envvar_problem)
509 : fd = -1;
510 0 : } else if (fd1)
511 : fd = 1;
512 0 : else if (fd2)
513 0 : fd = 2;
514 :
515 0 : if (cfg->cfg.dst == ZLOG_5424_DST_FD && cfg->cfg.fd == fd &&
516 0 : cfg->cfg.active && cfg->cfg.fmt == fmtv)
517 : return CMD_SUCCESS;
518 :
519 0 : clear_dst(cfg);
520 :
521 0 : cfg->cfg.dst = ZLOG_5424_DST_FD;
522 0 : cfg->cfg.fd = fd;
523 0 : cfg->cfg.fmt = fmtv;
524 0 : if (envvar)
525 0 : cfg->envvar = XSTRDUP(MTYPE_LOG_5424_DATA, envvar);
526 :
527 0 : if (envvar_problem)
528 0 : vty_out(vty,
529 : "%% environment variable \"%s\" not present or invalid.\n",
530 : envvar);
531 0 : if (!frr_is_startup_fd(fd))
532 0 : vty_out(vty,
533 : "%% file descriptor %d was not open when this process was started\n",
534 : (int)fd);
535 0 : if (envvar_problem || !frr_is_startup_fd(fd))
536 0 : vty_out(vty,
537 : "%% configuration will be saved but has no effect currently\n");
538 :
539 0 : return reconf_dst(cfg, vty);
540 : }
541 :
542 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
550 :
551 0 : return reconf_clear_dst(cfg, vty);
552 : }
553 :
554 : /* end of destinations */
555 :
556 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
564 0 : int prio_min = log_level_match(levelarg);
565 :
566 0 : if (prio_min == cfg->cfg.prio_min)
567 : return CMD_SUCCESS;
568 :
569 0 : cfg->cfg.prio_min = prio_min;
570 0 : return reconf_meta(cfg, vty);
571 : }
572 :
573 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
581 0 : int facility = facility_match(facilityarg);
582 :
583 0 : if (cfg->cfg.facility == facility)
584 : return CMD_SUCCESS;
585 :
586 0 : cfg->cfg.facility = facility;
587 0 : return reconf_meta(cfg, vty);
588 : }
589 :
590 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
602 0 : bool val = !no, *ptr;
603 0 : struct log_option *opt = log_opts;
604 :
605 0 : while (opt->name && strcmp(opt->name, option))
606 0 : opt++;
607 0 : if (!opt->name)
608 : return CMD_WARNING;
609 :
610 0 : ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
611 0 : if (*ptr == val)
612 : return CMD_SUCCESS;
613 :
614 0 : *ptr = val;
615 0 : return reconf_meta(cfg, vty);
616 : }
617 :
618 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
627 0 : uint32_t ts_flags = cfg->cfg.ts_flags;
628 :
629 0 : ts_flags &= ~ZLOG_TS_PREC;
630 0 : if (no)
631 0 : ts_flags |= DFLT_TS_FLAGS & ZLOG_TS_PREC;
632 : else
633 0 : ts_flags |= precision;
634 :
635 0 : if (ts_flags == cfg->cfg.ts_flags)
636 : return CMD_SUCCESS;
637 :
638 0 : cfg->cfg.ts_flags = ts_flags;
639 0 : return reconf_meta(cfg, vty);
640 : }
641 :
642 0 : 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 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
650 0 : uint32_t ts_flags = cfg->cfg.ts_flags;
651 :
652 0 : ts_flags &= ~ZLOG_TS_UTC;
653 0 : if (no)
654 0 : ts_flags |= DFLT_TS_FLAGS & ZLOG_TS_UTC;
655 : else
656 : ts_flags |= (~DFLT_TS_FLAGS) & ZLOG_TS_UTC;
657 :
658 0 : if (ts_flags == cfg->cfg.ts_flags)
659 : return CMD_SUCCESS;
660 :
661 0 : cfg->cfg.ts_flags = ts_flags;
662 0 : return reconf_meta(cfg, vty);
663 : }
664 :
665 0 : static int log_5424_node_exit(struct vty *vty)
666 : {
667 0 : VTY_DECLVAR_CONTEXT(zlog_cfg_5424_user, cfg);
668 :
669 0 : if ((cfg->reconf_dst || cfg->reconf_meta) && vty->type != VTY_FILE)
670 0 : vty_out(vty, "%% applying changes.\n");
671 :
672 0 : if (cfg->reconf_dst)
673 0 : zlog_5424_apply_dst(&cfg->cfg);
674 0 : else if (cfg->reconf_meta)
675 0 : zlog_5424_apply_meta(&cfg->cfg);
676 :
677 0 : cfg->reconf_dst = cfg->reconf_meta = false;
678 0 : return 1;
679 : }
680 :
681 0 : static int log_5424_config_write(struct vty *vty)
682 : {
683 0 : struct zlog_cfg_5424_user *cfg;
684 :
685 0 : frr_each (targets, &targets, cfg) {
686 0 : const char *fmt_str = "";
687 :
688 0 : vty_out(vty, "log extended %s\n", cfg->name);
689 :
690 0 : switch (cfg->cfg.fmt) {
691 0 : case ZLOG_FMT_5424:
692 0 : fmt_str = " format rfc5424";
693 0 : break;
694 0 : case ZLOG_FMT_3164:
695 0 : fmt_str = " format rfc3164";
696 0 : break;
697 0 : case ZLOG_FMT_LOCAL:
698 0 : fmt_str = " format local-syslogd";
699 0 : break;
700 0 : case ZLOG_FMT_JOURNALD:
701 0 : fmt_str = " format journald";
702 0 : break;
703 : }
704 :
705 0 : switch (cfg->cfg.dst) {
706 0 : case ZLOG_5424_DST_NONE:
707 0 : vty_out(vty, " ! no destination configured\n");
708 0 : break;
709 :
710 0 : case ZLOG_5424_DST_FD:
711 0 : if (cfg->cfg.fmt == ZLOG_FMT_5424)
712 0 : fmt_str = "";
713 :
714 0 : if (cfg->envvar)
715 0 : vty_out(vty, " destination fd envvar %s%s\n",
716 : cfg->envvar, fmt_str);
717 0 : else if (cfg->cfg.fd == 1)
718 0 : vty_out(vty, " destination stdout%s\n",
719 : fmt_str);
720 0 : else if (cfg->cfg.fd == 2)
721 0 : vty_out(vty, " destination stderr%s\n",
722 : fmt_str);
723 : else
724 0 : vty_out(vty, " destination fd %d%s\n",
725 : cfg->cfg.fd, fmt_str);
726 : break;
727 :
728 0 : case ZLOG_5424_DST_FILE:
729 : case ZLOG_5424_DST_FIFO:
730 0 : if (cfg->cfg.fmt == ZLOG_FMT_5424)
731 0 : fmt_str = "";
732 :
733 0 : vty_out(vty, " destination %s %s",
734 : (cfg->cfg.dst == ZLOG_5424_DST_FIFO) ? "fifo"
735 : : "file",
736 : cfg->filename);
737 :
738 0 : if (cfg->cfg.file_nocreate)
739 0 : vty_out(vty, " no-create");
740 0 : else if (cfg->file_user || cfg->file_group ||
741 0 : cfg->cfg.file_mode != (LOGFILE_MASK & 0666)) {
742 0 : vty_out(vty, " create");
743 :
744 0 : if (cfg->file_user)
745 0 : vty_out(vty, " user %s",
746 : cfg->file_user);
747 0 : if (cfg->file_group)
748 0 : vty_out(vty, " group %s",
749 : cfg->file_group);
750 0 : if (cfg->cfg.file_mode != (LOGFILE_MASK & 0666))
751 0 : vty_out(vty, " mode %04o",
752 : cfg->cfg.file_mode);
753 : }
754 0 : vty_out(vty, "%s\n", fmt_str);
755 0 : break;
756 :
757 0 : case ZLOG_5424_DST_UNIX:
758 0 : switch (cfg->unix_special) {
759 0 : case SPECIAL_NONE:
760 0 : vty_out(vty, " destination unix %s%s\n",
761 : cfg->filename, fmt_str);
762 0 : break;
763 0 : case SPECIAL_SYSLOG:
764 0 : if (cfg->cfg.fmt == ZLOG_FMT_DEV_LOG)
765 0 : vty_out(vty, " destination syslog\n");
766 : else
767 0 : vty_out(vty,
768 : " destination syslog supports-rfc5424\n");
769 : break;
770 0 : case SPECIAL_JOURNALD:
771 0 : vty_out(vty, " destination journald\n");
772 0 : break;
773 : }
774 : break;
775 : }
776 :
777 0 : if (cfg->cfg.prio_min != LOG_DEBUG)
778 0 : vty_out(vty, " priority %s\n",
779 : zlog_priority_str(cfg->cfg.prio_min));
780 0 : if (cfg->cfg.facility != DFLT_FACILITY)
781 0 : vty_out(vty, " facility %s\n",
782 : facility_name(cfg->cfg.facility));
783 :
784 0 : for (struct log_option *opt = log_opts; opt->name; opt++) {
785 0 : bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
786 :
787 0 : if (*ptr != opt->dflt)
788 0 : vty_out(vty, " %sstructured-data %s\n",
789 : *ptr ? "" : "no ", opt->name);
790 : }
791 :
792 0 : if ((cfg->cfg.ts_flags ^ DFLT_TS_FLAGS) & ZLOG_TS_PREC)
793 0 : vty_out(vty, " timestamp precision %u\n",
794 : cfg->cfg.ts_flags & ZLOG_TS_PREC);
795 :
796 0 : if ((cfg->cfg.ts_flags ^ DFLT_TS_FLAGS) & ZLOG_TS_UTC) {
797 0 : if (cfg->cfg.ts_flags & ZLOG_TS_UTC)
798 0 : vty_out(vty, " no timestamp local-time\n");
799 : else
800 0 : vty_out(vty, " timestamp local-time\n");
801 : }
802 :
803 0 : vty_out(vty, "!\n");
804 : }
805 0 : return 0;
806 : }
807 :
808 0 : static int log_5424_show(struct vty *vty)
809 : {
810 0 : struct zlog_cfg_5424_user *cfg;
811 :
812 0 : frr_each (targets, &targets, cfg) {
813 0 : vty_out(vty, "\nExtended log target %pSQq\n", cfg->name);
814 :
815 0 : switch (cfg->cfg.dst) {
816 0 : case ZLOG_5424_DST_NONE:
817 0 : vty_out(vty,
818 : " Inactive (no destination configured)\n");
819 0 : break;
820 :
821 0 : case ZLOG_5424_DST_FD:
822 0 : if (cfg->envvar)
823 0 : vty_out(vty,
824 : " logging to fd %d from environment variable %pSE\n",
825 : cfg->cfg.fd, cfg->envvar);
826 0 : else if (cfg->cfg.fd == 1)
827 0 : vty_out(vty, " logging to stdout\n");
828 0 : else if (cfg->cfg.fd == 2)
829 0 : vty_out(vty, " logging to stderr\n");
830 : else
831 0 : vty_out(vty, " logging to fd %d\n",
832 : cfg->cfg.fd);
833 : break;
834 :
835 0 : case ZLOG_5424_DST_FILE:
836 : case ZLOG_5424_DST_FIFO:
837 : case ZLOG_5424_DST_UNIX:
838 0 : 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 0 : : "file",
843 : cfg->filename);
844 0 : break;
845 : }
846 :
847 0 : 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 0 : bool any_meta = false, first = true;
852 :
853 0 : for (struct log_option *opt = log_opts; opt->name; opt++) {
854 0 : bool *ptr = (bool *)(((char *)&cfg->cfg) + opt->offs);
855 :
856 0 : any_meta |= *ptr;
857 : }
858 :
859 0 : if (!any_meta)
860 0 : continue;
861 :
862 0 : switch (cfg->cfg.fmt) {
863 0 : case ZLOG_FMT_5424:
864 : case ZLOG_FMT_JOURNALD:
865 0 : vty_out(vty, " structured data: ");
866 :
867 0 : for (struct log_option *opt = log_opts; opt->name;
868 0 : opt++) {
869 0 : bool *ptr = (bool *)(((char *)&cfg->cfg) +
870 0 : opt->offs);
871 :
872 0 : if (*ptr) {
873 0 : vty_out(vty, "%s%s", first ? "" : ", ",
874 : opt->name);
875 0 : first = false;
876 : }
877 : }
878 : break;
879 :
880 0 : case ZLOG_FMT_3164:
881 : case ZLOG_FMT_LOCAL:
882 0 : vty_out(vty,
883 : " structured data is not supported by the selected format\n");
884 0 : break;
885 : }
886 :
887 0 : vty_out(vty, "\n");
888 :
889 0 : size_t lost_msgs;
890 0 : int last_errno;
891 0 : bool stale_errno;
892 0 : struct timeval err_ts;
893 0 : int64_t since;
894 :
895 0 : zlog_5424_state(&cfg->cfg, &lost_msgs, &last_errno,
896 : &stale_errno, &err_ts);
897 0 : vty_out(vty, " number of lost messages: %zu\n", lost_msgs);
898 :
899 0 : if (last_errno == 0)
900 : since = 0;
901 : else
902 0 : since = monotime_since(&err_ts, NULL);
903 0 : vty_out(vty,
904 : " last error: %s (%lld.%06llds ago, currently %s)\n",
905 0 : last_errno ? safe_strerror(last_errno) : "none",
906 : since / 1000000LL, since % 1000000LL,
907 0 : stale_errno ? "OK" : "erroring");
908 : }
909 0 : 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 0 : static void log_5424_autocomplete(vector comps, struct cmd_token *token)
923 : {
924 0 : struct zlog_cfg_5424_user *cfg;
925 :
926 0 : frr_each (targets, &targets, cfg)
927 0 : vector_set(comps, XSTRDUP(MTYPE_COMPLETION, cfg->name));
928 0 : }
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 16 : void log_5424_cmd_init(void)
936 : {
937 16 : hook_register(zlog_cli_show, log_5424_show);
938 :
939 16 : cmd_variable_handler_register(log_5424_var_handlers);
940 :
941 : /* CLI commands. */
942 16 : install_node(&extlog_node);
943 16 : install_default(EXTLOG_NODE);
944 :
945 16 : install_element(CONFIG_NODE, &log_5424_target_cmd);
946 16 : install_element(CONFIG_NODE, &no_log_5424_target_cmd);
947 :
948 16 : install_element(EXTLOG_NODE, &log_5424_destination_file_cmd);
949 16 : install_element(EXTLOG_NODE, &log_5424_destination_fifo_cmd);
950 16 : install_element(EXTLOG_NODE, &log_5424_destination_unix_cmd);
951 16 : install_element(EXTLOG_NODE, &log_5424_destination_journald_cmd);
952 16 : install_element(EXTLOG_NODE, &log_5424_destination_syslog_cmd);
953 16 : install_element(EXTLOG_NODE, &log_5424_destination_fd_cmd);
954 :
955 16 : install_element(EXTLOG_NODE, &log_5424_meta_cmd);
956 16 : install_element(EXTLOG_NODE, &log_5424_prio_cmd);
957 16 : install_element(EXTLOG_NODE, &log_5424_facility_cmd);
958 16 : install_element(EXTLOG_NODE, &log_5424_ts_prec_cmd);
959 16 : install_element(EXTLOG_NODE, &log_5424_ts_local_cmd);
960 16 : }
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 16 : __attribute__((_CONSTRUCTOR(475))) static void zlog_5424_startup_init(void)
969 : {
970 16 : hook_register(frr_early_init, log_5424_early_init);
971 16 : hook_register(zlog_rotate, log_5424_rotate);
972 16 : hook_register(frr_fini, log_5424_fini);
973 16 : }
974 :
975 16 : static int log_5424_early_init(struct thread_master *master)
976 : {
977 16 : log_5424_master = master;
978 :
979 16 : return 0;
980 : }
981 :
982 0 : static int log_5424_rotate(void)
983 : {
984 0 : struct zlog_cfg_5424_user *cfg;
985 :
986 0 : frr_each (targets, &targets, cfg)
987 0 : if (!zlog_5424_rotate(&cfg->cfg))
988 0 : zlog_err(
989 : "log rotation on extended log target %s failed",
990 : cfg->name);
991 :
992 0 : return 0;
993 : }
994 :
995 16 : static int log_5424_fini(void)
996 : {
997 16 : struct zlog_cfg_5424_user *cfg;
998 :
999 16 : while ((cfg = targets_pop(&targets)))
1000 0 : log_5424_free(cfg, true);
1001 :
1002 16 : log_5424_master = NULL;
1003 :
1004 16 : return 0;
1005 : }
|