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 ---------------
#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;
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 (di->dryrun)
exit(0);
- if (di->daemon_mode)
+ if (di->daemon_mode || di->terminal)
frr_daemonize();
if (!di->pid_file)
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);
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)
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;
/* 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)
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;
}
XFREE(MTYPE_VTY, vty);
if (was_stdio)
- vty_stdio_reset();
+ vty_stdio_reset(0);
}
/* When time out occur output message then close connection. */
vty_master = master_thread;
- atexit(vty_stdio_reset);
+ atexit(vty_stdio_atexit);
/* Initilize server thread vector. */
Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE);
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);
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);
return CMD_SUCCESS;
}
-static void vty_do_exit(void)
+static void vty_do_exit(int isexit)
{
printf("\nend.\n");
cmd_terminate();
closezlog();
log_memstats_stderr("testcli");
- exit(0);
+ if (!isexit)
+ exit(0);
}
/* main routine. */