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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 * cli.c: Unix stdin/socket CLI.
18 * Copyright (c) 2008 Eliot Dresselhaus
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:
28 * The above copyright notice and this permission notice shall be
29 * included in all copies or substantial portions of the Software.
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.
40 #include <vlib/vlib.h>
41 #include <vlib/unix/unix.h>
47 #include <arpa/telnet.h>
52 /* Vector of output pending write to file descriptor. */
55 /* Vector of input saved by Unix input node to be processed by
60 u8 ** command_history;
67 u32 process_node_index;
71 unix_cli_file_free (unix_cli_file_t * f)
73 vec_free (f->output_vector);
74 vec_free (f->input_vector);
78 /* Prompt string for CLI. */
81 unix_cli_file_t * cli_file_pool;
83 u32 * unused_cli_process_node_indices;
85 /* File pool index of current input. */
86 u32 current_input_file_index;
89 static unix_cli_main_t unix_cli_main;
92 unix_cli_add_pending_output (unix_file_t * uf,
97 unix_main_t * um = &unix_main;
99 vec_add (cf->output_vector, buffer, buffer_bytes);
100 if (vec_len (cf->output_vector) > 0)
102 int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
103 uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
105 um->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
110 unix_cli_del_pending_output (unix_file_t * uf,
111 unix_cli_file_t * cf,
114 unix_main_t * um = &unix_main;
116 vec_delete (cf->output_vector, n_bytes, 0);
117 if (vec_len (cf->output_vector) <= 0)
119 int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
120 uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
122 um->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
126 /* VLIB cli output function. */
127 static void unix_vlib_cli_output (uword cli_file_index,
131 unix_main_t * um = &unix_main;
132 unix_cli_main_t * cm = &unix_cli_main;
133 unix_cli_file_t * cf;
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);
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");
145 else if ((word) n < (word) buffer_bytes)
148 unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
152 static int unix_cli_line_edit (unix_main_t * um, unix_cli_file_t * cf)
154 unix_file_t * uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
158 for (i = 0; i < vec_len (cf->input_vector); i++)
160 switch (cf->input_vector[i])
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);
170 unix_cli_add_pending_output (uf, cf, (u8 *) "\r\nHistory:\r\n", 12);
172 for (j = 0; j < vec_len (cf->command_history); j++)
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);
180 /* ^R - reverse search */
183 if (cf->search_mode == 0)
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);
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;
198 if (cf->input_vector[i] == 'R' - '@')
199 cf->search_mode = -1;
203 cf->excursion += cf->search_mode;
204 unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
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);
218 /* ^P - previous, ^N - next */
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))
228 if (cf->input_vector[i] == 'P' - '@')
233 cf->excursion += delta;
235 if (cf->excursion > (i32) vec_len (cf->command_history) -1)
237 else if (cf->excursion < 0)
238 cf->excursion = vec_len (cf->command_history) -1;
240 prev = cf->command_history [cf->excursion];
241 vec_validate (cf->current_command, vec_len(prev)-1);
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));
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))
257 unix_cli_add_pending_output (uf, cf, (u8 *) "\b \b", 3);
258 _vec_len (cf->current_command)--;
263 vec_reset_length (cf->search_key);
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);
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);
278 if (vec_len(cf->command_history) >= cf->history_limit)
280 vec_free (cf->command_history[0]);
281 vec_delete (cf->command_history, 1, 0);
283 /* Don't add blank lines to the cmd history */
284 if (vec_len (cf->current_command) > 2)
286 _vec_len (cf->current_command) -= 2;
287 vec_add1 (cf->command_history, cf->current_command);
288 cf->current_command = 0;
291 vec_reset_length (cf->current_command);
294 vec_reset_length (cf->search_key);
297 /* telnet "mode character" blort, echo but don't process. */
299 unix_cli_add_pending_output (uf, cf, cf->input_vector + i,
307 int j, k, limit, offset;
310 vec_add1 (cf->search_key, cf->input_vector[i]);
313 for (j = 0; j < vec_len(cf->command_history); j++)
315 if (cf->excursion > (i32) vec_len (cf->command_history) -1)
317 else if (cf->excursion < 0)
318 cf->excursion = vec_len (cf->command_history) -1;
320 item = cf->command_history[cf->excursion];
322 limit = (vec_len(cf->search_key) > vec_len (item)) ?
323 vec_len(item) : vec_len (cf->search_key);
325 for (offset = 0; offset <= vec_len(item) - limit; offset++)
327 for (k = 0; k < limit; k++)
329 if (item[k+offset] != cf->search_key[k])
332 goto 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);
343 vec_validate (cf->current_command, vec_len(item)-1);
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));
352 cf->excursion += cf->search_mode;
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);
362 vec_add1 (cf->current_command, cf->input_vector[i]);
369 vec_reset_length(cf->input_vector);
373 static void unix_cli_process_input (unix_cli_main_t * cm, uword cli_file_index)
375 unix_main_t * um = &unix_main;
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 *);
381 /* Try vlibplex first. Someday... */
382 if (0 && vlib_parse_eval (cf->input_vector) == 0)
385 /* Line edit, echo, etc. */
386 if (cf->has_history && unix_cli_line_edit (um, cf))
392 vec_reset_length (lv);
393 lv = format (lv, "%U[%d]: %v",
395 0 /* current bat-time */,
396 0 /* current bat-format */,
400 int rv __attribute__((unused)) =
401 write (um->log_fd, lv, vec_len(lv));
405 unformat_init_vector (&input, cf->input_vector);
407 /* Remove leading white space from input. */
408 (void) unformat (&input, "");
410 cm->current_input_file_index = cli_file_index;
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);
415 /* Re-fetch pointer since pool may have moved. */
416 cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
418 /* Zero buffer since otherwise unformat_free will call vec_free on it. */
421 unformat_free (&input);
423 /* Re-use input vector. */
425 _vec_len (cf->input_vector) = 0;
428 uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
429 unix_cli_add_pending_output (uf, cf,
431 vec_len (cm->cli_prompt));
434 static void unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
436 unix_main_t * um = &unix_main;
437 unix_cli_file_t * cf;
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);
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);
448 vec_free (cf->current_command);
449 vec_free (cf->search_key);
451 for (i = 0; i < vec_len (cf->command_history); i++)
452 vec_free (cf->command_history[i]);
454 vec_free (cf->command_history);
456 unix_file_del (um, uf);
458 unix_cli_file_free (cf);
459 pool_put (cm->cli_file_pool, cf);
463 UNIX_CLI_PROCESS_EVENT_READ_READY,
464 UNIX_CLI_PROCESS_EVENT_QUIT,
465 } unix_cli_process_event_type_t;
468 unix_cli_process (vlib_main_t * vm,
469 vlib_node_runtime_t * rt,
472 unix_cli_main_t * cm = &unix_cli_main;
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);
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]);
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]);
502 vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
504 /* Add node index so we can re-use this process later. */
505 vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);
510 static clib_error_t * unix_cli_write_ready (unix_file_t * uf)
512 unix_cli_main_t * cm = &unix_cli_main;
513 unix_cli_file_t * cf;
516 cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
518 /* Flush output vector. */
519 n = write (uf->file_descriptor,
520 cf->output_vector, vec_len (cf->output_vector));
522 if (n < 0 && errno != EAGAIN)
523 return clib_error_return_unix (0, "write");
526 unix_cli_del_pending_output (uf, cf, n);
528 return /* no error */ 0;
531 static clib_error_t * unix_cli_read_ready (unix_file_t * uf)
533 unix_main_t * um = &unix_main;
534 unix_cli_main_t * cm = &unix_cli_main;
535 unix_cli_file_t * cf;
537 int n, n_read, n_try;
539 cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
543 l = vec_len (cf->input_vector);
544 vec_resize (cf->input_vector, l + n_try);
546 n = read (uf->file_descriptor, cf->input_vector + l, n_try);
549 if (n < 0 && errno != EAGAIN)
550 return clib_error_return_unix (0, "read");
552 n_read = n < 0 ? 0 : n;
553 _vec_len (cf->input_vector) = l + n_read;
557 vlib_process_signal_event (um->vlib_main,
558 cf->process_node_index,
560 ? UNIX_CLI_PROCESS_EVENT_QUIT
561 : UNIX_CLI_PROCESS_EVENT_READ_READY),
562 /* event data */ uf->private_data);
564 return /* no error */ 0;
567 static u32 unix_cli_file_add (unix_cli_main_t * cm, char * name, int fd)
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;
575 name = (char *) format (0, "unix-cli-%s", name);
577 if (vec_len (cm->unused_cli_process_node_indices) > 0)
579 uword l = vec_len (cm->unused_cli_process_node_indices);
581 /* Find node and give it new name. */
582 n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);
584 n->name = (u8 *) name;
586 vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
588 _vec_len (cm->unused_cli_process_node_indices) = l - 1;
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,
599 vlib_register_node (vm, &r);
602 n = vlib_get_node (vm, r.index);
605 pool_get (cm->cli_file_pool, cf);
606 memset (cf, 0, sizeof (*cf));
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;
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;
618 uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
621 unix_cli_add_pending_output (uf, cf,
622 cm->cli_prompt, vec_len (cm->cli_prompt));
624 vlib_start_process (vm, n->runtime_index);
625 return cf - cm->cli_file_pool;
628 static clib_error_t * unix_cli_listen_read_ready (unix_file_t * uf)
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;
635 clib_error_t * error;
636 unix_cli_file_t * cf;
639 error = clib_socket_accept (s, &client);
643 client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
645 cf_index = unix_cli_file_add (cm, client_name, client.fd);
646 cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
648 /* No longer need CLIB version of socket. */
649 clib_socket_free (&client);
651 vec_free (client_name);
653 /* if we're supposed to run telnet session in character mode (default) */
654 if (um->cli_line_mode == 0)
656 u8 charmode_option[6];
659 cf->history_limit = um->cli_history_limit ? um->cli_history_limit : 50;
662 * Set telnet client character mode, echo on, suppress "go-ahead"
663 * Empirically, this sequence works. YMMV.
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;
674 uf = pool_elt_at_index (um->file_pool, cf->unix_file_index);
676 unix_cli_add_pending_output (uf, cf, charmode_option,
677 ARRAY_LEN(charmode_option));
683 static clib_error_t *
684 unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
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;
691 /* We depend on unix flags being set. */
692 if ((error = vlib_call_config_function (vm, unix_config)))
695 if (um->flags & UNIX_FLAG_INTERACTIVE)
697 standard_input_fd = 0;
699 /* Set stdin to be non-blocking. */
700 if ((flags = fcntl (standard_input_fd, F_GETFL, 0)) < 0)
702 fcntl (standard_input_fd, F_SETFL, flags | O_NONBLOCK);
704 unix_cli_file_add (cm, "stdin", standard_input_fd);
709 clib_socket_t * s = &um->cli_listen_socket;
710 unix_file_t template = {0};
712 s->flags = SOCKET_IS_SERVER; /* listen, don't connect */
714 error = clib_socket_init (s);
718 template.read_function = unix_cli_listen_read_ready;
719 template.file_descriptor = s->fd;
721 unix_file_add (um, &template);
724 /* Set CLI prompt. */
725 if (! cm->cli_prompt)
726 cm->cli_prompt = format (0, "VLIB: ");
731 VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
733 void vlib_unix_cli_set_prompt (char * prompt)
735 char * fmt = (prompt[strlen(prompt)-1] == ' ') ? "%s" : "%s ";
736 unix_cli_main_t * cm = &unix_cli_main;
738 vec_free (cm->cli_prompt);
739 cm->cli_prompt = format (0, fmt, prompt);
742 static clib_error_t *
743 unix_cli_quit (vlib_main_t * vm,
744 unformat_input_t * input,
745 vlib_cli_command_t * cmd)
747 unix_cli_main_t * cm = &unix_cli_main;
749 vlib_process_signal_event (vm,
750 vlib_current_process (vm),
751 UNIX_CLI_PROCESS_EVENT_QUIT,
752 cm->current_input_file_index);
756 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
758 .short_help = "Exit CLI",
759 .function = unix_cli_quit,
762 static clib_error_t *
763 unix_cli_exec (vlib_main_t * vm,
764 unformat_input_t * input,
765 vlib_cli_command_t * cmd)
769 unformat_input_t sub_input;
770 clib_error_t * error;
776 if (! unformat (input, "%s", &file_name))
778 error = clib_error_return (0, "expecting file name, got `%U'",
779 format_unformat_error, input);
783 fd = open (file_name, O_RDONLY);
786 error = clib_error_return_unix (0, "failed to open `%s'", file_name);
790 /* Make sure its a regular file. */
794 if (fstat (fd, &s) < 0)
796 error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
800 if (! (S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
802 error = clib_error_return (0, "not a regular file `%s'", file_name);
807 unformat_init_unix_file (&sub_input, fd);
809 vlib_cli_input (vm, &sub_input, 0, 0);
810 unformat_free (&sub_input);
815 vec_free (file_name);
820 VLIB_CLI_COMMAND (cli_exec, static) = {
822 .short_help = "Execute commands from file",
823 .function = unix_cli_exec,
826 static clib_error_t *
827 unix_show_errors (vlib_main_t * vm,
828 unformat_input_t * input,
829 vlib_cli_command_t * cmd)
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;
836 n_errors_to_show = 1 << 30;
838 if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
840 if (! unformat (input, "%d", &n_errors_to_show))
842 error = clib_error_return (0, "expecting integer number of errors to show, got `%U'",
843 format_unformat_error, input);
848 n_errors_to_show = clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
850 i = um->error_history_index > 0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
852 while (n_errors_to_show > 0)
854 unix_error_history_t * eh = um->error_history + i;
859 vec_add1 (unix_errors, eh[0]);
860 n_errors_to_show -= 1;
862 i = ARRAY_LEN (um->error_history) - 1;
867 if (vec_len (unix_errors) == 0)
868 vlib_cli_output (vm, "no Unix errors so far");
871 vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
872 for (i = vec_len (unix_errors) - 1; i >= 0; i--)
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);
879 vlib_cli_output (vm, "%U: time now",
880 format_time_interval, "h:m:s:u", vlib_time_now (vm));
884 vec_free (unix_errors);
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,
894 static clib_error_t *
895 unix_cli_init (vlib_main_t * vm)
900 VLIB_INIT_FUNCTION (unix_cli_init);