b0c950cbd8f1ebf63c154dca744406faa8c1a4bd
[vpp.git] / vlib / 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 #include <vlib/vlib.h>
41 #include <vlib/unix/unix.h>
42 #include <vppinfra/timer.h>
43
44 #include <ctype.h>
45 #include <fcntl.h>
46 #include <sys/stat.h>
47 #include <termios.h>
48 #include <signal.h>
49 #include <unistd.h>
50 #include <arpa/telnet.h>
51 #include <sys/ioctl.h>
52
53 /* ANSI Escape code. */
54 #define ESC "\x1b"
55
56 /* ANSI Control Sequence Introducer. */
57 #define CSI ESC "["
58
59 /* ANSI sequences. */
60 #define ANSI_CLEAR      CSI "2J" CSI "1;1H"
61 #define ANSI_RESET      CSI "0m"
62 #define ANSI_BOLD       CSI "1m"
63 #define ANSI_DIM        CSI "2m"
64 #define ANSI_DRED       ANSI_DIM CSI "31m"
65 #define ANSI_BRED       ANSI_BOLD CSI "31m"
66 #define ANSI_CLEAR      CSI "2J" CSI "1;1H"
67 #define ANSI_CLEARLINE  CSI "2K"
68 #define ANSI_SCROLLDN   CSI "1T"
69 #define ANSI_SAVECURSOR CSI "s"
70 #define ANSI_RESTCURSOR CSI "u"
71
72 /** Maximum depth into a byte stream from which to compile a Telnet
73  * protocol message. This is a saftey measure. */
74 #define UNIX_CLI_MAX_DEPTH_TELNET 24
75
76 /** Unix standard in */
77 #define UNIX_CLI_STDIN_FD 0
78
79
80 typedef struct {
81   u8 * line;
82   u32 length;
83 } unix_cli_banner_t;
84
85 #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 }
86 /** Plain welcome banner. */
87 static unix_cli_banner_t unix_cli_banner[] = {
88 _("    _______    _        _   _____  ___ \n"),
89 _(" __/ __/ _ \\  (_)__    | | / / _ \\/ _ \\\n"),
90 _(" _/ _// // / / / _ \\   | |/ / ___/ ___/\n"),
91 _(" /_/ /____(_)_/\\___/   |___/_/  /_/    \n"),
92 _("\n")
93 };
94 /** ANSI color welcome banner. */
95 static unix_cli_banner_t unix_cli_banner_color[] = {
96 _(ANSI_BRED "    _______    _     " ANSI_RESET "   _   _____  ___ \n"),
97 _(ANSI_BRED " __/ __/ _ \\  (_)__ " ANSI_RESET "   | | / / _ \\/ _ \\\n"),
98 _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET "   | |/ / ___/ ___/\n"),
99 _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET "   |___/_/  /_/    \n"),
100 _("\n")
101 };
102 #undef _
103
104
105 /** Unix CLI session. */
106 typedef struct {
107   u32 unix_file_index;
108
109   /* Vector of output pending write to file descriptor. */
110   u8 * output_vector;
111
112   /* Vector of input saved by Unix input node to be processed by
113      CLI process. */
114   u8 * input_vector;
115
116   u8 has_history;
117   u8 ** command_history;
118   u8 * current_command;
119   i32 excursion;
120
121   /* Maximum number of history entries this session will store. */
122   u32 history_limit;
123
124   /* Current command line counter */
125   u32 command_number;
126
127   u8 * search_key;
128   int search_mode;
129
130   /* Position of the insert cursor on the current input line */
131   u32 cursor;
132
133   /* Line mode or char mode */
134   u8 line_mode;
135
136   /* Set if the CRLF mode wants CR + LF */
137   u8 crlf_mode;
138
139   /* Can we do ANSI output? */
140   u8 ansi_capable;
141
142   /* Has the session started? */
143   u8 started;
144
145   /* Disable the pager? */
146   u8 no_pager;
147
148   /* Pager buffer */
149   u8 ** pager_vector;
150
151   /* Lines currently displayed */
152   u32 pager_lines;
153
154   /* Line number of top of page */
155   u32 pager_start;
156
157   /* Terminal size */
158   u32 width, height;
159
160   u32 process_node_index;
161 } unix_cli_file_t;
162
163 always_inline void
164 unix_cli_pager_reset (unix_cli_file_t *f)
165 {
166   u8 ** p;
167
168   f->pager_lines = f->pager_start = 0;
169   vec_foreach (p, f->pager_vector)
170     {
171       vec_free(*p);
172     }
173   vec_free(f->pager_vector);
174   f->pager_vector = 0;
175 }
176
177 always_inline void
178 unix_cli_file_free (unix_cli_file_t * f)
179 {
180   vec_free (f->output_vector);
181   vec_free (f->input_vector);
182   unix_cli_pager_reset(f);
183 }
184
185 /* CLI actions */
186 typedef enum {
187   UNIX_CLI_PARSE_ACTION_NOACTION = 0,
188   UNIX_CLI_PARSE_ACTION_CRLF,
189   UNIX_CLI_PARSE_ACTION_TAB,
190   UNIX_CLI_PARSE_ACTION_ERASE,
191   UNIX_CLI_PARSE_ACTION_ERASERIGHT,
192   UNIX_CLI_PARSE_ACTION_UP,
193   UNIX_CLI_PARSE_ACTION_DOWN,
194   UNIX_CLI_PARSE_ACTION_LEFT,
195   UNIX_CLI_PARSE_ACTION_RIGHT,
196   UNIX_CLI_PARSE_ACTION_HOME,
197   UNIX_CLI_PARSE_ACTION_END,
198   UNIX_CLI_PARSE_ACTION_WORDLEFT,
199   UNIX_CLI_PARSE_ACTION_WORDRIGHT,
200   UNIX_CLI_PARSE_ACTION_ERASELINELEFT,
201   UNIX_CLI_PARSE_ACTION_ERASELINERIGHT,
202   UNIX_CLI_PARSE_ACTION_CLEAR,
203   UNIX_CLI_PARSE_ACTION_REVSEARCH,
204   UNIX_CLI_PARSE_ACTION_FWDSEARCH,
205   UNIX_CLI_PARSE_ACTION_YANK,
206   UNIX_CLI_PARSE_ACTION_TELNETIAC,
207
208   UNIX_CLI_PARSE_ACTION_PAGER_CRLF,
209   UNIX_CLI_PARSE_ACTION_PAGER_QUIT,
210   UNIX_CLI_PARSE_ACTION_PAGER_NEXT,
211   UNIX_CLI_PARSE_ACTION_PAGER_DN,
212   UNIX_CLI_PARSE_ACTION_PAGER_UP,
213   UNIX_CLI_PARSE_ACTION_PAGER_TOP,
214   UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM,
215   UNIX_CLI_PARSE_ACTION_PAGER_PGDN,
216   UNIX_CLI_PARSE_ACTION_PAGER_PGUP,
217   UNIX_CLI_PARSE_ACTION_PAGER_SEARCH,
218
219   UNIX_CLI_PARSE_ACTION_PARTIALMATCH,
220   UNIX_CLI_PARSE_ACTION_NOMATCH
221 } unix_cli_parse_action_t;
222
223 /** \brief Mapping of input buffer strings to action values.
224  * @note This won't work as a hash since we need to be able to do
225  *       partial matches on the string.
226  */
227 typedef struct {
228   u8 *input;                        /**< Input string to match. */
229   u32 len;                          /**< Length of input without final NUL. */
230   unix_cli_parse_action_t action;   /**< Action to take when matched. */
231 } unix_cli_parse_actions_t;
232
233 /** \brief Given a capital ASCII letter character return a NUL terminated
234  * string with the control code for that letter.
235  * \example CTL('A') returns { 0x01, 0x00 } as a u8[].
236  */
237 #define CTL(c) (u8[]){ (c) - '@', 0 }
238
239 #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) }
240 static unix_cli_parse_actions_t unix_cli_parse_strings[] = {
241  /* Line handling */
242  _( "\r\n",   UNIX_CLI_PARSE_ACTION_CRLF ),       /* Must be before '\r' */
243  _( "\n",     UNIX_CLI_PARSE_ACTION_CRLF ),
244  _( "\r\0",   UNIX_CLI_PARSE_ACTION_CRLF ),       /* Telnet does this */
245  _( "\r",     UNIX_CLI_PARSE_ACTION_CRLF ),
246
247  /* Unix shell control codes */
248  _( CTL('B'), UNIX_CLI_PARSE_ACTION_LEFT ),
249  _( CTL('F'), UNIX_CLI_PARSE_ACTION_RIGHT ),
250  _( CTL('P'), UNIX_CLI_PARSE_ACTION_UP ),
251  _( CTL('N'), UNIX_CLI_PARSE_ACTION_DOWN ),
252  _( CTL('A'), UNIX_CLI_PARSE_ACTION_HOME ),
253  _( CTL('E'), UNIX_CLI_PARSE_ACTION_END ),
254  _( CTL('D'), UNIX_CLI_PARSE_ACTION_ERASERIGHT ),
255  _( CTL('U'), UNIX_CLI_PARSE_ACTION_ERASELINELEFT ),
256  _( CTL('K'), UNIX_CLI_PARSE_ACTION_ERASELINERIGHT ),
257  _( CTL('Y'), UNIX_CLI_PARSE_ACTION_YANK ),
258  _( CTL('L'), UNIX_CLI_PARSE_ACTION_CLEAR ),
259  _( ESC "b",  UNIX_CLI_PARSE_ACTION_WORDLEFT ),   /* Alt-B */
260  _( ESC "f",  UNIX_CLI_PARSE_ACTION_WORDRIGHT ),  /* Alt-F */
261  _( "\b",     UNIX_CLI_PARSE_ACTION_ERASE ),      /* ^H */
262  _( "\x7f",   UNIX_CLI_PARSE_ACTION_ERASE ),      /* Backspace */
263  _( "\t",     UNIX_CLI_PARSE_ACTION_TAB ),        /* ^I */
264
265  /* VT100 Normal mode - Broadest support */
266  _( CSI "A",  UNIX_CLI_PARSE_ACTION_UP ),
267  _( CSI "B",  UNIX_CLI_PARSE_ACTION_DOWN ),
268  _( CSI "C",  UNIX_CLI_PARSE_ACTION_RIGHT ),
269  _( CSI "D",  UNIX_CLI_PARSE_ACTION_LEFT ),
270  _( CSI "H",  UNIX_CLI_PARSE_ACTION_HOME ),
271  _( CSI "F",  UNIX_CLI_PARSE_ACTION_END ),
272  _( CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT ), /* Delete */
273  _( CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT ), /* C-Left */
274  _( CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT ),/* C-Right */
275
276   /* VT100 Application mode - Some Gnome Terminal functions use these */
277  _( ESC "OA", UNIX_CLI_PARSE_ACTION_UP ),
278  _( ESC "OB", UNIX_CLI_PARSE_ACTION_DOWN ),
279  _( ESC "OC", UNIX_CLI_PARSE_ACTION_RIGHT ),
280  _( ESC "OD", UNIX_CLI_PARSE_ACTION_LEFT ),
281  _( ESC "OH", UNIX_CLI_PARSE_ACTION_HOME ),
282  _( ESC "OF", UNIX_CLI_PARSE_ACTION_END ),
283
284  /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */
285  _( CSI "1~", UNIX_CLI_PARSE_ACTION_HOME ),
286  _( CSI "4~", UNIX_CLI_PARSE_ACTION_END ),
287
288  /* Emacs-ish history search */
289  _( CTL('S'), UNIX_CLI_PARSE_ACTION_FWDSEARCH ),
290  _( CTL('R'), UNIX_CLI_PARSE_ACTION_REVSEARCH ),
291
292  /* Other protocol things */
293  _( "\xff",   UNIX_CLI_PARSE_ACTION_TELNETIAC ),  /* IAC */
294  _( "\0",     UNIX_CLI_PARSE_ACTION_NOACTION ),   /* NUL */
295  _( NULL,     UNIX_CLI_PARSE_ACTION_NOMATCH )
296 };
297 static unix_cli_parse_actions_t unix_cli_parse_pager[] = {
298  /* Line handling */
299  _( "\r\n",   UNIX_CLI_PARSE_ACTION_PAGER_CRLF ),       /* Must be before '\r' */
300  _( "\n",     UNIX_CLI_PARSE_ACTION_PAGER_CRLF ),
301  _( "\r\0",   UNIX_CLI_PARSE_ACTION_PAGER_CRLF ),       /* Telnet does this */
302  _( "\r",     UNIX_CLI_PARSE_ACTION_PAGER_CRLF ),
303
304  /* Pager commands */
305  _( " ",      UNIX_CLI_PARSE_ACTION_PAGER_NEXT ),
306  _( "q",      UNIX_CLI_PARSE_ACTION_PAGER_QUIT ),
307  _( "/",      UNIX_CLI_PARSE_ACTION_PAGER_SEARCH ),
308
309  /* VT100 */
310  _( CSI "A",  UNIX_CLI_PARSE_ACTION_PAGER_UP ),
311  _( CSI "B",  UNIX_CLI_PARSE_ACTION_PAGER_DN ),
312  _( CSI "H",  UNIX_CLI_PARSE_ACTION_PAGER_TOP ),
313  _( CSI "F",  UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM ),
314
315  /* VT100 Application mode */
316  _( ESC "OA", UNIX_CLI_PARSE_ACTION_PAGER_UP ),
317  _( ESC "OB", UNIX_CLI_PARSE_ACTION_PAGER_DN ),
318  _( ESC "OH", UNIX_CLI_PARSE_ACTION_PAGER_TOP ),
319  _( ESC "OF", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM ),
320
321  /* ANSI X3.41-1974 */
322  _( CSI "1~", UNIX_CLI_PARSE_ACTION_PAGER_TOP ),
323  _( CSI "4~", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM ),
324  _( CSI "5~", UNIX_CLI_PARSE_ACTION_PAGER_PGUP ),
325  _( CSI "6~", UNIX_CLI_PARSE_ACTION_PAGER_PGDN ),
326
327  /* Other protocol things */
328  _( "\xff",   UNIX_CLI_PARSE_ACTION_TELNETIAC ),  /* IAC */
329  _( "\0",     UNIX_CLI_PARSE_ACTION_NOACTION ),   /* NUL */
330  _( NULL,     UNIX_CLI_PARSE_ACTION_NOMATCH )
331 };
332 #undef _
333
334 typedef enum {
335   UNIX_CLI_PROCESS_EVENT_READ_READY,
336   UNIX_CLI_PROCESS_EVENT_QUIT,
337 } unix_cli_process_event_type_t;
338
339 typedef struct {
340   /* Prompt string for CLI. */
341   u8 * cli_prompt;
342
343   unix_cli_file_t * cli_file_pool;
344
345   u32 * unused_cli_process_node_indices;
346
347   /* The session index of the stdin cli */
348   u32 stdin_cli_file_index;
349
350   /* File pool index of current input. */
351   u32 current_input_file_index;
352 } unix_cli_main_t;
353
354 static unix_cli_main_t unix_cli_main;
355
356 /**
357  * \brief Search for a byte sequence in the action list.
358  *
359  * Searches unix_cli_parse_actions[] for a match with the bytes in \c input
360  * of maximum length \c ilen . When a match is made \c *matched indicates how
361  * many bytes were matched. Returns a value from the enum
362  * \c unix_cli_parse_action_t to indicate whether no match was found, a
363  * partial match was found or a complete match was found and what action,
364  * if any, should be taken.
365  *
366  * @param a       Actions list to search within.
367  * @param input   String fragment to search for.
368  * @param ilen    Length of the string in 'input'.
369  * @param matched Pointer to an integer that will contain the number of
370  *                bytes matched when a complete match is found.
371  *
372  * @return Action from \v unix_cli_parse_action_t that the string fragment
373  *         matches.
374  *         \c UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the whole
375  *         input string matches the start of at least one action.
376  *         \c UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no
377  *         match at all.
378  */
379 static unix_cli_parse_action_t
380 unix_cli_match_action(unix_cli_parse_actions_t *a,
381           u8 *input, u32 ilen, i32 *matched)
382 {
383   u8 partial = 0;
384
385   while (a->input)
386     {
387         if (ilen >= a->len)
388           {
389             /* see if the start of the input buffer exactly matches the current
390              * action string. */
391             if (memcmp(input, a->input, a->len) == 0)
392               {
393                 *matched = a->len;
394                 return a->action;
395               }
396           }
397         else
398           {
399             /* if the first ilen characters match, flag this as a partial -
400              * meaning keep collecting bytes in case of a future match */
401             if (memcmp(input, a->input, ilen) == 0)
402                 partial = 1;
403           }
404
405         /* check next action */
406         a ++;
407     }
408
409   return partial ?
410         UNIX_CLI_PARSE_ACTION_PARTIALMATCH :
411         UNIX_CLI_PARSE_ACTION_NOMATCH;
412 }
413
414
415 static void
416 unix_cli_add_pending_output (unix_file_t * uf,
417                              unix_cli_file_t * cf,
418                              u8 * buffer,
419                              uword buffer_bytes)
420 {
421   unix_main_t * um = &unix_main;
422
423   vec_add (cf->output_vector, buffer, buffer_bytes);
424   if (vec_len (cf->output_vector) > 0)
425     {
426       int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
427       uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
428       if (! skip_update)
429         um->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
430     }
431 }
432
433 static void
434 unix_cli_del_pending_output (unix_file_t * uf,
435                              unix_cli_file_t * cf,
436                              uword n_bytes)
437 {
438   unix_main_t * um = &unix_main;
439
440   vec_delete (cf->output_vector, n_bytes, 0);
441   if (vec_len (cf->output_vector) <= 0)
442     {
443       int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
444       uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
445       if (! skip_update)
446         um->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
447     }
448 }
449
450 /** \brief A bit like strchr with a buffer length limit.
451  * Search a buffer for the first instance of a character up to the limit of
452  * the buffer length. If found then return the position of that character.
453  *
454  * The key departure from strchr is that if the character is not found then
455  * return the buffer length.
456  *
457  * @param chr The byte value to search for.
458  * @param str The buffer in which to search for the value.
459  * @param len The depth into the buffer to search.
460  *
461  * @return The index of the first occurence of \c chr. If \c chr is not
462  *          found then \c len instead.
463  */
464 always_inline word unix_vlib_findchr(u8 chr, u8 *str, word len)
465 {
466     word i = 0;
467     for (i = 0; i < len; i++, str++)
468       {
469         if (*str == chr)
470           return i;
471       }
472     return len;
473 }
474
475 /** \brief Send a buffer to the CLI stream if possible, enqueue it otherwise.
476  * Attempts to write given buffer to the file descriptor of the given
477  * Unix CLI session. If that session already has data in the output buffer
478  * or if the write attempt tells us to try again later then the given buffer
479  * is appended to the pending output buffer instead.
480  *
481  * This is typically called only from \c unix_vlib_cli_output_cooked since
482  * that is where CRLF handling occurs or from places where we explicitly do
483  * not want cooked handling.
484  *
485  * @param cf Unix CLI session of the desired stream to write to.
486  * @param uf The Unix file structure of the desired stream to write to.
487  * @param buffer Pointer to the buffer that needs to be written.
488  * @param buffer_bytes The number of bytes from \c buffer to write.
489  */
490 static void unix_vlib_cli_output_raw(unix_cli_file_t * cf,
491           unix_file_t * uf,
492           u8 * buffer,
493           uword buffer_bytes)
494 {
495   int n = 0;
496
497   if (vec_len (cf->output_vector) == 0)
498       n = write (uf->file_descriptor, buffer, buffer_bytes);
499
500   if (n < 0 && errno != EAGAIN)
501     {
502       clib_unix_warning ("write");
503     }
504   else if ((word) n < (word) buffer_bytes)
505     {
506       /* We got EAGAIN or we already have stuff in the buffer;
507        * queue up whatever didn't get sent for later. */
508       if (n < 0) n = 0;
509       unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
510     }
511 }
512
513 /** \brief Process a buffer for CRLF handling before outputting it to the CLI.
514  *
515  * @param cf Unix CLI session of the desired stream to write to.
516  * @param uf The Unix file structure of the desired stream to write to.
517  * @param buffer Pointer to the buffer that needs to be written.
518  * @param buffer_bytes The number of bytes from \c buffer to write.
519  */
520 static void unix_vlib_cli_output_cooked(unix_cli_file_t * cf,
521           unix_file_t * uf,
522           u8 * buffer,
523           uword buffer_bytes)
524 {
525   word end = 0, start = 0;
526
527   while (end < buffer_bytes)
528     {
529       if (cf->crlf_mode)
530         {
531           /* iterate the line on \n's so we can insert a \r before it */
532           end = unix_vlib_findchr('\n',
533                                   buffer + start,
534                                   buffer_bytes - start) + start;
535         }
536       else
537         {
538           /* otherwise just send the whole buffer */
539           end = buffer_bytes;
540         }
541
542       unix_vlib_cli_output_raw(cf, uf, buffer + start, end - start);
543
544       if (cf->crlf_mode)
545         {
546           if (end < buffer_bytes)
547             {
548               unix_vlib_cli_output_raw(cf, uf, (u8 *)"\r\n", 2);
549               end ++; /* skip the \n that we already sent */
550             }
551           start = end;
552         }
553     }
554 }
555
556 /** \brief Output the CLI prompt */
557 static void unix_cli_cli_prompt(unix_cli_file_t * cf, unix_file_t * uf)
558 {
559   unix_cli_main_t * cm = &unix_cli_main;
560
561   unix_vlib_cli_output_raw (cf, uf, cm->cli_prompt, vec_len (cm->cli_prompt));
562 }
563
564 /** \brief Output a pager prompt and show number of buffered lines */
565 static void unix_cli_pager_prompt(unix_cli_file_t * cf, unix_file_t * uf)
566 {
567   u8 * prompt;
568
569   prompt = format(0, "\r%s-- more -- (%d-%d/%d)%s",
570     cf->ansi_capable ? ANSI_BOLD : "",
571     cf->pager_start + 1,
572     cf->pager_start + cf->height,
573     cf->pager_lines,
574     cf->ansi_capable ? ANSI_RESET: "");
575
576   unix_vlib_cli_output_cooked(cf, uf, prompt, vec_len(prompt));
577
578   vec_free(prompt);
579 }
580
581 /** \brief Output a pager "skipping" message */
582 static void unix_cli_pager_message(unix_cli_file_t * cf, unix_file_t * uf,
583     char *message, char *postfix)
584 {
585   u8 * prompt;
586
587   prompt = format(0, "\r%s-- %s --%s%s",
588     cf->ansi_capable ? ANSI_BOLD : "",
589     message,
590     cf->ansi_capable ? ANSI_RESET: "",
591     postfix);
592
593   unix_vlib_cli_output_cooked(cf, uf, prompt, vec_len(prompt));
594
595   vec_free(prompt);
596 }
597
598 /** \brief Erase the printed pager prompt */
599 static void unix_cli_pager_prompt_erase(unix_cli_file_t * cf, unix_file_t * uf)
600 {
601   if (cf->ansi_capable)
602     {
603       unix_vlib_cli_output_cooked(cf, uf, (u8 *)"\r", 1);
604       unix_vlib_cli_output_cooked(cf, uf,
605           (u8 *)ANSI_CLEARLINE, sizeof(ANSI_CLEARLINE) - 1);
606     }
607   else
608     {
609       int i;
610
611       unix_vlib_cli_output_cooked(cf, uf, (u8 *)"\r", 1);
612       for (i = 0; i < cf->width - 1; i ++)
613         unix_vlib_cli_output_cooked(cf, uf, (u8 *)" ", 1);
614       unix_vlib_cli_output_cooked(cf, uf, (u8 *)"\r", 1);
615     }
616 }
617
618 /** \brief Uses an ANSI escape sequence to move the cursor */
619 static void unix_cli_ansi_cursor(unix_cli_file_t * cf, unix_file_t * uf,
620       u16 x, u16 y)
621 {
622   u8 * str;
623
624   str = format(0, "%s%d;%dH", CSI, y, x);
625
626   unix_vlib_cli_output_cooked(cf, uf, str, vec_len(str));
627
628   vec_free(str);
629 }
630
631 /** \brief VLIB CLI output function. */
632 static void unix_vlib_cli_output (uword cli_file_index,
633                                   u8 * buffer,
634                                   uword buffer_bytes)
635 {
636   unix_main_t * um = &unix_main;
637   unix_cli_main_t * cm = &unix_cli_main;
638   unix_cli_file_t * cf;
639   unix_file_t * uf;
640
641   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
642   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
643
644   if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
645     {
646       unix_vlib_cli_output_cooked(cf, uf, buffer, buffer_bytes);
647     }
648   else
649     {
650       /* At each \n add the line to the pager buffer.
651        * If we've not yet displayed a whole page in this command then
652        * also output the line.
653        * If we have displayed a whole page then display a pager prompt
654        * and count the lines we've buffered.
655        */
656       u8 * p = buffer;
657       u8 * line;
658       uword i, len = buffer_bytes;
659
660       while (len)
661         {
662           i = unix_vlib_findchr('\n', p, len);
663           if (i < len)
664             i ++; /* include the '\n' */
665
666           /* make a vec out of this substring */
667           line = vec_new(u8, i);
668           memcpy(line, p, i);
669
670           /* store in pager buffer */
671           vec_add1(cf->pager_vector, line);
672           cf->pager_lines ++;
673
674           if (cf->pager_lines < cf->height)
675             {
676               /* output this line */
677               unix_vlib_cli_output_cooked(cf, uf, p, i);
678               /* TODO: stop if line longer than cf->width and would
679                * overflow cf->height */
680             }
681           else
682             {
683               /* Display the pager prompt every 10 lines */
684               if (!(cf->pager_lines % 10))
685                 unix_cli_pager_prompt(cf, uf);
686             }
687
688           p += i;
689           len -= i;
690         }
691
692         /* Check if we went over the pager buffer limit */
693         if (cf->pager_lines > um->cli_pager_buffer_limit)
694           {
695             /* Stop using the pager for the remainder of this CLI command */
696             cf->no_pager = 2;
697
698             /* If we likely printed the prompt, erase it */
699             if (cf->pager_lines > cf->height - 1)
700               unix_cli_pager_prompt_erase (cf, uf);
701
702             /* Dump out the contents of the buffer */
703             for (i = cf->pager_start + (cf->height - 1);
704                           i < cf->pager_lines; i ++)
705               unix_vlib_cli_output_cooked (cf, uf,
706                 cf->pager_vector[i],
707                 vec_len(cf->pager_vector[i]));
708
709             unix_cli_pager_reset (cf);
710           }
711     }
712 }
713
714 /** \brief Identify whether a terminal type is ANSI capable. */
715 static u8 unix_cli_terminal_type(u8 * term, uword len)
716 {
717   /* This may later be better done as a hash of some sort. */
718 #define _(a) do { \
719     if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
720   } while(0)
721
722   _("xterm");
723   _("xterm-color");
724   _("xterm-256color"); /* iTerm on Mac */
725   _("screen");
726   _("ansi"); /* Microsoft Telnet */
727 #undef _
728
729   return 0;
730 }
731
732 /** \brief Emit initial welcome banner and prompt on a connection. */
733 static void unix_cli_file_welcome(unix_cli_main_t * cm, unix_cli_file_t * cf)
734 {
735   unix_main_t * um = &unix_main;
736   unix_file_t * uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
737   unix_cli_banner_t *banner;
738   int i, len;
739
740   /*
741    * Put the first bytes directly into the buffer so that further output is
742    * queued until everything is ready. (oterwise initial prompt can appear
743    * mid way through VPP initialization)
744    */
745   unix_cli_add_pending_output (uf, cf, (u8 *)"\r", 1);
746
747   if (!um->cli_no_banner)
748     {
749       if (cf->ansi_capable)
750         {
751           banner = unix_cli_banner_color;
752           len = ARRAY_LEN(unix_cli_banner_color);
753         }
754       else
755         {
756           banner = unix_cli_banner;
757           len = ARRAY_LEN(unix_cli_banner);
758         }
759
760       for (i = 0; i < len; i++)
761         {
762           unix_vlib_cli_output_cooked(cf, uf,
763             banner[i].line,
764             banner[i].length);
765         }
766       }
767
768   /* Prompt. */
769   unix_cli_cli_prompt (cf, uf);
770
771   cf->started = 1;
772 }
773
774 /** \brief A failsafe triggered on a timer to ensure we send the prompt
775  * to telnet sessions that fail to negotiate the terminal type. */
776 static void unix_cli_file_welcome_timer(any arg, f64 delay)
777 {
778   unix_cli_main_t * cm = &unix_cli_main;
779   unix_cli_file_t * cf;
780   (void)delay;
781
782   /* Check the connection didn't close already */
783   if (pool_is_free_index (cm->cli_file_pool, (uword)arg))
784     return;
785
786   cf = pool_elt_at_index (cm->cli_file_pool, (uword)arg);
787
788   if (!cf->started)
789     unix_cli_file_welcome(cm, cf);
790 }
791
792 /** \brief A mostly no-op Telnet state machine.
793  * Process Telnet command bytes in a way that ensures we're mostly
794  * transparent to the Telnet protocol. That is, it's mostly a no-op.
795  *
796  * @return -1 if we need more bytes, otherwise a positive integer number of
797  *          bytes to consume from the input_vector, not including the initial
798  *          IAC byte.
799  */
800 static i32 unix_cli_process_telnet(unix_main_t * um,
801         unix_cli_file_t * cf,
802         unix_file_t * uf,
803         u8 * input_vector,
804         uword len)
805 {
806   /* Input_vector starts at IAC byte.
807    * See if we have a complete message; if not, return -1 so we wait for more.
808    * if we have a complete message, consume those bytes from the vector.
809    */
810   i32 consume = 0;
811
812   if (len == 1)
813     return -1; /* want more bytes */
814
815   switch (input_vector[1])
816     {
817       case IAC:
818         /* two IAC's in a row means to pass through 0xff.
819          * since that makes no sense here, just consume it.
820          */
821         consume = 1;
822         break;
823
824       case WILL:
825       case WONT:
826       case DO:
827       case DONT:
828         /* Expect 3 bytes */
829         if (vec_len(input_vector) < 3)
830           return -1; /* want more bytes */
831
832         consume = 2;
833         break;
834
835       case SB:
836         {
837           /* Sub option - search ahead for IAC SE to end it */
838           i32 i;
839           for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
840             {
841               if (input_vector[i - 1] == IAC && input_vector[i] == SE)
842                 {
843                   /* We have a complete message; see if we care about it */
844                   switch (input_vector[2])
845                     {
846                       case TELOPT_TTYPE:
847                         if (input_vector[3] != 0)
848                           break;
849                         /* See if the terminal type is ANSI capable */
850                         cf->ansi_capable =
851                             unix_cli_terminal_type(input_vector + 4, i - 5);
852                         /* If session not started, we can release the pause */
853                         if (!cf->started)
854                           /* Send the welcome banner and initial prompt */
855                           unix_cli_file_welcome(&unix_cli_main, cf);
856                         break;
857
858                       case TELOPT_NAWS:
859                         /* Window size */
860                         if (i != 8) /* check message is correct size */
861                           break;
862                         cf->width = clib_net_to_host_u16(*((u16 *)(input_vector + 3)));
863                         cf->height = clib_net_to_host_u16(*((u16 *)(input_vector + 5)));
864                         break;
865
866                       default:
867                         break;
868                     }
869                   /* Consume it all */
870                   consume = i;
871                   break;
872                 }
873             }
874
875           if (i == UNIX_CLI_MAX_DEPTH_TELNET)
876             consume = 1; /* hit max search depth, advance one byte */
877
878           if (consume == 0)
879             return -1; /* want more bytes */
880
881           break;
882         }
883
884       case GA:
885       case EL:
886       case EC:
887       case AO:
888       case IP:
889       case BREAK:
890       case DM:
891       case NOP:
892       case SE:
893       case EOR:
894       case ABORT:
895       case SUSP:
896       case xEOF:
897         /* Simple one-byte messages */
898         consume = 1;
899         break;
900
901       case AYT:
902         /* Are You There - trigger a visible response */
903         consume = 1;
904         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
905         break;
906
907       default:
908         /* Unknown command! Eat the IAC byte */
909         break;
910     }
911
912     return consume;
913 }
914
915 /** \brief Process actionable input.
916  * Based on the \c action process the input; this typically involves
917  * searching the command history or editing the current command line.
918  */
919 static int unix_cli_line_process_one(unix_cli_main_t * cm,
920         unix_main_t * um,
921         unix_cli_file_t * cf,
922         unix_file_t * uf,
923         u8 input,
924         unix_cli_parse_action_t action)
925 {
926   u8 * prev;
927   int j, delta;
928
929   switch (action)
930     {
931     case UNIX_CLI_PARSE_ACTION_NOACTION:
932       break;
933
934     case UNIX_CLI_PARSE_ACTION_REVSEARCH:
935     case UNIX_CLI_PARSE_ACTION_FWDSEARCH:
936       if (!cf->has_history || !cf->history_limit)
937         break;
938       if (cf->search_mode == 0)
939         {
940           /* Erase the current command (if any) */
941           for (j = 0; j < (vec_len (cf->current_command)); j++)
942               unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
943
944           vec_reset_length (cf->search_key);
945           vec_reset_length (cf->current_command);
946           if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
947               cf->search_mode = -1;
948           else
949               cf->search_mode = 1;
950           cf->cursor = 0;
951         }
952       else
953         {
954           if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
955             cf->search_mode = -1;
956           else
957             cf->search_mode = 1;
958
959           cf->excursion += cf->search_mode;
960           goto search_again;
961         }
962       break;
963
964     case UNIX_CLI_PARSE_ACTION_ERASELINELEFT:
965       /* Erase the command from the cursor to the start */
966
967       /* Shimmy forwards to the new end of line position */
968       delta = vec_len (cf->current_command) - cf->cursor;
969       for (j = cf->cursor; j > delta; j--)
970         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
971       /* Zap from here to the end of what is currently displayed */
972       for (; j < (vec_len (cf->current_command)); j++)
973         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
974       /* Get back to the start of the line */
975       for (j = 0; j < (vec_len (cf->current_command)); j++)
976         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
977
978       j = vec_len(cf->current_command) - cf->cursor;
979       memmove(cf->current_command,
980               cf->current_command + cf->cursor,
981               j);
982       _vec_len(cf->current_command) = j;
983
984       /* Print the new contents */
985       unix_vlib_cli_output_cooked (cf, uf, cf->current_command, j);
986       /* Shimmy back to the start */
987       for (j = 0; j < (vec_len (cf->current_command)); j++)
988         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
989       cf->cursor = 0;
990
991       cf->search_mode = 0;
992       break;
993
994     case UNIX_CLI_PARSE_ACTION_ERASELINERIGHT:
995       /* Erase the command from the cursor to the end */
996
997       /* Zap from cursor to end of what is currently displayed */
998       for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
999         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1000       /* Get back to where we were */
1001       for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1002         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1003
1004       /* Truncate the line at the cursor */
1005       _vec_len(cf->current_command) = cf->cursor;
1006
1007       cf->search_mode = 0;
1008       break;
1009
1010     case UNIX_CLI_PARSE_ACTION_LEFT:
1011       if (cf->cursor > 0)
1012         {
1013           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1014           cf->cursor --;
1015         }
1016
1017       cf->search_mode = 0;
1018       break;
1019
1020     case UNIX_CLI_PARSE_ACTION_RIGHT:
1021       if (cf->cursor < vec_len(cf->current_command))
1022         {
1023           /* have to emit the character under the cursor */
1024           unix_vlib_cli_output_cooked (cf, uf, cf->current_command + cf->cursor, 1);
1025           cf->cursor ++;
1026         }
1027
1028       cf->search_mode = 0;
1029       break;
1030
1031     case UNIX_CLI_PARSE_ACTION_UP:
1032     case UNIX_CLI_PARSE_ACTION_DOWN:
1033       if (!cf->has_history || !cf->history_limit)
1034         break;
1035       cf->search_mode = 0;
1036       /* Erase the command */
1037       for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1038         unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1039       for (j = 0; j < (vec_len (cf->current_command)); j++)
1040         unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1041       vec_reset_length (cf->current_command);
1042       if (vec_len (cf->command_history))
1043         {
1044           if (action == UNIX_CLI_PARSE_ACTION_UP)
1045             delta = -1;
1046           else
1047             delta = 1;
1048
1049           cf->excursion += delta;
1050
1051           if (cf->excursion == vec_len (cf->command_history))
1052             {
1053               /* down-arrowed to last entry - want a blank line */
1054               _vec_len (cf->current_command) = 0;
1055             }
1056           else if (cf->excursion < 0)
1057             {
1058               /* up-arrowed over the start to the end, want a blank line */
1059               cf->excursion = vec_len (cf->command_history);
1060               _vec_len (cf->current_command) = 0;
1061             }
1062           else
1063             {
1064               if (cf->excursion > (i32) vec_len (cf->command_history) -1)
1065                 /* down-arrowed past end - wrap to start */
1066                 cf->excursion = 0;
1067
1068               /* Print the command at the current position */
1069               prev = cf->command_history [cf->excursion];
1070               vec_validate (cf->current_command, vec_len(prev)-1);
1071
1072               clib_memcpy (cf->current_command, prev, vec_len(prev));
1073               _vec_len (cf->current_command) = vec_len(prev);
1074               unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
1075                                            vec_len (cf->current_command));
1076             }
1077           cf->cursor = vec_len(cf->current_command);
1078
1079           break;
1080         }
1081       break;
1082
1083     case UNIX_CLI_PARSE_ACTION_HOME:
1084       if (vec_len (cf->current_command) && cf->cursor > 0)
1085         {
1086           while (cf->cursor)
1087             {
1088               unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1089               cf->cursor --;
1090             }
1091         }
1092
1093       cf->search_mode = 0;
1094       break;
1095
1096     case UNIX_CLI_PARSE_ACTION_END:
1097       if (vec_len (cf->current_command) &&
1098               cf->cursor < vec_len(cf->current_command))
1099         {
1100           unix_vlib_cli_output_cooked (cf, uf,
1101                 cf->current_command + cf->cursor,
1102                 vec_len(cf->current_command) - cf->cursor);
1103           cf->cursor = vec_len(cf->current_command);
1104         }
1105
1106       cf->search_mode = 0;
1107       break;
1108
1109     case UNIX_CLI_PARSE_ACTION_WORDLEFT:
1110       if (vec_len (cf->current_command) && cf->cursor > 0)
1111         {
1112           j = cf->cursor;
1113
1114           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1115           j --;
1116
1117           while (j && isspace(cf->current_command[j]))
1118             {
1119               unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1120               j --;
1121             }
1122           while (j && !isspace(cf->current_command[j]))
1123             {
1124               if (isspace(cf->current_command[j - 1]))
1125                 break;
1126               unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1127               j --;
1128             }
1129
1130           cf->cursor = j;
1131         }
1132
1133       cf->search_mode = 0;
1134       break;
1135
1136     case UNIX_CLI_PARSE_ACTION_WORDRIGHT:
1137       if (vec_len (cf->current_command) &&
1138               cf->cursor < vec_len(cf->current_command))
1139         {
1140           int e = vec_len(cf->current_command);
1141           j = cf->cursor;
1142           while (j < e && !isspace(cf->current_command[j]))
1143             j ++;
1144           while (j < e && isspace(cf->current_command[j]))
1145             j ++;
1146           unix_vlib_cli_output_cooked (cf, uf,
1147                 cf->current_command + cf->cursor,
1148                 j - cf->cursor);
1149           cf->cursor = j;
1150         }
1151
1152       cf->search_mode = 0;
1153       break;
1154
1155
1156     case UNIX_CLI_PARSE_ACTION_ERASE:
1157       if (vec_len (cf->current_command))
1158         {
1159           if (cf->cursor == vec_len(cf->current_command))
1160             {
1161               unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1162               _vec_len (cf->current_command)--;
1163               cf->cursor --;
1164             }
1165           else if (cf->cursor > 0)
1166             {
1167               /* shift everything at & to the right of the cursor left by 1 */
1168               j = vec_len (cf->current_command) - cf->cursor;
1169               memmove (cf->current_command + cf->cursor - 1,
1170                 cf->current_command + cf->cursor,
1171                 j);
1172               _vec_len (cf->current_command)--;
1173               cf->cursor --;
1174               /* redraw the rest of the line */
1175               unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1176               unix_vlib_cli_output_cooked (cf, uf,
1177                     cf->current_command + cf->cursor, j);
1178               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " \b\b", 3);
1179               /* and shift the terminal cursor back where it should be */
1180               while (-- j)
1181                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1182             }
1183         }
1184       cf->search_mode = 0;
1185       cf->excursion = 0;
1186       vec_reset_length (cf->search_key);
1187       break;
1188
1189     case UNIX_CLI_PARSE_ACTION_ERASERIGHT:
1190       if (vec_len (cf->current_command))
1191         {
1192           if (cf->cursor < vec_len(cf->current_command))
1193             {
1194               /* shift everything to the right of the cursor left by 1 */
1195               j = vec_len (cf->current_command) - cf->cursor - 1;
1196               memmove (cf->current_command + cf->cursor,
1197                 cf->current_command + cf->cursor + 1,
1198                 j);
1199               _vec_len (cf->current_command)--;
1200               /* redraw the rest of the line */
1201               unix_vlib_cli_output_cooked (cf, uf,
1202                     cf->current_command + cf->cursor, j);
1203               unix_vlib_cli_output_cooked (cf, uf, (u8 *) " \b", 2);
1204               /* and shift the terminal cursor back where it should be */
1205               if (j)
1206                 {
1207                   unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1208                   while (-- j)
1209                     unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1210                 }
1211             }
1212         }
1213       else if (input == 'D' - '@')
1214         {
1215           /* ^D with no command entered = quit */
1216           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
1217           vlib_process_signal_event (um->vlib_main,
1218                 vlib_current_process (um->vlib_main),
1219                 UNIX_CLI_PROCESS_EVENT_QUIT,
1220                 cf - cm->cli_file_pool);
1221         }
1222       cf->search_mode = 0;
1223       cf->excursion = 0;
1224       vec_reset_length (cf->search_key);
1225       break;
1226
1227     case UNIX_CLI_PARSE_ACTION_CLEAR:
1228       /* If we're in ANSI mode, clear the screen.
1229        * Then redraw the prompt and any existing command input, then put
1230        * the cursor back where it was in that line.
1231        */
1232       if (cf->ansi_capable)
1233           unix_vlib_cli_output_cooked (cf, uf,
1234                     (u8 *) ANSI_CLEAR,
1235                     sizeof(ANSI_CLEAR)-1);
1236       else
1237           unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1238
1239       unix_vlib_cli_output_raw (cf, uf,
1240                 cm->cli_prompt,
1241                 vec_len (cm->cli_prompt));
1242       unix_vlib_cli_output_raw (cf, uf,
1243                 cf->current_command,
1244                 vec_len (cf->current_command));
1245       for (j = cf->cursor; j < vec_len(cf->current_command); j++)
1246          unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1247
1248       break;
1249
1250     case UNIX_CLI_PARSE_ACTION_TAB:
1251     case UNIX_CLI_PARSE_ACTION_YANK:
1252       /* TODO */
1253       break;
1254
1255
1256     case UNIX_CLI_PARSE_ACTION_PAGER_QUIT:
1257     pager_quit:
1258       unix_cli_pager_prompt_erase (cf, uf);
1259       unix_cli_pager_reset (cf);
1260       unix_cli_cli_prompt (cf, uf);
1261       break;
1262
1263     case UNIX_CLI_PARSE_ACTION_PAGER_NEXT:
1264     case UNIX_CLI_PARSE_ACTION_PAGER_PGDN:
1265       /* show next page of the buffer */
1266       if (cf->height + cf->pager_start < cf->pager_lines)
1267         {
1268           u8 * line;
1269           int m = cf->pager_start + (cf->height - 1);
1270           unix_cli_pager_prompt_erase (cf, uf);
1271           for (j = m;
1272                 j < cf->pager_lines && cf->pager_start < m;
1273                 j ++, cf->pager_start ++)
1274             {
1275               line = cf->pager_vector[j];
1276               unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1277             }
1278           unix_cli_pager_prompt (cf, uf);
1279         }
1280       else
1281         {
1282           if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
1283             /* no more in buffer, exit, but only if it was <space> */
1284             goto pager_quit;
1285         }
1286       break;
1287
1288     case UNIX_CLI_PARSE_ACTION_PAGER_DN:
1289     case UNIX_CLI_PARSE_ACTION_PAGER_CRLF:
1290       /* display the next line of the buffer */
1291       if (cf->pager_start < cf->pager_lines - (cf->height - 1))
1292         {
1293           u8 * line;
1294           unix_cli_pager_prompt_erase (cf, uf);
1295           line = cf->pager_vector[cf->pager_start + (cf->height - 1)];
1296           unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1297           cf->pager_start ++;
1298           unix_cli_pager_prompt (cf, uf);
1299         }
1300       else
1301         {
1302           if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
1303             /* no more in buffer, exit, but only if it was <enter> */
1304             goto pager_quit;
1305         }
1306
1307       break;
1308
1309     case UNIX_CLI_PARSE_ACTION_PAGER_UP:
1310       /* scroll the page back one line */
1311       if (cf->pager_start > 0)
1312         {
1313           u8 * line;
1314           cf->pager_start --;
1315           if (cf->ansi_capable)
1316             {
1317               unix_cli_pager_prompt_erase (cf, uf);
1318               unix_vlib_cli_output_cooked (cf, uf, (u8 *)ANSI_SCROLLDN, sizeof(ANSI_SCROLLDN) - 1);
1319               unix_vlib_cli_output_cooked (cf, uf, (u8 *)ANSI_SAVECURSOR, sizeof(ANSI_SAVECURSOR) - 1);
1320               unix_cli_ansi_cursor(cf, uf, 1, 1);
1321               unix_vlib_cli_output_cooked (cf, uf, (u8 *)ANSI_CLEARLINE, sizeof(ANSI_CLEARLINE) - 1);
1322               unix_vlib_cli_output_cooked (cf, uf, cf->pager_vector[cf->pager_start], vec_len(cf->pager_vector[cf->pager_start]));
1323               unix_vlib_cli_output_cooked (cf, uf, (u8 *)ANSI_RESTCURSOR, sizeof(ANSI_RESTCURSOR) - 1);
1324               unix_cli_pager_prompt_erase (cf, uf);
1325               unix_cli_pager_prompt (cf, uf);
1326             }
1327           else
1328             {
1329               int m = cf->pager_start + (cf->height - 1);
1330               unix_cli_pager_prompt_erase (cf, uf);
1331               for (j = cf->pager_start; j < cf->pager_lines && j < m; j ++)
1332                 {
1333                   line = cf->pager_vector[j];
1334                   unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1335                 }
1336               unix_cli_pager_prompt (cf, uf);
1337             }
1338         }
1339       break;
1340
1341     case UNIX_CLI_PARSE_ACTION_PAGER_TOP:
1342       /* back to the first page of the buffer */
1343       if (cf->pager_start > 0)
1344         {
1345           u8 * line;
1346           cf->pager_start = 0;
1347           int m = cf->pager_start + (cf->height - 1);
1348           unix_cli_pager_prompt_erase (cf, uf);
1349           for (j = cf->pager_start; j < cf->pager_lines && j < m; j ++)
1350             {
1351               line = cf->pager_vector[j];
1352               unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1353             }
1354           unix_cli_pager_prompt (cf, uf);
1355         }
1356       break;
1357
1358     case UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM:
1359       /* skip to the last page of the buffer */
1360       if (cf->pager_start < cf->pager_lines - (cf->height - 1))
1361         {
1362           u8 * line;
1363           cf->pager_start = cf->pager_lines - (cf->height - 1);
1364           unix_cli_pager_prompt_erase (cf, uf);
1365           unix_cli_pager_message (cf, uf, "skipping", "\n");
1366           for (j = cf->pager_start; j < cf->pager_lines; j ++)
1367             {
1368               line = cf->pager_vector[j];
1369               unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1370             }
1371           unix_cli_pager_prompt (cf, uf);
1372         }
1373       break;
1374
1375     case UNIX_CLI_PARSE_ACTION_PAGER_PGUP:
1376       /* wander back one page in the buffer */
1377       if (cf->pager_start > 0)
1378         {
1379           u8 * line;
1380           int m;
1381           if (cf->pager_start >= cf->height)
1382             cf->pager_start -= cf->height - 1;
1383           else
1384             cf->pager_start = 1;
1385           m = cf->pager_start + cf->height - 1;
1386           unix_cli_pager_prompt_erase (cf, uf);
1387           for (j = cf->pager_start; j < cf->pager_lines && j < m; j ++)
1388             {
1389               line = cf->pager_vector[j];
1390               unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1391             }
1392           unix_cli_pager_prompt (cf, uf);
1393         }
1394       break;
1395
1396     case UNIX_CLI_PARSE_ACTION_PAGER_SEARCH:
1397       /* search forwards in the buffer */
1398       break;
1399
1400
1401     case UNIX_CLI_PARSE_ACTION_CRLF:
1402     crlf:
1403       unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1404
1405       if (cf->has_history && cf->history_limit)
1406         {
1407           if (cf->command_history && vec_len(cf->command_history) >= cf->history_limit)
1408             {
1409               vec_free (cf->command_history[0]);
1410               vec_delete (cf->command_history, 1, 0);
1411             }
1412           /* Don't add blank lines to the cmd history */
1413           if (vec_len (cf->current_command))
1414             {
1415               /* Don't duplicate the previous command */
1416               j = vec_len(cf->command_history);
1417               if (j == 0 ||
1418                 (vec_len (cf->current_command) != vec_len (cf->command_history[j - 1]) ||
1419                     memcmp(cf->current_command, cf->command_history[j - 1],
1420                            vec_len (cf->current_command)) != 0))
1421                 {
1422                   /* copy the command to the history */
1423                   u8 * c = 0;
1424                   vec_append(c, cf->current_command);
1425                   vec_add1 (cf->command_history, c);
1426                   cf->command_number ++;
1427                 }
1428             }
1429           cf->excursion = vec_len (cf->command_history);
1430         }
1431
1432       cf->search_mode = 0;
1433       vec_reset_length (cf->search_key);
1434       cf->cursor = 0;
1435
1436       return 0;
1437
1438     case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
1439     case UNIX_CLI_PARSE_ACTION_NOMATCH:
1440       if (cf->pager_lines)
1441         {
1442           /* no-op for now */
1443         }
1444       else if (cf->has_history && cf->search_mode && isprint(input))
1445         {
1446           int k, limit, offset;
1447           u8 * item;
1448
1449           vec_add1 (cf->search_key, input);
1450
1451         search_again:
1452           for (j = 0; j < vec_len(cf->command_history); j++)
1453             {
1454               if (cf->excursion > (i32) vec_len (cf->command_history) -1)
1455                 cf->excursion = 0;
1456               else if (cf->excursion < 0)
1457                 cf->excursion = vec_len (cf->command_history) -1;
1458
1459               item = cf->command_history[cf->excursion];
1460
1461               limit = (vec_len(cf->search_key) > vec_len (item)) ?
1462                 vec_len(item) : vec_len (cf->search_key);
1463
1464               for (offset = 0; offset <= vec_len(item) - limit; offset++)
1465                 {
1466                   for (k = 0; k < limit; k++)
1467                     {
1468                       if (item[k+offset] != cf->search_key[k])
1469                         goto next_offset;
1470                     }
1471                   goto found_at_offset;
1472
1473                 next_offset:
1474                   ;
1475                 }
1476               goto next;
1477
1478             found_at_offset:
1479               for (j = 0; j < vec_len (cf->current_command); j++)
1480                 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1481
1482               vec_validate (cf->current_command, vec_len(item)-1);
1483               clib_memcpy (cf->current_command, item, vec_len(item));
1484               _vec_len (cf->current_command) = vec_len(item);
1485
1486               unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
1487                                            vec_len (cf->current_command));
1488               cf->cursor = vec_len (cf->current_command);
1489               goto found;
1490
1491             next:
1492               cf->excursion += cf->search_mode;
1493             }
1494
1495           unix_vlib_cli_output_cooked (cf, uf, (u8 *)"\nNo match...", 12);
1496           vec_reset_length (cf->search_key);
1497           vec_reset_length (cf->current_command);
1498           cf->search_mode = 0;
1499           cf->cursor = 0;
1500           goto crlf;
1501         }
1502       else if (isprint(input)) /* skip any errant control codes */
1503         {
1504           if (cf->cursor == vec_len(cf->current_command))
1505             {
1506               /* Append to end */
1507               vec_add1 (cf->current_command, input);
1508               cf->cursor ++;
1509
1510               /* Echo the character back to the client */
1511               unix_vlib_cli_output_raw (cf, uf, &input, 1);
1512             }
1513           else
1514             {
1515               /* Insert at cursor: resize +1 byte, move everything over */
1516               j = vec_len (cf->current_command) - cf->cursor;
1517               vec_add1 (cf->current_command, (u8)'A');
1518               memmove (cf->current_command + cf->cursor + 1,
1519                 cf->current_command + cf->cursor,
1520                 j);
1521               cf->current_command[cf->cursor] = input;
1522               /* Redraw the line */
1523               j ++;
1524               unix_vlib_cli_output_raw (cf, uf,
1525                     cf->current_command + cf->cursor, j);
1526               /* Put terminal cursor back */
1527               while (-- j)
1528                 unix_vlib_cli_output_raw (cf, uf, (u8 *)"\b", 1);
1529               cf->cursor ++;
1530             }
1531         }
1532       else
1533         {
1534           /* no-op - not printable or otherwise not actionable */
1535         }
1536
1537     found:
1538
1539       break;
1540
1541     case UNIX_CLI_PARSE_ACTION_TELNETIAC:
1542       break;
1543     }
1544     return 1;
1545 }
1546
1547 /** \brief Process input bytes on a stream to provide line editing and
1548  * command history in the CLI. */
1549 static int unix_cli_line_edit (unix_cli_main_t * cm,
1550                       unix_main_t * um,
1551                       unix_cli_file_t * cf)
1552 {
1553   unix_file_t * uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
1554   int i;
1555
1556   for (i = 0; i < vec_len (cf->input_vector); i++)
1557     {
1558       unix_cli_parse_action_t action;
1559       i32 matched = 0;
1560       unix_cli_parse_actions_t *a;
1561
1562       /* If we're in the pager mode, search the pager actions */
1563       a = cf->pager_lines ? unix_cli_parse_pager : unix_cli_parse_strings;
1564
1565       /* See if the input buffer is some sort of control code */
1566       action = unix_cli_match_action(a, &cf->input_vector[i],
1567         vec_len (cf->input_vector) - i, &matched);
1568
1569       switch (action)
1570         {
1571         case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
1572           if (i)
1573             {
1574               /* There was a partial match which means we need more bytes
1575                * than the input buffer currently has.
1576                * Since the bytes before here have been processed, shift
1577                * the remaining contents to the start of the input buffer.
1578                */
1579               vec_delete (cf->input_vector, i, 0);
1580             }
1581           return 1; /* wait for more */
1582
1583         case UNIX_CLI_PARSE_ACTION_TELNETIAC:
1584           /* process telnet options */
1585           matched = unix_cli_process_telnet(um, cf, uf,
1586                 cf->input_vector + i, vec_len(cf->input_vector) - i);
1587           if (matched < 0)
1588             {
1589               if (i)
1590                 {
1591                   /* There was a partial match which means we need more bytes
1592                    * than the input buffer currently has.
1593                    * Since the bytes before here have been processed, shift
1594                    * the remaining contents to the start of the input buffer.
1595                    */
1596                   vec_delete (cf->input_vector, i, 0);
1597                 }
1598               return 1; /* wait for more */
1599             }
1600           break;
1601
1602         default:
1603           /* process the action */
1604           if (!unix_cli_line_process_one(cm, um, cf, uf,
1605                 cf->input_vector[i], action))
1606             {
1607               /* CRLF found. Consume the bytes from the input_vector */
1608               vec_delete (cf->input_vector, i + matched, 0);
1609               /* And tell our caller to execute cf->input_command */
1610               return 0;
1611             }
1612         }
1613
1614       i += matched;
1615     }
1616
1617   vec_reset_length(cf->input_vector);
1618   return 1;
1619 }
1620
1621 /** \brief Process input to a CLI session. */
1622 static void unix_cli_process_input (unix_cli_main_t * cm,
1623                         uword cli_file_index)
1624 {
1625   unix_main_t * um = &unix_main;
1626   unix_file_t * uf;
1627   unix_cli_file_t * cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1628   unformat_input_t input;
1629   int vlib_parse_eval (u8 *);
1630
1631 more:
1632   /* Try vlibplex first.  Someday... */
1633   if (0 && vlib_parse_eval (cf->input_vector) == 0)
1634       goto done;
1635
1636   if (cf->line_mode)
1637     {
1638       /* just treat whatever we got as a complete line of input */
1639       cf->current_command = cf->input_vector;
1640     }
1641   else
1642     {
1643       /* Line edit, echo, etc. */
1644       if (unix_cli_line_edit (cm, um, cf))
1645         /* want more input */
1646         return;
1647     }
1648
1649   if (um->log_fd)
1650     {
1651       static u8 * lv;
1652       vec_reset_length (lv);
1653       lv = format (lv, "%U[%d]: %v", 
1654                    format_timeval,
1655                    0 /* current bat-time */,
1656                    0 /* current bat-format */,
1657                    cli_file_index,
1658                    cf->input_vector);
1659       {
1660         int rv __attribute__((unused)) = 
1661           write (um->log_fd, lv, vec_len(lv));
1662       }
1663     }
1664
1665   /* Copy our input command to a new string */
1666   unformat_init_vector (&input, cf->current_command);
1667
1668   /* Remove leading white space from input. */
1669   (void) unformat (&input, "");
1670
1671   cm->current_input_file_index = cli_file_index;
1672   cf->pager_lines = 0; /* start a new pager session */
1673   cf->pager_start = 0;
1674
1675   if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
1676     vlib_cli_input (um->vlib_main, &input, unix_vlib_cli_output, cli_file_index);
1677
1678   /* Zero buffer since otherwise unformat_free will call vec_free on it. */
1679   input.buffer = 0;
1680
1681   unformat_free (&input);
1682
1683   /* Re-fetch pointer since pool may have moved. */
1684   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1685   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
1686
1687 done:
1688   /* reset vector; we'll re-use it later  */
1689   if (cf->line_mode)
1690     vec_reset_length (cf->input_vector);
1691   else
1692     vec_reset_length (cf->current_command);
1693
1694   if (cf->no_pager == 2)
1695     {
1696       /* Pager was programmatically disabled */
1697       unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
1698       cf->no_pager = um->cli_no_pager;
1699     }
1700
1701   if (cf->pager_lines == 0 || cf->pager_lines < cf->height)
1702     {
1703       /* There was no need for the pager */
1704       unix_cli_pager_reset (cf);
1705
1706       /* Prompt. */
1707       unix_cli_cli_prompt (cf, uf);
1708     }
1709   else
1710     {
1711       /* Display the pager prompt */
1712       unix_cli_pager_prompt(cf, uf);
1713     }
1714
1715     /* Any residual data in the input vector? */
1716     if (vec_len (cf->input_vector))
1717       goto more;
1718 }
1719
1720 static void unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
1721 {
1722   unix_main_t * um = &unix_main;
1723   unix_cli_file_t * cf;
1724   unix_file_t * uf;
1725   int i;
1726
1727   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1728   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
1729
1730   /* Quit/EOF on stdin means quit program. */
1731   if (uf->file_descriptor == UNIX_CLI_STDIN_FD)
1732     clib_longjmp (&um->vlib_main->main_loop_exit, VLIB_MAIN_LOOP_EXIT_CLI);
1733
1734   vec_free (cf->current_command);
1735   vec_free (cf->search_key);
1736
1737   for (i = 0; i < vec_len (cf->command_history); i++)
1738       vec_free (cf->command_history[i]);
1739
1740   vec_free (cf->command_history);
1741
1742   unix_file_del (um, uf);
1743
1744   unix_cli_file_free (cf);
1745   pool_put (cm->cli_file_pool, cf);
1746 }
1747
1748 static uword
1749 unix_cli_process (vlib_main_t * vm,
1750                   vlib_node_runtime_t * rt,
1751                   vlib_frame_t * f)
1752 {
1753   unix_cli_main_t * cm = &unix_cli_main;
1754   uword i, * data = 0;
1755
1756   while (1)
1757     {
1758       unix_cli_process_event_type_t event_type;
1759       vlib_process_wait_for_event (vm);
1760       event_type = vlib_process_get_events (vm, &data);
1761
1762       switch (event_type)
1763         {
1764         case UNIX_CLI_PROCESS_EVENT_READ_READY:
1765           for (i = 0; i < vec_len (data); i++)
1766             unix_cli_process_input (cm, data[i]);
1767           break;
1768
1769         case UNIX_CLI_PROCESS_EVENT_QUIT:
1770           /* Kill this process. */
1771           for (i = 0; i < vec_len (data); i++)
1772             unix_cli_kill (cm, data[i]);
1773           goto done;
1774         }
1775
1776       if (data)
1777         _vec_len (data) = 0;
1778     }
1779
1780  done:
1781   vec_free (data);
1782
1783   vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
1784
1785   /* Add node index so we can re-use this process later. */
1786   vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);
1787
1788   return 0;
1789 }
1790
1791 static clib_error_t * unix_cli_write_ready (unix_file_t * uf)
1792 {
1793   unix_cli_main_t * cm = &unix_cli_main;
1794   unix_cli_file_t * cf;
1795   int n;
1796
1797   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
1798
1799   /* Flush output vector. */
1800   n = write (uf->file_descriptor,
1801              cf->output_vector, vec_len (cf->output_vector));
1802
1803   if (n < 0 && errno != EAGAIN)
1804     return clib_error_return_unix (0, "write");
1805
1806   else if (n > 0)
1807     unix_cli_del_pending_output (uf, cf, n);
1808
1809   return /* no error */ 0;
1810 }
1811
1812 static clib_error_t * unix_cli_read_ready (unix_file_t * uf)
1813 {
1814   unix_main_t * um = &unix_main;
1815   unix_cli_main_t * cm = &unix_cli_main;
1816   unix_cli_file_t * cf;
1817   uword l;
1818   int n, n_read, n_try;
1819
1820   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
1821
1822   n = n_try = 4096;
1823   while (n == n_try) {
1824       l = vec_len (cf->input_vector);
1825       vec_resize (cf->input_vector, l + n_try);
1826
1827       n = read (uf->file_descriptor, cf->input_vector + l, n_try);
1828
1829       /* Error? */
1830       if (n < 0 && errno != EAGAIN)
1831           return clib_error_return_unix (0, "read");
1832   
1833       n_read = n < 0 ? 0 : n;
1834       _vec_len (cf->input_vector) = l + n_read;
1835   }
1836
1837   if (! (n < 0))
1838     vlib_process_signal_event (um->vlib_main,
1839                                cf->process_node_index,
1840                                (n_read == 0
1841                                 ? UNIX_CLI_PROCESS_EVENT_QUIT
1842                                 : UNIX_CLI_PROCESS_EVENT_READ_READY),
1843                                /* event data */ uf->private_data);
1844
1845   return /* no error */ 0;
1846 }
1847
1848 static u32 unix_cli_file_add (unix_cli_main_t * cm, char * name, int fd)
1849 {
1850   unix_main_t * um = &unix_main;
1851   unix_cli_file_t * cf;
1852   unix_file_t template = {0};
1853   vlib_main_t * vm = um->vlib_main;
1854   vlib_node_t * n;
1855
1856   name = (char *) format (0, "unix-cli-%s", name);
1857
1858   if (vec_len (cm->unused_cli_process_node_indices) > 0)
1859     {
1860       uword l = vec_len (cm->unused_cli_process_node_indices);
1861
1862       /* Find node and give it new name. */
1863       n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);
1864       vec_free (n->name);
1865       n->name = (u8 *) name;
1866
1867       vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
1868
1869       _vec_len (cm->unused_cli_process_node_indices) = l - 1;
1870     }
1871   else
1872     {
1873       static vlib_node_registration_t r = {
1874         .function = unix_cli_process,
1875         .type = VLIB_NODE_TYPE_PROCESS,
1876         .process_log2_n_stack_bytes = 14,
1877       };
1878
1879       r.name = name;
1880       vlib_register_node (vm, &r);
1881       vec_free (name);
1882
1883       n = vlib_get_node (vm, r.index);
1884     }
1885
1886   pool_get (cm->cli_file_pool, cf);
1887   memset (cf, 0, sizeof (*cf));
1888
1889   template.read_function = unix_cli_read_ready;
1890   template.write_function = unix_cli_write_ready;
1891   template.file_descriptor = fd;
1892   template.private_data = cf - cm->cli_file_pool;
1893
1894   cf->process_node_index = n->index;
1895   cf->unix_file_index = unix_file_add (um, &template);
1896   cf->output_vector = 0;
1897   cf->input_vector = 0;
1898
1899   vlib_start_process (vm, n->runtime_index);
1900
1901   vlib_process_t * p = vlib_get_process_from_node(vm, n);
1902   p->output_function = unix_vlib_cli_output;
1903   p->output_function_arg = cf - cm->cli_file_pool;
1904
1905   return cf - cm->cli_file_pool;
1906 }
1907
1908 static clib_error_t * unix_cli_listen_read_ready (unix_file_t * uf)
1909 {
1910   unix_main_t * um = &unix_main;
1911   unix_cli_main_t * cm = &unix_cli_main;
1912   clib_socket_t * s = &um->cli_listen_socket;
1913   clib_socket_t client;
1914   char * client_name;
1915   clib_error_t * error;
1916   unix_cli_file_t * cf;
1917   u32 cf_index;
1918
1919   error = clib_socket_accept (s, &client);
1920   if (error)
1921     return error;
1922
1923   client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
1924
1925   cf_index = unix_cli_file_add (cm, client_name, client.fd);
1926   cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
1927
1928   /* No longer need CLIB version of socket. */
1929   clib_socket_free (&client);
1930
1931   vec_free (client_name);
1932
1933   /* if we're supposed to run telnet session in character mode (default) */
1934   if (um->cli_line_mode == 0)
1935     {
1936       /*
1937        * Set telnet client character mode, echo on, suppress "go-ahead".
1938        * Technically these should be negotiated, but this works.
1939        */
1940       u8 charmode_option[] = {
1941         IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */
1942         IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */
1943         IAC, WILL, TELOPT_SGA,      /* server willl supress GA */
1944         IAC, DO,   TELOPT_SGA,      /* client should supress Go Ahead */
1945         IAC, WILL, TELOPT_ECHO,     /* server will do echo */
1946         IAC, DONT, TELOPT_ECHO,     /* client should not echo */
1947         IAC, DO,   TELOPT_TTYPE,    /* client should tell us its term type */
1948         IAC, SB,   TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */
1949         IAC, DO,   TELOPT_NAWS,     /* client should tell us its window sz */
1950         IAC, SB,   TELOPT_NAWS, 1, IAC, SE,  /* now tell me window size */
1951       };
1952
1953       /* Enable history on this CLI */
1954       cf->history_limit = um->cli_history_limit;
1955       cf->has_history = cf->history_limit != 0;
1956
1957       /* Make sure this session is in line mode */
1958       cf->line_mode = 0;
1959
1960       /* We need CRLF */
1961       cf->crlf_mode = 1;
1962
1963       /* Setup the pager */
1964       cf->no_pager = um->cli_no_pager;
1965
1966       uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
1967
1968       /* Send the telnet options */
1969       unix_vlib_cli_output_raw (cf, uf, charmode_option,
1970                                    ARRAY_LEN(charmode_option));
1971
1972       /* In case the client doesn't negotiate terminal type, use
1973        * a timer to kick off the initial prompt. */
1974       timer_call (unix_cli_file_welcome_timer, cf_index, 1);
1975     }
1976
1977   return error;
1978 }
1979
1980 static void
1981 unix_cli_resize_interrupt (int signum)
1982 {
1983   unix_cli_main_t * cm = &unix_cli_main;
1984   unix_cli_file_t * cf = pool_elt_at_index (cm->cli_file_pool,
1985                                 cm->stdin_cli_file_index);
1986   struct winsize ws;
1987   (void)signum;
1988
1989   /* Terminal resized, fetch the new size */
1990   ioctl(UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws);
1991   cf->width = ws.ws_col;
1992   cf->height = ws.ws_row;
1993 }
1994
1995 static clib_error_t *
1996 unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
1997 {
1998   unix_main_t * um = &unix_main;
1999   unix_cli_main_t * cm = &unix_cli_main;
2000   int flags;
2001   clib_error_t * error = 0;
2002   unix_cli_file_t * cf;
2003   u32 cf_index;
2004   struct termios tio;
2005   struct sigaction sa;
2006   struct winsize ws;
2007   u8 * term;
2008
2009   /* We depend on unix flags being set. */
2010   if ((error = vlib_call_config_function (vm, unix_config)))
2011     return error;
2012
2013   if (um->flags & UNIX_FLAG_INTERACTIVE)
2014     {
2015       /* Set stdin to be non-blocking. */
2016       if ((flags = fcntl (UNIX_CLI_STDIN_FD, F_GETFL, 0)) < 0)
2017         flags = 0;
2018       fcntl (UNIX_CLI_STDIN_FD, F_SETFL, flags | O_NONBLOCK);
2019
2020       cf_index = unix_cli_file_add (cm, "stdin", UNIX_CLI_STDIN_FD);
2021       cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2022       cm->stdin_cli_file_index = cf_index;
2023
2024       /* If stdin is a tty and we are using chacracter mode, enable
2025        * history on the CLI and set the tty line discipline accordingly. */
2026       if (isatty(UNIX_CLI_STDIN_FD) && um->cli_line_mode == 0)
2027         {
2028           /* Capture terminal resize events */
2029           sa.sa_handler = unix_cli_resize_interrupt;
2030           sa.sa_flags = 0;
2031           if (sigaction (SIGWINCH, &sa, 0) < 0)
2032               clib_panic ("sigaction");
2033
2034           /* Retrieve the current terminal size */
2035           ioctl(UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws);
2036           cf->width = ws.ws_col;
2037           cf->height = ws.ws_row;
2038
2039           if (cf->width == 0 || cf->height == 0)
2040             /* We have a tty, but no size. Stick to line mode. */
2041             goto notty;
2042
2043           /* Setup the history */
2044           cf->history_limit = um->cli_history_limit;
2045           cf->has_history = cf->history_limit != 0;
2046
2047           /* Setup the pager */
2048           cf->no_pager = um->cli_no_pager;
2049
2050           /* We're going to be in char by char mode */
2051           cf->line_mode = 0;
2052
2053           /* Save the original tty state so we can restore it later */
2054           tcgetattr(UNIX_CLI_STDIN_FD, &um->tio_stdin);
2055           um->tio_isset = 1;
2056
2057           /* Tweak the tty settings */
2058           tio = um->tio_stdin;
2059           /* echo off, canonical mode off, ext'd input processing off */
2060           tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
2061           tio.c_cc[VMIN] = 1; /* 1 byte at a time */
2062           tio.c_cc[VTIME] = 0; /* no timer */
2063           tcsetattr(UNIX_CLI_STDIN_FD, TCSAFLUSH, &tio);
2064
2065           /* See if we can do ANSI/VT100 output */
2066           term = (u8 *)getenv("TERM");
2067           if (term != NULL)
2068             cf->ansi_capable = unix_cli_terminal_type(term,
2069                         strlen((char *)term));
2070         }
2071       else
2072         {
2073         notty:
2074           /* No tty, so make sure these things are off */
2075           cf->no_pager = 1;
2076           cf->history_limit = 0;
2077           cf->has_history = 0;
2078           cf->line_mode = 1;
2079         }
2080
2081       /* Send banner and initial prompt */
2082       unix_cli_file_welcome(cm, cf);
2083     }
2084
2085   /* If we have socket config, LISTEN, otherwise, don't */
2086   clib_socket_t * s = &um->cli_listen_socket;
2087   if(s->config && s->config[0] != 0) {
2088     /* CLI listen. */
2089     unix_file_t template = {0};
2090
2091     s->flags = SOCKET_IS_SERVER; /* listen, don't connect */
2092     error = clib_socket_init (s);
2093
2094     if (error)
2095       return error;
2096
2097     template.read_function = unix_cli_listen_read_ready;
2098     template.file_descriptor = s->fd;
2099
2100     unix_file_add (um, &template);
2101   }
2102
2103   /* Set CLI prompt. */
2104   if (! cm->cli_prompt)
2105     cm->cli_prompt = format (0, "VLIB: ");
2106
2107   return 0;
2108 }
2109
2110 VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
2111
2112 static clib_error_t *
2113 unix_cli_exit (vlib_main_t * vm)
2114 {
2115   unix_main_t * um = &unix_main;
2116
2117   /* If stdin is a tty and we saved the tty state, reset the tty state */
2118   if (isatty(UNIX_CLI_STDIN_FD) && um->tio_isset)
2119     tcsetattr(UNIX_CLI_STDIN_FD, TCSAFLUSH, &um->tio_stdin);
2120
2121   return 0;
2122 }
2123
2124 VLIB_MAIN_LOOP_EXIT_FUNCTION (unix_cli_exit);
2125
2126 void vlib_unix_cli_set_prompt (char * prompt)
2127 {
2128   char * fmt = (prompt[strlen(prompt)-1] == ' ') ? "%s" : "%s ";
2129   unix_cli_main_t * cm = &unix_cli_main;
2130   if (cm->cli_prompt)
2131     vec_free (cm->cli_prompt);
2132   cm->cli_prompt = format (0, fmt, prompt);
2133 }
2134
2135 static clib_error_t *
2136 unix_cli_quit (vlib_main_t * vm,
2137                unformat_input_t * input,
2138                vlib_cli_command_t * cmd)
2139 {
2140   unix_cli_main_t * cm = &unix_cli_main;
2141
2142   vlib_process_signal_event (vm,
2143                              vlib_current_process (vm),
2144                              UNIX_CLI_PROCESS_EVENT_QUIT,
2145                              cm->current_input_file_index);
2146   return 0;
2147 }
2148
2149 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
2150   .path = "quit",
2151   .short_help = "Exit CLI",
2152   .function = unix_cli_quit,
2153 };
2154
2155 static clib_error_t *
2156 unix_cli_exec (vlib_main_t * vm,
2157                unformat_input_t * input,
2158                vlib_cli_command_t * cmd)
2159 {
2160   char * file_name;
2161   int fd;
2162   unformat_input_t sub_input;
2163   clib_error_t * error;
2164
2165   file_name = 0;
2166   fd = -1;
2167   error = 0;
2168
2169   if (! unformat (input, "%s", &file_name))
2170     {
2171       error = clib_error_return (0, "expecting file name, got `%U'",
2172                                  format_unformat_error, input);
2173       goto done;
2174     }
2175
2176   fd = open (file_name, O_RDONLY);
2177   if (fd < 0)
2178     {
2179       error = clib_error_return_unix (0, "failed to open `%s'", file_name);
2180       goto done;
2181     }
2182
2183   /* Make sure its a regular file. */
2184   {
2185     struct stat s;
2186
2187     if (fstat (fd, &s) < 0)
2188       {
2189         error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
2190         goto done;
2191       }
2192     
2193     if (! (S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
2194       {
2195         error = clib_error_return (0, "not a regular file `%s'", file_name);
2196         goto done;
2197       }
2198   }
2199
2200   unformat_init_unix_file (&sub_input, fd);
2201
2202   vlib_cli_input (vm, &sub_input, 0, 0);
2203   unformat_free (&sub_input);
2204
2205  done:
2206   if (fd > 0)
2207     close (fd);
2208   vec_free (file_name);
2209
2210   return error;
2211 }
2212
2213 VLIB_CLI_COMMAND (cli_exec, static) = {
2214   .path = "exec",
2215   .short_help = "Execute commands from file",
2216   .function = unix_cli_exec,
2217   .is_mp_safe = 1,
2218 };
2219
2220 static clib_error_t *
2221 unix_show_errors (vlib_main_t * vm,
2222                   unformat_input_t * input,
2223                   vlib_cli_command_t * cmd)
2224 {
2225   unix_main_t * um = &unix_main;
2226   clib_error_t * error = 0;
2227   int i, n_errors_to_show;
2228   unix_error_history_t * unix_errors = 0;
2229
2230   n_errors_to_show = 1 << 30;
2231
2232   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
2233     {
2234       if (! unformat (input, "%d", &n_errors_to_show))
2235         {
2236           error = clib_error_return (0, "expecting integer number of errors to show, got `%U'",
2237                                      format_unformat_error, input);
2238           goto done;
2239         }
2240     }
2241
2242   n_errors_to_show = clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
2243
2244   i = um->error_history_index > 0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
2245
2246   while (n_errors_to_show > 0)
2247     {
2248       unix_error_history_t * eh = um->error_history + i;
2249
2250       if (! eh->error)
2251         break;
2252
2253       vec_add1 (unix_errors, eh[0]);
2254       n_errors_to_show -= 1;
2255       if (i == 0)
2256         i = ARRAY_LEN (um->error_history) - 1;
2257       else
2258         i--;
2259     }
2260
2261   if (vec_len (unix_errors) == 0)
2262     vlib_cli_output (vm, "no Unix errors so far");
2263   else
2264     {
2265       vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
2266       for (i = vec_len (unix_errors) - 1; i >= 0; i--)
2267         {
2268           unix_error_history_t * eh = vec_elt_at_index (unix_errors, i);
2269           vlib_cli_output (vm, "%U: %U",
2270                            format_time_interval, "h:m:s:u", eh->time,
2271                            format_clib_error, eh->error);
2272         }
2273       vlib_cli_output (vm, "%U: time now",
2274                        format_time_interval, "h:m:s:u", vlib_time_now (vm));
2275     }
2276
2277  done:
2278   vec_free (unix_errors);
2279   return error;
2280 }
2281
2282 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
2283   .path = "show unix-errors",
2284   .short_help = "Show Unix system call error history",
2285   .function = unix_show_errors,
2286 };
2287
2288 static clib_error_t *
2289 unix_cli_show_history (vlib_main_t * vm,
2290       unformat_input_t * input,
2291       vlib_cli_command_t * cmd)
2292 {
2293   unix_cli_main_t * cm = &unix_cli_main;
2294   unix_cli_file_t * cf;
2295   int i, j;
2296
2297   cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
2298
2299   if (cf->has_history && cf->history_limit)
2300     {
2301       i = 1 + cf->command_number - vec_len(cf->command_history);
2302       for (j = 0; j < vec_len (cf->command_history); j++)
2303           vlib_cli_output (vm, "%d  %v\n", i + j, cf->command_history[j]);
2304     }
2305   else
2306     {
2307       vlib_cli_output (vm, "History not enabled.\n");
2308     }
2309
2310   return 0;
2311 }
2312
2313 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
2314   .path = "history",
2315   .short_help = "Show current session command history",
2316   .function = unix_cli_show_history,
2317 };
2318
2319 static clib_error_t *
2320 unix_cli_show_terminal (vlib_main_t * vm,
2321       unformat_input_t * input,
2322       vlib_cli_command_t * cmd)
2323 {
2324   unix_main_t * um = &unix_main;
2325   unix_cli_main_t * cm = &unix_cli_main;
2326   unix_cli_file_t * cf;
2327   vlib_node_t * n;
2328
2329   cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
2330   n = vlib_get_node (vm, cf->process_node_index);
2331
2332   vlib_cli_output (vm, "Terminal name:   %v\n", n->name);
2333   vlib_cli_output (vm, "Terminal mode:   %s\n", cf->line_mode ? 
2334                                                 "line-by-line" :
2335                                                 "char-by-char");
2336   vlib_cli_output (vm, "Terminal width:  %d\n", cf->width);
2337   vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
2338   vlib_cli_output (vm, "ANSI capable:    %s\n", cf->ansi_capable ? "yes" : "no");
2339   vlib_cli_output (vm, "History enabled: %s%s\n",
2340         cf->has_history ? "yes" : "no",
2341         !cf->has_history || cf->history_limit ? "" : " (disabled by history limit)");
2342   if (cf->has_history)
2343     vlib_cli_output (vm, "History limit:   %d\n", cf->history_limit);
2344   vlib_cli_output (vm, "Pager enabled:   %s%s%s\n",
2345         cf->no_pager ? "no" : "yes",
2346         cf->no_pager || cf->height ? "" : " (disabled by terminal height)",
2347         cf->no_pager || um->cli_pager_buffer_limit ? "" : " (disabled by buffer limit)");
2348   if (!cf->no_pager)
2349     vlib_cli_output (vm, "Pager limit:     %d\n", um->cli_pager_buffer_limit);
2350   vlib_cli_output (vm, "CRLF mode:       %s\n", cf->crlf_mode ? "CR+LF" : "LF");
2351
2352   return 0;
2353 }
2354
2355 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
2356   .path = "show terminal",
2357   .short_help = "Show current session terminal settings",
2358   .function = unix_cli_show_terminal,
2359 };
2360
2361 static clib_error_t *
2362 unix_cli_set_terminal_pager (vlib_main_t * vm,
2363       unformat_input_t * input,
2364       vlib_cli_command_t * cmd)
2365 {
2366   unix_main_t * um = &unix_main;
2367   unix_cli_main_t * cm = &unix_cli_main;
2368   unix_cli_file_t * cf;
2369   unformat_input_t _line_input, * line_input = &_line_input;
2370
2371   if (! unformat_user (input, unformat_line_input, line_input))
2372     return 0;
2373
2374   cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
2375
2376   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2377     {
2378     if (unformat (line_input, "on"))
2379       cf->no_pager = 0;
2380     else if (unformat (line_input, "off"))
2381       cf->no_pager = 1;
2382     else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
2383       vlib_cli_output (vm, "Pager limit set to %u lines; note, this is global.\n",
2384         um->cli_pager_buffer_limit);
2385     else
2386       return clib_error_return (0, "unknown parameter: `%U`",
2387                                 format_unformat_error, line_input);
2388   }
2389
2390   unformat_free(line_input);
2391
2392   return 0;
2393 }
2394
2395 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
2396   .path = "set terminal pager",
2397   .short_help = "set terminal pager [on|off] [limit <lines>]",
2398   .function = unix_cli_set_terminal_pager,
2399 };
2400
2401 static clib_error_t *
2402 unix_cli_set_terminal_history (vlib_main_t * vm,
2403       unformat_input_t * input,
2404       vlib_cli_command_t * cmd)
2405 {
2406   unix_cli_main_t * cm = &unix_cli_main;
2407   unix_cli_file_t * cf;
2408   unformat_input_t _line_input, * line_input = &_line_input;
2409   u32 limit;
2410
2411   if (! unformat_user (input, unformat_line_input, line_input))
2412     return 0;
2413
2414   cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
2415
2416   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2417     {
2418     if (unformat (line_input, "on"))
2419       cf->has_history = 1;
2420     else if (unformat (line_input, "off"))
2421       cf->has_history = 0;
2422     else if (unformat (line_input, "limit %u", &cf->history_limit))
2423       ;
2424     else
2425       return clib_error_return (0, "unknown parameter: `%U`",
2426                                 format_unformat_error, line_input);
2427
2428     /* If we reduced history size, or turned it off, purge the history */
2429     limit = cf->has_history ? cf->history_limit : 0;
2430
2431     while (cf->command_history && vec_len(cf->command_history) >= limit)
2432       {
2433         vec_free (cf->command_history[0]);
2434         vec_delete (cf->command_history, 1, 0);
2435       }
2436   }
2437
2438   unformat_free(line_input);
2439
2440   return 0;
2441 }
2442
2443 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
2444   .path = "set terminal history",
2445   .short_help = "set terminal history [on|off] [limit <lines>]",
2446   .function = unix_cli_set_terminal_history,
2447 };
2448
2449 static clib_error_t *
2450 unix_cli_set_terminal_ansi (vlib_main_t * vm,
2451       unformat_input_t * input,
2452       vlib_cli_command_t * cmd)
2453 {
2454   unix_cli_main_t * cm = &unix_cli_main;
2455   unix_cli_file_t * cf;
2456
2457   cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
2458
2459   if (unformat (input, "on"))
2460     cf->ansi_capable = 1;
2461   else if (unformat (input, "off"))
2462     cf->ansi_capable = 0;
2463   else
2464     return clib_error_return (0, "unknown parameter: `%U`",
2465                               format_unformat_error, input);
2466
2467   return 0;
2468 }
2469
2470 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
2471   .path = "set terminal ansi",
2472   .short_help = "set terminal ansi [on|off]",
2473   .function = unix_cli_set_terminal_ansi,
2474 };
2475
2476 static clib_error_t *
2477 unix_cli_init (vlib_main_t * vm)
2478 {
2479   return 0;
2480 }
2481
2482 VLIB_INIT_FUNCTION (unix_cli_init);