]> git.proxmox.com Git - mirror_frr.git/blob - watchfrr/watchfrr_vty.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / watchfrr / watchfrr_vty.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * watchfrr CLI functions.
4 *
5 * Copyright (C) 2016 David Lamparter for NetDEF, Inc.
6 */
7
8 #include <zebra.h>
9 #include <sys/wait.h>
10
11 #include "memory.h"
12 #include "log.h"
13 #include "log_vty.h"
14 #include "vty.h"
15 #include "command.h"
16
17 #include "watchfrr.h"
18
19 pid_t integrated_write_pid;
20 static int integrated_result_fd;
21
22 DEFUN(config_write_integrated,
23 config_write_integrated_cmd,
24 "write integrated",
25 "Write running configuration to memory, network, or terminal\n"
26 "Write integrated all-daemon frr.conf file\n")
27 {
28 pid_t child;
29 sigset_t oldmask, sigmask;
30
31 const char *e_inprog = "Configuration write already in progress.";
32 const char *e_dmn = "Not all daemons are up, cannot write config.";
33
34 if (integrated_write_pid != -1) {
35 vty_out(vty, "%% %s\n", e_inprog);
36 return CMD_WARNING;
37 }
38
39 /* check that all daemons are up before clobbering config */
40 if (!check_all_up()) {
41 vty_out(vty, "%% %s\n", e_dmn);
42 /*
43 * vtysh interprets this return value to mean that it should
44 * not try to write the config itself
45 */
46 return CMD_WARNING_CONFIG_FAILED;
47 }
48
49 fflush(stdout);
50 fflush(stderr);
51
52 /* need to temporarily block SIGCHLD because it could arrive between
53 * fork() call and setting the integrated_write_pid variable. This
54 * would mean the completion call gets lost and this hangs forever.
55 */
56 sigemptyset(&oldmask);
57 sigemptyset(&sigmask);
58 sigaddset(&sigmask, SIGCHLD);
59 sigprocmask(SIG_BLOCK, &sigmask, &oldmask);
60
61 child = fork();
62 if (child == -1) {
63 vty_out(vty, "%% configuration write fork() failed: %s.\n",
64 safe_strerror(errno));
65 sigprocmask(SIG_SETMASK, &oldmask, NULL);
66 return CMD_WARNING;
67 }
68 if (child != 0) {
69 /* note: the VTY won't write a command return value to vtysh;
70 * the
71 * session temporarily enters an intentional "hang" state. This
72 * is
73 * to make sure latency in vtysh doing the config write (several
74 * seconds is not rare to see) does not interfere with
75 * watchfrr's
76 * supervisor job.
77 *
78 * The fd is duplicated here so we don't need to hold a vty
79 * pointer
80 * (which could become invalid in the meantime).
81 */
82 integrated_write_pid = child;
83 integrated_result_fd = dup(vty->wfd);
84 sigprocmask(SIG_SETMASK, &oldmask, NULL);
85 return CMD_SUSPEND;
86 }
87
88 /* redirect stdout/stderr to vty session. Note vty->wfd is marked
89 * CLOEXEC, but dup2 will clear that flag. */
90 dup2(vty->wfd, 1);
91 dup2(vty->wfd, 2);
92
93 /* don't allow the user to pass parameters, we're root here!
94 * should probably harden vtysh at some point too... */
95 if (pathspace)
96 execl(VTYSH_BIN_PATH, "vtysh", "-N", pathspace, "-w", NULL);
97 else
98 execl(VTYSH_BIN_PATH, "vtysh", "-w", NULL);
99
100 /* unbuffered write; we just messed with stdout... */
101 char msg[512];
102 snprintf(msg, sizeof(msg), "error executing %s: %s\n", VTYSH_BIN_PATH,
103 safe_strerror(errno));
104 write(1, msg, strlen(msg));
105 exit(1);
106 }
107
108 DEFUN_NOSH (show_debugging_watchfrr,
109 show_debugging_watchfrr_cmd,
110 "show debugging [watchfrr]",
111 SHOW_STR
112 DEBUG_STR
113 WATCHFRR_STR)
114 {
115 cmd_show_lib_debugs(vty);
116
117 return CMD_SUCCESS;
118 }
119
120 DEFUN (show_watchfrr,
121 show_watchfrr_cmd,
122 "show watchfrr",
123 SHOW_STR
124 WATCHFRR_STR)
125 {
126 watchfrr_status(vty);
127 return CMD_SUCCESS;
128 }
129
130 /* we don't have the other logging commands since watchfrr only accepts
131 * log config through command line options
132 */
133 DEFUN_NOSH (show_logging,
134 show_logging_cmd,
135 "show logging",
136 SHOW_STR
137 "Show current logging configuration\n")
138 {
139 log_show_syslog(vty);
140 return CMD_SUCCESS;
141 }
142
143 #include "watchfrr/watchfrr_vty_clippy.c"
144
145 DEFPY (watchfrr_ignore_daemon,
146 watchfrr_ignore_daemon_cmd,
147 "[no] watchfrr ignore DAEMON$dname",
148 NO_STR
149 "Watchfrr Specific sub-command\n"
150 "Ignore a specified daemon when it does not respond to echo request\n"
151 "The daemon to ignore\n")
152 {
153 watchfrr_set_ignore_daemon(vty, dname, no ? false : true );
154
155 return CMD_SUCCESS;
156 }
157
158 void integrated_write_sigchld(int status)
159 {
160 uint8_t reply[4] = {0, 0, 0, CMD_WARNING};
161
162 if (WIFEXITED(status)) {
163 zlog_info("configuration write completed with exit code %d",
164 WEXITSTATUS(status));
165 reply[3] = WEXITSTATUS(status);
166 } else if (WIFSIGNALED(status)) {
167 zlog_warn("configuration write terminated by signal %d",
168 WTERMSIG(status));
169 } else {
170 zlog_warn("configuration write terminated");
171 }
172
173 if (reply[3] != CMD_SUCCESS) {
174 /* failure might be silent in vtysh without this */
175 static const char msg[] = "% Configuration write failed.\n";
176 write(integrated_result_fd, msg, strlen(msg));
177 }
178
179 /* don't care about failures here, if the connection is broken the
180 * return value will just be lost. */
181 write(integrated_result_fd, reply, sizeof(reply));
182 close(integrated_result_fd);
183
184 integrated_write_pid = -1;
185 }
186
187 void watchfrr_vty_init(void)
188 {
189 integrated_write_pid = -1;
190 install_element(ENABLE_NODE, &config_write_integrated_cmd);
191 install_element(ENABLE_NODE, &show_debugging_watchfrr_cmd);
192
193 install_element(ENABLE_NODE, &watchfrr_ignore_daemon_cmd);
194
195 install_element(CONFIG_NODE, &show_debugging_watchfrr_cmd);
196 install_element(VIEW_NODE, &show_watchfrr_cmd);
197 install_element(VIEW_NODE, &show_logging_cmd);
198 }