mirror of
https://github.com/Xevion/banner.git
synced 2025-12-06 03:14:24 -06:00
107 lines
3.7 KiB
Rust
107 lines
3.7 KiB
Rust
use crate::services::ServiceResult;
|
|
use crate::services::manager::ServiceManager;
|
|
use std::process::ExitCode;
|
|
use std::time::Duration;
|
|
use tokio::signal;
|
|
use tracing::{error, info, warn};
|
|
|
|
/// Handle application shutdown signals and graceful shutdown
|
|
pub async fn handle_shutdown_signals(
|
|
mut service_manager: ServiceManager,
|
|
shutdown_timeout: Duration,
|
|
) -> ExitCode {
|
|
// Set up signal handling for both SIGINT (Ctrl+C) and SIGTERM
|
|
let ctrl_c = async {
|
|
signal::ctrl_c()
|
|
.await
|
|
.expect("Failed to install CTRL+C signal handler");
|
|
info!("received ctrl+c, gracefully shutting down...");
|
|
};
|
|
|
|
#[cfg(unix)]
|
|
let sigterm = async {
|
|
use tokio::signal::unix::{SignalKind, signal};
|
|
let mut sigterm_stream =
|
|
signal(SignalKind::terminate()).expect("Failed to install SIGTERM signal handler");
|
|
sigterm_stream.recv().await;
|
|
info!("received SIGTERM, gracefully shutting down...");
|
|
};
|
|
|
|
#[cfg(not(unix))]
|
|
let sigterm = async {
|
|
// On non-Unix systems, create a future that never completes
|
|
// This ensures the select! macro works correctly
|
|
std::future::pending::<()>().await;
|
|
};
|
|
|
|
// Main application loop - wait for services or signals
|
|
let mut exit_code = ExitCode::SUCCESS;
|
|
|
|
tokio::select! {
|
|
(service_name, result) = service_manager.run() => {
|
|
// A service completed unexpectedly
|
|
match result {
|
|
ServiceResult::GracefulShutdown => {
|
|
info!(service = service_name, "service completed gracefully");
|
|
}
|
|
ServiceResult::NormalCompletion => {
|
|
warn!(service = service_name, "service completed unexpectedly");
|
|
exit_code = ExitCode::FAILURE;
|
|
}
|
|
ServiceResult::Error(e) => {
|
|
error!(service = service_name, error = ?e, "service failed");
|
|
exit_code = ExitCode::FAILURE;
|
|
}
|
|
}
|
|
|
|
// Shutdown remaining services
|
|
exit_code = handle_graceful_shutdown(service_manager, shutdown_timeout, exit_code).await;
|
|
}
|
|
_ = ctrl_c => {
|
|
// User requested shutdown via Ctrl+C
|
|
info!("user requested shutdown via ctrl+c");
|
|
exit_code = handle_graceful_shutdown(service_manager, shutdown_timeout, ExitCode::SUCCESS).await;
|
|
}
|
|
_ = sigterm => {
|
|
// System requested shutdown via SIGTERM
|
|
info!("system requested shutdown via SIGTERM");
|
|
exit_code = handle_graceful_shutdown(service_manager, shutdown_timeout, ExitCode::SUCCESS).await;
|
|
}
|
|
}
|
|
|
|
info!(exit_code = ?exit_code, "application shutdown complete");
|
|
exit_code
|
|
}
|
|
|
|
/// Handle graceful shutdown of remaining services
|
|
async fn handle_graceful_shutdown(
|
|
mut service_manager: ServiceManager,
|
|
shutdown_timeout: Duration,
|
|
current_exit_code: ExitCode,
|
|
) -> ExitCode {
|
|
match service_manager.shutdown(shutdown_timeout).await {
|
|
Ok(elapsed) => {
|
|
info!(
|
|
remaining = format!("{:.2?}", shutdown_timeout - elapsed),
|
|
"graceful shutdown complete"
|
|
);
|
|
current_exit_code
|
|
}
|
|
Err(pending_services) => {
|
|
warn!(
|
|
pending_count = pending_services.len(),
|
|
pending_services = ?pending_services,
|
|
"graceful shutdown elapsed - {} service(s) did not complete",
|
|
pending_services.len()
|
|
);
|
|
|
|
// Non-zero exit code, default to FAILURE if not set
|
|
if current_exit_code == ExitCode::SUCCESS {
|
|
ExitCode::FAILURE
|
|
} else {
|
|
current_exit_code
|
|
}
|
|
}
|
|
}
|
|
}
|