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