From bc61bfddcc2c74e7fa9f8adeec758cb2325346e4 Mon Sep 17 00:00:00 2001 From: Lionel DARNIS Date: Sat, 8 Aug 2026 18:04:09 +0200 Subject: [PATCH] fix(session): relancer cosmic-settings-daemon s'il sort proprement en session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit launch-pad ne relance que les processus qui sortent en erreur: une sortie propre du demon (par ex. un pkill -x cosmic-settings qui touche aussi le demon a cause de la troncature comm a 15 caracteres) laissait la session sans service varlink audio jusqu'au logout. Le demon est desormais relance par cosmic-session tant que la session n'est pas en cours d'extinction, avec une limite de 5 relances rapprochées pour éviter une boucle sur un démon cassé. --- Cargo.lock | 26 +++++----- src/main.rs | 145 ++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 130 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f113ea..efb8899 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,7 +299,7 @@ dependencies = [ [[package]] name = "cosmic-session" -version = "1.0.0" +version = "1.3.0" dependencies = [ "color-eyre", "cosmic-dbus-a11y", @@ -1885,6 +1885,18 @@ dependencies = [ "winnow", ] +[[patch.unused]] +name = "softbuffer" +version = "0.4.1" + +[[patch.unused]] +name = "cosmic-freedesktop-icons" +version = "0.4.0" + +[[patch.unused]] +name = "smithay-clipboard" +version = "0.8.0" + [[patch.unused]] name = "dpi" version = "0.1.2" @@ -1932,15 +1944,3 @@ version = "0.31.0-beta.2" [[patch.unused]] name = "winit-x11" version = "0.31.0-beta.2" - -[[patch.unused]] -name = "smithay-clipboard" -version = "0.8.0" - -[[patch.unused]] -name = "cosmic-freedesktop-icons" -version = "0.4.0" - -[[patch.unused]] -name = "softbuffer" -version = "0.4.1" diff --git a/src/main.rs b/src/main.rs index 0f2c00f..6b5d6bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -221,38 +221,24 @@ async fn start( None }; - let stdout_span = info_span!(parent: None, "cosmic-settings-daemon"); - let stderr_span = stdout_span.clone(); let (settings_exit_tx, settings_exit_rx) = oneshot::channel(); let settings_exit_tx = Arc::new(std::sync::Mutex::new(Some(settings_exit_tx))); + let shutting_down = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let settings_daemon_key = Arc::new(std::sync::Mutex::new(None)); + let respawn_count = Arc::new(std::sync::atomic::AtomicUsize::new(0)); + let last_respawn = Arc::new(std::sync::Mutex::new(std::time::Instant::now())); let settings_daemon = process_manager - .start( - Process::new() - .with_executable("cosmic-settings-daemon") - .with_env(env_vars.iter().cloned()) - .with_on_stdout(move |_, _, line| { - let stdout_span = stdout_span.clone(); - async move { - info!("{}", line); - } - .instrument(stdout_span) - }) - .with_on_stderr(move |_, _, line| { - let stderr_span = stderr_span.clone(); - async move { - warn!("{}", line); - } - .instrument(stderr_span) - }) - .with_on_exit(move |_, _, _, will_restart| { - if !will_restart && let Some(tx) = settings_exit_tx.lock().unwrap().take() { - _ = tx.send(()); - } - async {} - }), - ) + .start(settings_daemon_process( + env_vars.clone(), + Arc::clone(&shutting_down), + Arc::clone(&settings_exit_tx), + Arc::clone(&settings_daemon_key), + Arc::clone(&respawn_count), + Arc::clone(&last_respawn), + )) .await .expect("failed to start settings daemon"); + *settings_daemon_key.lock().unwrap() = Some(settings_daemon); // notifying the user service manager that we've reached the // graphical-session.target, which should only happen after: @@ -487,7 +473,13 @@ async fn start( compositor_handle.abort(); token.cancel(); - if let Err(err) = process_manager.stop_process(settings_daemon).await { + shutting_down.store(true, std::sync::atomic::Ordering::Relaxed); + let settings_daemon_stop = settings_daemon_key + .lock() + .unwrap() + .take() + .unwrap_or(settings_daemon); + if let Err(err) = process_manager.stop_process(settings_daemon_stop).await { tracing::error!(?err, "Failed to gracefully stop settings daemon."); } else { match tokio::time::timeout(Duration::from_secs(1), settings_exit_rx).await { @@ -502,6 +494,103 @@ async fn start( Ok(status) } +/// Build the cosmic-settings-daemon process. +/// +/// The daemon is respawned whenever it exits while the session is still +/// running: launch-pad only restarts processes that exit with an error, so a +/// clean exit (for example a stray `pkill` matching the daemon's truncated +/// comm name) would otherwise leave the session without the varlink audio +/// service until logout. Rapid successive exits are capped to avoid a respawn +/// loop of a daemon that cannot stay alive. +fn settings_daemon_process( + env_vars: Vec<(String, String)>, + shutting_down: Arc, + settings_exit_tx: Arc>>>, + settings_daemon_key: Arc>>, + respawn_count: Arc, + last_respawn: Arc>, +) -> Process { + let stdout_span = info_span!(parent: None, "cosmic-settings-daemon"); + let stderr_span = stdout_span.clone(); + let respawn_env = env_vars.clone(); + + Process::new() + .with_executable("cosmic-settings-daemon") + .with_env(env_vars.iter().cloned()) + .with_on_stdout(move |_, _, line| { + let stdout_span = stdout_span.clone(); + async move { + info!("{}", line); + } + .instrument(stdout_span) + }) + .with_on_stderr(move |_, _, line| { + let stderr_span = stderr_span.clone(); + async move { + warn!("{}", line); + } + .instrument(stderr_span) + }) + .with_on_exit(move |pman, _key, _err_code, will_restart| { + let shutting_down = Arc::clone(&shutting_down); + let settings_exit_tx = Arc::clone(&settings_exit_tx); + let settings_daemon_key = Arc::clone(&settings_daemon_key); + let respawn_count = Arc::clone(&respawn_count); + let last_respawn = Arc::clone(&last_respawn); + let respawn_env = respawn_env.clone(); + + async move { + if will_restart { + // launch-pad restarts processes that exit with an error on its own. + return; + } + + if shutting_down.load(std::sync::atomic::Ordering::Relaxed) { + if let Some(tx) = settings_exit_tx.lock().unwrap().take() { + _ = tx.send(()); + } + return; + } + + let now = std::time::Instant::now(); + { + let mut last = last_respawn.lock().unwrap(); + if now.duration_since(*last) > Duration::from_secs(60) { + respawn_count.store(0, std::sync::atomic::Ordering::Relaxed); + } + *last = now; + } + let count = + respawn_count.fetch_add(1, std::sync::atomic::Ordering::Relaxed) + 1; + if count > 5 { + error!( + "cosmic-settings-daemon exited {count} times in quick succession; giving up on respawns" + ); + return; + } + + warn!("cosmic-settings-daemon exited unexpectedly; respawning it"); + let new_process = settings_daemon_process( + respawn_env, + shutting_down.clone(), + settings_exit_tx.clone(), + settings_daemon_key.clone(), + respawn_count.clone(), + last_respawn.clone(), + ); + + match pman.start(new_process).await { + Ok(new_key) => { + *settings_daemon_key.lock().unwrap() = Some(new_key); + } + Err(err) => { + error!(?err, "Failed to respawn cosmic-settings-daemon"); + } + } + } + }) +} + async fn start_component( cmd: impl Into>, span: tracing::Span,