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