1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-09-16 03:36:58 +02:00

refactor(networkmanager): replace state-based w/ event-based approach

This commit is contained in:
Reinout Meliesie 2025-08-16 15:31:06 +02:00
commit 1836ab2943
Signed by: zedfrigg
GPG key ID: 3AFCC06481308BC6
6 changed files with 230 additions and 205 deletions

View file

@ -12,46 +12,10 @@ pub(super) trait Dbus {
fn active_connections(&self) -> Result<Vec<ObjectPath<'_>>>;
#[zbus(property)]
fn devices(&self) -> Result<Vec<ObjectPath<'_>>>;
// #[zbus(property)]
// fn networking_enabled(&self) -> Result<bool>;
// #[zbus(property)]
// fn primary_connection(&self) -> Result<ObjectPath>;
// #[zbus(property)]
// fn primary_connection_type(&self) -> Result<Str>;
// #[zbus(property)]
// fn wireless_enabled(&self) -> Result<bool>;
}
#[proxy(
default_service = "org.freedesktop.NetworkManager",
interface = "org.freedesktop.NetworkManager.Connection.Active"
)]
pub(super) trait ActiveConnectionDbus {
// #[zbus(property)]
// fn connection(&self) -> Result<ObjectPath>;
// #[zbus(property)]
// fn default(&self) -> Result<bool>;
// #[zbus(property)]
// fn default6(&self) -> Result<bool>;
fn all_devices(&self) -> Result<Vec<ObjectPath<'_>>>;
#[zbus(property)]
fn devices(&self) -> Result<Vec<ObjectPath<'_>>>;
// #[zbus(property)]
// fn id(&self) -> Result<Str>;
#[zbus(property)]
fn type_(&self) -> Result<Str<'_>>;
// #[zbus(property)]
// fn uuid(&self) -> Result<Str>;
}
#[proxy(
@ -59,19 +23,20 @@ pub(super) trait ActiveConnectionDbus {
interface = "org.freedesktop.NetworkManager.Device"
)]
pub(super) trait DeviceDbus {
// #[zbus(property)]
// fn active_connection(&self) -> Result<ObjectPath>;
#[zbus(property)]
fn device_type(&self) -> Result<DeviceType>;
#[zbus(property)]
fn interface(&self) -> Result<Str<'_>>;
#[zbus(property)]
fn state(&self) -> Result<DeviceState>;
}
// For reference: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/e1a7d5ac062f4f23ce3a6b33c62e856056161ad8/src/libnm-core-public/nm-dbus-interface.h#L212-L253
#[derive(Clone, Debug, Eq, Hash, OwnedValue, PartialEq)]
#[repr(u32)]
pub(super) enum DeviceType {
pub enum DeviceType {
Unknown = 0,
Ethernet = 1,
Wifi = 2,
@ -105,9 +70,10 @@ pub(super) enum DeviceType {
Hsr = 33,
}
// For reference: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/e1a7d5ac062f4f23ce3a6b33c62e856056161ad8/src/libnm-core-public/nm-dbus-interface.h#L501-L538
#[derive(Clone, Debug, OwnedValue, PartialEq)]
#[repr(u32)]
pub(super) enum DeviceState {
pub enum DeviceState {
Unknown = 0,
Unmanaged = 10,
Unavailable = 20,
@ -122,18 +88,3 @@ pub(super) enum DeviceState {
Deactivating = 110,
Failed = 120,
}
impl DeviceState {
pub(super) fn is_enabled(&self) -> bool {
!matches!(
self,
DeviceState::Unknown | DeviceState::Unmanaged | DeviceState::Unavailable,
)
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(super) struct Device<'l> {
pub object_path: ObjectPath<'l>,
pub type_: DeviceType,
}

View file

@ -1,2 +1,14 @@
use crate::clients::networkmanager::dbus::{DeviceState, DeviceType};
#[derive(Debug, Clone)]
pub enum Event {}
pub enum Event {
DeviceAdded {
interface: String,
r#type: DeviceType,
},
DeviceStateChanged {
interface: String,
r#type: DeviceType,
state: DeviceState,
},
}

View file

@ -1,22 +1,17 @@
use color_eyre::Result;
use color_eyre::eyre::Error;
use color_eyre::eyre::Ok;
use futures_lite::StreamExt;
use std::collections::HashSet;
use std::sync::Arc;
use std::time::Duration;
use tokio::join;
use tokio::sync::{RwLock, broadcast};
use tokio::time::sleep;
use tokio_stream::StreamMap;
use tokio::sync::broadcast;
use zbus::Connection;
use zbus::proxy::PropertyStream;
use zbus::zvariant::ObjectPath;
use zbus::zvariant::{ObjectPath, Str};
use crate::clients::networkmanager::dbus::{DbusProxy, Device, DeviceDbusProxy, DeviceState};
use crate::clients::networkmanager::dbus::{DbusProxy, DeviceDbusProxy};
use crate::clients::networkmanager::event::Event;
use crate::{register_fallible_client, spawn};
mod dbus;
pub mod dbus;
pub mod event;
#[derive(Debug)]
@ -31,103 +26,98 @@ impl Client {
}
async fn run(&self) -> Result<()> {
let dbus_connection = Connection::system().await?;
let root_object = DbusProxy::new(&dbus_connection).await?;
// TODO: Use glib::clone!()
let device_state_changes =
RwLock::new(StreamMap::<Device, PropertyStream<DeviceState>>::new());
let tx = self.tx.clone();
spawn(async move {
let dbus_connection = Connection::system().await?;
let root = DbusProxy::new(&dbus_connection).await?;
let _ = join!(
// Handles the addition and removal of device objects
async {
let mut devices_changes = root_object.receive_devices_changed().await;
while let Some(change) = devices_changes.next().await {
println!("here?");
// All device types get added to this, but not all types emit events
let mut devices = HashSet::new();
let devices = HashSet::from_iter(
device_state_changes
.read()
.await
.keys()
.map(|device| &device.object_path)
.cloned(),
);
let mut devices_changes = root.receive_all_devices_changed().await;
while let Some(devices_change) = devices_changes.next().await {
// The new list of devices from dbus, not to be confused with the added devices below
let new_devices = HashSet::from_iter(devices_change.get().await?);
// The new list of devices from dbus, not to be confused with the added devices below
let new_devices_vec = change.get().await?;
let new_devices = HashSet::<ObjectPath>::from_iter(new_devices_vec);
println!("Existing devices: {:?}", devices);
println!("New devices: {:?}", new_devices);
let added_devices = new_devices.difference(&devices);
println!("Added devices: {:?}", added_devices);
for added_device in added_devices {
let device_proxy =
DeviceDbusProxy::new(&dbus_connection, added_device).await?;
let device_type = device_proxy.device_type().await?;
let device_state_stream = device_proxy.receive_state_changed().await;
device_state_changes.write().await.insert(
Device {
object_path: added_device.clone(),
type_: device_type.clone(), // TODO: Remove clone when removing println below
},
device_state_stream,
);
println!("Device added: {} type {:?}", added_device, device_type);
}
let removed_devices = devices.difference(&new_devices);
println!("Removed devices: {:?}", removed_devices);
for removed_device in removed_devices {
let device_proxy =
DeviceDbusProxy::new(&dbus_connection, removed_device).await?;
let device_type = device_proxy.device_type().await?;
device_state_changes.write().await.remove(&Device {
object_path: removed_device.clone(),
type_: device_type.clone(), // TODO: Remove clone when removing println below
});
println!("Device removed: {} type {:?}", removed_device, device_type);
}
}
Ok::<(), Error>(())
},
// Handles changes to device properties
async {
sleep(Duration::from_secs(5)).await;
/*
Okay so this causes a deadlock, and we should rewrite all of this with spawn() anyway cause join!() is not multithreaded apparently.
In order to not leak memory we could have closures for objects that don't exist anymore check this manually and return.
*/
while let Some((device, property)) = device_state_changes.write().await.next().await
{
let property = property.get().await?;
println!(
"Device state changed: {} to {:?}",
device.object_path, property
);
let added_devices = new_devices.difference(&devices);
for added_device in added_devices {
spawn(watch_device(added_device.to_owned(), tx.clone()));
}
println!("Prop loop ended");
let removed_devices = devices.difference(&new_devices);
// TODO: Cook up some way to notify closures for removed devices to exit
Ok::<(), Error>(())
},
);
devices = new_devices;
}
Ok(())
});
Ok(())
}
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
// Maybe we should pass a direct receiver so that the UI module also gets the events from before it was started
self.tx.subscribe()
}
}
pub async fn create_client() -> Result<Arc<Client>> {
// TODO: Use spawn here after all, otherwise we block on creation
let client = Arc::new(Client::new().await?);
client.run().await?;
Ok(client)
}
async fn watch_device(device_path: ObjectPath<'_>, tx: broadcast::Sender<Event>) -> Result<()> {
let dbus_connection = Connection::system().await?;
let device = DeviceDbusProxy::new(&dbus_connection, device_path.to_owned()).await?;
let interface = device.interface().await?;
let device_type = device.device_type().await?;
tx.send(Event::DeviceAdded {
interface: interface.to_string(),
r#type: device_type,
})?;
spawn(watch_device_state(
device_path.to_owned(),
interface.to_owned(),
tx.clone(),
));
Ok(())
}
async fn watch_device_state(
device_path: ObjectPath<'_>,
interface: Str<'_>,
tx: broadcast::Sender<Event>,
) -> Result<()> {
let dbus_connection = Connection::system().await?;
let device = DeviceDbusProxy::new(&dbus_connection, &device_path).await?;
let r#type = device.device_type().await?;
// Send an event communicating the initial state
let state = device.state().await?;
tx.send(Event::DeviceStateChanged {
interface: interface.to_string(),
r#type: r#type.clone(),
state,
})?;
let mut state_changes = device.receive_state_changed().await;
while let Some(state_change) = state_changes.next().await {
let state = state_change.get().await?;
tx.send(Event::DeviceStateChanged {
interface: interface.to_string(),
r#type: r#type.clone(),
state,
})?;
}
Ok(())
}
register_fallible_client!(Client, network_manager);