From: David Lamparter Date: Thu, 1 Jun 2017 14:02:23 +0000 (+0200) Subject: lib: vty_stdio signal handling X-Git-Tag: frr-4.0-dev~435^2~10 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=154b9e8f9f08f5be2d3d84f7565e3ce7921b8b35;p=mirror%2Ffrr.git lib: vty_stdio signal handling - SIGTSTP appropriately suspends the foreground terminal - SIGINT causes the daemon to exit, regardless of -d - SIGQUIT causes the daemon to daemonize, regardless of -d Signed-off-by: David Lamparter --- diff --git a/configure.ac b/configure.ac index 8cc70f8a99..a10d4da640 100755 --- a/configure.ac +++ b/configure.ac @@ -1015,6 +1015,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/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 efd8991e82..d168d83053 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -379,22 +379,79 @@ 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); @@ -468,7 +525,7 @@ void frr_config_fork(void) if (di->dryrun) exit(0); - if (di->daemon_mode) + if (di->daemon_mode || di->terminal) frr_daemonize(); if (!di->pid_file) @@ -497,11 +554,18 @@ void frr_vty_serv(void) vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path); } -static void frr_terminal_close(void) +static void frr_terminal_close(int isexit) { - if (!di->daemon_mode) { + 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); - raise(SIGINT); + if (!isexit) + raise(SIGINT); + return; } else { printf("\n%s daemonizing\n", di->name); fflush(stdout); @@ -512,11 +576,43 @@ static void frr_terminal_close(void) dup2(nullfd, 1); dup2(nullfd, 2); close(nullfd); +} - if (daemon_ctl_sock != -1) { - close(daemon_ctl_sock); - daemon_ctl_sock = -1; +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) @@ -534,6 +630,11 @@ void frr_run(struct thread_master *master) 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 if (daemon_ctl_sock != -1) { close(daemon_ctl_sock); daemon_ctl_sock = -1; diff --git a/lib/vty.c b/lib/vty.c index 00579550e4..c6e82b40c3 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -1660,24 +1660,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) @@ -1695,23 +1753,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; } @@ -2155,7 +2197,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. */ @@ -2919,7 +2961,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); diff --git a/lib/vty.h b/lib/vty.h index 0839f7fb68..d2fbbe2610 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -252,7 +252,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); @@ -268,6 +268,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/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. */