From 24ca79658a54fcbe928eeebb0db0fc2b829108ee Mon Sep 17 00:00:00 2001 From: Alexei Znamensky <103110+russoz@users.noreply.github.com> Date: Wed, 15 Apr 2026 07:49:09 +1200 Subject: [PATCH] dconf: add dbus-broker support by improving D-Bus session discovery (#11772) * dconf: add dbus-broker support by improving D-Bus session discovery Extend DBusWrapper._get_existing_dbus_session() to check: 1. DBUS_SESSION_BUS_ADDRESS in the current process environment 2. /run/user//bus (canonical socket for systemd and dbus-broker) 3. Process scan (legacy fallback, as before) Also add _validate_address() to support both dbus-send and busctl, making the module work on systems using dbus-broker (e.g. Fedora Silverblue) where no process exposes DBUS_SESSION_BUS_ADDRESS in its environment. Fixes: https://github.com/ansible-collections/community.general/issues/495 Co-Authored-By: Claude Sonnet 4.6 * dconf: add changelog fragment for dbus-broker support Co-Authored-By: Claude Sonnet 4.6 * dconf: restore dbus validator requirement and example usage Restore fail_json when neither dbus-send nor busctl is available, preserving the original hard requirement for a validator binary. Restore the example invocation in DBusWrapper docstring. Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- .../fragments/11772-dconf-dbus-broker.yml | 3 + plugins/modules/dconf.py | 86 +++++++++++-------- 2 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 changelogs/fragments/11772-dconf-dbus-broker.yml diff --git a/changelogs/fragments/11772-dconf-dbus-broker.yml b/changelogs/fragments/11772-dconf-dbus-broker.yml new file mode 100644 index 0000000000..ac7de2b762 --- /dev/null +++ b/changelogs/fragments/11772-dconf-dbus-broker.yml @@ -0,0 +1,3 @@ +minor_changes: + - dconf - add support for C(dbus-broker) (https://github.com/ansible-collections/community.general/issues/495, + https://github.com/ansible-collections/community.general/pull/11772). diff --git a/plugins/modules/dconf.py b/plugins/modules/dconf.py index 8db0547bdc..badd007233 100644 --- a/plugins/modules/dconf.py +++ b/plugins/modules/dconf.py @@ -15,13 +15,15 @@ description: - This module allows modifications and reading of C(dconf) database. The module is implemented as a wrapper around C(dconf) tool. Please see the dconf(1) man page for more details. - Since C(dconf) requires a running D-Bus session to change values, the module tries to detect an existing session and reuse - it, or run the tool using C(dbus-run-session). + it, or run the tool using C(dbus-run-session). Both C(dbus-daemon) and C(dbus-broker) are supported. requirements: - Optionally the C(gi.repository) Python library (usually included in the OS on hosts which have C(dconf)); this is to become a non-optional requirement in a future major release of community.general. notes: - - This module depends on C(psutil) Python library (version 4.0.0 and upwards), C(dconf), C(dbus-send), and C(dbus-run-session) - binaries. Depending on distribution you are using, you may need to install additional packages to have these available. + - This module depends on C(psutil) Python library (version 4.0.0 and upwards), C(dconf), and either C(dbus-send) or C(busctl) + for D-Bus session validation, and C(dbus-run-session) as a fallback when no existing session is found. + Depending on distribution you are using, you may need to install additional packages to have these available. + Both the reference C(dbus-daemon) implementation and C(dbus-broker) are supported. - This module uses the C(gi.repository) Python library when available for accurate comparison of values in C(dconf) to values specified in Ansible code. C(gi.repository) is likely to be present on most systems which have C(dconf) but may not be present everywhere. When it is missing, a simple string comparison between values is used, and there may be false positives, @@ -156,6 +158,13 @@ class DBusWrapper: If possible, command will be run against an existing D-Bus session, otherwise the session will be spawned via dbus-run-session. + Discovery order: + 1. DBUS_SESSION_BUS_ADDRESS in the current process environment + 2. /run/user//bus -- canonical socket for systemd and dbus-broker + 3. Process scan for DBUS_SESSION_BUS_ADDRESS -- legacy fallback + + Validation uses C(dbus-send) if available, C(busctl) otherwise. + Example usage: dbus_wrapper = DBusWrapper(ansible_module) @@ -163,24 +172,32 @@ class DBusWrapper: """ def __init__(self, module): - """ - Initialises an instance of the class. - - :param module: Ansible module instance used to signal failures and run commands. - :type module: AnsibleModule - """ - - # Store passed-in arguments and set-up some defaults. self.module = module - - # Try to extract existing D-Bus session address. self.dbus_session_bus_address = self._get_existing_dbus_session() - # If no existing D-Bus session was detected, check if dbus-run-session - # is available. if self.dbus_session_bus_address is None: self.dbus_run_session_cmd = self.module.get_bin_path("dbus-run-session", required=True) + def _validate_address(self, address): + dbus_send = self.module.get_bin_path("dbus-send") + if dbus_send: + rc, dummy, dummy = self.module.run_command( + [dbus_send, f"--address={address}", "--type=signal", "/", "com.example.test"] + ) + if rc == 0: + return True + + busctl = self.module.get_bin_path("busctl") + if busctl: + rc, dummy, dummy = self.module.run_command([busctl, f"--address={address}", "list"]) + if rc == 0: + return True + + if not dbus_send and not busctl: + self.module.fail_json(msg="Neither dbus-send nor busctl is available. Please install one of them.") + + return False + def _get_existing_dbus_session(self): """ Detects and returns an existing D-Bus session bus address. @@ -191,11 +208,25 @@ class DBusWrapper: # We'll be checking the processes of current user only. uid = os.getuid() - # Go through all the pids for this user, try to extract the D-Bus - # session bus address from environment, and ensure it is possible to - # connect to it. - self.module.debug(f"Trying to detect existing D-Bus user session for user: {int(uid)}") + # Step 1: current process environment + address = os.environ.get("DBUS_SESSION_BUS_ADDRESS") + if address: + self.module.debug(f"Trying D-Bus address from environment: {address}") + if self._validate_address(address): + self.module.debug(f"Using D-Bus session from environment: {address}") + return address + # Step 2: canonical systemd/dbus-broker socket path + canonical_path = f"/run/user/{uid}/bus" + if os.path.exists(canonical_path): + address = f"unix:path={canonical_path}" + self.module.debug(f"Trying canonical D-Bus socket: {canonical_path}") + if self._validate_address(address): + self.module.debug(f"Using canonical D-Bus socket: {canonical_path}") + return address + + # Step 3: process scan (legacy fallback) + self.module.debug(f"Scanning processes for D-Bus session (uid={uid})") for pid in psutil.pids(): try: process = psutil.Process(pid) @@ -205,21 +236,10 @@ class DBusWrapper: self.module.debug( f"Found D-Bus user session candidate at address: {dbus_session_bus_address_candidate}" ) - dbus_send_cmd = self.module.get_bin_path("dbus-send", required=True) - command = [ - dbus_send_cmd, - f"--address={dbus_session_bus_address_candidate}", - "--type=signal", - "/", - "com.example.test", - ] - rc, dummy, dummy = self.module.run_command(command) - - if rc == 0: + if self._validate_address(dbus_session_bus_address_candidate): self.module.debug( f"Verified D-Bus user session candidate as usable at address: {dbus_session_bus_address_candidate}" ) - return dbus_session_bus_address_candidate # This can happen with things like SSH sessions etc. @@ -230,7 +250,6 @@ class DBusWrapper: pass self.module.debug("Failed to find running D-Bus user session, will use dbus-run-session") - return None def run_command(self, command): @@ -249,8 +268,7 @@ class DBusWrapper: self.module.debug("Using dbus-run-session wrapper for running commands.") command = [self.dbus_run_session_cmd] + command rc, out, err = self.module.run_command(command) - - if self.dbus_session_bus_address is None and rc == 127: + if rc == 127: self.module.fail_json( msg=f"Failed to run passed-in command, dbus-run-session faced an internal error: {err}" )