Revert 'Remove unused struct serial::name field'
[deliverable/binutils-gdb.git] / gdb / gdbserver / gdbreplay.c
index f089b3f79f8050027fc4db10aa8c4f81fe8ab1f7..3ed18f1f37225fa64f9ad3be708f09eb2b38a2e2 100644 (file)
@@ -1,13 +1,12 @@
 /* Replay a remote debug session logfile for GDB.
-   Copyright (C) 1996, 1998, 1999, 2000, 2002, 2003, 2005, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 1996-2020 Free Software Foundation, Inc.
    Written by Fred Fish (fnf@cygnus.com) from pieces of gdbserver.
 
    This file is part of GDB.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street, Fifth Floor,
-   Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/version.h"
 
-#include "config.h"
-#include <stdio.h>
 #if HAVE_SYS_FILE_H
 #include <sys/file.h>
 #endif
 #if HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
-#if HAVE_ERRNO_H
-#include <errno.h>
-#endif
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
 #if HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
 #endif
-#if HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 
 #if USE_WIN32API
-#include <winsock.h>
+#include <ws2tcpip.h>
 #endif
 
+#include "gdbsupport/netstuff.h"
+#include "gdbsupport/rsp-low.h"
+
 #ifndef HAVE_SOCKLEN_T
 typedef int socklen_t;
 #endif
@@ -124,38 +111,20 @@ strerror (DWORD error)
 
 #endif /* __MINGW32CE__ */
 
-/* Print the system error message for errno, and also mention STRING
-   as the file name for which the error was encountered.
-   Then return to command level.  */
-
 static void
-perror_with_name (char *string)
+sync_error (FILE *fp, const char *desc, int expect, int got)
 {
-#ifndef STDC_HEADERS
-  extern int errno;
-#endif
-  const char *err;
-  char *combined;
-
-  err = strerror (errno);
-  if (err == NULL)
-    err = "unknown error";
-
-  combined = (char *) alloca (strlen (err) + strlen (string) + 3);
-  strcpy (combined, string);
-  strcat (combined, ": ");
-  strcat (combined, err);
-  fprintf (stderr, "\n%s.\n", combined);
+  fprintf (stderr, "\n%s\n", desc);
+  fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
+          ftell (fp), expect, got);
   fflush (stderr);
   exit (1);
 }
 
 static void
-sync_error (FILE *fp, char *desc, int expect, int got)
+remote_error (const char *desc)
 {
   fprintf (stderr, "\n%s\n", desc);
-  fprintf (stderr, "At logfile offset %ld, expected '0x%x' got '0x%x'\n",
-          ftell (fp), expect, got);
   fflush (stderr);
   exit (1);
 }
@@ -176,74 +145,137 @@ remote_close (void)
 static void
 remote_open (char *name)
 {
-  if (!strchr (name, ':'))
+  char *last_colon = strrchr (name, ':');
+
+  if (last_colon == NULL)
     {
       fprintf (stderr, "%s: Must specify tcp connection as host:addr\n", name);
       fflush (stderr);
       exit (1);
     }
-  else
-    {
+
 #ifdef USE_WIN32API
-      static int winsock_initialized;
+  static int winsock_initialized;
 #endif
-      char *port_str;
-      int port;
-      struct sockaddr_in sockaddr;
-      socklen_t tmp;
-      int tmp_desc;
+  int tmp;
+  int tmp_desc;
+  struct addrinfo hint;
+  struct addrinfo *ainfo;
+
+  memset (&hint, 0, sizeof (hint));
+  /* Assume no prefix will be passed, therefore we should use
+     AF_UNSPEC.  */
+  hint.ai_family = AF_UNSPEC;
+  hint.ai_socktype = SOCK_STREAM;
+  hint.ai_protocol = IPPROTO_TCP;
 
-      port_str = strchr (name, ':');
+  parsed_connection_spec parsed = parse_connection_spec (name, &hint);
 
-      port = atoi (port_str + 1);
+  if (parsed.port_str.empty ())
+    error (_("Missing port on hostname '%s'"), name);
 
 #ifdef USE_WIN32API
-      if (!winsock_initialized)
-       {
-         WSADATA wsad;
+  if (!winsock_initialized)
+    {
+      WSADATA wsad;
 
-         WSAStartup (MAKEWORD (1, 0), &wsad);
-         winsock_initialized = 1;
-       }
+      WSAStartup (MAKEWORD (1, 0), &wsad);
+      winsock_initialized = 1;
+    }
 #endif
 
-      tmp_desc = socket (PF_INET, SOCK_STREAM, 0);
-      if (tmp_desc < 0)
-       perror_with_name ("Can't open socket");
+  int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
+                      &hint, &ainfo);
 
-      /* Allow rapid reuse of this port. */
-      tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
-                 sizeof (tmp));
+  if (r != 0)
+    {
+      fprintf (stderr, "%s:%s: cannot resolve name: %s\n",
+              parsed.host_str.c_str (), parsed.port_str.c_str (),
+              gai_strerror (r));
+      fflush (stderr);
+      exit (1);
+    }
+
+  scoped_free_addrinfo free_ainfo (ainfo);
 
