watchquagga: add "write integrated"

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>
This commit is contained in:
David Lamparter 2016-11-09 14:15:34 +01:00
parent 46d5d8ec82
commit 95c4aff294
8 changed files with 207 additions and 3 deletions

View file

@ -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

View 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

View file

@ -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);

View 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

View 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@

View file

@ -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));

29
watchquagga/watchquagga.h Normal file
View file

@ -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 */

View file

@ -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);
}