155 lines
6.0 KiB
Diff
155 lines
6.0 KiB
Diff
From 08a3d5d13142c79083e5cd6043eef8423c19f2c9 Mon Sep 17 00:00:00 2001
|
|
From: Marek Blaha <mblaha@redhat.com>
|
|
Date: Thu, 28 May 2026 14:21:48 +0000
|
|
Subject: [PATCH] automatic: Add systemd inhibitor lock
|
|
|
|
dnf-automatic now acquires a systemd inhibitor lock during the transaction
|
|
phase to prevent system shutdown or sleep from interrupting the process,
|
|
which could leave the system in an inconsistent state.
|
|
|
|
The implementation is opt-out via new `systemd_inhibit` option in the
|
|
[commands] section.
|
|
|
|
For: https://redhat.atlassian.net/browse/RHEL-111536
|
|
|
|
Original upstream commit: e18732b5
|
|
---
|
|
dnf.spec | 1 +
|
|
dnf/automatic/main.py | 53 +++++++++++++++++++++++++++++++++++-------
|
|
doc/automatic.rst | 5 ++++
|
|
etc/dnf/automatic.conf | 3 +++
|
|
4 files changed, 53 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/dnf.spec b/dnf.spec
|
|
index 770c70c1..ecf18454 100644
|
|
--- a/dnf.spec
|
|
+++ b/dnf.spec
|
|
@@ -195,6 +195,7 @@ Python 3 interface to DNF.
|
|
Summary: %{pkg_summary} - automated upgrades
|
|
BuildRequires: systemd
|
|
Requires: python3-%{name} = %{version}-%{release}
|
|
+Recommends: (python3-dbus if systemd)
|
|
%{?systemd_requires}
|
|
|
|
%description automatic
|
|
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
|
index 1a1e1cf0..c06c7d09 100644
|
|
--- a/dnf/automatic/main.py
|
|
+++ b/dnf/automatic/main.py
|
|
@@ -23,6 +23,7 @@ from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
import argparse
|
|
+import contextlib
|
|
import logging
|
|
import os
|
|
import random
|
|
@@ -46,6 +47,36 @@ import libdnf.conf
|
|
logger = logging.getLogger('dnf')
|
|
|
|
|
|
+@contextlib.contextmanager
|
|
+def systemd_inhibit():
|
|
+ """Acquire a systemd inhibitor lock to block system shutdown/sleep.
|
|
+ """
|
|
+ fd = None
|
|
+ try:
|
|
+ import dbus
|
|
+ except ImportError:
|
|
+ logger.debug(_("dbus module is not available, skipping systemd inhibitor lock"))
|
|
+ yield
|
|
+ return
|
|
+
|
|
+ try:
|
|
+ bus = dbus.SystemBus()
|
|
+ proxy = bus.get_object("org.freedesktop.login1",
|
|
+ "/org/freedesktop/login1")
|
|
+ manager = dbus.Interface(proxy, "org.freedesktop.login1.Manager")
|
|
+ fd = manager.Inhibit("idle:sleep:shutdown",
|
|
+ "dnf-automatic",
|
|
+ "Transaction running",
|
|
+ "block")
|
|
+ except dbus.DBusException as e:
|
|
+ logger.debug(_("Failed to acquire systemd inhibitor lock: %s"), e)
|
|
+ try:
|
|
+ yield
|
|
+ finally:
|
|
+ if fd is not None:
|
|
+ os.close(fd.take())
|
|
+
|
|
+
|
|
def build_emitters(conf):
|
|
emitters = dnf.util.MultiCallList([])
|
|
system_name = conf.emitters.system_name
|
|
@@ -190,6 +221,7 @@ class CommandsConfig(Config):
|
|
libdnf.conf.VectorString(['default', 'security'])))
|
|
self.add_option('random_sleep', libdnf.conf.OptionNumberInt32(300))
|
|
self.add_option('network_online_timeout', libdnf.conf.OptionNumberInt32(60))
|
|
+ self.add_option('systemd_inhibit', libdnf.conf.OptionBool(True))
|
|
self.add_option('reboot', libdnf.conf.OptionEnumString('never',
|
|
libdnf.conf.VectorString(['never', 'when-changed', 'when-needed'])))
|
|
self.add_option('reboot_command', libdnf.conf.OptionString(
|
|
@@ -363,17 +395,20 @@ def main(args):
|
|
emitters.commit()
|
|
return 0
|
|
|
|
- gpgsigcheck(base, trans.install_set)
|
|
- base.do_transaction()
|
|
+ inhibit = systemd_inhibit() if conf.commands.systemd_inhibit \
|
|
+ else contextlib.nullcontext()
|
|
+ with inhibit:
|
|
+ gpgsigcheck(base, trans.install_set)
|
|
+ base.do_transaction()
|
|
|
|
- # In case of no global error occurred within the transaction,
|
|
- # we need to check state of individual transaction items.
|
|
- for tsi in trans:
|
|
- if tsi.state == libdnf.transaction.TransactionItemState_ERROR:
|
|
- raise dnf.exceptions.Error(_('Transaction failed'))
|
|
+ # In case of no global error occurred within the transaction,
|
|
+ # we need to check state of individual transaction items.
|
|
+ for tsi in trans:
|
|
+ if tsi.state == libdnf.transaction.TransactionItemState_ERROR:
|
|
+ raise dnf.exceptions.Error(_('Transaction failed'))
|
|
|
|
- emitters.notify_applied()
|
|
- emitters.commit()
|
|
+ emitters.notify_applied()
|
|
+ emitters.commit()
|
|
|
|
if (conf.commands.reboot == 'when-changed' or
|
|
(conf.commands.reboot == 'when-needed' and base.reboot_needed())):
|
|
diff --git a/doc/automatic.rst b/doc/automatic.rst
|
|
index d342c957..cab1107a 100644
|
|
--- a/doc/automatic.rst
|
|
+++ b/doc/automatic.rst
|
|
@@ -90,6 +90,11 @@ Setting the mode of operation of the program.
|
|
|
|
What kind of upgrades to look at. ``default`` signals looking for all available updates, ``security`` only those with an issued security advisory.
|
|
|
|
+``systemd_inhibit``
|
|
+ boolean, default: True
|
|
+
|
|
+ Whether to block system shutdown while applying package updates. When enabled, dnf-automatic acquires a systemd inhibitor lock during the transaction phase to prevent scheduled reboots or system shutdown from interrupting package installation. The lock is automatically released when the transaction completes. If systemd-logind is unavailable (e.g., in containers), this setting has no effect. Set to ``False`` to disable if the inhibitor conflicts with your maintenance workflows.
|
|
+
|
|
``reboot``
|
|
either one of ``never``, ``when-changed``, ``when-needed``, default: ``never``
|
|
|
|
diff --git a/etc/dnf/automatic.conf b/etc/dnf/automatic.conf
|
|
index f60f75b9..dd27e798 100644
|
|
--- a/etc/dnf/automatic.conf
|
|
+++ b/etc/dnf/automatic.conf
|
|
@@ -9,6 +9,9 @@ random_sleep = 0
|
|
# connect to remote repositories.
|
|
network_online_timeout = 60
|
|
|
|
+# Whether to block system shutdown while applying package updates.
|
|
+systemd_inhibit = yes
|
|
+
|
|
# To just receive updates use dnf-automatic-notifyonly.timer
|
|
|
|
# Whether updates should be downloaded when they are available, by
|
|
--
|
|
2.54.0
|
|
|