diff options
| author | Donald Sharp <sharpd@cumulusnetworks.com> | 2017-08-09 14:32:44 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-08-09 14:32:44 -0400 |
| commit | f790234f2ff2e1d6c7a230e5c1d9dac280a12d44 (patch) | |
| tree | b4815bc05f3bc7f54ebf39c773af30cce3a5d19a | |
| parent | 75d837ac60b3838e1be02156a428e183197d7ad0 (diff) | |
| parent | 999f153ecea7eccb6fd541f7b38d88b7eb8dfd40 (diff) | |
Merge pull request #892 from opensourcerouting/watchfrr-simplify
simplify watchfrr, add --terminal, improve startup logging
| -rwxr-xr-x | configure.ac | 7 | ||||
| -rw-r--r-- | doc/watchfrr.8.in | 166 | ||||
| -rw-r--r-- | ldpd/lde.c | 1 | ||||
| -rw-r--r-- | ldpd/ldpe.c | 1 | ||||
| -rw-r--r-- | lib/gitversion.pl | 2 | ||||
| -rw-r--r-- | lib/grammar_sandbox_main.c | 5 | ||||
| -rw-r--r-- | lib/libfrr.c | 290 | ||||
| -rw-r--r-- | lib/libfrr.h | 1 | ||||
| -rw-r--r-- | lib/log.c | 61 | ||||
| -rw-r--r-- | lib/log.h | 3 | ||||
| -rw-r--r-- | lib/privs.c | 17 | ||||
| -rw-r--r-- | lib/privs.h | 1 | ||||
| -rw-r--r-- | lib/vty.c | 140 | ||||
| -rw-r--r-- | lib/vty.h | 7 | ||||
| -rw-r--r-- | ospfclient/ospfclient.c | 1 | ||||
| -rw-r--r-- | redhat/daemons | 2 | ||||
| -rw-r--r-- | tests/lib/cli/common_cli.c | 5 | ||||
| -rw-r--r-- | tests/lib/test_privs.c | 1 | ||||
| -rw-r--r-- | tools/etc/frr/daemons.conf | 2 | ||||
| -rw-r--r-- | watchfrr/Makefile.am | 2 | ||||
| -rw-r--r-- | watchfrr/watchfrr.c | 330 |
21 files changed, 577 insertions, 468 deletions
diff --git a/configure.ac b/configure.ac index 6b5cd19a5f..c818c16e6b 100755 --- a/configure.ac +++ b/configure.ac @@ -1025,6 +1025,13 @@ fi LIBS="$TMPLIBS" AC_SUBST(LIBM) +AC_CHECK_FUNCS([ppoll], [ + AC_DEFINE([HAVE_PPOLL], 1, [have Linux/BSD ppoll()]) +]) +AC_CHECK_FUNCS([pollts], [ + AC_DEFINE([HAVE_POLLTS], 1, [have NetBSD pollts()]) +]) + dnl --------------- dnl other functions dnl --------------- diff --git a/doc/watchfrr.8.in b/doc/watchfrr.8.in index 82098e1b0d..782ac7b46e 100644 --- a/doc/watchfrr.8.in +++ b/doc/watchfrr.8.in @@ -20,59 +20,6 @@ daemon's VTY UNIX stream socket, and send echo commands to ensure the daemon responds. When the daemon crashes, EOF is received from the socket, so that watchfrr can react immediately. .PP -This program can run in one of the following 5 modes: -.TP -.B Mode 0: monitor -In this mode, the program serves as a monitor and reports status changes. -.IP -Example usage: watchfrr \-d zebra ospfd bgpd -.TP -.B Mode 1: global restart -In this mode, whenever a daemon hangs or crashes, the given command is used -to restart all watched daemons. -.IP -Example usage: watchfrr \-dz \e -.br --R '/sbin/service zebra restart; /sbin/service ospfd restart' \e -.br -zebra ospfd -.TP -.B Mode 2: individual daemon restart -In this mode, whenever a single daemon hangs or crashes, the given command -is used to restart this daemon only. -.IP -Example usage: watchfrr \-dz \-r '/sbin/service %s restart' \e -.br -zebra ospfd bgpd -.TP -.B Mode 3: phased zebra restart -In this mode, whenever a single daemon hangs or crashes, the given command -is used to restart this daemon only. The only exception is the zebra -daemon; in this case, the following steps are taken: (1) all other daemons -are stopped, (2) zebra is restarted, and (3) other daemons are started -again. -.IP -Example usage: watchfrr \-adz \-r '/sbin/service %s restart' \e -.br -\-s '/sbin/service %s start' \e -.br -\-k '/sbin/service %s stop' zebra ospfd bgpd -.TP -.B Mode 4: phased global restart for any failure -In this mode, whenever a single daemon hangs or crashes, the following -steps are taken: (1) all other daemons are stopped, (2) zebra is restarted, -and (3) other daemons are started again. -.IP -Example usage: watchfrr \-Adz \-r '/sbin/service %s restart' \e -.br -\-s '/sbin/service %s start' \e -.br -\-k '/sbin/service %s stop' zebra ospfd bgpd -.PP -Important: It is believed that mode 2 (individual daemon restart) is not -safe, and mode 3 (phased zebra restart) may not be safe with certain -routing daemons. -.PP In order to avoid restarting the daemons in quick succession, you can supply the .B \-m @@ -87,6 +34,36 @@ the restart delay is set to the value of otherwise the interval is doubled (but capped at the value of .BR \-M ). .SH OPTIONS +The following 3 options specify scripts that +.B watchfrr +uses to perform start/stop/restart actions. These options are mandatory +unless the +.B --dry +option is used: +.TP +.BI \-s " command" "\fR, \fB\-\-start\-command " command +Supply a Bourne shell +.I command +to start a single daemon. The command string should contain the '%s' +placeholder to be substituted with the daemon name. +.TP +.BI \-k " command" "\fR, \fB\-\-kill\-command " command +Supply a Bourne shell +.I command +to stop a single daemon. The command string should contain the '%s' +placeholder to be substituted with the daemon name. +.TP +.BI \-r " command" "\fR, \fB\-\-restart " command +Supply a Bourne shell +.I command +to restart a single daemon. The command string should contain the '%s' +placeholder to be substituted with the daemon name. +.PP +Other options: +.TP +.BI \-\-dry +Run watchfrr in "dry-run" mode, only monitoring the specified daemons but not +performing any start/stop/restart actions. .TP .BR \-d ", " \-\-daemon Run in daemon mode. When supplied, error messages are sent to Syslog @@ -97,10 +74,6 @@ Set the VTY socket .I directory (the default value is "/var/run/frr"). .TP -.BR \-e ", " \-\-no\-echo -Do not ping the daemons to test whether they respond. This option is -necessary if one or more daemons do not support the echo command. -.TP .BI \-l " level" "\fR, \fB\-\-loglevel " level Set the logging .I level @@ -131,68 +104,6 @@ Set the restart (kill) timeout in seconds (the default value is "20"). If any background jobs are still running after this period has elapsed, they will be killed. .TP -.BI \-r " command" "\fR, \fB\-\-restart " command -Supply a Bourne shell -.I command -to restart a single daemon. The command string should contain the '%s' -placeholder to be substituted with the daemon name. -.IP -Note that -.B \-r -and -.B \-R -options are not compatible. -.TP -.BI \-s " command" "\fR, \fB\-\-start\-command " command -Supply a Bourne shell -.I command -to start a single daemon. The command string should contain the '%s' -placeholder to be substituted with the daemon name. -.TP -.BI \-k " command" "\fR, \fB\-\-kill\-command " command -Supply a Bourne shell -.I command -to stop a single daemon. The command string should contain the '%s' -placeholder to be substituted with the daemon name. -.TP -.BR \-R ", " \-\-restart\-all -When one or more daemons are shut down, try to restart them using the -Bourne shell command supplied on the command line. -.IP -Note that -.B \-r -and -.B \-R -options are not compatible. -.TP -.BR \-z ", " \-\-unresponsive\-restart -When a daemon is in an unresponsive state, treat it as being shut down for -the restart purposes. -.TP -.BR \-a ", " \-\-all\-restart -When zebra hangs or crashes, restart all daemons taking the following -steps: (1) stop all other daemons, (2) restart zebra, and (3) start other -daemons again. -.IP -Note that this option also requires -.BR \-r , -.BR \-s , -and -.B \-k -options to be specified. -.TP -.BR \-A ", " \-\-always\-all\-restart -When any daemon (i.e., not just zebra) hangs or crashes, restart all -daemons taking the following steps: (1) stop all other daemons, (2) restart -zebra, and (3) start other daemons again. -.IP -Note that this option also requires -.BR \-r , -.BR \-s , -and -.B \-k -options to be specified. -.TP .BI \-p " filename" "\fR, \fB\-\-pid\-file " filename Set the process identifier .I filename @@ -204,9 +115,8 @@ When the supplied is found in any of the command line option arguments (i.e., .BR \-r , .BR \-s , -.BR \-k , or -.BR \-R ), +.BR \-k ), replace it with a space. .IP This is an ugly hack to circumvent problems with passing the command line @@ -217,6 +127,20 @@ Display the version information and exit. .TP .BR \-h ", " \-\-help Display the usage information and exit. +.SH PREVIOUS OPTIONS +Prior versions of \fBwatchfrr\fR supported some additional options that no +longer exist: +.IP +.BR \-a ,\ \-A ,\ \-e ,\ \-R ,\ \-z +.PP +The \fB-a\fR, \fB-A\fR and \fB-R\fR options were used to select alternate +monitoring modes that offered different patterns of restarting daemons. The +"correct" mode (phased restart) is now the default. The \fB-e\fR and \fB-z\fR +options used to disable some monitoring aspects, watchfrr now always has all +monitoring features enabled. +.PP +Removing these options should result in correct operation, if it does not +please file a bug report. .SH SEE ALSO .BR zebra (8), .BR bgpd (8), diff --git a/ldpd/lde.c b/ldpd/lde.c index 588ccd6952..582582babf 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -164,6 +164,7 @@ lde_init(struct ldpd_init *init) /* drop privileges */ lde_privs.user = init->user; lde_privs.group = init->group; + zprivs_preinit(&lde_privs); zprivs_init(&lde_privs); /* start the LIB garbage collector */ diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index b2f9fdce55..1c0a8bdc84 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -142,6 +142,7 @@ ldpe_init(struct ldpd_init *init) /* drop privileges */ ldpe_privs.user = init->user; ldpe_privs.group = init->group; + zprivs_preinit(&ldpe_privs); zprivs_init(&ldpe_privs); /* listen on ldpd control socket */ diff --git a/lib/gitversion.pl b/lib/gitversion.pl index 8ddd9ffa55..2718046d0b 100644 --- a/lib/gitversion.pl +++ b/lib/gitversion.pl @@ -4,7 +4,7 @@ use strict; my $dir = shift; chdir $dir || die "$dir: $!\n"; -my $gitdesc = `git describe --always --dirty || echo -- \"0-gUNKNOWN\"`; +my $gitdesc = `git describe --always --first-parent --tags --dirty --match 'frr-*' || echo -- \"0-gUNKNOWN\"`; chomp $gitdesc; my $gitsuffix = ($gitdesc =~ /([0-9a-fA-F]{7}(-dirty)?)$/) ? "-g$1" : "-gUNKNOWN"; diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index c236d2c7be..89b0993d1d 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -26,10 +26,11 @@ #include "command.h" #include "memory_vty.h" -static void vty_do_exit(void) +static void vty_do_exit(int isexit) { printf("\nend.\n"); - exit(0); + if (!isexit) + exit(0); } struct thread_master *master; diff --git a/lib/libfrr.c b/lib/libfrr.c index 022296b3fb..31d93009fa 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -20,8 +20,12 @@ #include <zebra.h> +#include <sys/types.h> +#include <sys/wait.h> + #include "libfrr.h" #include "getopt.h" +#include "privs.h" #include "vty.h" #include "command.h" #include "version.h" @@ -29,6 +33,7 @@ #include "zclient.h" #include "log_int.h" #include "module.h" +#include "network.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) @@ -91,12 +96,15 @@ static const struct option lo_cfg_pid_dry[] = { {"pid_file", required_argument, NULL, 'i'}, {"config_file", required_argument, NULL, 'f'}, {"dryrun", no_argument, NULL, 'C'}, + {"terminal", no_argument, NULL, 't'}, {NULL}}; static const struct optspec os_cfg_pid_dry = { - "f:i:C", + "f:i:Ct", " -f, --config_file Set configuration file name\n" " -i, --pid_file Set process identifier file name\n" - " -C, --dryrun Check configuration for validity and exit\n", + " -C, --dryrun Check configuration for validity and exit\n" + " -t, --terminal Open terminal session on stdio\n" + " -d -t Daemonize after terminal session ends\n", lo_cfg_pid_dry}; @@ -230,6 +238,11 @@ static int frr_opt(int opt) return 1; di->dryrun = 1; break; + case 't': + if (di->flags & FRR_NO_CFG_PID_DRY) + return 1; + di->terminal = 1; + break; case 'z': if (di->flags & FRR_NO_ZCLIENT) return 1; @@ -320,6 +333,49 @@ int frr_getopt(int argc, char *const argv[], int *longindex) return opt; } +static void frr_mkdir(const char *path, bool strip) +{ + char buf[256]; + mode_t prev; + int ret; + struct zprivs_ids_t ids; + + if (strip) { + char *slash = strrchr(path, '/'); + size_t plen; + if (!slash) + return; + plen = slash - path; + if (plen > sizeof(buf) - 1) + return; + memcpy(buf, path, plen); + buf[plen] = '\0'; + path = buf; + } + + /* o+rx (..5) is needed for the frrvty group to work properly; + * without it, users in the frrvty group can't access the vty sockets. + */ + prev = umask(0022); + ret = mkdir(path, 0755); + umask(prev); + + if (ret != 0) { + /* if EEXIST, return without touching the permissions, + * so user-set custom permissions are left in place + */ + if (errno == EEXIST) + return; + + zlog_warn("failed to mkdir \"%s\": %s", path, strerror(errno)); + return; + } + + zprivs_get_ids(&ids); + if (chown(path, ids.uid_normal, ids.gid_normal)) + zlog_warn("failed to chown \"%s\": %s", path, strerror(errno)); +} + static struct thread_master *master; struct thread_master *frr_init(void) { @@ -335,12 +391,24 @@ struct thread_master *frr_init(void) snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]", di->logname, di->instance); + zprivs_preinit(di->privs); + openzlog(di->progname, di->logname, di->instance, LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON); #if defined(HAVE_CUMULUS) zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl); #endif + /* don't mkdir these as root... */ + if (!(di->flags & FRR_NO_PRIVSEP)) { + if (!di->pid_file || !di->vty_path) + frr_mkdir(frr_vtydir, false); + if (di->pid_file) + frr_mkdir(di->pid_file, true); + if (di->vty_path) + frr_mkdir(di->vty_path, true); + } + frrmod_init(di->module); while (modules) { modules = (oc = modules)->next; @@ -367,6 +435,134 @@ struct thread_master *frr_init(void) return master; } +static int rcvd_signal = 0; + +static void rcv_signal(int signum) +{ + rcvd_signal = signum; + /* poll() is interrupted by the signal; handled below */ +} + +static void frr_daemon_wait(int fd) +{ + struct pollfd pfd[1]; + int ret; + pid_t exitpid; + int exitstat; + sigset_t sigs, prevsigs; + + sigemptyset(&sigs); + sigaddset(&sigs, SIGTSTP); + sigaddset(&sigs, SIGQUIT); + sigaddset(&sigs, SIGINT); + sigprocmask(SIG_BLOCK, &sigs, &prevsigs); + + struct sigaction sa = { + .sa_handler = rcv_signal, .sa_flags = SA_RESETHAND, + }; + sigemptyset(&sa.sa_mask); + sigaction(SIGTSTP, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + do { + char buf[1]; + ssize_t nrecv; + + pfd[0].fd = fd; + pfd[0].events = POLLIN; + + rcvd_signal = 0; + +#if defined(HAVE_PPOLL) + ret = ppoll(pfd, 1, NULL, &prevsigs); +#elif defined(HAVE_POLLTS) + ret = pollts(pfd, 1, NULL, &prevsigs); +#else + /* racy -- only used on FreeBSD 9 */ + sigset_t tmpsigs; + sigprocmask(SIG_SETMASK, &prevsigs, &tmpsigs); + ret = poll(pfd, 1, -1); + sigprocmask(SIG_SETMASK, &tmpsigs, NULL); +#endif + if (ret < 0 && errno != EINTR && errno != EAGAIN) { + perror("poll()"); + exit(1); + } + switch (rcvd_signal) { + case SIGTSTP: + send(fd, "S", 1, 0); + do { + nrecv = recv(fd, buf, sizeof(buf), 0); + } while (nrecv == -1 + && (errno == EINTR || errno == EAGAIN)); + + raise(SIGTSTP); + sigaction(SIGTSTP, &sa, NULL); + send(fd, "R", 1, 0); + break; + case SIGINT: + send(fd, "I", 1, 0); + break; + case SIGQUIT: + send(fd, "Q", 1, 0); + break; + } + } while (ret <= 0); + + exitpid = waitpid(-1, &exitstat, WNOHANG); + if (exitpid == 0) + /* child successfully went to main loop & closed socket */ + exit(0); + + /* child failed one way or another ... */ + if (WIFEXITED(exitstat)) + fprintf(stderr, "%s failed to start, exited %d\n", di->name, + WEXITSTATUS(exitstat)); + else if (WIFSIGNALED(exitstat)) + fprintf(stderr, "%s crashed in startup, signal %d\n", di->name, + WTERMSIG(exitstat)); + else + fprintf(stderr, "%s failed to start, unknown problem\n", + di->name); + exit(1); +} + +static int daemon_ctl_sock = -1; + +static void frr_daemonize(void) +{ + int fds[2]; + pid_t pid; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) { + perror("socketpair() for daemon control"); + exit(1); + } + set_cloexec(fds[0]); + set_cloexec(fds[1]); + + pid = fork(); + if (pid < 0) { + perror("fork()"); + exit(1); + } + if (pid == 0) { + /* child */ + close(fds[0]); + if (setsid() < 0) { + perror("setsid()"); + exit(1); + } + + daemon_ctl_sock = fds[1]; + return; + } + + close(fds[1]); + frr_daemon_wait(fds[0]); +} + void frr_config_fork(void) { hook_call(frr_late_init, master); @@ -385,11 +581,8 @@ void frr_config_fork(void) if (di->dryrun) exit(0); - /* Daemonize. */ - if (di->daemon_mode && daemon(0, 0) < 0) { - zlog_err("Zebra daemon failed: %s", strerror(errno)); - exit(1); - } + if (di->daemon_mode || di->terminal) + frr_daemonize(); if (!di->pid_file) di->pid_file = pidfile_default; @@ -417,6 +610,67 @@ void frr_vty_serv(void) vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path); } +static void frr_terminal_close(int isexit) +{ + if (daemon_ctl_sock != -1) { + close(daemon_ctl_sock); + daemon_ctl_sock = -1; + } + + if (!di->daemon_mode || isexit) { + printf("\n%s exiting\n", di->name); + if (!isexit) + raise(SIGINT); + return; + } else { + printf("\n%s daemonizing\n", di->name); + fflush(stdout); + } + + int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY); + dup2(nullfd, 0); + dup2(nullfd, 1); + dup2(nullfd, 2); + close(nullfd); +} + +static struct thread *daemon_ctl_thread = NULL; + +static int frr_daemon_ctl(struct thread *t) +{ + char buf[1]; + ssize_t nr; + + nr = recv(daemon_ctl_sock, buf, sizeof(buf), 0); + if (nr < 0 && (errno == EINTR || errno == EAGAIN)) + goto out; + if (nr <= 0) + return 0; + + switch (buf[0]) { + case 'S': /* SIGTSTP */ + vty_stdio_suspend(); + send(daemon_ctl_sock, "s", 1, 0); + break; + case 'R': /* SIGTCNT [implicit] */ + vty_stdio_resume(); + break; + case 'I': /* SIGINT */ + di->daemon_mode = false; + raise(SIGINT); + break; + case 'Q': /* SIGQUIT */ + di->daemon_mode = true; + vty_stdio_close(); + break; + } + +out: + thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock, + &daemon_ctl_thread); + return 0; +} + void frr_run(struct thread_master *master) { char instanceinfo[64] = ""; @@ -430,6 +684,28 @@ void frr_run(struct thread_master *master) zlog_notice("%s %s starting: %svty@%d%s", di->name, FRR_VERSION, instanceinfo, di->vty_port, di->startinfo); + if (di->terminal) { + vty_stdio(frr_terminal_close); + if (daemon_ctl_sock != -1) { + set_nonblocking(daemon_ctl_sock); + thread_add_read(master, frr_daemon_ctl, NULL, + daemon_ctl_sock, &daemon_ctl_thread); + } + } else { + int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY); + dup2(nullfd, 0); + dup2(nullfd, 1); + dup2(nullfd, 2); + close(nullfd); + + if (daemon_ctl_sock != -1) + close(daemon_ctl_sock); + daemon_ctl_sock = -1; + } + + /* end fixed stderr startup logging */ + zlog_startup_stderr = false; + struct thread thread; while (thread_fetch(master, &thread)) thread_call(&thread); diff --git a/lib/libfrr.h b/lib/libfrr.h index 0f6ed0cb00..238a6f976d 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -49,6 +49,7 @@ struct frr_daemon_info { char *vty_sock_path; bool dryrun; bool daemon_mode; + bool terminal; const char *config_file; const char *pid_file; const char *vty_path; @@ -41,6 +41,7 @@ DEFINE_MTYPE_STATIC(LIB, ZLOG, "Logging") static int logfile_fd = -1; /* Used in signal handler. */ struct zlog *zlog_default = NULL; +bool zlog_startup_stderr = true; const char *zlog_priority[] = { "emergencies", "alerts", "critical", "errors", "warnings", @@ -172,6 +173,25 @@ static void time_print(FILE *fp, struct timestamp_control *ctl) } +static void vzlog_file(struct zlog *zl, struct timestamp_control *tsctl, + const char *proto_str, int record_priority, + int priority, FILE *fp, const char *format, + va_list args) +{ + va_list ac; + + time_print(fp, tsctl); + if (record_priority) + fprintf(fp, "%s: ", zlog_priority[priority]); + + fprintf(fp, "%s", proto_str); + va_copy(ac, args); + vfprintf(fp, format, ac); + va_end(ac); + fprintf(fp, "\n"); + fflush(fp); +} + /* va_list version of zlog. */ void vzlog(int priority, const char *format, va_list args) { @@ -210,32 +230,21 @@ void vzlog(int priority, const char *format, va_list args) sprintf(proto_str, "%s: ", zl->protoname); /* File output. */ - if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) { - va_list ac; - time_print(zl->fp, &tsctl); - if (zl->record_priority) - fprintf(zl->fp, "%s: ", zlog_priority[priority]); - fprintf(zl->fp, "%s", proto_str); - va_copy(ac, args); - vfprintf(zl->fp, format, ac); - va_end(ac); - fprintf(zl->fp, "\n"); - fflush(zl->fp); - } - - /* stdout output. */ - if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) { - va_list ac; - time_print(stdout, &tsctl); - if (zl->record_priority) - fprintf(stdout, "%s: ", zlog_priority[priority]); - fprintf(stdout, "%s", proto_str); - va_copy(ac, args); - vfprintf(stdout, format, ac); - va_end(ac); - fprintf(stdout, "\n"); - fflush(stdout); - } + if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) + vzlog_file(zl, &tsctl, proto_str, zl->record_priority, + priority, zl->fp, format, args); + + /* fixed-config logging to stderr while we're stating up & haven't + * daemonized / reached mainloop yet + * + * note the "else" on stdout output -- we don't want to print the same + * message to both stderr and stdout. */ + if (zlog_startup_stderr && priority <= LOG_WARNING) + vzlog_file(zl, &tsctl, proto_str, 1, + priority, stderr, format, args); + else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) + vzlog_file(zl, &tsctl, proto_str, zl->record_priority, + priority, stdout, format, args); /* Terminal monitor. */ if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) @@ -24,6 +24,7 @@ #include <syslog.h> #include <stdint.h> +#include <stdbool.h> #include <stdio.h> /* Here is some guidance on logging levels to use: @@ -54,6 +55,8 @@ typedef enum { } zlog_dest_t; #define ZLOG_NUM_DESTS (ZLOG_DEST_FILE+1) +extern bool zlog_startup_stderr; + /* Message structure. */ struct message { int key; diff --git a/lib/privs.c b/lib/privs.c index c971596117..eda3fb02d4 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -696,13 +696,10 @@ static int getgrouplist(const char *user, gid_t group, gid_t *groups, } #endif /* HAVE_GETGROUPLIST */ -void zprivs_init(struct zebra_privs_t *zprivs) +void zprivs_preinit(struct zebra_privs_t *zprivs) { struct passwd *pwentry = NULL; struct group *grentry = NULL; - gid_t groups[NGROUPS_MAX]; - int i, ngroups = 0; - int found = 0; if (!zprivs) { fprintf(stderr, "zprivs_init: called with NULL arg!\n"); @@ -751,6 +748,18 @@ void zprivs_init(struct zebra_privs_t *zprivs) zprivs_state.zgid = grentry->gr_gid; } +} + +void zprivs_init(struct zebra_privs_t *zprivs) +{ + gid_t groups[NGROUPS_MAX]; + int i, ngroups = 0; + int found = 0; + + /* NULL privs */ + if (!(zprivs->user || zprivs->group || zprivs->cap_num_p + || zprivs->cap_num_i)) + return; if (zprivs->user) { ngroups = sizeof(groups); diff --git a/lib/privs.h b/lib/privs.h index c18fe78add..7fe59328b2 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -74,6 +74,7 @@ struct zprivs_ids_t { }; /* initialise zebra privileges */ +extern void zprivs_preinit(struct zebra_privs_t *zprivs); extern void zprivs_init(struct zebra_privs_t *zprivs); /* drop all and terminate privileges */ extern void zprivs_terminate(struct zebra_privs_t *); @@ -1663,24 +1663,82 @@ static struct vty *vty_create(int vty_sock, union sockunion *su) /* create vty for stdio */ static struct termios stdio_orig_termios; static struct vty *stdio_vty = NULL; -static void (*stdio_vty_atclose)(void); +static bool stdio_termios = false; +static void (*stdio_vty_atclose)(int isexit); -static void vty_stdio_reset(void) +static void vty_stdio_reset(int isexit) { if (stdio_vty) { - tcsetattr(0, TCSANOW, &stdio_orig_termios); + if (stdio_termios) + tcsetattr(0, TCSANOW, &stdio_orig_termios); + stdio_termios = false; + stdio_vty = NULL; if (stdio_vty_atclose) - stdio_vty_atclose(); + stdio_vty_atclose(isexit); stdio_vty_atclose = NULL; } } -struct vty *vty_stdio(void (*atclose)()) +static void vty_stdio_atexit(void) +{ + vty_stdio_reset(1); +} + +void vty_stdio_suspend(void) +{ + if (!stdio_vty) + return; + + if (stdio_vty->t_write) + thread_cancel(stdio_vty->t_write); + if (stdio_vty->t_read) + thread_cancel(stdio_vty->t_read); + if (stdio_vty->t_timeout) + thread_cancel(stdio_vty->t_timeout); + + if (stdio_termios) + tcsetattr(0, TCSANOW, &stdio_orig_termios); + stdio_termios = false; +} + +void vty_stdio_resume(void) +{ + if (!stdio_vty) + return; + + if (!tcgetattr(0, &stdio_orig_termios)) { + struct termios termios; + + termios = stdio_orig_termios; + termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR + | IGNCR | ICRNL | IXON); + termios.c_oflag &= ~OPOST; + termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN); + termios.c_cflag &= ~(CSIZE | PARENB); + termios.c_cflag |= CS8; + tcsetattr(0, TCSANOW, &termios); + stdio_termios = true; + } + + vty_prompt(stdio_vty); + + /* Add read/write thread. */ + vty_event(VTY_WRITE, 1, stdio_vty); + vty_event(VTY_READ, 0, stdio_vty); +} + +void vty_stdio_close(void) +{ + if (!stdio_vty) + return; + vty_close(stdio_vty); +} + +struct vty *vty_stdio(void (*atclose)(int isexit)) { struct vty *vty; - struct termios termios; /* refuse creating two vtys on stdio */ if (stdio_vty) @@ -1698,23 +1756,7 @@ struct vty *vty_stdio(void (*atclose)()) vty->v_timeout = 0; strcpy(vty->address, "console"); - if (!tcgetattr(0, &stdio_orig_termios)) { - termios = stdio_orig_termios; - termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR - | IGNCR | ICRNL | IXON); - termios.c_oflag &= ~OPOST; - termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - termios.c_cflag &= ~(CSIZE | PARENB); - termios.c_cflag |= CS8; - tcsetattr(0, TCSANOW, &termios); - } - - vty_prompt(vty); - - /* Add read/write thread. */ - vty_event(VTY_WRITE, 1, vty); - vty_event(VTY_READ, 0, vty); - + vty_stdio_resume(); return vty; } @@ -1813,7 +1855,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) ret = getaddrinfo(hostname, port_str, &req, &ainfo); if (ret != 0) { - fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(ret)); + zlog_err("getaddrinfo failed: %s", gai_strerror(ret)); exit(1); } @@ -2163,7 +2205,7 @@ void vty_close(struct vty *vty) XFREE(MTYPE_VTY, vty); if (was_stdio) - vty_stdio_reset(); + vty_stdio_reset(0); } /* When time out occur output message then close connection. */ @@ -2213,23 +2255,22 @@ static void vty_read_file(FILE *confp) if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) { const char *message = NULL; + char *nl; + switch (ret) { case CMD_ERR_AMBIGUOUS: - message = - "*** Error reading config: Ambiguous command."; + message = "Ambiguous command"; break; case CMD_ERR_NO_MATCH: - message = - "*** Error reading config: There is no such command."; + message = "No such command"; break; } - fprintf(stderr, "%s\n", message); - zlog_err("%s", message); - fprintf(stderr, - "*** Error occurred processing line %u, below:\n%s\n", - line_num, vty->error_buf); - zlog_err("*** Error occurred processing line %u, below:\n%s", - line_num, vty->error_buf); + + nl = strchr(vty->error_buf, '\n'); + if (nl) + *nl = '\0'; + zlog_err("ERROR: %s on config line %u: %s", + message, line_num, vty->error_buf); } vty_close(vty); @@ -2301,8 +2342,7 @@ void vty_read_config(const char *config_file, char *config_default_dir) if (config_file != NULL) { if (!IS_DIRECTORY_SEP(config_file[0])) { if (getcwd(cwd, MAXPATHLEN) == NULL) { - fprintf(stderr, - "Failure to determine Current Working Directory %d!\n", + zlog_err("Failure to determine Current Working Directory %d!", errno); exit(1); } @@ -2316,17 +2356,14 @@ void vty_read_config(const char *config_file, char *config_default_dir) confp = fopen(fullpath, "r"); if (confp == NULL) { - fprintf(stderr, - "%s: failed to open configuration file %s: %s\n", + zlog_err("%s: failed to open configuration file %s: %s", __func__, fullpath, safe_strerror(errno)); confp = vty_use_backup_config(fullpath); if (confp) - fprintf(stderr, - "WARNING: using backup configuration file!\n"); + zlog_warn("WARNING: using backup configuration file!"); else { - fprintf(stderr, - "can't open configuration file [%s]\n", + zlog_err("can't open configuration file [%s]", config_file); exit(1); } @@ -2361,19 +2398,16 @@ void vty_read_config(const char *config_file, char *config_default_dir) #endif /* VTYSH */ confp = fopen(config_default_dir, "r"); if (confp == NULL) { - fprintf(stderr, - "%s: failed to open configuration file %s: %s\n", + zlog_err("%s: failed to open configuration file %s: %s", __func__, config_default_dir, safe_strerror(errno)); confp = vty_use_backup_config(config_default_dir); if (confp) { - fprintf(stderr, - "WARNING: using backup configuration file!\n"); + zlog_warn("WARNING: using backup configuration file!"); fullpath = config_default_dir; } else { - fprintf(stderr, - "can't open configuration file [%s]\n", + zlog_err("can't open configuration file [%s]", config_default_dir); goto tmp_free_and_out; } @@ -2883,12 +2917,12 @@ static void vty_save_cwd(void) * Hence not worrying about it too much. */ if (!chdir(SYSCONFDIR)) { - fprintf(stderr, "Failure to chdir to %s, errno: %d\n", + zlog_err("Failure to chdir to %s, errno: %d", SYSCONFDIR, errno); exit(-1); } if (getcwd(cwd, MAXPATHLEN) == NULL) { - fprintf(stderr, "Failure to getcwd, errno: %d\n", + zlog_err("Failure to getcwd, errno: %d", errno); exit(-1); } @@ -2928,7 +2962,7 @@ void vty_init(struct thread_master *master_thread) vty_master = master_thread; - atexit(vty_stdio_reset); + atexit(vty_stdio_atexit); /* Initilize server thread vector. */ Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE); @@ -257,7 +257,7 @@ extern void vty_init_vtysh(void); extern void vty_terminate(void); extern void vty_reset(void); extern struct vty *vty_new(void); -extern struct vty *vty_stdio(void (*atclose)(void)); +extern struct vty *vty_stdio(void (*atclose)(int isexit)); extern int vty_out(struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); extern void vty_read_config(const char *, char *); extern void vty_time_print(struct vty *, int); @@ -273,6 +273,11 @@ extern int vty_shell(struct vty *); extern int vty_shell_serv(struct vty *); extern void vty_hello(struct vty *); +/* ^Z / SIGTSTP handling */ +extern void vty_stdio_suspend(void); +extern void vty_stdio_resume(void); +extern void vty_stdio_close(void); + /* Send a fixed-size message to all vty terminal monitors; this should be an async-signal-safe function. */ extern void vty_log_fixed(char *buf, size_t len); diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c index 5713105f42..03b543a786 100644 --- a/ospfclient/ospfclient.c +++ b/ospfclient/ospfclient.c @@ -307,6 +307,7 @@ int main(int argc, char *argv[]) } /* Initialization */ + zprivs_preinit(&ospfd_privs); zprivs_init(&ospfd_privs); master = thread_master_create(NULL); diff --git a/redhat/daemons b/redhat/daemons index 94d6c72c30..8d12a1f5b8 100644 --- a/redhat/daemons +++ b/redhat/daemons @@ -35,7 +35,7 @@ # group "frrvty" and set to ug=rw,o= though. Check /etc/pam.d/frr, too. # watchfrr_enable=no -watchfrr_options=("-Az" "-b_" "-r/etc/init.d/frr_restart_%s" "-s/etc/init.d/frr_start_%s" "-k/etc/init.d/frr_stop_%s") +watchfrr_options=("-b_" "-r/etc/init.d/frr_restart_%s" "-s/etc/init.d/frr_start_%s" "-k/etc/init.d/frr_stop_%s") # zebra=no bgpd=no diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index b6df6d3b8d..77f1610fe2 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -45,7 +45,7 @@ int dump_args(struct vty *vty, const char *descr, int argc, return CMD_SUCCESS; } -static void vty_do_exit(void) +static void vty_do_exit(int isexit) { printf("\nend.\n"); cmd_terminate(); @@ -54,7 +54,8 @@ static void vty_do_exit(void) closezlog(); log_memstats_stderr("testcli"); - exit(0); + if (!isexit) + exit(0); } /* main routine. */ diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index c2cb5c2ea5..1984f28e63 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -108,6 +108,7 @@ int main(int argc, char **argv) /* Library inits. */ memory_init(); + zprivs_preinit(&test_privs); zprivs_init(&test_privs); #define PRIV_STATE() \ diff --git a/tools/etc/frr/daemons.conf b/tools/etc/frr/daemons.conf index 3f734eef02..0d92d13671 100644 --- a/tools/etc/frr/daemons.conf +++ b/tools/etc/frr/daemons.conf @@ -19,7 +19,7 @@ babeld_options=" --daemon -A 127.0.0.1" # The list of daemons to watch is automatically generated by the init script. watchfrr_enable=yes -watchfrr_options=(-adz -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB -t 30) +watchfrr_options=(-d -r /usr/sbin/servicebBfrrbBrestartbB%s -s /usr/sbin/servicebBfrrbBstartbB%s -k /usr/sbin/servicebBfrrbBstopbB%s -b bB -t 30) # If valgrind_enable is 'yes' the frr daemons will be started via valgrind. # The use case for doing so is tracking down memory leaks, etc in frr. diff --git a/watchfrr/Makefile.am b/watchfrr/Makefile.am index abe2266f20..bb53641eff 100644 --- a/watchfrr/Makefile.am +++ b/watchfrr/Makefile.am @@ -1,8 +1,6 @@ ## Process this file with Automake to create Makefile.in AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DEFS = @DEFS@ -DSTATEDIR=\"$(localstatedir)/\" - AM_CFLAGS = $(WERROR) sbin_PROGRAMS = watchfrr diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 6adb25f322..c9f721eacb 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -50,37 +50,14 @@ #define DEFAULT_LOGLEVEL LOG_INFO #define DEFAULT_MIN_RESTART 60 #define DEFAULT_MAX_RESTART 600 -#ifdef PATH_WATCHFRR_PID -#define DEFAULT_PIDFILE PATH_WATCHFRR_PID -#else -#define DEFAULT_PIDFILE STATEDIR "/watchfrr.pid" -#endif -#ifdef DAEMON_VTY_DIR -#define VTYDIR DAEMON_VTY_DIR -#else -#define VTYDIR STATEDIR -#endif #define PING_TOKEN "PING" /* Needs to be global, referenced somewhere inside libfrr. */ struct thread_master *master; +static char pidfile_default[256]; -typedef enum { - MODE_MONITOR = 0, - MODE_GLOBAL_RESTART, - MODE_SEPARATE_RESTART, - MODE_PHASED_ZEBRA_RESTART, - MODE_PHASED_ALL_RESTART -} watch_mode_t; - -static const char *mode_str[] = { - "monitor", - "global restart", - "individual daemon restart", - "phased zebra restart", - "phased global restart for any failure", -}; +static bool watch_only = false; typedef enum { PHASE_NONE = 0, @@ -112,7 +89,6 @@ struct restart_info { }; static struct global_state { - watch_mode_t mode; restart_phase_t phase; struct thread *t_phase_hanging; const char *vtydir; @@ -121,29 +97,25 @@ static struct global_state { long restart_timeout; long min_restart_interval; long max_restart_interval; - int do_ping; struct daemon *daemons; const char *restart_command; const char *start_command; const char *stop_command; struct restart_info restart; - int unresponsive_restart; int loglevel; struct daemon *special; /* points to zebra when doing phased restart */ int numdaemons; int numpids; int numdown; /* # of daemons that are not UP or UNRESPONSIVE */ } gs = { - .mode = MODE_MONITOR, .phase = PHASE_NONE, - .vtydir = VTYDIR, + .vtydir = frr_vtydir, .period = 1000 * DEFAULT_PERIOD, .timeout = DEFAULT_TIMEOUT, .restart_timeout = DEFAULT_RESTART_TIMEOUT, .loglevel = DEFAULT_LOGLEVEL, .min_restart_interval = DEFAULT_MIN_RESTART, .max_restart_interval = DEFAULT_MAX_RESTART, - .do_ping = 1, }; typedef enum { @@ -176,11 +148,11 @@ struct daemon { #define OPTION_MINRESTART 2000 #define OPTION_MAXRESTART 2001 +#define OPTION_DRY 2002 static const struct option longopts[] = { {"daemon", no_argument, NULL, 'd'}, {"statedir", required_argument, NULL, 'S'}, - {"no-echo", no_argument, NULL, 'e'}, {"loglevel", required_argument, NULL, 'l'}, {"interval", required_argument, NULL, 'i'}, {"timeout", required_argument, NULL, 't'}, @@ -188,10 +160,7 @@ static const struct option longopts[] = { {"restart", required_argument, NULL, 'r'}, {"start-command", required_argument, NULL, 's'}, {"kill-command", required_argument, NULL, 'k'}, - {"restart-all", required_argument, NULL, 'R'}, - {"all-restart", no_argument, NULL, 'a'}, - {"always-all-restart", no_argument, NULL, 'A'}, - {"unresponsive-restart", no_argument, NULL, 'z'}, + {"dry", no_argument, NULL, OPTION_DRY}, {"min-restart-interval", required_argument, NULL, OPTION_MINRESTART}, {"max-restart-interval", required_argument, NULL, OPTION_MAXRESTART}, {"pid-file", required_argument, NULL, 'p'}, @@ -217,55 +186,19 @@ It then repeatedly sends echo commands over that socket to determine whether\n\ the daemon is responsive. If the daemon crashes, we will receive an EOF\n\ on the socket connection and know immediately that the daemon is down.\n\n\ The daemons to be monitored should be listed on the command line.\n\n\ -This program can run in one of 5 modes:\n\n\ -0. Mode: %s.\n\ - Just monitor and report on status changes. Example:\n\ - %s -d zebra ospfd bgpd\n\n\ -1. Mode: %s.\n\ - Whenever any daemon hangs or crashes, use the given command to restart\n\ - them all. Example:\n\ - %s -dz \\\n\ - -R '/sbin/service zebra restart; /sbin/service ospfd restart' \\\n\ - zebra ospfd\n\n\ -2. Mode: %s.\n\ - When any single daemon hangs or crashes, restart only the daemon that's\n\ - in trouble using the supplied restart command. Example:\n\ - %s -dz -r '/sbin/service %%s restart' zebra ospfd bgpd\n\n\ -3. Mode: %s.\n\ - The same as the previous mode, except that there is special treatment when\n\ - the zebra daemon is in trouble. In that case, a phased restart approach\n\ - is used: 1. stop all other daemons; 2. restart zebra; 3. start the other\n\ - daemons. Example:\n\ - %s -adz -r '/sbin/service %%s restart' \\\n\ - -s '/sbin/service %%s start' \\\n\ - -k '/sbin/service %%s stop' zebra ospfd bgpd\n\n\ -4. Mode: %s.\n\ - This is the same as the previous mode, except that the phased restart\n\ - procedure is used whenever any of the daemons hangs or crashes. Example:\n\ - %s -Adz -r '/sbin/service %%s restart' \\\n\ - -s '/sbin/service %%s start' \\\n\ - -k '/sbin/service %%s stop' zebra ospfd bgpd\n\n\ -As of this writing, it is believed that mode 2 [%s]\n\ -is not safe, and mode 3 [%s] may not be safe with some of the\n\ -routing daemons.\n\n\ In order to avoid attempting to restart the daemons in a fast loop,\n\ the -m and -M options allow you to control the minimum delay between\n\ restart commands. The minimum restart delay is recalculated each time\n\ a restart is attempted: if the time since the last restart attempt exceeds\n\ twice the -M value, then the restart delay is set to the -m value.\n\ Otherwise, the interval is doubled (but capped at the -M value).\n\n", - progname, mode_str[0], progname, mode_str[1], progname, - mode_str[2], progname, mode_str[3], progname, mode_str[4], - progname, mode_str[2], mode_str[3]); + progname); fprintf(target, "Options:\n\ -d, --daemon Run in daemon mode. In this mode, error messages are sent\n\ to syslog instead of stdout.\n\ -S, --statedir Set the vty socket directory (default is %s)\n\ --e, --no-echo Do not ping the daemons to test responsiveness (this\n\ - option is necessary if the daemons do not support the\n\ - echo command)\n\ -l, --loglevel Set the logging level (default is %d).\n\ The value should range from %d (LOG_EMERG) to %d (LOG_DEBUG),\n\ but it can be set higher than %d if extra-verbose debugging\n\ @@ -285,7 +218,6 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\n", -r, --restart Supply a Bourne shell command to use to restart a single\n\ daemon. The command string should include '%%s' where the\n\ name of the daemon should be substituted.\n\ - Note that -r and -R are incompatible.\n\ -s, --start-command\n\ Supply a Bourne shell to command to use to start a single\n\ daemon. The command string should include '%%s' where the\n\ @@ -294,33 +226,19 @@ Otherwise, the interval is doubled (but capped at the -M value).\n\n", Supply a Bourne shell to command to use to stop a single\n\ daemon. The command string should include '%%s' where the\n\ name of the daemon should be substituted.\n\ --R, --restart-all\n\ - When one or more daemons is down, try to restart everything\n\ - using the Bourne shell command supplied as the argument.\n\ - Note that -r and -R are incompatible.\n\ --z, --unresponsive-restart\n\ - When a daemon is unresponsive, treat it as being down for\n\ - restart purposes.\n\ --a, --all-restart\n\ - When zebra hangs or crashes, restart all daemons using\n\ - this phased approach: 1. stop all other daemons; 2. restart\n\ - zebra; 3. start other daemons. Requires -r, -s, and -k.\n\ --A, --always-all-restart\n\ - When any daemon (not just zebra) hangs or crashes, use the\n\ - same phased restart mechanism described above for -a.\n\ - Requires -r, -s, and -k.\n\ + --dry Do not start or restart anything, just log.\n\ -p, --pid-file Set process identifier file name\n\ (default is %s).\n\ -b, --blank-string\n\ When the supplied argument string is found in any of the\n\ - various shell command arguments (-r, -s, -k, or -R), replace\n\ + various shell command arguments (-r, -s, or -k), replace\n\ it with a space. This is an ugly hack to circumvent problems\n\ passing command-line arguments with embedded spaces.\n\ -v, --version Print program version\n\ -h, --help Display this help and exit\n", - VTYDIR, DEFAULT_LOGLEVEL, LOG_EMERG, LOG_DEBUG, LOG_DEBUG, + frr_vtydir, DEFAULT_LOGLEVEL, LOG_EMERG, LOG_DEBUG, LOG_DEBUG, DEFAULT_MIN_RESTART, DEFAULT_MAX_RESTART, DEFAULT_PERIOD, - DEFAULT_TIMEOUT, DEFAULT_RESTART_TIMEOUT, DEFAULT_PIDFILE); + DEFAULT_TIMEOUT, DEFAULT_RESTART_TIMEOUT, pidfile_default); } static pid_t run_background(char *shell_cmd) @@ -390,15 +308,10 @@ static int restart_kill(struct thread *t_kill) static struct restart_info *find_child(pid_t child) { - if (gs.mode == MODE_GLOBAL_RESTART) { - if (gs.restart.pid == child) - return &gs.restart; - } else { - struct daemon *dmn; - for (dmn = gs.daemons; dmn; dmn = dmn->next) { - if (dmn->restart.pid == child) - return &dmn->restart; - } + struct daemon *dmn; + for (dmn = gs.daemons; dmn; dmn = dmn->next) { + if (dmn->restart.pid == child) + return &dmn->restart; } return NULL; } @@ -700,8 +613,7 @@ static void daemon_up(struct daemon *dmn, const char *why) dmn->connect_tries = 0; zlog_notice("%s state -> up : %s", dmn->name, why); daemon_send_ready(); - if (gs.do_ping) - SET_WAKEUP_ECHO(dmn); + SET_WAKEUP_ECHO(dmn); phase_check(); } @@ -889,61 +801,46 @@ static void phase_check(void) static void try_restart(struct daemon *dmn) { - switch (gs.mode) { - case MODE_MONITOR: + if (watch_only) return; - case MODE_GLOBAL_RESTART: - run_job(&gs.restart, "restart", gs.restart_command, 0, 1); - break; - case MODE_SEPARATE_RESTART: - run_job(&dmn->restart, "restart", gs.restart_command, 0, 1); - break; - case MODE_PHASED_ZEBRA_RESTART: - if (dmn != gs.special) { - if ((gs.special->state == DAEMON_UP) - && (gs.phase == PHASE_NONE)) - run_job(&dmn->restart, "restart", - gs.restart_command, 0, 1); - else - zlog_debug( - "%s: postponing restart attempt because master %s daemon " - "not up [%s], or phased restart in progress", - dmn->name, gs.special->name, - state_str[gs.special->state]); - break; - } - /*FALLTHRU*/ - case MODE_PHASED_ALL_RESTART: - if ((gs.phase != PHASE_NONE) || gs.numpids) { + if (dmn != gs.special) { + if ((gs.special->state == DAEMON_UP) + && (gs.phase == PHASE_NONE)) + run_job(&dmn->restart, "restart", gs.restart_command, 0, + 1); + else + zlog_debug( + "%s: postponing restart attempt because master %s daemon " + "not up [%s], or phased restart in progress", + dmn->name, gs.special->name, + state_str[gs.special->state]); + return; + } + + if ((gs.phase != PHASE_NONE) || gs.numpids) { + if (gs.loglevel > LOG_DEBUG + 1) + zlog_debug( + "postponing phased global restart: restart already in " + "progress [%s], or outstanding child processes [%d]", + phase_str[gs.phase], gs.numpids); + return; + } + /* Is it too soon for a restart? */ + { + struct timeval delay; + if (time_elapsed(&delay, &gs.special->restart.time)->tv_sec + < gs.special->restart.interval) { if (gs.loglevel > LOG_DEBUG + 1) zlog_debug( - "postponing phased global restart: restart already in " - "progress [%s], or outstanding child processes [%d]", - phase_str[gs.phase], gs.numpids); - break; + "postponing phased global restart: " + "elapsed time %ld < retry interval %ld", + (long)delay.tv_sec, + gs.special->restart.interval); + return; } - /* Is it too soon for a restart? */ - { - struct timeval delay; - if (time_elapsed(&delay, &gs.special->restart.time) - ->tv_sec - < gs.special->restart.interval) { - if (gs.loglevel > LOG_DEBUG + 1) - zlog_debug( - "postponing phased global restart: " - "elapsed time %ld < retry interval %ld", - (long)delay.tv_sec, - gs.special->restart.interval); - break; - } - } - run_job(&gs.restart, "restart", gs.restart_command, 0, 1); - break; - default: - zlog_err("error: unknown restart mode %d", gs.mode); - break; } + run_job(&gs.restart, "restart", gs.restart_command, 0, 1); } static int wakeup_unresponsive(struct thread *t_wakeup) @@ -973,10 +870,8 @@ static int wakeup_no_answer(struct thread *t_wakeup) "%s state -> unresponsive : no response yet to ping " "sent %ld seconds ago", dmn->name, gs.timeout); - if (gs.unresponsive_restart) { - SET_WAKEUP_UNRESPONSIVE(dmn); - try_restart(dmn); - } + SET_WAKEUP_UNRESPONSIVE(dmn); + try_restart(dmn); return 0; } @@ -1071,46 +966,41 @@ FRR_DAEMON_INFO(watchfrr, WATCHFRR, .privs = &watchfrr_privs, ) +#define DEPRECATED_OPTIONS "aAezR:" + int main(int argc, char **argv) { int opt; - const char *pidfile = DEFAULT_PIDFILE; + const char *pidfile = pidfile_default; const char *special = "zebra"; const char *blankstr = NULL; + snprintf(pidfile_default, sizeof(pidfile_default), "%s/watchfrr.pid", + frr_vtydir); + frr_preinit(&watchfrr_di, argc, argv); progname = watchfrr_di.progname; - frr_opt_add("aAb:dek:l:i:p:r:R:S:s:t:T:z", longopts, ""); + frr_opt_add("b:dk:l:i:p:r:S:s:t:T:" DEPRECATED_OPTIONS, longopts, ""); gs.restart.name = "all"; while ((opt = frr_getopt(argc, argv, NULL)) != EOF) { + if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) { + fprintf(stderr, + "The -%c option no longer exists.\n" + "Please refer to the watchfrr(8) man page.\n", + opt); + exit(1); + } + switch (opt) { case 0: break; - case 'a': - if ((gs.mode != MODE_MONITOR) - && (gs.mode != MODE_SEPARATE_RESTART)) { - fputs("Ambiguous operating mode selected.\n", - stderr); - frr_help_exit(1); - } - gs.mode = MODE_PHASED_ZEBRA_RESTART; - break; - case 'A': - if ((gs.mode != MODE_MONITOR) - && (gs.mode != MODE_SEPARATE_RESTART)) { - fputs("Ambiguous operating mode selected.\n", - stderr); - frr_help_exit(1); - } - gs.mode = MODE_PHASED_ALL_RESTART; - break; case 'b': blankstr = optarg; break; - case 'e': - gs.do_ping = 0; + case OPTION_DRY: + watch_only = true; break; case 'k': if (!valid_command(optarg)) { @@ -1172,12 +1062,6 @@ int main(int argc, char **argv) pidfile = optarg; break; case 'r': - if ((gs.mode == MODE_GLOBAL_RESTART) - || (gs.mode == MODE_SEPARATE_RESTART)) { - fputs("Ambiguous operating mode selected.\n", - stderr); - frr_help_exit(1); - } if (!valid_command(optarg)) { fprintf(stderr, "Invalid restart command, must contain '%%s': %s\n", @@ -1185,23 +1069,6 @@ int main(int argc, char **argv) frr_help_exit(1); } gs.restart_command = optarg; - if (gs.mode == MODE_MONITOR) - gs.mode = MODE_SEPARATE_RESTART; - break; - case 'R': - if (gs.mode != MODE_MONITOR) { - fputs("Ambiguous operating mode selected.\n", - stderr); - frr_help_exit(1); - } - if (strchr(optarg, '%')) { - fprintf(stderr, - "Invalid restart-all arg, must not contain '%%s': %s\n", - optarg); - frr_help_exit(1); - } - gs.restart_command = optarg; - gs.mode = MODE_GLOBAL_RESTART; break; case 's': if (!valid_command(optarg)) { @@ -1238,49 +1105,22 @@ int main(int argc, char **argv) frr_help_exit(1); } } break; - case 'z': - gs.unresponsive_restart = 1; - break; default: fputs("Invalid option.\n", stderr); frr_help_exit(1); } } - if (gs.unresponsive_restart && (gs.mode == MODE_MONITOR)) { - fputs("Option -z requires a -r or -R restart option.\n", + if (watch_only + && (gs.start_command || gs.stop_command || gs.restart_command)) { + fputs("Options -r/-s/-k are not used when --dry is active.\n", stderr); - frr_help_exit(1); } - switch (gs.mode) { - case MODE_MONITOR: - if (gs.restart_command || gs.start_command || gs.stop_command) { - fprintf(stderr, - "No kill/(re)start commands needed for %s mode.\n", - mode_str[gs.mode]); - frr_help_exit(1); - } - break; - case MODE_GLOBAL_RESTART: - case MODE_SEPARATE_RESTART: - if (!gs.restart_command || gs.start_command - || gs.stop_command) { - fprintf(stderr, - "No start/kill commands needed in [%s] mode.\n", - mode_str[gs.mode]); - frr_help_exit(1); - } - break; - case MODE_PHASED_ZEBRA_RESTART: - case MODE_PHASED_ALL_RESTART: - if (!gs.restart_command || !gs.start_command - || !gs.stop_command) { - fprintf(stderr, - "Need start, kill, and restart commands in [%s] mode.\n", - mode_str[gs.mode]); - frr_help_exit(1); - } - break; + if (!watch_only + && (!gs.restart_command || !gs.start_command || !gs.stop_command)) { + fprintf(stderr, + "Options -s (start), -k (kill), and -r (restart) are required.\n"); + frr_help_exit(1); } if (blankstr) { @@ -1343,9 +1183,7 @@ int main(int argc, char **argv) gs.daemons = dmn; tail = dmn; - if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) - || (gs.mode == MODE_PHASED_ALL_RESTART)) - && !strcmp(dmn->name, special)) + if (!strcmp(dmn->name, special)) gs.special = dmn; } } @@ -1353,12 +1191,9 @@ int main(int argc, char **argv) fputs("Must specify one or more daemons to monitor.\n", stderr); frr_help_exit(1); } - if (((gs.mode == MODE_PHASED_ZEBRA_RESTART) - || (gs.mode == MODE_PHASED_ALL_RESTART)) - && !gs.special) { - fprintf(stderr, - "In mode [%s], but cannot find master daemon %s\n", - mode_str[gs.mode], special); + if (!watch_only && !gs.special) { + fprintf(stderr, "\"%s\" daemon must be in daemon list\n", + special); frr_help_exit(1); } @@ -1383,8 +1218,9 @@ int main(int argc, char **argv) strcpy(p, dmn->name); p += strlen(p); } - zlog_notice("%s %s watching [%s], mode [%s]", progname, - FRR_VERSION, buf, mode_str[gs.mode]); + zlog_notice("%s %s watching [%s]%s", progname, + FRR_VERSION, buf, + watch_only ? ", monitor mode" : ""); } } |
