Why does systemd not send unit properties changed notifcations on session bus?

Viewed 239

I implemented a dbus systemd listener in Python (3.7) that shall monitor property changes of a systemd unit. On the session dbus it does not receive any notifications. Running on the system dbus the code does what is expected.

Is there a way to also receive unit changed notifications on the session bus?

My system: A Raspberry 4 running latest version of Raspberry PI OS.

This is the service I created.

[Unit]
Description = A dummy service
    
[Service]
Type = simple
ExecStart = /bin/true
RemainAfterExit=yes

I installed the service to /etc/systemd/system and ~/.config/systemd/user and executed daemon-reload for the system and the user session. After doing, the service is known as user and as system service.

This is the dummy_listener.py code

#!/usr/bin/env python3

# Python version required: >= 3.7 (because of used asyncio API)

"""A simple subscriber/listener for systemd unit signals"""
import sys
import asyncio
from dbus_next.aio import MessageBus
from dbus_next import BusType

class DbusDummyService():  # pylint: disable=no-self-use
    """Asyncio based dummy.service listener"""
    async def init(self, bus_type=BusType.SESSION):
        """Register listener callback with dbus bus_type"""
        bus = await MessageBus(bus_type=bus_type).connect()
        # Get introspection XML
        introspection = await bus.introspect('org.freedesktop.systemd1',
                                             '/org/freedesktop/systemd1/unit/dummy_2eservice')
        # Select systemd service object
        obj = bus.get_proxy_object('org.freedesktop.systemd1',
                                   '/org/freedesktop/systemd1/unit/dummy_2eservice', introspection)
        # Get required interfaces
        properties_if = obj.get_interface('org.freedesktop.DBus.Properties')
        # Monitor service status changes
        properties_if.on_properties_changed(self.on_properties_changed_cb)

    def on_properties_changed_cb(self, interface_name, changed_props, invalidated_props):
        """Callback expected to be called on unit property changes"""
        print(f"Callback invoked for interface {interface_name}:")
        print(f"  Properties updated")
        for prop, val in changed_props.items():
            print(f"    {prop} set to {val.value}")
        print(f"  Properties invalidated")
        for prop in invalidated_props:
            print(f"    {prop} invalidated")

async def main(bus_type):
    """Asyncio main"""
    # Initialize dbus listener
    await DbusDummyService().init(bus_type)
    # Run loop forever (waiting for dbus signals)
    await asyncio.get_running_loop().create_future()

if __name__ == "__main__":
    try:
        BUS_TYPE = BusType.SYSTEM if 'sys' in sys.argv[1] else BusType.SESSION
    except BaseException:
        BUS_TYPE = BusType.SESSION

    asyncio.run(main(BUS_TYPE))

The listener is ran like this on system dbus

sudo python3 dummy_lister.py sys

For session bus it is ran with

python3 dummy_lister.py

In a separate window I now restart the dummy services and expect the listener to output the prints.

For the session dbus:

systemctl --user restart dummy

For the system dbus:

sudo systemctl restart dummy

On the session dbus the listener just prints nothing. On the system dbus, I receive a bunch of messages.

Any ideas?

1 Answers

systemd doesn't send PropertiesChanged signals unless at least one client is subscribed to it. You need to call the Subscribe() method from the org.freedesktop.systemd1.Manager interface on the /org/freedesktop/systemd1 object.

Related