Initial commit of vpp code.
[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
43 #include <fcntl.h>
44 #include <sys/stat.h>
45 #include <termios.h>
46 #include <unistd.h>
47 #include <arpa/telnet.h>
48
49 typedef struct {
50   u32 unix_file_index;
51
52   /* Vector of output pending write to file descriptor. */
53   u8 * output_vector;
54
55   /* Vector of input saved by Unix input node to be processed by
56      CLI process. */
57   u8 * input_vector;
58
59   u8 has_history;
60   u8 ** command_history;
61   u8 * current_command;
62   i32 excursion;
63   u32 history_limit;
64   u8 * search_key;
65   int search_mode;
66
67   u32 process_node_index;
68 } unix_cli_file_t;
69
70 always_inline void
71 unix_cli_file_free (unix_cli_file_t * f)
72 {
73   vec_free (f->output_vector);
74   vec_free (f->input_vector);
75 }
76
77 typedef struct {
78   /* Prompt string for CLI. */
79   u8 * cli_prompt;
80
81   unix_cli_file_t * cli_file_pool;
82
83   u32 * unused_cli_process_node_indices;
84
85   /* File pool index of current input. */
86   u32 current_input_file_index;
87 } unix_cli_main_t;
88
89 static unix_cli_main_t unix_cli_main;
90
91 static void
92 unix_cli_add_pending_output (unix_file_t * uf,
93                              unix_cli_file_t * cf,
94                              u8 * buffer,
95                              uword buffer_bytes)
96 {
97   unix_main_t * um = &unix_main;
98
99   vec_add (cf->output_vector, buffer, buffer_bytes);
100   if (vec_len (cf->output_vector) > 0)
101     {
102       int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
103       uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
104       if (! skip_update)
105         um->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
106     }
107 }
108
109 static void
110 unix_cli_del_pending_output (unix_file_t * uf,
111                              unix_cli_file_t * cf,
112                              uword n_bytes)
113 {
114   unix_main_t * um = &unix_main;
115
116   vec_delete (cf->output_vector, n_bytes, 0);
117   if (vec_len (cf->output_vector) <= 0)
118     {
119       int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
120       uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
121       if (! skip_update)
122         um->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
123     }
124 }
125
126 /* VLIB cli output function. */
127 static void unix_vlib_cli_output (uword cli_file_index,
128                                   u8 * buffer,
129                                   uword buffer_bytes)
130 {
131   unix_main_t * um = &unix_main;
132   unix_cli_main_t * cm = &unix_cli_main;
133   unix_cli_file_t * cf;
134   unix_file_t * uf;
135   int n;
136  
137   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
138   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
139   n = 0;
140   if (vec_len (cf->output_vector) == 0)
141     n = write (uf->file_descriptor, buffer, buffer_bytes);
142   if (n < 0 && errno != EAGAIN)
143     clib_unix_warning ("write");
144
145   else if ((word) n < (word) buffer_bytes)
146     {
147       if (n < 0) n = 0;
148       unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
149     }
150 }
151
152 static int unix_cli_line_edit (unix_main_t * um, unix_cli_file_t * cf)
153 {
154   unix_file_t * uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
155   u8 * prev;
156   int i, j, delta;
157
158   for (i = 0; i < vec_len (cf->input_vector); i++)
159     {
160       switch (cf->input_vector[i])
161         {
162         case 0:
163           continue;
164             
165         case '?':
166           /* Erase the current command (if any) plus ?*/
167           for (j = 0; j < (vec_len (cf->current_command)+1); j++)
168             unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
169           
170           unix_cli_add_pending_output (uf, cf, (u8 *) "\r\nHistory:\r\n", 12);
171
172           for (j = 0; j < vec_len (cf->command_history); j++)
173             {
174               unix_cli_add_pending_output (uf, cf, cf->command_history[j],
175                                            vec_len(cf->command_history[j]));
176               unix_cli_add_pending_output (uf, cf, (u8 *) "\r\n", 2);
177             }
178           goto crlf;
179
180           /* ^R - reverse search */
181         case 'R' - '@':
182         case 'S' - '@':
183           if (cf->search_mode == 0)
184             {
185               /* Erase the current command (if any) plus ^R */
186               for (j = 0; j < (vec_len (cf->current_command)+2); j++)
187                   unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
188               
189               vec_reset_length (cf->search_key);
190               vec_reset_length (cf->current_command);
191               if (cf->input_vector[i] == 'R' - '@')
192                   cf->search_mode = -1;
193               else
194                   cf->search_mode = 1;
195             }
196           else
197             {
198               if (cf->input_vector[i] == 'R' - '@')
199                 cf->search_mode = -1;
200               else
201                 cf->search_mode = 1;
202
203               cf->excursion += cf->search_mode;
204               unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
205               goto search_again;
206             }
207           break;
208
209           /* ^U - line-kill */
210         case 'U'-'@':
211           /* Erase the command, plus ^U */
212           for (j = 0; j < (vec_len (cf->current_command)+2); j++)
213             unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
214           vec_reset_length (cf->current_command);
215           cf->search_mode = 0;
216           continue;
217
218           /* ^P - previous, ^N - next */
219         case 'P' - '@':
220         case 'N' - '@':
221           cf->search_mode = 0;
222           /* Erase the command, plus ^P */
223           for (j = 0; j < (vec_len (cf->current_command)+2); j++)
224             unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
225           vec_reset_length (cf->current_command);
226           if (vec_len (cf->command_history))
227             {
228               if (cf->input_vector[i] == 'P' - '@')
229                 delta = -1;
230               else
231                 delta = 1;
232
233               cf->excursion += delta;
234
235               if (cf->excursion > (i32) vec_len (cf->command_history) -1)
236                 cf->excursion = 0;
237               else if (cf->excursion < 0)
238                 cf->excursion = vec_len (cf->command_history) -1;
239
240               prev = cf->command_history [cf->excursion];
241               vec_validate (cf->current_command, vec_len(prev)-1);
242
243               memcpy (cf->current_command, prev, vec_len(prev));
244               _vec_len (cf->current_command) = vec_len(prev);
245               unix_cli_add_pending_output (uf, cf, cf->current_command,
246                                            vec_len (cf->current_command));
247               break;
248             }
249           break;
250
251         case 0x7f:
252         case 'H' - '@':
253           for (j = 0; j < 2; j++)
254             unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
255           if (vec_len (cf->current_command))
256             {
257               unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
258               _vec_len (cf->current_command)--;
259             }
260           cf->search_mode = 0;
261           cf->excursion = 0;
262           cf->search_mode = 0;
263           vec_reset_length (cf->search_key);
264           break;
265
266         case '\r':
267         case '\n':
268         crlf:
269           vec_add1 (cf->current_command, '\r');
270           vec_add1 (cf->current_command, '\n');
271           unix_cli_add_pending_output (uf, cf, (u8 *) "\b\b  \b\b\r\n", 8);
272
273           vec_validate (cf->input_vector, vec_len(cf->current_command)-1);
274           memcpy (cf->input_vector, cf->current_command, 
275                   vec_len(cf->current_command));
276           _vec_len(cf->input_vector) = _vec_len (cf->current_command);
277
278           if (vec_len(cf->command_history) >= cf->history_limit)
279             {
280               vec_free (cf->command_history[0]);
281               vec_delete (cf->command_history, 1, 0);
282             }
283           /* Don't add blank lines to the cmd history */
284           if (vec_len (cf->current_command) > 2)
285             {
286               _vec_len (cf->current_command) -= 2;
287               vec_add1 (cf->command_history, cf->current_command);
288               cf->current_command = 0;
289             }
290           else
291             vec_reset_length (cf->current_command);
292           cf->excursion = 0;
293           cf->search_mode = 0;
294           vec_reset_length (cf->search_key);
295           return 0;
296
297           /* telnet "mode character" blort, echo but don't process. */
298         case 0xff:
299             unix_cli_add_pending_output (uf, cf, cf->input_vector + i, 
300                                          6);
301             i += 6;
302             continue;
303
304         default:
305           if (cf->search_mode)
306             {
307               int j, k, limit, offset;
308               u8 * item;
309
310               vec_add1 (cf->search_key, cf->input_vector[i]);
311
312             search_again:
313               for (j = 0; j < vec_len(cf->command_history); j++)
314                 {
315                   if (cf->excursion > (i32) vec_len (cf->command_history) -1)
316                     cf->excursion = 0;
317                   else if (cf->excursion < 0)
318                     cf->excursion = vec_len (cf->command_history) -1;
319
320                   item = cf->command_history[cf->excursion];
321
322                   limit = (vec_len(cf->search_key) > vec_len (item)) ?
323                     vec_len(item) : vec_len (cf->search_key);
324
325                   for (offset = 0; offset <= vec_len(item) - limit; offset++)
326                     {
327                       for (k = 0; k < limit; k++)
328                         {
329                           if (item[k+offset] != cf->search_key[k])
330                             goto next_offset;
331                         }
332                       goto found_at_offset;
333
334                     next_offset:
335                       ;
336                     }
337                   goto next;
338
339                 found_at_offset:
340                   for (j = 0; j < vec_len (cf->current_command)+1; j++)
341                     unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
342
343                   vec_validate (cf->current_command, vec_len(item)-1);
344
345                   memcpy (cf->current_command, item, vec_len(item));
346                   _vec_len (cf->current_command) = vec_len(item);
347                   unix_cli_add_pending_output (uf, cf, cf->current_command,
348                                                vec_len (cf->current_command));
349                   goto found;
350
351                 next:
352                   cf->excursion += cf->search_mode;
353                 }
354               
355               unix_cli_add_pending_output (uf, cf, (u8 *)"\r\nno match..", 12);
356               vec_reset_length (cf->search_key);
357               vec_reset_length (cf->current_command);
358               cf->search_mode = 0;
359               goto crlf;
360             }
361           else
362             vec_add1 (cf->current_command, cf->input_vector[i]);
363
364         found:
365
366           break;
367         }
368     }
369   vec_reset_length(cf->input_vector);
370   return 1;
371 }
372
373 static void unix_cli_process_input (unix_cli_main_t * cm, uword cli_file_index)
374 {
375   unix_main_t * um = &unix_main;
376   unix_file_t * uf;
377   unix_cli_file_t * cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
378   unformat_input_t input;
379   int vlib_parse_eval (u8 *);
380
381   /* Try vlibplex first.  Someday... */
382   if (0 && vlib_parse_eval (cf->input_vector) == 0)
383       goto done;
384
385   /* Line edit, echo, etc. */
386   if (cf->has_history && unix_cli_line_edit (um, cf))
387     return;
388
389   if (um->log_fd)
390     {
391       static u8 * lv;
392       vec_reset_length (lv);
393       lv = format (lv, "%U[%d]: %v", 
394                    format_timeval,
395                    0 /* current bat-time */,
396                    0 /* current bat-format */,
397                    cli_file_index,
398                    cf->input_vector);
399       {
400         int rv __attribute__((unused)) = 
401           write (um->log_fd, lv, vec_len(lv));
402       }
403     }
404
405   unformat_init_vector (&input, cf->input_vector);
406
407   /* Remove leading white space from input. */
408   (void) unformat (&input, "");
409
410   cm->current_input_file_index = cli_file_index;
411
412   if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
413     vlib_cli_input (um->vlib_main, &input, unix_vlib_cli_output, cli_file_index);
414
415   /* Re-fetch pointer since pool may have moved. */
416   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
417
418   /* Zero buffer since otherwise unformat_free will call vec_free on it. */
419   input.buffer = 0;
420
421   unformat_free (&input);
422
423   /* Re-use input vector. */
424 done:
425   _vec_len (cf->input_vector) = 0;
426
427   /* Prompt. */
428   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
429   unix_cli_add_pending_output (uf, cf,
430                                cm->cli_prompt,
431                                vec_len (cm->cli_prompt));
432 }
433
434 static void unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
435 {
436   unix_main_t * um = &unix_main;
437   unix_cli_file_t * cf;
438   unix_file_t * uf;
439   int i;
440
441   cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
442   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
443
444   /* Quit/EOF on stdin means quit program. */
445   if (uf->file_descriptor == 0)
446     clib_longjmp (&um->vlib_main->main_loop_exit, VLIB_MAIN_LOOP_EXIT_CLI);
447
448   vec_free (cf->current_command);
449   vec_free (cf->search_key);
450
451   for (i = 0; i < vec_len (cf->command_history); i++)
452       vec_free (cf->command_history[i]);
453
454   vec_free (cf->command_history);
455
456   unix_file_del (um, uf);
457
458   unix_cli_file_free (cf);
459   pool_put (cm->cli_file_pool, cf);
460 }
461
462 typedef enum {
463   UNIX_CLI_PROCESS_EVENT_READ_READY,
464   UNIX_CLI_PROCESS_EVENT_QUIT,
465 } unix_cli_process_event_type_t;
466
467 static uword
468 unix_cli_process (vlib_main_t * vm,
469                   vlib_node_runtime_t * rt,
470                   vlib_frame_t * f)
471 {
472   unix_cli_main_t * cm = &unix_cli_main;
473   uword i, * data = 0;
474
475   while (1)
476     {
477       unix_cli_process_event_type_t event_type;
478       vlib_process_wait_for_event (vm);
479       event_type = vlib_process_get_events (vm, &data);
480
481       switch (event_type)
482         {
483         case UNIX_CLI_PROCESS_EVENT_READ_READY:
484           for (i = 0; i < vec_len (data); i++)
485             unix_cli_process_input (cm, data[i]);
486           break;
487
488         case UNIX_CLI_PROCESS_EVENT_QUIT:
489           /* Kill this process. */
490           for (i = 0; i < vec_len (data); i++)
491             unix_cli_kill (cm, data[i]);
492           goto done;
493         }
494
495       if (data)
496         _vec_len (data) = 0;
497     }
498
499  done:
500   vec_free (data);
501
502   vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
503
504   /* Add node index so we can re-use this process later. */
505   vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);
506
507   return 0;
508 }
509
510 static clib_error_t * unix_cli_write_ready (unix_file_t * uf)
511 {
512   unix_cli_main_t * cm = &unix_cli_main;
513   unix_cli_file_t * cf;
514   int n;
515
516   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
517
518   /* Flush output vector. */
519   n = write (uf->file_descriptor,
520              cf->output_vector, vec_len (cf->output_vector));
521
522   if (n < 0 && errno != EAGAIN)
523     return clib_error_return_unix (0, "write");
524
525   else if (n > 0)
526     unix_cli_del_pending_output (uf, cf, n);
527
528   return /* no error */ 0;
529 }
530
531 static clib_error_t * unix_cli_read_ready (unix_file_t * uf)
532 {
533   unix_main_t * um = &unix_main;
534   unix_cli_main_t * cm = &unix_cli_main;
535   unix_cli_file_t * cf;
536   uword l;
537   int n, n_read, n_try;
538
539   cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
540
541   n = n_try = 4096;
542   while (n == n_try) {
543       l = vec_len (cf->input_vector);
544       vec_resize (cf->input_vector, l + n_try);
545
546       n = read (uf->file_descriptor, cf->input_vector + l, n_try);
547
548       /* Error? */
549       if (n < 0 && errno != EAGAIN)
550           return clib_error_return_unix (0, "read");
551   
552       n_read = n < 0 ? 0 : n;
553       _vec_len (cf->input_vector) = l + n_read;
554   }
555
556   if (! (n < 0))
557     vlib_process_signal_event (um->vlib_main,
558                                cf->process_node_index,
559                                (n_read == 0
560                                 ? UNIX_CLI_PROCESS_EVENT_QUIT
561                                 : UNIX_CLI_PROCESS_EVENT_READ_READY),
562                                /* event data */ uf->private_data);
563
564   return /* no error */ 0;
565 }
566
567 static u32 unix_cli_file_add (unix_cli_main_t * cm, char * name, int fd)
568 {
569   unix_main_t * um = &unix_main;
570   unix_cli_file_t * cf;
571   unix_file_t * uf, template = {0};
572   vlib_main_t * vm = um->vlib_main;
573   vlib_node_t * n;
574
575   name = (char *) format (0, "unix-cli-%s", name);
576
577   if (vec_len (cm->unused_cli_process_node_indices) > 0)
578     {
579       uword l = vec_len (cm->unused_cli_process_node_indices);
580
581       /* Find node and give it new name. */
582       n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);
583       vec_free (n->name);
584       n->name = (u8 *) name;
585
586       vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
587
588       _vec_len (cm->unused_cli_process_node_indices) = l - 1;
589     }
590   else
591     {
592       static vlib_node_registration_t r = {
593         .function = unix_cli_process,
594         .type = VLIB_NODE_TYPE_PROCESS,
595         .process_log2_n_stack_bytes = 14,
596       };
597
598       r.name = name;
599       vlib_register_node (vm, &r);
600       vec_free (name);
601
602       n = vlib_get_node (vm, r.index);
603     }
604
605   pool_get (cm->cli_file_pool, cf);
606   memset (cf, 0, sizeof (*cf));
607
608   template.read_function = unix_cli_read_ready;
609   template.write_function = unix_cli_write_ready;
610   template.file_descriptor = fd;
611   template.private_data = cf - cm->cli_file_pool;
612
613   cf->process_node_index = n->index;
614   cf->unix_file_index = unix_file_add (um, &template);
615   cf->output_vector = 0;
616   cf->input_vector = 0;
617
618   uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
619
620   /* Prompt. */
621   unix_cli_add_pending_output (uf, cf,
622                                cm->cli_prompt, vec_len (cm->cli_prompt));
623
624   vlib_start_process (vm, n->runtime_index);
625   return cf - cm->cli_file_pool;
626 }
627
628 static clib_error_t * unix_cli_listen_read_ready (unix_file_t * uf)
629 {
630   unix_main_t * um = &unix_main;
631   unix_cli_main_t * cm = &unix_cli_main;
632   clib_socket_t * s = &um->cli_listen_socket;
633   clib_socket_t client;
634   char * client_name;
635   clib_error_t * error;
636   unix_cli_file_t * cf;
637   u32 cf_index;
638
639   error = clib_socket_accept (s, &client);
640   if (error)
641     return error;
642
643   client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
644
645   cf_index = unix_cli_file_add (cm, client_name, client.fd);
646   cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
647
648   /* No longer need CLIB version of socket. */
649   clib_socket_free (&client);
650
651   vec_free (client_name);
652
653   /* if we're supposed to run telnet session in character mode (default) */
654   if (um->cli_line_mode == 0)
655     {
656       u8 charmode_option[6];
657
658       cf->has_history = 1;
659       cf->history_limit = um->cli_history_limit ? um->cli_history_limit : 50;
660
661       /* 
662        * Set telnet client character mode, echo on, suppress "go-ahead" 
663        * Empirically, this sequence works. YMMV.
664        */
665
666       /* Tell the client no linemode, echo */
667       charmode_option[0] = IAC;
668       charmode_option[1] = DONT;
669       charmode_option[2] = TELOPT_LINEMODE;
670       charmode_option[3] = IAC;
671       charmode_option[4] = DO;
672       charmode_option[5] = TELOPT_SGA;
673       
674       uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
675       
676       unix_cli_add_pending_output (uf, cf, charmode_option, 
677                                    ARRAY_LEN(charmode_option));
678     }
679
680   return error;
681 }
682
683 static clib_error_t *
684 unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
685 {
686   unix_main_t * um = &unix_main;
687   unix_cli_main_t * cm = &unix_cli_main;
688   int flags, standard_input_fd;
689   clib_error_t * error;
690
691   /* We depend on unix flags being set. */
692   if ((error = vlib_call_config_function (vm, unix_config)))
693     return error;
694
695   if (um->flags & UNIX_FLAG_INTERACTIVE)
696     {
697       standard_input_fd = 0;
698
699       /* Set stdin to be non-blocking. */
700       if ((flags = fcntl (standard_input_fd, F_GETFL, 0)) < 0)
701         flags = 0;
702       fcntl (standard_input_fd, F_SETFL, flags | O_NONBLOCK);
703
704       unix_cli_file_add (cm, "stdin", standard_input_fd);
705     }
706
707   {
708     /* CLI listen. */
709     clib_socket_t * s = &um->cli_listen_socket;
710     unix_file_t template = {0};
711
712     s->flags = SOCKET_IS_SERVER; /* listen, don't connect */
713
714     error = clib_socket_init (s);
715     if (error)
716       return error;
717
718     template.read_function = unix_cli_listen_read_ready;
719     template.file_descriptor = s->fd;
720
721     unix_file_add (um, &template);
722   }
723
724   /* Set CLI prompt. */
725   if (! cm->cli_prompt)
726     cm->cli_prompt = format (0, "VLIB: ");
727
728   return 0;
729 }
730
731 VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
732
733 void vlib_unix_cli_set_prompt (char * prompt)
734 {
735   char * fmt = (prompt[strlen(prompt)-1] == ' ') ? "%s" : "%s ";
736   unix_cli_main_t * cm = &unix_cli_main;
737   if (cm->cli_prompt)
738     vec_free (cm->cli_prompt);
739   cm->cli_prompt = format (0, fmt, prompt);
740 }
741
742 static clib_error_t *
743 unix_cli_quit (vlib_main_t * vm,
744                unformat_input_t * input,
745                vlib_cli_command_t * cmd)
746 {
747   unix_cli_main_t * cm = &unix_cli_main;
748
749   vlib_process_signal_event (vm,
750                              vlib_current_process (vm),
751                              UNIX_CLI_PROCESS_EVENT_QUIT,
752                              cm->current_input_file_index);
753   return 0;
754 }
755
756 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
757   .path = "quit",
758   .short_help = "Exit CLI",
759   .function = unix_cli_quit,
760 };
761
762 static clib_error_t *
763 unix_cli_exec (vlib_main_t * vm,
764                unformat_input_t * input,
765                vlib_cli_command_t * cmd)
766 {
767   char * file_name;
768   int fd;
769   unformat_input_t sub_input;
770   clib_error_t * error;
771
772   file_name = 0;
773   fd = -1;
774   error = 0;
775
776   if (! unformat (input, "%s", &file_name))
777     {
778       error = clib_error_return (0, "expecting file name, got `%U'",
779                                  format_unformat_error, input);
780       goto done;
781     }
782
783   fd = open (file_name, O_RDONLY);
784   if (fd < 0)
785     {
786       error = clib_error_return_unix (0, "failed to open `%s'", file_name);
787       goto done;
788     }
789
790   /* Make sure its a regular file. */
791   {
792     struct stat s;
793
794     if (fstat (fd, &s) < 0)
795       {
796         error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
797         goto done;
798       }
799     
800     if (! (S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
801       {
802         error = clib_error_return (0, "not a regular file `%s'", file_name);
803         goto done;
804       }
805   }
806
807   unformat_init_unix_file (&sub_input, fd);
808
809   vlib_cli_input (vm, &sub_input, 0, 0);
810   unformat_free (&sub_input);
811
812  done:
813   if (fd > 0)
814     close (fd);
815   vec_free (file_name);
816
817   return error;
818 }
819
820 VLIB_CLI_COMMAND (cli_exec, static) = {
821   .path = "exec",
822   .short_help = "Execute commands from file",
823   .function = unix_cli_exec,
824 };
825
826 static clib_error_t *
827 unix_show_errors (vlib_main_t * vm,
828                   unformat_input_t * input,
829                   vlib_cli_command_t * cmd)
830 {
831   unix_main_t * um = &unix_main;
832   clib_error_t * error = 0;
833   int i, n_errors_to_show;
834   unix_error_history_t * unix_errors = 0;
835
836   n_errors_to_show = 1 << 30;
837
838   if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
839     {
840       if (! unformat (input, "%d", &n_errors_to_show))
841         {
842           error = clib_error_return (0, "expecting integer number of errors to show, got `%U'",
843                                      format_unformat_error, input);
844           goto done;
845         }
846     }
847
848   n_errors_to_show = clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
849
850   i = um->error_history_index > 0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
851
852   while (n_errors_to_show > 0)
853     {
854       unix_error_history_t * eh = um->error_history + i;
855
856       if (! eh->error)
857         break;
858
859       vec_add1 (unix_errors, eh[0]);
860       n_errors_to_show -= 1;
861       if (i == 0)
862         i = ARRAY_LEN (um->error_history) - 1;
863       else
864         i--;
865     }
866
867   if (vec_len (unix_errors) == 0)
868     vlib_cli_output (vm, "no Unix errors so far");
869   else
870     {
871       vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
872       for (i = vec_len (unix_errors) - 1; i >= 0; i--)
873         {
874           unix_error_history_t * eh = vec_elt_at_index (unix_errors, i);
875           vlib_cli_output (vm, "%U: %U",
876                            format_time_interval, "h:m:s:u", eh->time,
877                            format_clib_error, eh->error);
878         }
879       vlib_cli_output (vm, "%U: time now",
880                        format_time_interval, "h:m:s:u", vlib_time_now (vm));
881     }
882
883  done:
884   vec_free (unix_errors);
885   return error;
886 }
887
888 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
889   .path = "show unix-errors",
890   .short_help = "Show Unix system call error history",
891   .function = unix_show_errors,
892 };
893
894 static clib_error_t *
895 unix_cli_init (vlib_main_t * vm)
896 {
897   return 0;
898 }
899
900 VLIB_INIT_FUNCTION (unix_cli_init);