-      sockaddr.sin_family = PF_INET;
-      sockaddr.sin_port = htons (port);
-      sockaddr.sin_addr.s_addr = INADDR_ANY;
+  struct addrinfo *p;
 
-      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-         || listen (tmp_desc, 1))
-       perror_with_name ("Can't bind address");
+  for (p = ainfo; p != NULL; p = p->ai_next)
+    {
+      tmp_desc = socket (p->ai_family, p->ai_socktype, p->ai_protocol);
+
+      if (tmp_desc >= 0)
+       break;
+    }
+
+  if (p == NULL)
+    perror_with_name ("Cannot open socket");
+
+  /* Allow rapid reuse of this port. */
+  tmp = 1;
+  setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+             sizeof (tmp));
+
+  switch (p->ai_family)
+    {
+    case AF_INET:
+      ((struct sockaddr_in *) p->ai_addr)->sin_addr.s_addr = INADDR_ANY;
+      break;
+    case AF_INET6:
+      ((struct sockaddr_in6 *) p->ai_addr)->sin6_addr = in6addr_any;
+      break;
+    default:
+      fprintf (stderr, "Invalid 'ai_family' %d\n", p->ai_family);
+      exit (1);
+    }
+
+  if (bind (tmp_desc, p->ai_addr, p->ai_addrlen) != 0)
+    perror_with_name ("Can't bind address");
+
+  if (p->ai_socktype == SOCK_DGRAM)
+    remote_desc = tmp_desc;
+  else
+    {
+      struct sockaddr_storage sockaddr;
+      socklen_t sockaddrsize = sizeof (sockaddr);
+      char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
+
+      if (listen (tmp_desc, 1) != 0)
+       perror_with_name ("Can't listen on socket");
+
+      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr,
+                           &sockaddrsize);
 
-      tmp = sizeof (sockaddr);
-      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
       if (remote_desc == -1)
        perror_with_name ("Accept failed");
 
       /* Enable TCP keep alive process. */
       tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp));
+      setsockopt (tmp_desc, SOL_SOCKET, SO_KEEPALIVE,
+                 (char *) &tmp, sizeof (tmp));
 
       /* Tell TCP not to delay small packets.  This greatly speeds up
-         interactive response. */
+        interactive response. */
       tmp = 1;
       setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
                  (char *) &tmp, sizeof (tmp));
 
+      if (getnameinfo ((struct sockaddr *) &sockaddr, sockaddrsize,
+                      orig_host, sizeof (orig_host),
+                      orig_port, sizeof (orig_port),
+                      NI_NUMERICHOST | NI_NUMERICSERV) == 0)
+       {
+         fprintf (stderr, "Remote debugging from host %s, port %s\n",
+                  orig_host, orig_port);
+         fflush (stderr);
+       }
+
 #ifndef USE_WIN32API
       close (tmp_desc);                /* No longer need this */
 
-      signal (SIGPIPE, SIG_IGN);       /* If we don't do this, then gdbreplay simply
-                                          exits when the remote side dies.  */
+      signal (SIGPIPE, SIG_IGN);       /* If we don't do this, then
+                                          gdbreplay simply exits when
+                                          the remote side dies.  */
 #else
       closesocket (tmp_desc);  /* No longer need this */
 #endif
@@ -257,26 +289,6 @@ remote_open (char *name)
   fflush (stderr);
 }
 
