D-Bus Architecture
nmrs communicates with NetworkManager over the D-Bus system bus. Understanding this architecture helps with debugging and explains why certain operations work the way they do.
Overview
┌─────────────┐ D-Bus (system bus) ┌──────────────────┐
│ Your App │ ◄────────────────────────► │ NetworkManager │
│ (nmrs) │ │ Daemon │
└─────────────┘ └──────────────────┘
│ │
│ zbus (Rust D-Bus library) │
│ │
▼ ▼
nmrs::dbus module D-Bus interfaces:
(proxy types) - org.freedesktop.NetworkManager
- org.freedesktop.NetworkManager.Device
- org.freedesktop.NetworkManager.Device.Wireless
- org.freedesktop.NetworkManager.AccessPoint
- org.freedesktop.NetworkManager.Connection.Active
- org.freedesktop.NetworkManager.Settings
- ...
How nmrs Uses D-Bus
Connection Establishment
When you call NetworkManager::new(), nmrs connects to the system D-Bus using zbus:
#![allow(unused)] fn main() { let nm = NetworkManager::new().await?; // Internally: zbus::Connection::system().await }
This creates a persistent D-Bus connection that's shared across all operations.
Method Calls
API methods like list_devices() translate to D-Bus method calls:
nmrs: nm.list_devices()
→ D-Bus: GetDevices() on org.freedesktop.NetworkManager
← D-Bus: Array of device object paths
→ D-Bus: Get properties for each device path
← D-Bus: Device properties (interface, type, state, etc.)
→ nmrs: Vec<Device>
Signal Monitoring
monitor_network_changes() subscribes to D-Bus signals:
nmrs: nm.monitor_network_changes(callback)
→ D-Bus: Subscribe to AccessPointAdded/Removed signals
← D-Bus: Signal whenever an AP appears or disappears
→ nmrs: Invoke callback
Connection Settings
When connecting to a network, nmrs builds a settings dictionary and sends it via D-Bus:
nmrs: nm.connect("MyWiFi", WifiSecurity::WpaPsk { psk: "..." })
→ Build settings HashMap
→ D-Bus: AddAndActivateConnection(settings, device_path, specific_object)
← D-Bus: Active connection path
→ D-Bus: Monitor StateChanged signal
← D-Bus: State transitions until Activated or Failed
→ nmrs: Ok(()) or Err(ConnectionError)
D-Bus Proxy Types
nmrs wraps D-Bus interfaces in typed proxy structs (defined in nmrs::dbus):
| Proxy | D-Bus Interface | Purpose |
|---|---|---|
NMProxy | org.freedesktop.NetworkManager | Main NM interface |
NMDeviceProxy | org.freedesktop.NetworkManager.Device | Device properties and control |
NMWirelessProxy | org.freedesktop.NetworkManager.Device.Wireless | Wi-Fi scanning, AP list |
NMAccessPointProxy | org.freedesktop.NetworkManager.AccessPoint | AP signal, SSID, security |
NMActiveConnectionProxy | org.freedesktop.NetworkManager.Connection.Active | Active connection state |
NMWiredProxy | org.freedesktop.NetworkManager.Device.Wired | Wired device properties |
NMBluetoothProxy | org.freedesktop.NetworkManager.Device.Bluetooth | Bluetooth properties |
These are internal types — you interact with them through the high-level NetworkManager API.
D-Bus Errors
D-Bus communication errors surface as ConnectionError::Dbus or ConnectionError::DbusOperation:
#![allow(unused)] fn main() { use nmrs::ConnectionError; match result { Err(ConnectionError::Dbus(e)) => { eprintln!("D-Bus error: {}", e); } Err(ConnectionError::DbusOperation { context, source }) => { eprintln!("{}: {}", context, source); } _ => {} } }
Common causes:
- NetworkManager is not running
- D-Bus system bus is not available
- Insufficient permissions (PolicyKit)
Permissions
NetworkManager uses PolicyKit for authorization. Most operations require the user to be in the network group or to have appropriate PolicyKit rules. See Requirements for setup details.
Debugging D-Bus
Monitor D-Bus Traffic
Use dbus-monitor to see raw D-Bus messages:
sudo dbus-monitor --system "interface='org.freedesktop.NetworkManager'"
Check NetworkManager State
nmcli general status
nmcli device status
nmcli connection show
Verify D-Bus Service
busctl list | grep NetworkManager
Next Steps
- Logging and Debugging – enable nmrs debug logging
- Architecture – internal code structure