]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: vty_stdio signal handling
authorDavid Lamparter <equinox@opensourcerouting.org>
Thu, 1 Jun 2017 14:02:23 +0000 (16:02 +0200)
committerDavid Lamparter <equinox@opensourcerouting.org>
Wed, 2 Aug 2017 00:59:51 +0000 (02:59 +0200)
- 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 <equinox@opensourcerouting.org>
configure.ac
lib/grammar_sandbox_main.c
lib/libfrr.c
lib/vty.c
lib/vty.h
tests/lib/cli/common_cli.c

index 8cc70f8a99fcdba5504dc4f8a1184696f560603c..a10d4da64072a23d16b663cd4bb32baf4905ba01 100755 (executable)
@@ -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 ---------------
index c236d2c7be774c8a42f0c4e4bac6abbe0c14ca28..89b0993d1d0e6642828e69464ccf18dd66987bbc 100644 (file)
 #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;
index efd8991e82ed6a00834d09e0d99de5f54c0cbc07..d168d830535d78643fc5d4d80aba5517e46e8985 100644 (file)
@@ -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;
index 00579550e4723a2891d584b721c5cfad3524247e..c6e82b40c33141f332ca16908d69864ed675ab62 100644 (file)
--- 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);
index 0839f7fb68dd836d0759dfddef5f20667d8fa27c..d2fbbe2610391effaf3de0c29f6d6ad4117616cd 100644 (file)
--- 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);
index b6df6d3b8dc3971cb29d6efc84d6e91206f552a6..77f1610fe23ba16b09ac362704b038482a087399 100644 (file)
@@ -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. */