frr/lib/vty.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

3966 lines
90 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
2002-12-13 21:15:29 +01:00
/*
* Virtual terminal [aka TeletYpe] interface routine.
* Copyright (C) 1997, 98 Kunihiro Ishiguro
*/
#include <zebra.h>
#include <lib/version.h>
#include <sys/types.h>
#include <sys/types.h>
#ifdef HAVE_LIBPCRE2_POSIX
#ifndef _FRR_PCRE2_POSIX
#define _FRR_PCRE2_POSIX
#include <pcre2posix.h>
#endif /* _FRR_PCRE2_POSIX */
#elif defined(HAVE_LIBPCREPOSIX)
#include <pcreposix.h>
#else
#include <regex.h>
#endif /* HAVE_LIBPCRE2_POSIX */
#include <stdio.h>
#include "debug.h"
2002-12-13 21:15:29 +01:00
#include "linklist.h"
#include "frrevent.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"
#include "json.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
#include "lib/vty_clippy.c"
DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server");
DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer");
DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history");
DECLARE_DLIST(vtys, struct vty, itm);
2002-12-13 21:15:29 +01:00
/* Vty events */
enum vty_event {
2002-12-13 21:15:29 +01:00
VTY_SERV,
VTY_READ,
VTY_WRITE,
VTY_TIMEOUT_RESET,
#ifdef VTYSH
VTYSH_SERV,
VTYSH_READ,
VTYSH_WRITE
2002-12-13 21:15:29 +01:00
#endif /* VTYSH */
};
struct nb_config *vty_mgmt_candidate_config;
static struct mgmt_fe_client *mgmt_fe_client;
static bool mgmt_fe_connected;
static uint64_t mgmt_client_id_next;
static uint64_t mgmt_last_req_id = UINT64_MAX;
PREDECL_DLIST(vtyservs);
struct vty_serv {
struct vtyservs_item itm;
int sock;
bool vtysh;
struct event *t_accept;
};
DECLARE_DLIST(vtyservs, struct vty_serv, itm);
static void vty_event_serv(enum vty_event event, struct vty_serv *);
static void vty_event(enum vty_event, struct vty *);
static int vtysh_flush(struct vty *vty);
2002-12-13 21:15:29 +01:00
/* Extern host structure from command.c */
extern struct host host;
/* active listeners */
static struct vtyservs_head vty_servs[1] = {INIT_DLIST(vty_servs[0])};
/* active connections */
static struct vtys_head vty_sessions[1] = {INIT_DLIST(vty_sessions[0])};
static struct vtys_head vtysh_sessions[1] = {INIT_DLIST(vtysh_sessions[0])};
2002-12-13 21:15:29 +01:00
/* 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;
/* 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
bool vty_log_commands;
static bool vty_log_commands_perm;
char const *const mgmt_daemons[] = {
#ifdef HAVE_STATICD
"staticd",
#endif
};
uint mgmt_daemons_count = array_size(mgmt_daemons);
static int vty_mgmt_lock_candidate_inline(struct vty *vty)
{
assert(!vty->mgmt_locked_candidate_ds);
(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, true, true);
return vty->mgmt_locked_candidate_ds ? 0 : -1;
}
static int vty_mgmt_unlock_candidate_inline(struct vty *vty)
{
assert(vty->mgmt_locked_candidate_ds);
(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false, true);
return vty->mgmt_locked_candidate_ds ? -1 : 0;
}
static int vty_mgmt_lock_running_inline(struct vty *vty)
{
assert(!vty->mgmt_locked_running_ds);
(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, true, true);
return vty->mgmt_locked_running_ds ? 0 : -1;
}
static int vty_mgmt_unlock_running_inline(struct vty *vty)
{
assert(vty->mgmt_locked_running_ds);
(void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, false, true);
return vty->mgmt_locked_running_ds ? -1 : 0;
}
void vty_mgmt_resume_response(struct vty *vty, bool success)
{
uint8_t header[4] = {0, 0, 0, 0};
int ret = CMD_SUCCESS;
if (!vty->mgmt_req_pending_cmd) {
zlog_err(
"vty resume response called without mgmt_req_pending_cmd");
return;
}
if (!success)
ret = CMD_WARNING_CONFIG_FAILED;
MGMTD_FE_CLIENT_DBG(
"resuming CLI cmd after %s on vty session-id: %" PRIu64
" with '%s'",
vty->mgmt_req_pending_cmd, vty->mgmt_session_id,
success ? "succeeded" : "failed");
vty->mgmt_req_pending_cmd = NULL;
if (vty->type != VTY_FILE) {
header[3] = ret;
buffer_put(vty->obuf, header, 4);
if (!vty->t_write && (vtysh_flush(vty) < 0)) {
zlog_err("failed to vtysh_flush");
/* Try to flush results; exit if a write error occurs */
return;
}
}
if (vty->status == VTY_CLOSE)
vty_close(vty);
else if (vty->type != VTY_FILE)
vty_event(VTYSH_READ, vty);
else
/* should we assert here? */
zlog_err("mgmtd: unexpected resume while reading config file");
}
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(errcode, &vty->include, errbuf, sizeof(errbuf));
vty_out(vty, "%% Regex compilation error: %s\n", errbuf);
} else {
vty->filter = true;
}
return ret;
}
/* VTY standard output function. */
int vty_out(struct vty *vty, const char *format, ...)
2002-12-13 21:15:29 +01:00
{
va_list args;
ssize_t len;
2002-12-13 21:15:29 +01:00
char buf[1024];
char *p = NULL;
char *filtered;
/* format string may contain %m, keep errno intact for printfrr */
int saved_errno = errno;
if (vty->frame_pos) {
vty->frame_pos = 0;
vty_out(vty, "%s", vty->frame);
}
va_start(args, format);
errno = saved_errno;
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;
2002-12-13 21:15:29 +01:00
}
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);
2002-12-13 21:15:29 +01:00
return len;
}
static int vty_json_helper(struct vty *vty, struct json_object *json,
uint32_t options)
{
const char *text;
if (!json)
return CMD_SUCCESS;
text = json_object_to_json_string_ext(
json, options);
vty_out(vty, "%s\n", text);
json_object_free(json);
return CMD_SUCCESS;
}
int vty_json(struct vty *vty, struct json_object *json)
{
return vty_json_helper(vty, json,
JSON_C_TO_STRING_PRETTY |
JSON_C_TO_STRING_NOSLASHESCAPE);
}
int vty_json_no_pretty(struct vty *vty, struct json_object *json)
{
return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
}
void vty_json_empty(struct vty *vty)
{
json_object *json = json_object_new_object();
vty_json(vty, json);
}
2002-12-13 21:15:29 +01:00
/* Output current time to the vty. */
void vty_time_print(struct vty *vty, int cr)
{
char buf[FRR_TIMESTAMP_LEN];
if (frr_timestamp(0, buf, sizeof(buf)) == 0) {
zlog_info("frr_timestamp error");
2002-12-13 21:15:29 +01:00
return;
}
if (cr)
vty_out(vty, "%s\n", buf);
else
vty_out(vty, "%s ", buf);
return;
}
/* 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);
2002-12-13 21:15:29 +01:00
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
/* prompt formatting has a %s in the cmd_node prompt string.
*
* Also for some reason GCC emits the warning on the end of the function
* (optimization maybe?) rather than on the vty_out line, so this pragma
* wraps the entire function rather than just the vty_out line.
*/
2002-12-13 21:15:29 +01:00
/* Put out prompt and wait input from user. */
static void vty_prompt(struct vty *vty)
{
if (vty->type == VTY_TERM) {
vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
2002-12-13 21:15:29 +01:00
}
}
#pragma GCC diagnostic pop
2002-12-13 21:15:29 +01:00
/* Send WILL TELOPT_ECHO to remote server. */
static void vty_will_echo(struct vty *vty)
{
unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
2002-12-13 21:15:29 +01:00
vty_out(vty, "%s", cmd);
}
/* Make suppress Go-Ahead telnet option. */
static void vty_will_suppress_go_ahead(struct vty *vty)
{
unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
2002-12-13 21:15:29 +01:00
vty_out(vty, "%s", cmd);
}
/* Make don't use linemode over telnet. */
static void vty_dont_linemode(struct vty *vty)
{
unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
2002-12-13 21:15:29 +01:00
vty_out(vty, "%s", cmd);
}
/* Use window size. */
static void vty_do_window_size(struct vty *vty)
{
unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
2002-12-13 21:15:29 +01:00
vty_out(vty, "%s", cmd);
}
/* Authentication of vty */
static void vty_auth(struct vty *vty, char *buf)
{
char *passwd = NULL;
enum node_type next_node = 0;
int fail;
2002-12-13 21:15:29 +01:00
switch (vty->node) {
case AUTH_NODE:
if (host.encrypt)
passwd = host.password_encrypt;
2002-12-13 21:15:29 +01:00
else
passwd = host.password;
2002-12-13 21:15:29 +01:00
if (host.advanced)
next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
2002-12-13 21:15:29 +01:00
else
next_node = VIEW_NODE;
2002-12-13 21:15:29 +01:00
break;
case AUTH_ENABLE_NODE:
if (host.encrypt)
passwd = host.enable_encrypt;
2002-12-13 21:15:29 +01:00
else
passwd = host.enable;
2002-12-13 21:15:29 +01:00
next_node = ENABLE_NODE;
break;
}
2002-12-13 21:15:29 +01:00
if (passwd) {
if (host.encrypt)
fail = strcmp(crypt(buf, passwd), passwd);
2002-12-13 21:15:29 +01:00
else
fail = strcmp(buf, passwd);
2002-12-13 21:15:29 +01:00
} else
fail = 1;
2002-12-13 21:15:29 +01:00
if (!fail) {
vty->fail = 0;
vty->node = next_node; /* Success ! */
2002-12-13 21:15:29 +01:00
} 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 (vty_log_commands &&
strncmp(buf, "echo PING", strlen("echo PING")) != 0)
cp = buf;
if (cp != NULL) {
/* Skip white spaces. */
while (isspace((unsigned char)*cp) && *cp != '\0')
cp++;
}
if (cp != NULL && *cp != '\0') {
char vty_str[VTY_BUFSIZ];
char prompt_str[VTY_BUFSIZ];
/* format the base vty info */
snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", vty->fd,
vty->address);
/* format the prompt */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
/* prompt formatting has a %s in the cmd_node prompt string */
snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
vty_str);
#pragma GCC diagnostic pop
/* now log the command */
zlog_notice("%s%s", prompt_str, buf);
}
2002-12-13 21:15:29 +01:00
RUSAGE_T before;
RUSAGE_T after;
unsigned long walltime, cputime;
/* cmd_execute() may change cputime_enabled if we're executing the
* "service cputime-stats" command, which can result in nonsensical
* and very confusing warnings
*/
bool cputime_enabled_here = cputime_enabled;
GETRUSAGE(&before);
ret = cmd_execute(vty, buf, NULL, 0);
GETRUSAGE(&after);
walltime = event_consumed_time(&after, &before, &cputime);
if (cputime_enabled_here && cputime_enabled && cputime_threshold
&& cputime > cputime_threshold)
/* Warn about CPU hog that must be fixed. */
flog_warn(EC_LIB_SLOW_THREAD_CPU,
"CPU HOG: command took %lums (cpu time %lums): %s",
walltime / 1000, cputime / 1000, buf);
else if (walltime_threshold && walltime > walltime_threshold)
flog_warn(EC_LIB_SLOW_THREAD_WALL,
"STARVATION: command took %lums (cpu time %lums): %s",
walltime / 1000, cputime / 1000, buf);
/* Get the name of the protocol if any */
protocolname = frr_protoname;
2002-12-13 21:15:29 +01:00
if (ret != CMD_SUCCESS)
switch (ret) {
case CMD_WARNING:
if (vty->type == VTY_FILE)
vty_out(vty, "Warning...\n");
break;
2002-12-13 21:15:29 +01:00
case CMD_ERR_AMBIGUOUS:
vty_out(vty, "%% Ambiguous command.\n");
break;
2002-12-13 21:15:29 +01:00
case CMD_ERR_NO_MATCH:
vty_out(vty, "%% [%s] Unknown command: %s\n",
protocolname, buf);
break;
2002-12-13 21:15:29 +01:00
case CMD_ERR_INCOMPLETE:
vty_out(vty, "%% Command incomplete.\n");
break;
2002-12-13 21:15:29 +01:00
}
2002-12-13 21:15:29 +01:00
return ret;
}
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. */
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 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;
/* Should we do buffering here ? And make vty_flush (vty) ? */
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
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)
{
int i;
int length;
if (vty->length + 1 >= VTY_BUFSIZ)
return;
2002-12-13 21:15:29 +01:00
length = vty->length - vty->cp;
memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
vty->buf[vty->cp] = c;
vty_write(vty, &vty->buf[vty->cp], length + 1);
for (i = 0; i < length; i++)
vty_write(vty, &telnet_backward_char, 1);
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)
{
if (vty->cp == vty->length) {
vty_self_insert(vty, c);
return;
}
2002-12-13 21:15:29 +01:00
vty->buf[vty->cp++] = c;
2002-12-13 21:15:29 +01:00
vty_write(vty, &c, 1);
}
/**
* 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.
*/
2002-12-13 21:15:29 +01:00
static void vty_insert_word_overwrite(struct vty *vty, char *str)
{
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)
{
if (vty->cp < vty->length) {
vty_write(vty, &vty->buf[vty->cp], 1);
vty->cp++;
}
}
/* Backward character. */
static void vty_backward_char(struct vty *vty)
{
if (vty->cp > 0) {
vty->cp--;
vty_write(vty, &telnet_backward_char, 1);
}
}
/* Move to the beginning of the line. */
static void vty_beginning_of_line(struct vty *vty)
{
while (vty->cp)
vty_backward_char(vty);
}
/* Move to the end of the line. */
static void vty_end_of_line(struct vty *vty)
{
while (vty->cp < vty->length)
vty_forward_char(vty);
}
static void vty_kill_line_from_beginning(struct vty *);
static void vty_redraw_line(struct vty *);
/* Print command line history. This function is called from
vty_next_line and vty_previous_line. */
static void vty_history_print(struct vty *vty)
{
int length;
vty_kill_line_from_beginning(vty);
/* 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);
}
/* Show next command line history. */
static void vty_next_line(struct vty *vty)
{
int try_index;
if (vty->hp == vty->hindex)
return;
/* Try is there history exist or not. */
try_index = vty->hp;
if (try_index == (VTY_MAXHIST - 1))
try_index = 0;
else
try_index++;
/* If there is not history return. */
if (vty->hist[try_index] == NULL)
return;
else
vty->hp = try_index;
vty_history_print(vty);
}
/* Show previous command line history. */
static void vty_previous_line(struct vty *vty)
{
int try_index;
try_index = vty->hp;
if (try_index == 0)
try_index = VTY_MAXHIST - 1;
else
try_index--;
if (vty->hist[try_index] == NULL)
return;
else
vty->hp = try_index;
vty_history_print(vty);
}
/* This function redraw all of the command line character. */
static void vty_redraw_line(struct vty *vty)
{
vty_write(vty, vty->buf, vty->length);
vty->cp = vty->length;
}
/* Forward word. */
static void vty_forward_word(struct vty *vty)
{
while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
vty_forward_char(vty);
2002-12-13 21:15:29 +01:00
while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
vty_forward_char(vty);
}
/* Backward word without skipping training space. */
static void vty_backward_pure_word(struct vty *vty)
{
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
vty_backward_char(vty);
}
/* Backward word. */
static void vty_backward_word(struct vty *vty)
{
while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
vty_backward_char(vty);
while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
vty_backward_char(vty);
}
/* When '^D' is typed at the beginning of the line we move to the down
level. */
static void vty_down_level(struct vty *vty)
{
vty_out(vty, "\n");
cmd_exit(vty);
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
vty->cp = 0;
}
/* 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);
2002-12-13 21:15:29 +01:00
vty->node = ENABLE_NODE;
}
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
vty->cp = 0;
}
/* Delete a character at the current point. */
2002-12-13 21:15:29 +01:00
static void vty_delete_char(struct vty *vty)
{
int i;
int size;
if (vty->length == 0) {
vty_down_level(vty);
return;
}
if (vty->cp == vty->length)
return; /* completion need here? */
2002-12-13 21:15:29 +01:00
size = vty->length - vty->cp;
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);
for (i = 0; i < size; i++)
vty_write(vty, &telnet_backward_char, 1);
}
/* Delete a character before the point. */
static void vty_delete_backward_char(struct vty *vty)
{
if (vty->cp == 0)
return;
vty_backward_char(vty);
vty_delete_char(vty);
}
/* Kill rest of line from current point. */
static void vty_kill_line(struct vty *vty)
{
int i;
int size;
size = vty->length - vty->cp;
2002-12-13 21:15:29 +01:00
if (size == 0)
return;
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);
memset(&vty->buf[vty->cp], 0, size);
vty->length = vty->cp;
}
/* Kill line from the beginning. */
static void vty_kill_line_from_beginning(struct vty *vty)
{
vty_beginning_of_line(vty);
vty_kill_line(vty);
}
/* Delete a word before the point. */
static void vty_forward_kill_word(struct vty *vty)
{
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);
}
/* Delete a word before the point. */
static void vty_backward_kill_word(struct vty *vty)
{
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);
}
/* Transpose chars before or at the point. */
static void vty_transpose_chars(struct vty *vty)
{
char c1, c2;
/* If length is short or point is near by the beginning of line then
return. */
if (vty->length < 2 || vty->cp < 1)
return;
/* 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];
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];
vty_backward_char(vty);
vty_self_insert_overwrite(vty, c1);
vty_self_insert_overwrite(vty, c2);
}
}
/* Do completion at vty interface. */
static void vty_complete_command(struct vty *vty)
{
int i;
int ret;
char **matched = NULL;
vector vline;
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
vline = cmd_make_strvec(vty->buf);
if (vline == NULL)
return;
2002-12-13 21:15:29 +01:00
/* 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);
2002-12-13 21:15:29 +01:00
cmd_free_strvec(vline);
vty_out(vty, "\n");
2002-12-13 21:15:29 +01:00
switch (ret) {
case CMD_ERR_AMBIGUOUS:
vty_out(vty, "%% Ambiguous command.\n");
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
vty_redraw_line(vty);
break;
case CMD_ERR_NO_MATCH:
/* vty_out (vty, "%% There is no matched command.\n"); */
2002-12-13 21:15:29 +01:00
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;
}
2002-12-13 21:15:29 +01:00
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]);
2002-12-13 21:15:29 +01:00
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]);
2002-12-13 21:15:29 +01:00
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");
2002-12-13 21:15:29 +01:00
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);
2002-12-13 21:15:29 +01:00
}
2002-12-13 21:15:29 +01:00
static void vty_describe_fold(struct vty *vty, int cmd_width,
unsigned int desc_width, struct cmd_token *token)
2002-12-13 21:15:29 +01:00
{
char *buf;
const char *cmd, *p;
2002-12-13 21:15:29 +01:00
int pos;
cmd = token->text;
2002-12-13 21:15:29 +01:00
if (desc_width <= 0) {
vty_out(vty, " %-*s %s\n", cmd_width, cmd, token->desc);
2002-12-13 21:15:29 +01:00
return;
}
buf = XCALLOC(MTYPE_TMP, strlen(token->desc) + 1);
for (p = token->desc; strlen(p) > desc_width; p += pos + 1) {
2002-12-13 21:15:29 +01:00
for (pos = desc_width; pos > 0; pos--)
if (*(p + pos) == ' ')
break;
2002-12-13 21:15:29 +01:00
if (pos == 0)
break;
memcpy(buf, p, pos);
2002-12-13 21:15:29 +01:00
buf[pos] = '\0';
vty_out(vty, " %-*s %s\n", cmd_width, cmd, buf);
2002-12-13 21:15:29 +01:00
cmd = "";
}
vty_out(vty, " %-*s %s\n", cmd_width, cmd, p);
2002-12-13 21:15:29 +01:00
XFREE(MTYPE_TMP, buf);
}
/* 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;
2002-12-13 21:15:29 +01:00
vline = cmd_make_strvec(vty->buf);
2002-12-13 21:15:29 +01:00
/* 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);
2002-12-13 21:15:29 +01:00
describe = cmd_describe_command(vline, vty, &ret);
vty_out(vty, "\n");
2002-12-13 21:15:29 +01:00
/* Ambiguous error. */
switch (ret) {
case CMD_ERR_AMBIGUOUS:
vty_out(vty, "%% Ambiguous command.\n");
goto out;
2002-12-13 21:15:29 +01:00
break;
case CMD_ERR_NO_MATCH:
vty_out(vty, "%% There is no matched command.\n");
goto out;
2002-12-13 21:15:29 +01:00
break;
}
2002-12-13 21:15:29 +01:00
/* 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;
2002-12-13 21:15:29 +01:00
}
2002-12-13 21:15:29 +01:00
/* Get width of description string. */
desc_width = vty->width - (width + 6);
2002-12-13 21:15:29 +01:00
/* 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 ((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);
2002-12-13 21:15:29 +01:00
else
vty_describe_fold(vty, width, desc_width, token);
2002-12-13 21:15:29 +01:00
}
out:
2002-12-13 21:15:29 +01:00
cmd_free_strvec(vline);
if (describe)
vector_free(describe);
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
vty_redraw_line(vty);
}
static void vty_clear_buf(struct vty *vty)
{
memset(vty->buf, 0, vty->max);
}
/* ^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);
2002-12-13 21:15:29 +01:00
vty->node = ENABLE_NODE;
}
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
2002-12-13 21:15:29 +01:00
/* Set history pointer to the latest one. */
vty->hp = vty->hindex;
}
/* Add current command line to the history buffer. */
static void vty_hist_add(struct vty *vty)
{
int index;
if (vty->length == 0)
return;
index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
/* Ignore the same string as previous one. */
if (vty->hist[index])
if (strcmp(vty->buf, vty->hist[index]) == 0) {
vty->hp = vty->hindex;
return;
}
/* Insert history entry. */
XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
2002-12-13 21:15:29 +01:00
vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
/* History index rotation. */
vty->hindex++;
if (vty->hindex == VTY_MAXHIST)
vty->hindex = 0;
vty->hp = vty->hindex;
}
/* #define TELNET_OPTION_DEBUG */
/* Get telnet window size. */
static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
{
#ifdef TELNET_OPTION_DEBUG
int i;
2002-12-13 21:15:29 +01:00
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;
}
2002-12-13 21:15:29 +01:00
}
vty_out(vty, "\n");
2002-12-13 21:15:29 +01:00
#endif /* TELNET_OPTION_DEBUG */
switch (buf[0]) {
case SB:
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->sb_len = 0;
2002-12-13 21:15:29 +01:00
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;
2002-12-13 21:15:29 +01:00
}
default:
break;
}
return 1;
}
/* Execute current command line. */
static int vty_execute(struct vty *vty)
{
int ret;
ret = CMD_SUCCESS;
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);
2002-12-13 21:15:29 +01:00
break;
}
/* Clear command line buffer. */
vty->cp = vty->length = 0;
vty_clear_buf(vty);
2004-11-04 Andrew J. Schorr <ajschorr@alumni.princeton.edu> * vty.h: Remove fields in struct vty that were related to VTY_CONTINUE capabilities (that were used only in bgpd/bgp_route.c and are now removed). Also remove some other fields that were not being used at all. * vty.c: (vty_execute) Do not test for obsolete status values VTY_START and VTY_CONTINUE. (vty_read) Remove calls to vty->output_func since that was part of the VTY_CONTINUE infrastructure that has been removed. (vty_flush) Remove code to support VTY_START and VTY_CONTINUE. (vty_close) Remove code to cancel vty->t_output thread, since that thread was never actually used. * bgp_route.c: Remove all code related to VTY_CONTINUE; this feature is deprecated because the output did not represent a single point in time. All output needs to be generated inline and buffered by the library code. (route_vty_out,route_vty_out_tag,damp_route_vty_out, flap_route_vty_out) Remove code to count number of lines of output, since this was only useful for VTY_CONTINUE behavior. (bgp_show_callback) Removed. (bgp_show_table) Remove hooks for VTY_CONTINUE callback support. As a result, there's a new output_arg argument to this function. Make function static. (bgp_show) Make function static and add a new output_arg argument. Change all functions that call bgp_show or bgp_show_table to pass the new output_arg argument (that used to be passed inside vty->output_arg). * bgp_mplsvpn.c: Remove declarations of functions defined in bgp_route.c; these declarations belong in bgp_route.h. * bgp_route.h: Declare 3 global functions used in both bgp_route.c and in bgp_mplsvpn.c.
2004-11-05 02:25:55 +01:00
if (vty->status != VTY_CLOSE)
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
return ret;
}
#define CONTROL(X) ((X) - '@')
#define VTY_NORMAL 0
#define VTY_PRE_ESCAPE 1
#define VTY_ESCAPE 2
#define VTY_CR 3
2002-12-13 21:15:29 +01:00
/* 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;
}
2002-12-13 21:15:29 +01:00
/* Go back to normal mode. */
vty->escape = VTY_NORMAL;
}
/* Quit print out to the buffer. */
static void vty_buffer_reset(struct vty *vty)
{
buffer_reset(vty->obuf);
buffer_reset(vty->lbuf);
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
vty_redraw_line(vty);
}
/* Read data via vty socket. */
static void vty_read(struct event *thread)
2002-12-13 21:15:29 +01:00
{
int i;
int nbytes;
unsigned char buf[VTY_READ_BUFSIZ];
struct vty *vty = EVENT_ARG(thread);
2002-12-13 21:15:29 +01:00
/* Read raw data from socket */
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 ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
vty_event(VTY_READ, vty);
return;
}
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);
}
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->status = VTY_CLOSE;
}
for (i = 0; i < nbytes; i++) {
2002-12-13 21:15:29 +01:00
if (buf[i] == IAC) {
if (!vty->iac) {
vty->iac = 1;
continue;
} else {
vty->iac = 0;
}
}
2002-12-13 21:15:29 +01:00
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;
}
2002-12-13 21:15:29 +01:00
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;
}
2002-12-13 21:15:29 +01:00
if (vty->status == VTY_MORE) {
switch (buf[i]) {
case CONTROL('C'):
case 'q':
case 'Q':
vty_buffer_reset(vty);
break;
default:
break;
}
continue;
}
2002-12-13 21:15:29 +01:00
/* Escape character. */
if (vty->escape == VTY_ESCAPE) {
vty_escape_map(buf[i], vty);
continue;
}
2002-12-13 21:15:29 +01:00
/* 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;
}
if (vty->escape == VTY_CR) {
/* if we get CR+NL, the NL results in an extra empty
* prompt line being printed without this; just drop
* the NL if it immediately follows CR.
*/
vty->escape = VTY_NORMAL;
if (buf[i] == '\n')
continue;
}
2002-12-13 21:15:29 +01:00
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 '\r':
vty->escape = VTY_CR;
/* fallthru */
case '\n':
vty_out(vty, "\n");
buffer_flush_available(vty->obuf, vty->wfd);
vty_execute(vty);
if (vty->pass_fd != -1) {
close(vty->pass_fd);
vty->pass_fd = -1;
}
break;
case '\t':
vty_complete_command(vty);
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;
}
2002-12-13 21:15:29 +01:00
}
2002-12-13 21:15:29 +01:00
/* Check status. */
if (vty->status == VTY_CLOSE)
vty_close(vty);
else {
vty_event(VTY_WRITE, vty);
vty_event(VTY_READ, vty);
2002-12-13 21:15:29 +01:00
}
}
/* Flush buffer to the vty. */
static void vty_flush(struct event *thread)
2002-12-13 21:15:29 +01:00
{
int erase;
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
buffer_status_t flushrc;
struct vty *vty = EVENT_ARG(thread);
2002-12-13 21:15:29 +01:00
/* Tempolary disable read thread. */
if (vty->lines == 0)
EVENT_OFF(vty->t_read);
2002-12-13 21:15:29 +01:00
/* Function execution continue. */
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
erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
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
/* 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->wfd);
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
else if (vty->status == VTY_MORELINE)
flushrc = buffer_flush_window(vty->obuf, vty->wfd, vty->width,
1, erase, 0);
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
else
flushrc = buffer_flush_window(
vty->obuf, vty->wfd, vty->width,
vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
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
switch (flushrc) {
case BUFFER_ERROR:
zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
vty->fd, vty->wfd);
buffer_reset(vty->lbuf);
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
buffer_reset(vty->obuf);
vty_close(vty);
return;
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
case BUFFER_EMPTY:
if (vty->status == VTY_CLOSE)
vty_close(vty);
2002-12-13 21:15:29 +01:00
else {
vty->status = VTY_NORMAL;
if (vty->lines == 0)
vty_event(VTY_READ, vty);
}
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
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);
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
break;
}
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;
new->pass_fd = -1;
if (mgmt_fe_client) {
if (!mgmt_client_id_next)
mgmt_client_id_next++;
new->mgmt_client_id = mgmt_client_id_next++;
new->mgmt_session_id = 0;
mgmt_fe_create_client_session(
mgmt_fe_client, new->mgmt_client_id, (uintptr_t) new);
/* we short-circuit create the session so it must be set now */
assertf(new->mgmt_session_id != 0,
"Failed to create client session for VTY");
}
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;
vty->status = VTY_NORMAL;
vty->lines = -1;
vty->iac = 0;
vty->iac_sb_in_progress = 0;
vty->sb_len = 0;
vtys_add_tail(vty_sessions, vty);
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];
2002-12-13 21:15:29 +01:00
struct vty *vty;
sockunion2str(su, buf, SU_ADDRSTRLEN);
2002-12-13 21:15:29 +01:00
/* 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));
2002-12-13 21:15:29 +01:00
if (no_password_check) {
if (host.advanced)
vty->node = ENABLE_NODE;
2002-12-13 21:15:29 +01:00
else
vty->node = VIEW_NODE;
2002-12-13 21:15:29 +01:00
}
if (host.lines >= 0)
vty->lines = host.lines;
2002-12-13 21:15:29 +01:00
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;
}
}
2002-12-13 21:15:29 +01:00
/* Say hello to the world. */
vty_hello(vty);
if (!no_password_check)
vty_out(vty, "\nUser Access Verification\n\n");
2002-12-13 21:15:29 +01:00
/* Setting up terminal. */
vty_will_echo(vty);
vty_will_suppress_go_ahead(vty);
2002-12-13 21:15:29 +01:00
vty_dont_linemode(vty);
vty_do_window_size(vty);
/* vty_dont_lflow_ahead (vty); */
2002-12-13 21:15:29 +01:00
vty_prompt(vty);
2002-12-13 21:15:29 +01:00
/* Add read/write thread. */
vty_event(VTY_WRITE, vty);
vty_event(VTY_READ, vty);
2002-12-13 21:15:29 +01:00
return vty;
}
/* 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;
EVENT_OFF(stdio_vty->t_write);
EVENT_OFF(stdio_vty->t_read);
EVENT_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_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, stdio_vty);
vty_event(VTY_READ, 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 void vty_accept(struct event *thread)
2002-12-13 21:15:29 +01:00
{
struct vty_serv *vtyserv = EVENT_ARG(thread);
2002-12-13 21:15:29 +01:00
int vty_sock;
union sockunion su;
int ret;
unsigned int on;
int accept_sock = vtyserv->sock;
struct prefix p;
2002-12-13 21:15:29 +01:00
struct access_list *acl = NULL;
2002-12-13 21:15:29 +01:00
/* We continue hearing vty socket. */
vty_event_serv(VTY_SERV, vtyserv);
2002-12-13 21:15:29 +01:00
memset(&su, 0, sizeof(union sockunion));
2002-12-13 21:15:29 +01:00
/* 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;
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
set_nonblocking(vty_sock);
set_cloexec(vty_sock);
if (!sockunion2hostprefix(&su, &p)) {
close(vty_sock);
zlog_info("Vty unable to convert prefix from sockunion %pSU",
&su);
return;
}
2002-12-13 21:15:29 +01:00
/* VTY's accesslist apply. */
if (p.family == AF_INET && vty_accesslist_name) {
2002-12-13 21:15:29 +01:00
if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
&& (access_list_apply(acl, &p) == FILTER_DENY)) {
zlog_info("Vty connection refused from %pSU", &su);
close(vty_sock);
return;
}
}
2002-12-13 21:15:29 +01:00
/* 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 %pSU", &su);
close(vty_sock);
return;
}
2002-12-13 21:15:29 +01:00
}
on = 1;
ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on,
2002-12-13 21:15:29 +01:00
sizeof(on));
if (ret < 0)
zlog_info("can't set sockopt to vty_sock : %s",
safe_strerror(errno));
zlog_info("Vty connection from %pSU", &su);
vty_create(vty_sock, &su);
}
2002-12-13 21:15:29 +01:00
static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
{
int ret;
2002-12-13 21:15:29 +01:00
struct addrinfo req;
struct addrinfo *ainfo;
struct addrinfo *ainfo_save;
2002-12-13 21:15:29 +01:00
int sock;
char port_str[BUFSIZ];
memset(&req, 0, sizeof(req));
req.ai_flags = AI_PASSIVE;
2002-12-13 21:15:29 +01:00
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);
2002-12-13 21:15:29 +01:00
if (ret != 0) {
flog_err_sys(EC_LIB_SYSTEM_CALL, "getaddrinfo failed: %s",
gai_strerror(ret));
exit(1);
}
2002-12-13 21:15:29 +01:00
ainfo_save = ainfo;
do {
struct vty_serv *vtyserv;
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);
2002-12-13 21:15:29 +01:00
ret = bind(sock, ainfo->ai_addr, ainfo->ai_addrlen);
if (ret < 0) {
close(sock); /* Avoid sd leak. */
continue;
}
2002-12-13 21:15:29 +01:00
ret = listen(sock, 3);
if (ret < 0) {
close(sock); /* Avoid sd leak. */
continue;
}
vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv));
vtyserv->sock = sock;
vtyservs_add_tail(vty_servs, vtyserv);
vty_event_serv(VTY_SERV, vtyserv);
2002-12-13 21:15:29 +01:00
} while ((ainfo = ainfo->ai_next) != NULL);
2002-12-13 21:15:29 +01:00
freeaddrinfo(ainfo_save);
}
#ifdef VTYSH
/* For sockaddr_un. */
#include <sys/un.h>
/* VTY shell UNIX domain socket. */
2004-10-07 21:33:46 +02:00
static void vty_serv_un(const char *path)
2002-12-13 21:15:29 +01:00
{
struct vty_serv *vtyserv;
2002-12-13 21:15:29 +01:00
int ret;
int sock, len;
2002-12-13 21:15:29 +01:00
struct sockaddr_un serv;
mode_t old_mask;
struct zprivs_ids_t ids;
2002-12-13 21:15:29 +01:00
/* First of all, unlink existing socket */
unlink(path);
2002-12-13 21:15:29 +01:00
/* Set umask */
2003-05-23 10:12:36 +02:00
old_mask = umask(0007);
2002-12-13 21:15:29 +01:00
/* 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));
2002-12-13 21:15:29 +01:00
return;
}
2002-12-13 21:15:29 +01:00
/* Make server socket. */
memset(&serv, 0, sizeof(serv));
2002-12-13 21:15:29 +01:00
serv.sun_family = AF_UNIX;
strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
2002-12-13 21:15:29 +01:00
len = serv.sun_len = SUN_LEN(&serv);
#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);
2002-12-13 21:15:29 +01:00
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. */
2002-12-13 21:15:29 +01:00
return;
}
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. */
2002-12-13 21:15:29 +01:00
return;
}
umask(old_mask);
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));
}
}
vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv));
vtyserv->sock = sock;
vtyserv->vtysh = true;
vtyservs_add_tail(vty_servs, vtyserv);
vty_event_serv(VTYSH_SERV, vtyserv);
2002-12-13 21:15:29 +01:00
}
/* #define VTYSH_DEBUG 1 */
static void vtysh_accept(struct event *thread)
2002-12-13 21:15:29 +01:00
{
struct vty_serv *vtyserv = EVENT_ARG(thread);
int accept_sock = vtyserv->sock;
2002-12-13 21:15:29 +01:00
int sock;
int client_len;
struct sockaddr_un client;
struct vty *vty;
vty_event_serv(VTYSH_SERV, vtyserv);
2002-12-13 21:15:29 +01:00
memset(&client, 0, sizeof(client));
2002-12-13 21:15:29 +01:00
client_len = sizeof(struct sockaddr_un);
2004-09-26 18:08:11 +02: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;
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
if (set_nonblocking(sock) < 0) {
flog_err(
EC_LIB_SOCKET,
"vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
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
sock, safe_strerror(errno));
close(sock);
return;
}
set_cloexec(sock);
2002-12-13 21:15:29 +01:00
#ifdef VTYSH_DEBUG
printf("VTY shell accept\n");
#endif /* VTYSH_DEBUG */
vty = vty_new();
vty->fd = sock;
vty->wfd = sock;
2002-12-13 21:15:29 +01:00
vty->type = VTY_SHELL_SERV;
vty->node = VIEW_NODE;
vtys_add_tail(vtysh_sessions, vty);
vty_event(VTYSH_READ, vty);
2002-12-13 21:15:29 +01:00
}
static int vtysh_do_pass_fd(struct vty *vty)
{
struct iovec iov[1] = {
{
.iov_base = vty->pass_fd_status,
.iov_len = sizeof(vty->pass_fd_status),
},
};
union {
uint8_t buf[CMSG_SPACE(sizeof(int))];
struct cmsghdr align;
} u;
struct msghdr mh = {
.msg_iov = iov,
.msg_iovlen = array_size(iov),
.msg_control = u.buf,
.msg_controllen = sizeof(u.buf),
};
struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh);
ssize_t ret;
memset(&u.buf, 0, sizeof(u.buf));
cmh->cmsg_level = SOL_SOCKET;
cmh->cmsg_type = SCM_RIGHTS;
cmh->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int));
ret = sendmsg(vty->wfd, &mh, 0);
if (ret < 0 && ERRNO_IO_RETRY(errno))
return BUFFER_PENDING;
close(vty->pass_fd);
vty->pass_fd = -1;
vty->status = VTY_NORMAL;
if (ret <= 0)
return BUFFER_ERROR;
/* resume accepting commands (suspended in vtysh_read) */
vty_event(VTYSH_READ, vty);
if ((size_t)ret < sizeof(vty->pass_fd_status)) {
size_t remains = sizeof(vty->pass_fd_status) - ret;
buffer_put(vty->obuf, vty->pass_fd_status + ret, remains);
return BUFFER_PENDING;
}
return BUFFER_EMPTY;
}
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 int vtysh_flush(struct vty *vty)
{
int ret;
ret = buffer_flush_available(vty->obuf, vty->wfd);
if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD)
ret = vtysh_do_pass_fd(vty);
switch (ret) {
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
case BUFFER_PENDING:
vty_event(VTYSH_WRITE, vty);
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
break;
case BUFFER_ERROR:
flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
__func__, vty->fd);
buffer_reset(vty->lbuf);
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
buffer_reset(vty->obuf);
vty_close(vty);
return -1;
case BUFFER_EMPTY:
break;
}
return 0;
}
void vty_pass_fd(struct vty *vty, int fd)
{
if (vty->pass_fd != -1)
close(vty->pass_fd);
vty->pass_fd = fd;
}
bool mgmt_vty_read_configs(void)
{
char path[PATH_MAX];
struct vty *vty;
FILE *confp;
uint line_num = 0;
uint count = 0;
uint index;
vty = vty_new();
vty->wfd = STDERR_FILENO;
vty->type = VTY_FILE;
vty->node = CONFIG_NODE;
vty->config = true;
vty->pending_allowed = true;
vty->candidate_config = vty_shared_candidate_config;
vty_mgmt_lock_candidate_inline(vty);
vty_mgmt_lock_running_inline(vty);
for (index = 0; index < array_size(mgmt_daemons); index++) {
snprintf(path, sizeof(path), "%s/%s.conf", frr_sysconfdir,
mgmt_daemons[index]);
confp = vty_open_config(path, config_default);
if (!confp)
continue;
zlog_info("mgmtd: reading config file: %s", path);
/* Execute configuration file */
line_num = 0;
(void)config_from_file(vty, confp, &line_num);
count++;
fclose(confp);
}
snprintf(path, sizeof(path), "%s/mgmtd.conf", frr_sysconfdir);
confp = vty_open_config(path, config_default);
if (!confp) {
char *orig;
snprintf(path, sizeof(path), "%s/zebra.conf", frr_sysconfdir);
orig = XSTRDUP(MTYPE_TMP, host_config_get());
zlog_info("mgmtd: trying backup config file: %s", path);
confp = vty_open_config(path, config_default);
host_config_set(path);
XFREE(MTYPE_TMP, orig);
}
if (confp) {
zlog_info("mgmtd: reading config file: %s", path);
line_num = 0;
(void)config_from_file(vty, confp, &line_num);
count++;
fclose(confp);
}
/* Conditionally unlock as the config file may have "exit"d early which
* would then have unlocked things.
*/
if (vty->mgmt_locked_running_ds)
vty_mgmt_unlock_running_inline(vty);
if (vty->mgmt_locked_candidate_ds)
vty_mgmt_unlock_candidate_inline(vty);
vty->pending_allowed = false;
if (!count)
vty_close(vty);
else
vty_read_file_finish(vty, NULL);
zlog_info("mgmtd: finished reading config files");
return true;
}
static void vtysh_read(struct event *thread)
2002-12-13 21:15:29 +01:00
{
int ret;
int sock;
int nbytes;
struct vty *vty;
unsigned char buf[VTY_READ_BUFSIZ];
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
unsigned char *p;
uint8_t header[4] = {0, 0, 0, 0};
sock = EVENT_FD(thread);
vty = EVENT_ARG(thread);
/*
* This code looks like it can read multiple commands from the `buf`
* value returned by read(); however, it cannot in some cases.
*
* There are multiple paths out of the "copying to vty->buf" loop, which
* lose any content not yet copied from the stack `buf`, `passfd`,
* `CMD_SUSPEND` and finally if a front-end for mgmtd (generally this
* would be mgmtd itself). So these code paths are counting on vtysh not
* sending us more than 1 command line before waiting on the reply to
* that command.
*/
assert(vty->type == VTY_SHELL_SERV);
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 ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
if (nbytes < 0) {
if (ERRNO_IO_RETRY(errno)) {
vty_event(VTYSH_READ, vty);
return;
}
flog_err(
EC_LIB_SOCKET,
"%s: read failed on vtysh client fd %d, closing: %s",
__func__, sock, safe_strerror(errno));
}
buffer_reset(vty->lbuf);
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
buffer_reset(vty->obuf);
2002-12-13 21:15:29 +01:00
vty_close(vty);
#ifdef VTYSH_DEBUG
printf("close vtysh\n");
#endif /* VTYSH_DEBUG */
return;
2002-12-13 21:15:29 +01:00
}
#ifdef VTYSH_DEBUG
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
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 */
if (vty->pass_fd != -1) {
memset(vty->pass_fd_status, 0, 4);
vty->pass_fd_status[3] = ret;
vty->status = VTY_PASSFD;
if (!vty->t_write)
vty_event(VTYSH_WRITE, vty);
/* this introduces a "sequence point"
* command output is written normally,
* read processing is suspended until
* buffer is empty
* then retcode + FD is written
* then normal processing resumes
*
* => skip vty_event(VTYSH_READ, vty)!
*/
return;
}
/* hack for asynchronous "write integrated"
* - other commands in "buf" will be ditched
* - input during pending config-write is
* "unsupported" */
if (ret == CMD_SUSPEND)
break;
/* with new infra we need to stop response till
* we get response through callback.
*/
if (vty->mgmt_req_pending_cmd) {
MGMTD_FE_CLIENT_DBG(
"postpone CLI response pending mgmtd %s on vty session-id %" PRIu64,
vty->mgmt_req_pending_cmd,
vty->mgmt_session_id);
return;
}
/* 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;
}
}
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
}
2002-12-13 21:15:29 +01:00
if (vty->status == VTY_CLOSE)
vty_close(vty);
else
vty_event(VTYSH_READ, vty);
2002-12-13 21:15:29 +01:00
}
static void vtysh_write(struct event *thread)
{
struct vty *vty = EVENT_ARG(thread);
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
vtysh_flush(vty);
}
2002-12-13 21:15:29 +01:00
#endif /* VTYSH */
/* Determine address family to bind. */
void vty_serv_start(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);
#endif /* VTYSH */
}
void vty_serv_stop(void)
{
struct vty_serv *vtyserv;
while ((vtyserv = vtyservs_pop(vty_servs))) {
EVENT_OFF(vtyserv->t_accept);
close(vtyserv->sock);
XFREE(MTYPE_VTY_SERV, vtyserv);
}
vtyservs_fini(vty_servs);
vtyservs_init(vty_servs);
}
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). */
2002-12-13 21:15:29 +01:00
void vty_close(struct vty *vty)
{
int i;
bool was_stdio = false;
2002-12-13 21:15:29 +01:00
vty->status = VTY_CLOSE;
/*
* If we reach here with pending config to commit we will be losing it
* so warn the user.
*/
if (vty->mgmt_num_pending_setcfg)
MGMTD_FE_CLIENT_ERR(
"vty closed, uncommitted config will be lost.");
/* Drop out of configure / transaction if needed. */
vty_config_exit(vty);
if (mgmt_fe_client && vty->mgmt_session_id) {
MGMTD_FE_CLIENT_DBG("closing vty session");
mgmt_fe_destroy_client_session(mgmt_fe_client,
vty->mgmt_client_id);
vty->mgmt_session_id = 0;
}
2002-12-13 21:15:29 +01:00
/* Cancel threads.*/
EVENT_OFF(vty->t_read);
EVENT_OFF(vty->t_write);
EVENT_OFF(vty->t_timeout);
2002-12-13 21:15:29 +01:00
if (vty->pass_fd != -1) {
close(vty->pass_fd);
vty->pass_fd = -1;
}
zlog_live_close(&vty->live_log);
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) {
if (vty->type == VTY_SHELL_SERV)
vtys_del(vtysh_sessions, vty);
else if (vty->type == VTY_TERM)
vtys_del(vty_sessions, vty);
}
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_TMP, vty->pending_cmds_buf);
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);
}
2002-12-13 21:15:29 +01:00
/* 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 void vty_timeout(struct event *thread)
2002-12-13 21:15:29 +01:00
{
struct vty *vty;
vty = EVENT_ARG(thread);
2002-12-13 21:15:29 +01:00
vty->v_timeout = 0;
/* Clear buffer*/
buffer_reset(vty->lbuf);
2002-12-13 21:15:29 +01:00
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);
}
/* Read up configuration file from file_name. */
void vty_read_file(struct nb_config *config, FILE *confp)
2002-12-13 21:15:29 +01:00
{
struct vty *vty;
unsigned int line_num = 0;
2002-12-13 21:15:29 +01:00
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 better 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;
2002-12-13 21:15:29 +01:00
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);
}
2002-12-13 21:15:29 +01:00
/* Execute configuration file */
(void)config_from_file(vty, confp, &line_num);
vty_read_file_finish(vty, config);
}
void vty_read_file_finish(struct vty *vty, struct nb_config *config)
{
struct vty_error *ve;
struct listnode *node;
/* Flush any previous errors before printing messages below */
buffer_flush_all(vty->obuf, vty->wfd);
for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
const char *message = NULL;
char *nl;
switch (ve->cmd_ret) {
case CMD_SUCCESS:
message = "Command succeeded";
break;
case CMD_ERR_NOTHING_TODO:
message = "Nothing to do";
break;
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;
}
nl = strchr(ve->error_buf, '\n');
if (nl)
*nl = '\0';
flog_err(EC_LIB_VTY, "%s on config line %u: %s", message,
ve->line_num, ve->error_buf);
2002-12-13 21:15:29 +01:00
}
/*
* Automatically commit the candidate configuration after
* reading the configuration file.
*/
if (config == NULL) {
struct nb_context context = {};
char errmsg[BUFSIZ] = {0};
int ret;
context.client = NB_CLIENT_CLI;
context.user = vty;
ret = nb_candidate_commit(context, vty->candidate_config, true,
"Read configuration file", NULL,
errmsg, sizeof(errmsg));
if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
zlog_err(
"%s: failed to read configuration file: %s (%s)",
__func__, nb_err_name(ret), errmsg);
}
2002-12-13 21:15:29 +01:00
vty_close(vty);
}
static FILE *vty_use_backup_config(const char *fullpath)
2002-12-13 21:15:29 +01:00
{
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) {
2002-12-13 21:15:29 +01:00
free(fullpath_sav);
return NULL;
}
2002-12-13 21:15:29 +01:00
fullpath_tmp = malloc(strlen(fullpath) + 8);
snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath);
2002-12-13 21:15:29 +01:00
/* 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;
2002-12-13 21:15:29 +01:00
while ((c = read(sav, buffer, 512)) > 0) {
if (write(tmp, buffer, c) <= 0)
goto out_close;
}
2002-12-13 21:15:29 +01:00
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);
}
2002-12-13 21:15:29 +01:00
free(fullpath_sav);
free(fullpath_tmp);
return ret;
2002-12-13 21:15:29 +01:00
}
FILE *vty_open_config(const char *config_file, char *config_default_dir)
2002-12-13 21:15:29 +01:00
{
char cwd[MAXPATHLEN];
2002-12-13 21:15:29 +01:00
FILE *confp = NULL;
const char *fullpath;
char *tmp = NULL;
2002-12-13 21:15:29 +01:00
/* 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;
}
size_t tmp_len = strlen(cwd) + strlen(config_file) + 2;
tmp = XMALLOC(MTYPE_TMP, tmp_len);
snprintf(tmp, tmp_len, "%s/%s", cwd, config_file);
fullpath = tmp;
2002-12-13 21:15:29 +01:00
} else
fullpath = config_file;
2002-12-13 21:15:29 +01:00
confp = fopen(fullpath, "r");
2002-12-13 21:15:29 +01:00
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,
"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;
}
2002-12-13 21:15:29 +01:00
}
} 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)
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,
"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;
}
2002-12-13 21:15:29 +01:00
host_config_set(fullpath);
tmp_free_and_out:
XFREE(MTYPE_TMP, tmp);
return confp;
2002-12-13 21:15:29 +01:00
}
bool vty_read_config(struct nb_config *config, const char *config_file,
char *config_default_dir)
{
FILE *confp;
confp = vty_open_config(config_file, config_default_dir);
if (!confp)
return false;
vty_read_file(config, confp);
fclose(confp);
return true;
}
int vty_config_enter(struct vty *vty, bool private_config, bool exclusive,
bool file_lock)
2002-12-13 21:15:29 +01:00
{
if (exclusive && !vty_mgmt_fe_enabled() &&
nb_running_lock(NB_CLIENT_CLI, vty)) {
vty_out(vty, "%% Configuration is locked by other client\n");
return CMD_WARNING;
2002-12-13 21:15:29 +01:00
}
/*
* We only need to do a lock when reading a config file as we will be
* sending a batch of setcfg changes followed by a single commit
* message. For user interactive mode we are doing implicit commits
* those will obtain the lock (or not) when they try and commit.
*/
if (file_lock && vty_mgmt_fe_enabled() && !private_config) {
if (vty_mgmt_lock_candidate_inline(vty)) {
vty_out(vty,
"%% Can't enter config; candidate datastore locked by another session\n");
return CMD_WARNING_CONFIG_FAILED;
}
if (vty_mgmt_lock_running_inline(vty)) {
vty_out(vty,
"%% Can't enter config; running datastore locked by another session\n");
vty_mgmt_unlock_candidate_inline(vty);
return CMD_WARNING_CONFIG_FAILED;
}
assert(vty->mgmt_locked_candidate_ds);
assert(vty->mgmt_locked_running_ds);
}
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");
return CMD_SUCCESS;
}
/*
* NOTE: On the MGMTD daemon we point the VTY candidate DS to
* the global MGMTD candidate DS. Else we point to the VTY
* Shared Candidate Config.
*/
vty->candidate_config = vty_mgmt_candidate_config
? vty_mgmt_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;
/* TODO: could we check for un-commited changes here? */
if (vty->mgmt_locked_running_ds)
vty_mgmt_unlock_running_inline(vty);
if (vty->mgmt_locked_candidate_ds)
vty_mgmt_unlock_candidate_inline(vty);
northbound: KISS always batch yang config (file read), it's faster The backoff code assumed that yang operations always completed quickly. It checked for > 100 YANG modeled commands happening in under 1 second to enable batching. If 100 yang modeled commands always take longer than 1 second batching is never enabled. This is the exact opposite of what we want to happen since batching speeds the operations up. Here are the results for libyang2 code without and with batching. | action | 1K rts | 2K rts | 1K rts | 2K rts | 20k rts | | | nobatch | nobatch | batch | batch | batch | | Add IPv4 | .881 | 1.28 | .703 | 1.04 | 8.16 | | Add Same IPv4 | 28.7 | 113 | .590 | .860 | 6.09 | | Rem 1/2 IPv4 | .376 | .442 | .379 | .435 | 1.44 | | Add Same IPv4 | 28.7 | 113 | .576 | .841 | 6.02 | | Rem All IPv4 | 17.4 | 71.8 | .559 | .813 | 5.57 | (IPv6 numbers are basically the same as iPv4, a couple percent slower) Clearly we need this. Please note the growth (1K to 2K) w/o batching is non-linear and 100 times slower than batched. Notes on code: The use of the new `nb_cli_apply_changes_clear_pending` is to commit any pending changes (including the current one). This is done when the code would not correctly handle a single diff that included the current changes with possible following changes. For example, a "no" command followed by a new value to replace it would be merged into a change, and the code would not deal well with that. A good example of this is BGP neighbor peer-group changing. The other use is after entering a router level (e.g., "router bgp") where the follow-on command handlers expect that router object to now exists. The code eventually needs to be cleaned up to not fail in these cases, but that is for future NB cleanup. Signed-off-by: Christian Hopps <chopps@labn.net>
2021-05-28 21:16:18 +02:00
/* Perform any pending commits. */
(void)nb_cli_pending_commit_check(vty);
lib: introduce configuration back-off timer for YANG-modeled commands When using the default CLI mode, the northbound layer needs to create a separate transaction to process each YANG-modeled command since they are supposed to be applied immediately (there's no candidate configuration nor the "commit" command like in the transactional CLI). The problem is that configuration transactions have an overhead associated to them, in big part because of the use of some heavy libyang functions like `lyd_validate()` and `lyd_diff()`. As of now this overhead is substantial and doesn't scale well when large numbers of transactions need to be performed in sequence. As an example, loading 50k prefix-lists using a single transaction takes about 2 seconds on a modern CPU. Loading the same 50k prefix-lists using 50k transactions can take more than an hour to complete (which is unacceptable by any standard). To fix this problem, some heavy optimization work needs to be done on libyang and on the FRR northbound itself too (e.g. perform partial configuration diffs whenever possible). This, however, should be a long term effort since these optimizations shouldn't be trivial to implement and we're far from having the performance numbers we need. In the meanwhile, this commit introduces a simple but efficient workaround to alleviate the issue. In short, a new back-off timer was introduced in the CLI to monitor and detect when too many YANG-modeled commands are being received at the same time. When a certain threshold is reached (100 YANG-modeled commands within one second), the northbound starts to group all subsequent commands into a single large transaction, which allows them to be processed much faster (e.g. seconds and not hours). It's essentially a protection mechanism that creates dynamically-sized transactions when necessary to prevent performance issues from happening. This mechanism is enabled both when parsing configuration files and when reading commands from a terminal. The downside of this optimization is that, if several YANG-modeled commands are grouped into the same transaction and at least one of them fails, the whole transaction is rejected. This is undesirable since users don't expect transactional behavior when that's not enabled explicitly. To minimize this issue, the CLI will log all commands that were rejected whenever that happens, to make the user aware of what happened and have enough information to fix the problem. Commands that fail due to parsing errors or CLI-level validations in general are rejected separately. Again, this proposed workaround is intended to be temporary. The goal is to provided a quick fix to issues like #6658 while we work on better long-term solutions. Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
2020-07-02 19:43:36 +02:00
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,
"exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
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
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;
/*
* If this is a config file and we are dropping out of config end
* parsing.
*/
if (vty->type == VTY_FILE && vty->status != VTY_CLOSE) {
vty_out(vty, "exit from config node while reading config file");
vty->status = VTY_CLOSE;
}
return 1;
}
2002-12-13 21:15:29 +01:00
/* Master of the threads. */
static struct event_loop *vty_master;
2002-12-13 21:15:29 +01:00
static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv)
2002-12-13 21:15:29 +01:00
{
switch (event) {
case VTY_SERV:
event_add_read(vty_master, vty_accept, vty_serv, vty_serv->sock,
&vty_serv->t_accept);
2002-12-13 21:15:29 +01:00
break;
#ifdef VTYSH
case VTYSH_SERV:
event_add_read(vty_master, vtysh_accept, vty_serv,
vty_serv->sock, &vty_serv->t_accept);
2002-12-13 21:15:29 +01:00
break;
#endif /* VTYSH */
case VTY_READ:
case VTY_WRITE:
case VTY_TIMEOUT_RESET:
case VTYSH_READ:
case VTYSH_WRITE:
assert(!"vty_event_serv() called incorrectly");
}
}
static void vty_event(enum vty_event event, struct vty *vty)
{
switch (event) {
#ifdef VTYSH
2002-12-13 21:15:29 +01:00
case VTYSH_READ:
event_add_read(vty_master, vtysh_read, vty, vty->fd,
&vty->t_read);
break;
case VTYSH_WRITE:
event_add_write(vty_master, vtysh_write, vty, vty->wfd,
&vty->t_write);
2002-12-13 21:15:29 +01:00
break;
#endif /* VTYSH */
case VTY_READ:
event_add_read(vty_master, vty_read, vty, vty->fd,
&vty->t_read);
2002-12-13 21:15:29 +01:00
/* Time out treatment. */
if (vty->v_timeout) {
EVENT_OFF(vty->t_timeout);
event_add_timer(vty_master, vty_timeout, vty,
vty->v_timeout, &vty->t_timeout);
}
2002-12-13 21:15:29 +01:00
break;
case VTY_WRITE:
event_add_write(vty_master, vty_flush, vty, vty->wfd,
&vty->t_write);
2002-12-13 21:15:29 +01:00
break;
case VTY_TIMEOUT_RESET:
EVENT_OFF(vty->t_timeout);
if (vty->v_timeout)
event_add_timer(vty_master, vty_timeout, vty,
vty->v_timeout, &vty->t_timeout);
2002-12-13 21:15:29 +01:00
break;
case VTY_SERV:
case VTYSH_SERV:
assert(!"vty_event() called incorrectly");
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")
{
struct vty *v;
frr_each (vtys, vty_sessions, v)
vty_out(vty, "%svty[%d] connected from %s%s.\n",
v->config ? "*" : " ", v->fd, v->address,
zlog_live_is_null(&v->live_log) ? "" : ", live log");
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
/* 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;
}
/* 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;
/* 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);
vty_timeout_val = timeout;
vty->v_timeout = timeout;
vty_event(VTY_TIMEOUT_RESET, vty);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
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);
}
/* 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;
2002-12-13 21:15:29 +01:00
if (vty_accesslist_name)
XFREE(MTYPE_VTY, vty_accesslist_name);
vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
/* 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);
vty_accesslist_name = NULL;
return CMD_SUCCESS;
}
/* 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;
2002-12-13 21:15:29 +01:00
if (vty_ipv6_accesslist_name)
XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
/* 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;
2002-12-13 21:15:29 +01:00
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);
vty_ipv6_accesslist_name = NULL;
return CMD_SUCCESS;
}
/* vty login. */
DEFUN (vty_login,
vty_login_cmd,
"login",
"Enable password checking\n")
{
no_password_check = 0;
return CMD_SUCCESS;
}
DEFUN (no_vty_login,
no_vty_login_cmd,
"no login",
NO_STR
"Enable password checking\n")
{
no_password_check = 1;
return CMD_SUCCESS;
}
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;
}
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;
}
DEFUN_NOSH(terminal_monitor,
terminal_monitor_cmd,
"terminal monitor [detach]",
"Set terminal line parameters\n"
"Copy debug output to the current terminal line\n"
"Keep logging feed open independent of VTY session\n")
2002-12-13 21:15:29 +01:00
{
int fd_ret = -1;
if (vty->type != VTY_SHELL_SERV) {
vty_out(vty, "%% not supported\n");
return CMD_WARNING;
}
if (argc == 3) {
struct zlog_live_cfg detach_log = {};
zlog_live_open(&detach_log, LOG_DEBUG, &fd_ret);
zlog_live_disown(&detach_log);
} else
zlog_live_open(&vty->live_log, LOG_DEBUG, &fd_ret);
if (fd_ret == -1) {
vty_out(vty, "%% error opening live log: %m\n");
return CMD_WARNING;
}
vty_pass_fd(vty, fd_ret);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
DEFUN_NOSH(no_terminal_monitor,
no_terminal_monitor_cmd,
"no terminal monitor",
NO_STR
"Set terminal line parameters\n"
"Copy debug output to the current terminal line\n")
2002-12-13 21:15:29 +01:00
{
zlog_live_close(&vty->live_log);
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
DEFUN_NOSH(terminal_no_monitor,
terminal_no_monitor_cmd,
"terminal no monitor",
"Set terminal line parameters\n"
NO_STR
"Copy debug output to the current terminal line\n")
{
return no_terminal_monitor(self, vty, argc, argv);
}
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;
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++;
}
return CMD_SUCCESS;
}
/* vty login. */
DEFPY (log_commands,
log_commands_cmd,
"[no] log commands",
NO_STR
"Logging control\n"
"Log all commands\n")
{
if (no) {
if (vty_log_commands_perm) {
vty_out(vty,
"Daemon started with permanent logging turned on for commands, ignoring\n");
return CMD_WARNING;
}
vty_log_commands = false;
} else
vty_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)
{
vty_frame(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");
vty_endframe(vty, "exit\n");
if (vty_log_commands)
vty_out(vty, "log commands\n");
vty_out(vty, "!\n");
2002-12-13 21:15:29 +01:00
return CMD_SUCCESS;
}
static int vty_config_write(struct vty *vty);
2002-12-13 21:15:29 +01:00
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)
2002-12-13 21:15:29 +01:00
{
struct vty *vty;
frr_each_safe (vtys, vty_sessions, vty) {
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty->status = VTY_CLOSE;
vty_close(vty);
}
2002-12-13 21:15:29 +01:00
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
}
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 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;
}
int vty_shell(struct vty *vty)
{
return vty->type == VTY_SHELL ? 1 : 0;
}
int vty_shell_serv(struct vty *vty)
{
return vty->type == VTY_SHELL_SERV ? 1 : 0;
}
void vty_init_vtysh(void)
2002-12-13 21:15:29 +01:00
{
/* currently nothing to do, but likely to have future use */
2002-12-13 21:15:29 +01:00
}
/*
* These functions allow for CLI handling to be placed inside daemons; however,
* currently they are only used by mgmtd, with mgmtd having each daemons CLI
* functionality linked into it. This design choice was taken for efficiency.
*/
static void vty_mgmt_server_connected(struct mgmt_fe_client *client,
uintptr_t usr_data, bool connected)
{
MGMTD_FE_CLIENT_DBG("Got %sconnected %s MGMTD Frontend Server",
!connected ? "dis: " : "",
!connected ? "from" : "to");
/*
* We should not have any sessions for connecting or disconnecting case.
* The fe client library will delete all session on disconnect before
* calling us.
*/
assert(mgmt_fe_client_session_count(client) == 0);
mgmt_fe_connected = connected;
/* Start or stop listening for vty connections */
if (connected)
frr_vty_serv_start();
else
frr_vty_serv_stop();
}
/*
* A session has successfully been created for a vty.
*/
static void vty_mgmt_session_notify(struct mgmt_fe_client *client,
uintptr_t usr_data, uint64_t client_id,
bool create, bool success,
uintptr_t session_id, uintptr_t session_ctx)
{
struct vty *vty;
vty = (struct vty *)session_ctx;
if (!success) {
zlog_err("%s session for client %" PRIu64 " failed!",
create ? "Creating" : "Destroying", client_id);
return;
}
MGMTD_FE_CLIENT_DBG("%s session for client %" PRIu64 " successfully",
create ? "Created" : "Destroyed", client_id);
if (create) {
assert(session_id != 0);
vty->mgmt_session_id = session_id;
} else {
vty->mgmt_session_id = 0;
/* We may come here by way of vty_close() and short-circuits */
if (vty->status != VTY_CLOSE)
vty_close(vty);
}
}
static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client,
uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id,
uintptr_t session_ctx, uint64_t req_id,
bool lock_ds, bool success,
Mgmtd__DatastoreId ds_id,
char *errmsg_if_any)
{
struct vty *vty;
vty = (struct vty *)session_ctx;
assert(ds_id == MGMTD_DS_CANDIDATE || ds_id == MGMTD_DS_RUNNING);
if (!success)
zlog_err("%socking for DS %u failed, Err: '%s' vty %p",
lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty);
else {
MGMTD_FE_CLIENT_DBG("%socked DS %u successfully",
lock_ds ? "L" : "Unl", ds_id);
if (ds_id == MGMTD_DS_CANDIDATE)
vty->mgmt_locked_candidate_ds = lock_ds;
else
vty->mgmt_locked_running_ds = lock_ds;
}
if (vty->mgmt_req_pending_cmd)
vty_mgmt_resume_response(vty, success);
}
static void vty_mgmt_set_config_result_notified(
struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
bool success, Mgmtd__DatastoreId ds_id, bool implicit_commit,
char *errmsg_if_any)
{
struct vty *vty;
vty = (struct vty *)session_ctx;
if (!success) {
zlog_err("SET_CONFIG request for client 0x%" PRIx64
" failed, Error: '%s'",
client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
} else {
MGMTD_FE_CLIENT_DBG("SET_CONFIG request for client 0x%" PRIx64
" req-id %" PRIu64 " was successfull",
client_id, req_id);
}
if (implicit_commit) {
/* In this case the changes have been applied, we are done */
vty_mgmt_unlock_candidate_inline(vty);
vty_mgmt_unlock_running_inline(vty);
}
vty_mgmt_resume_response(vty, success);
}
static void vty_mgmt_commit_config_result_notified(
struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
bool success, Mgmtd__DatastoreId src_ds_id,
Mgmtd__DatastoreId dst_ds_id, bool validate_only, char *errmsg_if_any)
{
struct vty *vty;
vty = (struct vty *)session_ctx;
if (!success) {
zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64
" failed, Error: '%s'",
client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
} else {
MGMTD_FE_CLIENT_DBG(
"COMMIT_CONFIG request for client 0x%" PRIx64
" req-id %" PRIu64 " was successfull",
client_id, req_id);
if (errmsg_if_any)
vty_out(vty, "MGMTD: %s\n", errmsg_if_any);
}
vty_mgmt_resume_response(vty, success);
}
static int vty_mgmt_get_data_result_notified(
struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id,
uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id,
bool success, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data,
size_t num_data, int next_key, char *errmsg_if_any)
{
struct vty *vty;
size_t indx;
vty = (struct vty *)session_ctx;
if (!success) {
zlog_err("GET_DATA request for client 0x%" PRIx64
" failed, Error: '%s'",
client_id, errmsg_if_any ? errmsg_if_any : "Unknown");
vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n",
errmsg_if_any ? errmsg_if_any : "Unknown");
vty_mgmt_resume_response(vty, success);
return -1;
}
MGMTD_FE_CLIENT_DBG("GET_DATA request succeeded, client 0x%" PRIx64
" req-id %" PRIu64,
client_id, req_id);
if (req_id != mgmt_last_req_id) {
mgmt_last_req_id = req_id;
vty_out(vty, "[\n");
}
for (indx = 0; indx < num_data; indx++) {
vty_out(vty, " \"%s\": \"%s\"\n", yang_data[indx]->xpath,
yang_data[indx]->value->encoded_str_val);
}
if (next_key < 0) {
vty_out(vty, "]\n");
vty_mgmt_resume_response(vty, success);
}
return 0;
}
static struct mgmt_fe_client_cbs mgmt_cbs = {
.client_connect_notify = vty_mgmt_server_connected,
.client_session_notify = vty_mgmt_session_notify,
.lock_ds_notify = vty_mgmt_ds_lock_notified,
.set_config_notify = vty_mgmt_set_config_result_notified,
.commit_config_notify = vty_mgmt_commit_config_result_notified,
.get_data_notify = vty_mgmt_get_data_result_notified,
};
void vty_init_mgmt_fe(void)
{
char name[40];
assert(vty_master);
assert(!mgmt_fe_client);
snprintf(name, sizeof(name), "vty-%s-%ld", frr_get_progname(),
(long)getpid());
mgmt_fe_client = mgmt_fe_client_create(name, &mgmt_cbs, 0, vty_master);
assert(mgmt_fe_client);
}
bool vty_mgmt_fe_enabled(void)
{
return mgmt_fe_client && mgmt_fe_connected;
}
bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty)
{
return vty->type != VTY_FILE && vty_mgmt_fe_enabled();
}
int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id,
bool lock, bool scok)
{
assert(mgmt_fe_client);
assert(vty->mgmt_session_id);
vty->mgmt_req_id++;
if (mgmt_fe_send_lockds_req(mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, ds_id, lock, scok)) {
zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64,
lock ? "" : "UN", vty->mgmt_req_id);
vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n",
lock ? "" : "UN");
return -1;
}
if (!scok)
vty->mgmt_req_pending_cmd = "MESSAGE_LOCKDS_REQ";
return 0;
}
int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit)
{
Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES];
Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES];
Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES];
Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0};
size_t indx;
if (vty->type == VTY_FILE) {
/*
* if this is a config file read we will not send any of the
* changes until we are done reading the file and have modified
* the local candidate DS.
*/
/* no-one else should be sending data right now */
assert(!vty->mgmt_num_pending_setcfg);
return 0;
}
/* If we are FE client and we have a vty then we have a session */
assert(mgmt_fe_client && vty->mgmt_client_id && vty->mgmt_session_id);
if (!vty->num_cfg_changes)
return 0;
/* grab the candidate and running lock prior to sending implicit commit
* command
*/
if (implicit_commit) {
if (vty_mgmt_lock_candidate_inline(vty)) {
vty_out(vty,
"%% command failed, could not lock candidate DS\n");
return -1;
} else if (vty_mgmt_lock_running_inline(vty)) {
vty_out(vty,
"%% command failed, could not lock running DS\n");
return -1;
}
}
for (indx = 0; indx < vty->num_cfg_changes; indx++) {
mgmt_yang_data_init(&cfg_data[indx]);
if (vty->cfg_changes[indx].value) {
mgmt_yang_data_value_init(&value[indx]);
value[indx].encoded_str_val =
(char *)vty->cfg_changes[indx].value;
value[indx].value_case =
MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
cfg_data[indx].value = &value[indx];
}
cfg_data[indx].xpath = vty->cfg_changes[indx].xpath;
mgmt_yang_cfg_data_req_init(&cfg_req[indx]);
cfg_req[indx].data = &cfg_data[indx];
switch (vty->cfg_changes[indx].operation) {
case NB_OP_DESTROY:
cfg_req[indx].req_type =
MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
break;
case NB_OP_CREATE:
case NB_OP_MODIFY:
case NB_OP_MOVE:
case NB_OP_PRE_VALIDATE:
case NB_OP_APPLY_FINISH:
cfg_req[indx].req_type =
MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
break;
case NB_OP_GET_ELEM:
case NB_OP_GET_NEXT:
case NB_OP_GET_KEYS:
case NB_OP_LOOKUP_ENTRY:
case NB_OP_RPC:
default:
assertf(false,
"Invalid operation type for send config: %d",
vty->cfg_changes[indx].operation);
/*NOTREACHED*/
abort();
}
cfgreq[indx] = &cfg_req[indx];
}
if (!indx)
return 0;
vty->mgmt_req_id++;
if (mgmt_fe_send_setcfg_req(mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, MGMTD_DS_CANDIDATE,
cfgreq, indx, implicit_commit,
MGMTD_DS_RUNNING)) {
zlog_err("Failed to send %zu config xpaths to mgmtd", indx);
vty_out(vty, "%% Failed to send commands to mgmtd\n");
return -1;
}
vty->mgmt_req_pending_cmd = "MESSAGE_SETCFG_REQ";
return 0;
}
int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort)
{
if (mgmt_fe_client && vty->mgmt_session_id) {
vty->mgmt_req_id++;
if (mgmt_fe_send_commitcfg_req(
mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, MGMTD_DS_CANDIDATE,
MGMTD_DS_RUNNING, validate_only, abort)) {
zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64,
vty->mgmt_req_id);
vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n");
return -1;
}
vty->mgmt_req_pending_cmd = "MESSAGE_COMMCFG_REQ";
vty->mgmt_num_pending_setcfg = 0;
}
return 0;
}
int vty_mgmt_send_get_config(struct vty *vty, Mgmtd__DatastoreId datastore,
const char **xpath_list, int num_req)
{
Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
int i;
vty->mgmt_req_id++;
for (i = 0; i < num_req; i++) {
mgmt_yang_get_data_req_init(&get_req[i]);
mgmt_yang_data_init(&yang_data[i]);
yang_data->xpath = (char *)xpath_list[i];
get_req[i].data = &yang_data[i];
getreq[i] = &get_req[i];
}
if (mgmt_fe_send_getcfg_req(mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, datastore, getreq,
num_req)) {
zlog_err(
"Failed to send GET-CONFIG to MGMTD for req-id %" PRIu64
".",
vty->mgmt_req_id);
vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n");
return -1;
}
vty->mgmt_req_pending_cmd = "MESSAGE_GETCFG_REQ";
return 0;
}
int vty_mgmt_send_get_data(struct vty *vty, Mgmtd__DatastoreId datastore,
const char **xpath_list, int num_req)
{
Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES];
Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES];
Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES];
int i;
vty->mgmt_req_id++;
for (i = 0; i < num_req; i++) {
mgmt_yang_get_data_req_init(&get_req[i]);
mgmt_yang_data_init(&yang_data[i]);
yang_data->xpath = (char *)xpath_list[i];
get_req[i].data = &yang_data[i];
getreq[i] = &get_req[i];
}
if (mgmt_fe_send_getdata_req(mgmt_fe_client, vty->mgmt_session_id,
vty->mgmt_req_id, datastore, getreq,
num_req)) {
zlog_err("Failed to send GET-DATA to MGMTD for req-id %" PRIu64
".",
vty->mgmt_req_id);
vty_out(vty, "Failed to send GET-DATA to MGMTD!\n");
return -1;
}
vty->mgmt_req_pending_cmd = "MESSAGE_GETDATA_REQ";
return 0;
}
2002-12-13 21:15:29 +01:00
/* Install vty's own commands like `who' command. */
void vty_init(struct event_loop *master_thread, bool do_command_logging)
2002-12-13 21:15:29 +01:00
{
/* For further configuration read, preserve current directory. */
vty_save_cwd();
vty_master = master_thread;
atexit(vty_stdio_atexit);
2002-12-13 21:15:29 +01:00
/* Install bgp top node. */
install_node(&vty_node);
2002-12-13 21:15:29 +01:00
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) {
vty_log_commands = true;
vty_log_commands_perm = true;
}
2002-12-13 21:15:29 +01:00
install_element(ENABLE_NODE, &terminal_monitor_cmd);
install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
2002-12-13 21:15:29 +01:00
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);
}
[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
void vty_terminate(void)
{
struct vty *vty;
if (mgmt_fe_client) {
mgmt_fe_client_destroy(mgmt_fe_client);
mgmt_fe_client = 0;
}
memset(vty_cwd, 0x00, sizeof(vty_cwd));
vty_reset();
/* default state of vty_sessions is initialized & empty. */
vtys_fini(vty_sessions);
vtys_init(vty_sessions);
/* vty_reset() doesn't close vtysh sessions */
frr_each_safe (vtys, vtysh_sessions, vty) {
buffer_reset(vty->lbuf);
buffer_reset(vty->obuf);
vty->status = VTY_CLOSE;
vty_close(vty);
}
vtys_fini(vtysh_sessions);
vtys_init(vtysh_sessions);
vty_serv_stop();
[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
}