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