mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
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 <equinox@opensourcerouting.org>
This commit is contained in:
parent
cff2b2112e
commit
154b9e8f9f
|
@ -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 ---------------
|
||||
|
|
|
@ -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;
|
||||
|
|
115
lib/libfrr.c
115
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;
|
||||
|
|
92
lib/vty.c
92
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in a new issue