mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-29 10:11:52 +00:00
lib: implement terminal monitor
for vtysh
Adds a new logging target that sends log messages to vtysh. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
parent
b2dde56b2c
commit
0798d2760d
@ -113,6 +113,7 @@ lib_libfrr_la_SOURCES = \
|
||||
lib/zlog.c \
|
||||
lib/zlog_5424.c \
|
||||
lib/zlog_5424_cli.c \
|
||||
lib/zlog_live.c \
|
||||
lib/zlog_targets.c \
|
||||
lib/printf/printf-pos.c \
|
||||
lib/printf/vfprintf.c \
|
||||
@ -287,6 +288,7 @@ pkginclude_HEADERS += \
|
||||
lib/zebra.h \
|
||||
lib/zlog.h \
|
||||
lib/zlog_5424.h \
|
||||
lib/zlog_live.h \
|
||||
lib/zlog_targets.h \
|
||||
lib/pbr.h \
|
||||
lib/routing_nb.h \
|
||||
|
61
lib/vty.c
61
lib/vty.c
@ -1314,8 +1314,6 @@ static void vty_read(struct thread *thread)
|
||||
vty_event(VTY_READ, vty);
|
||||
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",
|
||||
@ -1529,8 +1527,6 @@ static void 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);
|
||||
@ -2078,8 +2074,6 @@ static int vtysh_flush(struct vty *vty)
|
||||
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);
|
||||
@ -2119,8 +2113,6 @@ static void vtysh_read(struct thread *thread)
|
||||
vty_event(VTYSH_READ, vty);
|
||||
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",
|
||||
@ -2254,6 +2246,7 @@ void vty_close(struct vty *vty)
|
||||
close(vty->pass_fd);
|
||||
vty->pass_fd = -1;
|
||||
}
|
||||
zlog_live_close(&vty->live_log);
|
||||
|
||||
/* Flush buffer. */
|
||||
buffer_flush_all(vty->obuf, vty->wfd);
|
||||
@ -2755,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;
|
||||
}
|
||||
|
||||
@ -2951,22 +2945,32 @@ DEFUN (no_service_advanced_vty,
|
||||
|
||||
DEFUN_NOSH(terminal_monitor,
|
||||
terminal_monitor_cmd,
|
||||
"terminal monitor",
|
||||
"terminal monitor [detach]",
|
||||
"Set terminal line parameters\n"
|
||||
"Copy debug output to the current terminal line\n")
|
||||
"Copy debug output to the current terminal line\n"
|
||||
"Keep logging feed open independent of VTY session\n")
|
||||
{
|
||||
vty->monitor = 1;
|
||||
return CMD_SUCCESS;
|
||||
int fd_ret = -1;
|
||||
|
||||
if (vty->type != VTY_SHELL_SERV) {
|
||||
vty_out(vty, "%% not supported\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
vty->monitor = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2977,7 +2981,18 @@ DEFUN_NOSH (no_terminal_monitor,
|
||||
"Set terminal line parameters\n"
|
||||
"Copy debug output to the current terminal line\n")
|
||||
{
|
||||
return terminal_no_monitor(self, vty, argc, argv);
|
||||
zlog_live_close(&vty->live_log);
|
||||
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")
|
||||
{
|
||||
return no_terminal_monitor(self, vty, argc, argv);
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "qobj.h"
|
||||
#include "compiler.h"
|
||||
#include "northbound.h"
|
||||
#include "zlog_live.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -175,6 +176,9 @@ struct vty {
|
||||
/* CLI command return value (likely CMD_SUCCESS) when pass_fd != -1 */
|
||||
uint8_t pass_fd_status[4];
|
||||
|
||||
/* live logging target / terminal monitor */
|
||||
struct zlog_live_cfg live_log;
|
||||
|
||||
/* IAC handling: was the last character received the
|
||||
IAC (interpret-as-command) escape character (and therefore the next
|
||||
character will be the command code)? Refer to Telnet RFC 854. */
|
||||
@ -198,9 +202,6 @@ struct vty {
|
||||
/* Configure lines. */
|
||||
int lines;
|
||||
|
||||
/* Terminal monitor. */
|
||||
int monitor;
|
||||
|
||||
/* Read and write thread. */
|
||||
struct thread *t_read;
|
||||
struct thread *t_write;
|
||||
|
245
lib/zlog_live.c
Normal file
245
lib/zlog_live.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "zebra.h"
|
||||
|
||||
#include "zlog_live.h"
|
||||
|
||||
#include "memory.h"
|
||||
#include "frrcu.h"
|
||||
#include "zlog.h"
|
||||
#include "printfrr.h"
|
||||
|
||||
DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target");
|
||||
|
||||
enum {
|
||||
STATE_NORMAL = 0,
|
||||
STATE_FD_DEAD,
|
||||
STATE_DISOWNED,
|
||||
};
|
||||
|
||||
struct zlt_live {
|
||||
struct zlog_target zt;
|
||||
|
||||
atomic_uint_fast32_t fd;
|
||||
struct rcu_head_close head_close;
|
||||
struct rcu_head head_self;
|
||||
|
||||
atomic_uint_fast32_t state;
|
||||
};
|
||||
|
||||
static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[],
|
||||
size_t nmsgs)
|
||||
{
|
||||
struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
|
||||
struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs;
|
||||
struct mmsghdr mmhs[nmsgs], *mmh = mmhs;
|
||||
struct iovec iovs[nmsgs * 3], *iov = iovs;
|
||||
struct timespec ts;
|
||||
size_t i, textlen;
|
||||
int fd;
|
||||
uint_fast32_t state;
|
||||
|
||||
fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
|
||||
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
memset(mmhs, 0, sizeof(mmhs));
|
||||
memset(hdrs, 0, sizeof(hdrs));
|
||||
|
||||
for (i = 0; i < nmsgs; i++) {
|
||||
const struct fmt_outpos *argpos;
|
||||
size_t n_argpos, arghdrlen;
|
||||
struct zlog_msg *msg = msgs[i];
|
||||
int prio = zlog_msg_prio(msg);
|
||||
|
||||
if (prio > zt->prio_min)
|
||||
continue;
|
||||
|
||||
zlog_msg_args(msg, &arghdrlen, &n_argpos, &argpos);
|
||||
|
||||
mmh->msg_hdr.msg_iov = iov;
|
||||
|
||||
iov->iov_base = hdr;
|
||||
iov->iov_len = sizeof(*hdr);
|
||||
iov++;
|
||||
|
||||
if (n_argpos) {
|
||||
iov->iov_base = (char *)argpos;
|
||||
iov->iov_len = sizeof(*argpos) * n_argpos;
|
||||
iov++;
|
||||
}
|
||||
|
||||
iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
|
||||
iov->iov_len = textlen;
|
||||
iov++;
|
||||
|
||||
zlog_msg_tsraw(msg, &ts);
|
||||
|
||||
hdr->ts_sec = ts.tv_sec;
|
||||
hdr->ts_nsec = ts.tv_nsec;
|
||||
hdr->prio = zlog_msg_prio(msg);
|
||||
hdr->flags = 0;
|
||||
hdr->textlen = textlen;
|
||||
hdr->arghdrlen = arghdrlen;
|
||||
hdr->n_argpos = n_argpos;
|
||||
|
||||
mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov;
|
||||
mmh++;
|
||||
hdr++;
|
||||
}
|
||||
|
||||
size_t msgtotal = mmh - mmhs;
|
||||
ssize_t sent;
|
||||
|
||||
for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) {
|
||||
sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0);
|
||||
|
||||
if (sent <= 0)
|
||||
goto out_err;
|
||||
}
|
||||
return;
|
||||
|
||||
out_err:
|
||||
fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
rcu_close(&zte->head_close, fd);
|
||||
zlog_target_replace(zt, NULL);
|
||||
|
||||
state = STATE_NORMAL;
|
||||
atomic_compare_exchange_strong_explicit(
|
||||
&zte->state, &state, STATE_FD_DEAD, memory_order_relaxed,
|
||||
memory_order_relaxed);
|
||||
if (state == STATE_DISOWNED)
|
||||
rcu_free(MTYPE_LOG_LIVE, zte, head_self);
|
||||
}
|
||||
|
||||
static void zlog_live_sigsafe(struct zlog_target *zt, const char *text,
|
||||
size_t len)
|
||||
{
|
||||
struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
|
||||
struct zlog_live_hdr hdr[1];
|
||||
struct iovec iovs[2], *iov = iovs;
|
||||
struct timespec ts;
|
||||
int fd;
|
||||
|
||||
fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
hdr->ts_sec = ts.tv_sec;
|
||||
hdr->ts_nsec = ts.tv_nsec;
|
||||
hdr->prio = LOG_CRIT;
|
||||
hdr->flags = 0;
|
||||
hdr->textlen = len;
|
||||
hdr->n_argpos = 0;
|
||||
|
||||
iov->iov_base = (char *)hdr;
|
||||
iov->iov_len = sizeof(hdr);
|
||||
iov++;
|
||||
|
||||
iov->iov_base = (char *)text;
|
||||
iov->iov_len = len;
|
||||
iov++;
|
||||
|
||||
writev(fd, iovs, iov - iovs);
|
||||
}
|
||||
|
||||
void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd)
|
||||
{
|
||||
int sockets[2];
|
||||
struct zlt_live *zte;
|
||||
struct zlog_target *zt;
|
||||
|
||||
if (cfg->target)
|
||||
zlog_live_close(cfg);
|
||||
|
||||
*other_fd = -1;
|
||||
if (prio_min == ZLOG_DISABLED)
|
||||
return;
|
||||
|
||||
/* the only reason for SEQPACKET here is getting close notifications.
|
||||
* otherwise if you open a bunch of vtysh connections with live logs
|
||||
* and close them all, the fds will stick around until we get an error
|
||||
* when trying to log something to them at some later point -- which
|
||||
* eats up fds and might be *much* later for some daemons.
|
||||
*/
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) {
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
|
||||
zlog_warn("%% could not open socket pair: %m");
|
||||
return;
|
||||
}
|
||||
} else
|
||||
/* SEQPACKET only: try to zap read direction */
|
||||
shutdown(sockets[0], SHUT_RD);
|
||||
|
||||
*other_fd = sockets[1];
|
||||
|
||||
zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
|
||||
zte = container_of(zt, struct zlt_live, zt);
|
||||
cfg->target = zte;
|
||||
|
||||
zte->fd = sockets[0];
|
||||
zte->zt.prio_min = prio_min;
|
||||
zte->zt.logfn = zlog_live;
|
||||
zte->zt.logfn_sigsafe = zlog_live_sigsafe;
|
||||
|
||||
zlog_target_replace(NULL, zt);
|
||||
}
|
||||
|
||||
void zlog_live_close(struct zlog_live_cfg *cfg)
|
||||
{
|
||||
struct zlt_live *zte;
|
||||
int fd;
|
||||
|
||||
if (!cfg->target)
|
||||
return;
|
||||
|
||||
zte = cfg->target;
|
||||
cfg->target = NULL;
|
||||
|
||||
fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
|
||||
|
||||
if (fd >= 0) {
|
||||
rcu_close(&zte->head_close, fd);
|
||||
zlog_target_replace(&zte->zt, NULL);
|
||||
}
|
||||
rcu_free(MTYPE_LOG_LIVE, zte, head_self);
|
||||
}
|
||||
|
||||
void zlog_live_disown(struct zlog_live_cfg *cfg)
|
||||
{
|
||||
struct zlt_live *zte;
|
||||
uint_fast32_t state;
|
||||
|
||||
if (!cfg->target)
|
||||
return;
|
||||
|
||||
zte = cfg->target;
|
||||
cfg->target = NULL;
|
||||
|
||||
state = STATE_NORMAL;
|
||||
atomic_compare_exchange_strong_explicit(
|
||||
&zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
|
||||
memory_order_relaxed);
|
||||
if (state == STATE_FD_DEAD)
|
||||
rcu_free(MTYPE_LOG_LIVE, zte, head_self);
|
||||
}
|
53
lib/zlog_live.h
Normal file
53
lib/zlog_live.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _FRR_ZLOG_LIVE_H
|
||||
#define _FRR_ZLOG_LIVE_H
|
||||
|
||||
#include "printfrr.h"
|
||||
|
||||
struct zlog_live_hdr {
|
||||
uint64_t ts_sec;
|
||||
uint32_t ts_nsec;
|
||||
uint32_t prio;
|
||||
uint32_t flags;
|
||||
uint32_t textlen;
|
||||
|
||||
uint32_t arghdrlen;
|
||||
uint32_t n_argpos;
|
||||
struct fmt_outpos argpos[0];
|
||||
};
|
||||
|
||||
struct zlt_live;
|
||||
|
||||
struct zlog_live_cfg {
|
||||
struct zlt_live *target;
|
||||
|
||||
/* nothing else here */
|
||||
};
|
||||
|
||||
extern void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min,
|
||||
int *other_fd);
|
||||
|
||||
static inline bool zlog_live_is_null(struct zlog_live_cfg *cfg)
|
||||
{
|
||||
return cfg->target == NULL;
|
||||
}
|
||||
|
||||
extern void zlog_live_close(struct zlog_live_cfg *cfg);
|
||||
extern void zlog_live_disown(struct zlog_live_cfg *cfg);
|
||||
|
||||
#endif /* _FRR_ZLOG_5424_H */
|
Loading…
Reference in New Issue
Block a user