summaryrefslogtreecommitdiffstats
path: root/watchfrr/watchfrr_vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'watchfrr/watchfrr_vty.c')
-rw-r--r--watchfrr/watchfrr_vty.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/watchfrr/watchfrr_vty.c b/watchfrr/watchfrr_vty.c
new file mode 100644
index 0000000..3afc767
--- /dev/null
+++ b/watchfrr/watchfrr_vty.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * watchfrr CLI functions.
+ *
+ * Copyright (C) 2016 David Lamparter for NetDEF, Inc.
+ */
+
+#include <zebra.h>
+#include <sys/wait.h>
+
+#include "memory.h"
+#include "log.h"
+#include "log_vty.h"
+#include "vty.h"
+#include "command.h"
+
+#include "watchfrr.h"
+
+pid_t integrated_write_pid;
+static int integrated_result_fd;
+
+DEFUN(config_write_integrated,
+ config_write_integrated_cmd,
+ "write integrated",
+ "Write running configuration to memory, network, or terminal\n"
+ "Write integrated all-daemon frr.conf file\n")
+{
+ pid_t child;
+ sigset_t oldmask, sigmask;
+
+ const char *e_inprog = "Configuration write already in progress.";
+ const char *e_dmn = "Not all daemons are up, cannot write config.";
+
+ if (integrated_write_pid != -1) {
+ vty_out(vty, "%% %s\n", e_inprog);
+ return CMD_WARNING;
+ }
+
+ /* check that all daemons are up before clobbering config */
+ if (!check_all_up()) {
+ vty_out(vty, "%% %s\n", e_dmn);
+ /*
+ * vtysh interprets this return value to mean that it should
+ * not try to write the config itself
+ */
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ /* need to temporarily block SIGCHLD because it could arrive between
+ * fork() call and setting the integrated_write_pid variable. This
+ * would mean the completion call gets lost and this hangs forever.
+ */
+ sigemptyset(&oldmask);
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &sigmask, &oldmask);
+
+ child = fork();
+ if (child == -1) {
+ vty_out(vty, "%% configuration write fork() failed: %s.\n",
+ safe_strerror(errno));
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ return CMD_WARNING;
+ }
+ if (child != 0) {
+ /* note: the VTY won't write a command return value to vtysh;
+ * the
+ * session temporarily enters an intentional "hang" state. This
+ * is
+ * to make sure latency in vtysh doing the config write (several
+ * seconds is not rare to see) does not interfere with
+ * watchfrr's
+ * supervisor job.
+ *
+ * The fd is duplicated here so we don't need to hold a vty
+ * pointer
+ * (which could become invalid in the meantime).
+ */
+ integrated_write_pid = child;
+ integrated_result_fd = dup(vty->wfd);
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ return CMD_SUSPEND;
+ }
+
+ /* redirect stdout/stderr to vty session. Note vty->wfd is marked
+ * CLOEXEC, but dup2 will clear that flag. */
+ dup2(vty->wfd, 1);
+ dup2(vty->wfd, 2);
+
+ /* don't allow the user to pass parameters, we're root here!
+ * should probably harden vtysh at some point too... */
+ if (pathspace)
+ execl(VTYSH_BIN_PATH, "vtysh", "-N", pathspace, "-w", NULL);
+ else
+ execl(VTYSH_BIN_PATH, "vtysh", "-w", NULL);
+
+ /* unbuffered write; we just messed with stdout... */
+ char msg[512];
+ snprintf(msg, sizeof(msg), "error executing %s: %s\n", VTYSH_BIN_PATH,
+ safe_strerror(errno));
+ write(1, msg, strlen(msg));
+ exit(1);
+}
+
+DEFUN_NOSH (show_debugging_watchfrr,
+ show_debugging_watchfrr_cmd,
+ "show debugging [watchfrr]",
+ SHOW_STR
+ DEBUG_STR
+ WATCHFRR_STR)
+{
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_watchfrr,
+ show_watchfrr_cmd,
+ "show watchfrr",
+ SHOW_STR
+ WATCHFRR_STR)
+{
+ watchfrr_status(vty);
+ return CMD_SUCCESS;
+}
+
+/* we don't have the other logging commands since watchfrr only accepts
+ * log config through command line options
+ */
+DEFUN_NOSH (show_logging,
+ show_logging_cmd,
+ "show logging",
+ SHOW_STR
+ "Show current logging configuration\n")
+{
+ log_show_syslog(vty);
+ return CMD_SUCCESS;
+}
+
+#include "watchfrr/watchfrr_vty_clippy.c"
+
+DEFPY (watchfrr_ignore_daemon,
+ watchfrr_ignore_daemon_cmd,
+ "[no] watchfrr ignore DAEMON$dname",
+ NO_STR
+ "Watchfrr Specific sub-command\n"
+ "Ignore a specified daemon when it does not respond to echo request\n"
+ "The daemon to ignore\n")
+{
+ watchfrr_set_ignore_daemon(vty, dname, no ? false : true );
+
+ return CMD_SUCCESS;
+}
+
+void integrated_write_sigchld(int status)
+{
+ uint8_t reply[4] = {0, 0, 0, CMD_WARNING};
+
+ if (WIFEXITED(status)) {
+ zlog_info("configuration write completed with exit code %d",
+ WEXITSTATUS(status));
+ reply[3] = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ zlog_warn("configuration write terminated by signal %d",
+ WTERMSIG(status));
+ } else {
+ zlog_warn("configuration write terminated");
+ }
+
+ if (reply[3] != CMD_SUCCESS) {
+ /* failure might be silent in vtysh without this */
+ static const char msg[] = "% Configuration write failed.\n";
+ write(integrated_result_fd, msg, strlen(msg));
+ }
+
+ /* don't care about failures here, if the connection is broken the
+ * return value will just be lost. */
+ write(integrated_result_fd, reply, sizeof(reply));
+ close(integrated_result_fd);
+
+ integrated_write_pid = -1;
+}
+
+void watchfrr_vty_init(void)
+{
+ integrated_write_pid = -1;
+ install_element(ENABLE_NODE, &config_write_integrated_cmd);
+ install_element(ENABLE_NODE, &show_debugging_watchfrr_cmd);
+
+ install_element(ENABLE_NODE, &watchfrr_ignore_daemon_cmd);
+
+ install_element(CONFIG_NODE, &show_debugging_watchfrr_cmd);
+ install_element(VIEW_NODE, &show_watchfrr_cmd);
+ install_element(VIEW_NODE, &show_logging_cmd);
+}