diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2025-02-20 22:54:56 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2025-02-20 22:54:56 +0100 |
| commit | b4d6df902253637f24647d3db2bc3781d69eec1c (patch) | |
| tree | d8bf9ac04a270fabdfee1c15628c702471ef8bf5 /src/main.cc | |
| parent | 441cafc7124f633e5abc684e85a11ce3c991f6ae (diff) | |
Diffstat (limited to 'src/main.cc')
| -rw-r--r-- | src/main.cc | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..980adba --- /dev/null +++ b/src/main.cc @@ -0,0 +1,153 @@ +#include <cerrno> +#include <charconv> +#include <cstring> +#include <iostream> +#include <unistd.h> + +#include "args.hh" +#include "logger.hh" +#include "logger_file.hh" +#include "logger_syslog.hh" +#include "server.hh" + +namespace { + +struct Context { + std::shared_ptr<Logger> logger_; + bool verbose_{false}; + bool daemon_{false}; + std::string host_{"localhost"}; + uint16_t port_{12000}; +}; + +bool parse_args(int argc, char** argv, std::shared_ptr<Logger> logger, + Context& context, int& exit_code) { + auto args = Args::create(); + auto opt_help = args->add_option('h', "help", "display this text and exit"); + auto opt_version = args->add_option( + 'V', "version", "display version and exit"); + auto opt_verbose = args->add_option('v', "verbose", "be verbose"); + auto opt_daemon = args->add_option( + 'd', "daemon", "fork into the background as a daemon"); + auto opt_logfile = args->add_option_with_arg( + 'L', "log", "log to FILE instead of default (" + "syslog for daemon, stdout otherwise)", "FILE"); + auto opt_host = args->add_option_with_arg( + 'h', "host", "HOST to listen for requests on (default localhost)", + "HOST"); + auto opt_port = args->add_option_with_arg( + 'p', "port", "PORT to listen for requests on (default 12000)", + "PORT"); + std::vector<std::string> extra; + if (!args->run(argc, argv, "buildhelper", std::cerr, extra)) { + std::cerr << "Try `buildhelper --help` for usage." << std::endl; + exit_code = 1; + return false; + } + if (opt_help->is_set()) { + std::cout << "Usage `buildhelper [OPTIONS]`.\n" + << "Runs a buildhelper instance, implementing the Remote Execution API. Answers requests from bazel and others.\n" + << "\n" + << "Options:" << std::endl; + args->print_descriptions(std::cout, 80); + exit_code = 0; + return false; + } + if (opt_version->is_set()) { + std::cout << "buildhelper 0.1 written by Joel Klinghed <the_jk@spawned.biz>" + << std::endl; + exit_code = 0; + return false; + } + + context.verbose_ = opt_verbose->is_set(); + context.daemon_ = opt_daemon->is_set(); + + if (opt_host->is_set()) { + context.host_ = opt_host->arg(); + } + + if (opt_port->is_set()) { + auto const& arg = opt_port->arg(); + auto [ptr, err] = + std::from_chars(arg.data(), arg.data() + arg.size(), context.port_); + if (ptr != arg.data() + arg.size() || err != std::errc()) { + std::cerr << "Invalid port value: " << arg << std::endl; + exit_code = 1; + return false; + } + } + + if (opt_logfile->is_set()) { + if (opt_logfile->arg() == "-") { + if (context.daemon_) { + std::cerr << "Cannot log to stdout AND fork into background." + << std::endl; + exit_code = 1; + return false; + } + context.logger_ = logger; + } else { + context.logger_ = LoggerFile::create( + std::filesystem::path(opt_logfile->arg()), + logger.get()); + } + } else { + if (context.daemon_) { + context.logger_ = LoggerSyslog::create("buildhelper"); + } else { + context.logger_ = logger; + } + } + + return true; +} + +} // namespace + +int main(int argc, char** argv) { + std::shared_ptr<Logger> logger = Logger::create_stdio(); + Context context; + int exit_code = 0; + if (!parse_args(argc, argv, logger, context, exit_code)) { + return exit_code; + } + + auto server = Server::create(); + if (!server->setup(logger, context.host_, context.port_)) { + return 1; + } + + if (context.daemon_) { + auto pid = fork(); + if (pid == -1) { + logger->err("Unable to fork: %s", strerror(errno)); + return 1; + } + if (pid == 0) { + // Child process + // Close stdin, stdout and stderr. + close(0); + close(1); + close(2); + + // Chdir to root, avoid keeping started directory alive + chdir("/"); + + // Create new session + setsid(); + } else { + // Parent process + if (context.verbose_) + logger->info("Forked as %ld", static_cast<long>(pid)); + // Don't run atexit or anything like that, all is owned by child. + _exit(0); + } + } + + logger.reset(); + + if (server->run(context.logger_)) + return 0; + return 1; +} |
