#include #include #include #include #include #include #include #include "smartbotic/common.hpp" #include "smartbotic/llm/config.hpp" #include "smartbotic/llm/grpc/server.hpp" #include "smartbotic/llm/server_config.hpp" namespace { // Global server pointer for signal handling smartbotic::llm::grpc::GrpcServer* g_server = nullptr; // Atomic flag for shutdown tracking std::atomic g_shutdown_requested{false}; // Self-pipe for async-signal-safe notification int g_shutdown_pipe[2] = {-1, -1}; void SignalHandler(int signal) { if (g_shutdown_requested.exchange(true)) { // Second signal - force exit const char* msg = "Received second signal, forcing immediate exit\n"; [[maybe_unused]] auto n = write(STDERR_FILENO, msg, 48); std::_Exit(1); } // Write to pipe to notify shutdown thread (async-signal-safe) char sig = static_cast(signal); [[maybe_unused]] auto n = write(g_shutdown_pipe[1], &sig, 1); } // Shutdown thread that waits for signal and calls gRPC Shutdown() void ShutdownThread() { char sig = 0; // Block until signal is received (read from pipe) ssize_t n = read(g_shutdown_pipe[0], &sig, 1); if (n > 0) { const char* signal_name = (sig == SIGINT) ? "SIGINT" : "SIGTERM"; spdlog::info("Received {} ({}), initiating graceful shutdown...", signal_name, static_cast(sig)); // Safe to call gRPC Shutdown() from this thread (not from signal handler) if (g_server != nullptr) { g_server->RequestShutdown(); } } } auto SetLogLevel(smartbotic::llm::LogLevel level) { switch (level) { case smartbotic::llm::LogLevel::kTrace: spdlog::set_level(spdlog::level::trace); break; case smartbotic::llm::LogLevel::kDebug: spdlog::set_level(spdlog::level::debug); break; case smartbotic::llm::LogLevel::kInfo: spdlog::set_level(spdlog::level::info); break; case smartbotic::llm::LogLevel::kWarn: spdlog::set_level(spdlog::level::warn); break; case smartbotic::llm::LogLevel::kError: spdlog::set_level(spdlog::level::err); break; case smartbotic::llm::LogLevel::kCritical: spdlog::set_level(spdlog::level::critical); break; case smartbotic::llm::LogLevel::kOff: spdlog::set_level(spdlog::level::off); break; } } } // namespace int main(int argc, char* argv[]) { // Initialize common logging first (with default settings) smartbotic::common::Initialize("smartbotic-llm"); spdlog::info("SmartBotic LLM Service starting..."); // Load configuration from file, environment, and command line auto config = smartbotic::llm::ConfigLoader::Load(argc, argv); // Apply configured log level SetLogLevel(config.log_level); // Print configuration smartbotic::llm::ConfigLoader::PrintConfig(config); // Create self-pipe for async-signal-safe shutdown notification if (pipe(g_shutdown_pipe) == -1) { spdlog::error("Failed to create shutdown pipe"); smartbotic::common::Shutdown(); return 1; } // Convert LlmConfig to ServerConfig for the gRPC server auto server_config = smartbotic::llm::ServerConfig::FromConfig(config); // Create and start the server smartbotic::llm::grpc::GrpcServer server(server_config); g_server = &server; // Start shutdown thread that will call gRPC Shutdown() when signaled std::thread shutdown_thread(ShutdownThread); // Set up signal handlers for graceful shutdown (SIGTERM/SIGINT) struct sigaction sa{}; sa.sa_handler = SignalHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGINT, &sa, nullptr) == -1) { spdlog::error("Failed to set SIGINT handler"); close(g_shutdown_pipe[1]); shutdown_thread.join(); close(g_shutdown_pipe[0]); smartbotic::common::Shutdown(); return 1; } if (sigaction(SIGTERM, &sa, nullptr) == -1) { spdlog::error("Failed to set SIGTERM handler"); close(g_shutdown_pipe[1]); shutdown_thread.join(); close(g_shutdown_pipe[0]); smartbotic::common::Shutdown(); return 1; } spdlog::info("Signal handlers registered for SIGINT and SIGTERM"); if (!server.Start()) { spdlog::error("Failed to start server"); close(g_shutdown_pipe[1]); shutdown_thread.join(); close(g_shutdown_pipe[0]); smartbotic::common::Shutdown(); return 1; } // Wait for server to stop (either by signal or error) server.Wait(); // Wait for shutdown thread to complete shutdown_thread.join(); // Perform cleanup after Wait() returns (safe to do in main thread) server.Cleanup(); // Close pipe close(g_shutdown_pipe[0]); close(g_shutdown_pipe[1]); spdlog::info("SmartBotic LLM Service shutdown complete"); g_server = nullptr; smartbotic::common::Shutdown(); return 0; }