================ @@ -0,0 +1,267 @@ +//===------- cc1modbuildd_main.cpp - Clang CC1 Module Build Daemon --------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/ModuleBuildDaemon/SocketMsgSupport.h" +#include "clang/Tooling/ModuleBuildDaemon/SocketSupport.h" +#include "clang/Tooling/ModuleBuildDaemon/Utils.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/ThreadPool.h" +#include "llvm/Support/Threading.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" + +// TODO: Make portable +#if LLVM_ON_UNIX + +#include <errno.h> +#include <fstream> +#include <mutex> +#include <optional> +#include <signal.h> +#include <sstream> +#include <stdbool.h> +#include <string> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <type_traits> +#include <unistd.h> +#include <unordered_map> + +using namespace llvm; +using namespace clang; +using namespace cc1modbuildd; + +// Create unbuffered STDOUT stream so that any logging done by module build +// daemon can be viewed without having to terminate the process +static raw_fd_ostream &unbuff_outs() { + static raw_fd_ostream S(STDOUT_FILENO, false, true); + return S; +} + +namespace { + +class ModuleBuildDaemonServer { +public: + SmallString<128> BasePath; + SmallString<128> SocketPath; + SmallString<128> PidPath; + + ModuleBuildDaemonServer(SmallString<128> Path, ArrayRef<const char *> Argv) + : BasePath(Path), SocketPath(Path) { + llvm::sys::path::append(SocketPath, SOCKET_FILE_NAME); + } + + ~ModuleBuildDaemonServer() { shutdownDaemon(SIGTERM); } + + int forkDaemon(); + int launchDaemon(); + int listenForClients(); + + static void handleClient(int Client); + + void shutdownDaemon(int signal) { + unlink(SocketPath.c_str()); + shutdown(ListenSocketFD, SHUT_RD); + close(ListenSocketFD); + exit(EXIT_SUCCESS); + } + +private: + // Initializes and returns DiagnosticsEngine + pid_t Pid = -1; + int ListenSocketFD = -1; +}; + +// Required to handle SIGTERM by calling Shutdown +ModuleBuildDaemonServer *DaemonPtr = nullptr; +void handleSignal(int Signal) { + if (DaemonPtr != nullptr) { + DaemonPtr->shutdownDaemon(Signal); + } +} +} // namespace + +static bool verbose = false; +static void verbose_print(const llvm::Twine &message) { + if (verbose) { + unbuff_outs() << message << '\n'; + } +} + +// Forks and detaches process, creating module build daemon +int ModuleBuildDaemonServer::forkDaemon() { + + pid_t pid = fork(); + + if (pid < 0) { + exit(EXIT_FAILURE); + } + if (pid > 0) { + exit(EXIT_SUCCESS); + } + + Pid = getpid(); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + SmallString<128> STDOUT = BasePath; + llvm::sys::path::append(STDOUT, STDOUT_FILE_NAME); + freopen(STDOUT.c_str(), "a", stdout); + + SmallString<128> STDERR = BasePath; + llvm::sys::path::append(STDERR, STDERR_FILE_NAME); + freopen(STDERR.c_str(), "a", stderr); + + if (signal(SIGTERM, handleSignal) == SIG_ERR) { + errs() << "failed to handle SIGTERM" << '\n'; + exit(EXIT_FAILURE); + } + if (signal(SIGHUP, SIG_IGN) == SIG_ERR) { + errs() << "failed to ignore SIGHUP" << '\n'; + exit(EXIT_FAILURE); + } + if (setsid() == -1) { + errs() << "setsid failed" << '\n'; + exit(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} + +// Creates unix socket for IPC with module build daemon +int ModuleBuildDaemonServer::launchDaemon() { + + // new socket + if ((ListenSocketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + std::perror("Socket create error: "); + exit(EXIT_FAILURE); + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, SocketPath.c_str(), sizeof(addr.sun_path) - 1); + + // bind to local address + if (bind(ListenSocketFD, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + + // If the socket address is already in use, exit because another module + // build daemon has successfully launched. When translation units are + // compiled in parallel, until the socket file is created, all clang + // invocations will spawn a module build daemon. + if (errno == EADDRINUSE) { + close(ListenSocketFD); + exit(EXIT_SUCCESS); + } + std::perror("Socket bind error: "); + exit(EXIT_FAILURE); + } + verbose_print("mbd created and binded to socket address at: " + SocketPath); + + // set socket to accept incoming connection request + unsigned MaxBacklog = llvm::hardware_concurrency().compute_thread_count(); + if (listen(ListenSocketFD, MaxBacklog) == -1) { + std::perror("Socket listen error: "); + exit(EXIT_FAILURE); + } + + return 0; +} + +// Function submitted to thread pool with each client connection. Not +// responsible for closing client connections +void ModuleBuildDaemonServer::handleClient(int Client) { + + // Read handshake from client + Expected<HandshakeMsg> MaybeHandshakeMsg = + readSocketMsgFromSocket<HandshakeMsg>(Client); + + if (!MaybeHandshakeMsg) { + writeError(MaybeHandshakeMsg.takeError(), + "Failed to read handshake message from socket: "); + return; + } + + // Handle HANDSHAKE + HandshakeMsg Msg(ActionType::HANDSHAKE, StatusType::SUCCESS); + llvm::Error WriteErr = writeSocketMsgToSocket(Msg, Client); + + if (WriteErr) { + writeError(std::move(WriteErr), + "Failed to notify client that handshake was received"); + return; + } + return; +} ---------------- Bigcheese wrote:
This needs to handle closing the connection. May want to use something like `ScopedHandle` to automatically close on return. https://github.com/llvm/llvm-project/pull/67562 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits