From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 11862 invoked by alias); 3 Dec 2001 19:18:12 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 11798 invoked from network); 3 Dec 2001 19:18:07 -0000 Received: from unknown (HELO cygnus.com) (205.180.230.5) by sources.redhat.com with SMTP; 3 Dec 2001 19:18:07 -0000 Received: from there (cse.cygnus.com [205.180.230.236]) by runyon.cygnus.com (8.8.7-cygnus/8.8.7) with SMTP id LAA18199 for ; Mon, 3 Dec 2001 11:18:05 -0800 (PST) Message-Id: <200112031918.LAA18199@cygnus.com> Content-Type: text/plain; charset="iso-8859-1" From: "Martin M. Hunt" Organization: Red Hat Inc To: gdb-patches@sources.redhat.com Subject: [RFA] new tcp_open Date: Mon, 03 Dec 2001 11:18:00 -0000 X-Mailer: KMail [version 1.3.2] MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SW-Source: 2001-12/txt/msg00051.txt.bz2 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 * 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 +#include #include #include #include @@ -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; }