FD.io VPP  v18.07-rc0-415-g6c78436
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  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2494 
2495  /* Quit/EOF on stdin means quit program. */
2496  if (uf->file_descriptor == STDIN_FILENO)
2498 
2499  vec_free (cf->current_command);
2500  vec_free (cf->search_key);
2501 
2502  for (i = 0; i < vec_len (cf->command_history); i++)
2503  vec_free (cf->command_history[i]);
2504 
2505  vec_free (cf->command_history);
2506 
2507  clib_file_del (fm, uf);
2508 
2509  unix_cli_file_free (cf);
2510  pool_put (cm->cli_file_pool, cf);
2511 }
2512 
2513 /** Handle system events. */
2514 static uword
2517 {
2519  uword i, *data = 0;
2520 
2521  while (1)
2522  {
2523  unix_cli_process_event_type_t event_type;
2525  event_type = vlib_process_get_events (vm, &data);
2526 
2527  switch (event_type)
2528  {
2530  for (i = 0; i < vec_len (data); i++)
2531  unix_cli_process_input (cm, data[i]);
2532  break;
2533 
2535  /* Kill this process. */
2536  for (i = 0; i < vec_len (data); i++)
2537  unix_cli_kill (cm, data[i]);
2538  goto done;
2539  }
2540 
2541  if (data)
2542  _vec_len (data) = 0;
2543  }
2544 
2545 done:
2546  vec_free (data);
2547 
2548  vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
2549 
2550  /* Add node index so we can re-use this process later. */
2552 
2553  return 0;
2554 }
2555 
2556 /** Called when a CLI session file descriptor can be written to without
2557  * blocking. */
2558 static clib_error_t *
2560 {
2562  unix_cli_file_t *cf;
2563  int n;
2564 
2566 
2567  /* Flush output vector. */
2568  if (cf->is_socket)
2569  /* If it's a socket we use MSG_NOSIGNAL to prevent SIGPIPE */
2570  n = send (uf->file_descriptor,
2571  cf->output_vector, vec_len (cf->output_vector), MSG_NOSIGNAL);
2572  else
2573  n = write (uf->file_descriptor,
2574  cf->output_vector, vec_len (cf->output_vector));
2575 
2576  if (n < 0 && errno != EAGAIN)
2577  {
2578  if (errno == EPIPE)
2579  {
2580  /* connection closed on us */
2581  unix_main_t *um = &unix_main;
2582  cf->has_epipe = 1;
2585  uf->private_data);
2586  }
2587  else
2588  {
2589  return clib_error_return_unix (0, "write");
2590  }
2591  }
2592 
2593  else if (n > 0)
2594  unix_cli_del_pending_output (uf, cf, n);
2595 
2596  return /* no error */ 0;
2597 }
2598 
2599 /** Called when a CLI session file descriptor has data to be read. */
2600 static clib_error_t *
2602 {
2603  unix_main_t *um = &unix_main;
2605  unix_cli_file_t *cf;
2606  uword l;
2607  int n, n_read, n_try;
2608 
2610 
2611  n = n_try = 4096;
2612  while (n == n_try)
2613  {
2614  l = vec_len (cf->input_vector);
2615  vec_resize (cf->input_vector, l + n_try);
2616 
2617  n = read (uf->file_descriptor, cf->input_vector + l, n_try);
2618 
2619  /* Error? */
2620  if (n < 0 && errno != EAGAIN)
2621  return clib_error_return_unix (0, "read");
2622 
2623  n_read = n < 0 ? 0 : n;
2624  _vec_len (cf->input_vector) = l + n_read;
2625  }
2626 
2627  if (!(n < 0))
2629  cf->process_node_index,
2630  (n_read == 0
2633  /* event data */ uf->private_data);
2634 
2635  return /* no error */ 0;
2636 }
2637 
2638 /** Called when a CLI session file descriptor has an error condition. */
2639 static clib_error_t *
2641 {
2642  unix_main_t *um = &unix_main;
2644  unix_cli_file_t *cf;
2645 
2647  cf->has_epipe = 1; /* prevent writes while the close is pending */
2649  cf->process_node_index,
2651  /* event data */ uf->private_data);
2652 
2653  return /* no error */ 0;
2654 }
2655 
2656 /** Store a new CLI session.
2657  * @param name The name of the session.
2658  * @param fd The file descriptor for the session I/O.
2659  * @return The session ID.
2660  */
2661 static u32
2662 unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
2663 {
2664  unix_main_t *um = &unix_main;
2665  clib_file_main_t *fm = &file_main;
2666  unix_cli_file_t *cf;
2667  clib_file_t template = { 0 };
2668  vlib_main_t *vm = um->vlib_main;
2669  vlib_node_t *n;
2670  u8 *file_desc = 0;
2671 
2672  file_desc = format (0, "%s", name);
2673 
2674  name = (char *) format (0, "unix-cli-%s", name);
2675 
2677  {
2679 
2680  /* Find node and give it new name. */
2681  n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);
2682  vec_free (n->name);
2683  n->name = (u8 *) name;
2684 
2685  vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
2686 
2687  _vec_len (cm->unused_cli_process_node_indices) = l - 1;
2688  }
2689  else
2690  {
2691  static vlib_node_registration_t r = {
2692  .function = unix_cli_process,
2693  .type = VLIB_NODE_TYPE_PROCESS,
2694  .process_log2_n_stack_bytes = 16,
2695  };
2696 
2697  r.name = name;
2698  vlib_register_node (vm, &r);
2699  vec_free (name);
2700 
2701  n = vlib_get_node (vm, r.index);
2702  }
2703 
2704  pool_get (cm->cli_file_pool, cf);
2705  memset (cf, 0, sizeof (*cf));
2706 
2707  template.read_function = unix_cli_read_ready;
2708  template.write_function = unix_cli_write_ready;
2709  template.error_function = unix_cli_error_detected;
2710  template.file_descriptor = fd;
2711  template.private_data = cf - cm->cli_file_pool;
2712  template.description = file_desc;
2713 
2714  cf->process_node_index = n->index;
2715  cf->clib_file_index = clib_file_add (fm, &template);
2716  cf->output_vector = 0;
2717  cf->input_vector = 0;
2718 
2720 
2723  p->output_function_arg = cf - cm->cli_file_pool;
2724 
2725  return cf - cm->cli_file_pool;
2726 }
2727 
2728 /** Telnet listening socket has a new connection. */
2729 static clib_error_t *
2731 {
2732  unix_main_t *um = &unix_main;
2733  clib_file_main_t *fm = &file_main;
2735  clib_socket_t *s = &um->cli_listen_socket;
2736  clib_socket_t client;
2737  char *client_name;
2738  clib_error_t *error;
2739  unix_cli_file_t *cf;
2740  u32 cf_index;
2741 
2742  error = clib_socket_accept (s, &client);
2743  if (error)
2744  return error;
2745 
2746  client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
2747 
2748  cf_index = unix_cli_file_add (cm, client_name, client.fd);
2749  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2750  cf->is_socket = 1;
2751 
2752  /* No longer need CLIB version of socket. */
2753  clib_socket_free (&client);
2754  vec_free (client_name);
2755 
2756  /* if we're supposed to run telnet session in character mode (default) */
2757  if (um->cli_line_mode == 0)
2758  {
2759  /*
2760  * Set telnet client character mode, echo on, suppress "go-ahead".
2761  * Technically these should be negotiated, but this works.
2762  */
2763  u8 charmode_option[] = {
2764  IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */
2765  IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */
2766  IAC, WILL, TELOPT_SGA, /* server willl supress GA */
2767  IAC, DO, TELOPT_SGA, /* client should supress Go Ahead */
2768  IAC, WILL, TELOPT_ECHO, /* server will do echo */
2769  IAC, DONT, TELOPT_ECHO, /* client should not echo */
2770  IAC, DO, TELOPT_TTYPE, /* client should tell us its term type */
2771  IAC, SB, TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */
2772  IAC, DO, TELOPT_NAWS, /* client should tell us its window sz */
2773  IAC, SB, TELOPT_NAWS, 1, IAC, SE, /* now tell me window size */
2774  };
2775 
2776  /* Enable history on this CLI */
2777  cf->history_limit = um->cli_history_limit;
2778  cf->has_history = cf->history_limit != 0;
2779 
2780  /* This is an interactive session until we decide otherwise */
2781  cf->is_interactive = 1;
2782 
2783  /* Make sure this session is in line mode */
2784  cf->line_mode = 0;
2785 
2786  /* We need CRLF */
2787  cf->crlf_mode = 1;
2788 
2789  /* Setup the pager */
2790  cf->no_pager = um->cli_no_pager;
2791 
2792  /* Default terminal dimensions, should the terminal
2793  * fail to provide any.
2794  */
2797 
2798  /* Send the telnet options */
2800  unix_vlib_cli_output_raw (cf, uf, charmode_option,
2801  ARRAY_LEN (charmode_option));
2802 
2803  /* In case the client doesn't negotiate terminal type, use
2804  * a timer to kick off the initial prompt. */
2805  timer_call (unix_cli_file_welcome_timer, cf_index, 1);
2806  }
2807 
2808  return error;
2809 }
2810 
2811 /** The system terminal has informed us that the window size
2812  * has changed.
2813  */
2814 static void
2816 {
2817  clib_file_main_t *fm = &file_main;
2820  cm->stdin_cli_file_index);
2822  struct winsize ws;
2823  (void) signum;
2824 
2825  /* Terminal resized, fetch the new size */
2826  if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
2827  {
2828  /* "Should never happen..." */
2829  clib_unix_warning ("TIOCGWINSZ");
2830  /* We can't trust ws.XXX... */
2831  return;
2832  }
2833 
2834  cf->width = ws.ws_col;
2837  if (cf->width == 0)
2839 
2840  cf->height = ws.ws_row;
2843  if (cf->height == 0)
2845 
2846  /* Reindex the pager buffer */
2848 
2849  /* Redraw the page */
2850  unix_cli_pager_redraw (cf, uf);
2851 }
2852 
2853 /** Handle configuration directives in the @em unix section. */
2854 static clib_error_t *
2856 {
2857  unix_main_t *um = &unix_main;
2858  clib_file_main_t *fm = &file_main;
2860  int flags;
2861  clib_error_t *error = 0;
2862  unix_cli_file_t *cf;
2863  u32 cf_index;
2864  struct termios tio;
2865  struct sigaction sa;
2866  struct winsize ws;
2867  u8 *term;
2868 
2869  /* We depend on unix flags being set. */
2870  if ((error = vlib_call_config_function (vm, unix_config)))
2871  return error;
2872 
2873  if (um->flags & UNIX_FLAG_INTERACTIVE)
2874  {
2875  /* Set stdin to be non-blocking. */
2876  if ((flags = fcntl (STDIN_FILENO, F_GETFL, 0)) < 0)
2877  flags = 0;
2878  (void) fcntl (STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
2879 
2880  cf_index = unix_cli_file_add (cm, "stdin", STDIN_FILENO);
2881  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2882  cm->stdin_cli_file_index = cf_index;
2883 
2884  /* If stdin is a tty and we are using chacracter mode, enable
2885  * history on the CLI and set the tty line discipline accordingly. */
2886  if (isatty (STDIN_FILENO) && um->cli_line_mode == 0)
2887  {
2888  /* Capture terminal resize events */
2889  memset (&sa, 0, sizeof (sa));
2890  sa.sa_handler = unix_cli_resize_interrupt;
2891  if (sigaction (SIGWINCH, &sa, 0) < 0)
2892  clib_panic ("sigaction");
2893 
2894  /* Retrieve the current terminal size */
2895  ioctl (STDIN_FILENO, TIOCGWINSZ, &ws);
2896  cf->width = ws.ws_col;
2897  cf->height = ws.ws_row;
2898 
2899  if (cf->width == 0 || cf->height == 0)
2900  {
2901  /*
2902  * We have a tty, but no size. Use defaults.
2903  * vpp "unix interactive" inside emacs + gdb ends up here.
2904  */
2907  }
2908 
2909  /* Setup the history */
2910  cf->history_limit = um->cli_history_limit;
2911  cf->has_history = cf->history_limit != 0;
2912 
2913  /* Setup the pager */
2914  cf->no_pager = um->cli_no_pager;
2915 
2916  /* This is an interactive session until we decide otherwise */
2917  cf->is_interactive = 1;
2918 
2919  /* We're going to be in char by char mode */
2920  cf->line_mode = 0;
2921 
2922  /* Save the original tty state so we can restore it later */
2923  tcgetattr (STDIN_FILENO, &um->tio_stdin);
2924  um->tio_isset = 1;
2925 
2926  /* Tweak the tty settings */
2927  tio = um->tio_stdin;
2928  /* echo off, canonical mode off, ext'd input processing off */
2929  tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
2930  /* disable XON/XOFF, so ^S invokes the history search */
2931  tio.c_iflag &= ~(IXON | IXOFF);
2932  tio.c_cc[VMIN] = 1; /* 1 byte at a time */
2933  tio.c_cc[VTIME] = 0; /* no timer */
2934  tio.c_cc[VSTOP] = _POSIX_VDISABLE; /* not ^S */
2935  tio.c_cc[VSTART] = _POSIX_VDISABLE; /* not ^Q */
2936  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio);
2937 
2938  /* See if we can do ANSI/VT100 output */
2939  term = (u8 *) getenv ("TERM");
2940  if (term != NULL)
2941  {
2942  int len = strlen ((char *) term);
2943  cf->ansi_capable = unix_cli_terminal_type_ansi (term, len);
2944  if (unix_cli_terminal_type_noninteractive (term, len))
2946  }
2947  }
2948  else
2949  {
2950  /* No tty, so make sure the session doesn't have tty-like features */
2952  }
2953 
2954  /* Send banner and initial prompt */
2955  unix_cli_file_welcome (cm, cf);
2956  }
2957 
2958  /* If we have socket config, LISTEN, otherwise, don't */
2959  clib_socket_t *s = &um->cli_listen_socket;
2960  if (s->config && s->config[0] != 0)
2961  {
2962  /* CLI listen. */
2963  clib_file_t template = { 0 };
2964 
2965  /* mkdir of file socketu, only under /run */
2966  if (strncmp (s->config, "/run", 4) == 0)
2967  {
2968  u8 *tmp = format (0, "%s", s->config);
2969  int i = vec_len (tmp);
2970  while (i && tmp[--i] != '/')
2971  ;
2972 
2973  tmp[i] = 0;
2974 
2975  if (i)
2976  vlib_unix_recursive_mkdir ((char *) tmp);
2977  vec_free (tmp);
2978  }
2979 
2980  s->flags = CLIB_SOCKET_F_IS_SERVER | /* listen, don't connect */
2981  CLIB_SOCKET_F_ALLOW_GROUP_WRITE; /* PF_LOCAL socket only */
2982  error = clib_socket_init (s);
2983 
2984  if (error)
2985  return error;
2986 
2987  template.read_function = unix_cli_listen_read_ready;
2988  template.file_descriptor = s->fd;
2989  template.description = format (0, "cli listener %s", s->config);
2990 
2991  clib_file_add (fm, &template);
2992  }
2993 
2994  /* Set CLI prompt. */
2995  if (!cm->cli_prompt)
2996  cm->cli_prompt = format (0, "VLIB: ");
2997 
2998  return 0;
2999 }
3000 
3001 /*?
3002  * This module has no configurable parameters.
3003 ?*/
3005 
3006 /** Called when VPP is shutting down, this restores the system
3007  * terminal state if previously saved.
3008  */
3009 static clib_error_t *
3011 {
3012  unix_main_t *um = &unix_main;
3013 
3014  /* If stdin is a tty and we saved the tty state, reset the tty state */
3015  if (isatty (STDIN_FILENO) && um->tio_isset)
3016  tcsetattr (STDIN_FILENO, TCSAFLUSH, &um->tio_stdin);
3017 
3018  return 0;
3019 }
3020 
3022 
3023 /** Set the CLI prompt.
3024  * @param prompt The C string to set the prompt to.
3025  * @note This setting is global; it impacts all current
3026  * and future CLI sessions.
3027  */
3028 void
3030 {
3031  char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s ";
3033  if (cm->cli_prompt)
3034  vec_free (cm->cli_prompt);
3035  cm->cli_prompt = format (0, fmt, prompt);
3036 }
3037 
3038 /** CLI command to quit the terminal session.
3039  * @note If this is a stdin session then this will
3040  * shutdown VPP also.
3041  */
3042 static clib_error_t *
3044  unformat_input_t * input, vlib_cli_command_t * cmd)
3045 {
3049 
3050  /* Cosmetic: suppress the final prompt from appearing before we die */
3051  cf->is_interactive = 0;
3052  cf->started = 1;
3053 
3055  vlib_current_process (vm),
3058  return 0;
3059 }
3060 
3061 /*?
3062  * Terminates the current CLI session.
3063  *
3064  * If VPP is running in @em interactive mode and this is the console session
3065  * (that is, the session on @c stdin) then this will also terminate VPP.
3066 ?*/
3067 /* *INDENT-OFF* */
3068 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
3069  .path = "quit",
3070  .short_help = "Exit CLI",
3071  .function = unix_cli_quit,
3072 };
3073 /* *INDENT-ON* */
3074 
3075 /* *INDENT-OFF* */
3076 VLIB_CLI_COMMAND (unix_cli_q_command, static) = {
3077  .path = "q",
3078  .short_help = "Exit CLI",
3079  .function = unix_cli_quit,
3080 };
3081 /* *INDENT-ON* */
3082 
3083 /** CLI command to execute a VPP command script. */
3084 static clib_error_t *
3086  unformat_input_t * input, vlib_cli_command_t * cmd)
3087 {
3088  char *file_name;
3089  int fd;
3090  unformat_input_t sub_input;
3091  clib_error_t *error;
3092 
3093  file_name = 0;
3094  fd = -1;
3095  error = 0;
3096 
3097  if (!unformat (input, "%s", &file_name))
3098  {
3099  error = clib_error_return (0, "expecting file name, got `%U'",
3100  format_unformat_error, input);
3101  goto done;
3102  }
3103 
3104  fd = open (file_name, O_RDONLY);
3105  if (fd < 0)
3106  {
3107  error = clib_error_return_unix (0, "failed to open `%s'", file_name);
3108  goto done;
3109  }
3110 
3111  /* Make sure its a regular file. */
3112  {
3113  struct stat s;
3114 
3115  if (fstat (fd, &s) < 0)
3116  {
3117  error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
3118  goto done;
3119  }
3120 
3121  if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
3122  {
3123  error = clib_error_return (0, "not a regular file `%s'", file_name);
3124  goto done;
3125  }
3126  }
3127 
3128  unformat_init_clib_file (&sub_input, fd);
3129 
3130  vlib_cli_input (vm, &sub_input, 0, 0);
3131  unformat_free (&sub_input);
3132 
3133 done:
3134  if (fd > 0)
3135  close (fd);
3136  vec_free (file_name);
3137 
3138  return error;
3139 }
3140 
3141 /*?
3142  * Executes a sequence of CLI commands which are read from a file. If
3143  * a command is unrecognised or otherwise invalid then the usual CLI
3144  * feedback will be generated, however execution of subsequent commands
3145  * from the file will continue.
3146  *
3147  * The VPP code is indifferent to the file location. However, if SELinux
3148  * is enabled, then the file needs to have an SELinux label the VPP
3149  * process is allowed to access. For example, if a file is created in
3150  * '<em>/usr/share/vpp/</em>', it will be allowed. However, files manually
3151  * created in '/tmp/' or '/home/<user>/' will not be accessible by the VPP
3152  * process when SELinux is enabled.
3153  *
3154  * @cliexpar
3155  * Sample file:
3156  * @clistart
3157  * <b><em>$ cat /usr/share/vpp/scripts/gigup.txt</em></b>
3158  * set interface state GigabitEthernet0/8/0 up
3159  * set interface state GigabitEthernet0/9/0 up
3160  * @cliend
3161  * Example of how to execute a set of CLI commands from a file:
3162  * @cliexcmd{exec /usr/share/vpp/scripts/gigup.txt}
3163 ?*/
3164 /* *INDENT-OFF* */
3165 VLIB_CLI_COMMAND (cli_exec, static) = {
3166  .path = "exec",
3167  .short_help = "exec <filename>",
3168  .function = unix_cli_exec,
3169  .is_mp_safe = 1,
3170 };
3171 /* *INDENT-ON* */
3172 
3173 /** CLI command to show various unix error statistics. */
3174 static clib_error_t *
3176  unformat_input_t * input, vlib_cli_command_t * cmd)
3177 {
3178  unix_main_t *um = &unix_main;
3179  clib_error_t *error = 0;
3180  int i, n_errors_to_show;
3181  unix_error_history_t *unix_errors = 0;
3182 
3183  n_errors_to_show = 1 << 30;
3184 
3186  {
3187  if (!unformat (input, "%d", &n_errors_to_show))
3188  {
3189  error =
3190  clib_error_return (0,
3191  "expecting integer number of errors to show, got `%U'",
3192  format_unformat_error, input);
3193  goto done;
3194  }
3195  }
3196 
3197  n_errors_to_show =
3198  clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
3199 
3200  i =
3201  um->error_history_index >
3202  0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
3203 
3204  while (n_errors_to_show > 0)
3205  {
3206  unix_error_history_t *eh = um->error_history + i;
3207 
3208  if (!eh->error)
3209  break;
3210 
3211  vec_add1 (unix_errors, eh[0]);
3212  n_errors_to_show -= 1;
3213  if (i == 0)
3214  i = ARRAY_LEN (um->error_history) - 1;
3215  else
3216  i--;
3217  }
3218 
3219  if (vec_len (unix_errors) == 0)
3220  vlib_cli_output (vm, "no Unix errors so far");
3221  else
3222  {
3223  vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
3224  for (i = vec_len (unix_errors) - 1; i >= 0; i--)
3225  {
3226  unix_error_history_t *eh = vec_elt_at_index (unix_errors, i);
3227  vlib_cli_output (vm, "%U: %U",
3228  format_time_interval, "h:m:s:u", eh->time,
3229  format_clib_error, eh->error);
3230  }
3231  vlib_cli_output (vm, "%U: time now",
3232  format_time_interval, "h:m:s:u", vlib_time_now (vm));
3233  }
3234 
3235 done:
3236  vec_free (unix_errors);
3237  return error;
3238 }
3239 
3240 /* *INDENT-OFF* */
3241 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
3242  .path = "show unix errors",
3243  .short_help = "Show Unix system call error history",
3244  .function = unix_show_errors,
3245 };
3246 /* *INDENT-ON* */
3247 
3248 /** CLI command to show various unix error statistics. */
3249 static clib_error_t *
3251  unformat_input_t * input, vlib_cli_command_t * cmd)
3252 {
3253  clib_error_t *error = 0;
3254  clib_file_main_t *fm = &file_main;
3255  clib_file_t *f;
3256  char path[PATH_MAX];
3257  u8 *s = 0;
3258 
3259  vlib_cli_output (vm, "%3s %6s %12s %12s %12s %-32s %s", "FD", "Thread",
3260  "Read", "Write", "Error", "File Name", "Description");
3261 
3262  /* *INDENT-OFF* */
3263  pool_foreach (f, fm->file_pool,(
3264  {
3265  int rv;
3266  s = format (s, "/proc/self/fd/%d%c", f->file_descriptor, 0);
3267  rv = readlink((char *) s, path, PATH_MAX - 1);
3268 
3269  path[rv < 0 ? 0 : rv] = 0;
3270 
3271  vlib_cli_output (vm, "%3d %6d %12d %12d %12d %-32s %v",
3272  f->file_descriptor, f->polling_thread_index,
3273  f->read_events, f->write_events, f->error_events,
3274  path, f->description);
3275  vec_reset_length (s);
3276  }));
3277  /* *INDENT-ON* */
3278  vec_free (s);
3279 
3280  return error;
3281 }
3282 
3283 /* *INDENT-OFF* */
3284 VLIB_CLI_COMMAND (cli_unix_show_files, static) = {
3285  .path = "show unix files",
3286  .short_help = "Show Unix files in use",
3287  .function = unix_show_files,
3288 };
3289 /* *INDENT-ON* */
3290 
3291 /** CLI command to show session command history. */
3292 static clib_error_t *
3294  unformat_input_t * input, vlib_cli_command_t * cmd)
3295 {
3297  unix_cli_file_t *cf;
3298  int i, j;
3299 
3301 
3302  if (!cf->is_interactive)
3303  return clib_error_return (0, "invalid for non-interactive sessions");
3304 
3305  if (cf->has_history && cf->history_limit)
3306  {
3307  i = 1 + cf->command_number - vec_len (cf->command_history);
3308  for (j = 0; j < vec_len (cf->command_history); j++)
3309  vlib_cli_output (vm, "%d %v\n", i + j, cf->command_history[j]);
3310  }
3311  else
3312  {
3313  vlib_cli_output (vm, "History not enabled.\n");
3314  }
3315 
3316  return 0;
3317 }
3318 
3319 /*?
3320  * Displays the command history for the current session, if any.
3321 ?*/
3322 /* *INDENT-OFF* */
3323 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
3324  .path = "history",
3325  .short_help = "Show current session command history",
3326  .function = unix_cli_show_history,
3327 };
3328 /* *INDENT-ON* */
3329 
3330 /** CLI command to show terminal status. */
3331 static clib_error_t *
3333  unformat_input_t * input, vlib_cli_command_t * cmd)
3334 {
3335  unix_main_t *um = &unix_main;
3337  unix_cli_file_t *cf;
3338  vlib_node_t *n;
3339 
3341  n = vlib_get_node (vm, cf->process_node_index);
3342 
3343  vlib_cli_output (vm, "Terminal name: %v\n", n->name);
3344  vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ?
3345  "line-by-line" : "char-by-char");
3346  vlib_cli_output (vm, "Terminal width: %d\n", cf->width);
3347  vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
3348  vlib_cli_output (vm, "ANSI capable: %s\n",
3349  cf->ansi_capable ? "yes" : "no");
3350  vlib_cli_output (vm, "Interactive: %s\n",
3351  cf->is_interactive ? "yes" : "no");
3352  vlib_cli_output (vm, "History enabled: %s%s\n",
3353  cf->has_history ? "yes" : "no", !cf->has_history
3354  || cf->history_limit ? "" :
3355  " (disabled by history limit)");
3356  if (cf->has_history)
3357  vlib_cli_output (vm, "History limit: %d\n", cf->history_limit);
3358  vlib_cli_output (vm, "Pager enabled: %s%s%s\n",
3359  cf->no_pager ? "no" : "yes",
3360  cf->no_pager
3361  || cf->height ? "" : " (disabled by terminal height)",
3362  cf->no_pager
3363  || um->cli_pager_buffer_limit ? "" :
3364  " (disabled by buffer limit)");
3365  if (!cf->no_pager)
3366  vlib_cli_output (vm, "Pager limit: %d\n", um->cli_pager_buffer_limit);
3367  vlib_cli_output (vm, "CRLF mode: %s\n",
3368  cf->crlf_mode ? "CR+LF" : "LF");
3369 
3370  return 0;
3371 }
3372 
3373 /*?
3374  * Displays various information about the state of the current terminal
3375  * session.
3376  *
3377  * @cliexpar
3378  * @cliexstart{show terminal}
3379  * Terminal name: unix-cli-stdin
3380  * Terminal mode: char-by-char
3381  * Terminal width: 123
3382  * Terminal height: 48
3383  * ANSI capable: yes
3384  * Interactive: yes
3385  * History enabled: yes
3386  * History limit: 50
3387  * Pager enabled: yes
3388  * Pager limit: 100000
3389  * CRLF mode: LF
3390  * @cliexend
3391 ?*/
3392 /* *INDENT-OFF* */
3393 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
3394  .path = "show terminal",
3395  .short_help = "Show current session terminal settings",
3396  .function = unix_cli_show_terminal,
3397 };
3398 /* *INDENT-ON* */
3399 
3400 /** CLI command to display a list of CLI sessions. */
3401 static clib_error_t *
3403  unformat_input_t * input,
3404  vlib_cli_command_t * cmd)
3405 {
3407  clib_file_main_t *fm = &file_main;
3408  unix_cli_file_t *cf;
3409  clib_file_t *uf;
3410  vlib_node_t *n;
3411 
3412  vlib_cli_output (vm, "%-5s %-5s %-20s %s", "PNI", "FD", "Name", "Flags");
3413 
3414 #define fl(x, y) ( (x) ? toupper((y)) : tolower((y)) )
3415  /* *INDENT-OFF* */
3416  pool_foreach (cf, cm->cli_file_pool, ({
3417  uf = pool_elt_at_index (fm->file_pool, cf->clib_file_index);
3418  n = vlib_get_node (vm, cf->process_node_index);
3419  vlib_cli_output (vm,
3420  "%-5d %-5d %-20v %c%c%c%c%c\n",
3421  cf->process_node_index,
3422  uf->file_descriptor,
3423  n->name,
3424  fl (cf->is_interactive, 'i'),
3425  fl (cf->is_socket, 's'),
3426  fl (cf->line_mode, 'l'),
3427  fl (cf->has_epipe, 'p'),
3428  fl (cf->ansi_capable, 'a'));
3429  }));
3430  /* *INDENT-ON* */
3431 #undef fl
3432 
3433  return 0;
3434 }
3435 
3436 /*?
3437  * Displays a summary of all the current CLI sessions.
3438  *
3439  * Typically used to diagnose connection issues with the CLI
3440  * socket.
3441  *
3442  * @cliexpar
3443  * @cliexstart{show cli-sessions}
3444  * PNI FD Name Flags
3445  * 343 0 unix-cli-stdin IslpA
3446  * 344 7 unix-cli-local:20 ISlpA
3447  * 346 8 unix-cli-local:21 iSLpa
3448  * @cliexend
3449 
3450  * In this example we have the debug console of the running process
3451  * on stdin/out, we have an interactive socket session and we also
3452  * have a non-interactive socket session.
3453  *
3454  * Fields:
3455  *
3456  * - @em PNI: Process node index.
3457  * - @em FD: Unix file descriptor.
3458  * - @em Name: Name of the session.
3459  * - @em Flags: Various flags that describe the state of the session.
3460  *
3461  * @em Flags have the following meanings; lower-case typically negates
3462  * upper-case:
3463  *
3464  * - @em I Interactive session.
3465  * - @em S Connected by socket.
3466  * - @em s Not a socket, likely stdin.
3467  * - @em L Line-by-line mode.
3468  * - @em l Char-by-char mode.
3469  * - @em P EPIPE detected on connection; it will close soon.
3470  * - @em A ANSI-capable terminal.
3471 ?*/
3472 /* *INDENT-OFF* */
3473 VLIB_CLI_COMMAND (cli_unix_cli_show_cli_sessions, static) = {
3474  .path = "show cli-sessions",
3475  .short_help = "Show current CLI sessions",
3476  .function = unix_cli_show_cli_sessions,
3477 };
3478 /* *INDENT-ON* */
3479 
3480 /** CLI command to set terminal pager settings. */
3481 static clib_error_t *
3483  unformat_input_t * input,
3484  vlib_cli_command_t * cmd)
3485 {
3486  unix_main_t *um = &unix_main;
3488  unix_cli_file_t *cf;
3489  unformat_input_t _line_input, *line_input = &_line_input;
3490  clib_error_t *error = 0;
3491 
3493 
3494  if (!cf->is_interactive)
3495  return clib_error_return (0, "invalid for non-interactive sessions");
3496 
3497  if (!unformat_user (input, unformat_line_input, line_input))
3498  return 0;
3499 
3500  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3501  {
3502  if (unformat (line_input, "on"))
3503  cf->no_pager = 0;
3504  else if (unformat (line_input, "off"))
3505  cf->no_pager = 1;
3506  else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
3507  vlib_cli_output (vm,
3508  "Pager limit set to %u lines; note, this is global.\n",
3510  else
3511  {
3512  error = clib_error_return (0, "unknown parameter: `%U`",
3513  format_unformat_error, line_input);
3514  goto done;
3515  }
3516  }
3517 
3518 done:
3519  unformat_free (line_input);
3520 
3521  return error;
3522 }
3523 
3524 /*?
3525  * Enables or disables the terminal pager for this session. Generally
3526  * this defaults to enabled.
3527  *
3528  * Additionally allows the pager buffer size to be set; though note that
3529  * this value is set globally and not per session.
3530 ?*/
3531 /* *INDENT-OFF* */
3532 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
3533  .path = "set terminal pager",
3534  .short_help = "set terminal pager [on|off] [limit <lines>]",
3535  .function = unix_cli_set_terminal_pager,
3536 };
3537 /* *INDENT-ON* */
3538 
3539 /** CLI command to set terminal history settings. */
3540 static clib_error_t *
3542  unformat_input_t * input,
3543  vlib_cli_command_t * cmd)
3544 {
3546  unix_cli_file_t *cf;
3547  unformat_input_t _line_input, *line_input = &_line_input;
3548  u32 limit;
3549  clib_error_t *error = 0;
3550 
3552 
3553  if (!cf->is_interactive)
3554  return clib_error_return (0, "invalid for non-interactive sessions");
3555 
3556  if (!unformat_user (input, unformat_line_input, line_input))
3557  return 0;
3558 
3559  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3560  {
3561  if (unformat (line_input, "on"))
3562  cf->has_history = 1;
3563  else if (unformat (line_input, "off"))
3564  cf->has_history = 0;
3565  else if (unformat (line_input, "limit %u", &cf->history_limit))
3566  ;
3567  else
3568  {
3569  error = clib_error_return (0, "unknown parameter: `%U`",
3570  format_unformat_error, line_input);
3571  goto done;
3572  }
3573 
3574  /* If we reduced history size, or turned it off, purge the history */
3575  limit = cf->has_history ? cf->history_limit : 0;
3576 
3577  while (cf->command_history && vec_len (cf->command_history) >= limit)
3578  {
3579  vec_free (cf->command_history[0]);
3580  vec_delete (cf->command_history, 1, 0);
3581  }
3582  }
3583 
3584 done:
3585  unformat_free (line_input);
3586 
3587  return error;
3588 }
3589 
3590 /*?
3591  * Enables or disables the command history function of the current
3592  * terminal. Generally this defaults to enabled.
3593  *
3594  * This command also allows the maximum size of the history buffer for
3595  * this session to be altered.
3596 ?*/
3597 /* *INDENT-OFF* */
3598 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
3599  .path = "set terminal history",
3600  .short_help = "set terminal history [on|off] [limit <lines>]",
3601  .function = unix_cli_set_terminal_history,
3602 };
3603 /* *INDENT-ON* */
3604 
3605 /** CLI command to set terminal ANSI settings. */
3606 static clib_error_t *
3608  unformat_input_t * input,
3609  vlib_cli_command_t * cmd)
3610 {
3612  unix_cli_file_t *cf;
3613 
3615 
3616  if (!cf->is_interactive)
3617  return clib_error_return (0, "invalid for non-interactive sessions");
3618 
3619  if (unformat (input, "on"))
3620  cf->ansi_capable = 1;
3621  else if (unformat (input, "off"))
3622  cf->ansi_capable = 0;
3623  else
3624  return clib_error_return (0, "unknown parameter: `%U`",
3625  format_unformat_error, input);
3626 
3627  return 0;
3628 }
3629 
3630 /*?
3631  * Enables or disables the use of ANSI control sequences by this terminal.
3632  * The default will vary based on terminal detection at the start of the
3633  * session.
3634  *
3635  * ANSI control sequences are used in a small number of places to provide,
3636  * for example, color text output and to control the cursor in the pager.
3637 ?*/
3638 /* *INDENT-OFF* */
3639 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
3640  .path = "set terminal ansi",
3641  .short_help = "set terminal ansi [on|off]",
3642  .function = unix_cli_set_terminal_ansi,
3643 };
3644 /* *INDENT-ON* */
3645 
3646 static clib_error_t *
3648 {
3649  return 0;
3650 }
3651 
3653 
3654 /*
3655  * fd.io coding-style-patch-verification: ON
3656  *
3657  * Local Variables:
3658  * eval: (c-set-style "gnu")
3659  * End:
3660  */
#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:2640
#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:537
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:3332
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:3250
#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:225
void vlib_unix_cli_set_prompt(char *prompt)
Set the CLI prompt.
Definition: cli.c:3029
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:2662
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:3607
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:95
#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:2730
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:227
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:3647
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 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:761
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:440
i64 word
Definition: types.h:111
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:3043
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:281
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:461
#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:273
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:2601
#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:3175
#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
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:230
#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:143
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:2559
#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:3541
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:270
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:3482
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:2855
#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:3293
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:161
#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:2515
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
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:2815
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:3085
Definition: file.h:51
#define ANSI_RESET
ANSI reset color settings.
Definition: cli.c:73
clib_longjmp_t main_loop_exit
Definition: main.h:91
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
u32 flags
Definition: vhost-user.h:77
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:3402
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:169
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:3010
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