LCOV - code coverage report
Current view: top level - vlib/unix - cli.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 56 1565 3.6 %
Date: 2023-07-05 22:20:52 Functions: 43 95 45.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2015 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : /*
      16             :  * cli.c: Unix stdin/socket CLI.
      17             :  *
      18             :  * Copyright (c) 2008 Eliot Dresselhaus
      19             :  *
      20             :  * Permission is hereby granted, free of charge, to any person obtaining
      21             :  * a copy of this software and associated documentation files (the
      22             :  * "Software"), to deal in the Software without restriction, including
      23             :  * without limitation the rights to use, copy, modify, merge, publish,
      24             :  * distribute, sublicense, and/or sell copies of the Software, and to
      25             :  * permit persons to whom the Software is furnished to do so, subject to
      26             :  * the following conditions:
      27             :  *
      28             :  * The above copyright notice and this permission notice shall be
      29             :  * included in all copies or substantial portions of the Software.
      30             :  *
      31             :  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      32             :  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      33             :  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      34             :  *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
      35             :  *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      36             :  *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
      37             :  *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      38             :  */
      39             : /**
      40             :  * @file
      41             :  * @brief Unix stdin/socket command line interface.
      42             :  * Provides a command line interface so humans can interact with VPP.
      43             :  * This is predominantly a debugging and testing mechanism.
      44             :  */
      45             : /*? %%clicmd:group_label Command line session %% ?*/
      46             : /*? %%syscfg:group_label Command line session %% ?*/
      47             : 
      48             : #include <vlib/vlib.h>
      49             : #include <vlib/unix/unix.h>
      50             : 
      51             : #include <ctype.h>
      52             : #include <fcntl.h>
      53             : #include <sys/stat.h>
      54             : #include <termios.h>
      55             : #include <signal.h>
      56             : #include <unistd.h>
      57             : #include <arpa/telnet.h>
      58             : #include <sys/ioctl.h>
      59             : #include <sys/types.h>
      60             : #include <unistd.h>
      61             : #include <limits.h>
      62             : #include <netinet/tcp.h>
      63             : #include <math.h>
      64             : #include <vppinfra/macros.h>
      65             : #include <vppinfra/format_table.h>
      66             : 
      67             : /** ANSI escape code. */
      68             : #define ESC "\x1b"
      69             : 
      70             : /** ANSI Control Sequence Introducer. */
      71             : #define CSI ESC "["
      72             : 
      73             : /** ANSI clear screen. */
      74             : #define ANSI_CLEAR      CSI "2J" CSI "1;1H"
      75             : /** ANSI reset color settings. */
      76             : #define ANSI_RESET      CSI "0m"
      77             : /** ANSI Start bold text. */
      78             : #define ANSI_BOLD       CSI "1m"
      79             : /** ANSI Stop bold text. */
      80             : #define ANSI_DIM        CSI "2m"
      81             : /** ANSI Start dark red text. */
      82             : #define ANSI_DRED       ANSI_DIM CSI "31m"
      83             : /** ANSI Start bright red text. */
      84             : #define ANSI_BRED       ANSI_BOLD CSI "31m"
      85             : /** ANSI clear line cursor is on. */
      86             : #define ANSI_CLEARLINE  CSI "2K"
      87             : /** ANSI scroll screen down one line. */
      88             : #define ANSI_SCROLLDN   CSI "1T"
      89             : /** ANSI save cursor position. */
      90             : #define ANSI_SAVECURSOR CSI "s"
      91             : /** ANSI restore cursor position if previously saved. */
      92             : #define ANSI_RESTCURSOR CSI "u"
      93             : 
      94             : /** Maximum depth into a byte stream from which to compile a Telnet
      95             :  * protocol message. This is a safety measure. */
      96             : #define UNIX_CLI_MAX_DEPTH_TELNET 32
      97             : 
      98             : /** Maximum terminal width we will accept */
      99             : #define UNIX_CLI_MAX_TERMINAL_WIDTH 512
     100             : /** Maximum terminal height we will accept */
     101             : #define UNIX_CLI_MAX_TERMINAL_HEIGHT 512
     102             : /** Default terminal height */
     103             : #define UNIX_CLI_DEFAULT_TERMINAL_HEIGHT 24
     104             : /** Default terminal width */
     105             : #define UNIX_CLI_DEFAULT_TERMINAL_WIDTH 80
     106             : 
     107             : /** A CLI banner line. */
     108             : typedef struct
     109             : {
     110             :   u8 *line;     /**< The line to print. */
     111             :   u32 length;   /**< The length of the line without terminating NUL. */
     112             : } unix_cli_banner_t;
     113             : 
     114             : #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 }
     115             : /** Plain welcome banner. */
     116             : static unix_cli_banner_t unix_cli_banner[] = {
     117             :   _("    _______    _        _   _____  ___ \n"),
     118             :   _(" __/ __/ _ \\  (_)__    | | / / _ \\/ _ \\\n"),
     119             :   _(" _/ _// // / / / _ \\   | |/ / ___/ ___/\n"),
     120             :   _(" /_/ /____(_)_/\\___/   |___/_/  /_/    \n"),
     121             :   _("\n")
     122             : };
     123             : 
     124             : /** ANSI color welcome banner. */
     125             : static unix_cli_banner_t unix_cli_banner_color[] = {
     126             :   _(ANSI_BRED "    _______    _     " ANSI_RESET "   _   _____  ___ \n"),
     127             :   _(ANSI_BRED " __/ __/ _ \\  (_)__ " ANSI_RESET "   | | / / _ \\/ _ \\\n"),
     128             :   _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET "   | |/ / ___/ ___/\n"),
     129             :   _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET "   |___/_/  /_/    \n"),
     130             :   _("\n")
     131             : };
     132             : 
     133             : #undef _
     134             : 
     135             : /** Pager line index */
     136             : typedef struct
     137             : {
     138             :   /** Index into pager_vector */
     139             :   u32 line;
     140             : 
     141             :   /** Offset of the string in the line */
     142             :   u32 offset;
     143             : 
     144             :   /** Length of the string in the line */
     145             :   u32 length;
     146             : } unix_cli_pager_index_t;
     147             : 
     148             : 
     149             : /** Unix CLI session. */
     150             : typedef struct
     151             : {
     152             :   /** The file index held by unix.c */
     153             :   u32 clib_file_index;
     154             : 
     155             :   /** Vector of output pending write to file descriptor. */
     156             :   u8 *output_vector;
     157             : 
     158             :   /** Vector of input saved by Unix input node to be processed by
     159             :      CLI process. */
     160             :   u8 *input_vector;
     161             : 
     162             :   /** This session has command history. */
     163             :   u8 has_history;
     164             :   /** Array of vectors of commands in the history. */
     165             :   u8 **command_history;
     166             :   /** The command currently pointed at by the history cursor. */
     167             :   u8 *current_command;
     168             :   /** How far from the end of the history array the user has browsed. */
     169             :   i32 excursion;
     170             : 
     171             :   /** Maximum number of history entries this session will store. */
     172             :   u32 history_limit;
     173             : 
     174             :   /** Current command line counter */
     175             :   u32 command_number;
     176             : 
     177             :   /** The string being searched for in the history. */
     178             :   u8 *search_key;
     179             :   /** If non-zero then the CLI is searching in the history array.
     180             :    * - @c -1 means search backwards.
     181             :    * - @c 1 means search forwards.
     182             :    */
     183             :   int search_mode;
     184             : 
     185             :   /** Position of the insert cursor on the current input line */
     186             :   u32 cursor;
     187             : 
     188             :   /** Line mode or char mode */
     189             :   u8 line_mode;
     190             : 
     191             :   /** Set if the CRLF mode wants CR + LF */
     192             :   u8 crlf_mode;
     193             : 
     194             :   /** Can we do ANSI output? */
     195             :   u8 ansi_capable;
     196             : 
     197             :   /** Has the session started? */
     198             :   u8 started;
     199             : 
     200             :   /** Disable the pager? */
     201             :   u8 no_pager;
     202             : 
     203             :   /** Whether the session is interactive or not.
     204             :    * Controls things like initial banner, the CLI prompt etc.  */
     205             :   u8 is_interactive;
     206             : 
     207             :   /** Whether the session is attached to a socket. */
     208             :   u8 is_socket;
     209             : 
     210             :   /** If EPIPE has been detected, prevent further write-related
     211             :    * activity on the descriptor.
     212             :    */
     213             :   u8 has_epipe;
     214             : 
     215             :   /** Pager buffer */
     216             :   u8 **pager_vector;
     217             : 
     218             :   /** Index of line fragments in the pager buffer */
     219             :   unix_cli_pager_index_t *pager_index;
     220             : 
     221             :   /** Line number of top of page */
     222             :   u32 pager_start;
     223             : 
     224             :   /** Terminal width */
     225             :   u32 width;
     226             : 
     227             :   /** Terminal height */
     228             :   u32 height;
     229             : 
     230             :   /** Process node identifier */
     231             :   u32 process_node_index;
     232             : 
     233             :   /** The current direction of cursor travel.
     234             :    *  This is important since when advancing left-to-right, at the
     235             :    *  right hand edge of the console the terminal typically defers
     236             :    *  wrapping the cursor to the next line until a character is
     237             :    *  actually displayed.
     238             :    *  This messes up our heuristic for whether to use ANSI to return
     239             :    *  the cursor to the end of the line and instead we have to
     240             :    *  nudge the cursor to the next line.
     241             :    *  A Value of @c 0 means we're advancing left-to-right; @c 1 means
     242             :    *  the opposite.
     243             :    */
     244             :   u8 cursor_direction;
     245             : 
     246             :   /** Macro tables for this session */
     247             :   clib_macro_main_t macro_main;
     248             : 
     249             :   /** Session name */
     250             :   u8 *name;
     251             : } unix_cli_file_t;
     252             : 
     253             : /** Resets the pager buffer and other data.
     254             :  * @param f The CLI session whose pager needs to be reset.
     255             :  */
     256             : always_inline void
     257           0 : unix_cli_pager_reset (unix_cli_file_t * f)
     258             : {
     259             :   u8 **p;
     260             : 
     261           0 :   f->pager_start = 0;
     262             : 
     263           0 :   vec_free (f->pager_index);
     264           0 :   f->pager_index = 0;
     265             : 
     266           0 :   vec_foreach (p, f->pager_vector)
     267             :   {
     268           0 :     vec_free (*p);
     269             :   }
     270           0 :   vec_free (f->pager_vector);
     271           0 :   f->pager_vector = 0;
     272           0 : }
     273             : 
     274             : /** Release storage used by a CLI session.
     275             :  * @param f The CLI session whose storage needs to be released.
     276             :  */
     277             : always_inline void
     278           0 : unix_cli_file_free (unix_cli_file_t * f)
     279             : {
     280           0 :   vec_free (f->output_vector);
     281           0 :   vec_free (f->input_vector);
     282           0 :   vec_free (f->name);
     283           0 :   unix_cli_pager_reset (f);
     284           0 : }
     285             : 
     286             : /** CLI actions */
     287             : typedef enum
     288             : {
     289             :   UNIX_CLI_PARSE_ACTION_NOACTION = 0,   /**< No action */
     290             :   UNIX_CLI_PARSE_ACTION_CRLF,           /**< Carriage return, newline or enter */
     291             :   UNIX_CLI_PARSE_ACTION_TAB,            /**< Tab key */
     292             :   UNIX_CLI_PARSE_ACTION_ERASE,          /**< Erase cursor left */
     293             :   UNIX_CLI_PARSE_ACTION_ERASERIGHT,     /**< Erase cursor right */
     294             :   UNIX_CLI_PARSE_ACTION_UP,             /**< Up arrow */
     295             :   UNIX_CLI_PARSE_ACTION_DOWN,           /**< Down arrow */
     296             :   UNIX_CLI_PARSE_ACTION_LEFT,           /**< Left arrow */
     297             :   UNIX_CLI_PARSE_ACTION_RIGHT,          /**< Right arrow */
     298             :   UNIX_CLI_PARSE_ACTION_HOME,           /**< Home key (jump to start of line) */
     299             :   UNIX_CLI_PARSE_ACTION_END,            /**< End key (jump to end of line) */
     300             :   UNIX_CLI_PARSE_ACTION_WORDLEFT,       /**< Jump cursor to start of left word */
     301             :   UNIX_CLI_PARSE_ACTION_WORDRIGHT,      /**< Jump cursor to start of right word */
     302             :   UNIX_CLI_PARSE_ACTION_ERASELINELEFT,  /**< Erase line to left of cursor */
     303             :   UNIX_CLI_PARSE_ACTION_ERASELINERIGHT, /**< Erase line to right & including cursor */
     304             :   UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT,  /**< Erase word left */
     305             :   UNIX_CLI_PARSE_ACTION_CLEAR,          /**< Clear the terminal */
     306             :   UNIX_CLI_PARSE_ACTION_REVSEARCH,      /**< Search backwards in command history */
     307             :   UNIX_CLI_PARSE_ACTION_FWDSEARCH,      /**< Search forwards in command history */
     308             :   UNIX_CLI_PARSE_ACTION_YANK,           /**< Undo last erase action */
     309             :   UNIX_CLI_PARSE_ACTION_TELNETIAC,      /**< Telnet control code */
     310             : 
     311             :   UNIX_CLI_PARSE_ACTION_PAGER_CRLF,     /**< Enter pressed (CR, CRLF, LF, etc) */
     312             :   UNIX_CLI_PARSE_ACTION_PAGER_QUIT,     /**< Exit the pager session */
     313             :   UNIX_CLI_PARSE_ACTION_PAGER_NEXT,     /**< Scroll to next page */
     314             :   UNIX_CLI_PARSE_ACTION_PAGER_DN,       /**< Scroll to next line */
     315             :   UNIX_CLI_PARSE_ACTION_PAGER_UP,       /**< Scroll to previous line */
     316             :   UNIX_CLI_PARSE_ACTION_PAGER_TOP,      /**< Scroll to first line */
     317             :   UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM,   /**< Scroll to last line */
     318             :   UNIX_CLI_PARSE_ACTION_PAGER_PGDN,     /**< Scroll to next page */
     319             :   UNIX_CLI_PARSE_ACTION_PAGER_PGUP,     /**< Scroll to previous page */
     320             :   UNIX_CLI_PARSE_ACTION_PAGER_REDRAW,   /**< Clear and redraw the page on the terminal */
     321             :   UNIX_CLI_PARSE_ACTION_PAGER_SEARCH,   /**< Search the pager buffer */
     322             : 
     323             :   UNIX_CLI_PARSE_ACTION_PARTIALMATCH,   /**< Action parser found a partial match */
     324             :   UNIX_CLI_PARSE_ACTION_NOMATCH         /**< Action parser did not find any match */
     325             : } unix_cli_parse_action_t;
     326             : 
     327             : /** @brief Mapping of input buffer strings to action values.
     328             :  * @note This won't work as a hash since we need to be able to do
     329             :  *       partial matches on the string.
     330             :  */
     331             : typedef struct
     332             : {
     333             :   u8 *input;                        /**< Input string to match. */
     334             :   u32 len;                          /**< Length of input without final NUL. */
     335             :   unix_cli_parse_action_t action;   /**< Action to take when matched. */
     336             : } unix_cli_parse_actions_t;
     337             : 
     338             : /** @brief Given a capital ASCII letter character return a @c NUL terminated
     339             :  * string with the control code for that letter.
     340             :  *
     341             :  * @param c An ASCII character.
     342             :  * @return A @c NUL terminated string of type @c u8[].
     343             :  *
     344             :  * @par Example
     345             :  *     @c CTL('A') returns <code>{ 0x01, 0x00 }</code> as a @c u8[].
     346             :  */
     347             : #define CTL(c) (u8[]){ (c) - '@', 0 }
     348             : 
     349             : #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) }
     350             : /**
     351             :  * Patterns to match on a CLI input stream.
     352             :  * @showinitializer
     353             :  */
     354             : static unix_cli_parse_actions_t unix_cli_parse_strings[] = {
     355             :   /* Line handling */
     356             :   _("\r\n", UNIX_CLI_PARSE_ACTION_CRLF),      /* Must be before '\r' */
     357             :   _("\n", UNIX_CLI_PARSE_ACTION_CRLF),
     358             :   _("\r\0", UNIX_CLI_PARSE_ACTION_CRLF),      /* Telnet does this */
     359             :   _("\r", UNIX_CLI_PARSE_ACTION_CRLF),
     360             : 
     361             :   /* Unix shell control codes */
     362             :   _(CTL ('B'), UNIX_CLI_PARSE_ACTION_LEFT),
     363             :   _(CTL ('F'), UNIX_CLI_PARSE_ACTION_RIGHT),
     364             :   _(CTL ('P'), UNIX_CLI_PARSE_ACTION_UP),
     365             :   _(CTL ('N'), UNIX_CLI_PARSE_ACTION_DOWN),
     366             :   _(CTL ('A'), UNIX_CLI_PARSE_ACTION_HOME),
     367             :   _(CTL ('E'), UNIX_CLI_PARSE_ACTION_END),
     368             :   _(CTL ('D'), UNIX_CLI_PARSE_ACTION_ERASERIGHT),
     369             :   _(CTL ('U'), UNIX_CLI_PARSE_ACTION_ERASELINELEFT),
     370             :   _(CTL ('K'), UNIX_CLI_PARSE_ACTION_ERASELINERIGHT),
     371             :   _(CTL ('W'), UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT),
     372             :   _(CTL ('Y'), UNIX_CLI_PARSE_ACTION_YANK),
     373             :   _(CTL ('L'), UNIX_CLI_PARSE_ACTION_CLEAR),
     374             :   _(ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* Alt-B */
     375             :   _(ESC "f", UNIX_CLI_PARSE_ACTION_WORDRIGHT),        /* Alt-F */
     376             :   _("\b", UNIX_CLI_PARSE_ACTION_ERASE),       /* ^H */
     377             :   _("\x7f", UNIX_CLI_PARSE_ACTION_ERASE),     /* Backspace */
     378             :   _("\t", UNIX_CLI_PARSE_ACTION_TAB), /* ^I */
     379             : 
     380             :   /* VT100 Normal mode - Broadest support */
     381             :   _(CSI "A", UNIX_CLI_PARSE_ACTION_UP),
     382             :   _(CSI "B", UNIX_CLI_PARSE_ACTION_DOWN),
     383             :   _(CSI "C", UNIX_CLI_PARSE_ACTION_RIGHT),
     384             :   _(CSI "D", UNIX_CLI_PARSE_ACTION_LEFT),
     385             :   _(CSI "H", UNIX_CLI_PARSE_ACTION_HOME),
     386             :   _(CSI "F", UNIX_CLI_PARSE_ACTION_END),
     387             :   _(CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT),      /* Delete */
     388             :   _(CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT),      /* C-Left */
     389             :   _(CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT),     /* C-Right */
     390             : 
     391             :   /* VT100 Application mode - Some Gnome Terminal functions use these */
     392             :   _(ESC "OA", UNIX_CLI_PARSE_ACTION_UP),
     393             :   _(ESC "OB", UNIX_CLI_PARSE_ACTION_DOWN),
     394             :   _(ESC "OC", UNIX_CLI_PARSE_ACTION_RIGHT),
     395             :   _(ESC "OD", UNIX_CLI_PARSE_ACTION_LEFT),
     396             :   _(ESC "OH", UNIX_CLI_PARSE_ACTION_HOME),
     397             :   _(ESC "OF", UNIX_CLI_PARSE_ACTION_END),
     398             : 
     399             :   /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */
     400             :   _(CSI "1~", UNIX_CLI_PARSE_ACTION_HOME),
     401             :   _(CSI "4~", UNIX_CLI_PARSE_ACTION_END),
     402             : 
     403             :   /* Emacs-ish history search */
     404             :   _(CTL ('S'), UNIX_CLI_PARSE_ACTION_FWDSEARCH),
     405             :   _(CTL ('R'), UNIX_CLI_PARSE_ACTION_REVSEARCH),
     406             : 
     407             :   /* Other protocol things */
     408             :   _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
     409             :   _("\0", UNIX_CLI_PARSE_ACTION_NOACTION),    /* NUL */
     410             :   _(NULL, UNIX_CLI_PARSE_ACTION_NOMATCH)
     411             : };
     412             : 
     413             : /**
     414             :  * Patterns to match when a CLI session is in the pager.
     415             :  * @showinitializer
     416             :  */
     417             : static unix_cli_parse_actions_t unix_cli_parse_pager[] = {
     418             :   /* Line handling */
     419             :   _("\r\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF),        /* Must be before '\r' */
     420             :   _("\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF),
     421             :   _("\r\0", UNIX_CLI_PARSE_ACTION_PAGER_CRLF),        /* Telnet does this */
     422             :   _("\r", UNIX_CLI_PARSE_ACTION_PAGER_CRLF),
     423             : 
     424             :   /* Pager commands */
     425             :   _(" ", UNIX_CLI_PARSE_ACTION_PAGER_NEXT),
     426             :   _("q", UNIX_CLI_PARSE_ACTION_PAGER_QUIT),
     427             :   _(CTL ('L'), UNIX_CLI_PARSE_ACTION_PAGER_REDRAW),
     428             :   _(CTL ('R'), UNIX_CLI_PARSE_ACTION_PAGER_REDRAW),
     429             :   _("/", UNIX_CLI_PARSE_ACTION_PAGER_SEARCH),
     430             : 
     431             :   /* VT100 */
     432             :   _(CSI "A", UNIX_CLI_PARSE_ACTION_PAGER_UP),
     433             :   _(CSI "B", UNIX_CLI_PARSE_ACTION_PAGER_DN),
     434             :   _(CSI "H", UNIX_CLI_PARSE_ACTION_PAGER_TOP),
     435             :   _(CSI "F", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM),
     436             : 
     437             :   /* VT100 Application mode */
     438             :   _(ESC "OA", UNIX_CLI_PARSE_ACTION_PAGER_UP),
     439             :   _(ESC "OB", UNIX_CLI_PARSE_ACTION_PAGER_DN),
     440             :   _(ESC "OH", UNIX_CLI_PARSE_ACTION_PAGER_TOP),
     441             :   _(ESC "OF", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM),
     442             : 
     443             :   /* ANSI X3.41-1974 */
     444             :   _(CSI "1~", UNIX_CLI_PARSE_ACTION_PAGER_TOP),
     445             :   _(CSI "4~", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM),
     446             :   _(CSI "5~", UNIX_CLI_PARSE_ACTION_PAGER_PGUP),
     447             :   _(CSI "6~", UNIX_CLI_PARSE_ACTION_PAGER_PGDN),
     448             : 
     449             :   /* Other protocol things */
     450             :   _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
     451             :   _("\0", UNIX_CLI_PARSE_ACTION_NOACTION),    /* NUL */
     452             :   _(NULL, UNIX_CLI_PARSE_ACTION_NOMATCH)
     453             : };
     454             : 
     455             : #undef _
     456             : 
     457             : /** CLI session telnet negotiation timer events. */
     458             : typedef enum
     459             : {
     460             :   UNIX_CLI_NEW_SESSION_EVENT_ADD, /**< Add a CLI session to the new session list */
     461             : } unix_cli_timeout_event_type_t;
     462             : 
     463             : /** Each new session is stored on a list with a deadline after which
     464             :  * a prompt is issued, in case the session TELNET negotiation fails to
     465             :  * complete. */
     466             : typedef struct
     467             : {
     468             :   uword cf_index; /**< Session index of the new session. */
     469             :   f64 deadline;   /**< Deadline after which the new session must have a prompt. */
     470             : } unix_cli_new_session_t;
     471             : 
     472             : /** CLI global state. */
     473             : typedef struct
     474             : {
     475             :   /** Prompt string for CLI. */
     476             :   u8 *cli_prompt;
     477             : 
     478             :   /** Vec pool of CLI sessions. */
     479             :   unix_cli_file_t *cli_file_pool;
     480             : 
     481             :   /** Vec pool of unused session indices. */
     482             :   u32 *unused_cli_process_node_indices;
     483             : 
     484             :   /** The session index of the stdin cli */
     485             :   u32 stdin_cli_file_index;
     486             : 
     487             :   /** File pool index of current input. */
     488             :   u32 current_input_file_index;
     489             : 
     490             :   /** New session process node identifier */
     491             :   u32 new_session_process_node_index;
     492             : 
     493             :   /** List of new sessions */
     494             :   unix_cli_new_session_t *new_sessions;
     495             : 
     496             :   /** system default macro table */
     497             :   clib_macro_main_t macro_main;
     498             : 
     499             : } unix_cli_main_t;
     500             : 
     501             : /** CLI global state */
     502             : static unix_cli_main_t unix_cli_main;
     503             : 
     504             : /** Return the macro main / tables we should use for this session
     505             :  */
     506             : static clib_macro_main_t *
     507           0 : get_macro_main (void)
     508             : {
     509           0 :   unix_cli_main_t *cm = &unix_cli_main;
     510           0 :   vlib_main_t *vm = vlib_get_main ();
     511           0 :   vlib_process_t *cp = vlib_get_current_process (vm);
     512             :   unix_cli_file_t *cf;
     513             : 
     514           0 :   if (pool_is_free_index (cm->cli_file_pool, cp->output_function_arg))
     515           0 :     return (&cm->macro_main);
     516             : 
     517           0 :   cf = pool_elt_at_index (cm->cli_file_pool, cp->output_function_arg);
     518             : 
     519           0 :   return (&cf->macro_main);
     520             : }
     521             : 
     522             : /**
     523             :  * @brief Search for a byte sequence in the action list.
     524             :  *
     525             :  * Searches the @ref unix_cli_parse_actions_t list in @a a for a match with
     526             :  * the bytes in @a input of maximum length @a ilen bytes.
     527             :  * When a match is made @a *matched indicates how many bytes were matched.
     528             :  * Returns a value from the enum @ref unix_cli_parse_action_t to indicate
     529             :  * whether no match was found, a partial match was found or a complete
     530             :  * match was found and what action, if any, should be taken.
     531             :  *
     532             :  * @param[in]  a        Actions list to search within.
     533             :  * @param[in]  input    String fragment to search for.
     534             :  * @param[in]  ilen     Length of the string in 'input'.
     535             :  * @param[out] matched  Pointer to an integer that will contain the number
     536             :  *                      of bytes matched when a complete match is found.
     537             :  *
     538             :  * @return Action from @ref unix_cli_parse_action_t that the string fragment
     539             :  *         matches.
     540             :  *         @ref UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the
     541             :  *         whole input string matches the start of at least one action.
     542             :  *         @ref UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no
     543             :  *         match at all.
     544             :  */
     545             : static unix_cli_parse_action_t
     546           0 : unix_cli_match_action (unix_cli_parse_actions_t * a,
     547             :                        u8 * input, u32 ilen, i32 * matched)
     548             : {
     549           0 :   u8 partial = 0;
     550             : 
     551           0 :   while (a->input)
     552             :     {
     553           0 :       if (ilen >= a->len)
     554             :         {
     555             :           /* see if the start of the input buffer exactly matches the current
     556             :            * action string. */
     557           0 :           if (memcmp (input, a->input, a->len) == 0)
     558             :             {
     559           0 :               *matched = a->len;
     560           0 :               return a->action;
     561             :             }
     562             :         }
     563             :       else
     564             :         {
     565             :           /* if the first ilen characters match, flag this as a partial -
     566             :            * meaning keep collecting bytes in case of a future match */
     567           0 :           if (memcmp (input, a->input, ilen) == 0)
     568           0 :             partial = 1;
     569             :         }
     570             : 
     571             :       /* check next action */
     572           0 :       a++;
     573             :     }
     574             : 
     575             :   return partial ?
     576           0 :     UNIX_CLI_PARSE_ACTION_PARTIALMATCH : UNIX_CLI_PARSE_ACTION_NOMATCH;
     577             : }
     578             : 
     579             : 
     580             : /** Add bytes to the output vector and then flagg the I/O system that bytes
     581             :  * are available to be sent.
     582             :  */
     583             : static void
     584           0 : unix_cli_add_pending_output (clib_file_t * uf,
     585             :                              unix_cli_file_t * cf,
     586             :                              u8 * buffer, uword buffer_bytes)
     587             : {
     588           0 :   clib_file_main_t *fm = &file_main;
     589             : 
     590           0 :   vec_add (cf->output_vector, buffer, buffer_bytes);
     591           0 :   if (vec_len (cf->output_vector) > 0)
     592             :     {
     593           0 :       int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
     594           0 :       uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
     595           0 :       if (!skip_update)
     596           0 :         fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
     597             :     }
     598           0 : }
     599             : 
     600             : /** Delete all bytes from the output vector and flag the I/O system
     601             :  * that no more bytes are available to be sent.
     602             :  */
     603             : static void
     604           0 : unix_cli_del_pending_output (clib_file_t * uf,
     605             :                              unix_cli_file_t * cf, uword n_bytes)
     606             : {
     607           0 :   clib_file_main_t *fm = &file_main;
     608             : 
     609           0 :   vec_delete (cf->output_vector, n_bytes, 0);
     610           0 :   if (vec_len (cf->output_vector) <= 0)
     611             :     {
     612           0 :       int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
     613           0 :       uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
     614           0 :       if (!skip_update)
     615           0 :         fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
     616             :     }
     617           0 : }
     618             : 
     619             : /** @brief A bit like strchr with a buffer length limit.
     620             :  * Search a buffer for the first instance of a character up to the limit of
     621             :  * the buffer length. If found then return the position of that character.
     622             :  *
     623             :  * The key departure from strchr is that if the character is not found then
     624             :  * return the buffer length.
     625             :  *
     626             :  * @param chr The byte value to search for.
     627             :  * @param str The buffer in which to search for the value.
     628             :  * @param len The depth into the buffer to search.
     629             :  *
     630             :  * @return The index of the first occurrence of \c chr. If \c chr is not
     631             :  *          found then \c len instead.
     632             :  */
     633             : always_inline word
     634           0 : unix_vlib_findchr (u8 chr, u8 * str, word len)
     635             : {
     636           0 :   word i = 0;
     637           0 :   for (i = 0; i < len; i++, str++)
     638             :     {
     639           0 :       if (*str == chr)
     640           0 :         return i;
     641             :     }
     642           0 :   return len;
     643             : }
     644             : 
     645             : /** @brief Send a buffer to the CLI stream if possible, enqueue it otherwise.
     646             :  * Attempts to write given buffer to the file descriptor of the given
     647             :  * Unix CLI session. If that session already has data in the output buffer
     648             :  * or if the write attempt tells us to try again later then the given buffer
     649             :  * is appended to the pending output buffer instead.
     650             :  *
     651             :  * This is typically called only from \c unix_vlib_cli_output_cooked since
     652             :  * that is where CRLF handling occurs or from places where we explicitly do
     653             :  * not want cooked handling.
     654             :  *
     655             :  * @param cf Unix CLI session of the desired stream to write to.
     656             :  * @param uf The Unix file structure of the desired stream to write to.
     657             :  * @param buffer Pointer to the buffer that needs to be written.
     658             :  * @param buffer_bytes The number of bytes from \c buffer to write.
     659             :  */
     660             : static void
     661           0 : unix_vlib_cli_output_raw (unix_cli_file_t * cf,
     662             :                           clib_file_t * uf, u8 * buffer, uword buffer_bytes)
     663             : {
     664           0 :   int n = 0;
     665             : 
     666           0 :   if (cf->has_epipe)         /* don't try writing anything */
     667           0 :     return;
     668             : 
     669           0 :   if (vec_len (cf->output_vector) == 0)
     670             :     {
     671           0 :       if (cf->is_socket)
     672             :         /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
     673           0 :         n = send (uf->file_descriptor, buffer, buffer_bytes, MSG_NOSIGNAL);
     674             :       else
     675           0 :         n = write (uf->file_descriptor, buffer, buffer_bytes);
     676             :     }
     677             : 
     678           0 :   if (n < 0 && errno != EAGAIN)
     679             :     {
     680           0 :       if (errno == EPIPE)
     681             :         {
     682             :           /* connection closed on us */
     683           0 :           unix_main_t *um = &unix_main;
     684           0 :           cf->has_epipe = 1;
     685           0 :           vlib_process_signal_event (um->vlib_main, cf->process_node_index,
     686             :                                      UNIX_CLI_PROCESS_EVENT_QUIT,
     687             :                                      uf->private_data);
     688             :         }
     689             :       else
     690             :         {
     691           0 :           clib_unix_warning ("write");
     692             :         }
     693             :     }
     694           0 :   else if ((word) n < (word) buffer_bytes)
     695             :     {
     696             :       /* We got EAGAIN or we already have stuff in the buffer;
     697             :        * queue up whatever didn't get sent for later. */
     698           0 :       if (n < 0)
     699           0 :         n = 0;
     700           0 :       unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
     701             :     }
     702             : }
     703             : 
     704             : /** @brief Process a buffer for CRLF handling before outputting it to the CLI.
     705             :  *
     706             :  * @param cf Unix CLI session of the desired stream to write to.
     707             :  * @param uf The Unix file structure of the desired stream to write to.
     708             :  * @param buffer Pointer to the buffer that needs to be written.
     709             :  * @param buffer_bytes The number of bytes from \c buffer to write.
     710             :  */
     711             : static void
     712           0 : unix_vlib_cli_output_cooked (unix_cli_file_t * cf,
     713             :                              clib_file_t * uf,
     714             :                              u8 * buffer, uword buffer_bytes)
     715             : {
     716           0 :   word end = 0, start = 0;
     717             : 
     718           0 :   while (end < buffer_bytes)
     719             :     {
     720           0 :       if (cf->crlf_mode)
     721             :         {
     722             :           /* iterate the line on \n's so we can insert a \r before it */
     723           0 :           end = unix_vlib_findchr ('\n',
     724             :                                    buffer + start,
     725           0 :                                    buffer_bytes - start) + start;
     726             :         }
     727             :       else
     728             :         {
     729             :           /* otherwise just send the whole buffer */
     730           0 :           end = buffer_bytes;
     731             :         }
     732             : 
     733           0 :       unix_vlib_cli_output_raw (cf, uf, buffer + start, end - start);
     734             : 
     735           0 :       if (cf->crlf_mode)
     736             :         {
     737           0 :           if (end < buffer_bytes)
     738             :             {
     739           0 :               unix_vlib_cli_output_raw (cf, uf, (u8 *) "\r\n", 2);
     740           0 :               end++;            /* skip the \n that we already sent */
     741             :             }
     742           0 :           start = end;
     743             :         }
     744             :     }
     745             : 
     746             :   /* Use the last character to determine the last direction of the cursor. */
     747           0 :   if (buffer_bytes > 0)
     748           0 :     cf->cursor_direction = (buffer[buffer_bytes - 1] == (u8) '\b');
     749           0 : }
     750             : 
     751             : /** @brief Moves the terminal cursor one character to the left, with
     752             :  * special handling when it reaches the left edge of the terminal window.
     753             :  *
     754             :  * Ordinarily we can simply send a '\b' to move the cursor left, however
     755             :  * most terminals will not reverse-wrap to the end of the previous line
     756             :  * if the cursor is in the left-most column. To counter this we must
     757             :  * check the cursor position + prompt length modulo terminal width and
     758             :  * if available use some other means, such as ANSI terminal escape
     759             :  * sequences, to move the cursor.
     760             :  *
     761             :  * @param cf Unix CLI session of the desired stream to write to.
     762             :  * @param uf The Unix file structure of the desired stream to write to.
     763             :  */
     764             : static void
     765           0 : unix_vlib_cli_output_cursor_left (unix_cli_file_t * cf, clib_file_t * uf)
     766             : {
     767           0 :   unix_cli_main_t *cm = &unix_cli_main;
     768             :   static u8 *ansi = 0;          /* assumes no reentry */
     769             :   u32 position;
     770             : 
     771           0 :   if (!cf->is_interactive || !cf->ansi_capable || !cf->width)
     772             :     {
     773             :       /* No special handling for dumb terminals */
     774           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
     775           0 :       return;
     776             :     }
     777             : 
     778           0 :   position = ((u32) vec_len (cm->cli_prompt) + cf->cursor) % cf->width;
     779             : 
     780           0 :   if (position != 0)
     781             :     {
     782             :       /* No special handling required if we're not at the left edge */
     783           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
     784           0 :       return;
     785             :     }
     786             : 
     787           0 :   if (!cf->cursor_direction)
     788             :     {
     789             :       /* Special handling for when we are at the left edge but
     790             :        * the cursor was going left-to-right, but in this situation
     791             :        * xterm-like terminals actually hide the cursor off the right
     792             :        * edge. A \b here seems to jump one char too many, so let's
     793             :        * force the cursor onto the next line instead.
     794             :        */
     795           0 :       if (cf->cursor < vec_len (cf->current_command))
     796           0 :         unix_vlib_cli_output_cooked (cf, uf, &cf->current_command[cf->cursor],
     797             :                                      1);
     798             :       else
     799           0 :         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
     800           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
     801             :     }
     802             : 
     803             :   /* Relocate the cursor at the right hand edge one line above */
     804           0 :   ansi = format (ansi, CSI "A" CSI "%dC", cf->width - 1);
     805           0 :   unix_vlib_cli_output_cooked (cf, uf, ansi, vec_len (ansi));
     806           0 :   vec_reset_length (ansi);      /* keep the vec around for next time */
     807           0 :   cf->cursor_direction = 1;  /* going backwards now */
     808             : }
     809             : 
     810             : /** @brief Output the CLI prompt */
     811             : static void
     812           0 : unix_cli_cli_prompt (unix_cli_file_t * cf, clib_file_t * uf)
     813             : {
     814           0 :   unix_cli_main_t *cm = &unix_cli_main;
     815             : 
     816           0 :   if (cf->is_interactive)    /* Only interactive sessions get a prompt */
     817           0 :     unix_vlib_cli_output_raw (cf, uf, cm->cli_prompt,
     818           0 :                               vec_len (cm->cli_prompt));
     819           0 : }
     820             : 
     821             : /** @brief Output a pager prompt and show number of buffered lines */
     822             : static void
     823           0 : unix_cli_pager_prompt (unix_cli_file_t * cf, clib_file_t * uf)
     824             : {
     825             :   u8 *prompt;
     826             :   u32 h;
     827             : 
     828           0 :   h = cf->pager_start + (cf->height - 1);
     829           0 :   if (h > vec_len (cf->pager_index))
     830           0 :     h = vec_len (cf->pager_index);
     831             : 
     832           0 :   prompt = format (0, "\r%s-- more -- (%d-%d/%d)%s",
     833           0 :                    cf->ansi_capable ? ANSI_BOLD : "",
     834           0 :                    cf->pager_start + 1,
     835             :                    h,
     836           0 :                    vec_len (cf->pager_index),
     837           0 :                    cf->ansi_capable ? ANSI_RESET : "");
     838             : 
     839           0 :   unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
     840             : 
     841           0 :   vec_free (prompt);
     842           0 : }
     843             : 
     844             : /** @brief Output a pager "skipping" message */
     845             : static void
     846           0 : unix_cli_pager_message (unix_cli_file_t * cf, clib_file_t * uf,
     847             :                         char *message, char *postfix)
     848             : {
     849             :   u8 *prompt;
     850             : 
     851           0 :   prompt = format (0, "\r%s-- %s --%s%s",
     852           0 :                    cf->ansi_capable ? ANSI_BOLD : "",
     853           0 :                    message, cf->ansi_capable ? ANSI_RESET : "", postfix);
     854             : 
     855           0 :   unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
     856             : 
     857           0 :   vec_free (prompt);
     858           0 : }
     859             : 
     860             : /** @brief Erase the printed pager prompt */
     861             : static void
     862           0 : unix_cli_pager_prompt_erase (unix_cli_file_t * cf, clib_file_t * uf)
     863             : {
     864           0 :   if (cf->ansi_capable)
     865             :     {
     866           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
     867           0 :       unix_vlib_cli_output_cooked (cf, uf,
     868             :                                    (u8 *) ANSI_CLEARLINE,
     869             :                                    sizeof (ANSI_CLEARLINE) - 1);
     870             :     }
     871             :   else
     872             :     {
     873             :       int i;
     874             : 
     875           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
     876           0 :       for (i = 0; i < cf->width - 1; i++)
     877           0 :         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
     878           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
     879             :     }
     880           0 : }
     881             : 
     882             : /** @brief Uses an ANSI escape sequence to move the cursor */
     883             : static void
     884           0 : unix_cli_ansi_cursor (unix_cli_file_t * cf, clib_file_t * uf, u16 x, u16 y)
     885             : {
     886             :   u8 *str;
     887             : 
     888           0 :   str = format (0, "%s%d;%dH", CSI, y, x);
     889             : 
     890           0 :   unix_vlib_cli_output_cooked (cf, uf, str, vec_len (str));
     891             : 
     892           0 :   vec_free (str);
     893           0 : }
     894             : 
     895             : /** Redraw the currently displayed page of text.
     896             :  * @param cf CLI session to redraw the pager buffer of.
     897             :  * @param uf Unix file of the CLI session.
     898             :  */
     899             : static void
     900           0 : unix_cli_pager_redraw (unix_cli_file_t * cf, clib_file_t * uf)
     901             : {
     902           0 :   unix_cli_pager_index_t *pi = NULL;
     903           0 :   u8 *line = NULL;
     904             :   word i;
     905             : 
     906             :   /* No active pager? Do nothing. */
     907           0 :   if (!vec_len (cf->pager_index))
     908           0 :     return;
     909             : 
     910           0 :   if (cf->ansi_capable)
     911             :     {
     912             :       /* If we have ANSI, send the clear screen sequence */
     913           0 :       unix_vlib_cli_output_cooked (cf, uf,
     914             :                                    (u8 *) ANSI_CLEAR,
     915             :                                    sizeof (ANSI_CLEAR) - 1);
     916             :     }
     917             :   else
     918             :     {
     919             :       /* Otherwise make sure we're on a blank line */
     920           0 :       unix_cli_pager_prompt_erase (cf, uf);
     921             :     }
     922             : 
     923             :   /* (Re-)send the current page of content */
     924           0 :   for (i = 0; i < cf->height - 1 &&
     925           0 :        i + cf->pager_start < vec_len (cf->pager_index); i++)
     926             :     {
     927           0 :       pi = &cf->pager_index[cf->pager_start + i];
     928           0 :       line = cf->pager_vector[pi->line] + pi->offset;
     929             : 
     930           0 :       unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
     931             :     }
     932             :   /* if the last line didn't end in newline, add a newline */
     933           0 :   if (pi && line[pi->length - 1] != '\n')
     934           0 :     unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
     935             : 
     936           0 :   unix_cli_pager_prompt (cf, uf);
     937             : }
     938             : 
     939             : /** @brief Process and add a line to the pager index.
     940             :  * In normal operation this function will take the given character string
     941             :  * found in @c line and with length @c len_or_index and iterates the over the
     942             :  * contents, adding each line of text discovered within it to the
     943             :  * pager index. Lines are identified by newlines ("<code>\\n</code>") and by
     944             :  * strings longer than the width of the terminal.
     945             :  *
     946             :  * If instead @c line is @c NULL then @c len_or_index is taken to mean the
     947             :  * index of an existing line in the pager buffer; this simply means that the
     948             :  * input line does not need to be cloned since we already have it. This is
     949             :  * typical if we are reindexing the pager buffer.
     950             :  *
     951             :  * @param cf           The CLI session whose pager we are adding to.
     952             :  * @param line         The string of text to be indexed into the pager buffer.
     953             :  *                     If @c line is @c NULL then the mode of operation
     954             :  *                     changes slightly; see the description above.
     955             :  * @param len_or_index If @c line is a pointer to a string then this parameter
     956             :  *                     indicates the length of that string; Otherwise this
     957             :  *                     value provides the index in the pager buffer of an
     958             :  *                     existing string to be indexed.
     959             :  */
     960             : static void
     961           0 : unix_cli_pager_add_line (unix_cli_file_t * cf, u8 * line, word len_or_index)
     962             : {
     963           0 :   u8 *p = NULL;
     964             :   word i, j, k;
     965             :   word line_index, len;
     966           0 :   u32 width = cf->width;
     967             :   unix_cli_pager_index_t *pi;
     968             : 
     969           0 :   if (line == NULL)
     970             :     {
     971             :       /* Use a line already in the pager buffer */
     972           0 :       line_index = len_or_index;
     973           0 :       if (cf->pager_vector != NULL)
     974           0 :         p = cf->pager_vector[line_index];
     975           0 :       len = vec_len (p);
     976             :     }
     977             :   else
     978             :     {
     979           0 :       len = len_or_index;
     980             :       /* Add a copy of the raw string to the pager buffer */
     981           0 :       p = vec_new (u8, len);
     982           0 :       clib_memcpy (p, line, len);
     983             : 
     984             :       /* store in pager buffer */
     985           0 :       line_index = vec_len (cf->pager_vector);
     986           0 :       vec_add1 (cf->pager_vector, p);
     987             :     }
     988             : 
     989           0 :   i = 0;
     990           0 :   while (i < len)
     991             :     {
     992             :       /* Find the next line, or run to terminal width, or run to EOL */
     993           0 :       int l = len - i;
     994           0 :       j = unix_vlib_findchr ((u8) '\n', p, l < width ? l : width);
     995             : 
     996           0 :       if (j < l && p[j] == '\n')     /* incl \n */
     997           0 :         j++;
     998             : 
     999             :       /* Add the line to the index */
    1000           0 :       k = vec_len (cf->pager_index);
    1001           0 :       vec_validate (cf->pager_index, k);
    1002           0 :       pi = &cf->pager_index[k];
    1003             : 
    1004           0 :       pi->line = line_index;
    1005           0 :       pi->offset = i;
    1006           0 :       pi->length = j;
    1007             : 
    1008           0 :       i += j;
    1009           0 :       p += j;
    1010             :     }
    1011           0 : }
    1012             : 
    1013             : /** @brief Reindex entire pager buffer.
    1014             :  * Resets the current pager index and then re-adds the lines in the pager
    1015             :  * buffer to the index.
    1016             :  *
    1017             :  * Additionally this function attempts to retain the current page start
    1018             :  * line offset by searching for the same top-of-screen line in the new index.
    1019             :  *
    1020             :  * @param cf The CLI session whose pager buffer should be reindexed.
    1021             :  */
    1022             : static void
    1023           0 : unix_cli_pager_reindex (unix_cli_file_t * cf)
    1024             : {
    1025             :   word i, old_line, old_offset;
    1026             :   unix_cli_pager_index_t *pi;
    1027             : 
    1028             :   /* If there is nothing in the pager buffer then make sure the index
    1029             :    * is empty and move on.
    1030             :    */
    1031           0 :   if (cf->pager_vector == 0)
    1032             :     {
    1033           0 :       vec_reset_length (cf->pager_index);
    1034           0 :       return;
    1035             :     }
    1036             : 
    1037             :   /* Retain a pointer to the current page start line so we can
    1038             :    * find it later
    1039             :    */
    1040           0 :   pi = &cf->pager_index[cf->pager_start];
    1041           0 :   old_line = pi->line;
    1042           0 :   old_offset = pi->offset;
    1043             : 
    1044             :   /* Re-add the buffered lines to the index */
    1045           0 :   vec_reset_length (cf->pager_index);
    1046           0 :   vec_foreach_index (i, cf->pager_vector)
    1047             :   {
    1048           0 :     unix_cli_pager_add_line (cf, NULL, i);
    1049             :   }
    1050             : 
    1051             :   /* Attempt to re-locate the previously stored page start line */
    1052           0 :   vec_foreach_index (i, cf->pager_index)
    1053             :   {
    1054           0 :     pi = &cf->pager_index[i];
    1055             : 
    1056           0 :     if (pi->line == old_line &&
    1057           0 :         (pi->offset <= old_offset || pi->offset + pi->length > old_offset))
    1058             :       {
    1059             :         /* Found it! */
    1060           0 :         cf->pager_start = i;
    1061           0 :         break;
    1062             :       }
    1063             :   }
    1064             : 
    1065             :   /* In case the start line was not found (rare), ensure the pager start
    1066             :    * index is within bounds
    1067             :    */
    1068           0 :   if (cf->pager_start >= vec_len (cf->pager_index))
    1069             :     {
    1070           0 :       if (!cf->height || vec_len (cf->pager_index) < (cf->height - 1))
    1071           0 :         cf->pager_start = 0;
    1072             :       else
    1073           0 :         cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
    1074             :     }
    1075             : }
    1076             : 
    1077             : /** VLIB CLI output function.
    1078             :  *
    1079             :  * If the terminal has a pager configured then this function takes care
    1080             :  * of collating output into the pager buffer; ensuring only the first page
    1081             :  * is displayed and any lines in excess of the first page are buffered.
    1082             :  *
    1083             :  * If the maximum number of index lines in the buffer is exceeded then the
    1084             :  * pager is cancelled and the contents of the current buffer are sent to the
    1085             :  * terminal.
    1086             :  *
    1087             :  * If there is no pager configured then the output is sent directly to the
    1088             :  * terminal.
    1089             :  *
    1090             :  * @param cli_file_index Index of the CLI session where this output is
    1091             :  *                       directed.
    1092             :  * @param buffer         String of printabe bytes to be output.
    1093             :  * @param buffer_bytes   The number of bytes in @c buffer to be output.
    1094             :  */
    1095             : static void
    1096           0 : unix_vlib_cli_output (uword cli_file_index, u8 * buffer, uword buffer_bytes)
    1097             : {
    1098           0 :   unix_main_t *um = &unix_main;
    1099           0 :   clib_file_main_t *fm = &file_main;
    1100           0 :   unix_cli_main_t *cm = &unix_cli_main;
    1101             :   unix_cli_file_t *cf;
    1102             :   clib_file_t *uf;
    1103             : 
    1104           0 :   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
    1105           0 :   uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    1106             : 
    1107           0 :   if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
    1108             :     {
    1109           0 :       unix_vlib_cli_output_cooked (cf, uf, buffer, buffer_bytes);
    1110             :     }
    1111             :   else
    1112             :     {
    1113           0 :       word row = vec_len (cf->pager_index);
    1114             :       u8 *line;
    1115             :       unix_cli_pager_index_t *pi;
    1116             : 
    1117             :       /* Index and add the output lines to the pager buffer. */
    1118           0 :       unix_cli_pager_add_line (cf, buffer, buffer_bytes);
    1119             : 
    1120             :       /* Now iterate what was added to display the lines.
    1121             :        * If we reach the bottom of the page, display a prompt.
    1122             :        */
    1123           0 :       while (row < vec_len (cf->pager_index))
    1124             :         {
    1125           0 :           if (row < cf->height - 1)
    1126             :             {
    1127             :               /* output this line */
    1128           0 :               pi = &cf->pager_index[row];
    1129           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    1130           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    1131             : 
    1132             :               /* if the last line didn't end in newline, and we're at the
    1133             :                * bottom of the page, add a newline */
    1134           0 :               if (line[pi->length - 1] != '\n' && row == cf->height - 2)
    1135           0 :                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    1136             :             }
    1137             :           else
    1138             :             {
    1139             :               /* Display the pager prompt every 10 lines */
    1140           0 :               if (!(row % 10))
    1141           0 :                 unix_cli_pager_prompt (cf, uf);
    1142             :             }
    1143           0 :           row++;
    1144             :         }
    1145             : 
    1146             :       /* Check if we went over the pager buffer limit */
    1147           0 :       if (vec_len (cf->pager_index) > um->cli_pager_buffer_limit)
    1148             :         {
    1149             :           /* Stop using the pager for the remainder of this CLI command */
    1150           0 :           cf->no_pager = 2;
    1151             : 
    1152             :           /* If we likely printed the prompt, erase it */
    1153           0 :           if (vec_len (cf->pager_index) > cf->height - 1)
    1154           0 :             unix_cli_pager_prompt_erase (cf, uf);
    1155             : 
    1156             :           /* Dump out the contents of the buffer */
    1157           0 :           for (row = cf->pager_start + (cf->height - 1);
    1158           0 :                row < vec_len (cf->pager_index); row++)
    1159             :             {
    1160           0 :               pi = &cf->pager_index[row];
    1161           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    1162           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    1163             :             }
    1164             : 
    1165           0 :           unix_cli_pager_reset (cf);
    1166             :         }
    1167             :     }
    1168           0 : }
    1169             : 
    1170             : /** Identify whether a terminal type is ANSI capable.
    1171             :  *
    1172             :  * Compares the string given in @c term with a list of terminal types known
    1173             :  * to support ANSI escape sequences.
    1174             :  *
    1175             :  * This list contains, for example, @c xterm, @c screen and @c ansi.
    1176             :  *
    1177             :  * @param term A string with a terminal type in it.
    1178             :  * @param len The length of the string in @c term.
    1179             :  *
    1180             :  * @return @c 1 if the terminal type is recognized as supporting ANSI
    1181             :  *         terminal sequences; @c 0 otherwise.
    1182             :  */
    1183             : static u8
    1184           0 : unix_cli_terminal_type_ansi (u8 * term, uword len)
    1185             : {
    1186             :   /* This may later be better done as a hash of some sort. */
    1187             : #define _(a) do { \
    1188             :     if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
    1189             :   } while(0)
    1190             : 
    1191           0 :   _("xterm");
    1192           0 :   _("xterm-color");
    1193           0 :   _("xterm-256color");                /* iTerm on Mac */
    1194           0 :   _("screen");
    1195           0 :   _("screen-256color");               /* Screen and tmux */
    1196           0 :   _("ansi");                  /* Microsoft Telnet */
    1197             : #undef _
    1198             : 
    1199           0 :   return 0;
    1200             : }
    1201             : 
    1202             : /** Identify whether a terminal type is non-interactive.
    1203             :  *
    1204             :  * Compares the string given in @c term with a list of terminal types known
    1205             :  * to be non-interactive, as send by tools such as @c vppctl .
    1206             :  *
    1207             :  * This list contains, for example, @c vppctl.
    1208             :  *
    1209             :  * @param term A string with a terminal type in it.
    1210             :  * @param len The length of the string in @c term.
    1211             :  *
    1212             :  * @return @c 1 if the terminal type is recognized as being non-interactive;
    1213             :  *         @c 0 otherwise.
    1214             :  */
    1215             : static u8
    1216           0 : unix_cli_terminal_type_noninteractive (u8 * term, uword len)
    1217             : {
    1218             :   /* This may later be better done as a hash of some sort. */
    1219             : #define _(a) do { \
    1220             :     if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
    1221             :   } while(0)
    1222             : 
    1223           0 :   _("vppctl");
    1224             : #undef _
    1225             : 
    1226           0 :   return 0;
    1227             : }
    1228             : 
    1229             : /** Set a session to be non-interactive. */
    1230             : static void
    1231           0 : unix_cli_set_session_noninteractive (unix_cli_file_t * cf)
    1232             : {
    1233             :   /* Non-interactive sessions don't get these */
    1234           0 :   cf->is_interactive = 0;
    1235           0 :   cf->no_pager = 1;
    1236           0 :   cf->history_limit = 0;
    1237           0 :   cf->has_history = 0;
    1238           0 :   cf->line_mode = 1;
    1239           0 : }
    1240             : 
    1241             : /** @brief Emit initial welcome banner and prompt on a connection. */
    1242             : static void
    1243           0 : unix_cli_file_welcome (unix_cli_main_t * cm, unix_cli_file_t * cf)
    1244             : {
    1245           0 :   unix_main_t *um = &unix_main;
    1246           0 :   clib_file_main_t *fm = &file_main;
    1247           0 :   clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    1248             :   unix_cli_banner_t *banner;
    1249             :   int i, len;
    1250             : 
    1251             :   /* Mark the session as started if we get here */
    1252           0 :   cf->started = 1;
    1253             : 
    1254           0 :   if (!(cf->is_interactive)) /* No banner for non-interactive sessions */
    1255           0 :     return;
    1256             : 
    1257             :   /*
    1258             :    * Put the first bytes directly into the buffer so that further output is
    1259             :    * queued until everything is ready. (oterwise initial prompt can appear
    1260             :    * mid way through VPP initialization)
    1261             :    */
    1262           0 :   unix_cli_add_pending_output (uf, cf, (u8 *) "\r", 1);
    1263             : 
    1264           0 :   if (!um->cli_no_banner && (um->flags & UNIX_FLAG_NOBANNER) == 0)
    1265             :     {
    1266           0 :       if (cf->ansi_capable && (um->flags & UNIX_FLAG_NOCOLOR) == 0)
    1267             :         {
    1268           0 :           banner = unix_cli_banner_color;
    1269           0 :           len = ARRAY_LEN (unix_cli_banner_color);
    1270             :         }
    1271             :       else
    1272             :         {
    1273           0 :           banner = unix_cli_banner;
    1274           0 :           len = ARRAY_LEN (unix_cli_banner);
    1275             :         }
    1276             : 
    1277           0 :       for (i = 0; i < len; i++)
    1278             :         {
    1279           0 :           unix_vlib_cli_output_cooked (cf, uf,
    1280           0 :                                        banner[i].line, banner[i].length);
    1281             :         }
    1282             :     }
    1283             : 
    1284             :   /* Prompt. */
    1285           0 :   unix_cli_cli_prompt (cf, uf);
    1286             : 
    1287             : }
    1288             : 
    1289             : /**
    1290             :  * @brief A failsafe manager that ensures CLI sessions issue an initial
    1291             :  * prompt if TELNET negotiation fails.
    1292             :  */
    1293             : static uword
    1294           0 : unix_cli_new_session_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
    1295             :                               vlib_frame_t * f)
    1296             : {
    1297           0 :   unix_cli_main_t *cm = &unix_cli_main;
    1298           0 :   uword event_type, *event_data = 0;
    1299           0 :   f64 wait = 10.0;
    1300             : 
    1301             :   while (1)
    1302             :     {
    1303           0 :       if (vec_len (cm->new_sessions) > 0)
    1304           0 :         wait = vlib_process_wait_for_event_or_clock (vm, wait);
    1305             :       else
    1306           0 :         vlib_process_wait_for_event (vm);
    1307             : 
    1308           0 :       event_type = vlib_process_get_events (vm, &event_data);
    1309             : 
    1310           0 :       switch (event_type)
    1311             :         {
    1312           0 :         case ~0:                /* no events => timeout */
    1313           0 :           break;
    1314             : 
    1315           0 :         case UNIX_CLI_NEW_SESSION_EVENT_ADD:
    1316             :           {
    1317             :             /* Add an identifier to the new session list */
    1318             :             unix_cli_new_session_t ns;
    1319             : 
    1320             :             /* Check the connection didn't close already */
    1321           0 :             if (pool_is_free_index (cm->cli_file_pool, event_data[0]))
    1322           0 :               break;
    1323             : 
    1324           0 :             ns.cf_index = event_data[0];
    1325           0 :             ns.deadline = vlib_time_now (vm) + 1.0;
    1326             : 
    1327           0 :             vec_add1 (cm->new_sessions, ns);
    1328             : 
    1329           0 :             if (wait > 0.1)
    1330           0 :               wait = 0.1;       /* force a re-evaluation soon, but not too soon */
    1331             :           }
    1332           0 :           break;
    1333             : 
    1334           0 :         default:
    1335           0 :           clib_warning ("BUG: unknown event type 0x%wx", event_type);
    1336           0 :           break;
    1337             :         }
    1338             : 
    1339           0 :       vec_reset_length (event_data);
    1340             : 
    1341           0 :       if (vlib_process_suspend_time_is_zero (wait))
    1342             :         {
    1343             :           /* Scan the list looking for expired deadlines.
    1344             :            * Emit needed prompts and remove from the list.
    1345             :            * While scanning, look for the nearest new deadline
    1346             :            * for the next iteration.
    1347             :            * Since the list is ordered with newest sessions first
    1348             :            * we can make assumptions about expired sessions being
    1349             :            * contiguous at the beginning and the next deadline is the
    1350             :            * next entry on the list, if any.
    1351             :            */
    1352           0 :           f64 now = vlib_time_now (vm);
    1353             :           unix_cli_new_session_t *nsp;
    1354           0 :           word index = 0;
    1355             : 
    1356           0 :           wait = INFINITY;
    1357             : 
    1358           0 :           vec_foreach (nsp, cm->new_sessions)
    1359             :           {
    1360           0 :             if (vlib_process_suspend_time_is_zero (nsp->deadline - now))
    1361             :               {
    1362             :                 /* Deadline reached */
    1363             :                 unix_cli_file_t *cf;
    1364             : 
    1365             :                 /* Mark the highwater */
    1366           0 :                 index++;
    1367             : 
    1368             :                 /* Check the connection didn't close already */
    1369           0 :                 if (pool_is_free_index (cm->cli_file_pool, nsp->cf_index))
    1370           0 :                   continue;
    1371             : 
    1372           0 :                 cf = pool_elt_at_index (cm->cli_file_pool, nsp->cf_index);
    1373             : 
    1374           0 :                 if (!cf->started)
    1375           0 :                   unix_cli_file_welcome (cm, cf);
    1376             :               }
    1377             :             else
    1378             :               {
    1379           0 :                 wait = nsp->deadline - now;
    1380           0 :                 break;
    1381             :               }
    1382             :           }
    1383             : 
    1384           0 :           if (index)
    1385             :             {
    1386             :               /* We have sessions to remove */
    1387           0 :               vec_delete (cm->new_sessions, index, 0);
    1388             :             }
    1389             :         }
    1390             :     }
    1391             : 
    1392             :   return 0;
    1393             : }
    1394             : 
    1395             : 
    1396             : /** @brief A mostly no-op Telnet state machine.
    1397             :  * Process Telnet command bytes in a way that ensures we're mostly
    1398             :  * transparent to the Telnet protocol. That is, it's mostly a no-op.
    1399             :  *
    1400             :  * @return -1 if we need more bytes, otherwise a positive integer number of
    1401             :  *          bytes to consume from the input_vector, not including the initial
    1402             :  *          IAC byte.
    1403             :  */
    1404             : static i32
    1405           0 : unix_cli_process_telnet (unix_main_t * um,
    1406             :                          unix_cli_file_t * cf,
    1407             :                          clib_file_t * uf, u8 * input_vector, uword len)
    1408             : {
    1409             :   /* Input_vector starts at IAC byte.
    1410             :    * See if we have a complete message; if not, return -1 so we wait for more.
    1411             :    * if we have a complete message, consume those bytes from the vector.
    1412             :    */
    1413           0 :   i32 consume = 0;
    1414             : 
    1415           0 :   if (len == 1)
    1416           0 :     return -1;                  /* want more bytes */
    1417             : 
    1418           0 :   switch (input_vector[1])
    1419             :     {
    1420           0 :     case IAC:
    1421             :       /* two IAC's in a row means to pass through 0xff.
    1422             :        * since that makes no sense here, just consume it.
    1423             :        */
    1424           0 :       consume = 1;
    1425           0 :       break;
    1426             : 
    1427           0 :     case WILL:
    1428             :     case WONT:
    1429             :     case DO:
    1430             :     case DONT:
    1431             :       /* Expect 3 bytes */
    1432           0 :       if (len < 3)
    1433           0 :         return -1;              /* want more bytes */
    1434             : 
    1435           0 :       consume = 2;
    1436           0 :       break;
    1437             : 
    1438           0 :     case SB:
    1439             :       {
    1440             :         /* Sub option - search ahead for IAC SE to end it */
    1441             :         i32 i;
    1442           0 :         for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
    1443             :           {
    1444           0 :             if (input_vector[i - 1] == IAC && input_vector[i] == SE)
    1445             :               {
    1446             :                 /* We have a complete message; see if we care about it */
    1447           0 :                 switch (input_vector[2])
    1448             :                   {
    1449           0 :                   case TELOPT_TTYPE:
    1450           0 :                     if (input_vector[3] != 0)
    1451           0 :                       break;
    1452             :                     {
    1453             :                       /* See if the the terminal type is recognized */
    1454           0 :                       u8 *term = input_vector + 4;
    1455           0 :                       uword len = i - 5;
    1456             : 
    1457             :                       /* See if the terminal type is ANSI capable */
    1458           0 :                       cf->ansi_capable =
    1459           0 :                         unix_cli_terminal_type_ansi (term, len);
    1460             : 
    1461             :                       /* See if the terminal type indicates non-interactive */
    1462           0 :                       if (unix_cli_terminal_type_noninteractive (term, len))
    1463           0 :                         unix_cli_set_session_noninteractive (cf);
    1464             :                     }
    1465             : 
    1466             :                     /* If session not started, we can release the pause */
    1467           0 :                     if (!cf->started)
    1468             :                       /* Send the welcome banner and initial prompt */
    1469           0 :                       unix_cli_file_welcome (&unix_cli_main, cf);
    1470           0 :                     break;
    1471             : 
    1472           0 :                   case TELOPT_NAWS:
    1473             :                     /* Window size */
    1474           0 :                     if (i != 8) /* check message is correct size */
    1475           0 :                       break;
    1476             : 
    1477           0 :                     cf->width =
    1478           0 :                       clib_net_to_host_u16 (*((u16 *) (input_vector + 3)));
    1479           0 :                     if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH)
    1480           0 :                       cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH;
    1481           0 :                     if (cf->width == 0)
    1482           0 :                       cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
    1483             : 
    1484           0 :                     cf->height =
    1485           0 :                       clib_net_to_host_u16 (*((u16 *) (input_vector + 5)));
    1486           0 :                     if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT)
    1487           0 :                       cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT;
    1488           0 :                     if (cf->height == 0)
    1489           0 :                       cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
    1490             : 
    1491             :                     /* reindex pager buffer */
    1492           0 :                     unix_cli_pager_reindex (cf);
    1493             :                     /* redraw page */
    1494           0 :                     unix_cli_pager_redraw (cf, uf);
    1495           0 :                     break;
    1496             : 
    1497           0 :                   default:
    1498           0 :                     break;
    1499             :                   }
    1500             :                 /* Consume it all */
    1501           0 :                 consume = i;
    1502           0 :                 break;
    1503             :               }
    1504             :           }
    1505             : 
    1506           0 :         if (i == UNIX_CLI_MAX_DEPTH_TELNET)
    1507           0 :           consume = 1;          /* hit max search depth, advance one byte */
    1508             : 
    1509           0 :         if (consume == 0)
    1510           0 :           return -1;            /* want more bytes */
    1511             : 
    1512           0 :         break;
    1513             :       }
    1514             : 
    1515           0 :     case GA:
    1516             :     case EL:
    1517             :     case EC:
    1518             :     case AO:
    1519             :     case IP:
    1520             :     case BREAK:
    1521             :     case DM:
    1522             :     case NOP:
    1523             :     case SE:
    1524             :     case EOR:
    1525             :     case ABORT:
    1526             :     case SUSP:
    1527             :     case xEOF:
    1528             :       /* Simple one-byte messages */
    1529           0 :       consume = 1;
    1530           0 :       break;
    1531             : 
    1532           0 :     case AYT:
    1533             :       /* Are You There - trigger a visible response */
    1534           0 :       consume = 1;
    1535           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
    1536           0 :       break;
    1537             : 
    1538           0 :     default:
    1539             :       /* Unknown command! Eat the IAC byte */
    1540           0 :       break;
    1541             :     }
    1542             : 
    1543           0 :   return consume;
    1544             : }
    1545             : 
    1546             : /** @brief Process actionable input.
    1547             :  * Based on the \c action process the input; this typically involves
    1548             :  * searching the command history or editing the current command line.
    1549             :  */
    1550             : static int
    1551           0 : unix_cli_line_process_one (unix_cli_main_t * cm,
    1552             :                            unix_main_t * um,
    1553             :                            unix_cli_file_t * cf,
    1554             :                            clib_file_t * uf,
    1555             :                            u8 input, unix_cli_parse_action_t action)
    1556             : {
    1557             :   u8 *prev;
    1558           0 :   u8 *save = 0;
    1559             :   u8 **possible_commands;
    1560             :   int j, delta;
    1561             : 
    1562           0 :   switch (action)
    1563             :     {
    1564           0 :     case UNIX_CLI_PARSE_ACTION_NOACTION:
    1565           0 :       break;
    1566             : 
    1567           0 :     case UNIX_CLI_PARSE_ACTION_REVSEARCH:
    1568             :     case UNIX_CLI_PARSE_ACTION_FWDSEARCH:
    1569           0 :       if (!cf->has_history || !cf->history_limit)
    1570             :         break;
    1571           0 :       if (cf->search_mode == 0)
    1572             :         {
    1573             :           /* Erase the current command (if any) */
    1574           0 :           for (; cf->cursor > 0; cf->cursor--)
    1575             :             {
    1576           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1577           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1578           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1579             :             }
    1580             : 
    1581           0 :           vec_reset_length (cf->search_key);
    1582           0 :           vec_reset_length (cf->current_command);
    1583             : 
    1584           0 :           if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
    1585           0 :             cf->search_mode = -1;
    1586             :           else
    1587           0 :             cf->search_mode = 1;
    1588             :         }
    1589             :       else
    1590             :         {
    1591           0 :           if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
    1592           0 :             cf->search_mode = -1;
    1593             :           else
    1594           0 :             cf->search_mode = 1;
    1595             : 
    1596           0 :           cf->excursion += cf->search_mode;
    1597           0 :           goto search_again;
    1598             :         }
    1599           0 :       break;
    1600             : 
    1601           0 :     case UNIX_CLI_PARSE_ACTION_ERASELINELEFT:
    1602             :       /* Erase the command from the cursor to the start */
    1603             : 
    1604           0 :       j = cf->cursor;
    1605             :       /* Shimmy backwards to the new end of line position */
    1606           0 :       delta = vec_len (cf->current_command) - cf->cursor;
    1607           0 :       for (; cf->cursor > delta; cf->cursor--)
    1608           0 :         unix_vlib_cli_output_cursor_left (cf, uf);
    1609             :       /* Zap from here to the end of what is currently displayed */
    1610           0 :       for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
    1611           0 :         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1612             :       /* Get back to the start of the line */
    1613           0 :       for (; cf->cursor > 0; cf->cursor--)
    1614           0 :         unix_vlib_cli_output_cursor_left (cf, uf);
    1615             : 
    1616             :       /* Delete the desired text from the command */
    1617           0 :       memmove (cf->current_command, cf->current_command + j, delta);
    1618           0 :       vec_set_len (cf->current_command, delta);
    1619             : 
    1620             :       /* Print the new contents */
    1621           0 :       unix_vlib_cli_output_cooked (cf, uf, cf->current_command, delta);
    1622           0 :       cf->cursor = delta;    /* for backspace tracking */
    1623             : 
    1624             :       /* Shimmy back to the start */
    1625           0 :       for (; cf->cursor > 0; cf->cursor--)
    1626           0 :         unix_vlib_cli_output_cursor_left (cf, uf);
    1627             : 
    1628           0 :       cf->search_mode = 0;
    1629           0 :       break;
    1630             : 
    1631           0 :     case UNIX_CLI_PARSE_ACTION_ERASELINERIGHT:
    1632             :       /* Erase the command from the cursor to the end */
    1633             : 
    1634           0 :       j = cf->cursor;
    1635             :       /* Zap from cursor to end of what is currently displayed */
    1636           0 :       for (; cf->cursor < (vec_len (cf->current_command)); cf->cursor++)
    1637           0 :         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1638             :       /* Get back to where we were */
    1639           0 :       for (; cf->cursor > j; cf->cursor--)
    1640           0 :         unix_vlib_cli_output_cursor_left (cf, uf);
    1641             : 
    1642             :       /* Truncate the line at the cursor */
    1643           0 :       vec_set_len (cf->current_command, cf->cursor);
    1644             : 
    1645           0 :       cf->search_mode = 0;
    1646           0 :       break;
    1647             : 
    1648           0 :     case UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT:
    1649             :       /* calculate num of caracter to be erased */
    1650           0 :       delta = 0;
    1651           0 :       while (cf->cursor > delta
    1652           0 :              && cf->current_command[cf->cursor - delta - 1] == ' ')
    1653           0 :         delta++;
    1654           0 :       while (cf->cursor > delta
    1655           0 :              && cf->current_command[cf->cursor - delta - 1] != ' ')
    1656           0 :         delta++;
    1657             : 
    1658           0 :       if (vec_len (cf->current_command))
    1659             :         {
    1660           0 :           if (cf->cursor > 0)
    1661             :             {
    1662             :               /* move cursor left delta times */
    1663           0 :               for (j = delta; j > 0; j--, cf->cursor--)
    1664           0 :                 unix_vlib_cli_output_cursor_left (cf, uf);
    1665           0 :               save = cf->current_command + cf->cursor;
    1666             : 
    1667             :               /* redraw remainder of line */
    1668           0 :               memmove (cf->current_command + cf->cursor,
    1669           0 :                        cf->current_command + cf->cursor + delta,
    1670           0 :                        _vec_len (cf->current_command) - cf->cursor - delta);
    1671           0 :               unix_vlib_cli_output_cooked (cf, uf,
    1672           0 :                                            cf->current_command + cf->cursor,
    1673           0 :                                            _vec_len (cf->current_command) -
    1674           0 :                                            cf->cursor);
    1675           0 :               cf->cursor += _vec_len (cf->current_command) - cf->cursor;
    1676             : 
    1677             :               /* print delta amount of blank spaces,
    1678             :                * then finally fix the cursor position */
    1679           0 :               for (j = delta; j > 0; j--, cf->cursor--)
    1680           0 :                 unix_vlib_cli_output_cursor_left (cf, uf);
    1681           0 :               for (j = delta; j > 0; j--, cf->cursor++)
    1682           0 :                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1683           0 :               for (; (cf->current_command + cf->cursor) > save; cf->cursor--)
    1684           0 :                 unix_vlib_cli_output_cursor_left (cf, uf);
    1685           0 :               vec_dec_len (cf->current_command, delta);
    1686             :             }
    1687             :         }
    1688           0 :       cf->search_mode = 0;
    1689           0 :       cf->excursion = 0;
    1690           0 :       vec_reset_length (cf->search_key);
    1691           0 :       break;
    1692             : 
    1693           0 :     case UNIX_CLI_PARSE_ACTION_LEFT:
    1694           0 :       if (cf->cursor > 0)
    1695             :         {
    1696           0 :           unix_vlib_cli_output_cursor_left (cf, uf);
    1697           0 :           cf->cursor--;
    1698             :         }
    1699             : 
    1700           0 :       cf->search_mode = 0;
    1701           0 :       break;
    1702             : 
    1703           0 :     case UNIX_CLI_PARSE_ACTION_RIGHT:
    1704           0 :       if (cf->cursor < vec_len (cf->current_command))
    1705             :         {
    1706             :           /* have to emit the character under the cursor */
    1707           0 :           unix_vlib_cli_output_cooked (cf, uf,
    1708           0 :                                        cf->current_command + cf->cursor, 1);
    1709           0 :           cf->cursor++;
    1710             :         }
    1711             : 
    1712           0 :       cf->search_mode = 0;
    1713           0 :       break;
    1714             : 
    1715           0 :     case UNIX_CLI_PARSE_ACTION_UP:
    1716             :     case UNIX_CLI_PARSE_ACTION_DOWN:
    1717           0 :       if (!cf->has_history || !cf->history_limit)
    1718             :         break;
    1719           0 :       cf->search_mode = 0;
    1720             :       /* Erase the command */
    1721           0 :       for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
    1722           0 :         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1723           0 :       for (; cf->cursor > 0; cf->cursor--)
    1724             :         {
    1725           0 :           unix_vlib_cli_output_cursor_left (cf, uf);
    1726           0 :           unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1727           0 :           unix_vlib_cli_output_cursor_left (cf, uf);
    1728             :         }
    1729           0 :       vec_reset_length (cf->current_command);
    1730           0 :       if (vec_len (cf->command_history))
    1731             :         {
    1732           0 :           if (action == UNIX_CLI_PARSE_ACTION_UP)
    1733           0 :             delta = -1;
    1734             :           else
    1735           0 :             delta = 1;
    1736             : 
    1737           0 :           cf->excursion += delta;
    1738             : 
    1739           0 :           if (cf->excursion == vec_len (cf->command_history))
    1740             :             {
    1741             :               /* down-arrowed to last entry - want a blank line */
    1742           0 :               vec_set_len (cf->current_command, 0);
    1743             :             }
    1744           0 :           else if (cf->excursion < 0)
    1745             :             {
    1746             :               /* up-arrowed over the start to the end, want a blank line */
    1747           0 :               cf->excursion = vec_len (cf->command_history);
    1748           0 :               vec_set_len (cf->current_command, 0);
    1749             :             }
    1750             :           else
    1751             :             {
    1752           0 :               if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
    1753             :                 /* down-arrowed past end - wrap to start */
    1754           0 :                 cf->excursion = 0;
    1755             : 
    1756             :               /* Print the command at the current position */
    1757           0 :               prev = cf->command_history[cf->excursion];
    1758           0 :               vec_validate (cf->current_command, vec_len (prev) - 1);
    1759             : 
    1760           0 :               clib_memcpy (cf->current_command, prev, vec_len (prev));
    1761           0 :               vec_set_len (cf->current_command, vec_len (prev));
    1762           0 :               unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
    1763           0 :                                            vec_len (cf->current_command));
    1764             :             }
    1765             :         }
    1766           0 :       cf->cursor = vec_len (cf->current_command);
    1767           0 :       break;
    1768             : 
    1769           0 :     case UNIX_CLI_PARSE_ACTION_HOME:
    1770           0 :       if (vec_len (cf->current_command) && cf->cursor > 0)
    1771             :         {
    1772           0 :           for (; cf->cursor > 0; cf->cursor--)
    1773           0 :             unix_vlib_cli_output_cursor_left (cf, uf);
    1774             :         }
    1775             : 
    1776           0 :       cf->search_mode = 0;
    1777           0 :       break;
    1778             : 
    1779           0 :     case UNIX_CLI_PARSE_ACTION_END:
    1780           0 :       if (vec_len (cf->current_command) &&
    1781           0 :           cf->cursor < vec_len (cf->current_command))
    1782             :         {
    1783           0 :           unix_vlib_cli_output_cooked (cf, uf,
    1784           0 :                                        cf->current_command + cf->cursor,
    1785           0 :                                        vec_len (cf->current_command) -
    1786           0 :                                        cf->cursor);
    1787           0 :           cf->cursor = vec_len (cf->current_command);
    1788             :         }
    1789             : 
    1790           0 :       cf->search_mode = 0;
    1791           0 :       break;
    1792             : 
    1793           0 :     case UNIX_CLI_PARSE_ACTION_WORDLEFT:
    1794           0 :       if (vec_len (cf->current_command) && cf->cursor > 0)
    1795             :         {
    1796           0 :           unix_vlib_cli_output_cursor_left (cf, uf);
    1797           0 :           cf->cursor--;
    1798             : 
    1799           0 :           while (cf->cursor && isspace (cf->current_command[cf->cursor]))
    1800             :             {
    1801           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1802           0 :               cf->cursor--;
    1803             :             }
    1804           0 :           while (cf->cursor && !isspace (cf->current_command[cf->cursor]))
    1805             :             {
    1806           0 :               if (isspace (cf->current_command[cf->cursor - 1]))
    1807           0 :                 break;
    1808           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1809           0 :               cf->cursor--;
    1810             :             }
    1811             : 
    1812             :         }
    1813             : 
    1814           0 :       cf->search_mode = 0;
    1815           0 :       break;
    1816             : 
    1817           0 :     case UNIX_CLI_PARSE_ACTION_WORDRIGHT:
    1818           0 :       if (vec_len (cf->current_command) &&
    1819           0 :           cf->cursor < vec_len (cf->current_command))
    1820             :         {
    1821           0 :           int e = vec_len (cf->current_command);
    1822           0 :           j = cf->cursor;
    1823           0 :           while (j < e && !isspace (cf->current_command[j]))
    1824           0 :             j++;
    1825           0 :           while (j < e && isspace (cf->current_command[j]))
    1826           0 :             j++;
    1827           0 :           unix_vlib_cli_output_cooked (cf, uf,
    1828           0 :                                        cf->current_command + cf->cursor,
    1829           0 :                                        j - cf->cursor);
    1830           0 :           cf->cursor = j;
    1831             :         }
    1832             : 
    1833           0 :       cf->search_mode = 0;
    1834           0 :       break;
    1835             : 
    1836             : 
    1837           0 :     case UNIX_CLI_PARSE_ACTION_ERASE:
    1838           0 :       if (vec_len (cf->current_command))
    1839             :         {
    1840           0 :           if (cf->cursor == vec_len (cf->current_command))
    1841             :             {
    1842           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1843           0 :               cf->cursor--;
    1844           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1845           0 :               cf->cursor++;
    1846           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1847           0 :               cf->cursor--;
    1848           0 :               vec_dec_len (cf->current_command, 1);
    1849             :             }
    1850           0 :           else if (cf->cursor > 0)
    1851             :             {
    1852             :               /* shift everything at & to the right of the cursor left by 1 */
    1853           0 :               j = vec_len (cf->current_command) - cf->cursor;
    1854           0 :               memmove (cf->current_command + cf->cursor - 1,
    1855           0 :                        cf->current_command + cf->cursor, j);
    1856           0 :               vec_dec_len (cf->current_command, 1);
    1857             : 
    1858             :               /* redraw the rest of the line */
    1859           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1860           0 :               cf->cursor--;
    1861           0 :               unix_vlib_cli_output_cooked (cf, uf,
    1862           0 :                                            cf->current_command + cf->cursor,
    1863             :                                            j);
    1864           0 :               cf->cursor += j;
    1865             :               /* erase last char */
    1866           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1867           0 :               cf->cursor++;
    1868             : 
    1869             :               /* and shift the terminal cursor back where it should be */
    1870           0 :               j += 2;           /* account for old string length and offset position */
    1871           0 :               while (--j)
    1872             :                 {
    1873           0 :                   unix_vlib_cli_output_cursor_left (cf, uf);
    1874           0 :                   cf->cursor--;
    1875             :                 }
    1876             :             }
    1877             :         }
    1878           0 :       cf->search_mode = 0;
    1879           0 :       cf->excursion = 0;
    1880           0 :       vec_reset_length (cf->search_key);
    1881           0 :       break;
    1882             : 
    1883           0 :     case UNIX_CLI_PARSE_ACTION_ERASERIGHT:
    1884           0 :       if (vec_len (cf->current_command))
    1885             :         {
    1886           0 :           if (cf->cursor < vec_len (cf->current_command))
    1887             :             {
    1888             :               /* shift everything to the right of the cursor left by 1 */
    1889           0 :               j = vec_len (cf->current_command) - cf->cursor - 1;
    1890           0 :               memmove (cf->current_command + cf->cursor,
    1891           0 :                        cf->current_command + cf->cursor + 1, j);
    1892           0 :               vec_dec_len (cf->current_command, 1);
    1893             :               /* redraw the rest of the line */
    1894           0 :               unix_vlib_cli_output_cooked (cf, uf,
    1895           0 :                                            cf->current_command + cf->cursor,
    1896             :                                            j);
    1897           0 :               cf->cursor += j;
    1898           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    1899           0 :               cf->cursor++;
    1900           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1901           0 :               cf->cursor--;
    1902             :               /* and shift the terminal cursor back where it should be */
    1903           0 :               if (j)
    1904             :                 {
    1905           0 :                   unix_vlib_cli_output_cursor_left (cf, uf);
    1906           0 :                   cf->cursor--;
    1907           0 :                   while (--j)
    1908             :                     {
    1909           0 :                       unix_vlib_cli_output_cursor_left (cf, uf);
    1910           0 :                       cf->cursor--;
    1911             :                     }
    1912             :                 }
    1913             :             }
    1914             :         }
    1915           0 :       else if (input == 'D' - '@')
    1916             :         {
    1917             :           /* ^D with no command entered = quit */
    1918           0 :           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
    1919           0 :           vlib_process_signal_event (um->vlib_main,
    1920             :                                      vlib_current_process (um->vlib_main),
    1921             :                                      UNIX_CLI_PROCESS_EVENT_QUIT,
    1922           0 :                                      cf - cm->cli_file_pool);
    1923             :         }
    1924           0 :       cf->search_mode = 0;
    1925           0 :       cf->excursion = 0;
    1926           0 :       vec_reset_length (cf->search_key);
    1927           0 :       break;
    1928             : 
    1929           0 :     case UNIX_CLI_PARSE_ACTION_CLEAR:
    1930             :       /* If we're in ANSI mode, clear the screen.
    1931             :        * Then redraw the prompt and any existing command input, then put
    1932             :        * the cursor back where it was in that line.
    1933             :        */
    1934           0 :       if (cf->ansi_capable)
    1935           0 :         unix_vlib_cli_output_cooked (cf, uf,
    1936             :                                      (u8 *) ANSI_CLEAR,
    1937             :                                      sizeof (ANSI_CLEAR) - 1);
    1938             :       else
    1939           0 :         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    1940             : 
    1941           0 :       unix_vlib_cli_output_raw (cf, uf,
    1942           0 :                                 cm->cli_prompt, vec_len (cm->cli_prompt));
    1943           0 :       unix_vlib_cli_output_cooked (cf, uf,
    1944             :                                    cf->current_command,
    1945           0 :                                    vec_len (cf->current_command));
    1946           0 :       j = cf->cursor;
    1947           0 :       cf->cursor = vec_len (cf->current_command);
    1948           0 :       for (; cf->cursor > j; cf->cursor--)
    1949           0 :         unix_vlib_cli_output_cursor_left (cf, uf);
    1950             : 
    1951           0 :       break;
    1952             : 
    1953           0 :     case UNIX_CLI_PARSE_ACTION_TAB:
    1954           0 :       if (cf->cursor < vec_len (cf->current_command))
    1955             :         {
    1956             :           /* if we are in the middle of a line, complete only if
    1957             :            * the cursor points to whitespace */
    1958           0 :           if (isspace (cf->current_command[cf->cursor]))
    1959             :             {
    1960             :               /* save and clear any input that is after the cursor */
    1961           0 :               vec_resize (save, vec_len (cf->current_command) - cf->cursor);
    1962           0 :               clib_memcpy (save, cf->current_command + cf->cursor,
    1963             :                            vec_len (cf->current_command) - cf->cursor);
    1964           0 :               vec_set_len (cf->current_command, cf->cursor);
    1965             :             }
    1966             :           else
    1967             :             {
    1968           0 :               unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
    1969           0 :               break;
    1970             :             }
    1971             :         }
    1972           0 :       possible_commands =
    1973           0 :         vlib_cli_get_possible_completions (cf->current_command);
    1974           0 :       if (vec_len (possible_commands) == 1)
    1975           0 :         {
    1976           0 :           u8 *completed = possible_commands[0];
    1977           0 :           j = cf->cursor;
    1978             : 
    1979             :           /* find the last word of current_command */
    1980           0 :           while (j >= 1 && !isspace (cf->current_command[j - 1]))
    1981             :             {
    1982           0 :               unix_vlib_cli_output_cursor_left (cf, uf);
    1983           0 :               cf->cursor--;
    1984           0 :               j--;
    1985             :             }
    1986           0 :           vec_set_len (cf->current_command, j);
    1987             : 
    1988             :           /* replace it with the newly expanded command */
    1989           0 :           vec_append (cf->current_command, completed);
    1990             : 
    1991             :           /* echo to the terminal */
    1992           0 :           unix_vlib_cli_output_cooked (cf, uf, completed,
    1993           0 :                                        vec_len (completed));
    1994             : 
    1995             :           /* add one trailing space if needed */
    1996           0 :           if (vec_len (save) == 0)
    1997             :             {
    1998           0 :               vec_add1 (cf->current_command, ' ');
    1999           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    2000             :             }
    2001             : 
    2002           0 :           cf->cursor = vec_len (cf->current_command);
    2003             : 
    2004             :         }
    2005           0 :       else if (vec_len (possible_commands) >= 2)
    2006           0 :         {
    2007             :           u8 **possible_command;
    2008           0 :           uword max_command_len = 0, min_command_len = ~0;
    2009             :           u32 i;
    2010             : 
    2011           0 :           vec_foreach (possible_command, possible_commands)
    2012             :           {
    2013           0 :             if (vec_len (*possible_command) > max_command_len)
    2014           0 :               max_command_len = vec_len (*possible_command);
    2015           0 :             if (vec_len (*possible_command) < min_command_len)
    2016           0 :               min_command_len = vec_len (*possible_command);
    2017             :           }
    2018             : 
    2019           0 :           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2020             : 
    2021           0 :           i = 0;
    2022           0 :           vec_foreach (possible_command, possible_commands)
    2023             :           {
    2024           0 :             if (i + max_command_len >= cf->width)
    2025             :               {
    2026           0 :                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2027           0 :                 i = 0;
    2028             :               }
    2029           0 :             unix_vlib_cli_output_cooked (cf, uf, *possible_command,
    2030           0 :                                          vec_len (*possible_command));
    2031           0 :             for (j = vec_len (*possible_command); j < max_command_len + 2;
    2032           0 :                  j++)
    2033             :               {
    2034           0 :                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    2035             :               }
    2036           0 :             i += max_command_len + 2;
    2037             :           }
    2038             : 
    2039           0 :           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2040             : 
    2041             :           /* rewrite prompt */
    2042           0 :           unix_cli_cli_prompt (cf, uf);
    2043           0 :           unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
    2044           0 :                                        vec_len (cf->current_command));
    2045             : 
    2046             :           /* count length of last word */
    2047           0 :           j = cf->cursor;
    2048           0 :           i = 0;
    2049           0 :           while (j >= 1 && !isspace (cf->current_command[j - 1]))
    2050             :             {
    2051           0 :               j--;
    2052           0 :               i++;
    2053             :             }
    2054             : 
    2055             :           /* determine smallest common command */
    2056           0 :           for (; i < min_command_len; i++)
    2057             :             {
    2058           0 :               u8 common = '\0';
    2059           0 :               int stop = 0;
    2060             : 
    2061           0 :               vec_foreach (possible_command, possible_commands)
    2062             :               {
    2063           0 :                 if (common == '\0')
    2064             :                   {
    2065           0 :                     common = (*possible_command)[i];
    2066             :                   }
    2067           0 :                 else if (common != (*possible_command)[i])
    2068             :                   {
    2069           0 :                     stop = 1;
    2070           0 :                     break;
    2071             :                   }
    2072             :               }
    2073             : 
    2074           0 :               if (!stop)
    2075             :                 {
    2076           0 :                   vec_add1 (cf->current_command, common);
    2077           0 :                   cf->cursor++;
    2078           0 :                   unix_vlib_cli_output_cooked (cf, uf, (u8 *) & common, 1);
    2079             :                 }
    2080             :               else
    2081             :                 {
    2082           0 :                   break;
    2083             :                 }
    2084             :             }
    2085             :         }
    2086             :       else
    2087             :         {
    2088           0 :           unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
    2089             :         }
    2090             : 
    2091           0 :       if (vec_len (save) > 0)
    2092             :         {
    2093             :           /* restore remaining input if tab was hit in the middle of a line */
    2094           0 :           unix_vlib_cli_output_cooked (cf, uf, save, vec_len (save));
    2095           0 :           cf->cursor += vec_len (save);
    2096           0 :           for (j = 0; j < vec_len (save); j++, cf->cursor--)
    2097           0 :             unix_vlib_cli_output_cursor_left (cf, uf);
    2098           0 :           vec_append (cf->current_command, save);
    2099           0 :           vec_free (save);
    2100             :         }
    2101           0 :       vec_free (possible_commands);
    2102             : 
    2103           0 :       break;
    2104           0 :     case UNIX_CLI_PARSE_ACTION_YANK:
    2105             :       /* TODO */
    2106           0 :       break;
    2107             : 
    2108             : 
    2109             :     case UNIX_CLI_PARSE_ACTION_PAGER_QUIT:
    2110           0 :     pager_quit:
    2111           0 :       unix_cli_pager_prompt_erase (cf, uf);
    2112           0 :       unix_cli_pager_reset (cf);
    2113           0 :       unix_cli_cli_prompt (cf, uf);
    2114           0 :       break;
    2115             : 
    2116           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_NEXT:
    2117             :     case UNIX_CLI_PARSE_ACTION_PAGER_PGDN:
    2118             :       /* show next page of the buffer */
    2119           0 :       if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
    2120             :         {
    2121           0 :           u8 *line = NULL;
    2122           0 :           unix_cli_pager_index_t *pi = NULL;
    2123             : 
    2124           0 :           int m = cf->pager_start + (cf->height - 1);
    2125           0 :           unix_cli_pager_prompt_erase (cf, uf);
    2126           0 :           for (j = m;
    2127           0 :                j < vec_len (cf->pager_index) && cf->pager_start < m;
    2128           0 :                j++, cf->pager_start++)
    2129             :             {
    2130           0 :               pi = &cf->pager_index[j];
    2131           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    2132           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2133             :             }
    2134             :           /* if the last line didn't end in newline, add a newline */
    2135           0 :           if (pi && line[pi->length - 1] != '\n')
    2136           0 :             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2137           0 :           unix_cli_pager_prompt (cf, uf);
    2138             :         }
    2139             :       else
    2140             :         {
    2141           0 :           if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
    2142             :             /* no more in buffer, exit, but only if it was <space> */
    2143           0 :             goto pager_quit;
    2144             :         }
    2145           0 :       break;
    2146             : 
    2147           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_DN:
    2148             :     case UNIX_CLI_PARSE_ACTION_PAGER_CRLF:
    2149             :       /* display the next line of the buffer */
    2150           0 :       if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
    2151             :         {
    2152             :           u8 *line;
    2153             :           unix_cli_pager_index_t *pi;
    2154             : 
    2155           0 :           unix_cli_pager_prompt_erase (cf, uf);
    2156           0 :           pi = &cf->pager_index[cf->pager_start + (cf->height - 1)];
    2157           0 :           line = cf->pager_vector[pi->line] + pi->offset;
    2158           0 :           unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2159           0 :           cf->pager_start++;
    2160             :           /* if the last line didn't end in newline, add a newline */
    2161           0 :           if (line[pi->length - 1] != '\n')
    2162           0 :             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2163           0 :           unix_cli_pager_prompt (cf, uf);
    2164             :         }
    2165             :       else
    2166             :         {
    2167           0 :           if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
    2168             :             /* no more in buffer, exit, but only if it was <enter> */
    2169           0 :             goto pager_quit;
    2170             :         }
    2171             : 
    2172           0 :       break;
    2173             : 
    2174           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_UP:
    2175             :       /* scroll the page back one line */
    2176           0 :       if (cf->pager_start > 0)
    2177             :         {
    2178           0 :           u8 *line = NULL;
    2179           0 :           unix_cli_pager_index_t *pi = NULL;
    2180             : 
    2181           0 :           cf->pager_start--;
    2182           0 :           if (cf->ansi_capable)
    2183             :             {
    2184           0 :               pi = &cf->pager_index[cf->pager_start];
    2185           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    2186           0 :               unix_cli_pager_prompt_erase (cf, uf);
    2187           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_SCROLLDN,
    2188             :                                            sizeof (ANSI_SCROLLDN) - 1);
    2189           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_SAVECURSOR,
    2190             :                                            sizeof (ANSI_SAVECURSOR) - 1);
    2191           0 :               unix_cli_ansi_cursor (cf, uf, 1, 1);
    2192           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_CLEARLINE,
    2193             :                                            sizeof (ANSI_CLEARLINE) - 1);
    2194           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2195           0 :               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_RESTCURSOR,
    2196             :                                            sizeof (ANSI_RESTCURSOR) - 1);
    2197           0 :               unix_cli_pager_prompt_erase (cf, uf);
    2198           0 :               unix_cli_pager_prompt (cf, uf);
    2199             :             }
    2200             :           else
    2201             :             {
    2202           0 :               int m = cf->pager_start + (cf->height - 1);
    2203           0 :               unix_cli_pager_prompt_erase (cf, uf);
    2204           0 :               for (j = cf->pager_start;
    2205           0 :                    j < vec_len (cf->pager_index) && j < m; j++)
    2206             :                 {
    2207           0 :                   pi = &cf->pager_index[j];
    2208           0 :                   line = cf->pager_vector[pi->line] + pi->offset;
    2209           0 :                   unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2210             :                 }
    2211             :               /* if the last line didn't end in newline, add a newline */
    2212           0 :               if (pi && line[pi->length - 1] != '\n')
    2213           0 :                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2214           0 :               unix_cli_pager_prompt (cf, uf);
    2215             :             }
    2216             :         }
    2217           0 :       break;
    2218             : 
    2219           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_TOP:
    2220             :       /* back to the first page of the buffer */
    2221           0 :       if (cf->pager_start > 0)
    2222             :         {
    2223           0 :           u8 *line = NULL;
    2224           0 :           unix_cli_pager_index_t *pi = NULL;
    2225             : 
    2226           0 :           cf->pager_start = 0;
    2227           0 :           int m = cf->pager_start + (cf->height - 1);
    2228           0 :           unix_cli_pager_prompt_erase (cf, uf);
    2229           0 :           for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
    2230           0 :                j++)
    2231             :             {
    2232           0 :               pi = &cf->pager_index[j];
    2233           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    2234           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2235             :             }
    2236             :           /* if the last line didn't end in newline, add a newline */
    2237           0 :           if (pi && line[pi->length - 1] != '\n')
    2238           0 :             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2239           0 :           unix_cli_pager_prompt (cf, uf);
    2240             :         }
    2241           0 :       break;
    2242             : 
    2243           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM:
    2244             :       /* skip to the last page of the buffer */
    2245           0 :       if (cf->pager_start < vec_len (cf->pager_index) - (cf->height - 1))
    2246             :         {
    2247           0 :           u8 *line = NULL;
    2248           0 :           unix_cli_pager_index_t *pi = NULL;
    2249             : 
    2250           0 :           cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
    2251           0 :           unix_cli_pager_prompt_erase (cf, uf);
    2252           0 :           unix_cli_pager_message (cf, uf, "skipping", "\n");
    2253           0 :           for (j = cf->pager_start; j < vec_len (cf->pager_index); j++)
    2254             :             {
    2255           0 :               pi = &cf->pager_index[j];
    2256           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    2257           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2258             :             }
    2259             :           /* if the last line didn't end in newline, add a newline */
    2260           0 :           if (pi && line[pi->length - 1] != '\n')
    2261           0 :             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2262           0 :           unix_cli_pager_prompt (cf, uf);
    2263             :         }
    2264           0 :       break;
    2265             : 
    2266           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_PGUP:
    2267             :       /* wander back one page in the buffer */
    2268           0 :       if (cf->pager_start > 0)
    2269             :         {
    2270           0 :           u8 *line = NULL;
    2271           0 :           unix_cli_pager_index_t *pi = NULL;
    2272             :           int m;
    2273             : 
    2274           0 :           if (cf->pager_start >= cf->height)
    2275           0 :             cf->pager_start -= cf->height - 1;
    2276             :           else
    2277           0 :             cf->pager_start = 0;
    2278           0 :           m = cf->pager_start + cf->height - 1;
    2279           0 :           unix_cli_pager_prompt_erase (cf, uf);
    2280           0 :           for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
    2281           0 :                j++)
    2282             :             {
    2283           0 :               pi = &cf->pager_index[j];
    2284           0 :               line = cf->pager_vector[pi->line] + pi->offset;
    2285           0 :               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
    2286             :             }
    2287             :           /* if the last line didn't end in newline, add a newline */
    2288           0 :           if (pi && line[pi->length - 1] != '\n')
    2289           0 :             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2290           0 :           unix_cli_pager_prompt (cf, uf);
    2291             :         }
    2292           0 :       break;
    2293             : 
    2294           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_REDRAW:
    2295             :       /* Redraw the current pager screen */
    2296           0 :       unix_cli_pager_redraw (cf, uf);
    2297           0 :       break;
    2298             : 
    2299           0 :     case UNIX_CLI_PARSE_ACTION_PAGER_SEARCH:
    2300             :       /* search forwards in the buffer */
    2301           0 :       break;
    2302             : 
    2303             : 
    2304             :     case UNIX_CLI_PARSE_ACTION_CRLF:
    2305           0 :     crlf:
    2306           0 :       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
    2307             : 
    2308           0 :       if (cf->has_history && cf->history_limit)
    2309             :         {
    2310           0 :           if (cf->command_history
    2311           0 :               && vec_len (cf->command_history) >= cf->history_limit)
    2312             :             {
    2313           0 :               vec_free (cf->command_history[0]);
    2314           0 :               vec_delete (cf->command_history, 1, 0);
    2315             :             }
    2316             :           /* Don't add blank lines to the cmd history */
    2317           0 :           if (vec_len (cf->current_command))
    2318             :             {
    2319             :               /* Don't duplicate the previous command */
    2320           0 :               j = vec_len (cf->command_history);
    2321           0 :               if (j == 0 ||
    2322           0 :                   (vec_len (cf->current_command) !=
    2323           0 :                    vec_len (cf->command_history[j - 1])
    2324           0 :                    || memcmp (cf->current_command, cf->command_history[j - 1],
    2325           0 :                               vec_len (cf->current_command)) != 0))
    2326             :                 {
    2327             :                   /* copy the command to the history */
    2328           0 :                   u8 *c = 0;
    2329           0 :                   vec_append (c, cf->current_command);
    2330           0 :                   vec_add1 (cf->command_history, c);
    2331           0 :                   cf->command_number++;
    2332             :                 }
    2333             :             }
    2334           0 :           cf->excursion = vec_len (cf->command_history);
    2335             :         }
    2336             : 
    2337           0 :       cf->search_mode = 0;
    2338           0 :       vec_reset_length (cf->search_key);
    2339           0 :       cf->cursor = 0;
    2340             : 
    2341           0 :       return 0;
    2342             : 
    2343           0 :     case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
    2344             :     case UNIX_CLI_PARSE_ACTION_NOMATCH:
    2345           0 :       if (vec_len (cf->pager_index))
    2346             :         {
    2347             :           /* no-op for now */
    2348             :         }
    2349           0 :       else if (cf->has_history && cf->search_mode != 0 && isprint (input))
    2350             :         {
    2351             :           int k, limit, offset;
    2352             :           u8 *item;
    2353             : 
    2354           0 :           vec_add1 (cf->search_key, input);
    2355             : 
    2356           0 :         search_again:
    2357           0 :           for (j = 0; j < vec_len (cf->command_history); j++)
    2358             :             {
    2359           0 :               if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
    2360           0 :                 cf->excursion = 0;
    2361           0 :               else if (cf->excursion < 0)
    2362           0 :                 cf->excursion = vec_len (cf->command_history) - 1;
    2363             : 
    2364           0 :               item = cf->command_history[cf->excursion];
    2365             : 
    2366           0 :               limit = (vec_len (cf->search_key) > vec_len (item)) ?
    2367           0 :                 vec_len (item) : vec_len (cf->search_key);
    2368             : 
    2369           0 :               for (offset = 0; offset <= vec_len (item) - limit; offset++)
    2370             :                 {
    2371           0 :                   for (k = 0; k < limit; k++)
    2372             :                     {
    2373           0 :                       if (item[k + offset] != cf->search_key[k])
    2374           0 :                         goto next_offset;
    2375             :                     }
    2376           0 :                   goto found_at_offset;
    2377             : 
    2378           0 :                 next_offset:
    2379             :                   ;
    2380             :                 }
    2381           0 :               goto next;
    2382             : 
    2383           0 :             found_at_offset:
    2384           0 :               for (; cf->cursor > 0; cf->cursor--)
    2385             :                 {
    2386           0 :                   unix_vlib_cli_output_cursor_left (cf, uf);
    2387           0 :                   unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
    2388           0 :                   unix_vlib_cli_output_cursor_left (cf, uf);
    2389             :                 }
    2390             : 
    2391           0 :               vec_validate (cf->current_command, vec_len (item) - 1);
    2392           0 :               clib_memcpy (cf->current_command, item, vec_len (item));
    2393           0 :               vec_set_len (cf->current_command, vec_len (item));
    2394             : 
    2395           0 :               unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
    2396           0 :                                            vec_len (cf->current_command));
    2397           0 :               cf->cursor = vec_len (cf->current_command);
    2398           0 :               goto found;
    2399             : 
    2400           0 :             next:
    2401           0 :               cf->excursion += cf->search_mode;
    2402             :             }
    2403             : 
    2404           0 :           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\nNo match...", 12);
    2405           0 :           vec_reset_length (cf->search_key);
    2406           0 :           vec_reset_length (cf->current_command);
    2407           0 :           cf->search_mode = 0;
    2408           0 :           cf->cursor = 0;
    2409           0 :           goto crlf;
    2410             :         }
    2411           0 :       else if (isprint (input)) /* skip any errant control codes */
    2412             :         {
    2413           0 :           if (cf->cursor == vec_len (cf->current_command))
    2414             :             {
    2415             :               /* Append to end */
    2416           0 :               vec_add1 (cf->current_command, input);
    2417           0 :               cf->cursor++;
    2418             : 
    2419             :               /* Echo the character back to the client */
    2420           0 :               unix_vlib_cli_output_cooked (cf, uf, &input, 1);
    2421             :             }
    2422             :           else
    2423             :             {
    2424             :               /* Insert at cursor: resize +1 byte, move everything over */
    2425           0 :               j = vec_len (cf->current_command) - cf->cursor;
    2426           0 :               vec_add1 (cf->current_command, (u8) 'A');
    2427           0 :               memmove (cf->current_command + cf->cursor + 1,
    2428           0 :                        cf->current_command + cf->cursor, j);
    2429           0 :               cf->current_command[cf->cursor] = input;
    2430             :               /* Redraw the line */
    2431           0 :               j++;
    2432           0 :               unix_vlib_cli_output_cooked (cf, uf,
    2433           0 :                                            cf->current_command + cf->cursor,
    2434             :                                            j);
    2435           0 :               cf->cursor += j;
    2436           0 :               j--;
    2437             :               /* Put terminal cursor back */
    2438           0 :               for (; j > 0; j--, cf->cursor--)
    2439           0 :                 unix_vlib_cli_output_cursor_left (cf, uf);
    2440             :             }
    2441             :         }
    2442             :       else
    2443             :         {
    2444             :           /* no-op - not printable or otherwise not actionable */
    2445             :         }
    2446             : 
    2447           0 :     found:
    2448             : 
    2449           0 :       break;
    2450             : 
    2451           0 :     case UNIX_CLI_PARSE_ACTION_TELNETIAC:
    2452           0 :       break;
    2453             :     }
    2454           0 :   return 1;
    2455             : }
    2456             : 
    2457             : /** @brief Process input bytes on a stream to provide line editing and
    2458             :  * command history in the CLI. */
    2459             : static int
    2460           0 : unix_cli_line_edit (unix_cli_main_t * cm, unix_main_t * um,
    2461             :                     clib_file_main_t * fm, unix_cli_file_t * cf)
    2462             : {
    2463           0 :   clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    2464             :   int i;
    2465             : 
    2466           0 :   for (i = 0; i < vec_len (cf->input_vector); i++)
    2467             :     {
    2468             :       unix_cli_parse_action_t action;
    2469           0 :       i32 matched = 0;
    2470             :       unix_cli_parse_actions_t *a;
    2471             : 
    2472             :       /* If we're in the pager mode, search the pager actions */
    2473           0 :       a =
    2474           0 :         vec_len (cf->pager_index) ? unix_cli_parse_pager :
    2475             :         unix_cli_parse_strings;
    2476             : 
    2477             :       /* See if the input buffer is some sort of control code */
    2478           0 :       action = unix_cli_match_action (a, &cf->input_vector[i],
    2479           0 :                                       vec_len (cf->input_vector) - i,
    2480             :                                       &matched);
    2481             : 
    2482           0 :       switch (action)
    2483             :         {
    2484           0 :         case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
    2485           0 :           if (i)
    2486             :             {
    2487             :               /* There was a partial match which means we need more bytes
    2488             :                * than the input buffer currently has.
    2489             :                * Since the bytes before here have been processed, shift
    2490             :                * the remaining contents to the start of the input buffer.
    2491             :                */
    2492           0 :               vec_delete (cf->input_vector, i, 0);
    2493             :             }
    2494           0 :           return 1;             /* wait for more */
    2495             : 
    2496           0 :         case UNIX_CLI_PARSE_ACTION_TELNETIAC:
    2497             :           /* process telnet options */
    2498           0 :           matched = unix_cli_process_telnet (um, cf, uf,
    2499           0 :                                              cf->input_vector + i,
    2500           0 :                                              vec_len (cf->input_vector) - i);
    2501           0 :           if (matched < 0)
    2502             :             {
    2503             :               /* There was a partial match which means we need more bytes
    2504             :                * than the input buffer currently has.
    2505             :                */
    2506           0 :               if (i)
    2507             :                 {
    2508             :                   /*
    2509             :                    * Since the bytes before here have been processed, shift
    2510             :                    * the remaining contents to the start of the input buffer.
    2511             :                    */
    2512           0 :                   vec_delete (cf->input_vector, i, 0);
    2513             :                 }
    2514           0 :               return 1;         /* wait for more */
    2515             :             }
    2516           0 :           break;
    2517             : 
    2518           0 :         default:
    2519             :           /* If telnet option processing switched us to line mode, get us
    2520             :            * out of here!
    2521             :            */
    2522           0 :           if (cf->line_mode)
    2523             :             {
    2524           0 :               vec_delete (cf->input_vector, i, 0);
    2525           0 :               vec_free (cf->current_command);
    2526           0 :               cf->current_command = cf->input_vector;
    2527           0 :               return 0;
    2528             :             }
    2529             : 
    2530             :           /* process the action */
    2531           0 :           if (!unix_cli_line_process_one (cm, um, cf, uf,
    2532           0 :                                           cf->input_vector[i], action))
    2533             :             {
    2534             :               /* CRLF found. Consume the bytes from the input_vector */
    2535           0 :               vec_delete (cf->input_vector, i + matched, 0);
    2536             :               /* And tell our caller to execute cf->input_command */
    2537           0 :               return 0;
    2538             :             }
    2539             :         }
    2540             : 
    2541           0 :       i += matched;
    2542             :     }
    2543             : 
    2544           0 :   vec_reset_length (cf->input_vector);
    2545           0 :   return 1;
    2546             : }
    2547             : 
    2548             : /** @brief Process input to a CLI session. */
    2549             : static void
    2550           0 : unix_cli_process_input (unix_cli_main_t * cm, uword cli_file_index)
    2551             : {
    2552           0 :   unix_main_t *um = &unix_main;
    2553           0 :   clib_file_main_t *fm = &file_main;
    2554             :   clib_file_t *uf;
    2555           0 :   unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
    2556             :   unformat_input_t input;
    2557             :   int vlib_parse_eval (u8 *);
    2558             : 
    2559           0 :   cm->current_input_file_index = cli_file_index;
    2560             : 
    2561           0 : more:
    2562             :   /* Try vlibplex first.  Someday... */
    2563             :   if (0 && vlib_parse_eval (cf->input_vector) == 0)
    2564             :     goto done;
    2565             : 
    2566             : 
    2567           0 :   if (cf->line_mode)
    2568             :     {
    2569             :       /* just treat whatever we got as a complete line of input */
    2570           0 :       cf->current_command = cf->input_vector;
    2571             :     }
    2572             :   else
    2573             :     {
    2574             :       /* Line edit, echo, etc. */
    2575           0 :       if (unix_cli_line_edit (cm, um, fm, cf))
    2576             :         /* want more input */
    2577           0 :         return;
    2578             :     }
    2579             : 
    2580           0 :   if (um->log_fd)
    2581             :     {
    2582             :       static u8 *lv;
    2583           0 :       vec_reset_length (lv);
    2584           0 :       lv = format (lv, "%U[%d]: %v", format_timeval,
    2585             :                    NULL /* current bat-format */, 0 /* current bat-time */,
    2586             :                    cli_file_index, cf->current_command);
    2587           0 :       if ((vec_len (cf->current_command) > 0) &&
    2588           0 :           (cf->current_command[vec_len (cf->current_command) - 1] != '\n'))
    2589           0 :         lv = format (lv, "\n");
    2590           0 :       int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv));
    2591             :     }
    2592             : 
    2593             :   /* Run the command through the macro processor */
    2594           0 :   if (vec_len (cf->current_command))
    2595             :     {
    2596             :       u8 *expanded;
    2597           0 :       vec_validate (cf->current_command, vec_len (cf->current_command));
    2598           0 :       cf->current_command[vec_len (cf->current_command) - 1] = 0;
    2599             :       /* The macro expander expects proper C-strings, not vectors */
    2600           0 :       expanded = (u8 *) clib_macro_eval (&cf->macro_main,
    2601           0 :                                          (i8 *) cf->current_command,
    2602             :                                          1 /* complain */ ,
    2603             :                                          0 /* level */ ,
    2604             :                                          8 /* max_level */ );
    2605             :       /* Macro processor NULL terminates the return */
    2606           0 :       vec_dec_len (expanded, 1);
    2607           0 :       vec_reset_length (cf->current_command);
    2608           0 :       vec_append (cf->current_command, expanded);
    2609           0 :       vec_free (expanded);
    2610             :     }
    2611             : 
    2612             :   /* Build an unformat structure around our command */
    2613           0 :   unformat_init_vector (&input, cf->current_command);
    2614             : 
    2615             :   /* Remove leading white space from input. */
    2616           0 :   (void) unformat (&input, "");
    2617             : 
    2618           0 :   cf->pager_start = 0;               /* start a new pager session */
    2619             : 
    2620           0 :   if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
    2621           0 :     vlib_cli_input (um->vlib_main, &input, unix_vlib_cli_output,
    2622             :                     cli_file_index);
    2623             : 
    2624             :   /* Zero buffer since otherwise unformat_free will call vec_free on it. */
    2625           0 :   input.buffer = 0;
    2626             : 
    2627           0 :   unformat_free (&input);
    2628             : 
    2629             :   /* Re-fetch pointer since pool may have moved. */
    2630           0 :   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
    2631           0 :   uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    2632             : 
    2633           0 : done:
    2634             :   /* reset vector; we'll re-use it later  */
    2635           0 :   if (cf->line_mode)
    2636             :     {
    2637           0 :       vec_reset_length (cf->input_vector);
    2638           0 :       cf->current_command = 0;
    2639             :     }
    2640             :   else
    2641             :     {
    2642           0 :       vec_reset_length (cf->current_command);
    2643             :     }
    2644             : 
    2645           0 :   if (cf->no_pager == 2)
    2646             :     {
    2647             :       /* Pager was programmatically disabled */
    2648           0 :       unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
    2649           0 :       cf->no_pager = um->cli_no_pager;
    2650             :     }
    2651             : 
    2652           0 :   if (vec_len (cf->pager_index) == 0
    2653           0 :       || vec_len (cf->pager_index) < cf->height)
    2654             :     {
    2655             :       /* There was no need for the pager */
    2656           0 :       unix_cli_pager_reset (cf);
    2657             : 
    2658             :       /* Prompt. */
    2659           0 :       unix_cli_cli_prompt (cf, uf);
    2660             :     }
    2661             :   else
    2662             :     {
    2663             :       /* Display the pager prompt */
    2664           0 :       unix_cli_pager_prompt (cf, uf);
    2665             :     }
    2666             : 
    2667             :   /* Any residual data in the input vector? */
    2668           0 :   if (vec_len (cf->input_vector))
    2669           0 :     goto more;
    2670             : 
    2671             :   /* For non-interactive sessions send a NUL byte.
    2672             :    * Specifically this is because vppctl needs to see some traffic in
    2673             :    * order to move on to closing the session. Commands with no output
    2674             :    * would thus cause vppctl to hang indefinitely in non-interactive mode
    2675             :    * since there is also no prompt sent after the command completes.
    2676             :    */
    2677           0 :   if (!cf->is_interactive)
    2678           0 :     unix_vlib_cli_output_raw (cf, uf, (u8 *) "\0", 1);
    2679             : }
    2680             : 
    2681             : /** Destroy a CLI session.
    2682             :  * @note If we destroy the @c stdin session this additionally signals
    2683             :  *       the shutdown of VPP.
    2684             :  */
    2685             : static void
    2686           0 : unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
    2687             : {
    2688           0 :   unix_main_t *um = &unix_main;
    2689           0 :   clib_file_main_t *fm = &file_main;
    2690             :   unix_cli_file_t *cf;
    2691             :   clib_file_t *uf;
    2692             :   int i;
    2693             : 
    2694             :   /* Validate cli_file_index */
    2695           0 :   if (pool_is_free_index (cm->cli_file_pool, cli_file_index))
    2696           0 :     return;
    2697             : 
    2698           0 :   vec_foreach_index (i, cm->new_sessions)
    2699             :     {
    2700           0 :       unix_cli_new_session_t *ns = vec_elt_at_index (cm->new_sessions, i);
    2701             : 
    2702           0 :       if (ns->cf_index == cli_file_index)
    2703             :         {
    2704           0 :           ns->cf_index = ~0;
    2705           0 :           break;
    2706             :         }
    2707             :     }
    2708             : 
    2709           0 :   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
    2710           0 :   uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    2711             : 
    2712             :   /* Quit/EOF on stdin means quit program. */
    2713           0 :   if (uf->file_descriptor == STDIN_FILENO)
    2714           0 :     clib_longjmp (&um->vlib_main->main_loop_exit, VLIB_MAIN_LOOP_EXIT_CLI);
    2715             : 
    2716           0 :   vec_free (cf->current_command);
    2717           0 :   vec_free (cf->search_key);
    2718             : 
    2719           0 :   for (i = 0; i < vec_len (cf->command_history); i++)
    2720           0 :     vec_free (cf->command_history[i]);
    2721             : 
    2722           0 :   vec_free (cf->command_history);
    2723           0 :   vec_free (cf->input_vector);
    2724             : 
    2725           0 :   clib_file_del (fm, uf);
    2726             : 
    2727           0 :   unix_cli_file_free (cf);
    2728           0 :   clib_macro_free (&cf->macro_main);
    2729           0 :   pool_put (cm->cli_file_pool, cf);
    2730             : }
    2731             : 
    2732             : /** Handle system events. */
    2733             : static uword
    2734           0 : unix_cli_process (vlib_main_t * vm,
    2735             :                   vlib_node_runtime_t * rt, vlib_frame_t * f)
    2736             : {
    2737           0 :   unix_cli_main_t *cm = &unix_cli_main;
    2738           0 :   uword i, *data = 0;
    2739             : 
    2740             :   while (1)
    2741           0 :     {
    2742             :       unix_cli_process_event_type_t event_type;
    2743           0 :       vlib_process_wait_for_event (vm);
    2744           0 :       event_type = vlib_process_get_events (vm, &data);
    2745             : 
    2746           0 :       switch (event_type)
    2747             :         {
    2748           0 :         case UNIX_CLI_PROCESS_EVENT_READ_READY:
    2749           0 :           for (i = 0; i < vec_len (data); i++)
    2750           0 :             unix_cli_process_input (cm, data[i]);
    2751           0 :           break;
    2752             : 
    2753           0 :         case UNIX_CLI_PROCESS_EVENT_QUIT:
    2754             :           /* Kill this process. */
    2755           0 :           for (i = 0; i < vec_len (data); i++)
    2756           0 :             unix_cli_kill (cm, data[i]);
    2757           0 :           goto done;
    2758             :         }
    2759             : 
    2760           0 :       if (data)
    2761           0 :         vec_set_len (data, 0);
    2762             :     }
    2763             : 
    2764           0 : done:
    2765           0 :   vec_free (data);
    2766             : 
    2767           0 :   vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
    2768             : 
    2769             :   /* Add node index so we can re-use this process later. */
    2770           0 :   vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);
    2771             : 
    2772           0 :   return 0;
    2773             : }
    2774             : 
    2775             : /** Called when a CLI session file descriptor can be written to without
    2776             :  * blocking. */
    2777             : static clib_error_t *
    2778           0 : unix_cli_write_ready (clib_file_t * uf)
    2779             : {
    2780           0 :   unix_cli_main_t *cm = &unix_cli_main;
    2781             :   unix_cli_file_t *cf;
    2782             :   int n;
    2783             : 
    2784           0 :   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
    2785             : 
    2786             :   /* Flush output vector. */
    2787           0 :   if (cf->is_socket)
    2788             :     /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
    2789           0 :     n = send (uf->file_descriptor,
    2790           0 :               cf->output_vector, vec_len (cf->output_vector), MSG_NOSIGNAL);
    2791             :   else
    2792           0 :     n = write (uf->file_descriptor,
    2793           0 :                cf->output_vector, vec_len (cf->output_vector));
    2794             : 
    2795           0 :   if (n < 0 && errno != EAGAIN)
    2796             :     {
    2797           0 :       if (errno == EPIPE)
    2798             :         {
    2799             :           /* connection closed on us */
    2800           0 :           unix_main_t *um = &unix_main;
    2801           0 :           cf->has_epipe = 1;
    2802           0 :           vlib_process_signal_event (um->vlib_main, cf->process_node_index,
    2803             :                                      UNIX_CLI_PROCESS_EVENT_QUIT,
    2804             :                                      uf->private_data);
    2805             :         }
    2806             :       else
    2807             :         {
    2808           0 :           return clib_error_return_unix (0, "write");
    2809             :         }
    2810             :     }
    2811             : 
    2812           0 :   else if (n > 0)
    2813           0 :     unix_cli_del_pending_output (uf, cf, n);
    2814             : 
    2815           0 :   return /* no error */ 0;
    2816             : }
    2817             : 
    2818             : /** Called when a CLI session file descriptor has data to be read. */
    2819             : static clib_error_t *
    2820           0 : unix_cli_read_ready (clib_file_t * uf)
    2821             : {
    2822           0 :   unix_main_t *um = &unix_main;
    2823           0 :   unix_cli_main_t *cm = &unix_cli_main;
    2824             :   unix_cli_file_t *cf;
    2825             :   uword l;
    2826             :   int n, n_read, n_try;
    2827             : 
    2828           0 :   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
    2829             : 
    2830           0 :   n = n_try = 4096;
    2831           0 :   while (n == n_try)
    2832             :     {
    2833           0 :       l = vec_len (cf->input_vector);
    2834           0 :       vec_resize (cf->input_vector, l + n_try);
    2835             : 
    2836           0 :       n = read (uf->file_descriptor, cf->input_vector + l, n_try);
    2837             : 
    2838             :       /* Error? */
    2839           0 :       if (n < 0 && errno != EAGAIN)
    2840           0 :         return clib_error_return_unix (0, "read");
    2841             : 
    2842           0 :       n_read = n < 0 ? 0 : n;
    2843           0 :       vec_set_len (cf->input_vector, l + n_read);
    2844             :     }
    2845             : 
    2846           0 :   if (!(n < 0))
    2847           0 :     vlib_process_signal_event (um->vlib_main,
    2848           0 :                                cf->process_node_index,
    2849             :                                (n_read == 0
    2850             :                                 ? UNIX_CLI_PROCESS_EVENT_QUIT
    2851             :                                 : UNIX_CLI_PROCESS_EVENT_READ_READY),
    2852             :                                /* event data */ uf->private_data);
    2853             : 
    2854           0 :   return /* no error */ 0;
    2855             : }
    2856             : 
    2857             : /** Called when a CLI session file descriptor has an error condition. */
    2858             : static clib_error_t *
    2859           0 : unix_cli_error_detected (clib_file_t * uf)
    2860             : {
    2861           0 :   unix_main_t *um = &unix_main;
    2862           0 :   unix_cli_main_t *cm = &unix_cli_main;
    2863             :   unix_cli_file_t *cf;
    2864             : 
    2865           0 :   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
    2866           0 :   cf->has_epipe = 1;         /* prevent writes while the close is pending */
    2867           0 :   vlib_process_signal_event (um->vlib_main,
    2868           0 :                              cf->process_node_index,
    2869             :                              UNIX_CLI_PROCESS_EVENT_QUIT,
    2870             :                              /* event data */ uf->private_data);
    2871             : 
    2872           0 :   return /* no error */ 0;
    2873             : }
    2874             : 
    2875             : /** Store a new CLI session.
    2876             :  * @param name The name of the session.
    2877             :  * @param fd   The file descriptor for the session I/O.
    2878             :  * @return The session ID.
    2879             :  */
    2880             : static u32
    2881           0 : unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
    2882             : {
    2883           0 :   unix_main_t *um = &unix_main;
    2884           0 :   clib_file_main_t *fm = &file_main;
    2885             :   unix_cli_file_t *cf;
    2886           0 :   clib_file_t template = { 0 };
    2887           0 :   vlib_main_t *vm = um->vlib_main;
    2888           0 :   vlib_node_t *n = 0;
    2889             : 
    2890           0 :   if (vec_len (cm->unused_cli_process_node_indices) > 0)
    2891             :     {
    2892           0 :       n = vlib_get_node (vm, vec_pop (cm->unused_cli_process_node_indices));
    2893             : 
    2894           0 :       vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
    2895             :     }
    2896             :   else
    2897             :     {
    2898             :       static vlib_node_registration_t r = {
    2899             :         .function = unix_cli_process,
    2900             :         .type = VLIB_NODE_TYPE_PROCESS,
    2901             :         .process_log2_n_stack_bytes = 18,
    2902             :       };
    2903             :       static u32 count = 0;
    2904             : 
    2905           0 :       vlib_worker_thread_barrier_sync (vm);
    2906             : 
    2907           0 :       vlib_register_node (vm, &r, "unix-cli-process-%u", count++);
    2908             : 
    2909           0 :       n = vlib_get_node (vm, r.index);
    2910           0 :       vlib_worker_thread_node_runtime_update ();
    2911           0 :       vlib_worker_thread_barrier_release (vm);
    2912             :     }
    2913             : 
    2914           0 :   pool_get_zero (cm->cli_file_pool, cf);
    2915           0 :   clib_macro_init (&cf->macro_main);
    2916             : 
    2917           0 :   template.read_function = unix_cli_read_ready;
    2918           0 :   template.write_function = unix_cli_write_ready;
    2919           0 :   template.error_function = unix_cli_error_detected;
    2920           0 :   template.file_descriptor = fd;
    2921           0 :   template.private_data = cf - cm->cli_file_pool;
    2922           0 :   template.description = format (0, "%s", name);
    2923             : 
    2924           0 :   cf->name = format (0, "unix-cli-%s", name);
    2925           0 :   cf->process_node_index = n->index;
    2926           0 :   cf->clib_file_index = clib_file_add (fm, &template);
    2927           0 :   cf->output_vector = 0;
    2928           0 :   cf->input_vector = 0;
    2929           0 :   vec_validate (cf->current_command, 0);
    2930           0 :   vec_set_len (cf->current_command, 0);
    2931             : 
    2932           0 :   vlib_start_process (vm, n->runtime_index);
    2933             : 
    2934           0 :   vlib_process_t *p = vlib_get_process_from_node (vm, n);
    2935           0 :   p->output_function = unix_vlib_cli_output;
    2936           0 :   p->output_function_arg = cf - cm->cli_file_pool;
    2937             : 
    2938           0 :   return cf - cm->cli_file_pool;
    2939             : }
    2940             : 
    2941             : /** Telnet listening socket has a new connection. */
    2942             : static clib_error_t *
    2943           0 : unix_cli_listen_read_ready (clib_file_t * uf)
    2944             : {
    2945           0 :   unix_main_t *um = &unix_main;
    2946           0 :   clib_file_main_t *fm = &file_main;
    2947           0 :   unix_cli_main_t *cm = &unix_cli_main;
    2948           0 :   clib_socket_t *s = &um->cli_listen_socket;
    2949             :   clib_socket_t client;
    2950             :   char *client_name;
    2951             :   clib_error_t *error;
    2952             :   unix_cli_file_t *cf;
    2953             :   u32 cf_index;
    2954             :   int one;
    2955             : 
    2956           0 :   error = clib_socket_accept (s, &client);
    2957           0 :   if (error)
    2958           0 :     return error;
    2959             : 
    2960             :   /* Disable Nagle, ignore any errors doing so eg on PF_LOCAL socket */
    2961           0 :   one = 1;
    2962           0 :   (void) setsockopt (client.fd, IPPROTO_TCP, TCP_NODELAY,
    2963             :                      (void *) &one, sizeof (one));
    2964             : 
    2965           0 :   client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
    2966             : 
    2967           0 :   cf_index = unix_cli_file_add (cm, client_name, client.fd);
    2968           0 :   cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
    2969           0 :   cf->is_socket = 1;
    2970             : 
    2971             :   /* No longer need CLIB version of socket. */
    2972           0 :   clib_socket_free (&client);
    2973           0 :   vec_free (client_name);
    2974             : 
    2975             :   /* if we're supposed to run telnet session in character mode (default) */
    2976           0 :   if (um->cli_line_mode == 0)
    2977             :     {
    2978             :       /*
    2979             :        * Set telnet client character mode, echo on, suppress "go-ahead".
    2980             :        * Technically these should be negotiated, but this works.
    2981             :        */
    2982           0 :       u8 charmode_option[] = {
    2983             :         IAC, WONT, TELOPT_LINEMODE,     /* server will do char-by-char */
    2984             :         IAC, DONT, TELOPT_LINEMODE,     /* client should do char-by-char */
    2985             :         IAC, WILL, TELOPT_SGA,  /* server willl supress GA */
    2986             :         IAC, DO, TELOPT_SGA,    /* client should supress Go Ahead */
    2987             :         IAC, WILL, TELOPT_ECHO, /* server will do echo */
    2988             :         IAC, DONT, TELOPT_ECHO, /* client should not echo */
    2989             :         IAC, DO, TELOPT_TTYPE,  /* client should tell us its term type */
    2990             :         IAC, SB, TELOPT_TTYPE, 1, IAC, SE,      /* now tell me ttype */
    2991             :         IAC, DO, TELOPT_NAWS,   /* client should tell us its window sz */
    2992             :         IAC, SB, TELOPT_NAWS, 1, IAC, SE,       /* now tell me window size */
    2993             :       };
    2994             : 
    2995             :       /* Enable history on this CLI */
    2996           0 :       cf->history_limit = um->cli_history_limit;
    2997           0 :       cf->has_history = cf->history_limit != 0;
    2998             : 
    2999             :       /* This is an interactive session until we decide otherwise */
    3000           0 :       cf->is_interactive = 1;
    3001             : 
    3002             :       /* Make sure this session is in line mode */
    3003           0 :       cf->line_mode = 0;
    3004             : 
    3005             :       /* We need CRLF */
    3006           0 :       cf->crlf_mode = 1;
    3007             : 
    3008             :       /* Setup the pager */
    3009           0 :       cf->no_pager = um->cli_no_pager;
    3010             : 
    3011             :       /* Default terminal dimensions, should the terminal
    3012             :        * fail to provide any.
    3013             :        */
    3014           0 :       cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
    3015           0 :       cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
    3016             : 
    3017             :       /* Send the telnet options */
    3018           0 :       uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    3019           0 :       unix_vlib_cli_output_raw (cf, uf, charmode_option,
    3020             :                                 ARRAY_LEN (charmode_option));
    3021             : 
    3022           0 :       if (cm->new_session_process_node_index == ~0)
    3023             :         {
    3024             :           /* Create thw new session deadline process */
    3025           0 :           cm->new_session_process_node_index =
    3026           0 :             vlib_process_create (um->vlib_main, "unix-cli-new-session",
    3027             :                                  unix_cli_new_session_process,
    3028             :                                  16 /* log2_n_stack_bytes */ );
    3029             :         }
    3030             : 
    3031             :       /* In case the client doesn't negotiate terminal type, register
    3032             :        * our session with a process that will emit the prompt if
    3033             :        * a deadline passes */
    3034           0 :       vlib_process_signal_event (um->vlib_main,
    3035           0 :                                  cm->new_session_process_node_index,
    3036             :                                  UNIX_CLI_NEW_SESSION_EVENT_ADD, cf_index);
    3037             : 
    3038             :     }
    3039             : 
    3040           0 :   return error;
    3041             : }
    3042             : 
    3043             : /** The system terminal has informed us that the window size
    3044             :  * has changed.
    3045             :  */
    3046             : static void
    3047           0 : unix_cli_resize_interrupt (int signum)
    3048             : {
    3049           0 :   clib_file_main_t *fm = &file_main;
    3050           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3051           0 :   unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool,
    3052             :                                            cm->stdin_cli_file_index);
    3053           0 :   clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    3054             :   struct winsize ws;
    3055             :   (void) signum;
    3056             : 
    3057             :   /* Terminal resized, fetch the new size */
    3058           0 :   if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
    3059             :     {
    3060             :       /* "Should never happen..." */
    3061           0 :       clib_unix_warning ("TIOCGWINSZ");
    3062             :       /* We can't trust ws.XXX... */
    3063           0 :       return;
    3064             :     }
    3065             : 
    3066           0 :   cf->width = ws.ws_col;
    3067           0 :   if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH)
    3068           0 :     cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH;
    3069           0 :   if (cf->width == 0)
    3070           0 :     cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
    3071             : 
    3072           0 :   cf->height = ws.ws_row;
    3073           0 :   if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT)
    3074           0 :     cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT;
    3075           0 :   if (cf->height == 0)
    3076           0 :     cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
    3077             : 
    3078             :   /* Reindex the pager buffer */
    3079           0 :   unix_cli_pager_reindex (cf);
    3080             : 
    3081             :   /* Redraw the page */
    3082           0 :   unix_cli_pager_redraw (cf, uf);
    3083             : }
    3084             : 
    3085             : /** Handle configuration directives in the @em unix section. */
    3086             : static clib_error_t *
    3087         559 : unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
    3088             : {
    3089         559 :   unix_main_t *um = &unix_main;
    3090         559 :   clib_file_main_t *fm = &file_main;
    3091         559 :   unix_cli_main_t *cm = &unix_cli_main;
    3092             :   int flags;
    3093         559 :   clib_error_t *error = 0;
    3094             :   unix_cli_file_t *cf;
    3095             :   u32 cf_index;
    3096             :   struct termios tio;
    3097             :   struct sigaction sa;
    3098             :   struct winsize ws;
    3099             :   u8 *term;
    3100             : 
    3101             :   /* We depend on unix flags being set. */
    3102         559 :   if ((error = vlib_call_config_function (vm, unix_config)))
    3103           0 :     return error;
    3104             : 
    3105         559 :   if (um->flags & UNIX_FLAG_INTERACTIVE)
    3106             :     {
    3107             :       /* Set stdin to be non-blocking. */
    3108           0 :       if ((flags = fcntl (STDIN_FILENO, F_GETFL, 0)) < 0)
    3109           0 :         flags = 0;
    3110           0 :       (void) fcntl (STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
    3111             : 
    3112           0 :       cf_index = unix_cli_file_add (cm, "stdin", STDIN_FILENO);
    3113           0 :       cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
    3114           0 :       cm->stdin_cli_file_index = cf_index;
    3115             : 
    3116             :       /* If stdin is a tty and we are using chacracter mode, enable
    3117             :        * history on the CLI and set the tty line discipline accordingly. */
    3118           0 :       if (isatty (STDIN_FILENO) && um->cli_line_mode == 0)
    3119             :         {
    3120             :           /* Capture terminal resize events */
    3121           0 :           clib_memset (&sa, 0, sizeof (sa));
    3122           0 :           sa.sa_handler = unix_cli_resize_interrupt;
    3123           0 :           if (sigaction (SIGWINCH, &sa, 0) < 0)
    3124           0 :             clib_panic ("sigaction");
    3125             : 
    3126             :           /* Retrieve the current terminal size */
    3127           0 :           if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
    3128             :             {
    3129           0 :               cf->width = ws.ws_col;
    3130           0 :               cf->height = ws.ws_row;
    3131             :             }
    3132             : 
    3133           0 :           if (cf->width == 0 || cf->height == 0)
    3134             :             {
    3135             :               /*
    3136             :                * We have a tty, but no size. Use defaults.
    3137             :                * vpp "unix interactive" inside emacs + gdb ends up here.
    3138             :                */
    3139           0 :               cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
    3140           0 :               cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
    3141             :             }
    3142             : 
    3143             :           /* Setup the history */
    3144           0 :           cf->history_limit = um->cli_history_limit;
    3145           0 :           cf->has_history = cf->history_limit != 0;
    3146             : 
    3147             :           /* Setup the pager */
    3148           0 :           cf->no_pager = um->cli_no_pager;
    3149             : 
    3150             :           /* This is an interactive session until we decide otherwise */
    3151           0 :           cf->is_interactive = 1;
    3152             : 
    3153             :           /* We're going to be in char by char mode */
    3154           0 :           cf->line_mode = 0;
    3155             : 
    3156             :           /* Save the original tty state so we can restore it later */
    3157           0 :           tcgetattr (STDIN_FILENO, &um->tio_stdin);
    3158           0 :           um->tio_isset = 1;
    3159             : 
    3160             :           /* Tweak the tty settings */
    3161           0 :           tio = um->tio_stdin;
    3162             :           /* echo off, canonical mode off, ext'd input processing off */
    3163           0 :           tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
    3164             :           /* disable XON/XOFF, so ^S invokes the history search */
    3165           0 :           tio.c_iflag &= ~(IXON | IXOFF);
    3166           0 :           tio.c_cc[VMIN] = 1;   /* 1 byte at a time */
    3167           0 :           tio.c_cc[VTIME] = 0;  /* no timer */
    3168           0 :           tio.c_cc[VSTOP] = _POSIX_VDISABLE;    /* not ^S */
    3169           0 :           tio.c_cc[VSTART] = _POSIX_VDISABLE;   /* not ^Q */
    3170           0 :           tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio);
    3171             : 
    3172             :           /* See if we can do ANSI/VT100 output */
    3173           0 :           term = (u8 *) getenv ("TERM");
    3174           0 :           if (term != NULL)
    3175             :             {
    3176           0 :               int len = strlen ((char *) term);
    3177           0 :               cf->ansi_capable = unix_cli_terminal_type_ansi (term, len);
    3178           0 :               if (unix_cli_terminal_type_noninteractive (term, len))
    3179           0 :                 unix_cli_set_session_noninteractive (cf);
    3180             :             }
    3181             :         }
    3182             :       else
    3183             :         {
    3184             :           /* No tty, so make sure the session doesn't have tty-like features */
    3185           0 :           unix_cli_set_session_noninteractive (cf);
    3186             :         }
    3187             : 
    3188             :       /* Send banner and initial prompt */
    3189           0 :       unix_cli_file_welcome (cm, cf);
    3190             :     }
    3191             : 
    3192             :   /* If we have socket config, LISTEN, otherwise, don't */
    3193         559 :   clib_socket_t *s = &um->cli_listen_socket;
    3194         559 :   if (s->config && s->config[0] != 0)
    3195             :     {
    3196             :       /* CLI listen. */
    3197           0 :       clib_file_t template = { 0 };
    3198             : 
    3199             :       /* mkdir of file socketu, only under /run  */
    3200           0 :       if (strncmp (s->config, "/run", 4) == 0)
    3201             :         {
    3202           0 :           u8 *tmp = format (0, "%s", s->config);
    3203           0 :           int i = vec_len (tmp);
    3204           0 :           while (i && tmp[--i] != '/')
    3205             :             ;
    3206             : 
    3207           0 :           tmp[i] = '\0';
    3208             : 
    3209           0 :           if (i)
    3210           0 :             vlib_unix_recursive_mkdir ((char *) tmp);
    3211           0 :           vec_free (tmp);
    3212             :         }
    3213             : 
    3214           0 :       s->flags = CLIB_SOCKET_F_IS_SERVER |   /* listen, don't connect */
    3215           0 :         CLIB_SOCKET_F_ALLOW_GROUP_WRITE;        /* PF_LOCAL socket only */
    3216           0 :       error = clib_socket_init (s);
    3217             : 
    3218           0 :       if (error)
    3219           0 :         return error;
    3220             : 
    3221           0 :       template.read_function = unix_cli_listen_read_ready;
    3222           0 :       template.file_descriptor = s->fd;
    3223           0 :       template.description = format (0, "cli listener %s", s->config);
    3224             : 
    3225           0 :       clib_file_add (fm, &template);
    3226             :     }
    3227             : 
    3228             :   /* Set CLI prompt. */
    3229         559 :   if (!cm->cli_prompt)
    3230           0 :     cm->cli_prompt = format (0, "VLIB: ");
    3231             : 
    3232         559 :   return 0;
    3233             : }
    3234             : 
    3235             : /*?
    3236             :  * This module has no configurable parameters.
    3237             : ?*/
    3238        7306 : VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
    3239             : 
    3240             : /** Called when VPP is shutting down, this restores the system
    3241             :  * terminal state if previously saved.
    3242             :  */
    3243             : static clib_error_t *
    3244         559 : unix_cli_exit (vlib_main_t * vm)
    3245             : {
    3246         559 :   unix_main_t *um = &unix_main;
    3247             : 
    3248             :   /* If stdin is a tty and we saved the tty state, reset the tty state */
    3249         559 :   if (isatty (STDIN_FILENO) && um->tio_isset)
    3250           0 :     tcsetattr (STDIN_FILENO, TCSAFLUSH, &um->tio_stdin);
    3251             : 
    3252         559 :   return 0;
    3253             : }
    3254             : 
    3255        1119 : VLIB_MAIN_LOOP_EXIT_FUNCTION (unix_cli_exit);
    3256             : 
    3257             : /** Set the CLI prompt.
    3258             :  * @param prompt The C string to set the prompt to.
    3259             :  * @note This setting is global; it impacts all current
    3260             :  *       and future CLI sessions.
    3261             :  */
    3262             : void
    3263         559 : vlib_unix_cli_set_prompt (char *prompt)
    3264             : {
    3265         559 :   char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s ";
    3266         559 :   unix_cli_main_t *cm = &unix_cli_main;
    3267         559 :   if (cm->cli_prompt)
    3268           0 :     vec_free (cm->cli_prompt);
    3269         559 :   cm->cli_prompt = format (0, fmt, prompt);
    3270         559 : }
    3271             : 
    3272             : static unix_cli_file_t *
    3273           0 : unix_cli_file_if_exists (unix_cli_main_t * cm)
    3274             : {
    3275           0 :   if (!cm->cli_file_pool)
    3276           0 :     return 0;
    3277           0 :   return pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
    3278             : }
    3279             : 
    3280             : static unix_cli_file_t *
    3281           0 : unix_cli_file_if_interactive (unix_cli_main_t * cm)
    3282             : {
    3283             :   unix_cli_file_t *cf;
    3284           0 :   if ((cf = unix_cli_file_if_exists (cm)) && !cf->is_interactive)
    3285           0 :     return 0;
    3286           0 :   return cf;
    3287             : }
    3288             : 
    3289             : /** CLI command to quit the terminal session.
    3290             :  * @note If this is a stdin session then this will
    3291             :  *       shutdown VPP also.
    3292             :  */
    3293             : static clib_error_t *
    3294           0 : unix_cli_quit (vlib_main_t * vm,
    3295             :                unformat_input_t * input, vlib_cli_command_t * cmd)
    3296             : {
    3297           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3298             :   unix_cli_file_t *cf;
    3299             : 
    3300           0 :   if (!(cf = unix_cli_file_if_exists (cm)))
    3301           0 :     return clib_error_return (0, "invalid session");
    3302             : 
    3303             :   /* Cosmetic: suppress the final prompt from appearing before we die */
    3304           0 :   cf->is_interactive = 0;
    3305           0 :   cf->started = 1;
    3306             : 
    3307           0 :   vlib_process_signal_event (vm,
    3308             :                              vlib_current_process (vm),
    3309             :                              UNIX_CLI_PROCESS_EVENT_QUIT,
    3310           0 :                              cm->current_input_file_index);
    3311           0 :   return 0;
    3312             : }
    3313             : 
    3314             : /*?
    3315             :  * Terminates the current CLI session.
    3316             :  *
    3317             :  * If VPP is running in @em interactive mode and this is the console session
    3318             :  * (that is, the session on @c stdin) then this will also terminate VPP.
    3319             : ?*/
    3320             : /* *INDENT-OFF* */
    3321      272887 : VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
    3322             :   .path = "quit",
    3323             :   .short_help = "Exit CLI",
    3324             :   .function = unix_cli_quit,
    3325             : };
    3326             : /* *INDENT-ON* */
    3327             : 
    3328             : /* *INDENT-OFF* */
    3329      272887 : VLIB_CLI_COMMAND (unix_cli_q_command, static) = {
    3330             :   .path = "q",
    3331             :   .short_help = "Exit CLI",
    3332             :   .function = unix_cli_quit,
    3333             : };
    3334             : /* *INDENT-ON* */
    3335             : 
    3336             : /** CLI command to execute a VPP command script. */
    3337             : static clib_error_t *
    3338           0 : unix_cli_exec (vlib_main_t * vm,
    3339             :                unformat_input_t * input, vlib_cli_command_t * cmd)
    3340             : {
    3341             :   char *file_name;
    3342           0 :   int fd, rv = 0;
    3343             :   unformat_input_t sub_input, in;
    3344             :   clib_error_t *error;
    3345           0 :   clib_macro_main_t *mm = 0;
    3346           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3347             :   unix_cli_file_t *cf;
    3348           0 :   u8 *file_data = 0;
    3349           0 :   file_name = 0;
    3350           0 :   fd = -1;
    3351           0 :   error = 0;
    3352             :   struct stat s;
    3353             : 
    3354             : 
    3355           0 :   if (!unformat (input, "%s", &file_name))
    3356             :     {
    3357           0 :       error = clib_error_return (0, "expecting file name, got `%U'",
    3358             :                                  format_unformat_error, input);
    3359           0 :       goto done;
    3360             :     }
    3361             : 
    3362           0 :   fd = open (file_name, O_RDONLY);
    3363           0 :   if (fd < 0)
    3364             :     {
    3365           0 :       error = clib_error_return_unix (0, "failed to open `%s'", file_name);
    3366           0 :       goto done;
    3367             :     }
    3368             : 
    3369             :   /* Make sure its a regular file. */
    3370           0 :   if (fstat (fd, &s) < 0)
    3371             :     {
    3372           0 :       error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
    3373           0 :       goto done;
    3374             :     }
    3375             : 
    3376           0 :   if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
    3377             :     {
    3378           0 :       error = clib_error_return (0, "not a regular file `%s'", file_name);
    3379           0 :       goto done;
    3380             :     }
    3381             : 
    3382           0 :   if (s.st_size < 1)
    3383             :     {
    3384           0 :       error = clib_error_return (0, "empty file `%s'", file_name);
    3385           0 :       goto done;
    3386             :     }
    3387             : 
    3388             :   /* Read the file */
    3389           0 :   vec_validate (file_data, s.st_size - 1);
    3390             : 
    3391           0 :   if (read (fd, file_data, s.st_size) != s.st_size)
    3392             :     {
    3393           0 :       error = clib_error_return_unix (0, "Failed to read %d bytes from '%s'",
    3394             :                                       s.st_size, file_name);
    3395           0 :       vec_free (file_data);
    3396           0 :       goto done;
    3397             :     }
    3398             : 
    3399           0 :   unformat_init_vector (&sub_input, file_data);
    3400             : 
    3401             :   /* Initial config process? Use the global macro table. */
    3402           0 :   if (pool_is_free_index (cm->cli_file_pool, cm->current_input_file_index))
    3403           0 :     mm = &cm->macro_main;
    3404             :   else
    3405             :     {
    3406             :       /* Otherwise, use the per-cli-process macro table */
    3407           0 :       cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
    3408           0 :       mm = &cf->macro_main;
    3409             :     }
    3410             : 
    3411           0 :   while (rv == 0 && unformat_user (&sub_input, unformat_vlib_cli_line, &in))
    3412             :     {
    3413             :       /* Run the file contents through the macro processor */
    3414           0 :       if (vec_len (in.buffer) > 1)
    3415             :         {
    3416             :           u8 *expanded;
    3417             : 
    3418             :           /* The macro expander expects a c string... */
    3419           0 :           vec_add1 (in.buffer, 0);
    3420             : 
    3421           0 :           expanded =
    3422           0 :             (u8 *) clib_macro_eval (mm, (i8 *) in.buffer, 1 /* complain */,
    3423             :                                     0 /* level */, 8 /* max_level */);
    3424             :           /* Macro processor NULL terminates the return */
    3425           0 :           vec_dec_len (expanded, 1);
    3426           0 :           vec_reset_length (in.buffer);
    3427           0 :           vec_append (in.buffer, expanded);
    3428           0 :           vec_free (expanded);
    3429             :         }
    3430             : 
    3431           0 :       if ((rv = vlib_cli_input (vm, &in, 0, 0)) != 0)
    3432           0 :         error = clib_error_return (0, "CLI line error: %U",
    3433             :                                    format_unformat_error, &in);
    3434           0 :       unformat_free (&in);
    3435             :     }
    3436           0 :   unformat_free (&sub_input);
    3437             : 
    3438           0 : done:
    3439           0 :   if (fd >= 0)
    3440           0 :     close (fd);
    3441           0 :   vec_free (file_name);
    3442             : 
    3443           0 :   return error;
    3444             : }
    3445             : 
    3446             : /*?
    3447             :  * Executes a sequence of CLI commands which are read from a file. If
    3448             :  * a command is unrecognized or otherwise invalid then the usual CLI
    3449             :  * feedback will be generated, however execution of subsequent commands
    3450             :  * from the file will continue.
    3451             :  *
    3452             :  * The VPP code is indifferent to the file location. However, if SELinux
    3453             :  * is enabled, then the file needs to have an SELinux label the VPP
    3454             :  * process is allowed to access. For example, if a file is created in
    3455             :  * '<em>/usr/share/vpp/</em>', it will be allowed. However, files manually
    3456             :  * created in '/tmp/' or '/home/<user>/' will not be accessible by the VPP
    3457             :  * process when SELinux is enabled.
    3458             :  *
    3459             :  * @cliexpar
    3460             :  * Sample file:
    3461             :  * @clistart
    3462             :  * <b><em>$ cat /usr/share/vpp/scripts/gigup.txt</em></b>
    3463             :  * set interface state GigabitEthernet0/8/0 up
    3464             :  * set interface state GigabitEthernet0/9/0 up
    3465             :  * @cliend
    3466             :  * Example of how to execute a set of CLI commands from a file:
    3467             :  * @cliexcmd{exec /usr/share/vpp/scripts/gigup.txt}
    3468             : ?*/
    3469             : /* *INDENT-OFF* */
    3470      272887 : VLIB_CLI_COMMAND (cli_exec, static) = {
    3471             :   .path = "exec",
    3472             :   .short_help = "exec <filename>",
    3473             :   .function = unix_cli_exec,
    3474             :   .is_mp_safe = 1,
    3475             : };
    3476             : /* *INDENT-ON* */
    3477             : 
    3478             : /** CLI command to show various unix error statistics. */
    3479             : static clib_error_t *
    3480           0 : unix_show_errors (vlib_main_t * vm,
    3481             :                   unformat_input_t * input, vlib_cli_command_t * cmd)
    3482             : {
    3483           0 :   unix_main_t *um = &unix_main;
    3484           0 :   clib_error_t *error = 0;
    3485             :   int i, n_errors_to_show;
    3486           0 :   unix_error_history_t *unix_errors = 0;
    3487             : 
    3488           0 :   n_errors_to_show = 1 << 30;
    3489             : 
    3490           0 :   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    3491             :     {
    3492           0 :       if (!unformat (input, "%d", &n_errors_to_show))
    3493             :         {
    3494             :           error =
    3495           0 :             clib_error_return (0,
    3496             :                                "expecting integer number of errors to show, got `%U'",
    3497             :                                format_unformat_error, input);
    3498           0 :           goto done;
    3499             :         }
    3500             :     }
    3501             : 
    3502           0 :   n_errors_to_show =
    3503           0 :     clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
    3504             : 
    3505           0 :   i =
    3506           0 :     um->error_history_index >
    3507           0 :     0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
    3508             : 
    3509           0 :   while (n_errors_to_show > 0)
    3510             :     {
    3511           0 :       unix_error_history_t *eh = um->error_history + i;
    3512             : 
    3513           0 :       if (!eh->error)
    3514           0 :         break;
    3515             : 
    3516           0 :       vec_add1 (unix_errors, eh[0]);
    3517           0 :       n_errors_to_show -= 1;
    3518           0 :       if (i == 0)
    3519           0 :         i = ARRAY_LEN (um->error_history) - 1;
    3520             :       else
    3521           0 :         i--;
    3522             :     }
    3523             : 
    3524           0 :   if (vec_len (unix_errors) == 0)
    3525           0 :     vlib_cli_output (vm, "no Unix errors so far");
    3526             :   else
    3527             :     {
    3528           0 :       vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
    3529           0 :       for (i = vec_len (unix_errors) - 1; i >= 0; i--)
    3530             :         {
    3531           0 :           unix_error_history_t *eh = vec_elt_at_index (unix_errors, i);
    3532           0 :           vlib_cli_output (vm, "%U: %U",
    3533             :                            format_time_interval, "h:m:s:u", eh->time,
    3534             :                            format_clib_error, eh->error);
    3535             :         }
    3536           0 :       vlib_cli_output (vm, "%U: time now",
    3537             :                        format_time_interval, "h:m:s:u", vlib_time_now (vm));
    3538             :     }
    3539             : 
    3540           0 : done:
    3541           0 :   vec_free (unix_errors);
    3542           0 :   return error;
    3543             : }
    3544             : 
    3545             : /* *INDENT-OFF* */
    3546      272887 : VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
    3547             :   .path = "show unix errors",
    3548             :   .short_help = "Show Unix system call error history",
    3549             :   .function = unix_show_errors,
    3550             : };
    3551             : /* *INDENT-ON* */
    3552             : 
    3553             : /** CLI command to show various unix error statistics. */
    3554             : static clib_error_t *
    3555           0 : unix_show_files (vlib_main_t * vm,
    3556             :                  unformat_input_t * input, vlib_cli_command_t * cmd)
    3557             : {
    3558           0 :   clib_error_t *error = 0;
    3559           0 :   clib_file_main_t *fm = &file_main;
    3560             :   clib_file_t *f;
    3561             :   char path[PATH_MAX];
    3562           0 :   u8 *s = 0;
    3563             : 
    3564           0 :   vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread",
    3565             :                    "Read", "Write", "Error", "File Name", "Description");
    3566             : 
    3567             :   /* *INDENT-OFF* */
    3568           0 :   pool_foreach (f, fm->file_pool)
    3569             :    {
    3570             :       int rv;
    3571           0 :       s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0);
    3572           0 :       rv = readlink((char *) s, path, PATH_MAX - 1);
    3573             : 
    3574           0 :       path[rv < 0 ? 0 : rv] = 0;
    3575             : 
    3576           0 :       vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v",
    3577             :                        f->file_descriptor, f->polling_thread_index,
    3578             :                        f->read_events, f->write_events, f->error_events,
    3579             :                        path, f->description);
    3580           0 :       vec_reset_length (s);
    3581             :     }
    3582             :   /* *INDENT-ON* */
    3583           0 :   vec_free (s);
    3584             : 
    3585           0 :   return error;
    3586             : }
    3587             : 
    3588             : /* *INDENT-OFF* */
    3589      272887 : VLIB_CLI_COMMAND (cli_unix_show_files, static) = {
    3590             :   .path = "show unix files",
    3591             :   .short_help = "Show Unix files in use",
    3592             :   .function = unix_show_files,
    3593             : };
    3594             : /* *INDENT-ON* */
    3595             : 
    3596             : /** CLI command to show session command history. */
    3597             : static clib_error_t *
    3598           0 : unix_cli_show_history (vlib_main_t * vm,
    3599             :                        unformat_input_t * input, vlib_cli_command_t * cmd)
    3600             : {
    3601           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3602             :   unix_cli_file_t *cf;
    3603             :   int i, j;
    3604             : 
    3605           0 :   if (!(cf = unix_cli_file_if_interactive (cm)))
    3606           0 :     return clib_error_return (0, "invalid for non-interactive sessions");
    3607             : 
    3608           0 :   if (cf->has_history && cf->history_limit)
    3609             :     {
    3610           0 :       i = 1 + cf->command_number - vec_len (cf->command_history);
    3611           0 :       for (j = 0; j < vec_len (cf->command_history); j++)
    3612           0 :         vlib_cli_output (vm, "%d  %v\n", i + j, cf->command_history[j]);
    3613             :     }
    3614             :   else
    3615             :     {
    3616           0 :       vlib_cli_output (vm, "History not enabled.\n");
    3617             :     }
    3618             : 
    3619           0 :   return 0;
    3620             : }
    3621             : 
    3622             : /*?
    3623             :  * Displays the command history for the current session, if any.
    3624             : ?*/
    3625             : /* *INDENT-OFF* */
    3626      272887 : VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
    3627             :   .path = "history",
    3628             :   .short_help = "Show current session command history",
    3629             :   .function = unix_cli_show_history,
    3630             : };
    3631             : /* *INDENT-ON* */
    3632             : 
    3633             : /** CLI command to show terminal status. */
    3634             : static clib_error_t *
    3635           0 : unix_cli_show_terminal (vlib_main_t * vm,
    3636             :                         unformat_input_t * input, vlib_cli_command_t * cmd)
    3637             : {
    3638           0 :   unix_main_t *um = &unix_main;
    3639           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3640             :   unix_cli_file_t *cf;
    3641             :   vlib_node_t *n;
    3642             : 
    3643           0 :   if (!(cf = unix_cli_file_if_exists (cm)))
    3644           0 :     return clib_error_return (0, "invalid session");
    3645             : 
    3646           0 :   n = vlib_get_node (vm, cf->process_node_index);
    3647             : 
    3648           0 :   vlib_cli_output (vm, "Terminal name:   %v\n", cf->name);
    3649           0 :   vlib_cli_output (vm, "Terminal node:   %v\n", n->name);
    3650           0 :   vlib_cli_output (vm, "Terminal mode:   %s\n", cf->line_mode ?
    3651             :                    "line-by-line" : "char-by-char");
    3652           0 :   vlib_cli_output (vm, "Terminal width:  %d\n", cf->width);
    3653           0 :   vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
    3654           0 :   vlib_cli_output (vm, "ANSI capable:    %s\n",
    3655           0 :                    cf->ansi_capable ? "yes" : "no");
    3656           0 :   vlib_cli_output (vm, "Interactive:     %s\n",
    3657           0 :                    cf->is_interactive ? "yes" : "no");
    3658           0 :   vlib_cli_output (vm, "History enabled: %s%s\n",
    3659           0 :                    cf->has_history ? "yes" : "no", !cf->has_history
    3660           0 :                    || cf->history_limit ? "" :
    3661             :                    " (disabled by history limit)");
    3662           0 :   if (cf->has_history)
    3663           0 :     vlib_cli_output (vm, "History limit:   %d\n", cf->history_limit);
    3664           0 :   vlib_cli_output (vm, "Pager enabled:   %s%s%s\n",
    3665           0 :                    cf->no_pager ? "no" : "yes",
    3666           0 :                    cf->no_pager
    3667           0 :                    || cf->height ? "" : " (disabled by terminal height)",
    3668           0 :                    cf->no_pager
    3669           0 :                    || um->cli_pager_buffer_limit ? "" :
    3670             :                    " (disabled by buffer limit)");
    3671           0 :   if (!cf->no_pager)
    3672           0 :     vlib_cli_output (vm, "Pager limit:     %d\n", um->cli_pager_buffer_limit);
    3673           0 :   vlib_cli_output (vm, "CRLF mode:       %s\n",
    3674           0 :                    cf->crlf_mode ? "CR+LF" : "LF");
    3675             : 
    3676           0 :   return 0;
    3677             : }
    3678             : 
    3679             : /*?
    3680             :  * Displays various information about the state of the current terminal
    3681             :  * session.
    3682             :  *
    3683             :  * @cliexpar
    3684             :  * @cliexstart{show terminal}
    3685             :  * Terminal name:   unix-cli-stdin
    3686             :  * Terminal mode:   char-by-char
    3687             :  * Terminal width:  123
    3688             :  * Terminal height: 48
    3689             :  * ANSI capable:    yes
    3690             :  * Interactive:     yes
    3691             :  * History enabled: yes
    3692             :  * History limit:   50
    3693             :  * Pager enabled:   yes
    3694             :  * Pager limit:     100000
    3695             :  * CRLF mode:       LF
    3696             :  * @cliexend
    3697             : ?*/
    3698             : /* *INDENT-OFF* */
    3699      272887 : VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
    3700             :   .path = "show terminal",
    3701             :   .short_help = "Show current session terminal settings",
    3702             :   .function = unix_cli_show_terminal,
    3703             : };
    3704             : /* *INDENT-ON* */
    3705             : 
    3706             : /** CLI command to display a list of CLI sessions. */
    3707             : static clib_error_t *
    3708           0 : unix_cli_show_cli_sessions (vlib_main_t * vm,
    3709             :                             unformat_input_t * input,
    3710             :                             vlib_cli_command_t * cmd)
    3711             : {
    3712           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3713           0 :   clib_file_main_t *fm = &file_main;
    3714           0 :   table_t table = {}, *t = &table;
    3715             :   unix_cli_file_t *cf;
    3716             :   clib_file_t *uf;
    3717             : 
    3718           0 :   table_add_header_col (t, 4, "PNI  ", "FD   ", "Name", "Flags");
    3719             : 
    3720             : #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) )
    3721           0 :   int i = 0;
    3722           0 :   pool_foreach (cf, cm->cli_file_pool)
    3723             :     {
    3724           0 :       int j = 0;
    3725             : 
    3726           0 :       uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
    3727           0 :       table_format_cell (t, i, j++, "%u", cf->process_node_index);
    3728           0 :       table_format_cell (t, i, j++, "%u", uf->file_descriptor);
    3729           0 :       table_format_cell (t, i, j++, "%v", cf->name);
    3730           0 :       table_format_cell (t, i++, j++, "%c%c%c%c%c",
    3731           0 :                          fl (cf->is_interactive, 'i'), fl (cf->is_socket, 's'),
    3732           0 :                          fl (cf->line_mode, 'l'), fl (cf->has_epipe, 'p'),
    3733           0 :                          fl (cf->ansi_capable, 'a'));
    3734             :     }
    3735             : #undef fl
    3736             : 
    3737           0 :   t->default_body.align = TTAA_LEFT;
    3738           0 :   t->default_header_col.align = TTAA_LEFT;
    3739           0 :   vlib_cli_output (vm, "%U", format_table, t);
    3740           0 :   table_free (t);
    3741             : 
    3742           0 :   return 0;
    3743             : }
    3744             : 
    3745             : /*?
    3746             :  * Displays a summary of all the current CLI sessions.
    3747             :  *
    3748             :  * Typically used to diagnose connection issues with the CLI
    3749             :  * socket.
    3750             :  *
    3751             :  * @cliexpar
    3752             :  * @cliexstart{show cli-sessions}
    3753             :  * PNI   FD    Name                 Flags
    3754             :  * 343   0     unix-cli-stdin       IslpA
    3755             :  * 344   7     unix-cli-local:20    ISlpA
    3756             :  * 346   8     unix-cli-local:21    iSLpa
    3757             :  * @cliexend
    3758             : 
    3759             :  * In this example we have the debug console of the running process
    3760             :  * on stdin/out, we have an interactive socket session and we also
    3761             :  * have a non-interactive socket session.
    3762             :  *
    3763             :  * Fields:
    3764             :  *
    3765             :  * - @em PNI: Process node index.
    3766             :  * - @em FD: Unix file descriptor.
    3767             :  * - @em Name: Name of the session.
    3768             :  * - @em Flags: Various flags that describe the state of the session.
    3769             :  *
    3770             :  * @em Flags have the following meanings; lower-case typically negates
    3771             :  * upper-case:
    3772             :  *
    3773             :  * - @em I Interactive session.
    3774             :  * - @em S Connected by socket.
    3775             :  * - @em s Not a socket, likely stdin.
    3776             :  * - @em L Line-by-line mode.
    3777             :  * - @em l Char-by-char mode.
    3778             :  * - @em P EPIPE detected on connection; it will close soon.
    3779             :  * - @em A ANSI-capable terminal.
    3780             : ?*/
    3781             : /* *INDENT-OFF* */
    3782      272887 : VLIB_CLI_COMMAND (cli_unix_cli_show_cli_sessions, static) = {
    3783             :   .path = "show cli-sessions",
    3784             :   .short_help = "Show current CLI sessions",
    3785             :   .function = unix_cli_show_cli_sessions,
    3786             : };
    3787             : /* *INDENT-ON* */
    3788             : 
    3789             : /** CLI command to set terminal pager settings. */
    3790             : static clib_error_t *
    3791           0 : unix_cli_set_terminal_pager (vlib_main_t * vm,
    3792             :                              unformat_input_t * input,
    3793             :                              vlib_cli_command_t * cmd)
    3794             : {
    3795           0 :   unix_main_t *um = &unix_main;
    3796           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3797             :   unix_cli_file_t *cf;
    3798           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    3799           0 :   clib_error_t *error = 0;
    3800             : 
    3801           0 :   if (!(cf = unix_cli_file_if_interactive (cm)))
    3802           0 :     return clib_error_return (0, "invalid for non-interactive sessions");
    3803             : 
    3804           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    3805           0 :     return 0;
    3806             : 
    3807           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    3808             :     {
    3809           0 :       if (unformat (line_input, "on"))
    3810           0 :         cf->no_pager = 0;
    3811           0 :       else if (unformat (line_input, "off"))
    3812           0 :         cf->no_pager = 1;
    3813           0 :       else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
    3814           0 :         vlib_cli_output (vm,
    3815             :                          "Pager limit set to %u lines; note, this is global.\n",
    3816             :                          um->cli_pager_buffer_limit);
    3817             :       else
    3818             :         {
    3819           0 :           error = clib_error_return (0, "unknown parameter: `%U`",
    3820             :                                      format_unformat_error, line_input);
    3821           0 :           goto done;
    3822             :         }
    3823             :     }
    3824             : 
    3825           0 : done:
    3826           0 :   unformat_free (line_input);
    3827             : 
    3828           0 :   return error;
    3829             : }
    3830             : 
    3831             : /*?
    3832             :  * Enables or disables the terminal pager for this session. Generally
    3833             :  * this defaults to enabled.
    3834             :  *
    3835             :  * Additionally allows the pager buffer size to be set; though note that
    3836             :  * this value is set globally and not per session.
    3837             : ?*/
    3838             : /* *INDENT-OFF* */
    3839      272887 : VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
    3840             :   .path = "set terminal pager",
    3841             :   .short_help = "set terminal pager [on|off] [limit <lines>]",
    3842             :   .function = unix_cli_set_terminal_pager,
    3843             : };
    3844             : /* *INDENT-ON* */
    3845             : 
    3846             : /** CLI command to set terminal history settings. */
    3847             : static clib_error_t *
    3848           0 : unix_cli_set_terminal_history (vlib_main_t * vm,
    3849             :                                unformat_input_t * input,
    3850             :                                vlib_cli_command_t * cmd)
    3851             : {
    3852           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3853             :   unix_cli_file_t *cf;
    3854           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    3855             :   u32 limit;
    3856           0 :   clib_error_t *error = 0;
    3857             : 
    3858           0 :   if (!(cf = unix_cli_file_if_interactive (cm)))
    3859           0 :     return clib_error_return (0, "invalid for non-interactive sessions");
    3860             : 
    3861           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    3862           0 :     return 0;
    3863             : 
    3864           0 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    3865             :     {
    3866           0 :       if (unformat (line_input, "on"))
    3867           0 :         cf->has_history = 1;
    3868           0 :       else if (unformat (line_input, "off"))
    3869           0 :         cf->has_history = 0;
    3870           0 :       else if (unformat (line_input, "limit %u", &cf->history_limit))
    3871             :         ;
    3872             :       else
    3873             :         {
    3874           0 :           error = clib_error_return (0, "unknown parameter: `%U`",
    3875             :                                      format_unformat_error, line_input);
    3876           0 :           goto done;
    3877             :         }
    3878             : 
    3879             :     }
    3880             : 
    3881             :   /* If we reduced history size, or turned it off, purge the history */
    3882           0 :   limit = cf->has_history ? cf->history_limit : 0;
    3883           0 :   if (limit < vec_len (cf->command_history))
    3884             :     {
    3885             :       u32 i;
    3886             : 
    3887             :       /* How many items to remove from the start of history */
    3888           0 :       limit = vec_len (cf->command_history) - limit;
    3889             : 
    3890           0 :       for (i = 0; i < limit; i++)
    3891           0 :         vec_free (cf->command_history[i]);
    3892             : 
    3893           0 :       vec_delete (cf->command_history, limit, 0);
    3894             :     }
    3895             : 
    3896           0 : done:
    3897           0 :   unformat_free (line_input);
    3898             : 
    3899           0 :   return error;
    3900             : }
    3901             : 
    3902             : /*?
    3903             :  * Enables or disables the command history function of the current
    3904             :  * terminal. Generally this defaults to enabled.
    3905             :  *
    3906             :  * This command also allows the maximum size of the history buffer for
    3907             :  * this session to be altered.
    3908             : ?*/
    3909             : /* *INDENT-OFF* */
    3910      272887 : VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
    3911             :   .path = "set terminal history",
    3912             :   .short_help = "set terminal history [on|off] [limit <lines>]",
    3913             :   .function = unix_cli_set_terminal_history,
    3914             : };
    3915             : /* *INDENT-ON* */
    3916             : 
    3917             : /** CLI command to set terminal ANSI settings. */
    3918             : static clib_error_t *
    3919           0 : unix_cli_set_terminal_ansi (vlib_main_t * vm,
    3920             :                             unformat_input_t * input,
    3921             :                             vlib_cli_command_t * cmd)
    3922             : {
    3923           0 :   unix_cli_main_t *cm = &unix_cli_main;
    3924             :   unix_cli_file_t *cf;
    3925             : 
    3926           0 :   if (!(cf = unix_cli_file_if_interactive (cm)))
    3927           0 :     return clib_error_return (0, "invalid for non-interactive sessions");
    3928             : 
    3929           0 :   if (unformat (input, "on"))
    3930           0 :     cf->ansi_capable = 1;
    3931           0 :   else if (unformat (input, "off"))
    3932           0 :     cf->ansi_capable = 0;
    3933             :   else
    3934           0 :     return clib_error_return (0, "unknown parameter: `%U`",
    3935             :                               format_unformat_error, input);
    3936             : 
    3937           0 :   return 0;
    3938             : }
    3939             : 
    3940             : /*?
    3941             :  * Enables or disables the use of ANSI control sequences by this terminal.
    3942             :  * The default will vary based on terminal detection at the start of the
    3943             :  * session.
    3944             :  *
    3945             :  * ANSI control sequences are used in a small number of places to provide,
    3946             :  * for example, color text output and to control the cursor in the pager.
    3947             : ?*/
    3948             : /* *INDENT-OFF* */
    3949      272887 : VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
    3950             :   .path = "set terminal ansi",
    3951             :   .short_help = "set terminal ansi [on|off]",
    3952             :   .function = unix_cli_set_terminal_ansi,
    3953             : };
    3954             : /* *INDENT-ON* */
    3955             : 
    3956             : 
    3957             : #define MAX_CLI_WAIT 86400
    3958             : /** CLI command to wait <sec> seconds. Useful for exec script. */
    3959             : static clib_error_t *
    3960           3 : unix_wait_cmd (vlib_main_t * vm,
    3961             :                unformat_input_t * input, vlib_cli_command_t * cmd)
    3962             : {
    3963           3 :   unformat_input_t _line_input, *line_input = &_line_input;
    3964           3 :   f64 sec = 1.0;
    3965             : 
    3966           3 :   if (!unformat_user (input, unformat_line_input, line_input))
    3967           0 :     return 0;
    3968             : 
    3969           6 :   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    3970             :     {
    3971           3 :       if (unformat (line_input, "%f", &sec))
    3972             :         ;
    3973             :       else
    3974           0 :         return clib_error_return (0, "unknown parameter: `%U`",
    3975             :                                   format_unformat_error, input);
    3976             :     }
    3977             : 
    3978           3 :   if (sec <= 0 || sec > MAX_CLI_WAIT || floor (sec * 1000) / 1000 != sec)
    3979           0 :     return clib_error_return (0,
    3980             :                               "<sec> must be a positive value and less than 86400 (one day) with no more than msec precision.");
    3981             : 
    3982           3 :   vlib_process_wait_for_event_or_clock (vm, sec);
    3983           3 :   vlib_cli_output (vm, "waited %.3f sec.", sec);
    3984             : 
    3985           3 :   unformat_free (line_input);
    3986           3 :   return 0;
    3987             : }
    3988             : /* *INDENT-OFF* */
    3989      272887 : VLIB_CLI_COMMAND (cli_unix_wait_cmd, static) = {
    3990             :   .path = "wait",
    3991             :   .short_help = "wait <sec>",
    3992             :   .function = unix_wait_cmd,
    3993             : };
    3994             : /* *INDENT-ON* */
    3995             : 
    3996             : static clib_error_t *
    3997           0 : echo_cmd (vlib_main_t * vm,
    3998             :           unformat_input_t * input, vlib_cli_command_t * cmd)
    3999             : {
    4000           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    4001             : 
    4002             :   /* Get a line of input. */
    4003           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    4004             :     {
    4005           0 :       vlib_cli_output (vm, "");
    4006           0 :       return 0;
    4007             :     }
    4008             : 
    4009           0 :   vlib_cli_output (vm, "%v", line_input->buffer);
    4010             : 
    4011           0 :   unformat_free (line_input);
    4012           0 :   return 0;
    4013             : }
    4014             : 
    4015             : /* *INDENT-OFF* */
    4016      272887 : VLIB_CLI_COMMAND (cli_unix_echo_cmd, static) = {
    4017             :   .path = "echo",
    4018             :   .short_help = "echo <rest-of-line>",
    4019             :   .function = echo_cmd,
    4020             : };
    4021             : /* *INDENT-ON* */
    4022             : 
    4023             : static clib_error_t *
    4024           0 : define_cmd_fn (vlib_main_t * vm,
    4025             :                unformat_input_t * input, vlib_cli_command_t * cmd)
    4026             : {
    4027             :   u8 *macro_name;
    4028           0 :   unformat_input_t _line_input, *line_input = &_line_input;
    4029           0 :   clib_macro_main_t *mm = get_macro_main ();
    4030             :   clib_error_t *error;
    4031             : 
    4032           0 :   if (!unformat (input, "%s", &macro_name))
    4033           0 :     return clib_error_return (0, "missing variable name...");
    4034             : 
    4035             :   /* Remove white space */
    4036           0 :   (void) unformat (input, "");
    4037             : 
    4038             :   /* Get a line of input. */
    4039           0 :   if (!unformat_user (input, unformat_line_input, line_input))
    4040             :     {
    4041           0 :       error = clib_error_return (0, "missing value for '%s'...", macro_name);
    4042           0 :       vec_free (macro_name);
    4043           0 :       return error;
    4044             :     }
    4045             :   /* the macro expander expects c-strings, not vectors... */
    4046           0 :   vec_add1 (line_input->buffer, 0);
    4047           0 :   clib_macro_set_value (mm, (char *) macro_name, (char *) line_input->buffer);
    4048           0 :   vec_free (macro_name);
    4049           0 :   unformat_free (line_input);
    4050           0 :   return 0;
    4051             : }
    4052             : 
    4053             : /* *INDENT-OFF* */
    4054      272887 : VLIB_CLI_COMMAND (define_cmd, static) = {
    4055             :   .path = "define",
    4056             :   .short_help = "define <variable-name> <value>",
    4057             :   .function = define_cmd_fn,
    4058             : };
    4059             : 
    4060             : /* *INDENT-ON* */
    4061             : 
    4062             : static clib_error_t *
    4063           0 : undefine_cmd_fn (vlib_main_t * vm,
    4064             :                  unformat_input_t * input, vlib_cli_command_t * cmd)
    4065             : {
    4066             :   u8 *macro_name;
    4067           0 :   clib_macro_main_t *mm = get_macro_main ();
    4068             : 
    4069           0 :   if (!unformat (input, "%s", &macro_name))
    4070           0 :     return clib_error_return (0, "missing variable name...");
    4071             : 
    4072           0 :   if (clib_macro_unset (mm, (char *) macro_name))
    4073           0 :     vlib_cli_output (vm, "%s wasn't set...", macro_name);
    4074             : 
    4075           0 :   vec_free (macro_name);
    4076           0 :   return 0;
    4077             : }
    4078             : 
    4079             : /* *INDENT-OFF* */
    4080      272887 : VLIB_CLI_COMMAND (undefine_cmd, static) = {
    4081             :   .path = "undefine",
    4082             :   .short_help = "undefine <variable-name>",
    4083             :   .function = undefine_cmd_fn,
    4084             : };
    4085             : /* *INDENT-ON* */
    4086             : 
    4087             : static clib_error_t *
    4088           0 : show_macro_cmd_fn (vlib_main_t * vm,
    4089             :                    unformat_input_t * input, vlib_cli_command_t * cmd)
    4090             : {
    4091           0 :   clib_macro_main_t *mm = get_macro_main ();
    4092           0 :   int evaluate = 1;
    4093             : 
    4094           0 :   if (unformat (input, "noevaluate %=", &evaluate, 0))
    4095             :     ;
    4096           0 :   else if (unformat (input, "noeval %=", &evaluate, 0))
    4097             :     ;
    4098             : 
    4099           0 :   vlib_cli_output (vm, "%U", format_clib_macro_main, mm, evaluate);
    4100           0 :   return 0;
    4101             : }
    4102             : 
    4103             : /* *INDENT-OFF* */
    4104      272887 : VLIB_CLI_COMMAND (show_macro, static) = {
    4105             :   .path = "show macro",
    4106             :   .short_help = "show macro [noevaluate]",
    4107             :   .function = show_macro_cmd_fn,
    4108             : };
    4109             : /* *INDENT-ON* */
    4110             : 
    4111             : static clib_error_t *
    4112         559 : unix_cli_init (vlib_main_t * vm)
    4113             : {
    4114         559 :   unix_cli_main_t *cm = &unix_cli_main;
    4115             : 
    4116             :   /* Breadcrumb to indicate the new session process
    4117             :    * has not been started */
    4118         559 :   cm->new_session_process_node_index = ~0;
    4119         559 :   clib_macro_init (&cm->macro_main);
    4120             : 
    4121         559 :   return 0;
    4122             : }
    4123             : 
    4124        5599 : VLIB_INIT_FUNCTION (unix_cli_init);
    4125             : 
    4126             : /*
    4127             :  * fd.io coding-style-patch-verification: ON
    4128             :  *
    4129             :  * Local Variables:
    4130             :  * eval: (c-set-style "gnu")
    4131             :  * End:
    4132             :  */

Generated by: LCOV version 1.14