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