Shutdown using systemd-logind if available (#2416)

The Reboot() and PowerOff() methods of the systemd D-Bus API are not
meant to be called directly when logind is in use. Make sure to use the
logind APIs if available.

Since OS release 5.1 systemd-logind is enabled.
This commit is contained in:
Stefan Agner 2021-01-11 11:21:45 +01:00 committed by GitHub
parent 6b32fa31b6
commit bd173fa333
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 5 deletions

View File

@ -6,6 +6,7 @@ from ..const import SOCKET_DBUS
from ..coresys import CoreSys, CoreSysAttributes from ..coresys import CoreSys, CoreSysAttributes
from .hostname import Hostname from .hostname import Hostname
from .interface import DBusInterface from .interface import DBusInterface
from .logind import Logind
from .network import NetworkManager from .network import NetworkManager
from .rauc import Rauc from .rauc import Rauc
from .systemd import Systemd from .systemd import Systemd
@ -21,6 +22,7 @@ class DBusManager(CoreSysAttributes):
self.coresys: CoreSys = coresys self.coresys: CoreSys = coresys
self._systemd: Systemd = Systemd() self._systemd: Systemd = Systemd()
self._logind: Logind = Logind()
self._hostname: Hostname = Hostname() self._hostname: Hostname = Hostname()
self._rauc: Rauc = Rauc() self._rauc: Rauc = Rauc()
self._network: NetworkManager = NetworkManager() self._network: NetworkManager = NetworkManager()
@ -30,6 +32,11 @@ class DBusManager(CoreSysAttributes):
"""Return the systemd interface.""" """Return the systemd interface."""
return self._systemd return self._systemd
@property
def logind(self) -> Logind:
"""Return the logind interface."""
return self._logind
@property @property
def hostname(self) -> Hostname: def hostname(self) -> Hostname:
"""Return the hostname interface.""" """Return the hostname interface."""
@ -55,6 +62,7 @@ class DBusManager(CoreSysAttributes):
dbus_loads: List[DBusInterface] = [ dbus_loads: List[DBusInterface] = [
self.systemd, self.systemd,
self.logind,
self.hostname, self.hostname,
self.network, self.network,
self.rauc, self.rauc,

View File

@ -18,6 +18,7 @@ DBUS_NAME_NM_CONNECTION_ACTIVE_CHANGED = (
"org.freedesktop.NetworkManager.Connection.Active.PropertiesChanged" "org.freedesktop.NetworkManager.Connection.Active.PropertiesChanged"
) )
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1" DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
DBUS_NAME_LOGIND = "org.freedesktop.login1"
DBUS_OBJECT_BASE = "/" DBUS_OBJECT_BASE = "/"
DBUS_OBJECT_DNS = "/org/freedesktop/NetworkManager/DnsManager" DBUS_OBJECT_DNS = "/org/freedesktop/NetworkManager/DnsManager"
@ -25,6 +26,7 @@ DBUS_OBJECT_SETTINGS = "/org/freedesktop/NetworkManager/Settings"
DBUS_OBJECT_HOSTNAME = "/org/freedesktop/hostname1" DBUS_OBJECT_HOSTNAME = "/org/freedesktop/hostname1"
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager" DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1" DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections" DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection" DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"

41
supervisor/dbus/logind.py Normal file
View File

@ -0,0 +1,41 @@
"""Interface to Logind over D-Bus."""
import logging
from ..exceptions import DBusError, DBusInterfaceError
from ..utils.gdbus import DBus
from .const import DBUS_NAME_LOGIND, DBUS_OBJECT_LOGIND
from .interface import DBusInterface
from .utils import dbus_connected
_LOGGER: logging.Logger = logging.getLogger(__name__)
class Logind(DBusInterface):
"""Logind function handler."""
name = DBUS_NAME_LOGIND
async def connect(self):
"""Connect to D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_LOGIND, DBUS_OBJECT_LOGIND)
except DBusError:
_LOGGER.warning("Can't connect to systemd-logind")
except DBusInterfaceError:
_LOGGER.info("No systemd-logind support on the host.")
@dbus_connected
def reboot(self):
"""Reboot host computer.
Return a coroutine.
"""
return self.dbus.Manager.Reboot(False)
@dbus_connected
def power_off(self):
"""Power off host computer.
Return a coroutine.
"""
return self.dbus.Manager.PowerOff(False)

View File

@ -19,7 +19,9 @@ class SystemControl(CoreSysAttributes):
def _check_dbus(self, flag): def _check_dbus(self, flag):
"""Check if systemd is connect or raise error.""" """Check if systemd is connect or raise error."""
if flag == MANAGER and self.sys_dbus.systemd.is_connected: if flag == MANAGER and (
self.sys_dbus.systemd.is_connected or self.sys_dbus.logind.is_connected
):
return return
if flag == HOSTNAME and self.sys_dbus.hostname.is_connected: if flag == HOSTNAME and self.sys_dbus.hostname.is_connected:
return return
@ -31,21 +33,35 @@ class SystemControl(CoreSysAttributes):
"""Reboot host system.""" """Reboot host system."""
self._check_dbus(MANAGER) self._check_dbus(MANAGER)
_LOGGER.info("Initialize host reboot over systemd") use_logind = self.sys_dbus.logind.is_connected
_LOGGER.info(
"Initialize host reboot using %s", "logind" if use_logind else "systemd"
)
try: try:
await self.sys_core.shutdown() await self.sys_core.shutdown()
finally: finally:
await self.sys_dbus.systemd.reboot() if use_logind:
await self.sys_dbus.logind.reboot()
else:
await self.sys_dbus.systemd.reboot()
async def shutdown(self): async def shutdown(self):
"""Shutdown host system.""" """Shutdown host system."""
self._check_dbus(MANAGER) self._check_dbus(MANAGER)
_LOGGER.info("Initialize host power off over systemd") use_logind = self.sys_dbus.logind.is_connected
_LOGGER.info(
"Initialize host power off %s", "logind" if use_logind else "systemd"
)
try: try:
await self.sys_core.shutdown() await self.sys_core.shutdown()
finally: finally:
await self.sys_dbus.systemd.power_off() if use_logind:
await self.sys_dbus.logind.power_off()
else:
await self.sys_dbus.systemd.power_off()
async def set_hostname(self, hostname): async def set_hostname(self, hostname):
"""Set local a new Hostname.""" """Set local a new Hostname."""