-static int
-tohex (int ch)
-{
-  if (ch >= '0' && ch <= '9')
-    {
-      return (ch - '0');
-    }
-  if (ch >= 'A' && ch <= 'F')
-    {
-      return (ch - 'A' + 10);
-    }
-  if (ch >= 'a' && ch <= 'f')
-    {
-      return (ch - 'a' + 10);
-    }
-  fprintf (stderr, "\nInvalid hex digit '%c'\n", ch);
-  fflush (stderr);
-  exit (1);
-}
-
 static int
 logchar (FILE *fp)
 {
@@ -284,10 +296,26 @@ logchar (FILE *fp)
   int ch2;
 
   ch = fgetc (fp);
-  fputc (ch, stdout);
-  fflush (stdout);
+  if (ch != '\r')
+    {
+      fputc (ch, stdout);
+      fflush (stdout);
+    }
   switch (ch)
     {
+      /* Treat \r\n as a newline.  */
+    case '\r':
+      ch = fgetc (fp);
+      if (ch == '\n')
+       ch = EOL;
+      else
+       {
+         ungetc (ch, fp);
+         ch = '\r';
+       }
+      fputc (ch == EOL ? '\n' : '\r', stdout);
+      fflush (stdout);
+      break;
     case '\n':
       ch = EOL;
       break;
@@ -321,11 +349,11 @@ logchar (FILE *fp)
          ch2 = fgetc (fp);
          fputc (ch2, stdout);
          fflush (stdout);
-         ch = tohex (ch2) << 4;
+         ch = fromhex (ch2) << 4;
          ch2 = fgetc (fp);
          fputc (ch2, stdout);
          fflush (stdout);
-         ch |= tohex (ch2);
+         ch |= fromhex (ch2);
          break;
        default:
          /* Treat any other char as just itself */
@@ -337,6 +365,17 @@ logchar (FILE *fp)
   return (ch);
 }
 
+static int
+gdbchar (int desc)
+{
+  unsigned char fromgdb;
+
+  if (read (desc, &fromgdb, 1) != 1)
+    return -1;
+  else
+    return fromgdb;
+}
+
 /* Accept input from gdb and match with chars from fp (after skipping one
    blank) up until a \n is read from fp (which is not matched) */
 
@@ -344,7 +383,7 @@ static void
 expect (FILE *fp)
 {
   int fromlog;
-  unsigned char fromgdb;
+  int fromgdb;
 
   if ((fromlog = logchar (fp)) != ' ')
     {
@@ -355,15 +394,16 @@ expect (FILE *fp)
     {
       fromlog = logchar (fp);
       if (fromlog == EOL)
-       {
-         break;
-       }
-      read (remote_desc, &fromgdb, 1);
+       break;
+      fromgdb = gdbchar (remote_desc);
+      if (fromgdb < 0)
+       remote_error ("Error during read from gdb");
     }
   while (fromlog == fromgdb);
+
   if (fromlog != EOL)
     {
-      sync_error (fp, "Sync error during read of gdb packet", fromlog,
+      sync_error (fp, "Sync error during read of gdb packet from log", fromlog,
                  fromgdb);
     }
 }
@@ -385,20 +425,53 @@ play (FILE *fp)
   while ((fromlog = logchar (fp)) != EOL)
     {
       ch = fromlog;
-      write (remote_desc, &ch, 1);
+      if (write (remote_desc, &ch, 1) != 1)
+       remote_error ("Error during write to gdb");
     }
 }
 
-int
-main (int argc, char *argv[])
+static void
+gdbreplay_version (void)
+{
+  printf ("GNU gdbreplay %s%s\n"
+         "Copyright (C) 2020 Free Software Foundation, Inc.\n"
+         "gdbreplay is free software, covered by "
+         "the GNU General Public License.\n"
+         "This gdbreplay was configured as \"%s\"\n",
+         PKGVERSION, version, host_name);
+}
+
+static void
+gdbreplay_usage (FILE *stream)
+{
+  fprintf (stream, "Usage:\tgdbreplay LOGFILE HOST:PORT\n");
+  if (REPORT_BUGS_TO[0] && stream == stdout)
+    fprintf (stream, "Report bugs to \"%s\".\n", REPORT_BUGS_TO);
+}
+
+/* Main function.  This is called by the real "main" function,
+   wrapped in a TRY_CATCH that handles any uncaught exceptions.  */
+
+static void ATTRIBUTE_NORETURN
+captured_main (int argc, char *argv[])
 {
   FILE *fp;
   int ch;
 
+  if (argc >= 2 && strcmp (argv[1], "--version") == 0)
+    {
+      gdbreplay_version ();
+      exit (0);
+    }
+  if (argc >= 2 && strcmp (argv[1], "--help") == 0)
+    {
+      gdbreplay_usage (stdout);
+      exit (0);
+    }
+
   if (argc < 3)
     {
-      fprintf (stderr, "Usage: gdbreplay <logfile> <host:port>\n");
-      fflush (stderr);
+      gdbreplay_usage (stderr);
       exit (1);
     }
   fp = fopen (argv[1], "r");
@@ -428,3 +501,24 @@ main (int argc, char *argv[])
   remote_close ();
   exit (0);
 }
+
+int
+main (int argc, char *argv[])
+{
+  try
+    {
+      captured_main (argc, argv);
+    }
+  catch (const gdb_exception &exception)
+    {
+      if (exception.reason == RETURN_ERROR)
+       {
+         fflush (stdout);
+         fprintf (stderr, "%s\n", exception.what ());
+       }
+
+      exit (1);
+    }
+
+  gdb_assert_not_reached ("captured_main should never return");
+}
This page took 0.030559 seconds and 4 git commands to generate.