X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fvlib%2Funix%2Fcli.c;h=e6f549b776f708a43839284b4047c143ff2b042f;hb=ecd6b1a985acd8e0256a170f1fe1f7e38df8c420;hp=2d5a22dc66a7f94fed5b3027385d9669569b4fad;hpb=5022c6ce34b5215e63a4ea5972ca0ed0fd196ab0;p=vpp.git diff --git a/src/vlib/unix/cli.c b/src/vlib/unix/cli.c index 2d5a22dc66a..e6f549b776f 100644 --- a/src/vlib/unix/cli.c +++ b/src/vlib/unix/cli.c @@ -47,7 +47,6 @@ #include #include -#include #include #include @@ -61,6 +60,8 @@ #include #include #include +#include +#include /** ANSI escape code. */ #define ESC "\x1b" @@ -91,7 +92,7 @@ /** Maximum depth into a byte stream from which to compile a Telnet * protocol message. This is a safety measure. */ -#define UNIX_CLI_MAX_DEPTH_TELNET 24 +#define UNIX_CLI_MAX_DEPTH_TELNET 32 /** Maximum terminal width we will accept */ #define UNIX_CLI_MAX_TERMINAL_WIDTH 512 @@ -241,6 +242,8 @@ typedef struct */ u8 cursor_direction; + /** Macro tables for this session */ + clib_macro_main_t macro_main; } unix_cli_file_t; /** Resets the pager buffer and other data. @@ -293,6 +296,7 @@ typedef enum UNIX_CLI_PARSE_ACTION_WORDRIGHT, /**< Jump cursor to start of right word */ UNIX_CLI_PARSE_ACTION_ERASELINELEFT, /**< Erase line to left of cursor */ UNIX_CLI_PARSE_ACTION_ERASELINERIGHT, /**< Erase line to right & including cursor */ + UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT, /**< Erase word left */ UNIX_CLI_PARSE_ACTION_CLEAR, /**< Clear the terminal */ UNIX_CLI_PARSE_ACTION_REVSEARCH, /**< Search backwards in command history */ UNIX_CLI_PARSE_ACTION_FWDSEARCH, /**< Search forwards in command history */ @@ -359,6 +363,7 @@ static unix_cli_parse_actions_t unix_cli_parse_strings[] = { _(CTL ('D'), UNIX_CLI_PARSE_ACTION_ERASERIGHT), _(CTL ('U'), UNIX_CLI_PARSE_ACTION_ERASELINELEFT), _(CTL ('K'), UNIX_CLI_PARSE_ACTION_ERASELINERIGHT), + _(CTL ('W'), UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT), _(CTL ('Y'), UNIX_CLI_PARSE_ACTION_YANK), _(CTL ('L'), UNIX_CLI_PARSE_ACTION_CLEAR), _(ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* Alt-B */ @@ -444,12 +449,20 @@ static unix_cli_parse_actions_t unix_cli_parse_pager[] = { #undef _ -/** CLI session events. */ +/** CLI session telnet negotiation timer events. */ typedef enum { - UNIX_CLI_PROCESS_EVENT_READ_READY, /**< A file descriptor has data to be read. */ - UNIX_CLI_PROCESS_EVENT_QUIT, /**< A CLI session wants to close. */ -} unix_cli_process_event_type_t; + UNIX_CLI_NEW_SESSION_EVENT_ADD, /**< Add a CLI session to the new session list */ +} unix_cli_timeout_event_type_t; + +/** Each new session is stored on a list with a deadline after which + * a prompt is issued, in case the session TELNET negotiation fails to + * complete. */ +typedef struct +{ + uword cf_index; /**< Session index of the new session. */ + f64 deadline; /**< Deadline after which the new session must have a prompt. */ +} unix_cli_new_session_t; /** CLI global state. */ typedef struct @@ -468,11 +481,39 @@ typedef struct /** File pool index of current input. */ u32 current_input_file_index; + + /** New session process node identifier */ + u32 new_session_process_node_index; + + /** List of new sessions */ + unix_cli_new_session_t *new_sessions; + + /** system default macro table */ + clib_macro_main_t macro_main; + } unix_cli_main_t; /** CLI global state */ static unix_cli_main_t unix_cli_main; +/** Return the macro main / tables we should use for this session + */ +static clib_macro_main_t * +get_macro_main (void) +{ + unix_cli_main_t *cm = &unix_cli_main; + vlib_main_t *vm = vlib_get_main (); + vlib_process_t *cp = vlib_get_current_process (vm); + unix_cli_file_t *cf; + + if (pool_is_free_index (cm->cli_file_pool, cp->output_function_arg)) + return (&cm->macro_main); + + cf = pool_elt_at_index (cm->cli_file_pool, cp->output_function_arg); + + return (&cf->macro_main); +} + /** * @brief Search for a byte sequence in the action list. * @@ -1215,9 +1256,9 @@ unix_cli_file_welcome (unix_cli_main_t * cm, unix_cli_file_t * cf) */ unix_cli_add_pending_output (uf, cf, (u8 *) "\r", 1); - if (!um->cli_no_banner) + if (!um->cli_no_banner && (um->flags & UNIX_FLAG_NOBANNER) == 0) { - if (cf->ansi_capable) + if (cf->ansi_capable && (um->flags & UNIX_FLAG_NOCOLOR) == 0) { banner = unix_cli_banner_color; len = ARRAY_LEN (unix_cli_banner_color); @@ -1240,25 +1281,109 @@ unix_cli_file_welcome (unix_cli_main_t * cm, unix_cli_file_t * cf) } -/** @brief A failsafe triggered on a timer to ensure we send the prompt - * to telnet sessions that fail to negotiate the terminal type. */ -static void -unix_cli_file_welcome_timer (any arg, f64 delay) +/** + * @brief A failsafe manager that ensures CLI sessions issue an initial + * prompt if TELNET negotiation fails. + */ +static uword +unix_cli_new_session_process (vlib_main_t * vm, vlib_node_runtime_t * rt, + vlib_frame_t * f) { unix_cli_main_t *cm = &unix_cli_main; - unix_cli_file_t *cf; - (void) delay; + uword event_type, *event_data = 0; + f64 wait = 10.0; - /* Check the connection didn't close already */ - if (pool_is_free_index (cm->cli_file_pool, (uword) arg)) - return; + while (1) + { + if (vec_len (cm->new_sessions) > 0) + wait = vlib_process_wait_for_event_or_clock (vm, wait); + else + vlib_process_wait_for_event (vm); + + event_type = vlib_process_get_events (vm, &event_data); + + switch (event_type) + { + case ~0: /* no events => timeout */ + break; - cf = pool_elt_at_index (cm->cli_file_pool, (uword) arg); + case UNIX_CLI_NEW_SESSION_EVENT_ADD: + { + /* Add an identifier to the new session list */ + unix_cli_new_session_t ns; + + ns.cf_index = event_data[0]; + ns.deadline = vlib_time_now (vm) + 1.0; + + vec_add1 (cm->new_sessions, ns); + + if (wait > 0.1) + wait = 0.1; /* force a re-evaluation soon, but not too soon */ + } + break; + + default: + clib_warning ("BUG: unknown event type 0x%wx", event_type); + break; + } + + vec_reset_length (event_data); + + if (vlib_process_suspend_time_is_zero (wait)) + { + /* Scan the list looking for expired deadlines. + * Emit needed prompts and remove from the list. + * While scanning, look for the nearest new deadline + * for the next iteration. + * Since the list is ordered with newest sessions first + * we can make assumptions about expired sessions being + * contiguous at the beginning and the next deadline is the + * next entry on the list, if any. + */ + f64 now = vlib_time_now (vm); + unix_cli_new_session_t *nsp; + word index = 0; + + wait = INFINITY; + + vec_foreach (nsp, cm->new_sessions) + { + if (vlib_process_suspend_time_is_zero (nsp->deadline - now)) + { + /* Deadline reached */ + unix_cli_file_t *cf; + + /* Mark the highwater */ + index++; + + /* Check the connection didn't close already */ + if (pool_is_free_index (cm->cli_file_pool, nsp->cf_index)) + continue; + + cf = pool_elt_at_index (cm->cli_file_pool, nsp->cf_index); + + if (!cf->started) + unix_cli_file_welcome (cm, cf); + } + else + { + wait = nsp->deadline - now; + break; + } + } + + if (index) + { + /* We have sessions to remove */ + vec_delete (cm->new_sessions, index, 0); + } + } + } - if (!cf->started) - unix_cli_file_welcome (cm, cf); + return 0; } + /** @brief A mostly no-op Telnet state machine. * Process Telnet command bytes in a way that ensures we're mostly * transparent to the Telnet protocol. That is, it's mostly a no-op. @@ -1511,6 +1636,51 @@ unix_cli_line_process_one (unix_cli_main_t * cm, cf->search_mode = 0; break; + case UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT: + /* calculate num of caracter to be erased */ + delta = 0; + while (cf->cursor > delta + && cf->current_command[cf->cursor - delta - 1] == ' ') + delta++; + while (cf->cursor > delta + && cf->current_command[cf->cursor - delta - 1] != ' ') + delta++; + + if (vec_len (cf->current_command)) + { + if (cf->cursor > 0) + { + /* move cursor left delta times */ + for (j = delta; j > 0; j--, cf->cursor--) + unix_vlib_cli_output_cursor_left (cf, uf); + save = cf->current_command + cf->cursor; + + /* redraw remainder of line */ + memmove (cf->current_command + cf->cursor, + cf->current_command + cf->cursor + delta, + _vec_len (cf->current_command) - cf->cursor - delta); + unix_vlib_cli_output_cooked (cf, uf, + cf->current_command + cf->cursor, + _vec_len (cf->current_command) - + cf->cursor); + cf->cursor += _vec_len (cf->current_command) - cf->cursor; + + /* print delta amount of blank spaces, + * then finally fix the cursor position */ + for (j = delta; j > 0; j--, cf->cursor--) + unix_vlib_cli_output_cursor_left (cf, uf); + for (j = delta; j > 0; j--, cf->cursor++) + unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1); + for (; (cf->current_command + cf->cursor) > save; cf->cursor--) + unix_vlib_cli_output_cursor_left (cf, uf); + _vec_len (cf->current_command) -= delta; + } + } + cf->search_mode = 0; + cf->excursion = 0; + vec_reset_length (cf->search_key); + break; + case UNIX_CLI_PARSE_ACTION_LEFT: if (cf->cursor > 0) { @@ -2343,6 +2513,7 @@ unix_cli_line_edit (unix_cli_main_t * cm, unix_main_t * um, if (cf->line_mode) { vec_delete (cf->input_vector, i, 0); + vec_free (cf->current_command); cf->current_command = cf->input_vector; return 0; } @@ -2405,9 +2576,31 @@ more: format_timeval, 0 /* current bat-time */ , 0 /* current bat-format */ , cli_file_index, cf->current_command); + if ((vec_len (cf->current_command) > 0) && + (cf->current_command[vec_len (cf->current_command) - 1] != '\n')) + lv = format (lv, "\n"); int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv)); } + /* Run the command through the macro processor */ + if (vec_len (cf->current_command)) + { + u8 *expanded; + vec_validate (cf->current_command, vec_len (cf->current_command)); + cf->current_command[vec_len (cf->current_command) - 1] = 0; + /* The macro expander expects proper C-strings, not vectors */ + expanded = (u8 *) clib_macro_eval (&cf->macro_main, + (i8 *) cf->current_command, + 1 /* complain */ , + 0 /* level */ , + 8 /* max_level */ ); + /* Macro processor NULL terminates the return */ + _vec_len (expanded) -= 1; + vec_reset_length (cf->current_command); + vec_append (cf->current_command, expanded); + vec_free (expanded); + } + /* Build an unformat structure around our command */ unformat_init_vector (&input, cf->current_command); @@ -2494,6 +2687,17 @@ unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index) if (pool_is_free_index (cm->cli_file_pool, cli_file_index)) return; + vec_foreach_index (i, cm->new_sessions) + { + unix_cli_new_session_t *ns = vec_elt_at_index (cm->new_sessions, i); + + if (ns->cf_index == cli_file_index) + { + vec_del1 (cm->new_sessions, i); + break; + } + } + cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index); uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index); @@ -2508,10 +2712,12 @@ unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index) vec_free (cf->command_history[i]); vec_free (cf->command_history); + vec_free (cf->input_vector); clib_file_del (fm, uf); unix_cli_file_free (cf); + clib_macro_free (&cf->macro_main); pool_put (cm->cli_file_pool, cf); } @@ -2668,6 +2874,7 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) { unix_main_t *um = &unix_main; clib_file_main_t *fm = &file_main; + vlib_node_main_t *nm = &vlib_get_main ()->node_main; unix_cli_file_t *cf; clib_file_t template = { 0 }; vlib_main_t *vm = um->vlib_main; @@ -2691,9 +2898,9 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) * the same new name. * Then, throw away the old shared name-vector. */ - for (i = 0; i < vec_len (vlib_mains); i++) + for (i = 0; i < vlib_get_n_threads (); i++) { - this_vlib_main = vlib_mains[i]; + this_vlib_main = vlib_get_main_by_index (i); if (this_vlib_main == 0) continue; n = vlib_get_node (this_vlib_main, @@ -2701,10 +2908,12 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) old_name = n->name; n->name = (u8 *) name; } + ASSERT (old_name); + hash_unset (nm->node_by_name, old_name); + hash_set (nm->node_by_name, name, n->index); vec_free (old_name); vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING); - _vec_len (cm->unused_cli_process_node_indices) = l - 1; } else @@ -2712,7 +2921,7 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) static vlib_node_registration_t r = { .function = unix_cli_process, .type = VLIB_NODE_TYPE_PROCESS, - .process_log2_n_stack_bytes = 16, + .process_log2_n_stack_bytes = 18, }; r.name = name; @@ -2729,6 +2938,7 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) pool_get (cm->cli_file_pool, cf); clib_memset (cf, 0, sizeof (*cf)); + clib_macro_init (&cf->macro_main); template.read_function = unix_cli_read_ready; template.write_function = unix_cli_write_ready; @@ -2741,6 +2951,8 @@ unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd) cf->clib_file_index = clib_file_add (fm, &template); cf->output_vector = 0; cf->input_vector = 0; + vec_validate (cf->current_command, 0); + _vec_len (cf->current_command) = 0; vlib_start_process (vm, n->runtime_index); @@ -2832,9 +3044,22 @@ unix_cli_listen_read_ready (clib_file_t * uf) unix_vlib_cli_output_raw (cf, uf, charmode_option, ARRAY_LEN (charmode_option)); - /* In case the client doesn't negotiate terminal type, use - * a timer to kick off the initial prompt. */ - timer_call (unix_cli_file_welcome_timer, cf_index, 1); + if (cm->new_session_process_node_index == ~0) + { + /* Create thw new session deadline process */ + cm->new_session_process_node_index = + vlib_process_create (um->vlib_main, "unix-cli-new-session", + unix_cli_new_session_process, + 16 /* log2_n_stack_bytes */ ); + } + + /* In case the client doesn't negotiate terminal type, register + * our session with a process that will emit the prompt if + * a deadline passes */ + vlib_process_signal_event (um->vlib_main, + cm->new_session_process_node_index, + UNIX_CLI_NEW_SESSION_EVENT_ADD, cf_index); + } return error; @@ -2924,9 +3149,11 @@ unix_cli_config (vlib_main_t * vm, unformat_input_t * input) clib_panic ("sigaction"); /* Retrieve the current terminal size */ - ioctl (STDIN_FILENO, TIOCGWINSZ, &ws); - cf->width = ws.ws_col; - cf->height = ws.ws_row; + if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0) + { + cf->width = ws.ws_col; + cf->height = ws.ws_row; + } if (cf->width == 0 || cf->height == 0) { @@ -3002,7 +3229,7 @@ unix_cli_config (vlib_main_t * vm, unformat_input_t * input) while (i && tmp[--i] != '/') ; - tmp[i] = 0; + tmp[i] = '\0'; if (i) vlib_unix_recursive_mkdir ((char *) tmp); @@ -3067,6 +3294,23 @@ vlib_unix_cli_set_prompt (char *prompt) cm->cli_prompt = format (0, fmt, prompt); } +static unix_cli_file_t * +unix_cli_file_if_exists (unix_cli_main_t * cm) +{ + if (!cm->cli_file_pool) + return 0; + return pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); +} + +static unix_cli_file_t * +unix_cli_file_if_interactive (unix_cli_main_t * cm) +{ + unix_cli_file_t *cf; + if ((cf = unix_cli_file_if_exists (cm)) && !cf->is_interactive) + return 0; + return cf; +} + /** CLI command to quit the terminal session. * @note If this is a stdin session then this will * shutdown VPP also. @@ -3076,8 +3320,10 @@ unix_cli_quit (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { unix_cli_main_t *cm = &unix_cli_main; - unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, - cm->current_input_file_index); + unix_cli_file_t *cf; + + if (!(cf = unix_cli_file_if_exists (cm))) + return clib_error_return (0, "invalid session"); /* Cosmetic: suppress the final prompt from appearing before we die */ cf->is_interactive = 0; @@ -3121,10 +3367,14 @@ unix_cli_exec (vlib_main_t * vm, int fd; unformat_input_t sub_input; clib_error_t *error; - + unix_cli_main_t *cm = &unix_cli_main; + unix_cli_file_t *cf; + u8 *file_data = 0; file_name = 0; fd = -1; error = 0; + struct stat s; + if (!unformat (input, "%s", &file_name)) { @@ -3141,29 +3391,69 @@ unix_cli_exec (vlib_main_t * vm, } /* Make sure its a regular file. */ - { - struct stat s; + if (fstat (fd, &s) < 0) + { + error = clib_error_return_unix (0, "failed to stat `%s'", file_name); + goto done; + } - if (fstat (fd, &s) < 0) - { - error = clib_error_return_unix (0, "failed to stat `%s'", file_name); - goto done; - } + if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode))) + { + error = clib_error_return (0, "not a regular file `%s'", file_name); + goto done; + } - if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode))) - { - error = clib_error_return (0, "not a regular file `%s'", file_name); - goto done; - } - } + /* Read the file */ + vec_validate (file_data, s.st_size); + + if (read (fd, file_data, s.st_size) != s.st_size) + { + error = clib_error_return_unix (0, "Failed to read %d bytes from '%s'", + s.st_size, file_name); + vec_free (file_data); + goto done; + } + + /* The macro expander expects a c string... */ + vec_add1 (file_data, 0); - unformat_init_clib_file (&sub_input, fd); + unformat_init_vector (&sub_input, file_data); + + /* Run the file contents through the macro processor */ + if (vec_len (sub_input.buffer) > 1) + { + u8 *expanded; + clib_macro_main_t *mm = 0; + + /* Initial config process? Use the global macro table. */ + if (pool_is_free_index + (cm->cli_file_pool, cm->current_input_file_index)) + mm = &cm->macro_main; + else + { + /* Otherwise, use the per-cli-process macro table */ + cf = pool_elt_at_index (cm->cli_file_pool, + cm->current_input_file_index); + mm = &cf->macro_main; + } + + expanded = (u8 *) clib_macro_eval (mm, + (i8 *) sub_input.buffer, + 1 /* complain */ , + 0 /* level */ , + 8 /* max_level */ ); + /* Macro processor NULL terminates the return */ + _vec_len (expanded) -= 1; + vec_reset_length (sub_input.buffer); + vec_append (sub_input.buffer, expanded); + vec_free (expanded); + } vlib_cli_input (vm, &sub_input, 0, 0); unformat_free (&sub_input); done: - if (fd > 0) + if (fd >= 0) close (fd); vec_free (file_name); @@ -3172,7 +3462,7 @@ done: /*? * Executes a sequence of CLI commands which are read from a file. If - * a command is unrecognised or otherwise invalid then the usual CLI + * a command is unrecognized or otherwise invalid then the usual CLI * feedback will be generated, however execution of subsequent commands * from the file will continue. * @@ -3292,7 +3582,7 @@ unix_show_files (vlib_main_t * vm, "Read", "Write", "Error", "File Name", "Description"); /* *INDENT-OFF* */ - pool_foreach (f, fm->file_pool,( + pool_foreach (f, fm->file_pool) { int rv; s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0); @@ -3305,7 +3595,7 @@ unix_show_files (vlib_main_t * vm, f->read_events, f->write_events, f->error_events, path, f->description); vec_reset_length (s); - })); + } /* *INDENT-ON* */ vec_free (s); @@ -3329,9 +3619,7 @@ unix_cli_show_history (vlib_main_t * vm, unix_cli_file_t *cf; int i, j; - cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); - - if (!cf->is_interactive) + if (!(cf = unix_cli_file_if_interactive (cm))) return clib_error_return (0, "invalid for non-interactive sessions"); if (cf->has_history && cf->history_limit) @@ -3369,7 +3657,9 @@ unix_cli_show_terminal (vlib_main_t * vm, unix_cli_file_t *cf; vlib_node_t *n; - cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); + if (!(cf = unix_cli_file_if_exists (cm))) + return clib_error_return (0, "invalid session"); + n = vlib_get_node (vm, cf->process_node_index); vlib_cli_output (vm, "Terminal name: %v\n", n->name); @@ -3445,7 +3735,7 @@ unix_cli_show_cli_sessions (vlib_main_t * vm, #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) ) /* *INDENT-OFF* */ - pool_foreach (cf, cm->cli_file_pool, ({ + pool_foreach (cf, cm->cli_file_pool) { uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index); n = vlib_get_node (vm, cf->process_node_index); vlib_cli_output (vm, @@ -3458,7 +3748,7 @@ unix_cli_show_cli_sessions (vlib_main_t * vm, fl (cf->line_mode, 'l'), fl (cf->has_epipe, 'p'), fl (cf->ansi_capable, 'a')); - })); + } /* *INDENT-ON* */ #undef fl @@ -3521,9 +3811,7 @@ unix_cli_set_terminal_pager (vlib_main_t * vm, unformat_input_t _line_input, *line_input = &_line_input; clib_error_t *error = 0; - cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); - - if (!cf->is_interactive) + if (!(cf = unix_cli_file_if_interactive (cm))) return clib_error_return (0, "invalid for non-interactive sessions"); if (!unformat_user (input, unformat_line_input, line_input)) @@ -3580,9 +3868,7 @@ unix_cli_set_terminal_history (vlib_main_t * vm, u32 limit; clib_error_t *error = 0; - cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); - - if (!cf->is_interactive) + if (!(cf = unix_cli_file_if_interactive (cm))) return clib_error_return (0, "invalid for non-interactive sessions"); if (!unformat_user (input, unformat_line_input, line_input)) @@ -3650,9 +3936,7 @@ unix_cli_set_terminal_ansi (vlib_main_t * vm, unix_cli_main_t *cm = &unix_cli_main; unix_cli_file_t *cf; - cf = pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index); - - if (!cf->is_interactive) + if (!(cf = unix_cli_file_if_interactive (cm))) return clib_error_return (0, "invalid for non-interactive sessions"); if (unformat (input, "on")) @@ -3682,9 +3966,171 @@ VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = { }; /* *INDENT-ON* */ + +#define MAX_CLI_WAIT 86400 +/** CLI command to wait seconds. Useful for exec script. */ +static clib_error_t * +unix_wait_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + f64 sec = 1.0; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%f", &sec)) + ; + else + return clib_error_return (0, "unknown parameter: `%U`", + format_unformat_error, input); + } + + if (sec <= 0 || sec > MAX_CLI_WAIT || floor (sec * 1000) / 1000 != sec) + return clib_error_return (0, + " must be a positive value and less than 86400 (one day) with no more than msec precision."); + + vlib_process_wait_for_event_or_clock (vm, sec); + vlib_cli_output (vm, "waited %.3f sec.", sec); + + unformat_free (line_input); + return 0; +} +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cli_unix_wait_cmd, static) = { + .path = "wait", + .short_help = "wait ", + .function = unix_wait_cmd, +}; +/* *INDENT-ON* */ + +static clib_error_t * +echo_cmd (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + vlib_cli_output (vm, ""); + return 0; + } + + vlib_cli_output (vm, "%v", line_input->buffer); + + unformat_free (line_input); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (cli_unix_echo_cmd, static) = { + .path = "echo", + .short_help = "echo ", + .function = echo_cmd, +}; +/* *INDENT-ON* */ + +static clib_error_t * +define_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u8 *macro_name; + unformat_input_t _line_input, *line_input = &_line_input; + clib_macro_main_t *mm = get_macro_main (); + clib_error_t *error; + + if (!unformat (input, "%s", ¯o_name)) + return clib_error_return (0, "missing variable name..."); + + /* Remove white space */ + (void) unformat (input, ""); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + { + error = clib_error_return (0, "missing value for '%s'...", macro_name); + vec_free (macro_name); + return error; + } + /* the macro expander expects c-strings, not vectors... */ + vec_add1 (line_input->buffer, 0); + clib_macro_set_value (mm, (char *) macro_name, (char *) line_input->buffer); + vec_free (macro_name); + unformat_free (line_input); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (define_cmd, static) = { + .path = "define", + .short_help = "define ", + .function = define_cmd_fn, +}; + +/* *INDENT-ON* */ + +static clib_error_t * +undefine_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u8 *macro_name; + clib_macro_main_t *mm = get_macro_main (); + + if (!unformat (input, "%s", ¯o_name)) + return clib_error_return (0, "missing variable name..."); + + if (clib_macro_unset (mm, (char *) macro_name)) + vlib_cli_output (vm, "%s wasn't set...", macro_name); + + vec_free (macro_name); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (undefine_cmd, static) = { + .path = "undefine", + .short_help = "undefine ", + .function = undefine_cmd_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_macro_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + clib_macro_main_t *mm = get_macro_main (); + int evaluate = 1; + + if (unformat (input, "noevaluate %=", &evaluate, 0)) + ; + else if (unformat (input, "noeval %=", &evaluate, 0)) + ; + + vlib_cli_output (vm, "%U", format_clib_macro_main, mm, evaluate); + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_macro, static) = { + .path = "show macro", + .short_help = "show macro [noevaluate]", + .function = show_macro_cmd_fn, +}; +/* *INDENT-ON* */ + static clib_error_t * unix_cli_init (vlib_main_t * vm) { + unix_cli_main_t *cm = &unix_cli_main; + + /* Breadcrumb to indicate the new session process + * has not been started */ + cm->new_session_process_node_index = ~0; + clib_macro_init (&cm->macro_main); + return 0; }