summaryrefslogtreecommitdiff
path: root/src/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cc')
-rw-r--r--src/main.cc153
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;
+}