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