diff options
Diffstat (limited to 'lib/vty.c')
| -rw-r--r-- | lib/vty.c | 309 |
1 files changed, 108 insertions, 201 deletions
@@ -48,6 +48,7 @@ #include "lib_errors.h" #include "northbound_cli.h" #include "printfrr.h" +#include "json.h" #include <arpa/telnet.h> #include <termios.h> @@ -57,9 +58,12 @@ #endif 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); + /* Vty events */ enum event { VTY_SERV, @@ -73,17 +77,31 @@ enum event { #endif /* VTYSH */ }; -static void vty_event_serv(enum event event, int sock); +PREDECL_DLIST(vtyservs); + +struct vty_serv { + struct vtyservs_item itm; + + int sock; + bool vtysh; + + struct thread *t_accept; +}; + +DECLARE_DLIST(vtyservs, struct vty_serv, itm); + +static void vty_event_serv(enum event event, struct vty_serv *); static void vty_event(enum event, struct vty *); /* Extern host structure from command.c */ extern struct host host; -/* Vector which store each vty structure. */ -static vector vtyvec; +/* active listeners */ +static struct vtyservs_head vty_servs[1] = {INIT_DLIST(vty_servs[0])}; -/* Vector for vtysh connections. */ -static vector vtyshvec; +/* 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])}; /* Vty timeout value. */ static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT; @@ -94,9 +112,6 @@ static char *vty_accesslist_name = NULL; /* Vty access-calss for IPv6. */ static char *vty_ipv6_accesslist_name = NULL; -/* VTY server thread. */ -static vector Vvty_serv_thread; - /* Current directory. */ static char vty_cwd[MAXPATHLEN]; @@ -266,70 +281,28 @@ done: return len; } -static int vty_log_out(struct vty *vty, const char *level, - const char *proto_str, const char *msg, - struct timestamp_control *ctl) +int vty_json(struct vty *vty, struct json_object *json) { - int ret; - int len; - char buf[1024]; + const char *text; - if (!ctl->already_rendered) { - ctl->len = quagga_timestamp(ctl->precision, ctl->buf, - sizeof(ctl->buf)); - ctl->already_rendered = 1; - } - if (ctl->len + 1 >= sizeof(buf)) - return -1; - memcpy(buf, ctl->buf, len = ctl->len); - buf[len++] = ' '; - buf[len] = '\0'; - - if (level) - ret = snprintf(buf + len, sizeof(buf) - len, "%s: %s: ", level, - proto_str); - else - ret = snprintf(buf + len, sizeof(buf) - len, "%s: ", proto_str); - if ((ret < 0) || ((size_t)(len += ret) >= sizeof(buf))) - return -1; + if (!json) + return CMD_SUCCESS; - if (((ret = snprintf(buf + len, sizeof(buf) - len, "%s", msg)) < 0) - || ((size_t)((len += ret) + 2) > sizeof(buf))) - return -1; + text = json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE); + vty_out(vty, "%s\n", text); + json_object_free(json); - buf[len++] = '\r'; - buf[len++] = '\n'; - - if (write(vty->wfd, buf, len) < 0) { - if (ERRNO_IO_RETRY(errno)) - /* Kernel buffer is full, probably too much debugging - output, so just - drop the data and ignore. */ - return -1; - /* Fatal I/O error. */ - vty->monitor = - 0; /* disable monitoring to avoid infinite recursion */ - flog_err(EC_LIB_SOCKET, - "%s: write failed to vty client fd %d, closing: %s", - __func__, vty->fd, safe_strerror(errno)); - buffer_reset(vty->obuf); - buffer_reset(vty->lbuf); - /* cannot call vty_close, because a parent routine may still try - to access the vty struct */ - vty->status = VTY_CLOSE; - shutdown(vty->fd, SHUT_RDWR); - return -1; - } - return 0; + return CMD_SUCCESS; } /* Output current time to the vty. */ void vty_time_print(struct vty *vty, int cr) { - char buf[QUAGGA_TIMESTAMP_LEN]; + char buf[FRR_TIMESTAMP_LEN]; - if (quagga_timestamp(0, buf, sizeof(buf)) == 0) { - zlog_info("quagga_timestamp error"); + if (frr_timestamp(0, buf, sizeof(buf)) == 0) { + zlog_info("frr_timestamp error"); return; } if (cr) @@ -480,19 +453,12 @@ static int vty_command(struct vty *vty, char *buf) cp++; } if (cp != NULL && *cp != '\0') { - unsigned i; char vty_str[VTY_BUFSIZ]; char prompt_str[VTY_BUFSIZ]; /* format the base vty info */ - snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address); - - for (i = 0; i < vector_active(vtyvec); i++) - if (vty == vector_slot(vtyvec, i)) { - snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", - i, vty->address); - break; - } + snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", vty->fd, + vty->address); /* format the prompt */ snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node), @@ -776,7 +742,7 @@ static void vty_end_config(struct vty *vty) vty->cp = 0; } -/* Delete a charcter at the current point. */ +/* Delete a character at the current point. */ static void vty_delete_char(struct vty *vty) { int i; @@ -1624,13 +1590,14 @@ static struct vty *vty_new_init(int vty_sock) memset(vty->xpath, 0, sizeof(vty->xpath)); vty->private_config = false; vty->candidate_config = vty_shared_candidate_config; - vector_set_index(vtyvec, vty_sock, vty); vty->status = VTY_NORMAL; vty->lines = -1; vty->iac = 0; vty->iac_sb_in_progress = 0; vty->sb_len = 0; + vtys_add_tail(vty_sessions, vty); + return vty; } @@ -1788,18 +1755,17 @@ struct vty *vty_stdio(void (*atclose)(int isexit)) /* Accept connection from the network. */ static int vty_accept(struct thread *thread) { + struct vty_serv *vtyserv = THREAD_ARG(thread); int vty_sock; union sockunion su; int ret; unsigned int on; - int accept_sock; + int accept_sock = vtyserv->sock; struct prefix p; struct access_list *acl = NULL; - accept_sock = THREAD_FD(thread); - /* We continue hearing vty socket. */ - vty_event_serv(VTY_SERV, accept_sock); + vty_event_serv(VTY_SERV, vtyserv); memset(&su, 0, sizeof(union sockunion)); @@ -1826,10 +1792,6 @@ static int vty_accept(struct thread *thread) && (access_list_apply(acl, &p) == FILTER_DENY)) { zlog_info("Vty connection refused from %pSU", &su); close(vty_sock); - - /* continue accepting connections */ - vty_event_serv(VTY_SERV, accept_sock); - return 0; } } @@ -1841,10 +1803,6 @@ static int vty_accept(struct thread *thread) && (access_list_apply(acl, &p) == FILTER_DENY)) { zlog_info("Vty connection refused from %pSU", &su); close(vty_sock); - - /* continue accepting connections */ - vty_event_serv(VTY_SERV, accept_sock); - return 0; } } @@ -1890,6 +1848,8 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) ainfo_save = ainfo; do { + struct vty_serv *vtyserv; + if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; @@ -1915,7 +1875,11 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) continue; } - vty_event_serv(VTY_SERV, sock); + vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv)); + vtyserv->sock = sock; + vtyservs_add_tail(vty_servs, vtyserv); + + vty_event_serv(VTY_SERV, vtyserv); } while ((ainfo = ainfo->ai_next) != NULL); freeaddrinfo(ainfo_save); @@ -1928,6 +1892,7 @@ static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port) /* VTY shell UNIX domain socket. */ static void vty_serv_un(const char *path) { + struct vty_serv *vtyserv; int ret; int sock, len; struct sockaddr_un serv; @@ -1993,22 +1958,26 @@ static void vty_serv_un(const char *path) } } - vty_event_serv(VTYSH_SERV, sock); + 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); } /* #define VTYSH_DEBUG 1 */ static int vtysh_accept(struct thread *thread) { - int accept_sock; + struct vty_serv *vtyserv = THREAD_ARG(thread); + int accept_sock = vtyserv->sock; int sock; int client_len; struct sockaddr_un client; struct vty *vty; - accept_sock = THREAD_FD(thread); - - vty_event_serv(VTYSH_SERV, accept_sock); + vty_event_serv(VTYSH_SERV, vtyserv); memset(&client, 0, sizeof(struct sockaddr_un)); client_len = sizeof(struct sockaddr_un); @@ -2041,7 +2010,7 @@ static int vtysh_accept(struct thread *thread) vty->wfd = sock; vty->type = VTY_SHELL_SERV; vty->node = VIEW_NODE; - vector_set_index(vtyshvec, sock, vty); + vtys_add_tail(vtysh_sessions, vty); vty_event(VTYSH_READ, vty); @@ -2217,9 +2186,9 @@ void vty_close(struct vty *vty) /* Unset vector. */ if (vty->fd != -1) { if (vty->type == VTY_SHELL_SERV) - vector_unset(vtyshvec, vty->fd); + vtys_del(vtysh_sessions, vty); else - vector_unset(vtyvec, vty->fd); + vtys_del(vty_sessions, vty); } if (vty->wfd > 0 && vty->type == VTY_FILE) @@ -2282,7 +2251,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp) vty = vty_new(); /* vty_close won't close stderr; if some config command prints - * something it'll end up there. (not ideal; it'd be beter if output + * 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.) * @@ -2533,52 +2502,6 @@ tmp_free_and_out: return read_success; } -/* Small utility function which output log to the VTY. */ -void vty_log(const char *level, const char *proto_str, const char *msg, - struct timestamp_control *ctl) -{ - unsigned int i; - struct vty *vty; - - if (!vtyvec) - return; - - for (i = 0; i < vector_active(vtyvec); i++) - if ((vty = vector_slot(vtyvec, i)) != NULL) - if (vty->monitor) - vty_log_out(vty, level, proto_str, msg, ctl); -} - -/* Async-signal-safe version of vty_log for fixed strings. */ -void vty_log_fixed(char *buf, size_t len) -{ - unsigned int i; - struct iovec iov[2]; - char crlf[4] = "\r\n"; - - /* vty may not have been initialised */ - if (!vtyvec) - return; - - iov[0].iov_base = buf; - iov[0].iov_len = len; - iov[1].iov_base = crlf; - iov[1].iov_len = 2; - - for (i = 0; i < vector_active(vtyvec); i++) { - struct vty *vty; - if (((vty = vector_slot(vtyvec, i)) != NULL) && vty->monitor) - /* N.B. We don't care about the return code, since - process is - most likely just about to die anyway. */ - if (writev(vty->wfd, iov, 2) == -1) { - fprintf(stderr, "Failure to writev: %d\n", - errno); - exit(-1); - } - } -} - static void update_xpath(struct vty *vty, const char *oldpath, const char *newpath) { @@ -2597,21 +2520,11 @@ static void update_xpath(struct vty *vty, const char *oldpath, void vty_update_xpath(const char *oldpath, const char *newpath) { struct vty *vty; - unsigned int i; - - for (i = 0; i < vector_active(vtyshvec); i++) { - if ((vty = vector_slot(vtyshvec, i)) == NULL) - continue; + frr_each (vtys, vtysh_sessions, vty) update_xpath(vty, oldpath, newpath); - } - - for (i = 0; i < vector_active(vtyvec); i++) { - if ((vty = vector_slot(vtyvec, i)) == NULL) - continue; - + frr_each (vtys, vty_sessions, vty) update_xpath(vty, oldpath, newpath); - } } int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) @@ -2695,21 +2608,17 @@ int vty_config_node_exit(struct vty *vty) /* Master of the threads. */ static struct thread_master *vty_master; -static void vty_event_serv(enum event event, int sock) +static void vty_event_serv(enum event event, struct vty_serv *vty_serv) { - struct thread *vty_serv_thread = NULL; - switch (event) { case VTY_SERV: - vty_serv_thread = thread_add_read(vty_master, vty_accept, - NULL, sock, NULL); - vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); + thread_add_read(vty_master, vty_accept, vty_serv, + vty_serv->sock, &vty_serv->t_accept); break; #ifdef VTYSH case VTYSH_SERV: - vty_serv_thread = thread_add_read(vty_master, vtysh_accept, - NULL, sock, NULL); - vector_set_index(Vvty_serv_thread, sock, vty_serv_thread); + thread_add_read(vty_master, vtysh_accept, vty_serv, + vty_serv->sock, &vty_serv->t_accept); break; #endif /* VTYSH */ default: @@ -2761,13 +2670,11 @@ DEFUN_NOSH (config_who, "who", "Display who is on vty\n") { - unsigned int i; struct vty *v; - for (i = 0; i < vector_active(vtyvec); i++) - if ((v = vector_slot(vtyvec, i)) != NULL) - vty_out(vty, "%svty[%d] connected from %s.\n", - v->config ? "*" : " ", i, v->address); + frr_each (vtys, vty_sessions, v) + vty_out(vty, "%svty[%d] connected from %s.\n", + v->config ? "*" : " ", v->fd, v->address); return CMD_SUCCESS; } @@ -3080,25 +2987,14 @@ struct cmd_node vty_node = { /* Reset all VTY status. */ void vty_reset(void) { - unsigned int i; struct vty *vty; - struct thread *vty_serv_thread; - - for (i = 0; i < vector_active(vtyvec); i++) - if ((vty = vector_slot(vtyvec, i)) != NULL) { - buffer_reset(vty->lbuf); - buffer_reset(vty->obuf); - vty->status = VTY_CLOSE; - vty_close(vty); - } - for (i = 0; i < vector_active(Vvty_serv_thread); i++) - if ((vty_serv_thread = vector_slot(Vvty_serv_thread, i)) - != NULL) { - THREAD_OFF(vty_serv_thread); - vector_slot(Vvty_serv_thread, i) = NULL; - close(i); - } + frr_each_safe (vtys, vty_sessions, vty) { + buffer_reset(vty->lbuf); + buffer_reset(vty->obuf); + vty->status = VTY_CLOSE; + vty_close(vty); + } vty_timeout_val = VTY_TIMEOUT_DEFAULT; @@ -3118,7 +3014,7 @@ static void vty_save_cwd(void) * the whole world is coming down around us * Hence not worrying about it too much. */ - if (!chdir(SYSCONFDIR)) { + if (chdir(SYSCONFDIR)) { flog_err_sys(EC_LIB_SYSTEM_CALL, "Failure to chdir to %s, errno: %d", SYSCONFDIR, errno); @@ -3149,7 +3045,7 @@ int vty_shell_serv(struct vty *vty) void vty_init_vtysh(void) { - vtyvec = vector_init(VECTOR_MIN_SIZE); + /* currently nothing to do, but likely to have future use */ } /* Install vty's own commands like `who' command. */ @@ -3158,16 +3054,10 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) /* For further configuration read, preserve current directory. */ vty_save_cwd(); - vtyvec = vector_init(VECTOR_MIN_SIZE); - vtyshvec = vector_init(VECTOR_MIN_SIZE); - vty_master = master_thread; atexit(vty_stdio_atexit); - /* Initilize server thread vector. */ - Vvty_serv_thread = vector_init(VECTOR_MIN_SIZE); - /* Install bgp top node. */ install_node(&vty_node); @@ -3202,17 +3092,34 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) void vty_terminate(void) { + struct vty *vty; + struct vty_serv *vtyserv; + memset(vty_cwd, 0x00, sizeof(vty_cwd)); - if (vtyvec && Vvty_serv_thread) { - vty_reset(); - vector_free(vtyvec); - vector_free(Vvty_serv_thread); - vtyvec = NULL; - Vvty_serv_thread = NULL; + 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); } - if (vtyshvec) { - vector_free(vtyshvec); - vtyshvec = NULL; + + vtys_fini(vtysh_sessions); + vtys_init(vtysh_sessions); + + while ((vtyserv = vtyservs_pop(vty_servs))) { + THREAD_OFF(vtyserv->t_accept); + close(vtyserv->sock); + XFREE(MTYPE_VTY_SERV, vtyserv); } + + vtyservs_fini(vty_servs); + vtyservs_init(vty_servs); } |
