#include "common.hh" #include "daemon.hh" #include "io.hh" #include "logger.hh" #include "logger_base.hh" #include "unique_pipe.hh" #include #include #include namespace { enum class Type { EXIT_GOOD, EXIT_BAD, LOG_ERR, LOG_WARN, LOG_INFO, LOG_DBG, }; struct MsgHeader { Type type_; size_t len_; MsgHeader() = default; MsgHeader(Type type, size_t len) : type_(type), len_(len) {} }; class DaemonWriter : public LoggerBase { public: explicit DaemonWriter(unique_fd fd) : fd_(std::move(fd)) {} void exit(bool success) { MsgHeader header(success ? Type::EXIT_GOOD : Type::EXIT_BAD, 0); if (!io::write_all(fd_.get(), &header, sizeof(header))) { abort(); } } protected: void msg(Level level, std::string_view str) override { Type type; switch (level) { case Level::ERR: type = Type::LOG_ERR; break; case Level::WARN: type = Type::LOG_WARN; break; case Level::INFO: type = Type::LOG_INFO; break; case Level::DBG: type = Type::LOG_DBG; break; } MsgHeader header(type, str.size()); if (!io::write_all(fd_.get(), &header, sizeof(header)) || !io::write_all(fd_.get(), str.data(), str.size())) { abort(); } } private: unique_fd fd_; }; class DaemonReader { public: explicit DaemonReader(unique_fd fd) : fd_(std::move(fd)) {} bool wait(Logger* logger) { MsgHeader header; std::string msg; while (true) { if (!io::read_all(fd_.get(), &header, sizeof(header))) { logger->err("Error reading pipe: %s", strerror(errno)); return false; } msg.resize(header.len_); if (!io::read_all(fd_.get(), msg.data(), header.len_)) { logger->err("Error reading pipe: %s", strerror(errno)); return false; } switch (header.type_) { case Type::EXIT_GOOD: return true; case Type::EXIT_BAD: return false; case Type::LOG_ERR: logger->err("%.*s", static_cast(msg.size()), msg.data()); break; case Type::LOG_WARN: logger->warn("%.*s", static_cast(msg.size()), msg.data()); break; case Type::LOG_INFO: logger->info("%.*s", static_cast(msg.size()), msg.data()); break; case Type::LOG_DBG: logger->dbg("%.*s", static_cast(msg.size()), msg.data()); break; } } } private: unique_fd fd_; }; } // namespace bool Daemon::run_in_foreground(Logger* logger, std::unique_ptr daemon) { return daemon->setup(logger) && daemon->run(); } bool Daemon::fork_in_background(Logger* logger, std::unique_ptr daemon) { unique_pipe pipe; if (!pipe) { logger->err("Unable to create pipe(): %s", strerror(errno)); return false; } auto pid = fork(); if (pid == -1) { logger->err("Failed to fork(): %s", strerror(errno)); return false; } if (pid == 0) { // Daemon process setpgrp(); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); { DaemonWriter writer(pipe.release_writer()); pipe.reset(); if (!daemon->setup(&writer)) { writer.exit(false); exit(EXIT_FAILURE); } writer.exit(true); } chdir("/"); exit(daemon->run() ? EXIT_SUCCESS : EXIT_FAILURE); } else { // Calling process DaemonReader reader(pipe.release_reader()); pipe.reset(); return reader.wait(logger); } }