2024-07-27 07:17:07 +00:00
|
|
|
use std::{
|
2025-02-23 01:17:45 -05:00
|
|
|
sync::{Arc, Weak, atomic::Ordering},
|
2024-07-27 07:17:07 +00:00
|
|
|
time::Duration,
|
|
|
|
|
};
|
2024-05-09 15:59:08 -07:00
|
|
|
|
2026-02-12 06:58:16 +00:00
|
|
|
use futures::{FutureExt, pin_mut};
|
|
|
|
|
use tuwunel_core::{
|
|
|
|
|
Error, Result, Server, debug, debug_error, debug_info, error, info,
|
|
|
|
|
utils::{BoolExt, future::OptionFutureExt},
|
|
|
|
|
};
|
2025-04-22 01:41:02 +00:00
|
|
|
use tuwunel_service::Services;
|
2024-05-09 15:59:08 -07:00
|
|
|
|
2026-02-03 02:30:50 +05:00
|
|
|
use crate::{handle::ServerHandle, serve};
|
2024-05-09 15:59:08 -07:00
|
|
|
|
|
|
|
|
/// Main loop base
|
|
|
|
|
#[tracing::instrument(skip_all)]
|
2025-07-08 12:08:13 +00:00
|
|
|
pub(crate) async fn run(services: Arc<Services>) -> Result {
|
2024-07-27 07:17:07 +00:00
|
|
|
let server = &services.server;
|
2024-07-13 06:07:09 +00:00
|
|
|
debug!("Start");
|
2024-05-09 15:59:08 -07:00
|
|
|
|
|
|
|
|
// Install the admin room callback here for now
|
2025-04-22 01:41:02 +00:00
|
|
|
tuwunel_admin::init(&services.admin).await;
|
2024-05-09 15:59:08 -07:00
|
|
|
|
|
|
|
|
// Setup shutdown/signal handling
|
|
|
|
|
let handle = ServerHandle::new();
|
2024-06-05 19:59:50 +00:00
|
|
|
let sigs = server
|
|
|
|
|
.runtime()
|
2026-02-03 02:30:50 +05:00
|
|
|
.spawn(signal(server.clone(), handle.clone()));
|
2024-05-09 15:59:08 -07:00
|
|
|
|
2026-02-12 06:58:16 +00:00
|
|
|
let listener = services
|
|
|
|
|
.config
|
|
|
|
|
.listening
|
|
|
|
|
.then_async(|| {
|
|
|
|
|
server
|
|
|
|
|
.runtime()
|
|
|
|
|
.spawn(serve::serve(services.clone(), handle))
|
|
|
|
|
.map(|res| res.map_err(Error::from).unwrap_or_else(Err))
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else_async(|| server.until_shutdown().map(Ok));
|
2024-07-13 06:07:09 +00:00
|
|
|
|
|
|
|
|
// Focal point
|
|
|
|
|
debug!("Running");
|
2026-02-12 06:58:16 +00:00
|
|
|
pin_mut!(listener);
|
2024-07-13 06:07:09 +00:00
|
|
|
let res = tokio::select! {
|
2026-02-12 06:58:16 +00:00
|
|
|
res = &mut listener => res.unwrap_or(Ok(())),
|
|
|
|
|
res = services.poll() => handle_services_finish(server, res, listener.await),
|
2024-07-13 06:07:09 +00:00
|
|
|
};
|
2024-05-09 15:59:08 -07:00
|
|
|
|
|
|
|
|
// Join the signal handler before we leave.
|
|
|
|
|
sigs.abort();
|
|
|
|
|
_ = sigs.await;
|
|
|
|
|
|
|
|
|
|
// Remove the admin room callback
|
2025-04-22 01:41:02 +00:00
|
|
|
tuwunel_admin::fini(&services.admin).await;
|
2024-05-09 15:59:08 -07:00
|
|
|
|
2024-07-13 06:07:09 +00:00
|
|
|
debug_info!("Finish");
|
2024-05-29 16:59:20 +00:00
|
|
|
res
|
2024-05-09 15:59:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Async initializations
|
|
|
|
|
#[tracing::instrument(skip_all)]
|
2024-07-27 07:17:07 +00:00
|
|
|
pub(crate) async fn start(server: Arc<Server>) -> Result<Arc<Services>> {
|
2024-05-09 15:59:08 -07:00
|
|
|
debug!("Starting...");
|
2024-06-05 05:41:29 +00:00
|
|
|
|
2024-07-27 07:17:07 +00:00
|
|
|
let services = Services::build(server).await?.start().await?;
|
2024-05-09 15:59:08 -07:00
|
|
|
|
2025-01-10 10:25:07 -05:00
|
|
|
#[cfg(all(feature = "systemd", target_os = "linux"))]
|
2024-12-15 00:05:47 -05:00
|
|
|
sd_notify::notify(true, &[sd_notify::NotifyState::Ready])
|
|
|
|
|
.expect("failed to notify systemd of ready state");
|
2024-05-09 15:59:08 -07:00
|
|
|
|
|
|
|
|
debug!("Started");
|
2024-07-27 07:17:07 +00:00
|
|
|
Ok(services)
|
2024-05-09 15:59:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Async destructions
|
|
|
|
|
#[tracing::instrument(skip_all)]
|
2025-07-08 12:08:13 +00:00
|
|
|
pub(crate) async fn stop(services: Arc<Services>) -> Result {
|
2024-05-09 15:59:08 -07:00
|
|
|
debug!("Shutting down...");
|
|
|
|
|
|
2025-04-02 04:12:24 +00:00
|
|
|
#[cfg(all(feature = "systemd", target_os = "linux"))]
|
|
|
|
|
sd_notify::notify(true, &[sd_notify::NotifyState::Stopping])
|
|
|
|
|
.expect("failed to notify systemd of stopping state");
|
|
|
|
|
|
2024-05-09 15:59:08 -07:00
|
|
|
// Wait for all completions before dropping or we'll lose them to the module
|
|
|
|
|
// unload and explode.
|
2024-07-27 07:17:07 +00:00
|
|
|
services.stop().await;
|
|
|
|
|
|
2024-12-03 06:34:56 +00:00
|
|
|
// Check that Services and Database will drop as expected, The complex of Arc's
|
|
|
|
|
// used for various components can easily lead to references being held
|
|
|
|
|
// somewhere improperly; this can hang shutdowns.
|
|
|
|
|
debug!("Cleaning up...");
|
|
|
|
|
let db = Arc::downgrade(&services.db);
|
2024-07-27 07:17:07 +00:00
|
|
|
if let Err(services) = Arc::try_unwrap(services) {
|
|
|
|
|
debug_error!(
|
|
|
|
|
"{} dangling references to Services after shutdown",
|
|
|
|
|
Arc::strong_count(&services)
|
|
|
|
|
);
|
|
|
|
|
}
|
2024-06-05 05:41:29 +00:00
|
|
|
|
2025-01-05 23:33:27 +00:00
|
|
|
if Weak::strong_count(&db) > 0 {
|
|
|
|
|
debug_error!(
|
|
|
|
|
"{} dangling references to Database after shutdown",
|
|
|
|
|
Weak::strong_count(&db)
|
|
|
|
|
);
|
2024-12-03 06:34:56 +00:00
|
|
|
}
|
2024-05-09 15:59:08 -07:00
|
|
|
|
|
|
|
|
info!("Shutdown complete.");
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tracing::instrument(skip_all)]
|
2026-02-03 02:30:50 +05:00
|
|
|
async fn signal(server: Arc<Server>, handle: ServerHandle) {
|
2026-01-10 09:08:48 +05:00
|
|
|
server.until_shutdown().await;
|
2026-02-03 02:30:50 +05:00
|
|
|
handle_shutdown(&server, &handle);
|
2024-06-10 06:02:17 +00:00
|
|
|
}
|
2024-05-09 15:59:08 -07:00
|
|
|
|
2026-02-03 02:30:50 +05:00
|
|
|
fn handle_shutdown(server: &Arc<Server>, handle: &ServerHandle) {
|
2025-02-02 10:43:02 +00:00
|
|
|
let timeout = server.config.client_shutdown_timeout;
|
|
|
|
|
let timeout = Duration::from_secs(timeout);
|
2024-07-16 06:49:47 +00:00
|
|
|
debug!(
|
|
|
|
|
?timeout,
|
|
|
|
|
handle_active = ?server.metrics.requests_handle_active.load(Ordering::Relaxed),
|
|
|
|
|
"Notifying for graceful shutdown"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
handle.graceful_shutdown(Some(timeout));
|
2024-05-09 15:59:08 -07:00
|
|
|
}
|
2024-07-13 06:07:09 +00:00
|
|
|
|
2026-02-12 06:58:16 +00:00
|
|
|
fn handle_services_finish(
|
2024-12-15 00:05:47 -05:00
|
|
|
server: &Arc<Server>,
|
2025-07-08 12:08:13 +00:00
|
|
|
result: Result,
|
2026-02-12 06:58:16 +00:00
|
|
|
listener: Option<Result>,
|
2025-07-08 12:08:13 +00:00
|
|
|
) -> Result {
|
2024-07-13 06:07:09 +00:00
|
|
|
debug!("Service manager finished: {result:?}");
|
|
|
|
|
|
|
|
|
|
if server.running()
|
|
|
|
|
&& let Err(e) = server.shutdown()
|
|
|
|
|
{
|
|
|
|
|
error!("Failed to send shutdown signal: {e}");
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 06:58:16 +00:00
|
|
|
if let Some(Err(e)) = listener {
|
2024-07-13 06:07:09 +00:00
|
|
|
error!("Client listener task finished with error: {e}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|