mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-05-07 22:02:50 +00:00
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/<uid>/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 <noreply@anthropic.com> * dconf: add changelog fragment for dbus-broker support Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * 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 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
3
changelogs/fragments/11772-dconf-dbus-broker.yml
Normal file
3
changelogs/fragments/11772-dconf-dbus-broker.yml
Normal file
@@ -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).
|
||||||
@@ -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)
|
- 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.
|
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
|
- 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:
|
requirements:
|
||||||
- Optionally the C(gi.repository) Python library (usually included in the OS on hosts which have C(dconf)); this is to become
|
- 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.
|
a non-optional requirement in a future major release of community.general.
|
||||||
notes:
|
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)
|
- This module depends on C(psutil) Python library (version 4.0.0 and upwards), C(dconf), and either C(dbus-send) or C(busctl)
|
||||||
binaries. Depending on distribution you are using, you may need to install additional packages to have these available.
|
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
|
- 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
|
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,
|
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,
|
If possible, command will be run against an existing D-Bus session,
|
||||||
otherwise the session will be spawned via dbus-run-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/<uid>/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:
|
Example usage:
|
||||||
|
|
||||||
dbus_wrapper = DBusWrapper(ansible_module)
|
dbus_wrapper = DBusWrapper(ansible_module)
|
||||||
@@ -163,24 +172,32 @@ class DBusWrapper:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, module):
|
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
|
self.module = module
|
||||||
|
|
||||||
# Try to extract existing D-Bus session address.
|
|
||||||
self.dbus_session_bus_address = self._get_existing_dbus_session()
|
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:
|
if self.dbus_session_bus_address is None:
|
||||||
self.dbus_run_session_cmd = self.module.get_bin_path("dbus-run-session", required=True)
|
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):
|
def _get_existing_dbus_session(self):
|
||||||
"""
|
"""
|
||||||
Detects and returns an existing D-Bus session bus address.
|
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.
|
# We'll be checking the processes of current user only.
|
||||||
uid = os.getuid()
|
uid = os.getuid()
|
||||||
|
|
||||||
# Go through all the pids for this user, try to extract the D-Bus
|
# Step 1: current process environment
|
||||||
# session bus address from environment, and ensure it is possible to
|
address = os.environ.get("DBUS_SESSION_BUS_ADDRESS")
|
||||||
# connect to it.
|
if address:
|
||||||
self.module.debug(f"Trying to detect existing D-Bus user session for user: {int(uid)}")
|
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():
|
for pid in psutil.pids():
|
||||||
try:
|
try:
|
||||||
process = psutil.Process(pid)
|
process = psutil.Process(pid)
|
||||||
@@ -205,21 +236,10 @@ class DBusWrapper:
|
|||||||
self.module.debug(
|
self.module.debug(
|
||||||
f"Found D-Bus user session candidate at address: {dbus_session_bus_address_candidate}"
|
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)
|
if self._validate_address(dbus_session_bus_address_candidate):
|
||||||
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:
|
|
||||||
self.module.debug(
|
self.module.debug(
|
||||||
f"Verified D-Bus user session candidate as usable at address: {dbus_session_bus_address_candidate}"
|
f"Verified D-Bus user session candidate as usable at address: {dbus_session_bus_address_candidate}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return dbus_session_bus_address_candidate
|
return dbus_session_bus_address_candidate
|
||||||
|
|
||||||
# This can happen with things like SSH sessions etc.
|
# This can happen with things like SSH sessions etc.
|
||||||
@@ -230,7 +250,6 @@ class DBusWrapper:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
self.module.debug("Failed to find running D-Bus user session, will use dbus-run-session")
|
self.module.debug("Failed to find running D-Bus user session, will use dbus-run-session")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def run_command(self, command):
|
def run_command(self, command):
|
||||||
@@ -249,8 +268,7 @@ class DBusWrapper:
|
|||||||
self.module.debug("Using dbus-run-session wrapper for running commands.")
|
self.module.debug("Using dbus-run-session wrapper for running commands.")
|
||||||
command = [self.dbus_run_session_cmd] + command
|
command = [self.dbus_run_session_cmd] + command
|
||||||
rc, out, err = self.module.run_command(command)
|
rc, out, err = self.module.run_command(command)
|
||||||
|
if rc == 127:
|
||||||
if self.dbus_session_bus_address is None and rc == 127:
|
|
||||||
self.module.fail_json(
|
self.module.fail_json(
|
||||||
msg=f"Failed to run passed-in command, dbus-run-session faced an internal error: {err}"
|
msg=f"Failed to run passed-in command, dbus-run-session faced an internal error: {err}"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user