diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2025-10-08 00:58:42 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2025-10-19 00:13:47 +0200 |
| commit | 86ec0b5386fc2078891a829026844d2ec21ea7db (patch) | |
| tree | 5f3ed650bc2957e06fd3c8c1ecfa7c6e7fc825b6 /src/main.cc | |
| parent | 3a002fb9c23fc9a6384bc1b30a8e364924bb574e (diff) | |
Add http module and implement basic http server
Diffstat (limited to 'src/main.cc')
| -rw-r--r-- | src/main.cc | 98 |
1 files changed, 97 insertions, 1 deletions
diff --git a/src/main.cc b/src/main.cc index e66f95a..54f3d0a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,16 +1,91 @@ #include "args.hh" +#include "cfg.hh" #include "config.h" +#include "http.hh" +#include "logger.hh" +#include "looper.hh" +#include <cerrno> +#include <cstring> +#include <format> #include <iostream> +#include <memory> +#include <unistd.h> +#include <utility> +#include <vector> #ifndef VERSION # define VERSION "unknown" #endif +namespace { + +class HttpServerDelegate : public http::Server::Delegate { + public: + HttpServerDelegate() = default; + + std::unique_ptr<http::Response> handle( + http::Request const& request) override { + if (request.method() != "GET") { + return http::Response::status(http::StatusCode::kMethodNotAllowed); + } + + if (request.path() == "/api/v1/status") { + return http::Response::content( + R"({ status: "OK" })", + *http::MimeType::create("application", "json")); + } + + return http::Response::status(http::StatusCode::kNotFound); + } +}; + +bool run(logger::Logger& logger, cfg::Config const& cfg, + std::unique_ptr<http::OpenPort> port) { + auto looper = looper::create(); + HttpServerDelegate delegate; + auto server = + http::create_server(logger, cfg, *looper, std::move(port), delegate); + return looper->run(logger); +} + +bool start_daemon(logger::Logger& logger, cfg::Config const& cfg, + std::unique_ptr<http::OpenPort> port, bool verbose) { + auto pid = fork(); + if (pid == 0) { + // Child process + // Move out of current workind directory to not keep it alive + chdir("/"); + // Create a new process group + setpgid(0, 0); + // Close stdin, stdout and stderr + close(0); + close(1); + close(2); + // Use syslog logger + auto logger2 = logger::syslog("bluetooth-jukebox", verbose); + _exit(run(*logger2, cfg, std::move(port)) ? 0 : 1); + } else if (pid == -1) { + // Error + logger.err(std::format("Unable to fork: {}", strerror(errno))); + return false; + } else { + // Parent process + logger.info(std::format("Forked into pid {}", pid)); + return true; + } +} + +} // namespace + int main(int argc, char** argv) { auto args = Args::create(); auto opt_help = args->option('h', "help", "display this text and exit."); auto opt_version = args->option('V', "version", "display version and exit."); + auto opt_config = args->option_argument( + 'C', "config", "FILE", "load config from FILE instead of default."); + auto opt_verbose = args->option('v', "verbose", "be more verbose."); + auto opt_daemon = args->option('d', "daemon", "fork into background."); if (!args->run(argc, argv)) { args->print_error(std::cerr); std::cerr << "Try 'bluetooth-jukebox --help' for more information.\n"; @@ -27,5 +102,26 @@ int main(int argc, char** argv) { << " written by Joel Klinghed <the_jk@spawned.biz>.\n"; return 0; } - return 0; + bool verbose = opt_verbose->is_set(); + bool daemon = opt_daemon->is_set(); + std::unique_ptr<cfg::Config> cfg; + std::vector<std::string> errors; + if (opt_config->is_set()) { + cfg = cfg::load_one(opt_config->argument(), errors); + } else { + cfg = cfg::load_all("bluetooth-jukebox", errors); + } + + auto logger = logger::stderr(verbose); + + // Open the port here, in case we fork() later, to fail early. + auto port = http::open_port(cfg->get("http.bind").value_or("localhost:5555"), + *logger); + if (!port) + return 1; + + if (daemon) + return start_daemon(*logger, *cfg, std::move(port), verbose) ? 0 : 1; + + return run(*logger, *cfg, std::move(port)) ? 0 : 1; } |
