From 7eff90acdcddb0288074815a3be689d2b111bf29 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Sat, 18 Mar 2023 16:37:23 -0700 Subject: [PATCH] base: Add support for unix domain sockets in ListenSocket. Change-Id: I6a5fa2cd3e4b567829203bf9d61ad2b55c259697 Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69164 Tested-by: kokoro Maintainer: Bobby Bruce Reviewed-by: Jui-min Lee --- src/base/socket.cc | 150 +++++++++++++++++++++++++++++++++++++++- src/base/socket.hh | 54 +++++++++++++++ src/base/socket.test.cc | 7 +- 3 files changed, 207 insertions(+), 4 deletions(-) diff --git a/src/base/socket.cc b/src/base/socket.cc index 13962d4b5c..2e9f815758 100644 --- a/src/base/socket.cc +++ b/src/base/socket.cc @@ -39,6 +39,7 @@ #include #include +#include #include "base/logging.hh" #include "base/output.hh" @@ -187,10 +188,10 @@ int ListenSocket::accept() { struct sockaddr_in sockaddr; - socklen_t slen = sizeof (sockaddr); + socklen_t slen = sizeof(sockaddr); int sfd = acceptCloexec(fd, (struct sockaddr *)&sockaddr, &slen); - if (sfd == -1) - return -1; + panic_if(sfd == -1, "%s: Failed to accept connection: %s", + name(), strerror(errno)); return sfd; } @@ -285,4 +286,147 @@ listenSocketInetConfig(int port) }); } +std::string +ListenSocketUnix::truncate(const std::string &original, size_t max_len) +{ + if (original.size() <= max_len) + return original; + + std::string truncated = original.substr(0, max_len); + warn("%s: Truncated \"%s\" to \"%s\"", name(), original, truncated); + return truncated; +} + +void +ListenSocketUnix::listen() +{ + panic_if(listening, "%s: Socket already listening!", name()); + + // only create socket if not already created by previous call + if (fd == -1) { + fd = socketCloexec(PF_UNIX, SOCK_STREAM, 0); + panic_if(fd < 0, "%s: Can't create unix socket:%s !", + name(), strerror(errno)); + } + + sockaddr_un serv_addr; + std::memset(&serv_addr, 0, sizeof(serv_addr)); + size_t addr_size = prepSockaddrUn(serv_addr); + + fatal_if(bind(fd, (struct sockaddr *)&(serv_addr), addr_size) != 0, + "%s: Cannot bind unix socket %s: %s", name(), *this, + strerror(errno)); + + fatal_if(::listen(fd, 1) == -1, "%s: Failed to listen on %s: %s\n", + name(), *this, strerror(errno)); + + ccprintf(std::cerr, "%s: Listening for connections on %s\n", + name(), *this); + + setListening(); +} + +ListenSocketUnixFile::ListenSocketUnixFile(const std::string &_name, + const std::string &_dir, const std::string &_fname) : + ListenSocketUnix(_name), dir(_dir), + fname(truncate(_fname, sizeof(sockaddr_un::sun_path) - 1)) +{ +} + +ListenSocketUnixFile::~ListenSocketUnixFile() +{ + if (fd != -1) { + close(fd); + fd = -1; + unlink(); + } +} + +bool +ListenSocketUnixFile::unlink() const +{ + auto path = resolvedDir + "/" + fname; + return ::unlink(path.c_str()) == 0; +} + +size_t +ListenSocketUnixFile::prepSockaddrUn(sockaddr_un &addr) const +{ + addr.sun_family = AF_UNIX; + std::memcpy(addr.sun_path, fname.c_str(), fname.size()); + return sizeof(addr.sun_path); +} + +void +ListenSocketUnixFile::listen() +{ + resolvedDir = simout.resolve(dir); + warn_if(unlink(), + "%s: server path %s was occupied and will be replaced. Please " + "make sure there is no other server using the same path.", + name(), resolvedDir + "/" + fname); + + // Make sure "dir" exists. + std::error_code ec; + std::filesystem::create_directory(resolvedDir, ec); + fatal_if(ec, "Failed to create directory %s", ec.message()); + + // Change the working directory to the directory containing the socket so + // that we maximize the limited space in sockaddr_un.sun_path. + auto cwd = std::filesystem::current_path(ec); + panic_if(ec, "Failed to get current working directory %s", ec.message()); + std::filesystem::current_path(resolvedDir, ec); + fatal_if(ec, "Failed to change to directory %s: %s", + resolvedDir, ec.message()); + + ListenSocketUnix::listen(); + + std::filesystem::current_path(cwd, ec); + panic_if(ec, "Failed to change back working directory %s", ec.message()); +} + +void +ListenSocketUnixFile::output(std::ostream &os) const +{ + os << "socket \"" << dir << "/" << fname << "\""; +} + +ListenSocketConfig +listenSocketUnixFileConfig(std::string dir, std::string fname) +{ + return ListenSocketConfig([dir, fname](const std::string &name) { + return std::make_unique(name, dir, fname); + }); +} + +size_t +ListenSocketUnixAbstract::prepSockaddrUn(sockaddr_un &addr) const +{ + addr.sun_family = AF_UNIX; + addr.sun_path[0] = '\0'; + std::memcpy(&addr.sun_path[1], path.c_str(), path.size()); + return offsetof(sockaddr_un, sun_path) + path.size() + 1; +} + +ListenSocketUnixAbstract::ListenSocketUnixAbstract( + const std::string &_name, const std::string &_path) : + ListenSocketUnix(_name), + path(truncate(_path, sizeof(sockaddr_un::sun_path) - 1)) +{ +} + +void +ListenSocketUnixAbstract::output(std::ostream &os) const +{ + os << "abstract socket \"" << path << "\""; +} + +ListenSocketConfig +listenSocketUnixAbstractConfig(std::string path) +{ + return ListenSocketConfig([path](const std::string &name) { + return std::make_unique(name, path); + }); +} + } // namespace gem5 diff --git a/src/base/socket.hh b/src/base/socket.hh index 761312b291..33c1c3a3cb 100644 --- a/src/base/socket.hh +++ b/src/base/socket.hh @@ -184,6 +184,60 @@ class ListenSocketInet : public ListenSocket ListenSocketConfig listenSocketInetConfig(int port); +// AF_UNIX based sockets. + +class ListenSocketUnix : public ListenSocket +{ + protected: + virtual size_t prepSockaddrUn(sockaddr_un &addr) const = 0; + + std::string truncate(const std::string &original, size_t max_len); + + ListenSocketUnix(const std::string &_name) : ListenSocket(_name) {} + + public: + void listen() override; +}; + +class ListenSocketUnixFile : public ListenSocketUnix +{ + protected: + std::string dir; + std::string resolvedDir; + std::string fname; + + bool unlink() const; + + size_t prepSockaddrUn(sockaddr_un &addr) const override; + + public: + ListenSocketUnixFile(const std::string &_name, const std::string &_dir, + const std::string &_fname); + ~ListenSocketUnixFile(); + + void listen() override; + void output(std::ostream &os) const override; +}; + +ListenSocketConfig listenSocketUnixFileConfig( + std::string dir, std::string fname); + +class ListenSocketUnixAbstract : public ListenSocketUnix +{ + protected: + std::string path; + + size_t prepSockaddrUn(sockaddr_un &addr) const override; + + public: + ListenSocketUnixAbstract( + const std::string &_name, const std::string &_path); + + void output(std::ostream &os) const override; +}; + +ListenSocketConfig listenSocketUnixAbstractConfig(std::string path); + } // namespace gem5 #endif //__SOCKET_HH__ diff --git a/src/base/socket.test.cc b/src/base/socket.test.cc index 0f0de54b6c..7bf9e180c7 100644 --- a/src/base/socket.test.cc +++ b/src/base/socket.test.cc @@ -207,5 +207,10 @@ TEST(SocketTest, RelistenWithDifferentInstanceOnSamePort) TEST(SocketTest, AcceptError) { MockListenSocket listen_socket(-1); - EXPECT_EQ(-1, listen_socket.accept()); + EXPECT_ANY_THROW(listen_socket.accept()); + std::string expected = + "panic: panic condition sfd == -1 occurred: mock: Failed to accept " + "connection: Bad file descriptor\n"; + std::string actual = gtestLogOutput.str(); + EXPECT_EQ(expected, actual); }