#include "database_service.hpp" #include #include #include #include #include #include #ifdef HAVE_SYSTEMD #include #endif namespace { smartbotic::database::DatabaseService* g_service = nullptr; void signalHandler(int signal) { spdlog::info("Received signal {}", signal); if (g_service) { g_service->signalStop(); } } void printUsage(const char* programName) { std::cout << "Usage: " << programName << " [OPTIONS]\n" << "\n" << "Options:\n" << " --config, -c PATH Configuration file path\n" << " --help, -h Show this help message\n" << " --version, -v Show version information\n" << "\n" << "Environment variables:\n" << " DATABASE_NODE_ID Override node ID from config\n" << " DATABASE_MASTER_KEY Encryption master key (hex-encoded)\n" << std::endl; } void printVersion() { std::cout << "smartbotic-database version 1.0.0\n" << "Smartbotic Database Service\n" << std::endl; } void initLogging(const std::string& serviceName) { try { // Create console sink auto console_sink = std::make_shared(); console_sink->set_level(spdlog::level::debug); // Create default logger with console sink auto logger = std::make_shared(serviceName, console_sink); logger->set_level(spdlog::level::info); // Check environment for log level if (const char* logLevel = std::getenv("LOG_LEVEL")) { std::string level(logLevel); if (level == "trace") logger->set_level(spdlog::level::trace); else if (level == "debug") logger->set_level(spdlog::level::debug); else if (level == "info") logger->set_level(spdlog::level::info); else if (level == "warn") logger->set_level(spdlog::level::warn); else if (level == "error") logger->set_level(spdlog::level::err); } spdlog::set_default_logger(logger); spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%n] %v"); } catch (const spdlog::spdlog_ex& ex) { std::cerr << "Log initialization failed: " << ex.what() << std::endl; } } std::filesystem::path findConfigFile(int argc, char* argv[]) { // Check command line arguments for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if ((arg == "--config" || arg == "-c") && i + 1 < argc) { return argv[i + 1]; } } // Check environment variable if (const char* configPath = std::getenv("DATABASE_CONFIG")) { return configPath; } // Default paths std::vector defaultPaths = { "./config/database.json", "./config/storage.json", // Backward compatibility "/etc/smartbotic/database.json", "/etc/callerai/storage.json", // Backward compatibility std::filesystem::path(std::getenv("HOME") ? std::getenv("HOME") : "") / ".config/smartbotic/database.json" }; for (const auto& path : defaultPaths) { if (std::filesystem::exists(path)) { return path; } } return "./config/database.json"; } } // anonymous namespace int main(int argc, char* argv[]) { // Parse command line arguments for (int i = 1; i < argc; ++i) { std::string arg = argv[i]; if (arg == "--help" || arg == "-h") { printUsage(argv[0]); return 0; } if (arg == "--version" || arg == "-v") { printVersion(); return 0; } } try { // Initialize logging initLogging("database"); // Find configuration file auto configPath = findConfigFile(argc, argv); spdlog::info("Loading configuration from {}", configPath.string()); // Load configuration smartbotic::database::DatabaseService::Config config; if (std::filesystem::exists(configPath)) { config = smartbotic::database::DatabaseService::loadConfig(configPath); } else { spdlog::warn("Configuration file not found, using defaults"); // Set default data directory const char* home = std::getenv("HOME"); if (home) { config.dataDirectory = std::filesystem::path(home) / ".local/share/smartbotic/database"; config.keyFilePath = std::filesystem::path(home) / ".config/smartbotic/database.key"; } else { config.dataDirectory = "/var/lib/smartbotic/database"; config.keyFilePath = "/etc/smartbotic/database.key"; } } // Override node ID from environment if set if (const char* nodeId = std::getenv("DATABASE_NODE_ID")) { config.nodeId = nodeId; } // Create service smartbotic::database::DatabaseService service(config); g_service = &service; // Set up signal handlers std::signal(SIGINT, signalHandler); std::signal(SIGTERM, signalHandler); // Initialize service if (!service.initialize()) { spdlog::error("Failed to initialize database service"); return 1; } // Start service service.start(); #ifdef HAVE_SYSTEMD // Notify systemd we're ready sd_notify(0, "READY=1"); spdlog::info("Notified systemd: READY"); #endif // Wait for shutdown int watchdogCounter = 0; while (service.isRunning()) { std::this_thread::sleep_for(std::chrono::seconds(1)); #ifdef HAVE_SYSTEMD // Send watchdog notification every 30 seconds (half of WatchdogSec=60) if (++watchdogCounter >= 30) { sd_notify(0, "WATCHDOG=1"); watchdogCounter = 0; } #endif } #ifdef HAVE_SYSTEMD sd_notify(0, "STOPPING=1"); #endif // Stop service service.stop(); g_service = nullptr; spdlog::info("Database service exited cleanly"); return 0; } catch (const std::exception& e) { spdlog::error("Fatal error: {}", e.what()); return 1; } }