fix(session): relancer cosmic-settings-daemon s'il sort proprement en session

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é.
This commit is contained in:
Lionel DARNIS 2026-08-08 18:04:09 +02:00
parent d97b142f35
commit bc61bfddcc
2 changed files with 130 additions and 41 deletions

26
Cargo.lock generated
View file

@ -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"

View file

@ -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<std::sync::atomic::AtomicBool>,
settings_exit_tx: Arc<std::sync::Mutex<Option<oneshot::Sender<()>>>>,
settings_daemon_key: Arc<std::sync::Mutex<Option<launch_pad::ProcessKey>>>,
respawn_count: Arc<std::sync::atomic::AtomicUsize>,
last_respawn: Arc<std::sync::Mutex<std::time::Instant>>,
) -> 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<Cow<'static, str>>,
span: tracing::Span,