From dec964eece1391e6954053673a25652210f0fe50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 12:13:56 +0000 Subject: [PATCH 01/14] Add an 'Active' property to the Connection interface --- .../src/network/dbus/interfaces.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index 3cd5fb8e3d..7ca4e1c3d9 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -275,6 +275,23 @@ impl Connection { connection.mac_address = MacAddress::from_str(mac_address)?; self.update_connection(connection).await } + + #[dbus_interface(property)] + pub async fn active(&self) -> bool { + let connection = self.get_connection().await; + connection.is_up() + } + + #[dbus_interface(property)] + pub async fn set_active(&mut self, active: bool) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + if active { + connection.set_up(); + } else { + connection.set_down(); + } + self.update_connection(connection).await + } } /// D-Bus interface for Match settings From 45d2ba75a7aefaf78e6d1f01f6438f09c61f5b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 14:49:03 +0000 Subject: [PATCH 02/14] AddConnection returns the D-Bus path of the new connection --- rust/agama-dbus-server/src/network/action.rs | 7 +++++- .../src/network/dbus/interfaces.rs | 12 ++++++--- .../src/network/dbus/tree.rs | 8 +++--- rust/agama-dbus-server/src/network/system.rs | 25 ++++++++++++++++--- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/rust/agama-dbus-server/src/network/action.rs b/rust/agama-dbus-server/src/network/action.rs index 3ddf6687d9..137cd717a5 100644 --- a/rust/agama-dbus-server/src/network/action.rs +++ b/rust/agama-dbus-server/src/network/action.rs @@ -2,6 +2,7 @@ use crate::network::model::Connection; use agama_lib::network::types::DeviceType; use tokio::sync::oneshot; use uuid::Uuid; +use zbus::zvariant::OwnedObjectPath; use super::error::NetworkStateError; @@ -15,7 +16,11 @@ pub type ControllerConnection = (Connection, Vec); #[derive(Debug)] pub enum Action { /// Add a new connection with the given name and type. - AddConnection(String, DeviceType), + AddConnection( + String, + DeviceType, + Responder>, + ), /// Gets a connection GetConnection(Uuid, Responder>), /// Gets a controller connection diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index 7ca4e1c3d9..123e3382a7 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -129,12 +129,18 @@ impl Connections { /// /// * `id`: connection name. /// * `ty`: connection type (see [agama_lib::network::types::DeviceType]). - pub async fn add_connection(&mut self, id: String, ty: u8) -> zbus::fdo::Result<()> { + pub async fn add_connection( + &mut self, + id: String, + ty: u8, + ) -> zbus::fdo::Result { let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); actions - .send(Action::AddConnection(id.clone(), ty.try_into()?)) + .send(Action::AddConnection(id.clone(), ty.try_into()?, tx)) .unwrap(); - Ok(()) + let result = rx.await.unwrap()?; + Ok(result) } /// Returns the D-Bus path of the network connection. diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index 45568fcf42..1c2c5612eb 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -76,7 +76,7 @@ impl Tree { Ok(()) } - /// Adds a connection to the D-Bus tree. + /// Adds a connection to the D-Bus tree and returns the D-Bus path. /// /// * `connection`: connection to add. /// * `notify`: whether to notify the added connection @@ -84,7 +84,7 @@ impl Tree { &self, conn: &mut Connection, notify: bool, - ) -> Result<(), ServiceError> { + ) -> Result { let mut objects = self.objects.lock().await; let orig_id = conn.id.to_owned(); @@ -98,7 +98,7 @@ impl Tree { let cloned = Arc::new(Mutex::new(conn.clone())); self.add_interface( &path, - interfaces::Connection::new(self.actions.clone(), Arc::clone(&cloned)), + interfaces::Connection::new(self.actions.clone(), uuid), ) .await?; @@ -131,7 +131,7 @@ impl Tree { self.notify_connection_added(&orig_id, &path).await?; } - Ok(()) + Ok(path.into()) } /// Removes a connection from the tree diff --git a/rust/agama-dbus-server/src/network/system.rs b/rust/agama-dbus-server/src/network/system.rs index 4500c3be43..af23d9e8d7 100644 --- a/rust/agama-dbus-server/src/network/system.rs +++ b/rust/agama-dbus-server/src/network/system.rs @@ -1,8 +1,10 @@ use super::error::NetworkStateError; use crate::network::{dbus::Tree, model::Connection, Action, Adapter, NetworkState}; +use agama_lib::network::types::DeviceType; use std::error::Error; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use uuid::Uuid; +use zbus::zvariant::OwnedObjectPath; /// Represents the network system using holding the state and setting up the D-Bus tree. pub struct NetworkSystem { @@ -67,10 +69,9 @@ impl NetworkSystem { /// Dispatch an action. pub async fn dispatch_action(&mut self, action: Action) -> Result<(), Box> { match action { - Action::AddConnection(name, ty) => { - let mut conn = Connection::new(name, ty); - self.tree.add_connection(&mut conn, true).await?; - self.state.add_connection(conn)?; + Action::AddConnection(name, ty, tx) => { + let result = self.add_connection_action(name, ty).await; + tx.send(result).unwrap(); } Action::GetConnection(uuid, rx) => { let conn = self.state.get_connection_by_uuid(uuid); @@ -104,6 +105,22 @@ impl NetworkSystem { Ok(()) } + async fn add_connection_action( + &mut self, + name: String, + ty: DeviceType, + ) -> Result { + let mut conn = Connection::new(name, ty); + // TODO: handle tree handling problems + let path = self + .tree + .add_connection(&mut conn, true) + .await + .expect("Could not update the D-Bus tree"); + self.state.add_connection(conn)?; + Ok(path) + } + fn set_ports_action( &mut self, uuid: Uuid, From e1929899e7f0c68793a99d2315acb7d307d7dace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 14:49:48 +0000 Subject: [PATCH 03/14] Better name for a channel variable --- rust/agama-dbus-server/src/network/system.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/agama-dbus-server/src/network/system.rs b/rust/agama-dbus-server/src/network/system.rs index af23d9e8d7..9e8de3e18d 100644 --- a/rust/agama-dbus-server/src/network/system.rs +++ b/rust/agama-dbus-server/src/network/system.rs @@ -73,17 +73,17 @@ impl NetworkSystem { let result = self.add_connection_action(name, ty).await; tx.send(result).unwrap(); } - Action::GetConnection(uuid, rx) => { + Action::GetConnection(uuid, tx) => { let conn = self.state.get_connection_by_uuid(uuid); - rx.send(conn.cloned()).unwrap(); + tx.send(conn.cloned()).unwrap(); } - Action::GetController(uuid, rx) => { + Action::GetController(uuid, tx) => { let result = self.get_controller_action(uuid); - rx.send(result).unwrap() + tx.send(result).unwrap() } - Action::SetPorts(uuid, ports, rx) => { + Action::SetPorts(uuid, ports, tx) => { let result = self.set_ports_action(uuid, ports); - rx.send(result).unwrap(); + tx.send(result).unwrap(); } Action::UpdateConnection(conn) => { self.state.update_connection(conn)?; From 6aad653329ff832baa7a18f915bcc84e7c68799f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 14:50:42 +0000 Subject: [PATCH 04/14] Use channels in the *.Network.Connection interface --- .../src/network/dbus/interfaces.rs | 95 ++++++++++--------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index 123e3382a7..b7ba282f88 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -188,7 +188,7 @@ impl Connections { /// It offers an API to query a connection. pub struct Connection { actions: Arc>>, - connection: Arc>, + uuid: Uuid, } impl Connection { @@ -196,32 +196,34 @@ impl Connection { /// /// * `actions`: sending-half of a channel to send actions. /// * `connection`: connection to expose over D-Bus. - pub fn new( - actions: UnboundedSender, - connection: Arc>, - ) -> Self { + pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { Self { actions: Arc::new(Mutex::new(actions)), - connection, + uuid, } } /// Returns the underlying connection. - async fn get_connection(&self) -> MutexGuard { - self.connection.lock().await + async fn get_connection(&self) -> Result { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); + rx.await + .unwrap() + .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) } /// Updates the connection data in the NetworkSystem. /// /// * `connection`: Updated connection. - async fn update_connection<'a>( - &self, - connection: MutexGuard<'a, NetworkConnection>, - ) -> zbus::fdo::Result<()> { + pub async fn update_connection(&self, func: F) -> Result<(), NetworkStateError> + where + F: FnOnce(&mut NetworkConnection), + { + let mut connection = self.get_connection().await?; + func(&mut connection); let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(connection.clone())) - .unwrap(); + actions.send(Action::UpdateConnection(connection)).unwrap(); Ok(()) } } @@ -234,8 +236,9 @@ impl Connection { /// backend. For instance, when using NetworkManager (which is the only supported backend by /// now), it uses the original ID but appending a number in case the ID is duplicated. #[dbus_interface(property)] - pub async fn id(&self) -> String { - self.get_connection().await.id.to_string() + pub async fn id(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.id) } /// Connection UUID. @@ -244,59 +247,65 @@ impl Connection { /// backend. #[dbus_interface(property)] pub async fn uuid(&self) -> String { - self.get_connection().await.uuid.to_string() + self.uuid.to_string() } #[dbus_interface(property)] - pub async fn controller(&self) -> String { - let connection = self.get_connection().await; - match connection.controller { + pub async fn controller(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + let result = match connection.controller { Some(uuid) => uuid.to_string(), None => "".to_string(), - } + }; + Ok(result) } #[dbus_interface(property)] - pub async fn interface(&self) -> String { - let connection = self.get_connection().await; - connection.interface.as_deref().unwrap_or("").to_string() + pub async fn interface(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.interface.unwrap_or_default()) } #[dbus_interface(property)] pub async fn set_interface(&mut self, name: &str) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.interface = Some(name.to_string()); - self.update_connection(connection).await + let interface = Some(name.to_string()); + self.update_connection(|c| c.interface = interface).await?; + Ok(()) } /// Custom mac-address #[dbus_interface(property)] - pub async fn mac_address(&self) -> String { - self.get_connection().await.mac_address.to_string() + pub async fn mac_address(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.mac_address.to_string()) } #[dbus_interface(property)] pub async fn set_mac_address(&mut self, mac_address: &str) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.mac_address = MacAddress::from_str(mac_address)?; - self.update_connection(connection).await + let mac_address = MacAddress::from_str(mac_address)?; + self.update_connection(|c| c.mac_address = mac_address) + .await?; + Ok(()) } + /// Whether the network interface should be active or not #[dbus_interface(property)] - pub async fn active(&self) -> bool { - let connection = self.get_connection().await; - connection.is_up() + pub async fn active(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.is_up()) } #[dbus_interface(property)] pub async fn set_active(&mut self, active: bool) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - if active { - connection.set_up(); - } else { - connection.set_down(); - } - self.update_connection(connection).await + self.update_connection(|c| { + if active { + c.set_up(); + } else { + c.set_down(); + } + }) + .await?; + Ok(()) } } From c2d906edc9b589f5f9092ba60316dbde8f10615a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 17:17:00 +0000 Subject: [PATCH 05/14] Split interfaces.rs into smaller files --- .../src/network/dbus/interfaces.rs | 647 +----------------- .../dbus/interfaces/connection_configs.rs | 253 +++++++ .../network/dbus/interfaces/connections.rs | 330 +++++++++ .../src/network/dbus/interfaces/devices.rs | 70 ++ 4 files changed, 659 insertions(+), 641 deletions(-) create mode 100644 rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs create mode 100644 rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs create mode 100644 rust/agama-dbus-server/src/network/dbus/interfaces/devices.rs diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index b7ba282f88..c803941132 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -3,646 +3,11 @@ //! This module contains the set of D-Bus interfaces that are exposed by [D-Bus network //! service](crate::NetworkService). -use super::ObjectsRegistry; -use crate::network::{ - action::Action, - error::NetworkStateError, - model::{ - BondConfig, Connection as NetworkConnection, ConnectionConfig, Device as NetworkDevice, - MacAddress, SecurityProtocol, WirelessConfig, WirelessMode, - }, -}; - -use agama_lib::network::types::SSID; -use std::{str::FromStr, sync::Arc}; -use tokio::sync::{mpsc::UnboundedSender, oneshot}; -use tokio::sync::{MappedMutexGuard, Mutex, MutexGuard}; -use uuid::Uuid; -use zbus::{ - dbus_interface, - zvariant::{ObjectPath, OwnedObjectPath}, - SignalContext, -}; - +mod connection_configs; +mod connections; +mod devices; mod ip_config; +pub use connection_configs::{Bond, Wireless}; +pub use connections::{Connection, Connections, Match}; +pub use devices::{Device, Devices}; pub use ip_config::Ip; - -/// D-Bus interface for the network devices collection -/// -/// It offers an API to query the devices collection. -pub struct Devices { - objects: Arc>, -} - -impl Devices { - /// Creates a Devices interface object. - /// - /// * `objects`: Objects paths registry. - pub fn new(objects: Arc>) -> Self { - Self { objects } - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Devices")] -impl Devices { - /// Returns the D-Bus paths of the network devices. - pub async fn get_devices(&self) -> Vec { - let objects = self.objects.lock().await; - objects - .devices_paths() - .iter() - .filter_map(|c| ObjectPath::try_from(c.clone()).ok()) - .collect() - } -} - -/// D-Bus interface for a network device -/// -/// It offers an API to query basic networking devices information (e.g., the name). -pub struct Device { - device: NetworkDevice, -} - -impl Device { - /// Creates an interface object. - /// - /// * `device`: network device. - pub fn new(device: NetworkDevice) -> Self { - Self { device } - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Device")] -impl Device { - /// Device name. - /// - /// Kernel device name, e.g., eth0, enp1s0, etc. - #[dbus_interface(property)] - pub fn name(&self) -> &str { - &self.device.name - } - - /// Device type. - /// - /// Possible values: 0 = loopback, 1 = ethernet, 2 = wireless. - /// - /// See [agama_lib::network::types::DeviceType]. - #[dbus_interface(property, name = "Type")] - pub fn device_type(&self) -> u8 { - self.device.type_ as u8 - } -} - -/// D-Bus interface for the set of connections. -/// -/// It offers an API to query the connections collection. -pub struct Connections { - actions: Arc>>, - objects: Arc>, -} - -impl Connections { - /// Creates a Connections interface object. - /// - /// * `objects`: Objects paths registry. - pub fn new(objects: Arc>, actions: UnboundedSender) -> Self { - Self { - objects, - actions: Arc::new(Mutex::new(actions)), - } - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Connections")] -impl Connections { - /// Returns the D-Bus paths of the network connections. - pub async fn get_connections(&self) -> Vec { - let objects = self.objects.lock().await; - objects - .connections_paths() - .iter() - .filter_map(|c| ObjectPath::try_from(c.clone()).ok()) - .collect() - } - - /// Adds a new network connection. - /// - /// * `id`: connection name. - /// * `ty`: connection type (see [agama_lib::network::types::DeviceType]). - pub async fn add_connection( - &mut self, - id: String, - ty: u8, - ) -> zbus::fdo::Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions - .send(Action::AddConnection(id.clone(), ty.try_into()?, tx)) - .unwrap(); - let result = rx.await.unwrap()?; - Ok(result) - } - - /// Returns the D-Bus path of the network connection. - /// - /// * `id`: connection ID. - pub async fn get_connection(&self, id: &str) -> zbus::fdo::Result { - let objects = self.objects.lock().await; - match objects.connection_path(id) { - Some(path) => Ok(path.into()), - None => Err(NetworkStateError::UnknownConnection(id.to_string()).into()), - } - } - - /// Removes a network connection. - /// - /// * `uuid`: connection UUID.. - pub async fn remove_connection(&mut self, id: &str) -> zbus::fdo::Result<()> { - let actions = self.actions.lock().await; - actions - .send(Action::RemoveConnection(id.to_string())) - .unwrap(); - Ok(()) - } - - /// Applies the network configuration. - /// - /// It includes adding, updating and removing connections as needed. - pub async fn apply(&self) -> zbus::fdo::Result<()> { - let actions = self.actions.lock().await; - actions.send(Action::Apply).unwrap(); - Ok(()) - } - - /// Notifies than a new interface has been added. - #[dbus_interface(signal)] - pub async fn connection_added( - ctxt: &SignalContext<'_>, - id: &str, - path: &ObjectPath<'_>, - ) -> zbus::Result<()>; -} - -/// D-Bus interface for a network connection -/// -/// It offers an API to query a connection. -pub struct Connection { - actions: Arc>>, - uuid: Uuid, -} - -impl Connection { - /// Creates a Connection interface object. - /// - /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. - pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { - Self { - actions: Arc::new(Mutex::new(actions)), - uuid, - } - } - - /// Returns the underlying connection. - async fn get_connection(&self) -> Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); - rx.await - .unwrap() - .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) - } - - /// Updates the connection data in the NetworkSystem. - /// - /// * `connection`: Updated connection. - pub async fn update_connection(&self, func: F) -> Result<(), NetworkStateError> - where - F: FnOnce(&mut NetworkConnection), - { - let mut connection = self.get_connection().await?; - func(&mut connection); - let actions = self.actions.lock().await; - actions.send(Action::UpdateConnection(connection)).unwrap(); - Ok(()) - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection")] -impl Connection { - /// Connection ID. - /// - /// Unique identifier of the network connection. It may or not be the same that the used by the - /// backend. For instance, when using NetworkManager (which is the only supported backend by - /// now), it uses the original ID but appending a number in case the ID is duplicated. - #[dbus_interface(property)] - pub async fn id(&self) -> zbus::fdo::Result { - let connection = self.get_connection().await?; - Ok(connection.id) - } - - /// Connection UUID. - /// - /// Unique identifier of the network connection. It may or not be the same that the used by the - /// backend. - #[dbus_interface(property)] - pub async fn uuid(&self) -> String { - self.uuid.to_string() - } - - #[dbus_interface(property)] - pub async fn controller(&self) -> zbus::fdo::Result { - let connection = self.get_connection().await?; - let result = match connection.controller { - Some(uuid) => uuid.to_string(), - None => "".to_string(), - }; - Ok(result) - } - - #[dbus_interface(property)] - pub async fn interface(&self) -> zbus::fdo::Result { - let connection = self.get_connection().await?; - Ok(connection.interface.unwrap_or_default()) - } - - #[dbus_interface(property)] - pub async fn set_interface(&mut self, name: &str) -> zbus::fdo::Result<()> { - let interface = Some(name.to_string()); - self.update_connection(|c| c.interface = interface).await?; - Ok(()) - } - - /// Custom mac-address - #[dbus_interface(property)] - pub async fn mac_address(&self) -> zbus::fdo::Result { - let connection = self.get_connection().await?; - Ok(connection.mac_address.to_string()) - } - - #[dbus_interface(property)] - pub async fn set_mac_address(&mut self, mac_address: &str) -> zbus::fdo::Result<()> { - let mac_address = MacAddress::from_str(mac_address)?; - self.update_connection(|c| c.mac_address = mac_address) - .await?; - Ok(()) - } - - /// Whether the network interface should be active or not - #[dbus_interface(property)] - pub async fn active(&self) -> zbus::fdo::Result { - let connection = self.get_connection().await?; - Ok(connection.is_up()) - } - - #[dbus_interface(property)] - pub async fn set_active(&mut self, active: bool) -> zbus::fdo::Result<()> { - self.update_connection(|c| { - if active { - c.set_up(); - } else { - c.set_down(); - } - }) - .await?; - Ok(()) - } -} - -/// D-Bus interface for Match settings -pub struct Match { - actions: Arc>>, - connection: Arc>, -} - -impl Match { - /// Creates a Match Settings interface object. - /// - /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. - pub fn new( - actions: UnboundedSender, - connection: Arc>, - ) -> Self { - Self { - actions: Arc::new(Mutex::new(actions)), - connection, - } - } - - /// Returns the underlying connection. - async fn get_connection(&self) -> MutexGuard { - self.connection.lock().await - } - - /// Updates the connection data in the NetworkSystem. - /// - /// * `connection`: Updated connection. - async fn update_connection<'a>( - &self, - connection: MutexGuard<'a, NetworkConnection>, - ) -> zbus::fdo::Result<()> { - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(connection.clone())) - .unwrap(); - Ok(()) - } -} - -/// D-Bus interface for Bond settings. -pub struct Bond { - actions: Arc>>, - uuid: Uuid, -} - -impl Bond { - /// Creates a Bond interface object. - /// - /// * `actions`: sending-half of a channel to send actions. - /// * `uuid`: connection UUID. - pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { - Self { - actions: Arc::new(Mutex::new(actions)), - uuid, - } - } - - /// Gets the connection. - async fn get_connection(&self) -> Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); - rx.await - .unwrap() - .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) - } - - /// Gets the bonding configuration. - pub async fn get_config(&self) -> Result { - let connection = self.get_connection().await?; - match connection.config { - ConnectionConfig::Bond(bond) => Ok(bond), - _ => panic!("Not a bond connection. This is most probably a bug."), - } - } - - /// Updates the bond configuration. - /// - /// * `func`: function to update the configuration. - pub async fn update_config(&self, func: F) -> Result<(), NetworkStateError> - where - F: FnOnce(&mut BondConfig), - { - let mut connection = self.get_connection().await?; - match &mut connection.config { - ConnectionConfig::Bond(bond) => func(bond), - _ => panic!("Not a bond connection. This is most probably a bug."), - } - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(connection.clone())) - .unwrap(); - Ok(()) - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Bond")] -impl Bond { - /// Bonding mode. - #[dbus_interface(property)] - pub async fn mode(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; - Ok(config.mode.to_string()) - } - - #[dbus_interface(property)] - pub async fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { - let mode = mode.try_into()?; - self.update_config(|c| c.mode = mode).await?; - Ok(()) - } - - /// List of bonding options. - #[dbus_interface(property)] - pub async fn options(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; - Ok(config.options.to_string()) - } - - #[dbus_interface(property)] - pub async fn set_options(&mut self, opts: &str) -> zbus::fdo::Result<()> { - let opts = opts.try_into()?; - self.update_config(|c| c.options = opts).await?; - Ok(()) - } - - /// List of bond ports. - /// - /// For the port names, it uses the interface name (preferred) or, as a fallback, - /// the connection ID of the port. - #[dbus_interface(property)] - pub async fn ports(&self) -> zbus::fdo::Result> { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetController(self.uuid, tx)).unwrap(); - - let (_, ports) = rx.await.unwrap()?; - Ok(ports) - } - - #[dbus_interface(property)] - pub async fn set_ports(&mut self, ports: Vec) -> zbus::fdo::Result<()> { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions - .send(Action::SetPorts(self.uuid, ports, tx)) - .unwrap(); - let result = rx.await.unwrap(); - Ok(result?) - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Match")] -impl Match { - /// List of driver names to match. - #[dbus_interface(property)] - pub async fn driver(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.driver.clone() - } - - #[dbus_interface(property)] - pub async fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.driver = driver; - self.update_connection(connection).await - } - - /// List of paths to match agains the ID_PATH udev property of devices. - #[dbus_interface(property)] - pub async fn path(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.path.clone() - } - - #[dbus_interface(property)] - pub async fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.path = path; - self.update_connection(connection).await - } - /// List of interface names to match. - #[dbus_interface(property)] - pub async fn interface(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.interface.clone() - } - - #[dbus_interface(property)] - pub async fn set_interface(&mut self, interface: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.interface = interface; - self.update_connection(connection).await - } - - /// List of kernel options to match. - #[dbus_interface(property)] - pub async fn kernel(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.kernel.clone() - } - - #[dbus_interface(property)] - pub async fn set_kernel(&mut self, kernel: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.kernel = kernel; - self.update_connection(connection).await - } -} - -/// D-Bus interface for wireless settings -pub struct Wireless { - actions: Arc>>, - connection: Arc>, -} - -impl Wireless { - /// Creates a Wireless interface object. - /// - /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. - pub fn new( - actions: UnboundedSender, - connection: Arc>, - ) -> Self { - Self { - actions: Arc::new(Mutex::new(actions)), - connection, - } - } - - /// Gets the wireless connection. - /// - /// Beware that it crashes when it is not a wireless connection. - async fn get_wireless(&self) -> MappedMutexGuard { - MutexGuard::map(self.connection.lock().await, |c| match &mut c.config { - ConnectionConfig::Wireless(config) => config, - _ => panic!("Not a wireless connection. This is most probably a bug."), - }) - } - - /// Updates the wireless configuration. - /// - /// * `func`: function to update the configuration. - pub async fn update_config(&self, func: F) -> Result<(), NetworkStateError> - where - F: FnOnce(&mut WirelessConfig), - { - let mut connection = self.connection.lock().await; - match &mut connection.config { - ConnectionConfig::Wireless(wireless) => func(wireless), - _ => panic!("Not a wireless connection. This is most probably a bug."), - } - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(connection.clone())) - .unwrap(); - Ok(()) - } -} - -#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Wireless")] -impl Wireless { - /// Network SSID. - #[dbus_interface(property, name = "SSID")] - pub async fn ssid(&self) -> Vec { - let wireless = self.get_wireless().await; - wireless.ssid.clone().into() - } - - #[dbus_interface(property, name = "SSID")] - pub async fn set_ssid(&mut self, ssid: Vec) -> zbus::fdo::Result<()> { - self.update_config(|c| c.ssid = SSID(ssid)).await?; - Ok(()) - } - - /// Wireless connection mode. - /// - /// Possible values: "unknown", "adhoc", "infrastructure", "ap" or "mesh". - /// - /// See [crate::network::model::WirelessMode]. - #[dbus_interface(property)] - pub async fn mode(&self) -> String { - let wireless = self.get_wireless().await; - wireless.mode.to_string() - } - - #[dbus_interface(property)] - pub async fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { - let mode: WirelessMode = mode.try_into()?; - self.update_config(|c| c.mode = mode).await?; - Ok(()) - } - - /// Password to connect to the wireless network. - #[dbus_interface(property)] - pub async fn password(&self) -> String { - let wireless = self.get_wireless().await; - wireless.password.clone().unwrap_or("".to_string()) - } - - #[dbus_interface(property)] - pub async fn set_password(&mut self, password: String) -> zbus::fdo::Result<()> { - self.update_config(|c| { - c.password = if password.is_empty() { - None - } else { - Some(password) - }; - }) - .await?; - Ok(()) - } - - /// Wireless security protocol. - /// - /// Possible values: "none", "owe", "ieee8021x", "wpa-psk", "sae", "wpa-eap", - /// "wpa-eap-suite-b192". - /// - /// See [crate::network::model::SecurityProtocol]. - #[dbus_interface(property)] - pub async fn security(&self) -> String { - let wireless = self.get_wireless().await; - wireless.security.to_string() - } - - #[dbus_interface(property)] - pub async fn set_security(&mut self, security: &str) -> zbus::fdo::Result<()> { - let security: SecurityProtocol = security - .try_into() - .map_err(|_| NetworkStateError::InvalidSecurityProtocol(security.to_string()))?; - self.update_config(|c| c.security = security).await?; - Ok(()) - } -} diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs new file mode 100644 index 0000000000..48f2da1b77 --- /dev/null +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs @@ -0,0 +1,253 @@ +use agama_lib::network::types::SSID; +use std::sync::Arc; +use tokio::sync::{mpsc::UnboundedSender, oneshot, MappedMutexGuard, Mutex, MutexGuard}; +use uuid::Uuid; +use zbus::dbus_interface; + +use crate::network::{ + action::Action, + error::NetworkStateError, + model::{ + BondConfig, Connection as NetworkConnection, ConnectionConfig, SecurityProtocol, + WirelessConfig, WirelessMode, + }, +}; + +/// D-Bus interface for Bond settings. +pub struct Bond { + actions: Arc>>, + uuid: Uuid, +} + +impl Bond { + /// Creates a Bond interface object. + /// + /// * `actions`: sending-half of a channel to send actions. + /// * `uuid`: connection UUID. + pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { + Self { + actions: Arc::new(Mutex::new(actions)), + uuid, + } + } + + /// Gets the connection. + async fn get_connection(&self) -> Result { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); + rx.await + .unwrap() + .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) + } + + /// Gets the bonding configuration. + pub async fn get_config(&self) -> Result { + let connection = self.get_connection().await?; + match connection.config { + ConnectionConfig::Bond(bond) => Ok(bond), + _ => panic!("Not a bond connection. This is most probably a bug."), + } + } + + /// Updates the bond configuration. + /// + /// * `func`: function to update the configuration. + pub async fn update_config(&self, func: F) -> Result<(), NetworkStateError> + where + F: FnOnce(&mut BondConfig), + { + let mut connection = self.get_connection().await?; + match &mut connection.config { + ConnectionConfig::Bond(bond) => func(bond), + _ => panic!("Not a bond connection. This is most probably a bug."), + } + let actions = self.actions.lock().await; + actions + .send(Action::UpdateConnection(connection.clone())) + .unwrap(); + Ok(()) + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Bond")] +impl Bond { + /// Bonding mode. + #[dbus_interface(property)] + pub async fn mode(&self) -> zbus::fdo::Result { + let config = self.get_config().await?; + Ok(config.mode.to_string()) + } + + #[dbus_interface(property)] + pub async fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { + let mode = mode.try_into()?; + self.update_config(|c| c.mode = mode).await?; + Ok(()) + } + + /// List of bonding options. + #[dbus_interface(property)] + pub async fn options(&self) -> zbus::fdo::Result { + let config = self.get_config().await?; + Ok(config.options.to_string()) + } + + #[dbus_interface(property)] + pub async fn set_options(&mut self, opts: &str) -> zbus::fdo::Result<()> { + let opts = opts.try_into()?; + self.update_config(|c| c.options = opts).await?; + Ok(()) + } + + /// List of bond ports. + /// + /// For the port names, it uses the interface name (preferred) or, as a fallback, + /// the connection ID of the port. + #[dbus_interface(property)] + pub async fn ports(&self) -> zbus::fdo::Result> { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions.send(Action::GetController(self.uuid, tx)).unwrap(); + + let (_, ports) = rx.await.unwrap()?; + Ok(ports) + } + + #[dbus_interface(property)] + pub async fn set_ports(&mut self, ports: Vec) -> zbus::fdo::Result<()> { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions + .send(Action::SetPorts(self.uuid, ports, tx)) + .unwrap(); + let result = rx.await.unwrap(); + Ok(result?) + } +} + +/// D-Bus interface for wireless settings +pub struct Wireless { + actions: Arc>>, + connection: Arc>, +} + +impl Wireless { + /// Creates a Wireless interface object. + /// + /// * `actions`: sending-half of a channel to send actions. + /// * `connection`: connection to expose over D-Bus. + pub fn new( + actions: UnboundedSender, + connection: Arc>, + ) -> Self { + Self { + actions: Arc::new(Mutex::new(actions)), + connection, + } + } + + /// Gets the wireless connection. + /// + /// Beware that it crashes when it is not a wireless connection. + async fn get_wireless(&self) -> MappedMutexGuard { + MutexGuard::map(self.connection.lock().await, |c| match &mut c.config { + ConnectionConfig::Wireless(config) => config, + _ => panic!("Not a wireless connection. This is most probably a bug."), + }) + } + + /// Updates the wireless configuration. + /// + /// * `func`: function to update the configuration. + pub async fn update_config(&self, func: F) -> Result<(), NetworkStateError> + where + F: FnOnce(&mut WirelessConfig), + { + let mut connection = self.connection.lock().await; + match &mut connection.config { + ConnectionConfig::Wireless(wireless) => func(wireless), + _ => panic!("Not a wireless connection. This is most probably a bug."), + } + let actions = self.actions.lock().await; + actions + .send(Action::UpdateConnection(connection.clone())) + .unwrap(); + Ok(()) + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Wireless")] +impl Wireless { + /// Network SSID. + #[dbus_interface(property, name = "SSID")] + pub async fn ssid(&self) -> Vec { + let wireless = self.get_wireless().await; + wireless.ssid.clone().into() + } + + #[dbus_interface(property, name = "SSID")] + pub async fn set_ssid(&mut self, ssid: Vec) -> zbus::fdo::Result<()> { + self.update_config(|c| c.ssid = SSID(ssid)).await?; + Ok(()) + } + + /// Wireless connection mode. + /// + /// Possible values: "unknown", "adhoc", "infrastructure", "ap" or "mesh". + /// + /// See [crate::network::model::WirelessMode]. + #[dbus_interface(property)] + pub async fn mode(&self) -> String { + let wireless = self.get_wireless().await; + wireless.mode.to_string() + } + + #[dbus_interface(property)] + pub async fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { + let mode: WirelessMode = mode.try_into()?; + self.update_config(|c| c.mode = mode).await?; + Ok(()) + } + + /// Password to connect to the wireless network. + #[dbus_interface(property)] + pub async fn password(&self) -> String { + let wireless = self.get_wireless().await; + wireless.password.clone().unwrap_or("".to_string()) + } + + #[dbus_interface(property)] + pub async fn set_password(&mut self, password: String) -> zbus::fdo::Result<()> { + self.update_config(|c| { + c.password = if password.is_empty() { + None + } else { + Some(password) + }; + }) + .await?; + Ok(()) + } + + /// Wireless security protocol. + /// + /// Possible values: "none", "owe", "ieee8021x", "wpa-psk", "sae", "wpa-eap", + /// "wpa-eap-suite-b192". + /// + /// See [crate::network::model::SecurityProtocol]. + #[dbus_interface(property)] + pub async fn security(&self) -> String { + let wireless = self.get_wireless().await; + wireless.security.to_string() + } + + #[dbus_interface(property)] + pub async fn set_security(&mut self, security: &str) -> zbus::fdo::Result<()> { + let security: SecurityProtocol = security + .try_into() + .map_err(|_| NetworkStateError::InvalidSecurityProtocol(security.to_string()))?; + self.update_config(|c| c.security = security).await?; + Ok(()) + } +} diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs new file mode 100644 index 0000000000..029c10365c --- /dev/null +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs @@ -0,0 +1,330 @@ +use std::{str::FromStr, sync::Arc}; +use tokio::sync::{mpsc::UnboundedSender, oneshot, Mutex, MutexGuard}; +use uuid::Uuid; +use zbus::{ + dbus_interface, + zvariant::{ObjectPath, OwnedObjectPath}, + SignalContext, +}; + +use crate::network::{ + dbus::ObjectsRegistry, + error::NetworkStateError, + model::{Connection as NetworkConnection, MacAddress}, + Action, +}; + +/// D-Bus interface for the set of connections. +/// +/// It offers an API to query the connections collection. +pub struct Connections { + actions: Arc>>, + objects: Arc>, +} + +impl Connections { + /// Creates a Connections interface object. + /// + /// * `objects`: Objects paths registry. + pub fn new(objects: Arc>, actions: UnboundedSender) -> Self { + Self { + objects, + actions: Arc::new(Mutex::new(actions)), + } + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Connections")] +impl Connections { + /// Returns the D-Bus paths of the network connections. + pub async fn get_connections(&self) -> Vec { + let objects = self.objects.lock().await; + objects + .connections_paths() + .iter() + .filter_map(|c| ObjectPath::try_from(c.clone()).ok()) + .collect() + } + + /// Adds a new network connection. + /// + /// * `id`: connection name. + /// * `ty`: connection type (see [agama_lib::network::types::DeviceType]). + pub async fn add_connection( + &mut self, + id: String, + ty: u8, + ) -> zbus::fdo::Result { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions + .send(Action::AddConnection(id.clone(), ty.try_into()?, tx)) + .unwrap(); + let result = rx.await.unwrap()?; + Ok(result) + } + + /// Returns the D-Bus path of the network connection. + /// + /// * `id`: connection ID. + pub async fn get_connection(&self, id: &str) -> zbus::fdo::Result { + let objects = self.objects.lock().await; + match objects.connection_path(id) { + Some(path) => Ok(path.into()), + None => Err(NetworkStateError::UnknownConnection(id.to_string()).into()), + } + } + + /// Removes a network connection. + /// + /// * `uuid`: connection UUID.. + pub async fn remove_connection(&mut self, id: &str) -> zbus::fdo::Result<()> { + let actions = self.actions.lock().await; + actions + .send(Action::RemoveConnection(id.to_string())) + .unwrap(); + Ok(()) + } + + /// Applies the network configuration. + /// + /// It includes adding, updating and removing connections as needed. + pub async fn apply(&self) -> zbus::fdo::Result<()> { + let actions = self.actions.lock().await; + actions.send(Action::Apply).unwrap(); + Ok(()) + } + + /// Notifies than a new interface has been added. + #[dbus_interface(signal)] + pub async fn connection_added( + ctxt: &SignalContext<'_>, + id: &str, + path: &ObjectPath<'_>, + ) -> zbus::Result<()>; +} + +/// D-Bus interface for a network connection +/// +/// It offers an API to query a connection. +pub struct Connection { + actions: Arc>>, + uuid: Uuid, +} + +impl Connection { + /// Creates a Connection interface object. + /// + /// * `actions`: sending-half of a channel to send actions. + /// * `connection`: connection to expose over D-Bus. + pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { + Self { + actions: Arc::new(Mutex::new(actions)), + uuid, + } + } + + /// Returns the underlying connection. + async fn get_connection(&self) -> Result { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); + rx.await + .unwrap() + .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) + } + + /// Updates the connection data in the NetworkSystem. + /// + /// * `connection`: Updated connection. + pub async fn update_connection(&self, func: F) -> Result<(), NetworkStateError> + where + F: FnOnce(&mut NetworkConnection), + { + let mut connection = self.get_connection().await?; + func(&mut connection); + let actions = self.actions.lock().await; + actions.send(Action::UpdateConnection(connection)).unwrap(); + Ok(()) + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection")] +impl Connection { + /// Connection ID. + /// + /// Unique identifier of the network connection. It may or not be the same that the used by the + /// backend. For instance, when using NetworkManager (which is the only supported backend by + /// now), it uses the original ID but appending a number in case the ID is duplicated. + #[dbus_interface(property)] + pub async fn id(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.id) + } + + /// Connection UUID. + /// + /// Unique identifier of the network connection. It may or not be the same that the used by the + /// backend. + #[dbus_interface(property)] + pub async fn uuid(&self) -> String { + self.uuid.to_string() + } + + #[dbus_interface(property)] + pub async fn controller(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + let result = match connection.controller { + Some(uuid) => uuid.to_string(), + None => "".to_string(), + }; + Ok(result) + } + + #[dbus_interface(property)] + pub async fn interface(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.interface.unwrap_or_default()) + } + + #[dbus_interface(property)] + pub async fn set_interface(&mut self, name: &str) -> zbus::fdo::Result<()> { + let interface = Some(name.to_string()); + self.update_connection(|c| c.interface = interface).await?; + Ok(()) + } + + /// Custom mac-address + #[dbus_interface(property)] + pub async fn mac_address(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.mac_address.to_string()) + } + + #[dbus_interface(property)] + pub async fn set_mac_address(&mut self, mac_address: &str) -> zbus::fdo::Result<()> { + let mac_address = MacAddress::from_str(mac_address)?; + self.update_connection(|c| c.mac_address = mac_address) + .await?; + Ok(()) + } + + /// Whether the network interface should be active or not + #[dbus_interface(property)] + pub async fn active(&self) -> zbus::fdo::Result { + let connection = self.get_connection().await?; + Ok(connection.is_up()) + } + + #[dbus_interface(property)] + pub async fn set_active(&mut self, active: bool) -> zbus::fdo::Result<()> { + self.update_connection(|c| { + if active { + c.set_up(); + } else { + c.set_down(); + } + }) + .await?; + Ok(()) + } +} + +/// D-Bus interface for Match settings +pub struct Match { + actions: Arc>>, + connection: Arc>, +} + +impl Match { + /// Creates a Match Settings interface object. + /// + /// * `actions`: sending-half of a channel to send actions. + /// * `connection`: connection to expose over D-Bus. + pub fn new( + actions: UnboundedSender, + connection: Arc>, + ) -> Self { + Self { + actions: Arc::new(Mutex::new(actions)), + connection, + } + } + + /// Returns the underlying connection. + async fn get_connection(&self) -> MutexGuard { + self.connection.lock().await + } + + /// Updates the connection data in the NetworkSystem. + /// + /// * `connection`: Updated connection. + async fn update_connection<'a>( + &self, + connection: MutexGuard<'a, NetworkConnection>, + ) -> zbus::fdo::Result<()> { + let actions = self.actions.lock().await; + actions + .send(Action::UpdateConnection(connection.clone())) + .unwrap(); + Ok(()) + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Match")] +impl Match { + /// List of driver names to match. + #[dbus_interface(property)] + pub async fn driver(&self) -> Vec { + let connection = self.get_connection().await; + connection.match_config.driver.clone() + } + + #[dbus_interface(property)] + pub async fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + connection.match_config.driver = driver; + self.update_connection(connection).await + } + + /// List of paths to match agains the ID_PATH udev property of devices. + #[dbus_interface(property)] + pub async fn path(&self) -> Vec { + let connection = self.get_connection().await; + connection.match_config.path.clone() + } + + #[dbus_interface(property)] + pub async fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + connection.match_config.path = path; + self.update_connection(connection).await + } + /// List of interface names to match. + #[dbus_interface(property)] + pub async fn interface(&self) -> Vec { + let connection = self.get_connection().await; + connection.match_config.interface.clone() + } + + #[dbus_interface(property)] + pub async fn set_interface(&mut self, interface: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + connection.match_config.interface = interface; + self.update_connection(connection).await + } + + /// List of kernel options to match. + #[dbus_interface(property)] + pub async fn kernel(&self) -> Vec { + let connection = self.get_connection().await; + connection.match_config.kernel.clone() + } + + #[dbus_interface(property)] + pub async fn set_kernel(&mut self, kernel: Vec) -> zbus::fdo::Result<()> { + let mut connection = self.get_connection().await; + connection.match_config.kernel = kernel; + self.update_connection(connection).await + } +} diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/devices.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/devices.rs new file mode 100644 index 0000000000..41339e01e5 --- /dev/null +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/devices.rs @@ -0,0 +1,70 @@ +use crate::network::{dbus::ObjectsRegistry, model::Device as NetworkDevice}; +use std::sync::Arc; +use tokio::sync::Mutex; +use zbus::{dbus_interface, zvariant::ObjectPath}; + +/// D-Bus interface for the network devices collection +/// +/// It offers an API to query the devices collection. +pub struct Devices { + objects: Arc>, +} + +impl Devices { + /// Creates a Devices interface object. + /// + /// * `objects`: Objects paths registry. + pub fn new(objects: Arc>) -> Self { + Self { objects } + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Devices")] +impl Devices { + /// Returns the D-Bus paths of the network devices. + pub async fn get_devices(&self) -> Vec { + let objects = self.objects.lock().await; + objects + .devices_paths() + .iter() + .filter_map(|c| ObjectPath::try_from(c.clone()).ok()) + .collect() + } +} + +/// D-Bus interface for a network device +/// +/// It offers an API to query basic networking devices information (e.g., the name). +pub struct Device { + device: NetworkDevice, +} + +impl Device { + /// Creates an interface object. + /// + /// * `device`: network device. + pub fn new(device: NetworkDevice) -> Self { + Self { device } + } +} + +#[dbus_interface(name = "org.opensuse.Agama1.Network.Device")] +impl Device { + /// Device name. + /// + /// Kernel device name, e.g., eth0, enp1s0, etc. + #[dbus_interface(property)] + pub fn name(&self) -> &str { + &self.device.name + } + + /// Device type. + /// + /// Possible values: 0 = loopback, 1 = ethernet, 2 = wireless. + /// + /// See [agama_lib::network::types::DeviceType]. + #[dbus_interface(property, name = "Type")] + pub fn device_type(&self) -> u8 { + self.device.type_ as u8 + } +} From 932de660ea5b485ec9ade0f43c2020221fd9ebc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 17:31:48 +0000 Subject: [PATCH 06/14] Use channels in the *.Connection.Wireless D-Bus interface --- .../dbus/interfaces/connection_configs.rs | 66 ++++++++++--------- .../src/network/dbus/tree.rs | 7 +- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs index 48f2da1b77..76e4bf2c77 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs @@ -1,6 +1,6 @@ use agama_lib::network::types::SSID; use std::sync::Arc; -use tokio::sync::{mpsc::UnboundedSender, oneshot, MappedMutexGuard, Mutex, MutexGuard}; +use tokio::sync::{mpsc::UnboundedSender, oneshot, Mutex}; use uuid::Uuid; use zbus::dbus_interface; @@ -129,32 +129,38 @@ impl Bond { /// D-Bus interface for wireless settings pub struct Wireless { actions: Arc>>, - connection: Arc>, + uuid: Uuid, } impl Wireless { /// Creates a Wireless interface object. /// /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. - pub fn new( - actions: UnboundedSender, - connection: Arc>, - ) -> Self { + /// * `uuid`: connection UUID. + pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { Self { actions: Arc::new(Mutex::new(actions)), - connection, + uuid, } } - /// Gets the wireless connection. - /// - /// Beware that it crashes when it is not a wireless connection. - async fn get_wireless(&self) -> MappedMutexGuard { - MutexGuard::map(self.connection.lock().await, |c| match &mut c.config { - ConnectionConfig::Wireless(config) => config, - _ => panic!("Not a wireless connection. This is most probably a bug."), - }) + /// Gets the connection. + async fn get_connection(&self) -> Result { + let actions = self.actions.lock().await; + let (tx, rx) = oneshot::channel(); + actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); + rx.await + .unwrap() + .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) + } + + /// Gets the wireless configuration. + pub async fn get_config(&self) -> Result { + let connection = self.get_connection().await?; + match connection.config { + ConnectionConfig::Wireless(wireless) => Ok(wireless), + _ => panic!("Not a bond connection. This is most probably a bug."), + } } /// Updates the wireless configuration. @@ -164,10 +170,10 @@ impl Wireless { where F: FnOnce(&mut WirelessConfig), { - let mut connection = self.connection.lock().await; + let mut connection = self.get_connection().await?; match &mut connection.config { ConnectionConfig::Wireless(wireless) => func(wireless), - _ => panic!("Not a wireless connection. This is most probably a bug."), + _ => panic!("Not a bond connection. This is most probably a bug."), } let actions = self.actions.lock().await; actions @@ -181,9 +187,9 @@ impl Wireless { impl Wireless { /// Network SSID. #[dbus_interface(property, name = "SSID")] - pub async fn ssid(&self) -> Vec { - let wireless = self.get_wireless().await; - wireless.ssid.clone().into() + pub async fn ssid(&self) -> zbus::fdo::Result> { + let config = self.get_config().await?; + Ok(config.ssid.into()) } #[dbus_interface(property, name = "SSID")] @@ -198,9 +204,9 @@ impl Wireless { /// /// See [crate::network::model::WirelessMode]. #[dbus_interface(property)] - pub async fn mode(&self) -> String { - let wireless = self.get_wireless().await; - wireless.mode.to_string() + pub async fn mode(&self) -> zbus::fdo::Result { + let config = self.get_config().await?; + Ok(config.mode.to_string()) } #[dbus_interface(property)] @@ -212,9 +218,9 @@ impl Wireless { /// Password to connect to the wireless network. #[dbus_interface(property)] - pub async fn password(&self) -> String { - let wireless = self.get_wireless().await; - wireless.password.clone().unwrap_or("".to_string()) + pub async fn password(&self) -> zbus::fdo::Result { + let config = self.get_config().await?; + Ok(config.password.unwrap_or_default()) } #[dbus_interface(property)] @@ -237,9 +243,9 @@ impl Wireless { /// /// See [crate::network::model::SecurityProtocol]. #[dbus_interface(property)] - pub async fn security(&self) -> String { - let wireless = self.get_wireless().await; - wireless.security.to_string() + pub async fn security(&self) -> zbus::fdo::Result { + let config = self.get_config().await?; + Ok(config.security.to_string()) } #[dbus_interface(property)] diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index 1c2c5612eb..7c84188b36 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -120,11 +120,8 @@ impl Tree { } if let ConnectionConfig::Wireless(_) = conn.config { - self.add_interface( - &path, - interfaces::Wireless::new(self.actions.clone(), Arc::clone(&cloned)), - ) - .await?; + self.add_interface(&path, interfaces::Wireless::new(self.actions.clone(), uuid)) + .await?; } if notify { From 116aaa9a40f9aa9095ec10a3471c10dcf2e2b161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 20 Dec 2023 16:27:58 +0000 Subject: [PATCH 07/14] Reduce the size of the Action enum variants * See https://rust-lang.github.io/rust-clippy/master/index.html#/large_enum_variant --- rust/agama-dbus-server/src/network/action.rs | 8 ++++++-- .../src/network/dbus/interfaces/connection_configs.rs | 6 +++--- .../src/network/dbus/interfaces/connections.rs | 6 ++++-- .../src/network/dbus/interfaces/ip_config.rs | 2 +- rust/agama-dbus-server/src/network/system.rs | 8 ++++---- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/rust/agama-dbus-server/src/network/action.rs b/rust/agama-dbus-server/src/network/action.rs index 137cd717a5..568410d579 100644 --- a/rust/agama-dbus-server/src/network/action.rs +++ b/rust/agama-dbus-server/src/network/action.rs @@ -30,9 +30,13 @@ pub enum Action { ), /// Sets a controller's ports. It uses the Uuid of the controller and the IDs or interface names /// of the ports. - SetPorts(Uuid, Vec, Responder>), + SetPorts( + Uuid, + Box>, + Responder>, + ), /// Update a connection (replacing the old one). - UpdateConnection(Connection), + UpdateConnection(Box), /// Remove the connection with the given Uuid. RemoveConnection(String), /// Apply the current configuration. diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs index 76e4bf2c77..5a2edad0f9 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs @@ -64,7 +64,7 @@ impl Bond { } let actions = self.actions.lock().await; actions - .send(Action::UpdateConnection(connection.clone())) + .send(Action::UpdateConnection(Box::new(connection.clone()))) .unwrap(); Ok(()) } @@ -119,7 +119,7 @@ impl Bond { let actions = self.actions.lock().await; let (tx, rx) = oneshot::channel(); actions - .send(Action::SetPorts(self.uuid, ports, tx)) + .send(Action::SetPorts(self.uuid, Box::new(ports), tx)) .unwrap(); let result = rx.await.unwrap(); Ok(result?) @@ -177,7 +177,7 @@ impl Wireless { } let actions = self.actions.lock().await; actions - .send(Action::UpdateConnection(connection.clone())) + .send(Action::UpdateConnection(Box::new(connection.clone()))) .unwrap(); Ok(()) } diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs index 029c10365c..2e04320fb0 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs @@ -144,7 +144,9 @@ impl Connection { let mut connection = self.get_connection().await?; func(&mut connection); let actions = self.actions.lock().await; - actions.send(Action::UpdateConnection(connection)).unwrap(); + actions + .send(Action::UpdateConnection(Box::new(connection))) + .unwrap(); Ok(()) } } @@ -265,7 +267,7 @@ impl Match { ) -> zbus::fdo::Result<()> { let actions = self.actions.lock().await; actions - .send(Action::UpdateConnection(connection.clone())) + .send(Action::UpdateConnection(Box::new(connection.clone()))) .unwrap(); Ok(()) } diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs index a0051c75c6..3b18337473 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs @@ -49,7 +49,7 @@ impl Ip { ) -> zbus::fdo::Result<()> { let actions = self.actions.lock().await; actions - .send(Action::UpdateConnection(connection.clone())) + .send(Action::UpdateConnection(Box::new(connection.clone()))) .unwrap(); Ok(()) } diff --git a/rust/agama-dbus-server/src/network/system.rs b/rust/agama-dbus-server/src/network/system.rs index 9e8de3e18d..ad44137fc7 100644 --- a/rust/agama-dbus-server/src/network/system.rs +++ b/rust/agama-dbus-server/src/network/system.rs @@ -81,12 +81,12 @@ impl NetworkSystem { let result = self.get_controller_action(uuid); tx.send(result).unwrap() } - Action::SetPorts(uuid, ports, tx) => { - let result = self.set_ports_action(uuid, ports); - tx.send(result).unwrap(); + Action::SetPorts(uuid, ports, rx) => { + let result = self.set_ports_action(uuid, *ports); + rx.send(result).unwrap(); } Action::UpdateConnection(conn) => { - self.state.update_connection(conn)?; + self.state.update_connection(*conn)?; } Action::RemoveConnection(id) => { self.tree.remove_connection(&id).await?; From ba90e0eb3c07d56c189fcf3e796d3b662a9bacf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Thu, 21 Dec 2023 13:33:30 +0000 Subject: [PATCH 08/14] Add some basic rustfmt configuration --- rust/rustfmt.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 rust/rustfmt.toml diff --git a/rust/rustfmt.toml b/rust/rustfmt.toml new file mode 100644 index 0000000000..3a26366d4d --- /dev/null +++ b/rust/rustfmt.toml @@ -0,0 +1 @@ +edition = "2021" From ac9b7915eb37b7a7d5a1a87d636f05748ff3b9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 26 Dec 2023 08:53:29 +0000 Subject: [PATCH 09/14] Use channels in the *.Connection.IPConfig interface --- .../src/network/dbus/interfaces/ip_config.rs | 97 +++++++++---------- .../src/network/dbus/tree.rs | 7 +- 2 files changed, 48 insertions(+), 56 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs index 3b18337473..cc9688f60b 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs @@ -6,18 +6,20 @@ //! to the `Ip` struct. use crate::network::{ action::Action, + error::NetworkStateError, model::{Connection as NetworkConnection, IpConfig, Ipv4Method, Ipv6Method}, }; use cidr::IpInet; use std::{net::IpAddr, sync::Arc}; -use tokio::sync::mpsc::UnboundedSender; -use tokio::sync::{MappedMutexGuard, Mutex, MutexGuard}; +use tokio::sync::Mutex; +use tokio::sync::{mpsc::UnboundedSender, oneshot}; +use uuid::Uuid; use zbus::dbus_interface; /// D-Bus interface for IPv4 and IPv6 settings pub struct Ip { actions: Arc>>, - connection: Arc>, + uuid: Uuid, } impl Ip { @@ -25,40 +27,26 @@ impl Ip { /// /// * `actions`: sending-half of a channel to send actions. /// * `connection`: connection to expose over D-Bus. - pub fn new( - actions: UnboundedSender, - connection: Arc>, - ) -> Self { + pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { Self { actions: Arc::new(Mutex::new(actions)), - connection, + uuid, } } - /// Returns the underlying connection. - async fn get_connection(&self) -> MutexGuard { - self.connection.lock().await - } - - /// Updates the connection data in the NetworkSystem. - /// - /// * `connection`: Updated connection. - async fn update_connection<'a>( - &self, - connection: MutexGuard<'a, NetworkConnection>, - ) -> zbus::fdo::Result<()> { + /// Gets the connection. + async fn get_connection(&self) -> Result { let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(Box::new(connection.clone()))) - .unwrap(); - Ok(()) + let (tx, rx) = oneshot::channel(); + actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); + rx.await + .unwrap() + .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) } -} -impl Ip { /// Returns the IpConfig struct. - async fn get_ip_config(&self) -> MappedMutexGuard { - MutexGuard::map(self.get_connection().await, |c| &mut c.ip_config) + async fn get_ip_config(&self) -> Result { + self.get_connection().await.map(|c| c.ip_config) } /// Updates the IpConfig struct. @@ -68,9 +56,12 @@ impl Ip { where F: Fn(&mut IpConfig), { - let mut connection = self.get_connection().await; + let mut connection = self.get_connection().await?; func(&mut connection.ip_config); - self.update_connection(connection).await?; + let actions = self.actions.lock().await; + actions + .send(Action::UpdateConnection(Box::new(connection.clone()))) + .unwrap(); Ok(()) } } @@ -81,9 +72,10 @@ impl Ip { /// /// When the method is 'auto', these addresses are used as additional addresses. #[dbus_interface(property)] - pub async fn addresses(&self) -> Vec { - let ip_config = self.get_ip_config().await; - ip_config.addresses.iter().map(|a| a.to_string()).collect() + pub async fn addresses(&self) -> zbus::fdo::Result> { + let ip_config = self.get_ip_config().await?; + let addresses = ip_config.addresses.iter().map(|a| a.to_string()).collect(); + Ok(addresses) } #[dbus_interface(property)] @@ -99,9 +91,9 @@ impl Ip { /// /// See [crate::network::model::Ipv4Method]. #[dbus_interface(property)] - pub async fn method4(&self) -> String { - let ip_config = self.get_ip_config().await; - ip_config.method4.to_string() + pub async fn method4(&self) -> zbus::fdo::Result { + let ip_config = self.get_ip_config().await?; + Ok(ip_config.method4.to_string()) } #[dbus_interface(property)] @@ -116,9 +108,9 @@ impl Ip { /// /// See [crate::network::model::Ipv6Method]. #[dbus_interface(property)] - pub async fn method6(&self) -> String { - let ip_config = self.get_ip_config().await; - ip_config.method6.to_string() + pub async fn method6(&self) -> zbus::fdo::Result { + let ip_config = self.get_ip_config().await?; + Ok(ip_config.method6.to_string()) } #[dbus_interface(property)] @@ -129,13 +121,14 @@ impl Ip { /// Name server addresses. #[dbus_interface(property)] - pub async fn nameservers(&self) -> Vec { - let ip_config = self.get_ip_config().await; - ip_config + pub async fn nameservers(&self) -> zbus::fdo::Result> { + let ip_config = self.get_ip_config().await?; + let nameservers = ip_config .nameservers .iter() .map(IpAddr::to_string) - .collect() + .collect(); + Ok(nameservers) } #[dbus_interface(property)] @@ -149,12 +142,13 @@ impl Ip { /// /// An empty string removes the current value. #[dbus_interface(property)] - pub async fn gateway4(&self) -> String { - let ip_config = self.get_ip_config().await; - match ip_config.gateway4 { + pub async fn gateway4(&self) -> zbus::fdo::Result { + let ip_config = self.get_ip_config().await?; + let gateway = match ip_config.gateway4 { Some(ref address) => address.to_string(), None => "".to_string(), - } + }; + Ok(gateway) } #[dbus_interface(property)] @@ -167,12 +161,13 @@ impl Ip { /// /// An empty string removes the current value. #[dbus_interface(property)] - pub async fn gateway6(&self) -> String { - let ip_config = self.get_ip_config().await; - match ip_config.gateway6 { + pub async fn gateway6(&self) -> zbus::fdo::Result { + let ip_config = self.get_ip_config().await?; + let result = match ip_config.gateway6 { Some(ref address) => address.to_string(), None => "".to_string(), - } + }; + Ok(result) } #[dbus_interface(property)] diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index 7c84188b36..3407b2cdb9 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -102,11 +102,8 @@ impl Tree { ) .await?; - self.add_interface( - &path, - interfaces::Ip::new(self.actions.clone(), Arc::clone(&cloned)), - ) - .await?; + self.add_interface(&path, interfaces::Ip::new(self.actions.clone(), uuid)) + .await?; self.add_interface( &path, From 3804859de5aceaa6c48f2dc87cacd85ef913cc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 26 Dec 2023 10:04:33 +0000 Subject: [PATCH 10/14] Fix the ConnectionAdded signal emission --- .../network/dbus/interfaces/connections.rs | 6 ++++-- .../src/network/dbus/tree.rs | 20 +------------------ rust/agama-dbus-server/src/network/system.rs | 2 +- rust/agama-lib/src/network/proxies.rs | 2 +- 4 files changed, 7 insertions(+), 23 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs index 2e04320fb0..24e28f3af4 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs @@ -54,14 +54,16 @@ impl Connections { &mut self, id: String, ty: u8, + #[zbus(signal_context)] ctxt: SignalContext<'_>, ) -> zbus::fdo::Result { let actions = self.actions.lock().await; let (tx, rx) = oneshot::channel(); actions .send(Action::AddConnection(id.clone(), ty.try_into()?, tx)) .unwrap(); - let result = rx.await.unwrap()?; - Ok(result) + let path = rx.await.unwrap()?; + Self::connection_added(&ctxt, &id, &path).await?; + Ok(path) } /// Returns the D-Bus path of the network connection. diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index 3407b2cdb9..f76599a8f2 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -83,7 +83,6 @@ impl Tree { pub async fn add_connection( &self, conn: &mut Connection, - notify: bool, ) -> Result { let mut objects = self.objects.lock().await; @@ -121,10 +120,6 @@ impl Tree { .await?; } - if notify { - self.notify_connection_added(&orig_id, &path).await?; - } - Ok(path.into()) } @@ -146,7 +141,7 @@ impl Tree { /// * `connections`: list of connections. async fn add_connections(&self, connections: &mut [Connection]) -> Result<(), ServiceError> { for conn in connections.iter_mut() { - self.add_connection(conn, false).await?; + self.add_connection(conn).await?; } self.add_interface( @@ -202,19 +197,6 @@ impl Tree { let object_server = self.connection.object_server(); Ok(object_server.at(path, iface).await?) } - - /// Notify that a new connection has been added - async fn notify_connection_added( - &self, - id: &str, - path: &ObjectPath<'_>, - ) -> Result<(), ServiceError> { - let object_server = self.connection.object_server(); - let iface_ref = object_server - .interface::<_, interfaces::Connections>(CONNECTIONS_PATH) - .await?; - Ok(interfaces::Connections::connection_added(iface_ref.signal_context(), id, path).await?) - } } /// Objects paths for known devices and connections diff --git a/rust/agama-dbus-server/src/network/system.rs b/rust/agama-dbus-server/src/network/system.rs index ad44137fc7..b0850064e5 100644 --- a/rust/agama-dbus-server/src/network/system.rs +++ b/rust/agama-dbus-server/src/network/system.rs @@ -114,7 +114,7 @@ impl NetworkSystem { // TODO: handle tree handling problems let path = self .tree - .add_connection(&mut conn, true) + .add_connection(&mut conn) .await .expect("Could not update the D-Bus tree"); self.state.add_connection(conn)?; diff --git a/rust/agama-lib/src/network/proxies.rs b/rust/agama-lib/src/network/proxies.rs index a3b1916abf..77aed57feb 100644 --- a/rust/agama-lib/src/network/proxies.rs +++ b/rust/agama-lib/src/network/proxies.rs @@ -37,7 +37,7 @@ trait Connections { /// /// `name`: connection name. /// `ty`: connection type. - fn add_connection(&self, name: &str, ty: u8) -> zbus::Result<()>; + fn add_connection(&self, name: &str, ty: u8) -> zbus::Result; /// Apply method fn apply(&self) -> zbus::Result<()>; From 16cada3275f2055dee67015b1e8f94b35b711aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 26 Dec 2023 11:17:44 +0000 Subject: [PATCH 11/14] Drop an unneeded variable --- rust/agama-dbus-server/src/network/dbus/tree.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index f76599a8f2..568eb11cbf 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -86,7 +86,6 @@ impl Tree { ) -> Result { let mut objects = self.objects.lock().await; - let orig_id = conn.id.to_owned(); let uuid = conn.uuid; let (id, path) = objects.register_connection(conn); if id != conn.id { From be094a5fe2fc7660843eaf5b7452881a2f790807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 26 Dec 2023 14:27:18 +0000 Subject: [PATCH 12/14] Implement conversions to/from ConnectionConfig --- rust/agama-dbus-server/src/network/error.rs | 2 ++ rust/agama-dbus-server/src/network/model.rs | 34 +++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/rust/agama-dbus-server/src/network/error.rs b/rust/agama-dbus-server/src/network/error.rs index e3873eaa27..6b674b7efb 100644 --- a/rust/agama-dbus-server/src/network/error.rs +++ b/rust/agama-dbus-server/src/network/error.rs @@ -27,6 +27,8 @@ pub enum NetworkStateError { InvalidBondOptions, #[error("Not a controller connection: '{0}'")] NotControllerConnection(String), + #[error("Unexpected configuration")] + UnexpectedConfiguration, } impl From for zbus::fdo::Error { diff --git a/rust/agama-dbus-server/src/network/model.rs b/rust/agama-dbus-server/src/network/model.rs index e37237f381..0205b2fe2b 100644 --- a/rust/agama-dbus-server/src/network/model.rs +++ b/rust/agama-dbus-server/src/network/model.rs @@ -412,6 +412,18 @@ pub enum ConnectionConfig { Bond(BondConfig), } +impl From for ConnectionConfig { + fn from(value: BondConfig) -> Self { + Self::Bond(value) + } +} + +impl From for ConnectionConfig { + fn from(value: WirelessConfig) -> Self { + Self::Wireless(value) + } +} + #[derive(Debug, Error)] #[error("Invalid MAC address: {0}")] pub struct InvalidMacAddress(String); @@ -613,6 +625,17 @@ pub struct WirelessConfig { pub security: SecurityProtocol, } +impl TryFrom for WirelessConfig { + type Error = NetworkStateError; + + fn try_from(value: ConnectionConfig) -> Result { + match value { + ConnectionConfig::Wireless(config) => Ok(config), + _ => Err(NetworkStateError::UnexpectedConfiguration), + } + } +} + #[derive(Debug, Default, Clone, Copy, PartialEq)] pub enum WirelessMode { Unknown = 0, @@ -735,3 +758,14 @@ pub struct BondConfig { pub mode: BondMode, pub options: BondOptions, } + +impl TryFrom for BondConfig { + type Error = NetworkStateError; + + fn try_from(value: ConnectionConfig) -> Result { + match value { + ConnectionConfig::Bond(config) => Ok(config), + _ => Err(NetworkStateError::UnexpectedConfiguration), + } + } +} From 615d4a1bb909bf710400180fd35ac16c7ad5a8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 27 Dec 2023 11:38:48 +0000 Subject: [PATCH 13/14] Reduce D-Bus network interface code by using traits --- rust/Cargo.lock | 5 +- rust/agama-dbus-server/Cargo.toml | 1 + .../src/network/dbus/interfaces.rs | 1 + .../src/network/dbus/interfaces/common.rs | 81 ++++++++++ .../dbus/interfaces/connection_configs.rs | 141 ++++++------------ .../network/dbus/interfaces/connections.rs | 137 +++++++---------- .../src/network/dbus/interfaces/ip_config.rs | 54 +++---- .../src/network/dbus/tree.rs | 8 +- 8 files changed, 217 insertions(+), 211 deletions(-) create mode 100644 rust/agama-dbus-server/src/network/dbus/interfaces/common.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f9481c5eb3..a1a1a8c5d2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -47,6 +47,7 @@ dependencies = [ "agama-lib", "agama-locale-data", "anyhow", + "async-trait", "cidr", "gettext-rs", "log", @@ -328,9 +329,9 @@ checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" dependencies = [ "proc-macro2", "quote", diff --git a/rust/agama-dbus-server/Cargo.toml b/rust/agama-dbus-server/Cargo.toml index e6e95c67a0..4079ee01ca 100644 --- a/rust/agama-dbus-server/Cargo.toml +++ b/rust/agama-dbus-server/Cargo.toml @@ -25,3 +25,4 @@ gettext-rs = { version = "0.7.0", features = ["gettext-system"] } regex = "1.10.2" once_cell = "1.18.0" macaddr = "1.0" +async-trait = "0.1.75" diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces.rs b/rust/agama-dbus-server/src/network/dbus/interfaces.rs index c803941132..be287a5776 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces.rs @@ -3,6 +3,7 @@ //! This module contains the set of D-Bus interfaces that are exposed by [D-Bus network //! service](crate::NetworkService). +mod common; mod connection_configs; mod connections; mod devices; diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs new file mode 100644 index 0000000000..41275c0388 --- /dev/null +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs @@ -0,0 +1,81 @@ +//! Traits to build network D-Bus interfaces. +//! +//! There are a set of operations that are shared by many D-Bus interfaces (retrieving or updating a connection, a configuration etc.). +//! The traits in this module implements the common pieces to make it easier to build new +//! interfaces and reduce code duplication. +//! +//! Note: it is not clear to us whether using traits or simple structs is better for this use case. +//! We could change the approach in the future. +use crate::network::{ + error::NetworkStateError, + model::{Connection as NetworkConnection, ConnectionConfig}, + Action, +}; +use async_trait::async_trait; +use tokio::sync::{mpsc::UnboundedSender, oneshot, MutexGuard}; +use uuid::Uuid; + +#[async_trait] +pub trait ConnectionInterface { + fn uuid(&self) -> Uuid; + + async fn actions(&self) -> MutexGuard>; + + async fn get_connection(&self) -> Result { + let actions = self.actions().await; + let (tx, rx) = oneshot::channel(); + actions + .send(Action::GetConnection(self.uuid(), tx)) + .unwrap(); + rx.await + .unwrap() + .ok_or(NetworkStateError::UnknownConnection( + self.uuid().to_string(), + )) + } + + /// Updates the connection data in the NetworkSystem. + /// + /// * `connection`: Updated connection. + async fn update_connection(&self, func: F) -> Result<(), NetworkStateError> + where + F: FnOnce(&mut NetworkConnection) + std::marker::Send, + { + let mut connection = self.get_connection().await?; + func(&mut connection); + let actions = self.actions().await; + actions + .send(Action::UpdateConnection(Box::new(connection))) + .unwrap(); + Ok(()) + } +} + +#[async_trait] +pub trait ConnectionConfigInterface: ConnectionInterface { + async fn get_config(&self) -> Result + where + T: TryFrom, + { + let connection = self.get_connection().await?; + connection.config.try_into() + } + + async fn update_config(&self, func: F) -> Result<(), NetworkStateError> + where + F: FnOnce(&mut T) + std::marker::Send, + T: Into + + TryFrom + + std::marker::Send, + { + let mut connection = self.get_connection().await?; + let mut config: T = connection.config.clone().try_into()?; + func(&mut config); + connection.config = config.into(); + let actions = self.actions().await; + actions + .send(Action::UpdateConnection(Box::new(connection))) + .unwrap(); + Ok(()) + } +} diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs index 5a2edad0f9..c0967bb93e 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connection_configs.rs @@ -1,18 +1,18 @@ use agama_lib::network::types::SSID; +use async_trait::async_trait; use std::sync::Arc; -use tokio::sync::{mpsc::UnboundedSender, oneshot, Mutex}; +use tokio::sync::{mpsc::UnboundedSender, oneshot, Mutex, MutexGuard}; use uuid::Uuid; use zbus::dbus_interface; use crate::network::{ action::Action, error::NetworkStateError, - model::{ - BondConfig, Connection as NetworkConnection, ConnectionConfig, SecurityProtocol, - WirelessConfig, WirelessMode, - }, + model::{BondConfig, SecurityProtocol, WirelessConfig, WirelessMode}, }; +use super::common::{ConnectionConfigInterface, ConnectionInterface}; + /// D-Bus interface for Bond settings. pub struct Bond { actions: Arc>>, @@ -30,44 +30,6 @@ impl Bond { uuid, } } - - /// Gets the connection. - async fn get_connection(&self) -> Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); - rx.await - .unwrap() - .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) - } - - /// Gets the bonding configuration. - pub async fn get_config(&self) -> Result { - let connection = self.get_connection().await?; - match connection.config { - ConnectionConfig::Bond(bond) => Ok(bond), - _ => panic!("Not a bond connection. This is most probably a bug."), - } - } - - /// Updates the bond configuration. - /// - /// * `func`: function to update the configuration. - pub async fn update_config(&self, func: F) -> Result<(), NetworkStateError> - where - F: FnOnce(&mut BondConfig), - { - let mut connection = self.get_connection().await?; - match &mut connection.config { - ConnectionConfig::Bond(bond) => func(bond), - _ => panic!("Not a bond connection. This is most probably a bug."), - } - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(Box::new(connection.clone()))) - .unwrap(); - Ok(()) - } } #[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Bond")] @@ -75,28 +37,30 @@ impl Bond { /// Bonding mode. #[dbus_interface(property)] pub async fn mode(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; + let config = self.get_config::().await?; Ok(config.mode.to_string()) } #[dbus_interface(property)] pub async fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { let mode = mode.try_into()?; - self.update_config(|c| c.mode = mode).await?; + self.update_config::(|c| c.mode = mode) + .await?; Ok(()) } /// List of bonding options. #[dbus_interface(property)] pub async fn options(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; + let config = self.get_config::().await?; Ok(config.options.to_string()) } #[dbus_interface(property)] pub async fn set_options(&mut self, opts: &str) -> zbus::fdo::Result<()> { let opts = opts.try_into()?; - self.update_config(|c| c.options = opts).await?; + self.update_config::(|c| c.options = opts) + .await?; Ok(()) } @@ -126,6 +90,19 @@ impl Bond { } } +#[async_trait] +impl ConnectionInterface for Bond { + fn uuid(&self) -> Uuid { + self.uuid + } + + async fn actions(&self) -> MutexGuard> { + self.actions.lock().await + } +} + +impl ConnectionConfigInterface for Bond {} + /// D-Bus interface for wireless settings pub struct Wireless { actions: Arc>>, @@ -143,44 +120,6 @@ impl Wireless { uuid, } } - - /// Gets the connection. - async fn get_connection(&self) -> Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); - rx.await - .unwrap() - .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) - } - - /// Gets the wireless configuration. - pub async fn get_config(&self) -> Result { - let connection = self.get_connection().await?; - match connection.config { - ConnectionConfig::Wireless(wireless) => Ok(wireless), - _ => panic!("Not a bond connection. This is most probably a bug."), - } - } - - /// Updates the wireless configuration. - /// - /// * `func`: function to update the configuration. - pub async fn update_config(&self, func: F) -> Result<(), NetworkStateError> - where - F: FnOnce(&mut WirelessConfig), - { - let mut connection = self.get_connection().await?; - match &mut connection.config { - ConnectionConfig::Wireless(wireless) => func(wireless), - _ => panic!("Not a bond connection. This is most probably a bug."), - } - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(Box::new(connection.clone()))) - .unwrap(); - Ok(()) - } } #[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Wireless")] @@ -188,13 +127,14 @@ impl Wireless { /// Network SSID. #[dbus_interface(property, name = "SSID")] pub async fn ssid(&self) -> zbus::fdo::Result> { - let config = self.get_config().await?; + let config = self.get_config::().await?; Ok(config.ssid.into()) } #[dbus_interface(property, name = "SSID")] pub async fn set_ssid(&mut self, ssid: Vec) -> zbus::fdo::Result<()> { - self.update_config(|c| c.ssid = SSID(ssid)).await?; + self.update_config::(|c| c.ssid = SSID(ssid)) + .await?; Ok(()) } @@ -205,27 +145,28 @@ impl Wireless { /// See [crate::network::model::WirelessMode]. #[dbus_interface(property)] pub async fn mode(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; + let config = self.get_config::().await?; Ok(config.mode.to_string()) } #[dbus_interface(property)] pub async fn set_mode(&mut self, mode: &str) -> zbus::fdo::Result<()> { let mode: WirelessMode = mode.try_into()?; - self.update_config(|c| c.mode = mode).await?; + self.update_config::(|c| c.mode = mode) + .await?; Ok(()) } /// Password to connect to the wireless network. #[dbus_interface(property)] pub async fn password(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; + let config = self.get_config::().await?; Ok(config.password.unwrap_or_default()) } #[dbus_interface(property)] pub async fn set_password(&mut self, password: String) -> zbus::fdo::Result<()> { - self.update_config(|c| { + self.update_config::(|c| { c.password = if password.is_empty() { None } else { @@ -244,7 +185,7 @@ impl Wireless { /// See [crate::network::model::SecurityProtocol]. #[dbus_interface(property)] pub async fn security(&self) -> zbus::fdo::Result { - let config = self.get_config().await?; + let config = self.get_config::().await?; Ok(config.security.to_string()) } @@ -253,7 +194,21 @@ impl Wireless { let security: SecurityProtocol = security .try_into() .map_err(|_| NetworkStateError::InvalidSecurityProtocol(security.to_string()))?; - self.update_config(|c| c.security = security).await?; + self.update_config::(|c| c.security = security) + .await?; Ok(()) } } + +#[async_trait] +impl ConnectionInterface for Wireless { + fn uuid(&self) -> Uuid { + self.uuid + } + + async fn actions(&self) -> MutexGuard> { + self.actions.lock().await + } +} + +impl ConnectionConfigInterface for Wireless {} diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs index 24e28f3af4..e8df645ec5 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/connections.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use std::{str::FromStr, sync::Arc}; use tokio::sync::{mpsc::UnboundedSender, oneshot, Mutex, MutexGuard}; use uuid::Uuid; @@ -7,12 +8,8 @@ use zbus::{ SignalContext, }; -use crate::network::{ - dbus::ObjectsRegistry, - error::NetworkStateError, - model::{Connection as NetworkConnection, MacAddress}, - Action, -}; +use super::common::ConnectionInterface; +use crate::network::{dbus::ObjectsRegistry, error::NetworkStateError, model::MacAddress, Action}; /// D-Bus interface for the set of connections. /// @@ -118,39 +115,13 @@ impl Connection { /// Creates a Connection interface object. /// /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. + /// * `uuid`: network connection's UUID. pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { Self { actions: Arc::new(Mutex::new(actions)), uuid, } } - - /// Returns the underlying connection. - async fn get_connection(&self) -> Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); - rx.await - .unwrap() - .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) - } - - /// Updates the connection data in the NetworkSystem. - /// - /// * `connection`: Updated connection. - pub async fn update_connection(&self, func: F) -> Result<(), NetworkStateError> - where - F: FnOnce(&mut NetworkConnection), - { - let mut connection = self.get_connection().await?; - func(&mut connection); - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(Box::new(connection))) - .unwrap(); - Ok(()) - } } #[dbus_interface(name = "org.opensuse.Agama1.Network.Connection")] @@ -234,101 +205,101 @@ impl Connection { } } +#[async_trait] +impl ConnectionInterface for Connection { + fn uuid(&self) -> Uuid { + self.uuid + } + + async fn actions(&self) -> MutexGuard> { + self.actions.lock().await + } +} + /// D-Bus interface for Match settings pub struct Match { actions: Arc>>, - connection: Arc>, + uuid: Uuid, } impl Match { /// Creates a Match Settings interface object. /// /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. - pub fn new( - actions: UnboundedSender, - connection: Arc>, - ) -> Self { + /// * `uuid`: nework connection's UUID. + pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { Self { actions: Arc::new(Mutex::new(actions)), - connection, + uuid, } } - - /// Returns the underlying connection. - async fn get_connection(&self) -> MutexGuard { - self.connection.lock().await - } - - /// Updates the connection data in the NetworkSystem. - /// - /// * `connection`: Updated connection. - async fn update_connection<'a>( - &self, - connection: MutexGuard<'a, NetworkConnection>, - ) -> zbus::fdo::Result<()> { - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(Box::new(connection.clone()))) - .unwrap(); - Ok(()) - } } #[dbus_interface(name = "org.opensuse.Agama1.Network.Connection.Match")] impl Match { /// List of driver names to match. #[dbus_interface(property)] - pub async fn driver(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.driver.clone() + pub async fn driver(&self) -> zbus::fdo::Result> { + let connection = self.get_connection().await?; + Ok(connection.match_config.driver) } #[dbus_interface(property)] pub async fn set_driver(&mut self, driver: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.driver = driver; - self.update_connection(connection).await + self.update_connection(|c| c.match_config.driver = driver) + .await?; + Ok(()) } /// List of paths to match agains the ID_PATH udev property of devices. #[dbus_interface(property)] - pub async fn path(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.path.clone() + pub async fn path(&self) -> zbus::fdo::Result> { + let connection = self.get_connection().await?; + Ok(connection.match_config.path) } #[dbus_interface(property)] pub async fn set_path(&mut self, path: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.path = path; - self.update_connection(connection).await + self.update_connection(|c| c.match_config.path = path) + .await?; + Ok(()) } /// List of interface names to match. #[dbus_interface(property)] - pub async fn interface(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.interface.clone() + pub async fn interface(&self) -> zbus::fdo::Result> { + let connection = self.get_connection().await?; + Ok(connection.match_config.interface) } #[dbus_interface(property)] pub async fn set_interface(&mut self, interface: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.interface = interface; - self.update_connection(connection).await + self.update_connection(|c| c.match_config.interface = interface) + .await?; + Ok(()) } /// List of kernel options to match. #[dbus_interface(property)] - pub async fn kernel(&self) -> Vec { - let connection = self.get_connection().await; - connection.match_config.kernel.clone() + pub async fn kernel(&self) -> zbus::fdo::Result> { + let connection = self.get_connection().await?; + Ok(connection.match_config.kernel) } #[dbus_interface(property)] pub async fn set_kernel(&mut self, kernel: Vec) -> zbus::fdo::Result<()> { - let mut connection = self.get_connection().await; - connection.match_config.kernel = kernel; - self.update_connection(connection).await + self.update_connection(|c| c.match_config.kernel = kernel) + .await?; + Ok(()) + } +} + +#[async_trait] +impl ConnectionInterface for Match { + fn uuid(&self) -> Uuid { + self.uuid + } + + async fn actions(&self) -> MutexGuard> { + self.actions.lock().await } } diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs index cc9688f60b..662b56f2df 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs @@ -7,15 +7,18 @@ use crate::network::{ action::Action, error::NetworkStateError, - model::{Connection as NetworkConnection, IpConfig, Ipv4Method, Ipv6Method}, + model::{IpConfig, Ipv4Method, Ipv6Method}, }; +use async_trait::async_trait; use cidr::IpInet; use std::{net::IpAddr, sync::Arc}; -use tokio::sync::Mutex; -use tokio::sync::{mpsc::UnboundedSender, oneshot}; +use tokio::sync::mpsc::UnboundedSender; +use tokio::sync::{Mutex, MutexGuard}; use uuid::Uuid; use zbus::dbus_interface; +use super::common::ConnectionInterface; + /// D-Bus interface for IPv4 and IPv6 settings pub struct Ip { actions: Arc>>, @@ -34,16 +37,6 @@ impl Ip { } } - /// Gets the connection. - async fn get_connection(&self) -> Result { - let actions = self.actions.lock().await; - let (tx, rx) = oneshot::channel(); - actions.send(Action::GetConnection(self.uuid, tx)).unwrap(); - rx.await - .unwrap() - .ok_or(NetworkStateError::UnknownConnection(self.uuid.to_string())) - } - /// Returns the IpConfig struct. async fn get_ip_config(&self) -> Result { self.get_connection().await.map(|c| c.ip_config) @@ -52,16 +45,12 @@ impl Ip { /// Updates the IpConfig struct. /// /// * `func`: function to update the configuration. - async fn update_config(&self, func: F) -> zbus::fdo::Result<()> + async fn update_ip_config(&self, func: F) -> zbus::fdo::Result<()> where - F: Fn(&mut IpConfig), + F: Fn(&mut IpConfig) + std::marker::Send, { - let mut connection = self.get_connection().await?; - func(&mut connection.ip_config); - let actions = self.actions.lock().await; - actions - .send(Action::UpdateConnection(Box::new(connection.clone()))) - .unwrap(); + self.update_connection(move |c| func(&mut c.ip_config)) + .await?; Ok(()) } } @@ -81,7 +70,7 @@ impl Ip { #[dbus_interface(property)] pub async fn set_addresses(&mut self, addresses: Vec) -> zbus::fdo::Result<()> { let addresses = helpers::parse_addresses::(addresses); - self.update_config(|ip| ip.addresses = addresses.clone()) + self.update_ip_config(|ip| ip.addresses = addresses.clone()) .await } @@ -99,7 +88,7 @@ impl Ip { #[dbus_interface(property)] pub async fn set_method4(&mut self, method: &str) -> zbus::fdo::Result<()> { let method: Ipv4Method = method.parse()?; - self.update_config(|ip| ip.method4 = method).await + self.update_ip_config(|ip| ip.method4 = method).await } /// IPv6 configuration method. @@ -116,7 +105,7 @@ impl Ip { #[dbus_interface(property)] pub async fn set_method6(&mut self, method: &str) -> zbus::fdo::Result<()> { let method: Ipv6Method = method.parse()?; - self.update_config(|ip| ip.method6 = method).await + self.update_ip_config(|ip| ip.method6 = method).await } /// Name server addresses. @@ -134,7 +123,7 @@ impl Ip { #[dbus_interface(property)] pub async fn set_nameservers(&mut self, addresses: Vec) -> zbus::fdo::Result<()> { let addresses = helpers::parse_addresses::(addresses); - self.update_config(|ip| ip.nameservers = addresses.clone()) + self.update_ip_config(|ip| ip.nameservers = addresses.clone()) .await } @@ -154,7 +143,7 @@ impl Ip { #[dbus_interface(property)] pub async fn set_gateway4(&mut self, gateway: String) -> zbus::fdo::Result<()> { let gateway = helpers::parse_gateway(gateway)?; - self.update_config(|ip| ip.gateway4 = gateway).await + self.update_ip_config(|ip| ip.gateway4 = gateway).await } /// Network gateway for IPv6. @@ -173,7 +162,7 @@ impl Ip { #[dbus_interface(property)] pub async fn set_gateway6(&mut self, gateway: String) -> zbus::fdo::Result<()> { let gateway = helpers::parse_gateway(gateway)?; - self.update_config(|ip| ip.gateway6 = gateway).await + self.update_ip_config(|ip| ip.gateway6 = gateway).await } } @@ -224,3 +213,14 @@ mod helpers { } } } + +#[async_trait] +impl ConnectionInterface for Ip { + fn uuid(&self) -> Uuid { + self.uuid + } + + async fn actions(&self) -> MutexGuard> { + self.actions.lock().await + } +} diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index 568eb11cbf..ba1f561ea3 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -93,7 +93,6 @@ impl Tree { } log::info!("Publishing network connection '{}'", id); - let cloned = Arc::new(Mutex::new(conn.clone())); self.add_interface( &path, interfaces::Connection::new(self.actions.clone(), uuid), @@ -103,11 +102,8 @@ impl Tree { self.add_interface(&path, interfaces::Ip::new(self.actions.clone(), uuid)) .await?; - self.add_interface( - &path, - interfaces::Match::new(self.actions.clone(), Arc::clone(&cloned)), - ) - .await?; + self.add_interface(&path, interfaces::Match::new(self.actions.clone(), uuid)) + .await?; if let ConnectionConfig::Bond(_) = conn.config { self.add_interface(&path, interfaces::Bond::new(self.actions.clone(), uuid)) From 3eb41cd681e03a1fa6c58d7878e59a3b7da4fa44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Wed, 27 Dec 2023 12:48:23 +0000 Subject: [PATCH 14/14] Documentation fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Knut Alejandro Anderssen González --- rust/agama-dbus-server/src/network/dbus/interfaces/common.rs | 2 +- rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs | 2 +- rust/agama-dbus-server/src/network/dbus/tree.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs index 41275c0388..e5a49f122c 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/common.rs @@ -36,7 +36,7 @@ pub trait ConnectionInterface { /// Updates the connection data in the NetworkSystem. /// - /// * `connection`: Updated connection. + /// * `func`: function to update the connection. async fn update_connection(&self, func: F) -> Result<(), NetworkStateError> where F: FnOnce(&mut NetworkConnection) + std::marker::Send, diff --git a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs index 662b56f2df..dfbfd6ad30 100644 --- a/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs +++ b/rust/agama-dbus-server/src/network/dbus/interfaces/ip_config.rs @@ -29,7 +29,7 @@ impl Ip { /// Creates an IP interface object. /// /// * `actions`: sending-half of a channel to send actions. - /// * `connection`: connection to expose over D-Bus. + /// * `uuid`: connection UUID.. pub fn new(actions: UnboundedSender, uuid: Uuid) -> Self { Self { actions: Arc::new(Mutex::new(actions)), diff --git a/rust/agama-dbus-server/src/network/dbus/tree.rs b/rust/agama-dbus-server/src/network/dbus/tree.rs index ba1f561ea3..d6080508d9 100644 --- a/rust/agama-dbus-server/src/network/dbus/tree.rs +++ b/rust/agama-dbus-server/src/network/dbus/tree.rs @@ -78,7 +78,7 @@ impl Tree { /// Adds a connection to the D-Bus tree and returns the D-Bus path. /// - /// * `connection`: connection to add. + /// * `conn`: connection to add. /// * `notify`: whether to notify the added connection pub async fn add_connection( &self,