wayle_network/
monitoring.rs1use std::sync::Arc;
2
3use tokio_stream::StreamExt;
4use tokio_util::sync::CancellationToken;
5use tracing::{debug, warn};
6use wayle_core::Property;
7use wayle_traits::{Reactive, ServiceMonitoring};
8use zbus::Connection;
9
10use super::{
11 core::settings::Settings,
12 discovery::NetworkServiceDiscovery,
13 error::Error,
14 proxy::manager::NetworkManagerProxy,
15 service::NetworkService,
16 types::connectivity::ConnectionType,
17 wifi::{LiveWifiParams, Wifi},
18 wired::{LiveWiredParams, Wired},
19};
20
21impl ServiceMonitoring for NetworkService {
22 type Error = Error;
23
24 async fn start_monitoring(&self) -> Result<(), Self::Error> {
25 spawn_primary_monitoring(
26 self.zbus_connection.clone(),
27 self.primary.clone(),
28 self.cancellation_token.child_token(),
29 )
30 .await?;
31
32 spawn_device_monitoring(
33 self.zbus_connection.clone(),
34 self.wifi.clone(),
35 self.wired.clone(),
36 self.settings.clone(),
37 self.cancellation_token.child_token(),
38 )
39 .await
40 }
41}
42
43async fn spawn_primary_monitoring(
44 connection: Connection,
45 primary: Property<ConnectionType>,
46 cancellation_token: CancellationToken,
47) -> Result<(), Error> {
48 let nm_proxy = NetworkManagerProxy::new(&connection)
49 .await
50 .map_err(Error::DbusError)?;
51
52 let initial_type = nm_proxy.primary_connection_type().await?;
53 update_primary_connection(&initial_type, &primary);
54
55 let mut type_changed = nm_proxy.receive_primary_connection_type_changed().await;
56
57 tokio::spawn(async move {
58 loop {
59 tokio::select! {
60 _ = cancellation_token.cancelled() => {
61 debug!("NetworkMonitoring primary monitoring cancelled");
62 return;
63 }
64 Some(change) = type_changed.next() => {
65 if let Ok(nm_type) = change.get().await {
66 debug!(nm_type = %nm_type, "Primary connection type changed");
67 update_primary_connection(&nm_type, &primary);
68 }
69 }
70 }
71 }
72 });
73
74 Ok(())
75}
76
77async fn spawn_device_monitoring(
78 connection: Connection,
79 wifi: Property<Option<Arc<Wifi>>>,
80 wired: Property<Option<Arc<Wired>>>,
81 settings: Arc<Settings>,
82 cancellation_token: CancellationToken,
83) -> Result<(), Error> {
84 let nm_proxy = NetworkManagerProxy::new(&connection)
85 .await
86 .map_err(Error::DbusError)?;
87
88 let mut device_added = nm_proxy.receive_device_added().await?;
89 let mut device_removed = nm_proxy.receive_device_removed().await?;
90
91 tokio::spawn(async move {
92 loop {
93 tokio::select! {
94 _ = cancellation_token.cancelled() => {
95 debug!("NetworkMonitoring device monitoring cancelled");
96 return;
97 }
98 Some(signal) = device_added.next() => {
99 let Ok(args) = signal.args() else { continue };
100 debug!(path = %args.device_path, "Network device added");
101
102 try_initialize_wifi(&connection, &wifi, &settings, &cancellation_token).await;
103 try_initialize_wired(&connection, &wired, &cancellation_token).await;
104 }
105 Some(signal) = device_removed.next() => {
106 let Ok(args) = signal.args() else { continue };
107 debug!(path = %args.device_path, "Network device removed");
108
109 handle_wifi_removed(&args.device_path, &wifi);
110 handle_wired_removed(&args.device_path, &wired);
111 }
112 }
113 }
114 });
115
116 Ok(())
117}
118
119async fn try_initialize_wifi(
120 connection: &Connection,
121 wifi: &Property<Option<Arc<Wifi>>>,
122 settings: &Arc<Settings>,
123 cancellation_token: &CancellationToken,
124) {
125 if wifi.get().is_some() {
126 return;
127 }
128
129 let Some(path) = NetworkServiceDiscovery::wifi_device_path(connection)
130 .await
131 .ok()
132 .flatten()
133 else {
134 return;
135 };
136
137 match Wifi::get_live(LiveWifiParams {
138 connection,
139 device_path: path.clone(),
140 cancellation_token,
141 settings: settings.clone(),
142 })
143 .await
144 {
145 Ok(new_wifi) => {
146 debug!(path = %path, "WiFi device initialized");
147 wifi.set(Some(new_wifi));
148 }
149 Err(err) => {
150 warn!(error = %err, path = %path, "Failed to initialize WiFi device");
151 }
152 }
153}
154
155async fn try_initialize_wired(
156 connection: &Connection,
157 wired: &Property<Option<Arc<Wired>>>,
158 cancellation_token: &CancellationToken,
159) {
160 if wired.get().is_some() {
161 return;
162 }
163
164 let Some(path) = NetworkServiceDiscovery::wired_device_path(connection)
165 .await
166 .ok()
167 .flatten()
168 else {
169 return;
170 };
171
172 match Wired::get_live(LiveWiredParams {
173 connection,
174 device_path: path.clone(),
175 cancellation_token,
176 })
177 .await
178 {
179 Ok(new_wired) => {
180 debug!(path = %path, "Wired device initialized");
181 wired.set(Some(new_wired));
182 }
183 Err(err) => {
184 warn!(error = %err, path = %path, "Failed to initialize wired device");
185 }
186 }
187}
188
189fn handle_wifi_removed(device_path: &str, wifi: &Property<Option<Arc<Wifi>>>) {
190 let Some(current) = wifi.get() else { return };
191
192 if current.device.core.object_path.as_str() == device_path {
193 debug!(path = %device_path, "WiFi device removed");
194 wifi.set(None);
195 }
196}
197
198fn handle_wired_removed(device_path: &str, wired: &Property<Option<Arc<Wired>>>) {
199 let Some(current) = wired.get() else { return };
200
201 if current.device.core.object_path.as_str() == device_path {
202 debug!(path = %device_path, "Wired device removed");
203 wired.set(None);
204 }
205}
206
207fn update_primary_connection(nm_type: &str, primary: &Property<ConnectionType>) {
208 let connection_type = ConnectionType::from_nm_type(nm_type);
209 debug!(?connection_type, "Primary connection type resolved");
210 primary.set(connection_type);
211}