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
#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
return;
}
+ if (child == integrated_write_pid)
+ {
+ integrated_write_sigchld(status);
+ return;
+ }
+
if ((restart = find_child(child)) != NULL)
{
name = restart->name;
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;
return res;
}
+struct zebra_privs_t watchquagga_privs =
+{
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+};
+
int
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));
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/*
+ 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);
+}