FD.io VPP  v20.01-48-g3e0dafb74
Vector Packet Processing
vppctl.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 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 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <sys/epoll.h>
19 #include <sys/ioctl.h>
20 #include <signal.h>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <string.h>
24 
25 #define DEBUG 0
26 
27 #if DEBUG
28 #define TELCMDS
29 #define TELOPTS
30 #endif
31 
32 #include <arpa/telnet.h>
33 
34 #include <vppinfra/mem.h>
35 #include <vppinfra/format.h>
36 #include <vppinfra/socket.h>
37 
38 #define SOCKET_FILE "/run/vpp/cli.sock"
39 
40 volatile int window_resized = 0;
41 struct termios orig_tio;
42 
43 static void
44 send_ttype (clib_socket_t * s, int is_interactive)
45 {
46  char *term;
47 
48  term = is_interactive ? getenv ("TERM") : "vppctl";
49  if (term == NULL)
50  term = "dumb";
51 
52  clib_socket_tx_add_formatted (s, "%c%c%c" "%c%s" "%c%c",
53  IAC, SB, TELOPT_TTYPE, 0, term, IAC, SE);
54  clib_socket_tx (s);
55 }
56 
57 static void
59 {
60  struct winsize ws;
61 
62  if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
63  {
64  clib_unix_warning ("ioctl(TIOCGWINSZ)");
65  return;
66  }
67 
68  clib_socket_tx_add_formatted (s, "%c%c%c" "%c%c%c%c" "%c%c",
69  IAC, SB, TELOPT_NAWS,
70  ws.ws_col >> 8, ws.ws_col & 0xff,
71  ws.ws_row >> 8, ws.ws_row & 0xff, IAC, SE);
72  clib_socket_tx (s);
73 }
74 
75 static void
77 {
78  window_resized = 1;
79 }
80 
81 static void
82 signal_handler_term (int signum)
83 {
84  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
85 }
86 
87 static u8 *
88 process_input (u8 * str, clib_socket_t * s, int is_interactive,
89  int *sent_ttype)
90 {
91  int i = 0;
92 
93  while (i < vec_len (s->rx_buffer))
94  {
95  if (s->rx_buffer[i] == IAC)
96  {
97  if (s->rx_buffer[i + 1] == SB)
98  {
99  u8 *sb = 0;
100  char opt = s->rx_buffer[i + 2];
101  i += 3;
102  while (s->rx_buffer[i] != IAC)
103  vec_add1 (sb, s->rx_buffer[i++]);
104 
105 #if DEBUG
106  clib_warning ("SB %s\n %U", TELOPT (opt),
107  format_hexdump, sb, vec_len (sb));
108 #endif
109  vec_free (sb);
110  i += 2;
111  if (opt == TELOPT_TTYPE)
112  {
113  send_ttype (s, is_interactive);
114  *sent_ttype = 1;
115  }
116  else if (is_interactive && opt == TELOPT_NAWS)
117  send_naws (s);
118  }
119  else
120  {
121 #if DEBUG
122  clib_warning ("IAC at %d, IAC %s %s", i,
123  TELCMD (s->rx_buffer[i + 1]),
124  TELOPT (s->rx_buffer[i + 2]));
125 #endif
126  i += 3;
127  }
128  }
129  else
130  vec_add1 (str, s->rx_buffer[i++]);
131  }
132  vec_reset_length (s->rx_buffer);
133  return str;
134 }
135 
136 
137 int
138 main (int argc, char *argv[])
139 {
140  clib_socket_t _s = { 0 }, *s = &_s;
141  clib_error_t *error = 0;
142  struct epoll_event event;
143  struct sigaction sa;
144  struct termios tio;
145  int efd = -1;
146  u8 *str = 0;
147  u8 *cmd = 0;
148  int do_quit = 0;
149  int is_interactive = 0;
150  int acked = 1; /* counts messages from VPP; starts at 1 */
151  int sent_ttype = 0;
152 
153 
154  clib_mem_init (0, 64ULL << 10);
155 
156  /* process command line */
157  argc--;
158  argv++;
159 
160  if (argc > 1 && strncmp (argv[0], "-s", 2) == 0)
161  {
162  s->config = argv[1];
163  argc -= 2;
164  argv += 2;
165  }
166  else
167  s->config = SOCKET_FILE;
168 
169  while (argc--)
170  cmd = format (cmd, "%s%c", (argv++)[0], argc ? ' ' : 0);
171 
172  s->flags = CLIB_SOCKET_F_IS_CLIENT;
173 
174  error = clib_socket_init (s);
175  if (error)
176  goto done;
177 
178  is_interactive = isatty (STDIN_FILENO) && cmd == 0;
179 
180  if (is_interactive)
181  {
182  /* Capture terminal resize events */
183  clib_memset (&sa, 0, sizeof (struct sigaction));
184  sa.sa_handler = signal_handler_winch;
185  if (sigaction (SIGWINCH, &sa, 0) < 0)
186  {
187  error = clib_error_return_unix (0, "sigaction");
188  goto done;
189  }
190 
191  /* Capture SIGTERM to reset tty settings */
192  sa.sa_handler = signal_handler_term;
193  if (sigaction (SIGTERM, &sa, 0) < 0)
194  {
195  error = clib_error_return_unix (0, "sigaction");
196  goto done;
197  }
198 
199  /* Save the original tty state so we can restore it later */
200  if (tcgetattr (STDIN_FILENO, &orig_tio) < 0)
201  {
202  error = clib_error_return_unix (0, "tcgetattr");
203  goto done;
204  }
205 
206  /* Tweak the tty settings */
207  tio = orig_tio;
208  /* echo off, canonical mode off, ext'd input processing off */
209  tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
210  tio.c_cc[VMIN] = 1; /* 1 byte at a time */
211  tio.c_cc[VTIME] = 0; /* no timer */
212 
213  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio) < 0)
214  {
215  error = clib_error_return_unix (0, "tcsetattr");
216  goto done;
217  }
218  }
219 
220  efd = epoll_create1 (0);
221 
222  /* register STDIN */
223  event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
224  event.data.fd = STDIN_FILENO;
225  if (epoll_ctl (efd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0)
226  {
227  /* ignore EPERM; it means stdin is something like /dev/null */
228  if (errno != EPERM)
229  {
230  error = clib_error_return_unix (0, "epoll_ctl[%d]", STDIN_FILENO);
231  goto done;
232  }
233  }
234 
235  /* register socket */
236  event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
237  event.data.fd = s->fd;
238  if (epoll_ctl (efd, EPOLL_CTL_ADD, s->fd, &event) != 0)
239  {
240  error = clib_error_return_unix (0, "epoll_ctl[%d]", s->fd);
241  goto done;
242  }
243 
244  while (1)
245  {
246  int n;
247 
248  if (window_resized)
249  {
250  window_resized = 0;
251  send_naws (s);
252  }
253 
254  if ((n = epoll_wait (efd, &event, 1, -1)) < 0)
255  {
256  /* maybe we received signal */
257  if (errno == EINTR)
258  continue;
259 
260  error = clib_error_return_unix (0, "epoll_wait");
261  goto done;
262  }
263 
264  if (n == 0)
265  continue;
266 
267  if (event.data.fd == STDIN_FILENO)
268  {
269  int n;
270  char c[100];
271 
272  if (!sent_ttype)
273  continue; /* not ready for this yet */
274 
275  n = read (STDIN_FILENO, c, sizeof (c));
276  if (n > 0)
277  {
278  memcpy (clib_socket_tx_add (s, n), c, n);
279  error = clib_socket_tx (s);
280  if (error)
281  goto done;
282  }
283  else if (n < 0)
284  clib_warning ("read rv=%d", n);
285  else /* EOF */
286  do_quit = 1;
287  }
288  else if (event.data.fd == s->fd)
289  {
290  error = clib_socket_rx (s, 100);
291  if (error)
292  break;
293 
295  break;
296 
297  str = process_input (str, s, is_interactive, &sent_ttype);
298 
299  if (vec_len (str) > 0)
300  {
301  int len = vec_len (str);
302  u8 *p = str, *q = str;
303 
304  while (len)
305  {
306  /* Search for and skip NUL bytes */
307  while (q < (p + len) && *q)
308  q++;
309 
310  n = write (STDOUT_FILENO, p, q - p);
311  if (n < 0)
312  {
313  error = clib_error_return_unix (0, "write");
314  goto done;
315  }
316 
317  while (q < (p + len) && !*q)
318  {
319  q++;
320  acked++; /* every NUL is an acknowledgement */
321  }
322  len -= q - p;
323  p = q;
324  }
325 
326  vec_reset_length (str);
327  }
328 
329  if (do_quit && do_quit < acked)
330  {
331  /* Ask the other end to close the connection */
332  clib_socket_tx_add_formatted (s, "quit\n");
333  clib_socket_tx (s);
334  do_quit = 0;
335  }
336  if (cmd && sent_ttype)
337  {
338  /* We wait until after the TELNET TTYPE option has been sent.
339  * That is to make sure the session at the VPP end has switched
340  * to line-by-line mode, and thus avoid prompts and echoing.
341  * Note that it does also disable further TELNET option processing.
342  */
343  clib_socket_tx_add_formatted (s, "%s\n", cmd);
344  clib_socket_tx (s);
345  vec_free (cmd);
346  do_quit = acked; /* quit after the next response */
347  }
348  }
349  else
350  {
351  error = clib_error_return (0, "unknown fd");
352  goto done;
353  }
354  }
355 
356  error = clib_socket_close (s);
357 
358 done:
359  vec_free (cmd);
360  vec_free (str);
361  if (efd > -1)
362  close (efd);
363 
364  if (is_interactive)
365  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
366 
367  if (error)
368  {
369  clib_error_report (error);
370  return 1;
371  }
372 
373  return 0;
374 }
375 
376 /* *INDENT-ON* */
377 
378 /*
379  * fd.io coding-style-patch-verification: ON
380  *
381  * Local Variables:
382  * eval: (c-set-style "gnu")
383  * End:
384  */
Optimized string handling code, including c11-compliant "safe C library" variants.
static void send_naws(clib_socket_t *s)
Definition: vppctl.c:58
#define NULL
Definition: clib.h:58
clib_memset(h->entries, 0, sizeof(h->entries[0]) *entries)
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:523
#define SOCKET_FILE
Definition: vppctl.c:38
int i
clib_error_t * clib_socket_init(clib_socket_t *s)
Definition: socket.c:384
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
static void signal_handler_term(int signum)
Definition: vppctl.c:82
unsigned char u8
Definition: types.h:56
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
int main(int argc, char *argv[])
Definition: vppctl.c:138
struct termios orig_tio
Definition: vppctl.c:41
static u8 * process_input(u8 *str, clib_socket_t *s, int is_interactive, int *sent_ttype)
Definition: vppctl.c:88
#define clib_error_return(e, args...)
Definition: error.h:99
static clib_error_t * clib_socket_close(clib_socket_t *sock)
Definition: socket.h:175
#define clib_error_return_unix(e, args...)
Definition: error.h:102
u8 len
Definition: ip_types.api:91
void * clib_mem_init(void *heap, uword size)
Definition: mem_dlmalloc.c:206
svmdb_client_t * c
volatile int window_resized
Definition: vppctl.c:40
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:342
#define clib_warning(format, args...)
Definition: error.h:59
u8 * format_hexdump(u8 *s, va_list *va)
Definition: std-formats.c:297
static void send_ttype(clib_socket_t *s, int is_interactive)
Definition: vppctl.c:44
#define CLIB_SOCKET_F_IS_CLIENT
Definition: socket.h:59
#define clib_error_report(e)
Definition: error.h:113
struct _socket_t clib_socket_t
static clib_error_t * clib_socket_rx(clib_socket_t *s, int n_bytes)
Definition: socket.h:145
static int clib_socket_rx_end_of_file(clib_socket_t *s)
Definition: socket.h:119
static void * clib_socket_tx_add(clib_socket_t *s, int n_bytes)
Definition: socket.h:125
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
#define clib_unix_warning(format, args...)
Definition: error.h:68
static clib_error_t * clib_socket_tx(clib_socket_t *s)
Definition: socket.h:139
static void signal_handler_winch(int signum)
Definition: vppctl.c:76
void clib_socket_tx_add_formatted(clib_socket_t *s, char *fmt,...)
Definition: socket.c:58