#include "ser-unix.h"
#include <sys/types.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h> /* For FIONBIO. */
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h> /* For FIONBIO. */
+#endif
+
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
-#ifndef __CYGWIN__
#include <netinet/tcp.h>
-#endif
#include <signal.h>
#include "gdb_string.h"
-static int tcp_open (serial_t scb, const char *name);
-static void tcp_close (serial_t scb);
-
+static int net_open (struct serial *scb, const char *name);
+static void net_close (struct serial *scb);
void _initialize_ser_tcp (void);
-/* Open up a raw tcp socket */
+/* seconds to wait for connect */
+#define TIMEOUT 15
+/* how many times per second to poll deprecated_ui_loop_hook */
+#define POLL_INTERVAL 2
+
+/* Open a tcp socket */
static int
-tcp_open (serial_t scb, const char *name)
+net_open (struct serial *scb, const char *name)
{
- char *port_str;
- int port;
+ char *port_str, hostname[100];
+ int n, port, tmp;
+ int use_udp;
struct hostent *hostent;
struct sockaddr_in sockaddr;
- int tmp;
- char hostname[100];
- struct protoent *protoent;
- int i;
+
+ use_udp = 0;
+ if (strncmp (name, "udp:", 4) == 0)
+ {
+ use_udp = 1;
+ name = name + 4;
+ }
+ else if (strncmp (name, "tcp:", 4) == 0)
+ name = name + 4;
port_str = strchr (name, ':');
if (!port_str)
- error ("tcp_open: No colon in host name!"); /* Shouldn't ever happen */
+ error ("net_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);
- hostent = gethostbyname (hostname);
+ /* default hostname is localhost */
+ if (!hostname[0])
+ strcpy (hostname, "localhost");
+ hostent = gethostbyname (hostname);
if (!hostent)
{
fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname);
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));
+ if (use_udp)
+ scb->fd = socket (PF_INET, SOCK_DGRAM, 0);
+ else
+ scb->fd = socket (PF_INET, SOCK_STREAM, 0);
- /* Enable TCP keep alive process. */
- tmp = 1;
- setsockopt (scb->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &tmp, sizeof (tmp));
+ 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));
- 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);
- if (!connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr)))
- break;
+ /* Use Non-blocking connect. connect() will return 0 if connected already. */
+ n = connect (scb->fd, (struct sockaddr *) &sockaddr, sizeof (sockaddr));
- close (scb->fd);
- scb->fd = -1;
+ if (n < 0 && errno != EINPROGRESS)
+ {
+ net_close (scb);
+ return -1;
+ }
-/* We retry for ECONNREFUSED because that is often a temporary condition, which
- happens when the server is being restarted. */
+ if (n)
+ {
+ /* looks like we need to wait for the connect */
+ struct timeval t;
+ fd_set rset, wset;
+ int polls = 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 (deprecated_ui_loop_hook)
+ {
+ if (deprecated_ui_loop_hook (0))
+ {
+ errno = EINTR;
+ net_close (scb);
+ return -1;
+ }
+ }
+
+ FD_SET (scb->fd, &rset);
+ wset = rset;
+ t.tv_sec = 0;
+ t.tv_usec = 1000000 / POLL_INTERVAL;
+
+ n = select (scb->fd + 1, &rset, &wset, NULL, &t);
+ polls++;
+ }
+ while (n == 0 && polls <= TIMEOUT * POLL_INTERVAL);
+ if (n < 0 || polls > TIMEOUT * POLL_INTERVAL)
+ {
+ if (polls > TIMEOUT * POLL_INTERVAL)
+ errno = ETIMEDOUT;
+ net_close (scb);
+ return -1;
+ }
+ }
- if (errno != ECONNREFUSED)
+ /* 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;
+ net_close (scb);
return -1;
+ }
+ }
- sleep (1);
- }
-
- protoent = getprotobyname ("tcp");
- if (!protoent)
- return -1;
+ /* turn off nonblocking */
+ tmp = 0;
+ ioctl (scb->fd, FIONBIO, &tmp);
- tmp = 1;
- if (setsockopt (scb->fd, protoent->p_proto, TCP_NODELAY,
- (char *) &tmp, sizeof (tmp)))
- return -1;
+ if (use_udp == 0)
+ {
+ /* Disable Nagle algorithm. Needed in some cases. */
+ tmp = 1;
+ setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&tmp, sizeof (tmp));
+ }
- signal (SIGPIPE, SIG_IGN); /* If we don't do this, then GDB simply exits
- when the remote side dies. */
+ /* If we don't do this, then GDB simply exits
+ when the remote side dies. */
+ signal (SIGPIPE, SIG_IGN);
return 0;
}
static void
-tcp_close (serial_t scb)
+net_close (struct serial *scb)
{
if (scb->fd < 0)
return;
_initialize_ser_tcp (void)
{
struct serial_ops *ops = XMALLOC (struct serial_ops);
- memset (ops, sizeof (struct serial_ops), 0);
+ memset (ops, 0, sizeof (struct serial_ops));
ops->name = "tcp";
ops->next = 0;
- ops->open = tcp_open;
- ops->close = tcp_close;
+ ops->open = net_open;
+ ops->close = net_close;
ops->readchar = ser_unix_readchar;
ops->write = ser_unix_write;
ops->flush_output = ser_unix_nop_flush_output;