frr/lib/vty.c

3158 lines
70 KiB
C
Raw Normal View History

2002-12-13 21:15:29 +01:00
/*
* Virtual terminal [aka TeletYpe] interface routine.
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* GNU Zebra is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2002-12-13 21:15:29 +01:00
*/
#include <zebra.h>
#include <lib/version.h>
#include <sys/types.h>
#include <sys/types.h>
#ifdef HAVE_LIBPCREPOSIX
#include <pcreposix.h>
#else
#include <regex.h>
#endif /* HAVE_LIBPCREPOSIX */
#include <stdio.h>
2002-12-13 21:15:29 +01:00
#include "linklist.h"
#include "thread.h"
2002-12-13 21:15:29 +01:00
#include "buffer.h"
#include "command.h"
#include "sockunion.h"
#include "memory.h"
#include "log.h"
#include "prefix.h"
#include "filter.h"
#include "vty.h"
#include "privs.h"
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
#include "network.h"
#include "libfrr.h"
#include "frrstr.h"
#include "lib_errors.h"
#include "northbound_cli.h"
#include "printfrr.h"
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
#include <arpa/telnet.h>
#include <termios.h>
2002-12-13 21:15:29 +01:00
#ifndef VTYSH_EXTRACT_PL
#include "lib/vty_clippy.c"
#endif
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY")
DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer")
DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history")
2002-12-13 21:15:29 +01:00
/* Vty events */
enum event {
VTY_SERV,
VTY_READ,
VTY_WRITE,
VTY_TIMEOUT_RESET,
2002-12-13 21:15:29 +01:00
#ifdef VTYSH
VTYSH_SERV,
VTYSH_READ,
VTYSH_WRITE
2002-12-13 21:15:29 +01:00
#endif /* VTYSH */
};
static void vty_event(enum event, int, struct vty *);
2002-12-13 21:15:29 +01:00
/* Extern host structure from command.c */
extern struct host host;
2002-12-13 21:15:29 +01:00
/* Vector which store each vty structure. */
static vector vtyvec;
/* Vty timeout value. */
static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
/* Vty access-class command */
static char *vty_accesslist_name = NULL;
/* Vty access-calss for IPv6. */
static char *vty_ipv6_accesslist_name = NULL;
/* VTY server thread. */
static vector Vvty_serv_thread;
2002-12-13 21:15:29 +01:00
/* Current directory. */
static char vty_cwd[MAXPATHLEN];
2002-12-13 21:15:29 +01:00
/* Login password check. */
static int no_password_check = 0;
/* Integrated configuration file path */
static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
2002-12-13 21:15:29 +01:00
static bool do_log_commands;
static bool do_log_commands_perm;
void vty_frame(struct vty *vty, const char *format, ...)
{
va_list args;
va_start(args, format);
vsnprintfrr(vty->frame + vty->frame_pos,
sizeof(vty->frame) - vty->frame_pos, format, args);
vty->frame_pos = strlen(vty->frame);
va_end(args);
}
void vty_endframe(struct vty *vty, const char *endtext)
{
if (vty->frame_pos == 0 && endtext)
vty_out(vty, "%s", endtext);
vty->frame_pos = 0;
}
bool vty_set_include(struct vty *vty, const char *regexp)
{
int errcode;
bool ret = true;
char errbuf[256];
if (!regexp) {
if (vty->filter) {
regfree(&vty->include);
vty->filter = false;
}
return true;
}
errcode = regcomp(&vty->include, regexp,
REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
if (errcode) {
ret = false;
regerror(ret, &vty->include, errbuf, sizeof(errbuf));
vty_out(vty, "%% Regex compilation error: %s", errbuf);
} else {
vty->filter = true;
}
return ret;
}
/* VTY standard output function. */
int vty_out(struct vty *vty, const char *format, ...)
{
va_list args;
ssize_t len;
char buf[1024];
char *p = NULL;
char *filtered;
if (vty->frame_pos) {
vty->frame_pos = 0;
vty_out(vty, "%s", vty->frame);
}
va_start(args, format);
p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args);
va_end(args);
len = strlen(p);
/* filter buffer */
if (vty->filter) {
vector lines = frrstr_split_vec(p, "\n");
/* Place first value in the cache */
char *firstline = vector_slot(lines, 0);
buffer_put(vty->lbuf, (uint8_t *) firstline, strlen(firstline));
/* If our split returned more than one entry, time to filter */
if (vector_active(lines) > 1) {
/*
* returned string is MTYPE_TMP so it matches the MTYPE
* of everything else in the vector
*/
char *bstr = buffer_getstr(vty->lbuf);
buffer_reset(vty->lbuf);
XFREE(MTYPE_TMP, lines->index[0]);
vector_set_index(lines, 0, bstr);
frrstr_filter_vec(lines, &vty->include);
vector_compact(lines);
/*
* Consider the string "foo\n". If the regex is an empty string
* and the line ended with a newline, then the vector will look
* like:
*
* [0]: 'foo'
* [1]: ''
*
* If the regex isn't empty, the vector will look like:
*
* [0]: 'foo'
*
* In this case we'd like to preserve the newline, so we add
* the empty string [1] as in the first example.
*/
if (p[strlen(p) - 1] == '\n' && vector_active(lines) > 0
&& strlen(vector_slot(lines, vector_active(lines) - 1)))
vector_set(lines, XSTRDUP(MTYPE_TMP, ""));
filtered = frrstr_join_vec(lines, "\n");
}
else {
filtered = NULL;
}
frrstr_strvec_free(lines);
} else {
filtered = p;
}
if (!filtered)
goto done;
switch (vty->type) {
case VTY_TERM:
/* print with crlf replacement */
buffer_put_crlf(vty->obuf, (uint8_t *)filtered,
strlen(filtered));
break;
case VTY_SHELL:
if (vty->of) {
fprintf(vty->of, "%s", filtered);
fflush(vty->of);
} else if (vty->of_saved) {
fprintf(vty->of_saved, "%s", filtered);
fflush(vty->of_saved);
}
break;
case VTY_SHELL_SERV:
case VTY_FILE:
default:
/* print without crlf replacement */
buffer_put(vty->obuf, (uint8_t *)filtered, strlen(filtered));
break;
}
done:
if (vty->filter && filtered)
XFREE(MTYPE_TMP, filtered);
/* If p is not different with buf, it is allocated buffer. */
if (p != buf)
XFREE(MTYPE_VTY_OUT_BUF, p);
return len;
}
static int vty_log_out(struct vty *vty, const char *level,
const char *proto_str, const char *msg,
struct timestamp_control *ctl)
{
int ret;
int len;
char buf[1024];
if (!ctl->already_rendered) {
ctl->len = quagga_timestamp(ctl->precision, ctl->buf,
sizeof(ctl->buf));
ctl->already_rendered = 1;
}
if (ctl->len + 1 >= sizeof(buf))
return -1;
memcpy(buf, ctl->buf, len = ctl->len);
buf[len++] = ' ';
buf[len] = '\0';
if (level)
ret = snprintf(buf + len, sizeof(buf) - len, "%s: %s: ", level,
proto_str);
else
ret = snprintf(buf + len, sizeof(buf) - len, "%s: ", proto_str);
if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf)))
return -1;
if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0)
|| ((size_t)((len += ret) + 2) > sizeof(buf)))
return -1;
buf[len++] = '\r';
buf[len++] = '\n';
if (write(vty->wfd, buf, len) < 0) {
if (ERRNO_IO_RETRY(errno))
/* Kernel buffer is full, probably too much debugging
output, so just
drop the data and ignore. */
return -1;
/* Fatal I/O error. */
vty->monitor =
0; /* disable monitoring to avoid infinite recursion */
flog_err(EC_LIB_SOCKET,
"%s: write failed to vty client fd %d, closing: %s",
__func__, vty->fd, safe_strerror(errno));
buffer_reset(vty->obuf);
buffer_reset(vty->lbuf);
/* cannot call vty_close, because a parent routine may still try
to access the vty struct */
vty->status = VTY_CLOSE;
shutdown(vty->fd, SHUT_RDWR);
return -1;
}
return 0;
2002-12-13 21:15:29 +01:00
}
/* Output current time to the vty. */
void vty_time_print(struct vty *vty, int cr)
2002-12-13 21:15:29 +01:00
{
char buf[QUAGGA_TIMESTAMP_LEN];
if (quagga_timestamp(0, buf, sizeof(buf)) == 0) {
zlog_info("quagga_timestamp error");
return;
}
if (cr)
vty_out(vty, "%s\n", buf);
else
vty_out(vty, "%s ", buf);
2002-12-13 21:15:29 +01:00
return;
2002-12-13 21:15:29 +01:00
}
/* Say hello to vty interface. */
void vty_hello(struct vty *vty)
{
if (host.motdfile) {
FILE *f;
char buf[4096];
f = fopen(host.motdfile, "r");
if (f) {
while (fgets(buf, sizeof(buf), f)) {
char *s;
/* work backwards to ignore trailling isspace()
*/
for (s = buf + strlen(buf);
(s > buf) && isspace((unsigned char)s[-1]);
s--)
;
*s = '\0';
vty_out(vty, "%s\n", buf);
}
fclose(f);
} else
vty_out(vty, "MOTD file not found\n");
} else if (host.motd)
vty_out(vty, "%s", host.motd);
#if CONFDATE > 20200901
CPP_NOTICE("Please remove solaris code from system as it is deprecated");
#endif
#ifdef SUNOS_5
zlog_warn("If you are using FRR on Solaris, the FRR developers would love to hear from you\n");
zlog_warn("Please send email to dev@lists.frrouting.org about this message\n");
zlog_warn("We are considering deprecating Solaris and want to find users of Solaris systems\n");
#endif
2002-12-13 21:15:29 +01:00
}
/* Put out prompt and wait input from user. */
static void vty_prompt(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
if (vty->type == VTY_TERM) {
vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
}
2002-12-13 21:15:29 +01:00
}
/* Send WILL TELOPT_ECHO to remote server. */
static void vty_will_echo(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
vty_out(vty, "%s", cmd);
2002-12-13 21:15:29 +01:00
}
/* Make suppress Go-Ahead telnet option. */
static void vty_will_suppress_go_ahead(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
vty_out(vty, "%s", cmd);
2002-12-13 21:15:29 +01:00
}
/* Make don't use linemode over telnet. */
static void vty_dont_linemode(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
vty_out(vty, "%s", cmd);
2002-12-13 21:15:29 +01:00
}
/* Use window size. */
static void vty_do_window_size(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
vty_out(vty, "%s", cmd);
2002-12-13 21:15:29 +01:00
}
#if 0 /* Currently not used. */
2002-12-13 21:15:29 +01:00
/* Make don't use lflow vty interface. */
static void
vty_dont_lflow_ahead (struct vty *vty)
{
unsigned char cmd[] = { IAC, DONT, TELOPT_LFLOW, '\0' };
2002-12-13 21:15:29 +01:00
vty_out (vty, "%s", cmd);
}
#endif /* 0 */
/* Authentication of vty */
static void vty_auth(struct vty *vty, char *buf)
{
char *passwd = NULL;
enum node_type next_node = 0;
int fail;
char *crypt(const char *, const char *);
switch (vty->node) {
case AUTH_NODE:
if (host.encrypt)
passwd = host.password_encrypt;
else
passwd = host.password;
if (host.advanced)
next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
else
next_node = VIEW_NODE;
break;
case AUTH_ENABLE_NODE:
if (host.encrypt)
passwd = host.enable_encrypt;
else
passwd = host.enable;
next_node = ENABLE_NODE;
break;
}
if (passwd) {
if (host.encrypt)
fail = strcmp(crypt(buf, passwd), passwd);
else
fail = strcmp(buf, passwd);
} else
fail = 1;
if (!fail) {
vty->fail = 0;
vty->node = next_node; /* Success ! */
} else {
vty->fail++;
if (vty->fail >= 3) {
if (vty->node == AUTH_NODE) {
vty_out(vty,
"%% Bad passwords, too many failures!\n");
vty->status = VTY_CLOSE;
} else {
/* AUTH_ENABLE_NODE */
vty->fail = 0;
vty_out(vty,
"%% Bad enable passwords, too many failures!\n");
vty->status = VTY_CLOSE;
}
}
}
2002-12-13 21:15:29 +01:00
}
/* Command execution over the vty interface. */
static int vty_command(struct vty *vty, char *buf)
{
int ret;
const char *protocolname;
char *cp = NULL;
assert(vty);
/*
* Log non empty command lines
*/
if (do_log_commands)
cp = buf;
if (cp != NULL) {
/* Skip white spaces. */
while (isspace((unsigned char)*cp) && *cp != '\0')
cp++;
}
if (cp != NULL && *cp != '\0') {
unsigned i;
char vty_str[VTY_BUFSIZ];
char prompt_str[VTY_BUFSIZ];
/* format the base vty info */
snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address);
for (i = 0; i < vector_active(vtyvec); i++)
if (vty == vector_slot(vtyvec, i)) {
snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s",
i, vty->address);
break;
}
/* format the prompt */
snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
vty_str);
/* now log the command */
zlog_notice("%s%s", prompt_str, buf);
}
2002-12-13 21:15:29 +01:00
#ifdef CONSUMED_TIME_CHECK
{
RUSAGE_T before;
RUSAGE_T after;
unsigned long realtime, cputime;
GETRUSAGE(&before);
#endif /* CONSUMED_TIME_CHECK */
ret = cmd_execute(vty, buf, NULL, 0);
2002-12-13 21:15:29 +01:00
/* Get the name of the protocol if any */
protocolname = frr_protoname;
#ifdef CONSUMED_TIME_CHECK
GETRUSAGE(&after);
if ((realtime = thread_consumed_time(&after, &before, &cputime))
> CONSUMED_TIME_CHECK)
/* Warn about CPU hog that must be fixed. */
flog_warn(
EC_LIB_SLOW_THREAD,
"SLOW COMMAND: command took %lums (cpu time %lums): %s",
realtime / 1000, cputime / 1000, buf);
}
#endif /* CONSUMED_TIME_CHECK */
if (ret != CMD_SUCCESS)
switch (ret) {
case CMD_WARNING:
if (vty->type == VTY_FILE)
vty_out(vty, "Warning...\n");
break;
case CMD_ERR_AMBIGUOUS:
vty_out(vty, "%% Ambiguous command.\n");
break;
case CMD_ERR_NO_MATCH:
vty_out(vty, "%% [%s] Unknown command: %s\n",
protocolname, buf);
break;
case CMD_ERR_INCOMPLETE:
vty_out(vty, "%% Command incomplete.\n");
break;
}
return ret;
2002-12-13 21:15:29 +01:00
}
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
static const char telnet_backward_char = 0x08;
static const char telnet_space_char = ' ';
2002-12-13 21:15:29 +01:00
/* Basic function to write buffer to vty. */
static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
2002-12-13 21:15:29 +01:00
{
if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
return;
2002-12-13 21:15:29 +01:00
/* Should we do buffering here ? And make vty_flush (vty) ? */
buffer_put(vty->obuf, buf, nbytes);
2002-12-13 21:15:29 +01:00
}
/* Basic function to insert character into vty. */
static void vty_self_insert(struct vty *vty, char c)
2002-12-13 21:15:29 +01:00
{
int i;
int length;
2002-12-13 21:15:29 +01:00
if (vty->length + 1 >= VTY_BUFSIZ)
return;
length = vty->length - vty->cp;
memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
vty->buf[vty->cp] = c;
2002-12-13 21:15:29 +01:00
vty_write(vty, &vty->buf[vty->cp], length + 1);
for (i = 0; i < length; i++)
vty_write(vty, &telnet_backward_char, 1);
2002-12-13 21:15:29 +01:00
vty->cp++;
vty->length++;
vty->buf[vty->length] = '\0';
2002-12-13 21:15:29 +01:00
}
/* Self insert character 'c' in overwrite mode. */
static void vty_self_insert_overwrite(struct vty *vty, char c)
2002-12-13 21:15:29 +01:00
{
if (vty->cp == vty->length) {
vty_self_insert(vty, c);
return;
}
2002-12-13 21:15:29 +01:00
vty->buf[vty->cp++] = c;
vty_write(vty, &c, 1);
2002-12-13 21:15:29 +01:00
}
/**
* Insert a string into vty->buf at the current cursor position.
*
* If the resultant string would be larger than VTY_BUFSIZ it is
* truncated to fit.
*/
static void vty_insert_word_overwrite(struct vty *vty, char *str)
2002-12-13 21:15:29 +01:00
{
if (vty->cp == VTY_BUFSIZ)
return;
size_t nwrite = MIN((int)strlen(str), VTY_BUFSIZ - vty->cp - 1);
memcpy(&vty->buf[vty->cp], str, nwrite);
vty->cp += nwrite;
vty->length = MAX(vty->cp, vty->length);
vty->buf[vty->length] = '\0';
vty_write(vty, str, nwrite);
2002-12-13 21:15:29 +01:00
}
/* Forward character. */
static void vty_forward_char(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
if (vty->cp < vty->length) {
vty_write(vty, &vty->buf[vty->cp], 1);
vty->cp++;
}
2002-12-13 21:15:29 +01:00
}
/* Backward character. */
static void vty_backward_char(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
if (vty->cp > 0) {
vty->cp--;
vty_write(vty, &telnet_backward_char, 1);
}
2002-12-13 21:15:29 +01:00
}
/* Move to the beginning of the line. */
static void vty_beginning_of_line(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp)
vty_backward_char(vty);
2002-12-13 21:15:29 +01:00
}
/* Move to the end of the line. */
static void vty_end_of_line(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp < vty->length)
vty_forward_char(vty);
2002-12-13 21:15:29 +01:00
}
static void vty_kill_line_from_beginning(struct vty *);
static void vty_redraw_line(struct vty *);
2002-12-13 21:15:29 +01:00
/* Print command line history. This function is called from
vty_next_line and vty_previous_line. */
static void vty_history_print(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int length;
2002-12-13 21:15:29 +01:00
vty_kill_line_from_beginning(vty);
2002-12-13 21:15:29 +01:00
/* Get previous line from history buffer */
length = strlen(vty->hist[vty->hp]);
memcpy(vty->buf, vty->hist[vty->hp], length);
vty->cp = vty->length = length;
vty->buf[vty->length] = '\0';
2002-12-13 21:15:29 +01:00
/* Redraw current line */
vty_redraw_line(vty);
2002-12-13 21:15:29 +01:00
}
/* Show next command line history. */
static void vty_next_line(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int try_index;
2002-12-13 21:15:29 +01:00
if (vty->hp == vty->hindex)
return;
2002-12-13 21:15:29 +01:00
/* Try is there history exist or not. */
try_index = vty->hp;
if (try_index == (VTY_MAXHIST - 1))
try_index = 0;
else
try_index++;
2002-12-13 21:15:29 +01:00
/* If there is not history return. */
if (vty->hist[try_index] == NULL)
return;
else
vty->hp = try_index;
2002-12-13 21:15:29 +01:00
vty_history_print(vty);
2002-12-13 21:15:29 +01:00
}
/* Show previous command line history. */
static void vty_previous_line(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int try_index;
2002-12-13 21:15:29 +01:00
try_index = vty->hp;
if (try_index == 0)
try_index = VTY_MAXHIST - 1;
else
try_index--;
2002-12-13 21:15:29 +01:00
if (vty->hist[try_index] == NULL)
return;
else
vty->hp = try_index;
2002-12-13 21:15:29 +01:00
vty_history_print(vty);
2002-12-13 21:15:29 +01:00
}
/* This function redraw all of the command line character. */
static void vty_redraw_line(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
vty_write(vty, vty->buf, vty->length);
vty->cp = vty->length;
2002-12-13 21:15:29 +01:00
}
/* Forward word. */
static void vty_forward_word(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
vty_forward_char(vty);
while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
vty_forward_char(vty);
2002-12-13 21:15:29 +01:00
}
/* Backward word without skipping training space. */
static void vty_backward_pure_word(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
vty_backward_char(vty);
2002-12-13 21:15:29 +01:00
}
/* Backward word. */
static void vty_backward_word(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
vty_backward_char(vty);
2002-12-13 21:15:29 +01:00
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
vty_backward_char(vty);
2002-12-13 21:15:29 +01:00
}
/* When '^D' is typed at the beginning of the line we move to the down
level. */
static void vty_down_level(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
vty_out(vty, "\n");
cmd_exit(vty);
vty_prompt(vty);
vty->cp = 0;
2002-12-13 21:15:29 +01:00
}
/* When '^Z' is received from vty, move down to the enable mode. */
static void vty_end_config(struct vty *vty)
{
vty_out(vty, "\n");
if (vty->config) {
vty_config_exit(vty);
vty->node = ENABLE_NODE;
}
vty_prompt(vty);
vty->cp = 0;
2002-12-13 21:15:29 +01:00
}
/* Delete a charcter at the current point. */
static void vty_delete_char(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int i;
int size;
2002-12-13 21:15:29 +01:00
if (vty->length == 0) {
vty_down_level(vty);
return;
}
2002-12-13 21:15:29 +01:00
if (vty->cp == vty->length)
return; /* completion need here? */
2002-12-13 21:15:29 +01:00
size = vty->length - vty->cp;
2002-12-13 21:15:29 +01:00
vty->length--;
memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
vty->buf[vty->length] = '\0';
if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
return;
2002-12-13 21:15:29 +01:00
vty_write(vty, &vty->buf[vty->cp], size - 1);
vty_write(vty, &telnet_space_char, 1);
2002-12-13 21:15:29 +01:00
for (i = 0; i < size; i++)
vty_write(vty, &telnet_backward_char, 1);
2002-12-13 21:15:29 +01:00
}
/* Delete a character before the point. */
static void vty_delete_backward_char(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
if (vty->cp == 0)
return;
2002-12-13 21:15:29 +01:00
vty_backward_char(vty);
vty_delete_char(vty);
2002-12-13 21:15:29 +01:00
}
/* Kill rest of line from current point. */
static void vty_kill_line(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int i;
int size;
2002-12-13 21:15:29 +01:00
size = vty->length - vty->cp;
if (size == 0)
return;
2002-12-13 21:15:29 +01:00
for (i = 0; i < size; i++)
vty_write(vty, &telnet_space_char, 1);
for (i = 0; i < size; i++)
vty_write(vty, &telnet_backward_char, 1);
2002-12-13 21:15:29 +01:00
memset(&vty->buf[vty->cp], 0, size);
vty->length = vty->cp;
2002-12-13 21:15:29 +01:00
}
/* Kill line from the beginning. */
static void vty_kill_line_from_beginning(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
vty_beginning_of_line(vty);
vty_kill_line(vty);
2002-12-13 21:15:29 +01:00
}
/* Delete a word before the point. */
static void vty_forward_kill_word(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
vty_delete_char(vty);
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
vty_delete_char(vty);
2002-12-13 21:15:29 +01:00
}
/* Delete a word before the point. */
static void vty_backward_kill_word(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
vty_delete_backward_char(vty);
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
vty_delete_backward_char(vty);
2002-12-13 21:15:29 +01:00
}
/* Transpose chars before or at the point. */
static void vty_transpose_chars(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
char c1, c2;
2002-12-13 21:15:29 +01:00
/* If length is short or point is near by the beginning of line then
return. */
if (vty->length < 2 || vty->cp < 1)
return;
2002-12-13 21:15:29 +01:00
/* In case of point is located at the end of the line. */
if (vty->cp == vty->length) {
c1 = vty->buf[vty->cp - 1];
c2 = vty->buf[vty->cp - 2];
2002-12-13 21:15:29 +01:00
vty_backward_char(vty);
vty_backward_char(vty);
vty_self_insert_overwrite(vty, c1);
vty_self_insert_overwrite(vty, c2);
} else {
c1 = vty->buf[vty->cp];
c2 = vty->buf[vty->cp - 1];
2002-12-13 21:15:29 +01:00
vty_backward_char(vty);
vty_self_insert_overwrite(vty, c1);
vty_self_insert_overwrite(vty, c2);
}
2002-12-13 21:15:29 +01:00
}
/* Do completion at vty interface. */
static void vty_complete_command(struct vty *vty)
{
int i;
int ret;
char **matched = NULL;
vector vline;
if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
return;
vline = cmd_make_strvec(vty->buf);
if (vline == NULL)
return;
/* In case of 'help \t'. */
if (isspace((unsigned char)vty->buf[vty->length - 1]))
vector_set(vline, NULL);
matched = cmd_complete_command(vline, vty, &ret);
cmd_free_strvec(vline);
vty_out(vty, "\n");
switch (ret) {
case CMD_ERR_AMBIGUOUS:
vty_out(vty, "%% Ambiguous command.\n");
vty_prompt(vty);
vty_redraw_line(vty);
break;
case CMD_ERR_NO_MATCH:
/* vty_out (vty, "%% There is no matched command.\n"); */
vty_prompt(vty);
vty_redraw_line(vty);
break;
case CMD_COMPLETE_FULL_MATCH:
if (!matched[0]) {
/* 2016-11-28 equinox -- need to debug, SEGV here */
vty_out(vty, "%% CLI BUG: FULL_MATCH with NULL str\n");
vty_prompt(vty);
vty_redraw_line(vty);
break;
}
vty_prompt(vty);
vty_redraw_line(vty);
vty_backward_pure_word(vty);
vty_insert_word_overwrite(vty, matched[0]);
vty_self_insert(vty, ' ');
XFREE(MTYPE_COMPLETION, matched[0]);
break;
case CMD_COMPLETE_MATCH:
vty_prompt(vty);
vty_redraw_line(vty);
vty_backward_pure_word(vty);
vty_insert_word_overwrite(vty, matched[0]);
XFREE(MTYPE_COMPLETION, matched[0]);
break;
case CMD_COMPLETE_LIST_MATCH:
for (i = 0; matched[i] != NULL; i++) {
if (i != 0 && ((i % 6) == 0))
vty_out(vty, "\n");
vty_out(vty, "%-10s ", matched[i]);
XFREE(MTYPE_COMPLETION, matched[i]);
}
vty_out(vty, "\n");
vty_prompt(vty);
vty_redraw_line(vty);
break;
case CMD_ERR_NOTHING_TODO:
vty_prompt(vty);
vty_redraw_line(vty);
break;
default:
break;
}
XFREE(MTYPE_TMP, matched);
}
static void vty_describe_fold(struct vty *vty, int cmd_width,
unsigned int desc_width, struct cmd_token *token)
{
char *buf;
const char *cmd, *p;
int pos;
cmd = token->text;
if (desc_width <= 0) {
vty_out(vty, " %-*s %s\n", cmd_width, cmd, token->desc);
return;
}
buf = XCALLOC(MTYPE_TMP, strlen(token->desc) + 1);
for (p = token->desc; strlen(p) > desc_width; p += pos + 1) {
for (pos = desc_width; pos > 0; pos--)
if (*(p + pos) == ' ')
break;
if (pos == 0)
break;
memcpy(buf, p, pos);
buf[pos] = '\0';
vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf);
cmd = "";
}
vty_out(vty, " %-*s %s\n", cmd_width, cmd, p);
XFREE(MTYPE_TMP, buf);
2002-12-13 21:15:29 +01:00
}
/* Describe matched command function. */
static void vty_describe_command(struct vty *vty)
{
int ret;
vector vline;
vector describe;
unsigned int i, width, desc_width;
struct cmd_token *token, *token_cr = NULL;
vline = cmd_make_strvec(vty->buf);
/* In case of '> ?'. */
if (vline == NULL) {
vline = vector_init(1);
vector_set(vline, NULL);
} else if (isspace((unsigned char)vty->buf[vty->length - 1]))
vector_set(vline, NULL);
describe = cmd_describe_command(vline, vty, &ret);
vty_out(vty, "\n");
/* Ambiguous error. */
switch (ret) {
case CMD_ERR_AMBIGUOUS:
vty_out(vty, "%% Ambiguous command.\n");
goto out;
break;
case CMD_ERR_NO_MATCH:
vty_out(vty, "%% There is no matched command.\n");
goto out;
break;
}
/* Get width of command string. */
width = 0;
for (i = 0; i < vector_active(describe); i++)
if ((token = vector_slot(describe, i)) != NULL) {
unsigned int len;
if (token->text[0] == '\0')
continue;
len = strlen(token->text);
if (width < len)
width = len;
}
/* Get width of description string. */
desc_width = vty->width - (width + 6);
/* Print out description. */
for (i = 0; i < vector_active(describe); i++)
if ((token = vector_slot(describe, i)) != NULL) {
if (token->text[0] == '\0')
continue;
if (strcmp(token->text, CMD_CR_TEXT) == 0) {
token_cr = token;
continue;
}
if (!token->desc)
vty_out(vty, " %-s\n", token->text);
else if (desc_width >= strlen(token->desc))
vty_out(vty, " %-*s %s\n", width, token->text,
token->desc);
else
vty_describe_fold(vty, width, desc_width,
token);
if (IS_VARYING_TOKEN(token->type)) {
const char *ref = vector_slot(
vline, vector_active(vline) - 1);
vector varcomps = vector_init(VECTOR_MIN_SIZE);
cmd_variable_complete(token, ref, varcomps);
if (vector_active(varcomps) > 0) {
char *ac = cmd_variable_comp2str(
varcomps, vty->width);
vty_out(vty, "%s\n", ac);
XFREE(MTYPE_TMP, ac);
}
vector_free(varcomps);
}
2002-12-13 21:15:29 +01:00
#if 0
vty_out (vty, " %-*s %s\n", width
desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd,
desc->str ? desc->str : "");
2002-12-13 21:15:29 +01:00
#endif /* 0 */
}
if ((token = token_cr)) {
if (!token->desc)
vty_out(vty, " %-s\n", token->text);
else if (desc_width >= strlen(token->desc))
vty_out(vty, " %-*s %s\n", width, token->text,
token->desc);
else
vty_describe_fold(vty, width, desc_width, token);
}
2002-12-13 21:15:29 +01:00
out:
cmd_free_strvec(vline);
if (describe)
vector_free(describe);
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
vty_redraw_line(vty);
2002-12-13 21:15:29 +01:00
}
static void vty_clear_buf(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
memset(vty->buf, 0, vty->max);
2002-12-13 21:15:29 +01:00
}
/* ^C stop current input and do not add command line to the history. */
static void vty_stop_input(struct vty *vty)
{
vty->cp = vty->length = 0;
vty_clear_buf(vty);
vty_out(vty, "\n");
if (vty->config) {
vty_config_exit(vty);
vty->node = ENABLE_NODE;
}
vty_prompt(vty);
/* Set history pointer to the latest one. */
vty->hp = vty->hindex;
2002-12-13 21:15:29 +01:00
}
/* Add current command line to the history buffer. */
static void vty_hist_add(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int index;
2002-12-13 21:15:29 +01:00
if (vty->length == 0)
return;
2002-12-13 21:15:29 +01:00
index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
2002-12-13 21:15:29 +01:00
/* Ignore the same string as previous one. */
if (vty->hist[index])
if (strcmp(vty->buf, vty->hist[index]) == 0) {
vty->hp = vty->hindex;
return;
}
2002-12-13 21:15:29 +01:00
/* Insert history entry. */
XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
2002-12-13 21:15:29 +01:00
/* History index rotation. */
vty->hindex++;
if (vty->hindex == VTY_MAXHIST)
vty->hindex = 0;
2002-12-13 21:15:29 +01:00
vty->hp = vty->hindex;
2002-12-13 21:15:29 +01:00
}
/* #define TELNET_OPTION_DEBUG */
/* Get telnet window size. */
static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
2002-12-13 21:15:29 +01:00
{
#ifdef TELNET_OPTION_DEBUG
int i;
for (i = 0; i < nbytes; i++) {
switch (buf[i]) {
case IAC:
vty_out(vty, "IAC ");
break;
case WILL:
vty_out(vty, "WILL ");
break;
case WONT:
vty_out(vty, "WONT ");
break;
case DO:
vty_out(vty, "DO ");
break;
case DONT:
vty_out(vty, "DONT ");
break;
case SB:
vty_out(vty, "SB ");
break;
case SE:
vty_out(vty, "SE ");
break;
case TELOPT_ECHO:
vty_out(vty, "TELOPT_ECHO \n");
break;
case TELOPT_SGA:
vty_out(vty, "TELOPT_SGA \n");
break;
case TELOPT_NAWS:
vty_out(vty, "TELOPT_NAWS \n");
break;
default:
vty_out(vty, "%x ", buf[i]);
break;
}
}
vty_out(vty, "\n");
2002-12-13 21:15:29 +01:00
#endif /* TELNET_OPTION_DEBUG */
switch (buf[0]) {
case SB:
vty->sb_len = 0;
vty->iac_sb_in_progress = 1;
return 0;
case SE: {
if (!vty->iac_sb_in_progress)
return 0;
if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) {
vty->iac_sb_in_progress = 0;
return 0;
}
switch (vty->sb_buf[0]) {
case TELOPT_NAWS:
if (vty->sb_len != TELNET_NAWS_SB_LEN)
flog_err(
EC_LIB_SYSTEM_CALL,
"RFC 1073 violation detected: telnet NAWS option "
"should send %d characters, but we received %lu",
TELNET_NAWS_SB_LEN,
(unsigned long)vty->sb_len);
else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
flog_err(
EC_LIB_DEVELOPMENT,
"Bug detected: sizeof(vty->sb_buf) %lu < %d, too small to handle the telnet NAWS option",
(unsigned long)sizeof(vty->sb_buf),
TELNET_NAWS_SB_LEN);
else {
vty->width = ((vty->sb_buf[1] << 8)
| vty->sb_buf[2]);
vty->height = ((vty->sb_buf[3] << 8)
| vty->sb_buf[4]);
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
#ifdef TELNET_OPTION_DEBUG
vty_out(vty,
"TELNET NAWS window size negotiation completed: "
"width %d, height %d\n",
vty->width, vty->height);
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
#endif
}
break;
}
vty->iac_sb_in_progress = 0;
return 0;
}
default:
break;
}
return 1;
2002-12-13 21:15:29 +01:00
}
/* Execute current command line. */
static int vty_execute(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int ret;
2002-12-13 21:15:29 +01:00
ret = CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
switch (vty->node) {
case AUTH_NODE:
case AUTH_ENABLE_NODE:
vty_auth(vty, vty->buf);
break;
default:
ret = vty_command(vty, vty->buf);
if (vty->type == VTY_TERM)
vty_hist_add(vty);
break;
}
2002-12-13 21:15:29 +01:00
/* Clear command line buffer. */
vty->cp = vty->length = 0;
vty_clear_buf(vty);
2002-12-13 21:15:29 +01:00
if (vty->status != VTY_CLOSE)
vty_prompt(vty);
2002-12-13 21:15:29 +01:00
return ret;
2002-12-13 21:15:29 +01:00
}
#define CONTROL(X) ((X) - '@')
#define VTY_NORMAL 0
#define VTY_PRE_ESCAPE 1
#define VTY_ESCAPE 2
/* Escape character command map. */
static void vty_escape_map(unsigned char c, struct vty *vty)
{
switch (c) {
case ('A'):
vty_previous_line(vty);
break;
case ('B'):
vty_next_line(vty);
break;
case ('C'):
vty_forward_char(vty);
break;
case ('D'):
vty_backward_char(vty);
break;
default:
break;
}
/* Go back to normal mode. */
vty->escape = VTY_NORMAL;
2002-12-13 21:15:29 +01:00
}
/* Quit print out to the buffer. */
static void vty_buffer_reset(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
buffer_reset(vty->obuf);
buffer_reset(vty->lbuf);
vty_prompt(vty);
vty_redraw_line(vty);
2002-12-13 21:15:29 +01:00
}
/* Read data via vty socket. */
static int vty_read(struct thread *thread)
{
int i;
int nbytes;
unsigned char buf[VTY_READ_BUFSIZ];
int vty_sock = THREAD_FD(thread);
struct vty *vty = THREAD_ARG(thread);
/* Read raw data from socket */
if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
vty_event(VTY_READ, vty_sock, vty);
return 0;
}
vty->monitor = 0; /* disable monitoring to avoid
infinite recursion */
flog_err(
EC_LIB_SOCKET,
"%s: read error on vty client fd %d, closing: %s",
__func__, vty->fd, safe_strerror(errno));
buffer_reset(vty->obuf);
buffer_reset(vty->lbuf);
}
vty->status = VTY_CLOSE;
}
for (i = 0; i < nbytes; i++) {
if (buf[i] == IAC) {
if (!vty->iac) {
vty->iac = 1;
continue;
} else {
vty->iac = 0;
}
}
if (vty->iac_sb_in_progress && !vty->iac) {
if (vty->sb_len < sizeof(vty->sb_buf))
vty->sb_buf[vty->sb_len] = buf[i];
vty->sb_len++;
continue;
}
if (vty->iac) {
/* In case of telnet command */
int ret = 0;
ret = vty_telnet_option(vty, buf + i, nbytes - i);
vty->iac = 0;
i += ret;
continue;
}
if (vty->status == VTY_MORE) {
switch (buf[i]) {
case CONTROL('C'):
case 'q':
case 'Q':
vty_buffer_reset(vty);
break;
2002-12-13 21:15:29 +01:00
#if 0 /* More line does not work for "show ip bgp". */
case '\n':
case '\r':
vty->status = VTY_MORELINE;
break;
2002-12-13 21:15:29 +01:00
#endif
default:
break;
}
continue;
}
/* Escape character. */
if (vty->escape == VTY_ESCAPE) {
vty_escape_map(buf[i], vty);
continue;
}
/* Pre-escape status. */
if (vty->escape == VTY_PRE_ESCAPE) {
switch (buf[i]) {
case '[':
vty->escape = VTY_ESCAPE;
break;
case 'b':
vty_backward_word(vty);
vty->escape = VTY_NORMAL;
break;
case 'f':
vty_forward_word(vty);
vty->escape = VTY_NORMAL;
break;
case 'd':
vty_forward_kill_word(vty);
vty->escape = VTY_NORMAL;
break;
case CONTROL('H'):
case 0x7f:
vty_backward_kill_word(vty);
vty->escape = VTY_NORMAL;
break;
default:
vty->escape = VTY_NORMAL;
break;
}
continue;
}
switch (buf[i]) {
case CONTROL('A'):
vty_beginning_of_line(vty);
break;
case CONTROL('B'):
vty_backward_char(vty);
break;
case CONTROL('C'):
vty_stop_input(vty);
break;
case CONTROL('D'):
vty_delete_char(vty);
break;
case CONTROL('E'):
vty_end_of_line(vty);
break;
case CONTROL('F'):
vty_forward_char(vty);
break;
case CONTROL('H'):
case 0x7f:
vty_delete_backward_char(vty);
break;
case CONTROL('K'):
vty_kill_line(vty);
break;
case CONTROL('N'):
vty_next_line(vty);
break;
case CONTROL('P'):
vty_previous_line(vty);
break;
case CONTROL('T'):
vty_transpose_chars(vty);
break;
case CONTROL('U'):
vty_kill_line_from_beginning(vty);
break;
case CONTROL('W'):
vty_backward_kill_word(vty);
break;
case CONTROL('Z'):
vty_end_config(vty);
break;
case '\n':
case '\r':
vty_out(vty, "\n");
vty_execute(vty);
break;
case '\t':
vty_complete_command(vty);
break;
case '?':
if (vty->node == AUTH_NODE
|| vty->node == AUTH_ENABLE_NODE)
vty_self_insert(vty, buf[i]);
else
vty_describe_command(vty);
break;
case '\033':
if (i + 1 < nbytes && buf[i + 1] == '[') {
vty->escape = VTY_ESCAPE;
i++;
} else
vty->escape = VTY_PRE_ESCAPE;
break;
default:
if (buf[i] > 31 && buf[i] < 127)
vty_self_insert(vty, buf[i]);
break;
}
}
/* Check status. */
if (vty->status == VTY_CLOSE)
vty_close(vty);
else {
vty_event(VTY_WRITE, vty->wfd, vty);
vty_event(VTY_READ, vty_sock, vty);
}
return 0;
2002-12-13 21:15:29 +01:00
}
/* Flush buffer to the vty. */
static int vty_flush(struct thread *thread)
{
int erase;
buffer_status_t flushrc;
int vty_sock = THREAD_FD(thread);
struct vty *vty = THREAD_ARG(thread);
/* Tempolary disable read thread. */
if (vty->lines == 0)
THREAD_OFF(vty->t_read);
/* Function execution continue. */
erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
/* N.B. if width is 0, that means we don't know the window size. */
if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
flushrc = buffer_flush_available(vty->obuf, vty_sock);
else if (vty->status == VTY_MORELINE)
flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width,
1, erase, 0);
else
flushrc = buffer_flush_window(
vty->obuf, vty_sock, vty->width,
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, closing",
vty->fd);
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty_close(vty);
return 0;
case BUFFER_EMPTY:
if (vty->status == VTY_CLOSE)
vty_close(vty);
else {
vty->status = VTY_NORMAL;
if (vty->lines == 0)
vty_event(VTY_READ, vty_sock, vty);
}
break;
case BUFFER_PENDING:
/* There is more data waiting to be written. */
vty->status = VTY_MORE;
if (vty->lines == 0)
vty_event(VTY_WRITE, vty_sock, vty);
break;
}
return 0;
2002-12-13 21:15:29 +01:00
}
/* Allocate new vty struct. */
struct vty *vty_new(void)
{
struct vty *new = XCALLOC(MTYPE_VTY, sizeof(struct vty));
new->fd = new->wfd = -1;
new->of = stdout;
new->lbuf = buffer_new(0);
new->obuf = buffer_new(0); /* Use default buffer size. */
new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
new->max = VTY_BUFSIZ;
return new;
}
/* allocate and initialise vty */
static struct vty *vty_new_init(int vty_sock)
{
struct vty *vty;
vty = vty_new();
vty->fd = vty_sock;
vty->wfd = vty_sock;
vty->type = VTY_TERM;
vty->node = AUTH_NODE;
vty->fail = 0;
vty->cp = 0;
vty_clear_buf(vty);
vty->length = 0;
memset(vty->hist, 0, sizeof(vty->hist));
vty->hp = 0;
vty->hindex = 0;
vty->xpath_index = 0;
memset(vty->xpath, 0, sizeof(vty->xpath));
vty->private_config = false;
vty->candidate_config = vty_shared_candidate_config;
vector_set_index(vtyvec, vty_sock, vty);
vty->status = VTY_NORMAL;
vty->lines = -1;
vty->iac = 0;
vty->iac_sb_in_progress = 0;
vty->sb_len = 0;
return vty;
}
2002-12-13 21:15:29 +01:00
/* Create new vty structure. */
static struct vty *vty_create(int vty_sock, union sockunion *su)
{
char buf[SU_ADDRSTRLEN];
struct vty *vty;
sockunion2str(su, buf, SU_ADDRSTRLEN);
/* Allocate new vty structure and set up default values. */
vty = vty_new_init(vty_sock);
/* configurable parameters not part of basic init */
vty->v_timeout = vty_timeout_val;
strlcpy(vty->address, buf, sizeof(vty->address));
if (no_password_check) {
if (host.advanced)
vty->node = ENABLE_NODE;
else
vty->node = VIEW_NODE;
}
if (host.lines >= 0)
vty->lines = host.lines;
if (!no_password_check) {
/* Vty is not available if password isn't set. */
if (host.password == NULL && host.password_encrypt == NULL) {
vty_out(vty, "Vty password is not set.\n");
vty->status = VTY_CLOSE;
vty_close(vty);
return NULL;
}
}
/* Say hello to the world. */
vty_hello(vty);
if (!no_password_check)
vty_out(vty, "\nUser Access Verification\n\n");
/* Setting up terminal. */
vty_will_echo(vty);
vty_will_suppress_go_ahead(vty);
vty_dont_linemode(vty);
vty_do_window_size(vty);
/* vty_dont_lflow_ahead (vty); */
vty_prompt(vty);
/* Add read/write thread. */
vty_event(VTY_WRITE, vty_sock, vty);
vty_event(VTY_READ, vty_sock, vty);
return vty;
2002-12-13 21:15:29 +01:00
}
/* create vty for stdio */
static struct termios stdio_orig_termios;
static struct vty *stdio_vty = NULL;
static bool stdio_termios = false;
static void (*stdio_vty_atclose)(int isexit);
static void vty_stdio_reset(int isexit)
{
if (stdio_vty) {
if (stdio_termios)
tcsetattr(0, TCSANOW, &stdio_orig_termios);
stdio_termios = false;
stdio_vty = NULL;
if (stdio_vty_atclose)
stdio_vty_atclose(isexit);
stdio_vty_atclose = NULL;
}
}
static void vty_stdio_atexit(void)
{
vty_stdio_reset(1);
}
void vty_stdio_suspend(void)
{
if (!stdio_vty)
return;
THREAD_OFF(stdio_vty->t_write);
THREAD_OFF(stdio_vty->t_read);
THREAD_OFF(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;
/* refuse creating two vtys on stdio */
if (stdio_vty)
return NULL;
vty = stdio_vty = vty_new_init(0);
stdio_vty_atclose = atclose;
vty->wfd = 1;
/* always have stdio vty in a known _unchangeable_ state, don't want
* config
* to have any effect here to make sure scripting this works as intended
*/
vty->node = ENABLE_NODE;
vty->v_timeout = 0;
strlcpy(vty->address, "console", sizeof(vty->address));
vty_stdio_resume();
return vty;
}
2002-12-13 21:15:29 +01:00
/* Accept connection from the network. */
static int vty_accept(struct thread *thread)
{
int vty_sock;
union sockunion su;
int ret;
unsigned int on;
int accept_sock;
struct prefix p;
struct access_list *acl = NULL;
char buf[SU_ADDRSTRLEN];
accept_sock = THREAD_FD(thread);
/* We continue hearing vty socket. */
vty_event(VTY_SERV, accept_sock, NULL);
memset(&su, 0, sizeof(union sockunion));
/* We can handle IPv4 or IPv6 socket. */
vty_sock = sockunion_accept(accept_sock, &su);
if (vty_sock < 0) {
flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
safe_strerror(errno));
return -1;
}
set_nonblocking(vty_sock);
set_cloexec(vty_sock);
sockunion2hostprefix(&su, &p);
/* VTY's accesslist apply. */
if (p.family == AF_INET && vty_accesslist_name) {
if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
zlog_info("Vty connection refused from %s",
sockunion2str(&su, buf, SU_ADDRSTRLEN));
close(vty_sock);
/* continue accepting connections */
vty_event(VTY_SERV, accept_sock, NULL);
return 0;
}
}
/* VTY's ipv6 accesslist apply. */
if (p.family == AF_INET6 && vty_ipv6_accesslist_name) {
if ((acl = access_list_lookup(AFI_IP6,
vty_ipv6_accesslist_name))
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
zlog_info("Vty connection refused from %s",
sockunion2str(&su, buf, SU_ADDRSTRLEN));
close(vty_sock);
/* continue accepting connections */
vty_event(VTY_SERV, accept_sock, NULL);
return 0;
}
}
on = 1;
ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on,
sizeof(on));
if (ret < 0)
zlog_info("can't set sockopt to vty_sock : %s",
safe_strerror(errno));
zlog_info("Vty connection from %s",
sockunion2str(&su, buf, SU_ADDRSTRLEN));
vty_create(vty_sock, &su);
return 0;
}
static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
{
int ret;
struct addrinfo req;
struct addrinfo *ainfo;
struct addrinfo *ainfo_save;
int sock;
char port_str[BUFSIZ];
memset(&req, 0, sizeof(struct addrinfo));
req.ai_flags = AI_PASSIVE;
req.ai_family = AF_UNSPEC;
req.ai_socktype = SOCK_STREAM;
snprintf(port_str, sizeof(port_str), "%d", port);
port_str[sizeof(port_str) - 1] = '\0';
ret = getaddrinfo(hostname, port_str, &req, &ainfo);
if (ret != 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL, "getaddrinfo failed: %s",
gai_strerror(ret));
exit(1);
}
ainfo_save = ainfo;
do {
if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
continue;
sock = socket(ainfo->ai_family, ainfo->ai_socktype,
ainfo->ai_protocol);
if (sock < 0)
continue;
2002-12-13 21:15:29 +01:00
sockopt_v6only(ainfo->ai_family, sock);
sockopt_reuseaddr(sock);
sockopt_reuseport(sock);
set_cloexec(sock);
ret = bind(sock, ainfo->ai_addr, ainfo->ai_addrlen);
if (ret < 0) {
close(sock); /* Avoid sd leak. */
continue;
}
ret = listen(sock, 3);
if (ret < 0) {
close(sock); /* Avoid sd leak. */
continue;
}
vty_event(VTY_SERV, sock, NULL);
} while ((ainfo = ainfo->ai_next) != NULL);
freeaddrinfo(ainfo_save);
2002-12-13 21:15:29 +01:00
}
#ifdef VTYSH
/* For sockaddr_un. */
#include <sys/un.h>
/* VTY shell UNIX domain socket. */
static void vty_serv_un(const char *path)
{
int ret;
int sock, len;
struct sockaddr_un serv;
mode_t old_mask;
struct zprivs_ids_t ids;
/* First of all, unlink existing socket */
unlink(path);
/* Set umask */
old_mask = umask(0007);
/* Make UNIX domain socket. */
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
flog_err_sys(EC_LIB_SOCKET,
"Cannot create unix stream socket: %s",
safe_strerror(errno));
return;
}
/* Make server socket. */
memset(&serv, 0, sizeof(struct sockaddr_un));
serv.sun_family = AF_UNIX;
strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
len = serv.sun_len = SUN_LEN(&serv);
2002-12-13 21:15:29 +01:00
#else
len = sizeof(serv.sun_family) + strlen(serv.sun_path);
#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
2002-12-13 21:15:29 +01:00
set_cloexec(sock);
ret = bind(sock, (struct sockaddr *)&serv, len);
if (ret < 0) {
flog_err_sys(EC_LIB_SOCKET, "Cannot bind path %s: %s", path,
safe_strerror(errno));
close(sock); /* Avoid sd leak. */
return;
}
2002-12-13 21:15:29 +01:00
ret = listen(sock, 5);
if (ret < 0) {
flog_err_sys(EC_LIB_SOCKET, "listen(fd %d) failed: %s", sock,
safe_strerror(errno));
close(sock); /* Avoid sd leak. */
return;
}
2002-12-13 21:15:29 +01:00
umask(old_mask);
2002-12-13 21:15:29 +01:00
zprivs_get_ids(&ids);
/* Hack: ids.gid_vty is actually a uint, but we stored -1 in it
earlier for the case when we don't need to chown the file
type casting it here to make a compare */
if ((int)ids.gid_vty > 0) {
/* set group of socket */
if (chown(path, -1, ids.gid_vty)) {
flog_err_sys(EC_LIB_SYSTEM_CALL,
"vty_serv_un: could chown socket, %s",
safe_strerror(errno));
}
}
vty_event(VTYSH_SERV, sock, NULL);
2002-12-13 21:15:29 +01:00
}
/* #define VTYSH_DEBUG 1 */
static int vtysh_accept(struct thread *thread)
2002-12-13 21:15:29 +01:00
{
int accept_sock;
int sock;
int client_len;
struct sockaddr_un client;
struct vty *vty;
accept_sock = THREAD_FD(thread);
2002-12-13 21:15:29 +01:00
vty_event(VTYSH_SERV, accept_sock, NULL);
2002-12-13 21:15:29 +01:00
memset(&client, 0, sizeof(struct sockaddr_un));
client_len = sizeof(struct sockaddr_un);
2002-12-13 21:15:29 +01:00
sock = accept(accept_sock, (struct sockaddr *)&client,
(socklen_t *)&client_len);
2002-12-13 21:15:29 +01:00
if (sock < 0) {
flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
safe_strerror(errno));
return -1;
}
2002-12-13 21:15:29 +01:00
if (set_nonblocking(sock) < 0) {
flog_err(
EC_LIB_SOCKET,
"vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
sock, safe_strerror(errno));
close(sock);
return -1;
}
set_cloexec(sock);
2002-12-13 21:15:29 +01:00
#ifdef VTYSH_DEBUG
printf("VTY shell accept\n");
2002-12-13 21:15:29 +01:00
#endif /* VTYSH_DEBUG */
vty = vty_new();
vty->fd = sock;
vty->wfd = sock;
vty->type = VTY_SHELL_SERV;
vty->node = VIEW_NODE;
vty_event(VTYSH_READ, sock, vty);
return 0;
}
static int vtysh_flush(struct vty *vty)
{
switch (buffer_flush_available(vty->obuf, vty->wfd)) {
case BUFFER_PENDING:
vty_event(VTYSH_WRITE, vty->wfd, 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);
buffer_reset(vty->obuf);
vty_close(vty);
return -1;
case BUFFER_EMPTY:
break;
}
return 0;
}
static int vtysh_read(struct thread *thread)
{
int ret;
int sock;
int nbytes;
struct vty *vty;
unsigned char buf[VTY_READ_BUFSIZ];
unsigned char *p;
uint8_t header[4] = {0, 0, 0, 0};
sock = THREAD_FD(thread);
vty = THREAD_ARG(thread);
if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
vty_event(VTYSH_READ, sock, vty);
return 0;
}
vty->monitor = 0; /* disable monitoring to avoid
infinite recursion */
flog_err(
EC_LIB_SOCKET,
"%s: read failed on vtysh client fd %d, closing: %s",
__func__, sock, safe_strerror(errno));
}
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty_close(vty);
2002-12-13 21:15:29 +01:00
#ifdef VTYSH_DEBUG
printf("close vtysh\n");
2002-12-13 21:15:29 +01:00
#endif /* VTYSH_DEBUG */
return 0;
}
2002-12-13 21:15:29 +01:00
#ifdef VTYSH_DEBUG
printf("line: %.*s\n", nbytes, buf);
2002-12-13 21:15:29 +01:00
#endif /* VTYSH_DEBUG */
if (vty->length + nbytes >= VTY_BUFSIZ) {
/* Clear command line buffer. */
vty->cp = vty->length = 0;
vty_clear_buf(vty);
vty_out(vty, "%% Command is too long.\n");
} else {
for (p = buf; p < buf + nbytes; p++) {
vty->buf[vty->length++] = *p;
if (*p == '\0') {
/* Pass this line to parser. */
ret = vty_execute(vty);
/* Note that vty_execute clears the command buffer and resets
vty->length to 0. */
/* Return result. */
2002-12-13 21:15:29 +01:00
#ifdef VTYSH_DEBUG
printf("result: %d\n", ret);
printf("vtysh node: %d\n", vty->node);
2002-12-13 21:15:29 +01:00
#endif /* VTYSH_DEBUG */
/* hack for asynchronous "write integrated"
* - other commands in "buf" will be ditched
* - input during pending config-write is
* "unsupported" */
if (ret == CMD_SUSPEND)
break;
/* warning: watchfrr hardcodes this result write
*/
header[3] = ret;
buffer_put(vty->obuf, header, 4);
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
if (!vty->t_write && (vtysh_flush(vty) < 0))
/* Try to flush results; exit if a write
* error occurs. */
return 0;
}
}
}
2002-12-13 21:15:29 +01:00
if (vty->status == VTY_CLOSE)
vty_close(vty);
else
vty_event(VTYSH_READ, sock, vty);
2002-12-13 21:15:29 +01:00
return 0;
2002-12-13 21:15:29 +01:00
}
static int vtysh_write(struct thread *thread)
{
struct vty *vty = THREAD_ARG(thread);
vtysh_flush(vty);
return 0;
}
2002-12-13 21:15:29 +01:00
#endif /* VTYSH */
/* Determine address family to bind. */
void vty_serv_sock(const char *addr, unsigned short port, const char *path)
2002-12-13 21:15:29 +01:00
{
/* If port is set to 0, do not listen on TCP/IP at all! */
if (port)
vty_serv_sock_addrinfo(addr, port);
2002-12-13 21:15:29 +01:00
#ifdef VTYSH
vty_serv_un(path);
2002-12-13 21:15:29 +01:00
#endif /* VTYSH */
}
static void vty_error_delete(void *arg)
{
struct vty_error *ve = arg;
XFREE(MTYPE_TMP, ve);
}
/* Close vty interface. Warning: call this only from functions that
will be careful not to access the vty afterwards (since it has
now been freed). This is safest from top-level functions (called
directly by the thread dispatcher). */
void vty_close(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
int i;
bool was_stdio = false;
2002-12-13 21:15:29 +01:00
/* Drop out of configure / transaction if needed. */
vty_config_exit(vty);
/* Cancel threads.*/
THREAD_OFF(vty->t_read);
THREAD_OFF(vty->t_write);
THREAD_OFF(vty->t_timeout);
2002-12-13 21:15:29 +01:00
/* Flush buffer. */
buffer_flush_all(vty->obuf, vty->wfd);
2002-12-13 21:15:29 +01:00
/* Free input buffer. */
buffer_free(vty->obuf);
buffer_free(vty->lbuf);
2002-12-13 21:15:29 +01:00
/* Free command history. */
for (i = 0; i < VTY_MAXHIST; i++) {
XFREE(MTYPE_VTY_HIST, vty->hist[i]);
}
2002-12-13 21:15:29 +01:00
/* Unset vector. */
if (vty->fd != -1)
vector_unset(vtyvec, vty->fd);
2002-12-13 21:15:29 +01:00
if (vty->wfd > 0 && vty->type == VTY_FILE)
fsync(vty->wfd);
/* Close socket.
* note check is for fd > STDERR_FILENO, not fd != -1.
* We never close stdin/stdout/stderr here, because we may be
* running in foreground mode with logging to stdout. Also,
* additionally, we'd need to replace these fds with /dev/null. */
if (vty->wfd > STDERR_FILENO && vty->wfd != vty->fd)
close(vty->wfd);
if (vty->fd > STDERR_FILENO)
close(vty->fd);
if (vty->fd == STDIN_FILENO)
was_stdio = true;
2002-12-13 21:15:29 +01:00
XFREE(MTYPE_VTY, vty->buf);
2002-12-13 21:15:29 +01:00
if (vty->error) {
vty->error->del = vty_error_delete;
list_delete(&vty->error);
}
/* OK free vty. */
XFREE(MTYPE_VTY, vty);
if (was_stdio)
vty_stdio_reset(0);
2002-12-13 21:15:29 +01:00
}
/* When time out occur output message then close connection. */
static int vty_timeout(struct thread *thread)
2002-12-13 21:15:29 +01:00
{
struct vty *vty;
2002-12-13 21:15:29 +01:00
vty = THREAD_ARG(thread);
vty->v_timeout = 0;
2002-12-13 21:15:29 +01:00
/* Clear buffer*/
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty_out(vty, "\nVty connection is timed out.\n");
2002-12-13 21:15:29 +01:00
/* Close connection. */
vty->status = VTY_CLOSE;
vty_close(vty);
2002-12-13 21:15:29 +01:00
return 0;
2002-12-13 21:15:29 +01:00
}
/* Read up configuration file from file_name. */
static void vty_read_file(struct nb_config *config, FILE *confp)
{
int ret;
struct vty *vty;
struct vty_error *ve;
struct listnode *node;
unsigned int line_num = 0;
vty = vty_new();
/* vty_close won't close stderr; if some config command prints
* something it'll end up there. (not ideal; it'd be beter if output
* from a file-load went to logging instead. Also note that if this
* function is called after daemonizing, stderr will be /dev/null.)
*
* vty->fd will be -1 from vty_new()
*/
vty->wfd = STDERR_FILENO;
vty->type = VTY_FILE;
vty->node = CONFIG_NODE;
vty->config = true;
if (config)
vty->candidate_config = config;
else {
vty->private_config = true;
vty->candidate_config = nb_config_new(NULL);
}
/* Execute configuration file */
ret = config_from_file(vty, confp, &line_num);
/* Flush any previous errors before printing messages below */
buffer_flush_all(vty->obuf, vty->wfd);
if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) {
const char *message = NULL;
char *nl;
switch (ret) {
case CMD_ERR_AMBIGUOUS:
message = "Ambiguous command";
break;
case CMD_ERR_NO_MATCH:
message = "No such command";
break;
case CMD_WARNING:
message = "Command returned Warning";
break;
case CMD_WARNING_CONFIG_FAILED:
message = "Command returned Warning Config Failed";
break;
case CMD_ERR_INCOMPLETE:
message = "Command returned Incomplete";
break;
case CMD_ERR_EXEED_ARGC_MAX:
message =
"Command exceeded maximum number of Arguments";
break;
default:
message = "Command returned unhandled error message";
break;
}
for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
nl = strchr(ve->error_buf, '\n');
if (nl)
*nl = '\0';
flog_err(EC_LIB_VTY, "ERROR: %s on config line %u: %s",
message, ve->line_num, ve->error_buf);
}
}
/*
* Automatically commit the candidate configuration after
* reading the configuration file.
*/
if (config == NULL) {
ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI,
vty, true, "Read configuration file",
NULL);
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
zlog_err("%s: failed to read configuration file.",
__func__);
}
vty_close(vty);
}
static FILE *vty_use_backup_config(const char *fullpath)
{
char *fullpath_sav, *fullpath_tmp;
FILE *ret = NULL;
int tmp, sav;
int c;
char buffer[512];
size_t fullpath_sav_sz = strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1;
fullpath_sav = malloc(fullpath_sav_sz);
strlcpy(fullpath_sav, fullpath, fullpath_sav_sz);
strlcat(fullpath_sav, CONF_BACKUP_EXT, fullpath_sav_sz);
sav = open(fullpath_sav, O_RDONLY);
if (sav < 0) {
free(fullpath_sav);
return NULL;
}
fullpath_tmp = malloc(strlen(fullpath) + 8);
snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath);
/* Open file to configuration write. */
tmp = mkstemp(fullpath_tmp);
if (tmp < 0)
goto out_close_sav;
if (fchmod(tmp, CONFIGFILE_MASK) != 0)
goto out_close;
while ((c = read(sav, buffer, 512)) > 0) {
if (write(tmp, buffer, c) <= 0)
goto out_close;
}
close(sav);
close(tmp);
if (rename(fullpath_tmp, fullpath) == 0)
ret = fopen(fullpath, "r");
else
unlink(fullpath_tmp);
if (0) {
out_close:
close(tmp);
unlink(fullpath_tmp);
out_close_sav:
close(sav);
}
free(fullpath_sav);
free(fullpath_tmp);
return ret;
2002-12-13 21:15:29 +01:00
}
/* Read up configuration file from file_name. */
bool vty_read_config(struct nb_config *config, const char *config_file,
char *config_default_dir)
{
char cwd[MAXPATHLEN];
FILE *confp = NULL;
const char *fullpath;
char *tmp = NULL;
bool read_success = false;
/* If -f flag specified. */
if (config_file != NULL) {
if (!IS_DIRECTORY_SEP(config_file[0])) {
if (getcwd(cwd, MAXPATHLEN) == NULL) {
flog_err_sys(
EC_LIB_SYSTEM_CALL,
"%s: failure to determine Current Working Directory %d!",
__func__, errno);
goto tmp_free_and_out;
}
tmp = XMALLOC(MTYPE_TMP,
strlen(cwd) + strlen(config_file) + 2);
sprintf(tmp, "%s/%s", cwd, config_file);
fullpath = tmp;
} else
fullpath = config_file;
confp = fopen(fullpath, "r");
if (confp == NULL) {
flog_warn(
EC_LIB_BACKUP_CONFIG,
"%s: failed to open configuration file %s: %s, checking backup",
__func__, fullpath, safe_strerror(errno));
confp = vty_use_backup_config(fullpath);
if (confp)
flog_warn(
EC_LIB_BACKUP_CONFIG,
"WARNING: using backup configuration file!");
else {
flog_err(
EC_LIB_VTY,
"%s: can't open configuration file [%s]",
__func__, config_file);
goto tmp_free_and_out;
}
}
} else {
host_config_set(config_default_dir);
2002-12-13 21:15:29 +01:00
#ifdef VTYSH
int ret;
struct stat conf_stat;
/* !!!!PLEASE LEAVE!!!!
* This is NEEDED for use with vtysh -b, or else you can get
* a real configuration food fight with a lot garbage in the
* merged configuration file it creates coming from the per
* daemon configuration files. This also allows the daemons
* to start if there default configuration file is not
* present or ignore them, as needed when using vtysh -b to
* configure the daemons at boot - MAG
*/
/* Stat for vtysh Zebra.conf, if found startup and wait for
* boot configuration
*/
if (strstr(config_default_dir, "vtysh") == NULL) {
ret = stat(integrate_default, &conf_stat);
if (ret >= 0) {
read_success = true;
goto tmp_free_and_out;
}
}
#endif /* VTYSH */
confp = fopen(config_default_dir, "r");
if (confp == NULL) {
flog_err(
EC_LIB_SYSTEM_CALL,
"%s: failed to open configuration file %s: %s, checking backup",
__func__, config_default_dir,
safe_strerror(errno));
confp = vty_use_backup_config(config_default_dir);
if (confp) {
flog_warn(
EC_LIB_BACKUP_CONFIG,
"WARNING: using backup configuration file!");
fullpath = config_default_dir;
} else {
flog_err(EC_LIB_VTY,
"can't open configuration file [%s]",
config_default_dir);
goto tmp_free_and_out;
}
} else
fullpath = config_default_dir;
}
vty_read_file(config, confp);
read_success = true;
fclose(confp);
host_config_set(fullpath);
tmp_free_and_out:
XFREE(MTYPE_TMP, tmp);
return read_success;
2002-12-13 21:15:29 +01:00
}
/* Small utility function which output log to the VTY. */
void vty_log(const char *level, const char *proto_str, const char *msg,
struct timestamp_control *ctl)
2002-12-13 21:15:29 +01:00
{
unsigned int i;
struct vty *vty;
if (!vtyvec)
return;
2002-12-13 21:15:29 +01:00
for (i = 0; i < vector_active(vtyvec); i++)
if ((vty = vector_slot(vtyvec, i)) != NULL)
if (vty->monitor)
vty_log_out(vty, level, proto_str, msg, ctl);
2002-12-13 21:15:29 +01:00
}
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * bgp_main.c: (main) The 2nd argument to openzlog has been removed. * isis_main.c: (main) The 2nd argument to openzlog has been removed. * ospf6_main.c: (main) The 2nd argument to openzlog has been removed. Note that stdout logging will no longer be enabled by default when not running as a daemon. * ospf_main.c: (main) The 2nd argument to openzlog has been removed. * rip_main.c: (main) The 2nd argument to openzlog has been removed. * ripng_main.c: (main) The 2nd argument to openzlog has been removed. * main.c: (main) The 2nd argument to openzlog has been removed. So stdout logging will no longer be enabled by default. * irdp_main.c: (irdp_finish) Reduce severity of shutdown message from LOG_WARNING to LOG_INFO. * vtysh.c: Make several functions static instead of global. Added several commands to support destination-specific logging levels. (vtysh_completion) This function is unused, so comment it out. * basic.texi: Document new logging features. Separate basic config commands from basic VTY commands. * log.h: Replace struct zlog flags and maskpri fields with maxlvl array to support individual logging levels for each destination. Remove the 2nd argument to openzlog since the default logging config should be standardized inside the library. Replaced the zlog_set_flag and zlog_reset_flag functions with zlog_set_level. And zlog_set_file now requires an additional log_level argument. Declare zlog_proto_names for use inside command.c in the "show logging" command. Added defines useful for command construction. * log.c: (vzlog) Decide where to send the message based on the individual logging levels configured for each destination. Remove support for ZLOG_STDERR since it was never actually used. Support record-priority for terminal monitors. (zlog_signal,zlog_backtrace_sigsafe) Support destination-specific logging levels. Remove stderr support (was never used). Added support for terminal monitor logging. (_zlog_assert_failed) Increase message severity to LOG_EMERG. (openzlog) Remove 2nd argument since default config should be standardized in library. By default, terminal monitoring is set to debug, and all other logging is disabled. (zlog_set_flag,zlog_reset_flag) Removed. (zlog_set_level) New function to replace zlog_set_flag and zlog_reset_flag. Supports destination-specific logging levels. (zlog_set_file,zlog_reset_file) Support file-specific logging level. (zlog_rotate) Log an error message if fopen fails, and support new file-specific logging level. * command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that command functions will be static instead of global. Remove declarations for config_exit and config_help. Define new macros DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can have deprecated commands in vtysh. Similarly, for completeness, define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED. Also, fix bug in ALIAS_ATTR macro (didn't matter because it was never used). * command.c: Make many functions static instead of global. (facility_name,facility_match,level_match) New functions to support enhanced destination-specific logging levels. (config_write_host) Support new destination-specific logging levels. (config_logmsg) Added new "logmsg" command to help test logging system. (show_logging) Added "show logging" command to show the current configuration of the logging system. (config_log_stdout_level) Support explicit stdout logging level. (no_config_log_stdout) Now takes optional LEVEL arg. (config_log_monitor,config_log_monitor_level,no_config_log_monitor) New commands creating new "log monitor" commands to set terminal monitoring log level. (config_log_file_level) Support explicit file logging level. (config_log_syslog_level) Support explicit syslog logging level. (config_log_facility,no_config_log_facility) Implement new "log facility" command. (cmd_init) Add hooks for new commands: "show logging", "logmsg", "log stdout <level>", "log monitor", "log monitor <level>", "no log monitor", "log file <filename> <level>", "no log file <filename> <level>", "log syslog <level>", "log facility", and "no log facility". * vty.h: Added a "level" argument to vty_log so it can support "log record-priority". Declare new function vty_log_fixed for use in signal handlers. * vty.c: (vty_log,vty_log_out) Added a "level" argument to support "log record-priority" for vty terminal monitors. (vty_down_level) Use config_exit_cmd.func instead of calling config_exit directly (since command functions will now be static instead of global). (vty_log_fixed) New function to send terminal monitor messages from inside a signal handler.
2004-12-07 16:39:31 +01:00
/* Async-signal-safe version of vty_log for fixed strings. */
void vty_log_fixed(char *buf, size_t len)
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * bgp_main.c: (main) The 2nd argument to openzlog has been removed. * isis_main.c: (main) The 2nd argument to openzlog has been removed. * ospf6_main.c: (main) The 2nd argument to openzlog has been removed. Note that stdout logging will no longer be enabled by default when not running as a daemon. * ospf_main.c: (main) The 2nd argument to openzlog has been removed. * rip_main.c: (main) The 2nd argument to openzlog has been removed. * ripng_main.c: (main) The 2nd argument to openzlog has been removed. * main.c: (main) The 2nd argument to openzlog has been removed. So stdout logging will no longer be enabled by default. * irdp_main.c: (irdp_finish) Reduce severity of shutdown message from LOG_WARNING to LOG_INFO. * vtysh.c: Make several functions static instead of global. Added several commands to support destination-specific logging levels. (vtysh_completion) This function is unused, so comment it out. * basic.texi: Document new logging features. Separate basic config commands from basic VTY commands. * log.h: Replace struct zlog flags and maskpri fields with maxlvl array to support individual logging levels for each destination. Remove the 2nd argument to openzlog since the default logging config should be standardized inside the library. Replaced the zlog_set_flag and zlog_reset_flag functions with zlog_set_level. And zlog_set_file now requires an additional log_level argument. Declare zlog_proto_names for use inside command.c in the "show logging" command. Added defines useful for command construction. * log.c: (vzlog) Decide where to send the message based on the individual logging levels configured for each destination. Remove support for ZLOG_STDERR since it was never actually used. Support record-priority for terminal monitors. (zlog_signal,zlog_backtrace_sigsafe) Support destination-specific logging levels. Remove stderr support (was never used). Added support for terminal monitor logging. (_zlog_assert_failed) Increase message severity to LOG_EMERG. (openzlog) Remove 2nd argument since default config should be standardized in library. By default, terminal monitoring is set to debug, and all other logging is disabled. (zlog_set_flag,zlog_reset_flag) Removed. (zlog_set_level) New function to replace zlog_set_flag and zlog_reset_flag. Supports destination-specific logging levels. (zlog_set_file,zlog_reset_file) Support file-specific logging level. (zlog_rotate) Log an error message if fopen fails, and support new file-specific logging level. * command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that command functions will be static instead of global. Remove declarations for config_exit and config_help. Define new macros DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can have deprecated commands in vtysh. Similarly, for completeness, define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED. Also, fix bug in ALIAS_ATTR macro (didn't matter because it was never used). * command.c: Make many functions static instead of global. (facility_name,facility_match,level_match) New functions to support enhanced destination-specific logging levels. (config_write_host) Support new destination-specific logging levels. (config_logmsg) Added new "logmsg" command to help test logging system. (show_logging) Added "show logging" command to show the current configuration of the logging system. (config_log_stdout_level) Support explicit stdout logging level. (no_config_log_stdout) Now takes optional LEVEL arg. (config_log_monitor,config_log_monitor_level,no_config_log_monitor) New commands creating new "log monitor" commands to set terminal monitoring log level. (config_log_file_level) Support explicit file logging level. (config_log_syslog_level) Support explicit syslog logging level. (config_log_facility,no_config_log_facility) Implement new "log facility" command. (cmd_init) Add hooks for new commands: "show logging", "logmsg", "log stdout <level>", "log monitor", "log monitor <level>", "no log monitor", "log file <filename> <level>", "no log file <filename> <level>", "log syslog <level>", "log facility", and "no log facility". * vty.h: Added a "level" argument to vty_log so it can support "log record-priority". Declare new function vty_log_fixed for use in signal handlers. * vty.c: (vty_log,vty_log_out) Added a "level" argument to support "log record-priority" for vty terminal monitors. (vty_down_level) Use config_exit_cmd.func instead of calling config_exit directly (since command functions will now be static instead of global). (vty_log_fixed) New function to send terminal monitor messages from inside a signal handler.
2004-12-07 16:39:31 +01:00
{
unsigned int i;
struct iovec iov[2];
char crlf[4] = "\r\n";
2005-02-23 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * buffer.h: Make the struct buffer and struct buffer_data structures private by moving them inside buffer.c. Add comments for all functions. Rename buffer_write as buffer_put (to be more consistent with the buffer_putc and buffer_putstr functions). Declare a new buffer_write function that is used to write data to a file descriptor and/or add it to the buffer queue. Remove unused function buffer_flush_vty_all. Create a new enum typedef buffer_status_t to be used as the return code for all buffer_flush* functions and buffer_write. * buffer.c: The struct buffer and struct buffer_data declarations are now private to this file. In conjunction with that, remove some unnecessary fields: struct buffer (alloc, unused_head, unused_tail, length), struct buffer_data (prev). (buffer_data_new) Removed: functionality incorporated into buffer_add. (buffer_data_free) Removed: use a macro BUFFER_DATA_FREE instead. (buffer_new) Use calloc instead of malloc + memset(zero). Supply an appropriate default size if the specified size is 0. (buffer_free) Eliminate code duplication by calling buffer_reset to free the contents of the buffer (and remove unused code related to unused_head). (buffer_empty,buffer_putc,buffer_putstr) Aesthetic change (make more compact). (buffer_reset) Use macro BUFFER_DATA_FREE. No need to set alloc and length to 0 (these fields have been removed). (buffer_add) Fix scope to be static. Call XMALLOC directly instead of calling removed buffer_data_new function. Simplify the logic (since it's now a singly-linked list instead of doubly-linked). (buffer_write) Renamed to buffer_put. Change to void, since return code of 1 was meaningless. No need to adjust length field, since it has been removed. (buffer_putw,buffer_flush,buffer_flush_vty_all,buffer_flush_vty) Remove unused functions. (buffer_flush_all) Rewrite using buffer_flush_available to eliminate a possible failure mode if IOV_MAX is less than the number of buffers on the queue. (buffer_flush_window) Incorporate logic from buffer_flush_vty. Log an error message if there is a writev error. (buffer_flush_available) Be more paranoid: check for case where buffer is already empty. Use new ERRNO_IO_RETRY macro, and use new enum for return codes. Simplify deletion logic (since it's now a singly-linked list). (buffer_write) New function for use with non-blocking I/O. * vty.h: Replace the struct vty sb_buffer field with a fixed-size (5-character) sb_buf field and an sb_len field, since using a struct buffer was inappropriate for this task. Add some useful comments about telnet window size negotiation. * vty.c: Include <arpa/telnet.h> (no longer included by zebra.h). Remove VTY_OBUF_SIZE (instead use buffer_new default size). Make telnet_backward_char and telnet_space_char static const. (vty_out) Replace buffer_write with buffer_put. (vty_log_out) Check for I/O errors. If fatal, close the vty session. Consolidate 3 separate writes into a single write call. (vty_will_echo,vty_command,vty_next_line,vty_previous_line, vty_end_config,vty_describe_fold,vty_clear_buf,vty_serv_sock_addrinfo, vty_serv_sock_family,vty_serv_un,vty_use_backup_config,exec_timeout, vty_config_write,vty_save_cwd) Fix scope to static. (vty_new) Let buffer_new use its default buffer size. (vty_write) Fix signature: 2nd arg should be const char *. Replaced buffer_write with buffer_put. (vty_telnet_option) Fix minor bug (window height or width greater than 255 was broken). Use sb_buf and sb_len instead of removed sb_buffer (which was being used improperly). (vty_read) On error, use ERRNO_IO_RETRY to decide whether it's fatal. If the error is fatal, call buffer_reset so vty_close does not attempt to flush the data. Use new sb_buf and sb_len instead of sb_buffer to store the SB negotiation string. (vty_flush) When vty->lines is 0, call buffer_flush_available instead of buffer_flush_window. Look at the return code from buffer_flush to detect I/O errors (and in that case, log an error message and close the vty). (vty_create) Fix scope to static. Initialize sb_len to 0 instead of creating sb_buffer. (vty_accept) Set socket nonblocking. (vtysh_accept) Use new set_nonblocking function instead of calling fcntl directly. (vtysh_flush) New function called from vtysh_read (after command execution) and from vtysh_write. This flushes the buffer and reacts appropriately to the return code (by closing the vty or scheduling further flushes). (vtysh_read) Check whether error is fatal using ERRNO_IO_RETRY. If not, just try again later. Otherwise, call buffer_reset before calling vty_close (to avoid trying to flush the buffer in vty_close). Fix logic to allow case where a command does not arrive atomically in a single read call by checking for the terminating NUL char. (vtysh_write) Use new vtysh_flush helper function. (vty_close) No need to call buffer_empty, just call buffer_flush_all in any case (it will check whether the buffer is empty). Do not free sb_buffer (since it has been removed). (vty_log_fixed) Use writev instead of write. * zebra.h: Do not include <arpa/telnet.h>, since this is used only by lib/vty.c.
2005-02-23 16:12:34 +01:00
/* vty may not have been initialised */
if (!vtyvec)
return;
iov[0].iov_base = buf;
iov[0].iov_len = len;
iov[1].iov_base = crlf;
iov[1].iov_len = 2;
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * bgp_main.c: (main) The 2nd argument to openzlog has been removed. * isis_main.c: (main) The 2nd argument to openzlog has been removed. * ospf6_main.c: (main) The 2nd argument to openzlog has been removed. Note that stdout logging will no longer be enabled by default when not running as a daemon. * ospf_main.c: (main) The 2nd argument to openzlog has been removed. * rip_main.c: (main) The 2nd argument to openzlog has been removed. * ripng_main.c: (main) The 2nd argument to openzlog has been removed. * main.c: (main) The 2nd argument to openzlog has been removed. So stdout logging will no longer be enabled by default. * irdp_main.c: (irdp_finish) Reduce severity of shutdown message from LOG_WARNING to LOG_INFO. * vtysh.c: Make several functions static instead of global. Added several commands to support destination-specific logging levels. (vtysh_completion) This function is unused, so comment it out. * basic.texi: Document new logging features. Separate basic config commands from basic VTY commands. * log.h: Replace struct zlog flags and maskpri fields with maxlvl array to support individual logging levels for each destination. Remove the 2nd argument to openzlog since the default logging config should be standardized inside the library. Replaced the zlog_set_flag and zlog_reset_flag functions with zlog_set_level. And zlog_set_file now requires an additional log_level argument. Declare zlog_proto_names for use inside command.c in the "show logging" command. Added defines useful for command construction. * log.c: (vzlog) Decide where to send the message based on the individual logging levels configured for each destination. Remove support for ZLOG_STDERR since it was never actually used. Support record-priority for terminal monitors. (zlog_signal,zlog_backtrace_sigsafe) Support destination-specific logging levels. Remove stderr support (was never used). Added support for terminal monitor logging. (_zlog_assert_failed) Increase message severity to LOG_EMERG. (openzlog) Remove 2nd argument since default config should be standardized in library. By default, terminal monitoring is set to debug, and all other logging is disabled. (zlog_set_flag,zlog_reset_flag) Removed. (zlog_set_level) New function to replace zlog_set_flag and zlog_reset_flag. Supports destination-specific logging levels. (zlog_set_file,zlog_reset_file) Support file-specific logging level. (zlog_rotate) Log an error message if fopen fails, and support new file-specific logging level. * command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that command functions will be static instead of global. Remove declarations for config_exit and config_help. Define new macros DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can have deprecated commands in vtysh. Similarly, for completeness, define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED. Also, fix bug in ALIAS_ATTR macro (didn't matter because it was never used). * command.c: Make many functions static instead of global. (facility_name,facility_match,level_match) New functions to support enhanced destination-specific logging levels. (config_write_host) Support new destination-specific logging levels. (config_logmsg) Added new "logmsg" command to help test logging system. (show_logging) Added "show logging" command to show the current configuration of the logging system. (config_log_stdout_level) Support explicit stdout logging level. (no_config_log_stdout) Now takes optional LEVEL arg. (config_log_monitor,config_log_monitor_level,no_config_log_monitor) New commands creating new "log monitor" commands to set terminal monitoring log level. (config_log_file_level) Support explicit file logging level. (config_log_syslog_level) Support explicit syslog logging level. (config_log_facility,no_config_log_facility) Implement new "log facility" command. (cmd_init) Add hooks for new commands: "show logging", "logmsg", "log stdout <level>", "log monitor", "log monitor <level>", "no log monitor", "log file <filename> <level>", "no log file <filename> <level>", "log syslog <level>", "log facility", and "no log facility". * vty.h: Added a "level" argument to vty_log so it can support "log record-priority". Declare new function vty_log_fixed for use in signal handlers. * vty.c: (vty_log,vty_log_out) Added a "level" argument to support "log record-priority" for vty terminal monitors. (vty_down_level) Use config_exit_cmd.func instead of calling config_exit directly (since command functions will now be static instead of global). (vty_log_fixed) New function to send terminal monitor messages from inside a signal handler.
2004-12-07 16:39:31 +01:00
for (i = 0; i < vector_active(vtyvec); i++) {
struct vty *vty;
if (((vty = vector_slot(vtyvec, i)) != NULL) && vty->monitor)
/* N.B. We don't care about the return code, since
process is
most likely just about to die anyway. */
if (writev(vty->wfd, iov, 2) == -1) {
fprintf(stderr, "Failure to writev: %d\n",
errno);
exit(-1);
}
}
2004-12-07 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * bgp_main.c: (main) The 2nd argument to openzlog has been removed. * isis_main.c: (main) The 2nd argument to openzlog has been removed. * ospf6_main.c: (main) The 2nd argument to openzlog has been removed. Note that stdout logging will no longer be enabled by default when not running as a daemon. * ospf_main.c: (main) The 2nd argument to openzlog has been removed. * rip_main.c: (main) The 2nd argument to openzlog has been removed. * ripng_main.c: (main) The 2nd argument to openzlog has been removed. * main.c: (main) The 2nd argument to openzlog has been removed. So stdout logging will no longer be enabled by default. * irdp_main.c: (irdp_finish) Reduce severity of shutdown message from LOG_WARNING to LOG_INFO. * vtysh.c: Make several functions static instead of global. Added several commands to support destination-specific logging levels. (vtysh_completion) This function is unused, so comment it out. * basic.texi: Document new logging features. Separate basic config commands from basic VTY commands. * log.h: Replace struct zlog flags and maskpri fields with maxlvl array to support individual logging levels for each destination. Remove the 2nd argument to openzlog since the default logging config should be standardized inside the library. Replaced the zlog_set_flag and zlog_reset_flag functions with zlog_set_level. And zlog_set_file now requires an additional log_level argument. Declare zlog_proto_names for use inside command.c in the "show logging" command. Added defines useful for command construction. * log.c: (vzlog) Decide where to send the message based on the individual logging levels configured for each destination. Remove support for ZLOG_STDERR since it was never actually used. Support record-priority for terminal monitors. (zlog_signal,zlog_backtrace_sigsafe) Support destination-specific logging levels. Remove stderr support (was never used). Added support for terminal monitor logging. (_zlog_assert_failed) Increase message severity to LOG_EMERG. (openzlog) Remove 2nd argument since default config should be standardized in library. By default, terminal monitoring is set to debug, and all other logging is disabled. (zlog_set_flag,zlog_reset_flag) Removed. (zlog_set_level) New function to replace zlog_set_flag and zlog_reset_flag. Supports destination-specific logging levels. (zlog_set_file,zlog_reset_file) Support file-specific logging level. (zlog_rotate) Log an error message if fopen fails, and support new file-specific logging level. * command.h: Change DEFUN_CMD_FUNC_DECL and DEFUN_CMD_FUNC_TEXT so that command functions will be static instead of global. Remove declarations for config_exit and config_help. Define new macros DEFUNSH_ATTR, DEFUNSH_HIDDEN, and DEFUNSH_DEPRECATED so we can have deprecated commands in vtysh. Similarly, for completeness, define macros ALIAS_SH, ALIAS_SH_HIDDEN, and ALIAS_SH_DEPRECATED. Also, fix bug in ALIAS_ATTR macro (didn't matter because it was never used). * command.c: Make many functions static instead of global. (facility_name,facility_match,level_match) New functions to support enhanced destination-specific logging levels. (config_write_host) Support new destination-specific logging levels. (config_logmsg) Added new "logmsg" command to help test logging system. (show_logging) Added "show logging" command to show the current configuration of the logging system. (config_log_stdout_level) Support explicit stdout logging level. (no_config_log_stdout) Now takes optional LEVEL arg. (config_log_monitor,config_log_monitor_level,no_config_log_monitor) New commands creating new "log monitor" commands to set terminal monitoring log level. (config_log_file_level) Support explicit file logging level. (config_log_syslog_level) Support explicit syslog logging level. (config_log_facility,no_config_log_facility) Implement new "log facility" command. (cmd_init) Add hooks for new commands: "show logging", "logmsg", "log stdout <level>", "log monitor", "log monitor <level>", "no log monitor", "log file <filename> <level>", "no log file <filename> <level>", "log syslog <level>", "log facility", and "no log facility". * vty.h: Added a "level" argument to vty_log so it can support "log record-priority". Declare new function vty_log_fixed for use in signal handlers. * vty.c: (vty_log,vty_log_out) Added a "level" argument to support "log record-priority" for vty terminal monitors. (vty_down_level) Use config_exit_cmd.func instead of calling config_exit directly (since command functions will now be static instead of global). (vty_log_fixed) New function to send terminal monitor messages from inside a signal handler.
2004-12-07 16:39:31 +01:00
}
int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
2002-12-13 21:15:29 +01:00
{
if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) {
vty_out(vty, "%% Configuration is locked by other client\n");
return CMD_WARNING;
}
vty->node = CONFIG_NODE;
vty->config = true;
vty->private_config = private_config;
vty->xpath_index = 0;
if (private_config) {
vty->candidate_config = nb_config_dup(running_config);
vty->candidate_config_base = nb_config_dup(running_config);
vty_out(vty,
"Warning: uncommitted changes will be discarded on exit.\n\n");
} else {
vty->candidate_config = vty_shared_candidate_config;
if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
vty->candidate_config_base =
nb_config_dup(running_config);
}
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
void vty_config_exit(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
enum node_type node = vty->node;
struct cmd_node *cnode;
/* unlock and jump up to ENABLE_NODE if -and only if- we're
* somewhere below CONFIG_NODE */
while (node && node != CONFIG_NODE) {
cnode = vector_lookup(cmdvec, node);
node = cnode->parent_node;
}
if (node != CONFIG_NODE)
/* called outside config, e.g. vty_close() in ENABLE_NODE */
return;
while (vty->node != ENABLE_NODE)
/* will call vty_config_node_exit() below */
cmd_exit(vty);
}
int vty_config_node_exit(struct vty *vty)
{
vty->xpath_index = 0;
lib: add support for confirmed commits Confirmed commits allow the user to request an automatic rollback to the previous configuration if the commit operation is not confirmed within a number of minutes. This is particularly useful when the user is accessing the CLI through the network (e.g. using SSH) and any configuration change might cause an unexpected loss of connectivity between the user and the managed device (e.g. misconfiguration of a routing protocol). By using a confirmed commit, the user can rest assured the connectivity will be restored after the given timeout expires, avoiding the need to access the router physically to fix the problem. When "commit confirmed TIMEOUT" is used, a new "commit" command is expected to confirm the previous commit before the given timeout expires. If "commit confirmed TIMEOUT" is used while there's already a confirmed-commit in progress, the confirmed-commit timeout is reset to the new value. In the current implementation, if other users perform commits while there's a confirmed-commit in progress, all commits are rolled back when the confirmed-commit timeout expires. It's recommended to use the "configure exclusive" configuration mode to prevent unexpected outcomes when using confirmed commits. When an user exits from the configuration mode while there's a confirmed-commit in progress, the commit is automatically rolled back and the user is notified about it. In the future we might want to prompt the user if he or she really wants to exit from the configuration mode when there's a pending confirmed commit. Needless to say, confirmed commit only work for configuration commands converted to the new northbound model. vtysh support will be implemented at a later time. Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
2018-12-06 23:37:05 +01:00
/* Check if there's a pending confirmed commit. */
if (vty->t_confirmed_commit_timeout) {
vty_out(vty,
"WARNING: exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
nb_cli_confirmed_commit_rollback(vty);
nb_cli_confirmed_commit_clean(vty);
}
(void)nb_running_unlock(NB_CLIENT_CLI, vty);
if (vty->candidate_config) {
if (vty->private_config)
nb_config_free(vty->candidate_config);
vty->candidate_config = NULL;
}
if (vty->candidate_config_base) {
nb_config_free(vty->candidate_config_base);
vty->candidate_config_base = NULL;
}
vty->config = false;
return 1;
}
2002-12-13 21:15:29 +01:00
/* Master of the threads. */
static struct thread_master *vty_master;
2002-12-13 21:15:29 +01:00
static void vty_event(enum event event, int sock, struct vty *vty)
2002-12-13 21:15:29 +01:00
{
struct thread *vty_serv_thread = NULL;
switch (event) {
case VTY_SERV:
vty_serv_thread = thread_add_read(vty_master, vty_accept, vty,
sock, NULL);
vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
break;
2002-12-13 21:15:29 +01:00
#ifdef VTYSH
case VTYSH_SERV:
vty_serv_thread = thread_add_read(vty_master, vtysh_accept, vty,
sock, NULL);
vector_set_index(Vvty_serv_thread, sock, vty_serv_thread);
break;
case VTYSH_READ:
thread_add_read(vty_master, vtysh_read, vty, sock,
&vty->t_read);
break;
case VTYSH_WRITE:
thread_add_write(vty_master, vtysh_write, vty, sock,
&vty->t_write);
break;
2002-12-13 21:15:29 +01:00
#endif /* VTYSH */
case VTY_READ:
thread_add_read(vty_master, vty_read, vty, sock, &vty->t_read);
/* Time out treatment. */
if (vty->v_timeout) {
THREAD_OFF(vty->t_timeout);
thread_add_timer(vty_master, vty_timeout, vty,
vty->v_timeout, &vty->t_timeout);
}
break;
case VTY_WRITE:
thread_add_write(vty_master, vty_flush, vty, sock,
&vty->t_write);
break;
case VTY_TIMEOUT_RESET:
THREAD_OFF(vty->t_timeout);
if (vty->v_timeout)
thread_add_timer(vty_master, vty_timeout, vty,
vty->v_timeout, &vty->t_timeout);
break;
}
2002-12-13 21:15:29 +01:00
}
DEFUN_NOSH (config_who,
2002-12-13 21:15:29 +01:00
config_who_cmd,
"who",
"Display who is on vty\n")
{
unsigned int i;
struct vty *v;
2002-12-13 21:15:29 +01:00
for (i = 0; i < vector_active(vtyvec); i++)
if ((v = vector_slot(vtyvec, i)) != NULL)
vty_out(vty, "%svty[%d] connected from %s.\n",
v->config ? "*" : " ", i, v->address);
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* Move to vty configuration mode. */
DEFUN_NOSH (line_vty,
2002-12-13 21:15:29 +01:00
line_vty_cmd,
"line vty",
"Configure a terminal line\n"
"Virtual terminal\n")
{
vty->node = VTY_NODE;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* Set time out value. */
static int exec_timeout(struct vty *vty, const char *min_str,
const char *sec_str)
2002-12-13 21:15:29 +01:00
{
unsigned long timeout = 0;
2002-12-13 21:15:29 +01:00
/* min_str and sec_str are already checked by parser. So it must be
all digit string. */
if (min_str) {
timeout = strtol(min_str, NULL, 10);
timeout *= 60;
}
if (sec_str)
timeout += strtol(sec_str, NULL, 10);
2002-12-13 21:15:29 +01:00
vty_timeout_val = timeout;
vty->v_timeout = timeout;
vty_event(VTY_TIMEOUT_RESET, 0, vty);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
DEFUN (exec_timeout_min,
exec_timeout_min_cmd,
"exec-timeout (0-35791)",
2002-12-13 21:15:29 +01:00
"Set timeout value\n"
"Timeout value in minutes\n")
{
int idx_number = 1;
return exec_timeout(vty, argv[idx_number]->arg, NULL);
2002-12-13 21:15:29 +01:00
}
DEFUN (exec_timeout_sec,
exec_timeout_sec_cmd,
"exec-timeout (0-35791) (0-2147483)",
2002-12-13 21:15:29 +01:00
"Set the EXEC timeout\n"
"Timeout in minutes\n"
"Timeout in seconds\n")
{
int idx_number = 1;
int idx_number_2 = 2;
return exec_timeout(vty, argv[idx_number]->arg,
argv[idx_number_2]->arg);
2002-12-13 21:15:29 +01:00
}
DEFUN (no_exec_timeout,
no_exec_timeout_cmd,
"no exec-timeout",
NO_STR
"Set the EXEC timeout\n")
{
return exec_timeout(vty, NULL, NULL);
2002-12-13 21:15:29 +01:00
}
/* Set vty access class. */
DEFUN (vty_access_class,
vty_access_class_cmd,
"access-class WORD",
"Filter connections based on an IP access list\n"
"IP access list\n")
{
int idx_word = 1;
if (vty_accesslist_name)
XFREE(MTYPE_VTY, vty_accesslist_name);
2002-12-13 21:15:29 +01:00
vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* Clear vty access class. */
DEFUN (no_vty_access_class,
no_vty_access_class_cmd,
"no access-class [WORD]",
NO_STR
"Filter connections based on an IP access list\n"
"IP access list\n")
{
int idx_word = 2;
const char *accesslist = (argc == 3) ? argv[idx_word]->arg : NULL;
if (!vty_accesslist_name
|| (argc == 3 && strcmp(vty_accesslist_name, accesslist))) {
vty_out(vty, "Access-class is not currently applied to vty\n");
return CMD_WARNING_CONFIG_FAILED;
}
2002-12-13 21:15:29 +01:00
XFREE(MTYPE_VTY, vty_accesslist_name);
2002-12-13 21:15:29 +01:00
vty_accesslist_name = NULL;
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* Set vty access class. */
DEFUN (vty_ipv6_access_class,
vty_ipv6_access_class_cmd,
"ipv6 access-class WORD",
IPV6_STR
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
int idx_word = 2;
if (vty_ipv6_accesslist_name)
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2002-12-13 21:15:29 +01:00
vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* Clear vty access class. */
DEFUN (no_vty_ipv6_access_class,
no_vty_ipv6_access_class_cmd,
"no ipv6 access-class [WORD]",
NO_STR
IPV6_STR
"Filter connections based on an IP access list\n"
"IPv6 access list\n")
{
int idx_word = 3;
const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL;
if (!vty_ipv6_accesslist_name
|| (argc == 4 && strcmp(vty_ipv6_accesslist_name, accesslist))) {
vty_out(vty,
"IPv6 access-class is not currently applied to vty\n");
return CMD_WARNING_CONFIG_FAILED;
}
2002-12-13 21:15:29 +01:00
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2002-12-13 21:15:29 +01:00
vty_ipv6_accesslist_name = NULL;
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* vty login. */
DEFUN (vty_login,
vty_login_cmd,
"login",
"Enable password checking\n")
{
no_password_check = 0;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
DEFUN (no_vty_login,
no_vty_login_cmd,
"no login",
NO_STR
"Enable password checking\n")
{
no_password_check = 1;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
DEFUN (service_advanced_vty,
service_advanced_vty_cmd,
"service advanced-vty",
"Set up miscellaneous service\n"
"Enable advanced mode vty interface\n")
{
host.advanced = 1;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
DEFUN (no_service_advanced_vty,
no_service_advanced_vty_cmd,
"no service advanced-vty",
NO_STR
"Set up miscellaneous service\n"
"Enable advanced mode vty interface\n")
{
host.advanced = 0;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
DEFUN_NOSH (terminal_monitor,
2002-12-13 21:15:29 +01:00
terminal_monitor_cmd,
"terminal monitor",
"Set terminal line parameters\n"
"Copy debug output to the current terminal line\n")
{
vty->monitor = 1;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
DEFUN_NOSH (terminal_no_monitor,
2002-12-13 21:15:29 +01:00
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;
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
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")
{
return terminal_no_monitor(self, vty, argc, argv);
}
DEFUN_NOSH (show_history,
2002-12-13 21:15:29 +01:00
show_history_cmd,
"show history",
SHOW_STR
"Display the session command history\n")
{
int index;
2002-12-13 21:15:29 +01:00
for (index = vty->hindex + 1; index != vty->hindex;) {
if (index == VTY_MAXHIST) {
index = 0;
continue;
}
2002-12-13 21:15:29 +01:00
if (vty->hist[index] != NULL)
vty_out(vty, " %s\n", vty->hist[index]);
2002-12-13 21:15:29 +01:00
index++;
}
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
/* vty login. */
DEFPY (log_commands,
log_commands_cmd,
"[no] log commands",
NO_STR
"Logging control\n"
"Log all commands\n")
{
if (no) {
if (do_log_commands_perm) {
vty_out(vty,
"Daemon started with permanent logging turned on for commands, ignoring\n");
return CMD_WARNING;
}
do_log_commands = false;
} else
do_log_commands = true;
return CMD_SUCCESS;
}
2002-12-13 21:15:29 +01:00
/* Display current configuration. */
static int vty_config_write(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
vty_out(vty, "line vty\n");
2002-12-13 21:15:29 +01:00
if (vty_accesslist_name)
vty_out(vty, " access-class %s\n", vty_accesslist_name);
2002-12-13 21:15:29 +01:00
if (vty_ipv6_accesslist_name)
vty_out(vty, " ipv6 access-class %s\n",
vty_ipv6_accesslist_name);
2002-12-13 21:15:29 +01:00
/* exec-timeout */
if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
vty_out(vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
vty_timeout_val % 60);
2002-12-13 21:15:29 +01:00
/* login */
if (no_password_check)
vty_out(vty, " no login\n");
if (do_log_commands)
vty_out(vty, "log commands\n");
vty_out(vty, "!\n");
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
2002-12-13 21:15:29 +01:00
}
static int vty_config_write(struct vty *vty);
struct cmd_node vty_node = {
.name = "vty",
.node = VTY_NODE,
.parent_node = CONFIG_NODE,
.prompt = "%s(config-line)# ",
.config_write = vty_config_write,
2002-12-13 21:15:29 +01:00
};
/* Reset all VTY status. */
void vty_reset(void)
{
unsigned int i;
struct vty *vty;
struct thread *vty_serv_thread;
for (i = 0; i < vector_active(vtyvec); i++)
if ((vty = vector_slot(vtyvec, i)) != NULL) {
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty->status = VTY_CLOSE;
vty_close(vty);
}
for (i = 0; i < vector_active(Vvty_serv_thread); i++)
if ((vty_serv_thread = vector_slot(Vvty_serv_thread, i))
!= NULL) {
THREAD_OFF(vty_serv_thread);
vector_slot(Vvty_serv_thread, i) = NULL;
close(i);
}
vty_timeout_val = VTY_TIMEOUT_DEFAULT;
XFREE(MTYPE_VTY, vty_accesslist_name);
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
2002-12-13 21:15:29 +01:00
}
static void vty_save_cwd(void)
2002-12-13 21:15:29 +01:00
{
char *c;
c = getcwd(vty_cwd, sizeof(vty_cwd));
2002-12-13 21:15:29 +01:00
if (!c) {
/*
* At this point if these go wrong, more than likely
* the whole world is coming down around us
* Hence not worrying about it too much.
*/
if (!chdir(SYSCONFDIR)) {
flog_err_sys(EC_LIB_SYSTEM_CALL,
"Failure to chdir to %s, errno: %d",
SYSCONFDIR, errno);
exit(-1);
}
if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) {
flog_err_sys(EC_LIB_SYSTEM_CALL,
"Failure to getcwd, errno: %d", errno);
exit(-1);
}
}
2002-12-13 21:15:29 +01:00
}
char *vty_get_cwd(void)
2002-12-13 21:15:29 +01:00
{
return vty_cwd;
2002-12-13 21:15:29 +01:00
}
int vty_shell(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
return vty->type == VTY_SHELL ? 1 : 0;
2002-12-13 21:15:29 +01:00
}
int vty_shell_serv(struct vty *vty)
2002-12-13 21:15:29 +01:00
{
return vty->type == VTY_SHELL_SERV ? 1 : 0;
2002-12-13 21:15:29 +01:00
}
void vty_init_vtysh(void)
2002-12-13 21:15:29 +01:00
{
vtyvec = vector_init(VECTOR_MIN_SIZE);
2002-12-13 21:15:29 +01:00
}
/* Install vty's own commands like `who' command. */
void vty_init(struct thread_master *master_thread, bool do_command_logging)
{
/* For further configuration read, preserve current directory. */
vty_save_cwd();
vtyvec = vector_init(VECTOR_MIN_SIZE);
vty_master = master_thread;
atexit(vty_stdio_atexit);
/* Initilize server thread vector. */
Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE);
/* Install bgp top node. */
install_node(&vty_node);
install_element(VIEW_NODE, &config_who_cmd);
install_element(VIEW_NODE, &show_history_cmd);
install_element(CONFIG_NODE, &line_vty_cmd);
install_element(CONFIG_NODE, &service_advanced_vty_cmd);
install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
install_element(CONFIG_NODE, &show_history_cmd);
install_element(CONFIG_NODE, &log_commands_cmd);
if (do_command_logging) {
do_log_commands = true;
do_log_commands_perm = true;
}
install_element(ENABLE_NODE, &terminal_monitor_cmd);
install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
install_default(VTY_NODE);
install_element(VTY_NODE, &exec_timeout_min_cmd);
install_element(VTY_NODE, &exec_timeout_sec_cmd);
install_element(VTY_NODE, &no_exec_timeout_cmd);
install_element(VTY_NODE, &vty_access_class_cmd);
install_element(VTY_NODE, &no_vty_access_class_cmd);
install_element(VTY_NODE, &vty_login_cmd);
install_element(VTY_NODE, &no_vty_login_cmd);
install_element(VTY_NODE, &vty_ipv6_access_class_cmd);
install_element(VTY_NODE, &no_vty_ipv6_access_class_cmd);
}
void vty_terminate(void)
{
memset(vty_cwd, 0x00, sizeof(vty_cwd));
if (vtyvec && Vvty_serv_thread) {
vty_reset();
vector_free(vtyvec);
vector_free(Vvty_serv_thread);
vtyvec = NULL;
Vvty_serv_thread = NULL;
}
[bgpd] Stability fixes including bugs 397, 492 I've spent the last several weeks working on stability fixes to bgpd. These patches fix all of the numerous crashes, assertion failures, memory leaks and memory stomping I could find. Valgrind was used extensively. Added new function bgp_exit() to help catch problems. If "debug bgp" is configured and bgpd exits with status of 0, statistics on remaining lib/memory.c allocations are printed to stderr. It is my hope that other developers will use this to stay on top of memory issues. Example questionable exit: bgpd: memstats: Current memory utilization in module LIB: bgpd: memstats: Link List : 6 bgpd: memstats: Link Node : 5 bgpd: memstats: Hash : 8 bgpd: memstats: Hash Bucket : 2 bgpd: memstats: Hash Index : 8 bgpd: memstats: Work queue : 3 bgpd: memstats: Work queue item : 2 bgpd: memstats: Work queue name string : 3 bgpd: memstats: Current memory utilization in module BGP: bgpd: memstats: BGP instance : 1 bgpd: memstats: BGP peer : 1 bgpd: memstats: BGP peer hostname : 1 bgpd: memstats: BGP attribute : 1 bgpd: memstats: BGP extra attributes : 1 bgpd: memstats: BGP aspath : 1 bgpd: memstats: BGP aspath str : 1 bgpd: memstats: BGP table : 24 bgpd: memstats: BGP node : 1 bgpd: memstats: BGP route : 1 bgpd: memstats: BGP synchronise : 8 bgpd: memstats: BGP Process queue : 1 bgpd: memstats: BGP node clear queue : 1 bgpd: memstats: NOTE: If configuration exists, utilization may be expected. Example clean exit: bgpd: memstats: No remaining tracked memory utilization. This patch fixes bug #397: "Invalid free in bgp_announce_check()". This patch fixes bug #492: "SIGBUS in bgpd/bgp_route.c: bgp_clear_route_node()". My apologies for not separating out these changes into individual patches. The complexity of doing so boggled what is left of my brain. I hope this is all still useful to the community. This code has been production tested, in non-route-server-client mode, on a linux 32-bit box and a 64-bit box. Release/reset functions, used by bgp_exit(), added to: bgpd/bgp_attr.c,h bgpd/bgp_community.c,h bgpd/bgp_dump.c,h bgpd/bgp_ecommunity.c,h bgpd/bgp_filter.c,h bgpd/bgp_nexthop.c,h bgpd/bgp_route.c,h lib/routemap.c,h File by file analysis: * bgpd/bgp_aspath.c: Prevent re-use of ashash after it is released. * bgpd/bgp_attr.c: #if removed uncalled cluster_dup(). * bgpd/bgp_clist.c,h: Allow community_list_terminate() to be called from bgp_exit(). * bgpd/bgp_filter.c: Fix aslist->name use without allocation check, and also fix memory leak. * bgpd/bgp_main.c: Created bgp_exit() exit routine. This function frees allocations made as part of bgpd initialization and, to some extent, configuration. If "debug bgp" is configured, memory stats are printed as described above. * bgpd/bgp_nexthop.c: zclient_new() already allocates stream for ibuf/obuf, so bgp_scan_init() shouldn't do it too. Also, made it so zlookup is global so bgp_exit() can use it. * bgpd/bgp_packet.c: bgp_capability_msg_parse() call to bgp_clear_route() adjusted to use new BGP_CLEAR_ROUTE_NORMAL flag. * bgpd/bgp_route.h: Correct reference counter "lock" to be signed. bgp_clear_route() now accepts a bgp_clear_route_type of either BGP_CLEAR_ROUTE_NORMAL or BGP_CLEAR_ROUTE_MY_RSCLIENT. * bgpd/bgp_route.c: - bgp_process_rsclient(): attr was being zero'ed and then bgp_attr_extra_free() was being called with it, even though it was never filled with valid data. - bgp_process_rsclient(): Make sure rsclient->group is not NULL before use. - bgp_processq_del(): Add call to bgp_table_unlock(). - bgp_process(): Add call to bgp_table_lock(). - bgp_update_rsclient(): memset clearing of new_attr not needed since declarationw with "= { 0 }" does it. memset was already commented out. - bgp_update_rsclient(): Fix screwed up misleading indentation. - bgp_withdraw_rsclient(): Fix screwed up misleading indentation. - bgp_clear_route_node(): Support BGP_CLEAR_ROUTE_MY_RSCLIENT. - bgp_clear_node_queue_del(): Add call to bgp_table_unlock() and also free struct bgp_clear_node_queue used for work item. - bgp_clear_node_complete(): Do peer_unlock() after BGP_EVENT_ADD() in case peer is released by peer_unlock() call. - bgp_clear_route_table(): Support BGP_CLEAR_ROUTE_MY_RSCLIENT. Use struct bgp_clear_node_queue to supply data to worker. Add call to bgp_table_lock(). - bgp_clear_route(): Add support for BGP_CLEAR_ROUTE_NORMAL or BGP_CLEAR_ROUTE_MY_RSCLIENT. - bgp_clear_route_all(): Use BGP_CLEAR_ROUTE_NORMAL. Bug 397 fixes: - bgp_default_originate() - bgp_announce_table() * bgpd/bgp_table.h: - struct bgp_table: Added reference count. Changed type of owner to be "struct peer *" rather than "void *". - struct bgp_node: Correct reference counter "lock" to be signed. * bgpd/bgp_table.c: - Added bgp_table reference counting. - bgp_table_free(): Fixed cleanup code. Call peer_unlock() on owner if set. - bgp_unlock_node(): Added assertion. - bgp_node_get(): Added call to bgp_lock_node() to code path that it was missing from. * bgpd/bgp_vty.c: - peer_rsclient_set_vty(): Call peer_lock() as part of peer assignment to owner. Handle failure gracefully. - peer_rsclient_unset_vty(): Add call to bgp_clear_route() with BGP_CLEAR_ROUTE_MY_RSCLIENT purpose. * bgpd/bgp_zebra.c: Made it so zclient is global so bgp_exit() can use it. * bgpd/bgpd.c: - peer_lock(): Allow to be called when status is "Deleted". - peer_deactivate(): Supply BGP_CLEAR_ROUTE_NORMAL purpose to bgp_clear_route() call. - peer_delete(): Common variable listnode pn. Fix bug in which rsclient was only dealt with if not part of a peer group. Call bgp_clear_route() for rsclient, if appropriate, and do so with BGP_CLEAR_ROUTE_MY_RSCLIENT purpose. - peer_group_get(): Use XSTRDUP() instead of strdup() for conf->host. - peer_group_bind(): Call bgp_clear_route() for rsclient, and do so with BGP_CLEAR_ROUTE_MY_RSCLIENT purpose. - bgp_create(): Use XSTRDUP() instead of strdup() for peer_self->host. - bgp_delete(): Delete peers before groups, rather than after. And then rather than deleting rsclients, verify that there are none at this point. - bgp_unlock(): Add assertion. - bgp_free(): Call bgp_table_finish() rather than doing XFREE() itself. * lib/command.c,h: Compiler warning fixes. Add cmd_terminate(). Fixed massive leak in install_element() in which cmd_make_descvec() was being called more than once for the same cmd->strvec/string/doc. * lib/log.c: Make closezlog() check fp before calling fclose(). * lib/memory.c: Catch when alloc count goes negative by using signed counts. Correct #endif comment. Add log_memstats_stderr(). * lib/memory.h: Add log_memstats_stderr(). * lib/thread.c: thread->funcname was being accessed in thread_call() after it had been freed. Rearranged things so that thread_call() frees funcname. Also made it so thread_master_free() cleans up cpu_record. * lib/vty.c,h: Use global command_cr. Add vty_terminate(). * lib/zclient.c,h: Re-enable zclient_free().
2009-07-18 07:44:03 +02:00
}