From e03d253310f1decab97361dd32cb09c286c83a06 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Fri, 24 Jun 2022 22:56:13 -0700 Subject: [PATCH] FEXServer: Adds -w option for waiting on current FEXServer It can be useful to know in tooling when the current active FEXServer has exited. Two things can happen when this command is run. No FEXServer is active, returns immediately. A FEXServer is active, we query for a pidfd from the active server, then we wait until it exits. Both instances of this is valid to use. --- Source/Common/FEXServerClient.cpp | 132 ++++++++++++---------- Source/Common/FEXServerClient.h | 11 ++ Source/Tools/FEXServer/ArgumentLoader.cpp | 9 ++ Source/Tools/FEXServer/ArgumentLoader.h | 1 + Source/Tools/FEXServer/Main.cpp | 24 ++++ Source/Tools/FEXServer/ProcessPipe.cpp | 125 ++++++++++++-------- 6 files changed, 195 insertions(+), 107 deletions(-) diff --git a/Source/Common/FEXServerClient.cpp b/Source/Common/FEXServerClient.cpp index a22dbdddc..7f8dbe538 100644 --- a/Source/Common/FEXServerClient.cpp +++ b/Source/Common/FEXServerClient.cpp @@ -21,6 +21,71 @@ #include namespace FEXServerClient { + int RequestPIDFDPacket(int ServerSocket, PacketType Type) { + FEXServerRequestPacket Req { + .Header { + .Type = Type, + }, + }; + + int Result = write(ServerSocket, &Req, sizeof(Req.BasicRequest)); + if (Result != -1) { + // Wait for success response with SCM_RIGHTS + + FEXServerResultPacket Res{}; + struct iovec iov { + .iov_base = &Res, + .iov_len = sizeof(Res), + }; + + struct msghdr msg { + .msg_name = nullptr, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + // Setup the ancillary buffer. This is where we will be getting pipe FDs + // We only need 4 bytes for the FD + constexpr size_t CMSG_SIZE = CMSG_SPACE(sizeof(int)); + union AncillaryBuffer { + struct cmsghdr Header; + uint8_t Buffer[CMSG_SIZE]; + }; + AncillaryBuffer AncBuf{}; + + // Now link to our ancilllary buffer + msg.msg_control = AncBuf.Buffer; + msg.msg_controllen = CMSG_SIZE; + + ssize_t DataResult = recvmsg(ServerSocket, &msg, 0); + if (DataResult > 0) { + // Now that we have the data, we can extract the FD from the ancillary buffer + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + + // Do some error checking + if (cmsg == nullptr || + cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + // Couldn't get a socket + } + else { + // Check for Success. + // If type error was returned then the FEXServer doesn't have a log to pipe in to + if (Res.Header.Type == PacketType::TYPE_SUCCESS) { + // Now that we know the cmsg is sane, read the FD + int NewFD{}; + memcpy(&NewFD, CMSG_DATA(cmsg), sizeof(NewFD)); + return NewFD; + } + } + } + } + + return -1; + } + static int ServerFD {-1}; std::string GetServerLockFolder() { @@ -163,68 +228,7 @@ namespace FEXServerClient { } int RequestLogFD(int ServerSocket) { - FEXServerRequestPacket Req { - .Header { - .Type = PacketType::TYPE_GET_LOG_FD, - }, - }; - - int Result = write(ServerSocket, &Req, sizeof(Req.BasicRequest)); - if (Result != -1) { - // Wait for success response with SCM_RIGHTS - - FEXServerResultPacket Res{}; - struct iovec iov { - .iov_base = &Res, - .iov_len = sizeof(Res), - }; - - struct msghdr msg { - .msg_name = nullptr, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - }; - - // Setup the ancillary buffer. This is where we will be getting pipe FDs - // We only need 4 bytes for the FD - constexpr size_t CMSG_SIZE = CMSG_SPACE(sizeof(int)); - union AncillaryBuffer { - struct cmsghdr Header; - uint8_t Buffer[CMSG_SIZE]; - }; - AncillaryBuffer AncBuf{}; - - // Now link to our ancilllary buffer - msg.msg_control = AncBuf.Buffer; - msg.msg_controllen = CMSG_SIZE; - - ssize_t DataResult = recvmsg(ServerSocket, &msg, 0); - if (DataResult > 0) { - // Now that we have the data, we can extract the FD from the ancillary buffer - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - - // Do some error checking - if (cmsg == nullptr || - cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) { - // Couldn't get a socket - } - else { - // Check for Success. - // If type error was returned then the FEXServer doesn't have a log to pipe in to - if (Res.Header.Type == PacketType::TYPE_SUCCESS) { - // Now that we know the cmsg is sane, read the FD - int NewFD{}; - memcpy(&NewFD, CMSG_DATA(cmsg), sizeof(NewFD)); - return NewFD; - } - } - } - } - - return -1; + return RequestPIDFDPacket(ServerSocket, PacketType::TYPE_GET_LOG_FD); } std::string RequestRootFSPath(int ServerSocket) { @@ -252,6 +256,10 @@ namespace FEXServerClient { return {}; } + int RequestPIDFD(int ServerSocket) { + return RequestPIDFDPacket(ServerSocket, PacketType::TYPE_GET_PID_FD); + } + /** @} */ /** diff --git a/Source/Common/FEXServerClient.h b/Source/Common/FEXServerClient.h index 3dbeda442..21a4d9d04 100644 --- a/Source/Common/FEXServerClient.h +++ b/Source/Common/FEXServerClient.h @@ -11,6 +11,7 @@ namespace FEXServerClient { TYPE_KILL, TYPE_GET_LOG_FD, TYPE_GET_ROOTFS_PATH, + TYPE_GET_PID_FD, // Result only TYPE_SUCCESS, @@ -88,6 +89,16 @@ namespace FEXServerClient { int RequestLogFD(int ServerSocket); std::string RequestRootFSPath(int ServerSocket); + + /** + * @brief Request a FEXServer to give us a pidfd of the process + * + * @param ServerSocket - Socket to the server + * + * @return FD for pidfd + */ + int RequestPIDFD(int ServerSocket); + /** @} */ /** diff --git a/Source/Tools/FEXServer/ArgumentLoader.cpp b/Source/Tools/FEXServer/ArgumentLoader.cpp index d7fac83aa..4c4ac3121 100644 --- a/Source/Tools/FEXServer/ArgumentLoader.cpp +++ b/Source/Tools/FEXServer/ArgumentLoader.cpp @@ -31,6 +31,11 @@ namespace FEXServer::Config { .metavar("n") .help("Make FEXServer persistent. Optional number of seconds"); + Parser.add_option("-w", "--wait") + .action("store_true") + .set_default(false) + .help("Wait for the FEXServer to shutdown"); + Parser.add_option("-v") .action("version") .help("Version string"); @@ -39,6 +44,10 @@ namespace FEXServer::Config { FEXOptions.Kill = Options.get("kill"); FEXOptions.Foreground = Options.get("foreground"); + FEXOptions.Wait = Options.get("wait"); + if (FEXOptions.Wait) { + FEXOptions.Foreground = true; + } FEXOptions.PersistentTimeout = Options.get("persistent"); return FEXOptions; diff --git a/Source/Tools/FEXServer/ArgumentLoader.h b/Source/Tools/FEXServer/ArgumentLoader.h index c9a9b9547..a47d8dd1d 100644 --- a/Source/Tools/FEXServer/ArgumentLoader.h +++ b/Source/Tools/FEXServer/ArgumentLoader.h @@ -5,6 +5,7 @@ namespace FEXServer::Config { struct FEXServerOptions { bool Kill; bool Foreground; + bool Wait; uint32_t PersistentTimeout; }; diff --git a/Source/Tools/FEXServer/Main.cpp b/Source/Tools/FEXServer/Main.cpp index 2e1e1be85..d3dc6e55b 100644 --- a/Source/Tools/FEXServer/Main.cpp +++ b/Source/Tools/FEXServer/Main.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -145,6 +146,29 @@ int main(int argc, char **argv, char **const envp) { // Reload the meta layer FEXCore::Config::ReloadMetaLayer(); + if (Options.Wait) { + int ServerPipe = FEXServerClient::ConnectToServer(); + if (ServerPipe != -1) { + int FEXServerPID = FEXServerClient::RequestPIDFD(ServerPipe); + close(ServerPipe); + if (FEXServerPID != -1) { + LogMan::Msg::IFmt("[FEXServer] Waiting for FEXServer to close"); + // We can't use waitid (P_PIDFD) here because the active FEXServer isn't a child of this process. + // Use poll instead which will return once the pidfd closes. + pollfd PollFD; + PollFD.fd = FEXServerPID; + PollFD.events = POLLIN | POLLOUT | POLLRDHUP | POLLERR | POLLHUP | POLLNVAL; + + // Wait for a result on the pipe that isn't EINTR + while (poll(&PollFD, 1, -1) == -1 && errno == EINTR); + + LogMan::Msg::IFmt("[FEXServer] FEXServer shutdown"); + } + PipeScanner::ClosePipes(); + } + return 0; + } + if (Options.Kill) { int ServerPipe = FEXServerClient::ConnectToServer(); diff --git a/Source/Tools/FEXServer/ProcessPipe.cpp b/Source/Tools/FEXServer/ProcessPipe.cpp index 3cf9bfd9d..5b678bc9d 100644 --- a/Source/Tools/FEXServer/ProcessPipe.cpp +++ b/Source/Tools/FEXServer/ProcessPipe.cpp @@ -215,6 +215,72 @@ namespace ProcessPipe { return true; } + void SendEmptyErrorPacket(int Socket) { + FEXServerClient::FEXServerResultPacket Res { + .Header { + .Type = FEXServerClient::PacketType::TYPE_ERROR, + }, + }; + + struct iovec iov { + .iov_base = &Res, + .iov_len = sizeof(Res), + }; + + struct msghdr msg { + .msg_name = nullptr, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + sendmsg(Socket, &msg, 0); + } + + void SendFDSuccessPacket(int Socket, int FD) { + FEXServerClient::FEXServerResultPacket Res { + .Header { + .Type = FEXServerClient::PacketType::TYPE_SUCCESS, + }, + }; + + struct iovec iov { + .iov_base = &Res, + .iov_len = sizeof(Res), + }; + + struct msghdr msg { + .msg_name = nullptr, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + // Setup the ancillary buffer. This is where we will be getting pipe FDs + // We only need 4 bytes for the FD + constexpr size_t CMSG_SIZE = CMSG_SPACE(sizeof(int)); + union AncillaryBuffer { + struct cmsghdr Header; + uint8_t Buffer[CMSG_SIZE]; + }; + AncillaryBuffer AncBuf{}; + + // Now link to our ancilllary buffer + msg.msg_control = AncBuf.Buffer; + msg.msg_controllen = CMSG_SIZE; + + // Now we need to setup the ancillary buffer data. We are only sending an FD + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + // We are giving the daemon the write side of the pipe + memcpy(CMSG_DATA(cmsg), &FD, sizeof(int)); + + sendmsg(Socket, &msg, 0); + } + void HandleSocketData(int Socket) { std::vector Data(1500); size_t CurrentRead{}; @@ -266,54 +332,14 @@ namespace ProcessPipe { CurrentOffset += sizeof(FEXServerClient::FEXServerRequestPacket::BasicRequest); break; case FEXServerClient::PacketType::TYPE_GET_LOG_FD: { - FEXServerClient::FEXServerResultPacket Res { - .Header { - .Type = FEXServerClient::PacketType::TYPE_SUCCESS, - }, - }; - - struct iovec iov { - .iov_base = &Res, - .iov_len = sizeof(Res), - }; - - struct msghdr msg { - .msg_name = nullptr, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - }; - if (Logger::LogThreadRunning()) { - // Setup the ancillary buffer. This is where we will be getting pipe FDs - // We only need 4 bytes for the FD - constexpr size_t CMSG_SIZE = CMSG_SPACE(sizeof(int)); - union AncillaryBuffer { - struct cmsghdr Header; - uint8_t Buffer[CMSG_SIZE]; - }; - AncillaryBuffer AncBuf{}; - - // Now link to our ancilllary buffer - msg.msg_control = AncBuf.Buffer; - msg.msg_controllen = CMSG_SIZE; - - // Now we need to setup the ancillary buffer data. We are only sending an FD - struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - int fds[2]{}; pipe2(fds, 0); // 0 = Read // 1 = Write Logger::AppendLogFD(fds[0]); - // We are giving the daemon the write side of the pipe - memcpy(CMSG_DATA(cmsg), &fds[1], sizeof(int)); - - sendmsg(Socket, &msg, 0); + SendFDSuccessPacket(Socket, fds[1]); // Close the write side now, doesn't matter to us close(fds[1]); @@ -324,14 +350,12 @@ namespace ProcessPipe { } else { // Log thread isn't running. Let FEXInterpreter know it can't have one. - Res.Header.Type = FEXServerClient::PacketType::TYPE_ERROR; - - sendmsg(Socket, &msg, 0); + SendEmptyErrorPacket(Socket); } CurrentOffset += sizeof(FEXServerClient::FEXServerRequestPacket::Header); break; - } + } case FEXServerClient::PacketType::TYPE_GET_ROOTFS_PATH: { std::string MountFolder = SquashFS::GetMountFolder(); @@ -372,6 +396,17 @@ namespace ProcessPipe { CurrentOffset += sizeof(FEXServerClient::FEXServerRequestPacket::BasicRequest); break; + } + case FEXServerClient::PacketType::TYPE_GET_PID_FD: { + int FD = ::syscall(SYS_pidfd_open, ::getpid(), 0); + + SendFDSuccessPacket(Socket, FD); + + // Close the FD now since we've sent it + close(FD); + + CurrentOffset += sizeof(FEXServerClient::FEXServerRequestPacket::Header); + break; } // Invalid case FEXServerClient::PacketType::TYPE_ERROR: