From de220b0a65d67140ad599cc41e92a79687e81c5a Mon Sep 17 00:00:00 2001 From: Jared Baur Date: Sat, 3 Jan 2026 17:05:02 -0800 Subject: [PATCH 1/2] switch-to-configuration-ng: fix systemd reexec/reload According to systemd's D-Bus API documentation, a `Reloading` signal will be sent with the value `false` will be sent when reloading has finished (indicating that reloading is not active). The entire reloading sequence has two signals, the with the first value indicating that reloading has started (active = true). Thus, we must wait for the active value to be false before we can continue. --- .../sw/switch-to-configuration-ng/src/src/main.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs b/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs index 6d13b3c7605f..84b13e3b0593 100644 --- a/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs +++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs @@ -1158,19 +1158,19 @@ won't take effect until you reboot the system. let submitted_jobs = Rc::new(RefCell::new(HashMap::new())); let finished_jobs = Rc::new(RefCell::new(HashMap::new())); - let systemd_reload_status = Rc::new(RefCell::new(false)); + let systemd_is_reloading = Rc::new(RefCell::new(false)); systemd .subscribe() .context("Failed to subscribe to systemd dbus messages")?; - let _systemd_reload_status = systemd_reload_status.clone(); + let _systemd_is_reloading = systemd_is_reloading.clone(); let reloading_token = systemd .match_signal( move |signal: OrgFreedesktopSystemd1ManagerReloading, _: &LocalConnection, _msg: &Message| { - *_systemd_reload_status.borrow_mut() = signal.active; + *_systemd_is_reloading.borrow_mut() = signal.active; true }, @@ -1690,10 +1690,12 @@ won't take effect until you reboot the system. // just in case the new one has trouble communicating with the running pid 1. if restart_systemd { eprintln!("restarting systemd..."); + *systemd_is_reloading.borrow_mut() = true; _ = systemd.reexecute(); // we don't get a dbus reply here log::debug!("waiting for systemd restart to finish"); - while !*systemd_reload_status.borrow() { + + while *systemd_is_reloading.borrow() { _ = dbus_conn .process(Duration::from_millis(500)) .context("Failed to process dbus messages")?; @@ -1706,9 +1708,11 @@ won't take effect until you reboot the system. .context("Failed to reset failed units")?; // Make systemd reload its units. + *systemd_is_reloading.borrow_mut() = true; _ = systemd.reload(); // we don't get a dbus reply here log::debug!("waiting for systemd reload to finish"); - while !*systemd_reload_status.borrow() { + + while *systemd_is_reloading.borrow() { _ = dbus_conn .process(Duration::from_millis(500)) .context("Failed to process dbus messages")?; From 039a1bee5f01e703d8a1d7e9c24ece8645270635 Mon Sep 17 00:00:00 2001 From: Jared Baur Date: Thu, 8 Jan 2026 10:20:00 -0800 Subject: [PATCH 2/2] switch-to-configuration-ng: add timeout to systemd daemon reload/reexecute Just like `systemctl daemon-reload`/`systemctl daemon-reexec`, we ensure we don't wait forever for the reload action to finish. --- .../src/src/main.rs | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs b/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs index 84b13e3b0593..22d5f4954faf 100644 --- a/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs +++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs @@ -87,6 +87,12 @@ const DRY_RELOAD_BY_ACTIVATION_LIST_FILE: &str = "/run/nixos/dry-activation-relo // Reuse the same default timeout that systemd uses. See https://github.com/systemd/systemd/blob/8b4278d12ec55cc3f96764bc8197e1055fbb6d3f/src/libsystemd/sd-bus/bus-internal.h#L312 const BUS_TIMEOUT: Duration = Duration::from_secs(25); +// Reuse the same daemon reload/reexecute timeout that systemd uses. See https://github.com/systemd/systemd/blob/5366dbdbd44ba4dc0c914dd4daa1a5297e0b2bde/src/basic/constants.h#L18 +const DAEMON_RELOAD_TIMEOUT: Duration = Duration::from_secs(180); + +// Used during times of waiting for D-Bus to process messages. +const DBUS_PROCESS_TIME: Duration = Duration::from_millis(500); + #[derive(Debug, Clone, PartialEq)] enum Action { Switch, @@ -932,7 +938,7 @@ fn block_on_jobs( "waiting for submitted jobs to finish, still have {} job(s)", submitted_jobs.borrow().len() ); - _ = conn.process(Duration::from_millis(500)); + _ = conn.process(DBUS_PROCESS_TIME); } } @@ -987,7 +993,7 @@ fn do_user_switch(parent_exe: String) -> anyhow::Result<()> { log::debug!("waiting for nixos activation to finish"); while !*nixos_activation_done.borrow() { _ = dbus_conn - .process(Duration::from_secs(500)) + .process(DBUS_PROCESS_TIME) .context("Failed to process dbus messages")?; } @@ -1695,10 +1701,18 @@ won't take effect until you reboot the system. log::debug!("waiting for systemd restart to finish"); + let mut reexec_time_waited = Duration::from_secs(0); while *systemd_is_reloading.borrow() { _ = dbus_conn - .process(Duration::from_millis(500)) + .process(DBUS_PROCESS_TIME) .context("Failed to process dbus messages")?; + reexec_time_waited += DBUS_PROCESS_TIME; + if reexec_time_waited >= DAEMON_RELOAD_TIMEOUT { + anyhow::bail!( + "systemd daemon reexecute failed, timeout after {:?}", + DAEMON_RELOAD_TIMEOUT + ); + } } } @@ -1712,10 +1726,18 @@ won't take effect until you reboot the system. _ = systemd.reload(); // we don't get a dbus reply here log::debug!("waiting for systemd reload to finish"); + let mut reload_time_waited = Duration::from_secs(0); while *systemd_is_reloading.borrow() { _ = dbus_conn - .process(Duration::from_millis(500)) + .process(DBUS_PROCESS_TIME) .context("Failed to process dbus messages")?; + reload_time_waited += DBUS_PROCESS_TIME; + if reload_time_waited >= DAEMON_RELOAD_TIMEOUT { + anyhow::bail!( + "systemd daemon reload failed, timeout after {:?}", + DAEMON_RELOAD_TIMEOUT + ); + } } dbus_conn