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