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" 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") + ); + } }