From 179dfe521b4bd5475270769b349c00e91f3efc4e Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Sat, 18 Mar 2023 22:05:47 -0700 Subject: [PATCH] util: Make m5term able to connect to unix domain sockets. To connect to a unix domain socket, it must start with a non-digit character to avoid being confused with a TCP port. If it starts with an "@" character, then it is treated as an abstract socket. Change-Id: I3a71eb8ef80018546f3bbf9d781770bd37ecec09 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69167 Reviewed-by: Jui-min Lee Maintainer: Gabe Black Tested-by: kokoro --- util/term/term.c | 170 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 31 deletions(-) diff --git a/util/term/term.c b/util/term/term.c index 529712c870..cf3fdda9b2 100644 --- a/util/term/term.c +++ b/util/term/term.c @@ -27,26 +27,30 @@ */ #include -#include -#include -#include -#include -#include -#include +#include #include #include #include +#include +#include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include ssize_t atomicio(ssize_t (*)(), int, void *, size_t); void readwrite(int); -int remote_connect(char *, char *, struct addrinfo); +int remote_connect_inet(char *, char *); +int remote_connect_unix(const char *); struct termios saved_ios; void raw_term(); @@ -60,7 +64,6 @@ main(int argc, char *argv[]) { int ch, s, ret; char *host, *port, *endp; - struct addrinfo hints; socklen_t len; ret = 1; @@ -87,33 +90,38 @@ main(int argc, char *argv[]) raw_term(); + if (strcmp(host, "--unix") == 0) { + s = remote_connect_unix(port); + } else { + s = remote_connect_inet(host, port); + } + + if (s != -1) { + readwrite(s); + close(s); + } + + exit(0); +} + +/* + * remote_connect_inet() + * Return's a socket connected to a remote host. Properly bind's to a local + * port or source address if needed. Return's -1 on failure. + */ +int +remote_connect_inet(char *host, char *port) +{ + struct addrinfo hints; + struct addrinfo *res, *res0; + int s, error; + /* Initialize addrinfo structure */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - s = remote_connect(host, port, hints); - ret = 0; - readwrite(s); - - if (s) - close(s); - - exit(ret); -} - -/* - * remote_connect() - * Return's a socket connected to a remote host. Properly bind's to a local - * port or source address if needed. Return's -1 on failure. - */ -int -remote_connect(char *host, char *port, struct addrinfo hints) -{ - struct addrinfo *res, *res0; - int s, error; - if ((error = getaddrinfo(host, port, &hints, &res))) errx(1, "getaddrinfo: %s", gai_strerror(error)); @@ -135,6 +143,104 @@ remote_connect(char *host, char *port, struct addrinfo hints) return (s); } +/* + * remote_connect_inet() + * Return's a socket connected to a remote host. Properly bind's to a local + * port or source address if needed. Return's -1 on failure. + */ +int +remote_connect_unix(const char *cpath) +{ + struct sockaddr_un addr; + + // Create a copy of path so we can safely modify it in place. + char *path = strdup(cpath); + char *const path_buf = path; + + // Create a unix domain socket. + int s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == -1) + return s; + + // Prepare the scokaddr_un. + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + // Keep track of where we're filling in the path, and the remaining space. + int path_size = sizeof(addr.sun_path); + char *sun_path = &addr.sun_path[0]; + + // Keep track of the current directory in case we change it to maximize + // what we can fit in the limited space in sun_path. + char *cwd = NULL; + + if (path[0] == '@') { + // If this is an abstract socket, prefix it with a null byte. + *sun_path++ = '\0'; + path++; + path_size--; + // Keep track of how much of sun_path is actual data since everything + // we include will be part of the lookup. + int len = strlen(path); + if (len < path_size) { + fprintf(stderr, + "warning: Truncated abstract socket from %d to %d bytes.\n", + len, path_size); + path_size = len; + } + } else { + // Switch to the parent directory of the socket. + cwd = (char *)malloc(PATH_MAX); + if (!cwd) + errx(1, "Failed to allocate %d byte buffer.", PATH_MAX); + if (!getcwd(cwd, PATH_MAX)) { + perror("getcwd failed"); + exit(1); + } + char *dirc = strdup(path); + if (!dirc) { + perror("strdup failed"); + exit(1); + } + char *dname = dirname(dirc); + if (chdir(dname) != 0) { + perror("chdir to socket dir failed"); + exit(1); + } + free(dirc); + + // Replace the path with just the filename part. We still have a + // pointer to the cpath argument, so we can clean it up later. + path = basename(path); + } + + // Copy the path into sun_path. + strncpy(sun_path, path, path_size); + + // Figure out how much actual data we have in sockaddr_un. + int struct_len = (char *)sun_path + path_size - (char *)&addr; + + // Actually connect to the socket. + if (connect(s, (struct sockaddr *)&addr, struct_len) == -1) { + // If that didn't work, switch our dir back and error out. + if (cwd) + chdir(cwd); + errx(1, "Failed to connect"); + } + + // We're connected, clean up memory and switch the current dir back. + free(path_buf); + if (cwd) { + if (chdir(cwd) != 0) { + perror("chdir back failed:"); + exit(1); + } + } + + // Return the FD of our new connection. + return s; +} + /* * readwrite() * Loop that selects on the network file descriptor and stdin. @@ -165,7 +271,8 @@ readwrite(int nfd) n = select(max_fd, &read_fds, NULL, NULL, &timeout); if (n < 0) { close(nfd); - perror("Select Error:"); + perror("Select Error"); + exit(1); } if (n == 0) { @@ -221,7 +328,8 @@ readwrite(int nfd) void usage(int ret) { - fprintf(stderr, "usage: %s hostname port\n", progname); + fprintf(stderr, "usage: %s [hostname] port\n", progname); + fprintf(stderr, "usage: %s --unix socket\n", progname); if (ret) exit(1); }