Fixes for 'make UNATTENDED=yes CC=clang CXX=clang verify'
[vpp.git] / src / vpp / app / vppctl.c
index a8f3eab..b7582c0 100644 (file)
@@ -41,12 +41,16 @@ volatile int window_resized = 0;
 struct termios orig_tio;
 
 static void
-send_ttype (clib_socket_t * s, int is_dumb)
+send_ttype (clib_socket_t * s, int is_interactive)
 {
+  char *term;
+
+  term = is_interactive ? getenv ("TERM") : "vppctl";
+  if (term == NULL)
+    term = "dumb";
+
   clib_socket_tx_add_formatted (s, "%c%c%c" "%c%s" "%c%c",
-                               IAC, SB, TELOPT_TTYPE,
-                               0, is_dumb ? "dumb" : getenv ("TERM"),
-                               IAC, SE);
+                               IAC, SB, TELOPT_TTYPE, 0, term, IAC, SE);
   clib_socket_tx (s);
 }
 
@@ -81,7 +85,8 @@ signal_handler_term (int signum)
 }
 
 static u8 *
-process_input (u8 * str, clib_socket_t * s, int is_interactive)
+process_input (u8 * str, clib_socket_t * s, int is_interactive,
+              int *sent_ttype)
 {
   int i = 0;
 
@@ -104,7 +109,10 @@ process_input (u8 * str, clib_socket_t * s, int is_interactive)
              vec_free (sb);
              i += 2;
              if (opt == TELOPT_TTYPE)
-               send_ttype (s, !is_interactive);
+               {
+                 send_ttype (s, is_interactive);
+                 *sent_ttype = 1;
+               }
              else if (is_interactive && opt == TELOPT_NAWS)
                send_naws (s);
            }
@@ -138,6 +146,9 @@ main (int argc, char *argv[])
   u8 *str = 0;
   u8 *cmd = 0;
   int do_quit = 0;
+  int is_interactive = 0;
+  int acked = 1;               /* counts messages from VPP; starts at 1 */
+  int sent_ttype = 0;
 
 
   clib_mem_init (0, 64ULL << 10);
@@ -146,7 +157,7 @@ main (int argc, char *argv[])
   argc--;
   argv++;
 
-  if (argc > 1 && strcmp (argv[0], "-s") == 0)
+  if (argc > 1 && strncmp (argv[0], "-s", 2) == 0)
     {
       s->config = argv[1];
       argc -= 2;
@@ -158,39 +169,53 @@ main (int argc, char *argv[])
   while (argc--)
     cmd = format (cmd, "%s%c", (argv++)[0], argc ? ' ' : 0);
 
-  s->flags = SOCKET_IS_CLIENT;
+  s->flags = CLIB_SOCKET_F_IS_CLIENT;
 
   error = clib_socket_init (s);
   if (error)
     goto done;
 
-  /* Capture terminal resize events */
-  memset (&sa, 0, sizeof (struct sigaction));
-  sa.sa_handler = signal_handler_winch;
+  is_interactive = isatty (STDIN_FILENO) && cmd == 0;
 
-  if (sigaction (SIGWINCH, &sa, 0) < 0)
+  if (is_interactive)
     {
-      error = clib_error_return_unix (0, "sigaction");
-      goto done;
-    }
+      /* Capture terminal resize events */
+      memset (&sa, 0, sizeof (struct sigaction));
+      sa.sa_handler = signal_handler_winch;
+      if (sigaction (SIGWINCH, &sa, 0) < 0)
+       {
+         error = clib_error_return_unix (0, "sigaction");
+         goto done;
+       }
 
-  sa.sa_handler = signal_handler_term;
-  if (sigaction (SIGTERM, &sa, 0) < 0)
-    {
-      error = clib_error_return_unix (0, "sigaction");
-      goto done;
-    }
+      /* Capture SIGTERM to reset tty settings */
+      sa.sa_handler = signal_handler_term;
+      if (sigaction (SIGTERM, &sa, 0) < 0)
+       {
+         error = clib_error_return_unix (0, "sigaction");
+         goto done;
+       }
 
-  /* Save the original tty state so we can restore it later */
-  tcgetattr (STDIN_FILENO, &orig_tio);
+      /* Save the original tty state so we can restore it later */
+      if (tcgetattr (STDIN_FILENO, &orig_tio) < 0)
+       {
+         error = clib_error_return_unix (0, "tcgetattr");
+         goto done;
+       }
+
+      /* Tweak the tty settings */
+      tio = orig_tio;
+      /* echo off, canonical mode off, ext'd input processing off */
+      tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
+      tio.c_cc[VMIN] = 1;      /* 1 byte at a time */
+      tio.c_cc[VTIME] = 0;     /* no timer */
 
-  /* Tweak the tty settings */
-  tio = orig_tio;
-  /* echo off, canonical mode off, ext'd input processing off */
-  tio.c_lflag &= ~(ECHO | ICANON | IEXTEN);
-  tio.c_cc[VMIN] = 1;          /* 1 byte at a time */
-  tio.c_cc[VTIME] = 0;         /* no timer */
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio);
+      if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &tio) < 0)
+       {
+         error = clib_error_return_unix (0, "tcsetattr");
+         goto done;
+       }
+    }
 
   efd = epoll_create1 (0);
 
@@ -199,8 +224,12 @@ main (int argc, char *argv[])
   event.data.fd = STDIN_FILENO;
   if (epoll_ctl (efd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0)
     {
-      error = clib_error_return_unix (0, "epoll_ctl[%d]", STDIN_FILENO);
-      goto done;
+      /* ignore EPERM; it means stdin is something like /dev/null */
+      if (errno != EPERM)
+       {
+         error = clib_error_return_unix (0, "epoll_ctl[%d]", STDIN_FILENO);
+         goto done;
+       }
     }
 
   /* register socket */
@@ -240,6 +269,9 @@ main (int argc, char *argv[])
          int n;
          char c[100];
 
+         if (!sent_ttype)
+           continue;           /* not ready for this yet */
+
          n = read (STDIN_FILENO, c, sizeof (c));
          if (n > 0)
            {
@@ -250,6 +282,8 @@ main (int argc, char *argv[])
            }
          else if (n < 0)
            clib_warning ("read rv=%d", n);
+         else                  /* EOF */
+           do_quit = 1;
        }
       else if (event.data.fd == s->fd)
        {
@@ -260,31 +294,56 @@ main (int argc, char *argv[])
          if (clib_socket_rx_end_of_file (s))
            break;
 
-         str = process_input (str, s, cmd == 0);
+         str = process_input (str, s, is_interactive, &sent_ttype);
 
          if (vec_len (str) > 0)
            {
-             n = write (STDOUT_FILENO, str, vec_len (str));
-             if (n < 0)
+             int len = vec_len (str);
+             u8 *p = str, *q = str;
+
+             while (len)
                {
-                 error = clib_error_return_unix (0, "write");
-                 goto done;
+                 /* Search for and skip NUL bytes */
+                 while (q < (p + len) && *q)
+                   q++;
+
+                 n = write (STDOUT_FILENO, p, q - p);
+                 if (n < 0)
+                   {
+                     error = clib_error_return_unix (0, "write");
+                     goto done;
+                   }
+
+                 while (q < (p + len) && !*q)
+                   {
+                     q++;
+                     acked++;  /* every NUL is an acknowledgement */
+                   }
+                 len -= q - p;
+                 p = q;
                }
+
              vec_reset_length (str);
            }
 
-         if (do_quit)
+         if (do_quit && do_quit < acked)
            {
-             clib_socket_tx_add_formatted (s, "q\n");
+             /* Ask the other end to close the connection */
+             clib_socket_tx_add_formatted (s, "quit\n");
              clib_socket_tx (s);
              do_quit = 0;
            }
-         if (cmd)
+         if (cmd && sent_ttype)
            {
+             /* We wait until after the TELNET TTYPE option has been sent.
+              * That is to make sure the session at the VPP end has switched
+              * to line-by-line mode, and thus avoid prompts and echoing.
+              * Note that it does also disable further TELNET option processing.
+              */
              clib_socket_tx_add_formatted (s, "%s\n", cmd);
              clib_socket_tx (s);
              vec_free (cmd);
-             do_quit = 1;
+             do_quit = acked;  /* quit after the next response */
            }
        }
       else
@@ -302,12 +361,15 @@ done:
   if (efd > -1)
     close (efd);
 
+  if (is_interactive)
+    tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
+
   if (error)
     {
       clib_error_report (error);
       return 1;
     }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_tio);
+
   return 0;
 }