From: Pedro Alves <palves@redhat.com>
To: John Darrington <john@darrington.wattle.id.au>,
gdb-patches@sourceware.org
Subject: Re: [PATCH] GDBSERVER: Listen on a unix domain (instead of TCP) socket if requested.
Date: Tue, 09 Oct 2018 18:02:00 -0000 [thread overview]
Message-ID: <cc6ec9f9-20c0-ee07-7047-acd1801a2b1a@redhat.com> (raw)
In-Reply-To: <20181009173257.11250-2-john@darrington.wattle.id.au>
On 10/09/2018 06:32 PM, John Darrington wrote:
> When invoking gdbserver, if the COMM parameter does not include a colon (:) and
> is not the name of an existing character device, then a local (unix) domain
> socket will be created with that name and gdbserver will listen for connections
> on that.
Is that "colon/no-colon" magic something that tools frequently do?
Off hand it doesn't seem like a good idea to me, because if you typo a
character device name or unix tcp host name, you end up creating a
unix socket. I'd think an explicit option would be better. E.g.,
reuse gdb's connection specs, i.e., a prefix, like:
gdbserver unix:/tmp/some/path
which can be extended just like gdb's:
gdbserver tcp:host:port
gdbserver tcp6:host:port
etc.
That would suggest extending parse_connection_spec to support "unix" specs.
>
> gdb/doc/
> * gdb.texinfo (Server): Describe connection over a local domain socket.
>
> gdb/gdbserver/
> * NEWS: Mention new feature.
NEWS is under gdb/, not gdb/gdbserver/.
> * configure.ac (AC_CHECK_HEADERS): Add sys/un.h.
> * remote-utils.c (remote_prepare): Create a local socket if requested.
> * remote-utils.c (remote_open): Don't attempt to open a file if it's a socket.
> * remote-utils.c (handle_accept_event): Display the name of the socket on connection.
Don't repeat "* remote-utils.c" for the second and third functions.
Add "* configure: Regenerate.".
> ---
> gdb/NEWS | 4 ++
> gdb/doc/gdb.texinfo | 26 +++++--
> gdb/gdbserver/configure.ac | 2 +-
> gdb/gdbserver/remote-utils.c | 164 ++++++++++++++++++++++++++++++-------------
> 4 files changed, 141 insertions(+), 55 deletions(-)
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 78d20713a8..95c5083d9b 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -15,6 +15,10 @@
> can be passed using the '[ADDRESS]:PORT' notation, or the regular
> 'ADDRESS:PORT' method.
>
> +* GDB and GDBserver now support local domain socket connections. The
> + name of a local domain socket may be provided instead of the
> + [ADDRESS]:PORT notation.
> +
> * DWARF index cache: GDB can now automatically save indices of DWARF
> symbols on disk to speed up further loading of the same binaries.
>
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index efd6dffb1e..44b319ebc7 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -21075,9 +21075,13 @@ syntax is:
> target> gdbserver @var{comm} @var{program} [ @var{args} @dots{} ]
> @end smallexample
>
> -@var{comm} is either a device name (to use a serial line), or a TCP
> +@var{comm} is either an existing device name (to use a serial line), or a TCP
> hostname and portnumber, or @code{-} or @code{stdio} to use
> stdin/stdout of @code{gdbserver}.
> +If @var{comm} is none of the above, then a local domain socket
> +will be created with that name.
> +@cindex local socket
> +@cindex Unix domain socket
> For example, to debug Emacs with the argument
> @samp{foo.txt} and communicate with @value{GDBN} over the serial port
> @file{/dev/com1}:
> @@ -21107,6 +21111,20 @@ conflicts with another service, @code{gdbserver} prints an error message
> and exits.} You must use the same port number with the host @value{GDBN}
> @code{target remote} command.
>
> +If the target and local machine are one and the same, then you can
> +use local domain socket instead of a TCP connection:
> +
> +@smallexample
> +target> gdbserver /tmp/local-socket emacs foo.txt
> +@end smallexample
> +
> +A local domain socket called @file{/tmp/local-socket} will be created
> +in the filesystem.
> +If there is already a local domain socket with this name it will be removed.
> +@smallexample
> +(gdb) target remote /tmp/local-socket
> +@end smallexample
> +
> The @code{stdio} connection is useful when starting @code{gdbserver}
> with ssh:
>
> @@ -21155,10 +21173,10 @@ In case more than one copy of @var{program} is running, or @var{program}
> has multiple threads, most versions of @code{pidof} support the
> @code{-s} option to only return the first process ID.
>
> -@subsubsection TCP port allocation lifecycle of @code{gdbserver}
> +@subsubsection Socket lifecycle of @code{gdbserver}
>
> This section applies only when @code{gdbserver} is run to listen on a TCP
> -port.
> +port or a unix domain socket.
>
> @code{gdbserver} normally terminates after all of its debugged processes have
> terminated in @kbd{target remote} mode. On the other hand, for @kbd{target
> @@ -21174,7 +21192,7 @@ Such reconnecting is useful for features like @ref{disconnected tracing}. For
> completeness, at most one @value{GDBN} can be connected at a time.
>
> @cindex @option{--once}, @code{gdbserver} option
> -By default, @code{gdbserver} keeps the listening TCP port open, so that
> +By default, @code{gdbserver} keeps the listening socket open, so that
> subsequent connections are possible. However, if you start @code{gdbserver}
> with the @option{--once} option, it will stop listening for any further
> connection attempts after connecting to the first @value{GDBN} session. This
> diff --git a/gdb/gdbserver/configure.ac b/gdb/gdbserver/configure.ac
> index fa3ca53efd..de7e7d4103 100644
> --- a/gdb/gdbserver/configure.ac
> +++ b/gdb/gdbserver/configure.ac
> @@ -98,7 +98,7 @@ ACX_CONFIGURE_DIR(["../../libiberty"], ["build-libiberty-gdbserver"])
> AC_CHECK_HEADERS(termios.h sys/reg.h string.h dnl
> proc_service.h sys/procfs.h linux/elf.h dnl
> fcntl.h signal.h sys/file.h dnl
> - sys/ioctl.h netinet/in.h sys/socket.h netdb.h dnl
> + sys/ioctl.h netinet/in.h sys/socket.h sys/un.h netdb.h dnl
> netinet/tcp.h arpa/inet.h)
> AC_FUNC_FORK
> AC_CHECK_FUNCS(getauxval pread pwrite pread64 setns)
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 9199a9c7ad..00e3340156 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -38,6 +38,9 @@
> #if HAVE_NETINET_IN_H
> #include <netinet/in.h>
> #endif
> +#if HAVE_SYS_UN_H
> +#include <sys/un.h>
> +#endif
> #if HAVE_SYS_SOCKET_H
> #include <sys/socket.h>
> #endif
> @@ -193,20 +196,35 @@ handle_accept_event (int err, gdb_client_data client_data)
> descriptor open for add_file_handler to wait for a new connection. */
> delete_file_handler (listen_desc);
>
> - /* Convert IP address to string. */
> - char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
> -
> - int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> - orig_host, sizeof (orig_host),
> - orig_port, sizeof (orig_port),
> - NI_NUMERICHOST | NI_NUMERICSERV);
> + if (sockaddr.ss_family == AF_UNIX)
> + {
> + struct sockaddr_un su;
> + socklen_t len;
> + if (0 != getsockname (listen_desc, (struct sockaddr *) &su, &len))
We don't use that "constant on lhs style". Put the 0 != on the rhs.
> + {
> + perror (_("Could not obtain remote address"));
> + }
Remove unnecessary curly braces.
>
> - if (r != 0)
> - fprintf (stderr, _("Could not obtain remote address: %s\n"),
> - gai_strerror (r));
> + fprintf (stderr, _("Remote debugging on local socket bound to %s\n"),
> + su.sun_path);
> + }
> else
> - fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
> - orig_host, orig_port);
> + {
> + /* Convert IP address to string. */
> + char orig_host[GDB_NI_MAX_ADDR], orig_port[GDB_NI_MAX_PORT];
> +
> + int r = getnameinfo ((struct sockaddr *) &sockaddr, len,
> + orig_host, sizeof (orig_host),
> + orig_port, sizeof (orig_port),
> + NI_NUMERICHOST | NI_NUMERICSERV);
> +
> + if (r != 0)
> + fprintf (stderr, _("Could not obtain remote address: %s\n"),
> + gai_strerror (r));
> + else
> + fprintf (stderr, _("Remote debugging from host %s, port %s\n"),
> + orig_host, orig_port);
> + }
>
> enable_async_notification (remote_desc);
>
> @@ -250,6 +268,9 @@ remote_prepare (const char *name)
> struct addrinfo hint;
> struct addrinfo *ainfo;
>
> + struct sockaddr *addr;
> + socklen_t addrlen;
> +
> memset (&hint, 0, sizeof (hint));
> /* Assume no prefix will be passed, therefore we should use
> AF_UNSPEC. */
> @@ -260,10 +281,17 @@ remote_prepare (const char *name)
> parsed_connection_spec parsed
> = parse_connection_spec_without_prefix (name, &hint);
>
> + struct stat statbuf;
> + int stat_result = stat (name, &statbuf);
> +
> if (parsed.port_str.empty ())
> {
> - cs.transport_is_reliable = 0;
> - return;
> + if (stat_result == 0
> + && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
> + {
> + cs.transport_is_reliable = 0;
> + return;
> + }
> }
>
> #ifdef USE_WIN32API
> @@ -276,47 +304,82 @@ remote_prepare (const char *name)
> }
> #endif
>
> - int r = getaddrinfo (parsed.host_str.c_str (), parsed.port_str.c_str (),
> - &hint, &ainfo);
> + struct sockaddr_un unix_addr;
>
> - if (r != 0)
> - error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
> +#ifndef UNIX_PATH_MAX
> +#define UNIX_PATH_MAX sizeof(((struct sockaddr_un *) NULL)->sun_path)
> +#endif
>
> - scoped_free_addrinfo freeaddrinfo (ainfo);
> + if (parsed.port_str.empty ())
> + {
> + if (strlen (name) > UNIX_PATH_MAX - 1)
> + {
> + error
> + (_("%s is too long. Socket names may be no longer than %s bytes."),
> + name, pulongest (UNIX_PATH_MAX - 1));
> + return;
> + }
> + listen_desc = socket (AF_UNIX, SOCK_STREAM, 0);
> + if (listen_desc < 0)
> + perror_with_name ("Can't open socket");
> +
> + memset (&unix_addr, 0, sizeof (unix_addr));
> + unix_addr.sun_family = AF_UNIX;
> + strncpy (unix_addr.sun_path, parsed.host_str.c_str(), UNIX_PATH_MAX - 1);
> + if (stat_result == 0
> + && (S_IFSOCK & statbuf.st_mode))
> + unlink (name);
> +
> + addr = (struct sockaddr *) &unix_addr;
> + addrlen = sizeof (unix_addr);
> + }
> + else
> + {
> + struct addrinfo *iter;
>
> - struct addrinfo *iter;
> + int r = getaddrinfo (parsed.host_str.c_str (),
> + parsed.port_str.c_str (),
> + &hint, &ainfo);
>
> - for (iter = ainfo; iter != NULL; iter = iter->ai_next)
> - {
> - listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
> - iter->ai_protocol);
> + if (r != 0)
> + error (_("%s: cannot resolve name: %s"), name, gai_strerror (r));
>
> - if (listen_desc >= 0)
> - break;
> - }
> + scoped_free_addrinfo freeaddrinfo (ainfo);
>
> - if (iter == NULL)
> - perror_with_name ("Can't open socket");
> + for (iter = ainfo; iter != NULL; iter = iter->ai_next)
> + {
> + listen_desc = gdb_socket_cloexec (iter->ai_family, iter->ai_socktype,
> + iter->ai_protocol);
>
> - /* Allow rapid reuse of this port. */
> - tmp = 1;
> - setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
> - sizeof (tmp));
> + if (listen_desc >= 0)
> + break;
> + }
>
> - switch (iter->ai_family)
> - {
> - case AF_INET:
> - ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
> - break;
> - case AF_INET6:
> - ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
> - break;
> - default:
> - internal_error (__FILE__, __LINE__,
> - _("Invalid 'ai_family' %d\n"), iter->ai_family);
> + if (iter == NULL)
> + perror_with_name ("Can't open socket");
> +
> + /* Allow rapid reuse of this port. */
> + tmp = 1;
> + setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
> + sizeof (tmp));
> +
> + switch (iter->ai_family)
> + {
> + case AF_INET:
> + ((struct sockaddr_in *) iter->ai_addr)->sin_addr.s_addr = INADDR_ANY;
> + break;
> + case AF_INET6:
> + ((struct sockaddr_in6 *) iter->ai_addr)->sin6_addr = in6addr_any;
> + break;
> + default:
> + internal_error (__FILE__, __LINE__,
> + _("Invalid 'ai_family' %d\n"), iter->ai_family);
> + }
> + addr = iter->ai_addr;
> + addrlen = iter->ai_addrlen;
> }
>
> - if (bind (listen_desc, iter->ai_addr, iter->ai_addrlen) != 0)
> + if (bind (listen_desc, addr, addrlen) != 0)
> perror_with_name ("Can't bind address");
>
> if (listen (listen_desc, 1) != 0)
> @@ -334,6 +397,9 @@ remote_open (const char *name)
> const char *port_str;
>
> port_str = strchr (name, ':');
> + struct stat statbuf;
> + int stat_result = stat (name, &statbuf);
> +
> #ifdef USE_WIN32API
> if (port_str == NULL)
> error ("Only HOST:PORT is supported on this platform.");
> @@ -353,12 +419,10 @@ remote_open (const char *name)
> add_file_handler (remote_desc, handle_serial_event, NULL);
> }
> #ifndef USE_WIN32API
> - else if (port_str == NULL)
> + else if (port_str == NULL && stat_result == 0
> + && 0 == (S_IFSOCK & statbuf.st_mode))
Constant on rhs.
> {
> - struct stat statbuf;
> -
> - if (stat (name, &statbuf) == 0
> - && (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode)))
> + if (S_ISCHR (statbuf.st_mode) || S_ISFIFO (statbuf.st_mode))
> remote_desc = open (name, O_RDWR);
> else
> {
>
Thanks,
Pedro Alves
next prev parent reply other threads:[~2018-10-09 18:02 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-10-09 17:33 Gdbserver can listen on local domain sockets John Darrington
2018-10-09 17:33 ` [PATCH] GDBSERVER: Listen on a unix domain (instead of TCP) socket if requested John Darrington
2018-10-09 17:56 ` Eli Zaretskii
2018-10-09 18:02 ` Pedro Alves [this message]
2018-10-09 18:41 ` John Darrington
2018-10-09 18:53 ` Pedro Alves
2018-10-09 19:00 ` John Darrington
2018-10-09 19:06 ` Pedro Alves
2018-10-13 17:58 ` [PATCH 1/4] " John Darrington
2018-10-13 17:58 ` [PATCH 2/4] GDB: Document the unix::/path/to/socket of remote connection John Darrington
2018-10-13 18:11 ` Eli Zaretskii
2018-10-15 9:31 ` Simon Tatham
2018-10-18 20:21 ` Sergio Durigan Junior
2018-10-13 17:58 ` [PATCH 4/4] GDB: Remote target can now accept the form unix::/path/to/socket John Darrington
2018-10-13 17:58 ` [PATCH 3/4] GDB: Fix documentation for invoking GDBSERVER John Darrington
2018-10-13 18:10 ` Eli Zaretskii
2018-10-18 20:27 ` Sergio Durigan Junior
2018-10-19 7:05 ` John Darrington
2018-10-19 20:45 ` Sergio Durigan Junior
2018-10-21 7:33 ` John Darrington
2018-10-21 16:47 ` Sergio Durigan Junior
2018-10-23 18:25 ` John Darrington
2018-10-23 18:58 ` Sergio Durigan Junior
2018-10-13 18:12 ` [PATCH 1/4] GDBSERVER: Listen on a unix domain (instead of TCP) socket if requested Eli Zaretskii
2018-10-18 20:18 ` Sergio Durigan Junior
2018-10-19 18:55 ` John Darrington
2018-10-19 19:43 ` Sergio Durigan Junior
2018-10-28 16:20 ` Simon Marchi
2018-10-28 18:10 ` John Darrington
2018-10-28 18:51 ` Simon Marchi
2018-10-29 8:24 ` John Darrington
2018-10-29 9:13 ` Rainer Orth
2018-10-29 9:38 ` Rainer Orth
[not found] ` <4da7206f-6e6a-7aad-634e-a4485d99e988@ericsson.com>
[not found] ` <20181029162513.oztznqp74gudrqgm@jocasta.intra>
2018-10-29 16:42 ` Sergio Durigan Junior
2018-10-29 17:34 ` John Darrington
2018-10-31 13:54 ` Simon Marchi
2018-10-29 18:32 ` Simon Marchi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=cc6ec9f9-20c0-ee07-7047-acd1801a2b1a@redhat.com \
--to=palves@redhat.com \
--cc=gdb-patches@sourceware.org \
--cc=john@darrington.wattle.id.au \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox