back to topotato report
topotato coverage report
Current view: top level - lib - vty.c (source / functions) Hit Total Coverage
Test: test_bgp_ecmp_enhe.py::BGP_Unnumbered_ECMP Lines: 401 1940 20.7 %
Date: 2023-11-16 17:19:14 Functions: 39 264 14.8 %

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

Generated by: LCOV version v1.16-topotato