FD.io VPP  v18.07.1-19-g511ce25
Vector Packet Processing
cli.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * cli.c: Unix stdin/socket CLI.
17  *
18  * Copyright (c) 2008 Eliot Dresselhaus
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining
21  * a copy of this software and associated documentation files (the
22  * "Software"), to deal in the Software without restriction, including
23  * without limitation the rights to use, copy, modify, merge, publish,
24  * distribute, sublicense, and/or sell copies of the Software, and to
25  * permit persons to whom the Software is furnished to do so, subject to
26  * the following conditions:
27  *
28  * The above copyright notice and this permission notice shall be
29  * included in all copies or substantial portions of the Software.
30  *
31  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38  */
39 /**
40  * @file
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.
44  */
45 /*? %%clicmd:group_label Command line session %% ?*/
46 /*? %%syscfg:group_label Command line session %% ?*/
47 
48 #include <vlib/vlib.h>
49 #include <vlib/unix/unix.h>
50 #include <vppinfra/timer.h>
51 
52 #include <ctype.h>
53 #include <fcntl.h>
54 #include <sys/stat.h>
55 #include <termios.h>
56 #include <signal.h>
57 #include <unistd.h>
58 #include <arpa/telnet.h>
59 #include <sys/ioctl.h>
60 #include <sys/types.h>
61 #include <unistd.h>
62 #include <limits.h>
63 
64 /** ANSI escape code. */
65 #define ESC "\x1b"
66 
67 /** ANSI Control Sequence Introducer. */
68 #define CSI ESC "["
69 
70 /** ANSI clear screen. */
71 #define ANSI_CLEAR CSI "2J" CSI "1;1H"
72 /** ANSI reset color settings. */
73 #define ANSI_RESET CSI "0m"
74 /** ANSI Start bold text. */
75 #define ANSI_BOLD CSI "1m"
76 /** ANSI Stop bold text. */
77 #define ANSI_DIM CSI "2m"
78 /** ANSI Start dark red text. */
79 #define ANSI_DRED ANSI_DIM CSI "31m"
80 /** ANSI Start bright red text. */
81 #define ANSI_BRED ANSI_BOLD CSI "31m"
82 /** ANSI clear line cursor is on. */
83 #define ANSI_CLEARLINE CSI "2K"
84 /** ANSI scroll screen down one line. */
85 #define ANSI_SCROLLDN CSI "1T"
86 /** ANSI save cursor position. */
87 #define ANSI_SAVECURSOR CSI "s"
88 /** ANSI restore cursor position if previously saved. */
89 #define ANSI_RESTCURSOR CSI "u"
90 
91 /** Maximum depth into a byte stream from which to compile a Telnet
92  * protocol message. This is a safety measure. */
93 #define UNIX_CLI_MAX_DEPTH_TELNET 24
94 
95 /** Maximum terminal width we will accept */
96 #define UNIX_CLI_MAX_TERMINAL_WIDTH 512
97 /** Maximum terminal height we will accept */
98 #define UNIX_CLI_MAX_TERMINAL_HEIGHT 512
99 /** Default terminal height */
100 #define UNIX_CLI_DEFAULT_TERMINAL_HEIGHT 24
101 /** Default terminal width */
102 #define UNIX_CLI_DEFAULT_TERMINAL_WIDTH 80
103 
104 /** A CLI banner line. */
105 typedef struct
106 {
107  u8 *line; /**< The line to print. */
108  u32 length; /**< The length of the line without terminating NUL. */
110 
111 #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 }
112 /** Plain welcome banner. */
113 static unix_cli_banner_t unix_cli_banner[] = {
114  _(" _______ _ _ _____ ___ \n"),
115  _(" __/ __/ _ \\ (_)__ | | / / _ \\/ _ \\\n"),
116  _(" _/ _// // / / / _ \\ | |/ / ___/ ___/\n"),
117  _(" /_/ /____(_)_/\\___/ |___/_/ /_/ \n"),
118  _("\n")
119 };
120 
121 /** ANSI color welcome banner. */
122 static unix_cli_banner_t unix_cli_banner_color[] = {
123  _(ANSI_BRED " _______ _ " ANSI_RESET " _ _____ ___ \n"),
124  _(ANSI_BRED " __/ __/ _ \\ (_)__ " ANSI_RESET " | | / / _ \\/ _ \\\n"),
125  _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET " | |/ / ___/ ___/\n"),
126  _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET " |___/_/ /_/ \n"),
127  _("\n")
128 };
129 
130 #undef _
131 
132 /** Pager line index */
133 typedef struct
134 {
135  /** Index into pager_vector */
137 
138  /** Offset of the string in the line */
140 
141  /** Length of the string in the line */
144 
145 
146 /** Unix CLI session. */
147 typedef struct
148 {
149  /** The file index held by unix.c */
151 
152  /** Vector of output pending write to file descriptor. */
154 
155  /** Vector of input saved by Unix input node to be processed by
156  CLI process. */
158 
159  /** This session has command history. */
161  /** Array of vectors of commands in the history. */
163  /** The command currently pointed at by the history cursor. */
165  /** How far from the end of the history array the user has browsed. */
167 
168  /** Maximum number of history entries this session will store. */
170 
171  /** Current command line counter */
173 
174  /** The string being searched for in the history. */
176  /** If non-zero then the CLI is searching in the history array.
177  * - @c -1 means search backwards.
178  * - @c 1 means search forwards.
179  */
181 
182  /** Position of the insert cursor on the current input line */
184 
185  /** Line mode or char mode */
187 
188  /** Set if the CRLF mode wants CR + LF */
190 
191  /** Can we do ANSI output? */
193 
194  /** Has the session started? */
196 
197  /** Disable the pager? */
199 
200  /** Whether the session is interactive or not.
201  * Controls things like initial banner, the CLI prompt etc. */
203 
204  /** Whether the session is attached to a socket. */
206 
207  /** If EPIPE has been detected, prevent further write-related
208  * activity on the descriptor.
209  */
211 
212  /** Pager buffer */
214 
215  /** Index of line fragments in the pager buffer */
217 
218  /** Line number of top of page */
220 
221  /** Terminal width */
223 
224  /** Terminal height */
226 
227  /** Process node identifier */
229 
230  /** The current direction of cursor travel.
231  * This is important since when advancing left-to-right, at the
232  * right hand edge of the console the terminal typically defers
233  * wrapping the cursor to the next line until a character is
234  * actually displayed.
235  * This messes up our heuristic for whether to use ANSI to return
236  * the cursor to the end of the line and instead we have to
237  * nudge the cursor to the next line.
238  * A Value of @c 0 means we're advancing left-to-right; @c 1 means
239  * the opposite.
240  */
242 
244 
245 /** Resets the pager buffer and other data.
246  * @param f The CLI session whose pager needs to be reset.
247  */
248 always_inline void
250 {
251  u8 **p;
252 
253  f->pager_start = 0;
254 
255  vec_free (f->pager_index);
256  f->pager_index = 0;
257 
258  vec_foreach (p, f->pager_vector)
259  {
260  vec_free (*p);
261  }
262  vec_free (f->pager_vector);
263  f->pager_vector = 0;
264 }
265 
266 /** Release storage used by a CLI session.
267  * @param f The CLI session whose storage needs to be released.
268  */
269 always_inline void
271 {
272  vec_free (f->output_vector);
273  vec_free (f->input_vector);
275 }
276 
277 /** CLI actions */
278 typedef enum
279 {
280  UNIX_CLI_PARSE_ACTION_NOACTION = 0, /**< No action */
281  UNIX_CLI_PARSE_ACTION_CRLF, /**< Carriage return, newline or enter */
282  UNIX_CLI_PARSE_ACTION_TAB, /**< Tab key */
283  UNIX_CLI_PARSE_ACTION_ERASE, /**< Erase cursor left */
284  UNIX_CLI_PARSE_ACTION_ERASERIGHT, /**< Erase cursor right */
285  UNIX_CLI_PARSE_ACTION_UP, /**< Up arrow */
286  UNIX_CLI_PARSE_ACTION_DOWN, /**< Down arrow */
287  UNIX_CLI_PARSE_ACTION_LEFT, /**< Left arrow */
288  UNIX_CLI_PARSE_ACTION_RIGHT, /**< Right arrow */
289  UNIX_CLI_PARSE_ACTION_HOME, /**< Home key (jump to start of line) */
290  UNIX_CLI_PARSE_ACTION_END, /**< End key (jump to end of line) */
291  UNIX_CLI_PARSE_ACTION_WORDLEFT, /**< Jump cursor to start of left word */
292  UNIX_CLI_PARSE_ACTION_WORDRIGHT, /**< Jump cursor to start of right word */
293  UNIX_CLI_PARSE_ACTION_ERASELINELEFT, /**< Erase line to left of cursor */
294  UNIX_CLI_PARSE_ACTION_ERASELINERIGHT, /**< Erase line to right & including cursor */
295  UNIX_CLI_PARSE_ACTION_CLEAR, /**< Clear the terminal */
296  UNIX_CLI_PARSE_ACTION_REVSEARCH, /**< Search backwards in command history */
297  UNIX_CLI_PARSE_ACTION_FWDSEARCH, /**< Search forwards in command history */
298  UNIX_CLI_PARSE_ACTION_YANK, /**< Undo last erase action */
299  UNIX_CLI_PARSE_ACTION_TELNETIAC, /**< Telnet control code */
300 
301  UNIX_CLI_PARSE_ACTION_PAGER_CRLF, /**< Enter pressed (CR, CRLF, LF, etc) */
302  UNIX_CLI_PARSE_ACTION_PAGER_QUIT, /**< Exit the pager session */
303  UNIX_CLI_PARSE_ACTION_PAGER_NEXT, /**< Scroll to next page */
304  UNIX_CLI_PARSE_ACTION_PAGER_DN, /**< Scroll to next line */
305  UNIX_CLI_PARSE_ACTION_PAGER_UP, /**< Scroll to previous line */
306  UNIX_CLI_PARSE_ACTION_PAGER_TOP, /**< Scroll to first line */
307  UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM, /**< Scroll to last line */
308  UNIX_CLI_PARSE_ACTION_PAGER_PGDN, /**< Scroll to next page */
309  UNIX_CLI_PARSE_ACTION_PAGER_PGUP, /**< Scroll to previous page */
310  UNIX_CLI_PARSE_ACTION_PAGER_REDRAW, /**< Clear and redraw the page on the terminal */
311  UNIX_CLI_PARSE_ACTION_PAGER_SEARCH, /**< Search the pager buffer */
312 
313  UNIX_CLI_PARSE_ACTION_PARTIALMATCH, /**< Action parser found a partial match */
314  UNIX_CLI_PARSE_ACTION_NOMATCH /**< Action parser did not find any match */
316 
317 /** @brief Mapping of input buffer strings to action values.
318  * @note This won't work as a hash since we need to be able to do
319  * partial matches on the string.
320  */
321 typedef struct
322 {
323  u8 *input; /**< Input string to match. */
324  u32 len; /**< Length of input without final NUL. */
325  unix_cli_parse_action_t action; /**< Action to take when matched. */
327 
328 /** @brief Given a capital ASCII letter character return a @c NUL terminated
329  * string with the control code for that letter.
330  *
331  * @param c An ASCII character.
332  * @return A @c NUL terminated string of type @c u8[].
333  *
334  * @par Example
335  * @c CTL('A') returns <code>{ 0x01, 0x00 }</code> as a @c u8[].
336  */
337 #define CTL(c) (u8[]){ (c) - '@', 0 }
338 
339 #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) }
340 /**
341  * Patterns to match on a CLI input stream.
342  * @showinitializer
343  */
344 static unix_cli_parse_actions_t unix_cli_parse_strings[] = {
345  /* Line handling */
346  _("\r\n", UNIX_CLI_PARSE_ACTION_CRLF), /* Must be before '\r' */
348  _("\r\0", UNIX_CLI_PARSE_ACTION_CRLF), /* Telnet does this */
350 
351  /* Unix shell control codes */
354  _(CTL ('P'), UNIX_CLI_PARSE_ACTION_UP),
357  _(CTL ('E'), UNIX_CLI_PARSE_ACTION_END),
363  _(ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* Alt-B */
364  _(ESC "f", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* Alt-F */
365  _("\b", UNIX_CLI_PARSE_ACTION_ERASE), /* ^H */
366  _("\x7f", UNIX_CLI_PARSE_ACTION_ERASE), /* Backspace */
367  _("\t", UNIX_CLI_PARSE_ACTION_TAB), /* ^I */
368 
369  /* VT100 Normal mode - Broadest support */
376  _(CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT), /* Delete */
377  _(CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* C-Left */
378  _(CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* C-Right */
379 
380  /* VT100 Application mode - Some Gnome Terminal functions use these */
381  _(ESC "OA", UNIX_CLI_PARSE_ACTION_UP),
387 
388  /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */
391 
392  /* Emacs-ish history search */
395 
396  /* Other protocol things */
397  _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
398  _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */
400 };
401 
402 /**
403  * Patterns to match when a CLI session is in the pager.
404  * @showinitializer
405  */
406 static unix_cli_parse_actions_t unix_cli_parse_pager[] = {
407  /* Line handling */
408  _("\r\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Must be before '\r' */
410  _("\r\0", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Telnet does this */
412 
413  /* Pager commands */
419 
420  /* VT100 */
425 
426  /* VT100 Application mode */
431 
432  /* ANSI X3.41-1974 */
437 
438  /* Other protocol things */
439  _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
440  _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */
442 };
443 
444 #undef _
445 
446 /** CLI session events. */
447 typedef enum
448 {
449  UNIX_CLI_PROCESS_EVENT_READ_READY, /**< A file descriptor has data to be read. */
450  UNIX_CLI_PROCESS_EVENT_QUIT, /**< A CLI session wants to close. */
452 
453 /** CLI global state. */
454 typedef struct
455 {
456  /** Prompt string for CLI. */
458 
459  /** Vec pool of CLI sessions. */
461 
462  /** Vec pool of unused session indices. */
464 
465  /** The session index of the stdin cli */
467 
468  /** File pool index of current input. */
471 
472 /** CLI global state */
474 
475 /**
476  * @brief Search for a byte sequence in the action list.
477  *
478  * Searches the @ref unix_cli_parse_actions_t list in @a a for a match with
479  * the bytes in @a input of maximum length @a ilen bytes.
480  * When a match is made @a *matched indicates how many bytes were matched.
481  * Returns a value from the enum @ref unix_cli_parse_action_t to indicate
482  * whether no match was found, a partial match was found or a complete
483  * match was found and what action, if any, should be taken.
484  *
485  * @param[in] a Actions list to search within.
486  * @param[in] input String fragment to search for.
487  * @param[in] ilen Length of the string in 'input'.
488  * @param[out] matched Pointer to an integer that will contain the number
489  * of bytes matched when a complete match is found.
490  *
491  * @return Action from @ref unix_cli_parse_action_t that the string fragment
492  * matches.
493  * @ref UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the
494  * whole input string matches the start of at least one action.
495  * @ref UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no
496  * match at all.
497  */
500  u8 * input, u32 ilen, i32 * matched)
501 {
502  u8 partial = 0;
503 
504  while (a->input)
505  {
506  if (ilen >= a->len)
507  {
508  /* see if the start of the input buffer exactly matches the current
509  * action string. */
510  if (memcmp (input, a->input, a->len) == 0)
511  {
512  *matched = a->len;
513  return a->action;
514  }
515  }
516  else
517  {
518  /* if the first ilen characters match, flag this as a partial -
519  * meaning keep collecting bytes in case of a future match */
520  if (memcmp (input, a->input, ilen) == 0)
521  partial = 1;
522  }
523 
524  /* check next action */
525  a++;
526  }
527 
528  return partial ?
530 }
531 
532 
533 /** Add bytes to the output vector and then flagg the I/O system that bytes
534  * are available to be sent.
535  */
536 static void
538  unix_cli_file_t * cf,
539  u8 * buffer, uword buffer_bytes)
540 {
542 
543  vec_add (cf->output_vector, buffer, buffer_bytes);
544  if (vec_len (cf->output_vector) > 0)
545  {
546  int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
548  if (!skip_update)
550  }
551 }
552 
553 /** Delete all bytes from the output vector and flag the I/O system
554  * that no more bytes are available to be sent.
555  */
556 static void
558  unix_cli_file_t * cf, uword n_bytes)
559 {
561 
562  vec_delete (cf->output_vector, n_bytes, 0);
563  if (vec_len (cf->output_vector) <= 0)
564  {
565  int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
567  if (!skip_update)
569  }
570 }
571 
572 /** @brief A bit like strchr with a buffer length limit.
573  * Search a buffer for the first instance of a character up to the limit of
574  * the buffer length. If found then return the position of that character.
575  *
576  * The key departure from strchr is that if the character is not found then
577  * return the buffer length.
578  *
579  * @param chr The byte value to search for.
580  * @param str The buffer in which to search for the value.
581  * @param len The depth into the buffer to search.
582  *
583  * @return The index of the first occurence of \c chr. If \c chr is not
584  * found then \c len instead.
585  */
587 unix_vlib_findchr (u8 chr, u8 * str, word len)
588 {
589  word i = 0;
590  for (i = 0; i < len; i++, str++)
591  {
592  if (*str == chr)
593  return i;
594  }
595  return len;
596 }
597 
598 /** @brief Send a buffer to the CLI stream if possible, enqueue it otherwise.
599  * Attempts to write given buffer to the file descriptor of the given
600  * Unix CLI session. If that session already has data in the output buffer
601  * or if the write attempt tells us to try again later then the given buffer
602  * is appended to the pending output buffer instead.
603  *
604  * This is typically called only from \c unix_vlib_cli_output_cooked since
605  * that is where CRLF handling occurs or from places where we explicitly do
606  * not want cooked handling.
607  *
608  * @param cf Unix CLI session of the desired stream to write to.
609  * @param uf The Unix file structure of the desired stream to write to.
610  * @param buffer Pointer to the buffer that needs to be written.
611  * @param buffer_bytes The number of bytes from \c buffer to write.
612  */
613 static void
615  clib_file_t * uf, u8 * buffer, uword buffer_bytes)
616 {
617  int n = 0;
618 
619  if (cf->has_epipe) /* don't try writing anything */
620  return;
621 
622  if (vec_len (cf->output_vector) == 0)
623  {
624  if (cf->is_socket)
625  /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
626  n = send (uf->file_descriptor, buffer, buffer_bytes, MSG_NOSIGNAL);
627  else
628  n = write (uf->file_descriptor, buffer, buffer_bytes);
629  }
630 
631  if (n < 0 && errno != EAGAIN)
632  {
633  if (errno == EPIPE)
634  {
635  /* connection closed on us */
636  unix_main_t *um = &unix_main;
637  cf->has_epipe = 1;
640  uf->private_data);
641  }
642  else
643  {
644  clib_unix_warning ("write");
645  }
646  }
647  else if ((word) n < (word) buffer_bytes)
648  {
649  /* We got EAGAIN or we already have stuff in the buffer;
650  * queue up whatever didn't get sent for later. */
651  if (n < 0)
652  n = 0;
653  unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
654  }
655 }
656 
657 /** @brief Process a buffer for CRLF handling before outputting it to the CLI.
658  *
659  * @param cf Unix CLI session of the desired stream to write to.
660  * @param uf The Unix file structure of the desired stream to write to.
661  * @param buffer Pointer to the buffer that needs to be written.
662  * @param buffer_bytes The number of bytes from \c buffer to write.
663  */
664 static void
666  clib_file_t * uf,
667  u8 * buffer, uword buffer_bytes)
668 {
669  word end = 0, start = 0;
670 
671  while (end < buffer_bytes)
672  {
673  if (cf->crlf_mode)
674  {
675  /* iterate the line on \n's so we can insert a \r before it */
676  end = unix_vlib_findchr ('\n',
677  buffer + start,
678  buffer_bytes - start) + start;
679  }
680  else
681  {
682  /* otherwise just send the whole buffer */
683  end = buffer_bytes;
684  }
685 
686  unix_vlib_cli_output_raw (cf, uf, buffer + start, end - start);
687 
688  if (cf->crlf_mode)
689  {
690  if (end < buffer_bytes)
691  {
692  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\r\n", 2);
693  end++; /* skip the \n that we already sent */
694  }
695  start = end;
696  }
697  }
698 
699  /* Use the last character to determine the last direction of the cursor. */
700  if (buffer_bytes > 0)
701  cf->cursor_direction = (buffer[buffer_bytes - 1] == (u8) '\b');
702 }
703 
704 /** @brief Moves the terminal cursor one character to the left, with
705  * special handling when it reaches the left edge of the terminal window.
706  *
707  * Ordinarily we can simply send a '\b' to move the cursor left, however
708  * most terminals will not reverse-wrap to the end of the previous line
709  * if the cursor is in the left-most column. To counter this we must
710  * check the cursor position + prompt length modulo terminal width and
711  * if available use some other means, such as ANSI terminal escape
712  * sequences, to move the cursor.
713  *
714  * @param cf Unix CLI session of the desired stream to write to.
715  * @param uf The Unix file structure of the desired stream to write to.
716  */
717 static void
719 {
721  static u8 *ansi = 0; /* assumes no reentry */
722  u32 position;
723 
724  if (!cf->is_interactive || !cf->ansi_capable || !cf->width)
725  {
726  /* No special handling for dumb terminals */
727  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
728  return;
729  }
730 
731  position = ((u32) vec_len (cm->cli_prompt) + cf->cursor) % cf->width;
732 
733  if (position != 0)
734  {
735  /* No special handling required if we're not at the left edge */
736  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
737  return;
738  }
739 
740  if (!cf->cursor_direction)
741  {
742  /* Special handling for when we are at the left edge but
743  * the cursor was going left-to-right, but in this situation
744  * xterm-like terminals actually hide the cursor off the right
745  * edge. A \b here seems to jump one char too many, so let's
746  * force the cursor onto the next line instead.
747  */
748  if (cf->cursor < vec_len (cf->current_command))
750  1);
751  else
752  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
753  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
754  }
755 
756  /* Relocate the cursor at the right hand edge one line above */
757  ansi = format (ansi, CSI "A" CSI "%dC", cf->width - 1);
758  unix_vlib_cli_output_cooked (cf, uf, ansi, vec_len (ansi));
759  vec_reset_length (ansi); /* keep the vec around for next time */
760  cf->cursor_direction = 1; /* going backwards now */
761 }
762 
763 /** @brief Output the CLI prompt */
764 static void
766 {
768 
769  if (cf->is_interactive) /* Only interactive sessions get a prompt */
771  vec_len (cm->cli_prompt));
772 }
773 
774 /** @brief Output a pager prompt and show number of buffered lines */
775 static void
777 {
778  u8 *prompt;
779  u32 h;
780 
781  h = cf->pager_start + (cf->height - 1);
782  if (h > vec_len (cf->pager_index))
783  h = vec_len (cf->pager_index);
784 
785  prompt = format (0, "\r%s-- more -- (%d-%d/%d)%s",
786  cf->ansi_capable ? ANSI_BOLD : "",
787  cf->pager_start + 1,
788  h,
789  vec_len (cf->pager_index),
790  cf->ansi_capable ? ANSI_RESET : "");
791 
792  unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
793 
794  vec_free (prompt);
795 }
796 
797 /** @brief Output a pager "skipping" message */
798 static void
800  char *message, char *postfix)
801 {
802  u8 *prompt;
803 
804  prompt = format (0, "\r%s-- %s --%s%s",
805  cf->ansi_capable ? ANSI_BOLD : "",
806  message, cf->ansi_capable ? ANSI_RESET : "", postfix);
807 
808  unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
809 
810  vec_free (prompt);
811 }
812 
813 /** @brief Erase the printed pager prompt */
814 static void
816 {
817  if (cf->ansi_capable)
818  {
819  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
821  (u8 *) ANSI_CLEARLINE,
822  sizeof (ANSI_CLEARLINE) - 1);
823  }
824  else
825  {
826  int i;
827 
828  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
829  for (i = 0; i < cf->width - 1; i++)
830  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
831  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
832  }
833 }
834 
835 /** @brief Uses an ANSI escape sequence to move the cursor */
836 static void
838 {
839  u8 *str;
840 
841  str = format (0, "%s%d;%dH", CSI, y, x);
842 
843  unix_vlib_cli_output_cooked (cf, uf, str, vec_len (str));
844 
845  vec_free (str);
846 }
847 
848 /** Redraw the currently displayed page of text.
849  * @param cf CLI session to redraw the pager buffer of.
850  * @param uf Unix file of the CLI session.
851  */
852 static void
854 {
856  u8 *line = NULL;
857  word i;
858 
859  /* No active pager? Do nothing. */
860  if (!vec_len (cf->pager_index))
861  return;
862 
863  if (cf->ansi_capable)
864  {
865  /* If we have ANSI, send the clear screen sequence */
867  (u8 *) ANSI_CLEAR,
868  sizeof (ANSI_CLEAR) - 1);
869  }
870  else
871  {
872  /* Otherwise make sure we're on a blank line */
874  }
875 
876  /* (Re-)send the current page of content */
877  for (i = 0; i < cf->height - 1 &&
878  i + cf->pager_start < vec_len (cf->pager_index); i++)
879  {
880  pi = &cf->pager_index[cf->pager_start + i];
881  line = cf->pager_vector[pi->line] + pi->offset;
882 
883  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
884  }
885  /* if the last line didn't end in newline, add a newline */
886  if (pi && line[pi->length - 1] != '\n')
887  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
888 
889  unix_cli_pager_prompt (cf, uf);
890 }
891 
892 /** @brief Process and add a line to the pager index.
893  * In normal operation this function will take the given character string
894  * found in @c line and with length @c len_or_index and iterates the over the
895  * contents, adding each line of text discovered within it to the
896  * pager index. Lines are identified by newlines ("<code>\\n</code>") and by
897  * strings longer than the width of the terminal.
898  *
899  * If instead @c line is @c NULL then @c len_or_index is taken to mean the
900  * index of an existing line in the pager buffer; this simply means that the
901  * input line does not need to be cloned since we alreayd have it. This is
902  * typical if we are reindexing the pager buffer.
903  *
904  * @param cf The CLI session whose pager we are adding to.
905  * @param line The string of text to be indexed into the pager buffer.
906  * If @c line is @c NULL then the mode of operation
907  * changes slightly; see the description above.
908  * @param len_or_index If @c line is a pointer to a string then this parameter
909  * indicates the length of that string; Otherwise this
910  * value provides the index in the pager buffer of an
911  * existing string to be indexed.
912  */
913 static void
914 unix_cli_pager_add_line (unix_cli_file_t * cf, u8 * line, word len_or_index)
915 {
916  u8 *p = NULL;
917  word i, j, k;
918  word line_index, len;
919  u32 width = cf->width;
921 
922  if (line == NULL)
923  {
924  /* Use a line already in the pager buffer */
925  line_index = len_or_index;
926  if (cf->pager_vector != NULL)
927  p = cf->pager_vector[line_index];
928  len = vec_len (p);
929  }
930  else
931  {
932  len = len_or_index;
933  /* Add a copy of the raw string to the pager buffer */
934  p = vec_new (u8, len);
935  clib_memcpy (p, line, len);
936 
937  /* store in pager buffer */
938  line_index = vec_len (cf->pager_vector);
939  vec_add1 (cf->pager_vector, p);
940  }
941 
942  i = 0;
943  while (i < len)
944  {
945  /* Find the next line, or run to terminal width, or run to EOL */
946  int l = len - i;
947  j = unix_vlib_findchr ((u8) '\n', p, l < width ? l : width);
948 
949  if (j < l && p[j] == '\n') /* incl \n */
950  j++;
951 
952  /* Add the line to the index */
953  k = vec_len (cf->pager_index);
954  vec_validate (cf->pager_index, k);
955  pi = &cf->pager_index[k];
956 
957  pi->line = line_index;
958  pi->offset = i;
959  pi->length = j;
960 
961  i += j;
962  p += j;
963  }
964 }
965 
966 /** @brief Reindex entire pager buffer.
967  * Resets the current pager index and then re-adds the lines in the pager
968  * buffer to the index.
969  *
970  * Additionally this function attempts to retain the current page start
971  * line offset by searching for the same top-of-screen line in the new index.
972  *
973  * @param cf The CLI session whose pager buffer should be reindexed.
974  */
975 static void
977 {
978  word i, old_line, old_offset;
980 
981  /* If there is nothing in the pager buffer then make sure the index
982  * is empty and move on.
983  */
984  if (cf->pager_vector == 0)
985  {
987  return;
988  }
989 
990  /* Retain a pointer to the current page start line so we can
991  * find it later
992  */
993  pi = &cf->pager_index[cf->pager_start];
994  old_line = pi->line;
995  old_offset = pi->offset;
996 
997  /* Re-add the buffered lines to the index */
1000  {
1001  unix_cli_pager_add_line (cf, NULL, i);
1002  }
1003 
1004  /* Attempt to re-locate the previously stored page start line */
1006  {
1007  pi = &cf->pager_index[i];
1008 
1009  if (pi->line == old_line &&
1010  (pi->offset <= old_offset || pi->offset + pi->length > old_offset))
1011  {
1012  /* Found it! */
1013  cf->pager_start = i;
1014  break;
1015  }
1016  }
1017 
1018  /* In case the start line was not found (rare), ensure the pager start
1019  * index is within bounds
1020  */
1021  if (cf->pager_start >= vec_len (cf->pager_index))
1022  {
1023  if (!cf->height || vec_len (cf->pager_index) < (cf->height - 1))
1024  cf->pager_start = 0;
1025  else
1026  cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
1027  }
1028 }
1029 
1030 /** VLIB CLI output function.
1031  *
1032  * If the terminal has a pager configured then this function takes care
1033  * of collating output into the pager buffer; ensuring only the first page
1034  * is displayed and any lines in excess of the first page are buffered.
1035  *
1036  * If the maximum number of index lines in the buffer is exceeded then the
1037  * pager is cancelled and the contents of the current buffer are sent to the
1038  * terminal.
1039  *
1040  * If there is no pager configured then the output is sent directly to the
1041  * terminal.
1042  *
1043  * @param cli_file_index Index of the CLI session where this output is
1044  * directed.
1045  * @param buffer String of printabe bytes to be output.
1046  * @param buffer_bytes The number of bytes in @c buffer to be output.
1047  */
1048 static void
1049 unix_vlib_cli_output (uword cli_file_index, u8 * buffer, uword buffer_bytes)
1050 {
1051  unix_main_t *um = &unix_main;
1052  clib_file_main_t *fm = &file_main;
1054  unix_cli_file_t *cf;
1055  clib_file_t *uf;
1056 
1057  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1059 
1060  if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
1061  {
1062  unix_vlib_cli_output_cooked (cf, uf, buffer, buffer_bytes);
1063  }
1064  else
1065  {
1066  word row = vec_len (cf->pager_index);
1067  u8 *line;
1069 
1070  /* Index and add the output lines to the pager buffer. */
1071  unix_cli_pager_add_line (cf, buffer, buffer_bytes);
1072 
1073  /* Now iterate what was added to display the lines.
1074  * If we reach the bottom of the page, display a prompt.
1075  */
1076  while (row < vec_len (cf->pager_index))
1077  {
1078  if (row < cf->height - 1)
1079  {
1080  /* output this line */
1081  pi = &cf->pager_index[row];
1082  line = cf->pager_vector[pi->line] + pi->offset;
1083  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1084 
1085  /* if the last line didn't end in newline, and we're at the
1086  * bottom of the page, add a newline */
1087  if (line[pi->length - 1] != '\n' && row == cf->height - 2)
1088  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1089  }
1090  else
1091  {
1092  /* Display the pager prompt every 10 lines */
1093  if (!(row % 10))
1094  unix_cli_pager_prompt (cf, uf);
1095  }
1096  row++;
1097  }
1098 
1099  /* Check if we went over the pager buffer limit */
1100  if (vec_len (cf->pager_index) > um->cli_pager_buffer_limit)
1101  {
1102  /* Stop using the pager for the remainder of this CLI command */
1103  cf->no_pager = 2;
1104 
1105  /* If we likely printed the prompt, erase it */
1106  if (vec_len (cf->pager_index) > cf->height - 1)
1107  unix_cli_pager_prompt_erase (cf, uf);
1108 
1109  /* Dump out the contents of the buffer */
1110  for (row = cf->pager_start + (cf->height - 1);
1111  row < vec_len (cf->pager_index); row++)
1112  {
1113  pi = &cf->pager_index[row];
1114  line = cf->pager_vector[pi->line] + pi->offset;
1115  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1116  }
1117 
1118  unix_cli_pager_reset (cf);
1119  }
1120  }
1121 }
1122 
1123 /** Identify whether a terminal type is ANSI capable.
1124  *
1125  * Compares the string given in @c term with a list of terminal types known
1126  * to support ANSI escape sequences.
1127  *
1128  * This list contains, for example, @c xterm, @c screen and @c ansi.
1129  *
1130  * @param term A string with a terminal type in it.
1131  * @param len The length of the string in @c term.
1132  *
1133  * @return @c 1 if the terminal type is recognized as supporting ANSI
1134  * terminal sequences; @c 0 otherwise.
1135  */
1136 static u8
1138 {
1139  /* This may later be better done as a hash of some sort. */
1140 #define _(a) do { \
1141  if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
1142  } while(0)
1143 
1144  _("xterm");
1145  _("xterm-color");
1146  _("xterm-256color"); /* iTerm on Mac */
1147  _("screen");
1148  _("screen-256color"); /* Screen and tmux */
1149  _("ansi"); /* Microsoft Telnet */
1150 #undef _
1151 
1152  return 0;
1153 }
1154 
1155 /** Identify whether a terminal type is non-interactive.
1156  *
1157  * Compares the string given in @c term with a list of terminal types known
1158  * to be non-interactive, as send by tools such as @c vppctl .
1159  *
1160  * This list contains, for example, @c vppctl.
1161  *
1162  * @param term A string with a terminal type in it.
1163  * @param len The length of the string in @c term.
1164  *
1165  * @return @c 1 if the terminal type is recognized as being non-interactive;
1166  * @c 0 otherwise.
1167  */
1168 static u8
1170 {
1171  /* This may later be better done as a hash of some sort. */
1172 #define _(a) do { \
1173  if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
1174  } while(0)
1175 
1176  _("vppctl");
1177 #undef _
1178 
1179  return 0;
1180 }
1181 
1182 /** Set a session to be non-interactive. */
1183 static void
1185 {
1186  /* Non-interactive sessions don't get these */
1187  cf->is_interactive = 0;
1188  cf->no_pager = 1;
1189  cf->history_limit = 0;
1190  cf->has_history = 0;
1191  cf->line_mode = 1;
1192 }
1193 
1194 /** @brief Emit initial welcome banner and prompt on a connection. */
1195 static void
1197 {
1198  unix_main_t *um = &unix_main;
1199  clib_file_main_t *fm = &file_main;
1201  unix_cli_banner_t *banner;
1202  int i, len;
1203 
1204  /* Mark the session as started if we get here */
1205  cf->started = 1;
1206 
1207  if (!(cf->is_interactive)) /* No banner for non-interactive sessions */
1208  return;
1209 
1210  /*
1211  * Put the first bytes directly into the buffer so that further output is
1212  * queued until everything is ready. (oterwise initial prompt can appear
1213  * mid way through VPP initialization)
1214  */
1215  unix_cli_add_pending_output (uf, cf, (u8 *) "\r", 1);
1216 
1217  if (!um->cli_no_banner)
1218  {
1219  if (cf->ansi_capable)
1220  {
1221  banner = unix_cli_banner_color;
1222  len = ARRAY_LEN (unix_cli_banner_color);
1223  }
1224  else
1225  {
1226  banner = unix_cli_banner;
1227  len = ARRAY_LEN (unix_cli_banner);
1228  }
1229 
1230  for (i = 0; i < len; i++)
1231  {
1233  banner[i].line, banner[i].length);
1234  }
1235  }
1236 
1237  /* Prompt. */
1238  unix_cli_cli_prompt (cf, uf);
1239 
1240 }
1241 
1242 /** @brief A failsafe triggered on a timer to ensure we send the prompt
1243  * to telnet sessions that fail to negotiate the terminal type. */
1244 static void
1246 {
1248  unix_cli_file_t *cf;
1249  (void) delay;
1250 
1251  /* Check the connection didn't close already */
1252  if (pool_is_free_index (cm->cli_file_pool, (uword) arg))
1253  return;
1254 
1255  cf = pool_elt_at_index (cm->cli_file_pool, (uword) arg);
1256 
1257  if (!cf->started)
1258  unix_cli_file_welcome (cm, cf);
1259 }
1260 
1261 /** @brief A mostly no-op Telnet state machine.
1262  * Process Telnet command bytes in a way that ensures we're mostly
1263  * transparent to the Telnet protocol. That is, it's mostly a no-op.
1264  *
1265  * @return -1 if we need more bytes, otherwise a positive integer number of
1266  * bytes to consume from the input_vector, not including the initial
1267  * IAC byte.
1268  */
1269 static i32
1271  unix_cli_file_t * cf,
1272  clib_file_t * uf, u8 * input_vector, uword len)
1273 {
1274  /* Input_vector starts at IAC byte.
1275  * See if we have a complete message; if not, return -1 so we wait for more.
1276  * if we have a complete message, consume those bytes from the vector.
1277  */
1278  i32 consume = 0;
1279 
1280  if (len == 1)
1281  return -1; /* want more bytes */
1282 
1283  switch (input_vector[1])
1284  {
1285  case IAC:
1286  /* two IAC's in a row means to pass through 0xff.
1287  * since that makes no sense here, just consume it.
1288  */
1289  consume = 1;
1290  break;
1291 
1292  case WILL:
1293  case WONT:
1294  case DO:
1295  case DONT:
1296  /* Expect 3 bytes */
1297  if (vec_len (input_vector) < 3)
1298  return -1; /* want more bytes */
1299 
1300  consume = 2;
1301  break;
1302 
1303  case SB:
1304  {
1305  /* Sub option - search ahead for IAC SE to end it */
1306  i32 i;
1307  for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
1308  {
1309  if (input_vector[i - 1] == IAC && input_vector[i] == SE)
1310  {
1311  /* We have a complete message; see if we care about it */
1312  switch (input_vector[2])
1313  {
1314  case TELOPT_TTYPE:
1315  if (input_vector[3] != 0)
1316  break;
1317  {
1318  /* See if the the terminal type is recognized */
1319  u8 *term = input_vector + 4;
1320  uword len = i - 5;
1321 
1322  /* See if the terminal type is ANSI capable */
1323  cf->ansi_capable =
1324  unix_cli_terminal_type_ansi (term, len);
1325 
1326  /* See if the terminal type indicates non-interactive */
1327  if (unix_cli_terminal_type_noninteractive (term, len))
1329  }
1330 
1331  /* If session not started, we can release the pause */
1332  if (!cf->started)
1333  /* Send the welcome banner and initial prompt */
1334  unix_cli_file_welcome (&unix_cli_main, cf);
1335  break;
1336 
1337  case TELOPT_NAWS:
1338  /* Window size */
1339  if (i != 8) /* check message is correct size */
1340  break;
1341 
1342  cf->width =
1343  clib_net_to_host_u16 (*((u16 *) (input_vector + 3)));
1346  if (cf->width == 0)
1348 
1349  cf->height =
1350  clib_net_to_host_u16 (*((u16 *) (input_vector + 5)));
1353  if (cf->height == 0)
1355 
1356  /* reindex pager buffer */
1358  /* redraw page */
1359  unix_cli_pager_redraw (cf, uf);
1360  break;
1361 
1362  default:
1363  break;
1364  }
1365  /* Consume it all */
1366  consume = i;
1367  break;
1368  }
1369  }
1370 
1371  if (i == UNIX_CLI_MAX_DEPTH_TELNET)
1372  consume = 1; /* hit max search depth, advance one byte */
1373 
1374  if (consume == 0)
1375  return -1; /* want more bytes */
1376 
1377  break;
1378  }
1379 
1380  case GA:
1381  case EL:
1382  case EC:
1383  case AO:
1384  case IP:
1385  case BREAK:
1386  case DM:
1387  case NOP:
1388  case SE:
1389  case EOR:
1390  case ABORT:
1391  case SUSP:
1392  case xEOF:
1393  /* Simple one-byte messages */
1394  consume = 1;
1395  break;
1396 
1397  case AYT:
1398  /* Are You There - trigger a visible response */
1399  consume = 1;
1400  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
1401  break;
1402 
1403  default:
1404  /* Unknown command! Eat the IAC byte */
1405  break;
1406  }
1407 
1408  return consume;
1409 }
1410 
1411 /** @brief Process actionable input.
1412  * Based on the \c action process the input; this typically involves
1413  * searching the command history or editing the current command line.
1414  */
1415 static int
1417  unix_main_t * um,
1418  unix_cli_file_t * cf,
1419  clib_file_t * uf,
1420  u8 input, unix_cli_parse_action_t action)
1421 {
1422  u8 *prev;
1423  u8 *save = 0;
1424  u8 **possible_commands;
1425  int j, delta;
1426 
1427  switch (action)
1428  {
1430  break;
1431 
1434  if (!cf->has_history || !cf->history_limit)
1435  break;
1436  if (cf->search_mode == 0)
1437  {
1438  /* Erase the current command (if any) */
1439  for (; cf->cursor > 0; cf->cursor--)
1440  {
1442  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1444  }
1445 
1448 
1449  if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1450  cf->search_mode = -1;
1451  else
1452  cf->search_mode = 1;
1453  }
1454  else
1455  {
1456  if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1457  cf->search_mode = -1;
1458  else
1459  cf->search_mode = 1;
1460 
1461  cf->excursion += cf->search_mode;
1462  goto search_again;
1463  }
1464  break;
1465 
1467  /* Erase the command from the cursor to the start */
1468 
1469  j = cf->cursor;
1470  /* Shimmy backwards to the new end of line position */
1471  delta = vec_len (cf->current_command) - cf->cursor;
1472  for (; cf->cursor > delta; cf->cursor--)
1474  /* Zap from here to the end of what is currently displayed */
1475  for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
1476  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1477  /* Get back to the start of the line */
1478  for (; cf->cursor > 0; cf->cursor--)
1480 
1481  /* Delete the desired text from the command */
1482  memmove (cf->current_command, cf->current_command + j, delta);
1483  _vec_len (cf->current_command) = delta;
1484 
1485  /* Print the new contents */
1486  unix_vlib_cli_output_cooked (cf, uf, cf->current_command, delta);
1487  cf->cursor = delta; /* for backspace tracking */
1488 
1489  /* Shimmy back to the start */
1490  for (; cf->cursor > 0; cf->cursor--)
1492 
1493  cf->search_mode = 0;
1494  break;
1495 
1497  /* Erase the command from the cursor to the end */
1498 
1499  j = cf->cursor;
1500  /* Zap from cursor to end of what is currently displayed */
1501  for (; cf->cursor < (vec_len (cf->current_command)); cf->cursor++)
1502  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1503  /* Get back to where we were */
1504  for (; cf->cursor > j; cf->cursor--)
1506 
1507  /* Truncate the line at the cursor */
1508  _vec_len (cf->current_command) = cf->cursor;
1509 
1510  cf->search_mode = 0;
1511  break;
1512 
1514  if (cf->cursor > 0)
1515  {
1517  cf->cursor--;
1518  }
1519 
1520  cf->search_mode = 0;
1521  break;
1522 
1524  if (cf->cursor < vec_len (cf->current_command))
1525  {
1526  /* have to emit the character under the cursor */
1528  cf->current_command + cf->cursor, 1);
1529  cf->cursor++;
1530  }
1531 
1532  cf->search_mode = 0;
1533  break;
1534 
1537  if (!cf->has_history || !cf->history_limit)
1538  break;
1539  cf->search_mode = 0;
1540  /* Erase the command */
1541  for (; cf->cursor < vec_len (cf->current_command); cf->cursor++)
1542  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1543  for (; cf->cursor > 0; cf->cursor--)
1544  {
1546  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1548  }
1550  if (vec_len (cf->command_history))
1551  {
1552  if (action == UNIX_CLI_PARSE_ACTION_UP)
1553  delta = -1;
1554  else
1555  delta = 1;
1556 
1557  cf->excursion += delta;
1558 
1559  if (cf->excursion == vec_len (cf->command_history))
1560  {
1561  /* down-arrowed to last entry - want a blank line */
1562  _vec_len (cf->current_command) = 0;
1563  }
1564  else if (cf->excursion < 0)
1565  {
1566  /* up-arrowed over the start to the end, want a blank line */
1567  cf->excursion = vec_len (cf->command_history);
1568  _vec_len (cf->current_command) = 0;
1569  }
1570  else
1571  {
1572  if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
1573  /* down-arrowed past end - wrap to start */
1574  cf->excursion = 0;
1575 
1576  /* Print the command at the current position */
1577  prev = cf->command_history[cf->excursion];
1578  vec_validate (cf->current_command, vec_len (prev) - 1);
1579 
1580  clib_memcpy (cf->current_command, prev, vec_len (prev));
1581  _vec_len (cf->current_command) = vec_len (prev);
1583  vec_len (cf->current_command));
1584  }
1585  }
1586  cf->cursor = vec_len (cf->current_command);
1587  break;
1588 
1590  if (vec_len (cf->current_command) && cf->cursor > 0)
1591  {
1592  for (; cf->cursor > 0; cf->cursor--)
1594  }
1595 
1596  cf->search_mode = 0;
1597  break;
1598 
1600  if (vec_len (cf->current_command) &&
1601  cf->cursor < vec_len (cf->current_command))
1602  {
1604  cf->current_command + cf->cursor,
1605  vec_len (cf->current_command) -
1606  cf->cursor);
1607  cf->cursor = vec_len (cf->current_command);
1608  }
1609 
1610  cf->search_mode = 0;
1611  break;
1612 
1614  if (vec_len (cf->current_command) && cf->cursor > 0)
1615  {
1617  cf->cursor--;
1618 
1619  while (cf->cursor && isspace (cf->current_command[cf->cursor]))
1620  {
1622  cf->cursor--;
1623  }
1624  while (cf->cursor && !isspace (cf->current_command[cf->cursor]))
1625  {
1626  if (isspace (cf->current_command[cf->cursor - 1]))
1627  break;
1629  cf->cursor--;
1630  }
1631 
1632  }
1633 
1634  cf->search_mode = 0;
1635  break;
1636 
1638  if (vec_len (cf->current_command) &&
1639  cf->cursor < vec_len (cf->current_command))
1640  {
1641  int e = vec_len (cf->current_command);
1642  j = cf->cursor;
1643  while (j < e && !isspace (cf->current_command[j]))
1644  j++;
1645  while (j < e && isspace (cf->current_command[j]))
1646  j++;
1648  cf->current_command + cf->cursor,
1649  j - cf->cursor);
1650  cf->cursor = j;
1651  }
1652 
1653  cf->search_mode = 0;
1654  break;
1655 
1656 
1658  if (vec_len (cf->current_command))
1659  {
1660  if (cf->cursor == vec_len (cf->current_command))
1661  {
1663  cf->cursor--;
1664  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1665  cf->cursor++;
1667  cf->cursor--;
1668  _vec_len (cf->current_command)--;
1669  }
1670  else if (cf->cursor > 0)
1671  {
1672  /* shift everything at & to the right of the cursor left by 1 */
1673  j = vec_len (cf->current_command) - cf->cursor;
1674  memmove (cf->current_command + cf->cursor - 1,
1675  cf->current_command + cf->cursor, j);
1676  _vec_len (cf->current_command)--;
1677 
1678  /* redraw the rest of the line */
1680  cf->cursor--;
1682  cf->current_command + cf->cursor,
1683  j);
1684  cf->cursor += j;
1685  /* erase last char */
1686  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1687  cf->cursor++;
1688 
1689  /* and shift the terminal cursor back where it should be */
1690  j += 2; /* account for old string length and offset position */
1691  while (--j)
1692  {
1694  cf->cursor--;
1695  }
1696  }
1697  }
1698  cf->search_mode = 0;
1699  cf->excursion = 0;
1701  break;
1702 
1704  if (vec_len (cf->current_command))
1705  {
1706  if (cf->cursor < vec_len (cf->current_command))
1707  {
1708  /* shift everything to the right of the cursor left by 1 */
1709  j = vec_len (cf->current_command) - cf->cursor - 1;
1710  memmove (cf->current_command + cf->cursor,
1711  cf->current_command + cf->cursor + 1, j);
1712  _vec_len (cf->current_command)--;
1713  /* redraw the rest of the line */
1715  cf->current_command + cf->cursor,
1716  j);
1717  cf->cursor += j;
1718  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1719  cf->cursor++;
1721  cf->cursor--;
1722  /* and shift the terminal cursor back where it should be */
1723  if (j)
1724  {
1726  cf->cursor--;
1727  while (--j)
1728  {
1730  cf->cursor--;
1731  }
1732  }
1733  }
1734  }
1735  else if (input == 'D' - '@')
1736  {
1737  /* ^D with no command entered = quit */
1738  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
1742  cf - cm->cli_file_pool);
1743  }
1744  cf->search_mode = 0;
1745  cf->excursion = 0;
1747  break;
1748 
1750  /* If we're in ANSI mode, clear the screen.
1751  * Then redraw the prompt and any existing command input, then put
1752  * the cursor back where it was in that line.
1753  */
1754  if (cf->ansi_capable)
1756  (u8 *) ANSI_CLEAR,
1757  sizeof (ANSI_CLEAR) - 1);
1758  else
1759  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1760 
1761  unix_vlib_cli_output_raw (cf, uf,
1762  cm->cli_prompt, vec_len (cm->cli_prompt));
1764  cf->current_command,
1765  vec_len (cf->current_command));
1766  j = cf->cursor;
1767  cf->cursor = vec_len (cf->current_command);
1768  for (; cf->cursor > j; cf->cursor--)
1770 
1771  break;
1772 
1774  if (cf->cursor < vec_len (cf->current_command))
1775  {
1776  /* if we are in the middle of a line, complete only if
1777  * the cursor points to whitespace */
1778  if (isspace (cf->current_command[cf->cursor]))
1779  {
1780  /* save and clear any input that is after the cursor */
1781  vec_resize (save, vec_len (cf->current_command) - cf->cursor);
1782  clib_memcpy (save, cf->current_command + cf->cursor,
1783  vec_len (cf->current_command) - cf->cursor);
1784  _vec_len (cf->current_command) = cf->cursor;
1785  }
1786  else
1787  {
1788  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
1789  break;
1790  }
1791  }
1792  possible_commands =
1794  if (vec_len (possible_commands) == 1)
1795  {
1796  u8 *completed = possible_commands[0];
1797  j = cf->cursor;
1798 
1799  /* find the last word of current_command */
1800  while (j >= 1 && !isspace (cf->current_command[j - 1]))
1801  {
1803  cf->cursor--;
1804  j--;
1805  }
1806  _vec_len (cf->current_command) = j;
1807 
1808  /* replace it with the newly expanded command */
1809  vec_append (cf->current_command, completed);
1810 
1811  /* echo to the terminal */
1812  unix_vlib_cli_output_cooked (cf, uf, completed,
1813  vec_len (completed));
1814 
1815  /* add one trailing space if needed */
1816  if (vec_len (save) == 0)
1817  {
1818  vec_add1 (cf->current_command, ' ');
1819  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1820  }
1821 
1822  cf->cursor = vec_len (cf->current_command);
1823 
1824  }
1825  else if (vec_len (possible_commands) >= 2)
1826  {
1827  u8 **possible_command;
1828  uword max_command_len = 0, min_command_len = ~0;
1829  u32 i;
1830 
1831  vec_foreach (possible_command, possible_commands)
1832  {
1833  if (vec_len (*possible_command) > max_command_len)
1834  max_command_len = vec_len (*possible_command);
1835  if (vec_len (*possible_command) < min_command_len)
1836  min_command_len = vec_len (*possible_command);
1837  }
1838 
1839  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1840 
1841  i = 0;
1842  vec_foreach (possible_command, possible_commands)
1843  {
1844  if (i + max_command_len >= cf->width)
1845  {
1846  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1847  i = 0;
1848  }
1849  unix_vlib_cli_output_cooked (cf, uf, *possible_command,
1850  vec_len (*possible_command));
1851  for (j = vec_len (*possible_command); j < max_command_len + 2;
1852  j++)
1853  {
1854  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1855  }
1856  i += max_command_len + 2;
1857  }
1858 
1859  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1860 
1861  /* rewrite prompt */
1862  unix_cli_cli_prompt (cf, uf);
1864  vec_len (cf->current_command));
1865 
1866  /* count length of last word */
1867  j = cf->cursor;
1868  i = 0;
1869  while (j >= 1 && !isspace (cf->current_command[j - 1]))
1870  {
1871  j--;
1872  i++;
1873  }
1874 
1875  /* determine smallest common command */
1876  for (; i < min_command_len; i++)
1877  {
1878  u8 common = '\0';
1879  int stop = 0;
1880 
1881  vec_foreach (possible_command, possible_commands)
1882  {
1883  if (common == '\0')
1884  {
1885  common = (*possible_command)[i];
1886  }
1887  else if (common != (*possible_command)[i])
1888  {
1889  stop = 1;
1890  break;
1891  }
1892  }
1893 
1894  if (!stop)
1895  {
1896  vec_add1 (cf->current_command, common);
1897  cf->cursor++;
1898  unix_vlib_cli_output_cooked (cf, uf, (u8 *) & common, 1);
1899  }
1900  else
1901  {
1902  break;
1903  }
1904  }
1905  }
1906  else
1907  {
1908  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
1909  }
1910 
1911  if (vec_len (save) > 0)
1912  {
1913  /* restore remaining input if tab was hit in the middle of a line */
1914  unix_vlib_cli_output_cooked (cf, uf, save, vec_len (save));
1915  cf->cursor += vec_len (save);
1916  for (j = 0; j < vec_len (save); j++, cf->cursor--)
1918  vec_append (cf->current_command, save);
1919  vec_free (save);
1920  }
1921  vec_free (possible_commands);
1922 
1923  break;
1925  /* TODO */
1926  break;
1927 
1928 
1930  pager_quit:
1931  unix_cli_pager_prompt_erase (cf, uf);
1932  unix_cli_pager_reset (cf);
1933  unix_cli_cli_prompt (cf, uf);
1934  break;
1935 
1938  /* show next page of the buffer */
1939  if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
1940  {
1941  u8 *line = NULL;
1943 
1944  int m = cf->pager_start + (cf->height - 1);
1945  unix_cli_pager_prompt_erase (cf, uf);
1946  for (j = m;
1947  j < vec_len (cf->pager_index) && cf->pager_start < m;
1948  j++, cf->pager_start++)
1949  {
1950  pi = &cf->pager_index[j];
1951  line = cf->pager_vector[pi->line] + pi->offset;
1952  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1953  }
1954  /* if the last line didn't end in newline, add a newline */
1955  if (pi && line[pi->length - 1] != '\n')
1956  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1957  unix_cli_pager_prompt (cf, uf);
1958  }
1959  else
1960  {
1961  if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
1962  /* no more in buffer, exit, but only if it was <space> */
1963  goto pager_quit;
1964  }
1965  break;
1966 
1969  /* display the next line of the buffer */
1970  if (cf->height + cf->pager_start <= vec_len (cf->pager_index))
1971  {
1972  u8 *line;
1974 
1975  unix_cli_pager_prompt_erase (cf, uf);
1976  pi = &cf->pager_index[cf->pager_start + (cf->height - 1)];
1977  line = cf->pager_vector[pi->line] + pi->offset;
1978  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1979  cf->pager_start++;
1980  /* if the last line didn't end in newline, add a newline */
1981  if (line[pi->length - 1] != '\n')
1982  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1983  unix_cli_pager_prompt (cf, uf);
1984  }
1985  else
1986  {
1987  if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
1988  /* no more in buffer, exit, but only if it was <enter> */
1989  goto pager_quit;
1990  }
1991 
1992  break;
1993 
1995  /* scroll the page back one line */
1996  if (cf->pager_start > 0)
1997  {
1998  u8 *line = NULL;
2000 
2001  cf->pager_start--;
2002  if (cf->ansi_capable)
2003  {
2004  pi = &cf->pager_index[cf->pager_start];
2005  line = cf->pager_vector[pi->line] + pi->offset;
2006  unix_cli_pager_prompt_erase (cf, uf);
2008  sizeof (ANSI_SCROLLDN) - 1);
2010  sizeof (ANSI_SAVECURSOR) - 1);
2011  unix_cli_ansi_cursor (cf, uf, 1, 1);
2013  sizeof (ANSI_CLEARLINE) - 1);
2014  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2016  sizeof (ANSI_RESTCURSOR) - 1);
2017  unix_cli_pager_prompt_erase (cf, uf);
2018  unix_cli_pager_prompt (cf, uf);
2019  }
2020  else
2021  {
2022  int m = cf->pager_start + (cf->height - 1);
2023  unix_cli_pager_prompt_erase (cf, uf);
2024  for (j = cf->pager_start;
2025  j < vec_len (cf->pager_index) && j < m; j++)
2026  {
2027  pi = &cf->pager_index[j];
2028  line = cf->pager_vector[pi->line] + pi->offset;
2029  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2030  }
2031  /* if the last line didn't end in newline, add a newline */
2032  if (pi && line[pi->length - 1] != '\n')
2033  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2034  unix_cli_pager_prompt (cf, uf);
2035  }
2036  }
2037  break;
2038 
2040  /* back to the first page of the buffer */
2041  if (cf->pager_start > 0)
2042  {
2043  u8 *line = NULL;
2045 
2046  cf->pager_start = 0;
2047  int m = cf->pager_start + (cf->height - 1);
2048  unix_cli_pager_prompt_erase (cf, uf);
2049  for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
2050  j++)
2051  {
2052  pi = &cf->pager_index[j];
2053  line = cf->pager_vector[pi->line] + pi->offset;
2054  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2055  }
2056  /* if the last line didn't end in newline, add a newline */
2057  if (pi && line[pi->length - 1] != '\n')
2058  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2059  unix_cli_pager_prompt (cf, uf);
2060  }
2061  break;
2062 
2064  /* skip to the last page of the buffer */
2065  if (cf->pager_start < vec_len (cf->pager_index) - (cf->height - 1))
2066  {
2067  u8 *line = NULL;
2069 
2070  cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
2071  unix_cli_pager_prompt_erase (cf, uf);
2072  unix_cli_pager_message (cf, uf, "skipping", "\n");
2073  for (j = cf->pager_start; j < vec_len (cf->pager_index); j++)
2074  {
2075  pi = &cf->pager_index[j];
2076  line = cf->pager_vector[pi->line] + pi->offset;
2077  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2078  }
2079  /* if the last line didn't end in newline, add a newline */
2080  if (pi && line[pi->length - 1] != '\n')
2081  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2082  unix_cli_pager_prompt (cf, uf);
2083  }
2084  break;
2085 
2087  /* wander back one page in the buffer */
2088  if (cf->pager_start > 0)
2089  {
2090  u8 *line = NULL;
2092  int m;
2093 
2094  if (cf->pager_start >= cf->height)
2095  cf->pager_start -= cf->height - 1;
2096  else
2097  cf->pager_start = 0;
2098  m = cf->pager_start + cf->height - 1;
2099  unix_cli_pager_prompt_erase (cf, uf);
2100  for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
2101  j++)
2102  {
2103  pi = &cf->pager_index[j];
2104  line = cf->pager_vector[pi->line] + pi->offset;
2105  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
2106  }
2107  /* if the last line didn't end in newline, add a newline */
2108  if (pi && line[pi->length - 1] != '\n')
2109  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2110  unix_cli_pager_prompt (cf, uf);
2111  }
2112  break;
2113 
2115  /* Redraw the current pager screen */
2116  unix_cli_pager_redraw (cf, uf);
2117  break;
2118 
2120  /* search forwards in the buffer */
2121  break;
2122 
2123 
2125  crlf:
2126  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
2127 
2128  if (cf->has_history && cf->history_limit)
2129  {
2130  if (cf->command_history
2131  && vec_len (cf->command_history) >= cf->history_limit)
2132  {
2133  vec_free (cf->command_history[0]);
2134  vec_delete (cf->command_history, 1, 0);
2135  }
2136  /* Don't add blank lines to the cmd history */
2137  if (vec_len (cf->current_command))
2138  {
2139  /* Don't duplicate the previous command */
2140  j = vec_len (cf->command_history);
2141  if (j == 0 ||
2142  (vec_len (cf->current_command) !=
2143  vec_len (cf->command_history[j - 1])
2144  || memcmp (cf->current_command, cf->command_history[j - 1],
2145  vec_len (cf->current_command)) != 0))
2146  {
2147  /* copy the command to the history */
2148  u8 *c = 0;
2149  vec_append (c, cf->current_command);
2150  vec_add1 (cf->command_history, c);
2151  cf->command_number++;
2152  }
2153  }
2154  cf->excursion = vec_len (cf->command_history);
2155  }
2156 
2157  cf->search_mode = 0;
2159  cf->cursor = 0;
2160 
2161  return 0;
2162 
2165  if (vec_len (cf->pager_index))
2166  {
2167  /* no-op for now */
2168  }
2169  else if (cf->has_history && cf->search_mode != 0 && isprint (input))
2170  {
2171  int k, limit, offset;
2172  u8 *item;
2173 
2174  vec_add1 (cf->search_key, input);
2175 
2176  search_again:
2177  for (j = 0; j < vec_len (cf->command_history); j++)
2178  {
2179  if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
2180  cf->excursion = 0;
2181  else if (cf->excursion < 0)
2182  cf->excursion = vec_len (cf->command_history) - 1;
2183 
2184  item = cf->command_history[cf->excursion];
2185 
2186  limit = (vec_len (cf->search_key) > vec_len (item)) ?
2187  vec_len (item) : vec_len (cf->search_key);
2188 
2189  for (offset = 0; offset <= vec_len (item) - limit; offset++)
2190  {
2191  for (k = 0; k < limit; k++)
2192  {
2193  if (item[k + offset] != cf->search_key[k])
2194  goto next_offset;
2195  }
2196  goto found_at_offset;
2197 
2198  next_offset:
2199  ;
2200  }
2201  goto next;
2202 
2203  found_at_offset:
2204  for (; cf->cursor > 0; cf->cursor--)
2205  {
2207  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
2209  }
2210 
2211  vec_validate (cf->current_command, vec_len (item) - 1);
2212  clib_memcpy (cf->current_command, item, vec_len (item));
2213  _vec_len (cf->current_command) = vec_len (item);
2214 
2216  vec_len (cf->current_command));
2217  cf->cursor = vec_len (cf->current_command);
2218  goto found;
2219 
2220  next:
2221  cf->excursion += cf->search_mode;
2222  }
2223 
2224  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\nNo match...", 12);
2227  cf->search_mode = 0;
2228  cf->cursor = 0;
2229  goto crlf;
2230  }
2231  else if (isprint (input)) /* skip any errant control codes */
2232  {
2233  if (cf->cursor == vec_len (cf->current_command))
2234  {
2235  /* Append to end */
2236  vec_add1 (cf->current_command, input);
2237  cf->cursor++;
2238 
2239  /* Echo the character back to the client */
2240  unix_vlib_cli_output_cooked (cf, uf, &input, 1);
2241  }
2242  else
2243  {
2244  /* Insert at cursor: resize +1 byte, move everything over */
2245  j = vec_len (cf->current_command) - cf->cursor;
2246  vec_add1 (cf->current_command, (u8) 'A');
2247  memmove (cf->current_command + cf->cursor + 1,
2248  cf->current_command + cf->cursor, j);
2249  cf->current_command[cf->cursor] = input;
2250  /* Redraw the line */
2251  j++;
2253  cf->current_command + cf->cursor,
2254  j);
2255  cf->cursor += j;
2256  j--;
2257  /* Put terminal cursor back */
2258  for (; j > 0; j--, cf->cursor--)
2260  }
2261  }
2262  else
2263  {
2264  /* no-op - not printable or otherwise not actionable */
2265  }
2266 
2267  found:
2268 
2269  break;
2270 
2272  break;
2273  }
2274  return 1;
2275 }
2276 
2277 /** @brief Process input bytes on a stream to provide line editing and
2278  * command history in the CLI. */
2279 static int
2281  clib_file_main_t * fm, unix_cli_file_t * cf)
2282 {
2284  int i;
2285 
2286  for (i = 0; i < vec_len (cf->input_vector); i++)
2287  {
2288  unix_cli_parse_action_t action;
2289  i32 matched = 0;
2291 
2292  /* If we're in the pager mode, search the pager actions */
2293  a =
2294  vec_len (cf->pager_index) ? unix_cli_parse_pager :
2296 
2297  /* See if the input buffer is some sort of control code */
2298  action = unix_cli_match_action (a, &cf->input_vector[i],
2299  vec_len (cf->input_vector) - i,
2300  &matched);
2301 
2302  switch (action)
2303  {
2305  if (i)
2306  {
2307  /* There was a partial match which means we need more bytes
2308  * than the input buffer currently has.
2309  * Since the bytes before here have been processed, shift
2310  * the remaining contents to the start of the input buffer.
2311  */
2312  vec_delete (cf->input_vector, i, 0);
2313  }
2314  return 1; /* wait for more */
2315 
2317  /* process telnet options */
2318  matched = unix_cli_process_telnet (um, cf, uf,
2319  cf->input_vector + i,
2320  vec_len (cf->input_vector) - i);
2321  if (matched < 0)
2322  {
2323  /* There was a partial match which means we need more bytes
2324  * than the input buffer currently has.
2325  */
2326  if (i)
2327  {
2328  /*
2329  * Since the bytes before here have been processed, shift
2330  * the remaining contents to the start of the input buffer.
2331  */
2332  vec_delete (cf->input_vector, i, 0);
2333  }
2334  return 1; /* wait for more */
2335  }
2336  break;
2337 
2338  default:
2339  /* If telnet option processing switched us to line mode, get us
2340  * out of here!
2341  */
2342  if (cf->line_mode)
2343  {
2344  vec_delete (cf->input_vector, i, 0);
2345  cf->current_command = cf->input_vector;
2346  return 0;
2347  }
2348 
2349  /* process the action */
2350  if (!unix_cli_line_process_one (cm, um, cf, uf,
2351  cf->input_vector[i], action))
2352  {
2353  /* CRLF found. Consume the bytes from the input_vector */
2354  vec_delete (cf->input_vector, i + matched, 0);
2355  /* And tell our caller to execute cf->input_command */
2356  return 0;
2357  }
2358  }
2359 
2360  i += matched;
2361  }
2362 
2364  return 1;
2365 }
2366 
2367 /** @brief Process input to a CLI session. */
2368 static void
2370 {
2371  unix_main_t *um = &unix_main;
2372  clib_file_main_t *fm = &file_main;
2373  clib_file_t *uf;
2374  unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2375  unformat_input_t input;
2376  int vlib_parse_eval (u8 *);
2377 
2378  cm->current_input_file_index = cli_file_index;
2379 
2380 more:
2381  /* Try vlibplex first. Someday... */
2382  if (0 && vlib_parse_eval (cf->input_vector) == 0)
2383  goto done;
2384 
2385 
2386  if (cf->line_mode)
2387  {
2388  /* just treat whatever we got as a complete line of input */
2389  cf->current_command = cf->input_vector;
2390  }
2391  else
2392  {
2393  /* Line edit, echo, etc. */
2394  if (unix_cli_line_edit (cm, um, fm, cf))
2395  /* want more input */
2396  return;
2397  }
2398 
2399  if (um->log_fd)
2400  {
2401  static u8 *lv;
2402  vec_reset_length (lv);
2403  lv = format (lv, "%U[%d]: %v",
2404  format_timeval, 0 /* current bat-time */ ,
2405  0 /* current bat-format */ ,
2406  cli_file_index, cf->current_command);
2407  int rv __attribute__ ((unused)) = write (um->log_fd, lv, vec_len (lv));
2408  }
2409 
2410  /* Build an unformat structure around our command */
2411  unformat_init_vector (&input, cf->current_command);
2412 
2413  /* Remove leading white space from input. */
2414  (void) unformat (&input, "");
2415 
2416  cf->pager_start = 0; /* start a new pager session */
2417 
2420  cli_file_index);
2421 
2422  /* Zero buffer since otherwise unformat_free will call vec_free on it. */
2423  input.buffer = 0;
2424 
2425  unformat_free (&input);
2426 
2427  /* Re-fetch pointer since pool may have moved. */
2428  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2430 
2431 done:
2432  /* reset vector; we'll re-use it later */
2433  if (cf->line_mode)
2434  {
2436  cf->current_command = 0;
2437  }
2438  else
2439  {
2441  }
2442 
2443  if (cf->no_pager == 2)
2444  {
2445  /* Pager was programmatically disabled */
2446  unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
2447  cf->no_pager = um->cli_no_pager;
2448  }
2449 
2450  if (vec_len (cf->pager_index) == 0
2451  || vec_len (cf->pager_index) < cf->height)
2452  {
2453  /* There was no need for the pager */
2454  unix_cli_pager_reset (cf);
2455 
2456  /* Prompt. */
2457  unix_cli_cli_prompt (cf, uf);
2458  }
2459  else
2460  {
2461  /* Display the pager prompt */
2462  unix_cli_pager_prompt (cf, uf);
2463  }
2464 
2465  /* Any residual data in the input vector? */
2466  if (vec_len (cf->input_vector))
2467  goto more;
2468 
2469  /* For non-interactive sessions send a NUL byte.
2470  * Specifically this is because vppctl needs to see some traffic in
2471  * order to move on to closing the session. Commands with no output
2472  * would thus cause vppctl to hang indefinitely in non-interactive mode
2473  * since there is also no prompt sent after the command completes.
2474  */
2475  if (!cf->is_interactive)
2476  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\0", 1);
2477 }
2478 
2479 /** Destroy a CLI session.
2480  * @note If we destroy the @c stdin session this additionally signals
2481  * the shutdown of VPP.
2482  */
2483 static void
2484 unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
2485 {
2486  unix_main_t *um = &unix_main;
2487  clib_file_main_t *fm = &file_main;
2488  unix_cli_file_t *cf;
2489  clib_file_t *uf;
2490  int i;
2491 
2492  /* Validate cli_file_index */
2493  if (pool_is_free_index (cm->cli_file_pool, cli_file_index))
2494  return;
2495 
2496  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2498 
2499  /* Quit/EOF on stdin means quit program. */
2500  if (uf->file_descriptor == STDIN_FILENO)
2502 
2503  vec_free (cf->current_command);
2504  vec_free (cf->search_key);
2505 
2506  for (i = 0; i < vec_len (cf->command_history); i++)
2507  vec_free (cf->command_history[i]);
2508 
2509  vec_free (cf->command_history);
2510 
2511  clib_file_del (fm, uf);
2512 
2513  unix_cli_file_free (cf);
2514  pool_put (cm->cli_file_pool, cf);
2515 }
2516 
2517 /** Handle system events. */
2518 static uword
2521 {
2523  uword i, *data = 0;
2524 
2525  while (1)
2526  {
2527  unix_cli_process_event_type_t event_type;
2529  event_type = vlib_process_get_events (vm, &data);
2530 
2531  switch (event_type)
2532  {
2534  for (i = 0; i < vec_len (data); i++)
2535  unix_cli_process_input (cm, data[i]);
2536  break;
2537 
2539  /* Kill this process. */
2540  for (i = 0; i < vec_len (data); i++)
2541  unix_cli_kill (cm, data[i]);
2542  goto done;
2543  }
2544 
2545  if (data)
2546  _vec_len (data) = 0;
2547  }
2548 
2549 done:
2550  vec_free (data);
2551 
2552  vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
2553 
2554  /* Add node index so we can re-use this process later. */
2556 
2557  return 0;
2558 }
2559 
2560 /** Called when a CLI session file descriptor can be written to without
2561  * blocking. */
2562 static clib_error_t *
2564 {
2566  unix_cli_file_t *cf;
2567  int n;
2568 
2570 
2571  /* Flush output vector. */
2572  if (cf->is_socket)
2573  /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
2574  n = send (uf->file_descriptor,
2575  cf->output_vector, vec_len (cf->output_vector), MSG_NOSIGNAL);
2576  else
2577  n = write (uf->file_descriptor,
2578  cf->output_vector, vec_len (cf->output_vector));
2579 
2580  if (n < 0 && errno != EAGAIN)
2581  {
2582  if (errno == EPIPE)
2583  {
2584  /* connection closed on us */
2585  unix_main_t *um = &unix_main;
2586  cf->has_epipe = 1;
2589  uf->private_data);
2590  }
2591  else
2592  {
2593  return clib_error_return_unix (0, "write");
2594  }
2595  }
2596 
2597  else if (n > 0)
2598  unix_cli_del_pending_output (uf, cf, n);
2599 
2600  return /* no error */ 0;
2601 }
2602 
2603 /** Called when a CLI session file descriptor has data to be read. */
2604 static clib_error_t *
2606 {
2607  unix_main_t *um = &unix_main;
2609  unix_cli_file_t *cf;
2610  uword l;
2611  int n, n_read, n_try;
2612 
2614 
2615  n = n_try = 4096;
2616  while (n == n_try)
2617  {
2618  l = vec_len (cf->input_vector);
2619  vec_resize (cf->input_vector, l + n_try);
2620 
2621  n = read (uf->file_descriptor, cf->input_vector + l, n_try);
2622 
2623  /* Error? */
2624  if (n < 0 && errno != EAGAIN)
2625  return clib_error_return_unix (0, "read");
2626 
2627  n_read = n < 0 ? 0 : n;
2628  _vec_len (cf->input_vector) = l + n_read;
2629  }
2630 
2631  if (!(n < 0))
2633  cf->process_node_index,
2634  (n_read == 0
2637  /* event data */ uf->private_data);
2638 
2639  return /* no error */ 0;
2640 }
2641 
2642 /** Called when a CLI session file descriptor has an error condition. */
2643 static clib_error_t *
2645 {
2646  unix_main_t *um = &unix_main;
2648  unix_cli_file_t *cf;
2649 
2651  cf->has_epipe = 1; /* prevent writes while the close is pending */
2653  cf->process_node_index,
2655  /* event data */ uf->private_data);
2656 
2657  return /* no error */ 0;
2658 }
2659 
2660 /** Store a new CLI session.
2661  * @param name The name of the session.
2662  * @param fd The file descriptor for the session I/O.
2663  * @return The session ID.
2664  */
2665 static u32
2666 unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
2667 {
2668  unix_main_t *um = &unix_main;
2669  clib_file_main_t *fm = &file_main;
2670  unix_cli_file_t *cf;
2671  clib_file_t template = { 0 };
2672  vlib_main_t *vm = um->vlib_main;
2673  vlib_node_t *n = 0;
2674  u8 *file_desc = 0;
2675 
2676  file_desc = format (0, "%s", name);
2677 
2678  name = (char *) format (0, "unix-cli-%s", name);
2679 
2681  {
2683  int i;
2684  vlib_main_t *this_vlib_main;
2685  u8 *old_name = 0;
2686 
2687  /*
2688  * Nodes are bulk-copied, so node name pointers are shared.
2689  * Find the cli node in all graph replicas, and give all of them
2690  * the same new name.
2691  * Then, throw away the old shared name-vector.
2692  */
2693  for (i = 0; i < vec_len (vlib_mains); i++)
2694  {
2695  this_vlib_main = vlib_mains[i];
2696  if (this_vlib_main == 0)
2697  continue;
2698  n = vlib_get_node (this_vlib_main,
2699  cm->unused_cli_process_node_indices[l - 1]);
2700  old_name = n->name;
2701  n->name = (u8 *) name;
2702  }
2703  vec_free (old_name);
2704 
2705  vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
2706 
2707  _vec_len (cm->unused_cli_process_node_indices) = l - 1;
2708  }
2709  else
2710  {
2711  static vlib_node_registration_t r = {
2712  .function = unix_cli_process,
2713  .type = VLIB_NODE_TYPE_PROCESS,
2714  .process_log2_n_stack_bytes = 16,
2715  };
2716 
2717  r.name = name;
2718 
2720 
2721  vlib_register_node (vm, &r);
2722  vec_free (name);
2723 
2724  n = vlib_get_node (vm, r.index);
2727  }
2728 
2729  pool_get (cm->cli_file_pool, cf);
2730  memset (cf, 0, sizeof (*cf));
2731 
2732  template.read_function = unix_cli_read_ready;
2733  template.write_function = unix_cli_write_ready;
2734  template.error_function = unix_cli_error_detected;
2735  template.file_descriptor = fd;
2736  template.private_data = cf - cm->cli_file_pool;
2737  template.description = file_desc;
2738 
2739  cf->process_node_index = n->index;
2740  cf->clib_file_index = clib_file_add (fm, &template);
2741  cf->output_vector = 0;
2742  cf->input_vector = 0;
2743 
2745 
2748  p->output_function_arg = cf - cm->cli_file_pool;
2749 
2750  return cf - cm->cli_file_pool;
2751 }
2752 
2753 /** Telnet listening socket has a new connection. */
2754 static clib_error_t *
2756 {
2757  unix_main_t *um = &unix_main;
2758  clib_file_main_t *fm = &file_main;
2760  clib_socket_t *s = &um->cli_listen_socket;
2761  clib_socket_t client;
2762  char *client_name;
2763  clib_error_t *error;
2764  unix_cli_file_t *cf;
2765  u32 cf_index;
2766 
2767  error = clib_socket_accept (s, &client);
2768  if (error)
2769  return error;
2770 
2771  client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
2772 
2773  cf_index = unix_cli_file_add (cm, client_name, client.fd);
2774  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2775  cf->is_socket = 1;
2776 
2777  /* No longer need CLIB version of socket. */
2778  clib_socket_free (&client);
2779  vec_free (client_name);
2780 
2781  /* if we're supposed to run telnet session in character mode (default) */
2782  if (um->cli_line_mode == 0)
2783  {
2784  /*
2785  * Set telnet client character mode, echo on, suppress "go-ahead".
2786  * Technically these should be negotiated, but this works.
2787  */
2788  u8 charmode_option[] = {
2789  IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */
2790  IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */
2791  IAC, WILL, TELOPT_SGA, /* server willl supress GA */
2792  IAC, DO, TELOPT_SGA, /* client should supress Go Ahead */
2793  IAC, WILL, TELOPT_ECHO, /* server will do echo */
2794  IAC, DONT, TELOPT_ECHO, /* client should not echo */
2795  IAC, DO, TELOPT_TTYPE, /* client should tell us its term type */
2796  IAC, SB, TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */
2797  IAC, DO, TELOPT_NAWS, /* client should tell us its window sz */
2798  IAC, SB, TELOPT_NAWS, 1, IAC, SE, /* now tell me window size */
2799  };
2800 
2801  /* Enable history on this CLI */
2802  cf->history_limit = um->cli_history_limit;
2803  cf->has_history = cf->history_limit != 0;
2804 
2805  /* This is an interactive session until we decide otherwise */
2806  cf->is_interactive = 1;
2807 
2808  /* Make sure this session is in line mode */
2809  cf->line_mode = 0;
2810 
2811  /* We need CRLF */
2812  cf->crlf_mode = 1;
2813 
2814  /* Setup the pager */
2815  cf->no_pager = um->cli_no_pager;
2816 
2817  /* Default terminal dimensions, should the terminal
2818  * fail to provide any.
2819  */
2822 
2823  /* Send the telnet options */
2825  unix_vlib_cli_output_raw (cf, uf, charmode_option,
2826  ARRAY_LEN (charmode_option));
2827 
2828  /* In case the client doesn't negotiate terminal type, use
2829  * a timer to kick off the initial prompt. */
2830  timer_call (unix_cli_file_welcome_timer, cf_index, 1);
2831  }
2832 
2833  return error;
2834 }
2835 
2836 /** The system terminal has informed us that the window size
2837  * has changed.
2838  */
2839 static void
2841 {
2842  clib_file_main_t *fm = &file_main;
2845  cm->stdin_cli_file_index);
2847  struct winsize ws;
2848  (void) signum;
2849 
2850  /* Terminal resized, fetch the new size */
2851  if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
2852  {
2853  /* "Should never happen..." */
2854  clib_unix_warning ("TIOCGWINSZ");
2855  /* We can't trust ws.XXX... */
2856  return;
2857  }
2858 
2859  cf->width = ws.ws_col;
2862  if (cf->width == 0)
2864 
2865  cf->height = ws.ws_row;
2868  if (cf->height == 0)
2870 
2871  /* Reindex the pager buffer */
2873 
2874  /* Redraw the page */
2875  unix_cli_pager_redraw (cf, uf);
2876 }
2877 
2878 /** Handle configuration directives in the @em unix section. */
2879 static clib_error_t *
2881 {
2882  unix_main_t *um = &unix_main;
2883  clib_file_main_t *fm = &file_main;
2885  int flags;
2886  clib_error_t *error = 0;
2887  unix_cli_file_t *cf;
2888  u32 cf_index;
2889  struct termios tio;
2890  struct sigaction sa;
2891  struct winsize ws;
2892  u8 *term;
2893 
2894  /* We depend on unix flags being set. */
2895  if ((error = vlib_call_config_function (vm, unix_config)))
2896  return error;
2897 
2898  if (um->flags & UNIX_FLAG_INTERACTIVE)
2899  {
2900  /* Set stdin to be non-blocking. */
2901  if ((flags = fcntl (STDIN_FILENO, F_GETFL, 0)) < 0)
2902  flags = 0;
2903  (void) fcntl (STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
2904 
2905  cf_index = unix_cli_file_add (cm, "stdin", STDIN_FILENO);
2906  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2907  cm->stdin_cli_file_index = cf_index;
2908 
2909  /* If stdin is a tty and we are using chacracter mode, enable
2910  * history on the CLI and set the tty line discipline accordingly. */
2911  if (isatty (STDIN_FILENO) && um->cli_line_mode == 0)
2912  {
2913  /* Capture terminal resize events */
2914  memset (&sa, 0, sizeof (sa));
2915  sa.sa_handler = unix_cli_resize_interrupt;
2916  if (sigaction (SIGWINCH, &sa, 0) < 0)
2917  clib_panic ("sigaction");
2918 
2919  /* Retrieve the current terminal size */
2920  ioctl (STDIN_FILENO, TIOCGWINSZ, &ws);
2921  cf->width = ws.ws_col;
2922  cf->height = ws.ws_row;
2923 
2924  if (cf->width == 0 || cf->height == 0)
2925  {
2926  /*
2927  * We have a tty, but no size. Use defaults.
2928  * vpp "unix interactive" inside emacs + gdb ends up here.
2929  */
2932  }
2933 
2934  /* Setup the history */
2935  cf->history_limit = um->cli_history_limit;
2936  cf->has_history = cf->history_limit != 0;
2937 
2938  /* Setup the pager */
2939  cf->no_pager = um->cli_no_pager;
2940 
2941  /* This is an interactive session until we decide otherwise */
2942  cf->is_interactive = 1;
2943 
2944  /* We're going to be in char by char mode */
2945  cf->line_mode = 0;
2946 
2947  /* Save the original tty state so we can restore it later */
2948  tcgetattr (STDIN_FILENO, &um->tio_stdin);
2949  um->tio_isset = 1;
2950 
2951  /* Tweak the tty settings */
2952  tio = um->tio_stdin;
2953  /* echo off, canonical mode off, ext'd input processing off */
2954  tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
2955  /* disable XON/XOFF, so ^S invokes the history search */
2956  tio.c_iflag &= ~(IXON | IXOFF);
2957  tio.c_cc[VMIN] = 1; /* 1 byte at a time */
2958  tio.c_cc[VTIME] = 0; /* no timer */
2959  tio.c_cc[VSTOP] = _POSIX_VDISABLE; /* not ^S */
2960  tio.c_cc[VSTART] = _POSIX_VDISABLE; /* not ^Q */
2961  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio);
2962 
2963  /* See if we can do ANSI/VT100 output */
2964  term = (u8 *) getenv ("TERM");
2965  if (term != NULL)
2966  {
2967  int len = strlen ((char *) term);
2968  cf->ansi_capable = unix_cli_terminal_type_ansi (term, len);
2969  if (unix_cli_terminal_type_noninteractive (term, len))
2971  }
2972  }
2973  else
2974  {
2975  /* No tty, so make sure the session doesn't have tty-like features */
2977  }
2978 
2979  /* Send banner and initial prompt */
2980  unix_cli_file_welcome (cm, cf);
2981  }
2982 
2983  /* If we have socket config, LISTEN, otherwise, don't */
2984  clib_socket_t *s = &um->cli_listen_socket;
2985  if (s->config && s->config[0] != 0)
2986  {
2987  /* CLI listen. */
2988  clib_file_t template = { 0 };
2989 
2990  /* mkdir of file socketu, only under /run */
2991  if (strncmp (s->config, "/run", 4) == 0)
2992  {
2993  u8 *tmp = format (0, "%s", s->config);
2994  int i = vec_len (tmp);
2995  while (i && tmp[--i] != '/')
2996  ;
2997 
2998  tmp[i] = 0;
2999 
3000  if (i)
3001  vlib_unix_recursive_mkdir ((char *) tmp);
3002  vec_free (tmp);
3003  }
3004 
3005  s->flags = CLIB_SOCKET_F_IS_SERVER | /* listen, don't connect */
3006  CLIB_SOCKET_F_ALLOW_GROUP_WRITE; /* PF_LOCAL socket only */
3007  error = clib_socket_init (s);
3008 
3009  if (error)
3010  return error;
3011 
3012  template.read_function = unix_cli_listen_read_ready;
3013  template.file_descriptor = s->fd;
3014  template.description = format (0, "cli listener %s", s->config);
3015 
3016  clib_file_add (fm, &template);
3017  }
3018 
3019  /* Set CLI prompt. */
3020  if (!cm->cli_prompt)
3021  cm->cli_prompt = format (0, "VLIB: ");
3022 
3023  return 0;
3024 }
3025 
3026 /*?
3027  * This module has no configurable parameters.
3028 ?*/
3030 
3031 /** Called when VPP is shutting down, this restores the system
3032  * terminal state if previously saved.
3033  */
3034 static clib_error_t *
3036 {
3037  unix_main_t *um = &unix_main;
3038 
3039  /* If stdin is a tty and we saved the tty state, reset the tty state */
3040  if (isatty (STDIN_FILENO) && um->tio_isset)
3041  tcsetattr (STDIN_FILENO, TCSAFLUSH, &um->tio_stdin);
3042 
3043  return 0;
3044 }
3045 
3047 
3048 /** Set the CLI prompt.
3049  * @param prompt The C string to set the prompt to.
3050  * @note This setting is global; it impacts all current
3051  * and future CLI sessions.
3052  */
3053 void
3055 {
3056  char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s ";
3058  if (cm->cli_prompt)
3059  vec_free (cm->cli_prompt);
3060  cm->cli_prompt = format (0, fmt, prompt);
3061 }
3062 
3063 /** CLI command to quit the terminal session.
3064  * @note If this is a stdin session then this will
3065  * shutdown VPP also.
3066  */
3067 static clib_error_t *
3069  unformat_input_t * input, vlib_cli_command_t * cmd)
3070 {
3074 
3075  /* Cosmetic: suppress the final prompt from appearing before we die */
3076  cf->is_interactive = 0;
3077  cf->started = 1;
3078 
3080  vlib_current_process (vm),
3083  return 0;
3084 }
3085 
3086 /*?
3087  * Terminates the current CLI session.
3088  *
3089  * If VPP is running in @em interactive mode and this is the console session
3090  * (that is, the session on @c stdin) then this will also terminate VPP.
3091 ?*/
3092 /* *INDENT-OFF* */
3093 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
3094  .path = "quit",
3095  .short_help = "Exit CLI",
3096  .function = unix_cli_quit,
3097 };
3098 /* *INDENT-ON* */
3099 
3100 /* *INDENT-OFF* */
3101 VLIB_CLI_COMMAND (unix_cli_q_command, static) = {
3102  .path = "q",
3103  .short_help = "Exit CLI",
3104  .function = unix_cli_quit,
3105 };
3106 /* *INDENT-ON* */
3107 
3108 /** CLI command to execute a VPP command script. */
3109 static clib_error_t *
3111  unformat_input_t * input, vlib_cli_command_t * cmd)
3112 {
3113  char *file_name;
3114  int fd;
3115  unformat_input_t sub_input;
3116  clib_error_t *error;
3117 
3118  file_name = 0;
3119  fd = -1;
3120  error = 0;
3121 
3122  if (!unformat (input, "%s", &file_name))
3123  {
3124  error = clib_error_return (0, "expecting file name, got `%U'",
3125  format_unformat_error, input);
3126  goto done;
3127  }
3128 
3129  fd = open (file_name, O_RDONLY);
3130  if (fd < 0)
3131  {
3132  error = clib_error_return_unix (0, "failed to open `%s'", file_name);
3133  goto done;
3134  }
3135 
3136  /* Make sure its a regular file. */
3137  {
3138  struct stat s;
3139 
3140  if (fstat (fd, &s) < 0)
3141  {
3142  error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
3143  goto done;
3144  }
3145 
3146  if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
3147  {
3148  error = clib_error_return (0, "not a regular file `%s'", file_name);
3149  goto done;
3150  }
3151  }
3152 
3153  unformat_init_clib_file (&sub_input, fd);
3154 
3155  vlib_cli_input (vm, &sub_input, 0, 0);
3156  unformat_free (&sub_input);
3157 
3158 done:
3159  if (fd > 0)
3160  close (fd);
3161  vec_free (file_name);
3162 
3163  return error;
3164 }
3165 
3166 /*?
3167  * Executes a sequence of CLI commands which are read from a file. If
3168  * a command is unrecognised or otherwise invalid then the usual CLI
3169  * feedback will be generated, however execution of subsequent commands
3170  * from the file will continue.
3171  *
3172  * The VPP code is indifferent to the file location. However, if SELinux
3173  * is enabled, then the file needs to have an SELinux label the VPP
3174  * process is allowed to access. For example, if a file is created in
3175  * '<em>/usr/share/vpp/</em>', it will be allowed. However, files manually
3176  * created in '/tmp/' or '/home/<user>/' will not be accessible by the VPP
3177  * process when SELinux is enabled.
3178  *
3179  * @cliexpar
3180  * Sample file:
3181  * @clistart
3182  * <b><em>$ cat /usr/share/vpp/scripts/gigup.txt</em></b>
3183  * set interface state GigabitEthernet0/8/0 up
3184  * set interface state GigabitEthernet0/9/0 up
3185  * @cliend
3186  * Example of how to execute a set of CLI commands from a file:
3187  * @cliexcmd{exec /usr/share/vpp/scripts/gigup.txt}
3188 ?*/
3189 /* *INDENT-OFF* */
3190 VLIB_CLI_COMMAND (cli_exec, static) = {
3191  .path = "exec",
3192  .short_help = "exec <filename>",
3193  .function = unix_cli_exec,
3194  .is_mp_safe = 1,
3195 };
3196 /* *INDENT-ON* */
3197 
3198 /** CLI command to show various unix error statistics. */
3199 static clib_error_t *
3201  unformat_input_t * input, vlib_cli_command_t * cmd)
3202 {
3203  unix_main_t *um = &unix_main;
3204  clib_error_t *error = 0;
3205  int i, n_errors_to_show;
3206  unix_error_history_t *unix_errors = 0;
3207 
3208  n_errors_to_show = 1 << 30;
3209 
3211  {
3212  if (!unformat (input, "%d", &n_errors_to_show))
3213  {
3214  error =
3215  clib_error_return (0,
3216  "expecting integer number of errors to show, got `%U'",
3217  format_unformat_error, input);
3218  goto done;
3219  }
3220  }
3221 
3222  n_errors_to_show =
3223  clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
3224 
3225  i =
3226  um->error_history_index >
3227  0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
3228 
3229  while (n_errors_to_show > 0)
3230  {
3231  unix_error_history_t *eh = um->error_history + i;
3232 
3233  if (!eh->error)
3234  break;
3235 
3236  vec_add1 (unix_errors, eh[0]);
3237  n_errors_to_show -= 1;
3238  if (i == 0)
3239  i = ARRAY_LEN (um->error_history) - 1;
3240  else
3241  i--;
3242  }
3243 
3244  if (vec_len (unix_errors) == 0)
3245  vlib_cli_output (vm, "no Unix errors so far");
3246  else
3247  {
3248  vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
3249  for (i = vec_len (unix_errors) - 1; i >= 0; i--)
3250  {
3251  unix_error_history_t *eh = vec_elt_at_index (unix_errors, i);
3252  vlib_cli_output (vm, "%U: %U",
3253  format_time_interval, "h:m:s:u", eh->time,
3254  format_clib_error, eh->error);
3255  }
3256  vlib_cli_output (vm, "%U: time now",
3257  format_time_interval, "h:m:s:u", vlib_time_now (vm));
3258  }
3259 
3260 done:
3261  vec_free (unix_errors);
3262  return error;
3263 }
3264 
3265 /* *INDENT-OFF* */
3266 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
3267  .path = "show unix errors",
3268  .short_help = "Show Unix system call error history",
3269  .function = unix_show_errors,
3270 };
3271 /* *INDENT-ON* */
3272 
3273 /** CLI command to show various unix error statistics. */
3274 static clib_error_t *
3276  unformat_input_t * input, vlib_cli_command_t * cmd)
3277 {
3278  clib_error_t *error = 0;
3279  clib_file_main_t *fm = &file_main;
3280  clib_file_t *f;
3281  char path[PATH_MAX];
3282  u8 *s = 0;
3283 
3284  vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread",
3285  "Read", "Write", "Error", "File Name", "Description");
3286 
3287  /* *INDENT-OFF* */
3288  pool_foreach (f, fm->file_pool,(
3289  {
3290  int rv;
3291  s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0);
3292  rv = readlink((char *) s, path, PATH_MAX - 1);
3293 
3294  path[rv < 0 ? 0 : rv] = 0;
3295 
3296  vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v",
3297  f->file_descriptor, f->polling_thread_index,
3298  f->read_events, f->write_events, f->error_events,
3299  path, f->description);
3300  vec_reset_length (s);
3301  }));
3302  /* *INDENT-ON* */
3303  vec_free (s);
3304 
3305  return error;
3306 }
3307 
3308 /* *INDENT-OFF* */
3309 VLIB_CLI_COMMAND (cli_unix_show_files, static) = {
3310  .path = "show unix files",
3311  .short_help = "Show Unix files in use",
3312  .function = unix_show_files,
3313 };
3314 /* *INDENT-ON* */
3315 
3316 /** CLI command to show session command history. */
3317 static clib_error_t *
3319  unformat_input_t * input, vlib_cli_command_t * cmd)
3320 {
3322  unix_cli_file_t *cf;
3323  int i, j;
3324 
3326 
3327  if (!cf->is_interactive)
3328  return clib_error_return (0, "invalid for non-interactive sessions");
3329 
3330  if (cf->has_history && cf->history_limit)
3331  {
3332  i = 1 + cf->command_number - vec_len (cf->command_history);
3333  for (j = 0; j < vec_len (cf->command_history); j++)
3334  vlib_cli_output (vm, "%d %v\n", i + j, cf->command_history[j]);
3335  }
3336  else
3337  {
3338  vlib_cli_output (vm, "History not enabled.\n");
3339  }
3340 
3341  return 0;
3342 }
3343 
3344 /*?
3345  * Displays the command history for the current session, if any.
3346 ?*/
3347 /* *INDENT-OFF* */
3348 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
3349  .path = "history",
3350  .short_help = "Show current session command history",
3351  .function = unix_cli_show_history,
3352 };
3353 /* *INDENT-ON* */
3354 
3355 /** CLI command to show terminal status. */
3356 static clib_error_t *
3358  unformat_input_t * input, vlib_cli_command_t * cmd)
3359 {
3360  unix_main_t *um = &unix_main;
3362  unix_cli_file_t *cf;
3363  vlib_node_t *n;
3364 
3366  n = vlib_get_node (vm, cf->process_node_index);
3367 
3368  vlib_cli_output (vm, "Terminal name: %v\n", n->name);
3369  vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ?
3370  "line-by-line" : "char-by-char");
3371  vlib_cli_output (vm, "Terminal width: %d\n", cf->width);
3372  vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
3373  vlib_cli_output (vm, "ANSI capable: %s\n",
3374  cf->ansi_capable ? "yes" : "no");
3375  vlib_cli_output (vm, "Interactive: %s\n",
3376  cf->is_interactive ? "yes" : "no");
3377  vlib_cli_output (vm, "History enabled: %s%s\n",
3378  cf->has_history ? "yes" : "no", !cf->has_history
3379  || cf->history_limit ? "" :
3380  " (disabled by history limit)");
3381  if (cf->has_history)
3382  vlib_cli_output (vm, "History limit: %d\n", cf->history_limit);
3383  vlib_cli_output (vm, "Pager enabled: %s%s%s\n",
3384  cf->no_pager ? "no" : "yes",
3385  cf->no_pager
3386  || cf->height ? "" : " (disabled by terminal height)",
3387  cf->no_pager
3388  || um->cli_pager_buffer_limit ? "" :
3389  " (disabled by buffer limit)");
3390  if (!cf->no_pager)
3391  vlib_cli_output (vm, "Pager limit: %d\n", um->cli_pager_buffer_limit);
3392  vlib_cli_output (vm, "CRLF mode: %s\n",
3393  cf->crlf_mode ? "CR+LF" : "LF");
3394 
3395  return 0;
3396 }
3397 
3398 /*?
3399  * Displays various information about the state of the current terminal
3400  * session.
3401  *
3402  * @cliexpar
3403  * @cliexstart{show terminal}
3404  * Terminal name: unix-cli-stdin
3405  * Terminal mode: char-by-char
3406  * Terminal width: 123
3407  * Terminal height: 48
3408  * ANSI capable: yes
3409  * Interactive: yes
3410  * History enabled: yes
3411  * History limit: 50
3412  * Pager enabled: yes
3413  * Pager limit: 100000
3414  * CRLF mode: LF
3415  * @cliexend
3416 ?*/
3417 /* *INDENT-OFF* */
3418 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
3419  .path = "show terminal",
3420  .short_help = "Show current session terminal settings",
3421  .function = unix_cli_show_terminal,
3422 };
3423 /* *INDENT-ON* */
3424 
3425 /** CLI command to display a list of CLI sessions. */
3426 static clib_error_t *
3428  unformat_input_t * input,
3429  vlib_cli_command_t * cmd)
3430 {
3432  clib_file_main_t *fm = &file_main;
3433  unix_cli_file_t *cf;
3434  clib_file_t *uf;
3435  vlib_node_t *n;
3436 
3437  vlib_cli_output (vm, "%-5s %-5s %-20s %s", "PNI", "FD", "Name", "Flags");
3438 
3439 #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) )
3440  /* *INDENT-OFF* */
3441  pool_foreach (cf, cm->cli_file_pool, ({
3442  uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3443  n = vlib_get_node (vm, cf->process_node_index);
3444  vlib_cli_output (vm,
3445  "%-5d %-5d %-20v %c%c%c%c%c\n",
3446  cf->process_node_index,
3447  uf->file_descriptor,
3448  n->name,
3449  fl (cf->is_interactive, 'i'),
3450  fl (cf->is_socket, 's'),
3451  fl (cf->line_mode, 'l'),
3452  fl (cf->has_epipe, 'p'),
3453  fl (cf->ansi_capable, 'a'));
3454  }));
3455  /* *INDENT-ON* */
3456 #undef fl
3457 
3458  return 0;
3459 }
3460 
3461 /*?
3462  * Displays a summary of all the current CLI sessions.
3463  *
3464  * Typically used to diagnose connection issues with the CLI
3465  * socket.
3466  *
3467  * @cliexpar
3468  * @cliexstart{show cli-sessions}
3469  * PNI FD Name Flags
3470  * 343 0 unix-cli-stdin IslpA
3471  * 344 7 unix-cli-local:20 ISlpA
3472  * 346 8 unix-cli-local:21 iSLpa
3473  * @cliexend
3474 
3475  * In this example we have the debug console of the running process
3476  * on stdin/out, we have an interactive socket session and we also
3477  * have a non-interactive socket session.
3478  *
3479  * Fields:
3480  *
3481  * - @em PNI: Process node index.
3482  * - @em FD: Unix file descriptor.
3483  * - @em Name: Name of the session.
3484  * - @em Flags: Various flags that describe the state of the session.
3485  *
3486  * @em Flags have the following meanings; lower-case typically negates
3487  * upper-case:
3488  *
3489  * - @em I Interactive session.
3490  * - @em S Connected by socket.
3491  * - @em s Not a socket, likely stdin.
3492  * - @em L Line-by-line mode.
3493  * - @em l Char-by-char mode.
3494  * - @em P EPIPE detected on connection; it will close soon.
3495  * - @em A ANSI-capable terminal.
3496 ?*/
3497 /* *INDENT-OFF* */
3498 VLIB_CLI_COMMAND (cli_unix_cli_show_cli_sessions, static) = {
3499  .path = "show cli-sessions",
3500  .short_help = "Show current CLI sessions",
3501  .function = unix_cli_show_cli_sessions,
3502 };
3503 /* *INDENT-ON* */
3504 
3505 /** CLI command to set terminal pager settings. */
3506 static clib_error_t *
3508  unformat_input_t * input,
3509  vlib_cli_command_t * cmd)
3510 {
3511  unix_main_t *um = &unix_main;
3513  unix_cli_file_t *cf;
3514  unformat_input_t _line_input, *line_input = &_line_input;
3515  clib_error_t *error = 0;
3516 
3518 
3519  if (!cf->is_interactive)
3520  return clib_error_return (0, "invalid for non-interactive sessions");
3521 
3522  if (!unformat_user (input, unformat_line_input, line_input))
3523  return 0;
3524 
3525  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3526  {
3527  if (unformat (line_input, "on"))
3528  cf->no_pager = 0;
3529  else if (unformat (line_input, "off"))
3530  cf->no_pager = 1;
3531  else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
3532  vlib_cli_output (vm,
3533  "Pager limit set to %u lines; note, this is global.\n",
3535  else
3536  {
3537  error = clib_error_return (0, "unknown parameter: `%U`",
3538  format_unformat_error, line_input);
3539  goto done;
3540  }
3541  }
3542 
3543 done:
3544  unformat_free (line_input);
3545 
3546  return error;
3547 }
3548 
3549 /*?
3550  * Enables or disables the terminal pager for this session. Generally
3551  * this defaults to enabled.
3552  *
3553  * Additionally allows the pager buffer size to be set; though note that
3554  * this value is set globally and not per session.
3555 ?*/
3556 /* *INDENT-OFF* */
3557 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
3558  .path = "set terminal pager",
3559  .short_help = "set terminal pager [on|off] [limit <lines>]",
3560  .function = unix_cli_set_terminal_pager,
3561 };
3562 /* *INDENT-ON* */
3563 
3564 /** CLI command to set terminal history settings. */
3565 static clib_error_t *
3567  unformat_input_t * input,
3568  vlib_cli_command_t * cmd)
3569 {
3571  unix_cli_file_t *cf;
3572  unformat_input_t _line_input, *line_input = &_line_input;
3573  u32 limit;
3574  clib_error_t *error = 0;
3575 
3577 
3578  if (!cf->is_interactive)
3579  return clib_error_return (0, "invalid for non-interactive sessions");
3580 
3581  if (!unformat_user (input, unformat_line_input, line_input))
3582  return 0;
3583 
3584  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3585  {
3586  if (unformat (line_input, "on"))
3587  cf->has_history = 1;
3588  else if (unformat (line_input, "off"))
3589  cf->has_history = 0;
3590  else if (unformat (line_input, "limit %u", &cf->history_limit))
3591  ;
3592  else
3593  {
3594  error = clib_error_return (0, "unknown parameter: `%U`",
3595  format_unformat_error, line_input);
3596  goto done;
3597  }
3598 
3599  /* If we reduced history size, or turned it off, purge the history */
3600  limit = cf->has_history ? cf->history_limit : 0;
3601 
3602  while (cf->command_history && vec_len (cf->command_history) >= limit)
3603  {
3604  vec_free (cf->command_history[0]);
3605  vec_delete (cf->command_history, 1, 0);
3606  }
3607  }
3608 
3609 done:
3610  unformat_free (line_input);
3611 
3612  return error;
3613 }
3614 
3615 /*?
3616  * Enables or disables the command history function of the current
3617  * terminal. Generally this defaults to enabled.
3618  *
3619  * This command also allows the maximum size of the history buffer for
3620  * this session to be altered.
3621 ?*/
3622 /* *INDENT-OFF* */
3623 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
3624  .path = "set terminal history",
3625  .short_help = "set terminal history [on|off] [limit <lines>]",
3626  .function = unix_cli_set_terminal_history,
3627 };
3628 /* *INDENT-ON* */
3629 
3630 /** CLI command to set terminal ANSI settings. */
3631 static clib_error_t *
3633  unformat_input_t * input,
3634  vlib_cli_command_t * cmd)
3635 {
3637  unix_cli_file_t *cf;
3638 
3640 
3641  if (!cf->is_interactive)
3642  return clib_error_return (0, "invalid for non-interactive sessions");
3643 
3644  if (unformat (input, "on"))
3645  cf->ansi_capable = 1;
3646  else if (unformat (input, "off"))
3647  cf->ansi_capable = 0;
3648  else
3649  return clib_error_return (0, "unknown parameter: `%U`",
3650  format_unformat_error, input);
3651 
3652  return 0;
3653 }
3654 
3655 /*?
3656  * Enables or disables the use of ANSI control sequences by this terminal.
3657  * The default will vary based on terminal detection at the start of the
3658  * session.
3659  *
3660  * ANSI control sequences are used in a small number of places to provide,
3661  * for example, color text output and to control the cursor in the pager.
3662 ?*/
3663 /* *INDENT-OFF* */
3664 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
3665  .path = "set terminal ansi",
3666  .short_help = "set terminal ansi [on|off]",
3667  .function = unix_cli_set_terminal_ansi,
3668 };
3669 /* *INDENT-ON* */
3670 
3671 static clib_error_t *
3673 {
3674  return 0;
3675 }
3676 
3678 
3679 /*
3680  * fd.io coding-style-patch-verification: ON
3681  *
3682  * Local Variables:
3683  * eval: (c-set-style "gnu")
3684  * End:
3685  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:437
u32 history_limit
Maximum number of history entries this session will store.
Definition: cli.c:169
u32 command_number
Current command line counter.
Definition: cli.c:172
uword output_function_arg
Definition: node.h:589
u32 error_history_index
Definition: unix.h:68
unix_main_t unix_main
Definition: main.c:62
#define vec_foreach_index(var, v)
Iterate over vector indices.
u8 * format_clib_error(u8 *s, va_list *va)
Definition: error.c:191
static void clib_file_del(clib_file_main_t *um, clib_file_t *f)
Definition: file.h:109
static void unix_cli_ansi_cursor(unix_cli_file_t *cf, clib_file_t *uf, u16 x, u16 y)
Uses an ANSI escape sequence to move the cursor.
Definition: cli.c:837
#define UNIX_CLI_MAX_DEPTH_TELNET
Maximum depth into a byte stream from which to compile a Telnet protocol message. ...
Definition: cli.c:93
static clib_error_t * unix_cli_error_detected(clib_file_t *uf)
Called when a CLI session file descriptor has an error condition.
Definition: cli.c:2644
#define clib_min(x, y)
Definition: clib.h:289
u8 is_interactive
Whether the session is interactive or not.
Definition: cli.c:202
u32 pager_start
Line number of top of page.
Definition: cli.c:219
static void unix_vlib_cli_output_raw(unix_cli_file_t *cf, clib_file_t *uf, u8 *buffer, uword buffer_bytes)
Send a buffer to the CLI stream if possible, enqueue it otherwise.
Definition: cli.c:614
Action parser found a partial match.
Definition: cli.c:313
Carriage return, newline or enter.
Definition: cli.c:281
Telnet control code.
Definition: cli.c:299
Search forwards in command history.
Definition: cli.c:297
static u8 unix_cli_terminal_type_noninteractive(u8 *term, uword len)
Identify whether a terminal type is non-interactive.
Definition: cli.c:1169
unix_cli_process_event_type_t
CLI session events.
Definition: cli.c:447
a
Definition: bitmap.h:538
Exit the pager session.
Definition: cli.c:302
u8 * input_vector
Vector of input saved by Unix input node to be processed by CLI process.
Definition: cli.c:157
static void unix_cli_pager_message(unix_cli_file_t *cf, clib_file_t *uf, char *message, char *postfix)
Output a pager "skipping" message.
Definition: cli.c:799
static uword * vlib_process_wait_for_event(vlib_main_t *vm)
Definition: node_funcs.h:619
void vlib_cli_input(vlib_main_t *vm, unformat_input_t *input, vlib_cli_output_function_t *function, uword function_arg)
Definition: cli.c:644
static void unix_cli_del_pending_output(clib_file_t *uf, unix_cli_file_t *cf, uword n_bytes)
Delete all bytes from the output vector and flag the I/O system that no more bytes are available to b...
Definition: cli.c:557
static clib_error_t * unix_cli_show_terminal(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to show terminal status.
Definition: cli.c:3357
static uword vlib_current_process(vlib_main_t *vm)
Definition: node_funcs.h:426
#define CTL(c)
Given a capital ASCII letter character return a NUL terminated string with the control code for that ...
Definition: cli.c:337
static unix_cli_parse_action_t unix_cli_match_action(unix_cli_parse_actions_t *a, u8 *input, u32 ilen, i32 *matched)
Search for a byte sequence in the action list.
Definition: cli.c:499
u8 * line
The line to print.
Definition: cli.c:107
u32 flags
Definition: unix.h:58
u32 width
Terminal width.
Definition: cli.c:222
Erase cursor right.
Definition: cli.c:284
u8 * cli_prompt
Prompt string for CLI.
Definition: cli.c:457
#define UNIX_CLI_DEFAULT_TERMINAL_WIDTH
Default terminal width.
Definition: cli.c:102
static void unix_cli_set_session_noninteractive(unix_cli_file_t *cf)
Set a session to be non-interactive.
Definition: cli.c:1184
u32 cli_pager_buffer_limit
Definition: unix.h:97
static clib_error_t * unix_show_files(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to show various unix error statistics.
Definition: cli.c:3275
#define NULL
Definition: clib.h:55
u8 ** command_history
Array of vectors of commands in the history.
Definition: cli.c:162
static void unix_vlib_cli_output(uword cli_file_index, u8 *buffer, uword buffer_bytes)
VLIB CLI output function.
Definition: cli.c:1049
u32 index
Definition: node.h:273
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:228
void vlib_unix_cli_set_prompt(char *prompt)
Set the CLI prompt.
Definition: cli.c:3054
Scroll to last line.
Definition: cli.c:307
static void unix_cli_pager_redraw(unix_cli_file_t *cf, clib_file_t *uf)
Redraw the currently displayed page of text.
Definition: cli.c:853
struct termios tio_stdin
Definition: unix.h:103
static void unix_cli_file_free(unix_cli_file_t *f)
Release storage used by a CLI session.
Definition: cli.c:270
u32 file_descriptor
Definition: file.h:54
static u32 unix_cli_file_add(unix_cli_main_t *cm, char *name, int fd)
Store a new CLI session.
Definition: cli.c:2666
Scroll to first line.
Definition: cli.c:306
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:523
static clib_error_t * unix_cli_set_terminal_ansi(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to set terminal ANSI settings.
Definition: cli.c:3632
word any
Definition: types.h:139
Unix CLI session.
Definition: cli.c:147
unix_cli_pager_index_t * pager_index
Index of line fragments in the pager buffer.
Definition: cli.c:216
u8 * current_command
The command currently pointed at by the history cursor.
Definition: cli.c:164
int i
clib_error_t * clib_socket_init(clib_socket_t *s)
Definition: socket.c:376
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:983
#define VLIB_MAIN_LOOP_EXIT_CLI
Definition: main.h:98
#define vlib_call_config_function(vm, x)
Definition: init.h:261
void clib_longjmp(clib_longjmp_t *save, uword return_value)
static clib_error_t * unix_cli_listen_read_ready(clib_file_t *uf)
Telnet listening socket has a new connection.
Definition: cli.c:2755
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
#define UNIX_CLI_MAX_TERMINAL_WIDTH
Maximum terminal width we will accept.
Definition: cli.c:96
u32 cursor
Position of the insert cursor on the current input line.
Definition: cli.c:183
clib_socket_t cli_listen_socket
Definition: unix.h:64
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:228
vlib_main_t ** vlib_mains
Definition: buffer.c:303
unix_cli_parse_action_t action
Action to take when matched.
Definition: cli.c:325
unsigned char u8
Definition: types.h:56
static clib_error_t * unix_cli_init(vlib_main_t *vm)
Definition: cli.c:3672
void unformat_init_clib_file(unformat_input_t *input, int file_descriptor)
Definition: unformat.c:1058
int log_fd
Definition: unix.h:85
Clear the terminal.
Definition: cli.c:295
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
double f64
Definition: types.h:142
#define vlib_worker_thread_barrier_sync(X)
Definition: threads.h:212
#define vec_add(V, E, N)
Add N elements to end of vector V (no header, unspecified alignment)
Definition: vec.h:600
#define CLIB_SOCKET_F_IS_SERVER
Definition: socket.h:58
clib_file_t * file_pool
Definition: file.h:88
u8 has_epipe
If EPIPE has been detected, prevent further write-related activity on the descriptor.
Definition: cli.c:210
u8 ansi_capable
Can we do ANSI output?
Definition: cli.c:192
u8 * format_timeval(u8 *s, va_list *args)
Definition: unix-formats.c:762
static void unix_cli_pager_reindex(unix_cli_file_t *cf)
Reindex entire pager buffer.
Definition: cli.c:976
A CLI session wants to close.
Definition: cli.c:450
#define pool_foreach(VAR, POOL, BODY)
Iterate through pool.
Definition: pool.h:443
i64 word
Definition: types.h:111
void vlib_worker_thread_node_runtime_update(void)
Definition: threads.c:1230
u8 * output_vector
Vector of output pending write to file descriptor.
Definition: cli.c:153
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:156
static uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Return the first event type which has occurred and a vector of per-event data of that type...
Definition: node_funcs.h:542
#define always_inline
Definition: clib.h:92
End key (jump to end of line)
Definition: cli.c:290
#define vec_new(T, N)
Create new vector of given type and length (unspecified alignment, no header).
Definition: vec.h:309
u8 is_socket
Whether the session is attached to a socket.
Definition: cli.c:205
Jump cursor to start of right word.
Definition: cli.c:292
Undo last erase action.
Definition: cli.c:298
u32 len
Length of input without final NUL.
Definition: cli.c:324
Mapping of input buffer strings to action values.
Definition: cli.c:321
vlib_parse_match_t vlib_parse_eval(u8 *input)
#define UNIX_CLI_DEFAULT_TERMINAL_HEIGHT
Default terminal height.
Definition: cli.c:100
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
#define clib_error_return(e, args...)
Definition: error.h:99
#define ANSI_SCROLLDN
ANSI scroll screen down one line.
Definition: cli.c:85
clib_file_main_t file_main
Definition: main.c:63
u32 process_node_index
Process node identifier.
Definition: cli.c:228
#define vec_resize(V, N)
Resize a vector (no header, unspecified alignment) Add N elements to end of given vector V...
Definition: vec.h:240
u32 offset
Offset of the string in the line.
Definition: cli.c:139
unsigned int u32
Definition: types.h:88
u32 length
Length of the string in the line.
Definition: cli.c:142
vlib_main_t * vlib_main
Definition: unix.h:56
Erase line to right & including cursor.
Definition: cli.c:294
#define ANSI_CLEARLINE
ANSI clear line cursor is on.
Definition: cli.c:83
static void unix_vlib_cli_output_cursor_left(unix_cli_file_t *cf, clib_file_t *uf)
Moves the terminal cursor one character to the left, with special handling when it reaches the left e...
Definition: cli.c:718
static i32 unix_cli_process_telnet(unix_main_t *um, unix_cli_file_t *cf, clib_file_t *uf, u8 *input_vector, uword len)
A mostly no-op Telnet state machine.
Definition: cli.c:1270
u32 * unused_cli_process_node_indices
Vec pool of unused session indices.
Definition: cli.c:463
static clib_error_t * unix_cli_quit(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to quit the terminal session.
Definition: cli.c:3068
static void unix_cli_pager_prompt(unix_cli_file_t *cf, clib_file_t *uf)
Output a pager prompt and show number of buffered lines.
Definition: cli.c:776
unformat_function_t unformat_line_input
Definition: format.h:282
unix_cli_file_t * cli_file_pool
Vec pool of CLI sessions.
Definition: cli.c:460
int search_mode
If non-zero then the CLI is searching in the history array.
Definition: cli.c:180
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:464
#define ANSI_RESTCURSOR
ANSI restore cursor position if previously saved.
Definition: cli.c:89
int cli_line_mode
Definition: unix.h:88
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:952
clib_error_t * clib_socket_accept(clib_socket_t *server, clib_socket_t *client)
Definition: socket.c:514
static void unix_cli_pager_reset(unix_cli_file_t *f)
Resets the pager buffer and other data.
Definition: cli.c:249
Scroll to previous page.
Definition: cli.c:309
u8 ** vlib_cli_get_possible_completions(u8 *str)
Definition: cli.c:250
struct _unformat_input_t unformat_input_t
unsigned short u16
Definition: types.h:57
#define clib_error_return_unix(e, args...)
Definition: error.h:102
static u8 unix_cli_terminal_type_ansi(u8 *term, uword len)
Identify whether a terminal type is ANSI capable.
Definition: cli.c:1137
int cli_no_banner
Definition: unix.h:94
Home key (jump to start of line)
Definition: cli.c:289
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:274
static clib_error_t * unix_cli_read_ready(clib_file_t *uf)
Called when a CLI session file descriptor has data to be read.
Definition: cli.c:2605
#define ANSI_BOLD
ANSI Start bold text.
Definition: cli.c:75
static unix_cli_banner_t unix_cli_banner[]
Plain welcome banner.
Definition: cli.c:113
u32 flags
Definition: file.h:56
void timer_call(timer_func_t *func, any arg, f64 dt)
Definition: timer.c:159
static clib_error_t * unix_show_errors(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to show various unix error statistics.
Definition: cli.c:3200
#define VLIB_CONFIG_FUNCTION(x, n,...)
Definition: init.h:164
Search backwards in command history.
Definition: cli.c:296
u8 * input
Input string to match.
Definition: cli.c:323
void(* file_update)(clib_file_t *file, clib_file_update_type_t update_type)
Definition: file.h:90
u32 node_index
Node index.
Definition: node.h:473
static void unix_cli_cli_prompt(unix_cli_file_t *cf, clib_file_t *uf)
Output the CLI prompt.
Definition: cli.c:765
u32 cli_history_limit
Definition: unix.h:91
Scroll to next page.
Definition: cli.c:303
u32 flags
Definition: vhost_user.h:110
Erase line to left of cursor.
Definition: cli.c:293
u8 * name
Definition: node.h:257
u8 * format_sockaddr(u8 *s, va_list *args)
Definition: unix-formats.c:231
#define CSI
ANSI Control Sequence Introducer.
Definition: cli.c:68
void unformat_init_vector(unformat_input_t *input, u8 *vector_string)
Definition: unformat.c:1031
u8 ** pager_vector
Pager buffer.
Definition: cli.c:213
#define UNFORMAT_END_OF_INPUT
Definition: format.h:144
unix_error_history_t error_history[128]
Definition: unix.h:67
svmdb_client_t * c
u32 runtime_index
Definition: node.h:276
vlib_main_t * vm
Definition: buffer.c:294
static void unix_vlib_cli_output_cooked(unix_cli_file_t *cf, clib_file_t *uf, u8 *buffer, uword buffer_bytes)
Process a buffer for CRLF handling before outputting it to the CLI.
Definition: cli.c:665
static unix_cli_banner_t unix_cli_banner_color[]
ANSI color welcome banner.
Definition: cli.c:122
u32 current_input_file_index
File pool index of current input.
Definition: cli.c:469
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:339
int cli_no_pager
Definition: unix.h:100
#define VLIB_MAIN_LOOP_EXIT_FUNCTION(x)
Definition: init.h:161
static clib_error_t * unix_cli_write_ready(clib_file_t *uf)
Called when a CLI session file descriptor can be written to without blocking.
Definition: cli.c:2563
#define ANSI_SAVECURSOR
ANSI save cursor position.
Definition: cli.c:87
#define clib_memcpy(a, b, c)
Definition: string.h:75
static clib_error_t * unix_cli_set_terminal_history(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to set terminal history settings.
Definition: cli.c:3566
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:271
u32 line
Index into pager_vector.
Definition: cli.c:136
Down arrow.
Definition: cli.c:286
#define ARRAY_LEN(x)
Definition: clib.h:59
A file descriptor has data to be read.
Definition: cli.c:449
#define ESC
ANSI escape code.
Definition: cli.c:65
Jump cursor to start of left word.
Definition: cli.c:291
u8 no_pager
Disable the pager?
Definition: cli.c:198
int tio_isset
Definition: unix.h:104
Right arrow.
Definition: cli.c:288
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
Enter pressed (CR, CRLF, LF, etc)
Definition: cli.c:301
signed int i32
Definition: types.h:81
Up arrow.
Definition: cli.c:285
static void unix_cli_kill(unix_cli_main_t *cm, uword cli_file_index)
Destroy a CLI session.
Definition: cli.c:2484
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:786
static word unix_vlib_findchr(u8 chr, u8 *str, word len)
A bit like strchr with a buffer length limit.
Definition: cli.c:587
unix_cli_parse_action_t
CLI actions.
Definition: cli.c:278
static uword clib_file_add(clib_file_main_t *um, clib_file_t *template)
Definition: file.h:96
u32 vlib_register_node(vlib_main_t *vm, vlib_node_registration_t *r)
Definition: node.c:518
#define UNIX_FLAG_INTERACTIVE
Definition: unix.h:60
Erase cursor left.
Definition: cli.c:283
static clib_error_t * unix_cli_set_terminal_pager(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to set terminal pager settings.
Definition: cli.c:3507
static clib_error_t * unix_cli_config(vlib_main_t *vm, unformat_input_t *input)
Handle configuration directives in the unix section.
Definition: cli.c:2880
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:820
struct _socket_t clib_socket_t
static int unix_cli_line_process_one(unix_cli_main_t *cm, unix_main_t *um, unix_cli_file_t *cf, clib_file_t *uf, u8 input, unix_cli_parse_action_t action)
Process actionable input.
Definition: cli.c:1416
#define UNIX_FILE_DATA_AVAILABLE_TO_WRITE
Definition: file.h:57
Scroll to next page.
Definition: cli.c:308
static void vlib_node_set_state(vlib_main_t *vm, u32 node_index, vlib_node_state_t new_state)
Set node dispatch state.
Definition: node_funcs.h:147
u8 has_history
This session has command history.
Definition: cli.c:160
A CLI banner line.
Definition: cli.c:105
#define ANSI_CLEAR
ANSI clear screen.
Definition: cli.c:71
static clib_error_t * unix_cli_show_history(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to show session command history.
Definition: cli.c:3318
static void clib_socket_free(clib_socket_t *s)
Definition: socket.h:165
Pager line index.
Definition: cli.c:133
Action parser did not find any match.
Definition: cli.c:314
u8 started
Has the session started?
Definition: cli.c:195
struct _vlib_node_registration vlib_node_registration_t
clib_error_t * vlib_unix_recursive_mkdir(char *path)
Definition: util.c:103
Scroll to next line.
Definition: cli.c:304
clib_error_t * error
Definition: unix.h:50
CLI global state.
Definition: cli.c:454
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
#define ANSI_BRED
ANSI Start bright red text.
Definition: cli.c:81
u8 * search_key
The string being searched for in the history.
Definition: cli.c:175
u64 uword
Definition: types.h:112
static void unformat_free(unformat_input_t *i)
Definition: format.h:162
#define clib_unix_warning(format, args...)
Definition: error.h:68
u8 * format_time_interval(u8 *s, va_list *args)
Definition: std-formats.c:122
static uword unix_cli_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Handle system events.
Definition: cli.c:2519
static void unix_cli_file_welcome(unix_cli_main_t *cm, unix_cli_file_t *cf)
Emit initial welcome banner and prompt on a connection.
Definition: cli.c:1196
static clib_error_t * unix_config(vlib_main_t *vm, unformat_input_t *input)
Definition: main.c:353
struct clib_bihash_value offset
template key/value backing page structure
static vlib_process_t * vlib_get_process_from_node(vlib_main_t *vm, vlib_node_t *node)
Definition: node_funcs.h:207
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
void vlib_worker_thread_barrier_release(vlib_main_t *vm)
Definition: threads.c:1545
static void unix_cli_pager_prompt_erase(unix_cli_file_t *cf, clib_file_t *uf)
Erase the printed pager prompt.
Definition: cli.c:815
static void unix_cli_resize_interrupt(int signum)
The system terminal has informed us that the window size has changed.
Definition: cli.c:2840
Clear and redraw the page on the terminal.
Definition: cli.c:310
static int unix_cli_line_edit(unix_cli_main_t *cm, unix_main_t *um, clib_file_main_t *fm, unix_cli_file_t *cf)
Process input bytes on a stream to provide line editing and command history in the CLI...
Definition: cli.c:2280
static vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Get vlib node by index.
Definition: node_funcs.h:59
#define vec_foreach(var, vec)
Vector iterator.
uword private_data
Definition: file.h:64
u8 cursor_direction
The current direction of cursor travel.
Definition: cli.c:241
u8 crlf_mode
Set if the CRLF mode wants CR + LF.
Definition: cli.c:189
static clib_error_t * unix_cli_exec(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to execute a VPP command script.
Definition: cli.c:3110
Definition: file.h:51
#define ANSI_RESET
ANSI reset color settings.
Definition: cli.c:73
clib_longjmp_t main_loop_exit
Definition: main.h:94
vlib_cli_output_function_t * output_function
Definition: node.h:588
u32 clib_file_index
The file index held by unix.c.
Definition: cli.c:150
static unix_cli_main_t unix_cli_main
CLI global state.
Definition: cli.c:473
#define CLIB_SOCKET_F_ALLOW_GROUP_WRITE
Definition: socket.h:62
i32 excursion
How far from the end of the history array the user has browsed.
Definition: cli.c:166
void vlib_start_process(vlib_main_t *vm, uword process_index)
Definition: main.c:1346
Search the pager buffer.
Definition: cli.c:311
u8 line_mode
Line mode or char mode.
Definition: cli.c:186
u32 length
The length of the line without terminating NUL.
Definition: cli.c:108
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:681
Left arrow.
Definition: cli.c:287
u32 stdin_cli_file_index
The session index of the stdin cli.
Definition: cli.c:466
static void unix_cli_add_pending_output(clib_file_t *uf, unix_cli_file_t *cf, u8 *buffer, uword buffer_bytes)
Add bytes to the output vector and then flagg the I/O system that bytes are available to be sent...
Definition: cli.c:537
static clib_error_t * unix_cli_show_cli_sessions(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
CLI command to display a list of CLI sessions.
Definition: cli.c:3427
static void unix_cli_pager_add_line(unix_cli_file_t *cf, u8 *line, word len_or_index)
Process and add a line to the pager index.
Definition: cli.c:914
#define clib_panic(format, args...)
Definition: error.h:72
#define UNIX_CLI_MAX_TERMINAL_HEIGHT
Maximum terminal height we will accept.
Definition: cli.c:98
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
Scroll to previous line.
Definition: cli.c:305
u64 n_total_errors
Definition: unix.h:69
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:170
u32 height
Terminal height.
Definition: cli.c:225
static clib_error_t * unix_cli_exit(vlib_main_t *vm)
Called when VPP is shutting down, this restores the system terminal state if previously saved...
Definition: cli.c:3035
static void unix_cli_file_welcome_timer(any arg, f64 delay)
A failsafe triggered on a timer to ensure we send the prompt to telnet sessions that fail to negotiat...
Definition: cli.c:1245
static void unix_cli_process_input(unix_cli_main_t *cm, uword cli_file_index)
Process input to a CLI session.
Definition: cli.c:2369
static unix_cli_parse_actions_t unix_cli_parse_strings[]
Patterns to match on a CLI input stream.
Definition: cli.c:344