FD.io VPP  v17.07.01-10-g3be13f0
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 
63 /** ANSI escape code. */
64 #define ESC "\x1b"
65 
66 /** ANSI Control Sequence Introducer. */
67 #define CSI ESC "["
68 
69 /** ANSI clear screen. */
70 #define ANSI_CLEAR CSI "2J" CSI "1;1H"
71 /** ANSI reset color settings. */
72 #define ANSI_RESET CSI "0m"
73 /** ANSI Start bold text. */
74 #define ANSI_BOLD CSI "1m"
75 /** ANSI Stop bold text. */
76 #define ANSI_DIM CSI "2m"
77 /** ANSI Start dark red text. */
78 #define ANSI_DRED ANSI_DIM CSI "31m"
79 /** ANSI Start bright red text. */
80 #define ANSI_BRED ANSI_BOLD CSI "31m"
81 /** ANSI clear line cursor is on. */
82 #define ANSI_CLEARLINE CSI "2K"
83 /** ANSI scroll screen down one line. */
84 #define ANSI_SCROLLDN CSI "1T"
85 /** ANSI save cursor position. */
86 #define ANSI_SAVECURSOR CSI "s"
87 /** ANSI restore cursor position if previously saved. */
88 #define ANSI_RESTCURSOR CSI "u"
89 
90 /** Maximum depth into a byte stream from which to compile a Telnet
91  * protocol message. This is a saftey measure. */
92 #define UNIX_CLI_MAX_DEPTH_TELNET 24
93 
94 /** Unix standard in */
95 #define UNIX_CLI_STDIN_FD 0
96 
97 
98 /** A CLI banner line. */
99 typedef struct
100 {
101  u8 *line; /**< The line to print. */
102  u32 length; /**< The length of the line without terminating NUL. */
104 
105 #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 }
106 /** Plain welcome banner. */
107 static unix_cli_banner_t unix_cli_banner[] = {
108  _(" _______ _ _ _____ ___ \n"),
109  _(" __/ __/ _ \\ (_)__ | | / / _ \\/ _ \\\n"),
110  _(" _/ _// // / / / _ \\ | |/ / ___/ ___/\n"),
111  _(" /_/ /____(_)_/\\___/ |___/_/ /_/ \n"),
112  _("\n")
113 };
114 
115 /** ANSI color welcome banner. */
116 static unix_cli_banner_t unix_cli_banner_color[] = {
117  _(ANSI_BRED " _______ _ " ANSI_RESET " _ _____ ___ \n"),
118  _(ANSI_BRED " __/ __/ _ \\ (_)__ " ANSI_RESET " | | / / _ \\/ _ \\\n"),
119  _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET " | |/ / ___/ ___/\n"),
120  _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET " |___/_/ /_/ \n"),
121  _("\n")
122 };
123 
124 #undef _
125 
126 /** Pager line index */
127 typedef struct
128 {
129  /** Index into pager_vector */
131 
132  /** Offset of the string in the line */
134 
135  /** Length of the string in the line */
138 
139 
140 /** Unix CLI session. */
141 typedef struct
142 {
143  /** The file index held by unix.c */
145 
146  /** Vector of output pending write to file descriptor. */
148 
149  /** Vector of input saved by Unix input node to be processed by
150  CLI process. */
152 
153  /** This session has command history. */
155  /** Array of vectors of commands in the history. */
157  /** The command currently pointed at by the history cursor. */
159  /** How far from the end of the history array the user has browsed. */
161 
162  /** Maximum number of history entries this session will store. */
164 
165  /** Current command line counter */
167 
168  /** The string being searched for in the history. */
170  /** If non-zero then the CLI is searching in the history array.
171  * - @c -1 means search backwards.
172  * - @c 1 means search forwards.
173  */
175 
176  /** Position of the insert cursor on the current input line */
178 
179  /** Line mode or char mode */
181 
182  /** Set if the CRLF mode wants CR + LF */
184 
185  /** Can we do ANSI output? */
187 
188  /** Has the session started? */
190 
191  /** Disable the pager? */
193 
194  /** Pager buffer */
196 
197  /** Index of line fragments in the pager buffer */
199 
200  /** Line number of top of page */
202 
203  /** Terminal width */
205 
206  /** Terminal height */
208 
209  /** Process node identifier */
212 
213 /** Resets the pager buffer and other data.
214  * @param f The CLI session whose pager needs to be reset.
215  */
216 always_inline void
218 {
219  u8 **p;
220 
221  f->pager_start = 0;
222 
223  vec_free (f->pager_index);
224  f->pager_index = 0;
225 
226  vec_foreach (p, f->pager_vector)
227  {
228  vec_free (*p);
229  }
230  vec_free (f->pager_vector);
231  f->pager_vector = 0;
232 }
233 
234 /** Release storage used by a CLI session.
235  * @param f The CLI session whose storage needs to be released.
236  */
237 always_inline void
239 {
240  vec_free (f->output_vector);
241  vec_free (f->input_vector);
243 }
244 
245 /** CLI actions */
246 typedef enum
247 {
248  UNIX_CLI_PARSE_ACTION_NOACTION = 0, /**< No action */
249  UNIX_CLI_PARSE_ACTION_CRLF, /**< Carriage return, newline or enter */
250  UNIX_CLI_PARSE_ACTION_TAB, /**< Tab key */
251  UNIX_CLI_PARSE_ACTION_ERASE, /**< Erase cursor left */
252  UNIX_CLI_PARSE_ACTION_ERASERIGHT, /**< Erase cursor right */
253  UNIX_CLI_PARSE_ACTION_UP, /**< Up arrow */
254  UNIX_CLI_PARSE_ACTION_DOWN, /**< Down arrow */
255  UNIX_CLI_PARSE_ACTION_LEFT, /**< Left arrow */
256  UNIX_CLI_PARSE_ACTION_RIGHT, /**< Right arrow */
257  UNIX_CLI_PARSE_ACTION_HOME, /**< Home key (jump to start of line) */
258  UNIX_CLI_PARSE_ACTION_END, /**< End key (jump to end of line) */
259  UNIX_CLI_PARSE_ACTION_WORDLEFT, /**< Jump cursor to start of left word */
260  UNIX_CLI_PARSE_ACTION_WORDRIGHT, /**< Jump cursor to start of right word */
261  UNIX_CLI_PARSE_ACTION_ERASELINELEFT, /**< Erase line to left of cursor */
262  UNIX_CLI_PARSE_ACTION_ERASELINERIGHT, /**< Erase line to right & including cursor */
263  UNIX_CLI_PARSE_ACTION_CLEAR, /**< Clear the terminal */
264  UNIX_CLI_PARSE_ACTION_REVSEARCH, /**< Search backwards in command history */
265  UNIX_CLI_PARSE_ACTION_FWDSEARCH, /**< Search forwards in command history */
266  UNIX_CLI_PARSE_ACTION_YANK, /**< Undo last erase action */
267  UNIX_CLI_PARSE_ACTION_TELNETIAC, /**< Telnet control code */
268 
269  UNIX_CLI_PARSE_ACTION_PAGER_CRLF, /**< Enter pressed (CR, CRLF, LF, etc) */
270  UNIX_CLI_PARSE_ACTION_PAGER_QUIT, /**< Exit the pager session */
271  UNIX_CLI_PARSE_ACTION_PAGER_NEXT, /**< Scroll to next page */
272  UNIX_CLI_PARSE_ACTION_PAGER_DN, /**< Scroll to next line */
273  UNIX_CLI_PARSE_ACTION_PAGER_UP, /**< Scroll to previous line */
274  UNIX_CLI_PARSE_ACTION_PAGER_TOP, /**< Scroll to first line */
275  UNIX_CLI_PARSE_ACTION_PAGER_BOTTOM, /**< Scroll to last line */
276  UNIX_CLI_PARSE_ACTION_PAGER_PGDN, /**< Scroll to next page */
277  UNIX_CLI_PARSE_ACTION_PAGER_PGUP, /**< Scroll to previous page */
278  UNIX_CLI_PARSE_ACTION_PAGER_REDRAW, /**< Clear and redraw the page on the terminal */
279  UNIX_CLI_PARSE_ACTION_PAGER_SEARCH, /**< Search the pager buffer */
280 
281  UNIX_CLI_PARSE_ACTION_PARTIALMATCH, /**< Action parser found a partial match */
282  UNIX_CLI_PARSE_ACTION_NOMATCH /**< Action parser did not find any match */
284 
285 /** @brief Mapping of input buffer strings to action values.
286  * @note This won't work as a hash since we need to be able to do
287  * partial matches on the string.
288  */
289 typedef struct
290 {
291  u8 *input; /**< Input string to match. */
292  u32 len; /**< Length of input without final NUL. */
293  unix_cli_parse_action_t action; /**< Action to take when matched. */
295 
296 /** @brief Given a capital ASCII letter character return a @c NUL terminated
297  * string with the control code for that letter.
298  *
299  * @param c An ASCII character.
300  * @return A @c NUL terminated string of type @c u8[].
301  *
302  * @par Example
303  * @c CTL('A') returns <code>{ 0x01, 0x00 }</code> as a @c u8[].
304  */
305 #define CTL(c) (u8[]){ (c) - '@', 0 }
306 
307 #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) }
308 /**
309  * Patterns to match on a CLI input stream.
310  * @showinitializer
311  */
312 static unix_cli_parse_actions_t unix_cli_parse_strings[] = {
313  /* Line handling */
314  _("\r\n", UNIX_CLI_PARSE_ACTION_CRLF), /* Must be before '\r' */
316  _("\r\0", UNIX_CLI_PARSE_ACTION_CRLF), /* Telnet does this */
318 
319  /* Unix shell control codes */
322  _(CTL ('P'), UNIX_CLI_PARSE_ACTION_UP),
325  _(CTL ('E'), UNIX_CLI_PARSE_ACTION_END),
331  _(ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* Alt-B */
332  _(ESC "f", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* Alt-F */
333  _("\b", UNIX_CLI_PARSE_ACTION_ERASE), /* ^H */
334  _("\x7f", UNIX_CLI_PARSE_ACTION_ERASE), /* Backspace */
335  _("\t", UNIX_CLI_PARSE_ACTION_TAB), /* ^I */
336 
337  /* VT100 Normal mode - Broadest support */
344  _(CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT), /* Delete */
345  _(CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT), /* C-Left */
346  _(CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT), /* C-Right */
347 
348  /* VT100 Application mode - Some Gnome Terminal functions use these */
349  _(ESC "OA", UNIX_CLI_PARSE_ACTION_UP),
355 
356  /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */
359 
360  /* Emacs-ish history search */
363 
364  /* Other protocol things */
365  _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
366  _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */
368 };
369 
370 /**
371  * Patterns to match when a CLI session is in the pager.
372  * @showinitializer
373  */
374 static unix_cli_parse_actions_t unix_cli_parse_pager[] = {
375  /* Line handling */
376  _("\r\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Must be before '\r' */
378  _("\r\0", UNIX_CLI_PARSE_ACTION_PAGER_CRLF), /* Telnet does this */
380 
381  /* Pager commands */
387 
388  /* VT100 */
393 
394  /* VT100 Application mode */
399 
400  /* ANSI X3.41-1974 */
405 
406  /* Other protocol things */
407  _("\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC), /* IAC */
408  _("\0", UNIX_CLI_PARSE_ACTION_NOACTION), /* NUL */
410 };
411 
412 #undef _
413 
414 /** CLI session events. */
415 typedef enum
416 {
417  UNIX_CLI_PROCESS_EVENT_READ_READY, /**< A file descriptor has data to be read. */
418  UNIX_CLI_PROCESS_EVENT_QUIT, /**< A CLI session wants to close. */
420 
421 /** CLI global state. */
422 typedef struct
423 {
424  /** Prompt string for CLI. */
426 
427  /** Vec pool of CLI sessions. */
429 
430  /** Vec pool of unused session indices. */
432 
433  /** The session index of the stdin cli */
435 
436  /** File pool index of current input. */
439 
440 /** CLI global state */
442 
443 /**
444  * @brief Search for a byte sequence in the action list.
445  *
446  * Searches the @ref unix_cli_parse_actions_t list in @a a for a match with
447  * the bytes in @a input of maximum length @a ilen bytes.
448  * When a match is made @a *matched indicates how many bytes were matched.
449  * Returns a value from the enum @ref unix_cli_parse_action_t to indicate
450  * whether no match was found, a partial match was found or a complete
451  * match was found and what action, if any, should be taken.
452  *
453  * @param[in] a Actions list to search within.
454  * @param[in] input String fragment to search for.
455  * @param[in] ilen Length of the string in 'input'.
456  * @param[out] matched Pointer to an integer that will contain the number
457  * of bytes matched when a complete match is found.
458  *
459  * @return Action from @ref unix_cli_parse_action_t that the string fragment
460  * matches.
461  * @ref UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the
462  * whole input string matches the start of at least one action.
463  * @ref UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no
464  * match at all.
465  */
468  u8 * input, u32 ilen, i32 * matched)
469 {
470  u8 partial = 0;
471 
472  while (a->input)
473  {
474  if (ilen >= a->len)
475  {
476  /* see if the start of the input buffer exactly matches the current
477  * action string. */
478  if (memcmp (input, a->input, a->len) == 0)
479  {
480  *matched = a->len;
481  return a->action;
482  }
483  }
484  else
485  {
486  /* if the first ilen characters match, flag this as a partial -
487  * meaning keep collecting bytes in case of a future match */
488  if (memcmp (input, a->input, ilen) == 0)
489  partial = 1;
490  }
491 
492  /* check next action */
493  a++;
494  }
495 
496  return partial ?
498 }
499 
500 
501 /** Add bytes to the output vector and then flagg the I/O system that bytes
502  * are available to be sent.
503  */
504 static void
506  unix_cli_file_t * cf,
507  u8 * buffer, uword buffer_bytes)
508 {
509  unix_main_t *um = &unix_main;
510 
511  vec_add (cf->output_vector, buffer, buffer_bytes);
512  if (vec_len (cf->output_vector) > 0)
513  {
514  int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
516  if (!skip_update)
518  }
519 }
520 
521 /** Delete all bytes from the output vector and flag the I/O system
522  * that no more bytes are available to be sent.
523  */
524 static void
526  unix_cli_file_t * cf, uword n_bytes)
527 {
528  unix_main_t *um = &unix_main;
529 
530  vec_delete (cf->output_vector, n_bytes, 0);
531  if (vec_len (cf->output_vector) <= 0)
532  {
533  int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
535  if (!skip_update)
537  }
538 }
539 
540 /** @brief A bit like strchr with a buffer length limit.
541  * Search a buffer for the first instance of a character up to the limit of
542  * the buffer length. If found then return the position of that character.
543  *
544  * The key departure from strchr is that if the character is not found then
545  * return the buffer length.
546  *
547  * @param chr The byte value to search for.
548  * @param str The buffer in which to search for the value.
549  * @param len The depth into the buffer to search.
550  *
551  * @return The index of the first occurence of \c chr. If \c chr is not
552  * found then \c len instead.
553  */
555 unix_vlib_findchr (u8 chr, u8 * str, word len)
556 {
557  word i = 0;
558  for (i = 0; i < len; i++, str++)
559  {
560  if (*str == chr)
561  return i;
562  }
563  return len;
564 }
565 
566 /** @brief Send a buffer to the CLI stream if possible, enqueue it otherwise.
567  * Attempts to write given buffer to the file descriptor of the given
568  * Unix CLI session. If that session already has data in the output buffer
569  * or if the write attempt tells us to try again later then the given buffer
570  * is appended to the pending output buffer instead.
571  *
572  * This is typically called only from \c unix_vlib_cli_output_cooked since
573  * that is where CRLF handling occurs or from places where we explicitly do
574  * not want cooked handling.
575  *
576  * @param cf Unix CLI session of the desired stream to write to.
577  * @param uf The Unix file structure of the desired stream to write to.
578  * @param buffer Pointer to the buffer that needs to be written.
579  * @param buffer_bytes The number of bytes from \c buffer to write.
580  */
581 static void
583  unix_file_t * uf, u8 * buffer, uword buffer_bytes)
584 {
585  int n = 0;
586 
587  if (vec_len (cf->output_vector) == 0)
588  n = write (uf->file_descriptor, buffer, buffer_bytes);
589 
590  if (n < 0 && errno != EAGAIN)
591  {
592  clib_unix_warning ("write");
593  }
594  else if ((word) n < (word) buffer_bytes)
595  {
596  /* We got EAGAIN or we already have stuff in the buffer;
597  * queue up whatever didn't get sent for later. */
598  if (n < 0)
599  n = 0;
600  unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
601  }
602 }
603 
604 /** @brief Process a buffer for CRLF handling before outputting it to the CLI.
605  *
606  * @param cf Unix CLI session of the desired stream to write to.
607  * @param uf The Unix file structure of the desired stream to write to.
608  * @param buffer Pointer to the buffer that needs to be written.
609  * @param buffer_bytes The number of bytes from \c buffer to write.
610  */
611 static void
613  unix_file_t * uf,
614  u8 * buffer, uword buffer_bytes)
615 {
616  word end = 0, start = 0;
617 
618  while (end < buffer_bytes)
619  {
620  if (cf->crlf_mode)
621  {
622  /* iterate the line on \n's so we can insert a \r before it */
623  end = unix_vlib_findchr ('\n',
624  buffer + start,
625  buffer_bytes - start) + start;
626  }
627  else
628  {
629  /* otherwise just send the whole buffer */
630  end = buffer_bytes;
631  }
632 
633  unix_vlib_cli_output_raw (cf, uf, buffer + start, end - start);
634 
635  if (cf->crlf_mode)
636  {
637  if (end < buffer_bytes)
638  {
639  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\r\n", 2);
640  end++; /* skip the \n that we already sent */
641  }
642  start = end;
643  }
644  }
645 }
646 
647 /** @brief Output the CLI prompt */
648 static void
650 {
652 
654 }
655 
656 /** @brief Output a pager prompt and show number of buffered lines */
657 static void
659 {
660  u8 *prompt;
661  u32 h;
662 
663  h = cf->pager_start + (cf->height - 1);
664  if (h > vec_len (cf->pager_index))
665  h = vec_len (cf->pager_index);
666 
667  prompt = format (0, "\r%s-- more -- (%d-%d/%d)%s",
668  cf->ansi_capable ? ANSI_BOLD : "",
669  cf->pager_start + 1,
670  h,
671  vec_len (cf->pager_index),
672  cf->ansi_capable ? ANSI_RESET : "");
673 
674  unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
675 
676  vec_free (prompt);
677 }
678 
679 /** @brief Output a pager "skipping" message */
680 static void
682  char *message, char *postfix)
683 {
684  u8 *prompt;
685 
686  prompt = format (0, "\r%s-- %s --%s%s",
687  cf->ansi_capable ? ANSI_BOLD : "",
688  message, cf->ansi_capable ? ANSI_RESET : "", postfix);
689 
690  unix_vlib_cli_output_cooked (cf, uf, prompt, vec_len (prompt));
691 
692  vec_free (prompt);
693 }
694 
695 /** @brief Erase the printed pager prompt */
696 static void
698 {
699  if (cf->ansi_capable)
700  {
701  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
703  (u8 *) ANSI_CLEARLINE,
704  sizeof (ANSI_CLEARLINE) - 1);
705  }
706  else
707  {
708  int i;
709 
710  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
711  for (i = 0; i < cf->width - 1; i++)
712  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
713  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\r", 1);
714  }
715 }
716 
717 /** @brief Uses an ANSI escape sequence to move the cursor */
718 static void
720 {
721  u8 *str;
722 
723  str = format (0, "%s%d;%dH", CSI, y, x);
724 
725  unix_vlib_cli_output_cooked (cf, uf, str, vec_len (str));
726 
727  vec_free (str);
728 }
729 
730 /** Redraw the currently displayed page of text.
731  * @param cf CLI session to redraw the pager buffer of.
732  * @param uf Unix file of the CLI session.
733  */
734 static void
736 {
738  u8 *line = NULL;
739  word i;
740 
741  /* No active pager? Do nothing. */
742  if (!vec_len (cf->pager_index))
743  return;
744 
745  if (cf->ansi_capable)
746  {
747  /* If we have ANSI, send the clear screen sequence */
749  (u8 *) ANSI_CLEAR,
750  sizeof (ANSI_CLEAR) - 1);
751  }
752  else
753  {
754  /* Otherwise make sure we're on a blank line */
756  }
757 
758  /* (Re-)send the current page of content */
759  for (i = 0; i < cf->height - 1 &&
760  i + cf->pager_start < vec_len (cf->pager_index); i++)
761  {
762  pi = &cf->pager_index[cf->pager_start + i];
763  line = cf->pager_vector[pi->line] + pi->offset;
764 
765  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
766  }
767  /* if the last line didn't end in newline, add a newline */
768  if (pi && line[pi->length - 1] != '\n')
769  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
770 
771  unix_cli_pager_prompt (cf, uf);
772 }
773 
774 /** @brief Process and add a line to the pager index.
775  * In normal operation this function will take the given character string
776  * found in @c line and with length @c len_or_index and iterates the over the
777  * contents, adding each line of text discovered within it to the
778  * pager index. Lines are identified by newlines ("<code>\\n</code>") and by
779  * strings longer than the width of the terminal.
780  *
781  * If instead @c line is @c NULL then @c len_or_index is taken to mean the
782  * index of an existing line in the pager buffer; this simply means that the
783  * input line does not need to be cloned since we alreayd have it. This is
784  * typical if we are reindexing the pager buffer.
785  *
786  * @param cf The CLI session whose pager we are adding to.
787  * @param line The string of text to be indexed into the pager buffer.
788  * If @c line is @c NULL then the mode of operation
789  * changes slightly; see the description above.
790  * @param len_or_index If @c line is a pointer to a string then this parameter
791  * indicates the length of that string; Otherwise this
792  * value provides the index in the pager buffer of an
793  * existing string to be indexed.
794  */
795 static void
796 unix_cli_pager_add_line (unix_cli_file_t * cf, u8 * line, word len_or_index)
797 {
798  u8 *p;
799  word i, j, k;
800  word line_index, len;
801  u32 width = cf->width;
803 
804  if (line == NULL)
805  {
806  /* Use a line already in the pager buffer */
807  line_index = len_or_index;
808  p = cf->pager_vector[line_index];
809  len = vec_len (p);
810  }
811  else
812  {
813  len = len_or_index;
814  /* Add a copy of the raw string to the pager buffer */
815  p = vec_new (u8, len);
816  clib_memcpy (p, line, len);
817 
818  /* store in pager buffer */
819  line_index = vec_len (cf->pager_vector);
820  vec_add1 (cf->pager_vector, p);
821  }
822 
823  i = 0;
824  while (i < len)
825  {
826  /* Find the next line, or run to terminal width, or run to EOL */
827  int l = len - i;
828  j = unix_vlib_findchr ((u8) '\n', p, l < width ? l : width);
829 
830  if (j < l && p[j] == '\n') /* incl \n */
831  j++;
832 
833  /* Add the line to the index */
834  k = vec_len (cf->pager_index);
835  vec_validate (cf->pager_index, k);
836  pi = &cf->pager_index[k];
837 
838  pi->line = line_index;
839  pi->offset = i;
840  pi->length = j;
841 
842  i += j;
843  p += j;
844  }
845 }
846 
847 /** @brief Reindex entire pager buffer.
848  * Resets the current pager index and then re-adds the lines in the pager
849  * buffer to the index.
850  *
851  * Additionally this function attempts to retain the current page start
852  * line offset by searching for the same top-of-screen line in the new index.
853  *
854  * @param cf The CLI session whose pager buffer should be reindexed.
855  */
856 static void
858 {
859  word i, old_line, old_offset;
861 
862  /* If there is nothing in the pager buffer then make sure the index
863  * is empty and move on.
864  */
865  if (cf->pager_vector == 0)
866  {
868  return;
869  }
870 
871  /* Retain a pointer to the current page start line so we can
872  * find it later
873  */
874  pi = &cf->pager_index[cf->pager_start];
875  old_line = pi->line;
876  old_offset = pi->offset;
877 
878  /* Re-add the buffered lines to the index */
881  {
882  unix_cli_pager_add_line (cf, NULL, i);
883  }
884 
885  /* Attempt to re-locate the previously stored page start line */
887  {
888  pi = &cf->pager_index[i];
889 
890  if (pi->line == old_line &&
891  (pi->offset <= old_offset || pi->offset + pi->length > old_offset))
892  {
893  /* Found it! */
894  cf->pager_start = i;
895  break;
896  }
897  }
898 
899  /* In case the start line was not found (rare), ensure the pager start
900  * index is within bounds
901  */
902  if (cf->pager_start >= vec_len (cf->pager_index))
903  {
904  if (!cf->height || vec_len (cf->pager_index) < (cf->height - 1))
905  cf->pager_start = 0;
906  else
907  cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
908  }
909 }
910 
911 /** VLIB CLI output function.
912  *
913  * If the terminal has a pager configured then this function takes care
914  * of collating output into the pager buffer; ensuring only the first page
915  * is displayed and any lines in excess of the first page are buffered.
916  *
917  * If the maximum number of index lines in the buffer is exceeded then the
918  * pager is cancelled and the contents of the current buffer are sent to the
919  * terminal.
920  *
921  * If there is no pager configured then the output is sent directly to the
922  * terminal.
923  *
924  * @param cli_file_index Index of the CLI session where this output is
925  * directed.
926  * @param buffer String of printabe bytes to be output.
927  * @param buffer_bytes The number of bytes in @c buffer to be output.
928  */
929 static void
930 unix_vlib_cli_output (uword cli_file_index, u8 * buffer, uword buffer_bytes)
931 {
932  unix_main_t *um = &unix_main;
934  unix_cli_file_t *cf;
935  unix_file_t *uf;
936 
937  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
939 
940  if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
941  {
942  unix_vlib_cli_output_cooked (cf, uf, buffer, buffer_bytes);
943  }
944  else
945  {
946  word row = vec_len (cf->pager_index);
947  u8 *line;
949 
950  /* Index and add the output lines to the pager buffer. */
951  unix_cli_pager_add_line (cf, buffer, buffer_bytes);
952 
953  /* Now iterate what was added to display the lines.
954  * If we reach the bottom of the page, display a prompt.
955  */
956  while (row < vec_len (cf->pager_index))
957  {
958  if (row < cf->height - 1)
959  {
960  /* output this line */
961  pi = &cf->pager_index[row];
962  line = cf->pager_vector[pi->line] + pi->offset;
963  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
964 
965  /* if the last line didn't end in newline, and we're at the
966  * bottom of the page, add a newline */
967  if (line[pi->length - 1] != '\n' && row == cf->height - 2)
968  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
969  }
970  else
971  {
972  /* Display the pager prompt every 10 lines */
973  if (!(row % 10))
974  unix_cli_pager_prompt (cf, uf);
975  }
976  row++;
977  }
978 
979  /* Check if we went over the pager buffer limit */
981  {
982  /* Stop using the pager for the remainder of this CLI command */
983  cf->no_pager = 2;
984 
985  /* If we likely printed the prompt, erase it */
986  if (vec_len (cf->pager_index) > cf->height - 1)
988 
989  /* Dump out the contents of the buffer */
990  for (row = cf->pager_start + (cf->height - 1);
991  row < vec_len (cf->pager_index); row++)
992  {
993  pi = &cf->pager_index[row];
994  line = cf->pager_vector[pi->line] + pi->offset;
995  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
996  }
997 
999  }
1000  }
1001 }
1002 
1003 /** Identify whether a terminal type is ANSI capable.
1004  *
1005  * Compares the string given in @c term with a list of terminal types known
1006  * to support ANSI escape sequences.
1007  *
1008  * This list contains, for example, @c xterm, @c screen and @c ansi.
1009  *
1010  * @param term A string with a terminal type in it.
1011  * @param len The length of the string in @c term.
1012  *
1013  * @return @c 1 if the terminal type is recognized as supporting ANSI
1014  * terminal sequences; @c 0 otherwise.
1015  */
1016 static u8
1018 {
1019  /* This may later be better done as a hash of some sort. */
1020 #define _(a) do { \
1021  if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
1022  } while(0)
1023 
1024  _("xterm");
1025  _("xterm-color");
1026  _("xterm-256color"); /* iTerm on Mac */
1027  _("screen");
1028  _("screen-256color"); /* Screen and tmux */
1029  _("ansi"); /* Microsoft Telnet */
1030 #undef _
1031 
1032  return 0;
1033 }
1034 
1035 /** @brief Emit initial welcome banner and prompt on a connection. */
1036 static void
1038 {
1039  unix_main_t *um = &unix_main;
1041  unix_cli_banner_t *banner;
1042  int i, len;
1043 
1044  /*
1045  * Put the first bytes directly into the buffer so that further output is
1046  * queued until everything is ready. (oterwise initial prompt can appear
1047  * mid way through VPP initialization)
1048  */
1049  unix_cli_add_pending_output (uf, cf, (u8 *) "\r", 1);
1050 
1051  if (!um->cli_no_banner)
1052  {
1053  if (cf->ansi_capable)
1054  {
1055  banner = unix_cli_banner_color;
1056  len = ARRAY_LEN (unix_cli_banner_color);
1057  }
1058  else
1059  {
1060  banner = unix_cli_banner;
1061  len = ARRAY_LEN (unix_cli_banner);
1062  }
1063 
1064  for (i = 0; i < len; i++)
1065  {
1067  banner[i].line, banner[i].length);
1068  }
1069  }
1070 
1071  /* Prompt. */
1072  unix_cli_cli_prompt (cf, uf);
1073 
1074  cf->started = 1;
1075 }
1076 
1077 /** @brief A failsafe triggered on a timer to ensure we send the prompt
1078  * to telnet sessions that fail to negotiate the terminal type. */
1079 static void
1081 {
1083  unix_cli_file_t *cf;
1084  (void) delay;
1085 
1086  /* Check the connection didn't close already */
1087  if (pool_is_free_index (cm->cli_file_pool, (uword) arg))
1088  return;
1089 
1090  cf = pool_elt_at_index (cm->cli_file_pool, (uword) arg);
1091 
1092  if (!cf->started)
1093  unix_cli_file_welcome (cm, cf);
1094 }
1095 
1096 /** @brief A mostly no-op Telnet state machine.
1097  * Process Telnet command bytes in a way that ensures we're mostly
1098  * transparent to the Telnet protocol. That is, it's mostly a no-op.
1099  *
1100  * @return -1 if we need more bytes, otherwise a positive integer number of
1101  * bytes to consume from the input_vector, not including the initial
1102  * IAC byte.
1103  */
1104 static i32
1106  unix_cli_file_t * cf,
1107  unix_file_t * uf, u8 * input_vector, uword len)
1108 {
1109  /* Input_vector starts at IAC byte.
1110  * See if we have a complete message; if not, return -1 so we wait for more.
1111  * if we have a complete message, consume those bytes from the vector.
1112  */
1113  i32 consume = 0;
1114 
1115  if (len == 1)
1116  return -1; /* want more bytes */
1117 
1118  switch (input_vector[1])
1119  {
1120  case IAC:
1121  /* two IAC's in a row means to pass through 0xff.
1122  * since that makes no sense here, just consume it.
1123  */
1124  consume = 1;
1125  break;
1126 
1127  case WILL:
1128  case WONT:
1129  case DO:
1130  case DONT:
1131  /* Expect 3 bytes */
1132  if (vec_len (input_vector) < 3)
1133  return -1; /* want more bytes */
1134 
1135  consume = 2;
1136  break;
1137 
1138  case SB:
1139  {
1140  /* Sub option - search ahead for IAC SE to end it */
1141  i32 i;
1142  for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
1143  {
1144  if (input_vector[i - 1] == IAC && input_vector[i] == SE)
1145  {
1146  /* We have a complete message; see if we care about it */
1147  switch (input_vector[2])
1148  {
1149  case TELOPT_TTYPE:
1150  if (input_vector[3] != 0)
1151  break;
1152  /* See if the terminal type is ANSI capable */
1153  cf->ansi_capable =
1154  unix_cli_terminal_type (input_vector + 4, i - 5);
1155  /* If session not started, we can release the pause */
1156  if (!cf->started)
1157  /* Send the welcome banner and initial prompt */
1158  unix_cli_file_welcome (&unix_cli_main, cf);
1159  break;
1160 
1161  case TELOPT_NAWS:
1162  /* Window size */
1163  if (i != 8) /* check message is correct size */
1164  break;
1165  cf->width =
1166  clib_net_to_host_u16 (*((u16 *) (input_vector + 3)));
1167  cf->height =
1168  clib_net_to_host_u16 (*((u16 *) (input_vector + 5)));
1169  /* reindex pager buffer */
1171  /* redraw page */
1172  unix_cli_pager_redraw (cf, uf);
1173  break;
1174 
1175  default:
1176  break;
1177  }
1178  /* Consume it all */
1179  consume = i;
1180  break;
1181  }
1182  }
1183 
1184  if (i == UNIX_CLI_MAX_DEPTH_TELNET)
1185  consume = 1; /* hit max search depth, advance one byte */
1186 
1187  if (consume == 0)
1188  return -1; /* want more bytes */
1189 
1190  break;
1191  }
1192 
1193  case GA:
1194  case EL:
1195  case EC:
1196  case AO:
1197  case IP:
1198  case BREAK:
1199  case DM:
1200  case NOP:
1201  case SE:
1202  case EOR:
1203  case ABORT:
1204  case SUSP:
1205  case xEOF:
1206  /* Simple one-byte messages */
1207  consume = 1;
1208  break;
1209 
1210  case AYT:
1211  /* Are You There - trigger a visible response */
1212  consume = 1;
1213  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
1214  break;
1215 
1216  default:
1217  /* Unknown command! Eat the IAC byte */
1218  break;
1219  }
1220 
1221  return consume;
1222 }
1223 
1224 /** @brief Process actionable input.
1225  * Based on the \c action process the input; this typically involves
1226  * searching the command history or editing the current command line.
1227  */
1228 static int
1230  unix_main_t * um,
1231  unix_cli_file_t * cf,
1232  unix_file_t * uf,
1233  u8 input, unix_cli_parse_action_t action)
1234 {
1235  u8 *prev;
1236  u8 *save = 0;
1237  u8 **possible_commands;
1238  int j, delta;
1239 
1240  switch (action)
1241  {
1243  break;
1244 
1247  if (!cf->has_history || !cf->history_limit)
1248  break;
1249  if (cf->search_mode == 0)
1250  {
1251  /* Erase the current command (if any) */
1252  for (j = 0; j < (vec_len (cf->current_command)); j++)
1253  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1254 
1257  if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1258  cf->search_mode = -1;
1259  else
1260  cf->search_mode = 1;
1261  cf->cursor = 0;
1262  }
1263  else
1264  {
1265  if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1266  cf->search_mode = -1;
1267  else
1268  cf->search_mode = 1;
1269 
1270  cf->excursion += cf->search_mode;
1271  goto search_again;
1272  }
1273  break;
1274 
1276  /* Erase the command from the cursor to the start */
1277 
1278  /* Shimmy forwards to the new end of line position */
1279  delta = vec_len (cf->current_command) - cf->cursor;
1280  for (j = cf->cursor; j > delta; j--)
1281  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1282  /* Zap from here to the end of what is currently displayed */
1283  for (; j < (vec_len (cf->current_command)); j++)
1284  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1285  /* Get back to the start of the line */
1286  for (j = 0; j < (vec_len (cf->current_command)); j++)
1287  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1288 
1289  j = vec_len (cf->current_command) - cf->cursor;
1290  memmove (cf->current_command, cf->current_command + cf->cursor, j);
1291  _vec_len (cf->current_command) = j;
1292 
1293  /* Print the new contents */
1295  /* Shimmy back to the start */
1296  for (j = 0; j < (vec_len (cf->current_command)); j++)
1297  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1298  cf->cursor = 0;
1299 
1300  cf->search_mode = 0;
1301  break;
1302 
1304  /* Erase the command from the cursor to the end */
1305 
1306  /* Zap from cursor to end of what is currently displayed */
1307  for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1308  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1309  /* Get back to where we were */
1310  for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1311  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1312 
1313  /* Truncate the line at the cursor */
1314  _vec_len (cf->current_command) = cf->cursor;
1315 
1316  cf->search_mode = 0;
1317  break;
1318 
1320  if (cf->cursor > 0)
1321  {
1322  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1323  cf->cursor--;
1324  }
1325 
1326  cf->search_mode = 0;
1327  break;
1328 
1330  if (cf->cursor < vec_len (cf->current_command))
1331  {
1332  /* have to emit the character under the cursor */
1334  cf->current_command + cf->cursor, 1);
1335  cf->cursor++;
1336  }
1337 
1338  cf->search_mode = 0;
1339  break;
1340 
1343  if (!cf->has_history || !cf->history_limit)
1344  break;
1345  cf->search_mode = 0;
1346  /* Erase the command */
1347  for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1348  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1349  for (j = 0; j < (vec_len (cf->current_command)); j++)
1350  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1352  if (vec_len (cf->command_history))
1353  {
1354  if (action == UNIX_CLI_PARSE_ACTION_UP)
1355  delta = -1;
1356  else
1357  delta = 1;
1358 
1359  cf->excursion += delta;
1360 
1361  if (cf->excursion == vec_len (cf->command_history))
1362  {
1363  /* down-arrowed to last entry - want a blank line */
1364  _vec_len (cf->current_command) = 0;
1365  }
1366  else if (cf->excursion < 0)
1367  {
1368  /* up-arrowed over the start to the end, want a blank line */
1369  cf->excursion = vec_len (cf->command_history);
1370  _vec_len (cf->current_command) = 0;
1371  }
1372  else
1373  {
1374  if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
1375  /* down-arrowed past end - wrap to start */
1376  cf->excursion = 0;
1377 
1378  /* Print the command at the current position */
1379  prev = cf->command_history[cf->excursion];
1380  vec_validate (cf->current_command, vec_len (prev) - 1);
1381 
1382  clib_memcpy (cf->current_command, prev, vec_len (prev));
1383  _vec_len (cf->current_command) = vec_len (prev);
1385  vec_len (cf->current_command));
1386  }
1387  cf->cursor = vec_len (cf->current_command);
1388 
1389  break;
1390  }
1391  break;
1392 
1394  if (vec_len (cf->current_command) && cf->cursor > 0)
1395  {
1396  while (cf->cursor)
1397  {
1398  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1399  cf->cursor--;
1400  }
1401  }
1402 
1403  cf->search_mode = 0;
1404  break;
1405 
1407  if (vec_len (cf->current_command) &&
1408  cf->cursor < vec_len (cf->current_command))
1409  {
1411  cf->current_command + cf->cursor,
1412  vec_len (cf->current_command) -
1413  cf->cursor);
1414  cf->cursor = vec_len (cf->current_command);
1415  }
1416 
1417  cf->search_mode = 0;
1418  break;
1419 
1421  if (vec_len (cf->current_command) && cf->cursor > 0)
1422  {
1423  j = cf->cursor;
1424 
1425  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1426  j--;
1427 
1428  while (j && isspace (cf->current_command[j]))
1429  {
1430  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1431  j--;
1432  }
1433  while (j && !isspace (cf->current_command[j]))
1434  {
1435  if (isspace (cf->current_command[j - 1]))
1436  break;
1437  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1438  j--;
1439  }
1440 
1441  cf->cursor = j;
1442  }
1443 
1444  cf->search_mode = 0;
1445  break;
1446 
1448  if (vec_len (cf->current_command) &&
1449  cf->cursor < vec_len (cf->current_command))
1450  {
1451  int e = vec_len (cf->current_command);
1452  j = cf->cursor;
1453  while (j < e && !isspace (cf->current_command[j]))
1454  j++;
1455  while (j < e && isspace (cf->current_command[j]))
1456  j++;
1458  cf->current_command + cf->cursor,
1459  j - cf->cursor);
1460  cf->cursor = j;
1461  }
1462 
1463  cf->search_mode = 0;
1464  break;
1465 
1466 
1468  if (vec_len (cf->current_command))
1469  {
1470  if (cf->cursor == vec_len (cf->current_command))
1471  {
1472  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1473  _vec_len (cf->current_command)--;
1474  cf->cursor--;
1475  }
1476  else if (cf->cursor > 0)
1477  {
1478  /* shift everything at & to the right of the cursor left by 1 */
1479  j = vec_len (cf->current_command) - cf->cursor;
1480  memmove (cf->current_command + cf->cursor - 1,
1481  cf->current_command + cf->cursor, j);
1482  _vec_len (cf->current_command)--;
1483  cf->cursor--;
1484  /* redraw the rest of the line */
1485  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1487  cf->current_command + cf->cursor,
1488  j);
1489  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " \b\b", 3);
1490  /* and shift the terminal cursor back where it should be */
1491  while (--j)
1492  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1493  }
1494  }
1495  cf->search_mode = 0;
1496  cf->excursion = 0;
1498  break;
1499 
1501  if (vec_len (cf->current_command))
1502  {
1503  if (cf->cursor < vec_len (cf->current_command))
1504  {
1505  /* shift everything to the right of the cursor left by 1 */
1506  j = vec_len (cf->current_command) - cf->cursor - 1;
1507  memmove (cf->current_command + cf->cursor,
1508  cf->current_command + cf->cursor + 1, j);
1509  _vec_len (cf->current_command)--;
1510  /* redraw the rest of the line */
1512  cf->current_command + cf->cursor,
1513  j);
1514  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " \b", 2);
1515  /* and shift the terminal cursor back where it should be */
1516  if (j)
1517  {
1518  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1519  while (--j)
1520  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1521  }
1522  }
1523  }
1524  else if (input == 'D' - '@')
1525  {
1526  /* ^D with no command entered = quit */
1527  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
1531  cf - cm->cli_file_pool);
1532  }
1533  cf->search_mode = 0;
1534  cf->excursion = 0;
1536  break;
1537 
1539  /* If we're in ANSI mode, clear the screen.
1540  * Then redraw the prompt and any existing command input, then put
1541  * the cursor back where it was in that line.
1542  */
1543  if (cf->ansi_capable)
1545  (u8 *) ANSI_CLEAR,
1546  sizeof (ANSI_CLEAR) - 1);
1547  else
1548  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1549 
1550  unix_vlib_cli_output_raw (cf, uf,
1551  cm->cli_prompt, vec_len (cm->cli_prompt));
1552  unix_vlib_cli_output_raw (cf, uf,
1553  cf->current_command,
1554  vec_len (cf->current_command));
1555  for (j = cf->cursor; j < vec_len (cf->current_command); j++)
1556  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1557 
1558  break;
1559 
1561  if (cf->cursor < vec_len (cf->current_command))
1562  {
1563  /* if we are in the middle of a line, complete only if
1564  * the cursor points to whitespace */
1565  if (isspace (cf->current_command[cf->cursor]))
1566  {
1567  /* save and clear any input that is after the cursor */
1568  vec_resize (save, vec_len (cf->current_command) - cf->cursor);
1569  clib_memcpy (save, cf->current_command + cf->cursor,
1570  vec_len (cf->current_command) - cf->cursor);
1571  _vec_len (cf->current_command) = cf->cursor;
1572  }
1573  else
1574  {
1575  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
1576  break;
1577  }
1578  }
1579  possible_commands =
1581  if (vec_len (possible_commands) == 1)
1582  {
1583  u32 j = cf->cursor;
1584  u8 *completed = possible_commands[0];
1585 
1586  /* find the last word of current_command */
1587  while (j >= 1 && !isspace (cf->current_command[j - 1]))
1588  {
1589  j--;
1590  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\b", 1);
1591  }
1592  _vec_len (cf->current_command) = j;
1593 
1594  /* replace it with the newly expanded command */
1595  vec_append (cf->current_command, completed);
1596 
1597  /* echo to the terminal */
1598  unix_vlib_cli_output_raw (cf, uf, completed, vec_len (completed));
1599 
1600  /* add one trailing space if needed */
1601  if (vec_len (save) == 0)
1602  {
1603  vec_add1 (cf->current_command, ' ');
1604  unix_vlib_cli_output_raw (cf, uf, (u8 *) " ", 1);
1605  }
1606 
1607  cf->cursor = vec_len (cf->current_command);
1608 
1609  }
1610  else if (vec_len (possible_commands) >= 2)
1611  {
1612  u8 **possible_command;
1613  uword max_command_len = 0, min_command_len = ~0;
1614  u32 i, j;
1615 
1616  vec_foreach (possible_command, possible_commands)
1617  {
1618  if (vec_len (*possible_command) > max_command_len)
1619  {
1620  max_command_len = vec_len (*possible_command);
1621  }
1622  if (vec_len (*possible_command) < min_command_len)
1623  {
1624  min_command_len = vec_len (*possible_command);
1625  }
1626  }
1627 
1628  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1629 
1630  i = 0;
1631  vec_foreach (possible_command, possible_commands)
1632  {
1633  if (i + max_command_len >= cf->width)
1634  {
1635  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1636  i = 0;
1637  }
1638  unix_vlib_cli_output_raw (cf, uf, *possible_command,
1639  vec_len (*possible_command));
1640  for (j = vec_len (*possible_command); j < max_command_len + 2;
1641  j++)
1642  {
1643  unix_vlib_cli_output_raw (cf, uf, (u8 *) " ", 1);
1644  }
1645  i += max_command_len + 2;
1646  }
1647 
1648  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1649 
1650  /* rewrite prompt */
1651  unix_cli_cli_prompt (cf, uf);
1653  vec_len (cf->current_command));
1654 
1655  /* count length of last word */
1656  j = cf->cursor;
1657  i = 0;
1658  while (j >= 1 && !isspace (cf->current_command[j - 1]))
1659  {
1660  j--;
1661  i++;
1662  }
1663 
1664  /* determine smallest common command */
1665  for (; i < min_command_len; i++)
1666  {
1667  u8 common = '\0';
1668  int stop = 0;
1669  vec_foreach (possible_command, possible_commands)
1670  {
1671  if (common == '\0')
1672  {
1673  common = (*possible_command)[i];
1674  }
1675  else if (common != (*possible_command)[i])
1676  {
1677  stop = 1;
1678  break;
1679  }
1680  }
1681  if (!stop)
1682  {
1683  vec_add1 (cf->current_command, common);
1684  cf->cursor++;
1685  unix_vlib_cli_output_raw (cf, uf, (u8 *) & common, 1);
1686  }
1687  else
1688  {
1689  break;
1690  }
1691  }
1692  }
1693  else
1694  {
1695  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\a", 1);
1696  }
1697 
1698  if (vec_len (save) > 0)
1699  {
1700  /* restore remaining input if tab was hit in the middle of a line */
1701  unix_vlib_cli_output_raw (cf, uf, save, vec_len (save));
1702  for (j = 0; j < vec_len (save); j++)
1703  {
1704  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\b", 1);
1705  }
1706  vec_append (cf->current_command, save);
1707  vec_free (save);
1708  }
1709  vec_free (possible_commands);
1710 
1711  break;
1713  /* TODO */
1714  break;
1715 
1716 
1718  pager_quit:
1719  unix_cli_pager_prompt_erase (cf, uf);
1720  unix_cli_pager_reset (cf);
1721  unix_cli_cli_prompt (cf, uf);
1722  break;
1723 
1726  /* show next page of the buffer */
1727  if (cf->height + cf->pager_start < vec_len (cf->pager_index))
1728  {
1729  u8 *line = NULL;
1731 
1732  int m = cf->pager_start + (cf->height - 1);
1733  unix_cli_pager_prompt_erase (cf, uf);
1734  for (j = m;
1735  j < vec_len (cf->pager_index) && cf->pager_start < m;
1736  j++, cf->pager_start++)
1737  {
1738  pi = &cf->pager_index[j];
1739  line = cf->pager_vector[pi->line] + pi->offset;
1740  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1741  }
1742  /* if the last line didn't end in newline, add a newline */
1743  if (pi && line[pi->length - 1] != '\n')
1744  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1745  unix_cli_pager_prompt (cf, uf);
1746  }
1747  else
1748  {
1749  if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
1750  /* no more in buffer, exit, but only if it was <space> */
1751  goto pager_quit;
1752  }
1753  break;
1754 
1757  /* display the next line of the buffer */
1758  if (cf->pager_start < vec_len (cf->pager_index) - (cf->height - 1))
1759  {
1760  u8 *line;
1762 
1763  unix_cli_pager_prompt_erase (cf, uf);
1764  pi = &cf->pager_index[cf->pager_start + (cf->height - 1)];
1765  line = cf->pager_vector[pi->line] + pi->offset;
1766  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1767  cf->pager_start++;
1768  /* if the last line didn't end in newline, add a newline */
1769  if (line[pi->length - 1] != '\n')
1770  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1771  unix_cli_pager_prompt (cf, uf);
1772  }
1773  else
1774  {
1775  if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
1776  /* no more in buffer, exit, but only if it was <enter> */
1777  goto pager_quit;
1778  }
1779 
1780  break;
1781 
1783  /* scroll the page back one line */
1784  if (cf->pager_start > 0)
1785  {
1786  u8 *line = NULL;
1788 
1789  cf->pager_start--;
1790  if (cf->ansi_capable)
1791  {
1792  pi = &cf->pager_index[cf->pager_start];
1793  line = cf->pager_vector[pi->line] + pi->offset;
1794  unix_cli_pager_prompt_erase (cf, uf);
1796  sizeof (ANSI_SCROLLDN) - 1);
1798  sizeof (ANSI_SAVECURSOR) - 1);
1799  unix_cli_ansi_cursor (cf, uf, 1, 1);
1801  sizeof (ANSI_CLEARLINE) - 1);
1802  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1804  sizeof (ANSI_RESTCURSOR) - 1);
1805  unix_cli_pager_prompt_erase (cf, uf);
1806  unix_cli_pager_prompt (cf, uf);
1807  }
1808  else
1809  {
1810  int m = cf->pager_start + (cf->height - 1);
1811  unix_cli_pager_prompt_erase (cf, uf);
1812  for (j = cf->pager_start;
1813  j < vec_len (cf->pager_index) && j < m; j++)
1814  {
1815  pi = &cf->pager_index[j];
1816  line = cf->pager_vector[pi->line] + pi->offset;
1817  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1818  }
1819  /* if the last line didn't end in newline, add a newline */
1820  if (pi && line[pi->length - 1] != '\n')
1821  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1822  unix_cli_pager_prompt (cf, uf);
1823  }
1824  }
1825  break;
1826 
1828  /* back to the first page of the buffer */
1829  if (cf->pager_start > 0)
1830  {
1831  u8 *line = NULL;
1833 
1834  cf->pager_start = 0;
1835  int m = cf->pager_start + (cf->height - 1);
1836  unix_cli_pager_prompt_erase (cf, uf);
1837  for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
1838  j++)
1839  {
1840  pi = &cf->pager_index[j];
1841  line = cf->pager_vector[pi->line] + pi->offset;
1842  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1843  }
1844  /* if the last line didn't end in newline, add a newline */
1845  if (pi && line[pi->length - 1] != '\n')
1846  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1847  unix_cli_pager_prompt (cf, uf);
1848  }
1849  break;
1850 
1852  /* skip to the last page of the buffer */
1853  if (cf->pager_start < vec_len (cf->pager_index) - (cf->height - 1))
1854  {
1855  u8 *line = NULL;
1857 
1858  cf->pager_start = vec_len (cf->pager_index) - (cf->height - 1);
1859  unix_cli_pager_prompt_erase (cf, uf);
1860  unix_cli_pager_message (cf, uf, "skipping", "\n");
1861  for (j = cf->pager_start; j < vec_len (cf->pager_index); j++)
1862  {
1863  pi = &cf->pager_index[j];
1864  line = cf->pager_vector[pi->line] + pi->offset;
1865  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1866  }
1867  /* if the last line didn't end in newline, add a newline */
1868  if (pi && line[pi->length - 1] != '\n')
1869  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1870  unix_cli_pager_prompt (cf, uf);
1871  }
1872  break;
1873 
1875  /* wander back one page in the buffer */
1876  if (cf->pager_start > 0)
1877  {
1878  u8 *line = NULL;
1880  int m;
1881 
1882  if (cf->pager_start >= cf->height)
1883  cf->pager_start -= cf->height - 1;
1884  else
1885  cf->pager_start = 0;
1886  m = cf->pager_start + cf->height - 1;
1887  unix_cli_pager_prompt_erase (cf, uf);
1888  for (j = cf->pager_start; j < vec_len (cf->pager_index) && j < m;
1889  j++)
1890  {
1891  pi = &cf->pager_index[j];
1892  line = cf->pager_vector[pi->line] + pi->offset;
1893  unix_vlib_cli_output_cooked (cf, uf, line, pi->length);
1894  }
1895  /* if the last line didn't end in newline, add a newline */
1896  if (pi && line[pi->length - 1] != '\n')
1897  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1898  unix_cli_pager_prompt (cf, uf);
1899  }
1900  break;
1901 
1903  /* Redraw the current pager screen */
1904  unix_cli_pager_redraw (cf, uf);
1905  break;
1906 
1908  /* search forwards in the buffer */
1909  break;
1910 
1911 
1913  crlf:
1914  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1915 
1916  if (cf->has_history && cf->history_limit)
1917  {
1918  if (cf->command_history
1919  && vec_len (cf->command_history) >= cf->history_limit)
1920  {
1921  vec_free (cf->command_history[0]);
1922  vec_delete (cf->command_history, 1, 0);
1923  }
1924  /* Don't add blank lines to the cmd history */
1925  if (vec_len (cf->current_command))
1926  {
1927  /* Don't duplicate the previous command */
1928  j = vec_len (cf->command_history);
1929  if (j == 0 ||
1930  (vec_len (cf->current_command) !=
1931  vec_len (cf->command_history[j - 1])
1932  || memcmp (cf->current_command, cf->command_history[j - 1],
1933  vec_len (cf->current_command)) != 0))
1934  {
1935  /* copy the command to the history */
1936  u8 *c = 0;
1937  vec_append (c, cf->current_command);
1938  vec_add1 (cf->command_history, c);
1939  cf->command_number++;
1940  }
1941  }
1942  cf->excursion = vec_len (cf->command_history);
1943  }
1944 
1945  cf->search_mode = 0;
1947  cf->cursor = 0;
1948 
1949  return 0;
1950 
1953  if (vec_len (cf->pager_index))
1954  {
1955  /* no-op for now */
1956  }
1957  else if (cf->has_history && cf->search_mode && isprint (input))
1958  {
1959  int k, limit, offset;
1960  u8 *item;
1961 
1962  vec_add1 (cf->search_key, input);
1963 
1964  search_again:
1965  for (j = 0; j < vec_len (cf->command_history); j++)
1966  {
1967  if (cf->excursion > (i32) vec_len (cf->command_history) - 1)
1968  cf->excursion = 0;
1969  else if (cf->excursion < 0)
1970  cf->excursion = vec_len (cf->command_history) - 1;
1971 
1972  item = cf->command_history[cf->excursion];
1973 
1974  limit = (vec_len (cf->search_key) > vec_len (item)) ?
1975  vec_len (item) : vec_len (cf->search_key);
1976 
1977  for (offset = 0; offset <= vec_len (item) - limit; offset++)
1978  {
1979  for (k = 0; k < limit; k++)
1980  {
1981  if (item[k + offset] != cf->search_key[k])
1982  goto next_offset;
1983  }
1984  goto found_at_offset;
1985 
1986  next_offset:
1987  ;
1988  }
1989  goto next;
1990 
1991  found_at_offset:
1992  for (j = 0; j < vec_len (cf->current_command); j++)
1993  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1994 
1995  vec_validate (cf->current_command, vec_len (item) - 1);
1996  clib_memcpy (cf->current_command, item, vec_len (item));
1997  _vec_len (cf->current_command) = vec_len (item);
1998 
2000  vec_len (cf->current_command));
2001  cf->cursor = vec_len (cf->current_command);
2002  goto found;
2003 
2004  next:
2005  cf->excursion += cf->search_mode;
2006  }
2007 
2008  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\nNo match...", 12);
2011  cf->search_mode = 0;
2012  cf->cursor = 0;
2013  goto crlf;
2014  }
2015  else if (isprint (input)) /* skip any errant control codes */
2016  {
2017  if (cf->cursor == vec_len (cf->current_command))
2018  {
2019  /* Append to end */
2020  vec_add1 (cf->current_command, input);
2021  cf->cursor++;
2022 
2023  /* Echo the character back to the client */
2024  unix_vlib_cli_output_raw (cf, uf, &input, 1);
2025  }
2026  else
2027  {
2028  /* Insert at cursor: resize +1 byte, move everything over */
2029  j = vec_len (cf->current_command) - cf->cursor;
2030  vec_add1 (cf->current_command, (u8) 'A');
2031  memmove (cf->current_command + cf->cursor + 1,
2032  cf->current_command + cf->cursor, j);
2033  cf->current_command[cf->cursor] = input;
2034  /* Redraw the line */
2035  j++;
2036  unix_vlib_cli_output_raw (cf, uf,
2037  cf->current_command + cf->cursor, j);
2038  /* Put terminal cursor back */
2039  while (--j)
2040  unix_vlib_cli_output_raw (cf, uf, (u8 *) "\b", 1);
2041  cf->cursor++;
2042  }
2043  }
2044  else
2045  {
2046  /* no-op - not printable or otherwise not actionable */
2047  }
2048 
2049  found:
2050 
2051  break;
2052 
2054  break;
2055  }
2056  return 1;
2057 }
2058 
2059 /** @brief Process input bytes on a stream to provide line editing and
2060  * command history in the CLI. */
2061 static int
2063  unix_main_t * um, unix_cli_file_t * cf)
2064 {
2066  int i;
2067 
2068  for (i = 0; i < vec_len (cf->input_vector); i++)
2069  {
2070  unix_cli_parse_action_t action;
2071  i32 matched = 0;
2073 
2074  /* If we're in the pager mode, search the pager actions */
2075  a =
2076  vec_len (cf->pager_index) ? unix_cli_parse_pager :
2078 
2079  /* See if the input buffer is some sort of control code */
2080  action = unix_cli_match_action (a, &cf->input_vector[i],
2081  vec_len (cf->input_vector) - i,
2082  &matched);
2083 
2084  switch (action)
2085  {
2087  if (i)
2088  {
2089  /* There was a partial match which means we need more bytes
2090  * than the input buffer currently has.
2091  * Since the bytes before here have been processed, shift
2092  * the remaining contents to the start of the input buffer.
2093  */
2094  vec_delete (cf->input_vector, i, 0);
2095  }
2096  return 1; /* wait for more */
2097 
2099  /* process telnet options */
2100  matched = unix_cli_process_telnet (um, cf, uf,
2101  cf->input_vector + i,
2102  vec_len (cf->input_vector) - i);
2103  if (matched < 0)
2104  {
2105  if (i)
2106  {
2107  /* There was a partial match which means we need more bytes
2108  * than the input buffer currently has.
2109  * Since the bytes before here have been processed, shift
2110  * the remaining contents to the start of the input buffer.
2111  */
2112  vec_delete (cf->input_vector, i, 0);
2113  }
2114  return 1; /* wait for more */
2115  }
2116  break;
2117 
2118  default:
2119  /* process the action */
2120  if (!unix_cli_line_process_one (cm, um, cf, uf,
2121  cf->input_vector[i], action))
2122  {
2123  /* CRLF found. Consume the bytes from the input_vector */
2124  vec_delete (cf->input_vector, i + matched, 0);
2125  /* And tell our caller to execute cf->input_command */
2126  return 0;
2127  }
2128  }
2129 
2130  i += matched;
2131  }
2132 
2134  return 1;
2135 }
2136 
2137 /** @brief Process input to a CLI session. */
2138 static void
2140 {
2141  unix_main_t *um = &unix_main;
2142  unix_file_t *uf;
2143  unix_cli_file_t *cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2144  unformat_input_t input;
2145  int vlib_parse_eval (u8 *);
2146 
2147 more:
2148  /* Try vlibplex first. Someday... */
2149  if (0 && vlib_parse_eval (cf->input_vector) == 0)
2150  goto done;
2151 
2152  if (cf->line_mode)
2153  {
2154  /* just treat whatever we got as a complete line of input */
2155  cf->current_command = cf->input_vector;
2156  }
2157  else
2158  {
2159  /* Line edit, echo, etc. */
2160  if (unix_cli_line_edit (cm, um, cf))
2161  /* want more input */
2162  return;
2163  }
2164 
2165  if (um->log_fd)
2166  {
2167  static u8 *lv;
2168  vec_reset_length (lv);
2169  lv = format (lv, "%U[%d]: %v",
2170  format_timeval, 0 /* current bat-time */ ,
2171  0 /* current bat-format */ ,
2172  cli_file_index, cf->input_vector);
2173  {
2174  int rv __attribute__ ((unused)) =
2175  write (um->log_fd, lv, vec_len (lv));
2176  }
2177  }
2178 
2179  /* Copy our input command to a new string */
2180  unformat_init_vector (&input, cf->current_command);
2181 
2182  /* Remove leading white space from input. */
2183  (void) unformat (&input, "");
2184 
2185  cm->current_input_file_index = cli_file_index;
2186  cf->pager_start = 0; /* start a new pager session */
2187 
2190  cli_file_index);
2191 
2192  /* Zero buffer since otherwise unformat_free will call vec_free on it. */
2193  input.buffer = 0;
2194 
2195  unformat_free (&input);
2196 
2197  /* Re-fetch pointer since pool may have moved. */
2198  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2200 
2201 done:
2202  /* reset vector; we'll re-use it later */
2203  if (cf->line_mode)
2205  else
2207 
2208  if (cf->no_pager == 2)
2209  {
2210  /* Pager was programmatically disabled */
2211  unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
2212  cf->no_pager = um->cli_no_pager;
2213  }
2214 
2215  if (vec_len (cf->pager_index) == 0
2216  || vec_len (cf->pager_index) < cf->height)
2217  {
2218  /* There was no need for the pager */
2219  unix_cli_pager_reset (cf);
2220 
2221  /* Prompt. */
2222  unix_cli_cli_prompt (cf, uf);
2223  }
2224  else
2225  {
2226  /* Display the pager prompt */
2227  unix_cli_pager_prompt (cf, uf);
2228  }
2229 
2230  /* Any residual data in the input vector? */
2231  if (vec_len (cf->input_vector))
2232  goto more;
2233 }
2234 
2235 /** Destroy a CLI session.
2236  * @note If we destroy the @c stdin session this additionally signals
2237  * the shutdown of VPP.
2238  */
2239 static void
2240 unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
2241 {
2242  unix_main_t *um = &unix_main;
2243  unix_cli_file_t *cf;
2244  unix_file_t *uf;
2245  int i;
2246 
2247  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
2249 
2250  /* Quit/EOF on stdin means quit program. */
2253 
2254  vec_free (cf->current_command);
2255  vec_free (cf->search_key);
2256 
2257  for (i = 0; i < vec_len (cf->command_history); i++)
2258  vec_free (cf->command_history[i]);
2259 
2260  vec_free (cf->command_history);
2261 
2262  unix_file_del (um, uf);
2263 
2264  unix_cli_file_free (cf);
2265  pool_put (cm->cli_file_pool, cf);
2266 }
2267 
2268 /** Handle system events. */
2269 static uword
2272 {
2274  uword i, *data = 0;
2275 
2276  while (1)
2277  {
2278  unix_cli_process_event_type_t event_type;
2280  event_type = vlib_process_get_events (vm, &data);
2281 
2282  switch (event_type)
2283  {
2285  for (i = 0; i < vec_len (data); i++)
2286  unix_cli_process_input (cm, data[i]);
2287  break;
2288 
2290  /* Kill this process. */
2291  for (i = 0; i < vec_len (data); i++)
2292  unix_cli_kill (cm, data[i]);
2293  goto done;
2294  }
2295 
2296  if (data)
2297  _vec_len (data) = 0;
2298  }
2299 
2300 done:
2301  vec_free (data);
2302 
2303  vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
2304 
2305  /* Add node index so we can re-use this process later. */
2307 
2308  return 0;
2309 }
2310 
2311 /** Called when a CLI session file descriptor can be written to without
2312  * blocking. */
2313 static clib_error_t *
2315 {
2317  unix_cli_file_t *cf;
2318  int n;
2319 
2321 
2322  /* Flush output vector. */
2323  n = write (uf->file_descriptor,
2324  cf->output_vector, vec_len (cf->output_vector));
2325 
2326  if (n < 0 && errno != EAGAIN)
2327  return clib_error_return_unix (0, "write");
2328 
2329  else if (n > 0)
2330  unix_cli_del_pending_output (uf, cf, n);
2331 
2332  return /* no error */ 0;
2333 }
2334 
2335 /** Called when a CLI session file descriptor has data to be read. */
2336 static clib_error_t *
2338 {
2339  unix_main_t *um = &unix_main;
2341  unix_cli_file_t *cf;
2342  uword l;
2343  int n, n_read, n_try;
2344 
2346 
2347  n = n_try = 4096;
2348  while (n == n_try)
2349  {
2350  l = vec_len (cf->input_vector);
2351  vec_resize (cf->input_vector, l + n_try);
2352 
2353  n = read (uf->file_descriptor, cf->input_vector + l, n_try);
2354 
2355  /* Error? */
2356  if (n < 0 && errno != EAGAIN)
2357  return clib_error_return_unix (0, "read");
2358 
2359  n_read = n < 0 ? 0 : n;
2360  _vec_len (cf->input_vector) = l + n_read;
2361  }
2362 
2363  if (!(n < 0))
2365  cf->process_node_index,
2366  (n_read == 0
2369  /* event data */ uf->private_data);
2370 
2371  return /* no error */ 0;
2372 }
2373 
2374 /** Store a new CLI session.
2375  * @param name The name of the session.
2376  * @param fd The file descriptor for the session I/O.
2377  * @return The session ID.
2378  */
2379 static u32
2380 unix_cli_file_add (unix_cli_main_t * cm, char *name, int fd)
2381 {
2382  unix_main_t *um = &unix_main;
2383  unix_cli_file_t *cf;
2384  unix_file_t template = { 0 };
2385  vlib_main_t *vm = um->vlib_main;
2386  vlib_node_t *n;
2387 
2388  name = (char *) format (0, "unix-cli-%s", name);
2389 
2391  {
2393 
2394  /* Find node and give it new name. */
2395  n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);
2396  vec_free (n->name);
2397  n->name = (u8 *) name;
2398 
2399  vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
2400 
2401  _vec_len (cm->unused_cli_process_node_indices) = l - 1;
2402  }
2403  else
2404  {
2405  static vlib_node_registration_t r = {
2406  .function = unix_cli_process,
2407  .type = VLIB_NODE_TYPE_PROCESS,
2408  .process_log2_n_stack_bytes = 16,
2409  };
2410 
2411  r.name = name;
2412  vlib_register_node (vm, &r);
2413  vec_free (name);
2414 
2415  n = vlib_get_node (vm, r.index);
2416  }
2417 
2418  pool_get (cm->cli_file_pool, cf);
2419  memset (cf, 0, sizeof (*cf));
2420 
2421  template.read_function = unix_cli_read_ready;
2422  template.write_function = unix_cli_write_ready;
2423  template.file_descriptor = fd;
2424  template.private_data = cf - cm->cli_file_pool;
2425 
2426  cf->process_node_index = n->index;
2427  cf->unix_file_index = unix_file_add (um, &template);
2428  cf->output_vector = 0;
2429  cf->input_vector = 0;
2430 
2432 
2435  p->output_function_arg = cf - cm->cli_file_pool;
2436 
2437  return cf - cm->cli_file_pool;
2438 }
2439 
2440 /** Telnet listening socket has a new connection. */
2441 static clib_error_t *
2443 {
2444  unix_main_t *um = &unix_main;
2446  clib_socket_t *s = &um->cli_listen_socket;
2447  clib_socket_t client;
2448  char *client_name;
2449  clib_error_t *error;
2450  unix_cli_file_t *cf;
2451  u32 cf_index;
2452 
2453  error = clib_socket_accept (s, &client);
2454  if (error)
2455  return error;
2456 
2457  client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
2458 
2459  cf_index = unix_cli_file_add (cm, client_name, client.fd);
2460  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2461 
2462  /* No longer need CLIB version of socket. */
2463  clib_socket_free (&client);
2464 
2465  vec_free (client_name);
2466 
2467  /* if we're supposed to run telnet session in character mode (default) */
2468  if (um->cli_line_mode == 0)
2469  {
2470  /*
2471  * Set telnet client character mode, echo on, suppress "go-ahead".
2472  * Technically these should be negotiated, but this works.
2473  */
2474  u8 charmode_option[] = {
2475  IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */
2476  IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */
2477  IAC, WILL, TELOPT_SGA, /* server willl supress GA */
2478  IAC, DO, TELOPT_SGA, /* client should supress Go Ahead */
2479  IAC, WILL, TELOPT_ECHO, /* server will do echo */
2480  IAC, DONT, TELOPT_ECHO, /* client should not echo */
2481  IAC, DO, TELOPT_TTYPE, /* client should tell us its term type */
2482  IAC, SB, TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */
2483  IAC, DO, TELOPT_NAWS, /* client should tell us its window sz */
2484  IAC, SB, TELOPT_NAWS, 1, IAC, SE, /* now tell me window size */
2485  };
2486 
2487  /* Enable history on this CLI */
2488  cf->history_limit = um->cli_history_limit;
2489  cf->has_history = cf->history_limit != 0;
2490 
2491  /* Make sure this session is in line mode */
2492  cf->line_mode = 0;
2493 
2494  /* We need CRLF */
2495  cf->crlf_mode = 1;
2496 
2497  /* Setup the pager */
2498  cf->no_pager = um->cli_no_pager;
2499 
2501 
2502  /* Send the telnet options */
2503  unix_vlib_cli_output_raw (cf, uf, charmode_option,
2504  ARRAY_LEN (charmode_option));
2505 
2506  /* In case the client doesn't negotiate terminal type, use
2507  * a timer to kick off the initial prompt. */
2508  timer_call (unix_cli_file_welcome_timer, cf_index, 1);
2509  }
2510 
2511  return error;
2512 }
2513 
2514 /** The system terminal has informed us that the window size
2515  * has changed.
2516  */
2517 static void
2519 {
2520  unix_main_t *um = &unix_main;
2523  cm->stdin_cli_file_index);
2525  struct winsize ws;
2526  (void) signum;
2527 
2528  /* Terminal resized, fetch the new size */
2529  if (ioctl (UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws) < 0)
2530  {
2531  /* "Should never happen..." */
2532  clib_unix_warning ("TIOCGWINSZ");
2533  /* We can't trust ws.XXX... */
2534  return;
2535  }
2536  cf->width = ws.ws_col;
2537  cf->height = ws.ws_row;
2538 
2539  /* Reindex the pager buffer */
2541 
2542  /* Redraw the page */
2543  unix_cli_pager_redraw (cf, uf);
2544 }
2545 
2546 /** Handle configuration directives in the @em unix section. */
2547 static clib_error_t *
2549 {
2550  unix_main_t *um = &unix_main;
2552  int flags;
2553  clib_error_t *error = 0;
2554  unix_cli_file_t *cf;
2555  u32 cf_index;
2556  struct termios tio;
2557  struct sigaction sa;
2558  struct winsize ws;
2559  u8 *term;
2560 
2561  /* We depend on unix flags being set. */
2562  if ((error = vlib_call_config_function (vm, unix_config)))
2563  return error;
2564 
2565  if (um->flags & UNIX_FLAG_INTERACTIVE)
2566  {
2567  /* Set stdin to be non-blocking. */
2568  if ((flags = fcntl (UNIX_CLI_STDIN_FD, F_GETFL, 0)) < 0)
2569  flags = 0;
2570  (void) fcntl (UNIX_CLI_STDIN_FD, F_SETFL, flags | O_NONBLOCK);
2571 
2572  cf_index = unix_cli_file_add (cm, "stdin", UNIX_CLI_STDIN_FD);
2573  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2574  cm->stdin_cli_file_index = cf_index;
2575 
2576  /* If stdin is a tty and we are using chacracter mode, enable
2577  * history on the CLI and set the tty line discipline accordingly. */
2578  if (isatty (UNIX_CLI_STDIN_FD) && um->cli_line_mode == 0)
2579  {
2580  /* Capture terminal resize events */
2581  memset (&sa, 0, sizeof (sa));
2582  sa.sa_handler = unix_cli_resize_interrupt;
2583  if (sigaction (SIGWINCH, &sa, 0) < 0)
2584  clib_panic ("sigaction");
2585 
2586  /* Retrieve the current terminal size */
2587  ioctl (UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws);
2588  cf->width = ws.ws_col;
2589  cf->height = ws.ws_row;
2590 
2591  if (cf->width == 0 || cf->height == 0)
2592  /* We have a tty, but no size. Stick to line mode. */
2593  goto notty;
2594 
2595  /* Setup the history */
2596  cf->history_limit = um->cli_history_limit;
2597  cf->has_history = cf->history_limit != 0;
2598 
2599  /* Setup the pager */
2600  cf->no_pager = um->cli_no_pager;
2601 
2602  /* We're going to be in char by char mode */
2603  cf->line_mode = 0;
2604 
2605  /* Save the original tty state so we can restore it later */
2606  tcgetattr (UNIX_CLI_STDIN_FD, &um->tio_stdin);
2607  um->tio_isset = 1;
2608 
2609  /* Tweak the tty settings */
2610  tio = um->tio_stdin;
2611  /* echo off, canonical mode off, ext'd input processing off */
2612  tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
2613  tio.c_cc[VMIN] = 1; /* 1 byte at a time */
2614  tio.c_cc[VTIME] = 0; /* no timer */
2615  tcsetattr (UNIX_CLI_STDIN_FD, TCSAFLUSH, &tio);
2616 
2617  /* See if we can do ANSI/VT100 output */
2618  term = (u8 *) getenv ("TERM");
2619  if (term != NULL)
2621  strlen ((char *)
2622  term));
2623  }
2624  else
2625  {
2626  notty:
2627  /* No tty, so make sure these things are off */
2628  cf->no_pager = 1;
2629  cf->history_limit = 0;
2630  cf->has_history = 0;
2631  cf->line_mode = 1;
2632  }
2633 
2634  /* Send banner and initial prompt */
2635  unix_cli_file_welcome (cm, cf);
2636  }
2637 
2638  /* If we have socket config, LISTEN, otherwise, don't */
2639  clib_socket_t *s = &um->cli_listen_socket;
2640  if (s->config && s->config[0] != 0)
2641  {
2642  /* CLI listen. */
2643  unix_file_t template = { 0 };
2644 
2645  s->flags = SOCKET_IS_SERVER | /* listen, don't connect */
2646  SOCKET_ALLOW_GROUP_WRITE; /* PF_LOCAL socket only */
2647  error = clib_socket_init (s);
2648 
2649  if (error)
2650  return error;
2651 
2652  template.read_function = unix_cli_listen_read_ready;
2653  template.file_descriptor = s->fd;
2654 
2655  unix_file_add (um, &template);
2656  }
2657 
2658  /* Set CLI prompt. */
2659  if (!cm->cli_prompt)
2660  cm->cli_prompt = format (0, "VLIB: ");
2661 
2662  return 0;
2663 }
2664 
2665 /*?
2666  * This module has no configurable parameters.
2667 ?*/
2669 
2670 /** Called when VPP is shutting down, this restores the system
2671  * terminal state if previously saved.
2672  */
2673 static clib_error_t *
2675 {
2676  unix_main_t *um = &unix_main;
2677 
2678  /* If stdin is a tty and we saved the tty state, reset the tty state */
2679  if (isatty (UNIX_CLI_STDIN_FD) && um->tio_isset)
2680  tcsetattr (UNIX_CLI_STDIN_FD, TCSAFLUSH, &um->tio_stdin);
2681 
2682  return 0;
2683 }
2684 
2686 
2687 /** Set the CLI prompt.
2688  * @param prompt The C string to set the prompt to.
2689  * @note This setting is global; it impacts all current
2690  * and future CLI sessions.
2691  */
2692 void
2694 {
2695  char *fmt = (prompt[strlen (prompt) - 1] == ' ') ? "%s" : "%s ";
2697  if (cm->cli_prompt)
2698  vec_free (cm->cli_prompt);
2699  cm->cli_prompt = format (0, fmt, prompt);
2700 }
2701 
2702 /** CLI command to quit the terminal session.
2703  * @note If this is a stdin session then this will
2704  * shutdown VPP also.
2705  */
2706 static clib_error_t *
2708  unformat_input_t * input, vlib_cli_command_t * cmd)
2709 {
2711 
2713  vlib_current_process (vm),
2716  return 0;
2717 }
2718 
2719 /*?
2720  * Terminates the current CLI session.
2721  *
2722  * If VPP is running in @em interactive mode and this is the console session
2723  * (that is, the session on @c stdin) then this will also terminate VPP.
2724 ?*/
2725 /* *INDENT-OFF* */
2726 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
2727  .path = "quit",
2728  .short_help = "Exit CLI",
2729  .function = unix_cli_quit,
2730 };
2731 /* *INDENT-ON* */
2732 
2733 /** CLI command to execute a VPP command script. */
2734 static clib_error_t *
2736  unformat_input_t * input, vlib_cli_command_t * cmd)
2737 {
2738  char *file_name;
2739  int fd;
2740  unformat_input_t sub_input;
2741  clib_error_t *error;
2742 
2743  file_name = 0;
2744  fd = -1;
2745  error = 0;
2746 
2747  if (!unformat (input, "%s", &file_name))
2748  {
2749  error = clib_error_return (0, "expecting file name, got `%U'",
2750  format_unformat_error, input);
2751  goto done;
2752  }
2753 
2754  fd = open (file_name, O_RDONLY);
2755  if (fd < 0)
2756  {
2757  error = clib_error_return_unix (0, "failed to open `%s'", file_name);
2758  goto done;
2759  }
2760 
2761  /* Make sure its a regular file. */
2762  {
2763  struct stat s;
2764 
2765  if (fstat (fd, &s) < 0)
2766  {
2767  error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
2768  goto done;
2769  }
2770 
2771  if (!(S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
2772  {
2773  error = clib_error_return (0, "not a regular file `%s'", file_name);
2774  goto done;
2775  }
2776  }
2777 
2778  unformat_init_unix_file (&sub_input, fd);
2779 
2780  vlib_cli_input (vm, &sub_input, 0, 0);
2781  unformat_free (&sub_input);
2782 
2783 done:
2784  if (fd > 0)
2785  close (fd);
2786  vec_free (file_name);
2787 
2788  return error;
2789 }
2790 
2791 /*?
2792  * Executes a sequence of CLI commands which are read from a file.
2793  *
2794  * If a command is unrecognised or otherwise invalid then the usual CLI
2795  * feedback will be generated, however execution of subsequent commands
2796  * from the file will continue.
2797 ?*/
2798 /* *INDENT-OFF* */
2799 VLIB_CLI_COMMAND (cli_exec, static) = {
2800  .path = "exec",
2801  .short_help = "Execute commands from file",
2802  .function = unix_cli_exec,
2803  .is_mp_safe = 1,
2804 };
2805 /* *INDENT-ON* */
2806 
2807 /** CLI command to show various unix error statistics. */
2808 static clib_error_t *
2810  unformat_input_t * input, vlib_cli_command_t * cmd)
2811 {
2812  unix_main_t *um = &unix_main;
2813  clib_error_t *error = 0;
2814  int i, n_errors_to_show;
2815  unix_error_history_t *unix_errors = 0;
2816 
2817  n_errors_to_show = 1 << 30;
2818 
2820  {
2821  if (!unformat (input, "%d", &n_errors_to_show))
2822  {
2823  error =
2824  clib_error_return (0,
2825  "expecting integer number of errors to show, got `%U'",
2826  format_unformat_error, input);
2827  goto done;
2828  }
2829  }
2830 
2831  n_errors_to_show =
2832  clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
2833 
2834  i =
2835  um->error_history_index >
2836  0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
2837 
2838  while (n_errors_to_show > 0)
2839  {
2840  unix_error_history_t *eh = um->error_history + i;
2841 
2842  if (!eh->error)
2843  break;
2844 
2845  vec_add1 (unix_errors, eh[0]);
2846  n_errors_to_show -= 1;
2847  if (i == 0)
2848  i = ARRAY_LEN (um->error_history) - 1;
2849  else
2850  i--;
2851  }
2852 
2853  if (vec_len (unix_errors) == 0)
2854  vlib_cli_output (vm, "no Unix errors so far");
2855  else
2856  {
2857  vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
2858  for (i = vec_len (unix_errors) - 1; i >= 0; i--)
2859  {
2860  unix_error_history_t *eh = vec_elt_at_index (unix_errors, i);
2861  vlib_cli_output (vm, "%U: %U",
2862  format_time_interval, "h:m:s:u", eh->time,
2863  format_clib_error, eh->error);
2864  }
2865  vlib_cli_output (vm, "%U: time now",
2866  format_time_interval, "h:m:s:u", vlib_time_now (vm));
2867  }
2868 
2869 done:
2870  vec_free (unix_errors);
2871  return error;
2872 }
2873 
2874 /* *INDENT-OFF* */
2875 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
2876  .path = "show unix-errors",
2877  .short_help = "Show Unix system call error history",
2878  .function = unix_show_errors,
2879 };
2880 /* *INDENT-ON* */
2881 
2882 /** CLI command to show session command history. */
2883 static clib_error_t *
2885  unformat_input_t * input, vlib_cli_command_t * cmd)
2886 {
2888  unix_cli_file_t *cf;
2889  int i, j;
2890 
2892 
2893  if (cf->has_history && cf->history_limit)
2894  {
2895  i = 1 + cf->command_number - vec_len (cf->command_history);
2896  for (j = 0; j < vec_len (cf->command_history); j++)
2897  vlib_cli_output (vm, "%d %v\n", i + j, cf->command_history[j]);
2898  }
2899  else
2900  {
2901  vlib_cli_output (vm, "History not enabled.\n");
2902  }
2903 
2904  return 0;
2905 }
2906 
2907 /*?
2908  * Displays the command history for the current session, if any.
2909 ?*/
2910 /* *INDENT-OFF* */
2911 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
2912  .path = "history",
2913  .short_help = "Show current session command history",
2914  .function = unix_cli_show_history,
2915 };
2916 /* *INDENT-ON* */
2917 
2918 /** CLI command to show terminal status. */
2919 static clib_error_t *
2921  unformat_input_t * input, vlib_cli_command_t * cmd)
2922 {
2923  unix_main_t *um = &unix_main;
2925  unix_cli_file_t *cf;
2926  vlib_node_t *n;
2927 
2929  n = vlib_get_node (vm, cf->process_node_index);
2930 
2931  vlib_cli_output (vm, "Terminal name: %v\n", n->name);
2932  vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ?
2933  "line-by-line" : "char-by-char");
2934  vlib_cli_output (vm, "Terminal width: %d\n", cf->width);
2935  vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
2936  vlib_cli_output (vm, "ANSI capable: %s\n",
2937  cf->ansi_capable ? "yes" : "no");
2938  vlib_cli_output (vm, "History enabled: %s%s\n",
2939  cf->has_history ? "yes" : "no", !cf->has_history
2940  || cf->history_limit ? "" :
2941  " (disabled by history limit)");
2942  if (cf->has_history)
2943  vlib_cli_output (vm, "History limit: %d\n", cf->history_limit);
2944  vlib_cli_output (vm, "Pager enabled: %s%s%s\n",
2945  cf->no_pager ? "no" : "yes",
2946  cf->no_pager
2947  || cf->height ? "" : " (disabled by terminal height)",
2948  cf->no_pager
2949  || um->cli_pager_buffer_limit ? "" :
2950  " (disabled by buffer limit)");
2951  if (!cf->no_pager)
2952  vlib_cli_output (vm, "Pager limit: %d\n", um->cli_pager_buffer_limit);
2953  vlib_cli_output (vm, "CRLF mode: %s\n",
2954  cf->crlf_mode ? "CR+LF" : "LF");
2955 
2956  return 0;
2957 }
2958 
2959 /*?
2960  * Displays various information about the state of the current terminal
2961  * session.
2962  *
2963  * @cliexpar
2964  * @cliexstart{show terminal}
2965  * Terminal name: unix-cli-stdin
2966  * Terminal mode: char-by-char
2967  * Terminal width: 123
2968  * Terminal height: 48
2969  * ANSI capable: yes
2970  * History enabled: yes
2971  * History limit: 50
2972  * Pager enabled: yes
2973  * Pager limit: 100000
2974  * CRLF mode: LF
2975  * @cliexend
2976 ?*/
2977 /* *INDENT-OFF* */
2978 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
2979  .path = "show terminal",
2980  .short_help = "Show current session terminal settings",
2981  .function = unix_cli_show_terminal,
2982 };
2983 /* *INDENT-ON* */
2984 
2985 /** CLI command to set terminal pager settings. */
2986 static clib_error_t *
2988  unformat_input_t * input,
2989  vlib_cli_command_t * cmd)
2990 {
2991  unix_main_t *um = &unix_main;
2993  unix_cli_file_t *cf;
2994  unformat_input_t _line_input, *line_input = &_line_input;
2995  clib_error_t *error = 0;
2996 
2997  if (!unformat_user (input, unformat_line_input, line_input))
2998  return 0;
2999 
3001 
3002  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3003  {
3004  if (unformat (line_input, "on"))
3005  cf->no_pager = 0;
3006  else if (unformat (line_input, "off"))
3007  cf->no_pager = 1;
3008  else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
3009  vlib_cli_output (vm,
3010  "Pager limit set to %u lines; note, this is global.\n",
3012  else
3013  {
3014  error = clib_error_return (0, "unknown parameter: `%U`",
3015  format_unformat_error, line_input);
3016  goto done;
3017  }
3018  }
3019 
3020 done:
3021  unformat_free (line_input);
3022 
3023  return error;
3024 }
3025 
3026 /*?
3027  * Enables or disables the terminal pager for this session. Generally
3028  * this defaults to enabled.
3029  *
3030  * Additionally allows the pager buffer size to be set; though note that
3031  * this value is set globally and not per session.
3032 ?*/
3033 /* *INDENT-OFF* */
3034 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
3035  .path = "set terminal pager",
3036  .short_help = "set terminal pager [on|off] [limit <lines>]",
3037  .function = unix_cli_set_terminal_pager,
3038 };
3039 /* *INDENT-ON* */
3040 
3041 /** CLI command to set terminal history settings. */
3042 static clib_error_t *
3044  unformat_input_t * input,
3045  vlib_cli_command_t * cmd)
3046 {
3048  unix_cli_file_t *cf;
3049  unformat_input_t _line_input, *line_input = &_line_input;
3050  u32 limit;
3051  clib_error_t *error = 0;
3052 
3053  if (!unformat_user (input, unformat_line_input, line_input))
3054  return 0;
3055 
3057 
3058  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
3059  {
3060  if (unformat (line_input, "on"))
3061  cf->has_history = 1;
3062  else if (unformat (line_input, "off"))
3063  cf->has_history = 0;
3064  else if (unformat (line_input, "limit %u", &cf->history_limit))
3065  ;
3066  else
3067  {
3068  error = clib_error_return (0, "unknown parameter: `%U`",
3069  format_unformat_error, line_input);
3070  goto done;
3071  }
3072 
3073  /* If we reduced history size, or turned it off, purge the history */
3074  limit = cf->has_history ? cf->history_limit : 0;
3075 
3076  while (cf->command_history && vec_len (cf->command_history) >= limit)
3077  {
3078  vec_free (cf->command_history[0]);
3079  vec_delete (cf->command_history, 1, 0);
3080  }
3081  }
3082 
3083 done:
3084  unformat_free (line_input);
3085 
3086  return error;
3087 }
3088 
3089 /*?
3090  * Enables or disables the command history function of the current
3091  * terminal. Generally this defaults to enabled.
3092  *
3093  * This command also allows the maximum size of the history buffer for
3094  * this session to be altered.
3095 ?*/
3096 /* *INDENT-OFF* */
3097 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
3098  .path = "set terminal history",
3099  .short_help = "set terminal history [on|off] [limit <lines>]",
3100  .function = unix_cli_set_terminal_history,
3101 };
3102 /* *INDENT-ON* */
3103 
3104 /** CLI command to set terminal ANSI settings. */
3105 static clib_error_t *
3107  unformat_input_t * input,
3108  vlib_cli_command_t * cmd)
3109 {
3111  unix_cli_file_t *cf;
3112 
3114 
3115  if (unformat (input, "on"))
3116  cf->ansi_capable = 1;
3117  else if (unformat (input, "off"))
3118  cf->ansi_capable = 0;
3119  else
3120  return clib_error_return (0, "unknown parameter: `%U`",
3121  format_unformat_error, input);
3122 
3123  return 0;
3124 }
3125 
3126 /*?
3127  * Enables or disables the use of ANSI control sequences by this terminal.
3128  * The default will vary based on terminal detection at the start of the
3129  * session.
3130  *
3131  * ANSI control sequences are used in a small number of places to provide,
3132  * for example, color text output and to control the cursor in the pager.
3133 ?*/
3134 /* *INDENT-OFF* */
3135 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
3136  .path = "set terminal ansi",
3137  .short_help = "set terminal ansi [on|off]",
3138  .function = unix_cli_set_terminal_ansi,
3139 };
3140 /* *INDENT-ON* */
3141 
3142 static clib_error_t *
3144 {
3145  return 0;
3146 }
3147 
3149 
3150 /*
3151  * fd.io coding-style-patch-verification: ON
3152  *
3153  * Local Variables:
3154  * eval: (c-set-style "gnu")
3155  * End:
3156  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:436
unix_file_t * file_pool
Definition: unix.h:89
static i32 unix_cli_process_telnet(unix_main_t *um, unix_cli_file_t *cf, unix_file_t *uf, u8 *input_vector, uword len)
A mostly no-op Telnet state machine.
Definition: cli.c:1105
u32 history_limit
Maximum number of history entries this session will store.
Definition: cli.c:163
u32 command_number
Current command line counter.
Definition: cli.c:166
uword output_function_arg
Definition: node.h:551
u32 error_history_index
Definition: unix.h:99
#define vec_foreach_index(var, v)
Iterate over vector indices.
u8 * format_clib_error(u8 *s, va_list *va)
Definition: error.c:191
u32 unix_file_index
The file index held by unix.c.
Definition: cli.c:144
#define UNIX_CLI_MAX_DEPTH_TELNET
Maximum depth into a byte stream from which to compile a Telnet protocol message. ...
Definition: cli.c:92
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:337
#define clib_min(x, y)
Definition: clib.h:332
u32 pager_start
Line number of top of page.
Definition: cli.c:201
u32 flags
Definition: unix.h:54
static void unix_vlib_cli_output_cooked(unix_cli_file_t *cf, unix_file_t *uf, u8 *buffer, uword buffer_bytes)
Process a buffer for CRLF handling before outputting it to the CLI.
Definition: cli.c:612
Action parser found a partial match.
Definition: cli.c:281
Carriage return, newline or enter.
Definition: cli.c:249
Telnet control code.
Definition: cli.c:267
Search forwards in command history.
Definition: cli.c:265
unix_cli_process_event_type_t
CLI session events.
Definition: cli.c:415
a
Definition: bitmap.h:516
Exit the pager session.
Definition: cli.c:270
u8 * input_vector
Vector of input saved by Unix input node to be processed by CLI process.
Definition: cli.c:151
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 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:2920
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:305
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:467
u8 * line
The line to print.
Definition: cli.c:101
u32 flags
Definition: unix.h:83
u32 width
Terminal width.
Definition: cli.c:204
Erase cursor right.
Definition: cli.c:252
u8 * cli_prompt
Prompt string for CLI.
Definition: cli.c:425
static void unix_cli_ansi_cursor(unix_cli_file_t *cf, unix_file_t *uf, u16 x, u16 y)
Uses an ANSI escape sequence to move the cursor.
Definition: cli.c:719
unix_main_t unix_main
Definition: main.c:60
u32 cli_pager_buffer_limit
Definition: unix.h:122
#define NULL
Definition: clib.h:55
u8 ** command_history
Array of vectors of commands in the history.
Definition: cli.c:156
static void unix_vlib_cli_output(uword cli_file_index, u8 *buffer, uword buffer_bytes)
VLIB CLI output function.
Definition: cli.c:930
u32 index
Definition: node.h:238
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:192
void vlib_unix_cli_set_prompt(char *prompt)
Set the CLI prompt.
Definition: cli.c:2693
Scroll to last line.
Definition: cli.c:275
struct termios tio_stdin
Definition: unix.h:128
static void unix_cli_file_free(unix_cli_file_t *f)
Release storage used by a CLI session.
Definition: cli.c:238
static u32 unix_cli_file_add(unix_cli_main_t *cm, char *name, int fd)
Store a new CLI session.
Definition: cli.c:2380
Scroll to first line.
Definition: cli.c:274
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
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:3106
Unix CLI session.
Definition: cli.c:141
unix_cli_pager_index_t * pager_index
Index of line fragments in the pager buffer.
Definition: cli.c:198
struct _vlib_node_registration vlib_node_registration_t
u8 * current_command
The command currently pointed at by the history cursor.
Definition: cli.c:158
#define UNIX_CLI_STDIN_FD
Unix standard in.
Definition: cli.c:95
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:983
#define VLIB_MAIN_LOOP_EXIT_CLI
Definition: main.h:92
#define vlib_call_config_function(vm, x)
Definition: init.h:196
void clib_longjmp(clib_longjmp_t *save, uword return_value)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:419
add_epi add_epi sub_epi sub_epi adds_epu subs_epu i16x8 y
Definition: vector_sse2.h:293
u32 cursor
Position of the insert cursor on the current input line.
Definition: cli.c:177
clib_socket_t cli_listen_socket
Definition: unix.h:92
#define pool_get(P, E)
Allocate an object E from a pool P (unspecified alignment).
Definition: pool.h:200
unix_cli_parse_action_t action
Action to take when matched.
Definition: cli.c:293
static clib_error_t * unix_cli_init(vlib_main_t *vm)
Definition: cli.c:3143
int log_fd
Definition: unix.h:110
static void unix_cli_del_pending_output(unix_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:525
#define SOCKET_IS_SERVER
Definition: socket.h:58
Clear the terminal.
Definition: cli.c:263
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
static int unix_cli_line_process_one(unix_cli_main_t *cm, unix_main_t *um, unix_cli_file_t *cf, unix_file_t *uf, u8 input, unix_cli_parse_action_t action)
Process actionable input.
Definition: cli.c:1229
#define vec_add(V, E, N)
Add N elements to end of vector V (no header, unspecified alignment)
Definition: vec.h:599
u8 ansi_capable
Can we do ANSI output?
Definition: cli.c:186
u8 * format_timeval(u8 *s, va_list *args)
Definition: unix-formats.c:749
static void unix_cli_pager_reindex(unix_cli_file_t *cf)
Reindex entire pager buffer.
Definition: cli.c:857
A CLI session wants to close.
Definition: cli.c:418
struct _socket_t clib_socket_t
u8 * output_vector
Vector of output pending write to file descriptor.
Definition: cli.c:147
#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:84
static void unix_cli_add_pending_output(unix_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:505
End key (jump to end of line)
Definition: cli.c:258
#define vec_new(T, N)
Create new vector of given type and length (unspecified alignment, no header).
Definition: vec.h:310
Jump cursor to start of right word.
Definition: cli.c:260
Undo last erase action.
Definition: cli.c:266
int i32
Definition: types.h:81
clib_error_t * clib_socket_accept(clib_socket_t *server, clib_socket_t *client)
Definition: socket.c:388
u32 len
Length of input without final NUL.
Definition: cli.c:292
Mapping of input buffer strings to action values.
Definition: cli.c:289
#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:84
u32 process_node_index
Process node identifier.
Definition: cli.c:210
#define vec_resize(V, N)
Resize a vector (no header, unspecified alignment) Add N elements to end of given vector V...
Definition: vec.h:241
u32 offset
Offset of the string in the line.
Definition: cli.c:133
u32 length
Length of the string in the line.
Definition: cli.c:136
vlib_main_t * vlib_main
Definition: unix.h:81
Erase line to right & including cursor.
Definition: cli.c:262
#define ANSI_CLEARLINE
ANSI clear line cursor is on.
Definition: cli.c:82
u32 * unused_cli_process_node_indices
Vec pool of unused session indices.
Definition: cli.c:431
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:2707
clib_error_t * clib_socket_init(clib_socket_t *s)
Definition: socket.c:284
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:428
static uword unix_file_add(unix_main_t *um, unix_file_t *template)
Definition: unix.h:136
int search_mode
If non-zero then the CLI is searching in the history array.
Definition: cli.c:174
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:397
#define ANSI_RESTCURSOR
ANSI restore cursor position if previously saved.
Definition: cli.c:88
int cli_line_mode
Definition: unix.h:113
static void unix_cli_pager_prompt(unix_cli_file_t *cf, unix_file_t *uf)
Output a pager prompt and show number of buffered lines.
Definition: cli.c:658
u32 file_descriptor
Definition: unix.h:52
static void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:946
uword private_data
Definition: unix.h:59
static clib_error_t * unix_config(vlib_main_t *vm, unformat_input_t *input)
Definition: main.c:313
static void unix_cli_pager_reset(unix_cli_file_t *f)
Resets the pager buffer and other data.
Definition: cli.c:217
Scroll to previous page.
Definition: cli.c:277
u8 ** vlib_cli_get_possible_completions(u8 *str)
Definition: cli.c:249
struct _unformat_input_t unformat_input_t
static clib_error_t * unix_cli_listen_read_ready(unix_file_t *uf)
Telnet listening socket has a new connection.
Definition: cli.c:2442
#define clib_error_return_unix(e, args...)
Definition: error.h:102
int cli_no_banner
Definition: unix.h:119
Home key (jump to start of line)
Definition: cli.c:257
#define pool_put(P, E)
Free an object E in pool P.
Definition: pool.h:241
#define ANSI_BOLD
ANSI Start bold text.
Definition: cli.c:74
static unix_cli_banner_t unix_cli_banner[]
Plain welcome banner.
Definition: cli.c:107
void timer_call(timer_func_t *func, any arg, f64 dt)
Definition: timer.c:158
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:2809
#define VLIB_CONFIG_FUNCTION(x, n,...)
Definition: init.h:119
Search backwards in command history.
Definition: cli.c:264
static int unix_cli_line_edit(unix_cli_main_t *cm, unix_main_t *um, unix_cli_file_t *cf)
Process input bytes on a stream to provide line editing and command history in the CLI...
Definition: cli.c:2062
static void clib_socket_free(clib_socket_t *s)
Definition: socket.h:134
u8 * input
Input string to match.
Definition: cli.c:291
u32 node_index
Node index.
Definition: node.h:441
word any
Definition: types.h:139
u32 cli_history_limit
Definition: unix.h:116
Scroll to next page.
Definition: cli.c:271
Erase line to left of cursor.
Definition: cli.c:261
u8 * name
Definition: node.h:222
u8 * format_sockaddr(u8 *s, va_list *args)
Definition: unix-formats.c:230
#define CSI
ANSI Control Sequence Introducer.
Definition: cli.c:67
void unformat_init_vector(unformat_input_t *input, u8 *vector_string)
Definition: unformat.c:1031
u8 ** pager_vector
Pager buffer.
Definition: cli.c:195
#define UNFORMAT_END_OF_INPUT
Definition: format.h:143
unix_error_history_t error_history[128]
Definition: unix.h:98
vlib_parse_match_t vlib_parse_eval(u8 *input)
Definition: parse.c:407
svmdb_client_t * c
u32 runtime_index
Definition: node.h:241
static unix_cli_banner_t unix_cli_banner_color[]
ANSI color welcome banner.
Definition: cli.c:116
u32 current_input_file_index
File pool index of current input.
Definition: cli.c:437
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:340
int cli_no_pager
Definition: unix.h:125
#define VLIB_MAIN_LOOP_EXIT_FUNCTION(x)
Definition: init.h:116
static void unix_cli_cli_prompt(unix_cli_file_t *cf, unix_file_t *uf)
Output the CLI prompt.
Definition: cli.c:649
#define ANSI_SAVECURSOR
ANSI save cursor position.
Definition: cli.c:86
#define clib_memcpy(a, b, c)
Definition: string.h:69
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:3043
static clib_error_t * unix_cli_write_ready(unix_file_t *uf)
Called when a CLI session file descriptor can be written to without blocking.
Definition: cli.c:2314
#define pool_is_free_index(P, I)
Use free bitmap to query whether given index is free.
Definition: pool.h:238
u32 line
Index into pager_vector.
Definition: cli.c:130
Down arrow.
Definition: cli.c:254
#define ARRAY_LEN(x)
Definition: clib.h:59
A file descriptor has data to be read.
Definition: cli.c:417
#define ESC
ANSI escape code.
Definition: cli.c:64
Jump cursor to start of left word.
Definition: cli.c:259
#define UNIX_FILE_DATA_AVAILABLE_TO_WRITE
Definition: unix.h:55
void vlib_start_process(vlib_main_t *vm, uword process_index)
Definition: main.c:1365
u8 no_pager
Disable the pager?
Definition: cli.c:192
int tio_isset
Definition: unix.h:129
Right arrow.
Definition: cli.c:256
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:154
static void unix_cli_pager_prompt_erase(unix_cli_file_t *cf, unix_file_t *uf)
Erase the printed pager prompt.
Definition: cli.c:697
Enter pressed (CR, CRLF, LF, etc)
Definition: cli.c:269
unsigned int u32
Definition: types.h:88
Up arrow.
Definition: cli.c:253
static clib_error_t * unix_cli_read_ready(unix_file_t *uf)
Called when a CLI session file descriptor has data to be read.
Definition: cli.c:2337
static void unix_cli_kill(unix_cli_main_t *cm, uword cli_file_index)
Destroy a CLI session.
Definition: cli.c:2240
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:785
static word unix_vlib_findchr(u8 chr, u8 *str, word len)
A bit like strchr with a buffer length limit.
Definition: cli.c:555
unix_cli_parse_action_t
CLI actions.
Definition: cli.c:246
u32 vlib_register_node(vlib_main_t *vm, vlib_node_registration_t *r)
Definition: node.c:498
#define UNIX_FLAG_INTERACTIVE
Definition: unix.h:85
Erase cursor left.
Definition: cli.c:251
#define SOCKET_ALLOW_GROUP_WRITE
Definition: socket.h:61
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:2987
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:2548
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:819
Scroll to next page.
Definition: cli.c:276
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:146
u8 has_history
This session has command history.
Definition: cli.c:154
A CLI banner line.
Definition: cli.c:99
#define ANSI_CLEAR
ANSI clear screen.
Definition: cli.c:70
static u8 unix_cli_terminal_type(u8 *term, uword len)
Identify whether a terminal type is ANSI capable.
Definition: cli.c:1017
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:2884
Pager line index.
Definition: cli.c:127
u64 uword
Definition: types.h:112
Action parser did not find any match.
Definition: cli.c:282
u8 started
Has the session started?
Definition: cli.c:189
void(* file_update)(unix_file_t *file, unix_file_update_type_t update_type)
Definition: unix.h:94
unsigned short u16
Definition: types.h:57
Scroll to next line.
Definition: cli.c:272
i64 word
Definition: types.h:111
clib_error_t * error
Definition: unix.h:68
CLI global state.
Definition: cli.c:422
#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:80
u8 * search_key
The string being searched for in the history.
Definition: cli.c:169
static void unformat_free(unformat_input_t *i)
Definition: format.h:161
#define clib_unix_warning(format, args...)
Definition: error.h:68
Definition: unix.h:49
u8 * format_time_interval(u8 *s, va_list *args)
Definition: std-formats.c:122
static void unix_cli_pager_redraw(unix_cli_file_t *cf, unix_file_t *uf)
Redraw the currently displayed page of text.
Definition: cli.c:735
static uword unix_cli_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Handle system events.
Definition: cli.c:2270
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:1037
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:206
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
static void unix_cli_resize_interrupt(int signum)
The system terminal has informed us that the window size has changed.
Definition: cli.c:2518
Clear and redraw the page on the terminal.
Definition: cli.c:278
static vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Get vlib node by index.
Definition: node_funcs.h:58
#define vec_foreach(var, vec)
Vector iterator.
static void unix_cli_pager_message(unix_cli_file_t *cf, unix_file_t *uf, char *message, char *postfix)
Output a pager "skipping" message.
Definition: cli.c:681
void unformat_init_unix_file(unformat_input_t *input, int file_descriptor)
Definition: unformat.c:1058
u8 crlf_mode
Set if the CRLF mode wants CR + LF.
Definition: cli.c:183
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:2735
#define ANSI_RESET
ANSI reset color settings.
Definition: cli.c:72
clib_longjmp_t main_loop_exit
Definition: main.h:88
vlib_cli_output_function_t * output_function
Definition: node.h:550
static void unix_vlib_cli_output_raw(unix_cli_file_t *cf, unix_file_t *uf, u8 *buffer, uword buffer_bytes)
Send a buffer to the CLI stream if possible, enqueue it otherwise.
Definition: cli.c:582
u32 flags
Definition: vhost-user.h:76
static unix_cli_main_t unix_cli_main
CLI global state.
Definition: cli.c:441
i32 excursion
How far from the end of the history array the user has browsed.
Definition: cli.c:160
Search the pager buffer.
Definition: cli.c:279
u8 line_mode
Line mode or char mode.
Definition: cli.c:180
u32 length
The length of the line without terminating NUL.
Definition: cli.c:102
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:680
Left arrow.
Definition: cli.c:255
u32 stdin_cli_file_index
The session index of the stdin cli.
Definition: cli.c:434
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:796
#define clib_panic(format, args...)
Definition: error.h:72
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:972
Scroll to previous line.
Definition: cli.c:273
u64 n_total_errors
Definition: unix.h:100
static uword unformat_check_input(unformat_input_t *i)
Definition: format.h:169
u32 height
Terminal height.
Definition: cli.c:207
static void unix_file_del(unix_main_t *um, unix_file_t *f)
Definition: unix.h:146
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:2674
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:1080
static void unix_cli_process_input(unix_cli_main_t *cm, uword cli_file_index)
Process input to a CLI session.
Definition: cli.c:2139
static unix_cli_parse_actions_t unix_cli_parse_strings[]
Patterns to match on a CLI input stream.
Definition: cli.c:312