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

View File

@ -18,6 +18,7 @@ DBUS_NAME_NM_CONNECTION_ACTIVE_CHANGED = (
"org.freedesktop.NetworkManager.Connection.Active.PropertiesChanged"
)
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
DBUS_NAME_LOGIND = "org.freedesktop.login1"
DBUS_OBJECT_BASE = "/"
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_NM = "/org/freedesktop/NetworkManager"
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
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):
"""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
if flag == HOSTNAME and self.sys_dbus.hostname.is_connected:
return
@ -31,21 +33,35 @@ class SystemControl(CoreSysAttributes):
"""Reboot host system."""
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:
await self.sys_core.shutdown()
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):
"""Shutdown host system."""
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:
await self.sys_core.shutdown()
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):
"""Set local a new Hostname."""