diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/build.rs b/pkgs/by-name/sw/switch-to-configuration-ng/src/build.rs index 41fed3086e69..bc07ec47a661 100644 --- a/pkgs/by-name/sw/switch-to-configuration-ng/src/build.rs +++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/build.rs @@ -18,6 +18,10 @@ fn main() { let out_path = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap()); + let fdo_dbus_code = code_for_dbus_xml("org.freedesktop.DBus.xml"); + let mut file = std::fs::File::create(out_path.join("fdo_dbus.rs")).unwrap(); + file.write_all(fdo_dbus_code.as_bytes()).unwrap(); + let systemd_manager_code = code_for_dbus_xml(systemd_dbus_interface_dir.join("org.freedesktop.systemd1.Manager.xml")); let mut file = std::fs::File::create(out_path.join("systemd_manager.rs")).unwrap(); diff --git a/pkgs/by-name/sw/switch-to-configuration-ng/src/org.freedesktop.DBus.xml b/pkgs/by-name/sw/switch-to-configuration-ng/src/org.freedesktop.DBus.xml new file mode 100644 index 000000000000..6ab33de9dc40 --- /dev/null +++ b/pkgs/by-name/sw/switch-to-configuration-ng/src/org.freedesktop.DBus.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + 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 705b9284f6a1..4e84e8d1ae09 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 @@ -17,6 +17,8 @@ use std::{ use anyhow::{anyhow, bail, Context, Result}; use dbus::{ blocking::{stdintf::org_freedesktop_dbus::Properties, LocalConnection, Proxy}, + channel::Sender, + strings::{BusName, Interface, Member}, Message, }; use glob::glob; @@ -32,6 +34,15 @@ use nix::{ use regex::Regex; use syslog::Facility; +mod fdo_dbus { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(unused)] + #![allow(clippy::all)] + include!(concat!(env!("OUT_DIR"), "/fdo_dbus.rs")); +} + mod systemd_manager { #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] @@ -50,7 +61,9 @@ mod logind_manager { include!(concat!(env!("OUT_DIR"), "/logind_manager.rs")); } -use crate::systemd_manager::OrgFreedesktopSystemd1Manager; +use crate::{ + fdo_dbus::OrgFreedesktopDBusNameOwnerChanged, systemd_manager::OrgFreedesktopSystemd1Manager, +}; use crate::{ logind_manager::OrgFreedesktopLogin1Manager, systemd_manager::{ @@ -894,6 +907,14 @@ impl std::fmt::Display for Job { } } +fn fdo_dbus_proxy(conn: &LocalConnection) -> Proxy<'_, &LocalConnection> { + conn.with_proxy( + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + Duration::from_millis(500), + ) +} + fn systemd1_proxy(conn: &LocalConnection) -> Proxy<'_, &LocalConnection> { conn.with_proxy( "org.freedesktop.systemd1", @@ -930,6 +951,54 @@ fn remove_file_if_exists(p: impl AsRef) -> std::io::Result<()> { } } +fn reexecute_systemd_manager( + dbus_conn: &LocalConnection, + fdo_dbus: &Proxy<'_, &LocalConnection>, +) -> anyhow::Result<()> { + let reexecute_done = Rc::new(RefCell::new(false)); + let _reexecute_done = reexecute_done.clone(); + let owner_changed_token = fdo_dbus + .match_signal( + move |signal: OrgFreedesktopDBusNameOwnerChanged, _: &LocalConnection, _: &Message| { + if signal.name.as_str() == "org.freedesktop.systemd1" { + *_reexecute_done.borrow_mut() = true; + } + + true + }, + ) + .context("Failed to add signal match for DBus name owner changes")?; + + let bus_name = BusName::from("org.freedesktop.systemd1"); + let object_path = dbus::Path::from("/org/freedesktop/systemd1"); + let interface = Interface::new("org.freedesktop.systemd1.Manager") + .expect("the org.freedesktop.systemd1.Manager interface name should be valid"); + let method_name = Member::new("Reexecute").expect("the Reexecute method name should be valid"); + + // Systemd does not reply to the Reexecute method. + let _serial = dbus_conn + .send(Message::method_call( + &bus_name, + &object_path, + &interface, + &method_name, + )) + .map_err(|_err| anyhow!("Failed to send org.freedesktop.systemd1.Manager.Reexecute"))?; + + log::debug!("waiting for systemd to finish reexecuting"); + while !*reexecute_done.borrow() { + _ = dbus_conn + .process(Duration::from_secs(500)) + .context("Failed to process dbus messages")?; + } + + dbus_conn + .remove_match(owner_changed_token) + .context("Failed to remove jobs token")?; + + Ok(()) +} + /// Performs switch-to-configuration functionality for a single non-root user fn do_user_switch(parent_exe: String) -> anyhow::Result<()> { if Path::new(&parent_exe) @@ -945,8 +1014,11 @@ fn do_user_switch(parent_exe: String) -> anyhow::Result<()> { } let dbus_conn = LocalConnection::new_session().context("Failed to open dbus connection")?; + let fdo_dbus = fdo_dbus_proxy(&dbus_conn); let systemd = systemd1_proxy(&dbus_conn); + reexecute_systemd_manager(&dbus_conn, &fdo_dbus)?; + let nixos_activation_done = Rc::new(RefCell::new(false)); let _nixos_activation_done = nixos_activation_done.clone(); let jobs_token = systemd @@ -963,10 +1035,6 @@ fn do_user_switch(parent_exe: String) -> anyhow::Result<()> { ) .context("Failed to add signal match for systemd removed jobs")?; - // The systemd user session seems to not send a Reloaded signal, so we don't have anything to - // wait on here. - _ = systemd.reexecute(); - systemd .restart_unit("nixos-activation.service", "replace") .context("Failed to restart nixos-activation.service")?; @@ -1139,6 +1207,7 @@ won't take effect until you reboot the system. let mut units_to_reload = map_from_list_file(RELOAD_LIST_FILE); let dbus_conn = LocalConnection::new_system().context("Failed to open dbus connection")?; + let fdo_dbus = fdo_dbus_proxy(&dbus_conn); let systemd = systemd1_proxy(&dbus_conn); let logind = login1_proxy(&dbus_conn); @@ -1664,7 +1733,7 @@ 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.reexecute(); // we don't get a dbus reply here + reexecute_systemd_manager(&dbus_conn, &fdo_dbus)?; log::debug!("waiting for systemd restart to finish"); while !*systemd_reload_status.borrow() {