[RFA] new tcp_open

Martin M. Hunt hunt@redhat.com
Mon Dec 3 11:18:00 GMT 2001


Problem:  Using "target remote" to open a tcp connection to a target board 
doesn't timeout properly if the target isn't running or you mistype the name
or port number. The tcp_open function tries to connect 15 times, each of which can take
a couple of minutes to timeout.  You can't interrupt the connect from the GUI, which
is not updated, or from the command line with a ^C.

I propose replacing tcp_open with a new function that does a non-blocking connect 
with a maximum timeout of 15 seconds.  This is easily interruptable by typing ^C or
clicking on the stop button. I have tested this with Linux, Solaris, and Cygwin.

-- 
Martin Hunt
GDB Engineer
Red Hat, Inc.

2001-12-03  Martin M. Hunt  <hunt@redhat.com>

	* ser-tcp.c (tcp_open): Rewrite to use a non-blocking connect.
	Allow UI and CLI to abort connect.  Instead of trying 15 times
	with very long timeouts, just try one connect with a maximum timeout
	of 15 seconds.

Here is the new function, followed by the patch:

#define TIMEOUT 30

/* Open a tcp socket */

static int
tcp_open (struct serial *scb, const char *name)
{
  char *port_str, hostname[100];
  int n, port, tmp;
  struct hostent *hostent;
  struct sockaddr_in sockaddr;

  port_str = strchr (name, ':');

  if (!port_str)
    error ("tcp_open: No colon in host name!");	   /* Shouldn't ever happen */

  tmp = min (port_str - name, (int) sizeof hostname - 1);
  strncpy (hostname, name, tmp);	/* Don't want colon */
  hostname[tmp] = '\000';	/* Tie off host name */
  port = atoi (port_str + 1);

  /* default hostname is localhost */
  if (!hostname[0])
    strcpy (hostname, "localhost");

  hostent = gethostbyname (hostname);
  if (!hostent)
    {
      fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
      errno = ENOENT;
      return -1;
    }

  scb->fd = socket (PF_INET, SOCK_STREAM, 0);
  if (scb->fd < 0)
    return -1;
  
  sockaddr.sin_family = PF_INET;
  sockaddr.sin_port = htons (port);
  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
	  sizeof (struct in_addr));

  /* set socket nonblocking */
  tmp = 1;
  ioctl (scb->fd, FIONBIO, &tmp);

  /* Use Non-blocking connect.  connect() will return 0 if connected already. */
  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
  if (n)
    {
      /* looks like we need to wait for the connect */
      struct timeval t;
      fd_set rset, wset;
      int secs = 0;
      FD_ZERO (&rset);

      do 
	{
	  /* While we wait for the connect to complete */
	  /* poll the UI so it can update or the user can */
	  /* interrupt. */
	  if (ui_loop_hook)
	    {
	      if (ui_loop_hook (0))
		{
		  errno = EINTR;
		  tcp_close (scb);
		  return -1;
		}
	    }
	  
	  FD_SET (scb->fd, &rset);
	  wset = rset;
	  t.tv_sec = 0;
	  t.tv_usec = 500000; /* 0.5 seconds */
	  
	  n = select (scb->fd + 1, &rset, &wset, NULL, &t);
	  secs++;
	} 
      while (n == 0 && secs <= TIMEOUT);
      
      if (n < 0 || secs > TIMEOUT)
	{
	  if (secs > TIMEOUT)
	    errno = ETIMEDOUT;
	  tcp_close (scb);
	  return -1;
	}
    }


  /* Got something.  Is it an error? */
  {
    int res, err, len;
    len = sizeof(err);
    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, &err, &len);
    if (res < 0 || err)
      {
	if (err)
	  errno = err;
	tcp_close (scb);
	return -1;
      }
  } 
  
  /* turn off nonblocking */
  tmp = 0;
  ioctl (scb->fd, FIONBIO, &tmp);

  /* If we don't do this, then GDB simply exits
     when the remote side dies.  */
  signal (SIGPIPE, SIG_IGN);

  return 0;
}

And the patch:

Index: ser-tcp.c
===================================================================
RCS file: /cvs/src/src/gdb/ser-tcp.c,v
retrieving revision 1.7
diff -u -p -r1.7 ser-tcp.c
--- ser-tcp.c	2001/08/02 09:08:03	1.7
+++ ser-tcp.c	2001/12/03 18:29:41
@@ -24,6 +24,7 @@
 #include "ser-unix.h"
 
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #include <sys/time.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -36,90 +37,122 @@
 
 static int tcp_open (struct serial *scb, const char *name);
 static void tcp_close (struct serial *scb);
-
+extern int (*ui_loop_hook) (int);
 void _initialize_ser_tcp (void);
+
+#define TIMEOUT 30
 
