python-sdbus / python-sdbus-networkmanager

python-sdbus binds for NetworkManager
GNU Lesser General Public License v2.1
30 stars 6 forks source link

get DbusServiceUnknownError in child thread #49

Closed zhanglongqi closed 1 year ago

zhanglongqi commented 1 year ago
from threading import Thread

import sdbus
from python_sdbus_networkmanager.sdbus_block.networkmanager import (
    ConnectionType,
    NetworkConnectionSettings,
    NetworkManager,
    NetworkManagerSettings,
    NetworkDeviceGeneric,
    IPv4Config,
    DeviceType,
    NetworkDeviceWireless,
    WiFiOperationMode,
    AccessPoint,
)
from typing import Any, Dict, List, Optional, Tuple
NetworkManagerAddressData = List[Dict[str, Tuple[str, Any]]]

class WifiThread(Thread):

    def __init__(self):
        super().__init__()
        self.name = 'WIFI'
        self.daemon = True

        sdbus.set_default_bus(sdbus.sd_bus_open_system())

    @staticmethod
    def list_networkdevice_details_blocking() -> None:

        for device_path in NetworkManager().get_devices():
            generic_device = NetworkDeviceGeneric(device_path)
            device_ip4_conf_path: str = generic_device.ip4_config
            if device_ip4_conf_path == "/":
                continue
            if not generic_device.managed:
                continue
            dev_type = DeviceType(generic_device.device_type).name
            if dev_type == DeviceType.BRIDGE.name:
                continue

            dev_name = generic_device.interface
            ip4_conf = IPv4Config(device_ip4_conf_path)
            gateway: str = ip4_conf.gateway

            print("Type:   ", dev_type.title())
            print("Name:   ", dev_name)

            if gateway:
                print("Gateway:", gateway)

            address_data: NetworkManagerAddressData = ip4_conf.address_data
            for inetaddr in address_data:
                print(f'Address: {inetaddr["address"][1]}/{inetaddr["prefix"][1]}')

            nameservers: NetworkManagerAddressData = ip4_conf.nameserver_data
            for dns in nameservers:
                print("DNS:    ", dns["address"][1])

            if dev_type == DeviceType.WIFI.name:
                wifi = NetworkDeviceWireless(device_path)
                print("Wifi:   ", WiFiOperationMode(wifi.mode).name.title())
                ap = AccessPoint(wifi.active_access_point)
                ssid: bytes = ap.ssid
                if ssid:
                    print("SSID:   ", ssid.decode("utf-8", "ignore"))
                if ap.strength:
                    print("Signal: ", ap.strength)

    def run(self):
        counter = 0
        while True:
            self.list_networkdevice_details_blocking()

if __name__ == '__main__':
    # does not work
    wifi = WifiThread()
    wifi.start()
    wifi.join()
    # does work
    # sdbus.set_default_bus(sdbus.sd_bus_open_system())
    # WifiThread.list_networkdevice_details_blocking()

I will get error DbusServiceUnknownError if I run it in a child thread.

Exception has occurred: DbusServiceUnknownError
The name org.freedesktop.NetworkManager was not provided by any .service files
  File "/home/longqi/PycharmProjects/uranus/wifi/daemon.py", line 33, in list_networkdevice_details_blocking
    for device_path in NetworkManager().get_devices():
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/longqi/PycharmProjects/uranus/wifi/daemon.py", line 75, in run
    self.list_networkdevice_details_blocking()
sdbus.dbus_exceptions.DbusServiceUnknownError: The name org.freedesktop.NetworkManager was not provided by any .service files
igo95862 commented 1 year ago

Hello,

Sd-bus is not thread safe. Each thread has its own default bus.

The default bus is usually the user session bus NetworkManager is ran over the system bus.

To fix this sdbus.set_default_bus(sdbus.sd_bus_open_system()) should be ran after the thread starts.

zhanglongqi commented 1 year ago

it works. Thank you.