]> git.proxmox.com Git - mirror_frr.git/commitdiff
watchquagga: add "write integrated"
authorDavid Lamparter <equinox@opensourcerouting.org>
Wed, 9 Nov 2016 13:15:34 +0000 (14:15 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Wed, 9 Nov 2016 13:29:39 +0000 (14:29 +0100)
This new command - available for internal use by vtysh and explicit
usage by users - calls "vtysh -w" from watchquagga.  This ensures vtysh
is run with privileges to actually write the integrated-config file.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
configure.ac
lib/command.h
lib/vty.c
vtysh/Makefile.am
watchquagga/Makefile.am
watchquagga/watchquagga.c
watchquagga/watchquagga.h [new file with mode: 0644]
watchquagga/watchquagga_vty.c [new file with mode: 0644]

index 3ca5fe8523ddc1720894902e81d1445103660598..c99ba222d6d7f8823c8edb20c9b740df870378f0 100755 (executable)
@@ -1737,6 +1737,17 @@ AC_DEFINE_UNQUOTED(ISIS_VTYSH_PATH, "$quagga_statedir/isisd.vty",isisd vty socke
 AC_DEFINE_UNQUOTED(PIM_VTYSH_PATH, "$quagga_statedir/pimd.vty",pimd vty socket)
 AC_DEFINE_UNQUOTED(DAEMON_VTY_DIR, "$quagga_statedir",daemon vty directory)
 
+dnl autoconf does this, but it does it too late...
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+dnl get the full path, recursing through variables...
+vtysh_bin="$bindir/vtysh"
+for I in 1 2 3 4 5 6 7 8 9 10; do
+       eval vtysh_bin="\"$vtysh_bin\""
+done
+AC_DEFINE_UNQUOTED(VTYSH_BIN_PATH, "$vtysh_bin",path to vtysh binary)
+
 dnl -------------------------------
 dnl Quagga sources should always be 
 dnl current wrt interfaces. Dont
index e4aa10196fc890a2d3c1fe75819c65eef967536b..ba40770ba3188375bfaac14444cecddea648cacf 100644 (file)
@@ -226,6 +226,7 @@ struct cmd_token
 #define CMD_COMPLETE_LIST_MATCH  9
 #define CMD_SUCCESS_DAEMON      10
 #define CMD_ERR_NO_FILE         11
+#define CMD_SUSPEND             12
 
 /* Argc max counts. */
 #define CMD_ARGC_MAX   25
index 10ae99db07875a3b861e23f60293708a02763127..26d0b67ecf7d5492df6afd665194814f4b951449 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -2235,6 +2235,13 @@ vtysh_read (struct thread *thread)
          printf ("vtysh node: %d\n", vty->node);
 #endif /* VTYSH_DEBUG */
 
+          /* hack for asynchronous "write integrated"
+           * - other commands in "buf" will be ditched
+           * - input during pending config-write is "unsupported" */
+          if (ret == CMD_SUSPEND)
+            break;
+
+          /* warning: watchquagga hardcodes this result write */
          header[3] = ret;
          buffer_put(vty->obuf, header, 4);
 
index 58ffdfca26d5d0e99fcbdfc572b44923578c8d28..0ff2d738f01984f092fd6d3d03b346a2b658be17 100644 (file)
@@ -86,6 +86,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \
                  $(top_srcdir)/zebra/zebra_fpm.c \
                  $(top_srcdir)/zebra/zebra_ptm.c \
                  $(top_srcdir)/zebra/zebra_mpls_vty.c \
+                 $(top_srcdir)/watchquagga/watchquagga_vty.c \
                  $(BGP_VNC_RFAPI_SRC) $(BGP_VNC_RFP_SRC)
 
 vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl
index 1f05f26cedd66593b8b9175e7498b11f49c0aaee..43f743eba2f151ab989fd12efe38e214a8ceb73c 100644 (file)
@@ -7,5 +7,7 @@ AM_CFLAGS = $(WERROR)
 
 sbin_PROGRAMS = watchquagga
 
-watchquagga_SOURCES = watchquagga.c
+noinst_HEADERS = watchquagga.h
+
+watchquagga_SOURCES = watchquagga.c watchquagga_vty.c
 watchquagga_LDADD = ../lib/libzebra.la @LIBCAP@
index aa5d1bcd80dc07d2f5f40a9a28d4c5f4f924e929..70b35f775d03b8717c84f90463de98d063e2657e 100644 (file)
 #include <network.h>
 #include <sigevent.h>
 #include <lib/version.h>
+#include "command.h"
+
 #include <getopt.h>
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <memory.h>
 #include <systemd.h>
 
+#include "watchquagga.h"
+
 #ifndef MIN
 #define MIN(X,Y) (((X) <= (Y)) ? (X) : (Y))
 #endif
@@ -437,6 +441,12 @@ sigchild(void)
       return;
     }
 
+  if (child == integrated_write_pid)
+    {
+      integrated_write_sigchld(status);
+      return;
+    }
+
   if ((restart = find_child(child)) != NULL)
     {
       name = restart->name;
@@ -775,9 +785,9 @@ try_connect(struct daemon *dmn)
       return -1;
     }
 
-  if (set_nonblocking(sock) < 0)
+  if (set_nonblocking(sock) < 0 || set_cloexec(sock) < 0)
     {
-      zlog_err("%s(%s): set_nonblocking(%d) failed",
+      zlog_err("%s(%s): set_nonblocking/cloexec(%d) failed",
               __func__, addr.sun_path, sock);
       close(sock);
       return -1;
@@ -1028,6 +1038,13 @@ translate_blanks(const char *cmd, const char *blankstr)
   return res;
 }
 
+struct zebra_privs_t watchquagga_privs =
+{
+#ifdef VTY_GROUP
+  .vty_group = VTY_GROUP,
+#endif
+};
+
 int
 main(int argc, char **argv)
 {
@@ -1283,7 +1300,15 @@ main(int argc, char **argv)
     }
       
   gs.restart.interval = gs.min_restart_interval;
+
+  zprivs_init (&watchquagga_privs);
+
   master = thread_master_create();
+  cmd_init(1);
+  vty_init(master);
+  watchquagga_vty_init();
+  vty_serv_sock(NULL, 0, WATCHQUAGGA_VTYSH_PATH);
+
   systemd_send_started (master, 0);
   signal_init (master, array_size(my_signals), my_signals);
   srandom(time(NULL));
diff --git a/watchquagga/watchquagga.h b/watchquagga/watchquagga.h
new file mode 100644 (file)
index 0000000..ecadf22
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+    Common definitions for watchquagga API socket.
+
+    Copyright (C) 2016  David Lamparter for NetDEF, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef QUAGGA_WATCHQUAGGA_H
+#define QUAGGA_WATCHQUAGGA_H
+
+extern void watchquagga_vty_init(void);
+
+extern pid_t integrated_write_pid;
+extern void integrated_write_sigchld(int status);
+
+#endif /* QUAGGA_WATCHQUAGGA_H */
diff --git a/watchquagga/watchquagga_vty.c b/watchquagga/watchquagga_vty.c
new file mode 100644 (file)
index 0000000..bf41337
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+    watchquagga CLI functions.
+
+    Copyright (C) 2016  David Lamparter for NetDEF, Inc.
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <zebra.h>
+#include <sys/wait.h>
+
+#include "memory.h"
+#include "log.h"
+#include "vty.h"
+#include "command.h"
+
+#include "watchquagga.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 Quagga.conf file\n")
+{
+       pid_t child;
+       sigset_t oldmask, sigmask;
+
+       if (integrated_write_pid != -1) {
+               vty_out(vty, "%% configuration write already in progress.%s",
+                               VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       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.%s",
+                               safe_strerror(errno), VTY_NEWLINE);
+               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 watchquagga'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... */
+       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);
+}
+
+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");
+       }
+
+       /* 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 watchquagga_vty_init(void)
+{
+       integrated_write_pid = -1;
+       install_element(ENABLE_NODE, &config_write_integrated_cmd);
+}