summaryrefslogtreecommitdiff
path: root/lib/vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vty.c')
-rw-r--r--lib/vty.c227
1 files changed, 162 insertions, 65 deletions
diff --git a/lib/vty.c b/lib/vty.c
index 8eaf13619b..78ef9894de 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -65,7 +65,7 @@ DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history");
DECLARE_DLIST(vtys, struct vty, itm);
/* Vty events */
-enum event {
+enum vty_event {
VTY_SERV,
VTY_READ,
VTY_WRITE,
@@ -90,8 +90,8 @@ struct vty_serv {
DECLARE_DLIST(vtyservs, struct vty_serv, itm);
-static void vty_event_serv(enum event event, struct vty_serv *);
-static void vty_event(enum event, struct vty *);
+static void vty_event_serv(enum vty_event event, struct vty_serv *);
+static void vty_event(enum vty_event, struct vty *);
/* Extern host structure from command.c */
extern struct host host;
@@ -1299,7 +1299,7 @@ static void vty_buffer_reset(struct vty *vty)
}
/* Read data via vty socket. */
-static int vty_read(struct thread *thread)
+static void vty_read(struct thread *thread)
{
int i;
int nbytes;
@@ -1312,10 +1312,8 @@ static int vty_read(struct thread *thread)
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
vty_event(VTY_READ, vty);
- return 0;
+ return;
}
- vty->monitor = 0; /* disable monitoring to avoid
- infinite recursion */
flog_err(
EC_LIB_SOCKET,
"%s: read error on vty client fd %d, closing: %s",
@@ -1464,6 +1462,11 @@ static int vty_read(struct thread *thread)
vty_out(vty, "\n");
buffer_flush_available(vty->obuf, vty->wfd);
vty_execute(vty);
+
+ if (vty->pass_fd != -1) {
+ close(vty->pass_fd);
+ vty->pass_fd = -1;
+ }
break;
case '\t':
vty_complete_command(vty);
@@ -1496,11 +1499,10 @@ static int vty_read(struct thread *thread)
vty_event(VTY_WRITE, vty);
vty_event(VTY_READ, vty);
}
- return 0;
}
/* Flush buffer to the vty. */
-static int vty_flush(struct thread *thread)
+static void vty_flush(struct thread *thread)
{
int erase;
buffer_status_t flushrc;
@@ -1525,14 +1527,12 @@ static int vty_flush(struct thread *thread)
vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
switch (flushrc) {
case BUFFER_ERROR:
- vty->monitor =
- 0; /* disable monitoring to avoid infinite recursion */
zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
vty->fd, vty->wfd);
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty_close(vty);
- return 0;
+ return;
case BUFFER_EMPTY:
if (vty->status == VTY_CLOSE)
vty_close(vty);
@@ -1549,8 +1549,6 @@ static int vty_flush(struct thread *thread)
vty_event(VTY_WRITE, vty);
break;
}
-
- return 0;
}
/* Allocate new vty struct. */
@@ -1564,6 +1562,7 @@ struct vty *vty_new(void)
new->obuf = buffer_new(0); /* Use default buffer size. */
new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
new->max = VTY_BUFSIZ;
+ new->pass_fd = -1;
return new;
}
@@ -1753,7 +1752,7 @@ struct vty *vty_stdio(void (*atclose)(int isexit))
}
/* Accept connection from the network. */
-static int vty_accept(struct thread *thread)
+static void vty_accept(struct thread *thread)
{
struct vty_serv *vtyserv = THREAD_ARG(thread);
int vty_sock;
@@ -1774,7 +1773,7 @@ static int vty_accept(struct thread *thread)
if (vty_sock < 0) {
flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
safe_strerror(errno));
- return -1;
+ return;
}
set_nonblocking(vty_sock);
set_cloexec(vty_sock);
@@ -1783,7 +1782,7 @@ static int vty_accept(struct thread *thread)
close(vty_sock);
zlog_info("Vty unable to convert prefix from sockunion %pSU",
&su);
- return -1;
+ return;
}
/* VTY's accesslist apply. */
@@ -1792,7 +1791,7 @@ static int vty_accept(struct thread *thread)
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
zlog_info("Vty connection refused from %pSU", &su);
close(vty_sock);
- return 0;
+ return;
}
}
@@ -1803,7 +1802,7 @@ static int vty_accept(struct thread *thread)
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
zlog_info("Vty connection refused from %pSU", &su);
close(vty_sock);
- return 0;
+ return;
}
}
@@ -1817,8 +1816,6 @@ static int vty_accept(struct thread *thread)
zlog_info("Vty connection from %pSU", &su);
vty_create(vty_sock, &su);
-
- return 0;
}
static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
@@ -1968,7 +1965,7 @@ static void vty_serv_un(const char *path)
/* #define VTYSH_DEBUG 1 */
-static int vtysh_accept(struct thread *thread)
+static void vtysh_accept(struct thread *thread)
{
struct vty_serv *vtyserv = THREAD_ARG(thread);
int accept_sock = vtyserv->sock;
@@ -1988,7 +1985,7 @@ static int vtysh_accept(struct thread *thread)
if (sock < 0) {
flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
safe_strerror(errno));
- return -1;
+ return;
}
if (set_nonblocking(sock) < 0) {
@@ -1997,7 +1994,7 @@ static int vtysh_accept(struct thread *thread)
"vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
sock, safe_strerror(errno));
close(sock);
- return -1;
+ return;
}
set_cloexec(sock);
@@ -2013,19 +2010,70 @@ static int vtysh_accept(struct thread *thread)
vtys_add_tail(vtysh_sessions, vty);
vty_event(VTYSH_READ, vty);
+}
- return 0;
+static int vtysh_do_pass_fd(struct vty *vty)
+{
+ struct iovec iov[1] = {
+ {
+ .iov_base = vty->pass_fd_status,
+ .iov_len = sizeof(vty->pass_fd_status),
+ },
+ };
+ union {
+ uint8_t buf[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr align;
+ } u;
+ struct msghdr mh = {
+ .msg_iov = iov,
+ .msg_iovlen = array_size(iov),
+ .msg_control = u.buf,
+ .msg_controllen = sizeof(u.buf),
+ };
+ struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh);
+ ssize_t ret;
+
+ cmh->cmsg_level = SOL_SOCKET;
+ cmh->cmsg_type = SCM_RIGHTS;
+ cmh->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int));
+
+ ret = sendmsg(vty->wfd, &mh, 0);
+ if (ret < 0 && ERRNO_IO_RETRY(errno))
+ return BUFFER_PENDING;
+
+ close(vty->pass_fd);
+ vty->pass_fd = -1;
+ vty->status = VTY_NORMAL;
+
+ if (ret <= 0)
+ return BUFFER_ERROR;
+
+ /* resume accepting commands (suspended in vtysh_read) */
+ vty_event(VTYSH_READ, vty);
+
+ if ((size_t)ret < sizeof(vty->pass_fd_status)) {
+ size_t remains = sizeof(vty->pass_fd_status) - ret;
+
+ buffer_put(vty->obuf, vty->pass_fd_status + ret, remains);
+ return BUFFER_PENDING;
+ }
+ return BUFFER_EMPTY;
}
static int vtysh_flush(struct vty *vty)
{
- switch (buffer_flush_available(vty->obuf, vty->wfd)) {
+ int ret;
+
+ ret = buffer_flush_available(vty->obuf, vty->wfd);
+ if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD)
+ ret = vtysh_do_pass_fd(vty);
+
+ switch (ret) {
case BUFFER_PENDING:
vty_event(VTYSH_WRITE, vty);
break;
case BUFFER_ERROR:
- vty->monitor =
- 0; /* disable monitoring to avoid infinite recursion */
flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
__func__, vty->fd);
buffer_reset(vty->lbuf);
@@ -2038,7 +2086,15 @@ static int vtysh_flush(struct vty *vty)
return 0;
}
-static int vtysh_read(struct thread *thread)
+void vty_pass_fd(struct vty *vty, int fd)
+{
+ if (vty->pass_fd != -1)
+ close(vty->pass_fd);
+
+ vty->pass_fd = fd;
+}
+
+static void vtysh_read(struct thread *thread)
{
int ret;
int sock;
@@ -2055,10 +2111,8 @@ static int vtysh_read(struct thread *thread)
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
vty_event(VTYSH_READ, vty);
- return 0;
+ return;
}
- vty->monitor = 0; /* disable monitoring to avoid
- infinite recursion */
flog_err(
EC_LIB_SOCKET,
"%s: read failed on vtysh client fd %d, closing: %s",
@@ -2070,7 +2124,7 @@ static int vtysh_read(struct thread *thread)
#ifdef VTYSH_DEBUG
printf("close vtysh\n");
#endif /* VTYSH_DEBUG */
- return 0;
+ return;
}
#ifdef VTYSH_DEBUG
@@ -2097,6 +2151,26 @@ static int vtysh_read(struct thread *thread)
printf("vtysh node: %d\n", vty->node);
#endif /* VTYSH_DEBUG */
+ if (vty->pass_fd != -1) {
+ memset(vty->pass_fd_status, 0, 4);
+ vty->pass_fd_status[3] = ret;
+ vty->status = VTY_PASSFD;
+
+ if (!vty->t_write)
+ vty_event(VTYSH_WRITE, vty);
+
+ /* this introduces a "sequence point"
+ * command output is written normally,
+ * read processing is suspended until
+ * buffer is empty
+ * then retcode + FD is written
+ * then normal processing resumes
+ *
+ * => skip vty_event(VTYSH_READ, vty)!
+ */
+ return;
+ }
+
/* hack for asynchronous "write integrated"
* - other commands in "buf" will be ditched
* - input during pending config-write is
@@ -2112,7 +2186,7 @@ static int vtysh_read(struct thread *thread)
if (!vty->t_write && (vtysh_flush(vty) < 0))
/* Try to flush results; exit if a write
* error occurs. */
- return 0;
+ return;
}
}
}
@@ -2121,16 +2195,13 @@ static int vtysh_read(struct thread *thread)
vty_close(vty);
else
vty_event(VTYSH_READ, vty);
-
- return 0;
}
-static int vtysh_write(struct thread *thread)
+static void vtysh_write(struct thread *thread)
{
struct vty *vty = THREAD_ARG(thread);
vtysh_flush(vty);
- return 0;
}
#endif /* VTYSH */
@@ -2171,6 +2242,12 @@ void vty_close(struct vty *vty)
THREAD_OFF(vty->t_write);
THREAD_OFF(vty->t_timeout);
+ if (vty->pass_fd != -1) {
+ close(vty->pass_fd);
+ vty->pass_fd = -1;
+ }
+ zlog_live_close(&vty->live_log);
+
/* Flush buffer. */
buffer_flush_all(vty->obuf, vty->wfd);
@@ -2221,7 +2298,7 @@ void vty_close(struct vty *vty)
}
/* When time out occur output message then close connection. */
-static int vty_timeout(struct thread *thread)
+static void vty_timeout(struct thread *thread)
{
struct vty *vty;
@@ -2236,8 +2313,6 @@ static int vty_timeout(struct thread *thread)
/* Close connection. */
vty->status = VTY_CLOSE;
vty_close(vty);
-
- return 0;
}
/* Read up configuration file from file_name. */
@@ -2608,7 +2683,7 @@ int vty_config_node_exit(struct vty *vty)
/* Master of the threads. */
static struct thread_master *vty_master;
-static void vty_event_serv(enum event event, struct vty_serv *vty_serv)
+static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv)
{
switch (event) {
case VTY_SERV:
@@ -2626,7 +2701,7 @@ static void vty_event_serv(enum event event, struct vty_serv *vty_serv)
}
}
-static void vty_event(enum event event, struct vty *vty)
+static void vty_event(enum vty_event event, struct vty *vty)
{
switch (event) {
#ifdef VTYSH
@@ -2673,8 +2748,9 @@ DEFUN_NOSH (config_who,
struct vty *v;
frr_each (vtys, vty_sessions, v)
- vty_out(vty, "%svty[%d] connected from %s.\n",
- v->config ? "*" : " ", v->fd, v->address);
+ vty_out(vty, "%svty[%d] connected from %s%s.\n",
+ v->config ? "*" : " ", v->fd, v->address,
+ zlog_live_is_null(&v->live_log) ? "" : ", live log");
return CMD_SUCCESS;
}
@@ -2867,35 +2943,56 @@ DEFUN (no_service_advanced_vty,
return CMD_SUCCESS;
}
-DEFUN_NOSH (terminal_monitor,
- terminal_monitor_cmd,
- "terminal monitor",
- "Set terminal line parameters\n"
- "Copy debug output to the current terminal line\n")
+DEFUN_NOSH(terminal_monitor,
+ terminal_monitor_cmd,
+ "terminal monitor [detach]",
+ "Set terminal line parameters\n"
+ "Copy debug output to the current terminal line\n"
+ "Keep logging feed open independent of VTY session\n")
{
- vty->monitor = 1;
+ int fd_ret = -1;
+
+ if (vty->type != VTY_SHELL_SERV) {
+ vty_out(vty, "%% not supported\n");
+ return CMD_WARNING;
+ }
+
+ if (argc == 3) {
+ struct zlog_live_cfg detach_log = {};
+
+ zlog_live_open(&detach_log, LOG_DEBUG, &fd_ret);
+ zlog_live_disown(&detach_log);
+ } else
+ zlog_live_open(&vty->live_log, LOG_DEBUG, &fd_ret);
+
+ if (fd_ret == -1) {
+ vty_out(vty, "%% error opening live log: %m\n");
+ return CMD_WARNING;
+ }
+
+ vty_pass_fd(vty, fd_ret);
return CMD_SUCCESS;
}
-DEFUN_NOSH (terminal_no_monitor,
- terminal_no_monitor_cmd,
- "terminal no monitor",
- "Set terminal line parameters\n"
- NO_STR
- "Copy debug output to the current terminal line\n")
+DEFUN_NOSH(no_terminal_monitor,
+ no_terminal_monitor_cmd,
+ "no terminal monitor",
+ NO_STR
+ "Set terminal line parameters\n"
+ "Copy debug output to the current terminal line\n")
{
- vty->monitor = 0;
+ zlog_live_close(&vty->live_log);
return CMD_SUCCESS;
}
-DEFUN_NOSH (no_terminal_monitor,
- no_terminal_monitor_cmd,
- "no terminal monitor",
- NO_STR
- "Set terminal line parameters\n"
- "Copy debug output to the current terminal line\n")
+DEFUN_NOSH(terminal_no_monitor,
+ terminal_no_monitor_cmd,
+ "terminal no monitor",
+ "Set terminal line parameters\n"
+ NO_STR
+ "Copy debug output to the current terminal line\n")
{
- return terminal_no_monitor(self, vty, argc, argv);
+ return no_terminal_monitor(self, vty, argc, argv);
}