ethernet: check destination mac for L3 in ethernet-input node
[vpp.git] / src / vlib / unix / cli.c
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 unix_cli_pager_reset (unix_cli_file_t * f)
258 {
259   u8 **p;
260
261   f->pager_start = 0;
262
263   vec_free (f->pager_index);
264   f->pager_index = 0;
265
266   vec_foreach (p, f->pager_vector)
267   {
268     vec_free (*p);
269   }
270   vec_free (f->pager_vector);
271   f->pager_vector = 0;
272 }
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 unix_cli_file_free (unix_cli_file_t * f)
279 {
280   vec_free (f->output_vector);
281   vec_free (f->input_vector);
282   vec_free (f->name);
283   unix_cli_pager_reset (f);
284 }
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 get_macro_main (void)
508 {
509   unix_cli_main_t *cm = &unix_cli_main;
510   vlib_main_t *vm = vlib_get_main ();
511   vlib_process_t *cp = vlib_get_current_process (vm);
512   unix_cli_file_t *cf;
513
514   if (pool_is_free_index (cm->cli_file_pool, cp->output_function_arg))
515     return (&cm->macro_main);
516
517   cf = pool_elt_at_index (cm->cli_file_pool, cp->output_function_arg);
518
519   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 unix_cli_match_action (unix_cli_parse_actions_t * a,
547                        u8 * input, u32 ilen, i32 * matched)
548 {
549   u8 partial = 0;
550
551   while (a->input)
552     {
553       if (ilen >= a->len)
554         {
555           /* see if the start of the input buffer exactly matches the current
556            * action string. */
557           if (memcmp (input, a->input, a->len) == 0)
558             {
559               *matched = a->len;
560               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           if (memcmp (input, a->input, ilen) == 0)
568             partial = 1;
569         }
570
571       /* check next action */
572       a++;
573     }
574
575   return partial ?
576     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 unix_cli_add_pending_output (clib_file_t * uf,
585                              unix_cli_file_t * cf,
586                              u8 * buffer, uword buffer_bytes)
587 {
588   clib_file_main_t *fm = &file_main;
589
590   vec_add (cf->output_vector, buffer, buffer_bytes);
591   if (vec_len (cf->output_vector) > 0)
592     {
593       int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
594       uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
595       if (!skip_update)
596         fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
597     }
598 }
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 unix_cli_del_pending_output (clib_file_t * uf,
605                              unix_cli_file_t * cf, uword n_bytes)
606 {
607   clib_file_main_t *fm = &file_main;
608
609   vec_delete (cf->output_vector, n_bytes, 0);
610   if (vec_len (cf->output_vector) <= 0)
611     {
612       int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
613       uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
614       if (!skip_update)
615         fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
616     }
617 }
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 unix_vlib_findchr (u8 chr, u8 * str, word len)
635 {
636   word i = 0;
637   for (i = 0; i < len; i++, str++)
638     {
639       if (*str == chr)
640         return i;
641     }
642   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 unix_vlib_cli_output_raw (unix_cli_file_t * cf,
662                           clib_file_t * uf, u8 * buffer, uword buffer_bytes)
663 {
664   int n = 0;
665
666   if (cf->has_epipe)            /* don't try writing anything */
667     return;
668
669   if (vec_len (cf->output_vector) == 0)
670     {
671       if (cf->is_socket)
672         /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
673         n = send (uf->file_descriptor, buffer, buffer_bytes, MSG_NOSIGNAL);
674       else
675         n = write (uf->file_descriptor, buffer, buffer_bytes);
676     }
677
678   if (n < 0 && errno != EAGAIN)
679     {
680       if (errno == EPIPE)
681         {
682           /* connection closed on us */
683           unix_main_t *um = &unix_main;
684           cf->has_epipe = 1;
685           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           clib_unix_warning ("write");
692         }
693     }
694   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       if (n < 0)
699         n = 0;
700       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 unix_vlib_cli_output_cooked (unix_cli_file_t * cf,
713                              clib_file_t * uf,
714                              u8 * buffer, uword buffer_bytes)
715 {
716   word end = 0, start = 0;
717
718   while (end < buffer_bytes)
719     {
720       if (cf->crlf_mode)
721         {
722           /* iterate the line on \n's so we can insert a \r before it */
723           end = unix_vlib_findchr ('\n',
724                                    buffer + start,
725                                    buffer_bytes - start) + start;
726         }
727       else
728         {
729           /* otherwise just send the whole buffer */
730           end = buffer_bytes;
731         }
732
733       unix_vlib_cli_output_raw (cf, uf, buffer + start, end - start);
734
735       if (cf->crlf_mode)
736         {
737           if (end < buffer_bytes)
738             {
739               unix_vlib_cli_output_raw (cf, uf, (u8 *) "\r\n", 2);
740               end++;            /* skip the \n that we already sent */
741             }
742           start = end;
743         }
744     }
745
746   /* Use the last character to determine the last direction of the cursor. */
747   if (buffer_bytes > 0)
748     cf->cursor_direction = (buffer[buffer_bytes - 1] == (u8) '\b');
749 }
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 unix_vlib_cli_output_cursor_left (unix_cli_file_t * cf, clib_file_t * uf)
766 {
767   unix_cli_main_t *cm = &unix_cli_main;
768   static u8 *ansi = 0;          /* assumes no reentry */
769   u32 position;
770
771   if (!cf->is_interactive || !cf->ansi_capable || !cf->width)
772     {
773       /* No special handling for dumb terminals */
774       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
775       return;
776     }
777
778   position = ((u32) vec_len (cm->cli_prompt) + cf->cursor) % cf->width;
779
780   if (position != 0)
781     {
782       /* No special handling required if we're not at the left edge */
783       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
784       return;
785     }
786
787   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       if (cf->cursor < vec_len (cf->current_command))
796         unix_vlib_cli_output_cooked (cf, uf, &cf->current_command[cf->cursor],
797                                      1);
798       else
799         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
800       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   ansi = format (ansi, CSI "A" CSI "%dC", cf->width - 1);
805   unix_vlib_cli_output_cooked (cf, uf, ansi, vec_len (ansi));
806   vec_reset_length (ansi);      /* keep the vec around for next time */
807   cf->cursor_direction = 1;     /* going backwards now */
808 }
809
810 /** @brief Output the CLI prompt */
811 static void
812 unix_cli_cli_prompt (unix_cli_file_t * cf, clib_file_t * uf)
813 {
814   unix_cli_main_t *cm = &unix_cli_main;
815
816   if (cf->is_interactive)       /* Only interactive sessions get a prompt */
817     unix_vlib_cli_output_raw (cf, uf, cm->cli_prompt,
818                               vec_len (cm->cli_prompt));
819 }
820
821 /** @brief Output a pager prompt and show number of buffered lines */
822 static void
823 unix_cli_pager_prompt (unix_cli_file_t * cf, clib_file_t * uf)
824 {
825   u8 *prompt;
826   u32 h;
827
828   h = cf->pager_start + (cf->height - 1);
829   if (h > vec_len (cf->pager_index))
830     h = vec_len (cf->pager_index);
831
832   prompt = format (0, "\r%s-- more -- (%d-%d/%d)%s",
833                    cf->ansi_capable ? ANSI_BOLD : "",
834                    cf->pager_start + 1,
835                    h,
836                    vec_len (cf->pager_index),
837                    cf->ansi_capable ? ANSI_RESET : "");
838
839   unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
840
841   vec_free (prompt);
842 }
843
844 /** @brief Output a pager "skipping" message */
845 static void
846 unix_cli_pager_message (unix_cli_file_t * cf, clib_file_t * uf,
847                         char *message, char *postfix)
848 {
849   u8 *prompt;
850
851   prompt = format (0, "\r%s-- %s --%s%s",
852                    cf->ansi_capable ? ANSI_BOLD : "",
853                    message, cf->ansi_capable ? ANSI_RESET : "", postfix);
854
855   unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
856
857   vec_free (prompt);
858 }
859
860 /** @brief Erase the printed pager prompt */
861 static void
862 unix_cli_pager_prompt_erase (unix_cli_file_t * cf, clib_file_t * uf)
863 {
864   if (cf->ansi_capable)
865     {
866       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
867       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       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
876       for (i = 0; i < cf->width - 1; i++)
877         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
878       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
879     }
880 }
881
882 /** @brief Uses an ANSI escape sequence to move the cursor */
883 static void
884 unix_cli_ansi_cursor (unix_cli_file_t * cf, clib_file_t * uf, u16 x, u16 y)
885 {
886   u8 *str;
887
888   str = format (0, "%s%d;%dH", CSI, y, x);
889
890   unix_vlib_cli_output_cooked (cf, uf, str, vec_len (str));
891
892   vec_free (str);
893 }
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 unix_cli_pager_redraw (unix_cli_file_t * cf, clib_file_t * uf)
901 {
902   unix_cli_pager_index_t *pi = NULL;
903   u8 *line = NULL;
904   word i;
905
906   /* No active pager? Do nothing. */
907   if (!vec_len (cf->pager_index))
908     return;
909
910   if (cf->ansi_capable)
911     {
912       /* If we have ANSI, send the clear screen sequence */
913       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       unix_cli_pager_prompt_erase (cf, uf);
921     }
922
923   /* (Re-)send the current page of content */
924   for (i = 0; i < cf->height - 1 &&
925        i + cf->pager_start < vec_len (cf->pager_index); i++)
926     {
927       pi = &cf->pager_index[cf->pager_start + i];
928       line = cf->pager_vector[pi->line] + pi->offset;
929
930       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   if (pi && line[pi->length - 1] != '\n')
934     unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
935
936   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 unix_cli_pager_add_line (unix_cli_file_t * cf, u8 * line, word len_or_index)
962 {
963   u8 *p = NULL;
964   word i, j, k;
965   word line_index, len;
966   u32 width = cf->width;
967   unix_cli_pager_index_t *pi;
968
969   if (line == NULL)
970     {
971       /* Use a line already in the pager buffer */
972       line_index = len_or_index;
973       if (cf->pager_vector != NULL)
974         p = cf->pager_vector[line_index];
975       len = vec_len (p);
976     }
977   else
978     {
979       len = len_or_index;
980       /* Add a copy of the raw string to the pager buffer */
981       p = vec_new (u8, len);
982       clib_memcpy (p, line, len);
983
984       /* store in pager buffer */
985       line_index = vec_len (cf->pager_vector);
986       vec_add1 (cf->pager_vector, p);
987     }
988
989   i = 0;
990   while (i < len)
991     {
992       /* Find the next line, or run to terminal width, or run to EOL */
993       int l = len - i;
994       j = unix_vlib_findchr ((u8) '\n', p, l < width ? l : width);
995
996       if (j < l && p[j] == '\n')        /* incl \n */
997         j++;
998
999       /* Add the line to the index */
1000       k = vec_len (cf->pager_index);
1001       vec_validate (cf->pager_index, k);
1002       pi = &cf->pager_index[k];
1003
1004       pi->line = line_index;
1005       pi->offset = i;
1006       pi->length = j;
1007
1008       i += j;
1009       p += j;
1010     }
1011 }
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 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   if (cf->pager_vector == 0)
1032     {
1033       vec_reset_length (cf->pager_index);
1034       return;
1035     }
1036
1037   /* Retain a pointer to the current page start line so we can
1038    * find it later
1039    */
1040   pi = &cf->pager_index[cf->pager_start];
1041   old_line = pi->line;
1042   old_offset = pi->offset;
1043
1044   /* Re-add the buffered lines to the index */
1045   vec_reset_length (cf->pager_index);
1046   vec_foreach_index (i, cf->pager_vector)
1047   {
1048     unix_cli_pager_add_line (cf, NULL, i);
1049   }
1050
1051   /* Attempt to re-locate the previously stored page start line */
1052   vec_foreach_index (i, cf->pager_index)
1053   {
1054     pi = &cf->pager_index[i];
1055
1056     if (pi->line == old_line &&
1057         (pi->offset <= old_offset || pi->offset + pi->length > old_offset))
1058       {
1059         /* Found it! */
1060         cf->pager_start = i;
1061         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   if (cf->pager_start >= vec_len (cf->pager_index))
1069     {
1070       if (!cf->height || vec_len (cf->pager_index) < (cf->height - 1))
1071         cf->pager_start = 0;
1072       else
1073         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 unix_vlib_cli_output (uword cli_file_index, u8 * buffer, uword buffer_bytes)
1097 {
1098   unix_main_t *um = &unix_main;
1099   clib_file_main_t *fm = &file_main;
1100   unix_cli_main_t *cm = &unix_cli_main;
1101   unix_cli_file_t *cf;
1102   clib_file_t *uf;
1103
1104   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1105   uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
1106
1107   if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
1108     {
1109       unix_vlib_cli_output_cooked (cf, uf, buffer, buffer_bytes);
1110     }
1111   else
1112     {
1113       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       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       while (row < vec_len (cf->pager_index))
1124         {
1125           if (row < cf->height - 1)
1126             {
1127               /* output this line */
1128               pi = &cf->pager_index[row];
1129               line = cf->pager_vector[pi->line] + pi->offset;
1130               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               if (line[pi->length - 1] != '\n' && row == cf->height - 2)
1135                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1136             }
1137           else
1138             {
1139               /* Display the pager prompt every 10 lines */
1140               if (!(row % 10))
1141                 unix_cli_pager_prompt (cf, uf);
1142             }
1143           row++;
1144         }
1145
1146       /* Check if we went over the pager buffer limit */
1147       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           cf->no_pager = 2;
1151
1152           /* If we likely printed the prompt, erase it */
1153           if (vec_len (cf->pager_index) > cf->height - 1)
1154             unix_cli_pager_prompt_erase (cf, uf);
1155
1156           /* Dump out the contents of the buffer */
1157           for (row = cf->pager_start + (cf->height - 1);
1158                row < vec_len (cf->pager_index); row++)
1159             {
1160               pi = &cf->pager_index[row];
1161               line = cf->pager_vector[pi->line] + pi->offset;
1162               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1163             }
1164
1165           unix_cli_pager_reset (cf);
1166         }
1167     }
1168 }
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 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   _("xterm");
1192   _("xterm-color");
1193   _("xterm-256color");          /* iTerm on Mac */
1194   _("screen");
1195   _("screen-256color");         /* Screen and tmux */
1196   _("ansi");                    /* Microsoft Telnet */
1197 #undef _
1198
1199   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 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   _("vppctl");
1224 #undef _
1225
1226   return 0;
1227 }
1228
1229 /** Set a session to be non-interactive. */
1230 static void
1231 unix_cli_set_session_noninteractive (unix_cli_file_t * cf)
1232 {
1233   /* Non-interactive sessions don't get these */
1234   cf->is_interactive = 0;
1235   cf->no_pager = 1;
1236   cf->history_limit = 0;
1237   cf->has_history = 0;
1238   cf->line_mode = 1;
1239 }
1240
1241 /** @brief Emit initial welcome banner and prompt on a connection. */
1242 static void
1243 unix_cli_file_welcome (unix_cli_main_t * cm, unix_cli_file_t * cf)
1244 {
1245   unix_main_t *um = &unix_main;
1246   clib_file_main_t *fm = &file_main;
1247   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   cf->started = 1;
1253
1254   if (!(cf->is_interactive))    /* No banner for non-interactive sessions */
1255     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   unix_cli_add_pending_output (uf, cf, (u8 *) "\r", 1);
1263
1264   if (!um->cli_no_banner && (um->flags & UNIX_FLAG_NOBANNER) == 0)
1265     {
1266       if (cf->ansi_capable && (um->flags & UNIX_FLAG_NOCOLOR) == 0)
1267         {
1268           banner = unix_cli_banner_color;
1269           len = ARRAY_LEN (unix_cli_banner_color);
1270         }
1271       else
1272         {
1273           banner = unix_cli_banner;
1274           len = ARRAY_LEN (unix_cli_banner);
1275         }
1276
1277       for (i = 0; i < len; i++)
1278         {
1279           unix_vlib_cli_output_cooked (cf, uf,
1280                                        banner[i].line, banner[i].length);
1281         }
1282     }
1283
1284   /* Prompt. */
1285   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 unix_cli_new_session_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
1295                               vlib_frame_t * f)
1296 {
1297   unix_cli_main_t *cm = &unix_cli_main;
1298   uword event_type, *event_data = 0;
1299   f64 wait = 10.0;
1300
1301   while (1)
1302     {
1303       if (vec_len (cm->new_sessions) > 0)
1304         wait = vlib_process_wait_for_event_or_clock (vm, wait);
1305       else
1306         vlib_process_wait_for_event (vm);
1307
1308       event_type = vlib_process_get_events (vm, &event_data);
1309
1310       switch (event_type)
1311         {
1312         case ~0:                /* no events => timeout */
1313           break;
1314
1315         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             if (pool_is_free_index (cm->cli_file_pool, event_data[0]))
1322               break;
1323
1324             ns.cf_index = event_data[0];
1325             ns.deadline = vlib_time_now (vm) + 1.0;
1326
1327             vec_add1 (cm->new_sessions, ns);
1328
1329             if (wait > 0.1)
1330               wait = 0.1;       /* force a re-evaluation soon, but not too soon */
1331           }
1332           break;
1333
1334         default:
1335           clib_warning ("BUG: unknown event type 0x%wx", event_type);
1336           break;
1337         }
1338
1339       vec_reset_length (event_data);
1340
1341       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           f64 now = vlib_time_now (vm);
1353           unix_cli_new_session_t *nsp;
1354           word index = 0;
1355
1356           wait = INFINITY;
1357
1358           vec_foreach (nsp, cm->new_sessions)
1359           {
1360             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                 index++;
1367
1368                 /* Check the connection didn't close already */
1369                 if (pool_is_free_index (cm->cli_file_pool, nsp->cf_index))
1370                   continue;
1371
1372                 cf = pool_elt_at_index (cm->cli_file_pool, nsp->cf_index);
1373
1374                 if (!cf->started)
1375                   unix_cli_file_welcome (cm, cf);
1376               }
1377             else
1378               {
1379                 wait = nsp->deadline - now;
1380                 break;
1381               }
1382           }
1383
1384           if (index)
1385             {
1386               /* We have sessions to remove */
1387               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 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   i32 consume = 0;
1414
1415   if (len == 1)
1416     return -1;                  /* want more bytes */
1417
1418   switch (input_vector[1])
1419     {
1420     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       consume = 1;
1425       break;
1426
1427     case WILL:
1428     case WONT:
1429     case DO:
1430     case DONT:
1431       /* Expect 3 bytes */
1432       if (len < 3)
1433         return -1;              /* want more bytes */
1434
1435       consume = 2;
1436       break;
1437
1438     case SB:
1439       {
1440         /* Sub option - search ahead for IAC SE to end it */
1441         i32 i;
1442         for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
1443           {
1444             if (input_vector[i - 1] == IAC && input_vector[i] == SE)
1445               {
1446                 /* We have a complete message; see if we care about it */
1447                 switch (input_vector[2])
1448                   {
1449                   case TELOPT_TTYPE:
1450                     if (input_vector[3] != 0)
1451                       break;
1452                     {
1453                       /* See if the the terminal type is recognized */
1454                       u8 *term = input_vector + 4;
1455                       uword len = i - 5;
1456
1457                       /* See if the terminal type is ANSI capable */
1458                       cf->ansi_capable =
1459                         unix_cli_terminal_type_ansi (term, len);
1460
1461                       /* See if the terminal type indicates non-interactive */
1462                       if (unix_cli_terminal_type_noninteractive (term, len))
1463                         unix_cli_set_session_noninteractive (cf);
1464                     }
1465
1466                     /* If session not started, we can release the pause */
1467                     if (!cf->started)
1468                       /* Send the welcome banner and initial prompt */
1469                       unix_cli_file_welcome (&unix_cli_main, cf);
1470                     break;
1471
1472                   case TELOPT_NAWS:
1473                     /* Window size */
1474                     if (i != 8) /* check message is correct size */
1475                       break;
1476
1477                     cf->width =
1478                       clib_net_to_host_u16 (*((u16 *) (input_vector + 3)));
1479                     if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH)
1480                       cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH;
1481                     if (cf->width == 0)
1482                       cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
1483
1484                     cf->height =
1485                       clib_net_to_host_u16 (*((u16 *) (input_vector + 5)));
1486                     if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT)
1487                       cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT;
1488                     if (cf->height == 0)
1489                       cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
1490
1491                     /* reindex pager buffer */
1492                     unix_cli_pager_reindex (cf);
1493                     /* redraw page */
1494                     unix_cli_pager_redraw (cf, uf);
1495                     break;
1496
1497                   default:
1498                     break;
1499                   }
1500                 /* Consume it all */
1501                 consume = i;
1502                 break;
1503               }
1504           }
1505
1506         if (i == UNIX_CLI_MAX_DEPTH_TELNET)
1507           consume = 1;          /* hit max search depth, advance one byte */
1508
1509         if (consume == 0)
1510           return -1;            /* want more bytes */
1511
1512         break;
1513       }
1514
1515     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       consume = 1;
1530       break;
1531
1532     case AYT:
1533       /* Are You There - trigger a visible response */
1534       consume = 1;
1535       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
1536       break;
1537
1538     default:
1539       /* Unknown command! Eat the IAC byte */
1540       break;
1541     }
1542
1543   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 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   u8 *save = 0;
1559   u8 **possible_commands;
1560   int j, delta;
1561
1562   switch (action)
1563     {
1564     case UNIX_CLI_PARSE_ACTION_NOACTION:
1565       break;
1566
1567     case UNIX_CLI_PARSE_ACTION_REVSEARCH:
1568     case UNIX_CLI_PARSE_ACTION_FWDSEARCH:
1569       if (!cf->has_history || !cf->history_limit)
1570         break;
1571       if (cf->search_mode == 0)
1572         {
1573           /* Erase the current command (if any) */
1574           for (; cf->cursor > 0; cf->cursor--)
1575             {
1576               unix_vlib_cli_output_cursor_left (cf, uf);
1577               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1578               unix_vlib_cli_output_cursor_left (cf, uf);
1579             }
1580
1581           vec_reset_length (cf->search_key);
1582           vec_reset_length (cf->current_command);
1583
1584           if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1585             cf->search_mode = -1;
1586           else
1587             cf->search_mode = 1;
1588         }
1589       else
1590         {
1591           if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1592             cf->search_mode = -1;
1593           else
1594             cf->search_mode = 1;
1595
1596           cf->excursion += cf->search_mode;
1597           goto search_again;
1598         }
1599       break;
1600
1601     case UNIX_CLI_PARSE_ACTION_ERASELINELEFT:
1602       /* Erase the command from the cursor to the start */
1603
1604       j = cf->cursor;
1605       /* Shimmy backwards to the new end of line position */
1606       delta = vec_len (cf->current_command) - cf->cursor;
1607       for (; cf->cursor > delta; cf->cursor--)
1608         unix_vlib_cli_output_cursor_left (cf, uf);
1609       /* Zap from here to the end of what is currently displayed */
1610       for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
1611         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1612       /* Get back to the start of the line */
1613       for (; cf->cursor > 0; cf->cursor--)
1614         unix_vlib_cli_output_cursor_left (cf, uf);
1615
1616       /* Delete the desired text from the command */
1617       memmove (cf->current_command, cf->current_command + j, delta);
1618       vec_set_len (cf->current_command, delta);
1619
1620       /* Print the new contents */
1621       unix_vlib_cli_output_cooked (cf, uf, cf->current_command, delta);
1622       cf->cursor = delta;       /* for backspace tracking */
1623
1624       /* Shimmy back to the start */
1625       for (; cf->cursor > 0; cf->cursor--)
1626         unix_vlib_cli_output_cursor_left (cf, uf);
1627
1628       cf->search_mode = 0;
1629       break;
1630
1631     case UNIX_CLI_PARSE_ACTION_ERASELINERIGHT:
1632       /* Erase the command from the cursor to the end */
1633
1634       j = cf->cursor;
1635       /* Zap from cursor to end of what is currently displayed */
1636       for (; cf->cursor < (vec_len (cf->current_command)); cf->cursor++)
1637         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1638       /* Get back to where we were */
1639       for (; cf->cursor > j; cf->cursor--)
1640         unix_vlib_cli_output_cursor_left (cf, uf);
1641
1642       /* Truncate the line at the cursor */
1643       vec_set_len (cf->current_command, cf->cursor);
1644
1645       cf->search_mode = 0;
1646       break;
1647
1648     case UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT:
1649       /* calculate num of caracter to be erased */
1650       delta = 0;
1651       while (cf->cursor > delta
1652              && cf->current_command[cf->cursor - delta - 1] == ' ')
1653         delta++;
1654       while (cf->cursor > delta
1655              && cf->current_command[cf->cursor - delta - 1] != ' ')
1656         delta++;
1657
1658       if (vec_len (cf->current_command))
1659         {
1660           if (cf->cursor > 0)
1661             {
1662               /* move cursor left delta times */
1663               for (j = delta; j > 0; j--, cf->cursor--)
1664                 unix_vlib_cli_output_cursor_left (cf, uf);
1665               save = cf->current_command + cf->cursor;
1666
1667               /* redraw remainder of line */
1668               memmove (cf->current_command + cf->cursor,
1669                        cf->current_command + cf->cursor + delta,
1670                        _vec_len (cf->current_command) - cf->cursor - delta);
1671               unix_vlib_cli_output_cooked (cf, uf,
1672                                            cf->current_command + cf->cursor,
1673                                            _vec_len (cf->current_command) -
1674                                            cf->cursor);
1675               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               for (j = delta; j > 0; j--, cf->cursor--)
1680                 unix_vlib_cli_output_cursor_left (cf, uf);
1681               for (j = delta; j > 0; j--, cf->cursor++)
1682                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1683               for (; (cf->current_command + cf->cursor) > save; cf->cursor--)
1684                 unix_vlib_cli_output_cursor_left (cf, uf);
1685               vec_dec_len (cf->current_command, delta);
1686             }
1687         }
1688       cf->search_mode = 0;
1689       cf->excursion = 0;
1690       vec_reset_length (cf->search_key);
1691       break;
1692
1693     case UNIX_CLI_PARSE_ACTION_LEFT:
1694       if (cf->cursor > 0)
1695         {
1696           unix_vlib_cli_output_cursor_left (cf, uf);
1697           cf->cursor--;
1698         }
1699
1700       cf->search_mode = 0;
1701       break;
1702
1703     case UNIX_CLI_PARSE_ACTION_RIGHT:
1704       if (cf->cursor < vec_len (cf->current_command))
1705         {
1706           /* have to emit the character under the cursor */
1707           unix_vlib_cli_output_cooked (cf, uf,
1708                                        cf->current_command + cf->cursor, 1);
1709           cf->cursor++;
1710         }
1711
1712       cf->search_mode = 0;
1713       break;
1714
1715     case UNIX_CLI_PARSE_ACTION_UP:
1716     case UNIX_CLI_PARSE_ACTION_DOWN:
1717       if (!cf->has_history || !cf->history_limit)
1718         break;
1719       cf->search_mode = 0;
1720       /* Erase the command */
1721       for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
1722         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1723       for (; cf->cursor > 0; cf->cursor--)
1724         {
1725           unix_vlib_cli_output_cursor_left (cf, uf);
1726           unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1727           unix_vlib_cli_output_cursor_left (cf, uf);
1728         }
1729       vec_reset_length (cf->current_command);
1730       if (vec_len (cf->command_history))
1731         {
1732           if (action == UNIX_CLI_PARSE_ACTION_UP)
1733             delta = -1;
1734           else
1735             delta = 1;
1736
1737           cf->excursion += delta;
1738
1739           if (cf->excursion == vec_len (cf->command_history))
1740             {
1741               /* down-arrowed to last entry - want a blank line */
1742               vec_set_len (cf->current_command, 0);
1743             }
1744           else if (cf->excursion < 0)
1745             {
1746               /* up-arrowed over the start to the end, want a blank line */
1747               cf->excursion = vec_len (cf->command_history);
1748               vec_set_len (cf->current_command, 0);
1749             }
1750           else
1751             {
1752               if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
1753                 /* down-arrowed past end - wrap to start */
1754                 cf->excursion = 0;
1755
1756               /* Print the command at the current position */
1757               prev = cf->command_history[cf->excursion];
1758               vec_validate (cf->current_command, vec_len (prev) - 1);
1759
1760               clib_memcpy (cf->current_command, prev, vec_len (prev));
1761               vec_set_len (cf->current_command, vec_len (prev));
1762               unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
1763                                            vec_len (cf->current_command));
1764             }
1765         }
1766       cf->cursor = vec_len (cf->current_command);
1767       break;
1768
1769     case UNIX_CLI_PARSE_ACTION_HOME:
1770       if (vec_len (cf->current_command) && cf->cursor > 0)
1771         {
1772           for (; cf->cursor > 0; cf->cursor--)
1773             unix_vlib_cli_output_cursor_left (cf, uf);
1774         }
1775
1776       cf->search_mode = 0;
1777       break;
1778
1779     case UNIX_CLI_PARSE_ACTION_END:
1780       if (vec_len (cf->current_command) &&
1781           cf->cursor < vec_len (cf->current_command))
1782         {
1783           unix_vlib_cli_output_cooked (cf, uf,
1784                                        cf->current_command + cf->cursor,
1785                                        vec_len (cf->current_command) -
1786                                        cf->cursor);
1787           cf->cursor = vec_len (cf->current_command);
1788         }
1789
1790       cf->search_mode = 0;
1791       break;
1792
1793     case UNIX_CLI_PARSE_ACTION_WORDLEFT:
1794       if (vec_len (cf->current_command) && cf->cursor > 0)
1795         {
1796           unix_vlib_cli_output_cursor_left (cf, uf);
1797           cf->cursor--;
1798
1799           while (cf->cursor && isspace (cf->current_command[cf->cursor]))
1800             {
1801               unix_vlib_cli_output_cursor_left (cf, uf);
1802               cf->cursor--;
1803             }
1804           while (cf->cursor && !isspace (cf->current_command[cf->cursor]))
1805             {
1806               if (isspace (cf->current_command[cf->cursor - 1]))
1807                 break;
1808               unix_vlib_cli_output_cursor_left (cf, uf);
1809               cf->cursor--;
1810             }
1811
1812         }
1813
1814       cf->search_mode = 0;
1815       break;
1816
1817     case UNIX_CLI_PARSE_ACTION_WORDRIGHT:
1818       if (vec_len (cf->current_command) &&
1819           cf->cursor < vec_len (cf->current_command))
1820         {
1821           int e = vec_len (cf->current_command);
1822           j = cf->cursor;
1823           while (j < e && !isspace (cf->current_command[j]))
1824             j++;
1825           while (j < e && isspace (cf->current_command[j]))
1826             j++;
1827           unix_vlib_cli_output_cooked (cf, uf,
1828                                        cf->current_command + cf->cursor,
1829                                        j - cf->cursor);
1830           cf->cursor = j;
1831         }
1832
1833       cf->search_mode = 0;
1834       break;
1835
1836
1837     case UNIX_CLI_PARSE_ACTION_ERASE:
1838       if (vec_len (cf->current_command))
1839         {
1840           if (cf->cursor == vec_len (cf->current_command))
1841             {
1842               unix_vlib_cli_output_cursor_left (cf, uf);
1843               cf->cursor--;
1844               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1845               cf->cursor++;
1846               unix_vlib_cli_output_cursor_left (cf, uf);
1847               cf->cursor--;
1848               vec_dec_len (cf->current_command, 1);
1849             }
1850           else if (cf->cursor > 0)
1851             {
1852               /* shift everything at & to the right of the cursor left by 1 */
1853               j = vec_len (cf->current_command) - cf->cursor;
1854               memmove (cf->current_command + cf->cursor - 1,
1855                        cf->current_command + cf->cursor, j);
1856               vec_dec_len (cf->current_command, 1);
1857
1858               /* redraw the rest of the line */
1859               unix_vlib_cli_output_cursor_left (cf, uf);
1860               cf->cursor--;
1861               unix_vlib_cli_output_cooked (cf, uf,
1862                                            cf->current_command + cf->cursor,
1863                                            j);
1864               cf->cursor += j;
1865               /* erase last char */
1866               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1867               cf->cursor++;
1868
1869               /* and shift the terminal cursor back where it should be */
1870               j += 2;           /* account for old string length and offset position */
1871               while (--j)
1872                 {
1873                   unix_vlib_cli_output_cursor_left (cf, uf);
1874                   cf->cursor--;
1875                 }
1876             }
1877         }
1878       cf->search_mode = 0;
1879       cf->excursion = 0;
1880       vec_reset_length (cf->search_key);
1881       break;
1882
1883     case UNIX_CLI_PARSE_ACTION_ERASERIGHT:
1884       if (vec_len (cf->current_command))
1885         {
1886           if (cf->cursor < vec_len (cf->current_command))
1887             {
1888               /* shift everything to the right of the cursor left by 1 */
1889               j = vec_len (cf->current_command) - cf->cursor - 1;
1890               memmove (cf->current_command + cf->cursor,
1891                        cf->current_command + cf->cursor + 1, j);
1892               vec_dec_len (cf->current_command, 1);
1893               /* redraw the rest of the line */
1894               unix_vlib_cli_output_cooked (cf, uf,
1895                                            cf->current_command + cf->cursor,
1896                                            j);
1897               cf->cursor += j;
1898               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1899               cf->cursor++;
1900               unix_vlib_cli_output_cursor_left (cf, uf);
1901               cf->cursor--;
1902               /* and shift the terminal cursor back where it should be */
1903               if (j)
1904                 {
1905                   unix_vlib_cli_output_cursor_left (cf, uf);
1906                   cf->cursor--;
1907                   while (--j)
1908                     {
1909                       unix_vlib_cli_output_cursor_left (cf, uf);
1910                       cf->cursor--;
1911                     }
1912                 }
1913             }
1914         }
1915       else if (input == 'D' - '@')
1916         {
1917           /* ^D with no command entered = quit */
1918           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
1919           vlib_process_signal_event (um->vlib_main,
1920                                      vlib_current_process (um->vlib_main),
1921                                      UNIX_CLI_PROCESS_EVENT_QUIT,
1922                                      cf - cm->cli_file_pool);
1923         }
1924       cf->search_mode = 0;
1925       cf->excursion = 0;
1926       vec_reset_length (cf->search_key);
1927       break;
1928
1929     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       if (cf->ansi_capable)
1935         unix_vlib_cli_output_cooked (cf, uf,
1936                                      (u8 *) ANSI_CLEAR,
1937                                      sizeof (ANSI_CLEAR) - 1);
1938       else
1939         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1940
1941       unix_vlib_cli_output_raw (cf, uf,
1942                                 cm->cli_prompt, vec_len (cm->cli_prompt));
1943       unix_vlib_cli_output_cooked (cf, uf,
1944                                    cf->current_command,
1945                                    vec_len (cf->current_command));
1946       j = cf->cursor;
1947       cf->cursor = vec_len (cf->current_command);
1948       for (; cf->cursor > j; cf->cursor--)
1949         unix_vlib_cli_output_cursor_left (cf, uf);
1950
1951       break;
1952
1953     case UNIX_CLI_PARSE_ACTION_TAB:
1954       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           if (isspace (cf->current_command[cf->cursor]))
1959             {
1960               /* save and clear any input that is after the cursor */
1961               vec_resize (save, vec_len (cf->current_command) - cf->cursor);
1962               clib_memcpy (save, cf->current_command + cf->cursor,
1963                            vec_len (cf->current_command) - cf->cursor);
1964               vec_set_len (cf->current_command, cf->cursor);
1965             }
1966           else
1967             {
1968               unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
1969               break;
1970             }
1971         }
1972       possible_commands =
1973         vlib_cli_get_possible_completions (cf->current_command);
1974       if (vec_len (possible_commands) == 1)
1975         {
1976           u8 *completed = possible_commands[0];
1977           j = cf->cursor;
1978
1979           /* find the last word of current_command */
1980           while (j >= 1 && !isspace (cf->current_command[j - 1]))
1981             {
1982               unix_vlib_cli_output_cursor_left (cf, uf);
1983               cf->cursor--;
1984               j--;
1985             }
1986           vec_set_len (cf->current_command, j);
1987
1988           /* replace it with the newly expanded command */
1989           vec_append (cf->current_command, completed);
1990
1991           /* echo to the terminal */
1992           unix_vlib_cli_output_cooked (cf, uf, completed,
1993                                        vec_len (completed));
1994
1995           /* add one trailing space if needed */
1996           if (vec_len (save) == 0)
1997             {
1998               vec_add1 (cf->current_command, ' ');
1999               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
2000             }
2001
2002           cf->cursor = vec_len (cf->current_command);
2003
2004         }
2005       else if (vec_len (possible_commands) >= 2)
2006         {
2007           u8 **possible_command;
2008           uword max_command_len = 0, min_command_len = ~0;
2009           u32 i;
2010
2011           vec_foreach (possible_command, possible_commands)
2012           {
2013             if (vec_len (*possible_command) > max_command_len)
2014               max_command_len = vec_len (*possible_command);
2015             if (vec_len (*possible_command) < min_command_len)
2016               min_command_len = vec_len (*possible_command);
2017           }
2018
2019           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2020
2021           i = 0;
2022           vec_foreach (possible_command, possible_commands)
2023           {
2024             if (i + max_command_len >= cf->width)
2025               {
2026                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2027                 i = 0;
2028               }
2029             unix_vlib_cli_output_cooked (cf, uf, *possible_command,
2030                                          vec_len (*possible_command));
2031             for (j = vec_len (*possible_command); j < max_command_len + 2;
2032                  j++)
2033               {
2034                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
2035               }
2036             i += max_command_len + 2;
2037           }
2038
2039           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2040
2041           /* rewrite prompt */
2042           unix_cli_cli_prompt (cf, uf);
2043           unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
2044                                        vec_len (cf->current_command));
2045
2046           /* count length of last word */
2047           j = cf->cursor;
2048           i = 0;
2049           while (j >= 1 && !isspace (cf->current_command[j - 1]))
2050             {
2051               j--;
2052               i++;
2053             }
2054
2055           /* determine smallest common command */
2056           for (; i < min_command_len; i++)
2057             {
2058               u8 common = '\0';
2059               int stop = 0;
2060
2061               vec_foreach (possible_command, possible_commands)
2062               {
2063                 if (common == '\0')
2064                   {
2065                     common = (*possible_command)[i];
2066                   }
2067                 else if (common != (*possible_command)[i])
2068                   {
2069                     stop = 1;
2070                     break;
2071                   }
2072               }
2073
2074               if (!stop)
2075                 {
2076                   vec_add1 (cf->current_command, common);
2077                   cf->cursor++;
2078                   unix_vlib_cli_output_cooked (cf, uf, (u8 *) & common, 1);
2079                 }
2080               else
2081                 {
2082                   break;
2083                 }
2084             }
2085         }
2086       else
2087         {
2088           unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
2089         }
2090
2091       if (vec_len (save) > 0)
2092         {
2093           /* restore remaining input if tab was hit in the middle of a line */
2094           unix_vlib_cli_output_cooked (cf, uf, save, vec_len (save));
2095           cf->cursor += vec_len (save);
2096           for (j = 0; j < vec_len (save); j++, cf->cursor--)
2097             unix_vlib_cli_output_cursor_left (cf, uf);
2098           vec_append (cf->current_command, save);
2099           vec_free (save);
2100         }
2101       vec_free (possible_commands);
2102
2103       break;
2104     case UNIX_CLI_PARSE_ACTION_YANK:
2105       /* TODO */
2106       break;
2107
2108
2109     case UNIX_CLI_PARSE_ACTION_PAGER_QUIT:
2110     pager_quit:
2111       unix_cli_pager_prompt_erase (cf, uf);
2112       unix_cli_pager_reset (cf);
2113       unix_cli_cli_prompt (cf, uf);
2114       break;
2115
2116     case UNIX_CLI_PARSE_ACTION_PAGER_NEXT:
2117     case UNIX_CLI_PARSE_ACTION_PAGER_PGDN:
2118       /* show next page of the buffer */
2119       if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
2120         {
2121           u8 *line = NULL;
2122           unix_cli_pager_index_t *pi = NULL;
2123
2124           int m = cf->pager_start + (cf->height - 1);
2125           unix_cli_pager_prompt_erase (cf, uf);
2126           for (j = m;
2127                j < vec_len (cf->pager_index) && cf->pager_start < m;
2128                j++, cf->pager_start++)
2129             {
2130               pi = &cf->pager_index[j];
2131               line = cf->pager_vector[pi->line] + pi->offset;
2132               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           if (pi && line[pi->length - 1] != '\n')
2136             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2137           unix_cli_pager_prompt (cf, uf);
2138         }
2139       else
2140         {
2141           if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
2142             /* no more in buffer, exit, but only if it was <space> */
2143             goto pager_quit;
2144         }
2145       break;
2146
2147     case UNIX_CLI_PARSE_ACTION_PAGER_DN:
2148     case UNIX_CLI_PARSE_ACTION_PAGER_CRLF:
2149       /* display the next line of the buffer */
2150       if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
2151         {
2152           u8 *line;
2153           unix_cli_pager_index_t *pi;
2154
2155           unix_cli_pager_prompt_erase (cf, uf);
2156           pi = &cf->pager_index[cf->pager_start + (cf->height - 1)];
2157           line = cf->pager_vector[pi->line] + pi->offset;
2158           unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2159           cf->pager_start++;
2160           /* if the last line didn't end in newline, add a newline */
2161           if (line[pi->length - 1] != '\n')
2162             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2163           unix_cli_pager_prompt (cf, uf);
2164         }
2165       else
2166         {
2167           if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
2168             /* no more in buffer, exit, but only if it was <enter> */
2169             goto pager_quit;
2170         }
2171
2172       break;
2173
2174     case UNIX_CLI_PARSE_ACTION_PAGER_UP:
2175       /* scroll the page back one line */
2176       if (cf->pager_start > 0)
2177         {
2178           u8 *line = NULL;
2179           unix_cli_pager_index_t *pi = NULL;
2180
2181           cf->pager_start--;
2182           if (cf->ansi_capable)
2183             {
2184               pi = &cf->pager_index[cf->pager_start];
2185               line = cf->pager_vector[pi->line] + pi->offset;
2186               unix_cli_pager_prompt_erase (cf, uf);
2187               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_SCROLLDN,
2188                                            sizeof (ANSI_SCROLLDN) - 1);
2189               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_SAVECURSOR,
2190                                            sizeof (ANSI_SAVECURSOR) - 1);
2191               unix_cli_ansi_cursor (cf, uf, 1, 1);
2192               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_CLEARLINE,
2193                                            sizeof (ANSI_CLEARLINE) - 1);
2194               unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2195               unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_RESTCURSOR,
2196                                            sizeof (ANSI_RESTCURSOR) - 1);
2197               unix_cli_pager_prompt_erase (cf, uf);
2198               unix_cli_pager_prompt (cf, uf);
2199             }
2200           else
2201             {
2202               int m = cf->pager_start + (cf->height - 1);
2203               unix_cli_pager_prompt_erase (cf, uf);
2204               for (j = cf->pager_start;
2205                    j < vec_len (cf->pager_index) && j < m; j++)
2206                 {
2207                   pi = &cf->pager_index[j];
2208                   line = cf->pager_vector[pi->line] + pi->offset;
2209                   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               if (pi && line[pi->length - 1] != '\n')
2213                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2214               unix_cli_pager_prompt (cf, uf);
2215             }
2216         }
2217       break;
2218
2219     case UNIX_CLI_PARSE_ACTION_PAGER_TOP:
2220       /* back to the first page of the buffer */
2221       if (cf->pager_start > 0)
2222         {
2223           u8 *line = NULL;
2224           unix_cli_pager_index_t *pi = NULL;
2225
2226           cf->pager_start = 0;
2227           int m = cf->pager_start + (cf->height - 1);
2228           unix_cli_pager_prompt_erase (cf, uf);
2229           for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
2230                j++)
2231             {
2232               pi = &cf->pager_index[j];
2233               line = cf->pager_vector[pi->line] + pi->offset;
2234               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           if (pi && line[pi->length - 1] != '\n')
2238             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2239           unix_cli_pager_prompt (cf, uf);
2240         }
2241       break;
2242
2243     case UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM:
2244       /* skip to the last page of the buffer */
2245       if (cf->pager_start < vec_len (cf->pager_index) - (cf->height - 1))
2246         {
2247           u8 *line = NULL;
2248           unix_cli_pager_index_t *pi = NULL;
2249
2250           cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
2251           unix_cli_pager_prompt_erase (cf, uf);
2252           unix_cli_pager_message (cf, uf, "skipping", "\n");
2253           for (j = cf->pager_start; j < vec_len (cf->pager_index); j++)
2254             {
2255               pi = &cf->pager_index[j];
2256               line = cf->pager_vector[pi->line] + pi->offset;
2257               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           if (pi && line[pi->length - 1] != '\n')
2261             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2262           unix_cli_pager_prompt (cf, uf);
2263         }
2264       break;
2265
2266     case UNIX_CLI_PARSE_ACTION_PAGER_PGUP:
2267       /* wander back one page in the buffer */
2268       if (cf->pager_start > 0)
2269         {
2270           u8 *line = NULL;
2271           unix_cli_pager_index_t *pi = NULL;
2272           int m;
2273
2274           if (cf->pager_start >= cf->height)
2275             cf->pager_start -= cf->height - 1;
2276           else
2277             cf->pager_start = 0;
2278           m = cf->pager_start + cf->height - 1;
2279           unix_cli_pager_prompt_erase (cf, uf);
2280           for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
2281                j++)
2282             {
2283               pi = &cf->pager_index[j];
2284               line = cf->pager_vector[pi->line] + pi->offset;
2285               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           if (pi && line[pi->length - 1] != '\n')
2289             unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2290           unix_cli_pager_prompt (cf, uf);
2291         }
2292       break;
2293
2294     case UNIX_CLI_PARSE_ACTION_PAGER_REDRAW:
2295       /* Redraw the current pager screen */
2296       unix_cli_pager_redraw (cf, uf);
2297       break;
2298
2299     case UNIX_CLI_PARSE_ACTION_PAGER_SEARCH:
2300       /* search forwards in the buffer */
2301       break;
2302
2303
2304     case UNIX_CLI_PARSE_ACTION_CRLF:
2305     crlf:
2306       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2307
2308       if (cf->has_history && cf->history_limit)
2309         {
2310           if (cf->command_history
2311               && vec_len (cf->command_history) >= cf->history_limit)
2312             {
2313               vec_free (cf->command_history[0]);
2314               vec_delete (cf->command_history, 1, 0);
2315             }
2316           /* Don't add blank lines to the cmd history */
2317           if (vec_len (cf->current_command))
2318             {
2319               /* Don't duplicate the previous command */
2320               j = vec_len (cf->command_history);
2321               if (j == 0 ||
2322                   (vec_len (cf->current_command) !=
2323                    vec_len (cf->command_history[j - 1])
2324                    || memcmp (cf->current_command, cf->command_history[j - 1],
2325                               vec_len (cf->current_command)) != 0))
2326                 {
2327                   /* copy the command to the history */
2328                   u8 *c = 0;
2329                   vec_append (c, cf->current_command);
2330                   vec_add1 (cf->command_history, c);
2331                   cf->command_number++;
2332                 }
2333             }
2334           cf->excursion = vec_len (cf->command_history);
2335         }
2336
2337       cf->search_mode = 0;
2338       vec_reset_length (cf->search_key);
2339       cf->cursor = 0;
2340
2341       return 0;
2342
2343     case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
2344     case UNIX_CLI_PARSE_ACTION_NOMATCH:
2345       if (vec_len (cf->pager_index))
2346         {
2347           /* no-op for now */
2348         }
2349       else if (cf->has_history && cf->search_mode != 0 && isprint (input))
2350         {
2351           int k, limit, offset;
2352           u8 *item;
2353
2354           vec_add1 (cf->search_key, input);
2355
2356         search_again:
2357           for (j = 0; j < vec_len (cf->command_history); j++)
2358             {
2359               if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
2360                 cf->excursion = 0;
2361               else if (cf->excursion < 0)
2362                 cf->excursion = vec_len (cf->command_history) - 1;
2363
2364               item = cf->command_history[cf->excursion];
2365
2366               limit = (vec_len (cf->search_key) > vec_len (item)) ?
2367                 vec_len (item) : vec_len (cf->search_key);
2368
2369               for (offset = 0; offset <= vec_len (item) - limit; offset++)
2370                 {
2371                   for (k = 0; k < limit; k++)
2372                     {
2373                       if (item[k + offset] != cf->search_key[k])
2374                         goto next_offset;
2375                     }
2376                   goto found_at_offset;
2377
2378                 next_offset:
2379                   ;
2380                 }
2381               goto next;
2382
2383             found_at_offset:
2384               for (; cf->cursor > 0; cf->cursor--)
2385                 {
2386                   unix_vlib_cli_output_cursor_left (cf, uf);
2387                   unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
2388                   unix_vlib_cli_output_cursor_left (cf, uf);
2389                 }
2390
2391               vec_validate (cf->current_command, vec_len (item) - 1);
2392               clib_memcpy (cf->current_command, item, vec_len (item));
2393               vec_set_len (cf->current_command, vec_len (item));
2394
2395               unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
2396                                            vec_len (cf->current_command));
2397               cf->cursor = vec_len (cf->current_command);
2398               goto found;
2399
2400             next:
2401               cf->excursion += cf->search_mode;
2402             }
2403
2404           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\nNo match...", 12);
2405           vec_reset_length (cf->search_key);
2406           vec_reset_length (cf->current_command);
2407           cf->search_mode = 0;
2408           cf->cursor = 0;
2409           goto crlf;
2410         }
2411       else if (isprint (input)) /* skip any errant control codes */
2412         {
2413           if (cf->cursor == vec_len (cf->current_command))
2414             {
2415               /* Append to end */
2416               vec_add1 (cf->current_command, input);
2417               cf->cursor++;
2418
2419               /* Echo the character back to the client */
2420               unix_vlib_cli_output_cooked (cf, uf, &input, 1);
2421             }
2422           else
2423             {
2424               /* Insert at cursor: resize +1 byte, move everything over */
2425               j = vec_len (cf->current_command) - cf->cursor;
2426               vec_add1 (cf->current_command, (u8) 'A');
2427               memmove (cf->current_command + cf->cursor + 1,
2428                        cf->current_command + cf->cursor, j);
2429               cf->current_command[cf->cursor] = input;
2430               /* Redraw the line */
2431               j++;
2432               unix_vlib_cli_output_cooked (cf, uf,
2433                                            cf->current_command + cf->cursor,
2434                                            j);
2435               cf->cursor += j;
2436               j--;
2437               /* Put terminal cursor back */
2438               for (; j > 0; j--, cf->cursor--)
2439                 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     found:
2448
2449       break;
2450
2451     case UNIX_CLI_PARSE_ACTION_TELNETIAC:
2452       break;
2453     }
2454   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 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   clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
2464   int i;
2465
2466   for (i = 0; i < vec_len (cf->input_vector); i++)
2467     {
2468       unix_cli_parse_action_t action;
2469       i32 matched = 0;
2470       unix_cli_parse_actions_t *a;
2471
2472       /* If we're in the pager mode, search the pager actions */
2473       a =
2474         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       action = unix_cli_match_action (a, &cf->input_vector[i],
2479                                       vec_len (cf->input_vector) - i,
2480                                       &matched);
2481
2482       switch (action)
2483         {
2484         case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
2485           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               vec_delete (cf->input_vector, i, 0);
2493             }
2494           return 1;             /* wait for more */
2495
2496         case UNIX_CLI_PARSE_ACTION_TELNETIAC:
2497           /* process telnet options */
2498           matched = unix_cli_process_telnet (um, cf, uf,
2499                                              cf->input_vector + i,
2500                                              vec_len (cf->input_vector) - i);
2501           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               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                   vec_delete (cf->input_vector, i, 0);
2513                 }
2514               return 1;         /* wait for more */
2515             }
2516           break;
2517
2518         default:
2519           /* If telnet option processing switched us to line mode, get us
2520            * out of here!
2521            */
2522           if (cf->line_mode)
2523             {
2524               vec_delete (cf->input_vector, i, 0);
2525               vec_free (cf->current_command);
2526               cf->current_command = cf->input_vector;
2527               return 0;
2528             }
2529
2530           /* process the action */
2531           if (!unix_cli_line_process_one (cm, um, cf, uf,
2532                                           cf->input_vector[i], action))
2533             {
2534               /* CRLF found. Consume the bytes from the input_vector */
2535               vec_delete (cf->input_vector, i + matched, 0);
2536               /* And tell our caller to execute cf->input_command */
2537               return 0;
2538             }
2539         }
2540
2541       i += matched;
2542     }
2543
2544   vec_reset_length (cf->input_vector);
2545   return 1;
2546 }
2547
2548 /** @brief Process input to a CLI session. */
2549 static void
2550 unix_cli_process_input (unix_cli_main_t * cm, uword cli_file_index)
2551 {
2552   unix_main_t *um = &unix_main;
2553   clib_file_main_t *fm = &file_main;
2554   clib_file_t *uf;
2555   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   cm->current_input_file_index = cli_file_index;
2560
2561 more:
2562   /* Try vlibplex first.  Someday... */
2563   if (0 && vlib_parse_eval (cf->input_vector) == 0)
2564     goto done;
2565
2566
2567   if (cf->line_mode)
2568     {
2569       /* just treat whatever we got as a complete line of input */
2570       cf->current_command = cf->input_vector;
2571     }
2572   else
2573     {
2574       /* Line edit, echo, etc. */
2575       if (unix_cli_line_edit (cm, um, fm, cf))
2576         /* want more input */
2577         return;
2578     }
2579
2580   if (um->log_fd)
2581     {
2582       static u8 *lv;
2583       vec_reset_length (lv);
2584       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       if ((vec_len (cf->current_command) > 0) &&
2588           (cf->current_command[vec_len (cf->current_command) - 1] != '\n'))
2589         lv = format (lv, "\n");
2590       int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv));
2591     }
2592
2593   /* Run the command through the macro processor */
2594   if (vec_len (cf->current_command))
2595     {
2596       u8 *expanded;
2597       vec_validate (cf->current_command, vec_len (cf->current_command));
2598       cf->current_command[vec_len (cf->current_command) - 1] = 0;
2599       /* The macro expander expects proper C-strings, not vectors */
2600       expanded = (u8 *) clib_macro_eval (&cf->macro_main,
2601                                          (i8 *) cf->current_command,
2602                                          1 /* complain */ ,
2603                                          0 /* level */ ,
2604                                          8 /* max_level */ );
2605       /* Macro processor NULL terminates the return */
2606       vec_dec_len (expanded, 1);
2607       vec_reset_length (cf->current_command);
2608       vec_append (cf->current_command, expanded);
2609       vec_free (expanded);
2610     }
2611
2612   /* Build an unformat structure around our command */
2613   unformat_init_vector (&input, cf->current_command);
2614
2615   /* Remove leading white space from input. */
2616   (void) unformat (&input, "");
2617
2618   cf->pager_start = 0;          /* start a new pager session */
2619
2620   if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
2621     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   input.buffer = 0;
2626
2627   unformat_free (&input);
2628
2629   /* Re-fetch pointer since pool may have moved. */
2630   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2631   uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
2632
2633 done:
2634   /* reset vector; we'll re-use it later  */
2635   if (cf->line_mode)
2636     {
2637       vec_reset_length (cf->input_vector);
2638       cf->current_command = 0;
2639     }
2640   else
2641     {
2642       vec_reset_length (cf->current_command);
2643     }
2644
2645   if (cf->no_pager == 2)
2646     {
2647       /* Pager was programmatically disabled */
2648       unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
2649       cf->no_pager = um->cli_no_pager;
2650     }
2651
2652   if (vec_len (cf->pager_index) == 0
2653       || vec_len (cf->pager_index) < cf->height)
2654     {
2655       /* There was no need for the pager */
2656       unix_cli_pager_reset (cf);
2657
2658       /* Prompt. */
2659       unix_cli_cli_prompt (cf, uf);
2660     }
2661   else
2662     {
2663       /* Display the pager prompt */
2664       unix_cli_pager_prompt (cf, uf);
2665     }
2666
2667   /* Any residual data in the input vector? */
2668   if (vec_len (cf->input_vector))
2669     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   if (!cf->is_interactive)
2678     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 unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
2687 {
2688   unix_main_t *um = &unix_main;
2689   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   if (pool_is_free_index (cm->cli_file_pool, cli_file_index))
2696     return;
2697
2698   vec_foreach_index (i, cm->new_sessions)
2699     {
2700       unix_cli_new_session_t *ns = vec_elt_at_index (cm->new_sessions, i);
2701
2702       if (ns->cf_index == cli_file_index)
2703         {
2704           ns->cf_index = ~0;
2705           break;
2706         }
2707     }
2708
2709   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2710   uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
2711
2712   /* Quit/EOF on stdin means quit program. */
2713   if (uf->file_descriptor == STDIN_FILENO)
2714     clib_longjmp (&um->vlib_main->main_loop_exit, VLIB_MAIN_LOOP_EXIT_CLI);
2715
2716   vec_free (cf->current_command);
2717   vec_free (cf->search_key);
2718
2719   for (i = 0; i < vec_len (cf->command_history); i++)
2720     vec_free (cf->command_history[i]);
2721
2722   vec_free (cf->command_history);
2723   vec_free (cf->input_vector);
2724
2725   clib_file_del (fm, uf);
2726
2727   unix_cli_file_free (cf);
2728   clib_macro_free (&cf->macro_main);
2729   pool_put (cm->cli_file_pool, cf);
2730 }
2731
2732 /** Handle system events. */
2733 static uword
2734 unix_cli_process (vlib_main_t * vm,
2735                   vlib_node_runtime_t * rt, vlib_frame_t * f)
2736 {
2737   unix_cli_main_t *cm = &unix_cli_main;
2738   uword i, *data = 0;
2739
2740   while (1)
2741     {
2742       unix_cli_process_event_type_t event_type;
2743       vlib_process_wait_for_event (vm);
2744       event_type = vlib_process_get_events (vm, &data);
2745
2746       switch (event_type)
2747         {
2748         case UNIX_CLI_PROCESS_EVENT_READ_READY:
2749           for (i = 0; i < vec_len (data); i++)
2750             unix_cli_process_input (cm, data[i]);
2751           break;
2752
2753         case UNIX_CLI_PROCESS_EVENT_QUIT:
2754           /* Kill this process. */
2755           for (i = 0; i < vec_len (data); i++)
2756             unix_cli_kill (cm, data[i]);
2757           goto done;
2758         }
2759
2760       if (data)
2761         vec_set_len (data, 0);
2762     }
2763
2764 done:
2765   vec_free (data);
2766
2767   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   vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);
2771
2772   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 unix_cli_write_ready (clib_file_t * uf)
2779 {
2780   unix_cli_main_t *cm = &unix_cli_main;
2781   unix_cli_file_t *cf;
2782   int n;
2783
2784   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
2785
2786   /* Flush output vector. */
2787   if (cf->is_socket)
2788     /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
2789     n = send (uf->file_descriptor,
2790               cf->output_vector, vec_len (cf->output_vector), MSG_NOSIGNAL);
2791   else
2792     n = write (uf->file_descriptor,
2793                cf->output_vector, vec_len (cf->output_vector));
2794
2795   if (n < 0 && errno != EAGAIN)
2796     {
2797       if (errno == EPIPE)
2798         {
2799           /* connection closed on us */
2800           unix_main_t *um = &unix_main;
2801           cf->has_epipe = 1;
2802           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           return clib_error_return_unix (0, "write");
2809         }
2810     }
2811
2812   else if (n > 0)
2813     unix_cli_del_pending_output (uf, cf, n);
2814
2815   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 unix_cli_read_ready (clib_file_t * uf)
2821 {
2822   unix_main_t *um = &unix_main;
2823   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   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
2829
2830   n = n_try = 4096;
2831   while (n == n_try)
2832     {
2833       l = vec_len (cf->input_vector);
2834       vec_resize (cf->input_vector, l + n_try);
2835
2836       n = read (uf->file_descriptor, cf->input_vector + l, n_try);
2837
2838       /* Error? */
2839       if (n < 0 && errno != EAGAIN)
2840         return clib_error_return_unix (0, "read");
2841
2842       n_read = n < 0 ? 0 : n;
2843       vec_set_len (cf->input_vector, l + n_read);
2844     }
2845
2846   if (!(n < 0))
2847     vlib_process_signal_event (um->vlib_main,
2848                                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   return /* no error */ 0;
2855 }
2856
2857 /** Called when a CLI session file descriptor has an error condition. */
2858 static clib_error_t *
2859 unix_cli_error_detected (clib_file_t * uf)
2860 {
2861   unix_main_t *um = &unix_main;
2862   unix_cli_main_t *cm = &unix_cli_main;
2863   unix_cli_file_t *cf;
2864
2865   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
2866   cf->has_epipe = 1;            /* prevent writes while the close is pending */
2867   vlib_process_signal_event (um->vlib_main,
2868                              cf->process_node_index,
2869                              UNIX_CLI_PROCESS_EVENT_QUIT,
2870                              /* event data */ uf->private_data);
2871
2872   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 unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
2882 {
2883   unix_main_t *um = &unix_main;
2884   clib_file_main_t *fm = &file_main;
2885   unix_cli_file_t *cf;
2886   clib_file_t template = { 0 };
2887   vlib_main_t *vm = um->vlib_main;
2888   vlib_node_t *n = 0;
2889
2890   if (vec_len (cm->unused_cli_process_node_indices) > 0)
2891     {
2892       n = vlib_get_node (vm, vec_pop (cm->unused_cli_process_node_indices));
2893
2894       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       vlib_worker_thread_barrier_sync (vm);
2906
2907       vlib_register_node (vm, &r, "unix-cli-process-%u", count++);
2908
2909       n = vlib_get_node (vm, r.index);
2910       vlib_worker_thread_node_runtime_update ();
2911       vlib_worker_thread_barrier_release (vm);
2912     }
2913
2914   pool_get_zero (cm->cli_file_pool, cf);
2915   clib_macro_init (&cf->macro_main);
2916
2917   template.read_function = unix_cli_read_ready;
2918   template.write_function = unix_cli_write_ready;
2919   template.error_function = unix_cli_error_detected;
2920   template.file_descriptor = fd;
2921   template.private_data = cf - cm->cli_file_pool;
2922   template.description = format (0, "%s", name);
2923
2924   cf->name = format (0, "unix-cli-%s", name);
2925   cf->process_node_index = n->index;
2926   cf->clib_file_index = clib_file_add (fm, &template);
2927   cf->output_vector = 0;
2928   cf->input_vector = 0;
2929   vec_validate (cf->current_command, 0);
2930   vec_set_len (cf->current_command, 0);
2931
2932   vlib_start_process (vm, n->runtime_index);
2933
2934   vlib_process_t *p = vlib_get_process_from_node (vm, n);
2935   p->output_function = unix_vlib_cli_output;
2936   p->output_function_arg = cf - cm->cli_file_pool;
2937
2938   return cf - cm->cli_file_pool;
2939 }
2940
2941 /** Telnet listening socket has a new connection. */
2942 static clib_error_t *
2943 unix_cli_listen_read_ready (clib_file_t * uf)
2944 {
2945   unix_main_t *um = &unix_main;
2946   clib_file_main_t *fm = &file_main;
2947   unix_cli_main_t *cm = &unix_cli_main;
2948   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   error = clib_socket_accept (s, &client);
2957   if (error)
2958     return error;
2959
2960   /* Disable Nagle, ignore any errors doing so eg on PF_LOCAL socket */
2961   one = 1;
2962   (void) setsockopt (client.fd, IPPROTO_TCP, TCP_NODELAY,
2963                      (void *) &one, sizeof (one));
2964
2965   client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
2966
2967   cf_index = unix_cli_file_add (cm, client_name, client.fd);
2968   cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2969   cf->is_socket = 1;
2970
2971   /* No longer need CLIB version of socket. */
2972   clib_socket_free (&client);
2973   vec_free (client_name);
2974
2975   /* if we're supposed to run telnet session in character mode (default) */
2976   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       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       cf->history_limit = um->cli_history_limit;
2997       cf->has_history = cf->history_limit != 0;
2998
2999       /* This is an interactive session until we decide otherwise */
3000       cf->is_interactive = 1;
3001
3002       /* Make sure this session is in line mode */
3003       cf->line_mode = 0;
3004
3005       /* We need CRLF */
3006       cf->crlf_mode = 1;
3007
3008       /* Setup the pager */
3009       cf->no_pager = um->cli_no_pager;
3010
3011       /* Default terminal dimensions, should the terminal
3012        * fail to provide any.
3013        */
3014       cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
3015       cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
3016
3017       /* Send the telnet options */
3018       uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3019       unix_vlib_cli_output_raw (cf, uf, charmode_option,
3020                                 ARRAY_LEN (charmode_option));
3021
3022       if (cm->new_session_process_node_index == ~0)
3023         {
3024           /* Create thw new session deadline process */
3025           cm->new_session_process_node_index =
3026             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       vlib_process_signal_event (um->vlib_main,
3035                                  cm->new_session_process_node_index,
3036                                  UNIX_CLI_NEW_SESSION_EVENT_ADD, cf_index);
3037
3038     }
3039
3040   return error;
3041 }
3042
3043 /** The system terminal has informed us that the window size
3044  * has changed.
3045  */
3046 static void
3047 unix_cli_resize_interrupt (int signum)
3048 {
3049   clib_file_main_t *fm = &file_main;
3050   unix_cli_main_t *cm = &unix_cli_main;
3051   unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool,
3052                                            cm->stdin_cli_file_index);
3053   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   if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
3059     {
3060       /* "Should never happen..." */
3061       clib_unix_warning ("TIOCGWINSZ");
3062       /* We can't trust ws.XXX... */
3063       return;
3064     }
3065
3066   cf->width = ws.ws_col;
3067   if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH)
3068     cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH;
3069   if (cf->width == 0)
3070     cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
3071
3072   cf->height = ws.ws_row;
3073   if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT)
3074     cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT;
3075   if (cf->height == 0)
3076     cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
3077
3078   /* Reindex the pager buffer */
3079   unix_cli_pager_reindex (cf);
3080
3081   /* Redraw the page */
3082   unix_cli_pager_redraw (cf, uf);
3083 }
3084
3085 /** Handle configuration directives in the @em unix section. */
3086 static clib_error_t *
3087 unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
3088 {
3089   unix_main_t *um = &unix_main;
3090   clib_file_main_t *fm = &file_main;
3091   unix_cli_main_t *cm = &unix_cli_main;
3092   int flags;
3093   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   if ((error = vlib_call_config_function (vm, unix_config)))
3103     return error;
3104
3105   if (um->flags & UNIX_FLAG_INTERACTIVE)
3106     {
3107       /* Set stdin to be non-blocking. */
3108       if ((flags = fcntl (STDIN_FILENO, F_GETFL, 0)) < 0)
3109         flags = 0;
3110       (void) fcntl (STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
3111
3112       cf_index = unix_cli_file_add (cm, "stdin", STDIN_FILENO);
3113       cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
3114       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       if (isatty (STDIN_FILENO) && um->cli_line_mode == 0)
3119         {
3120           /* Capture terminal resize events */
3121           clib_memset (&sa, 0, sizeof (sa));
3122           sa.sa_handler = unix_cli_resize_interrupt;
3123           if (sigaction (SIGWINCH, &sa, 0) < 0)
3124             clib_panic ("sigaction");
3125
3126           /* Retrieve the current terminal size */
3127           if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
3128             {
3129               cf->width = ws.ws_col;
3130               cf->height = ws.ws_row;
3131             }
3132
3133           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               cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
3140               cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
3141             }
3142
3143           /* Setup the history */
3144           cf->history_limit = um->cli_history_limit;
3145           cf->has_history = cf->history_limit != 0;
3146
3147           /* Setup the pager */
3148           cf->no_pager = um->cli_no_pager;
3149
3150           /* This is an interactive session until we decide otherwise */
3151           cf->is_interactive = 1;
3152
3153           /* We're going to be in char by char mode */
3154           cf->line_mode = 0;
3155
3156           /* Save the original tty state so we can restore it later */
3157           tcgetattr (STDIN_FILENO, &um->tio_stdin);
3158           um->tio_isset = 1;
3159
3160           /* Tweak the tty settings */
3161           tio = um->tio_stdin;
3162           /* echo off, canonical mode off, ext'd input processing off */
3163           tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
3164           /* disable XON/XOFF, so ^S invokes the history search */
3165           tio.c_iflag &= ~(IXON | IXOFF);
3166           tio.c_cc[VMIN] = 1;   /* 1 byte at a time */
3167           tio.c_cc[VTIME] = 0;  /* no timer */
3168           tio.c_cc[VSTOP] = _POSIX_VDISABLE;    /* not ^S */
3169           tio.c_cc[VSTART] = _POSIX_VDISABLE;   /* not ^Q */
3170           tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio);
3171
3172           /* See if we can do ANSI/VT100 output */
3173           term = (u8 *) getenv ("TERM");
3174           if (term != NULL)
3175             {
3176               int len = strlen ((char *) term);
3177               cf->ansi_capable = unix_cli_terminal_type_ansi (term, len);
3178               if (unix_cli_terminal_type_noninteractive (term, len))
3179                 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           unix_cli_set_session_noninteractive (cf);
3186         }
3187
3188       /* Send banner and initial prompt */
3189       unix_cli_file_welcome (cm, cf);
3190     }
3191
3192   /* If we have socket config, LISTEN, otherwise, don't */
3193   clib_socket_t *s = &um->cli_listen_socket;
3194   if (s->config && s->config[0] != 0)
3195     {
3196       /* CLI listen. */
3197       clib_file_t template = { 0 };
3198
3199       /* mkdir of file socketu, only under /run  */
3200       if (strncmp (s->config, "/run", 4) == 0)
3201         {
3202           u8 *tmp = format (0, "%s", s->config);
3203           int i = vec_len (tmp);
3204           while (i && tmp[--i] != '/')
3205             ;
3206
3207           tmp[i] = '\0';
3208
3209           if (i)
3210             vlib_unix_recursive_mkdir ((char *) tmp);
3211           vec_free (tmp);
3212         }
3213
3214       s->flags = CLIB_SOCKET_F_IS_SERVER |      /* listen, don't connect */
3215         CLIB_SOCKET_F_ALLOW_GROUP_WRITE;        /* PF_LOCAL socket only */
3216       error = clib_socket_init (s);
3217
3218       if (error)
3219         return error;
3220
3221       template.read_function = unix_cli_listen_read_ready;
3222       template.file_descriptor = s->fd;
3223       template.description = format (0, "cli listener %s", s->config);
3224
3225       clib_file_add (fm, &template);
3226     }
3227
3228   /* Set CLI prompt. */
3229   if (!cm->cli_prompt)
3230     cm->cli_prompt = format (0, "VLIB: ");
3231
3232   return 0;
3233 }
3234
3235 /*?
3236  * This module has no configurable parameters.
3237 ?*/
3238 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 unix_cli_exit (vlib_main_t * vm)
3245 {
3246   unix_main_t *um = &unix_main;
3247
3248   /* If stdin is a tty and we saved the tty state, reset the tty state */
3249   if (isatty (STDIN_FILENO) && um->tio_isset)
3250     tcsetattr (STDIN_FILENO, TCSAFLUSH, &um->tio_stdin);
3251
3252   return 0;
3253 }
3254
3255 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 vlib_unix_cli_set_prompt (char *prompt)
3264 {
3265   char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s ";
3266   unix_cli_main_t *cm = &unix_cli_main;
3267   if (cm->cli_prompt)
3268     vec_free (cm->cli_prompt);
3269   cm->cli_prompt = format (0, fmt, prompt);
3270 }
3271
3272 static unix_cli_file_t *
3273 unix_cli_file_if_exists (unix_cli_main_t * cm)
3274 {
3275   if (!cm->cli_file_pool)
3276     return 0;
3277   return pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
3278 }
3279
3280 static unix_cli_file_t *
3281 unix_cli_file_if_interactive (unix_cli_main_t * cm)
3282 {
3283   unix_cli_file_t *cf;
3284   if ((cf = unix_cli_file_if_exists (cm)) && !cf->is_interactive)
3285     return 0;
3286   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 unix_cli_quit (vlib_main_t * vm,
3295                unformat_input_t * input, vlib_cli_command_t * cmd)
3296 {
3297   unix_cli_main_t *cm = &unix_cli_main;
3298   unix_cli_file_t *cf;
3299
3300   if (!(cf = unix_cli_file_if_exists (cm)))
3301     return clib_error_return (0, "invalid session");
3302
3303   /* Cosmetic: suppress the final prompt from appearing before we die */
3304   cf->is_interactive = 0;
3305   cf->started = 1;
3306
3307   vlib_process_signal_event (vm,
3308                              vlib_current_process (vm),
3309                              UNIX_CLI_PROCESS_EVENT_QUIT,
3310                              cm->current_input_file_index);
3311   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 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
3321   .path = "quit",
3322   .short_help = "Exit CLI",
3323   .function = unix_cli_quit,
3324 };
3325
3326 VLIB_CLI_COMMAND (unix_cli_q_command, static) = {
3327   .path = "q",
3328   .short_help = "Exit CLI",
3329   .function = unix_cli_quit,
3330 };
3331
3332 /** CLI command to execute a VPP command script. */
3333 static clib_error_t *
3334 unix_cli_exec (vlib_main_t * vm,
3335                unformat_input_t * input, vlib_cli_command_t * cmd)
3336 {
3337   char *file_name;
3338   int fd, rv = 0;
3339   unformat_input_t sub_input, in;
3340   clib_error_t *error;
3341   clib_macro_main_t *mm = 0;
3342   unix_cli_main_t *cm = &unix_cli_main;
3343   unix_cli_file_t *cf;
3344   u8 *file_data = 0;
3345   file_name = 0;
3346   fd = -1;
3347   error = 0;
3348   struct stat s;
3349
3350
3351   if (!unformat (input, "%s", &file_name))
3352     {
3353       error = clib_error_return (0, "expecting file name, got `%U'",
3354                                  format_unformat_error, input);
3355       goto done;
3356     }
3357
3358   fd = open (file_name, O_RDONLY);
3359   if (fd < 0)
3360     {
3361       error = clib_error_return_unix (0, "failed to open `%s'", file_name);
3362       goto done;
3363     }
3364
3365   /* Make sure its a regular file. */
3366   if (fstat (fd, &s) < 0)
3367     {
3368       error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
3369       goto done;
3370     }
3371
3372   if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
3373     {
3374       error = clib_error_return (0, "not a regular file `%s'", file_name);
3375       goto done;
3376     }
3377
3378   if (s.st_size < 1)
3379     {
3380       error = clib_error_return (0, "empty file `%s'", file_name);
3381       goto done;
3382     }
3383
3384   /* Read the file */
3385   vec_validate (file_data, s.st_size - 1);
3386
3387   if (read (fd, file_data, s.st_size) != s.st_size)
3388     {
3389       error = clib_error_return_unix (0, "Failed to read %d bytes from '%s'",
3390                                       s.st_size, file_name);
3391       vec_free (file_data);
3392       goto done;
3393     }
3394
3395   unformat_init_vector (&sub_input, file_data);
3396
3397   /* Initial config process? Use the global macro table. */
3398   if (pool_is_free_index (cm->cli_file_pool, cm->current_input_file_index))
3399     mm = &cm->macro_main;
3400   else
3401     {
3402       /* Otherwise, use the per-cli-process macro table */
3403       cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
3404       mm = &cf->macro_main;
3405     }
3406
3407   while (rv == 0 && unformat_user (&sub_input, unformat_vlib_cli_line, &in))
3408     {
3409       /* Run the file contents through the macro processor */
3410       if (vec_len (in.buffer) > 1)
3411         {
3412           u8 *expanded;
3413
3414           /* The macro expander expects a c string... */
3415           vec_add1 (in.buffer, 0);
3416
3417           expanded =
3418             (u8 *) clib_macro_eval (mm, (i8 *) in.buffer, 1 /* complain */,
3419                                     0 /* level */, 8 /* max_level */);
3420           /* Macro processor NULL terminates the return */
3421           vec_dec_len (expanded, 1);
3422           vec_reset_length (in.buffer);
3423           vec_append (in.buffer, expanded);
3424           vec_free (expanded);
3425         }
3426
3427       if ((rv = vlib_cli_input (vm, &in, 0, 0)) != 0)
3428         error = clib_error_return (0, "CLI line error: %U",
3429                                    format_unformat_error, &in);
3430       unformat_free (&in);
3431     }
3432   unformat_free (&sub_input);
3433
3434 done:
3435   if (fd >= 0)
3436     close (fd);
3437   vec_free (file_name);
3438
3439   return error;
3440 }
3441
3442 /*?
3443  * Executes a sequence of CLI commands which are read from a file. If
3444  * a command is unrecognized or otherwise invalid then the usual CLI
3445  * feedback will be generated, however execution of subsequent commands
3446  * from the file will continue.
3447  *
3448  * The VPP code is indifferent to the file location. However, if SELinux
3449  * is enabled, then the file needs to have an SELinux label the VPP
3450  * process is allowed to access. For example, if a file is created in
3451  * '<em>/usr/share/vpp/</em>', it will be allowed. However, files manually
3452  * created in '/tmp/' or '/home/<user>/' will not be accessible by the VPP
3453  * process when SELinux is enabled.
3454  *
3455  * @cliexpar
3456  * Sample file:
3457  * @clistart
3458  * <b><em>$ cat /usr/share/vpp/scripts/gigup.txt</em></b>
3459  * set interface state GigabitEthernet0/8/0 up
3460  * set interface state GigabitEthernet0/9/0 up
3461  * @cliend
3462  * Example of how to execute a set of CLI commands from a file:
3463  * @cliexcmd{exec /usr/share/vpp/scripts/gigup.txt}
3464 ?*/
3465 VLIB_CLI_COMMAND (cli_exec, static) = {
3466   .path = "exec",
3467   .short_help = "exec <filename>",
3468   .function = unix_cli_exec,
3469   .is_mp_safe = 1,
3470 };
3471
3472 /** CLI command to show various unix error statistics. */
3473 static clib_error_t *
3474 unix_show_errors (vlib_main_t * vm,
3475                   unformat_input_t * input, vlib_cli_command_t * cmd)
3476 {
3477   unix_main_t *um = &unix_main;
3478   clib_error_t *error = 0;
3479   int i, n_errors_to_show;
3480   unix_error_history_t *unix_errors = 0;
3481
3482   n_errors_to_show = 1 << 30;
3483
3484   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3485     {
3486       if (!unformat (input, "%d", &n_errors_to_show))
3487         {
3488           error =
3489             clib_error_return (0,
3490                                "expecting integer number of errors to show, got `%U'",
3491                                format_unformat_error, input);
3492           goto done;
3493         }
3494     }
3495
3496   n_errors_to_show =
3497     clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
3498
3499   i =
3500     um->error_history_index >
3501     0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
3502
3503   while (n_errors_to_show > 0)
3504     {
3505       unix_error_history_t *eh = um->error_history + i;
3506
3507       if (!eh->error)
3508         break;
3509
3510       vec_add1 (unix_errors, eh[0]);
3511       n_errors_to_show -= 1;
3512       if (i == 0)
3513         i = ARRAY_LEN (um->error_history) - 1;
3514       else
3515         i--;
3516     }
3517
3518   if (vec_len (unix_errors) == 0)
3519     vlib_cli_output (vm, "no Unix errors so far");
3520   else
3521     {
3522       vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
3523       for (i = vec_len (unix_errors) - 1; i >= 0; i--)
3524         {
3525           unix_error_history_t *eh = vec_elt_at_index (unix_errors, i);
3526           vlib_cli_output (vm, "%U: %U",
3527                            format_time_interval, "h:m:s:u", eh->time,
3528                            format_clib_error, eh->error);
3529         }
3530       vlib_cli_output (vm, "%U: time now",
3531                        format_time_interval, "h:m:s:u", vlib_time_now (vm));
3532     }
3533
3534 done:
3535   vec_free (unix_errors);
3536   return error;
3537 }
3538
3539 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
3540   .path = "show unix errors",
3541   .short_help = "Show Unix system call error history",
3542   .function = unix_show_errors,
3543 };
3544
3545 /** CLI command to show various unix error statistics. */
3546 static clib_error_t *
3547 unix_show_files (vlib_main_t * vm,
3548                  unformat_input_t * input, vlib_cli_command_t * cmd)
3549 {
3550   clib_error_t *error = 0;
3551   clib_file_main_t *fm = &file_main;
3552   clib_file_t *f;
3553   char path[PATH_MAX];
3554   u8 *s = 0;
3555
3556   vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread",
3557                    "Read", "Write", "Error", "File Name", "Description");
3558
3559   pool_foreach (f, fm->file_pool)
3560    {
3561       int rv;
3562       s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0);
3563       rv = readlink((char *) s, path, PATH_MAX - 1);
3564
3565       path[rv < 0 ? 0 : rv] = 0;
3566
3567       vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v",
3568                        f->file_descriptor, f->polling_thread_index,
3569                        f->read_events, f->write_events, f->error_events,
3570                        path, f->description);
3571       vec_reset_length (s);
3572     }
3573   vec_free (s);
3574
3575   return error;
3576 }
3577
3578 VLIB_CLI_COMMAND (cli_unix_show_files, static) = {
3579   .path = "show unix files",
3580   .short_help = "Show Unix files in use",
3581   .function = unix_show_files,
3582 };
3583
3584 /** CLI command to show session command history. */
3585 static clib_error_t *
3586 unix_cli_show_history (vlib_main_t * vm,
3587                        unformat_input_t * input, vlib_cli_command_t * cmd)
3588 {
3589   unix_cli_main_t *cm = &unix_cli_main;
3590   unix_cli_file_t *cf;
3591   int i, j;
3592
3593   if (!(cf = unix_cli_file_if_interactive (cm)))
3594     return clib_error_return (0, "invalid for non-interactive sessions");
3595
3596   if (cf->has_history && cf->history_limit)
3597     {
3598       i = 1 + cf->command_number - vec_len (cf->command_history);
3599       for (j = 0; j < vec_len (cf->command_history); j++)
3600         vlib_cli_output (vm, "%d  %v\n", i + j, cf->command_history[j]);
3601     }
3602   else
3603     {
3604       vlib_cli_output (vm, "History not enabled.\n");
3605     }
3606
3607   return 0;
3608 }
3609
3610 /*?
3611  * Displays the command history for the current session, if any.
3612 ?*/
3613 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
3614   .path = "history",
3615   .short_help = "Show current session command history",
3616   .function = unix_cli_show_history,
3617 };
3618
3619 /** CLI command to show terminal status. */
3620 static clib_error_t *
3621 unix_cli_show_terminal (vlib_main_t * vm,
3622                         unformat_input_t * input, vlib_cli_command_t * cmd)
3623 {
3624   unix_main_t *um = &unix_main;
3625   unix_cli_main_t *cm = &unix_cli_main;
3626   unix_cli_file_t *cf;
3627   vlib_node_t *n;
3628
3629   if (!(cf = unix_cli_file_if_exists (cm)))
3630     return clib_error_return (0, "invalid session");
3631
3632   n = vlib_get_node (vm, cf->process_node_index);
3633
3634   vlib_cli_output (vm, "Terminal name:   %v\n", cf->name);
3635   vlib_cli_output (vm, "Terminal node:   %v\n", n->name);
3636   vlib_cli_output (vm, "Terminal mode:   %s\n", cf->line_mode ?
3637                    "line-by-line" : "char-by-char");
3638   vlib_cli_output (vm, "Terminal width:  %d\n", cf->width);
3639   vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
3640   vlib_cli_output (vm, "ANSI capable:    %s\n",
3641                    cf->ansi_capable ? "yes" : "no");
3642   vlib_cli_output (vm, "Interactive:     %s\n",
3643                    cf->is_interactive ? "yes" : "no");
3644   vlib_cli_output (vm, "History enabled: %s%s\n",
3645                    cf->has_history ? "yes" : "no", !cf->has_history
3646                    || cf->history_limit ? "" :
3647                    " (disabled by history limit)");
3648   if (cf->has_history)
3649     vlib_cli_output (vm, "History limit:   %d\n", cf->history_limit);
3650   vlib_cli_output (vm, "Pager enabled:   %s%s%s\n",
3651                    cf->no_pager ? "no" : "yes",
3652                    cf->no_pager
3653                    || cf->height ? "" : " (disabled by terminal height)",
3654                    cf->no_pager
3655                    || um->cli_pager_buffer_limit ? "" :
3656                    " (disabled by buffer limit)");
3657   if (!cf->no_pager)
3658     vlib_cli_output (vm, "Pager limit:     %d\n", um->cli_pager_buffer_limit);
3659   vlib_cli_output (vm, "CRLF mode:       %s\n",
3660                    cf->crlf_mode ? "CR+LF" : "LF");
3661
3662   return 0;
3663 }
3664
3665 /*?
3666  * Displays various information about the state of the current terminal
3667  * session.
3668  *
3669  * @cliexpar
3670  * @cliexstart{show terminal}
3671  * Terminal name:   unix-cli-stdin
3672  * Terminal mode:   char-by-char
3673  * Terminal width:  123
3674  * Terminal height: 48
3675  * ANSI capable:    yes
3676  * Interactive:     yes
3677  * History enabled: yes
3678  * History limit:   50
3679  * Pager enabled:   yes
3680  * Pager limit:     100000
3681  * CRLF mode:       LF
3682  * @cliexend
3683 ?*/
3684 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
3685   .path = "show terminal",
3686   .short_help = "Show current session terminal settings",
3687   .function = unix_cli_show_terminal,
3688 };
3689
3690 /** CLI command to display a list of CLI sessions. */
3691 static clib_error_t *
3692 unix_cli_show_cli_sessions (vlib_main_t * vm,
3693                             unformat_input_t * input,
3694                             vlib_cli_command_t * cmd)
3695 {
3696   unix_cli_main_t *cm = &unix_cli_main;
3697   clib_file_main_t *fm = &file_main;
3698   table_t table = {}, *t = &table;
3699   unix_cli_file_t *cf;
3700   clib_file_t *uf;
3701
3702   table_add_header_col (t, 4, "PNI  ", "FD   ", "Name", "Flags");
3703
3704 #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) )
3705   int i = 0;
3706   pool_foreach (cf, cm->cli_file_pool)
3707     {
3708       int j = 0;
3709
3710       uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3711       table_format_cell (t, i, j++, "%u", cf->process_node_index);
3712       table_format_cell (t, i, j++, "%u", uf->file_descriptor);
3713       table_format_cell (t, i, j++, "%v", cf->name);
3714       table_format_cell (t, i++, j++, "%c%c%c%c%c",
3715                          fl (cf->is_interactive, 'i'), fl (cf->is_socket, 's'),
3716                          fl (cf->line_mode, 'l'), fl (cf->has_epipe, 'p'),
3717                          fl (cf->ansi_capable, 'a'));
3718     }
3719 #undef fl
3720
3721   t->default_body.align = TTAA_LEFT;
3722   t->default_header_col.align = TTAA_LEFT;
3723   vlib_cli_output (vm, "%U", format_table, t);
3724   table_free (t);
3725
3726   return 0;
3727 }
3728
3729 /*?
3730  * Displays a summary of all the current CLI sessions.
3731  *
3732  * Typically used to diagnose connection issues with the CLI
3733  * socket.
3734  *
3735  * @cliexpar
3736  * @cliexstart{show cli-sessions}
3737  * PNI   FD    Name                 Flags
3738  * 343   0     unix-cli-stdin       IslpA
3739  * 344   7     unix-cli-local:20    ISlpA
3740  * 346   8     unix-cli-local:21    iSLpa
3741  * @cliexend
3742
3743  * In this example we have the debug console of the running process
3744  * on stdin/out, we have an interactive socket session and we also
3745  * have a non-interactive socket session.
3746  *
3747  * Fields:
3748  *
3749  * - @em PNI: Process node index.
3750  * - @em FD: Unix file descriptor.
3751  * - @em Name: Name of the session.
3752  * - @em Flags: Various flags that describe the state of the session.
3753  *
3754  * @em Flags have the following meanings; lower-case typically negates
3755  * upper-case:
3756  *
3757  * - @em I Interactive session.
3758  * - @em S Connected by socket.
3759  * - @em s Not a socket, likely stdin.
3760  * - @em L Line-by-line mode.
3761  * - @em l Char-by-char mode.
3762  * - @em P EPIPE detected on connection; it will close soon.
3763  * - @em A ANSI-capable terminal.
3764 ?*/
3765 VLIB_CLI_COMMAND (cli_unix_cli_show_cli_sessions, static) = {
3766   .path = "show cli-sessions",
3767   .short_help = "Show current CLI sessions",
3768   .function = unix_cli_show_cli_sessions,
3769 };
3770
3771 /** CLI command to set terminal pager settings. */
3772 static clib_error_t *
3773 unix_cli_set_terminal_pager (vlib_main_t * vm,
3774                              unformat_input_t * input,
3775                              vlib_cli_command_t * cmd)
3776 {
3777   unix_main_t *um = &unix_main;
3778   unix_cli_main_t *cm = &unix_cli_main;
3779   unix_cli_file_t *cf;
3780   unformat_input_t _line_input, *line_input = &_line_input;
3781   clib_error_t *error = 0;
3782
3783   if (!(cf = unix_cli_file_if_interactive (cm)))
3784     return clib_error_return (0, "invalid for non-interactive sessions");
3785
3786   if (!unformat_user (input, unformat_line_input, line_input))
3787     return 0;
3788
3789   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3790     {
3791       if (unformat (line_input, "on"))
3792         cf->no_pager = 0;
3793       else if (unformat (line_input, "off"))
3794         cf->no_pager = 1;
3795       else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
3796         vlib_cli_output (vm,
3797                          "Pager limit set to %u lines; note, this is global.\n",
3798                          um->cli_pager_buffer_limit);
3799       else
3800         {
3801           error = clib_error_return (0, "unknown parameter: `%U`",
3802                                      format_unformat_error, line_input);
3803           goto done;
3804         }
3805     }
3806
3807 done:
3808   unformat_free (line_input);
3809
3810   return error;
3811 }
3812
3813 /*?
3814  * Enables or disables the terminal pager for this session. Generally
3815  * this defaults to enabled.
3816  *
3817  * Additionally allows the pager buffer size to be set; though note that
3818  * this value is set globally and not per session.
3819 ?*/
3820 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
3821   .path = "set terminal pager",
3822   .short_help = "set terminal pager [on|off] [limit <lines>]",
3823   .function = unix_cli_set_terminal_pager,
3824 };
3825
3826 /** CLI command to set terminal history settings. */
3827 static clib_error_t *
3828 unix_cli_set_terminal_history (vlib_main_t * vm,
3829                                unformat_input_t * input,
3830                                vlib_cli_command_t * cmd)
3831 {
3832   unix_cli_main_t *cm = &unix_cli_main;
3833   unix_cli_file_t *cf;
3834   unformat_input_t _line_input, *line_input = &_line_input;
3835   u32 limit;
3836   clib_error_t *error = 0;
3837
3838   if (!(cf = unix_cli_file_if_interactive (cm)))
3839     return clib_error_return (0, "invalid for non-interactive sessions");
3840
3841   if (!unformat_user (input, unformat_line_input, line_input))
3842     return 0;
3843
3844   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3845     {
3846       if (unformat (line_input, "on"))
3847         cf->has_history = 1;
3848       else if (unformat (line_input, "off"))
3849         cf->has_history = 0;
3850       else if (unformat (line_input, "limit %u", &cf->history_limit))
3851         ;
3852       else
3853         {
3854           error = clib_error_return (0, "unknown parameter: `%U`",
3855                                      format_unformat_error, line_input);
3856           goto done;
3857         }
3858
3859     }
3860
3861   /* If we reduced history size, or turned it off, purge the history */
3862   limit = cf->has_history ? cf->history_limit : 0;
3863   if (limit < vec_len (cf->command_history))
3864     {
3865       u32 i;
3866
3867       /* How many items to remove from the start of history */
3868       limit = vec_len (cf->command_history) - limit;
3869
3870       for (i = 0; i < limit; i++)
3871         vec_free (cf->command_history[i]);
3872
3873       vec_delete (cf->command_history, limit, 0);
3874     }
3875
3876 done:
3877   unformat_free (line_input);
3878
3879   return error;
3880 }
3881
3882 /*?
3883  * Enables or disables the command history function of the current
3884  * terminal. Generally this defaults to enabled.
3885  *
3886  * This command also allows the maximum size of the history buffer for
3887  * this session to be altered.
3888 ?*/
3889 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
3890   .path = "set terminal history",
3891   .short_help = "set terminal history [on|off] [limit <lines>]",
3892   .function = unix_cli_set_terminal_history,
3893 };
3894
3895 /** CLI command to set terminal ANSI settings. */
3896 static clib_error_t *
3897 unix_cli_set_terminal_ansi (vlib_main_t * vm,
3898                             unformat_input_t * input,
3899                             vlib_cli_command_t * cmd)
3900 {
3901   unix_cli_main_t *cm = &unix_cli_main;
3902   unix_cli_file_t *cf;
3903
3904   if (!(cf = unix_cli_file_if_interactive (cm)))
3905     return clib_error_return (0, "invalid for non-interactive sessions");
3906
3907   if (unformat (input, "on"))
3908     cf->ansi_capable = 1;
3909   else if (unformat (input, "off"))
3910     cf->ansi_capable = 0;
3911   else
3912     return clib_error_return (0, "unknown parameter: `%U`",
3913                               format_unformat_error, input);
3914
3915   return 0;
3916 }
3917
3918 /*?
3919  * Enables or disables the use of ANSI control sequences by this terminal.
3920  * The default will vary based on terminal detection at the start of the
3921  * session.
3922  *
3923  * ANSI control sequences are used in a small number of places to provide,
3924  * for example, color text output and to control the cursor in the pager.
3925 ?*/
3926 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
3927   .path = "set terminal ansi",
3928   .short_help = "set terminal ansi [on|off]",
3929   .function = unix_cli_set_terminal_ansi,
3930 };
3931
3932
3933 #define MAX_CLI_WAIT 86400
3934 /** CLI command to wait <sec> seconds. Useful for exec script. */
3935 static clib_error_t *
3936 unix_wait_cmd (vlib_main_t * vm,
3937                unformat_input_t * input, vlib_cli_command_t * cmd)
3938 {
3939   unformat_input_t _line_input, *line_input = &_line_input;
3940   f64 sec = 1.0;
3941
3942   if (!unformat_user (input, unformat_line_input, line_input))
3943     return 0;
3944
3945   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3946     {
3947       if (unformat (line_input, "%f", &sec))
3948         ;
3949       else
3950         return clib_error_return (0, "unknown parameter: `%U`",
3951                                   format_unformat_error, input);
3952     }
3953
3954   if (sec <= 0 || sec > MAX_CLI_WAIT || floor (sec * 1000) / 1000 != sec)
3955     return clib_error_return (0,
3956                               "<sec> must be a positive value and less than 86400 (one day) with no more than msec precision.");
3957
3958   vlib_process_wait_for_event_or_clock (vm, sec);
3959   vlib_cli_output (vm, "waited %.3f sec.", sec);
3960
3961   unformat_free (line_input);
3962   return 0;
3963 }
3964 VLIB_CLI_COMMAND (cli_unix_wait_cmd, static) = {
3965   .path = "wait",
3966   .short_help = "wait <sec>",
3967   .function = unix_wait_cmd,
3968 };
3969
3970 static clib_error_t *
3971 echo_cmd (vlib_main_t * vm,
3972           unformat_input_t * input, vlib_cli_command_t * cmd)
3973 {
3974   unformat_input_t _line_input, *line_input = &_line_input;
3975
3976   /* Get a line of input. */
3977   if (!unformat_user (input, unformat_line_input, line_input))
3978     {
3979       vlib_cli_output (vm, "");
3980       return 0;
3981     }
3982
3983   vlib_cli_output (vm, "%v", line_input->buffer);
3984
3985   unformat_free (line_input);
3986   return 0;
3987 }
3988
3989 VLIB_CLI_COMMAND (cli_unix_echo_cmd, static) = {
3990   .path = "echo",
3991   .short_help = "echo <rest-of-line>",
3992   .function = echo_cmd,
3993 };
3994
3995 static clib_error_t *
3996 define_cmd_fn (vlib_main_t * vm,
3997                unformat_input_t * input, vlib_cli_command_t * cmd)
3998 {
3999   u8 *macro_name;
4000   unformat_input_t _line_input, *line_input = &_line_input;
4001   clib_macro_main_t *mm = get_macro_main ();
4002   clib_error_t *error;
4003
4004   if (!unformat (input, "%s", &macro_name))
4005     return clib_error_return (0, "missing variable name...");
4006
4007   /* Remove white space */
4008   (void) unformat (input, "");
4009
4010   /* Get a line of input. */
4011   if (!unformat_user (input, unformat_line_input, line_input))
4012     {
4013       error = clib_error_return (0, "missing value for '%s'...", macro_name);
4014       vec_free (macro_name);
4015       return error;
4016     }
4017   /* the macro expander expects c-strings, not vectors... */
4018   vec_add1 (line_input->buffer, 0);
4019   clib_macro_set_value (mm, (char *) macro_name, (char *) line_input->buffer);
4020   vec_free (macro_name);
4021   unformat_free (line_input);
4022   return 0;
4023 }
4024
4025 VLIB_CLI_COMMAND (define_cmd, static) = {
4026   .path = "define",
4027   .short_help = "define <variable-name> <value>",
4028   .function = define_cmd_fn,
4029 };
4030
4031
4032 static clib_error_t *
4033 undefine_cmd_fn (vlib_main_t * vm,
4034                  unformat_input_t * input, vlib_cli_command_t * cmd)
4035 {
4036   u8 *macro_name;
4037   clib_macro_main_t *mm = get_macro_main ();
4038
4039   if (!unformat (input, "%s", &macro_name))
4040     return clib_error_return (0, "missing variable name...");
4041
4042   if (clib_macro_unset (mm, (char *) macro_name))
4043     vlib_cli_output (vm, "%s wasn't set...", macro_name);
4044
4045   vec_free (macro_name);
4046   return 0;
4047 }
4048
4049 VLIB_CLI_COMMAND (undefine_cmd, static) = {
4050   .path = "undefine",
4051   .short_help = "undefine <variable-name>",
4052   .function = undefine_cmd_fn,
4053 };
4054
4055 static clib_error_t *
4056 show_macro_cmd_fn (vlib_main_t * vm,
4057                    unformat_input_t * input, vlib_cli_command_t * cmd)
4058 {
4059   clib_macro_main_t *mm = get_macro_main ();
4060   int evaluate = 1;
4061
4062   if (unformat (input, "noevaluate %=", &evaluate, 0))
4063     ;
4064   else if (unformat (input, "noeval %=", &evaluate, 0))
4065     ;
4066
4067   vlib_cli_output (vm, "%U", format_clib_macro_main, mm, evaluate);
4068   return 0;
4069 }
4070
4071 VLIB_CLI_COMMAND (show_macro, static) = {
4072   .path = "show macro",
4073   .short_help = "show macro [noevaluate]",
4074   .function = show_macro_cmd_fn,
4075 };
4076
4077 static clib_error_t *
4078 unix_cli_init (vlib_main_t * vm)
4079 {
4080   unix_cli_main_t *cm = &unix_cli_main;
4081
4082   /* Breadcrumb to indicate the new session process
4083    * has not been started */
4084   cm->new_session_process_node_index = ~0;
4085   clib_macro_init (&cm->macro_main);
4086
4087   return 0;
4088 }
4089
4090 VLIB_INIT_FUNCTION (unix_cli_init);
4091
4092 /*
4093  * fd.io coding-style-patch-verification: ON
4094  *
4095  * Local Variables:
4096  * eval: (c-set-style "gnu")
4097  * End:
4098  */