Search code examples
linuxsystemddbus

Detect inhibited shutdown on D-Bus


I use x86_64 Debian 9 Stretch. I run systemd-inhibit cat and then in another console systemctl poweroff. Shutdown correctly gets inhibited. According to this doc signal PrepareForShutdown(false) is supposed to be emitted, but I can't see it. I watch dbus with dbus-monitor --system and using a python program:

#!/usr/bin/env python

import dbus
import gobject
from dbus.mainloop.glib import DBusGMainLoop

def handle(*args):
    print "PrepareForShutdown %s" % (args)

DBusGMainLoop(set_as_default=True)     # integrate into gobject main loop
bus = dbus.SystemBus()                 # connect to system wide dbus
bus.add_signal_receiver(               # define the signal to listen to
    handle,                            # callback function
    'PrepareForShutdown',              # signal name
    'org.freedesktop.login1.Manager',  # interface
    'org.freedesktop.login1'           # bus name
)

loop = gobject.MainLoop()
loop.run()

The program prints nothing. The dbus-monitor outputs few obscure messages (looks like smth calls ListInhibitors).

Is signal not being emited or I just can't catch it? My goal is to detect inhibited shutdown by listening D-Bus, how do I do it?

EDIT: Turned out when non-delayed inhibition is used, shutdown request just gets discarded, signal doesn't fire. But if I use delay lock via systemd-inhibit --mode=delay --what=shutdown cat then PrepareForShutdown signal fires.


Solution

  • Is signal not being emited or I just can't catch it?

    Not sure. My guess would be that systemd only emits the signal to processes which have taken a delay lock (unicast signal emission), as the documentation page has some pretty dire warnings about race conditions if you listen for PrepareForShutdown without taking a delay lock first.

    The way to check this would be to read the systemd source code.

    My goal is to detect inhibited shutdown by listening D-Bus, how do I do it?

    If I run sudo dbus-monitor --system in one terminal, and then run systemd-inhibit cat in another, I see the following signal emission:

    signal time=1543917703.712998 sender=:1.9 -> destination=(null destination) serial=1150 path=/org/freedesktop/login1; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
       string "org.freedesktop.login1.Manager"
       array [
          dict entry(
             string "BlockInhibited"
             variant             string "shutdown:sleep:idle:handle-power-key:handle-suspend-key:handle-hibernate-key:handle-lid-switch"
          )
       ]
       array [
       ]
    

    Hence you could watch for property changes on the /org/freedesktop/login1 object exposed by service org.freedesktop.login1, and see when its BlockInhibited or DelayInhibited properties change. Shutdown is inhibited when either of those properties contains shutdown. They are documented on the same documentation page:

    The BlockInhibited and DelayInhibited properties encode what types of locks are currently taken. These fields are a colon separated list of shutdown, sleep, idle, handle-power-key, handle-suspend-key, handle-hibernate-key, handle-lid-switch. The list is basically the union of the What fields of all currently active locks of the specific mode.