vpp: set asan default options
[vpp.git] / src / vpp / app / vppctl.c
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 #include <errno.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27
28 #define DEBUG 0
29
30 #if DEBUG
31 #define TELCMDS
32 #define TELOPTS
33 #endif
34
35 #include <vppinfra/clib.h>
36 #include <arpa/telnet.h>
37 #include <vpp/vnet/config.h>
38
39 #define SOCKET_FILE "/run/vpp/cli.sock"
40
41 volatile int window_resized = 0;
42 struct termios orig_tio;
43
44 static void
45 send_ttype (int sock_fd, int is_interactive)
46 {
47   char *term;
48   static char buf[2048];
49
50   /* wipe the buffer so there is no potential
51    * for inter-invocation leakage */
52   memset (buf, 0, sizeof (buf));
53
54   term = is_interactive ? getenv ("TERM") : "vppctl";
55   if (term == NULL)
56     term = "dumb";
57
58   int len = snprintf (buf, sizeof (buf),
59                       "%c%c%c"
60                       "%c%s"
61                       "%c%c",
62                       IAC, SB, TELOPT_TTYPE, 0, term, IAC, SE);
63   if (send (sock_fd, buf, len, 0) < 0)
64     {
65       perror ("send_ttype");
66     }
67 }
68
69 static void
70 send_naws (int sock_fd)
71 {
72   struct winsize ws;
73   static char buf[2048];
74
75   memset (buf, 0, sizeof (buf));
76   if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
77     {
78       fprintf (stderr, "ioctl(TIOCGWINSZ)");
79       return;
80     }
81
82   int len = snprintf (buf, sizeof (buf),
83                       "%c%c%c"
84                       "%c%c%c%c"
85                       "%c%c",
86                       IAC, SB, TELOPT_NAWS, ws.ws_col >> 8, ws.ws_col & 0xff,
87                       ws.ws_row >> 8, ws.ws_row & 0xff, IAC, SE);
88   int n_written = write (sock_fd, buf, len);
89   if (n_written < len)
90     {
91       perror ("send_naws");
92     }
93 }
94
95 static void
96 signal_handler_winch (int signum)
97 {
98   window_resized = 1;
99 }
100
101 static void
102 signal_handler_term (int signum)
103 {
104   tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
105 }
106
107 static int
108 process_input (int sock_fd, unsigned char *rx_buf, int rx_buf_len,
109                int is_interactive, int *sent_ttype)
110 {
111   int i = 0;
112   int j = 0;
113
114   while (i < rx_buf_len)
115     {
116       if (rx_buf[i] == IAC)
117         {
118           if (rx_buf[i + 1] == SB)
119             {
120               char opt = rx_buf[i + 2];
121               i += 3;
122 #if DEBUG
123               if (rx_buf[i] != IAC)
124                 {
125                   fprintf (stderr, "SB ");
126                 }
127               while (rx_buf[i] != IAC && i < rx_buf_len)
128                 fprintf (stderr, "%02x ", rx_buf[i++]);
129               fprintf (stderr, "\n");
130 #else
131               while (rx_buf[i] != IAC && i < rx_buf_len)
132                 {
133                   i++;
134                 }
135 #endif
136               i += 2;
137               if (opt == TELOPT_TTYPE)
138                 {
139                   send_ttype (sock_fd, is_interactive);
140                   *sent_ttype = 1;
141                 }
142               else if (is_interactive && opt == TELOPT_NAWS)
143                 send_naws (sock_fd);
144             }
145           else
146             {
147 #if DEBUG
148               fprintf (stderr, "IAC at %d, IAC %s %s", i,
149                        TELCMD (rx_buf[i + 1]), TELOPT (rx_buf[i + 2]));
150 #endif
151               i += 3;
152             }
153         }
154       else
155         {
156           /* i is always the same or ahead of j, so at worst this is a no-op */
157           rx_buf[j] = rx_buf[i];
158           i++;
159           j++;
160         }
161     }
162   return j;
163 }
164
165 #ifdef CLIB_SANITIZE_ADDR
166 /* default options for Address Sanitizer */
167 const char *
168 __asan_default_options (void)
169 {
170   return VPP_SANITIZE_ADDR_OPTIONS;
171 }
172 #endif /* CLIB_SANITIZE_ADDR */
173
174 int
175 main (int argc, char *argv[])
176 {
177   struct epoll_event event;
178   struct sigaction sa;
179   struct termios tio;
180   int efd = -1;
181   char *cmd = 0;
182   unsigned long cmd_len = 0;
183   int do_quit = 0;
184   int is_interactive = 0;
185   int acked = 1;                /* counts messages from VPP; starts at 1 */
186   int sent_ttype = 0;
187   char *sock_fname = SOCKET_FILE;
188   int sock_fd = -1;
189   int error = 0;
190   int arg = 0;
191
192   /* process command line */
193   argc--;
194   argv++;
195
196   if (argc > 1 && strncmp (argv[0], "-s", 2) == 0)
197     {
198       sock_fname = argv[1];
199       argc -= 2;
200       argv += 2;
201     }
202
203   struct sockaddr_un saddr = { 0 };
204   saddr.sun_family = AF_UNIX;
205
206   if (strlen (sock_fname) > sizeof (saddr.sun_path) - 1)
207     {
208       perror ("socket path too long");
209       exit (1);
210     }
211
212   strncpy (saddr.sun_path, sock_fname, sizeof (saddr.sun_path) - 1);
213
214   sock_fd = socket (AF_UNIX, SOCK_STREAM, 0);
215   if (sock_fd < 0)
216     {
217       perror ("socket");
218       exit (1);
219     }
220
221   if (connect (sock_fd, (struct sockaddr *) &saddr, sizeof (saddr)) < 0)
222     {
223       perror ("connect");
224       exit (1);
225     }
226
227   for (arg = 0; arg < argc; arg++)
228     {
229       cmd_len += strlen (argv[arg]) + 1;
230     }
231   if (cmd_len > 0)
232     {
233       cmd_len++; // account for 0 at end
234       cmd = malloc (cmd_len);
235       if (!cmd)
236         {
237           error = errno;
238           perror ("malloc failed");
239           goto done;
240         }
241       memset (cmd, 0, cmd_len);
242       unsigned long space_left = cmd_len - 1; // reserve space for 0 at end
243       while (argc--)
244         {
245           strncat (cmd, *argv, space_left);
246           space_left -= strlen (*argv);
247           ++argv;
248           strncat (cmd, " ", space_left);
249           --space_left;
250         }
251       cmd[cmd_len - 2] = '\n';
252       cmd[cmd_len - 1] = 0;
253     }
254
255   is_interactive = isatty (STDIN_FILENO) && cmd == 0;
256
257   if (is_interactive)
258     {
259       /* Capture terminal resize events */
260       memset (&sa, 0, sizeof (struct sigaction));
261       sa.sa_handler = signal_handler_winch;
262       if (sigaction (SIGWINCH, &sa, 0) < 0)
263         {
264           error = errno;
265           perror ("sigaction for SIGWINCH");
266           goto done;
267         }
268
269       /* Capture SIGTERM to reset tty settings */
270       sa.sa_handler = signal_handler_term;
271       if (sigaction (SIGTERM, &sa, 0) < 0)
272         {
273           error = errno;
274           perror ("sigaction for SIGTERM");
275           goto done;
276         }
277
278       /* Save the original tty state so we can restore it later */
279       if (tcgetattr (STDIN_FILENO, &orig_tio) < 0)
280         {
281           error = errno;
282           perror ("tcgetattr");
283           goto done;
284         }
285
286       /* Tweak the tty settings */
287       tio = orig_tio;
288       /* echo off, canonical mode off, ext'd input processing off */
289       tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
290       tio.c_cc[VMIN] = 1;       /* 1 byte at a time */
291       tio.c_cc[VTIME] = 0;      /* no timer */
292
293       if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio) < 0)
294         {
295           error = errno;
296           perror ("tcsetattr");
297           goto done;
298         }
299     }
300
301   efd = epoll_create1 (0);
302
303   /* register STDIN */
304   if (cmd == 0)
305     {
306       event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
307       event.data.fd = STDIN_FILENO;
308       if (epoll_ctl (efd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0)
309         {
310           /* ignore EPERM; it means stdin is something like /dev/null */
311           if (errno != EPERM)
312             {
313               error = errno;
314               fprintf (stderr, "epoll_ctl[%d]", STDIN_FILENO);
315               perror (0);
316               goto done;
317             }
318         }
319     }
320
321   /* register socket */
322   event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
323   event.data.fd = sock_fd;
324   if (epoll_ctl (efd, EPOLL_CTL_ADD, sock_fd, &event) != 0)
325     {
326       error = errno;
327       fprintf (stderr, "epoll_ctl[%d]", sock_fd);
328       perror (0);
329       goto done;
330     }
331
332   while (1)
333     {
334       int n;
335       static int sent_cmd = 0;
336
337       if (window_resized)
338         {
339           window_resized = 0;
340           send_naws (sock_fd);
341         }
342
343       if ((n = epoll_wait (efd, &event, 1, -1)) < 0)
344         {
345           /* maybe we received signal */
346           if (errno == EINTR)
347             continue;
348
349           error = errno;
350           perror ("epoll_wait");
351           goto done;
352         }
353
354       if (n == 0)
355         continue;
356
357       if (event.data.fd == STDIN_FILENO && cmd == 0)
358         {
359           int n;
360           char c[100];
361
362           if (!sent_ttype)
363             continue;           /* not ready for this yet */
364
365           n = read (STDIN_FILENO, c, sizeof (c));
366           if (n > 0)
367             {
368               int n_written = write (sock_fd, c, n);
369               if (n_written < n)
370                 error = errno;
371               if (error)
372                 goto done;
373             }
374           else if (n < 0)
375             fprintf (stderr, "read rv=%d", n);
376           else /* EOF */
377             do_quit = 1;
378         }
379       else if (event.data.fd == sock_fd)
380         {
381           unsigned char rx_buf[100];
382           memset (rx_buf, 0, sizeof (rx_buf));
383           int nread = recv (sock_fd, rx_buf, sizeof (rx_buf), 0);
384
385           if (nread < 0)
386             error = errno;
387           if (error)
388             break;
389
390           if (nread == 0)
391             break;
392
393           int len = process_input (sock_fd, rx_buf, nread, is_interactive,
394                                    &sent_ttype);
395
396           if (len > 0)
397             {
398               unsigned char *p = rx_buf, *q = rx_buf;
399
400               while (len)
401                 {
402                   /* Search for and skip NUL bytes */
403                   while (q < (p + len) && *q)
404                     q++;
405
406                   n = write (STDOUT_FILENO, p, q - p);
407                   if (n < 0)
408                     {
409                       error = errno;
410                       perror ("write");
411                       goto done;
412                     }
413
414                   while (q < (p + len) && !*q)
415                     {
416                       q++;
417                       acked++;  /* every NUL is an acknowledgement */
418                     }
419                   len -= q - p;
420                   p = q;
421                 }
422             }
423
424           if (do_quit && do_quit < acked)
425             {
426               /* Ask the other end to close the connection */
427               char quit_str[] = "quit\n";
428               int n = write (sock_fd, quit_str, strlen (quit_str));
429               if (n < strlen (quit_str))
430                 {
431                   error = errno;
432                   perror ("write quit");
433                 }
434               do_quit = 0;
435             }
436           if (cmd && sent_ttype && !sent_cmd)
437             {
438               /* We wait until after the TELNET TTYPE option has been sent.
439                * That is to make sure the session at the VPP end has switched
440                * to line-by-line mode, and thus avoid prompts and echoing.
441                * Note that it does also disable further TELNET option processing.
442                */
443               int n_written = write (sock_fd, cmd, strlen (cmd) + 1);
444               sent_cmd = 1;
445               if (n_written < strlen (cmd))
446                 {
447                   error = errno;
448                   perror ("write command");
449                   goto done;
450                 }
451               do_quit = acked;  /* quit after the next response */
452             }
453         }
454       else
455         {
456           error = errno;
457           perror ("unknown fd");
458           goto done;
459         }
460     }
461
462   close (sock_fd);
463
464 done:
465   free (cmd);
466   if (efd > -1)
467     close (efd);
468
469   if (is_interactive)
470     tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
471
472   if (error)
473     {
474       return 1;
475     }
476
477   return 0;
478 }
479
480 /* *INDENT-ON* */
481
482 /*
483  * fd.io coding-style-patch-verification: ON
484  *
485  * Local Variables:
486  * eval: (c-set-style "gnu")
487  * End:
488  */