python – how to show a notification when trying to shutdown debian from the desktop environment?

Question:

I am developing an application that blocks the shutdown and shows a notification when the user tries to shutdown with a pendrive connected.

In other distributions, I have achieved it using polkit, with a .rules file that calls a script; but in Debian, the polkit version (0.105) does not support those files and only allows the use of .pkla files, which do not allow calling scripts.

My pkla file is this:

[Shutdown]
Identity=unix-user:*
Action=org.freedesktop.consolekit.system.stop; org.freedesktop.login1.power-off;org.freedesktop.login1.power-off-multiple-sessions;org.xfce.session.xfsm-shutdown-helper
ResultAny=no
ResultInactive=no
ResultActive=no

With this I have managed to block the shutdown, but I cannot show the notification.

It occurred to me that I could use a user-launched dbus client that is hooked up to system shutdown events, but I can't get it to work.

I have tried with this code:

#!/usr/bin/python3

from gi.repository import GLib
from gi.repository import Notify
import dbus
from dbus.mainloop.glib import DBusGMainLoop

dbus_loop = DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus(mainloop=dbus_loop)
loop = GLib.MainLoop()

def msg_handler(*args,**keywords):
    try:
         #show notification to desktop
         Notify.init('Pendrive Reminder')
         notify = Notify.Notification.new('Pendrive Reminder', 'Shutdown lock enabled. Disconnect pendrive to enable shutdown')
         notify.show()
     except:
        pass

 bus.add_signal_receiver(handler_function=msg_handler,dbus_interface='org.freedesktop.login1.power-off-multiple-sessions', path_keyword='path')

But, when I run it, with:

python client.py

I get this error:

 ValueError: Invalid interface or error name 'org.freedesktop.login1.power-off-multiple-sessions': contains invalid character '-'

how could I detect the event from dbus?

UPGRADE

Following the recommendations provided in the answers, I have changed the capture of the event. The resulting code is the following:

from gi.repository import GLib
from gi.repository import Notify
import dbus
from dbus.mainloop.glib import DBusGMainLoop

dbus_loop = DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus(mainloop=dbus_loop)
loop = GLib.MainLoop()

def msg_handler(*args,**keywords):
   try:
        #show notification to desktop
        Notify.init('Pendrive Reminder')
        notify = Notify.Notification.new('Pendrive Reminder', 'Shutdown lock enabled. Disconnect pendrive to enable shutdown')
        notify.show()
    except:
        pass

bus.add_signal_receiver(
    handler_function=msg_handler, 
    signal_name='PreparingForShutdown',
    dbus_interface='org.freedesktop.login1.Manager', 
    bus_name='org.freedesktop.login1'
)

bus.add_signal_receiver(
    handler_function=msg_handler, 
    signal_name='CanPowerOff',
    dbus_interface='org.freedesktop.login1.Manager', 
    bus_name='org.freedesktop.login1'
)

bus.add_signal_receiver(
    handler_function=msg_handler, 
    signal_name='PowerOff',
    dbus_interface='org.freedesktop.login1.Manager', 
    bus_name='org.freedesktop.login1'
)

loop.run()

With this I no longer get a syntax error, but I can't capture the signal either: when I press the off button, the notification is not displayed.

where could be the error?

In fact, I can't find it in the list of running processes either

UPDATE 2

Using dbus-monitor --system I have found that the action taken to block the shutdown is as follows:

signal time=1523017240.291438 sender=:1.16 -> destination=(null destination) serial=794 path=/org/freedesktop/login1; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
   string "org.freedesktop.login1.Manager"
   array [
      dict entry(
         string "DelayInhibited"
         variant             string "shutdown:sleep"
      )
   ]
   array [
   ]

I have tried to capture it with pydbus like so:

from pydbus import SystemBus
from gi.repository import GLib
from gi.repository import Notify
from dbus.mainloop.glib import DBusGMainLoop

bus = SystemBus()

# Create an object that will proxy for a particular remote object.
power = bus.get('org.freedesktop.login1', '/org/freedesktop/login1')
loop = GLib.MainLoop()


if power.PropertiesChanged() == True:
    #show notification to desktop
    Notify.init('Pendrive Reminder')
    notify = Notify.Notification.new('Pendrive Reminder', 'Shutdown lock enabled. Disconnect pendrive to enable shutdown')
    notify.show()


loop.run()

But now it returns this error:

 AttributeError: 'ProxySignal' object has no attribute 'emit'

I think the problem is that the signal returns a structure, instead of a boolean, but I don't know how I could recreate the structure in Python

any ideas?

Answer:

I'm afraid the problem is in the name you've used for the interface. Mid-hyphens are not allowed for them, according to Freedesktop.org's instructions:

D-BUS Valid names

If you consult these guidelines, you will see that the names used for interfaces, buses, and members have certain common characteristics:

  • A maximum of 255 characters.
  • Each element can contain alphanumeric characters, both upper and lower case, but cannot start with a number.
  • They must contain at least one period (.), but it cannot be the initial character.

But they also have certain restrictions:

  • Interfaces cannot contain hyphens, while bus names do.

My recommendation would be to use the familiar interface name org.freedesktop.login1 .

More information about it at freedesktop.org:

logind

DBUS walkthrough

Scroll to Top