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.
41 * @brief Unix stdin/socket command line interface.
42 * Provides a command line interface so humans can interact with VPP.
43 * This is predominantly a debugging and testing mechanism.
45 /*? %%clicmd:group_label Command line session %% ?*/
46 /*? %%syscfg:group_label Command line session %% ?*/
48 #include <vlib/vlib.h>
49 #include <vlib/unix/unix.h>
57 #include <arpa/telnet.h>
58 #include <sys/ioctl.h>
59 #include <sys/types.h>
62 #include <netinet/tcp.h>
64 #include <vppinfra/macros.h>
66 /** ANSI escape code. */
69 /** ANSI Control Sequence Introducer. */
72 /** ANSI clear screen. */
73 #define ANSI_CLEAR CSI "2J" CSI "1;1H"
74 /** ANSI reset color settings. */
75 #define ANSI_RESET CSI "0m"
76 /** ANSI Start bold text. */
77 #define ANSI_BOLD CSI "1m"
78 /** ANSI Stop bold text. */
79 #define ANSI_DIM CSI "2m"
80 /** ANSI Start dark red text. */
81 #define ANSI_DRED ANSI_DIM CSI "31m"
82 /** ANSI Start bright red text. */
83 #define ANSI_BRED ANSI_BOLD CSI "31m"
84 /** ANSI clear line cursor is on. */
85 #define ANSI_CLEARLINE CSI "2K"
86 /** ANSI scroll screen down one line. */
87 #define ANSI_SCROLLDN CSI "1T"
88 /** ANSI save cursor position. */
89 #define ANSI_SAVECURSOR CSI "s"
90 /** ANSI restore cursor position if previously saved. */
91 #define ANSI_RESTCURSOR CSI "u"
93 /** Maximum depth into a byte stream from which to compile a Telnet
94 * protocol message. This is a safety measure. */
95 #define UNIX_CLI_MAX_DEPTH_TELNET 24
97 /** Maximum terminal width we will accept */
98 #define UNIX_CLI_MAX_TERMINAL_WIDTH 512
99 /** Maximum terminal height we will accept */
100 #define UNIX_CLI_MAX_TERMINAL_HEIGHT 512
101 /** Default terminal height */
102 #define UNIX_CLI_DEFAULT_TERMINAL_HEIGHT 24
103 /** Default terminal width */
104 #define UNIX_CLI_DEFAULT_TERMINAL_WIDTH 80
106 /** A CLI banner line. */
109 u8 *line; /**< The line to print. */
110 u32 length; /**< The length of the line without terminating NUL. */
113 #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 }
114 /** Plain welcome banner. */
115 static unix_cli_banner_t unix_cli_banner[] = {
116 _(" _______ _ _ _____ ___ \n"),
117 _(" __/ __/ _ \\ (_)__ | | / / _ \\/ _ \\\n"),
118 _(" _/ _// // / / / _ \\ | |/ / ___/ ___/\n"),
119 _(" /_/ /____(_)_/\\___/ |___/_/ /_/ \n"),
123 /** ANSI color welcome banner. */
124 static unix_cli_banner_t unix_cli_banner_color[] = {
125 _(ANSI_BRED " _______ _ " ANSI_RESET " _ _____ ___ \n"),
126 _(ANSI_BRED " __/ __/ _ \\ (_)__ " ANSI_RESET " | | / / _ \\/ _ \\\n"),
127 _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET " | |/ / ___/ ___/\n"),
128 _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET " |___/_/ /_/ \n"),
134 /** Pager line index */
137 /** Index into pager_vector */
140 /** Offset of the string in the line */
143 /** Length of the string in the line */
145 } unix_cli_pager_index_t;
148 /** Unix CLI session. */
151 /** The file index held by unix.c */
154 /** Vector of output pending write to file descriptor. */
157 /** Vector of input saved by Unix input node to be processed by
161 /** This session has command history. */
163 /** Array of vectors of commands in the history. */
164 u8 **command_history;
165 /** The command currently pointed at by the history cursor. */
167 /** How far from the end of the history array the user has browsed. */
170 /** Maximum number of history entries this session will store. */
173 /** Current command line counter */
176 /** The string being searched for in the history. */
178 /** If non-zero then the CLI is searching in the history array.
179 * - @c -1 means search backwards.
180 * - @c 1 means search forwards.
184 /** Position of the insert cursor on the current input line */
187 /** Line mode or char mode */
190 /** Set if the CRLF mode wants CR + LF */
193 /** Can we do ANSI output? */
196 /** Has the session started? */
199 /** Disable the pager? */
202 /** Whether the session is interactive or not.
203 * Controls things like initial banner, the CLI prompt etc. */
206 /** Whether the session is attached to a socket. */
209 /** If EPIPE has been detected, prevent further write-related
210 * activity on the descriptor.
217 /** Index of line fragments in the pager buffer */
218 unix_cli_pager_index_t *pager_index;
220 /** Line number of top of page */
223 /** Terminal width */
226 /** Terminal height */
229 /** Process node identifier */
230 u32 process_node_index;
232 /** The current direction of cursor travel.
233 * This is important since when advancing left-to-right, at the
234 * right hand edge of the console the terminal typically defers
235 * wrapping the cursor to the next line until a character is
236 * actually displayed.
237 * This messes up our heuristic for whether to use ANSI to return
238 * the cursor to the end of the line and instead we have to
239 * nudge the cursor to the next line.
240 * A Value of @c 0 means we're advancing left-to-right; @c 1 means
247 /** Resets the pager buffer and other data.
248 * @param f The CLI session whose pager needs to be reset.
251 unix_cli_pager_reset (unix_cli_file_t * f)
257 vec_free (f->pager_index);
260 vec_foreach (p, f->pager_vector)
264 vec_free (f->pager_vector);
268 /** Release storage used by a CLI session.
269 * @param f The CLI session whose storage needs to be released.
272 unix_cli_file_free (unix_cli_file_t * f)
274 vec_free (f->output_vector);
275 vec_free (f->input_vector);
276 unix_cli_pager_reset (f);
282 UNIX_CLI_PARSE_ACTION_NOACTION = 0, /**< No action */
283 UNIX_CLI_PARSE_ACTION_CRLF, /**< Carriage return, newline or enter */
284 UNIX_CLI_PARSE_ACTION_TAB, /**< Tab key */
285 UNIX_CLI_PARSE_ACTION_ERASE, /**< Erase cursor left */
286 UNIX_CLI_PARSE_ACTION_ERASERIGHT, /**< Erase cursor right */
287 UNIX_CLI_PARSE_ACTION_UP, /**< Up arrow */
288 UNIX_CLI_PARSE_ACTION_DOWN, /**< Down arrow */
289 UNIX_CLI_PARSE_ACTION_LEFT, /**< Left arrow */
290 UNIX_CLI_PARSE_ACTION_RIGHT, /**< Right arrow */
291 UNIX_CLI_PARSE_ACTION_HOME, /**< Home key (jump to start of line) */
292 UNIX_CLI_PARSE_ACTION_END, /**< End key (jump to end of line) */
293 UNIX_CLI_PARSE_ACTION_WORDLEFT, /**< Jump cursor to start of left word */
294 UNIX_CLI_PARSE_ACTION_WORDRIGHT, /**< Jump cursor to start of right word */
295 UNIX_CLI_PARSE_ACTION_ERASELINELEFT, /**< Erase line to left of cursor */
296 UNIX_CLI_PARSE_ACTION_ERASELINERIGHT, /**< Erase line to right & including cursor */
297 UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT, /**< Erase word left */
298 UNIX_CLI_PARSE_ACTION_CLEAR, /**< Clear the terminal */
299 UNIX_CLI_PARSE_ACTION_REVSEARCH, /**< Search backwards in command history */
300 UNIX_CLI_PARSE_ACTION_FWDSEARCH, /**< Search forwards in command history */
301 UNIX_CLI_PARSE_ACTION_YANK, /**< Undo last erase action */
302 UNIX_CLI_PARSE_ACTION_TELNETIAC, /**< Telnet control code */
304 UNIX_CLI_PARSE_ACTION_PAGER_CRLF, /**< Enter pressed (CR, CRLF, LF, etc) */
305 UNIX_CLI_PARSE_ACTION_PAGER_QUIT, /**< Exit the pager session */
306 UNIX_CLI_PARSE_ACTION_PAGER_NEXT, /**< Scroll to next page */
307 UNIX_CLI_PARSE_ACTION_PAGER_DN, /**< Scroll to next line */
308 UNIX_CLI_PARSE_ACTION_PAGER_UP, /**< Scroll to previous line */
309 UNIX_CLI_PARSE_ACTION_PAGER_TOP, /**< Scroll to first line */
310 UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM, /**< Scroll to last line */
311 UNIX_CLI_PARSE_ACTION_PAGER_PGDN, /**< Scroll to next page */
312 UNIX_CLI_PARSE_ACTION_PAGER_PGUP, /**< Scroll to previous page */
313 UNIX_CLI_PARSE_ACTION_PAGER_REDRAW, /**< Clear and redraw the page on the terminal */
314 UNIX_CLI_PARSE_ACTION_PAGER_SEARCH, /**< Search the pager buffer */
316 UNIX_CLI_PARSE_ACTION_PARTIALMATCH, /**< Action parser found a partial match */
317 UNIX_CLI_PARSE_ACTION_NOMATCH /**< Action parser did not find any match */
318 } unix_cli_parse_action_t;
320 /** @brief Mapping of input buffer strings to action values.
321 * @note This won't work as a hash since we need to be able to do
322 * partial matches on the string.
326 u8 *input; /**< Input string to match. */
327 u32 len; /**< Length of input without final NUL. */
328 unix_cli_parse_action_t action; /**< Action to take when matched. */
329 } unix_cli_parse_actions_t;
331 /** @brief Given a capital ASCII letter character return a @c NUL terminated
332 * string with the control code for that letter.
334 * @param c An ASCII character.
335 * @return A @c NUL terminated string of type @c u8[].
338 * @c CTL('A') returns <code>{ 0x01, 0x00 }</code> as a @c u8[].
340 #define CTL(c) (u8[]){ (c) - '@', 0 }
342 #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) }
344 * Patterns to match on a CLI input stream.
347 static unix_cli_parse_actions_t unix_cli_parse_strings[] = {
349 _("\r\n", UNIX_CLI_PARSE_ACTION_CRLF), /* Must be before '\r' */
350 _("\n", UNIX_CLI_PARSE_ACTION_CRLF),
351 _("\r\0", UNIX_CLI_PARSE_ACTION_CRLF), /* Telnet does this */
352 _("\r", UNIX_CLI_PARSE_ACTION_CRLF),
354 /* Unix shell control codes */
355 _(CTL ('B'), UNIX_CLI_PARSE_ACTION_LEFT),
356 _(CTL ('F'), UNIX_CLI_PARSE_ACTION_RIGHT),
357 _(CTL ('P'), UNIX_CLI_PARSE_ACTION_UP),
358 _(CTL ('N'), UNIX_CLI_PARSE_ACTION_DOWN),
359 _(CTL ('A'), UNIX_CLI_PARSE_ACTION_HOME),
360 _(CTL ('E'), UNIX_CLI_PARSE_ACTION_END),
361 _(CTL ('D'), UNIX_CLI_PARSE_ACTION_ERASERIGHT),
362 _(CTL ('U'), UNIX_CLI_PARSE_ACTION_ERASELINELEFT),
363 _(CTL ('K'), UNIX_CLI_PARSE_ACTION_ERASELINERIGHT),
364 _(CTL ('W'), UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT),
365 _(CTL ('Y'), UNIX_CLI_PARSE_ACTION_YANK),
366 _(CTL ('L'), UNIX_CLI_PARSE_ACTION_CLEAR),
367 _(ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* Alt-B */
368 _(ESC "f", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* Alt-F */
369 _("\b", UNIX_CLI_PARSE_ACTION_ERASE), /* ^H */
370 _("\x7f", UNIX_CLI_PARSE_ACTION_ERASE), /* Backspace */
371 _("\t", UNIX_CLI_PARSE_ACTION_TAB), /* ^I */
373 /* VT100 Normal mode - Broadest support */
374 _(CSI "A", UNIX_CLI_PARSE_ACTION_UP),
375 _(CSI "B", UNIX_CLI_PARSE_ACTION_DOWN),
376 _(CSI "C", UNIX_CLI_PARSE_ACTION_RIGHT),
377 _(CSI "D", UNIX_CLI_PARSE_ACTION_LEFT),
378 _(CSI "H", UNIX_CLI_PARSE_ACTION_HOME),
379 _(CSI "F", UNIX_CLI_PARSE_ACTION_END),
380 _(CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT), /* Delete */
381 _(CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* C-Left */
382 _(CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* C-Right */
384 /* VT100 Application mode - Some Gnome Terminal functions use these */
385 _(ESC "OA", UNIX_CLI_PARSE_ACTION_UP),
386 _(ESC "OB", UNIX_CLI_PARSE_ACTION_DOWN),
387 _(ESC "OC", UNIX_CLI_PARSE_ACTION_RIGHT),
388 _(ESC "OD", UNIX_CLI_PARSE_ACTION_LEFT),
389 _(ESC "OH", UNIX_CLI_PARSE_ACTION_HOME),
390 _(ESC "OF", UNIX_CLI_PARSE_ACTION_END),
392 /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */
393 _(CSI "1~", UNIX_CLI_PARSE_ACTION_HOME),
394 _(CSI "4~", UNIX_CLI_PARSE_ACTION_END),
396 /* Emacs-ish history search */
397 _(CTL ('S'), UNIX_CLI_PARSE_ACTION_FWDSEARCH),
398 _(CTL ('R'), UNIX_CLI_PARSE_ACTION_REVSEARCH),
400 /* Other protocol things */
401 _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
402 _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */
403 _(NULL, UNIX_CLI_PARSE_ACTION_NOMATCH)
407 * Patterns to match when a CLI session is in the pager.
410 static unix_cli_parse_actions_t unix_cli_parse_pager[] = {
412 _("\r\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Must be before '\r' */
413 _("\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF),
414 _("\r\0", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Telnet does this */
415 _("\r", UNIX_CLI_PARSE_ACTION_PAGER_CRLF),
418 _(" ", UNIX_CLI_PARSE_ACTION_PAGER_NEXT),
419 _("q", UNIX_CLI_PARSE_ACTION_PAGER_QUIT),
420 _(CTL ('L'), UNIX_CLI_PARSE_ACTION_PAGER_REDRAW),
421 _(CTL ('R'), UNIX_CLI_PARSE_ACTION_PAGER_REDRAW),
422 _("/", UNIX_CLI_PARSE_ACTION_PAGER_SEARCH),
425 _(CSI "A", UNIX_CLI_PARSE_ACTION_PAGER_UP),
426 _(CSI "B", UNIX_CLI_PARSE_ACTION_PAGER_DN),
427 _(CSI "H", UNIX_CLI_PARSE_ACTION_PAGER_TOP),
428 _(CSI "F", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM),
430 /* VT100 Application mode */
431 _(ESC "OA", UNIX_CLI_PARSE_ACTION_PAGER_UP),
432 _(ESC "OB", UNIX_CLI_PARSE_ACTION_PAGER_DN),
433 _(ESC "OH", UNIX_CLI_PARSE_ACTION_PAGER_TOP),
434 _(ESC "OF", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM),
436 /* ANSI X3.41-1974 */
437 _(CSI "1~", UNIX_CLI_PARSE_ACTION_PAGER_TOP),
438 _(CSI "4~", UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM),
439 _(CSI "5~", UNIX_CLI_PARSE_ACTION_PAGER_PGUP),
440 _(CSI "6~", UNIX_CLI_PARSE_ACTION_PAGER_PGDN),
442 /* Other protocol things */
443 _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
444 _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */
445 _(NULL, UNIX_CLI_PARSE_ACTION_NOMATCH)
450 /** CLI session events. */
453 UNIX_CLI_PROCESS_EVENT_READ_READY, /**< A file descriptor has data to be read. */
454 UNIX_CLI_PROCESS_EVENT_QUIT, /**< A CLI session wants to close. */
455 } unix_cli_process_event_type_t;
457 /** CLI session telnet negotiation timer events. */
460 UNIX_CLI_NEW_SESSION_EVENT_ADD, /**< Add a CLI session to the new session list */
461 } unix_cli_timeout_event_type_t;
463 /** Each new session is stored on a list with a deadline after which
464 * a prompt is issued, in case the session TELNET negotiation fails to
468 uword cf_index; /**< Session index of the new session. */
469 f64 deadline; /**< Deadline after which the new session must have a prompt. */
470 } unix_cli_new_session_t;
472 /** CLI global state. */
475 /** Prompt string for CLI. */
478 /** Vec pool of CLI sessions. */
479 unix_cli_file_t *cli_file_pool;
481 /** Vec pool of unused session indices. */
482 u32 *unused_cli_process_node_indices;
484 /** The session index of the stdin cli */
485 u32 stdin_cli_file_index;
487 /** File pool index of current input. */
488 u32 current_input_file_index;
490 /** New session process node identifier */
491 u32 new_session_process_node_index;
493 /** List of new sessions */
494 unix_cli_new_session_t *new_sessions;
497 clib_macro_main_t macro_main;
501 /** CLI global state */
502 static unix_cli_main_t unix_cli_main;
505 * @brief Search for a byte sequence in the action list.
507 * Searches the @ref unix_cli_parse_actions_t list in @a a for a match with
508 * the bytes in @a input of maximum length @a ilen bytes.
509 * When a match is made @a *matched indicates how many bytes were matched.
510 * Returns a value from the enum @ref unix_cli_parse_action_t to indicate
511 * whether no match was found, a partial match was found or a complete
512 * match was found and what action, if any, should be taken.
514 * @param[in] a Actions list to search within.
515 * @param[in] input String fragment to search for.
516 * @param[in] ilen Length of the string in 'input'.
517 * @param[out] matched Pointer to an integer that will contain the number
518 * of bytes matched when a complete match is found.
520 * @return Action from @ref unix_cli_parse_action_t that the string fragment
522 * @ref UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the
523 * whole input string matches the start of at least one action.
524 * @ref UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no
527 static unix_cli_parse_action_t
528 unix_cli_match_action (unix_cli_parse_actions_t * a,
529 u8 * input, u32 ilen, i32 * matched)
537 /* see if the start of the input buffer exactly matches the current
539 if (memcmp (input, a->input, a->len) == 0)
547 /* if the first ilen characters match, flag this as a partial -
548 * meaning keep collecting bytes in case of a future match */
549 if (memcmp (input, a->input, ilen) == 0)
553 /* check next action */
558 UNIX_CLI_PARSE_ACTION_PARTIALMATCH : UNIX_CLI_PARSE_ACTION_NOMATCH;
562 /** Add bytes to the output vector and then flagg the I/O system that bytes
563 * are available to be sent.
566 unix_cli_add_pending_output (clib_file_t * uf,
567 unix_cli_file_t * cf,
568 u8 * buffer, uword buffer_bytes)
570 clib_file_main_t *fm = &file_main;
572 vec_add (cf->output_vector, buffer, buffer_bytes);
573 if (vec_len (cf->output_vector) > 0)
575 int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
576 uf->flags |= UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
578 fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
582 /** Delete all bytes from the output vector and flag the I/O system
583 * that no more bytes are available to be sent.
586 unix_cli_del_pending_output (clib_file_t * uf,
587 unix_cli_file_t * cf, uword n_bytes)
589 clib_file_main_t *fm = &file_main;
591 vec_delete (cf->output_vector, n_bytes, 0);
592 if (vec_len (cf->output_vector) <= 0)
594 int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
595 uf->flags &= ~UNIX_FILE_DATA_AVAILABLE_TO_WRITE;
597 fm->file_update (uf, UNIX_FILE_UPDATE_MODIFY);
601 /** @brief A bit like strchr with a buffer length limit.
602 * Search a buffer for the first instance of a character up to the limit of
603 * the buffer length. If found then return the position of that character.
605 * The key departure from strchr is that if the character is not found then
606 * return the buffer length.
608 * @param chr The byte value to search for.
609 * @param str The buffer in which to search for the value.
610 * @param len The depth into the buffer to search.
612 * @return The index of the first occurrence of \c chr. If \c chr is not
613 * found then \c len instead.
616 unix_vlib_findchr (u8 chr, u8 * str, word len)
619 for (i = 0; i < len; i++, str++)
627 /** @brief Send a buffer to the CLI stream if possible, enqueue it otherwise.
628 * Attempts to write given buffer to the file descriptor of the given
629 * Unix CLI session. If that session already has data in the output buffer
630 * or if the write attempt tells us to try again later then the given buffer
631 * is appended to the pending output buffer instead.
633 * This is typically called only from \c unix_vlib_cli_output_cooked since
634 * that is where CRLF handling occurs or from places where we explicitly do
635 * not want cooked handling.
637 * @param cf Unix CLI session of the desired stream to write to.
638 * @param uf The Unix file structure of the desired stream to write to.
639 * @param buffer Pointer to the buffer that needs to be written.
640 * @param buffer_bytes The number of bytes from \c buffer to write.
643 unix_vlib_cli_output_raw (unix_cli_file_t * cf,
644 clib_file_t * uf, u8 * buffer, uword buffer_bytes)
648 if (cf->has_epipe) /* don't try writing anything */
651 if (vec_len (cf->output_vector) == 0)
654 /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
655 n = send (uf->file_descriptor, buffer, buffer_bytes, MSG_NOSIGNAL);
657 n = write (uf->file_descriptor, buffer, buffer_bytes);
660 if (n < 0 && errno != EAGAIN)
664 /* connection closed on us */
665 unix_main_t *um = &unix_main;
667 vlib_process_signal_event (um->vlib_main, cf->process_node_index,
668 UNIX_CLI_PROCESS_EVENT_QUIT,
673 clib_unix_warning ("write");
676 else if ((word) n < (word) buffer_bytes)
678 /* We got EAGAIN or we already have stuff in the buffer;
679 * queue up whatever didn't get sent for later. */
682 unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
686 /** @brief Process a buffer for CRLF handling before outputting it to the CLI.
688 * @param cf Unix CLI session of the desired stream to write to.
689 * @param uf The Unix file structure of the desired stream to write to.
690 * @param buffer Pointer to the buffer that needs to be written.
691 * @param buffer_bytes The number of bytes from \c buffer to write.
694 unix_vlib_cli_output_cooked (unix_cli_file_t * cf,
696 u8 * buffer, uword buffer_bytes)
698 word end = 0, start = 0;
700 while (end < buffer_bytes)
704 /* iterate the line on \n's so we can insert a \r before it */
705 end = unix_vlib_findchr ('\n',
707 buffer_bytes - start) + start;
711 /* otherwise just send the whole buffer */
715 unix_vlib_cli_output_raw (cf, uf, buffer + start, end - start);
719 if (end < buffer_bytes)
721 unix_vlib_cli_output_raw (cf, uf, (u8 *) "\r\n", 2);
722 end++; /* skip the \n that we already sent */
728 /* Use the last character to determine the last direction of the cursor. */
729 if (buffer_bytes > 0)
730 cf->cursor_direction = (buffer[buffer_bytes - 1] == (u8) '\b');
733 /** @brief Moves the terminal cursor one character to the left, with
734 * special handling when it reaches the left edge of the terminal window.
736 * Ordinarily we can simply send a '\b' to move the cursor left, however
737 * most terminals will not reverse-wrap to the end of the previous line
738 * if the cursor is in the left-most column. To counter this we must
739 * check the cursor position + prompt length modulo terminal width and
740 * if available use some other means, such as ANSI terminal escape
741 * sequences, to move the cursor.
743 * @param cf Unix CLI session of the desired stream to write to.
744 * @param uf The Unix file structure of the desired stream to write to.
747 unix_vlib_cli_output_cursor_left (unix_cli_file_t * cf, clib_file_t * uf)
749 unix_cli_main_t *cm = &unix_cli_main;
750 static u8 *ansi = 0; /* assumes no reentry */
753 if (!cf->is_interactive || !cf->ansi_capable || !cf->width)
755 /* No special handling for dumb terminals */
756 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
760 position = ((u32) vec_len (cm->cli_prompt) + cf->cursor) % cf->width;
764 /* No special handling required if we're not at the left edge */
765 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
769 if (!cf->cursor_direction)
771 /* Special handling for when we are at the left edge but
772 * the cursor was going left-to-right, but in this situation
773 * xterm-like terminals actually hide the cursor off the right
774 * edge. A \b here seems to jump one char too many, so let's
775 * force the cursor onto the next line instead.
777 if (cf->cursor < vec_len (cf->current_command))
778 unix_vlib_cli_output_cooked (cf, uf, &cf->current_command[cf->cursor],
781 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
782 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
785 /* Relocate the cursor at the right hand edge one line above */
786 ansi = format (ansi, CSI "A" CSI "%dC", cf->width - 1);
787 unix_vlib_cli_output_cooked (cf, uf, ansi, vec_len (ansi));
788 vec_reset_length (ansi); /* keep the vec around for next time */
789 cf->cursor_direction = 1; /* going backwards now */
792 /** @brief Output the CLI prompt */
794 unix_cli_cli_prompt (unix_cli_file_t * cf, clib_file_t * uf)
796 unix_cli_main_t *cm = &unix_cli_main;
798 if (cf->is_interactive) /* Only interactive sessions get a prompt */
799 unix_vlib_cli_output_raw (cf, uf, cm->cli_prompt,
800 vec_len (cm->cli_prompt));
803 /** @brief Output a pager prompt and show number of buffered lines */
805 unix_cli_pager_prompt (unix_cli_file_t * cf, clib_file_t * uf)
810 h = cf->pager_start + (cf->height - 1);
811 if (h > vec_len (cf->pager_index))
812 h = vec_len (cf->pager_index);
814 prompt = format (0, "\r%s-- more -- (%d-%d/%d)%s",
815 cf->ansi_capable ? ANSI_BOLD : "",
818 vec_len (cf->pager_index),
819 cf->ansi_capable ? ANSI_RESET : "");
821 unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
826 /** @brief Output a pager "skipping" message */
828 unix_cli_pager_message (unix_cli_file_t * cf, clib_file_t * uf,
829 char *message, char *postfix)
833 prompt = format (0, "\r%s-- %s --%s%s",
834 cf->ansi_capable ? ANSI_BOLD : "",
835 message, cf->ansi_capable ? ANSI_RESET : "", postfix);
837 unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
842 /** @brief Erase the printed pager prompt */
844 unix_cli_pager_prompt_erase (unix_cli_file_t * cf, clib_file_t * uf)
846 if (cf->ansi_capable)
848 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
849 unix_vlib_cli_output_cooked (cf, uf,
850 (u8 *) ANSI_CLEARLINE,
851 sizeof (ANSI_CLEARLINE) - 1);
857 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
858 for (i = 0; i < cf->width - 1; i++)
859 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
860 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
864 /** @brief Uses an ANSI escape sequence to move the cursor */
866 unix_cli_ansi_cursor (unix_cli_file_t * cf, clib_file_t * uf, u16 x, u16 y)
870 str = format (0, "%s%d;%dH", CSI, y, x);
872 unix_vlib_cli_output_cooked (cf, uf, str, vec_len (str));
877 /** Redraw the currently displayed page of text.
878 * @param cf CLI session to redraw the pager buffer of.
879 * @param uf Unix file of the CLI session.
882 unix_cli_pager_redraw (unix_cli_file_t * cf, clib_file_t * uf)
884 unix_cli_pager_index_t *pi = NULL;
888 /* No active pager? Do nothing. */
889 if (!vec_len (cf->pager_index))
892 if (cf->ansi_capable)
894 /* If we have ANSI, send the clear screen sequence */
895 unix_vlib_cli_output_cooked (cf, uf,
897 sizeof (ANSI_CLEAR) - 1);
901 /* Otherwise make sure we're on a blank line */
902 unix_cli_pager_prompt_erase (cf, uf);
905 /* (Re-)send the current page of content */
906 for (i = 0; i < cf->height - 1 &&
907 i + cf->pager_start < vec_len (cf->pager_index); i++)
909 pi = &cf->pager_index[cf->pager_start + i];
910 line = cf->pager_vector[pi->line] + pi->offset;
912 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
914 /* if the last line didn't end in newline, add a newline */
915 if (pi && line[pi->length - 1] != '\n')
916 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
918 unix_cli_pager_prompt (cf, uf);
921 /** @brief Process and add a line to the pager index.
922 * In normal operation this function will take the given character string
923 * found in @c line and with length @c len_or_index and iterates the over the
924 * contents, adding each line of text discovered within it to the
925 * pager index. Lines are identified by newlines ("<code>\\n</code>") and by
926 * strings longer than the width of the terminal.
928 * If instead @c line is @c NULL then @c len_or_index is taken to mean the
929 * index of an existing line in the pager buffer; this simply means that the
930 * input line does not need to be cloned since we already have it. This is
931 * typical if we are reindexing the pager buffer.
933 * @param cf The CLI session whose pager we are adding to.
934 * @param line The string of text to be indexed into the pager buffer.
935 * If @c line is @c NULL then the mode of operation
936 * changes slightly; see the description above.
937 * @param len_or_index If @c line is a pointer to a string then this parameter
938 * indicates the length of that string; Otherwise this
939 * value provides the index in the pager buffer of an
940 * existing string to be indexed.
943 unix_cli_pager_add_line (unix_cli_file_t * cf, u8 * line, word len_or_index)
947 word line_index, len;
948 u32 width = cf->width;
949 unix_cli_pager_index_t *pi;
953 /* Use a line already in the pager buffer */
954 line_index = len_or_index;
955 if (cf->pager_vector != NULL)
956 p = cf->pager_vector[line_index];
962 /* Add a copy of the raw string to the pager buffer */
963 p = vec_new (u8, len);
964 clib_memcpy (p, line, len);
966 /* store in pager buffer */
967 line_index = vec_len (cf->pager_vector);
968 vec_add1 (cf->pager_vector, p);
974 /* Find the next line, or run to terminal width, or run to EOL */
976 j = unix_vlib_findchr ((u8) '\n', p, l < width ? l : width);
978 if (j < l && p[j] == '\n') /* incl \n */
981 /* Add the line to the index */
982 k = vec_len (cf->pager_index);
983 vec_validate (cf->pager_index, k);
984 pi = &cf->pager_index[k];
986 pi->line = line_index;
995 /** @brief Reindex entire pager buffer.
996 * Resets the current pager index and then re-adds the lines in the pager
997 * buffer to the index.
999 * Additionally this function attempts to retain the current page start
1000 * line offset by searching for the same top-of-screen line in the new index.
1002 * @param cf The CLI session whose pager buffer should be reindexed.
1005 unix_cli_pager_reindex (unix_cli_file_t * cf)
1007 word i, old_line, old_offset;
1008 unix_cli_pager_index_t *pi;
1010 /* If there is nothing in the pager buffer then make sure the index
1011 * is empty and move on.
1013 if (cf->pager_vector == 0)
1015 vec_reset_length (cf->pager_index);
1019 /* Retain a pointer to the current page start line so we can
1022 pi = &cf->pager_index[cf->pager_start];
1023 old_line = pi->line;
1024 old_offset = pi->offset;
1026 /* Re-add the buffered lines to the index */
1027 vec_reset_length (cf->pager_index);
1028 vec_foreach_index (i, cf->pager_vector)
1030 unix_cli_pager_add_line (cf, NULL, i);
1033 /* Attempt to re-locate the previously stored page start line */
1034 vec_foreach_index (i, cf->pager_index)
1036 pi = &cf->pager_index[i];
1038 if (pi->line == old_line &&
1039 (pi->offset <= old_offset || pi->offset + pi->length > old_offset))
1042 cf->pager_start = i;
1047 /* In case the start line was not found (rare), ensure the pager start
1048 * index is within bounds
1050 if (cf->pager_start >= vec_len (cf->pager_index))
1052 if (!cf->height || vec_len (cf->pager_index) < (cf->height - 1))
1053 cf->pager_start = 0;
1055 cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
1059 /** VLIB CLI output function.
1061 * If the terminal has a pager configured then this function takes care
1062 * of collating output into the pager buffer; ensuring only the first page
1063 * is displayed and any lines in excess of the first page are buffered.
1065 * If the maximum number of index lines in the buffer is exceeded then the
1066 * pager is cancelled and the contents of the current buffer are sent to the
1069 * If there is no pager configured then the output is sent directly to the
1072 * @param cli_file_index Index of the CLI session where this output is
1074 * @param buffer String of printabe bytes to be output.
1075 * @param buffer_bytes The number of bytes in @c buffer to be output.
1078 unix_vlib_cli_output (uword cli_file_index, u8 * buffer, uword buffer_bytes)
1080 unix_main_t *um = &unix_main;
1081 clib_file_main_t *fm = &file_main;
1082 unix_cli_main_t *cm = &unix_cli_main;
1083 unix_cli_file_t *cf;
1086 cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1087 uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
1089 if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
1091 unix_vlib_cli_output_cooked (cf, uf, buffer, buffer_bytes);
1095 word row = vec_len (cf->pager_index);
1097 unix_cli_pager_index_t *pi;
1099 /* Index and add the output lines to the pager buffer. */
1100 unix_cli_pager_add_line (cf, buffer, buffer_bytes);
1102 /* Now iterate what was added to display the lines.
1103 * If we reach the bottom of the page, display a prompt.
1105 while (row < vec_len (cf->pager_index))
1107 if (row < cf->height - 1)
1109 /* output this line */
1110 pi = &cf->pager_index[row];
1111 line = cf->pager_vector[pi->line] + pi->offset;
1112 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1114 /* if the last line didn't end in newline, and we're at the
1115 * bottom of the page, add a newline */
1116 if (line[pi->length - 1] != '\n' && row == cf->height - 2)
1117 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1121 /* Display the pager prompt every 10 lines */
1123 unix_cli_pager_prompt (cf, uf);
1128 /* Check if we went over the pager buffer limit */
1129 if (vec_len (cf->pager_index) > um->cli_pager_buffer_limit)
1131 /* Stop using the pager for the remainder of this CLI command */
1134 /* If we likely printed the prompt, erase it */
1135 if (vec_len (cf->pager_index) > cf->height - 1)
1136 unix_cli_pager_prompt_erase (cf, uf);
1138 /* Dump out the contents of the buffer */
1139 for (row = cf->pager_start + (cf->height - 1);
1140 row < vec_len (cf->pager_index); row++)
1142 pi = &cf->pager_index[row];
1143 line = cf->pager_vector[pi->line] + pi->offset;
1144 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1147 unix_cli_pager_reset (cf);
1152 /** Identify whether a terminal type is ANSI capable.
1154 * Compares the string given in @c term with a list of terminal types known
1155 * to support ANSI escape sequences.
1157 * This list contains, for example, @c xterm, @c screen and @c ansi.
1159 * @param term A string with a terminal type in it.
1160 * @param len The length of the string in @c term.
1162 * @return @c 1 if the terminal type is recognized as supporting ANSI
1163 * terminal sequences; @c 0 otherwise.
1166 unix_cli_terminal_type_ansi (u8 * term, uword len)
1168 /* This may later be better done as a hash of some sort. */
1170 if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
1175 _("xterm-256color"); /* iTerm on Mac */
1177 _("screen-256color"); /* Screen and tmux */
1178 _("ansi"); /* Microsoft Telnet */
1184 /** Identify whether a terminal type is non-interactive.
1186 * Compares the string given in @c term with a list of terminal types known
1187 * to be non-interactive, as send by tools such as @c vppctl .
1189 * This list contains, for example, @c vppctl.
1191 * @param term A string with a terminal type in it.
1192 * @param len The length of the string in @c term.
1194 * @return @c 1 if the terminal type is recognized as being non-interactive;
1198 unix_cli_terminal_type_noninteractive (u8 * term, uword len)
1200 /* This may later be better done as a hash of some sort. */
1202 if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
1211 /** Set a session to be non-interactive. */
1213 unix_cli_set_session_noninteractive (unix_cli_file_t * cf)
1215 /* Non-interactive sessions don't get these */
1216 cf->is_interactive = 0;
1218 cf->history_limit = 0;
1219 cf->has_history = 0;
1223 /** @brief Emit initial welcome banner and prompt on a connection. */
1225 unix_cli_file_welcome (unix_cli_main_t * cm, unix_cli_file_t * cf)
1227 unix_main_t *um = &unix_main;
1228 clib_file_main_t *fm = &file_main;
1229 clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
1230 unix_cli_banner_t *banner;
1233 /* Mark the session as started if we get here */
1236 if (!(cf->is_interactive)) /* No banner for non-interactive sessions */
1240 * Put the first bytes directly into the buffer so that further output is
1241 * queued until everything is ready. (oterwise initial prompt can appear
1242 * mid way through VPP initialization)
1244 unix_cli_add_pending_output (uf, cf, (u8 *) "\r", 1);
1246 if (!um->cli_no_banner)
1248 if (cf->ansi_capable)
1250 banner = unix_cli_banner_color;
1251 len = ARRAY_LEN (unix_cli_banner_color);
1255 banner = unix_cli_banner;
1256 len = ARRAY_LEN (unix_cli_banner);
1259 for (i = 0; i < len; i++)
1261 unix_vlib_cli_output_cooked (cf, uf,
1262 banner[i].line, banner[i].length);
1267 unix_cli_cli_prompt (cf, uf);
1272 * @brief A failsafe manager that ensures CLI sessions issue an initial
1273 * prompt if TELNET negotiation fails.
1276 unix_cli_new_session_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
1279 unix_cli_main_t *cm = &unix_cli_main;
1280 uword event_type, *event_data = 0;
1285 if (vec_len (cm->new_sessions) > 0)
1286 wait = vlib_process_wait_for_event_or_clock (vm, wait);
1288 vlib_process_wait_for_event (vm);
1290 event_type = vlib_process_get_events (vm, &event_data);
1294 case ~0: /* no events => timeout */
1297 case UNIX_CLI_NEW_SESSION_EVENT_ADD:
1299 /* Add an identifier to the new session list */
1300 unix_cli_new_session_t ns;
1302 ns.cf_index = event_data[0];
1303 ns.deadline = vlib_time_now (vm) + 1.0;
1305 vec_add1 (cm->new_sessions, ns);
1308 wait = 0.1; /* force a re-evaluation soon, but not too soon */
1313 clib_warning ("BUG: unknown event type 0x%wx", event_type);
1317 vec_reset_length (event_data);
1319 if (vlib_process_suspend_time_is_zero (wait))
1321 /* Scan the list looking for expired deadlines.
1322 * Emit needed prompts and remove from the list.
1323 * While scanning, look for the nearest new deadline
1324 * for the next iteration.
1325 * Since the list is ordered with newest sessions first
1326 * we can make assumptions about expired sessions being
1327 * contiguous at the beginning and the next deadline is the
1328 * next entry on the list, if any.
1330 f64 now = vlib_time_now (vm);
1331 unix_cli_new_session_t *nsp;
1336 vec_foreach (nsp, cm->new_sessions)
1338 if (vlib_process_suspend_time_is_zero (nsp->deadline - now))
1340 /* Deadline reached */
1341 unix_cli_file_t *cf;
1343 /* Mark the highwater */
1346 /* Check the connection didn't close already */
1347 if (pool_is_free_index (cm->cli_file_pool, nsp->cf_index))
1350 cf = pool_elt_at_index (cm->cli_file_pool, nsp->cf_index);
1353 unix_cli_file_welcome (cm, cf);
1357 wait = nsp->deadline - now;
1364 /* We have sessions to remove */
1365 vec_delete (cm->new_sessions, index, 0);
1374 /** @brief A mostly no-op Telnet state machine.
1375 * Process Telnet command bytes in a way that ensures we're mostly
1376 * transparent to the Telnet protocol. That is, it's mostly a no-op.
1378 * @return -1 if we need more bytes, otherwise a positive integer number of
1379 * bytes to consume from the input_vector, not including the initial
1383 unix_cli_process_telnet (unix_main_t * um,
1384 unix_cli_file_t * cf,
1385 clib_file_t * uf, u8 * input_vector, uword len)
1387 /* Input_vector starts at IAC byte.
1388 * See if we have a complete message; if not, return -1 so we wait for more.
1389 * if we have a complete message, consume those bytes from the vector.
1394 return -1; /* want more bytes */
1396 switch (input_vector[1])
1399 /* two IAC's in a row means to pass through 0xff.
1400 * since that makes no sense here, just consume it.
1409 /* Expect 3 bytes */
1411 return -1; /* want more bytes */
1418 /* Sub option - search ahead for IAC SE to end it */
1420 for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
1422 if (input_vector[i - 1] == IAC && input_vector[i] == SE)
1424 /* We have a complete message; see if we care about it */
1425 switch (input_vector[2])
1428 if (input_vector[3] != 0)
1431 /* See if the the terminal type is recognized */
1432 u8 *term = input_vector + 4;
1435 /* See if the terminal type is ANSI capable */
1437 unix_cli_terminal_type_ansi (term, len);
1439 /* See if the terminal type indicates non-interactive */
1440 if (unix_cli_terminal_type_noninteractive (term, len))
1441 unix_cli_set_session_noninteractive (cf);
1444 /* If session not started, we can release the pause */
1446 /* Send the welcome banner and initial prompt */
1447 unix_cli_file_welcome (&unix_cli_main, cf);
1452 if (i != 8) /* check message is correct size */
1456 clib_net_to_host_u16 (*((u16 *) (input_vector + 3)));
1457 if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH)
1458 cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH;
1460 cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
1463 clib_net_to_host_u16 (*((u16 *) (input_vector + 5)));
1464 if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT)
1465 cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT;
1466 if (cf->height == 0)
1467 cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
1469 /* reindex pager buffer */
1470 unix_cli_pager_reindex (cf);
1472 unix_cli_pager_redraw (cf, uf);
1478 /* Consume it all */
1484 if (i == UNIX_CLI_MAX_DEPTH_TELNET)
1485 consume = 1; /* hit max search depth, advance one byte */
1488 return -1; /* want more bytes */
1506 /* Simple one-byte messages */
1511 /* Are You There - trigger a visible response */
1513 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
1517 /* Unknown command! Eat the IAC byte */
1524 /** @brief Process actionable input.
1525 * Based on the \c action process the input; this typically involves
1526 * searching the command history or editing the current command line.
1529 unix_cli_line_process_one (unix_cli_main_t * cm,
1531 unix_cli_file_t * cf,
1533 u8 input, unix_cli_parse_action_t action)
1537 u8 **possible_commands;
1542 case UNIX_CLI_PARSE_ACTION_NOACTION:
1545 case UNIX_CLI_PARSE_ACTION_REVSEARCH:
1546 case UNIX_CLI_PARSE_ACTION_FWDSEARCH:
1547 if (!cf->has_history || !cf->history_limit)
1549 if (cf->search_mode == 0)
1551 /* Erase the current command (if any) */
1552 for (; cf->cursor > 0; cf->cursor--)
1554 unix_vlib_cli_output_cursor_left (cf, uf);
1555 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1556 unix_vlib_cli_output_cursor_left (cf, uf);
1559 vec_reset_length (cf->search_key);
1560 vec_reset_length (cf->current_command);
1562 if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1563 cf->search_mode = -1;
1565 cf->search_mode = 1;
1569 if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1570 cf->search_mode = -1;
1572 cf->search_mode = 1;
1574 cf->excursion += cf->search_mode;
1579 case UNIX_CLI_PARSE_ACTION_ERASELINELEFT:
1580 /* Erase the command from the cursor to the start */
1583 /* Shimmy backwards to the new end of line position */
1584 delta = vec_len (cf->current_command) - cf->cursor;
1585 for (; cf->cursor > delta; cf->cursor--)
1586 unix_vlib_cli_output_cursor_left (cf, uf);
1587 /* Zap from here to the end of what is currently displayed */
1588 for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
1589 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1590 /* Get back to the start of the line */
1591 for (; cf->cursor > 0; cf->cursor--)
1592 unix_vlib_cli_output_cursor_left (cf, uf);
1594 /* Delete the desired text from the command */
1595 memmove (cf->current_command, cf->current_command + j, delta);
1596 _vec_len (cf->current_command) = delta;
1598 /* Print the new contents */
1599 unix_vlib_cli_output_cooked (cf, uf, cf->current_command, delta);
1600 cf->cursor = delta; /* for backspace tracking */
1602 /* Shimmy back to the start */
1603 for (; cf->cursor > 0; cf->cursor--)
1604 unix_vlib_cli_output_cursor_left (cf, uf);
1606 cf->search_mode = 0;
1609 case UNIX_CLI_PARSE_ACTION_ERASELINERIGHT:
1610 /* Erase the command from the cursor to the end */
1613 /* Zap from cursor to end of what is currently displayed */
1614 for (; cf->cursor < (vec_len (cf->current_command)); cf->cursor++)
1615 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1616 /* Get back to where we were */
1617 for (; cf->cursor > j; cf->cursor--)
1618 unix_vlib_cli_output_cursor_left (cf, uf);
1620 /* Truncate the line at the cursor */
1621 _vec_len (cf->current_command) = cf->cursor;
1623 cf->search_mode = 0;
1626 case UNIX_CLI_PARSE_ACTION_ERASEWORDLEFT:
1627 /* calculate num of caracter to be erased */
1629 while (cf->cursor > delta
1630 && cf->current_command[cf->cursor - delta - 1] == ' ')
1632 while (cf->cursor > delta
1633 && cf->current_command[cf->cursor - delta - 1] != ' ')
1636 if (vec_len (cf->current_command))
1640 /* move cursor left delta times */
1641 for (j = delta; j > 0; j--, cf->cursor--)
1642 unix_vlib_cli_output_cursor_left (cf, uf);
1643 save = cf->current_command + cf->cursor;
1645 /* redraw remainder of line */
1646 memmove (cf->current_command + cf->cursor,
1647 cf->current_command + cf->cursor + delta,
1648 _vec_len (cf->current_command) - cf->cursor - delta);
1649 unix_vlib_cli_output_cooked (cf, uf,
1650 cf->current_command + cf->cursor,
1651 _vec_len (cf->current_command) -
1653 cf->cursor += _vec_len (cf->current_command) - cf->cursor;
1655 /* print delta amount of blank spaces,
1656 * then finally fix the cursor position */
1657 for (j = delta; j > 0; j--, cf->cursor--)
1658 unix_vlib_cli_output_cursor_left (cf, uf);
1659 for (j = delta; j > 0; j--, cf->cursor++)
1660 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1661 for (; (cf->current_command + cf->cursor) > save; cf->cursor--)
1662 unix_vlib_cli_output_cursor_left (cf, uf);
1663 _vec_len (cf->current_command) -= delta;
1666 cf->search_mode = 0;
1668 vec_reset_length (cf->search_key);
1671 case UNIX_CLI_PARSE_ACTION_LEFT:
1674 unix_vlib_cli_output_cursor_left (cf, uf);
1678 cf->search_mode = 0;
1681 case UNIX_CLI_PARSE_ACTION_RIGHT:
1682 if (cf->cursor < vec_len (cf->current_command))
1684 /* have to emit the character under the cursor */
1685 unix_vlib_cli_output_cooked (cf, uf,
1686 cf->current_command + cf->cursor, 1);
1690 cf->search_mode = 0;
1693 case UNIX_CLI_PARSE_ACTION_UP:
1694 case UNIX_CLI_PARSE_ACTION_DOWN:
1695 if (!cf->has_history || !cf->history_limit)
1697 cf->search_mode = 0;
1698 /* Erase the command */
1699 for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
1700 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1701 for (; cf->cursor > 0; cf->cursor--)
1703 unix_vlib_cli_output_cursor_left (cf, uf);
1704 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1705 unix_vlib_cli_output_cursor_left (cf, uf);
1707 vec_reset_length (cf->current_command);
1708 if (vec_len (cf->command_history))
1710 if (action == UNIX_CLI_PARSE_ACTION_UP)
1715 cf->excursion += delta;
1717 if (cf->excursion == vec_len (cf->command_history))
1719 /* down-arrowed to last entry - want a blank line */
1720 _vec_len (cf->current_command) = 0;
1722 else if (cf->excursion < 0)
1724 /* up-arrowed over the start to the end, want a blank line */
1725 cf->excursion = vec_len (cf->command_history);
1726 _vec_len (cf->current_command) = 0;
1730 if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
1731 /* down-arrowed past end - wrap to start */
1734 /* Print the command at the current position */
1735 prev = cf->command_history[cf->excursion];
1736 vec_validate (cf->current_command, vec_len (prev) - 1);
1738 clib_memcpy (cf->current_command, prev, vec_len (prev));
1739 _vec_len (cf->current_command) = vec_len (prev);
1740 unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
1741 vec_len (cf->current_command));
1744 cf->cursor = vec_len (cf->current_command);
1747 case UNIX_CLI_PARSE_ACTION_HOME:
1748 if (vec_len (cf->current_command) && cf->cursor > 0)
1750 for (; cf->cursor > 0; cf->cursor--)
1751 unix_vlib_cli_output_cursor_left (cf, uf);
1754 cf->search_mode = 0;
1757 case UNIX_CLI_PARSE_ACTION_END:
1758 if (vec_len (cf->current_command) &&
1759 cf->cursor < vec_len (cf->current_command))
1761 unix_vlib_cli_output_cooked (cf, uf,
1762 cf->current_command + cf->cursor,
1763 vec_len (cf->current_command) -
1765 cf->cursor = vec_len (cf->current_command);
1768 cf->search_mode = 0;
1771 case UNIX_CLI_PARSE_ACTION_WORDLEFT:
1772 if (vec_len (cf->current_command) && cf->cursor > 0)
1774 unix_vlib_cli_output_cursor_left (cf, uf);
1777 while (cf->cursor && isspace (cf->current_command[cf->cursor]))
1779 unix_vlib_cli_output_cursor_left (cf, uf);
1782 while (cf->cursor && !isspace (cf->current_command[cf->cursor]))
1784 if (isspace (cf->current_command[cf->cursor - 1]))
1786 unix_vlib_cli_output_cursor_left (cf, uf);
1792 cf->search_mode = 0;
1795 case UNIX_CLI_PARSE_ACTION_WORDRIGHT:
1796 if (vec_len (cf->current_command) &&
1797 cf->cursor < vec_len (cf->current_command))
1799 int e = vec_len (cf->current_command);
1801 while (j < e && !isspace (cf->current_command[j]))
1803 while (j < e && isspace (cf->current_command[j]))
1805 unix_vlib_cli_output_cooked (cf, uf,
1806 cf->current_command + cf->cursor,
1811 cf->search_mode = 0;
1815 case UNIX_CLI_PARSE_ACTION_ERASE:
1816 if (vec_len (cf->current_command))
1818 if (cf->cursor == vec_len (cf->current_command))
1820 unix_vlib_cli_output_cursor_left (cf, uf);
1822 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1824 unix_vlib_cli_output_cursor_left (cf, uf);
1826 _vec_len (cf->current_command)--;
1828 else if (cf->cursor > 0)
1830 /* shift everything at & to the right of the cursor left by 1 */
1831 j = vec_len (cf->current_command) - cf->cursor;
1832 memmove (cf->current_command + cf->cursor - 1,
1833 cf->current_command + cf->cursor, j);
1834 _vec_len (cf->current_command)--;
1836 /* redraw the rest of the line */
1837 unix_vlib_cli_output_cursor_left (cf, uf);
1839 unix_vlib_cli_output_cooked (cf, uf,
1840 cf->current_command + cf->cursor,
1843 /* erase last char */
1844 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1847 /* and shift the terminal cursor back where it should be */
1848 j += 2; /* account for old string length and offset position */
1851 unix_vlib_cli_output_cursor_left (cf, uf);
1856 cf->search_mode = 0;
1858 vec_reset_length (cf->search_key);
1861 case UNIX_CLI_PARSE_ACTION_ERASERIGHT:
1862 if (vec_len (cf->current_command))
1864 if (cf->cursor < vec_len (cf->current_command))
1866 /* shift everything to the right of the cursor left by 1 */
1867 j = vec_len (cf->current_command) - cf->cursor - 1;
1868 memmove (cf->current_command + cf->cursor,
1869 cf->current_command + cf->cursor + 1, j);
1870 _vec_len (cf->current_command)--;
1871 /* redraw the rest of the line */
1872 unix_vlib_cli_output_cooked (cf, uf,
1873 cf->current_command + cf->cursor,
1876 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1878 unix_vlib_cli_output_cursor_left (cf, uf);
1880 /* and shift the terminal cursor back where it should be */
1883 unix_vlib_cli_output_cursor_left (cf, uf);
1887 unix_vlib_cli_output_cursor_left (cf, uf);
1893 else if (input == 'D' - '@')
1895 /* ^D with no command entered = quit */
1896 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
1897 vlib_process_signal_event (um->vlib_main,
1898 vlib_current_process (um->vlib_main),
1899 UNIX_CLI_PROCESS_EVENT_QUIT,
1900 cf - cm->cli_file_pool);
1902 cf->search_mode = 0;
1904 vec_reset_length (cf->search_key);
1907 case UNIX_CLI_PARSE_ACTION_CLEAR:
1908 /* If we're in ANSI mode, clear the screen.
1909 * Then redraw the prompt and any existing command input, then put
1910 * the cursor back where it was in that line.
1912 if (cf->ansi_capable)
1913 unix_vlib_cli_output_cooked (cf, uf,
1915 sizeof (ANSI_CLEAR) - 1);
1917 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1919 unix_vlib_cli_output_raw (cf, uf,
1920 cm->cli_prompt, vec_len (cm->cli_prompt));
1921 unix_vlib_cli_output_cooked (cf, uf,
1922 cf->current_command,
1923 vec_len (cf->current_command));
1925 cf->cursor = vec_len (cf->current_command);
1926 for (; cf->cursor > j; cf->cursor--)
1927 unix_vlib_cli_output_cursor_left (cf, uf);
1931 case UNIX_CLI_PARSE_ACTION_TAB:
1932 if (cf->cursor < vec_len (cf->current_command))
1934 /* if we are in the middle of a line, complete only if
1935 * the cursor points to whitespace */
1936 if (isspace (cf->current_command[cf->cursor]))
1938 /* save and clear any input that is after the cursor */
1939 vec_resize (save, vec_len (cf->current_command) - cf->cursor);
1940 clib_memcpy (save, cf->current_command + cf->cursor,
1941 vec_len (cf->current_command) - cf->cursor);
1942 _vec_len (cf->current_command) = cf->cursor;
1946 unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
1951 vlib_cli_get_possible_completions (cf->current_command);
1952 if (vec_len (possible_commands) == 1)
1954 u8 *completed = possible_commands[0];
1957 /* find the last word of current_command */
1958 while (j >= 1 && !isspace (cf->current_command[j - 1]))
1960 unix_vlib_cli_output_cursor_left (cf, uf);
1964 _vec_len (cf->current_command) = j;
1966 /* replace it with the newly expanded command */
1967 vec_append (cf->current_command, completed);
1969 /* echo to the terminal */
1970 unix_vlib_cli_output_cooked (cf, uf, completed,
1971 vec_len (completed));
1973 /* add one trailing space if needed */
1974 if (vec_len (save) == 0)
1976 vec_add1 (cf->current_command, ' ');
1977 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1980 cf->cursor = vec_len (cf->current_command);
1983 else if (vec_len (possible_commands) >= 2)
1985 u8 **possible_command;
1986 uword max_command_len = 0, min_command_len = ~0;
1989 vec_foreach (possible_command, possible_commands)
1991 if (vec_len (*possible_command) > max_command_len)
1992 max_command_len = vec_len (*possible_command);
1993 if (vec_len (*possible_command) < min_command_len)
1994 min_command_len = vec_len (*possible_command);
1997 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2000 vec_foreach (possible_command, possible_commands)
2002 if (i + max_command_len >= cf->width)
2004 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2007 unix_vlib_cli_output_cooked (cf, uf, *possible_command,
2008 vec_len (*possible_command));
2009 for (j = vec_len (*possible_command); j < max_command_len + 2;
2012 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
2014 i += max_command_len + 2;
2017 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2019 /* rewrite prompt */
2020 unix_cli_cli_prompt (cf, uf);
2021 unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
2022 vec_len (cf->current_command));
2024 /* count length of last word */
2027 while (j >= 1 && !isspace (cf->current_command[j - 1]))
2033 /* determine smallest common command */
2034 for (; i < min_command_len; i++)
2039 vec_foreach (possible_command, possible_commands)
2043 common = (*possible_command)[i];
2045 else if (common != (*possible_command)[i])
2054 vec_add1 (cf->current_command, common);
2056 unix_vlib_cli_output_cooked (cf, uf, (u8 *) & common, 1);
2066 unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
2069 if (vec_len (save) > 0)
2071 /* restore remaining input if tab was hit in the middle of a line */
2072 unix_vlib_cli_output_cooked (cf, uf, save, vec_len (save));
2073 cf->cursor += vec_len (save);
2074 for (j = 0; j < vec_len (save); j++, cf->cursor--)
2075 unix_vlib_cli_output_cursor_left (cf, uf);
2076 vec_append (cf->current_command, save);
2079 vec_free (possible_commands);
2082 case UNIX_CLI_PARSE_ACTION_YANK:
2087 case UNIX_CLI_PARSE_ACTION_PAGER_QUIT:
2089 unix_cli_pager_prompt_erase (cf, uf);
2090 unix_cli_pager_reset (cf);
2091 unix_cli_cli_prompt (cf, uf);
2094 case UNIX_CLI_PARSE_ACTION_PAGER_NEXT:
2095 case UNIX_CLI_PARSE_ACTION_PAGER_PGDN:
2096 /* show next page of the buffer */
2097 if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
2100 unix_cli_pager_index_t *pi = NULL;
2102 int m = cf->pager_start + (cf->height - 1);
2103 unix_cli_pager_prompt_erase (cf, uf);
2105 j < vec_len (cf->pager_index) && cf->pager_start < m;
2106 j++, cf->pager_start++)
2108 pi = &cf->pager_index[j];
2109 line = cf->pager_vector[pi->line] + pi->offset;
2110 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2112 /* if the last line didn't end in newline, add a newline */
2113 if (pi && line[pi->length - 1] != '\n')
2114 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2115 unix_cli_pager_prompt (cf, uf);
2119 if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
2120 /* no more in buffer, exit, but only if it was <space> */
2125 case UNIX_CLI_PARSE_ACTION_PAGER_DN:
2126 case UNIX_CLI_PARSE_ACTION_PAGER_CRLF:
2127 /* display the next line of the buffer */
2128 if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
2131 unix_cli_pager_index_t *pi;
2133 unix_cli_pager_prompt_erase (cf, uf);
2134 pi = &cf->pager_index[cf->pager_start + (cf->height - 1)];
2135 line = cf->pager_vector[pi->line] + pi->offset;
2136 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2138 /* if the last line didn't end in newline, add a newline */
2139 if (line[pi->length - 1] != '\n')
2140 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2141 unix_cli_pager_prompt (cf, uf);
2145 if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
2146 /* no more in buffer, exit, but only if it was <enter> */
2152 case UNIX_CLI_PARSE_ACTION_PAGER_UP:
2153 /* scroll the page back one line */
2154 if (cf->pager_start > 0)
2157 unix_cli_pager_index_t *pi = NULL;
2160 if (cf->ansi_capable)
2162 pi = &cf->pager_index[cf->pager_start];
2163 line = cf->pager_vector[pi->line] + pi->offset;
2164 unix_cli_pager_prompt_erase (cf, uf);
2165 unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_SCROLLDN,
2166 sizeof (ANSI_SCROLLDN) - 1);
2167 unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_SAVECURSOR,
2168 sizeof (ANSI_SAVECURSOR) - 1);
2169 unix_cli_ansi_cursor (cf, uf, 1, 1);
2170 unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_CLEARLINE,
2171 sizeof (ANSI_CLEARLINE) - 1);
2172 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2173 unix_vlib_cli_output_cooked (cf, uf, (u8 *) ANSI_RESTCURSOR,
2174 sizeof (ANSI_RESTCURSOR) - 1);
2175 unix_cli_pager_prompt_erase (cf, uf);
2176 unix_cli_pager_prompt (cf, uf);
2180 int m = cf->pager_start + (cf->height - 1);
2181 unix_cli_pager_prompt_erase (cf, uf);
2182 for (j = cf->pager_start;
2183 j < vec_len (cf->pager_index) && j < m; j++)
2185 pi = &cf->pager_index[j];
2186 line = cf->pager_vector[pi->line] + pi->offset;
2187 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2189 /* if the last line didn't end in newline, add a newline */
2190 if (pi && line[pi->length - 1] != '\n')
2191 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2192 unix_cli_pager_prompt (cf, uf);
2197 case UNIX_CLI_PARSE_ACTION_PAGER_TOP:
2198 /* back to the first page of the buffer */
2199 if (cf->pager_start > 0)
2202 unix_cli_pager_index_t *pi = NULL;
2204 cf->pager_start = 0;
2205 int m = cf->pager_start + (cf->height - 1);
2206 unix_cli_pager_prompt_erase (cf, uf);
2207 for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
2210 pi = &cf->pager_index[j];
2211 line = cf->pager_vector[pi->line] + pi->offset;
2212 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2214 /* if the last line didn't end in newline, add a newline */
2215 if (pi && line[pi->length - 1] != '\n')
2216 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2217 unix_cli_pager_prompt (cf, uf);
2221 case UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM:
2222 /* skip to the last page of the buffer */
2223 if (cf->pager_start < vec_len (cf->pager_index) - (cf->height - 1))
2226 unix_cli_pager_index_t *pi = NULL;
2228 cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
2229 unix_cli_pager_prompt_erase (cf, uf);
2230 unix_cli_pager_message (cf, uf, "skipping", "\n");
2231 for (j = cf->pager_start; j < vec_len (cf->pager_index); j++)
2233 pi = &cf->pager_index[j];
2234 line = cf->pager_vector[pi->line] + pi->offset;
2235 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2237 /* if the last line didn't end in newline, add a newline */
2238 if (pi && line[pi->length - 1] != '\n')
2239 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2240 unix_cli_pager_prompt (cf, uf);
2244 case UNIX_CLI_PARSE_ACTION_PAGER_PGUP:
2245 /* wander back one page in the buffer */
2246 if (cf->pager_start > 0)
2249 unix_cli_pager_index_t *pi = NULL;
2252 if (cf->pager_start >= cf->height)
2253 cf->pager_start -= cf->height - 1;
2255 cf->pager_start = 0;
2256 m = cf->pager_start + cf->height - 1;
2257 unix_cli_pager_prompt_erase (cf, uf);
2258 for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
2261 pi = &cf->pager_index[j];
2262 line = cf->pager_vector[pi->line] + pi->offset;
2263 unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2265 /* if the last line didn't end in newline, add a newline */
2266 if (pi && line[pi->length - 1] != '\n')
2267 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2268 unix_cli_pager_prompt (cf, uf);
2272 case UNIX_CLI_PARSE_ACTION_PAGER_REDRAW:
2273 /* Redraw the current pager screen */
2274 unix_cli_pager_redraw (cf, uf);
2277 case UNIX_CLI_PARSE_ACTION_PAGER_SEARCH:
2278 /* search forwards in the buffer */
2282 case UNIX_CLI_PARSE_ACTION_CRLF:
2284 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2286 if (cf->has_history && cf->history_limit)
2288 if (cf->command_history
2289 && vec_len (cf->command_history) >= cf->history_limit)
2291 vec_free (cf->command_history[0]);
2292 vec_delete (cf->command_history, 1, 0);
2294 /* Don't add blank lines to the cmd history */
2295 if (vec_len (cf->current_command))
2297 /* Don't duplicate the previous command */
2298 j = vec_len (cf->command_history);
2300 (vec_len (cf->current_command) !=
2301 vec_len (cf->command_history[j - 1])
2302 || memcmp (cf->current_command, cf->command_history[j - 1],
2303 vec_len (cf->current_command)) != 0))
2305 /* copy the command to the history */
2307 vec_append (c, cf->current_command);
2308 vec_add1 (cf->command_history, c);
2309 cf->command_number++;
2312 cf->excursion = vec_len (cf->command_history);
2315 cf->search_mode = 0;
2316 vec_reset_length (cf->search_key);
2321 case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
2322 case UNIX_CLI_PARSE_ACTION_NOMATCH:
2323 if (vec_len (cf->pager_index))
2327 else if (cf->has_history && cf->search_mode != 0 && isprint (input))
2329 int k, limit, offset;
2332 vec_add1 (cf->search_key, input);
2335 for (j = 0; j < vec_len (cf->command_history); j++)
2337 if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
2339 else if (cf->excursion < 0)
2340 cf->excursion = vec_len (cf->command_history) - 1;
2342 item = cf->command_history[cf->excursion];
2344 limit = (vec_len (cf->search_key) > vec_len (item)) ?
2345 vec_len (item) : vec_len (cf->search_key);
2347 for (offset = 0; offset <= vec_len (item) - limit; offset++)
2349 for (k = 0; k < limit; k++)
2351 if (item[k + offset] != cf->search_key[k])
2354 goto found_at_offset;
2362 for (; cf->cursor > 0; cf->cursor--)
2364 unix_vlib_cli_output_cursor_left (cf, uf);
2365 unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
2366 unix_vlib_cli_output_cursor_left (cf, uf);
2369 vec_validate (cf->current_command, vec_len (item) - 1);
2370 clib_memcpy (cf->current_command, item, vec_len (item));
2371 _vec_len (cf->current_command) = vec_len (item);
2373 unix_vlib_cli_output_cooked (cf, uf, cf->current_command,
2374 vec_len (cf->current_command));
2375 cf->cursor = vec_len (cf->current_command);
2379 cf->excursion += cf->search_mode;
2382 unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\nNo match...", 12);
2383 vec_reset_length (cf->search_key);
2384 vec_reset_length (cf->current_command);
2385 cf->search_mode = 0;
2389 else if (isprint (input)) /* skip any errant control codes */
2391 if (cf->cursor == vec_len (cf->current_command))
2394 vec_add1 (cf->current_command, input);
2397 /* Echo the character back to the client */
2398 unix_vlib_cli_output_cooked (cf, uf, &input, 1);
2402 /* Insert at cursor: resize +1 byte, move everything over */
2403 j = vec_len (cf->current_command) - cf->cursor;
2404 vec_add1 (cf->current_command, (u8) 'A');
2405 memmove (cf->current_command + cf->cursor + 1,
2406 cf->current_command + cf->cursor, j);
2407 cf->current_command[cf->cursor] = input;
2408 /* Redraw the line */
2410 unix_vlib_cli_output_cooked (cf, uf,
2411 cf->current_command + cf->cursor,
2415 /* Put terminal cursor back */
2416 for (; j > 0; j--, cf->cursor--)
2417 unix_vlib_cli_output_cursor_left (cf, uf);
2422 /* no-op - not printable or otherwise not actionable */
2429 case UNIX_CLI_PARSE_ACTION_TELNETIAC:
2435 /** @brief Process input bytes on a stream to provide line editing and
2436 * command history in the CLI. */
2438 unix_cli_line_edit (unix_cli_main_t * cm, unix_main_t * um,
2439 clib_file_main_t * fm, unix_cli_file_t * cf)
2441 clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
2444 for (i = 0; i < vec_len (cf->input_vector); i++)
2446 unix_cli_parse_action_t action;
2448 unix_cli_parse_actions_t *a;
2450 /* If we're in the pager mode, search the pager actions */
2452 vec_len (cf->pager_index) ? unix_cli_parse_pager :
2453 unix_cli_parse_strings;
2455 /* See if the input buffer is some sort of control code */
2456 action = unix_cli_match_action (a, &cf->input_vector[i],
2457 vec_len (cf->input_vector) - i,
2462 case UNIX_CLI_PARSE_ACTION_PARTIALMATCH:
2465 /* There was a partial match which means we need more bytes
2466 * than the input buffer currently has.
2467 * Since the bytes before here have been processed, shift
2468 * the remaining contents to the start of the input buffer.
2470 vec_delete (cf->input_vector, i, 0);
2472 return 1; /* wait for more */
2474 case UNIX_CLI_PARSE_ACTION_TELNETIAC:
2475 /* process telnet options */
2476 matched = unix_cli_process_telnet (um, cf, uf,
2477 cf->input_vector + i,
2478 vec_len (cf->input_vector) - i);
2481 /* There was a partial match which means we need more bytes
2482 * than the input buffer currently has.
2487 * Since the bytes before here have been processed, shift
2488 * the remaining contents to the start of the input buffer.
2490 vec_delete (cf->input_vector, i, 0);
2492 return 1; /* wait for more */
2497 /* If telnet option processing switched us to line mode, get us
2502 vec_delete (cf->input_vector, i, 0);
2503 cf->current_command = cf->input_vector;
2507 /* process the action */
2508 if (!unix_cli_line_process_one (cm, um, cf, uf,
2509 cf->input_vector[i], action))
2511 /* CRLF found. Consume the bytes from the input_vector */
2512 vec_delete (cf->input_vector, i + matched, 0);
2513 /* And tell our caller to execute cf->input_command */
2521 vec_reset_length (cf->input_vector);
2525 /** @brief Process input to a CLI session. */
2527 unix_cli_process_input (unix_cli_main_t * cm, uword cli_file_index)
2529 unix_main_t *um = &unix_main;
2530 clib_file_main_t *fm = &file_main;
2532 unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2533 unformat_input_t input;
2534 int vlib_parse_eval (u8 *);
2536 cm->current_input_file_index = cli_file_index;
2539 /* Try vlibplex first. Someday... */
2540 if (0 && vlib_parse_eval (cf->input_vector) == 0)
2546 /* just treat whatever we got as a complete line of input */
2547 cf->current_command = cf->input_vector;
2551 /* Line edit, echo, etc. */
2552 if (unix_cli_line_edit (cm, um, fm, cf))
2553 /* want more input */
2560 vec_reset_length (lv);
2561 lv = format (lv, "%U[%d]: %v",
2562 format_timeval, 0 /* current bat-time */ ,
2563 0 /* current bat-format */ ,
2564 cli_file_index, cf->current_command);
2565 if ((vec_len (cf->current_command) > 0) &&
2566 (cf->current_command[vec_len (cf->current_command) - 1] != '\n'))
2567 lv = format (lv, "\n");
2568 int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv));
2571 /* Run the command through the macro processor */
2572 if (vec_len (cf->current_command))
2575 vec_validate (cf->current_command, vec_len (cf->current_command));
2576 cf->current_command[vec_len (cf->current_command) - 1] = 0;
2577 /* The macro expander expects proper C-strings, not vectors */
2578 expanded = (u8 *) clib_macro_eval (&cm->macro_main,
2579 (i8 *) cf->current_command,
2581 /* Macro processor NULL terminates the return */
2582 _vec_len (expanded) -= 1;
2583 vec_reset_length (cf->current_command);
2584 vec_append (cf->current_command, expanded);
2585 vec_free (expanded);
2588 /* Build an unformat structure around our command */
2589 unformat_init_vector (&input, cf->current_command);
2591 /* Remove leading white space from input. */
2592 (void) unformat (&input, "");
2594 cf->pager_start = 0; /* start a new pager session */
2596 if (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
2597 vlib_cli_input (um->vlib_main, &input, unix_vlib_cli_output,
2600 /* Zero buffer since otherwise unformat_free will call vec_free on it. */
2603 unformat_free (&input);
2605 /* Re-fetch pointer since pool may have moved. */
2606 cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2607 uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
2610 /* reset vector; we'll re-use it later */
2613 vec_reset_length (cf->input_vector);
2614 cf->current_command = 0;
2618 vec_reset_length (cf->current_command);
2621 if (cf->no_pager == 2)
2623 /* Pager was programmatically disabled */
2624 unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
2625 cf->no_pager = um->cli_no_pager;
2628 if (vec_len (cf->pager_index) == 0
2629 || vec_len (cf->pager_index) < cf->height)
2631 /* There was no need for the pager */
2632 unix_cli_pager_reset (cf);
2635 unix_cli_cli_prompt (cf, uf);
2639 /* Display the pager prompt */
2640 unix_cli_pager_prompt (cf, uf);
2643 /* Any residual data in the input vector? */
2644 if (vec_len (cf->input_vector))
2647 /* For non-interactive sessions send a NUL byte.
2648 * Specifically this is because vppctl needs to see some traffic in
2649 * order to move on to closing the session. Commands with no output
2650 * would thus cause vppctl to hang indefinitely in non-interactive mode
2651 * since there is also no prompt sent after the command completes.
2653 if (!cf->is_interactive)
2654 unix_vlib_cli_output_raw (cf, uf, (u8 *) "\0", 1);
2657 /** Destroy a CLI session.
2658 * @note If we destroy the @c stdin session this additionally signals
2659 * the shutdown of VPP.
2662 unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
2664 unix_main_t *um = &unix_main;
2665 clib_file_main_t *fm = &file_main;
2666 unix_cli_file_t *cf;
2670 /* Validate cli_file_index */
2671 if (pool_is_free_index (cm->cli_file_pool, cli_file_index))
2674 cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2675 uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
2677 /* Quit/EOF on stdin means quit program. */
2678 if (uf->file_descriptor == STDIN_FILENO)
2679 clib_longjmp (&um->vlib_main->main_loop_exit, VLIB_MAIN_LOOP_EXIT_CLI);
2681 vec_free (cf->current_command);
2682 vec_free (cf->search_key);
2684 for (i = 0; i < vec_len (cf->command_history); i++)
2685 vec_free (cf->command_history[i]);
2687 vec_free (cf->command_history);
2689 clib_file_del (fm, uf);
2691 unix_cli_file_free (cf);
2692 pool_put (cm->cli_file_pool, cf);
2695 /** Handle system events. */
2697 unix_cli_process (vlib_main_t * vm,
2698 vlib_node_runtime_t * rt, vlib_frame_t * f)
2700 unix_cli_main_t *cm = &unix_cli_main;
2705 unix_cli_process_event_type_t event_type;
2706 vlib_process_wait_for_event (vm);
2707 event_type = vlib_process_get_events (vm, &data);
2711 case UNIX_CLI_PROCESS_EVENT_READ_READY:
2712 for (i = 0; i < vec_len (data); i++)
2713 unix_cli_process_input (cm, data[i]);
2716 case UNIX_CLI_PROCESS_EVENT_QUIT:
2717 /* Kill this process. */
2718 for (i = 0; i < vec_len (data); i++)
2719 unix_cli_kill (cm, data[i]);
2724 _vec_len (data) = 0;
2730 vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
2732 /* Add node index so we can re-use this process later. */
2733 vec_add1 (cm->unused_cli_process_node_indices, rt->node_index);
2738 /** Called when a CLI session file descriptor can be written to without
2740 static clib_error_t *
2741 unix_cli_write_ready (clib_file_t * uf)
2743 unix_cli_main_t *cm = &unix_cli_main;
2744 unix_cli_file_t *cf;
2747 cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
2749 /* Flush output vector. */
2751 /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
2752 n = send (uf->file_descriptor,
2753 cf->output_vector, vec_len (cf->output_vector), MSG_NOSIGNAL);
2755 n = write (uf->file_descriptor,
2756 cf->output_vector, vec_len (cf->output_vector));
2758 if (n < 0 && errno != EAGAIN)
2762 /* connection closed on us */
2763 unix_main_t *um = &unix_main;
2765 vlib_process_signal_event (um->vlib_main, cf->process_node_index,
2766 UNIX_CLI_PROCESS_EVENT_QUIT,
2771 return clib_error_return_unix (0, "write");
2776 unix_cli_del_pending_output (uf, cf, n);
2778 return /* no error */ 0;
2781 /** Called when a CLI session file descriptor has data to be read. */
2782 static clib_error_t *
2783 unix_cli_read_ready (clib_file_t * uf)
2785 unix_main_t *um = &unix_main;
2786 unix_cli_main_t *cm = &unix_cli_main;
2787 unix_cli_file_t *cf;
2789 int n, n_read, n_try;
2791 cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
2796 l = vec_len (cf->input_vector);
2797 vec_resize (cf->input_vector, l + n_try);
2799 n = read (uf->file_descriptor, cf->input_vector + l, n_try);
2802 if (n < 0 && errno != EAGAIN)
2803 return clib_error_return_unix (0, "read");
2805 n_read = n < 0 ? 0 : n;
2806 _vec_len (cf->input_vector) = l + n_read;
2810 vlib_process_signal_event (um->vlib_main,
2811 cf->process_node_index,
2813 ? UNIX_CLI_PROCESS_EVENT_QUIT
2814 : UNIX_CLI_PROCESS_EVENT_READ_READY),
2815 /* event data */ uf->private_data);
2817 return /* no error */ 0;
2820 /** Called when a CLI session file descriptor has an error condition. */
2821 static clib_error_t *
2822 unix_cli_error_detected (clib_file_t * uf)
2824 unix_main_t *um = &unix_main;
2825 unix_cli_main_t *cm = &unix_cli_main;
2826 unix_cli_file_t *cf;
2828 cf = pool_elt_at_index (cm->cli_file_pool, uf->private_data);
2829 cf->has_epipe = 1; /* prevent writes while the close is pending */
2830 vlib_process_signal_event (um->vlib_main,
2831 cf->process_node_index,
2832 UNIX_CLI_PROCESS_EVENT_QUIT,
2833 /* event data */ uf->private_data);
2835 return /* no error */ 0;
2838 /** Store a new CLI session.
2839 * @param name The name of the session.
2840 * @param fd The file descriptor for the session I/O.
2841 * @return The session ID.
2844 unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
2846 unix_main_t *um = &unix_main;
2847 clib_file_main_t *fm = &file_main;
2848 unix_cli_file_t *cf;
2849 clib_file_t template = { 0 };
2850 vlib_main_t *vm = um->vlib_main;
2854 file_desc = format (0, "%s", name);
2856 name = (char *) format (0, "unix-cli-%s", name);
2858 if (vec_len (cm->unused_cli_process_node_indices) > 0)
2860 uword l = vec_len (cm->unused_cli_process_node_indices);
2862 vlib_main_t *this_vlib_main;
2866 * Nodes are bulk-copied, so node name pointers are shared.
2867 * Find the cli node in all graph replicas, and give all of them
2868 * the same new name.
2869 * Then, throw away the old shared name-vector.
2871 for (i = 0; i < vec_len (vlib_mains); i++)
2873 this_vlib_main = vlib_mains[i];
2874 if (this_vlib_main == 0)
2876 n = vlib_get_node (this_vlib_main,
2877 cm->unused_cli_process_node_indices[l - 1]);
2879 n->name = (u8 *) name;
2881 vec_free (old_name);
2883 vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
2885 _vec_len (cm->unused_cli_process_node_indices) = l - 1;
2889 static vlib_node_registration_t r = {
2890 .function = unix_cli_process,
2891 .type = VLIB_NODE_TYPE_PROCESS,
2892 .process_log2_n_stack_bytes = 18,
2897 vlib_worker_thread_barrier_sync (vm);
2899 vlib_register_node (vm, &r);
2902 n = vlib_get_node (vm, r.index);
2903 vlib_worker_thread_node_runtime_update ();
2904 vlib_worker_thread_barrier_release (vm);
2907 pool_get (cm->cli_file_pool, cf);
2908 clib_memset (cf, 0, sizeof (*cf));
2910 template.read_function = unix_cli_read_ready;
2911 template.write_function = unix_cli_write_ready;
2912 template.error_function = unix_cli_error_detected;
2913 template.file_descriptor = fd;
2914 template.private_data = cf - cm->cli_file_pool;
2915 template.description = file_desc;
2917 cf->process_node_index = n->index;
2918 cf->clib_file_index = clib_file_add (fm, &template);
2919 cf->output_vector = 0;
2920 cf->input_vector = 0;
2921 vec_validate (cf->current_command, 0);
2922 _vec_len (cf->current_command) = 0;
2924 vlib_start_process (vm, n->runtime_index);
2926 vlib_process_t *p = vlib_get_process_from_node (vm, n);
2927 p->output_function = unix_vlib_cli_output;
2928 p->output_function_arg = cf - cm->cli_file_pool;
2930 return cf - cm->cli_file_pool;
2933 /** Telnet listening socket has a new connection. */
2934 static clib_error_t *
2935 unix_cli_listen_read_ready (clib_file_t * uf)
2937 unix_main_t *um = &unix_main;
2938 clib_file_main_t *fm = &file_main;
2939 unix_cli_main_t *cm = &unix_cli_main;
2940 clib_socket_t *s = &um->cli_listen_socket;
2941 clib_socket_t client;
2943 clib_error_t *error;
2944 unix_cli_file_t *cf;
2948 error = clib_socket_accept (s, &client);
2952 /* Disable Nagle, ignore any errors doing so eg on PF_LOCAL socket */
2954 (void) setsockopt (client.fd, IPPROTO_TCP, TCP_NODELAY,
2955 (void *) &one, sizeof (one));
2957 client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
2959 cf_index = unix_cli_file_add (cm, client_name, client.fd);
2960 cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2963 /* No longer need CLIB version of socket. */
2964 clib_socket_free (&client);
2965 vec_free (client_name);
2967 /* if we're supposed to run telnet session in character mode (default) */
2968 if (um->cli_line_mode == 0)
2971 * Set telnet client character mode, echo on, suppress "go-ahead".
2972 * Technically these should be negotiated, but this works.
2974 u8 charmode_option[] = {
2975 IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */
2976 IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */
2977 IAC, WILL, TELOPT_SGA, /* server willl supress GA */
2978 IAC, DO, TELOPT_SGA, /* client should supress Go Ahead */
2979 IAC, WILL, TELOPT_ECHO, /* server will do echo */
2980 IAC, DONT, TELOPT_ECHO, /* client should not echo */
2981 IAC, DO, TELOPT_TTYPE, /* client should tell us its term type */
2982 IAC, SB, TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */
2983 IAC, DO, TELOPT_NAWS, /* client should tell us its window sz */
2984 IAC, SB, TELOPT_NAWS, 1, IAC, SE, /* now tell me window size */
2987 /* Enable history on this CLI */
2988 cf->history_limit = um->cli_history_limit;
2989 cf->has_history = cf->history_limit != 0;
2991 /* This is an interactive session until we decide otherwise */
2992 cf->is_interactive = 1;
2994 /* Make sure this session is in line mode */
3000 /* Setup the pager */
3001 cf->no_pager = um->cli_no_pager;
3003 /* Default terminal dimensions, should the terminal
3004 * fail to provide any.
3006 cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
3007 cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
3009 /* Send the telnet options */
3010 uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3011 unix_vlib_cli_output_raw (cf, uf, charmode_option,
3012 ARRAY_LEN (charmode_option));
3014 if (cm->new_session_process_node_index == ~0)
3016 /* Create thw new session deadline process */
3017 cm->new_session_process_node_index =
3018 vlib_process_create (um->vlib_main, "unix-cli-new-session",
3019 unix_cli_new_session_process,
3020 16 /* log2_n_stack_bytes */ );
3023 /* In case the client doesn't negotiate terminal type, register
3024 * our session with a process that will emit the prompt if
3025 * a deadline passes */
3026 vlib_process_signal_event (um->vlib_main,
3027 cm->new_session_process_node_index,
3028 UNIX_CLI_NEW_SESSION_EVENT_ADD, cf_index);
3035 /** The system terminal has informed us that the window size
3039 unix_cli_resize_interrupt (int signum)
3041 clib_file_main_t *fm = &file_main;
3042 unix_cli_main_t *cm = &unix_cli_main;
3043 unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool,
3044 cm->stdin_cli_file_index);
3045 clib_file_t *uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3049 /* Terminal resized, fetch the new size */
3050 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
3052 /* "Should never happen..." */
3053 clib_unix_warning ("TIOCGWINSZ");
3054 /* We can't trust ws.XXX... */
3058 cf->width = ws.ws_col;
3059 if (cf->width > UNIX_CLI_MAX_TERMINAL_WIDTH)
3060 cf->width = UNIX_CLI_MAX_TERMINAL_WIDTH;
3062 cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
3064 cf->height = ws.ws_row;
3065 if (cf->height > UNIX_CLI_MAX_TERMINAL_HEIGHT)
3066 cf->height = UNIX_CLI_MAX_TERMINAL_HEIGHT;
3067 if (cf->height == 0)
3068 cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
3070 /* Reindex the pager buffer */
3071 unix_cli_pager_reindex (cf);
3073 /* Redraw the page */
3074 unix_cli_pager_redraw (cf, uf);
3077 /** Handle configuration directives in the @em unix section. */
3078 static clib_error_t *
3079 unix_cli_config (vlib_main_t * vm, unformat_input_t * input)
3081 unix_main_t *um = &unix_main;
3082 clib_file_main_t *fm = &file_main;
3083 unix_cli_main_t *cm = &unix_cli_main;
3085 clib_error_t *error = 0;
3086 unix_cli_file_t *cf;
3089 struct sigaction sa;
3093 /* We depend on unix flags being set. */
3094 if ((error = vlib_call_config_function (vm, unix_config)))
3097 if (um->flags & UNIX_FLAG_INTERACTIVE)
3099 /* Set stdin to be non-blocking. */
3100 if ((flags = fcntl (STDIN_FILENO, F_GETFL, 0)) < 0)
3102 (void) fcntl (STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
3104 cf_index = unix_cli_file_add (cm, "stdin", STDIN_FILENO);
3105 cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
3106 cm->stdin_cli_file_index = cf_index;
3108 /* If stdin is a tty and we are using chacracter mode, enable
3109 * history on the CLI and set the tty line discipline accordingly. */
3110 if (isatty (STDIN_FILENO) && um->cli_line_mode == 0)
3112 /* Capture terminal resize events */
3113 clib_memset (&sa, 0, sizeof (sa));
3114 sa.sa_handler = unix_cli_resize_interrupt;
3115 if (sigaction (SIGWINCH, &sa, 0) < 0)
3116 clib_panic ("sigaction");
3118 /* Retrieve the current terminal size */
3119 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) == 0)
3121 cf->width = ws.ws_col;
3122 cf->height = ws.ws_row;
3125 if (cf->width == 0 || cf->height == 0)
3128 * We have a tty, but no size. Use defaults.
3129 * vpp "unix interactive" inside emacs + gdb ends up here.
3131 cf->width = UNIX_CLI_DEFAULT_TERMINAL_WIDTH;
3132 cf->height = UNIX_CLI_DEFAULT_TERMINAL_HEIGHT;
3135 /* Setup the history */
3136 cf->history_limit = um->cli_history_limit;
3137 cf->has_history = cf->history_limit != 0;
3139 /* Setup the pager */
3140 cf->no_pager = um->cli_no_pager;
3142 /* This is an interactive session until we decide otherwise */
3143 cf->is_interactive = 1;
3145 /* We're going to be in char by char mode */
3148 /* Save the original tty state so we can restore it later */
3149 tcgetattr (STDIN_FILENO, &um->tio_stdin);
3152 /* Tweak the tty settings */
3153 tio = um->tio_stdin;
3154 /* echo off, canonical mode off, ext'd input processing off */
3155 tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
3156 /* disable XON/XOFF, so ^S invokes the history search */
3157 tio.c_iflag &= ~(IXON | IXOFF);
3158 tio.c_cc[VMIN] = 1; /* 1 byte at a time */
3159 tio.c_cc[VTIME] = 0; /* no timer */
3160 tio.c_cc[VSTOP] = _POSIX_VDISABLE; /* not ^S */
3161 tio.c_cc[VSTART] = _POSIX_VDISABLE; /* not ^Q */
3162 tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio);
3164 /* See if we can do ANSI/VT100 output */
3165 term = (u8 *) getenv ("TERM");
3168 int len = strlen ((char *) term);
3169 cf->ansi_capable = unix_cli_terminal_type_ansi (term, len);
3170 if (unix_cli_terminal_type_noninteractive (term, len))
3171 unix_cli_set_session_noninteractive (cf);
3176 /* No tty, so make sure the session doesn't have tty-like features */
3177 unix_cli_set_session_noninteractive (cf);
3180 /* Send banner and initial prompt */
3181 unix_cli_file_welcome (cm, cf);
3184 /* If we have socket config, LISTEN, otherwise, don't */
3185 clib_socket_t *s = &um->cli_listen_socket;
3186 if (s->config && s->config[0] != 0)
3189 clib_file_t template = { 0 };
3191 /* mkdir of file socketu, only under /run */
3192 if (strncmp (s->config, "/run", 4) == 0)
3194 u8 *tmp = format (0, "%s", s->config);
3195 int i = vec_len (tmp);
3196 while (i && tmp[--i] != '/')
3202 vlib_unix_recursive_mkdir ((char *) tmp);
3206 s->flags = CLIB_SOCKET_F_IS_SERVER | /* listen, don't connect */
3207 CLIB_SOCKET_F_ALLOW_GROUP_WRITE; /* PF_LOCAL socket only */
3208 error = clib_socket_init (s);
3213 template.read_function = unix_cli_listen_read_ready;
3214 template.file_descriptor = s->fd;
3215 template.description = format (0, "cli listener %s", s->config);
3217 clib_file_add (fm, &template);
3220 /* Set CLI prompt. */
3221 if (!cm->cli_prompt)
3222 cm->cli_prompt = format (0, "VLIB: ");
3228 * This module has no configurable parameters.
3230 VLIB_CONFIG_FUNCTION (unix_cli_config, "unix-cli");
3232 /** Called when VPP is shutting down, this restores the system
3233 * terminal state if previously saved.
3235 static clib_error_t *
3236 unix_cli_exit (vlib_main_t * vm)
3238 unix_main_t *um = &unix_main;
3240 /* If stdin is a tty and we saved the tty state, reset the tty state */
3241 if (isatty (STDIN_FILENO) && um->tio_isset)
3242 tcsetattr (STDIN_FILENO, TCSAFLUSH, &um->tio_stdin);
3247 VLIB_MAIN_LOOP_EXIT_FUNCTION (unix_cli_exit);
3249 /** Set the CLI prompt.
3250 * @param prompt The C string to set the prompt to.
3251 * @note This setting is global; it impacts all current
3252 * and future CLI sessions.
3255 vlib_unix_cli_set_prompt (char *prompt)
3257 char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s ";
3258 unix_cli_main_t *cm = &unix_cli_main;
3260 vec_free (cm->cli_prompt);
3261 cm->cli_prompt = format (0, fmt, prompt);
3264 static unix_cli_file_t *
3265 unix_cli_file_if_exists (unix_cli_main_t * cm)
3267 if (!cm->cli_file_pool)
3269 return pool_elt_at_index (cm->cli_file_pool, cm->current_input_file_index);
3272 static unix_cli_file_t *
3273 unix_cli_file_if_interactive (unix_cli_main_t * cm)
3275 unix_cli_file_t *cf;
3276 if ((cf = unix_cli_file_if_exists (cm)) && !cf->is_interactive)
3281 /** CLI command to quit the terminal session.
3282 * @note If this is a stdin session then this will
3283 * shutdown VPP also.
3285 static clib_error_t *
3286 unix_cli_quit (vlib_main_t * vm,
3287 unformat_input_t * input, vlib_cli_command_t * cmd)
3289 unix_cli_main_t *cm = &unix_cli_main;
3290 unix_cli_file_t *cf;
3292 if (!(cf = unix_cli_file_if_exists (cm)))
3293 return clib_error_return (0, "invalid session");
3295 /* Cosmetic: suppress the final prompt from appearing before we die */
3296 cf->is_interactive = 0;
3299 vlib_process_signal_event (vm,
3300 vlib_current_process (vm),
3301 UNIX_CLI_PROCESS_EVENT_QUIT,
3302 cm->current_input_file_index);
3307 * Terminates the current CLI session.
3309 * If VPP is running in @em interactive mode and this is the console session
3310 * (that is, the session on @c stdin) then this will also terminate VPP.
3313 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
3315 .short_help = "Exit CLI",
3316 .function = unix_cli_quit,
3321 VLIB_CLI_COMMAND (unix_cli_q_command, static) = {
3323 .short_help = "Exit CLI",
3324 .function = unix_cli_quit,
3328 /** CLI command to execute a VPP command script. */
3329 static clib_error_t *
3330 unix_cli_exec (vlib_main_t * vm,
3331 unformat_input_t * input, vlib_cli_command_t * cmd)
3335 unformat_input_t sub_input;
3336 clib_error_t *error;
3342 if (!unformat (input, "%s", &file_name))
3344 error = clib_error_return (0, "expecting file name, got `%U'",
3345 format_unformat_error, input);
3349 fd = open (file_name, O_RDONLY);
3352 error = clib_error_return_unix (0, "failed to open `%s'", file_name);
3356 /* Make sure its a regular file. */
3360 if (fstat (fd, &s) < 0)
3362 error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
3366 if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
3368 error = clib_error_return (0, "not a regular file `%s'", file_name);
3373 unformat_init_clib_file (&sub_input, fd);
3375 vlib_cli_input (vm, &sub_input, 0, 0);
3376 unformat_free (&sub_input);
3381 vec_free (file_name);
3387 * Executes a sequence of CLI commands which are read from a file. If
3388 * a command is unrecognised or otherwise invalid then the usual CLI
3389 * feedback will be generated, however execution of subsequent commands
3390 * from the file will continue.
3392 * The VPP code is indifferent to the file location. However, if SELinux
3393 * is enabled, then the file needs to have an SELinux label the VPP
3394 * process is allowed to access. For example, if a file is created in
3395 * '<em>/usr/share/vpp/</em>', it will be allowed. However, files manually
3396 * created in '/tmp/' or '/home/<user>/' will not be accessible by the VPP
3397 * process when SELinux is enabled.
3402 * <b><em>$ cat /usr/share/vpp/scripts/gigup.txt</em></b>
3403 * set interface state GigabitEthernet0/8/0 up
3404 * set interface state GigabitEthernet0/9/0 up
3406 * Example of how to execute a set of CLI commands from a file:
3407 * @cliexcmd{exec /usr/share/vpp/scripts/gigup.txt}
3410 VLIB_CLI_COMMAND (cli_exec, static) = {
3412 .short_help = "exec <filename>",
3413 .function = unix_cli_exec,
3418 /** CLI command to show various unix error statistics. */
3419 static clib_error_t *
3420 unix_show_errors (vlib_main_t * vm,
3421 unformat_input_t * input, vlib_cli_command_t * cmd)
3423 unix_main_t *um = &unix_main;
3424 clib_error_t *error = 0;
3425 int i, n_errors_to_show;
3426 unix_error_history_t *unix_errors = 0;
3428 n_errors_to_show = 1 << 30;
3430 if (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
3432 if (!unformat (input, "%d", &n_errors_to_show))
3435 clib_error_return (0,
3436 "expecting integer number of errors to show, got `%U'",
3437 format_unformat_error, input);
3443 clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
3446 um->error_history_index >
3447 0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
3449 while (n_errors_to_show > 0)
3451 unix_error_history_t *eh = um->error_history + i;
3456 vec_add1 (unix_errors, eh[0]);
3457 n_errors_to_show -= 1;
3459 i = ARRAY_LEN (um->error_history) - 1;
3464 if (vec_len (unix_errors) == 0)
3465 vlib_cli_output (vm, "no Unix errors so far");
3468 vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
3469 for (i = vec_len (unix_errors) - 1; i >= 0; i--)
3471 unix_error_history_t *eh = vec_elt_at_index (unix_errors, i);
3472 vlib_cli_output (vm, "%U: %U",
3473 format_time_interval, "h:m:s:u", eh->time,
3474 format_clib_error, eh->error);
3476 vlib_cli_output (vm, "%U: time now",
3477 format_time_interval, "h:m:s:u", vlib_time_now (vm));
3481 vec_free (unix_errors);
3486 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
3487 .path = "show unix errors",
3488 .short_help = "Show Unix system call error history",
3489 .function = unix_show_errors,
3493 /** CLI command to show various unix error statistics. */
3494 static clib_error_t *
3495 unix_show_files (vlib_main_t * vm,
3496 unformat_input_t * input, vlib_cli_command_t * cmd)
3498 clib_error_t *error = 0;
3499 clib_file_main_t *fm = &file_main;
3501 char path[PATH_MAX];
3504 vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread",
3505 "Read", "Write", "Error", "File Name", "Description");
3508 pool_foreach (f, fm->file_pool,(
3511 s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0);
3512 rv = readlink((char *) s, path, PATH_MAX - 1);
3514 path[rv < 0 ? 0 : rv] = 0;
3516 vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v",
3517 f->file_descriptor, f->polling_thread_index,
3518 f->read_events, f->write_events, f->error_events,
3519 path, f->description);
3520 vec_reset_length (s);
3529 VLIB_CLI_COMMAND (cli_unix_show_files, static) = {
3530 .path = "show unix files",
3531 .short_help = "Show Unix files in use",
3532 .function = unix_show_files,
3536 /** CLI command to show session command history. */
3537 static clib_error_t *
3538 unix_cli_show_history (vlib_main_t * vm,
3539 unformat_input_t * input, vlib_cli_command_t * cmd)
3541 unix_cli_main_t *cm = &unix_cli_main;
3542 unix_cli_file_t *cf;
3545 if (!(cf = unix_cli_file_if_interactive (cm)))
3546 return clib_error_return (0, "invalid for non-interactive sessions");
3548 if (cf->has_history && cf->history_limit)
3550 i = 1 + cf->command_number - vec_len (cf->command_history);
3551 for (j = 0; j < vec_len (cf->command_history); j++)
3552 vlib_cli_output (vm, "%d %v\n", i + j, cf->command_history[j]);
3556 vlib_cli_output (vm, "History not enabled.\n");
3563 * Displays the command history for the current session, if any.
3566 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
3568 .short_help = "Show current session command history",
3569 .function = unix_cli_show_history,
3573 /** CLI command to show terminal status. */
3574 static clib_error_t *
3575 unix_cli_show_terminal (vlib_main_t * vm,
3576 unformat_input_t * input, vlib_cli_command_t * cmd)
3578 unix_main_t *um = &unix_main;
3579 unix_cli_main_t *cm = &unix_cli_main;
3580 unix_cli_file_t *cf;
3583 if (!(cf = unix_cli_file_if_exists (cm)))
3584 return clib_error_return (0, "invalid session");
3586 n = vlib_get_node (vm, cf->process_node_index);
3588 vlib_cli_output (vm, "Terminal name: %v\n", n->name);
3589 vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ?
3590 "line-by-line" : "char-by-char");
3591 vlib_cli_output (vm, "Terminal width: %d\n", cf->width);
3592 vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
3593 vlib_cli_output (vm, "ANSI capable: %s\n",
3594 cf->ansi_capable ? "yes" : "no");
3595 vlib_cli_output (vm, "Interactive: %s\n",
3596 cf->is_interactive ? "yes" : "no");
3597 vlib_cli_output (vm, "History enabled: %s%s\n",
3598 cf->has_history ? "yes" : "no", !cf->has_history
3599 || cf->history_limit ? "" :
3600 " (disabled by history limit)");
3601 if (cf->has_history)
3602 vlib_cli_output (vm, "History limit: %d\n", cf->history_limit);
3603 vlib_cli_output (vm, "Pager enabled: %s%s%s\n",
3604 cf->no_pager ? "no" : "yes",
3606 || cf->height ? "" : " (disabled by terminal height)",
3608 || um->cli_pager_buffer_limit ? "" :
3609 " (disabled by buffer limit)");
3611 vlib_cli_output (vm, "Pager limit: %d\n", um->cli_pager_buffer_limit);
3612 vlib_cli_output (vm, "CRLF mode: %s\n",
3613 cf->crlf_mode ? "CR+LF" : "LF");
3619 * Displays various information about the state of the current terminal
3623 * @cliexstart{show terminal}
3624 * Terminal name: unix-cli-stdin
3625 * Terminal mode: char-by-char
3626 * Terminal width: 123
3627 * Terminal height: 48
3630 * History enabled: yes
3632 * Pager enabled: yes
3633 * Pager limit: 100000
3638 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
3639 .path = "show terminal",
3640 .short_help = "Show current session terminal settings",
3641 .function = unix_cli_show_terminal,
3645 /** CLI command to display a list of CLI sessions. */
3646 static clib_error_t *
3647 unix_cli_show_cli_sessions (vlib_main_t * vm,
3648 unformat_input_t * input,
3649 vlib_cli_command_t * cmd)
3651 unix_cli_main_t *cm = &unix_cli_main;
3652 clib_file_main_t *fm = &file_main;
3653 unix_cli_file_t *cf;
3657 vlib_cli_output (vm, "%-5s %-5s %-20s %s", "PNI", "FD", "Name", "Flags");
3659 #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) )
3661 pool_foreach (cf, cm->cli_file_pool, ({
3662 uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3663 n = vlib_get_node (vm, cf->process_node_index);
3664 vlib_cli_output (vm,
3665 "%-5d %-5d %-20v %c%c%c%c%c\n",
3666 cf->process_node_index,
3667 uf->file_descriptor,
3669 fl (cf->is_interactive, 'i'),
3670 fl (cf->is_socket, 's'),
3671 fl (cf->line_mode, 'l'),
3672 fl (cf->has_epipe, 'p'),
3673 fl (cf->ansi_capable, 'a'));
3682 * Displays a summary of all the current CLI sessions.
3684 * Typically used to diagnose connection issues with the CLI
3688 * @cliexstart{show cli-sessions}
3690 * 343 0 unix-cli-stdin IslpA
3691 * 344 7 unix-cli-local:20 ISlpA
3692 * 346 8 unix-cli-local:21 iSLpa
3695 * In this example we have the debug console of the running process
3696 * on stdin/out, we have an interactive socket session and we also
3697 * have a non-interactive socket session.
3701 * - @em PNI: Process node index.
3702 * - @em FD: Unix file descriptor.
3703 * - @em Name: Name of the session.
3704 * - @em Flags: Various flags that describe the state of the session.
3706 * @em Flags have the following meanings; lower-case typically negates
3709 * - @em I Interactive session.
3710 * - @em S Connected by socket.
3711 * - @em s Not a socket, likely stdin.
3712 * - @em L Line-by-line mode.
3713 * - @em l Char-by-char mode.
3714 * - @em P EPIPE detected on connection; it will close soon.
3715 * - @em A ANSI-capable terminal.
3718 VLIB_CLI_COMMAND (cli_unix_cli_show_cli_sessions, static) = {
3719 .path = "show cli-sessions",
3720 .short_help = "Show current CLI sessions",
3721 .function = unix_cli_show_cli_sessions,
3725 /** CLI command to set terminal pager settings. */
3726 static clib_error_t *
3727 unix_cli_set_terminal_pager (vlib_main_t * vm,
3728 unformat_input_t * input,
3729 vlib_cli_command_t * cmd)
3731 unix_main_t *um = &unix_main;
3732 unix_cli_main_t *cm = &unix_cli_main;
3733 unix_cli_file_t *cf;
3734 unformat_input_t _line_input, *line_input = &_line_input;
3735 clib_error_t *error = 0;
3737 if (!(cf = unix_cli_file_if_interactive (cm)))
3738 return clib_error_return (0, "invalid for non-interactive sessions");
3740 if (!unformat_user (input, unformat_line_input, line_input))
3743 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3745 if (unformat (line_input, "on"))
3747 else if (unformat (line_input, "off"))
3749 else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
3750 vlib_cli_output (vm,
3751 "Pager limit set to %u lines; note, this is global.\n",
3752 um->cli_pager_buffer_limit);
3755 error = clib_error_return (0, "unknown parameter: `%U`",
3756 format_unformat_error, line_input);
3762 unformat_free (line_input);
3768 * Enables or disables the terminal pager for this session. Generally
3769 * this defaults to enabled.
3771 * Additionally allows the pager buffer size to be set; though note that
3772 * this value is set globally and not per session.
3775 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
3776 .path = "set terminal pager",
3777 .short_help = "set terminal pager [on|off] [limit <lines>]",
3778 .function = unix_cli_set_terminal_pager,
3782 /** CLI command to set terminal history settings. */
3783 static clib_error_t *
3784 unix_cli_set_terminal_history (vlib_main_t * vm,
3785 unformat_input_t * input,
3786 vlib_cli_command_t * cmd)
3788 unix_cli_main_t *cm = &unix_cli_main;
3789 unix_cli_file_t *cf;
3790 unformat_input_t _line_input, *line_input = &_line_input;
3792 clib_error_t *error = 0;
3794 if (!(cf = unix_cli_file_if_interactive (cm)))
3795 return clib_error_return (0, "invalid for non-interactive sessions");
3797 if (!unformat_user (input, unformat_line_input, line_input))
3800 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3802 if (unformat (line_input, "on"))
3803 cf->has_history = 1;
3804 else if (unformat (line_input, "off"))
3805 cf->has_history = 0;
3806 else if (unformat (line_input, "limit %u", &cf->history_limit))
3810 error = clib_error_return (0, "unknown parameter: `%U`",
3811 format_unformat_error, line_input);
3817 /* If we reduced history size, or turned it off, purge the history */
3818 limit = cf->has_history ? cf->history_limit : 0;
3819 if (limit < vec_len (cf->command_history))
3823 /* How many items to remove from the start of history */
3824 limit = vec_len (cf->command_history) - limit;
3826 for (i = 0; i < limit; i++)
3827 vec_free (cf->command_history[i]);
3829 vec_delete (cf->command_history, limit, 0);
3833 unformat_free (line_input);
3839 * Enables or disables the command history function of the current
3840 * terminal. Generally this defaults to enabled.
3842 * This command also allows the maximum size of the history buffer for
3843 * this session to be altered.
3846 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
3847 .path = "set terminal history",
3848 .short_help = "set terminal history [on|off] [limit <lines>]",
3849 .function = unix_cli_set_terminal_history,
3853 /** CLI command to set terminal ANSI settings. */
3854 static clib_error_t *
3855 unix_cli_set_terminal_ansi (vlib_main_t * vm,
3856 unformat_input_t * input,
3857 vlib_cli_command_t * cmd)
3859 unix_cli_main_t *cm = &unix_cli_main;
3860 unix_cli_file_t *cf;
3862 if (!(cf = unix_cli_file_if_interactive (cm)))
3863 return clib_error_return (0, "invalid for non-interactive sessions");
3865 if (unformat (input, "on"))
3866 cf->ansi_capable = 1;
3867 else if (unformat (input, "off"))
3868 cf->ansi_capable = 0;
3870 return clib_error_return (0, "unknown parameter: `%U`",
3871 format_unformat_error, input);
3877 * Enables or disables the use of ANSI control sequences by this terminal.
3878 * The default will vary based on terminal detection at the start of the
3881 * ANSI control sequences are used in a small number of places to provide,
3882 * for example, color text output and to control the cursor in the pager.
3885 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
3886 .path = "set terminal ansi",
3887 .short_help = "set terminal ansi [on|off]",
3888 .function = unix_cli_set_terminal_ansi,
3893 #define MAX_CLI_WAIT 86400
3894 /** CLI command to wait <sec> seconds. Useful for exec script. */
3895 static clib_error_t *
3896 unix_wait_cmd (vlib_main_t * vm,
3897 unformat_input_t * input, vlib_cli_command_t * cmd)
3899 unformat_input_t _line_input, *line_input = &_line_input;
3902 if (!unformat_user (input, unformat_line_input, line_input))
3905 while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3907 if (unformat (line_input, "%f", &sec))
3910 return clib_error_return (0, "unknown parameter: `%U`",
3911 format_unformat_error, input);
3914 if (sec <= 0 || sec > MAX_CLI_WAIT || floor (sec * 1000) / 1000 != sec)
3915 return clib_error_return (0,
3916 "<sec> must be a positive value and less than 86400 (one day) with no more than msec precision.");
3918 vlib_process_wait_for_event_or_clock (vm, sec);
3919 vlib_cli_output (vm, "waited %.3f sec.", sec);
3921 unformat_free (line_input);
3925 VLIB_CLI_COMMAND (cli_unix_wait_cmd, static) = {
3927 .short_help = "wait <sec>",
3928 .function = unix_wait_cmd,
3932 static clib_error_t *
3933 echo_cmd (vlib_main_t * vm,
3934 unformat_input_t * input, vlib_cli_command_t * cmd)
3936 unformat_input_t _line_input, *line_input = &_line_input;
3938 /* Get a line of input. */
3939 if (!unformat_user (input, unformat_line_input, line_input))
3941 vlib_cli_output (vm, "");
3945 vlib_cli_output (vm, "%v", line_input->buffer);
3947 unformat_free (line_input);
3952 VLIB_CLI_COMMAND (cli_unix_echo_cmd, static) = {
3954 .short_help = "echo <rest-of-line>",
3955 .function = echo_cmd,
3959 static clib_error_t *
3960 define_cmd_fn (vlib_main_t * vm,
3961 unformat_input_t * input, vlib_cli_command_t * cmd)
3964 unformat_input_t _line_input, *line_input = &_line_input;
3965 unix_cli_main_t *cm = &unix_cli_main;
3966 clib_error_t *error;
3968 if (!unformat (input, "%s", ¯o_name))
3969 return clib_error_return (0, "missing variable name...");
3971 /* Remove white space */
3972 (void) unformat (input, "");
3974 /* Get a line of input. */
3975 if (!unformat_user (input, unformat_line_input, line_input))
3977 error = clib_error_return (0, "missing value for '%s'...", macro_name);
3978 vec_free (macro_name);
3981 /* the macro expander expects c-strings, not vectors... */
3982 vec_add1 (line_input->buffer, 0);
3983 clib_macro_set_value (&cm->macro_main, (char *) macro_name,
3984 (char *) line_input->buffer);
3985 vec_free (macro_name);
3986 unformat_free (line_input);
3991 VLIB_CLI_COMMAND (define_cmd, static) = {
3993 .short_help = "define <variable-name> <value>",
3994 .function = define_cmd_fn,
3999 static clib_error_t *
4000 undefine_cmd_fn (vlib_main_t * vm,
4001 unformat_input_t * input, vlib_cli_command_t * cmd)
4004 unix_cli_main_t *cm = &unix_cli_main;
4006 if (!unformat (input, "%s", ¯o_name))
4007 return clib_error_return (0, "missing variable name...");
4009 if (clib_macro_unset (&cm->macro_main, (char *) macro_name))
4010 vlib_cli_output (vm, "%s wasn't set...", macro_name);
4012 vec_free (macro_name);
4017 VLIB_CLI_COMMAND (undefine_cmd, static) = {
4019 .short_help = "undefine <variable-name>",
4020 .function = undefine_cmd_fn,
4024 static clib_error_t *
4025 show_macro_cmd_fn (vlib_main_t * vm,
4026 unformat_input_t * input, vlib_cli_command_t * cmd)
4028 unix_cli_main_t *cm = &unix_cli_main;
4031 if (unformat (input, "noevaluate %=", &evaluate, 0))
4033 else if (unformat (input, "noeval %=", &evaluate, 0))
4036 vlib_cli_output (vm, "%U", format_clib_macro_main, &cm->macro_main,
4042 VLIB_CLI_COMMAND (show_macro, static) = {
4043 .path = "show macro",
4044 .short_help = "show macro [noevaluate]",
4045 .function = show_macro_cmd_fn,
4049 static clib_error_t *
4050 unix_cli_init (vlib_main_t * vm)
4052 unix_cli_main_t *cm = &unix_cli_main;
4054 /* Breadcrumb to indicate the new session process
4055 * has not been started */
4056 cm->new_session_process_node_index = ~0;
4057 clib_macro_init (&cm->macro_main);
4061 VLIB_INIT_FUNCTION (unix_cli_init);
4064 * fd.io coding-style-patch-verification: ON
4067 * eval: (c-set-style "gnu")