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>
32 #include <arpa/telnet.h>
34 #include <vppinfra/mem.h>
35 #include <vppinfra/format.h>
36 #include <vppinfra/socket.h>
38 #define SOCKET_FILE "/run/vpp/cli.sock"
40 volatile int window_resized = 0;
41 struct termios orig_tio;
44 send_ttype (clib_socket_t * s, int is_interactive)
48 term = is_interactive ? getenv ("TERM") : "vppctl";
52 clib_socket_tx_add_formatted (s, "%c%c%c" "%c%s" "%c%c",
53 IAC, SB, TELOPT_TTYPE, 0, term, IAC, SE);
58 send_naws (clib_socket_t * s)
62 if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
64 clib_unix_warning ("ioctl(TIOCGWINSZ)");
68 clib_socket_tx_add_formatted (s, "%c%c%c" "%c%c%c%c" "%c%c",
70 ws.ws_col >> 8, ws.ws_col & 0xff,
71 ws.ws_row >> 8, ws.ws_row & 0xff, IAC, SE);
76 signal_handler_winch (int signum)
82 signal_handler_term (int signum)
84 tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
88 process_input (u8 * str, clib_socket_t * s, int is_interactive,
93 while (i < vec_len (s->rx_buffer))
95 if (s->rx_buffer[i] == IAC)
97 if (s->rx_buffer[i + 1] == SB)
100 char opt = s->rx_buffer[i + 2];
102 while (s->rx_buffer[i] != IAC)
103 vec_add1 (sb, s->rx_buffer[i++]);
106 clib_warning ("SB %s\n %U", TELOPT (opt),
107 format_hexdump, sb, vec_len (sb));
111 if (opt == TELOPT_TTYPE)
113 send_ttype (s, is_interactive);
116 else if (is_interactive && opt == TELOPT_NAWS)
122 clib_warning ("IAC at %d, IAC %s %s", i,
123 TELCMD (s->rx_buffer[i + 1]),
124 TELOPT (s->rx_buffer[i + 2]));
130 vec_add1 (str, s->rx_buffer[i++]);
132 vec_reset_length (s->rx_buffer);
138 main (int argc, char *argv[])
140 clib_socket_t _s = { 0 }, *s = &_s;
141 clib_error_t *error = 0;
142 struct epoll_event event;
149 int is_interactive = 0;
150 int acked = 1; /* counts messages from VPP; starts at 1 */
154 clib_mem_init (0, 64ULL << 10);
156 /* process command line */
160 if (argc > 1 && strncmp (argv[0], "-s", 2) == 0)
167 s->config = SOCKET_FILE;
170 cmd = format (cmd, "%s%c", (argv++)[0], argc ? ' ' : 0);
172 s->flags = CLIB_SOCKET_F_IS_CLIENT;
174 error = clib_socket_init (s);
178 is_interactive = isatty (STDIN_FILENO) && cmd == 0;
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)
187 error = clib_error_return_unix (0, "sigaction");
191 /* Capture SIGTERM to reset tty settings */
192 sa.sa_handler = signal_handler_term;
193 if (sigaction (SIGTERM, &sa, 0) < 0)
195 error = clib_error_return_unix (0, "sigaction");
199 /* Save the original tty state so we can restore it later */
200 if (tcgetattr (STDIN_FILENO, &orig_tio) < 0)
202 error = clib_error_return_unix (0, "tcgetattr");
206 /* Tweak the tty settings */
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 */
213 if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio) < 0)
215 error = clib_error_return_unix (0, "tcsetattr");
220 efd = epoll_create1 (0);
223 event.events = EPOLLIN | EPOLLPRI | EPOLLERR;
224 event.data.fd = STDIN_FILENO;
225 if (epoll_ctl (efd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0)
227 /* ignore EPERM; it means stdin is something like /dev/null */
230 error = clib_error_return_unix (0, "epoll_ctl[%d]", STDIN_FILENO);
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)
240 error = clib_error_return_unix (0, "epoll_ctl[%d]", s->fd);
254 if ((n = epoll_wait (efd, &event, 1, -1)) < 0)
256 /* maybe we received signal */
260 error = clib_error_return_unix (0, "epoll_wait");
267 if (event.data.fd == STDIN_FILENO)
273 continue; /* not ready for this yet */
275 n = read (STDIN_FILENO, c, sizeof (c));
278 memcpy (clib_socket_tx_add (s, n), c, n);
279 error = clib_socket_tx (s);
284 clib_warning ("read rv=%d", n);
288 else if (event.data.fd == s->fd)
290 error = clib_socket_rx (s, 100);
294 if (clib_socket_rx_end_of_file (s))
297 str = process_input (str, s, is_interactive, &sent_ttype);
299 if (vec_len (str) > 0)
301 int len = vec_len (str);
302 u8 *p = str, *q = str;
306 /* Search for and skip NUL bytes */
307 while (q < (p + len) && *q)
310 n = write (STDOUT_FILENO, p, q - p);
313 error = clib_error_return_unix (0, "write");
317 while (q < (p + len) && !*q)
320 acked++; /* every NUL is an acknowledgement */
326 vec_reset_length (str);
329 if (do_quit && do_quit < acked)
331 /* Ask the other end to close the connection */
332 clib_socket_tx_add_formatted (s, "quit\n");
336 if (cmd && sent_ttype)
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.
343 clib_socket_tx_add_formatted (s, "%s\n", cmd);
346 do_quit = acked; /* quit after the next response */
351 error = clib_error_return (0, "unknown fd");
356 error = clib_socket_close (s);
365 tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
369 clib_error_report (error);
379 * fd.io coding-style-patch-verification: ON
382 * eval: (c-set-style "gnu")