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: