refactor: Simplify D-Bus handling by using address-based connections

Replaced direct `SystemBus` and `SessionBus` instances with
address-based D-Bus connections. BusProviders will open their own bus
connections and close it at the end.

Signed-off-by: Sven Sager <s.sager@kunbus.com>
This commit is contained in:
Sven Sager
2026-02-06 19:14:45 +01:00
parent 5dfd8b9f2f
commit 794089a5b5
4 changed files with 33 additions and 84 deletions

View File

@@ -4,16 +4,16 @@
"""Main daemon of revpi-middleware.""" """Main daemon of revpi-middleware."""
from logging import getLogger from logging import getLogger
from os import getuid, environ
from threading import Event from threading import Event
from time import perf_counter from time import perf_counter
from typing import Union, List from typing import List
from dbus import SystemBus, SessionBus from gi.repository import Gio
from . import proginit as pi from . import proginit as pi
from .dbus_ios1 import BusProviderIos1 from .dbus_ios1 import BusProviderIos1
from .dbus_middleware1 import BusProviderMiddleware1 from .dbus_middleware1 import BusProviderMiddleware1
from .helper import get_new_session_dbus_connection, get_new_system_dbus_connection
log = getLogger(__name__) log = getLogger(__name__)
@@ -43,19 +43,23 @@ class MiddlewareDaemon:
log.debug("leave MiddlewareDaemon._configure") log.debug("leave MiddlewareDaemon._configure")
def _get_bus(self) -> Union[SystemBus, SessionBus]: @staticmethod
return ( def _get_bus_address() -> str:
get_new_session_dbus_connection() if pi.pargs.use_session_bus:
if pi.pargs.use_session_bus environ_name = "DBUS_SESSION_BUS_ADDRESS"
else get_new_system_dbus_connection() bus_address = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SESSION)
) else:
environ_name = "DBUS_SYSTEM_BUS_ADDRESS"
bus_address = Gio.dbus_address_get_for_bus_sync(Gio.BusType.SYSTEM)
return environ.get(environ_name, bus_address)
def dbus_start(self): def dbus_start(self):
log.debug("enter MiddlewareDaemon.dbus_start") log.debug("enter MiddlewareDaemon.dbus_start")
dbus_middleware1_running = self.bp_middleware1 and self.bp_middleware1.is_alive() dbus_middleware1_running = self.bp_middleware1 and self.bp_middleware1.is_alive()
if not dbus_middleware1_running: if not dbus_middleware1_running:
self.bp_middleware1 = BusProviderMiddleware1(self._get_bus()) self.bp_middleware1 = BusProviderMiddleware1(self._get_bus_address())
self.bp_middleware1.start() self.bp_middleware1.start()
dbus_ios1_running = self.bp_ios1 and self.bp_ios1.is_alive() dbus_ios1_running = self.bp_ios1 and self.bp_ios1.is_alive()
@@ -79,14 +83,12 @@ class MiddlewareDaemon:
self.bp_middleware1.join(timeout=10.0) self.bp_middleware1.join(timeout=10.0)
if self.bp_middleware1.is_alive(): if self.bp_middleware1.is_alive():
log.warning("dbus middleware1 provider thread is still alive") log.warning("dbus middleware1 provider thread is still alive")
self.bp_middleware1.bus.con.close()
if self.bp_ios1 and self.bp_ios1 in bus_filter: if self.bp_ios1 and self.bp_ios1 in bus_filter:
self.bp_ios1.stop() self.bp_ios1.stop()
self.bp_ios1.join(timeout=10.0) self.bp_ios1.join(timeout=10.0)
if self.bp_ios1.is_alive(): if self.bp_ios1.is_alive():
log.warning("dbus ios1 provider thread is still alive") log.warning("dbus ios1 provider thread is still alive")
self.bp_ios1.bus.con.close()
log.debug("leave MiddlewareDaemon.dbus_stop") log.debug("leave MiddlewareDaemon.dbus_stop")

View File

@@ -5,11 +5,10 @@
from logging import getLogger from logging import getLogger
from threading import Thread, Event from threading import Thread, Event
from typing import Union
import revpimodio2 import revpimodio2
from gi.repository import GLib from gi.repository import GLib
from pydbus import SessionBus, SystemBus from pydbus import connect
from . import REVPI_DBUS_NAME from . import REVPI_DBUS_NAME
from .interface_devices import ( from .interface_devices import (
@@ -29,14 +28,14 @@ class BusProviderIos1(Thread):
def __init__( def __init__(
self, self,
dbus: Union[SystemBus, SessionBus], dbus_address: str,
picontrol_device="/dev/piControl0", picontrol_device="/dev/piControl0",
config_rsc="/etc/revpi/config.rsc", config_rsc="/etc/revpi/config.rsc",
): ):
log.debug("enter BusProviderIos1.__init__") log.debug("enter BusProviderIos1.__init__")
super().__init__() super().__init__()
self._bus = dbus self._bus_address = dbus_address
self._loop = GLib.MainLoop() self._loop = GLib.MainLoop()
self._lst_device_interfaces = [] self._lst_device_interfaces = []
self._lst_io_interfaces = [] self._lst_io_interfaces = []
@@ -52,22 +51,23 @@ class BusProviderIos1(Thread):
def run(self): def run(self):
log.debug("enter BusProviderIos1.run") log.debug("enter BusProviderIos1.run")
bus = connect(self._bus_address)
self._lst_device_interfaces.clear() self._lst_device_interfaces.clear()
self._lst_io_interfaces.clear() self._lst_io_interfaces.clear()
for device in self._modio.device: for device in self._modio.device:
self._lst_device_interfaces.append( self._lst_device_interfaces.append(
InterfaceDevice(self._bus, device), InterfaceDevice(bus, device),
) )
for io in self._modio.io: for io in self._modio.io:
interface = None interface = None
try: try:
if io.type == revpimodio2.INP: if io.type == revpimodio2.INP:
interface = InterfaceInput(self._bus, io) interface = InterfaceInput(bus, io)
elif io.type == revpimodio2.OUT: elif io.type == revpimodio2.OUT:
interface = InterfaceOutput(self._bus, io) interface = InterfaceOutput(bus, io)
elif io.type == revpimodio2.MEM: elif io.type == revpimodio2.MEM:
# todo: Implement memory # todo: Implement memory
pass pass
@@ -85,7 +85,7 @@ class BusProviderIos1(Thread):
(interface.object_path, interface) for interface in self._lst_io_interfaces (interface.object_path, interface) for interface in self._lst_io_interfaces
] ]
try: try:
self._bus.publish( bus.publish(
REVPI_DBUS_NAME, REVPI_DBUS_NAME,
InterfaceDeviceManager(self._lst_device_interfaces), InterfaceDeviceManager(self._lst_device_interfaces),
InterfaceIoManager(self._lst_io_interfaces, self._modio), InterfaceIoManager(self._lst_io_interfaces, self._modio),
@@ -100,6 +100,7 @@ class BusProviderIos1(Thread):
except Exception as e: except Exception as e:
log.error(f"can not run dbus mainloop: {e}") log.error(f"can not run dbus mainloop: {e}")
bus.con.close()
self._modio.cleanup() self._modio.cleanup()
log.debug("leave BusProviderIos1.run") log.debug("leave BusProviderIos1.run")
@@ -109,10 +110,6 @@ class BusProviderIos1(Thread):
self._loop.quit() self._loop.quit()
log.debug("leave BusProviderIos1.stop") log.debug("leave BusProviderIos1.stop")
@property
def bus(self) -> Union[SystemBus, SessionBus]:
return self._bus
@property @property
def running(self): def running(self):
return self._loop.is_running() return self._loop.is_running()

View File

@@ -5,10 +5,9 @@
from logging import getLogger from logging import getLogger
from threading import Thread, Event from threading import Thread, Event
from typing import Union
from gi.repository import GLib from gi.repository import GLib
from pydbus import SessionBus, SystemBus from pydbus import connect
from . import REVPI_DBUS_NAME from . import REVPI_DBUS_NAME
from .process_image import InterfacePiControl from .process_image import InterfacePiControl
@@ -21,14 +20,14 @@ class BusProviderMiddleware1(Thread):
def __init__( def __init__(
self, self,
dbus: Union[SystemBus, SessionBus], dbus_address: str,
picontrol_device="/dev/piControl0", picontrol_device="/dev/piControl0",
config_rsc="/etc/revpi/config.rsc", config_rsc="/etc/revpi/config.rsc",
): ):
log.debug("enter BusProviderMiddleware1.__init__") log.debug("enter BusProviderMiddleware1.__init__")
super().__init__() super().__init__()
self._bus = dbus self._bus_address = dbus_address
self._loop = GLib.MainLoop() self._loop = GLib.MainLoop()
self.picontrol_device = picontrol_device self.picontrol_device = picontrol_device
@@ -37,6 +36,7 @@ class BusProviderMiddleware1(Thread):
def run(self): def run(self):
log.debug("enter BusProviderMiddleware1.run") log.debug("enter BusProviderMiddleware1.run")
bus = connect(self._bus_address)
# The 2nd, 3rd, ... arguments can be objects or tuples of a path and an object # The 2nd, 3rd, ... arguments can be objects or tuples of a path and an object
# Example(), # Example(),
@@ -44,13 +44,13 @@ class BusProviderMiddleware1(Thread):
# ("Subdir2", Example()), # ("Subdir2", Example()),
# ("Subdir2/Whatever", Example()) # ("Subdir2/Whatever", Example())
lst_interfaces = [ lst_interfaces = [
InterfacePiControl(self._bus, self.picontrol_device, self.config_rsc), InterfacePiControl(bus, self.picontrol_device, self.config_rsc),
InterfaceRevpiConfig(self._bus), InterfaceRevpiConfig(bus),
InterfaceSoftwareServices(self._bus), InterfaceSoftwareServices(bus),
] ]
try: try:
self._bus.publish( bus.publish(
REVPI_DBUS_NAME, REVPI_DBUS_NAME,
*lst_interfaces, *lst_interfaces,
) )
@@ -63,6 +63,8 @@ class BusProviderMiddleware1(Thread):
except Exception as e: except Exception as e:
log.error(f"can not run dbus mainloop: {e}") log.error(f"can not run dbus mainloop: {e}")
bus.con.close()
# Clean up all interfaces # Clean up all interfaces
for interface in lst_interfaces: for interface in lst_interfaces:
if type(interface) is tuple: if type(interface) is tuple:
@@ -76,10 +78,6 @@ class BusProviderMiddleware1(Thread):
self._loop.quit() self._loop.quit()
log.debug("leave BusProviderMiddleware1.stop") log.debug("leave BusProviderMiddleware1.stop")
@property
def bus(self) -> Union[SystemBus, SessionBus]:
return self._bus
@property @property
def running(self): def running(self):
return self._loop.is_running() return self._loop.is_running()

View File

@@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
# SPDX-FileCopyrightText: 2025 KUNBUS GmbH
# SPDX-License-Identifier: GPL-2.0-or-later
"""Helper functions for the root project."""
from os import environ, getuid
from pydbus import connect
def get_new_system_dbus_connection():
"""
Establishes a new connection to the system D-Bus.
This function retrieves the system D-Bus address from the environment
variable `DBUS_SYSTEM_BUS_ADDRESS`, defaulting to
`unix:path=/run/dbus/system_bus_socket` if the variable is not set.
It then establishes and returns a connection to the D-Bus at that
address.
Raises:
An error may be raised if establishing the connection fails.
Returns:
The established system D-Bus connection.
"""
address = environ.get("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/run/dbus/system_bus_socket")
return connect(address)
def get_new_session_dbus_connection():
"""
Establishes a new connection to the session D-Bus.
This function retrieves the session D-Bus address from the environment
variable `DBUS_SESSION_BUS_ADDRESS`, defaulting to
`unix:path=/run/user/{UID}/bus` if the variable is not set.
It then establishes and returns a connection to the D-Bus at that
address.
Raises:
An error may be raised if establishing the connection fails.
Returns:
The established session D-Bus connection.
"""
address = environ.get("DBUS_SESSION_BUS_ADDRESS", f"unix:path=/run/user/{getuid()}/bus")
return connect(address)