diff --git a/crates/maestro-daemon/src/cli.rs b/crates/maestro-daemon/src/cli.rs index 47b5dc3..fd45d42 100644 --- a/crates/maestro-daemon/src/cli.rs +++ b/crates/maestro-daemon/src/cli.rs @@ -106,7 +106,7 @@ async fn run(config_override: Option) -> Result<(), Box> { projects.start_background_refresh(); let router = local_api::router(local_api::state_for( - manager, + manager.clone(), config.hostname.clone(), config.home_dir.clone(), config.spawn_presets.clone(), @@ -118,14 +118,25 @@ async fn run(config_override: Option) -> Result<(), Box> { let bound = listener.local_addr()?; tracing::info!(addr = %bound, "API listening"); + // Ctrl-C at a terminal or SIGTERM from systemd: either one is a + // shutdown, and both must reach the adapter children below (#134). The + // unit's `KillMode=process` signals only the daemon, so nothing else + // would end them. let shutdown = async { - let _ = tokio::signal::ctrl_c().await; - tracing::info!("ctrl-c received — shutting down"); + let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .expect("install SIGTERM handler"); + tokio::select! { + _ = tokio::signal::ctrl_c() => tracing::info!("ctrl-c received — shutting down"), + _ = sigterm.recv() => tracing::info!("SIGTERM received — shutting down"), + } }; axum::serve(listener, router) .tcp_nodelay(true) .with_graceful_shutdown(shutdown) .await?; + // tmux sessions outlive the daemon by design; structured sessions' + // adapter children do not (#134). + manager.shutdown().await; Ok(()) }