X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fser-tcp.c;h=5aa7105dc2cb4e8549f286994d918e3e7e4cd06a;hb=9a73f0ad6ca2a9c027f30fca3d79fa958784dcbf;hp=8e7da061825b8db295c17a7023f23cd64a7b0d18;hpb=104c1213b4821a4b8664e66db4643a951b461576;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/ser-tcp.c b/gdb/ser-tcp.c index 8e7da06182..5aa7105dc2 100644 --- a/gdb/ser-tcp.c +++ b/gdb/ser-tcp.c @@ -1,11 +1,12 @@ -/* Serial interface for raw TCP connections on Un*x like systems - Copyright 1992, 1993, 1998 Free Software Foundation, Inc. +/* Serial interface for raw TCP connections on Un*x like systems. + + Copyright (C) 1992-2019 Free Software Foundation, Inc. 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, @@ -14,391 +15,495 @@ 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., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + along with this program. If not, see . */ #include "defs.h" #include "serial.h" +#include "ser-base.h" +#include "ser-tcp.h" +#include "gdbcmd.h" +#include "cli/cli-decode.h" +#include "cli/cli-setshow.h" +#include "common/filestuff.h" +#include "common/netstuff.h" + #include -#include + +#ifdef HAVE_SYS_FILIO_H +#include /* For FIONBIO. */ +#endif +#ifdef HAVE_SYS_IOCTL_H +#include /* For FIONBIO. */ +#endif + +#include "common/gdb_sys_time.h" + +#ifdef USE_WIN32API +#include +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif +/* Gnulib defines close too, but gnulib's replacement + doesn't call closesocket unless we import the + socketlib module. */ +#undef close +#define close(fd) closesocket (fd) +#define ioctl ioctlsocket +#else #include #include #include #include - -#ifndef __CYGWIN32__ #include #endif -#include "signals.h" -#include "gdb_string.h" +#include +#include "gdb_select.h" +#include -extern int (*ui_loop_hook) PARAMS ((int)); +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif -struct tcp_ttystate - { - int bogus; - }; +/* For "set tcp" and "show tcp". */ -static int tcp_open PARAMS ((serial_t scb, const char *name)); -static void tcp_raw PARAMS ((serial_t scb)); -static int wait_for PARAMS ((serial_t scb, int timeout)); -static int tcp_readchar PARAMS ((serial_t scb, int timeout)); -static int tcp_setbaudrate PARAMS ((serial_t scb, int rate)); -static int tcp_setstopbits PARAMS ((serial_t scb, int num)); -static int tcp_write PARAMS ((serial_t scb, const char *str, int len)); -/* FIXME: static void tcp_restore PARAMS ((serial_t scb)); */ -static void tcp_close PARAMS ((serial_t scb)); -static serial_ttystate tcp_get_tty_state PARAMS ((serial_t scb)); -static int tcp_set_tty_state PARAMS ((serial_t scb, serial_ttystate state)); -static int tcp_return_0 PARAMS ((serial_t)); -static int tcp_noflush_set_tty_state PARAMS ((serial_t, serial_ttystate, - serial_ttystate)); -static void tcp_print_tty_state PARAMS ((serial_t, serial_ttystate)); +static struct cmd_list_element *tcp_set_cmdlist; +static struct cmd_list_element *tcp_show_cmdlist; -void _initialize_ser_tcp PARAMS ((void)); +/* Whether to auto-retry refused connections. */ -/* Open up a raw tcp socket */ +static int tcp_auto_retry = 1; -static int -tcp_open (scb, name) - serial_t scb; - const char *name; -{ - char *port_str; - int port; - struct hostent *hostent; - struct sockaddr_in sockaddr; - int tmp; - char hostname[100]; - struct protoent *protoent; - int i; +/* Timeout period for connections, in seconds. */ - port_str = strchr (name, ':'); +static unsigned int tcp_retry_limit = 15; - if (!port_str) - error ("tcp_open: No colon in host name!"); /* Shouldn't ever happen */ +/* How many times per second to poll deprecated_ui_loop_hook. */ - 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); +#define POLL_INTERVAL 5 - hostent = gethostbyname (hostname); +/* Helper function to wait a while. If SOCK is not -1, wait on its + file descriptor. Otherwise just wait on a timeout, updating + *POLLS. Returns -1 on timeout or interrupt, otherwise the value of + select. */ - if (!hostent) +static int +wait_for_connect (int sock, unsigned int *polls) +{ + struct timeval t; + int n; + + /* 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 && deprecated_ui_loop_hook (0)) { - fprintf_unfiltered (gdb_stderr, "%s: unknown host\n", hostname); - errno = ENOENT; + errno = EINTR; return -1; } - for (i = 1; i <= 15; i++) + /* Check for timeout. */ + if (*polls > tcp_retry_limit * POLL_INTERVAL) { - 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. */ - - if (errno != ECONNREFUSED) - return -1; - - sleep (1); + errno = ETIMEDOUT; + 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. */ + /* Back off to polling once per second after the first POLL_INTERVAL + polls. */ + if (*polls < POLL_INTERVAL) + { + t.tv_sec = 0; + t.tv_usec = 1000000 / POLL_INTERVAL; + } + else + { + t.tv_sec = 1; + t.tv_usec = 0; + } - return 0; + if (sock >= 0) + { + fd_set rset, wset, eset; + + FD_ZERO (&rset); + FD_SET (sock, &rset); + wset = rset; + eset = rset; + + /* POSIX systems return connection success or failure by signalling + wset. Windows systems return success in wset and failure in + eset. + + We must call select here, rather than gdb_select, because + the serial structure has not yet been initialized - the + MinGW select wrapper will not know that this FD refers + to a socket. */ + n = select (sock + 1, &rset, &wset, &eset, &t); + } + else + /* Use gdb_select here, since we have no file descriptors, and on + Windows, plain select doesn't work in that case. */ + n = gdb_select (0, NULL, NULL, NULL, &t); + + /* If we didn't time out, only count it as one poll. */ + if (n > 0 || *polls < POLL_INTERVAL) + (*polls)++; + else + (*polls) += POLL_INTERVAL; + + return n; } -static serial_ttystate -tcp_get_tty_state (scb) - serial_t scb; -{ - struct tcp_ttystate *state; - - state = (struct tcp_ttystate *) xmalloc (sizeof *state); - - return (serial_ttystate) state; -} +/* Try to connect to the host represented by AINFO. If the connection + succeeds, return its socket. Otherwise, return -1 and set ERRNO + accordingly. POLLS is used when 'connect' returns EINPROGRESS, and + we need to invoke 'wait_for_connect' to obtain the status. */ static int -tcp_set_tty_state (scb, ttystate) - serial_t scb; - serial_ttystate ttystate; +try_connect (const struct addrinfo *ainfo, unsigned int *polls) { - struct tcp_ttystate *state; + int sock = gdb_socket_cloexec (ainfo->ai_family, ainfo->ai_socktype, + ainfo->ai_protocol); - state = (struct tcp_ttystate *) ttystate; + if (sock < 0) + return -1; - return 0; -} + /* Set socket nonblocking. */ +#ifdef USE_WIN32API + u_long ioarg = 1; +#else + int ioarg = 1; +#endif -static int -tcp_return_0 (scb) - serial_t scb; -{ - return 0; -} + ioctl (sock, FIONBIO, &ioarg); -static void -tcp_raw (scb) - serial_t scb; -{ - return; /* Always in raw mode */ -} + /* Use Non-blocking connect. connect() will return 0 if connected + already. */ + if (connect (sock, ainfo->ai_addr, ainfo->ai_addrlen) < 0) + { +#ifdef USE_WIN32API + int err = WSAGetLastError(); +#else + int err = errno; +#endif -/* Wait for input on scb, with timeout seconds. Returns 0 on success, - otherwise SERIAL_TIMEOUT or SERIAL_ERROR. + /* If we've got a "connection refused" error, just return + -1. The caller will know what to do. */ + if ( +#ifdef USE_WIN32API + err == WSAECONNREFUSED +#else + err == ECONNREFUSED +#endif + ) + { + close (sock); + errno = err; + return -1; + } - For termio{s}, we actually just setup VTIME if necessary, and let the - timeout occur in the read() in tcp_read(). - */ + if ( + /* Any other error (except EINPROGRESS) will be "swallowed" + here. We return without specifying a return value, and + set errno if the caller wants to inspect what + happened. */ +#ifdef USE_WIN32API + /* Under Windows, calling "connect" with a non-blocking socket + results in WSAEWOULDBLOCK, not WSAEINPROGRESS. */ + err != WSAEWOULDBLOCK +#else + err != EINPROGRESS +#endif + ) + { + close (sock); + errno = err; + return -1; + } -static int -wait_for (scb, timeout) - serial_t scb; - int timeout; -{ - int numfds; - struct timeval tv; - fd_set readfds, exceptfds; + /* Looks like we need to wait for the connect. */ + int n; - FD_ZERO (&readfds); - FD_ZERO (&exceptfds); + do + n = wait_for_connect (sock, polls); + while (n == 0); - tv.tv_sec = timeout; - tv.tv_usec = 0; + if (n < 0) + { + int saved_errno = errno; - FD_SET (scb->fd, &readfds); - FD_SET (scb->fd, &exceptfds); + /* A negative value here means that we either timed out or + got interrupted by the user. Just return. */ + close (sock); + errno = saved_errno; + return -1; + } + } - while (1) - { - if (timeout >= 0) - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, &tv); - else - numfds = select (scb->fd + 1, &readfds, 0, &exceptfds, 0); + /* Got something. Is it an error? */ + int err; + socklen_t len = sizeof (err); - if (numfds <= 0) - { - if (numfds == 0) - return SERIAL_TIMEOUT; - else if (errno == EINTR) - continue; - else - return SERIAL_ERROR; /* Got an error from select or poll */ - } + /* On Windows, the fourth parameter to getsockopt is a "char *"; + on UNIX systems it is generally "void *". The cast to "char *" + is OK everywhere, since in C++ any data pointer type can be + implicitly converted to "void *". */ + int ret = getsockopt (sock, SOL_SOCKET, SO_ERROR, (char *) &err, &len); + + if (ret < 0) + { + int saved_errno = errno; - return 0; + close (sock); + errno = saved_errno; + return -1; + } + else if (ret == 0 && err != 0) + { + close (sock); + errno = err; + return -1; } + + /* The connection succeeded. Return the socket. */ + return sock; } -/* Read a character with user-specified timeout. TIMEOUT is number of seconds - to wait, or -1 to wait forever. Use timeout of 0 to effect a poll. Returns - char if successful. Returns -2 if timeout expired, EOF if line dropped - dead, or -3 for any other error (see errno in that case). */ +/* Open a tcp socket. */ -static int -tcp_readchar (scb, timeout) - serial_t scb; - int timeout; +int +net_open (struct serial *scb, const char *name) { - int status; - int delta; - - if (scb->bufcnt-- > 0) - return *scb->bufp++; + struct addrinfo hint; + struct addrinfo *ainfo; - /* We have to be able to keep the GUI alive here, so we break the original - timeout into steps of 1 second, running the "keep the GUI alive" hook - each time through the loop. + 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; - Also, timeout = 0 means to poll, so we just set the delta to 0, so we - will only go through the loop once. */ + parsed_connection_spec parsed = parse_connection_spec (name, &hint); - delta = (timeout == 0 ? 0 : 1); - while (1) - { + if (parsed.port_str.empty ()) + error (_("Missing port on hostname '%s'"), name); - /* N.B. The UI may destroy our world (for instance by calling - remote_stop,) in which case we want to get out of here as - quickly as possible. It is not safe to touch scb, since - someone else might have freed it. The ui_loop_hook signals that - we should exit by returning 1. */ + int r = getaddrinfo (parsed.host_str.c_str (), + parsed.port_str.c_str (), + &hint, &ainfo); - if (ui_loop_hook) - { - if (ui_loop_hook (0)) - return SERIAL_TIMEOUT; - } + if (r != 0) + { + fprintf_unfiltered (gdb_stderr, _("%s: cannot resolve name: %s\n"), + name, gai_strerror (r)); + errno = ENOENT; + return -1; + } - status = wait_for (scb, delta); - timeout -= delta; + scoped_free_addrinfo free_ainfo (ainfo); - /* If we got a character or an error back from wait_for, then we can - break from the loop before the timeout is completed. */ + /* Flag to indicate whether we've got a connection refused. It will + be true if any of the connections tried was refused. */ + bool got_connrefused; + /* If a connection succeeeds, SUCCESS_AINFO will point to the + 'struct addrinfo' that succeed. */ + struct addrinfo *success_ainfo = NULL; + unsigned int polls = 0; - if (status != SERIAL_TIMEOUT) - { - break; - } + /* Assume the worst. */ + scb->fd = -1; - /* If we have exhausted the original timeout, then generate - a SERIAL_TIMEOUT, and pass it out of the loop. */ + do + { + got_connrefused = false; - else if (timeout == 0) + for (addrinfo *iter = ainfo; iter != NULL; iter = iter->ai_next) { - status == SERIAL_TIMEOUT; - break; + /* Iterate over the list of possible addresses to connect + to. For each, we'll try to connect and see if it + succeeds. */ + int sock = try_connect (iter, &polls); + + if (sock >= 0) + { + /* We've gotten a successful connection. Save its + 'struct addrinfo', the socket, and break. */ + success_ainfo = iter; + scb->fd = sock; + break; + } + else if ( +#ifdef USE_WIN32API + errno == WSAECONNREFUSED +#else + errno == ECONNREFUSED +#endif + ) + got_connrefused = true; } } - - if (status < 0) - return status; - - while (1) + /* Just retry if: + + - tcp_auto_retry is true, and + - We haven't gotten a connection yet, and + - Any of our connection attempts returned with ECONNREFUSED, and + - wait_for_connect signals that we can keep going. */ + while (tcp_auto_retry + && success_ainfo == NULL + && got_connrefused + && wait_for_connect (-1, &polls) >= 0); + + if (success_ainfo == NULL) { - scb->bufcnt = read (scb->fd, scb->buf, BUFSIZ); - if (scb->bufcnt != -1 || errno != EINTR) - break; + net_close (scb); + return -1; } - if (scb->bufcnt <= 0) + /* Turn off nonblocking. */ +#ifdef USE_WIN32API + u_long ioarg = 0; +#else + int ioarg = 0; +#endif + + ioctl (scb->fd, FIONBIO, &ioarg); + + if (success_ainfo->ai_protocol == IPPROTO_TCP) { - if (scb->bufcnt == 0) - return SERIAL_TIMEOUT; /* 0 chars means timeout [may need to - distinguish between EOF & timeouts - someday] */ - else - return SERIAL_ERROR; /* Got an error from read */ + /* Disable Nagle algorithm. Needed in some cases. */ + int tmp = 1; + + setsockopt (scb->fd, IPPROTO_TCP, TCP_NODELAY, + (char *) &tmp, sizeof (tmp)); } - scb->bufcnt--; - scb->bufp = scb->buf; - return *scb->bufp++; -} +#ifdef SIGPIPE + /* If we don't do this, then GDB simply exits + when the remote side dies. */ + signal (SIGPIPE, SIG_IGN); +#endif -static int -tcp_noflush_set_tty_state (scb, new_ttystate, old_ttystate) - serial_t scb; - serial_ttystate new_ttystate; - serial_ttystate old_ttystate; -{ return 0; } -static void -tcp_print_tty_state (scb, ttystate) - serial_t scb; - serial_ttystate ttystate; +void +net_close (struct serial *scb) { - /* Nothing to print. */ - return; + if (scb->fd == -1) + return; + + close (scb->fd); + scb->fd = -1; } -static int -tcp_setbaudrate (scb, rate) - serial_t scb; - int rate; +int +net_read_prim (struct serial *scb, size_t count) { - return 0; /* Never fails! */ + /* Need to cast to silence -Wpointer-sign on MinGW, as Winsock's + 'recv' takes 'char *' as second argument, while 'scb->buf' is + 'unsigned char *'. */ + return recv (scb->fd, (char *) scb->buf, count, 0); } -static int -tcp_setstopbits (scb, num) - serial_t scb; - int num; +int +net_write_prim (struct serial *scb, const void *buf, size_t count) { - return 0; /* Never fails! */ + /* On Windows, the second parameter to send is a "const char *"; on + UNIX systems it is generally "const void *". The cast to "const + char *" is OK everywhere, since in C++ any data pointer type can + be implicitly converted to "const void *". */ + return send (scb->fd, (const char *) buf, count, 0); } -static int -tcp_write (scb, str, len) - serial_t scb; - const char *str; - int len; +int +ser_tcp_send_break (struct serial *scb) { - int cc; + /* Send telnet IAC and BREAK characters. */ + return (serial_write (scb, "\377\363", 2)); +} - while (len > 0) - { - cc = write (scb->fd, str, len); +/* Support for "set tcp" and "show tcp" commands. */ - if (cc < 0) - return 1; - len -= cc; - str += cc; - } - return 0; +static void +set_tcp_cmd (const char *args, int from_tty) +{ + help_list (tcp_set_cmdlist, "set tcp ", all_commands, gdb_stdout); } static void -tcp_close (scb) - serial_t scb; +show_tcp_cmd (const char *args, int from_tty) { - if (scb->fd < 0) - return; - - close (scb->fd); - scb->fd = -1; + help_list (tcp_show_cmdlist, "show tcp ", all_commands, gdb_stdout); } -static struct serial_ops tcp_ops = +#ifndef USE_WIN32API + +/* The TCP ops. */ + +static const struct serial_ops tcp_ops = { "tcp", - 0, - tcp_open, - tcp_close, - tcp_readchar, - tcp_write, - tcp_return_0, /* flush output */ - tcp_return_0, /* flush input */ - tcp_return_0, /* send break */ - tcp_raw, - tcp_get_tty_state, - tcp_set_tty_state, - tcp_print_tty_state, - tcp_noflush_set_tty_state, - tcp_setbaudrate, - tcp_setstopbits, - tcp_return_0, /* wait for output to drain */ + net_open, + net_close, + NULL, + ser_base_readchar, + ser_base_write, + ser_base_flush_output, + ser_base_flush_input, + ser_tcp_send_break, + ser_base_raw, + ser_base_get_tty_state, + ser_base_copy_tty_state, + ser_base_set_tty_state, + ser_base_print_tty_state, + ser_base_setbaudrate, + ser_base_setstopbits, + ser_base_setparity, + ser_base_drain_output, + ser_base_async, + net_read_prim, + net_write_prim }; +#endif /* USE_WIN32API */ + void -_initialize_ser_tcp () +_initialize_ser_tcp (void) { +#ifdef USE_WIN32API + /* Do nothing; the TCP serial operations will be initialized in + ser-mingw.c. */ +#else serial_add_interface (&tcp_ops); +#endif /* USE_WIN32API */ + + add_prefix_cmd ("tcp", class_maintenance, set_tcp_cmd, _("\ +TCP protocol specific variables\n\ +Configure variables specific to remote TCP connections"), + &tcp_set_cmdlist, "set tcp ", + 0 /* allow-unknown */, &setlist); + add_prefix_cmd ("tcp", class_maintenance, show_tcp_cmd, _("\ +TCP protocol specific variables\n\ +Configure variables specific to remote TCP connections"), + &tcp_show_cmdlist, "show tcp ", + 0 /* allow-unknown */, &showlist); + + add_setshow_boolean_cmd ("auto-retry", class_obscure, + &tcp_auto_retry, _("\ +Set auto-retry on socket connect"), _("\ +Show auto-retry on socket connect"), + NULL, NULL, NULL, + &tcp_set_cmdlist, &tcp_show_cmdlist); + + add_setshow_uinteger_cmd ("connect-timeout", class_obscure, + &tcp_retry_limit, _("\ +Set timeout limit in seconds for socket connection"), _("\ +Show timeout limit in seconds for socket connection"), _("\ +If set to \"unlimited\", GDB will keep attempting to establish a\n\ +connection forever, unless interrupted with Ctrl-c.\n\ +The default is 15 seconds."), + NULL, NULL, + &tcp_set_cmdlist, &tcp_show_cmdlist); }