#include <limits.h>
#include <netinet/tcp.h>
#include <math.h>
+#include <vppinfra/macros.h>
+#include <vppinfra/format_table.h>
/** ANSI escape code. */
#define ESC "\x1b"
/** 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
*/
u8 cursor_direction;
+ /** Macro tables for this session */
+ clib_macro_main_t macro_main;
+
+ /** Session name */
+ u8 *name;
} unix_cli_file_t;
/** Resets the pager buffer and other data.
{
vec_free (f->output_vector);
vec_free (f->input_vector);
+ vec_free (f->name);
unix_cli_pager_reset (f);
}
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 */
_(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 */
#undef _
-/** CLI session 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;
-
/** CLI session telnet negotiation timer events. */
typedef enum
{
/** 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.
*
*/
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);
/* Add an identifier to the new session list */
unix_cli_new_session_t ns;
+ /* Check the connection didn't close already */
+ if (pool_is_free_index (cm->cli_file_pool, event_data[0]))
+ break;
+
ns.cf_index = event_data[0];
ns.deadline = vlib_time_now (vm) + 1.0;
/* Delete the desired text from the command */
memmove (cf->current_command, cf->current_command + j, delta);
- _vec_len (cf->current_command) = delta;
+ vec_set_len (cf->current_command, delta);
/* Print the new contents */
unix_vlib_cli_output_cooked (cf, uf, cf->current_command, delta);
unix_vlib_cli_output_cursor_left (cf, uf);
/* Truncate the line at the cursor */
- _vec_len (cf->current_command) = cf->cursor;
+ vec_set_len (cf->current_command, cf->cursor);
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_dec_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)
{
if (cf->excursion == vec_len (cf->command_history))
{
/* down-arrowed to last entry - want a blank line */
- _vec_len (cf->current_command) = 0;
+ vec_set_len (cf->current_command, 0);
}
else if (cf->excursion < 0)
{
/* up-arrowed over the start to the end, want a blank line */
cf->excursion = vec_len (cf->command_history);
- _vec_len (cf->current_command) = 0;
+ vec_set_len (cf->current_command, 0);
}
else
{
vec_validate (cf->current_command, vec_len (prev) - 1);
clib_memcpy (cf->current_command, prev, vec_len (prev));
- _vec_len (cf->current_command) = vec_len (prev);
+ vec_set_len (cf->current_command, vec_len (prev));
unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
vec_len (cf->current_command));
}
cf->cursor++;
unix_vlib_cli_output_cursor_left (cf, uf);
cf->cursor--;
- _vec_len (cf->current_command)--;
+ vec_dec_len (cf->current_command, 1);
}
else if (cf->cursor > 0)
{
j = vec_len (cf->current_command) - cf->cursor;
memmove (cf->current_command + cf->cursor - 1,
cf->current_command + cf->cursor, j);
- _vec_len (cf->current_command)--;
+ vec_dec_len (cf->current_command, 1);
/* redraw the rest of the line */
unix_vlib_cli_output_cursor_left (cf, uf);
j = vec_len (cf->current_command) - cf->cursor - 1;
memmove (cf->current_command + cf->cursor,
cf->current_command + cf->cursor + 1, j);
- _vec_len (cf->current_command)--;
+ vec_dec_len (cf->current_command, 1);
/* redraw the rest of the line */
unix_vlib_cli_output_cooked (cf, uf,
cf->current_command + cf->cursor,
vec_resize (save, vec_len (cf->current_command) - cf->cursor);
clib_memcpy (save, cf->current_command + cf->cursor,
vec_len (cf->current_command) - cf->cursor);
- _vec_len (cf->current_command) = cf->cursor;
+ vec_set_len (cf->current_command, cf->cursor);
}
else
{
cf->cursor--;
j--;
}
- _vec_len (cf->current_command) = j;
+ vec_set_len (cf->current_command, j);
/* replace it with the newly expanded command */
vec_append (cf->current_command, completed);
vec_validate (cf->current_command, vec_len (item) - 1);
clib_memcpy (cf->current_command, item, vec_len (item));
- _vec_len (cf->current_command) = vec_len (item);
+ vec_set_len (cf->current_command, vec_len (item));
unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
vec_len (cf->current_command));
if (cf->line_mode)
{
vec_delete (cf->input_vector, i, 0);
+ vec_free (cf->current_command);
cf->current_command = cf->input_vector;
return 0;
}
{
static u8 *lv;
vec_reset_length (lv);
- lv = format (lv, "%U[%d]: %v",
- format_timeval, 0 /* current bat-time */ ,
- 0 /* current bat-format */ ,
+ lv = format (lv, "%U[%d]: %v", format_timeval,
+ NULL /* current bat-format */, 0 /* current bat-time */,
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_dec_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);
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)
+ {
+ ns->cf_index = ~0;
+ 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);
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);
}
}
if (data)
- _vec_len (data) = 0;
+ vec_set_len (data, 0);
}
done:
return clib_error_return_unix (0, "read");
n_read = n < 0 ? 0 : n;
- _vec_len (cf->input_vector) = l + n_read;
+ vec_set_len (cf->input_vector, l + n_read);
}
if (!(n < 0))
clib_file_t template = { 0 };
vlib_main_t *vm = um->vlib_main;
vlib_node_t *n = 0;
- u8 *file_desc = 0;
-
- file_desc = format (0, "%s", name);
-
- name = (char *) format (0, "unix-cli-%s", name);
if (vec_len (cm->unused_cli_process_node_indices) > 0)
{
- uword l = vec_len (cm->unused_cli_process_node_indices);
- int i;
- vlib_main_t *this_vlib_main;
- u8 *old_name = 0;
-
- /*
- * Nodes are bulk-copied, so node name pointers are shared.
- * Find the cli node in all graph replicas, and give all of them
- * the same new name.
- * Then, throw away the old shared name-vector.
- */
- for (i = 0; i < vec_len (vlib_mains); i++)
- {
- this_vlib_main = vlib_mains[i];
- if (this_vlib_main == 0)
- continue;
- n = vlib_get_node (this_vlib_main,
- cm->unused_cli_process_node_indices[l - 1]);
- old_name = n->name;
- n->name = (u8 *) name;
- }
- vec_free (old_name);
+ n = vlib_get_node (vm, vec_pop (cm->unused_cli_process_node_indices));
vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
-
- _vec_len (cm->unused_cli_process_node_indices) = l - 1;
}
else
{
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;
+ static u32 count = 0;
vlib_worker_thread_barrier_sync (vm);
- vlib_register_node (vm, &r);
- vec_free (name);
+ vlib_register_node (vm, &r, "unix-cli-process-%u", count++);
n = vlib_get_node (vm, r.index);
vlib_worker_thread_node_runtime_update ();
vlib_worker_thread_barrier_release (vm);
}
- pool_get (cm->cli_file_pool, cf);
- clib_memset (cf, 0, sizeof (*cf));
+ pool_get_zero (cm->cli_file_pool, cf);
+ clib_macro_init (&cf->macro_main);
template.read_function = unix_cli_read_ready;
template.write_function = unix_cli_write_ready;
template.error_function = unix_cli_error_detected;
template.file_descriptor = fd;
template.private_data = cf - cm->cli_file_pool;
- template.description = file_desc;
+ template.description = format (0, "%s", name);
+ cf->name = format (0, "unix-cli-%s", name);
cf->process_node_index = n->index;
cf->clib_file_index = clib_file_add (fm, &template);
cf->output_vector = 0;
cf->input_vector = 0;
+ vec_validate (cf->current_command, 0);
+ vec_set_len (cf->current_command, 0);
vlib_start_process (vm, n->runtime_index);
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)
{
while (i && tmp[--i] != '/')
;
- tmp[i] = 0;
+ tmp[i] = '\0';
if (i)
vlib_unix_recursive_mkdir ((char *) tmp);
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.
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;
unformat_input_t * input, vlib_cli_command_t * cmd)
{
char *file_name;
- int fd;
- unformat_input_t sub_input;
+ int fd, rv = 0;
+ unformat_input_t sub_input, in;
clib_error_t *error;
-
+ clib_macro_main_t *mm = 0;
+ 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))
{
}
/* 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;
- }
- }
+ if (s.st_size < 1)
+ {
+ error = clib_error_return (0, "empty file `%s'", file_name);
+ goto done;
+ }
- unformat_init_clib_file (&sub_input, fd);
+ /* Read the file */
+ vec_validate (file_data, s.st_size - 1);
+
+ 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;
+ }
+
+ unformat_init_vector (&sub_input, file_data);
+
+ /* 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;
+ }
+
+ while (rv == 0 && unformat_user (&sub_input, unformat_vlib_cli_line, &in))
+ {
+ /* Run the file contents through the macro processor */
+ if (vec_len (in.buffer) > 1)
+ {
+ u8 *expanded;
+
+ /* The macro expander expects a c string... */
+ vec_add1 (in.buffer, 0);
+
+ expanded =
+ (u8 *) clib_macro_eval (mm, (i8 *) in.buffer, 1 /* complain */,
+ 0 /* level */, 8 /* max_level */);
+ /* Macro processor NULL terminates the return */
+ vec_dec_len (expanded, 1);
+ vec_reset_length (in.buffer);
+ vec_append (in.buffer, expanded);
+ vec_free (expanded);
+ }
- vlib_cli_input (vm, &sub_input, 0, 0);
+ if ((rv = vlib_cli_input (vm, &in, 0, 0)) != 0)
+ error = clib_error_return (0, "CLI line error: %U",
+ format_unformat_error, &in);
+ unformat_free (&in);
+ }
unformat_free (&sub_input);
done:
- if (fd > 0)
+ if (fd >= 0)
close (fd);
vec_free (file_name);
/*?
* 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.
*
"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);
f->read_events, f->write_events, f->error_events,
path, f->description);
vec_reset_length (s);
- }));
+ }
/* *INDENT-ON* */
vec_free (s);
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)
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);
+ vlib_cli_output (vm, "Terminal name: %v\n", cf->name);
+ vlib_cli_output (vm, "Terminal node: %v\n", n->name);
vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ?
"line-by-line" : "char-by-char");
vlib_cli_output (vm, "Terminal width: %d\n", cf->width);
{
unix_cli_main_t *cm = &unix_cli_main;
clib_file_main_t *fm = &file_main;
+ table_t table = {}, *t = &table;
unix_cli_file_t *cf;
clib_file_t *uf;
- vlib_node_t *n;
- vlib_cli_output (vm, "%-5s %-5s %-20s %s", "PNI", "FD", "Name", "Flags");
+ table_add_header_col (t, 4, "PNI ", "FD ", "Name", "Flags");
#define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) )
- /* *INDENT-OFF* */
- 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,
- "%-5d %-5d %-20v %c%c%c%c%c\n",
- cf->process_node_index,
- uf->file_descriptor,
- n->name,
- fl (cf->is_interactive, 'i'),
- fl (cf->is_socket, 's'),
- fl (cf->line_mode, 'l'),
- fl (cf->has_epipe, 'p'),
- fl (cf->ansi_capable, 'a'));
- }));
- /* *INDENT-ON* */
+ int i = 0;
+ pool_foreach (cf, cm->cli_file_pool)
+ {
+ int j = 0;
+
+ uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
+ table_format_cell (t, i, j++, "%u", cf->process_node_index);
+ table_format_cell (t, i, j++, "%u", uf->file_descriptor);
+ table_format_cell (t, i, j++, "%v", cf->name);
+ table_format_cell (t, i++, j++, "%c%c%c%c%c",
+ fl (cf->is_interactive, 'i'), fl (cf->is_socket, 's'),
+ fl (cf->line_mode, 'l'), fl (cf->has_epipe, 'p'),
+ fl (cf->ansi_capable, 'a'));
+ }
#undef fl
+ t->default_body.align = TTAA_LEFT;
+ t->default_header_col.align = TTAA_LEFT;
+ vlib_cli_output (vm, "%U", format_table, t);
+ table_free (t);
+
return 0;
}
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))
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))
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"))
};
/* *INDENT-ON* */
+
+#define MAX_CLI_WAIT 86400
+/** CLI command to wait <sec> 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,
+ "<sec> 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 <sec>",
+ .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 <rest-of-line>",
+ .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 <variable-name> <value>",
+ .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 <variable-name>",
+ .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)
{
/* 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;
}