FD.io VPP  v16.06
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 vlib/vlib/unix/cli.c
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 
46 #include <vlib/vlib.h>
47 #include <vlib/unix/unix.h>
48 #include <vppinfra/timer.h>
49 
50 #include <ctype.h>
51 #include <fcntl.h>
52 #include <sys/stat.h>
53 #include <termios.h>
54 #include <signal.h>
55 #include <unistd.h>
56 #include <arpa/telnet.h>
57 #include <sys/ioctl.h>
58 
59 /** ANSI escape code. */
60 #define ESC "\x1b"
61 
62 /** ANSI Control Sequence Introducer. */
63 #define CSI ESC "["
64 
65 /** ANSI clear screen. */
66 #define ANSI_CLEAR CSI "2J" CSI "1;1H"
67 /** ANSI reset color settings. */
68 #define ANSI_RESET CSI "0m"
69 /** ANSI Start bold text. */
70 #define ANSI_BOLD CSI "1m"
71 /** ANSI Stop bold text. */
72 #define ANSI_DIM CSI "2m"
73 /** ANSI Start dark red text. */
74 #define ANSI_DRED ANSI_DIM CSI "31m"
75 /** ANSI Start bright red text. */
76 #define ANSI_BRED ANSI_BOLD CSI "31m"
77 /** ANSI clear line cursor is on. */
78 #define ANSI_CLEARLINE CSI "2K"
79 /** ANSI scroll screen down one line. */
80 #define ANSI_SCROLLDN CSI "1T"
81 /** ANSI save cursor position. */
82 #define ANSI_SAVECURSOR CSI "s"
83 /** ANSI restore cursor position if previously saved. */
84 #define ANSI_RESTCURSOR CSI "u"
85 
86 /** Maximum depth into a byte stream from which to compile a Telnet
87  * protocol message. This is a saftey measure. */
88 #define UNIX_CLI_MAX_DEPTH_TELNET 24
89 
90 /** Unix standard in */
91 #define UNIX_CLI_STDIN_FD 0
92 
93 
94 /** A CLI banner line. */
95 typedef struct {
96  u8 * line; /**< The line to print. */
97  u32 length; /**< The length of the line without terminating NUL. */
99 
100 #define _(a) { .line = (u8 *)(a), .length = sizeof(a) - 1 }
101 /** Plain welcome banner. */
102 static unix_cli_banner_t unix_cli_banner[] = {
103 _(" _______ _ _ _____ ___ \n"),
104 _(" __/ __/ _ \\ (_)__ | | / / _ \\/ _ \\\n"),
105 _(" _/ _// // / / / _ \\ | |/ / ___/ ___/\n"),
106 _(" /_/ /____(_)_/\\___/ |___/_/ /_/ \n"),
107 _("\n")
108 };
109 /** ANSI color welcome banner. */
110 static unix_cli_banner_t unix_cli_banner_color[] = {
111 _(ANSI_BRED " _______ _ " ANSI_RESET " _ _____ ___ \n"),
112 _(ANSI_BRED " __/ __/ _ \\ (_)__ " ANSI_RESET " | | / / _ \\/ _ \\\n"),
113 _(ANSI_BRED " _/ _// // / / / _ \\" ANSI_RESET " | |/ / ___/ ___/\n"),
114 _(ANSI_BRED " /_/ /____(_)_/\\___/" ANSI_RESET " |___/_/ /_/ \n"),
115 _("\n")
116 };
117 #undef _
118 
119 
120 /** Unix CLI session. */
121 typedef struct {
122  /** The file index held by unix.c */
124 
125  /** Vector of output pending write to file descriptor. */
127 
128  /** Vector of input saved by Unix input node to be processed by
129  CLI process. */
131 
136 
137  /** Maximum number of history entries this session will store. */
139 
140  /** Current command line counter */
142 
145 
146  /** Position of the insert cursor on the current input line */
148 
149  /** Line mode or char mode */
151 
152  /** Set if the CRLF mode wants CR + LF */
154 
155  /** Can we do ANSI output? */
157 
158  /** Has the session started? */
160 
161  /** Disable the pager? */
163 
164  /** Pager buffer */
166 
167  /** Lines currently displayed */
169 
170  /** Line number of top of page */
172 
173  /** Terminal width */
175 
176  /** Terminal height */
178 
179  /** Process node identifier */
182 
183 /** Resets the pager buffer and other data.
184  * @param f The CLI session whose pager needs to be reset.
185  */
186 always_inline void
188 {
189  u8 ** p;
190 
191  f->pager_lines = f->pager_start = 0;
192  vec_foreach (p, f->pager_vector)
193  {
194  vec_free(*p);
195  }
197  f->pager_vector = 0;
198 }
199 
200 /** Release storage used by a CLI session.
201  * @param f The CLI session whose storage needs to be released.
202  */
203 always_inline void
205 {
206  vec_free (f->output_vector);
207  vec_free (f->input_vector);
209 }
210 
211 /** CLI actions */
212 typedef enum {
213  UNIX_CLI_PARSE_ACTION_NOACTION = 0, /**< No action */
214  UNIX_CLI_PARSE_ACTION_CRLF, /**< Carriage return, newline or enter */
215  UNIX_CLI_PARSE_ACTION_TAB, /**< Tab key */
216  UNIX_CLI_PARSE_ACTION_ERASE, /**< Erase cursor left */
217  UNIX_CLI_PARSE_ACTION_ERASERIGHT, /**< Erase cursor right */
218  UNIX_CLI_PARSE_ACTION_UP, /**< Up arrow */
219  UNIX_CLI_PARSE_ACTION_DOWN, /**< Down arrow */
233 
244 
248 
249 /** \brief Mapping of input buffer strings to action values.
250  * @note This won't work as a hash since we need to be able to do
251  * partial matches on the string.
252  */
253 typedef struct {
254  u8 *input; /**< Input string to match. */
255  u32 len; /**< Length of input without final NUL. */
256  unix_cli_parse_action_t action; /**< Action to take when matched. */
258 
259 /** @brief Given a capital ASCII letter character return a @c NUL terminated
260  * string with the control code for that letter.
261  *
262  * @param c An ASCII character.
263  * @return A @c NUL terminated string of type @c u8[].
264  *
265  * @par Example
266  * @c CTL('A') returns <code>{ 0x01, 0x00 }</code> as a @c u8[].
267  */
268 #define CTL(c) (u8[]){ (c) - '@', 0 }
269 
270 #define _(a,b) { .input = (u8 *)(a), .len = sizeof(a) - 1, .action = (b) }
271 /**
272  * Patterns to match on a CLI input stream.
273  * @showinitializer
274  */
275 static unix_cli_parse_actions_t unix_cli_parse_strings[] = {
276  /* Line handling */
277  _( "\r\n", UNIX_CLI_PARSE_ACTION_CRLF ), /* Must be before '\r' */
278  _( "\n", UNIX_CLI_PARSE_ACTION_CRLF ),
279  _( "\r\0", UNIX_CLI_PARSE_ACTION_CRLF ), /* Telnet does this */
280  _( "\r", UNIX_CLI_PARSE_ACTION_CRLF ),
281 
282  /* Unix shell control codes */
283  _( CTL('B'), UNIX_CLI_PARSE_ACTION_LEFT ),
284  _( CTL('F'), UNIX_CLI_PARSE_ACTION_RIGHT ),
285  _( CTL('P'), UNIX_CLI_PARSE_ACTION_UP ),
286  _( CTL('N'), UNIX_CLI_PARSE_ACTION_DOWN ),
287  _( CTL('A'), UNIX_CLI_PARSE_ACTION_HOME ),
288  _( CTL('E'), UNIX_CLI_PARSE_ACTION_END ),
292  _( CTL('Y'), UNIX_CLI_PARSE_ACTION_YANK ),
293  _( CTL('L'), UNIX_CLI_PARSE_ACTION_CLEAR ),
294  _( ESC "b", UNIX_CLI_PARSE_ACTION_WORDLEFT ), /* Alt-B */
295  _( ESC "f", UNIX_CLI_PARSE_ACTION_WORDRIGHT ), /* Alt-F */
296  _( "\b", UNIX_CLI_PARSE_ACTION_ERASE ), /* ^H */
297  _( "\x7f", UNIX_CLI_PARSE_ACTION_ERASE ), /* Backspace */
298  _( "\t", UNIX_CLI_PARSE_ACTION_TAB ), /* ^I */
299 
300  /* VT100 Normal mode - Broadest support */
301  _( CSI "A", UNIX_CLI_PARSE_ACTION_UP ),
306  _( CSI "F", UNIX_CLI_PARSE_ACTION_END ),
307  _( CSI "3~", UNIX_CLI_PARSE_ACTION_ERASERIGHT ), /* Delete */
308  _( CSI "1;5D", UNIX_CLI_PARSE_ACTION_WORDLEFT ), /* C-Left */
309  _( CSI "1;5C", UNIX_CLI_PARSE_ACTION_WORDRIGHT ),/* C-Right */
310 
311  /* VT100 Application mode - Some Gnome Terminal functions use these */
312  _( ESC "OA", UNIX_CLI_PARSE_ACTION_UP ),
313  _( ESC "OB", UNIX_CLI_PARSE_ACTION_DOWN ),
314  _( ESC "OC", UNIX_CLI_PARSE_ACTION_RIGHT ),
315  _( ESC "OD", UNIX_CLI_PARSE_ACTION_LEFT ),
316  _( ESC "OH", UNIX_CLI_PARSE_ACTION_HOME ),
317  _( ESC "OF", UNIX_CLI_PARSE_ACTION_END ),
318 
319  /* ANSI X3.41-1974 - sent by Microsoft Telnet and PuTTY */
320  _( CSI "1~", UNIX_CLI_PARSE_ACTION_HOME ),
321  _( CSI "4~", UNIX_CLI_PARSE_ACTION_END ),
322 
323  /* Emacs-ish history search */
326 
327  /* Other protocol things */
328  _( "\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC ), /* IAC */
329  _( "\0", UNIX_CLI_PARSE_ACTION_NOACTION ), /* NUL */
331 };
332 
333 /**
334  * Patterns to match when a CLI session is in the pager.
335  * @showinitializer
336  */
337 static unix_cli_parse_actions_t unix_cli_parse_pager[] = {
338  /* Line handling */
339  _( "\r\n", UNIX_CLI_PARSE_ACTION_PAGER_CRLF ), /* Must be before '\r' */
341  _( "\r\0", UNIX_CLI_PARSE_ACTION_PAGER_CRLF ), /* Telnet does this */
343 
344  /* Pager commands */
348 
349  /* VT100 */
354 
355  /* VT100 Application mode */
360 
361  /* ANSI X3.41-1974 */
366 
367  /* Other protocol things */
368  _( "\xff", UNIX_CLI_PARSE_ACTION_TELNETIAC ), /* IAC */
369  _( "\0", UNIX_CLI_PARSE_ACTION_NOACTION ), /* NUL */
371 };
372 #undef _
373 
374 /** CLI session events. */
375 typedef enum {
376  UNIX_CLI_PROCESS_EVENT_READ_READY, /**< A file descriptor has data to be read. */
377  UNIX_CLI_PROCESS_EVENT_QUIT, /**< A CLI session wants to close. */
379 
380 /** CLI global state. */
381 typedef struct {
382  /** Prompt string for CLI. */
384 
385  /** Vec pool of CLI sessions. */
387 
388  /** Vec pool of unused session indices. */
390 
391  /** The session index of the stdin cli */
393 
394  /** File pool index of current input. */
397 
398 /** CLI global state */
400 
401 /**
402  * \brief Search for a byte sequence in the action list.
403  *
404  * Searches the @ref unix_cli_parse_actions_t list in @a a for a match with
405  * the bytes in @a input of maximum length @a ilen bytes.
406  * When a match is made @a *matched indicates how many bytes were matched.
407  * Returns a value from the enum @ref unix_cli_parse_action_t to indicate
408  * whether no match was found, a partial match was found or a complete
409  * match was found and what action, if any, should be taken.
410  *
411  * @param[in] a Actions list to search within.
412  * @param[in] input String fragment to search for.
413  * @param[in] ilen Length of the string in 'input'.
414  * @param[out] matched Pointer to an integer that will contain the number
415  * of bytes matched when a complete match is found.
416  *
417  * @return Action from @ref unix_cli_parse_action_t that the string fragment
418  * matches.
419  * @ref UNIX_CLI_PARSE_ACTION_PARTIALMATCH is returned when the
420  * whole input string matches the start of at least one action.
421  * @ref UNIX_CLI_PARSE_ACTION_NOMATCH is returned when there is no
422  * match at all.
423  */
426  u8 *input, u32 ilen, i32 *matched)
427 {
428  u8 partial = 0;
429 
430  while (a->input)
431  {
432  if (ilen >= a->len)
433  {
434  /* see if the start of the input buffer exactly matches the current
435  * action string. */
436  if (memcmp(input, a->input, a->len) == 0)
437  {
438  *matched = a->len;
439  return a->action;
440  }
441  }
442  else
443  {
444  /* if the first ilen characters match, flag this as a partial -
445  * meaning keep collecting bytes in case of a future match */
446  if (memcmp(input, a->input, ilen) == 0)
447  partial = 1;
448  }
449 
450  /* check next action */
451  a ++;
452  }
453 
454  return partial ?
457 }
458 
459 
460 static void
462  unix_cli_file_t * cf,
463  u8 * buffer,
464  uword buffer_bytes)
465 {
466  unix_main_t * um = &unix_main;
467 
468  vec_add (cf->output_vector, buffer, buffer_bytes);
469  if (vec_len (cf->output_vector) > 0)
470  {
471  int skip_update = 0 != (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
473  if (! skip_update)
475  }
476 }
477 
478 static void
480  unix_cli_file_t * cf,
481  uword n_bytes)
482 {
483  unix_main_t * um = &unix_main;
484 
485  vec_delete (cf->output_vector, n_bytes, 0);
486  if (vec_len (cf->output_vector) <= 0)
487  {
488  int skip_update = 0 == (uf->flags & UNIX_FILE_DATA_AVAILABLE_TO_WRITE);
490  if (! skip_update)
492  }
493 }
494 
495 /** \brief A bit like strchr with a buffer length limit.
496  * Search a buffer for the first instance of a character up to the limit of
497  * the buffer length. If found then return the position of that character.
498  *
499  * The key departure from strchr is that if the character is not found then
500  * return the buffer length.
501  *
502  * @param chr The byte value to search for.
503  * @param str The buffer in which to search for the value.
504  * @param len The depth into the buffer to search.
505  *
506  * @return The index of the first occurence of \c chr. If \c chr is not
507  * found then \c len instead.
508  */
510 {
511  word i = 0;
512  for (i = 0; i < len; i++, str++)
513  {
514  if (*str == chr)
515  return i;
516  }
517  return len;
518 }
519 
520 /** \brief Send a buffer to the CLI stream if possible, enqueue it otherwise.
521  * Attempts to write given buffer to the file descriptor of the given
522  * Unix CLI session. If that session already has data in the output buffer
523  * or if the write attempt tells us to try again later then the given buffer
524  * is appended to the pending output buffer instead.
525  *
526  * This is typically called only from \c unix_vlib_cli_output_cooked since
527  * that is where CRLF handling occurs or from places where we explicitly do
528  * not want cooked handling.
529  *
530  * @param cf Unix CLI session of the desired stream to write to.
531  * @param uf The Unix file structure of the desired stream to write to.
532  * @param buffer Pointer to the buffer that needs to be written.
533  * @param buffer_bytes The number of bytes from \c buffer to write.
534  */
536  unix_file_t * uf,
537  u8 * buffer,
538  uword buffer_bytes)
539 {
540  int n = 0;
541 
542  if (vec_len (cf->output_vector) == 0)
543  n = write (uf->file_descriptor, buffer, buffer_bytes);
544 
545  if (n < 0 && errno != EAGAIN)
546  {
547  clib_unix_warning ("write");
548  }
549  else if ((word) n < (word) buffer_bytes)
550  {
551  /* We got EAGAIN or we already have stuff in the buffer;
552  * queue up whatever didn't get sent for later. */
553  if (n < 0) n = 0;
554  unix_cli_add_pending_output (uf, cf, buffer + n, buffer_bytes - n);
555  }
556 }
557 
558 /** \brief Process a buffer for CRLF handling before outputting it to the CLI.
559  *
560  * @param cf Unix CLI session of the desired stream to write to.
561  * @param uf The Unix file structure of the desired stream to write to.
562  * @param buffer Pointer to the buffer that needs to be written.
563  * @param buffer_bytes The number of bytes from \c buffer to write.
564  */
566  unix_file_t * uf,
567  u8 * buffer,
568  uword buffer_bytes)
569 {
570  word end = 0, start = 0;
571 
572  while (end < buffer_bytes)
573  {
574  if (cf->crlf_mode)
575  {
576  /* iterate the line on \n's so we can insert a \r before it */
577  end = unix_vlib_findchr('\n',
578  buffer + start,
579  buffer_bytes - start) + start;
580  }
581  else
582  {
583  /* otherwise just send the whole buffer */
584  end = buffer_bytes;
585  }
586 
587  unix_vlib_cli_output_raw(cf, uf, buffer + start, end - start);
588 
589  if (cf->crlf_mode)
590  {
591  if (end < buffer_bytes)
592  {
593  unix_vlib_cli_output_raw(cf, uf, (u8 *)"\r\n", 2);
594  end ++; /* skip the \n that we already sent */
595  }
596  start = end;
597  }
598  }
599 }
600 
601 /** \brief Output the CLI prompt */
603 {
605 
607 }
608 
609 /** \brief Output a pager prompt and show number of buffered lines */
611 {
612  u8 * prompt;
613 
614  prompt = format(0, "\r%s-- more -- (%d-%d/%d)%s",
615  cf->ansi_capable ? ANSI_BOLD : "",
616  cf->pager_start + 1,
617  cf->pager_start + cf->height,
618  cf->pager_lines,
619  cf->ansi_capable ? ANSI_RESET: "");
620 
621  unix_vlib_cli_output_cooked(cf, uf, prompt, vec_len(prompt));
622 
623  vec_free(prompt);
624 }
625 
626 /** \brief Output a pager "skipping" message */
628  char *message, char *postfix)
629 {
630  u8 * prompt;
631 
632  prompt = format(0, "\r%s-- %s --%s%s",
633  cf->ansi_capable ? ANSI_BOLD : "",
634  message,
635  cf->ansi_capable ? ANSI_RESET: "",
636  postfix);
637 
638  unix_vlib_cli_output_cooked(cf, uf, prompt, vec_len(prompt));
639 
640  vec_free(prompt);
641 }
642 
643 /** \brief Erase the printed pager prompt */
645 {
646  if (cf->ansi_capable)
647  {
648  unix_vlib_cli_output_cooked(cf, uf, (u8 *)"\r", 1);
650  (u8 *)ANSI_CLEARLINE, sizeof(ANSI_CLEARLINE) - 1);
651  }
652  else
653  {
654  int i;
655 
656  unix_vlib_cli_output_cooked(cf, uf, (u8 *)"\r", 1);
657  for (i = 0; i < cf->width - 1; i ++)
658  unix_vlib_cli_output_cooked(cf, uf, (u8 *)" ", 1);
659  unix_vlib_cli_output_cooked(cf, uf, (u8 *)"\r", 1);
660  }
661 }
662 
663 /** \brief Uses an ANSI escape sequence to move the cursor */
665  u16 x, u16 y)
666 {
667  u8 * str;
668 
669  str = format(0, "%s%d;%dH", CSI, y, x);
670 
671  unix_vlib_cli_output_cooked(cf, uf, str, vec_len(str));
672 
673  vec_free(str);
674 }
675 
676 /** \brief VLIB CLI output function. */
677 static void unix_vlib_cli_output (uword cli_file_index,
678  u8 * buffer,
679  uword buffer_bytes)
680 {
681  unix_main_t * um = &unix_main;
683  unix_cli_file_t * cf;
684  unix_file_t * uf;
685 
686  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
688 
689  if (cf->no_pager || um->cli_pager_buffer_limit == 0 || cf->height == 0)
690  {
691  unix_vlib_cli_output_cooked(cf, uf, buffer, buffer_bytes);
692  }
693  else
694  {
695  /* At each \n add the line to the pager buffer.
696  * If we've not yet displayed a whole page in this command then
697  * also output the line.
698  * If we have displayed a whole page then display a pager prompt
699  * and count the lines we've buffered.
700  */
701  u8 * p = buffer;
702  u8 * line;
703  uword i, len = buffer_bytes;
704 
705  while (len)
706  {
707  i = unix_vlib_findchr('\n', p, len);
708  if (i < len)
709  i ++; /* include the '\n' */
710 
711  /* make a vec out of this substring */
712  line = vec_new(u8, i);
713  memcpy(line, p, i);
714 
715  /* store in pager buffer */
716  vec_add1(cf->pager_vector, line);
717  cf->pager_lines ++;
718 
719  if (cf->pager_lines < cf->height)
720  {
721  /* output this line */
722  unix_vlib_cli_output_cooked(cf, uf, p, i);
723  /* TODO: stop if line longer than cf->width and would
724  * overflow cf->height */
725  }
726  else
727  {
728  /* Display the pager prompt every 10 lines */
729  if (!(cf->pager_lines % 10))
730  unix_cli_pager_prompt(cf, uf);
731  }
732 
733  p += i;
734  len -= i;
735  }
736 
737  /* Check if we went over the pager buffer limit */
738  if (cf->pager_lines > um->cli_pager_buffer_limit)
739  {
740  /* Stop using the pager for the remainder of this CLI command */
741  cf->no_pager = 2;
742 
743  /* If we likely printed the prompt, erase it */
744  if (cf->pager_lines > cf->height - 1)
746 
747  /* Dump out the contents of the buffer */
748  for (i = cf->pager_start + (cf->height - 1);
749  i < cf->pager_lines; i ++)
751  cf->pager_vector[i],
752  vec_len(cf->pager_vector[i]));
753 
755  }
756  }
757 }
758 
759 /** \brief Identify whether a terminal type is ANSI capable. */
760 static u8 unix_cli_terminal_type(u8 * term, uword len)
761 {
762  /* This may later be better done as a hash of some sort. */
763 #define _(a) do { \
764  if (strncasecmp(a, (char *)term, (size_t)len) == 0) return 1; \
765  } while(0)
766 
767  _("xterm");
768  _("xterm-color");
769  _("xterm-256color"); /* iTerm on Mac */
770  _("screen");
771  _("ansi"); /* Microsoft Telnet */
772 #undef _
773 
774  return 0;
775 }
776 
777 /** \brief Emit initial welcome banner and prompt on a connection. */
779 {
780  unix_main_t * um = &unix_main;
782  unix_cli_banner_t *banner;
783  int i, len;
784 
785  /*
786  * Put the first bytes directly into the buffer so that further output is
787  * queued until everything is ready. (oterwise initial prompt can appear
788  * mid way through VPP initialization)
789  */
790  unix_cli_add_pending_output (uf, cf, (u8 *)"\r", 1);
791 
792  if (!um->cli_no_banner)
793  {
794  if (cf->ansi_capable)
795  {
796  banner = unix_cli_banner_color;
797  len = ARRAY_LEN(unix_cli_banner_color);
798  }
799  else
800  {
801  banner = unix_cli_banner;
802  len = ARRAY_LEN(unix_cli_banner);
803  }
804 
805  for (i = 0; i < len; i++)
806  {
808  banner[i].line,
809  banner[i].length);
810  }
811  }
812 
813  /* Prompt. */
814  unix_cli_cli_prompt (cf, uf);
815 
816  cf->started = 1;
817 }
818 
819 /** \brief A failsafe triggered on a timer to ensure we send the prompt
820  * to telnet sessions that fail to negotiate the terminal type. */
821 static void unix_cli_file_welcome_timer(any arg, f64 delay)
822 {
824  unix_cli_file_t * cf;
825  (void)delay;
826 
827  /* Check the connection didn't close already */
828  if (pool_is_free_index (cm->cli_file_pool, (uword)arg))
829  return;
830 
831  cf = pool_elt_at_index (cm->cli_file_pool, (uword)arg);
832 
833  if (!cf->started)
834  unix_cli_file_welcome(cm, cf);
835 }
836 
837 /** \brief A mostly no-op Telnet state machine.
838  * Process Telnet command bytes in a way that ensures we're mostly
839  * transparent to the Telnet protocol. That is, it's mostly a no-op.
840  *
841  * @return -1 if we need more bytes, otherwise a positive integer number of
842  * bytes to consume from the input_vector, not including the initial
843  * IAC byte.
844  */
846  unix_cli_file_t * cf,
847  unix_file_t * uf,
848  u8 * input_vector,
849  uword len)
850 {
851  /* Input_vector starts at IAC byte.
852  * See if we have a complete message; if not, return -1 so we wait for more.
853  * if we have a complete message, consume those bytes from the vector.
854  */
855  i32 consume = 0;
856 
857  if (len == 1)
858  return -1; /* want more bytes */
859 
860  switch (input_vector[1])
861  {
862  case IAC:
863  /* two IAC's in a row means to pass through 0xff.
864  * since that makes no sense here, just consume it.
865  */
866  consume = 1;
867  break;
868 
869  case WILL:
870  case WONT:
871  case DO:
872  case DONT:
873  /* Expect 3 bytes */
874  if (vec_len(input_vector) < 3)
875  return -1; /* want more bytes */
876 
877  consume = 2;
878  break;
879 
880  case SB:
881  {
882  /* Sub option - search ahead for IAC SE to end it */
883  i32 i;
884  for (i = 3; i < len && i < UNIX_CLI_MAX_DEPTH_TELNET; i++)
885  {
886  if (input_vector[i - 1] == IAC && input_vector[i] == SE)
887  {
888  /* We have a complete message; see if we care about it */
889  switch (input_vector[2])
890  {
891  case TELOPT_TTYPE:
892  if (input_vector[3] != 0)
893  break;
894  /* See if the terminal type is ANSI capable */
895  cf->ansi_capable =
896  unix_cli_terminal_type(input_vector + 4, i - 5);
897  /* If session not started, we can release the pause */
898  if (!cf->started)
899  /* Send the welcome banner and initial prompt */
900  unix_cli_file_welcome(&unix_cli_main, cf);
901  break;
902 
903  case TELOPT_NAWS:
904  /* Window size */
905  if (i != 8) /* check message is correct size */
906  break;
907  cf->width = clib_net_to_host_u16(*((u16 *)(input_vector + 3)));
908  cf->height = clib_net_to_host_u16(*((u16 *)(input_vector + 5)));
909  break;
910 
911  default:
912  break;
913  }
914  /* Consume it all */
915  consume = i;
916  break;
917  }
918  }
919 
920  if (i == UNIX_CLI_MAX_DEPTH_TELNET)
921  consume = 1; /* hit max search depth, advance one byte */
922 
923  if (consume == 0)
924  return -1; /* want more bytes */
925 
926  break;
927  }
928 
929  case GA:
930  case EL:
931  case EC:
932  case AO:
933  case IP:
934  case BREAK:
935  case DM:
936  case NOP:
937  case SE:
938  case EOR:
939  case ABORT:
940  case SUSP:
941  case xEOF:
942  /* Simple one-byte messages */
943  consume = 1;
944  break;
945 
946  case AYT:
947  /* Are You There - trigger a visible response */
948  consume = 1;
949  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "fd.io VPP\n", 10);
950  break;
951 
952  default:
953  /* Unknown command! Eat the IAC byte */
954  break;
955  }
956 
957  return consume;
958 }
959 
960 /** \brief Process actionable input.
961  * Based on the \c action process the input; this typically involves
962  * searching the command history or editing the current command line.
963  */
965  unix_main_t * um,
966  unix_cli_file_t * cf,
967  unix_file_t * uf,
968  u8 input,
970 {
971  u8 * prev;
972  int j, delta;
973 
974  switch (action)
975  {
977  break;
978 
981  if (!cf->has_history || !cf->history_limit)
982  break;
983  if (cf->search_mode == 0)
984  {
985  /* Erase the current command (if any) */
986  for (j = 0; j < (vec_len (cf->current_command)); j++)
987  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
988 
991  if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
992  cf->search_mode = -1;
993  else
994  cf->search_mode = 1;
995  cf->cursor = 0;
996  }
997  else
998  {
999  if (action == UNIX_CLI_PARSE_ACTION_REVSEARCH)
1000  cf->search_mode = -1;
1001  else
1002  cf->search_mode = 1;
1003 
1004  cf->excursion += cf->search_mode;
1005  goto search_again;
1006  }
1007  break;
1008 
1010  /* Erase the command from the cursor to the start */
1011 
1012  /* Shimmy forwards to the new end of line position */
1013  delta = vec_len (cf->current_command) - cf->cursor;
1014  for (j = cf->cursor; j > delta; j--)
1015  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1016  /* Zap from here to the end of what is currently displayed */
1017  for (; j < (vec_len (cf->current_command)); j++)
1018  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1019  /* Get back to the start of the line */
1020  for (j = 0; j < (vec_len (cf->current_command)); j++)
1021  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1022 
1023  j = vec_len(cf->current_command) - cf->cursor;
1024  memmove(cf->current_command,
1025  cf->current_command + cf->cursor,
1026  j);
1027  _vec_len(cf->current_command) = j;
1028 
1029  /* Print the new contents */
1031  /* Shimmy back to the start */
1032  for (j = 0; j < (vec_len (cf->current_command)); j++)
1033  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1034  cf->cursor = 0;
1035 
1036  cf->search_mode = 0;
1037  break;
1038 
1040  /* Erase the command from the cursor to the end */
1041 
1042  /* Zap from cursor to end of what is currently displayed */
1043  for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1044  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1045  /* Get back to where we were */
1046  for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1047  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1048 
1049  /* Truncate the line at the cursor */
1050  _vec_len(cf->current_command) = cf->cursor;
1051 
1052  cf->search_mode = 0;
1053  break;
1054 
1056  if (cf->cursor > 0)
1057  {
1058  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1059  cf->cursor --;
1060  }
1061 
1062  cf->search_mode = 0;
1063  break;
1064 
1066  if (cf->cursor < vec_len(cf->current_command))
1067  {
1068  /* have to emit the character under the cursor */
1069  unix_vlib_cli_output_cooked (cf, uf, cf->current_command + cf->cursor, 1);
1070  cf->cursor ++;
1071  }
1072 
1073  cf->search_mode = 0;
1074  break;
1075 
1078  if (!cf->has_history || !cf->history_limit)
1079  break;
1080  cf->search_mode = 0;
1081  /* Erase the command */
1082  for (j = cf->cursor; j < (vec_len (cf->current_command)); j++)
1083  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " ", 1);
1084  for (j = 0; j < (vec_len (cf->current_command)); j++)
1085  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1087  if (vec_len (cf->command_history))
1088  {
1089  if (action == UNIX_CLI_PARSE_ACTION_UP)
1090  delta = -1;
1091  else
1092  delta = 1;
1093 
1094  cf->excursion += delta;
1095 
1096  if (cf->excursion == vec_len (cf->command_history))
1097  {
1098  /* down-arrowed to last entry - want a blank line */
1099  _vec_len (cf->current_command) = 0;
1100  }
1101  else if (cf->excursion < 0)
1102  {
1103  /* up-arrowed over the start to the end, want a blank line */
1104  cf->excursion = vec_len (cf->command_history);
1105  _vec_len (cf->current_command) = 0;
1106  }
1107  else
1108  {
1109  if (cf->excursion > (i32) vec_len (cf->command_history) -1)
1110  /* down-arrowed past end - wrap to start */
1111  cf->excursion = 0;
1112 
1113  /* Print the command at the current position */
1114  prev = cf->command_history [cf->excursion];
1115  vec_validate (cf->current_command, vec_len(prev)-1);
1116 
1117  clib_memcpy (cf->current_command, prev, vec_len(prev));
1118  _vec_len (cf->current_command) = vec_len(prev);
1120  vec_len (cf->current_command));
1121  }
1122  cf->cursor = vec_len(cf->current_command);
1123 
1124  break;
1125  }
1126  break;
1127 
1129  if (vec_len (cf->current_command) && cf->cursor > 0)
1130  {
1131  while (cf->cursor)
1132  {
1133  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1134  cf->cursor --;
1135  }
1136  }
1137 
1138  cf->search_mode = 0;
1139  break;
1140 
1142  if (vec_len (cf->current_command) &&
1143  cf->cursor < vec_len(cf->current_command))
1144  {
1146  cf->current_command + cf->cursor,
1147  vec_len(cf->current_command) - cf->cursor);
1148  cf->cursor = vec_len(cf->current_command);
1149  }
1150 
1151  cf->search_mode = 0;
1152  break;
1153 
1155  if (vec_len (cf->current_command) && cf->cursor > 0)
1156  {
1157  j = cf->cursor;
1158 
1159  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1160  j --;
1161 
1162  while (j && isspace(cf->current_command[j]))
1163  {
1164  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1165  j --;
1166  }
1167  while (j && !isspace(cf->current_command[j]))
1168  {
1169  if (isspace(cf->current_command[j - 1]))
1170  break;
1171  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1172  j --;
1173  }
1174 
1175  cf->cursor = j;
1176  }
1177 
1178  cf->search_mode = 0;
1179  break;
1180 
1182  if (vec_len (cf->current_command) &&
1183  cf->cursor < vec_len(cf->current_command))
1184  {
1185  int e = vec_len(cf->current_command);
1186  j = cf->cursor;
1187  while (j < e && !isspace(cf->current_command[j]))
1188  j ++;
1189  while (j < e && isspace(cf->current_command[j]))
1190  j ++;
1192  cf->current_command + cf->cursor,
1193  j - cf->cursor);
1194  cf->cursor = j;
1195  }
1196 
1197  cf->search_mode = 0;
1198  break;
1199 
1200 
1202  if (vec_len (cf->current_command))
1203  {
1204  if (cf->cursor == vec_len(cf->current_command))
1205  {
1206  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1207  _vec_len (cf->current_command)--;
1208  cf->cursor --;
1209  }
1210  else if (cf->cursor > 0)
1211  {
1212  /* shift everything at & to the right of the cursor left by 1 */
1213  j = vec_len (cf->current_command) - cf->cursor;
1214  memmove (cf->current_command + cf->cursor - 1,
1215  cf->current_command + cf->cursor,
1216  j);
1217  _vec_len (cf->current_command)--;
1218  cf->cursor --;
1219  /* redraw the rest of the line */
1220  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1222  cf->current_command + cf->cursor, j);
1223  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " \b\b", 3);
1224  /* and shift the terminal cursor back where it should be */
1225  while (-- j)
1226  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1227  }
1228  }
1229  cf->search_mode = 0;
1230  cf->excursion = 0;
1232  break;
1233 
1235  if (vec_len (cf->current_command))
1236  {
1237  if (cf->cursor < vec_len(cf->current_command))
1238  {
1239  /* shift everything to the right of the cursor left by 1 */
1240  j = vec_len (cf->current_command) - cf->cursor - 1;
1241  memmove (cf->current_command + cf->cursor,
1242  cf->current_command + cf->cursor + 1,
1243  j);
1244  _vec_len (cf->current_command)--;
1245  /* redraw the rest of the line */
1247  cf->current_command + cf->cursor, j);
1248  unix_vlib_cli_output_cooked (cf, uf, (u8 *) " \b", 2);
1249  /* and shift the terminal cursor back where it should be */
1250  if (j)
1251  {
1252  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1253  while (-- j)
1254  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1255  }
1256  }
1257  }
1258  else if (input == 'D' - '@')
1259  {
1260  /* ^D with no command entered = quit */
1261  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "quit\n", 5);
1265  cf - cm->cli_file_pool);
1266  }
1267  cf->search_mode = 0;
1268  cf->excursion = 0;
1270  break;
1271 
1273  /* If we're in ANSI mode, clear the screen.
1274  * Then redraw the prompt and any existing command input, then put
1275  * the cursor back where it was in that line.
1276  */
1277  if (cf->ansi_capable)
1279  (u8 *) ANSI_CLEAR,
1280  sizeof(ANSI_CLEAR)-1);
1281  else
1282  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1283 
1284  unix_vlib_cli_output_raw (cf, uf,
1285  cm->cli_prompt,
1286  vec_len (cm->cli_prompt));
1287  unix_vlib_cli_output_raw (cf, uf,
1288  cf->current_command,
1289  vec_len (cf->current_command));
1290  for (j = cf->cursor; j < vec_len(cf->current_command); j++)
1291  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b", 1);
1292 
1293  break;
1294 
1297  /* TODO */
1298  break;
1299 
1300 
1302  pager_quit:
1303  unix_cli_pager_prompt_erase (cf, uf);
1304  unix_cli_pager_reset (cf);
1305  unix_cli_cli_prompt (cf, uf);
1306  break;
1307 
1310  /* show next page of the buffer */
1311  if (cf->height + cf->pager_start < cf->pager_lines)
1312  {
1313  u8 * line;
1314  int m = cf->pager_start + (cf->height - 1);
1315  unix_cli_pager_prompt_erase (cf, uf);
1316  for (j = m;
1317  j < cf->pager_lines && cf->pager_start < m;
1318  j ++, cf->pager_start ++)
1319  {
1320  line = cf->pager_vector[j];
1321  unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1322  }
1323  unix_cli_pager_prompt (cf, uf);
1324  }
1325  else
1326  {
1327  if (action == UNIX_CLI_PARSE_ACTION_PAGER_NEXT)
1328  /* no more in buffer, exit, but only if it was <space> */
1329  goto pager_quit;
1330  }
1331  break;
1332 
1335  /* display the next line of the buffer */
1336  if (cf->pager_start < cf->pager_lines - (cf->height - 1))
1337  {
1338  u8 * line;
1339  unix_cli_pager_prompt_erase (cf, uf);
1340  line = cf->pager_vector[cf->pager_start + (cf->height - 1)];
1341  unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1342  cf->pager_start ++;
1343  unix_cli_pager_prompt (cf, uf);
1344  }
1345  else
1346  {
1347  if (action == UNIX_CLI_PARSE_ACTION_PAGER_CRLF)
1348  /* no more in buffer, exit, but only if it was <enter> */
1349  goto pager_quit;
1350  }
1351 
1352  break;
1353 
1355  /* scroll the page back one line */
1356  if (cf->pager_start > 0)
1357  {
1358  u8 * line;
1359  cf->pager_start --;
1360  if (cf->ansi_capable)
1361  {
1362  unix_cli_pager_prompt_erase (cf, uf);
1363  unix_vlib_cli_output_cooked (cf, uf, (u8 *)ANSI_SCROLLDN, sizeof(ANSI_SCROLLDN) - 1);
1365  unix_cli_ansi_cursor(cf, uf, 1, 1);
1366  unix_vlib_cli_output_cooked (cf, uf, (u8 *)ANSI_CLEARLINE, sizeof(ANSI_CLEARLINE) - 1);
1369  unix_cli_pager_prompt_erase (cf, uf);
1370  unix_cli_pager_prompt (cf, uf);
1371  }
1372  else
1373  {
1374  int m = cf->pager_start + (cf->height - 1);
1375  unix_cli_pager_prompt_erase (cf, uf);
1376  for (j = cf->pager_start; j < cf->pager_lines && j < m; j ++)
1377  {
1378  line = cf->pager_vector[j];
1379  unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1380  }
1381  unix_cli_pager_prompt (cf, uf);
1382  }
1383  }
1384  break;
1385 
1387  /* back to the first page of the buffer */
1388  if (cf->pager_start > 0)
1389  {
1390  u8 * line;
1391  cf->pager_start = 0;
1392  int m = cf->pager_start + (cf->height - 1);
1393  unix_cli_pager_prompt_erase (cf, uf);
1394  for (j = cf->pager_start; j < cf->pager_lines && j < m; j ++)
1395  {
1396  line = cf->pager_vector[j];
1397  unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1398  }
1399  unix_cli_pager_prompt (cf, uf);
1400  }
1401  break;
1402 
1404  /* skip to the last page of the buffer */
1405  if (cf->pager_start < cf->pager_lines - (cf->height - 1))
1406  {
1407  u8 * line;
1408  cf->pager_start = cf->pager_lines - (cf->height - 1);
1409  unix_cli_pager_prompt_erase (cf, uf);
1410  unix_cli_pager_message (cf, uf, "skipping", "\n");
1411  for (j = cf->pager_start; j < cf->pager_lines; j ++)
1412  {
1413  line = cf->pager_vector[j];
1414  unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1415  }
1416  unix_cli_pager_prompt (cf, uf);
1417  }
1418  break;
1419 
1421  /* wander back one page in the buffer */
1422  if (cf->pager_start > 0)
1423  {
1424  u8 * line;
1425  int m;
1426  if (cf->pager_start >= cf->height)
1427  cf->pager_start -= cf->height - 1;
1428  else
1429  cf->pager_start = 1;
1430  m = cf->pager_start + cf->height - 1;
1431  unix_cli_pager_prompt_erase (cf, uf);
1432  for (j = cf->pager_start; j < cf->pager_lines && j < m; j ++)
1433  {
1434  line = cf->pager_vector[j];
1435  unix_vlib_cli_output_cooked (cf, uf, line, vec_len(line));
1436  }
1437  unix_cli_pager_prompt (cf, uf);
1438  }
1439  break;
1440 
1442  /* search forwards in the buffer */
1443  break;
1444 
1445 
1447  crlf:
1448  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\n", 1);
1449 
1450  if (cf->has_history && cf->history_limit)
1451  {
1452  if (cf->command_history && vec_len(cf->command_history) >= cf->history_limit)
1453  {
1454  vec_free (cf->command_history[0]);
1455  vec_delete (cf->command_history, 1, 0);
1456  }
1457  /* Don't add blank lines to the cmd history */
1458  if (vec_len (cf->current_command))
1459  {
1460  /* Don't duplicate the previous command */
1461  j = vec_len(cf->command_history);
1462  if (j == 0 ||
1463  (vec_len (cf->current_command) != vec_len (cf->command_history[j - 1]) ||
1464  memcmp(cf->current_command, cf->command_history[j - 1],
1465  vec_len (cf->current_command)) != 0))
1466  {
1467  /* copy the command to the history */
1468  u8 * c = 0;
1469  vec_append(c, cf->current_command);
1470  vec_add1 (cf->command_history, c);
1471  cf->command_number ++;
1472  }
1473  }
1474  cf->excursion = vec_len (cf->command_history);
1475  }
1476 
1477  cf->search_mode = 0;
1479  cf->cursor = 0;
1480 
1481  return 0;
1482 
1485  if (cf->pager_lines)
1486  {
1487  /* no-op for now */
1488  }
1489  else if (cf->has_history && cf->search_mode && isprint(input))
1490  {
1491  int k, limit, offset;
1492  u8 * item;
1493 
1494  vec_add1 (cf->search_key, input);
1495 
1496  search_again:
1497  for (j = 0; j < vec_len(cf->command_history); j++)
1498  {
1499  if (cf->excursion > (i32) vec_len (cf->command_history) -1)
1500  cf->excursion = 0;
1501  else if (cf->excursion < 0)
1502  cf->excursion = vec_len (cf->command_history) -1;
1503 
1504  item = cf->command_history[cf->excursion];
1505 
1506  limit = (vec_len(cf->search_key) > vec_len (item)) ?
1507  vec_len(item) : vec_len (cf->search_key);
1508 
1509  for (offset = 0; offset <= vec_len(item) - limit; offset++)
1510  {
1511  for (k = 0; k < limit; k++)
1512  {
1513  if (item[k+offset] != cf->search_key[k])
1514  goto next_offset;
1515  }
1516  goto found_at_offset;
1517 
1518  next_offset:
1519  ;
1520  }
1521  goto next;
1522 
1523  found_at_offset:
1524  for (j = 0; j < vec_len (cf->current_command); j++)
1525  unix_vlib_cli_output_cooked (cf, uf, (u8 *) "\b \b", 3);
1526 
1527  vec_validate (cf->current_command, vec_len(item)-1);
1528  clib_memcpy (cf->current_command, item, vec_len(item));
1529  _vec_len (cf->current_command) = vec_len(item);
1530 
1532  vec_len (cf->current_command));
1533  cf->cursor = vec_len (cf->current_command);
1534  goto found;
1535 
1536  next:
1537  cf->excursion += cf->search_mode;
1538  }
1539 
1540  unix_vlib_cli_output_cooked (cf, uf, (u8 *)"\nNo match...", 12);
1543  cf->search_mode = 0;
1544  cf->cursor = 0;
1545  goto crlf;
1546  }
1547  else if (isprint(input)) /* skip any errant control codes */
1548  {
1549  if (cf->cursor == vec_len(cf->current_command))
1550  {
1551  /* Append to end */
1552  vec_add1 (cf->current_command, input);
1553  cf->cursor ++;
1554 
1555  /* Echo the character back to the client */
1556  unix_vlib_cli_output_raw (cf, uf, &input, 1);
1557  }
1558  else
1559  {
1560  /* Insert at cursor: resize +1 byte, move everything over */
1561  j = vec_len (cf->current_command) - cf->cursor;
1562  vec_add1 (cf->current_command, (u8)'A');
1563  memmove (cf->current_command + cf->cursor + 1,
1564  cf->current_command + cf->cursor,
1565  j);
1566  cf->current_command[cf->cursor] = input;
1567  /* Redraw the line */
1568  j ++;
1569  unix_vlib_cli_output_raw (cf, uf,
1570  cf->current_command + cf->cursor, j);
1571  /* Put terminal cursor back */
1572  while (-- j)
1573  unix_vlib_cli_output_raw (cf, uf, (u8 *)"\b", 1);
1574  cf->cursor ++;
1575  }
1576  }
1577  else
1578  {
1579  /* no-op - not printable or otherwise not actionable */
1580  }
1581 
1582  found:
1583 
1584  break;
1585 
1587  break;
1588  }
1589  return 1;
1590 }
1591 
1592 /** \brief Process input bytes on a stream to provide line editing and
1593  * command history in the CLI. */
1595  unix_main_t * um,
1596  unix_cli_file_t * cf)
1597 {
1599  int i;
1600 
1601  for (i = 0; i < vec_len (cf->input_vector); i++)
1602  {
1603  unix_cli_parse_action_t action;
1604  i32 matched = 0;
1606 
1607  /* If we're in the pager mode, search the pager actions */
1608  a = cf->pager_lines ? unix_cli_parse_pager : unix_cli_parse_strings;
1609 
1610  /* See if the input buffer is some sort of control code */
1611  action = unix_cli_match_action(a, &cf->input_vector[i],
1612  vec_len (cf->input_vector) - i, &matched);
1613 
1614  switch (action)
1615  {
1617  if (i)
1618  {
1619  /* There was a partial match which means we need more bytes
1620  * than the input buffer currently has.
1621  * Since the bytes before here have been processed, shift
1622  * the remaining contents to the start of the input buffer.
1623  */
1624  vec_delete (cf->input_vector, i, 0);
1625  }
1626  return 1; /* wait for more */
1627 
1629  /* process telnet options */
1630  matched = unix_cli_process_telnet(um, cf, uf,
1631  cf->input_vector + i, vec_len(cf->input_vector) - i);
1632  if (matched < 0)
1633  {
1634  if (i)
1635  {
1636  /* There was a partial match which means we need more bytes
1637  * than the input buffer currently has.
1638  * Since the bytes before here have been processed, shift
1639  * the remaining contents to the start of the input buffer.
1640  */
1641  vec_delete (cf->input_vector, i, 0);
1642  }
1643  return 1; /* wait for more */
1644  }
1645  break;
1646 
1647  default:
1648  /* process the action */
1649  if (!unix_cli_line_process_one(cm, um, cf, uf,
1650  cf->input_vector[i], action))
1651  {
1652  /* CRLF found. Consume the bytes from the input_vector */
1653  vec_delete (cf->input_vector, i + matched, 0);
1654  /* And tell our caller to execute cf->input_command */
1655  return 0;
1656  }
1657  }
1658 
1659  i += matched;
1660  }
1661 
1663  return 1;
1664 }
1665 
1666 /** \brief Process input to a CLI session. */
1668  uword cli_file_index)
1669 {
1670  unix_main_t * um = &unix_main;
1671  unix_file_t * uf;
1672  unix_cli_file_t * cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1673  unformat_input_t input;
1674  int vlib_parse_eval (u8 *);
1675 
1676 more:
1677  /* Try vlibplex first. Someday... */
1678  if (0 && vlib_parse_eval (cf->input_vector) == 0)
1679  goto done;
1680 
1681  if (cf->line_mode)
1682  {
1683  /* just treat whatever we got as a complete line of input */
1684  cf->current_command = cf->input_vector;
1685  }
1686  else
1687  {
1688  /* Line edit, echo, etc. */
1689  if (unix_cli_line_edit (cm, um, cf))
1690  /* want more input */
1691  return;
1692  }
1693 
1694  if (um->log_fd)
1695  {
1696  static u8 * lv;
1697  vec_reset_length (lv);
1698  lv = format (lv, "%U[%d]: %v",
1700  0 /* current bat-time */,
1701  0 /* current bat-format */,
1702  cli_file_index,
1703  cf->input_vector);
1704  {
1705  int rv __attribute__((unused)) =
1706  write (um->log_fd, lv, vec_len(lv));
1707  }
1708  }
1709 
1710  /* Copy our input command to a new string */
1711  unformat_init_vector (&input, cf->current_command);
1712 
1713  /* Remove leading white space from input. */
1714  (void) unformat (&input, "");
1715 
1716  cm->current_input_file_index = cli_file_index;
1717  cf->pager_lines = 0; /* start a new pager session */
1718  cf->pager_start = 0;
1719 
1721  vlib_cli_input (um->vlib_main, &input, unix_vlib_cli_output, cli_file_index);
1722 
1723  /* Zero buffer since otherwise unformat_free will call vec_free on it. */
1724  input.buffer = 0;
1725 
1726  unformat_free (&input);
1727 
1728  /* Re-fetch pointer since pool may have moved. */
1729  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1731 
1732 done:
1733  /* reset vector; we'll re-use it later */
1734  if (cf->line_mode)
1736  else
1738 
1739  if (cf->no_pager == 2)
1740  {
1741  /* Pager was programmatically disabled */
1742  unix_cli_pager_message (cf, uf, "pager buffer overflowed", "\n");
1743  cf->no_pager = um->cli_no_pager;
1744  }
1745 
1746  if (cf->pager_lines == 0 || cf->pager_lines < cf->height)
1747  {
1748  /* There was no need for the pager */
1749  unix_cli_pager_reset (cf);
1750 
1751  /* Prompt. */
1752  unix_cli_cli_prompt (cf, uf);
1753  }
1754  else
1755  {
1756  /* Display the pager prompt */
1757  unix_cli_pager_prompt(cf, uf);
1758  }
1759 
1760  /* Any residual data in the input vector? */
1761  if (vec_len (cf->input_vector))
1762  goto more;
1763 }
1764 
1765 static void unix_cli_kill (unix_cli_main_t * cm, uword cli_file_index)
1766 {
1767  unix_main_t * um = &unix_main;
1768  unix_cli_file_t * cf;
1769  unix_file_t * uf;
1770  int i;
1771 
1772  cf = pool_elt_at_index (cm->cli_file_pool, cli_file_index);
1774 
1775  /* Quit/EOF on stdin means quit program. */
1778 
1779  vec_free (cf->current_command);
1780  vec_free (cf->search_key);
1781 
1782  for (i = 0; i < vec_len (cf->command_history); i++)
1783  vec_free (cf->command_history[i]);
1784 
1785  vec_free (cf->command_history);
1786 
1787  unix_file_del (um, uf);
1788 
1789  unix_cli_file_free (cf);
1790  pool_put (cm->cli_file_pool, cf);
1791 }
1792 
1793 static uword
1795  vlib_node_runtime_t * rt,
1796  vlib_frame_t * f)
1797 {
1799  uword i, * data = 0;
1800 
1801  while (1)
1802  {
1803  unix_cli_process_event_type_t event_type;
1805  event_type = vlib_process_get_events (vm, &data);
1806 
1807  switch (event_type)
1808  {
1810  for (i = 0; i < vec_len (data); i++)
1811  unix_cli_process_input (cm, data[i]);
1812  break;
1813 
1815  /* Kill this process. */
1816  for (i = 0; i < vec_len (data); i++)
1817  unix_cli_kill (cm, data[i]);
1818  goto done;
1819  }
1820 
1821  if (data)
1822  _vec_len (data) = 0;
1823  }
1824 
1825  done:
1826  vec_free (data);
1827 
1828  vlib_node_set_state (vm, rt->node_index, VLIB_NODE_STATE_DISABLED);
1829 
1830  /* Add node index so we can re-use this process later. */
1832 
1833  return 0;
1834 }
1835 
1837 {
1839  unix_cli_file_t * cf;
1840  int n;
1841 
1843 
1844  /* Flush output vector. */
1845  n = write (uf->file_descriptor,
1846  cf->output_vector, vec_len (cf->output_vector));
1847 
1848  if (n < 0 && errno != EAGAIN)
1849  return clib_error_return_unix (0, "write");
1850 
1851  else if (n > 0)
1852  unix_cli_del_pending_output (uf, cf, n);
1853 
1854  return /* no error */ 0;
1855 }
1856 
1858 {
1859  unix_main_t * um = &unix_main;
1861  unix_cli_file_t * cf;
1862  uword l;
1863  int n, n_read, n_try;
1864 
1866 
1867  n = n_try = 4096;
1868  while (n == n_try) {
1869  l = vec_len (cf->input_vector);
1870  vec_resize (cf->input_vector, l + n_try);
1871 
1872  n = read (uf->file_descriptor, cf->input_vector + l, n_try);
1873 
1874  /* Error? */
1875  if (n < 0 && errno != EAGAIN)
1876  return clib_error_return_unix (0, "read");
1877 
1878  n_read = n < 0 ? 0 : n;
1879  _vec_len (cf->input_vector) = l + n_read;
1880  }
1881 
1882  if (! (n < 0))
1884  cf->process_node_index,
1885  (n_read == 0
1888  /* event data */ uf->private_data);
1889 
1890  return /* no error */ 0;
1891 }
1892 
1893 /** Store a new CLI session.
1894  * @param name The name of the session.
1895  * @param fd The file descriptor for the session I/O.
1896  * @return The session ID.
1897  */
1898 static u32 unix_cli_file_add (unix_cli_main_t * cm, char * name, int fd)
1899 {
1900  unix_main_t * um = &unix_main;
1901  unix_cli_file_t * cf;
1902  unix_file_t template = {0};
1903  vlib_main_t * vm = um->vlib_main;
1904  vlib_node_t * n;
1905 
1906  name = (char *) format (0, "unix-cli-%s", name);
1907 
1909  {
1911 
1912  /* Find node and give it new name. */
1913  n = vlib_get_node (vm, cm->unused_cli_process_node_indices[l - 1]);
1914  vec_free (n->name);
1915  n->name = (u8 *) name;
1916 
1917  vlib_node_set_state (vm, n->index, VLIB_NODE_STATE_POLLING);
1918 
1919  _vec_len (cm->unused_cli_process_node_indices) = l - 1;
1920  }
1921  else
1922  {
1923  static vlib_node_registration_t r = {
1924  .function = unix_cli_process,
1925  .type = VLIB_NODE_TYPE_PROCESS,
1926  .process_log2_n_stack_bytes = 14,
1927  };
1928 
1929  r.name = name;
1930  vlib_register_node (vm, &r);
1931  vec_free (name);
1932 
1933  n = vlib_get_node (vm, r.index);
1934  }
1935 
1936  pool_get (cm->cli_file_pool, cf);
1937  memset (cf, 0, sizeof (*cf));
1938 
1939  template.read_function = unix_cli_read_ready;
1940  template.write_function = unix_cli_write_ready;
1941  template.file_descriptor = fd;
1942  template.private_data = cf - cm->cli_file_pool;
1943 
1944  cf->process_node_index = n->index;
1945  cf->unix_file_index = unix_file_add (um, &template);
1946  cf->output_vector = 0;
1947  cf->input_vector = 0;
1948 
1950 
1953  p->output_function_arg = cf - cm->cli_file_pool;
1954 
1955  return cf - cm->cli_file_pool;
1956 }
1957 
1958 /** Telnet listening socket has a new connection. */
1960 {
1961  unix_main_t * um = &unix_main;
1963  clib_socket_t * s = &um->cli_listen_socket;
1964  clib_socket_t client;
1965  char * client_name;
1966  clib_error_t * error;
1967  unix_cli_file_t * cf;
1968  u32 cf_index;
1969 
1970  error = clib_socket_accept (s, &client);
1971  if (error)
1972  return error;
1973 
1974  client_name = (char *) format (0, "%U%c", format_sockaddr, &client.peer, 0);
1975 
1976  cf_index = unix_cli_file_add (cm, client_name, client.fd);
1977  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
1978 
1979  /* No longer need CLIB version of socket. */
1980  clib_socket_free (&client);
1981 
1982  vec_free (client_name);
1983 
1984  /* if we're supposed to run telnet session in character mode (default) */
1985  if (um->cli_line_mode == 0)
1986  {
1987  /*
1988  * Set telnet client character mode, echo on, suppress "go-ahead".
1989  * Technically these should be negotiated, but this works.
1990  */
1991  u8 charmode_option[] = {
1992  IAC, WONT, TELOPT_LINEMODE, /* server will do char-by-char */
1993  IAC, DONT, TELOPT_LINEMODE, /* client should do char-by-char */
1994  IAC, WILL, TELOPT_SGA, /* server willl supress GA */
1995  IAC, DO, TELOPT_SGA, /* client should supress Go Ahead */
1996  IAC, WILL, TELOPT_ECHO, /* server will do echo */
1997  IAC, DONT, TELOPT_ECHO, /* client should not echo */
1998  IAC, DO, TELOPT_TTYPE, /* client should tell us its term type */
1999  IAC, SB, TELOPT_TTYPE, 1, IAC, SE, /* now tell me ttype */
2000  IAC, DO, TELOPT_NAWS, /* client should tell us its window sz */
2001  IAC, SB, TELOPT_NAWS, 1, IAC, SE, /* now tell me window size */
2002  };
2003 
2004  /* Enable history on this CLI */
2005  cf->history_limit = um->cli_history_limit;
2006  cf->has_history = cf->history_limit != 0;
2007 
2008  /* Make sure this session is in line mode */
2009  cf->line_mode = 0;
2010 
2011  /* We need CRLF */
2012  cf->crlf_mode = 1;
2013 
2014  /* Setup the pager */
2015  cf->no_pager = um->cli_no_pager;
2016 
2018 
2019  /* Send the telnet options */
2020  unix_vlib_cli_output_raw (cf, uf, charmode_option,
2021  ARRAY_LEN(charmode_option));
2022 
2023  /* In case the client doesn't negotiate terminal type, use
2024  * a timer to kick off the initial prompt. */
2025  timer_call (unix_cli_file_welcome_timer, cf_index, 1);
2026  }
2027 
2028  return error;
2029 }
2030 
2031 /** The system terminal has informed us that the window size
2032  * has changed.
2033  */
2034 static void
2036 {
2039  cm->stdin_cli_file_index);
2040  struct winsize ws;
2041  (void)signum;
2042 
2043  /* Terminal resized, fetch the new size */
2044  ioctl(UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws);
2045  cf->width = ws.ws_col;
2046  cf->height = ws.ws_row;
2047 }
2048 
2049 /** Handle configuration directives in the @em unix section. */
2050 static clib_error_t *
2052 {
2053  unix_main_t * um = &unix_main;
2055  int flags;
2056  clib_error_t * error = 0;
2057  unix_cli_file_t * cf;
2058  u32 cf_index;
2059  struct termios tio;
2060  struct sigaction sa;
2061  struct winsize ws;
2062  u8 * term;
2063 
2064  /* We depend on unix flags being set. */
2065  if ((error = vlib_call_config_function (vm, unix_config)))
2066  return error;
2067 
2068  if (um->flags & UNIX_FLAG_INTERACTIVE)
2069  {
2070  /* Set stdin to be non-blocking. */
2071  if ((flags = fcntl (UNIX_CLI_STDIN_FD, F_GETFL, 0)) < 0)
2072  flags = 0;
2073  fcntl (UNIX_CLI_STDIN_FD, F_SETFL, flags | O_NONBLOCK);
2074 
2075  cf_index = unix_cli_file_add (cm, "stdin", UNIX_CLI_STDIN_FD);
2076  cf = pool_elt_at_index (cm->cli_file_pool, cf_index);
2077  cm->stdin_cli_file_index = cf_index;
2078 
2079  /* If stdin is a tty and we are using chacracter mode, enable
2080  * history on the CLI and set the tty line discipline accordingly. */
2081  if (isatty(UNIX_CLI_STDIN_FD) && um->cli_line_mode == 0)
2082  {
2083  /* Capture terminal resize events */
2084  sa.sa_handler = unix_cli_resize_interrupt;
2085  sa.sa_flags = 0;
2086  if (sigaction (SIGWINCH, &sa, 0) < 0)
2087  clib_panic ("sigaction");
2088 
2089  /* Retrieve the current terminal size */
2090  ioctl(UNIX_CLI_STDIN_FD, TIOCGWINSZ, &ws);
2091  cf->width = ws.ws_col;
2092  cf->height = ws.ws_row;
2093 
2094  if (cf->width == 0 || cf->height == 0)
2095  /* We have a tty, but no size. Stick to line mode. */
2096  goto notty;
2097 
2098  /* Setup the history */
2099  cf->history_limit = um->cli_history_limit;
2100  cf->has_history = cf->history_limit != 0;
2101 
2102  /* Setup the pager */
2103  cf->no_pager = um->cli_no_pager;
2104 
2105  /* We're going to be in char by char mode */
2106  cf->line_mode = 0;
2107 
2108  /* Save the original tty state so we can restore it later */
2109  tcgetattr(UNIX_CLI_STDIN_FD, &um->tio_stdin);
2110  um->tio_isset = 1;
2111 
2112  /* Tweak the tty settings */
2113  tio = um->tio_stdin;
2114  /* echo off, canonical mode off, ext'd input processing off */
2115  tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
2116  tio.c_cc[VMIN] = 1; /* 1 byte at a time */
2117  tio.c_cc[VTIME] = 0; /* no timer */
2118  tcsetattr(UNIX_CLI_STDIN_FD, TCSAFLUSH, &tio);
2119 
2120  /* See if we can do ANSI/VT100 output */
2121  term = (u8 *)getenv("TERM");
2122  if (term != NULL)
2124  strlen((char *)term));
2125  }
2126  else
2127  {
2128  notty:
2129  /* No tty, so make sure these things are off */
2130  cf->no_pager = 1;
2131  cf->history_limit = 0;
2132  cf->has_history = 0;
2133  cf->line_mode = 1;
2134  }
2135 
2136  /* Send banner and initial prompt */
2137  unix_cli_file_welcome(cm, cf);
2138  }
2139 
2140  /* If we have socket config, LISTEN, otherwise, don't */
2141  clib_socket_t * s = &um->cli_listen_socket;
2142  if(s->config && s->config[0] != 0) {
2143  /* CLI listen. */
2144  unix_file_t template = {0};
2145 
2146  s->flags = SOCKET_IS_SERVER; /* listen, don't connect */
2147  error = clib_socket_init (s);
2148 
2149  if (error)
2150  return error;
2151 
2152  template.read_function = unix_cli_listen_read_ready;
2153  template.file_descriptor = s->fd;
2154 
2155  unix_file_add (um, &template);
2156  }
2157 
2158  /* Set CLI prompt. */
2159  if (! cm->cli_prompt)
2160  cm->cli_prompt = format (0, "VLIB: ");
2161 
2162  return 0;
2163 }
2164 
2166 
2167 /** Called when VPP is shutting down, this resets the system
2168  * terminal state, if previously saved.
2169  */
2170 static clib_error_t *
2172 {
2173  unix_main_t * um = &unix_main;
2174 
2175  /* If stdin is a tty and we saved the tty state, reset the tty state */
2176  if (isatty(UNIX_CLI_STDIN_FD) && um->tio_isset)
2177  tcsetattr(UNIX_CLI_STDIN_FD, TCSAFLUSH, &um->tio_stdin);
2178 
2179  return 0;
2180 }
2181 
2183 
2184 /** Set the CLI prompt.
2185  * @param The C string to set the prompt to.
2186  * @note This setting is global; it impacts all current
2187  * and future CLI sessions.
2188  */
2189 void vlib_unix_cli_set_prompt (char * prompt)
2190 {
2191  char * fmt = (prompt[strlen(prompt)-1] == ' ') ? "%s" : "%s ";
2193  if (cm->cli_prompt)
2194  vec_free (cm->cli_prompt);
2195  cm->cli_prompt = format (0, fmt, prompt);
2196 }
2197 
2198 /** CLI command to quit the terminal session.
2199  * @note If this is a stdin session then this will
2200  * shutdown VPP also.
2201  */
2202 static clib_error_t *
2204  unformat_input_t * input,
2205  vlib_cli_command_t * cmd)
2206 {
2208 
2210  vlib_current_process (vm),
2213  return 0;
2214 }
2215 
2216 VLIB_CLI_COMMAND (unix_cli_quit_command, static) = {
2217  .path = "quit",
2218  .short_help = "Exit CLI",
2219  .function = unix_cli_quit,
2220 };
2221 
2222 /** CLI command to execute a VPP command script. */
2223 static clib_error_t *
2225  unformat_input_t * input,
2226  vlib_cli_command_t * cmd)
2227 {
2228  char * file_name;
2229  int fd;
2230  unformat_input_t sub_input;
2231  clib_error_t * error;
2232 
2233  file_name = 0;
2234  fd = -1;
2235  error = 0;
2236 
2237  if (! unformat (input, "%s", &file_name))
2238  {
2239  error = clib_error_return (0, "expecting file name, got `%U'",
2240  format_unformat_error, input);
2241  goto done;
2242  }
2243 
2244  fd = open (file_name, O_RDONLY);
2245  if (fd < 0)
2246  {
2247  error = clib_error_return_unix (0, "failed to open `%s'", file_name);
2248  goto done;
2249  }
2250 
2251  /* Make sure its a regular file. */
2252  {
2253  struct stat s;
2254 
2255  if (fstat (fd, &s) < 0)
2256  {
2257  error = clib_error_return_unix (0, "failed to stat `%s'", file_name);
2258  goto done;
2259  }
2260 
2261  if (! (S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
2262  {
2263  error = clib_error_return (0, "not a regular file `%s'", file_name);
2264  goto done;
2265  }
2266  }
2267 
2268  unformat_init_unix_file (&sub_input, fd);
2269 
2270  vlib_cli_input (vm, &sub_input, 0, 0);
2271  unformat_free (&sub_input);
2272 
2273  done:
2274  if (fd > 0)
2275  close (fd);
2276  vec_free (file_name);
2277 
2278  return error;
2279 }
2280 
2281 VLIB_CLI_COMMAND (cli_exec, static) = {
2282  .path = "exec",
2283  .short_help = "Execute commands from file",
2284  .function = unix_cli_exec,
2285  .is_mp_safe = 1,
2286 };
2287 
2288 /** CLI command to show various unix error statistics. */
2289 static clib_error_t *
2291  unformat_input_t * input,
2292  vlib_cli_command_t * cmd)
2293 {
2294  unix_main_t * um = &unix_main;
2295  clib_error_t * error = 0;
2296  int i, n_errors_to_show;
2297  unix_error_history_t * unix_errors = 0;
2298 
2299  n_errors_to_show = 1 << 30;
2300 
2302  {
2303  if (! unformat (input, "%d", &n_errors_to_show))
2304  {
2305  error = clib_error_return (0, "expecting integer number of errors to show, got `%U'",
2306  format_unformat_error, input);
2307  goto done;
2308  }
2309  }
2310 
2311  n_errors_to_show = clib_min (ARRAY_LEN (um->error_history), n_errors_to_show);
2312 
2313  i = um->error_history_index > 0 ? um->error_history_index - 1 : ARRAY_LEN (um->error_history) - 1;
2314 
2315  while (n_errors_to_show > 0)
2316  {
2317  unix_error_history_t * eh = um->error_history + i;
2318 
2319  if (! eh->error)
2320  break;
2321 
2322  vec_add1 (unix_errors, eh[0]);
2323  n_errors_to_show -= 1;
2324  if (i == 0)
2325  i = ARRAY_LEN (um->error_history) - 1;
2326  else
2327  i--;
2328  }
2329 
2330  if (vec_len (unix_errors) == 0)
2331  vlib_cli_output (vm, "no Unix errors so far");
2332  else
2333  {
2334  vlib_cli_output (vm, "%Ld total errors seen", um->n_total_errors);
2335  for (i = vec_len (unix_errors) - 1; i >= 0; i--)
2336  {
2337  unix_error_history_t * eh = vec_elt_at_index (unix_errors, i);
2338  vlib_cli_output (vm, "%U: %U",
2339  format_time_interval, "h:m:s:u", eh->time,
2340  format_clib_error, eh->error);
2341  }
2342  vlib_cli_output (vm, "%U: time now",
2343  format_time_interval, "h:m:s:u", vlib_time_now (vm));
2344  }
2345 
2346  done:
2347  vec_free (unix_errors);
2348  return error;
2349 }
2350 
2351 VLIB_CLI_COMMAND (cli_unix_show_errors, static) = {
2352  .path = "show unix-errors",
2353  .short_help = "Show Unix system call error history",
2354  .function = unix_show_errors,
2355 };
2356 
2357 /** CLI command to show session command history. */
2358 static clib_error_t *
2360  unformat_input_t * input,
2361  vlib_cli_command_t * cmd)
2362 {
2364  unix_cli_file_t * cf;
2365  int i, j;
2366 
2368 
2369  if (cf->has_history && cf->history_limit)
2370  {
2371  i = 1 + cf->command_number - vec_len(cf->command_history);
2372  for (j = 0; j < vec_len (cf->command_history); j++)
2373  vlib_cli_output (vm, "%d %v\n", i + j, cf->command_history[j]);
2374  }
2375  else
2376  {
2377  vlib_cli_output (vm, "History not enabled.\n");
2378  }
2379 
2380  return 0;
2381 }
2382 
2383 VLIB_CLI_COMMAND (cli_unix_cli_show_history, static) = {
2384  .path = "history",
2385  .short_help = "Show current session command history",
2386  .function = unix_cli_show_history,
2387 };
2388 
2389 /** CLI command to show terminal status. */
2390 static clib_error_t *
2392  unformat_input_t * input,
2393  vlib_cli_command_t * cmd)
2394 {
2395  unix_main_t * um = &unix_main;
2397  unix_cli_file_t * cf;
2398  vlib_node_t * n;
2399 
2401  n = vlib_get_node (vm, cf->process_node_index);
2402 
2403  vlib_cli_output (vm, "Terminal name: %v\n", n->name);
2404  vlib_cli_output (vm, "Terminal mode: %s\n", cf->line_mode ?
2405  "line-by-line" :
2406  "char-by-char");
2407  vlib_cli_output (vm, "Terminal width: %d\n", cf->width);
2408  vlib_cli_output (vm, "Terminal height: %d\n", cf->height);
2409  vlib_cli_output (vm, "ANSI capable: %s\n", cf->ansi_capable ? "yes" : "no");
2410  vlib_cli_output (vm, "History enabled: %s%s\n",
2411  cf->has_history ? "yes" : "no",
2412  !cf->has_history || cf->history_limit ? "" : " (disabled by history limit)");
2413  if (cf->has_history)
2414  vlib_cli_output (vm, "History limit: %d\n", cf->history_limit);
2415  vlib_cli_output (vm, "Pager enabled: %s%s%s\n",
2416  cf->no_pager ? "no" : "yes",
2417  cf->no_pager || cf->height ? "" : " (disabled by terminal height)",
2418  cf->no_pager || um->cli_pager_buffer_limit ? "" : " (disabled by buffer limit)");
2419  if (!cf->no_pager)
2420  vlib_cli_output (vm, "Pager limit: %d\n", um->cli_pager_buffer_limit);
2421  vlib_cli_output (vm, "CRLF mode: %s\n", cf->crlf_mode ? "CR+LF" : "LF");
2422 
2423  return 0;
2424 }
2425 
2426 VLIB_CLI_COMMAND (cli_unix_cli_show_terminal, static) = {
2427  .path = "show terminal",
2428  .short_help = "Show current session terminal settings",
2429  .function = unix_cli_show_terminal,
2430 };
2431 
2432 /** CLI command to set terminal pager settings. */
2433 static clib_error_t *
2435  unformat_input_t * input,
2436  vlib_cli_command_t * cmd)
2437 {
2438  unix_main_t * um = &unix_main;
2440  unix_cli_file_t * cf;
2441  unformat_input_t _line_input, * line_input = &_line_input;
2442 
2443  if (! unformat_user (input, unformat_line_input, line_input))
2444  return 0;
2445 
2447 
2448  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2449  {
2450  if (unformat (line_input, "on"))
2451  cf->no_pager = 0;
2452  else if (unformat (line_input, "off"))
2453  cf->no_pager = 1;
2454  else if (unformat (line_input, "limit %u", &um->cli_pager_buffer_limit))
2455  vlib_cli_output (vm, "Pager limit set to %u lines; note, this is global.\n",
2457  else
2458  return clib_error_return (0, "unknown parameter: `%U`",
2459  format_unformat_error, line_input);
2460  }
2461 
2462  unformat_free(line_input);
2463 
2464  return 0;
2465 }
2466 
2467 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_pager, static) = {
2468  .path = "set terminal pager",
2469  .short_help = "set terminal pager [on|off] [limit <lines>]",
2470  .function = unix_cli_set_terminal_pager,
2471 };
2472 
2473 /** CLI command to set terminal history settings. */
2474 static clib_error_t *
2476  unformat_input_t * input,
2477  vlib_cli_command_t * cmd)
2478 {
2480  unix_cli_file_t * cf;
2481  unformat_input_t _line_input, * line_input = &_line_input;
2482  u32 limit;
2483 
2484  if (! unformat_user (input, unformat_line_input, line_input))
2485  return 0;
2486 
2488 
2489  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
2490  {
2491  if (unformat (line_input, "on"))
2492  cf->has_history = 1;
2493  else if (unformat (line_input, "off"))
2494  cf->has_history = 0;
2495  else if (unformat (line_input, "limit %u", &cf->history_limit))
2496  ;
2497  else
2498  return clib_error_return (0, "unknown parameter: `%U`",
2499  format_unformat_error, line_input);
2500 
2501  /* If we reduced history size, or turned it off, purge the history */
2502  limit = cf->has_history ? cf->history_limit : 0;
2503 
2504  while (cf->command_history && vec_len(cf->command_history) >= limit)
2505  {
2506  vec_free (cf->command_history[0]);
2507  vec_delete (cf->command_history, 1, 0);
2508  }
2509  }
2510 
2511  unformat_free(line_input);
2512 
2513  return 0;
2514 }
2515 
2516 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_history, static) = {
2517  .path = "set terminal history",
2518  .short_help = "set terminal history [on|off] [limit <lines>]",
2519  .function = unix_cli_set_terminal_history,
2520 };
2521 
2522 /** CLI command to set terminal ANSI settings. */
2523 static clib_error_t *
2525  unformat_input_t * input,
2526  vlib_cli_command_t * cmd)
2527 {
2529  unix_cli_file_t * cf;
2530 
2532 
2533  if (unformat (input, "on"))
2534  cf->ansi_capable = 1;
2535  else if (unformat (input, "off"))
2536  cf->ansi_capable = 0;
2537  else
2538  return clib_error_return (0, "unknown parameter: `%U`",
2539  format_unformat_error, input);
2540 
2541  return 0;
2542 }
2543 
2544 VLIB_CLI_COMMAND (cli_unix_cli_set_terminal_ansi, static) = {
2545  .path = "set terminal ansi",
2546  .short_help = "set terminal ansi [on|off]",
2547  .function = unix_cli_set_terminal_ansi,
2548 };
2549 
2550 static clib_error_t *
2552 {
2553  return 0;
2554 }
2555 
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:394
unix_file_t * file_pool
Definition: unix.h:85
u32 history_limit
Maximum number of history entries this session will store.
Definition: cli.c:138
u32 command_number
Current command line counter.
Definition: cli.c:141
uword output_function_arg
Definition: node.h:502
u32 error_history_index
Definition: unix.h:94
void unformat_init_vector(unformat_input_t *input, u8 *vector_string)
Definition: unformat.c:1000
u32 unix_file_index
The file index held by unix.c.
Definition: cli.c:123
sll srl srl sll sra u16x4 i
Definition: vector_sse2.h:267
static unix_cli_main_t unix_cli_main
CLI global state.
Definition: cli.c:399
#define clib_min(x, y)
Definition: clib.h:295
u32 pager_start
Line number of top of page.
Definition: cli.c:171
u32 flags
Definition: unix.h:53
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:942
always_inline uword vlib_process_get_events(vlib_main_t *vm, uword **data_vector)
Definition: node_funcs.h:410
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:425
a
Definition: bitmap.h:393
u8 * input_vector
Vector of input saved by Unix input node to be processed by CLI process.
Definition: cli.c:130
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:2475
static void(BVT(clib_bihash)*h, BVT(clib_bihash_value)*v)
always_inline vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Definition: node_funcs.h:46
u8 * line
The line to print.
Definition: cli.c:96
static void unix_vlib_cli_output(uword cli_file_index, u8 *buffer, uword buffer_bytes)
VLIB CLI output function.
Definition: cli.c:677
u8 * format_clib_error(u8 *s, va_list *va)
Definition: error.c:188
u32 flags
Definition: unix.h:79
#define ANSI_RESET
ANSI reset color settings.
Definition: cli.c:68
static void unix_cli_add_pending_output(unix_file_t *uf, unix_cli_file_t *cf, u8 *buffer, uword buffer_bytes)
Definition: cli.c:461
u32 pager_lines
Lines currently displayed.
Definition: cli.c:168
Carriage return, newline or enter.
Definition: cli.c:214
#define ANSI_RESTCURSOR
ANSI restore cursor position if previously saved.
Definition: cli.c:84
u32 width
Terminal width.
Definition: cli.c:174
u8 * cli_prompt
Prompt string for CLI.
Definition: cli.c:383
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:2434
always_inline void unformat_free(unformat_input_t *i)
Definition: format.h:160
#define UNFORMAT_END_OF_INPUT
Definition: format.h:142
u32 cli_pager_buffer_limit
Definition: unix.h:117
#define NULL
Definition: clib.h:55
u8 ** command_history
Definition: cli.c:133
u32 index
Definition: node.h:203
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:2051
struct termios tio_stdin
Definition: unix.h:123
always_inline uword unix_file_add(unix_main_t *um, unix_file_t *template)
Definition: unix.h:131
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:480
Unix CLI session.
Definition: cli.c:121
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:664
struct _vlib_node_registration vlib_node_registration_t
u8 * current_command
Definition: cli.c:134
static clib_error_t * unix_cli_read_ready(unix_file_t *uf)
Definition: cli.c:1857
always_inline void vlib_process_signal_event(vlib_main_t *vm, uword node_index, uword type_opaque, uword data)
Definition: node_funcs.h:789
#define VLIB_MAIN_LOOP_EXIT_CLI
Definition: main.h:91
#define vlib_call_config_function(vm, x)
Definition: init.h:193
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:231
u32 cursor
Position of the insert cursor on the current input line.
Definition: cli.c:147
clib_socket_t cli_listen_socket
Definition: unix.h:88
#define ANSI_SCROLLDN
ANSI scroll screen down one line.
Definition: cli.c:80
clib_error_t * clib_socket_init(clib_socket_t *s)
Definition: socket.c:280
#define pool_get(P, E)
Definition: pool.h:186
unix_cli_parse_action_t action
Action to take when matched.
Definition: cli.c:256
int log_fd
Definition: unix.h:105
#define SOCKET_IS_SERVER
Definition: socket.h:57
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
u32 vlib_register_node(vlib_main_t *vm, vlib_node_registration_t *r)
Definition: node.c:449
always_inline uword unformat_check_input(unformat_input_t *i)
Definition: format.h:168
#define vec_add(V, E, N)
Add N elements to end of vector V (no header, unspecified alignment)
Definition: vec.h:557
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:1594
u8 ansi_capable
Can we do ANSI output?
Definition: cli.c:156
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:2524
static clib_error_t * unix_cli_init(vlib_main_t *vm)
Definition: cli.c:2551
u8 * format_timeval(u8 *s, va_list *args)
Definition: unix-formats.c:748
#define CSI
ANSI Control Sequence Introducer.
Definition: cli.c:63
static void unix_cli_cli_prompt(unix_cli_file_t *cf, unix_file_t *uf)
Output the CLI prompt.
Definition: cli.c:602
struct _socket_t clib_socket_t
u8 * output_vector
Vector of output pending write to file descriptor.
Definition: cli.c:126
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:109
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:2391
#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:268
int i32
Definition: types.h:81
u32 len
Length of input without final NUL.
Definition: cli.c:255
Mapping of input buffer strings to action values.
Definition: cli.c:253
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
always_inline void vlib_node_set_state(vlib_main_t *vm, u32 node_index, vlib_node_state_t new_state)
Definition: node_funcs.h:100
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:610
always_inline void unix_cli_pager_reset(unix_cli_file_t *f)
Resets the pager buffer and other data.
Definition: cli.c:187
u32 process_node_index
Process node identifier.
Definition: cli.c:180
#define vec_resize(V, N)
Resize a vector (no header, unspecified alignment) Add N elements to end of given vector V...
Definition: vec.h:199
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:953
static void unix_cli_del_pending_output(unix_file_t *uf, unix_cli_file_t *cf, uword n_bytes)
Definition: cli.c:479
always_inline word unix_vlib_findchr(u8 chr, u8 *str, word len)
A bit like strchr with a buffer length limit.
Definition: cli.c:509
vlib_main_t * vlib_main
Definition: unix.h:77
u32 * unused_cli_process_node_indices
Vec pool of unused session indices.
Definition: cli.c:389
#define UNIX_CLI_MAX_DEPTH_TELNET
Maximum depth into a byte stream from which to compile a Telnet protocol message. ...
Definition: cli.c:88
static u32 unix_cli_file_add(unix_cli_main_t *cm, char *name, int fd)
Store a new CLI session.
Definition: cli.c:1898
static clib_error_t * unix_config(vlib_main_t *vm, unformat_input_t *input)
Definition: main.c:304
static uword unix_cli_process(vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f)
Definition: cli.c:1794
static clib_error_t * unix_cli_listen_read_ready(unix_file_t *uf)
Telnet listening socket has a new connection.
Definition: cli.c:1959
unix_cli_file_t * cli_file_pool
Vec pool of CLI sessions.
Definition: cli.c:386
int search_mode
Definition: cli.c:144
#define pool_elt_at_index(p, i)
Definition: pool.h:346
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:2290
static u8 unix_cli_terminal_type(u8 *term, uword len)
Identify whether a terminal type is ANSI capable.
Definition: cli.c:760
int cli_line_mode
Definition: unix.h:108
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:565
u32 file_descriptor
Definition: unix.h:51
uword private_data
Definition: unix.h:58
void vlib_unix_cli_set_prompt(char *prompt)
Set the CLI prompt.
Definition: cli.c:2189
static void unix_cli_pager_prompt_erase(unix_cli_file_t *cf, unix_file_t *uf)
Erase the printed pager prompt.
Definition: cli.c:644
always_inline uword * vlib_process_wait_for_event(vlib_main_t *vm)
Definition: node_funcs.h:484
static unix_cli_parse_actions_t unix_cli_parse_strings[]
Patterns to match on a CLI input stream.
Definition: cli.c:275
Erase cursor left.
Definition: cli.c:216
#define clib_error_return_unix(e, args...)
Definition: error.h:115
int cli_no_banner
Definition: unix.h:114
#define pool_put(P, E)
Definition: pool.h:200
void timer_call(timer_func_t *func, any arg, f64 dt)
Definition: timer.c:146
#define VLIB_CONFIG_FUNCTION(x, n,...)
Definition: init.h:116
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:2224
u8 * input
Input string to match.
Definition: cli.c:254
static unix_cli_banner_t unix_cli_banner[]
Plain welcome banner.
Definition: cli.c:102
Down arrow.
Definition: cli.c:219
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:538
word any
Definition: types.h:137
u32 cli_history_limit
Definition: unix.h:111
#define ANSI_BRED
ANSI Start bright red text.
Definition: cli.c:76
u8 * name
Definition: node.h:187
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:375
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:845
u8 ** pager_vector
Pager buffer.
Definition: cli.c:165
unix_error_history_t error_history[128]
Definition: unix.h:93
vlib_parse_match_t vlib_parse_eval(u8 *input)
Definition: parse.c:395
#define ANSI_SAVECURSOR
ANSI save cursor position.
Definition: cli.c:82
u32 runtime_index
Definition: node.h:206
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:535
u32 current_input_file_index
File pool index of current input.
Definition: cli.c:395
#define ANSI_CLEARLINE
ANSI clear line cursor is on.
Definition: cli.c:78
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:298
int cli_no_pager
Definition: unix.h:120
#define ANSI_BOLD
ANSI Start bold text.
Definition: cli.c:70
#define VLIB_MAIN_LOOP_EXIT_FUNCTION(x)
Definition: init.h:113
#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:376
#define clib_unix_warning(format, args...)
Definition: error.h:68
#define pool_is_free_index(P, I)
Definition: pool.h:197
#define ARRAY_LEN(x)
Definition: clib.h:59
void unformat_init_unix_file(unformat_input_t *input, int file_descriptor)
Definition: unformat.c:1026
#define ANSI_CLEAR
ANSI clear screen.
Definition: cli.c:66
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:778
static void unix_cli_resize_interrupt(int signum)
The system terminal has informed us that the window size has changed.
Definition: cli.c:2035
#define CTL(c)
Given a capital ASCII letter character return a NUL terminated string with the control code for that ...
Definition: cli.c:268
u8 no_pager
Disable the pager?
Definition: cli.c:162
int tio_isset
Definition: unix.h:124
static unix_cli_banner_t unix_cli_banner_color[]
ANSI color welcome banner.
Definition: cli.c:110
void vlib_start_process(vlib_main_t *vm, uword process_index)
Definition: main.c:1284
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:150
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:2203
always_inline void unix_file_del(unix_main_t *um, unix_file_t *f)
Definition: unix.h:141
unix_cli_parse_action_t
CLI actions.
Definition: cli.c:212
always_inline vlib_process_t * vlib_get_process_from_node(vlib_main_t *vm, vlib_node_t *node)
Definition: node_funcs.h:143
static void unix_cli_process_input(unix_cli_main_t *cm, uword cli_file_index)
Process input to a CLI session.
Definition: cli.c:1667
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:821
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:87
#define vec_delete(V, N, M)
Delete N elements starting at element M.
Definition: vec.h:743
u8 * format(u8 *s, char *fmt,...)
Definition: format.c:405
A CLI session wants to close.
Definition: cli.c:377
always_inline void clib_socket_free(clib_socket_t *s)
Definition: socket.h:117
static clib_error_t * unix_cli_exit(vlib_main_t *vm)
Called when VPP is shutting down, this resets the system terminal state, if previously saved...
Definition: cli.c:2171
#define vec_append(v1, v2)
Append v2 after v1.
Definition: vec.h:777
u8 has_history
Definition: cli.c:132
A CLI banner line.
Definition: cli.c:95
unix_main_t unix_main
Definition: main.c:57
#define ESC
ANSI escape code.
Definition: cli.c:60
u64 uword
Definition: types.h:112
u8 started
Has the session started?
Definition: cli.c:159
unsigned short u16
Definition: types.h:57
static clib_error_t * unix_cli_write_ready(unix_file_t *uf)
Definition: cli.c:1836
#define UNIX_FLAG_INTERACTIVE
Definition: unix.h:81
i64 word
Definition: types.h:111
clib_error_t * error
Definition: unix.h:66
CLI global state.
Definition: cli.c:381
Up arrow.
Definition: cli.c:218
#define UNIX_FILE_DATA_AVAILABLE_TO_WRITE
Definition: unix.h:54
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
double f64
Definition: types.h:140
unsigned char u8
Definition: types.h:56
#define UNIX_CLI_STDIN_FD
Unix standard in.
Definition: cli.c:91
u8 * search_key
Definition: cli.c:143
static void unix_cli_kill(unix_cli_main_t *cm, uword cli_file_index)
Definition: cli.c:1765
Definition: unix.h:49
Erase cursor right.
Definition: cli.c:217
u8 * format_time_interval(u8 *s, va_list *args)
Definition: std-formats.c:115
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:627
void(* file_update)(unix_file_t *file, unix_file_update_type_t update_type)
Definition: unix.h:90
A file descriptor has data to be read.
Definition: cli.c:376
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:964
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:2359
always_inline uword vlib_current_process(vlib_main_t *vm)
Definition: node_funcs.h:317
#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:153
always_inline f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:182
#define clib_error_return(e, args...)
Definition: error.h:112
clib_longjmp_t main_loop_exit
Definition: main.h:87
struct _unformat_input_t unformat_input_t
vlib_cli_output_function_t * output_function
Definition: node.h:501
u32 flags
Definition: vhost-user.h:73
i32 excursion
Definition: cli.c:135
unformat_function_t unformat_line_input
Definition: format.h:279
u8 line_mode
Line mode or char mode.
Definition: cli.c:150
u32 length
The length of the line without terminating NUL.
Definition: cli.c:97
void vlib_cli_input(vlib_main_t *vm, unformat_input_t *input, vlib_cli_output_function_t *function, uword function_arg)
Definition: cli.c:504
u32 stdin_cli_file_index
The session index of the stdin cli.
Definition: cli.c:392
always_inline void unix_cli_file_free(unix_cli_file_t *f)
Release storage used by a CLI session.
Definition: cli.c:204
u64 n_total_errors
Definition: unix.h:95
u32 height
Terminal height.
Definition: cli.c:177