From ebdd602e6baf527a1fe3380becda6a1b0ee7c5ca Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Thu, 20 Aug 2026 10:33:22 -0400 Subject: [PATCH 1/2] fix: release the agent object path on unregister Remove the SecretAgent interface from the object server unconditionally, so the object path is freed even when the NetworkManager Unregister call fails or the daemon is unreachable. The D-Bus error is still propagated after the removal. --- nmrs/src/agent/builder.rs | 140 +++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 9 deletions(-) diff --git a/nmrs/src/agent/builder.rs b/nmrs/src/agent/builder.rs index d0117585..56794974 100644 --- a/nmrs/src/agent/builder.rs +++ b/nmrs/src/agent/builder.rs @@ -325,22 +325,38 @@ impl SecretAgentHandle { Ok(()) } - /// Unregisters the agent from NetworkManager. + /// Unregisters the agent from NetworkManager and stops serving its object. /// /// After this call, the request stream returned by - /// [`SecretAgentBuilder::register`] will complete. + /// [`SecretAgentBuilder::register`] will complete and the agent's object + /// path is released, even if NetworkManager is no longer reachable. /// /// # Errors /// - /// Returns an error if the D-Bus `Unregister` call fails. + /// Returns an error if the D-Bus `Unregister` call fails or if the agent's + /// object could not be removed from the object server. pub async fn unregister(self) -> crate::Result<()> { - let proxy = AgentManagerProxy::new(&self.conn).await.map_err(|e| { - ConnectionError::DbusOperation { - context: "creating AgentManager proxy for unregistration".into(), + let unregistered = async { + let proxy = AgentManagerProxy::new(&self.conn).await.map_err(|e| { + ConnectionError::DbusOperation { + context: "creating AgentManager proxy for unregistration".into(), + source: e, + } + })?; + proxy.unregister().await.map_err(unregistration_error) + } + .await; + + self.conn + .object_server() + .remove::(&*self.object_path) + .await + .map_err(|e| ConnectionError::DbusOperation { + context: format!("removing SecretAgent interface at {}", self.object_path), source: e, - } - })?; - proxy.unregister().await.map_err(unregistration_error)?; + })?; + + unregistered?; debug!("Unregistered secret agent '{}'", self.identifier); Ok(()) } @@ -435,4 +451,110 @@ mod tests { assert_eq!(builder.object_path, "/org/example/nmrs/Agent"); assert_eq!(builder.queue_depth, 7); } + + async fn session_connection() -> Connection { + zbus::connection::Builder::session() + .expect("session bus address") + .build() + .await + .expect("session bus connection") + } + + async fn serve_agent(conn: &Connection, object_path: &str) -> SecretAgentHandle { + let (request_tx, _request_rx) = mpsc::channel(DEFAULT_QUEUE_DEPTH); + let (cancel_tx, cancel_rx) = mpsc::unbounded(); + let (store_tx, store_rx) = mpsc::unbounded(); + + conn.object_server() + .at( + object_path, + SecretAgentInterface { + request_tx, + cancel_tx, + store_tx, + pending: Arc::new(Mutex::new(HashMap::new())), + next_request_id: AtomicU64::new(1), + response_timeout: + crate::types::constants::timeouts::secret_agent_response_timeout(), + }, + ) + .await + .expect("serving the agent interface"); + + SecretAgentHandle { + conn: conn.clone(), + identifier: DEFAULT_IDENTIFIER.to_owned(), + capabilities: SecretAgentCapabilities::VPN_HINTS, + object_path: object_path.to_owned(), + cancel_rx, + store_rx, + } + } + + async fn introspect(conn: &Connection, path: &str) -> String { + zbus::fdo::IntrospectableProxy::builder(conn) + .destination(conn.unique_name().expect("unique name").clone()) + .expect("destination") + .path(path.to_owned()) + .expect("object path") + .build() + .await + .expect("introspectable proxy") + .introspect() + .await + .unwrap_or_default() + } + + async fn exported_nodes(conn: &Connection) -> Vec { + introspect(conn, "/") + .await + .lines() + .filter(|line| line.trim_start().starts_with("::new()); + } + + #[tokio::test(flavor = "multi_thread")] + async fn unregister_releases_a_custom_object_path() { + let conn = session_connection().await; + let handle = serve_agent(&conn, "/org/example/nmrs/Agent").await; + + let _ = handle.unregister().await; + + assert!( + !introspect(&conn, "/org/example/nmrs/Agent") + .await + .contains("org.freedesktop.NetworkManager.SecretAgent") + ); + } } From bb75c64df16ca3c86ebdfa9b7f6ae872ddfec0af Mon Sep 17 00:00:00 2001 From: Akrm Al-Hakimi Date: Thu, 20 Aug 2026 10:39:39 -0400 Subject: [PATCH 2/2] chore(deps): bump zbus and zvariant --- Cargo.lock | 69 +++++++++++++++++++++++++++++++++++------------------- Cargo.toml | 4 ++-- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f3bcb4e..2c0f4c72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,7 +95,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -130,7 +130,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -227,7 +227,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -342,7 +342,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -672,7 +672,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -683,7 +683,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -708,7 +708,7 @@ checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -750,6 +750,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -780,7 +791,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -806,7 +817,7 @@ checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -858,7 +869,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -938,7 +949,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -977,9 +988,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.18.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", "async-executor", @@ -1012,14 +1023,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.18.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 3.0.3", "zbus_names", "zvariant", "zvariant_utils", @@ -1036,42 +1047,52 @@ dependencies = [ "zvariant", ] +[[package]] +name = "zcheapstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + [[package]] name = "zvariant" -version = "5.13.1" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" +checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" dependencies = [ "endi", "enumflags2", "serde", "winnow", + "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.13.1" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" +checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 3.0.3", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.5.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" dependencies = [ "proc-macro2", "quote", "serde", - "syn", + "syn 3.0.3", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 40b54548..f21e9224 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ type_complexity = "allow" [workspace.dependencies] # Core dependencies -zbus = "5.16.0" -zvariant = "5.12.0" +zbus = "5.19.0" +zvariant = "5.15.0" log = "0.4.33" serde = { version = "1.0.228", features = ["derive"] } thiserror = "2.0.18"