back to topotato report
topotato coverage report
Current view: top level - lib - vty.c (source / functions) Hit Total Coverage
Test: test_bgp_aspath_zero.py::BGPAggregatorZero Lines: 385 1615 23.8 %
Date: 2023-02-24 18:36:48 Functions: 38 113 33.6 %

          Line data    Source code
       1             : /*
       2             :  * Virtual terminal [aka TeletYpe] interface routine.
       3             :  * Copyright (C) 1997, 98 Kunihiro Ishiguro
       4             :  *
       5             :  * This file is part of GNU Zebra.
       6             :  *
       7             :  * GNU Zebra is free software; you can redistribute it and/or modify it
       8             :  * under the terms of the GNU General Public License as published by the
       9             :  * Free Software Foundation; either version 2, or (at your option) any
      10             :  * later version.
      11             :  *
      12             :  * GNU Zebra is distributed in the hope that it will be useful, but
      13             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15             :  * General Public License for more details.
      16             :  *
      17             :  * You should have received a copy of the GNU General Public License along
      18             :  * with this program; see the file COPYING; if not, write to the Free Software
      19             :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      20             :  */
      21             : 
      22             : #include <zebra.h>
      23             : 
      24             : #include <lib/version.h>
      25             : #include <sys/types.h>
      26             : #include <sys/types.h>
      27             : #ifdef HAVE_LIBPCRE2_POSIX
      28             : #ifndef _FRR_PCRE2_POSIX
      29             : #define _FRR_PCRE2_POSIX
      30             : #include <pcre2posix.h>
      31             : #endif /* _FRR_PCRE2_POSIX */
      32             : #elif defined(HAVE_LIBPCREPOSIX)
      33             : #include <pcreposix.h>
      34             : #else
      35             : #include <regex.h>
      36             : #endif /* HAVE_LIBPCRE2_POSIX */
      37             : #include <stdio.h>
      38             : 
      39             : #include "linklist.h"
      40             : #include "thread.h"
      41             : #include "buffer.h"
      42             : #include "command.h"
      43             : #include "sockunion.h"
      44             : #include "memory.h"
      45             : #include "log.h"
      46             : #include "prefix.h"
      47             : #include "filter.h"
      48             : #include "vty.h"
      49             : #include "privs.h"
      50             : #include "network.h"
      51             : #include "libfrr.h"
      52             : #include "frrstr.h"
      53             : #include "lib_errors.h"
      54             : #include "northbound_cli.h"
      55             : #include "printfrr.h"
      56             : #include "json.h"
      57             : 
      58             : #include <arpa/telnet.h>
      59             : #include <termios.h>
      60             : 
      61             : #include "lib/vty_clippy.c"
      62             : 
      63          12 : DEFINE_MTYPE_STATIC(LIB, VTY, "VTY");
      64          12 : DEFINE_MTYPE_STATIC(LIB, VTY_SERV, "VTY server");
      65          12 : DEFINE_MTYPE_STATIC(LIB, VTY_OUT_BUF, "VTY output buffer");
      66          12 : DEFINE_MTYPE_STATIC(LIB, VTY_HIST, "VTY history");
      67             : 
      68           0 : DECLARE_DLIST(vtys, struct vty, itm);
      69             : 
      70             : /* Vty events */
      71             : enum vty_event {
      72             :         VTY_SERV,
      73             :         VTY_READ,
      74             :         VTY_WRITE,
      75             :         VTY_TIMEOUT_RESET,
      76             : #ifdef VTYSH
      77             :         VTYSH_SERV,
      78             :         VTYSH_READ,
      79             :         VTYSH_WRITE
      80             : #endif /* VTYSH */
      81             : };
      82             : 
      83             : PREDECL_DLIST(vtyservs);
      84             : 
      85             : struct vty_serv {
      86             :         struct vtyservs_item itm;
      87             : 
      88             :         int sock;
      89             :         bool vtysh;
      90             : 
      91             :         struct thread *t_accept;
      92             : };
      93             : 
      94          16 : DECLARE_DLIST(vtyservs, struct vty_serv, itm);
      95             : 
      96             : static void vty_event_serv(enum vty_event event, struct vty_serv *);
      97             : static void vty_event(enum vty_event, struct vty *);
      98             : 
      99             : /* Extern host structure from command.c */
     100             : extern struct host host;
     101             : 
     102             : /* active listeners */
     103             : static struct vtyservs_head vty_servs[1] = {INIT_DLIST(vty_servs[0])};
     104             : 
     105             : /* active connections */
     106             : static struct vtys_head vty_sessions[1] = {INIT_DLIST(vty_sessions[0])};
     107             : static struct vtys_head vtysh_sessions[1] = {INIT_DLIST(vtysh_sessions[0])};
     108             : 
     109             : /* Vty timeout value. */
     110             : static unsigned long vty_timeout_val = VTY_TIMEOUT_DEFAULT;
     111             : 
     112             : /* Vty access-class command */
     113             : static char *vty_accesslist_name = NULL;
     114             : 
     115             : /* Vty access-calss for IPv6. */
     116             : static char *vty_ipv6_accesslist_name = NULL;
     117             : 
     118             : /* Current directory. */
     119             : static char vty_cwd[MAXPATHLEN];
     120             : 
     121             : /* Login password check. */
     122             : static int no_password_check = 0;
     123             : 
     124             : /* Integrated configuration file path */
     125             : static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG;
     126             : 
     127             : static bool do_log_commands;
     128             : static bool do_log_commands_perm;
     129             : 
     130           0 : void vty_frame(struct vty *vty, const char *format, ...)
     131             : {
     132           0 :         va_list args;
     133             : 
     134           0 :         va_start(args, format);
     135           0 :         vsnprintfrr(vty->frame + vty->frame_pos,
     136           0 :                     sizeof(vty->frame) - vty->frame_pos, format, args);
     137           0 :         vty->frame_pos = strlen(vty->frame);
     138           0 :         va_end(args);
     139           0 : }
     140             : 
     141           0 : void vty_endframe(struct vty *vty, const char *endtext)
     142             : {
     143           0 :         if (vty->frame_pos == 0 && endtext)
     144           0 :                 vty_out(vty, "%s", endtext);
     145           0 :         vty->frame_pos = 0;
     146           0 : }
     147             : 
     148           0 : bool vty_set_include(struct vty *vty, const char *regexp)
     149             : {
     150           0 :         int errcode;
     151           0 :         bool ret = true;
     152           0 :         char errbuf[256];
     153             : 
     154           0 :         if (!regexp) {
     155           0 :                 if (vty->filter) {
     156           0 :                         regfree(&vty->include);
     157           0 :                         vty->filter = false;
     158             :                 }
     159           0 :                 return true;
     160             :         }
     161             : 
     162           0 :         errcode = regcomp(&vty->include, regexp,
     163             :                           REG_EXTENDED | REG_NEWLINE | REG_NOSUB);
     164           0 :         if (errcode) {
     165           0 :                 ret = false;
     166           0 :                 regerror(errcode, &vty->include, errbuf, sizeof(errbuf));
     167           0 :                 vty_out(vty, "%% Regex compilation error: %s\n", errbuf);
     168             :         } else {
     169           0 :                 vty->filter = true;
     170             :         }
     171             : 
     172             :         return ret;
     173             : }
     174             : 
     175             : /* VTY standard output function. */
     176          22 : int vty_out(struct vty *vty, const char *format, ...)
     177             : {
     178          22 :         va_list args;
     179          22 :         ssize_t len;
     180          22 :         char buf[1024];
     181          22 :         char *p = NULL;
     182          22 :         char *filtered;
     183             :         /* format string may contain %m, keep errno intact for printfrr */
     184          22 :         int saved_errno = errno;
     185             : 
     186          22 :         if (vty->frame_pos) {
     187           0 :                 vty->frame_pos = 0;
     188           0 :                 vty_out(vty, "%s", vty->frame);
     189             :         }
     190             : 
     191          22 :         va_start(args, format);
     192          22 :         errno = saved_errno;
     193          22 :         p = vasnprintfrr(MTYPE_VTY_OUT_BUF, buf, sizeof(buf), format, args);
     194          22 :         va_end(args);
     195             : 
     196          22 :         len = strlen(p);
     197             : 
     198             :         /* filter buffer */
     199          22 :         if (vty->filter) {
     200           0 :                 vector lines = frrstr_split_vec(p, "\n");
     201             : 
     202             :                 /* Place first value in the cache */
     203           0 :                 char *firstline = vector_slot(lines, 0);
     204           0 :                 buffer_put(vty->lbuf, (uint8_t *) firstline, strlen(firstline));
     205             : 
     206             :                 /* If our split returned more than one entry, time to filter */
     207           0 :                 if (vector_active(lines) > 1) {
     208             :                         /*
     209             :                          * returned string is MTYPE_TMP so it matches the MTYPE
     210             :                          * of everything else in the vector
     211             :                          */
     212           0 :                         char *bstr = buffer_getstr(vty->lbuf);
     213           0 :                         buffer_reset(vty->lbuf);
     214           0 :                         XFREE(MTYPE_TMP, lines->index[0]);
     215           0 :                         vector_set_index(lines, 0, bstr);
     216           0 :                         frrstr_filter_vec(lines, &vty->include);
     217           0 :                         vector_compact(lines);
     218             :                         /*
     219             :                          * Consider the string "foo\n". If the regex is an empty string
     220             :                          * and the line ended with a newline, then the vector will look
     221             :                          * like:
     222             :                          *
     223             :                          * [0]: 'foo'
     224             :                          * [1]: ''
     225             :                          *
     226             :                          * If the regex isn't empty, the vector will look like:
     227             :                          *
     228             :                          * [0]: 'foo'
     229             :                          *
     230             :                          * In this case we'd like to preserve the newline, so we add
     231             :                          * the empty string [1] as in the first example.
     232             :                          */
     233           0 :                         if (p[strlen(p) - 1] == '\n' && vector_active(lines) > 0
     234           0 :                             && strlen(vector_slot(lines, vector_active(lines) - 1)))
     235           0 :                                 vector_set(lines, XSTRDUP(MTYPE_TMP, ""));
     236             : 
     237           0 :                         filtered = frrstr_join_vec(lines, "\n");
     238             :                 }
     239             :                 else {
     240             :                         filtered = NULL;
     241             :                 }
     242             : 
     243           0 :                 frrstr_strvec_free(lines);
     244             : 
     245             :         } else {
     246             :                 filtered = p;
     247             :         }
     248             : 
     249           0 :         if (!filtered)
     250           0 :                 goto done;
     251             : 
     252          22 :         switch (vty->type) {
     253           0 :         case VTY_TERM:
     254             :                 /* print with crlf replacement */
     255           0 :                 buffer_put_crlf(vty->obuf, (uint8_t *)filtered,
     256             :                                 strlen(filtered));
     257           0 :                 break;
     258           0 :         case VTY_SHELL:
     259           0 :                 if (vty->of) {
     260           0 :                         fprintf(vty->of, "%s", filtered);
     261           0 :                         fflush(vty->of);
     262           0 :                 } else if (vty->of_saved) {
     263           0 :                         fprintf(vty->of_saved, "%s", filtered);
     264           0 :                         fflush(vty->of_saved);
     265             :                 }
     266             :                 break;
     267          22 :         case VTY_SHELL_SERV:
     268             :         case VTY_FILE:
     269             :         default:
     270             :                 /* print without crlf replacement */
     271          22 :                 buffer_put(vty->obuf, (uint8_t *)filtered, strlen(filtered));
     272          22 :                 break;
     273             :         }
     274             : 
     275          22 : done:
     276             : 
     277          22 :         if (vty->filter && filtered)
     278           0 :                 XFREE(MTYPE_TMP, filtered);
     279             : 
     280             :         /* If p is not different with buf, it is allocated buffer.  */
     281          22 :         if (p != buf)
     282           3 :                 XFREE(MTYPE_VTY_OUT_BUF, p);
     283             : 
     284          22 :         return len;
     285             : }
     286             : 
     287           1 : static int vty_json_helper(struct vty *vty, struct json_object *json,
     288             :                            uint32_t options)
     289             : {
     290           1 :         const char *text;
     291             : 
     292           1 :         if (!json)
     293             :                 return CMD_SUCCESS;
     294             : 
     295           1 :         text = json_object_to_json_string_ext(
     296             :                 json, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_NOSLASHESCAPE);
     297           1 :         vty_out(vty, "%s\n", text);
     298           1 :         json_object_free(json);
     299             : 
     300           1 :         return CMD_SUCCESS;
     301             : }
     302             : 
     303           0 : int vty_json(struct vty *vty, struct json_object *json)
     304             : {
     305           0 :         return vty_json_helper(vty, json,
     306             :                                JSON_C_TO_STRING_PRETTY |
     307             :                                        JSON_C_TO_STRING_NOSLASHESCAPE);
     308             : }
     309             : 
     310           1 : int vty_json_no_pretty(struct vty *vty, struct json_object *json)
     311             : {
     312           1 :         return vty_json_helper(vty, json, JSON_C_TO_STRING_NOSLASHESCAPE);
     313             : }
     314             : 
     315             : /* Output current time to the vty. */
     316           0 : void vty_time_print(struct vty *vty, int cr)
     317             : {
     318           0 :         char buf[FRR_TIMESTAMP_LEN];
     319             : 
     320           0 :         if (frr_timestamp(0, buf, sizeof(buf)) == 0) {
     321           0 :                 zlog_info("frr_timestamp error");
     322           0 :                 return;
     323             :         }
     324           0 :         if (cr)
     325           0 :                 vty_out(vty, "%s\n", buf);
     326             :         else
     327           0 :                 vty_out(vty, "%s ", buf);
     328             : 
     329             :         return;
     330             : }
     331             : 
     332             : /* Say hello to vty interface. */
     333           0 : void vty_hello(struct vty *vty)
     334             : {
     335           0 :         if (host.motdfile) {
     336           0 :                 FILE *f;
     337           0 :                 char buf[4096];
     338             : 
     339           0 :                 f = fopen(host.motdfile, "r");
     340           0 :                 if (f) {
     341           0 :                         while (fgets(buf, sizeof(buf), f)) {
     342           0 :                                 char *s;
     343             :                                 /* work backwards to ignore trailling isspace()
     344             :                                  */
     345           0 :                                 for (s = buf + strlen(buf);
     346           0 :                                      (s > buf) && isspace((unsigned char)s[-1]);
     347           0 :                                      s--)
     348             :                                         ;
     349           0 :                                 *s = '\0';
     350           0 :                                 vty_out(vty, "%s\n", buf);
     351             :                         }
     352           0 :                         fclose(f);
     353             :                 } else
     354           0 :                         vty_out(vty, "MOTD file not found\n");
     355           0 :         } else if (host.motd)
     356           0 :                 vty_out(vty, "%s", host.motd);
     357           0 : }
     358             : 
     359             : #pragma GCC diagnostic push
     360             : #pragma GCC diagnostic ignored "-Wformat-nonliteral"
     361             : /* prompt formatting has a %s in the cmd_node prompt string.
     362             :  *
     363             :  * Also for some reason GCC emits the warning on the end of the function
     364             :  * (optimization maybe?) rather than on the vty_out line, so this pragma
     365             :  * wraps the entire function rather than just the vty_out line.
     366             :  */
     367             : 
     368             : /* Put out prompt and wait input from user. */
     369          32 : static void vty_prompt(struct vty *vty)
     370             : {
     371          32 :         if (vty->type == VTY_TERM) {
     372           0 :                 vty_out(vty, cmd_prompt(vty->node), cmd_hostname_get());
     373             :         }
     374          32 : }
     375             : #pragma GCC diagnostic pop
     376             : 
     377             : /* Send WILL TELOPT_ECHO to remote server. */
     378           0 : static void vty_will_echo(struct vty *vty)
     379             : {
     380           0 :         unsigned char cmd[] = {IAC, WILL, TELOPT_ECHO, '\0'};
     381           0 :         vty_out(vty, "%s", cmd);
     382             : }
     383             : 
     384             : /* Make suppress Go-Ahead telnet option. */
     385           0 : static void vty_will_suppress_go_ahead(struct vty *vty)
     386             : {
     387           0 :         unsigned char cmd[] = {IAC, WILL, TELOPT_SGA, '\0'};
     388           0 :         vty_out(vty, "%s", cmd);
     389             : }
     390             : 
     391             : /* Make don't use linemode over telnet. */
     392           0 : static void vty_dont_linemode(struct vty *vty)
     393             : {
     394           0 :         unsigned char cmd[] = {IAC, DONT, TELOPT_LINEMODE, '\0'};
     395           0 :         vty_out(vty, "%s", cmd);
     396             : }
     397             : 
     398             : /* Use window size. */
     399           0 : static void vty_do_window_size(struct vty *vty)
     400             : {
     401           0 :         unsigned char cmd[] = {IAC, DO, TELOPT_NAWS, '\0'};
     402           0 :         vty_out(vty, "%s", cmd);
     403             : }
     404             : 
     405             : /* Authentication of vty */
     406           0 : static void vty_auth(struct vty *vty, char *buf)
     407             : {
     408           0 :         char *passwd = NULL;
     409           0 :         enum node_type next_node = 0;
     410           0 :         int fail;
     411             : 
     412           0 :         switch (vty->node) {
     413           0 :         case AUTH_NODE:
     414           0 :                 if (host.encrypt)
     415           0 :                         passwd = host.password_encrypt;
     416             :                 else
     417           0 :                         passwd = host.password;
     418           0 :                 if (host.advanced)
     419           0 :                         next_node = host.enable ? VIEW_NODE : ENABLE_NODE;
     420             :                 else
     421             :                         next_node = VIEW_NODE;
     422             :                 break;
     423           0 :         case AUTH_ENABLE_NODE:
     424           0 :                 if (host.encrypt)
     425           0 :                         passwd = host.enable_encrypt;
     426             :                 else
     427           0 :                         passwd = host.enable;
     428             :                 next_node = ENABLE_NODE;
     429             :                 break;
     430             :         }
     431             : 
     432           0 :         if (passwd) {
     433           0 :                 if (host.encrypt)
     434           0 :                         fail = strcmp(crypt(buf, passwd), passwd);
     435             :                 else
     436           0 :                         fail = strcmp(buf, passwd);
     437             :         } else
     438             :                 fail = 1;
     439             : 
     440           0 :         if (!fail) {
     441           0 :                 vty->fail = 0;
     442           0 :                 vty->node = next_node; /* Success ! */
     443             :         } else {
     444           0 :                 vty->fail++;
     445           0 :                 if (vty->fail >= 3) {
     446           0 :                         if (vty->node == AUTH_NODE) {
     447           0 :                                 vty_out(vty,
     448             :                                         "%% Bad passwords, too many failures!\n");
     449           0 :                                 vty->status = VTY_CLOSE;
     450             :                         } else {
     451             :                                 /* AUTH_ENABLE_NODE */
     452           0 :                                 vty->fail = 0;
     453           0 :                                 vty_out(vty,
     454             :                                         "%% Bad enable passwords, too many failures!\n");
     455           0 :                                 vty->status = VTY_CLOSE;
     456             :                         }
     457             :                 }
     458             :         }
     459           0 : }
     460             : 
     461             : /* Command execution over the vty interface. */
     462          32 : static int vty_command(struct vty *vty, char *buf)
     463             : {
     464          32 :         int ret;
     465          32 :         const char *protocolname;
     466          32 :         char *cp = NULL;
     467             : 
     468          32 :         assert(vty);
     469             : 
     470             :         /*
     471             :          * Log non empty command lines
     472             :          */
     473          32 :         if (do_log_commands &&
     474           0 :             strncmp(buf, "echo PING", strlen("echo PING")) != 0)
     475             :                 cp = buf;
     476             :         if (cp != NULL) {
     477             :                 /* Skip white spaces. */
     478           0 :                 while (isspace((unsigned char)*cp) && *cp != '\0')
     479           0 :                         cp++;
     480             :         }
     481          32 :         if (cp != NULL && *cp != '\0') {
     482           0 :                 char vty_str[VTY_BUFSIZ];
     483           0 :                 char prompt_str[VTY_BUFSIZ];
     484             : 
     485             :                 /* format the base vty info */
     486           0 :                 snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", vty->fd,
     487           0 :                          vty->address);
     488             : 
     489             :                 /* format the prompt */
     490             : #pragma GCC diagnostic push
     491             : #pragma GCC diagnostic ignored "-Wformat-nonliteral"
     492             :                 /* prompt formatting has a %s in the cmd_node prompt string */
     493           0 :                 snprintf(prompt_str, sizeof(prompt_str), cmd_prompt(vty->node),
     494             :                          vty_str);
     495             : #pragma GCC diagnostic pop
     496             : 
     497             :                 /* now log the command */
     498           0 :                 zlog_notice("%s%s", prompt_str, buf);
     499             :         }
     500             : 
     501          32 :         RUSAGE_T before;
     502          32 :         RUSAGE_T after;
     503          32 :         unsigned long walltime, cputime;
     504             : 
     505             :         /* cmd_execute() may change cputime_enabled if we're executing the
     506             :          * "service cputime-stats" command, which can result in nonsensical
     507             :          * and very confusing warnings
     508             :          */
     509          32 :         bool cputime_enabled_here = cputime_enabled;
     510             : 
     511          32 :         GETRUSAGE(&before);
     512             : 
     513          32 :         ret = cmd_execute(vty, buf, NULL, 0);
     514             : 
     515          32 :         GETRUSAGE(&after);
     516             : 
     517          32 :         walltime = thread_consumed_time(&after, &before, &cputime);
     518             : 
     519          32 :         if (cputime_enabled_here && cputime_enabled && cputime_threshold
     520          32 :             && cputime > cputime_threshold)
     521             :                 /* Warn about CPU hog that must be fixed. */
     522           0 :                 flog_warn(EC_LIB_SLOW_THREAD_CPU,
     523             :                           "CPU HOG: command took %lums (cpu time %lums): %s",
     524             :                           walltime / 1000, cputime / 1000, buf);
     525          32 :         else if (walltime_threshold && walltime > walltime_threshold)
     526           0 :                 flog_warn(EC_LIB_SLOW_THREAD_WALL,
     527             :                           "STARVATION: command took %lums (cpu time %lums): %s",
     528             :                           walltime / 1000, cputime / 1000, buf);
     529             : 
     530             :         /* Get the name of the protocol if any */
     531          32 :         protocolname = frr_protoname;
     532             : 
     533          32 :         if (ret != CMD_SUCCESS)
     534           0 :                 switch (ret) {
     535           0 :                 case CMD_WARNING:
     536           0 :                         if (vty->type == VTY_FILE)
     537           0 :                                 vty_out(vty, "Warning...\n");
     538             :                         break;
     539           0 :                 case CMD_ERR_AMBIGUOUS:
     540           0 :                         vty_out(vty, "%% Ambiguous command.\n");
     541           0 :                         break;
     542           0 :                 case CMD_ERR_NO_MATCH:
     543           0 :                         vty_out(vty, "%% [%s] Unknown command: %s\n",
     544             :                                 protocolname, buf);
     545           0 :                         break;
     546           0 :                 case CMD_ERR_INCOMPLETE:
     547           0 :                         vty_out(vty, "%% Command incomplete.\n");
     548           0 :                         break;
     549             :                 }
     550             : 
     551          32 :         return ret;
     552             : }
     553             : 
     554             : static const char telnet_backward_char = 0x08;
     555             : static const char telnet_space_char = ' ';
     556             : 
     557             : /* Basic function to write buffer to vty. */
     558           0 : static void vty_write(struct vty *vty, const char *buf, size_t nbytes)
     559             : {
     560           0 :         if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE))
     561             :                 return;
     562             : 
     563             :         /* Should we do buffering here ?  And make vty_flush (vty) ? */
     564           0 :         buffer_put(vty->obuf, buf, nbytes);
     565             : }
     566             : 
     567             : /* Basic function to insert character into vty. */
     568           0 : static void vty_self_insert(struct vty *vty, char c)
     569             : {
     570           0 :         int i;
     571           0 :         int length;
     572             : 
     573           0 :         if (vty->length + 1 >= VTY_BUFSIZ)
     574             :                 return;
     575             : 
     576           0 :         length = vty->length - vty->cp;
     577           0 :         memmove(&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length);
     578           0 :         vty->buf[vty->cp] = c;
     579             : 
     580           0 :         vty_write(vty, &vty->buf[vty->cp], length + 1);
     581           0 :         for (i = 0; i < length; i++)
     582           0 :                 vty_write(vty, &telnet_backward_char, 1);
     583             : 
     584           0 :         vty->cp++;
     585           0 :         vty->length++;
     586             : 
     587           0 :         vty->buf[vty->length] = '\0';
     588             : }
     589             : 
     590             : /* Self insert character 'c' in overwrite mode. */
     591           0 : static void vty_self_insert_overwrite(struct vty *vty, char c)
     592             : {
     593           0 :         if (vty->cp == vty->length) {
     594           0 :                 vty_self_insert(vty, c);
     595           0 :                 return;
     596             :         }
     597             : 
     598           0 :         vty->buf[vty->cp++] = c;
     599           0 :         vty_write(vty, &c, 1);
     600             : }
     601             : 
     602             : /**
     603             :  * Insert a string into vty->buf at the current cursor position.
     604             :  *
     605             :  * If the resultant string would be larger than VTY_BUFSIZ it is
     606             :  * truncated to fit.
     607             :  */
     608           0 : static void vty_insert_word_overwrite(struct vty *vty, char *str)
     609             : {
     610           0 :         if (vty->cp == VTY_BUFSIZ)
     611             :                 return;
     612             : 
     613           0 :         size_t nwrite = MIN((int)strlen(str), VTY_BUFSIZ - vty->cp - 1);
     614           0 :         memcpy(&vty->buf[vty->cp], str, nwrite);
     615           0 :         vty->cp += nwrite;
     616           0 :         vty->length = MAX(vty->cp, vty->length);
     617           0 :         vty->buf[vty->length] = '\0';
     618           0 :         vty_write(vty, str, nwrite);
     619             : }
     620             : 
     621             : /* Forward character. */
     622           0 : static void vty_forward_char(struct vty *vty)
     623             : {
     624           0 :         if (vty->cp < vty->length) {
     625           0 :                 vty_write(vty, &vty->buf[vty->cp], 1);
     626           0 :                 vty->cp++;
     627             :         }
     628           0 : }
     629             : 
     630             : /* Backward character. */
     631           0 : static void vty_backward_char(struct vty *vty)
     632             : {
     633           0 :         if (vty->cp > 0) {
     634           0 :                 vty->cp--;
     635           0 :                 vty_write(vty, &telnet_backward_char, 1);
     636             :         }
     637           0 : }
     638             : 
     639             : /* Move to the beginning of the line. */
     640           0 : static void vty_beginning_of_line(struct vty *vty)
     641             : {
     642           0 :         while (vty->cp)
     643           0 :                 vty_backward_char(vty);
     644             : }
     645             : 
     646             : /* Move to the end of the line. */
     647             : static void vty_end_of_line(struct vty *vty)
     648             : {
     649           0 :         while (vty->cp < vty->length)
     650           0 :                 vty_forward_char(vty);
     651             : }
     652             : 
     653             : static void vty_kill_line_from_beginning(struct vty *);
     654             : static void vty_redraw_line(struct vty *);
     655             : 
     656             : /* Print command line history.  This function is called from
     657             :    vty_next_line and vty_previous_line. */
     658           0 : static void vty_history_print(struct vty *vty)
     659             : {
     660           0 :         int length;
     661             : 
     662           0 :         vty_kill_line_from_beginning(vty);
     663             : 
     664             :         /* Get previous line from history buffer */
     665           0 :         length = strlen(vty->hist[vty->hp]);
     666           0 :         memcpy(vty->buf, vty->hist[vty->hp], length);
     667           0 :         vty->cp = vty->length = length;
     668           0 :         vty->buf[vty->length] = '\0';
     669             : 
     670             :         /* Redraw current line */
     671           0 :         vty_redraw_line(vty);
     672           0 : }
     673             : 
     674             : /* Show next command line history. */
     675           0 : static void vty_next_line(struct vty *vty)
     676             : {
     677           0 :         int try_index;
     678             : 
     679           0 :         if (vty->hp == vty->hindex)
     680             :                 return;
     681             : 
     682             :         /* Try is there history exist or not. */
     683           0 :         try_index = vty->hp;
     684           0 :         if (try_index == (VTY_MAXHIST - 1))
     685             :                 try_index = 0;
     686             :         else
     687           0 :                 try_index++;
     688             : 
     689             :         /* If there is not history return. */
     690           0 :         if (vty->hist[try_index] == NULL)
     691             :                 return;
     692             :         else
     693           0 :                 vty->hp = try_index;
     694             : 
     695           0 :         vty_history_print(vty);
     696             : }
     697             : 
     698             : /* Show previous command line history. */
     699           0 : static void vty_previous_line(struct vty *vty)
     700             : {
     701           0 :         int try_index;
     702             : 
     703           0 :         try_index = vty->hp;
     704           0 :         if (try_index == 0)
     705             :                 try_index = VTY_MAXHIST - 1;
     706             :         else
     707           0 :                 try_index--;
     708             : 
     709           0 :         if (vty->hist[try_index] == NULL)
     710             :                 return;
     711             :         else
     712           0 :                 vty->hp = try_index;
     713             : 
     714           0 :         vty_history_print(vty);
     715             : }
     716             : 
     717             : /* This function redraw all of the command line character. */
     718           0 : static void vty_redraw_line(struct vty *vty)
     719             : {
     720           0 :         vty_write(vty, vty->buf, vty->length);
     721           0 :         vty->cp = vty->length;
     722           0 : }
     723             : 
     724             : /* Forward word. */
     725           0 : static void vty_forward_word(struct vty *vty)
     726             : {
     727           0 :         while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
     728           0 :                 vty_forward_char(vty);
     729             : 
     730           0 :         while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
     731           0 :                 vty_forward_char(vty);
     732           0 : }
     733             : 
     734             : /* Backward word without skipping training space. */
     735           0 : static void vty_backward_pure_word(struct vty *vty)
     736             : {
     737           0 :         while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
     738           0 :                 vty_backward_char(vty);
     739           0 : }
     740             : 
     741             : /* Backward word. */
     742           0 : static void vty_backward_word(struct vty *vty)
     743             : {
     744           0 :         while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
     745           0 :                 vty_backward_char(vty);
     746             : 
     747           0 :         while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
     748           0 :                 vty_backward_char(vty);
     749           0 : }
     750             : 
     751             : /* When '^D' is typed at the beginning of the line we move to the down
     752             :    level. */
     753           0 : static void vty_down_level(struct vty *vty)
     754             : {
     755           0 :         vty_out(vty, "\n");
     756           0 :         cmd_exit(vty);
     757           0 :         vty_prompt(vty);
     758           0 :         vty->cp = 0;
     759           0 : }
     760             : 
     761             : /* When '^Z' is received from vty, move down to the enable mode. */
     762           0 : static void vty_end_config(struct vty *vty)
     763             : {
     764           0 :         vty_out(vty, "\n");
     765             : 
     766           0 :         if (vty->config) {
     767           0 :                 vty_config_exit(vty);
     768           0 :                 vty->node = ENABLE_NODE;
     769             :         }
     770             : 
     771           0 :         vty_prompt(vty);
     772           0 :         vty->cp = 0;
     773           0 : }
     774             : 
     775             : /* Delete a character at the current point. */
     776           0 : static void vty_delete_char(struct vty *vty)
     777             : {
     778           0 :         int i;
     779           0 :         int size;
     780             : 
     781           0 :         if (vty->length == 0) {
     782           0 :                 vty_down_level(vty);
     783           0 :                 return;
     784             :         }
     785             : 
     786           0 :         if (vty->cp == vty->length)
     787             :                 return; /* completion need here? */
     788             : 
     789           0 :         size = vty->length - vty->cp;
     790             : 
     791           0 :         vty->length--;
     792           0 :         memmove(&vty->buf[vty->cp], &vty->buf[vty->cp + 1], size - 1);
     793           0 :         vty->buf[vty->length] = '\0';
     794             : 
     795           0 :         if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
     796             :                 return;
     797             : 
     798           0 :         vty_write(vty, &vty->buf[vty->cp], size - 1);
     799           0 :         vty_write(vty, &telnet_space_char, 1);
     800             : 
     801           0 :         for (i = 0; i < size; i++)
     802           0 :                 vty_write(vty, &telnet_backward_char, 1);
     803             : }
     804             : 
     805             : /* Delete a character before the point. */
     806           0 : static void vty_delete_backward_char(struct vty *vty)
     807             : {
     808           0 :         if (vty->cp == 0)
     809             :                 return;
     810             : 
     811           0 :         vty_backward_char(vty);
     812           0 :         vty_delete_char(vty);
     813             : }
     814             : 
     815             : /* Kill rest of line from current point. */
     816           0 : static void vty_kill_line(struct vty *vty)
     817             : {
     818           0 :         int i;
     819           0 :         int size;
     820             : 
     821           0 :         size = vty->length - vty->cp;
     822             : 
     823           0 :         if (size == 0)
     824             :                 return;
     825             : 
     826           0 :         for (i = 0; i < size; i++)
     827           0 :                 vty_write(vty, &telnet_space_char, 1);
     828           0 :         for (i = 0; i < size; i++)
     829           0 :                 vty_write(vty, &telnet_backward_char, 1);
     830             : 
     831           0 :         memset(&vty->buf[vty->cp], 0, size);
     832           0 :         vty->length = vty->cp;
     833             : }
     834             : 
     835             : /* Kill line from the beginning. */
     836           0 : static void vty_kill_line_from_beginning(struct vty *vty)
     837             : {
     838           0 :         vty_beginning_of_line(vty);
     839           0 :         vty_kill_line(vty);
     840           0 : }
     841             : 
     842             : /* Delete a word before the point. */
     843           0 : static void vty_forward_kill_word(struct vty *vty)
     844             : {
     845           0 :         while (vty->cp != vty->length && vty->buf[vty->cp] == ' ')
     846           0 :                 vty_delete_char(vty);
     847           0 :         while (vty->cp != vty->length && vty->buf[vty->cp] != ' ')
     848           0 :                 vty_delete_char(vty);
     849           0 : }
     850             : 
     851             : /* Delete a word before the point. */
     852           0 : static void vty_backward_kill_word(struct vty *vty)
     853             : {
     854           0 :         while (vty->cp > 0 && vty->buf[vty->cp - 1] == ' ')
     855           0 :                 vty_delete_backward_char(vty);
     856           0 :         while (vty->cp > 0 && vty->buf[vty->cp - 1] != ' ')
     857           0 :                 vty_delete_backward_char(vty);
     858           0 : }
     859             : 
     860             : /* Transpose chars before or at the point. */
     861           0 : static void vty_transpose_chars(struct vty *vty)
     862             : {
     863           0 :         char c1, c2;
     864             : 
     865             :         /* If length is short or point is near by the beginning of line then
     866             :            return. */
     867           0 :         if (vty->length < 2 || vty->cp < 1)
     868             :                 return;
     869             : 
     870             :         /* In case of point is located at the end of the line. */
     871           0 :         if (vty->cp == vty->length) {
     872           0 :                 c1 = vty->buf[vty->cp - 1];
     873           0 :                 c2 = vty->buf[vty->cp - 2];
     874             : 
     875           0 :                 vty_backward_char(vty);
     876           0 :                 vty_backward_char(vty);
     877           0 :                 vty_self_insert_overwrite(vty, c1);
     878           0 :                 vty_self_insert_overwrite(vty, c2);
     879             :         } else {
     880           0 :                 c1 = vty->buf[vty->cp];
     881           0 :                 c2 = vty->buf[vty->cp - 1];
     882             : 
     883           0 :                 vty_backward_char(vty);
     884           0 :                 vty_self_insert_overwrite(vty, c1);
     885           0 :                 vty_self_insert_overwrite(vty, c2);
     886             :         }
     887             : }
     888             : 
     889             : /* Do completion at vty interface. */
     890           0 : static void vty_complete_command(struct vty *vty)
     891             : {
     892           0 :         int i;
     893           0 :         int ret;
     894           0 :         char **matched = NULL;
     895           0 :         vector vline;
     896             : 
     897           0 :         if (vty->node == AUTH_NODE || vty->node == AUTH_ENABLE_NODE)
     898           0 :                 return;
     899             : 
     900           0 :         vline = cmd_make_strvec(vty->buf);
     901           0 :         if (vline == NULL)
     902             :                 return;
     903             : 
     904             :         /* In case of 'help \t'. */
     905           0 :         if (isspace((unsigned char)vty->buf[vty->length - 1]))
     906           0 :                 vector_set(vline, NULL);
     907             : 
     908           0 :         matched = cmd_complete_command(vline, vty, &ret);
     909             : 
     910           0 :         cmd_free_strvec(vline);
     911             : 
     912           0 :         vty_out(vty, "\n");
     913           0 :         switch (ret) {
     914           0 :         case CMD_ERR_AMBIGUOUS:
     915           0 :                 vty_out(vty, "%% Ambiguous command.\n");
     916           0 :                 vty_prompt(vty);
     917           0 :                 vty_redraw_line(vty);
     918           0 :                 break;
     919           0 :         case CMD_ERR_NO_MATCH:
     920             :                 /* vty_out (vty, "%% There is no matched command.\n"); */
     921           0 :                 vty_prompt(vty);
     922           0 :                 vty_redraw_line(vty);
     923           0 :                 break;
     924           0 :         case CMD_COMPLETE_FULL_MATCH:
     925           0 :                 if (!matched[0]) {
     926             :                         /* 2016-11-28 equinox -- need to debug, SEGV here */
     927           0 :                         vty_out(vty, "%% CLI BUG: FULL_MATCH with NULL str\n");
     928           0 :                         vty_prompt(vty);
     929           0 :                         vty_redraw_line(vty);
     930           0 :                         break;
     931             :                 }
     932           0 :                 vty_prompt(vty);
     933           0 :                 vty_redraw_line(vty);
     934           0 :                 vty_backward_pure_word(vty);
     935           0 :                 vty_insert_word_overwrite(vty, matched[0]);
     936           0 :                 vty_self_insert(vty, ' ');
     937           0 :                 XFREE(MTYPE_COMPLETION, matched[0]);
     938           0 :                 break;
     939           0 :         case CMD_COMPLETE_MATCH:
     940           0 :                 vty_prompt(vty);
     941           0 :                 vty_redraw_line(vty);
     942           0 :                 vty_backward_pure_word(vty);
     943           0 :                 vty_insert_word_overwrite(vty, matched[0]);
     944           0 :                 XFREE(MTYPE_COMPLETION, matched[0]);
     945           0 :                 break;
     946             :         case CMD_COMPLETE_LIST_MATCH:
     947           0 :                 for (i = 0; matched[i] != NULL; i++) {
     948           0 :                         if (i != 0 && ((i % 6) == 0))
     949           0 :                                 vty_out(vty, "\n");
     950           0 :                         vty_out(vty, "%-10s ", matched[i]);
     951           0 :                         XFREE(MTYPE_COMPLETION, matched[i]);
     952             :                 }
     953           0 :                 vty_out(vty, "\n");
     954             : 
     955           0 :                 vty_prompt(vty);
     956           0 :                 vty_redraw_line(vty);
     957           0 :                 break;
     958           0 :         case CMD_ERR_NOTHING_TODO:
     959           0 :                 vty_prompt(vty);
     960           0 :                 vty_redraw_line(vty);
     961           0 :                 break;
     962             :         default:
     963             :                 break;
     964             :         }
     965           0 :         XFREE(MTYPE_TMP, matched);
     966             : }
     967             : 
     968           0 : static void vty_describe_fold(struct vty *vty, int cmd_width,
     969             :                               unsigned int desc_width, struct cmd_token *token)
     970             : {
     971           0 :         char *buf;
     972           0 :         const char *cmd, *p;
     973           0 :         int pos;
     974             : 
     975           0 :         cmd = token->text;
     976             : 
     977           0 :         if (desc_width <= 0) {
     978           0 :                 vty_out(vty, "  %-*s  %s\n", cmd_width, cmd, token->desc);
     979           0 :                 return;
     980             :         }
     981             : 
     982           0 :         buf = XCALLOC(MTYPE_TMP, strlen(token->desc) + 1);
     983             : 
     984           0 :         for (p = token->desc; strlen(p) > desc_width; p += pos + 1) {
     985           0 :                 for (pos = desc_width; pos > 0; pos--)
     986           0 :                         if (*(p + pos) == ' ')
     987             :                                 break;
     988             : 
     989           0 :                 if (pos == 0)
     990             :                         break;
     991             : 
     992           0 :                 memcpy(buf, p, pos);
     993           0 :                 buf[pos] = '\0';
     994           0 :                 vty_out(vty, "  %-*s  %s\n", cmd_width, cmd, buf);
     995             : 
     996           0 :                 cmd = "";
     997             :         }
     998             : 
     999           0 :         vty_out(vty, "  %-*s  %s\n", cmd_width, cmd, p);
    1000             : 
    1001           0 :         XFREE(MTYPE_TMP, buf);
    1002             : }
    1003             : 
    1004             : /* Describe matched command function. */
    1005           0 : static void vty_describe_command(struct vty *vty)
    1006             : {
    1007           0 :         int ret;
    1008           0 :         vector vline;
    1009           0 :         vector describe;
    1010           0 :         unsigned int i, width, desc_width;
    1011           0 :         struct cmd_token *token, *token_cr = NULL;
    1012             : 
    1013           0 :         vline = cmd_make_strvec(vty->buf);
    1014             : 
    1015             :         /* In case of '> ?'. */
    1016           0 :         if (vline == NULL) {
    1017           0 :                 vline = vector_init(1);
    1018           0 :                 vector_set(vline, NULL);
    1019           0 :         } else if (isspace((unsigned char)vty->buf[vty->length - 1]))
    1020           0 :                 vector_set(vline, NULL);
    1021             : 
    1022           0 :         describe = cmd_describe_command(vline, vty, &ret);
    1023             : 
    1024           0 :         vty_out(vty, "\n");
    1025             : 
    1026             :         /* Ambiguous error. */
    1027           0 :         switch (ret) {
    1028           0 :         case CMD_ERR_AMBIGUOUS:
    1029           0 :                 vty_out(vty, "%% Ambiguous command.\n");
    1030           0 :                 goto out;
    1031           0 :                 break;
    1032           0 :         case CMD_ERR_NO_MATCH:
    1033           0 :                 vty_out(vty, "%% There is no matched command.\n");
    1034           0 :                 goto out;
    1035             :                 break;
    1036             :         }
    1037             : 
    1038             :         /* Get width of command string. */
    1039             :         width = 0;
    1040           0 :         for (i = 0; i < vector_active(describe); i++)
    1041           0 :                 if ((token = vector_slot(describe, i)) != NULL) {
    1042           0 :                         unsigned int len;
    1043             : 
    1044           0 :                         if (token->text[0] == '\0')
    1045           0 :                                 continue;
    1046             : 
    1047           0 :                         len = strlen(token->text);
    1048             : 
    1049           0 :                         if (width < len)
    1050           0 :                                 width = len;
    1051             :                 }
    1052             : 
    1053             :         /* Get width of description string. */
    1054           0 :         desc_width = vty->width - (width + 6);
    1055             : 
    1056             :         /* Print out description. */
    1057           0 :         for (i = 0; i < vector_active(describe); i++)
    1058           0 :                 if ((token = vector_slot(describe, i)) != NULL) {
    1059           0 :                         if (token->text[0] == '\0')
    1060           0 :                                 continue;
    1061             : 
    1062           0 :                         if (strcmp(token->text, CMD_CR_TEXT) == 0) {
    1063           0 :                                 token_cr = token;
    1064           0 :                                 continue;
    1065             :                         }
    1066             : 
    1067           0 :                         if (!token->desc)
    1068           0 :                                 vty_out(vty, "  %-s\n", token->text);
    1069           0 :                         else if (desc_width >= strlen(token->desc))
    1070           0 :                                 vty_out(vty, "  %-*s  %s\n", width, token->text,
    1071             :                                         token->desc);
    1072             :                         else
    1073           0 :                                 vty_describe_fold(vty, width, desc_width,
    1074             :                                                   token);
    1075             : 
    1076           0 :                         if (IS_VARYING_TOKEN(token->type)) {
    1077           0 :                                 const char *ref = vector_slot(
    1078             :                                         vline, vector_active(vline) - 1);
    1079             : 
    1080           0 :                                 vector varcomps = vector_init(VECTOR_MIN_SIZE);
    1081           0 :                                 cmd_variable_complete(token, ref, varcomps);
    1082             : 
    1083           0 :                                 if (vector_active(varcomps) > 0) {
    1084           0 :                                         char *ac = cmd_variable_comp2str(
    1085           0 :                                                 varcomps, vty->width);
    1086           0 :                                         vty_out(vty, "%s\n", ac);
    1087           0 :                                         XFREE(MTYPE_TMP, ac);
    1088             :                                 }
    1089             : 
    1090           0 :                                 vector_free(varcomps);
    1091             :                         }
    1092             :                 }
    1093             : 
    1094           0 :         if ((token = token_cr)) {
    1095           0 :                 if (!token->desc)
    1096           0 :                         vty_out(vty, "  %-s\n", token->text);
    1097           0 :                 else if (desc_width >= strlen(token->desc))
    1098           0 :                         vty_out(vty, "  %-*s  %s\n", width, token->text,
    1099             :                                 token->desc);
    1100             :                 else
    1101           0 :                         vty_describe_fold(vty, width, desc_width, token);
    1102             :         }
    1103             : 
    1104           0 : out:
    1105           0 :         cmd_free_strvec(vline);
    1106           0 :         if (describe)
    1107           0 :                 vector_free(describe);
    1108             : 
    1109           0 :         vty_prompt(vty);
    1110           0 :         vty_redraw_line(vty);
    1111           0 : }
    1112             : 
    1113          32 : static void vty_clear_buf(struct vty *vty)
    1114             : {
    1115          32 :         memset(vty->buf, 0, vty->max);
    1116             : }
    1117             : 
    1118             : /* ^C stop current input and do not add command line to the history. */
    1119           0 : static void vty_stop_input(struct vty *vty)
    1120             : {
    1121           0 :         vty->cp = vty->length = 0;
    1122           0 :         vty_clear_buf(vty);
    1123           0 :         vty_out(vty, "\n");
    1124             : 
    1125           0 :         if (vty->config) {
    1126           0 :                 vty_config_exit(vty);
    1127           0 :                 vty->node = ENABLE_NODE;
    1128             :         }
    1129             : 
    1130           0 :         vty_prompt(vty);
    1131             : 
    1132             :         /* Set history pointer to the latest one. */
    1133           0 :         vty->hp = vty->hindex;
    1134           0 : }
    1135             : 
    1136             : /* Add current command line to the history buffer. */
    1137           0 : static void vty_hist_add(struct vty *vty)
    1138             : {
    1139           0 :         int index;
    1140             : 
    1141           0 :         if (vty->length == 0)
    1142             :                 return;
    1143             : 
    1144           0 :         index = vty->hindex ? vty->hindex - 1 : VTY_MAXHIST - 1;
    1145             : 
    1146             :         /* Ignore the same string as previous one. */
    1147           0 :         if (vty->hist[index])
    1148           0 :                 if (strcmp(vty->buf, vty->hist[index]) == 0) {
    1149           0 :                         vty->hp = vty->hindex;
    1150           0 :                         return;
    1151             :                 }
    1152             : 
    1153             :         /* Insert history entry. */
    1154           0 :         XFREE(MTYPE_VTY_HIST, vty->hist[vty->hindex]);
    1155           0 :         vty->hist[vty->hindex] = XSTRDUP(MTYPE_VTY_HIST, vty->buf);
    1156             : 
    1157             :         /* History index rotation. */
    1158           0 :         vty->hindex++;
    1159           0 :         if (vty->hindex == VTY_MAXHIST)
    1160           0 :                 vty->hindex = 0;
    1161             : 
    1162           0 :         vty->hp = vty->hindex;
    1163             : }
    1164             : 
    1165             : /* #define TELNET_OPTION_DEBUG */
    1166             : 
    1167             : /* Get telnet window size. */
    1168           0 : static int vty_telnet_option(struct vty *vty, unsigned char *buf, int nbytes)
    1169             : {
    1170             : #ifdef TELNET_OPTION_DEBUG
    1171             :         int i;
    1172             : 
    1173             :         for (i = 0; i < nbytes; i++) {
    1174             :                 switch (buf[i]) {
    1175             :                 case IAC:
    1176             :                         vty_out(vty, "IAC ");
    1177             :                         break;
    1178             :                 case WILL:
    1179             :                         vty_out(vty, "WILL ");
    1180             :                         break;
    1181             :                 case WONT:
    1182             :                         vty_out(vty, "WONT ");
    1183             :                         break;
    1184             :                 case DO:
    1185             :                         vty_out(vty, "DO ");
    1186             :                         break;
    1187             :                 case DONT:
    1188             :                         vty_out(vty, "DONT ");
    1189             :                         break;
    1190             :                 case SB:
    1191             :                         vty_out(vty, "SB ");
    1192             :                         break;
    1193             :                 case SE:
    1194             :                         vty_out(vty, "SE ");
    1195             :                         break;
    1196             :                 case TELOPT_ECHO:
    1197             :                         vty_out(vty, "TELOPT_ECHO \n");
    1198             :                         break;
    1199             :                 case TELOPT_SGA:
    1200             :                         vty_out(vty, "TELOPT_SGA \n");
    1201             :                         break;
    1202             :                 case TELOPT_NAWS:
    1203             :                         vty_out(vty, "TELOPT_NAWS \n");
    1204             :                         break;
    1205             :                 default:
    1206             :                         vty_out(vty, "%x ", buf[i]);
    1207             :                         break;
    1208             :                 }
    1209             :         }
    1210             :         vty_out(vty, "\n");
    1211             : 
    1212             : #endif /* TELNET_OPTION_DEBUG */
    1213             : 
    1214           0 :         switch (buf[0]) {
    1215           0 :         case SB:
    1216           0 :                 vty->sb_len = 0;
    1217           0 :                 vty->iac_sb_in_progress = 1;
    1218           0 :                 return 0;
    1219           0 :         case SE: {
    1220           0 :                 if (!vty->iac_sb_in_progress)
    1221             :                         return 0;
    1222             : 
    1223           0 :                 if ((vty->sb_len == 0) || (vty->sb_buf[0] == '\0')) {
    1224           0 :                         vty->iac_sb_in_progress = 0;
    1225           0 :                         return 0;
    1226             :                 }
    1227           0 :                 switch (vty->sb_buf[0]) {
    1228           0 :                 case TELOPT_NAWS:
    1229           0 :                         if (vty->sb_len != TELNET_NAWS_SB_LEN)
    1230           0 :                                 flog_err(
    1231             :                                         EC_LIB_SYSTEM_CALL,
    1232             :                                         "RFC 1073 violation detected: telnet NAWS option should send %d characters, but we received %lu",
    1233             :                                         TELNET_NAWS_SB_LEN,
    1234             :                                         (unsigned long)vty->sb_len);
    1235           0 :                         else if (sizeof(vty->sb_buf) < TELNET_NAWS_SB_LEN)
    1236             :                                 flog_err(
    1237             :                                         EC_LIB_DEVELOPMENT,
    1238             :                                         "Bug detected: sizeof(vty->sb_buf) %lu < %d, too small to handle the telnet NAWS option",
    1239             :                                         (unsigned long)sizeof(vty->sb_buf),
    1240             :                                         TELNET_NAWS_SB_LEN);
    1241             :                         else {
    1242           0 :                                 vty->width = ((vty->sb_buf[1] << 8)
    1243           0 :                                               | vty->sb_buf[2]);
    1244           0 :                                 vty->height = ((vty->sb_buf[3] << 8)
    1245           0 :                                                | vty->sb_buf[4]);
    1246             : #ifdef TELNET_OPTION_DEBUG
    1247             :                                 vty_out(vty,
    1248             :                                         "TELNET NAWS window size negotiation completed: width %d, height %d\n",
    1249             :                                         vty->width, vty->height);
    1250             : #endif
    1251             :                         }
    1252             :                         break;
    1253             :                 }
    1254           0 :                 vty->iac_sb_in_progress = 0;
    1255           0 :                 return 0;
    1256             :         }
    1257             :         default:
    1258             :                 break;
    1259             :         }
    1260             :         return 1;
    1261             : }
    1262             : 
    1263             : /* Execute current command line. */
    1264          32 : static int vty_execute(struct vty *vty)
    1265             : {
    1266          32 :         int ret;
    1267             : 
    1268          32 :         ret = CMD_SUCCESS;
    1269             : 
    1270          32 :         switch (vty->node) {
    1271           0 :         case AUTH_NODE:
    1272             :         case AUTH_ENABLE_NODE:
    1273           0 :                 vty_auth(vty, vty->buf);
    1274           0 :                 break;
    1275          32 :         default:
    1276          32 :                 ret = vty_command(vty, vty->buf);
    1277          32 :                 if (vty->type == VTY_TERM)
    1278           0 :                         vty_hist_add(vty);
    1279             :                 break;
    1280             :         }
    1281             : 
    1282             :         /* Clear command line buffer. */
    1283          32 :         vty->cp = vty->length = 0;
    1284          32 :         vty_clear_buf(vty);
    1285             : 
    1286          32 :         if (vty->status != VTY_CLOSE)
    1287          32 :                 vty_prompt(vty);
    1288             : 
    1289          32 :         return ret;
    1290             : }
    1291             : 
    1292             : #define CONTROL(X)  ((X) - '@')
    1293             : #define VTY_NORMAL     0
    1294             : #define VTY_PRE_ESCAPE 1
    1295             : #define VTY_ESCAPE     2
    1296             : #define VTY_CR         3
    1297             : 
    1298             : /* Escape character command map. */
    1299           0 : static void vty_escape_map(unsigned char c, struct vty *vty)
    1300             : {
    1301           0 :         switch (c) {
    1302           0 :         case ('A'):
    1303           0 :                 vty_previous_line(vty);
    1304           0 :                 break;
    1305           0 :         case ('B'):
    1306           0 :                 vty_next_line(vty);
    1307           0 :                 break;
    1308           0 :         case ('C'):
    1309           0 :                 vty_forward_char(vty);
    1310           0 :                 break;
    1311           0 :         case ('D'):
    1312           0 :                 vty_backward_char(vty);
    1313           0 :                 break;
    1314             :         default:
    1315             :                 break;
    1316             :         }
    1317             : 
    1318             :         /* Go back to normal mode. */
    1319           0 :         vty->escape = VTY_NORMAL;
    1320           0 : }
    1321             : 
    1322             : /* Quit print out to the buffer. */
    1323           0 : static void vty_buffer_reset(struct vty *vty)
    1324             : {
    1325           0 :         buffer_reset(vty->obuf);
    1326           0 :         buffer_reset(vty->lbuf);
    1327           0 :         vty_prompt(vty);
    1328           0 :         vty_redraw_line(vty);
    1329           0 : }
    1330             : 
    1331             : /* Read data via vty socket. */
    1332           0 : static void vty_read(struct thread *thread)
    1333             : {
    1334           0 :         int i;
    1335           0 :         int nbytes;
    1336           0 :         unsigned char buf[VTY_READ_BUFSIZ];
    1337             : 
    1338           0 :         struct vty *vty = THREAD_ARG(thread);
    1339             : 
    1340             :         /* Read raw data from socket */
    1341           0 :         if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) {
    1342           0 :                 if (nbytes < 0) {
    1343           0 :                         if (ERRNO_IO_RETRY(errno)) {
    1344           0 :                                 vty_event(VTY_READ, vty);
    1345           0 :                                 return;
    1346             :                         }
    1347           0 :                         flog_err(
    1348             :                                 EC_LIB_SOCKET,
    1349             :                                 "%s: read error on vty client fd %d, closing: %s",
    1350             :                                 __func__, vty->fd, safe_strerror(errno));
    1351           0 :                         buffer_reset(vty->obuf);
    1352           0 :                         buffer_reset(vty->lbuf);
    1353             :                 }
    1354           0 :                 vty->status = VTY_CLOSE;
    1355             :         }
    1356             : 
    1357           0 :         for (i = 0; i < nbytes; i++) {
    1358           0 :                 if (buf[i] == IAC) {
    1359           0 :                         if (!vty->iac) {
    1360           0 :                                 vty->iac = 1;
    1361           0 :                                 continue;
    1362             :                         } else {
    1363           0 :                                 vty->iac = 0;
    1364             :                         }
    1365             :                 }
    1366             : 
    1367           0 :                 if (vty->iac_sb_in_progress && !vty->iac) {
    1368           0 :                         if (vty->sb_len < sizeof(vty->sb_buf))
    1369           0 :                                 vty->sb_buf[vty->sb_len] = buf[i];
    1370           0 :                         vty->sb_len++;
    1371           0 :                         continue;
    1372             :                 }
    1373             : 
    1374           0 :                 if (vty->iac) {
    1375             :                         /* In case of telnet command */
    1376           0 :                         int ret = 0;
    1377           0 :                         ret = vty_telnet_option(vty, buf + i, nbytes - i);
    1378           0 :                         vty->iac = 0;
    1379           0 :                         i += ret;
    1380           0 :                         continue;
    1381             :                 }
    1382             : 
    1383             : 
    1384           0 :                 if (vty->status == VTY_MORE) {
    1385           0 :                         switch (buf[i]) {
    1386           0 :                         case CONTROL('C'):
    1387             :                         case 'q':
    1388             :                         case 'Q':
    1389           0 :                                 vty_buffer_reset(vty);
    1390           0 :                                 break;
    1391             :                         default:
    1392             :                                 break;
    1393             :                         }
    1394           0 :                         continue;
    1395             :                 }
    1396             : 
    1397             :                 /* Escape character. */
    1398           0 :                 if (vty->escape == VTY_ESCAPE) {
    1399           0 :                         vty_escape_map(buf[i], vty);
    1400           0 :                         continue;
    1401             :                 }
    1402             : 
    1403             :                 /* Pre-escape status. */
    1404           0 :                 if (vty->escape == VTY_PRE_ESCAPE) {
    1405           0 :                         switch (buf[i]) {
    1406           0 :                         case '[':
    1407           0 :                                 vty->escape = VTY_ESCAPE;
    1408           0 :                                 break;
    1409           0 :                         case 'b':
    1410           0 :                                 vty_backward_word(vty);
    1411           0 :                                 vty->escape = VTY_NORMAL;
    1412           0 :                                 break;
    1413           0 :                         case 'f':
    1414           0 :                                 vty_forward_word(vty);
    1415           0 :                                 vty->escape = VTY_NORMAL;
    1416           0 :                                 break;
    1417           0 :                         case 'd':
    1418           0 :                                 vty_forward_kill_word(vty);
    1419           0 :                                 vty->escape = VTY_NORMAL;
    1420           0 :                                 break;
    1421           0 :                         case CONTROL('H'):
    1422             :                         case 0x7f:
    1423           0 :                                 vty_backward_kill_word(vty);
    1424           0 :                                 vty->escape = VTY_NORMAL;
    1425           0 :                                 break;
    1426           0 :                         default:
    1427           0 :                                 vty->escape = VTY_NORMAL;
    1428           0 :                                 break;
    1429             :                         }
    1430           0 :                         continue;
    1431             :                 }
    1432             : 
    1433           0 :                 if (vty->escape == VTY_CR) {
    1434             :                         /* if we get CR+NL, the NL results in an extra empty
    1435             :                          * prompt line being printed without this;  just drop
    1436             :                          * the NL if it immediately follows CR.
    1437             :                          */
    1438           0 :                         vty->escape = VTY_NORMAL;
    1439             : 
    1440           0 :                         if (buf[i] == '\n')
    1441           0 :                                 continue;
    1442             :                 }
    1443             : 
    1444           0 :                 switch (buf[i]) {
    1445             :                 case CONTROL('A'):
    1446             :                         vty_beginning_of_line(vty);
    1447             :                         break;
    1448           0 :                 case CONTROL('B'):
    1449           0 :                         vty_backward_char(vty);
    1450           0 :                         break;
    1451           0 :                 case CONTROL('C'):
    1452           0 :                         vty_stop_input(vty);
    1453           0 :                         break;
    1454           0 :                 case CONTROL('D'):
    1455           0 :                         vty_delete_char(vty);
    1456           0 :                         break;
    1457             :                 case CONTROL('E'):
    1458             :                         vty_end_of_line(vty);
    1459             :                         break;
    1460           0 :                 case CONTROL('F'):
    1461           0 :                         vty_forward_char(vty);
    1462           0 :                         break;
    1463           0 :                 case CONTROL('H'):
    1464             :                 case 0x7f:
    1465           0 :                         vty_delete_backward_char(vty);
    1466           0 :                         break;
    1467           0 :                 case CONTROL('K'):
    1468           0 :                         vty_kill_line(vty);
    1469           0 :                         break;
    1470           0 :                 case CONTROL('N'):
    1471           0 :                         vty_next_line(vty);
    1472           0 :                         break;
    1473           0 :                 case CONTROL('P'):
    1474           0 :                         vty_previous_line(vty);
    1475           0 :                         break;
    1476           0 :                 case CONTROL('T'):
    1477           0 :                         vty_transpose_chars(vty);
    1478           0 :                         break;
    1479           0 :                 case CONTROL('U'):
    1480           0 :                         vty_kill_line_from_beginning(vty);
    1481           0 :                         break;
    1482           0 :                 case CONTROL('W'):
    1483           0 :                         vty_backward_kill_word(vty);
    1484           0 :                         break;
    1485           0 :                 case CONTROL('Z'):
    1486           0 :                         vty_end_config(vty);
    1487           0 :                         break;
    1488           0 :                 case '\r':
    1489           0 :                         vty->escape = VTY_CR;
    1490             :                         /* fallthru */
    1491           0 :                 case '\n':
    1492           0 :                         vty_out(vty, "\n");
    1493           0 :                         buffer_flush_available(vty->obuf, vty->wfd);
    1494           0 :                         vty_execute(vty);
    1495             : 
    1496           0 :                         if (vty->pass_fd != -1) {
    1497           0 :                                 close(vty->pass_fd);
    1498           0 :                                 vty->pass_fd = -1;
    1499             :                         }
    1500             :                         break;
    1501           0 :                 case '\t':
    1502           0 :                         vty_complete_command(vty);
    1503           0 :                         break;
    1504           0 :                 case '?':
    1505           0 :                         if (vty->node == AUTH_NODE
    1506           0 :                             || vty->node == AUTH_ENABLE_NODE)
    1507           0 :                                 vty_self_insert(vty, buf[i]);
    1508             :                         else
    1509           0 :                                 vty_describe_command(vty);
    1510             :                         break;
    1511           0 :                 case '\033':
    1512           0 :                         if (i + 1 < nbytes && buf[i + 1] == '[') {
    1513           0 :                                 vty->escape = VTY_ESCAPE;
    1514           0 :                                 i++;
    1515             :                         } else
    1516           0 :                                 vty->escape = VTY_PRE_ESCAPE;
    1517             :                         break;
    1518           0 :                 default:
    1519           0 :                         if (buf[i] > 31 && buf[i] < 127)
    1520           0 :                                 vty_self_insert(vty, buf[i]);
    1521             :                         break;
    1522             :                 }
    1523             :         }
    1524             : 
    1525             :         /* Check status. */
    1526           0 :         if (vty->status == VTY_CLOSE)
    1527           0 :                 vty_close(vty);
    1528             :         else {
    1529           0 :                 vty_event(VTY_WRITE, vty);
    1530           0 :                 vty_event(VTY_READ, vty);
    1531             :         }
    1532             : }
    1533             : 
    1534             : /* Flush buffer to the vty. */
    1535           0 : static void vty_flush(struct thread *thread)
    1536             : {
    1537           0 :         int erase;
    1538           0 :         buffer_status_t flushrc;
    1539           0 :         struct vty *vty = THREAD_ARG(thread);
    1540             : 
    1541             :         /* Tempolary disable read thread. */
    1542           0 :         if (vty->lines == 0)
    1543           0 :                 THREAD_OFF(vty->t_read);
    1544             : 
    1545             :         /* Function execution continue. */
    1546           0 :         erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE));
    1547             : 
    1548             :         /* N.B. if width is 0, that means we don't know the window size. */
    1549           0 :         if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0))
    1550           0 :                 flushrc = buffer_flush_available(vty->obuf, vty->wfd);
    1551           0 :         else if (vty->status == VTY_MORELINE)
    1552           0 :                 flushrc = buffer_flush_window(vty->obuf, vty->wfd, vty->width,
    1553             :                                               1, erase, 0);
    1554             :         else
    1555           0 :                 flushrc = buffer_flush_window(
    1556             :                         vty->obuf, vty->wfd, vty->width,
    1557             :                         vty->lines >= 0 ? vty->lines : vty->height, erase, 0);
    1558           0 :         switch (flushrc) {
    1559           0 :         case BUFFER_ERROR:
    1560           0 :                 zlog_info("buffer_flush failed on vty client fd %d/%d, closing",
    1561             :                           vty->fd, vty->wfd);
    1562           0 :                 buffer_reset(vty->lbuf);
    1563           0 :                 buffer_reset(vty->obuf);
    1564           0 :                 vty_close(vty);
    1565           0 :                 return;
    1566           0 :         case BUFFER_EMPTY:
    1567           0 :                 if (vty->status == VTY_CLOSE)
    1568           0 :                         vty_close(vty);
    1569             :                 else {
    1570           0 :                         vty->status = VTY_NORMAL;
    1571           0 :                         if (vty->lines == 0)
    1572           0 :                                 vty_event(VTY_READ, vty);
    1573             :                 }
    1574             :                 break;
    1575           0 :         case BUFFER_PENDING:
    1576             :                 /* There is more data waiting to be written. */
    1577           0 :                 vty->status = VTY_MORE;
    1578           0 :                 if (vty->lines == 0)
    1579           0 :                         vty_event(VTY_WRITE, vty);
    1580             :                 break;
    1581             :         }
    1582             : }
    1583             : 
    1584             : /* Allocate new vty struct. */
    1585          16 : struct vty *vty_new(void)
    1586             : {
    1587          16 :         struct vty *new = XCALLOC(MTYPE_VTY, sizeof(struct vty));
    1588             : 
    1589          16 :         new->fd = new->wfd = -1;
    1590          16 :         new->of = stdout;
    1591          16 :         new->lbuf = buffer_new(0);
    1592          16 :         new->obuf = buffer_new(0); /* Use default buffer size. */
    1593          16 :         new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
    1594          16 :         new->max = VTY_BUFSIZ;
    1595          16 :         new->pass_fd = -1;
    1596             : 
    1597          16 :         return new;
    1598             : }
    1599             : 
    1600             : 
    1601             : /* allocate and initialise vty */
    1602           0 : static struct vty *vty_new_init(int vty_sock)
    1603             : {
    1604           0 :         struct vty *vty;
    1605             : 
    1606           0 :         vty = vty_new();
    1607           0 :         vty->fd = vty_sock;
    1608           0 :         vty->wfd = vty_sock;
    1609           0 :         vty->type = VTY_TERM;
    1610           0 :         vty->node = AUTH_NODE;
    1611           0 :         vty->fail = 0;
    1612           0 :         vty->cp = 0;
    1613           0 :         vty_clear_buf(vty);
    1614           0 :         vty->length = 0;
    1615           0 :         memset(vty->hist, 0, sizeof(vty->hist));
    1616           0 :         vty->hp = 0;
    1617           0 :         vty->hindex = 0;
    1618           0 :         vty->xpath_index = 0;
    1619           0 :         memset(vty->xpath, 0, sizeof(vty->xpath));
    1620           0 :         vty->private_config = false;
    1621           0 :         vty->candidate_config = vty_shared_candidate_config;
    1622           0 :         vty->status = VTY_NORMAL;
    1623           0 :         vty->lines = -1;
    1624           0 :         vty->iac = 0;
    1625           0 :         vty->iac_sb_in_progress = 0;
    1626           0 :         vty->sb_len = 0;
    1627             : 
    1628           0 :         vtys_add_tail(vty_sessions, vty);
    1629             : 
    1630           0 :         return vty;
    1631             : }
    1632             : 
    1633             : /* Create new vty structure. */
    1634           0 : static struct vty *vty_create(int vty_sock, union sockunion *su)
    1635             : {
    1636           0 :         char buf[SU_ADDRSTRLEN];
    1637           0 :         struct vty *vty;
    1638             : 
    1639           0 :         sockunion2str(su, buf, SU_ADDRSTRLEN);
    1640             : 
    1641             :         /* Allocate new vty structure and set up default values. */
    1642           0 :         vty = vty_new_init(vty_sock);
    1643             : 
    1644             :         /* configurable parameters not part of basic init */
    1645           0 :         vty->v_timeout = vty_timeout_val;
    1646           0 :         strlcpy(vty->address, buf, sizeof(vty->address));
    1647           0 :         if (no_password_check) {
    1648           0 :                 if (host.advanced)
    1649           0 :                         vty->node = ENABLE_NODE;
    1650             :                 else
    1651           0 :                         vty->node = VIEW_NODE;
    1652             :         }
    1653           0 :         if (host.lines >= 0)
    1654           0 :                 vty->lines = host.lines;
    1655             : 
    1656           0 :         if (!no_password_check) {
    1657             :                 /* Vty is not available if password isn't set. */
    1658           0 :                 if (host.password == NULL && host.password_encrypt == NULL) {
    1659           0 :                         vty_out(vty, "Vty password is not set.\n");
    1660           0 :                         vty->status = VTY_CLOSE;
    1661           0 :                         vty_close(vty);
    1662           0 :                         return NULL;
    1663             :                 }
    1664             :         }
    1665             : 
    1666             :         /* Say hello to the world. */
    1667           0 :         vty_hello(vty);
    1668           0 :         if (!no_password_check)
    1669           0 :                 vty_out(vty, "\nUser Access Verification\n\n");
    1670             : 
    1671             :         /* Setting up terminal. */
    1672           0 :         vty_will_echo(vty);
    1673           0 :         vty_will_suppress_go_ahead(vty);
    1674             : 
    1675           0 :         vty_dont_linemode(vty);
    1676           0 :         vty_do_window_size(vty);
    1677             :         /* vty_dont_lflow_ahead (vty); */
    1678             : 
    1679           0 :         vty_prompt(vty);
    1680             : 
    1681             :         /* Add read/write thread. */
    1682           0 :         vty_event(VTY_WRITE, vty);
    1683           0 :         vty_event(VTY_READ, vty);
    1684             : 
    1685           0 :         return vty;
    1686             : }
    1687             : 
    1688             : /* create vty for stdio */
    1689             : static struct termios stdio_orig_termios;
    1690             : static struct vty *stdio_vty = NULL;
    1691             : static bool stdio_termios = false;
    1692             : static void (*stdio_vty_atclose)(int isexit);
    1693             : 
    1694           8 : static void vty_stdio_reset(int isexit)
    1695             : {
    1696           8 :         if (stdio_vty) {
    1697           0 :                 if (stdio_termios)
    1698           0 :                         tcsetattr(0, TCSANOW, &stdio_orig_termios);
    1699           0 :                 stdio_termios = false;
    1700             : 
    1701           0 :                 stdio_vty = NULL;
    1702             : 
    1703           0 :                 if (stdio_vty_atclose)
    1704           0 :                         stdio_vty_atclose(isexit);
    1705           0 :                 stdio_vty_atclose = NULL;
    1706             :         }
    1707           8 : }
    1708             : 
    1709           8 : static void vty_stdio_atexit(void)
    1710             : {
    1711           8 :         vty_stdio_reset(1);
    1712           8 : }
    1713             : 
    1714           0 : void vty_stdio_suspend(void)
    1715             : {
    1716           0 :         if (!stdio_vty)
    1717             :                 return;
    1718             : 
    1719           0 :         THREAD_OFF(stdio_vty->t_write);
    1720           0 :         THREAD_OFF(stdio_vty->t_read);
    1721           0 :         THREAD_OFF(stdio_vty->t_timeout);
    1722             : 
    1723           0 :         if (stdio_termios)
    1724           0 :                 tcsetattr(0, TCSANOW, &stdio_orig_termios);
    1725           0 :         stdio_termios = false;
    1726             : }
    1727             : 
    1728           0 : void vty_stdio_resume(void)
    1729             : {
    1730           0 :         if (!stdio_vty)
    1731             :                 return;
    1732             : 
    1733           0 :         if (!tcgetattr(0, &stdio_orig_termios)) {
    1734           0 :                 struct termios termios;
    1735             : 
    1736           0 :                 termios = stdio_orig_termios;
    1737           0 :                 termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR
    1738             :                                      | IGNCR | ICRNL | IXON);
    1739           0 :                 termios.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN);
    1740           0 :                 termios.c_cflag &= ~(CSIZE | PARENB);
    1741           0 :                 termios.c_cflag |= CS8;
    1742           0 :                 tcsetattr(0, TCSANOW, &termios);
    1743           0 :                 stdio_termios = true;
    1744             :         }
    1745             : 
    1746           0 :         vty_prompt(stdio_vty);
    1747             : 
    1748             :         /* Add read/write thread. */
    1749           0 :         vty_event(VTY_WRITE, stdio_vty);
    1750           0 :         vty_event(VTY_READ, stdio_vty);
    1751             : }
    1752             : 
    1753           0 : void vty_stdio_close(void)
    1754             : {
    1755           0 :         if (!stdio_vty)
    1756             :                 return;
    1757           0 :         vty_close(stdio_vty);
    1758             : }
    1759             : 
    1760           0 : struct vty *vty_stdio(void (*atclose)(int isexit))
    1761             : {
    1762           0 :         struct vty *vty;
    1763             : 
    1764             :         /* refuse creating two vtys on stdio */
    1765           0 :         if (stdio_vty)
    1766             :                 return NULL;
    1767             : 
    1768           0 :         vty = stdio_vty = vty_new_init(0);
    1769           0 :         stdio_vty_atclose = atclose;
    1770           0 :         vty->wfd = 1;
    1771             : 
    1772             :         /* always have stdio vty in a known _unchangeable_ state, don't want
    1773             :          * config
    1774             :          * to have any effect here to make sure scripting this works as intended
    1775             :          */
    1776           0 :         vty->node = ENABLE_NODE;
    1777           0 :         vty->v_timeout = 0;
    1778           0 :         strlcpy(vty->address, "console", sizeof(vty->address));
    1779             : 
    1780           0 :         vty_stdio_resume();
    1781           0 :         return vty;
    1782             : }
    1783             : 
    1784             : /* Accept connection from the network. */
    1785           0 : static void vty_accept(struct thread *thread)
    1786             : {
    1787           0 :         struct vty_serv *vtyserv = THREAD_ARG(thread);
    1788           0 :         int vty_sock;
    1789           0 :         union sockunion su;
    1790           0 :         int ret;
    1791           0 :         unsigned int on;
    1792           0 :         int accept_sock = vtyserv->sock;
    1793           0 :         struct prefix p;
    1794           0 :         struct access_list *acl = NULL;
    1795             : 
    1796             :         /* We continue hearing vty socket. */
    1797           0 :         vty_event_serv(VTY_SERV, vtyserv);
    1798             : 
    1799           0 :         memset(&su, 0, sizeof(union sockunion));
    1800             : 
    1801             :         /* We can handle IPv4 or IPv6 socket. */
    1802           0 :         vty_sock = sockunion_accept(accept_sock, &su);
    1803           0 :         if (vty_sock < 0) {
    1804           0 :                 flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
    1805             :                          safe_strerror(errno));
    1806           0 :                 return;
    1807             :         }
    1808           0 :         set_nonblocking(vty_sock);
    1809           0 :         set_cloexec(vty_sock);
    1810             : 
    1811           0 :         if (!sockunion2hostprefix(&su, &p)) {
    1812           0 :                 close(vty_sock);
    1813           0 :                 zlog_info("Vty unable to convert prefix from sockunion %pSU",
    1814             :                           &su);
    1815           0 :                 return;
    1816             :         }
    1817             : 
    1818             :         /* VTY's accesslist apply. */
    1819           0 :         if (p.family == AF_INET && vty_accesslist_name) {
    1820           0 :                 if ((acl = access_list_lookup(AFI_IP, vty_accesslist_name))
    1821           0 :                     && (access_list_apply(acl, &p) == FILTER_DENY)) {
    1822           0 :                         zlog_info("Vty connection refused from %pSU", &su);
    1823           0 :                         close(vty_sock);
    1824           0 :                         return;
    1825             :                 }
    1826             :         }
    1827             : 
    1828             :         /* VTY's ipv6 accesslist apply. */
    1829           0 :         if (p.family == AF_INET6 && vty_ipv6_accesslist_name) {
    1830           0 :                 if ((acl = access_list_lookup(AFI_IP6,
    1831             :                                               vty_ipv6_accesslist_name))
    1832           0 :                     && (access_list_apply(acl, &p) == FILTER_DENY)) {
    1833           0 :                         zlog_info("Vty connection refused from %pSU", &su);
    1834           0 :                         close(vty_sock);
    1835           0 :                         return;
    1836             :                 }
    1837             :         }
    1838             : 
    1839           0 :         on = 1;
    1840           0 :         ret = setsockopt(vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&on,
    1841             :                          sizeof(on));
    1842           0 :         if (ret < 0)
    1843           0 :                 zlog_info("can't set sockopt to vty_sock : %s",
    1844             :                           safe_strerror(errno));
    1845             : 
    1846           0 :         zlog_info("Vty connection from %pSU", &su);
    1847             : 
    1848           0 :         vty_create(vty_sock, &su);
    1849             : }
    1850             : 
    1851           4 : static void vty_serv_sock_addrinfo(const char *hostname, unsigned short port)
    1852             : {
    1853           4 :         int ret;
    1854           4 :         struct addrinfo req;
    1855           4 :         struct addrinfo *ainfo;
    1856           4 :         struct addrinfo *ainfo_save;
    1857           4 :         int sock;
    1858           4 :         char port_str[BUFSIZ];
    1859             : 
    1860           4 :         memset(&req, 0, sizeof(req));
    1861           4 :         req.ai_flags = AI_PASSIVE;
    1862           4 :         req.ai_family = AF_UNSPEC;
    1863           4 :         req.ai_socktype = SOCK_STREAM;
    1864           4 :         snprintf(port_str, sizeof(port_str), "%d", port);
    1865           4 :         port_str[sizeof(port_str) - 1] = '\0';
    1866             : 
    1867           4 :         ret = getaddrinfo(hostname, port_str, &req, &ainfo);
    1868             : 
    1869           4 :         if (ret != 0) {
    1870           0 :                 flog_err_sys(EC_LIB_SYSTEM_CALL, "getaddrinfo failed: %s",
    1871             :                              gai_strerror(ret));
    1872           0 :                 exit(1);
    1873             :         }
    1874             : 
    1875           4 :         ainfo_save = ainfo;
    1876             : 
    1877           8 :         do {
    1878           8 :                 struct vty_serv *vtyserv;
    1879             : 
    1880           8 :                 if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
    1881           0 :                         continue;
    1882             : 
    1883           8 :                 sock = socket(ainfo->ai_family, ainfo->ai_socktype,
    1884             :                               ainfo->ai_protocol);
    1885           8 :                 if (sock < 0)
    1886           0 :                         continue;
    1887             : 
    1888           8 :                 sockopt_v6only(ainfo->ai_family, sock);
    1889           8 :                 sockopt_reuseaddr(sock);
    1890           8 :                 sockopt_reuseport(sock);
    1891           8 :                 set_cloexec(sock);
    1892             : 
    1893           8 :                 ret = bind(sock, ainfo->ai_addr, ainfo->ai_addrlen);
    1894           8 :                 if (ret < 0) {
    1895           0 :                         close(sock); /* Avoid sd leak. */
    1896           0 :                         continue;
    1897             :                 }
    1898             : 
    1899           8 :                 ret = listen(sock, 3);
    1900           8 :                 if (ret < 0) {
    1901           0 :                         close(sock); /* Avoid sd leak. */
    1902           0 :                         continue;
    1903             :                 }
    1904             : 
    1905           8 :                 vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv));
    1906           8 :                 vtyserv->sock = sock;
    1907           8 :                 vtyservs_add_tail(vty_servs, vtyserv);
    1908             : 
    1909           8 :                 vty_event_serv(VTY_SERV, vtyserv);
    1910           8 :         } while ((ainfo = ainfo->ai_next) != NULL);
    1911             : 
    1912           4 :         freeaddrinfo(ainfo_save);
    1913           4 : }
    1914             : 
    1915             : #ifdef VTYSH
    1916             : /* For sockaddr_un. */
    1917             : #include <sys/un.h>
    1918             : 
    1919             : /* VTY shell UNIX domain socket. */
    1920           4 : static void vty_serv_un(const char *path)
    1921             : {
    1922           4 :         struct vty_serv *vtyserv;
    1923           4 :         int ret;
    1924           4 :         int sock, len;
    1925           4 :         struct sockaddr_un serv;
    1926           4 :         mode_t old_mask;
    1927           4 :         struct zprivs_ids_t ids;
    1928             : 
    1929             :         /* First of all, unlink existing socket */
    1930           4 :         unlink(path);
    1931             : 
    1932             :         /* Set umask */
    1933           4 :         old_mask = umask(0007);
    1934             : 
    1935             :         /* Make UNIX domain socket. */
    1936           4 :         sock = socket(AF_UNIX, SOCK_STREAM, 0);
    1937           4 :         if (sock < 0) {
    1938           0 :                 flog_err_sys(EC_LIB_SOCKET,
    1939             :                              "Cannot create unix stream socket: %s",
    1940             :                              safe_strerror(errno));
    1941           0 :                 return;
    1942             :         }
    1943             : 
    1944             :         /* Make server socket. */
    1945           4 :         memset(&serv, 0, sizeof(serv));
    1946           4 :         serv.sun_family = AF_UNIX;
    1947           4 :         strlcpy(serv.sun_path, path, sizeof(serv.sun_path));
    1948             : #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
    1949             :         len = serv.sun_len = SUN_LEN(&serv);
    1950             : #else
    1951           4 :         len = sizeof(serv.sun_family) + strlen(serv.sun_path);
    1952             : #endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */
    1953             : 
    1954           4 :         set_cloexec(sock);
    1955             : 
    1956           4 :         ret = bind(sock, (struct sockaddr *)&serv, len);
    1957           4 :         if (ret < 0) {
    1958           0 :                 flog_err_sys(EC_LIB_SOCKET, "Cannot bind path %s: %s", path,
    1959             :                              safe_strerror(errno));
    1960           0 :                 close(sock); /* Avoid sd leak. */
    1961           0 :                 return;
    1962             :         }
    1963             : 
    1964           4 :         ret = listen(sock, 5);
    1965           4 :         if (ret < 0) {
    1966           0 :                 flog_err_sys(EC_LIB_SOCKET, "listen(fd %d) failed: %s", sock,
    1967             :                              safe_strerror(errno));
    1968           0 :                 close(sock); /* Avoid sd leak. */
    1969           0 :                 return;
    1970             :         }
    1971             : 
    1972           4 :         umask(old_mask);
    1973             : 
    1974           4 :         zprivs_get_ids(&ids);
    1975             : 
    1976             :         /* Hack: ids.gid_vty is actually a uint, but we stored -1 in it
    1977             :            earlier for the case when we don't need to chown the file
    1978             :            type casting it here to make a compare */
    1979           4 :         if ((int)ids.gid_vty > 0) {
    1980             :                 /* set group of socket */
    1981           0 :                 if (chown(path, -1, ids.gid_vty)) {
    1982           0 :                         flog_err_sys(EC_LIB_SYSTEM_CALL,
    1983             :                                      "vty_serv_un: could chown socket, %s",
    1984             :                                      safe_strerror(errno));
    1985             :                 }
    1986             :         }
    1987             : 
    1988           4 :         vtyserv = XCALLOC(MTYPE_VTY_SERV, sizeof(*vtyserv));
    1989           4 :         vtyserv->sock = sock;
    1990           4 :         vtyserv->vtysh = true;
    1991           4 :         vtyservs_add_tail(vty_servs, vtyserv);
    1992             : 
    1993           4 :         vty_event_serv(VTYSH_SERV, vtyserv);
    1994             : }
    1995             : 
    1996             : /* #define VTYSH_DEBUG 1 */
    1997             : 
    1998          12 : static void vtysh_accept(struct thread *thread)
    1999             : {
    2000          12 :         struct vty_serv *vtyserv = THREAD_ARG(thread);
    2001          12 :         int accept_sock = vtyserv->sock;
    2002          12 :         int sock;
    2003          12 :         int client_len;
    2004          12 :         struct sockaddr_un client;
    2005          12 :         struct vty *vty;
    2006             : 
    2007          12 :         vty_event_serv(VTYSH_SERV, vtyserv);
    2008             : 
    2009          12 :         memset(&client, 0, sizeof(client));
    2010          12 :         client_len = sizeof(struct sockaddr_un);
    2011             : 
    2012          12 :         sock = accept(accept_sock, (struct sockaddr *)&client,
    2013             :                       (socklen_t *)&client_len);
    2014             : 
    2015          12 :         if (sock < 0) {
    2016           0 :                 flog_err(EC_LIB_SOCKET, "can't accept vty socket : %s",
    2017             :                          safe_strerror(errno));
    2018           0 :                 return;
    2019             :         }
    2020             : 
    2021          12 :         if (set_nonblocking(sock) < 0) {
    2022           0 :                 flog_err(
    2023             :                         EC_LIB_SOCKET,
    2024             :                         "vtysh_accept: could not set vty socket %d to non-blocking, %s, closing",
    2025             :                         sock, safe_strerror(errno));
    2026           0 :                 close(sock);
    2027           0 :                 return;
    2028             :         }
    2029          12 :         set_cloexec(sock);
    2030             : 
    2031             : #ifdef VTYSH_DEBUG
    2032             :         printf("VTY shell accept\n");
    2033             : #endif /* VTYSH_DEBUG */
    2034             : 
    2035          12 :         vty = vty_new();
    2036          12 :         vty->fd = sock;
    2037          12 :         vty->wfd = sock;
    2038          12 :         vty->type = VTY_SHELL_SERV;
    2039          12 :         vty->node = VIEW_NODE;
    2040          12 :         vtys_add_tail(vtysh_sessions, vty);
    2041             : 
    2042          12 :         vty_event(VTYSH_READ, vty);
    2043             : }
    2044             : 
    2045           0 : static int vtysh_do_pass_fd(struct vty *vty)
    2046             : {
    2047           0 :         struct iovec iov[1] = {
    2048             :                 {
    2049           0 :                         .iov_base = vty->pass_fd_status,
    2050             :                         .iov_len = sizeof(vty->pass_fd_status),
    2051             :                 },
    2052             :         };
    2053           0 :         union {
    2054             :                 uint8_t buf[CMSG_SPACE(sizeof(int))];
    2055             :                 struct cmsghdr align;
    2056             :         } u;
    2057           0 :         struct msghdr mh = {
    2058             :                 .msg_iov = iov,
    2059             :                 .msg_iovlen = array_size(iov),
    2060             :                 .msg_control = u.buf,
    2061             :                 .msg_controllen = sizeof(u.buf),
    2062             :         };
    2063           0 :         struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh);
    2064           0 :         ssize_t ret;
    2065             : 
    2066           0 :         memset(&u.buf, 0, sizeof(u.buf));
    2067           0 :         cmh->cmsg_level = SOL_SOCKET;
    2068           0 :         cmh->cmsg_type = SCM_RIGHTS;
    2069           0 :         cmh->cmsg_len = CMSG_LEN(sizeof(int));
    2070           0 :         memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int));
    2071             : 
    2072           0 :         ret = sendmsg(vty->wfd, &mh, 0);
    2073           0 :         if (ret < 0 && ERRNO_IO_RETRY(errno))
    2074             :                 return BUFFER_PENDING;
    2075             : 
    2076           0 :         close(vty->pass_fd);
    2077           0 :         vty->pass_fd = -1;
    2078           0 :         vty->status = VTY_NORMAL;
    2079             : 
    2080           0 :         if (ret <= 0)
    2081             :                 return BUFFER_ERROR;
    2082             : 
    2083             :         /* resume accepting commands (suspended in vtysh_read) */
    2084           0 :         vty_event(VTYSH_READ, vty);
    2085             : 
    2086           0 :         if ((size_t)ret < sizeof(vty->pass_fd_status)) {
    2087           0 :                 size_t remains = sizeof(vty->pass_fd_status) - ret;
    2088             : 
    2089           0 :                 buffer_put(vty->obuf, vty->pass_fd_status + ret, remains);
    2090           0 :                 return BUFFER_PENDING;
    2091             :         }
    2092             :         return BUFFER_EMPTY;
    2093             : }
    2094             : 
    2095          32 : static int vtysh_flush(struct vty *vty)
    2096             : {
    2097          32 :         int ret;
    2098             : 
    2099          32 :         ret = buffer_flush_available(vty->obuf, vty->wfd);
    2100          32 :         if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD)
    2101           0 :                 ret = vtysh_do_pass_fd(vty);
    2102             : 
    2103          32 :         switch (ret) {
    2104           0 :         case BUFFER_PENDING:
    2105           0 :                 vty_event(VTYSH_WRITE, vty);
    2106           0 :                 break;
    2107           0 :         case BUFFER_ERROR:
    2108           0 :                 flog_err(EC_LIB_SOCKET, "%s: write error to fd %d, closing",
    2109             :                          __func__, vty->fd);
    2110           0 :                 buffer_reset(vty->lbuf);
    2111           0 :                 buffer_reset(vty->obuf);
    2112           0 :                 vty_close(vty);
    2113           0 :                 return -1;
    2114             :         case BUFFER_EMPTY:
    2115             :                 break;
    2116             :         }
    2117             :         return 0;
    2118             : }
    2119             : 
    2120           0 : void vty_pass_fd(struct vty *vty, int fd)
    2121             : {
    2122           0 :         if (vty->pass_fd != -1)
    2123           0 :                 close(vty->pass_fd);
    2124             : 
    2125           0 :         vty->pass_fd = fd;
    2126           0 : }
    2127             : 
    2128          44 : static void vtysh_read(struct thread *thread)
    2129             : {
    2130          44 :         int ret;
    2131          44 :         int sock;
    2132          44 :         int nbytes;
    2133          44 :         struct vty *vty;
    2134          44 :         unsigned char buf[VTY_READ_BUFSIZ];
    2135          44 :         unsigned char *p;
    2136          44 :         uint8_t header[4] = {0, 0, 0, 0};
    2137             : 
    2138          44 :         sock = THREAD_FD(thread);
    2139          44 :         vty = THREAD_ARG(thread);
    2140             : 
    2141          44 :         if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) {
    2142          12 :                 if (nbytes < 0) {
    2143           0 :                         if (ERRNO_IO_RETRY(errno)) {
    2144           0 :                                 vty_event(VTYSH_READ, vty);
    2145          12 :                                 return;
    2146             :                         }
    2147           0 :                         flog_err(
    2148             :                                 EC_LIB_SOCKET,
    2149             :                                 "%s: read failed on vtysh client fd %d, closing: %s",
    2150             :                                 __func__, sock, safe_strerror(errno));
    2151             :                 }
    2152          12 :                 buffer_reset(vty->lbuf);
    2153          12 :                 buffer_reset(vty->obuf);
    2154          12 :                 vty_close(vty);
    2155             : #ifdef VTYSH_DEBUG
    2156             :                 printf("close vtysh\n");
    2157             : #endif /* VTYSH_DEBUG */
    2158          12 :                 return;
    2159             :         }
    2160             : 
    2161             : #ifdef VTYSH_DEBUG
    2162             :         printf("line: %.*s\n", nbytes, buf);
    2163             : #endif /* VTYSH_DEBUG */
    2164             : 
    2165          32 :         if (vty->length + nbytes >= VTY_BUFSIZ) {
    2166             :                 /* Clear command line buffer. */
    2167           0 :                 vty->cp = vty->length = 0;
    2168           0 :                 vty_clear_buf(vty);
    2169           0 :                 vty_out(vty, "%% Command is too long.\n");
    2170             :         } else {
    2171         652 :                 for (p = buf; p < buf + nbytes; p++) {
    2172         620 :                         vty->buf[vty->length++] = *p;
    2173         620 :                         if (*p == '\0') {
    2174             :                                 /* Pass this line to parser. */
    2175          32 :                                 ret = vty_execute(vty);
    2176             : /* Note that vty_execute clears the command buffer and resets
    2177             :    vty->length to 0. */
    2178             : 
    2179             : /* Return result. */
    2180             : #ifdef VTYSH_DEBUG
    2181             :                                 printf("result: %d\n", ret);
    2182             :                                 printf("vtysh node: %d\n", vty->node);
    2183             : #endif /* VTYSH_DEBUG */
    2184             : 
    2185          32 :                                 if (vty->pass_fd != -1) {
    2186           0 :                                         memset(vty->pass_fd_status, 0, 4);
    2187           0 :                                         vty->pass_fd_status[3] = ret;
    2188           0 :                                         vty->status = VTY_PASSFD;
    2189             : 
    2190           0 :                                         if (!vty->t_write)
    2191           0 :                                                 vty_event(VTYSH_WRITE, vty);
    2192             : 
    2193             :                                         /* this introduces a "sequence point"
    2194             :                                          * command output is written normally,
    2195             :                                          * read processing is suspended until
    2196             :                                          * buffer is empty
    2197             :                                          * then retcode + FD is written
    2198             :                                          * then normal processing resumes
    2199             :                                          *
    2200             :                                          * => skip vty_event(VTYSH_READ, vty)!
    2201             :                                          */
    2202           0 :                                         return;
    2203             :                                 }
    2204             : 
    2205             :                                 /* hack for asynchronous "write integrated"
    2206             :                                  * - other commands in "buf" will be ditched
    2207             :                                  * - input during pending config-write is
    2208             :                                  * "unsupported" */
    2209          32 :                                 if (ret == CMD_SUSPEND)
    2210             :                                         break;
    2211             : 
    2212             :                                 /* warning: watchfrr hardcodes this result write
    2213             :                                  */
    2214          32 :                                 header[3] = ret;
    2215          32 :                                 buffer_put(vty->obuf, header, 4);
    2216             : 
    2217          32 :                                 if (!vty->t_write && (vtysh_flush(vty) < 0))
    2218             :                                         /* Try to flush results; exit if a write
    2219             :                                          * error occurs. */
    2220             :                                         return;
    2221             :                         }
    2222             :                 }
    2223             :         }
    2224             : 
    2225          32 :         if (vty->status == VTY_CLOSE)
    2226           0 :                 vty_close(vty);
    2227             :         else
    2228          32 :                 vty_event(VTYSH_READ, vty);
    2229             : }
    2230             : 
    2231           0 : static void vtysh_write(struct thread *thread)
    2232             : {
    2233           0 :         struct vty *vty = THREAD_ARG(thread);
    2234             : 
    2235           0 :         vtysh_flush(vty);
    2236           0 : }
    2237             : 
    2238             : #endif /* VTYSH */
    2239             : 
    2240             : /* Determine address family to bind. */
    2241           4 : void vty_serv_sock(const char *addr, unsigned short port, const char *path)
    2242             : {
    2243             :         /* If port is set to 0, do not listen on TCP/IP at all! */
    2244           4 :         if (port)
    2245           4 :                 vty_serv_sock_addrinfo(addr, port);
    2246             : 
    2247             : #ifdef VTYSH
    2248           4 :         vty_serv_un(path);
    2249             : #endif /* VTYSH */
    2250           4 : }
    2251             : 
    2252           0 : static void vty_error_delete(void *arg)
    2253             : {
    2254           0 :         struct vty_error *ve = arg;
    2255             : 
    2256           0 :         XFREE(MTYPE_TMP, ve);
    2257           0 : }
    2258             : 
    2259             : /* Close vty interface.  Warning: call this only from functions that
    2260             :    will be careful not to access the vty afterwards (since it has
    2261             :    now been freed).  This is safest from top-level functions (called
    2262             :    directly by the thread dispatcher). */
    2263          16 : void vty_close(struct vty *vty)
    2264             : {
    2265          16 :         int i;
    2266          16 :         bool was_stdio = false;
    2267             : 
    2268             :         /* Drop out of configure / transaction if needed. */
    2269          16 :         vty_config_exit(vty);
    2270             : 
    2271             :         /* Cancel threads.*/
    2272          16 :         THREAD_OFF(vty->t_read);
    2273          16 :         THREAD_OFF(vty->t_write);
    2274          16 :         THREAD_OFF(vty->t_timeout);
    2275             : 
    2276          16 :         if (vty->pass_fd != -1) {
    2277           0 :                 close(vty->pass_fd);
    2278           0 :                 vty->pass_fd = -1;
    2279             :         }
    2280          16 :         zlog_live_close(&vty->live_log);
    2281             : 
    2282             :         /* Flush buffer. */
    2283          16 :         buffer_flush_all(vty->obuf, vty->wfd);
    2284             : 
    2285             :         /* Free input buffer. */
    2286          16 :         buffer_free(vty->obuf);
    2287          16 :         buffer_free(vty->lbuf);
    2288             : 
    2289             :         /* Free command history. */
    2290         352 :         for (i = 0; i < VTY_MAXHIST; i++) {
    2291         320 :                 XFREE(MTYPE_VTY_HIST, vty->hist[i]);
    2292             :         }
    2293             : 
    2294             :         /* Unset vector. */
    2295          16 :         if (vty->fd != -1) {
    2296          12 :                 if (vty->type == VTY_SHELL_SERV)
    2297          12 :                         vtys_del(vtysh_sessions, vty);
    2298             :                 else
    2299           0 :                         vtys_del(vty_sessions, vty);
    2300             :         }
    2301             : 
    2302          16 :         if (vty->wfd > 0 && vty->type == VTY_FILE)
    2303           4 :                 fsync(vty->wfd);
    2304             : 
    2305             :         /* Close socket.
    2306             :          * note check is for fd > STDERR_FILENO, not fd != -1.
    2307             :          * We never close stdin/stdout/stderr here, because we may be
    2308             :          * running in foreground mode with logging to stdout.  Also,
    2309             :          * additionally, we'd need to replace these fds with /dev/null. */
    2310          16 :         if (vty->wfd > STDERR_FILENO && vty->wfd != vty->fd)
    2311           0 :                 close(vty->wfd);
    2312          16 :         if (vty->fd > STDERR_FILENO)
    2313          12 :                 close(vty->fd);
    2314          16 :         if (vty->fd == STDIN_FILENO)
    2315           0 :                 was_stdio = true;
    2316             : 
    2317          16 :         XFREE(MTYPE_VTY, vty->buf);
    2318             : 
    2319          16 :         if (vty->error) {
    2320           0 :                 vty->error->del = vty_error_delete;
    2321           0 :                 list_delete(&vty->error);
    2322             :         }
    2323             : 
    2324             :         /* OK free vty. */
    2325          16 :         XFREE(MTYPE_VTY, vty);
    2326             : 
    2327          16 :         if (was_stdio)
    2328           0 :                 vty_stdio_reset(0);
    2329          16 : }
    2330             : 
    2331             : /* When time out occur output message then close connection. */
    2332           0 : static void vty_timeout(struct thread *thread)
    2333             : {
    2334           0 :         struct vty *vty;
    2335             : 
    2336           0 :         vty = THREAD_ARG(thread);
    2337           0 :         vty->v_timeout = 0;
    2338             : 
    2339             :         /* Clear buffer*/
    2340           0 :         buffer_reset(vty->lbuf);
    2341           0 :         buffer_reset(vty->obuf);
    2342           0 :         vty_out(vty, "\nVty connection is timed out.\n");
    2343             : 
    2344             :         /* Close connection. */
    2345           0 :         vty->status = VTY_CLOSE;
    2346           0 :         vty_close(vty);
    2347           0 : }
    2348             : 
    2349             : /* Read up configuration file from file_name. */
    2350           4 : static void vty_read_file(struct nb_config *config, FILE *confp)
    2351             : {
    2352           4 :         int ret;
    2353           4 :         struct vty *vty;
    2354           4 :         struct vty_error *ve;
    2355           4 :         struct listnode *node;
    2356           4 :         unsigned int line_num = 0;
    2357             : 
    2358           4 :         vty = vty_new();
    2359             :         /* vty_close won't close stderr;  if some config command prints
    2360             :          * something it'll end up there.  (not ideal; it'd be better if output
    2361             :          * from a file-load went to logging instead.  Also note that if this
    2362             :          * function is called after daemonizing, stderr will be /dev/null.)
    2363             :          *
    2364             :          * vty->fd will be -1 from vty_new()
    2365             :          */
    2366           4 :         vty->wfd = STDERR_FILENO;
    2367           4 :         vty->type = VTY_FILE;
    2368           4 :         vty->node = CONFIG_NODE;
    2369           4 :         vty->config = true;
    2370           4 :         if (config)
    2371           4 :                 vty->candidate_config = config;
    2372             :         else {
    2373           0 :                 vty->private_config = true;
    2374           0 :                 vty->candidate_config = nb_config_new(NULL);
    2375             :         }
    2376             : 
    2377             :         /* Execute configuration file */
    2378           4 :         ret = config_from_file(vty, confp, &line_num);
    2379             : 
    2380             :         /* Flush any previous errors before printing messages below */
    2381           4 :         buffer_flush_all(vty->obuf, vty->wfd);
    2382             : 
    2383           4 :         if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) {
    2384           0 :                 const char *message = NULL;
    2385           0 :                 char *nl;
    2386             : 
    2387           0 :                 switch (ret) {
    2388             :                 case CMD_ERR_AMBIGUOUS:
    2389             :                         message = "Ambiguous command";
    2390             :                         break;
    2391           0 :                 case CMD_ERR_NO_MATCH:
    2392           0 :                         message = "No such command";
    2393           0 :                         break;
    2394           0 :                 case CMD_WARNING:
    2395           0 :                         message = "Command returned Warning";
    2396           0 :                         break;
    2397           0 :                 case CMD_WARNING_CONFIG_FAILED:
    2398           0 :                         message = "Command returned Warning Config Failed";
    2399           0 :                         break;
    2400           0 :                 case CMD_ERR_INCOMPLETE:
    2401           0 :                         message = "Command returned Incomplete";
    2402           0 :                         break;
    2403           0 :                 case CMD_ERR_EXEED_ARGC_MAX:
    2404           0 :                         message =
    2405             :                                 "Command exceeded maximum number of Arguments";
    2406           0 :                         break;
    2407           0 :                 default:
    2408           0 :                         message = "Command returned unhandled error message";
    2409           0 :                         break;
    2410             :                 }
    2411             : 
    2412           0 :                 for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) {
    2413           0 :                         nl = strchr(ve->error_buf, '\n');
    2414           0 :                         if (nl)
    2415           0 :                                 *nl = '\0';
    2416           0 :                         flog_err(EC_LIB_VTY, "%s on config line %u: %s",
    2417             :                                  message, ve->line_num, ve->error_buf);
    2418             :                 }
    2419             :         }
    2420             : 
    2421             :         /*
    2422             :          * Automatically commit the candidate configuration after
    2423             :          * reading the configuration file.
    2424             :          */
    2425           4 :         if (config == NULL) {
    2426           0 :                 struct nb_context context = {};
    2427           0 :                 char errmsg[BUFSIZ] = {0};
    2428             : 
    2429           0 :                 context.client = NB_CLIENT_CLI;
    2430           0 :                 context.user = vty;
    2431           0 :                 ret = nb_candidate_commit(&context, vty->candidate_config, true,
    2432             :                                           "Read configuration file", NULL,
    2433             :                                           errmsg, sizeof(errmsg));
    2434           0 :                 if (ret != NB_OK && ret != NB_ERR_NO_CHANGES)
    2435           0 :                         zlog_err(
    2436             :                                 "%s: failed to read configuration file: %s (%s)",
    2437             :                                 __func__, nb_err_name(ret), errmsg);
    2438             :         }
    2439             : 
    2440           4 :         vty_close(vty);
    2441           4 : }
    2442             : 
    2443           0 : static FILE *vty_use_backup_config(const char *fullpath)
    2444             : {
    2445           0 :         char *fullpath_sav, *fullpath_tmp;
    2446           0 :         FILE *ret = NULL;
    2447           0 :         int tmp, sav;
    2448           0 :         int c;
    2449           0 :         char buffer[512];
    2450             : 
    2451           0 :         size_t fullpath_sav_sz = strlen(fullpath) + strlen(CONF_BACKUP_EXT) + 1;
    2452           0 :         fullpath_sav = malloc(fullpath_sav_sz);
    2453           0 :         strlcpy(fullpath_sav, fullpath, fullpath_sav_sz);
    2454           0 :         strlcat(fullpath_sav, CONF_BACKUP_EXT, fullpath_sav_sz);
    2455             : 
    2456           0 :         sav = open(fullpath_sav, O_RDONLY);
    2457           0 :         if (sav < 0) {
    2458           0 :                 free(fullpath_sav);
    2459           0 :                 return NULL;
    2460             :         }
    2461             : 
    2462           0 :         fullpath_tmp = malloc(strlen(fullpath) + 8);
    2463           0 :         snprintf(fullpath_tmp, strlen(fullpath) + 8, "%s.XXXXXX", fullpath);
    2464             : 
    2465             :         /* Open file to configuration write. */
    2466           0 :         tmp = mkstemp(fullpath_tmp);
    2467           0 :         if (tmp < 0)
    2468           0 :                 goto out_close_sav;
    2469             : 
    2470           0 :         if (fchmod(tmp, CONFIGFILE_MASK) != 0)
    2471           0 :                 goto out_close;
    2472             : 
    2473           0 :         while ((c = read(sav, buffer, 512)) > 0) {
    2474           0 :                 if (write(tmp, buffer, c) <= 0)
    2475           0 :                         goto out_close;
    2476             :         }
    2477           0 :         close(sav);
    2478           0 :         close(tmp);
    2479             : 
    2480           0 :         if (rename(fullpath_tmp, fullpath) == 0)
    2481           0 :                 ret = fopen(fullpath, "r");
    2482             :         else
    2483           0 :                 unlink(fullpath_tmp);
    2484             : 
    2485             :         if (0) {
    2486           0 :         out_close:
    2487           0 :                 close(tmp);
    2488           0 :                 unlink(fullpath_tmp);
    2489           0 :         out_close_sav:
    2490           0 :                 close(sav);
    2491             :         }
    2492             : 
    2493           0 :         free(fullpath_sav);
    2494           0 :         free(fullpath_tmp);
    2495           0 :         return ret;
    2496             : }
    2497             : 
    2498             : /* Read up configuration file from file_name. */
    2499           4 : bool vty_read_config(struct nb_config *config, const char *config_file,
    2500             :                      char *config_default_dir)
    2501             : {
    2502           4 :         char cwd[MAXPATHLEN];
    2503           4 :         FILE *confp = NULL;
    2504           4 :         const char *fullpath;
    2505           4 :         char *tmp = NULL;
    2506           4 :         bool read_success = false;
    2507             : 
    2508             :         /* If -f flag specified. */
    2509           4 :         if (config_file != NULL) {
    2510           4 :                 if (!IS_DIRECTORY_SEP(config_file[0])) {
    2511           0 :                         if (getcwd(cwd, MAXPATHLEN) == NULL) {
    2512           0 :                                 flog_err_sys(
    2513             :                                         EC_LIB_SYSTEM_CALL,
    2514             :                                         "%s: failure to determine Current Working Directory %d!",
    2515             :                                         __func__, errno);
    2516           0 :                                 goto tmp_free_and_out;
    2517             :                         }
    2518           0 :                         size_t tmp_len = strlen(cwd) + strlen(config_file) + 2;
    2519           0 :                         tmp = XMALLOC(MTYPE_TMP, tmp_len);
    2520           0 :                         snprintf(tmp, tmp_len, "%s/%s", cwd, config_file);
    2521           0 :                         fullpath = tmp;
    2522             :                 } else
    2523             :                         fullpath = config_file;
    2524             : 
    2525           4 :                 confp = fopen(fullpath, "r");
    2526             : 
    2527           4 :                 if (confp == NULL) {
    2528           0 :                         flog_warn(
    2529             :                                 EC_LIB_BACKUP_CONFIG,
    2530             :                                 "%s: failed to open configuration file %s: %s, checking backup",
    2531             :                                 __func__, fullpath, safe_strerror(errno));
    2532             : 
    2533           0 :                         confp = vty_use_backup_config(fullpath);
    2534           0 :                         if (confp)
    2535           0 :                                 flog_warn(EC_LIB_BACKUP_CONFIG,
    2536             :                                           "using backup configuration file!");
    2537             :                         else {
    2538           0 :                                 flog_err(
    2539             :                                         EC_LIB_VTY,
    2540             :                                         "%s: can't open configuration file [%s]",
    2541             :                                         __func__, config_file);
    2542           0 :                                 goto tmp_free_and_out;
    2543             :                         }
    2544             :                 }
    2545             :         } else {
    2546             : 
    2547           0 :                 host_config_set(config_default_dir);
    2548             : 
    2549             : #ifdef VTYSH
    2550           0 :                 int ret;
    2551           0 :                 struct stat conf_stat;
    2552             : 
    2553             :                 /* !!!!PLEASE LEAVE!!!!
    2554             :                  * This is NEEDED for use with vtysh -b, or else you can get
    2555             :                  * a real configuration food fight with a lot garbage in the
    2556             :                  * merged configuration file it creates coming from the per
    2557             :                  * daemon configuration files.  This also allows the daemons
    2558             :                  * to start if there default configuration file is not
    2559             :                  * present or ignore them, as needed when using vtysh -b to
    2560             :                  * configure the daemons at boot - MAG
    2561             :                  */
    2562             : 
    2563             :                 /* Stat for vtysh Zebra.conf, if found startup and wait for
    2564             :                  * boot configuration
    2565             :                  */
    2566             : 
    2567           0 :                 if (strstr(config_default_dir, "vtysh") == NULL) {
    2568           0 :                         ret = stat(integrate_default, &conf_stat);
    2569           0 :                         if (ret >= 0) {
    2570           0 :                                 read_success = true;
    2571           0 :                                 goto tmp_free_and_out;
    2572             :                         }
    2573             :                 }
    2574             : #endif /* VTYSH */
    2575           0 :                 confp = fopen(config_default_dir, "r");
    2576           0 :                 if (confp == NULL) {
    2577           0 :                         flog_err(
    2578             :                                 EC_LIB_SYSTEM_CALL,
    2579             :                                 "%s: failed to open configuration file %s: %s, checking backup",
    2580             :                                 __func__, config_default_dir,
    2581             :                                 safe_strerror(errno));
    2582             : 
    2583           0 :                         confp = vty_use_backup_config(config_default_dir);
    2584           0 :                         if (confp) {
    2585           0 :                                 flog_warn(EC_LIB_BACKUP_CONFIG,
    2586             :                                           "using backup configuration file!");
    2587           0 :                                 fullpath = config_default_dir;
    2588             :                         } else {
    2589           0 :                                 flog_err(EC_LIB_VTY,
    2590             :                                          "can't open configuration file [%s]",
    2591             :                                          config_default_dir);
    2592           0 :                                 goto tmp_free_and_out;
    2593             :                         }
    2594             :                 } else
    2595             :                         fullpath = config_default_dir;
    2596             :         }
    2597             : 
    2598           4 :         vty_read_file(config, confp);
    2599           4 :         read_success = true;
    2600             : 
    2601           4 :         fclose(confp);
    2602             : 
    2603           4 :         host_config_set(fullpath);
    2604             : 
    2605           4 : tmp_free_and_out:
    2606           4 :         XFREE(MTYPE_TMP, tmp);
    2607             : 
    2608           4 :         return read_success;
    2609             : }
    2610             : 
    2611           0 : static void update_xpath(struct vty *vty, const char *oldpath,
    2612             :                          const char *newpath)
    2613             : {
    2614           0 :         int i;
    2615             : 
    2616           0 :         for (i = 0; i < vty->xpath_index; i++) {
    2617           0 :                 if (!frrstr_startswith(vty->xpath[i], oldpath))
    2618             :                         break;
    2619             : 
    2620           0 :                 char *tmp = frrstr_replace(vty->xpath[i], oldpath, newpath);
    2621           0 :                 strlcpy(vty->xpath[i], tmp, sizeof(vty->xpath[0]));
    2622           0 :                 XFREE(MTYPE_TMP, tmp);
    2623             :         }
    2624           0 : }
    2625             : 
    2626           0 : void vty_update_xpath(const char *oldpath, const char *newpath)
    2627             : {
    2628           0 :         struct vty *vty;
    2629             : 
    2630           0 :         frr_each (vtys, vtysh_sessions, vty)
    2631           0 :                 update_xpath(vty, oldpath, newpath);
    2632           0 :         frr_each (vtys, vty_sessions, vty)
    2633           0 :                 update_xpath(vty, oldpath, newpath);
    2634           0 : }
    2635             : 
    2636           4 : int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
    2637             : {
    2638           4 :         if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) {
    2639           0 :                 vty_out(vty, "%% Configuration is locked by other client\n");
    2640           0 :                 return CMD_WARNING;
    2641             :         }
    2642             : 
    2643           4 :         vty->node = CONFIG_NODE;
    2644           4 :         vty->config = true;
    2645           4 :         vty->private_config = private_config;
    2646           4 :         vty->xpath_index = 0;
    2647             : 
    2648           4 :         if (private_config) {
    2649           0 :                 vty->candidate_config = nb_config_dup(running_config);
    2650           0 :                 vty->candidate_config_base = nb_config_dup(running_config);
    2651           0 :                 vty_out(vty,
    2652             :                         "Warning: uncommitted changes will be discarded on exit.\n\n");
    2653             :         } else {
    2654           4 :                 vty->candidate_config = vty_shared_candidate_config;
    2655           4 :                 if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
    2656           0 :                         vty->candidate_config_base =
    2657           0 :                                 nb_config_dup(running_config);
    2658             :         }
    2659             : 
    2660             :         return CMD_SUCCESS;
    2661             : }
    2662             : 
    2663          20 : void vty_config_exit(struct vty *vty)
    2664             : {
    2665          20 :         enum node_type node = vty->node;
    2666          20 :         struct cmd_node *cnode;
    2667             : 
    2668             :         /* unlock and jump up to ENABLE_NODE if -and only if- we're
    2669             :          * somewhere below CONFIG_NODE */
    2670          35 :         while (node && node != CONFIG_NODE) {
    2671          15 :                 cnode = vector_lookup(cmdvec, node);
    2672          15 :                 node = cnode->parent_node;
    2673             :         }
    2674          20 :         if (node != CONFIG_NODE)
    2675             :                 /* called outside config, e.g. vty_close() in ENABLE_NODE */
    2676             :                 return;
    2677             : 
    2678          19 :         while (vty->node != ENABLE_NODE)
    2679             :                 /* will call vty_config_node_exit() below */
    2680          11 :                 cmd_exit(vty);
    2681             : }
    2682             : 
    2683           8 : int vty_config_node_exit(struct vty *vty)
    2684             : {
    2685           8 :         vty->xpath_index = 0;
    2686             : 
    2687             :         /* Perform any pending commits. */
    2688           8 :         (void)nb_cli_pending_commit_check(vty);
    2689             : 
    2690             :         /* Check if there's a pending confirmed commit. */
    2691           8 :         if (vty->t_confirmed_commit_timeout) {
    2692           0 :                 vty_out(vty,
    2693             :                         "exiting with a pending confirmed commit. Rolling back to previous configuration.\n\n");
    2694           0 :                 nb_cli_confirmed_commit_rollback(vty);
    2695           0 :                 nb_cli_confirmed_commit_clean(vty);
    2696             :         }
    2697             : 
    2698           8 :         (void)nb_running_unlock(NB_CLIENT_CLI, vty);
    2699             : 
    2700           8 :         if (vty->candidate_config) {
    2701           8 :                 if (vty->private_config)
    2702           0 :                         nb_config_free(vty->candidate_config);
    2703           8 :                 vty->candidate_config = NULL;
    2704             :         }
    2705           8 :         if (vty->candidate_config_base) {
    2706           0 :                 nb_config_free(vty->candidate_config_base);
    2707           0 :                 vty->candidate_config_base = NULL;
    2708             :         }
    2709             : 
    2710           8 :         vty->config = false;
    2711           8 :         return 1;
    2712             : }
    2713             : 
    2714             : /* Master of the threads. */
    2715             : static struct thread_master *vty_master;
    2716             : 
    2717          24 : static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv)
    2718             : {
    2719          24 :         switch (event) {
    2720           8 :         case VTY_SERV:
    2721           8 :                 thread_add_read(vty_master, vty_accept, vty_serv,
    2722             :                                 vty_serv->sock, &vty_serv->t_accept);
    2723           8 :                 break;
    2724             : #ifdef VTYSH
    2725          16 :         case VTYSH_SERV:
    2726          16 :                 thread_add_read(vty_master, vtysh_accept, vty_serv,
    2727             :                                 vty_serv->sock, &vty_serv->t_accept);
    2728          16 :                 break;
    2729             : #endif /* VTYSH */
    2730             :         case VTY_READ:
    2731             :         case VTY_WRITE:
    2732             :         case VTY_TIMEOUT_RESET:
    2733             :         case VTYSH_READ:
    2734             :         case VTYSH_WRITE:
    2735           0 :                 assert(!"vty_event_serv() called incorrectly");
    2736             :         }
    2737          24 : }
    2738             : 
    2739          44 : static void vty_event(enum vty_event event, struct vty *vty)
    2740             : {
    2741          44 :         switch (event) {
    2742             : #ifdef VTYSH
    2743          44 :         case VTYSH_READ:
    2744          44 :                 thread_add_read(vty_master, vtysh_read, vty, vty->fd,
    2745             :                                 &vty->t_read);
    2746          44 :                 break;
    2747           0 :         case VTYSH_WRITE:
    2748           0 :                 thread_add_write(vty_master, vtysh_write, vty, vty->wfd,
    2749             :                                  &vty->t_write);
    2750           0 :                 break;
    2751             : #endif /* VTYSH */
    2752           0 :         case VTY_READ:
    2753           0 :                 thread_add_read(vty_master, vty_read, vty, vty->fd,
    2754             :                                 &vty->t_read);
    2755             : 
    2756             :                 /* Time out treatment. */
    2757           0 :                 if (vty->v_timeout) {
    2758           0 :                         THREAD_OFF(vty->t_timeout);
    2759           0 :                         thread_add_timer(vty_master, vty_timeout, vty,
    2760             :                                          vty->v_timeout, &vty->t_timeout);
    2761             :                 }
    2762             :                 break;
    2763           0 :         case VTY_WRITE:
    2764           0 :                 thread_add_write(vty_master, vty_flush, vty, vty->wfd,
    2765             :                                  &vty->t_write);
    2766           0 :                 break;
    2767           0 :         case VTY_TIMEOUT_RESET:
    2768           0 :                 THREAD_OFF(vty->t_timeout);
    2769           0 :                 if (vty->v_timeout)
    2770           0 :                         thread_add_timer(vty_master, vty_timeout, vty,
    2771             :                                          vty->v_timeout, &vty->t_timeout);
    2772             :                 break;
    2773             :         case VTY_SERV:
    2774             :         case VTYSH_SERV:
    2775           0 :                 assert(!"vty_event() called incorrectly");
    2776             :         }
    2777          44 : }
    2778             : 
    2779           0 : DEFUN_NOSH (config_who,
    2780             :        config_who_cmd,
    2781             :        "who",
    2782             :        "Display who is on vty\n")
    2783             : {
    2784           0 :         struct vty *v;
    2785             : 
    2786           0 :         frr_each (vtys, vty_sessions, v)
    2787           0 :                 vty_out(vty, "%svty[%d] connected from %s%s.\n",
    2788           0 :                         v->config ? "*" : " ", v->fd, v->address,
    2789           0 :                         zlog_live_is_null(&v->live_log) ? "" : ", live log");
    2790           0 :         return CMD_SUCCESS;
    2791             : }
    2792             : 
    2793             : /* Move to vty configuration mode. */
    2794           2 : DEFUN_NOSH (line_vty,
    2795             :        line_vty_cmd,
    2796             :        "line vty",
    2797             :        "Configure a terminal line\n"
    2798             :        "Virtual terminal\n")
    2799             : {
    2800           2 :         vty->node = VTY_NODE;
    2801           2 :         return CMD_SUCCESS;
    2802             : }
    2803             : 
    2804             : /* Set time out value. */
    2805           0 : static int exec_timeout(struct vty *vty, const char *min_str,
    2806             :                         const char *sec_str)
    2807             : {
    2808           0 :         unsigned long timeout = 0;
    2809             : 
    2810             :         /* min_str and sec_str are already checked by parser.  So it must be
    2811             :            all digit string. */
    2812           0 :         if (min_str) {
    2813           0 :                 timeout = strtol(min_str, NULL, 10);
    2814           0 :                 timeout *= 60;
    2815             :         }
    2816           0 :         if (sec_str)
    2817           0 :                 timeout += strtol(sec_str, NULL, 10);
    2818             : 
    2819           0 :         vty_timeout_val = timeout;
    2820           0 :         vty->v_timeout = timeout;
    2821           0 :         vty_event(VTY_TIMEOUT_RESET, vty);
    2822             : 
    2823             : 
    2824           0 :         return CMD_SUCCESS;
    2825             : }
    2826             : 
    2827           0 : DEFUN (exec_timeout_min,
    2828             :        exec_timeout_min_cmd,
    2829             :        "exec-timeout (0-35791)",
    2830             :        "Set timeout value\n"
    2831             :        "Timeout value in minutes\n")
    2832             : {
    2833           0 :         int idx_number = 1;
    2834           0 :         return exec_timeout(vty, argv[idx_number]->arg, NULL);
    2835             : }
    2836             : 
    2837           0 : DEFUN (exec_timeout_sec,
    2838             :        exec_timeout_sec_cmd,
    2839             :        "exec-timeout (0-35791) (0-2147483)",
    2840             :        "Set the EXEC timeout\n"
    2841             :        "Timeout in minutes\n"
    2842             :        "Timeout in seconds\n")
    2843             : {
    2844           0 :         int idx_number = 1;
    2845           0 :         int idx_number_2 = 2;
    2846           0 :         return exec_timeout(vty, argv[idx_number]->arg,
    2847           0 :                             argv[idx_number_2]->arg);
    2848             : }
    2849             : 
    2850           0 : DEFUN (no_exec_timeout,
    2851             :        no_exec_timeout_cmd,
    2852             :        "no exec-timeout",
    2853             :        NO_STR
    2854             :        "Set the EXEC timeout\n")
    2855             : {
    2856           0 :         return exec_timeout(vty, NULL, NULL);
    2857             : }
    2858             : 
    2859             : /* Set vty access class. */
    2860           0 : DEFUN (vty_access_class,
    2861             :        vty_access_class_cmd,
    2862             :        "access-class WORD",
    2863             :        "Filter connections based on an IP access list\n"
    2864             :        "IP access list\n")
    2865             : {
    2866           0 :         int idx_word = 1;
    2867           0 :         if (vty_accesslist_name)
    2868           0 :                 XFREE(MTYPE_VTY, vty_accesslist_name);
    2869             : 
    2870           0 :         vty_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
    2871             : 
    2872           0 :         return CMD_SUCCESS;
    2873             : }
    2874             : 
    2875             : /* Clear vty access class. */
    2876           0 : DEFUN (no_vty_access_class,
    2877             :        no_vty_access_class_cmd,
    2878             :        "no access-class [WORD]",
    2879             :        NO_STR
    2880             :        "Filter connections based on an IP access list\n"
    2881             :        "IP access list\n")
    2882             : {
    2883           0 :         int idx_word = 2;
    2884           0 :         const char *accesslist = (argc == 3) ? argv[idx_word]->arg : NULL;
    2885           0 :         if (!vty_accesslist_name
    2886           0 :             || (argc == 3 && strcmp(vty_accesslist_name, accesslist))) {
    2887           0 :                 vty_out(vty, "Access-class is not currently applied to vty\n");
    2888           0 :                 return CMD_WARNING_CONFIG_FAILED;
    2889             :         }
    2890             : 
    2891           0 :         XFREE(MTYPE_VTY, vty_accesslist_name);
    2892             : 
    2893           0 :         vty_accesslist_name = NULL;
    2894             : 
    2895           0 :         return CMD_SUCCESS;
    2896             : }
    2897             : 
    2898             : /* Set vty access class. */
    2899           0 : DEFUN (vty_ipv6_access_class,
    2900             :        vty_ipv6_access_class_cmd,
    2901             :        "ipv6 access-class WORD",
    2902             :        IPV6_STR
    2903             :        "Filter connections based on an IP access list\n"
    2904             :        "IPv6 access list\n")
    2905             : {
    2906           0 :         int idx_word = 2;
    2907           0 :         if (vty_ipv6_accesslist_name)
    2908           0 :                 XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
    2909             : 
    2910           0 :         vty_ipv6_accesslist_name = XSTRDUP(MTYPE_VTY, argv[idx_word]->arg);
    2911             : 
    2912           0 :         return CMD_SUCCESS;
    2913             : }
    2914             : 
    2915             : /* Clear vty access class. */
    2916           0 : DEFUN (no_vty_ipv6_access_class,
    2917             :        no_vty_ipv6_access_class_cmd,
    2918             :        "no ipv6 access-class [WORD]",
    2919             :        NO_STR
    2920             :        IPV6_STR
    2921             :        "Filter connections based on an IP access list\n"
    2922             :        "IPv6 access list\n")
    2923             : {
    2924           0 :         int idx_word = 3;
    2925           0 :         const char *accesslist = (argc == 4) ? argv[idx_word]->arg : NULL;
    2926             : 
    2927           0 :         if (!vty_ipv6_accesslist_name
    2928           0 :             || (argc == 4 && strcmp(vty_ipv6_accesslist_name, accesslist))) {
    2929           0 :                 vty_out(vty,
    2930             :                         "IPv6 access-class is not currently applied to vty\n");
    2931           0 :                 return CMD_WARNING_CONFIG_FAILED;
    2932             :         }
    2933             : 
    2934           0 :         XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
    2935             : 
    2936           0 :         vty_ipv6_accesslist_name = NULL;
    2937             : 
    2938           0 :         return CMD_SUCCESS;
    2939             : }
    2940             : 
    2941             : /* vty login. */
    2942           0 : DEFUN (vty_login,
    2943             :        vty_login_cmd,
    2944             :        "login",
    2945             :        "Enable password checking\n")
    2946             : {
    2947           0 :         no_password_check = 0;
    2948           0 :         return CMD_SUCCESS;
    2949             : }
    2950             : 
    2951           0 : DEFUN (no_vty_login,
    2952             :        no_vty_login_cmd,
    2953             :        "no login",
    2954             :        NO_STR
    2955             :        "Enable password checking\n")
    2956             : {
    2957           0 :         no_password_check = 1;
    2958           0 :         return CMD_SUCCESS;
    2959             : }
    2960             : 
    2961           2 : DEFUN (service_advanced_vty,
    2962             :        service_advanced_vty_cmd,
    2963             :        "service advanced-vty",
    2964             :        "Set up miscellaneous service\n"
    2965             :        "Enable advanced mode vty interface\n")
    2966             : {
    2967           2 :         host.advanced = 1;
    2968           2 :         return CMD_SUCCESS;
    2969             : }
    2970             : 
    2971           0 : DEFUN (no_service_advanced_vty,
    2972             :        no_service_advanced_vty_cmd,
    2973             :        "no service advanced-vty",
    2974             :        NO_STR
    2975             :        "Set up miscellaneous service\n"
    2976             :        "Enable advanced mode vty interface\n")
    2977             : {
    2978           0 :         host.advanced = 0;
    2979           0 :         return CMD_SUCCESS;
    2980             : }
    2981             : 
    2982           0 : DEFUN_NOSH(terminal_monitor,
    2983             :            terminal_monitor_cmd,
    2984             :            "terminal monitor [detach]",
    2985             :            "Set terminal line parameters\n"
    2986             :            "Copy debug output to the current terminal line\n"
    2987             :            "Keep logging feed open independent of VTY session\n")
    2988             : {
    2989           0 :         int fd_ret = -1;
    2990             : 
    2991           0 :         if (vty->type != VTY_SHELL_SERV) {
    2992           0 :                 vty_out(vty, "%% not supported\n");
    2993           0 :                 return CMD_WARNING;
    2994             :         }
    2995             : 
    2996           0 :         if (argc == 3) {
    2997           0 :                 struct zlog_live_cfg detach_log = {};
    2998             : 
    2999           0 :                 zlog_live_open(&detach_log, LOG_DEBUG, &fd_ret);
    3000           0 :                 zlog_live_disown(&detach_log);
    3001             :         } else
    3002           0 :                 zlog_live_open(&vty->live_log, LOG_DEBUG, &fd_ret);
    3003             : 
    3004           0 :         if (fd_ret == -1) {
    3005           0 :                 vty_out(vty, "%% error opening live log: %m\n");
    3006           0 :                 return CMD_WARNING;
    3007             :         }
    3008             : 
    3009           0 :         vty_pass_fd(vty, fd_ret);
    3010           0 :         return CMD_SUCCESS;
    3011             : }
    3012             : 
    3013           0 : DEFUN_NOSH(no_terminal_monitor,
    3014             :            no_terminal_monitor_cmd,
    3015             :            "no terminal monitor",
    3016             :            NO_STR
    3017             :            "Set terminal line parameters\n"
    3018             :            "Copy debug output to the current terminal line\n")
    3019             : {
    3020           0 :         zlog_live_close(&vty->live_log);
    3021           0 :         return CMD_SUCCESS;
    3022             : }
    3023             : 
    3024           0 : DEFUN_NOSH(terminal_no_monitor,
    3025             :            terminal_no_monitor_cmd,
    3026             :            "terminal no monitor",
    3027             :            "Set terminal line parameters\n"
    3028             :            NO_STR
    3029             :            "Copy debug output to the current terminal line\n")
    3030             : {
    3031           0 :         return no_terminal_monitor(self, vty, argc, argv);
    3032             : }
    3033             : 
    3034             : 
    3035           0 : DEFUN_NOSH (show_history,
    3036             :        show_history_cmd,
    3037             :        "show history",
    3038             :        SHOW_STR
    3039             :        "Display the session command history\n")
    3040             : {
    3041           0 :         int index;
    3042             : 
    3043           0 :         for (index = vty->hindex + 1; index != vty->hindex;) {
    3044           0 :                 if (index == VTY_MAXHIST) {
    3045           0 :                         index = 0;
    3046           0 :                         continue;
    3047             :                 }
    3048             : 
    3049           0 :                 if (vty->hist[index] != NULL)
    3050           0 :                         vty_out(vty, "  %s\n", vty->hist[index]);
    3051             : 
    3052           0 :                 index++;
    3053             :         }
    3054             : 
    3055           0 :         return CMD_SUCCESS;
    3056             : }
    3057             : 
    3058             : /* vty login. */
    3059           0 : DEFPY (log_commands,
    3060             :        log_commands_cmd,
    3061             :        "[no] log commands",
    3062             :        NO_STR
    3063             :        "Logging control\n"
    3064             :        "Log all commands\n")
    3065             : {
    3066           0 :         if (no) {
    3067           0 :                 if (do_log_commands_perm) {
    3068           0 :                         vty_out(vty,
    3069             :                                 "Daemon started with permanent logging turned on for commands, ignoring\n");
    3070           0 :                         return CMD_WARNING;
    3071             :                 }
    3072             : 
    3073           0 :                 do_log_commands = false;
    3074             :         } else
    3075           0 :                 do_log_commands = true;
    3076             : 
    3077             :         return CMD_SUCCESS;
    3078             : }
    3079             : 
    3080             : /* Display current configuration. */
    3081           0 : static int vty_config_write(struct vty *vty)
    3082             : {
    3083           0 :         vty_frame(vty, "line vty\n");
    3084             : 
    3085           0 :         if (vty_accesslist_name)
    3086           0 :                 vty_out(vty, " access-class %s\n", vty_accesslist_name);
    3087             : 
    3088           0 :         if (vty_ipv6_accesslist_name)
    3089           0 :                 vty_out(vty, " ipv6 access-class %s\n",
    3090             :                         vty_ipv6_accesslist_name);
    3091             : 
    3092             :         /* exec-timeout */
    3093           0 :         if (vty_timeout_val != VTY_TIMEOUT_DEFAULT)
    3094           0 :                 vty_out(vty, " exec-timeout %ld %ld\n", vty_timeout_val / 60,
    3095             :                         vty_timeout_val % 60);
    3096             : 
    3097             :         /* login */
    3098           0 :         if (no_password_check)
    3099           0 :                 vty_out(vty, " no login\n");
    3100             : 
    3101           0 :         vty_endframe(vty, "exit\n");
    3102             : 
    3103           0 :         if (do_log_commands)
    3104           0 :                 vty_out(vty, "log commands\n");
    3105             : 
    3106           0 :         vty_out(vty, "!\n");
    3107             : 
    3108           0 :         return CMD_SUCCESS;
    3109             : }
    3110             : 
    3111             : static int vty_config_write(struct vty *vty);
    3112             : struct cmd_node vty_node = {
    3113             :         .name = "vty",
    3114             :         .node = VTY_NODE,
    3115             :         .parent_node = CONFIG_NODE,
    3116             :         .prompt = "%s(config-line)# ",
    3117             :         .config_write = vty_config_write,
    3118             : };
    3119             : 
    3120             : /* Reset all VTY status. */
    3121           4 : void vty_reset(void)
    3122             : {
    3123           4 :         struct vty *vty;
    3124             : 
    3125           8 :         frr_each_safe (vtys, vty_sessions, vty) {
    3126           0 :                 buffer_reset(vty->lbuf);
    3127           0 :                 buffer_reset(vty->obuf);
    3128           0 :                 vty->status = VTY_CLOSE;
    3129           0 :                 vty_close(vty);
    3130             :         }
    3131             : 
    3132           4 :         vty_timeout_val = VTY_TIMEOUT_DEFAULT;
    3133             : 
    3134           4 :         XFREE(MTYPE_VTY, vty_accesslist_name);
    3135           4 :         XFREE(MTYPE_VTY, vty_ipv6_accesslist_name);
    3136           4 : }
    3137             : 
    3138           4 : static void vty_save_cwd(void)
    3139             : {
    3140           4 :         char *c;
    3141             : 
    3142           4 :         c = getcwd(vty_cwd, sizeof(vty_cwd));
    3143             : 
    3144           4 :         if (!c) {
    3145             :                 /*
    3146             :                  * At this point if these go wrong, more than likely
    3147             :                  * the whole world is coming down around us
    3148             :                  * Hence not worrying about it too much.
    3149             :                  */
    3150           0 :                 if (chdir(SYSCONFDIR)) {
    3151           0 :                         flog_err_sys(EC_LIB_SYSTEM_CALL,
    3152             :                                      "Failure to chdir to %s, errno: %d",
    3153             :                                      SYSCONFDIR, errno);
    3154           0 :                         exit(-1);
    3155             :                 }
    3156           0 :                 if (getcwd(vty_cwd, sizeof(vty_cwd)) == NULL) {
    3157           0 :                         flog_err_sys(EC_LIB_SYSTEM_CALL,
    3158             :                                      "Failure to getcwd, errno: %d", errno);
    3159           0 :                         exit(-1);
    3160             :                 }
    3161             :         }
    3162           4 : }
    3163             : 
    3164           0 : char *vty_get_cwd(void)
    3165             : {
    3166           0 :         return vty_cwd;
    3167             : }
    3168             : 
    3169           0 : int vty_shell(struct vty *vty)
    3170             : {
    3171           0 :         return vty->type == VTY_SHELL ? 1 : 0;
    3172             : }
    3173             : 
    3174           0 : int vty_shell_serv(struct vty *vty)
    3175             : {
    3176           0 :         return vty->type == VTY_SHELL_SERV ? 1 : 0;
    3177             : }
    3178             : 
    3179           0 : void vty_init_vtysh(void)
    3180             : {
    3181             :         /* currently nothing to do, but likely to have future use */
    3182           0 : }
    3183             : 
    3184             : /* Install vty's own commands like `who' command. */
    3185           4 : void vty_init(struct thread_master *master_thread, bool do_command_logging)
    3186             : {
    3187             :         /* For further configuration read, preserve current directory. */
    3188           4 :         vty_save_cwd();
    3189             : 
    3190           4 :         vty_master = master_thread;
    3191             : 
    3192           4 :         atexit(vty_stdio_atexit);
    3193             : 
    3194             :         /* Install bgp top node. */
    3195           4 :         install_node(&vty_node);
    3196             : 
    3197           4 :         install_element(VIEW_NODE, &config_who_cmd);
    3198           4 :         install_element(VIEW_NODE, &show_history_cmd);
    3199           4 :         install_element(CONFIG_NODE, &line_vty_cmd);
    3200           4 :         install_element(CONFIG_NODE, &service_advanced_vty_cmd);
    3201           4 :         install_element(CONFIG_NODE, &no_service_advanced_vty_cmd);
    3202           4 :         install_element(CONFIG_NODE, &show_history_cmd);
    3203           4 :         install_element(CONFIG_NODE, &log_commands_cmd);
    3204             : 
    3205           4 :         if (do_command_logging) {
    3206           0 :                 do_log_commands = true;
    3207           0 :                 do_log_commands_perm = true;
    3208             :         }
    3209             : 
    3210           4 :         install_element(ENABLE_NODE, &terminal_monitor_cmd);
    3211           4 :         install_element(ENABLE_NODE, &terminal_no_monitor_cmd);
    3212           4 :         install_element(ENABLE_NODE, &no_terminal_monitor_cmd);
    3213             : 
    3214           4 :         install_default(VTY_NODE);
    3215           4 :         install_element(VTY_NODE, &exec_timeout_min_cmd);
    3216           4 :         install_element(VTY_NODE, &exec_timeout_sec_cmd);
    3217           4 :         install_element(VTY_NODE, &no_exec_timeout_cmd);
    3218           4 :         install_element(VTY_NODE, &vty_access_class_cmd);
    3219           4 :         install_element(VTY_NODE, &no_vty_access_class_cmd);
    3220           4 :         install_element(VTY_NODE, &vty_login_cmd);
    3221           4 :         install_element(VTY_NODE, &no_vty_login_cmd);
    3222           4 :         install_element(VTY_NODE, &vty_ipv6_access_class_cmd);
    3223           4 :         install_element(VTY_NODE, &no_vty_ipv6_access_class_cmd);
    3224           4 : }
    3225             : 
    3226           4 : void vty_terminate(void)
    3227             : {
    3228           4 :         struct vty *vty;
    3229           4 :         struct vty_serv *vtyserv;
    3230             : 
    3231           4 :         memset(vty_cwd, 0x00, sizeof(vty_cwd));
    3232             : 
    3233           4 :         vty_reset();
    3234             : 
    3235             :         /* default state of vty_sessions is initialized & empty. */
    3236           4 :         vtys_fini(vty_sessions);
    3237           4 :         vtys_init(vty_sessions);
    3238             : 
    3239             :         /* vty_reset() doesn't close vtysh sessions */
    3240           8 :         frr_each_safe (vtys, vtysh_sessions, vty) {
    3241           0 :                 buffer_reset(vty->lbuf);
    3242           0 :                 buffer_reset(vty->obuf);
    3243           0 :                 vty->status = VTY_CLOSE;
    3244           0 :                 vty_close(vty);
    3245             :         }
    3246             : 
    3247           4 :         vtys_fini(vtysh_sessions);
    3248           4 :         vtys_init(vtysh_sessions);
    3249             : 
    3250          16 :         while ((vtyserv = vtyservs_pop(vty_servs))) {
    3251          12 :                 THREAD_OFF(vtyserv->t_accept);
    3252          12 :                 close(vtyserv->sock);
    3253          16 :                 XFREE(MTYPE_VTY_SERV, vtyserv);
    3254             :         }
    3255             : 
    3256           4 :         vtyservs_fini(vty_servs);
    3257           4 :         vtyservs_init(vty_servs);
    3258           4 : }

Generated by: LCOV version v1.16-topotato