-/* Open up a raw tcp socket */
+/* Open a tcp socket */
 
 static int
 tcp_open (struct serial *scb, const char *name)
 {
-  char *port_str;
-  int port;
+  char *port_str, hostname[100];
+  int n, port, tmp;
   struct hostent *hostent;
   struct sockaddr_in sockaddr;
-  int tmp;
-  char hostname[100];
-  struct protoent *protoent;
-  int i;
 
   port_str = strchr (name, ':');
 
   if (!port_str)
-    error ("tcp_open: No colon in host name!");		/* Shouldn't ever happen */
+    error ("tcp_open: No colon in host name!");	   /* Shouldn't ever happen */
 
   tmp = min (port_str - name, (int) sizeof hostname - 1);
   strncpy (hostname, name, tmp);	/* Don't want colon */
   hostname[tmp] = '\000';	/* Tie off host name */
   port = atoi (port_str + 1);
 
+  /* default hostname is localhost */
   if (!hostname[0])
     strcpy (hostname, "localhost");
 
   hostent = gethostbyname (hostname);
-
   if (!hostent)
     {
       fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
       errno = ENOENT;
       return -1;
     }
-
-  for (i = 1; i <= 15; i++)
-    {
-      scb->fd = socket (PF_INET, SOCK_STREAM, 0);
-      if (scb->fd < 0)
-	return -1;
-
-      /* Allow rapid reuse of this port. */
-      tmp = 1;
-      setsockopt (scb->fd, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp));
-
-      /* Enable TCP keep alive process. */
-      tmp = 1;
-      setsockopt (scb->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp));
-
-      sockaddr.sin_family = PF_INET;
-      sockaddr.sin_port = htons (port);
-      memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
-	      sizeof (struct in_addr));
-
-      if (!connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)))
-	break;
 
-      close (scb->fd);
-      scb->fd = -1;
-
-/* We retry for ECONNREFUSED because that is often a temporary condition, which
-   happens when the server is being restarted.  */
+  scb->fd = socket (PF_INET, SOCK_STREAM, 0);
+  if (scb->fd < 0)
+    return -1;
+  
+  sockaddr.sin_family = PF_INET;
+  sockaddr.sin_port = htons (port);
+  memcpy (&sockaddr.sin_addr.s_addr, hostent->h_addr,
+	  sizeof (struct in_addr));
 
-      if (errno != ECONNREFUSED)
-	return -1;
+  /* set socket nonblocking */
+  tmp = 1;
+  ioctl (scb->fd, FIONBIO, &tmp);
 
-      sleep (1);
+  /* Use Non-blocking connect.  connect() will return 0 if connected already. */
+  n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
+  if (n)
+    {
+      /* looks like we need to wait for the connect */
+      struct timeval t;
+      fd_set rset, wset;
+      int secs = 0;
+      FD_ZERO (&rset);
+
+      do 
+	{
+	  /* While we wait for the connect to complete */
+	  /* poll the UI so it can update or the user can */
+	  /* interrupt. */
+	  if (ui_loop_hook)
+	    {
+	      if (ui_loop_hook (0))
+		{
+		  errno = EINTR;
+		  tcp_close (scb);
+		  return -1;
+		}
+	    }
+	  
+	  FD_SET (scb->fd, &rset);
+	  wset = rset;
+	  t.tv_sec = 0;
+	  t.tv_usec = 500000; /* 0.5 seconds */
+	  
+	  n = select (scb->fd + 1, &rset, &wset, NULL, &t);
+	  secs++;
+	} 
+      while (n == 0 && secs <= TIMEOUT);
+      
+      if (n < 0 || secs > TIMEOUT)
+	{
+	  if (secs > TIMEOUT)
+	    errno = ETIMEDOUT;
+	  tcp_close (scb);
+	  return -1;
+	}
     }
 
-  protoent = getprotobyname ("tcp");
-  if (!protoent)
-    return -1;
 
-  tmp = 1;
-  if (setsockopt (scb->fd, protoent->p_proto, TCP_NODELAY,
-		  (char *) &tmp, sizeof (tmp)))
-    return -1;
-
-  signal (SIGPIPE, SIG_IGN);	/* If we don't do this, then GDB simply exits
-				   when the remote side dies.  */
+  /* Got something.  Is it an error? */
+  {
+    int res, err, len;
+    len = sizeof(err);
+    res = getsockopt (scb->fd, SOL_SOCKET, SO_ERROR, &err, &len);
+    if (res < 0 || err)
+      {
+	if (err)
+	  errno = err;
+	tcp_close (scb);
+	return -1;
+      }
+  } 
+  
+  /* turn off nonblocking */
+  tmp = 0;
+  ioctl (scb->fd, FIONBIO, &tmp);
+
+  /* If we don't do this, then GDB simply exits
+     when the remote side dies.  */
+  signal (SIGPIPE, SIG_IGN);
 
   return 0;
 }



More information about the Gdb-patches mailing list