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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <sys/socket.h>
18 #include <sys/epoll.h>
19 #include <sys/ioctl.h>
35 #include <vppinfra/clib.h>
36 #include <arpa/telnet.h>
38 #include <vpp/vnet/config.h>
41 #define SOCKET_FILE "/run/vpp/cli.sock"
43 volatile int window_resized = 0;
44 struct termios orig_tio;
47 send_ttype (int sock_fd, int is_interactive)
50 static char buf[2048];
52 /* wipe the buffer so there is no potential
53 * for inter-invocation leakage */
54 memset (buf, 0, sizeof (buf));
56 term = is_interactive ? getenv ("TERM") : "vppctl";
60 int len = snprintf (buf, sizeof (buf),
64 IAC, SB, TELOPT_TTYPE, 0, term, IAC, SE);
65 if (send (sock_fd, buf, len, 0) < 0)
67 perror ("send_ttype");
72 send_naws (int sock_fd)
75 static char buf[2048];
77 memset (buf, 0, sizeof (buf));
78 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
80 fprintf (stderr, "ioctl(TIOCGWINSZ)");
84 int len = snprintf (buf, sizeof (buf),
88 IAC, SB, TELOPT_NAWS, ws.ws_col >> 8, ws.ws_col & 0xff,
89 ws.ws_row >> 8, ws.ws_row & 0xff, IAC, SE);
90 int n_written = write (sock_fd, buf, len);
98 signal_handler_winch (int signum)
104 signal_handler_term (int signum)
106 tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
110 process_input (int sock_fd, unsigned char *rx_buf, int rx_buf_len,
111 int is_interactive, int *sent_ttype)
116 while (i < rx_buf_len)
118 if (rx_buf[i] == IAC)
120 if (rx_buf[i + 1] == SB)
122 char opt = rx_buf[i + 2];
125 if (rx_buf[i] != IAC)
127 fprintf (stderr, "SB ");
129 while (rx_buf[i] != IAC && i < rx_buf_len)
130 fprintf (stderr, "%02x ", rx_buf[i++]);
131 fprintf (stderr, "\n");
133 while (rx_buf[i] != IAC && i < rx_buf_len)
139 if (opt == TELOPT_TTYPE)
141 send_ttype (sock_fd, is_interactive);
144 else if (is_interactive && opt == TELOPT_NAWS)
150 fprintf (stderr, "IAC at %d, IAC %s %s", i,
151 TELCMD (rx_buf[i + 1]), TELOPT (rx_buf[i + 2]));
158 /* i is always the same or ahead of j, so at worst this is a no-op */
159 rx_buf[j] = rx_buf[i];
167 #if !defined(STATIC_VPPCTL) && defined(CLIB_SANITIZE_ADDR)
168 /* default options for Address Sanitizer */
170 __asan_default_options (void)
172 return VPP_SANITIZE_ADDR_OPTIONS;
174 #endif /* CLIB_SANITIZE_ADDR */
177 main (int argc, char *argv[])
179 struct epoll_event event;
184 unsigned long cmd_len = 0;
186 int is_interactive = 0;
187 int acked = 1; /* counts messages from VPP; starts at 1 */
189 char *sock_fname = SOCKET_FILE;
194 /* process command line */
198 if (argc > 1 && strncmp (argv[0], "-s", 2) == 0)
200 sock_fname = argv[1];
205 struct sockaddr_un saddr = { 0 };
206 saddr.sun_family = AF_UNIX;
208 if (strlen (sock_fname) > sizeof (saddr.sun_path) - 1)
210 perror ("socket path too long");
214 strncpy (saddr.sun_path, sock_fname, sizeof (saddr.sun_path) - 1);
216 sock_fd = socket (AF_UNIX, SOCK_STREAM, 0);
223 if (connect (sock_fd, (struct sockaddr *) &saddr, sizeof (saddr)) < 0)
229 for (arg = 0; arg < argc; arg++)
231 cmd_len += strlen (argv[arg]) + 1;
235 cmd_len++; // account for 0 at end
236 cmd = malloc (cmd_len);
240 perror ("malloc failed");
243 memset (cmd, 0, cmd_len);
244 unsigned long space_left = cmd_len - 1; // reserve space for 0 at end
247 strncat (cmd, *argv, space_left);
248 space_left -= strlen (*argv);
250 strncat (cmd, " ", space_left);
253 cmd[cmd_len - 2] = '\n';
254 cmd[cmd_len - 1] = 0;
257 is_interactive = isatty (STDIN_FILENO) && cmd == 0;
261 /* Capture terminal resize events */
262 memset (&sa, 0, sizeof (struct sigaction));
263 sa.sa_handler = signal_handler_winch;
264 if (sigaction (SIGWINCH, &sa, 0) < 0)
267 perror ("sigaction for SIGWINCH");
271 /* Capture SIGTERM to reset tty settings */
272 sa.sa_handler = signal_handler_term;
273 if (sigaction (SIGTERM, &sa, 0) < 0)
276 perror ("sigaction for SIGTERM");
280 /* Save the original tty state so we can restore it later */
281 if (tcgetattr (STDIN_FILENO, &orig_tio) < 0)
284 perror ("tcgetattr");
288 /* Tweak the tty settings */
290 /* echo off, canonical mode off, ext'd input processing off */
291 tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
292 tio.c_cc[VMIN] = 1; /* 1 byte at a time */
293 tio.c_cc[VTIME] = 0; /* no timer */
295 if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio) < 0)
298 perror ("tcsetattr");
303 efd = epoll_create1 (0);
308 event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
309 event.data.fd = STDIN_FILENO;
310 if (epoll_ctl (efd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0)
312 /* ignore EPERM; it means stdin is something like /dev/null */
316 fprintf (stderr, "epoll_ctl[%d]", STDIN_FILENO);
323 /* register socket */
324 event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
325 event.data.fd = sock_fd;
326 if (epoll_ctl (efd, EPOLL_CTL_ADD, sock_fd, &event) != 0)
329 fprintf (stderr, "epoll_ctl[%d]", sock_fd);
337 static int sent_cmd = 0;
345 if ((n = epoll_wait (efd, &event, 1, -1)) < 0)
347 /* maybe we received signal */
352 perror ("epoll_wait");
359 if (event.data.fd == STDIN_FILENO && cmd == 0)
365 continue; /* not ready for this yet */
367 n = read (STDIN_FILENO, c, sizeof (c));
370 int n_written = write (sock_fd, c, n);
377 fprintf (stderr, "read rv=%d", n);
381 else if (event.data.fd == sock_fd)
383 unsigned char rx_buf[100];
384 memset (rx_buf, 0, sizeof (rx_buf));
385 int nread = recv (sock_fd, rx_buf, sizeof (rx_buf), 0);
395 int len = process_input (sock_fd, rx_buf, nread, is_interactive,
400 unsigned char *p = rx_buf, *q = rx_buf;
404 /* Search for and skip NUL bytes */
405 while (q < (p + len) && *q)
408 n = write (STDOUT_FILENO, p, q - p);
416 while (q < (p + len) && !*q)
419 acked++; /* every NUL is an acknowledgement */
426 if (do_quit && do_quit < acked)
428 /* Ask the other end to close the connection */
429 char quit_str[] = "quit\n";
430 int n = write (sock_fd, quit_str, strlen (quit_str));
431 if (n < strlen (quit_str))
434 perror ("write quit");
438 if (cmd && sent_ttype && !sent_cmd)
440 /* We wait until after the TELNET TTYPE option has been sent.
441 * That is to make sure the session at the VPP end has switched
442 * to line-by-line mode, and thus avoid prompts and echoing.
443 * Note that it does also disable further TELNET option processing.
445 int n_written = write (sock_fd, cmd, strlen (cmd) + 1);
447 if (n_written < strlen (cmd))
450 perror ("write command");
453 do_quit = acked; /* quit after the next response */
459 perror ("unknown fd");
472 tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
484 * fd.io coding-style-patch-verification: ON
487 * eval: (c-set-style "gnu")