summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xconfigure.ac11
-rw-r--r--lib/command.h1
-rw-r--r--lib/vty.c7
-rw-r--r--vtysh/Makefile.am1
-rw-r--r--watchquagga/Makefile.am4
-rw-r--r--watchquagga/watchquagga.c29
-rw-r--r--watchquagga/watchquagga.h29
-rw-r--r--watchquagga/watchquagga_vty.c128
8 files changed, 207 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac
index 3ca5fe852..c99ba222d 100755
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/lib/command.h b/lib/command.h
index e4aa10196..ba40770ba 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -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
diff --git a/lib/vty.c b/lib/vty.c
index 10ae99db0..26d0b67ec 100644
--- 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);
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index 58ffdfca2..0ff2d738f 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -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
diff --git a/watchquagga/Makefile.am b/watchquagga/Makefile.am
index 1f05f26ce..43f743eba 100644
--- a/watchquagga/Makefile.am
+++ b/watchquagga/Makefile.am
@@ -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@
diff --git a/watchquagga/watchquagga.c b/watchquagga/watchquagga.c
index aa5d1bcd8..70b35f775 100644
--- a/watchquagga/watchquagga.c
+++ b/watchquagga/watchquagga.c
@@ -24,12 +24,16 @@
#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
index 000000000..ecadf22c5
--- /dev/null
+++ b/watchquagga/watchquagga.h
@@ -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
index 000000000..bf413376a
--- /dev/null
+++ b/watchquagga/watchquagga_vty.c
@@ -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);
+}