Debrand for AlmaLinux

This commit is contained in:
Andrew Lukoshko 2026-09-03 11:56:48 +00:00 committed by root
commit 287346993c
19 changed files with 2593 additions and 2 deletions

View File

@ -0,0 +1,53 @@
From 13549c3d3be415eafa0104199cea247706dfa452 Mon Sep 17 00:00:00 2001
From: Milan Kyselica <mil.kyselica@gmail.com>
Date: Thu, 9 Apr 2026 19:43:14 +0200
Subject: [PATCH] resolved: replace assert() with error return in DNSSEC verify
functions
dnssec_rsa_verify_raw() asserts that RSA_size(key) matches the RRSIG
signature size, and dnssec_ecdsa_verify_raw() asserts that
EC_KEY_check_key() succeeds. Both conditions depend on parsed DNS
record content. Replace with proper error returns.
The actual crypto verify calls (EVP_PKEY_verify / ECDSA_do_verify)
handle mismatches fine on their own, so the asserts were also redundant.
While at it, fix the misleading "EC_POINT_bn2point failed" log message
that actually refers to an EC_KEY_set_public_key() failure.
Fixes: https://github.com/systemd/systemd/issues/41569
(cherry picked from commit dd80e5a348bdb8185e040f66ede00fd4ffdee777)
Resolves: RHEL-208860
---
src/resolve/resolved-dns-dnssec.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index df25b7f619..7243c2119f 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -125,7 +125,8 @@ static int dnssec_rsa_verify_raw(
return -EIO;
e = m = NULL;
- assert((size_t) RSA_size(rpubkey) == signature_size);
+ if ((size_t) RSA_size(rpubkey) != signature_size)
+ return -EINVAL;
epubkey = EVP_PKEY_new();
if (!epubkey)
@@ -337,9 +338,11 @@ static int dnssec_ecdsa_verify_raw(
if (EC_KEY_set_public_key(eckey, p) <= 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
- "EC_POINT_bn2point failed: 0x%lx", ERR_get_error());
+ "EC_KEY_set_public_key failed: 0x%lx", ERR_get_error());
- assert(EC_KEY_check_key(eckey) == 1);
+ if (EC_KEY_check_key(eckey) != 1)
+ return log_debug_errno(SYNTHETIC_ERRNO(EIO),
+ "EC_KEY_check_key failed: 0x%lx", ERR_get_error());
r = BN_bin2bn(signature_r, signature_r_size, NULL);
if (!r)

View File

@ -0,0 +1,399 @@
From 3c1b490d6c754b8f91c3b7860a75ab59f8c3e6c8 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 24 Mar 2023 16:04:34 +0100
Subject: [PATCH] pid1: introduce new SERVICE_{DEAD|FAILED}_BEFORE_AUTO_RESTART
service substates
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When a service deactivates and is then automatically restarted via
Restart= we currently quickly transition through
SERVICE_DEAD/SERVICE_FAILED. Which is weird given it's not the
normal ("permanent") dead/failed state, but a transitory one we
immediately leave from again. We do this so that software that looks for
failures/successes can take notice, even if we restart as a consequence
of the deactivation.
Let's clean this up a bit: let's introduce two new states:
SERVICE_DEAD_BEFORE_AUTO_RESTART and SERVICE_FAILED_BEFORE_AUTO_RESTART
that are used for the transitory states. Both the SERVICE_DEAD and
SERVICE_DEAD_BEFORE_AUTO_RESTART will map to the high-level
UNIT_INACTIVE state though. (and similar for the respective failed
states). This means the high-level state machine won't change by this,
only the low-level one.
This clearly seperates the substates, which makes the state engine
cleaner, and allows clients to follow precisely whether we are in a
transitory dead/failed state, or a permanent one, by looking at the
service substate. Moreover it allows us to remove the 'n_keep_fd_store'
which so far we used to ensure the fdstore was not released during this
transitory dead/failed state but only during the permanent one. Since we
can now distinguish these states properly we can just use that.
This has been bugging me for a while. Let's clean this up.
Note that the unit restart logic is already nicely covered in the
testsiute, hence this adds no new tests for that.
And yes, this could be considered a compat break, but sofar we took the
liberty to make changes to the low-level state machine (i.e. SERVICE_xyz
states, sometimes called "substates") without considering this a bad
breakage the high-level state machine (i.e. UNIT_xyz states) should
be considered API that cannot be changed.
(cherry picked from commit a1d315730ffddf283d4bb9d73878fbcd97a4d244)
Related: RHEL-137251
---
src/basic/unit-def.c | 44 ++++++++--------
src/basic/unit-def.h | 2 +
src/core/service.c | 116 +++++++++++++++++++++++++++++--------------
src/core/service.h | 1 -
src/core/socket.c | 4 +-
5 files changed, 105 insertions(+), 62 deletions(-)
diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c
index bdb1860246..a0fab46a19 100644
--- a/src/basic/unit-def.c
+++ b/src/basic/unit-def.c
@@ -180,27 +180,29 @@ static const char* const scope_state_table[_SCOPE_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState);
static const char* const service_state_table[_SERVICE_STATE_MAX] = {
- [SERVICE_DEAD] = "dead",
- [SERVICE_CONDITION] = "condition",
- [SERVICE_START_PRE] = "start-pre",
- [SERVICE_START] = "start",
- [SERVICE_START_POST] = "start-post",
- [SERVICE_RUNNING] = "running",
- [SERVICE_EXITED] = "exited",
- [SERVICE_RELOAD] = "reload",
- [SERVICE_RELOAD_SIGNAL] = "reload-signal",
- [SERVICE_RELOAD_NOTIFY] = "reload-notify",
- [SERVICE_STOP] = "stop",
- [SERVICE_STOP_WATCHDOG] = "stop-watchdog",
- [SERVICE_STOP_SIGTERM] = "stop-sigterm",
- [SERVICE_STOP_SIGKILL] = "stop-sigkill",
- [SERVICE_STOP_POST] = "stop-post",
- [SERVICE_FINAL_WATCHDOG] = "final-watchdog",
- [SERVICE_FINAL_SIGTERM] = "final-sigterm",
- [SERVICE_FINAL_SIGKILL] = "final-sigkill",
- [SERVICE_FAILED] = "failed",
- [SERVICE_AUTO_RESTART] = "auto-restart",
- [SERVICE_CLEANING] = "cleaning",
+ [SERVICE_DEAD] = "dead",
+ [SERVICE_CONDITION] = "condition",
+ [SERVICE_START_PRE] = "start-pre",
+ [SERVICE_START] = "start",
+ [SERVICE_START_POST] = "start-post",
+ [SERVICE_RUNNING] = "running",
+ [SERVICE_EXITED] = "exited",
+ [SERVICE_RELOAD] = "reload",
+ [SERVICE_RELOAD_SIGNAL] = "reload-signal",
+ [SERVICE_RELOAD_NOTIFY] = "reload-notify",
+ [SERVICE_STOP] = "stop",
+ [SERVICE_STOP_WATCHDOG] = "stop-watchdog",
+ [SERVICE_STOP_SIGTERM] = "stop-sigterm",
+ [SERVICE_STOP_SIGKILL] = "stop-sigkill",
+ [SERVICE_STOP_POST] = "stop-post",
+ [SERVICE_FINAL_WATCHDOG] = "final-watchdog",
+ [SERVICE_FINAL_SIGTERM] = "final-sigterm",
+ [SERVICE_FINAL_SIGKILL] = "final-sigkill",
+ [SERVICE_FAILED] = "failed",
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = "dead-before-auto-restart",
+ [SERVICE_FAILED_BEFORE_AUTO_RESTART] = "failed-before-auto-restart",
+ [SERVICE_AUTO_RESTART] = "auto-restart",
+ [SERVICE_CLEANING] = "cleaning",
};
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h
index bae132ea09..2fab42e9c7 100644
--- a/src/basic/unit-def.h
+++ b/src/basic/unit-def.h
@@ -144,6 +144,8 @@ typedef enum ServiceState {
SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */
SERVICE_FINAL_SIGKILL,
SERVICE_FAILED,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART,
+ SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_AUTO_RESTART,
SERVICE_CLEANING,
_SERVICE_STATE_MAX,
diff --git a/src/core/service.c b/src/core/service.c
index e152fb6227..e6c0c71595 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -65,6 +65,8 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
@@ -91,6 +93,8 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_DEAD_BEFORE_AUTO_RESTART] = UNIT_INACTIVE,
+ [SERVICE_FAILED_BEFORE_AUTO_RESTART] = UNIT_FAILED,
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
@@ -335,9 +339,6 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
static void service_release_fd_store(Service *s) {
assert(s);
- if (s->n_keep_fd_store > 0)
- return;
-
log_unit_debug(UNIT(s), "Releasing all stored fds");
while (s->fd_store)
service_fd_store_unlink(s->fd_store);
@@ -350,6 +351,10 @@ static void service_release_resources(Unit *u) {
assert(s);
+ /* Don't release resources if this is a transitionary failed/dead state */
+ if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART))
+ return;
+
if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
return;
@@ -1124,7 +1129,9 @@ static void service_set_state(Service *s, ServiceState state) {
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
}
- if (IN_SET(state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) {
+ if (IN_SET(state,
+ SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART)) {
unit_unwatch_all_pids(UNIT(s));
unit_dequeue_rewatch_pids(UNIT(s));
}
@@ -1237,7 +1244,10 @@ static int service_coldplug(Unit *u) {
return r;
}
- if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART, SERVICE_CLEANING)) {
+ if (!IN_SET(s->deserialized_state,
+ SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART,
+ SERVICE_CLEANING)) {
(void) unit_enqueue_rewatch_pids(u);
(void) unit_setup_dynamic_creds(u);
(void) unit_setup_exec_runtime(u);
@@ -1815,14 +1825,14 @@ static bool service_will_restart(Unit *u) {
if (s->will_auto_restart)
return true;
- if (s->state == SERVICE_AUTO_RESTART)
+ if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
return true;
return unit_will_restart_default(u);
}
static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) {
- ServiceState end_state;
+ ServiceState end_state, restart_state;
int r;
assert(s);
@@ -1838,12 +1848,15 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
if (s->result == SERVICE_SUCCESS) {
unit_log_success(UNIT(s));
end_state = SERVICE_DEAD;
+ restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
} else if (s->result == SERVICE_SKIP_CONDITION) {
unit_log_skip(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_DEAD;
+ restart_state = SERVICE_DEAD_BEFORE_AUTO_RESTART;
} else {
unit_log_failure(UNIT(s), service_result_to_string(s->result));
end_state = SERVICE_FAILED;
+ restart_state = SERVICE_FAILED_BEFORE_AUTO_RESTART;
}
unit_warn_leftover_processes(UNIT(s), unit_log_leftover_process_stop);
@@ -1861,30 +1874,33 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
s->will_auto_restart = true;
}
- /* Make sure service_release_resources() doesn't destroy our FD store, while we are changing through
- * SERVICE_FAILED/SERVICE_DEAD before entering into SERVICE_AUTO_RESTART. */
- s->n_keep_fd_store ++;
-
- service_set_state(s, end_state);
-
if (s->will_auto_restart) {
s->will_auto_restart = false;
+ /* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED
+ * state (i.e. a state indicating deactivation), and then one that that maps to the
+ * high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that
+ * external software can watch the state changes and see all service failures, even if they
+ * are only transitionary and followed by an automatic restart. We have fine-grained
+ * low-level states for this though so that software can distinguish the permanent UNIT_INACTIVE
+ * state from this transitionary UNIT_INACTIVE state by looking at the low-level states. */
+ service_set_state(s, restart_state);
+
r = service_arm_timer(s, /* relative= */ true, s->restart_usec);
- if (r < 0) {
- s->n_keep_fd_store--;
+ if (r < 0)
goto fail;
- }
service_set_state(s, SERVICE_AUTO_RESTART);
- } else
+ } else {
+ service_set_state(s, end_state);
+
/* If we shan't restart, then flush out the restart counter. But don't do that immediately, so that the
* user can still introspect the counter. Do so on the next start. */
s->flush_n_restarts = true;
+ }
/* The new state is in effect, let's decrease the fd store ref counter again. Let's also re-add us to the GC
* queue, so that the fd store is possibly gc'ed again */
- s->n_keep_fd_store--;
unit_add_to_gc_queue(UNIT(s));
/* The next restart might not be a manual stop, hence reset the flag indicating manual stops */
@@ -2611,34 +2627,55 @@ static int service_stop(Unit *u) {
/* Don't create restart jobs from manual stops. */
s->forbid_restart = true;
- /* Already on it */
- if (IN_SET(s->state,
- SERVICE_STOP, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
+ switch (s->state) {
+
+ case SERVICE_STOP:
+ case SERVICE_STOP_SIGTERM:
+ case SERVICE_STOP_SIGKILL:
+ case SERVICE_STOP_POST:
+ case SERVICE_FINAL_WATCHDOG:
+ case SERVICE_FINAL_SIGTERM:
+ case SERVICE_FINAL_SIGKILL:
+ /* Already on it */
return 0;
- /* A restart will be scheduled or is in progress. */
- if (s->state == SERVICE_AUTO_RESTART) {
+ case SERVICE_AUTO_RESTART:
+ /* A restart will be scheduled or is in progress. */
service_set_state(s, SERVICE_DEAD);
return 0;
- }
- /* If there's already something running we go directly into kill mode. */
- if (IN_SET(s->state, SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_STOP_WATCHDOG)) {
+ case SERVICE_CONDITION:
+ case SERVICE_START_PRE:
+ case SERVICE_START:
+ case SERVICE_START_POST:
+ case SERVICE_RELOAD:
+ case SERVICE_RELOAD_SIGNAL:
+ case SERVICE_RELOAD_NOTIFY:
+ case SERVICE_STOP_WATCHDOG:
+ /* If there's already something running we go directly into kill mode. */
service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_SUCCESS);
return 0;
- }
- /* If we are currently cleaning, then abort it, brutally. */
- if (s->state == SERVICE_CLEANING) {
+ case SERVICE_CLEANING:
+ /* If we are currently cleaning, then abort it, brutally. */
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
return 0;
+
+ case SERVICE_RUNNING:
+ case SERVICE_EXITED:
+ service_enter_stop(s, SERVICE_SUCCESS);
+ return 1;
+
+ case SERVICE_DEAD_BEFORE_AUTO_RESTART:
+ case SERVICE_FAILED_BEFORE_AUTO_RESTART:
+ case SERVICE_DEAD:
+ case SERVICE_FAILED:
+ default:
+ /* Unknown state, or unit_stop() should already have handled these */
+ assert_not_reached();
}
- assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
- service_enter_stop(s, SERVICE_SUCCESS);
- return 1;
}
static int service_reload(Unit *u) {
@@ -3214,6 +3251,11 @@ static bool service_may_gc(Unit *u) {
control_pid_good(s) > 0)
return false;
+ /* Only allow collection of actually dead services, i.e. not those that are in the transitionary
+ * SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART states. */
+ if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED))
+ return false;
+
return true;
}
@@ -3375,11 +3417,9 @@ static void service_notify_cgroup_empty_event(Unit *u) {
switch (s->state) {
- /* Waiting for SIGCHLD is usually more interesting,
- * because it includes return codes/signals. Which is
- * why we ignore the cgroup events for most cases,
- * except when we don't know pid which to expect the
- * SIGCHLD for. */
+ /* Waiting for SIGCHLD is usually more interesting, because it includes return
+ * codes/signals. Which is why we ignore the cgroup events for most cases, except when we
+ * don't know pid which to expect the SIGCHLD for. */
case SERVICE_START:
if (IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD) &&
diff --git a/src/core/service.h b/src/core/service.h
index 194067f0e1..58780ebd41 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -203,7 +203,6 @@ struct Service {
ServiceFDStore *fd_store;
size_t n_fd_store;
unsigned n_fd_store_max;
- unsigned n_keep_fd_store;
char *usb_function_descriptors;
char *usb_function_strings;
diff --git a/src/core/socket.c b/src/core/socket.c
index 103b399ab8..ebb83cd5b0 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2471,7 +2471,7 @@ static int socket_start(Unit *u) {
/* If the service is already active we cannot start the
* socket */
- if (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART))
+ if (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), "Socket service %s already active, refusing.", UNIT(service)->id);
}
@@ -3274,7 +3274,7 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
return;
if (IN_SET(SERVICE(other)->state,
- SERVICE_DEAD, SERVICE_FAILED,
+ SERVICE_DEAD, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED, SERVICE_FAILED_BEFORE_AUTO_RESTART,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
SERVICE_AUTO_RESTART))
socket_enter_listening(s);

View File

@ -0,0 +1,423 @@
From 219f89250a9d41c357eb1224f624ae3cf08cf4e9 Mon Sep 17 00:00:00 2001
From: Richard Phibel <rphibel@googlemail.com>
Date: Thu, 6 Jul 2023 14:33:52 +0200
Subject: [PATCH] service: add new RestartMode option
When this option is set to direct, the service restarts without entering a failed
state. Dependent units are not notified of transitory failure.
This is useful for the following use case:
We have a target with Requires=my-service, After=my-service.
my-service.service is a oneshot service and has Restart=on-failure in
its definition.
my-service.service can get stuck for various reasons and time out, in
which case it is restarted. Currently, when it fails the first time, the
target fails, even though my-service is restarted.
The behavior we're looking for is that until my-service is not restarted
anymore, the target stays pending waiting for my-service.service to
start successfully or fail without being restarted anymore.
(cherry picked from commit e568fea9fcd2189d4366df254a8a4031dc433762)
Resolves: RHEL-137251
---
man/org.freedesktop.systemd1.xml | 6 +++++
man/systemd.service.xml | 22 +++++++++++++++++++
src/core/dbus-service.c | 6 +++++
src/core/load-fragment-gperf.gperf.in | 1 +
src/core/load-fragment.c | 2 ++
src/core/load-fragment.h | 1 +
src/core/service.c | 10 ++++++++-
src/core/service.h | 11 ++++++++++
src/shared/bus-unit-util.c | 1 +
src/test/test-tables.c | 1 +
.../fails-on-restart-restartdirect.service | 11 ++++++++++
.../fails-on-restart-restartdirect.target | 3 +++
.../fails-on-restart.service | 11 ++++++++++
.../fails-on-restart.target | 3 +++
.../succeeds-on-restart-restartdirect.service | 6 +++++
.../succeeds-on-restart-restartdirect.target | 3 +++
.../succeeds-on-restart.service | 6 +++++
.../testsuite-03.units/succeeds-on-restart.sh | 10 +++++++++
.../succeeds-on-restart.target | 3 +++
test/units/testsuite-03.sh | 13 +++++++++++
20 files changed, 129 insertions(+), 1 deletion(-)
create mode 100644 test/testsuite-03.units/fails-on-restart-restartdirect.service
create mode 100755 test/testsuite-03.units/fails-on-restart-restartdirect.target
create mode 100644 test/testsuite-03.units/fails-on-restart.service
create mode 100755 test/testsuite-03.units/fails-on-restart.target
create mode 100755 test/testsuite-03.units/succeeds-on-restart-restartdirect.service
create mode 100755 test/testsuite-03.units/succeeds-on-restart-restartdirect.target
create mode 100755 test/testsuite-03.units/succeeds-on-restart.service
create mode 100755 test/testsuite-03.units/succeeds-on-restart.sh
create mode 100755 test/testsuite-03.units/succeeds-on-restart.target
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index e7b9b0a127..8298b726b7 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2524,6 +2524,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s Restart = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly s RestartMode = '...';
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PIDFile = '...';
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s NotifyAccess = '...';
@@ -3128,6 +3130,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property Restart is not documented!-->
+ <!--property RestartMode is not documented!-->
+
<!--property PIDFile is not documented!-->
<!--property NotifyAccess is not documented!-->
@@ -3666,6 +3670,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="Restart"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="RestartMode"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="PIDFile"/>
<variablelist class="dbus-property" generated="True" extra-ref="NotifyAccess"/>
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index ae54332440..d2d6f589da 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -941,6 +941,28 @@
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>RestartMode=</varname></term>
+
+ <listitem>
+ <para>Takes a string value that specifies how a service should restart:
+ <itemizedlist>
+ <listitem><para>If set to <option>normal</option> (the default), the service restarts by
+ going through a failed/inactive state.</para></listitem>
+
+ <listitem><para>If set to <option>direct</option>, the service transitions to the activating
+ state directly during auto-restart, skipping failed/inactive state.
+ <varname>ExecStopPost=</varname> is invoked.
+ <varname>OnSuccess=</varname> and <varname>OnFailure=</varname> are skipped.</para></listitem>
+ </itemizedlist>
+ </para>
+
+ <para>This option is useful in cases where a dependency can fail temporarily
+ but we don't want these temporary failures to make the dependent units fail.
+ When this option is set to <option>direct</option>, dependent units are not notified of these temporary failures.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>SuccessExitStatus=</varname></term>
diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c
index 3d130db66a..f8bf455707 100644
--- a/src/core/dbus-service.c
+++ b/src/core/dbus-service.c
@@ -31,6 +31,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exit_type, service_exit_type, S
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess);
+static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart_mode, service_restart_mode, ServiceRestartMode);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
static BUS_DEFINE_PROPERTY_GET(property_get_timeout_abort_usec, "t", Service, service_timeout_abort_usec);
static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec);
@@ -193,6 +194,7 @@ const sd_bus_vtable bus_service_vtable[] = {
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ExitType", "s", property_get_exit_type, offsetof(Service, exit_type), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Restart", "s", property_get_restart, offsetof(Service, restart), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RestartMode", "s", property_get_restart_mode, offsetof(Service, restart_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PIDFile", "s", NULL, offsetof(Service, pid_file), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NotifyAccess", "s", property_get_notify_access, offsetof(Service, notify_access), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -372,6 +374,7 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_exit_type, ServiceExitType, service_exit_type_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string);
+static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart_mode, ServiceRestartMode, service_restart_mode_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(oom_policy, OOMPolicy, oom_policy_from_string);
static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, sd_bus_service_name_is_valid);
static BUS_DEFINE_SET_TRANSIENT_PARSE(timeout_failure_mode, ServiceTimeoutFailureMode, service_timeout_failure_mode_from_string);
@@ -511,6 +514,9 @@ static int bus_service_set_transient_property(
if (streq(name, "Restart"))
return bus_set_transient_service_restart(u, name, &s->restart, message, flags, error);
+ if (streq(name, "RestartMode"))
+ return bus_set_transient_service_restart_mode(u, name, &s->restart_mode, message, flags, error);
+
if (streq(name, "RestartPreventExitStatus"))
return bus_set_transient_exit_status(u, name, &s->restart_prevent_status, message, flags, error);
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 53089d5590..602cd2befd 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -407,6 +407,7 @@ Service.RebootArgument, config_parse_unit_string_printf,
Service.Type, config_parse_service_type, 0, offsetof(Service, type)
Service.ExitType, config_parse_service_exit_type, 0, offsetof(Service, exit_type)
Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart)
+Service.RestartMode, config_parse_service_restart_mode, 0, offsetof(Service, restart_mode)
Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only)
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 2699ccf6ef..17863d7139 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -140,6 +140,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode,
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_exit_type, service_exit_type, ServiceExitType, "Failed to parse service exit type");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart_mode, service_restart_mode, ServiceRestartMode, "Failed to parse service restart mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_socket_bind, socket_address_bind_ipv6_only_or_bool, SocketAddressBindIPv6Only, "Failed to parse bind IPv6 only value");
DEFINE_CONFIG_PARSE_ENUM(config_parse_oom_policy, oom_policy, OOMPolicy, "Failed to parse OOM policy");
@@ -6166,6 +6167,7 @@ void unit_dump_config_items(FILE *f) {
{ config_parse_service_type, "SERVICETYPE" },
{ config_parse_service_exit_type, "SERVICEEXITTYPE" },
{ config_parse_service_restart, "SERVICERESTART" },
+ { config_parse_service_restart_mode, "SERVICERESTARTMODE" },
{ config_parse_service_timeout_failure_mode, "TIMEOUTMODE" },
{ config_parse_kill_mode, "KILLMODE" },
{ config_parse_signal, "SIGNAL" },
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index c57a6b2277..bdedbc51e2 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -39,6 +39,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_service_timeout_failure_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_service_type);
CONFIG_PARSER_PROTOTYPE(config_parse_service_exit_type);
CONFIG_PARSER_PROTOTYPE(config_parse_service_restart);
+CONFIG_PARSER_PROTOTYPE(config_parse_service_restart_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_socket_bindtodevice);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_output);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_input);
diff --git a/src/core/service.c b/src/core/service.c
index e6c0c71595..96f419bb47 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1884,7 +1884,8 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
* are only transitionary and followed by an automatic restart. We have fine-grained
* low-level states for this though so that software can distinguish the permanent UNIT_INACTIVE
* state from this transitionary UNIT_INACTIVE state by looking at the low-level states. */
- service_set_state(s, restart_state);
+ if (s->restart_mode != SERVICE_RESTART_MODE_DIRECT)
+ service_set_state(s, restart_state);
r = service_arm_timer(s, /* relative= */ true, s->restart_usec);
if (r < 0)
@@ -4771,6 +4772,13 @@ static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
+static const char* const service_restart_mode_table[_SERVICE_RESTART_MODE_MAX] = {
+ [SERVICE_RESTART_MODE_NORMAL] = "normal",
+ [SERVICE_RESTART_MODE_DIRECT] = "direct",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_restart_mode, ServiceRestartMode);
+
static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
[SERVICE_SIMPLE] = "simple",
[SERVICE_FORKING] = "forking",
diff --git a/src/core/service.h b/src/core/service.h
index 58780ebd41..bfbb3261a8 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -90,6 +90,13 @@ typedef enum ServiceTimeoutFailureMode {
_SERVICE_TIMEOUT_FAILURE_MODE_INVALID = -EINVAL,
} ServiceTimeoutFailureMode;
+typedef enum ServiceRestartMode {
+ SERVICE_RESTART_MODE_NORMAL,
+ SERVICE_RESTART_MODE_DIRECT,
+ _SERVICE_RESTART_MODE_MAX,
+ _SERVICE_RESTART_MODE_INVALID = -EINVAL,
+} ServiceRestartMode;
+
struct ServiceFDStore {
Service *service;
@@ -107,6 +114,7 @@ struct Service {
ServiceType type;
ServiceExitType exit_type;
ServiceRestart restart;
+ ServiceRestartMode restart_mode;
ExitStatusSet restart_prevent_status;
ExitStatusSet restart_force_status;
ExitStatusSet success_status;
@@ -238,6 +246,9 @@ void service_close_socket_fd(Service *s);
const char* service_restart_to_string(ServiceRestart i) _const_;
ServiceRestart service_restart_from_string(const char *s) _pure_;
+const char* service_restart_mode_to_string(ServiceRestartMode i) _const_;
+ServiceRestartMode service_restart_mode_from_string(const char *s) _pure_;
+
const char* service_type_to_string(ServiceType i) _const_;
ServiceType service_type_from_string(const char *s) _pure_;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index a9844e1cc3..43abdce1b4 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -2156,6 +2156,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
"Type",
"ExitType",
"Restart",
+ "RestartMode",
"BusName",
"NotifyAccess",
"USBFunctionDescriptors",
diff --git a/src/test/test-tables.c b/src/test/test-tables.c
index d47d3d75cc..0be3c43cde 100644
--- a/src/test/test-tables.c
+++ b/src/test/test-tables.c
@@ -98,6 +98,7 @@ int main(int argc, char **argv) {
test_table(scope_state, SCOPE_STATE);
test_table(service_exec_command, SERVICE_EXEC_COMMAND);
test_table(service_restart, SERVICE_RESTART);
+ test_table(service_restart_mode, SERVICE_RESTART_MODE);
test_table(service_result, SERVICE_RESULT);
test_table(service_state, SERVICE_STATE);
test_table(service_type, SERVICE_TYPE);
diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.service b/test/testsuite-03.units/fails-on-restart-restartdirect.service
new file mode 100644
index 0000000000..60ffd7a600
--- /dev/null
+++ b/test/testsuite-03.units/fails-on-restart-restartdirect.service
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=oneshot
+ExecStart=false
+Restart=on-failure
+RestartMode=direct
diff --git a/test/testsuite-03.units/fails-on-restart-restartdirect.target b/test/testsuite-03.units/fails-on-restart-restartdirect.target
new file mode 100755
index 0000000000..58e2561039
--- /dev/null
+++ b/test/testsuite-03.units/fails-on-restart-restartdirect.target
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=fails-on-restart-restartdirect.service
diff --git a/test/testsuite-03.units/fails-on-restart.service b/test/testsuite-03.units/fails-on-restart.service
new file mode 100644
index 0000000000..fb7e7aeb4c
--- /dev/null
+++ b/test/testsuite-03.units/fails-on-restart.service
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Fail on restart
+StartLimitIntervalSec=1m
+StartLimitBurst=3
+
+[Service]
+Type=oneshot
+ExecStart=false
+Restart=on-failure
+RestartMode=normal
diff --git a/test/testsuite-03.units/fails-on-restart.target b/test/testsuite-03.units/fails-on-restart.target
new file mode 100755
index 0000000000..865fb2af44
--- /dev/null
+++ b/test/testsuite-03.units/fails-on-restart.target
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=fails-on-restart.service
diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.service b/test/testsuite-03.units/succeeds-on-restart-restartdirect.service
new file mode 100755
index 0000000000..b05f2f8dcf
--- /dev/null
+++ b/test/testsuite-03.units/succeeds-on-restart-restartdirect.service
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
+Restart=on-failure
+RestartMode=direct
diff --git a/test/testsuite-03.units/succeeds-on-restart-restartdirect.target b/test/testsuite-03.units/succeeds-on-restart-restartdirect.target
new file mode 100755
index 0000000000..2cf3c60d2a
--- /dev/null
+++ b/test/testsuite-03.units/succeeds-on-restart-restartdirect.target
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=succeeds-on-restart-restartdirect.service
diff --git a/test/testsuite-03.units/succeeds-on-restart.service b/test/testsuite-03.units/succeeds-on-restart.service
new file mode 100755
index 0000000000..d7b3c7a210
--- /dev/null
+++ b/test/testsuite-03.units/succeeds-on-restart.service
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Service]
+Type=oneshot
+ExecStart=/usr/lib/systemd/tests/testdata/testsuite-03.units/succeeds-on-restart.sh
+Restart=on-failure
+RestartMode=normal
diff --git a/test/testsuite-03.units/succeeds-on-restart.sh b/test/testsuite-03.units/succeeds-on-restart.sh
new file mode 100755
index 0000000000..1428b186e5
--- /dev/null
+++ b/test/testsuite-03.units/succeeds-on-restart.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+if [[ ! -f "/succeeds-on-restart.ko" ]]
+then
+ touch "/succeeds-on-restart.ko"
+ exit 1
+else
+ rm "/succeeds-on-restart.ko"
+ exit 0
+fi
diff --git a/test/testsuite-03.units/succeeds-on-restart.target b/test/testsuite-03.units/succeeds-on-restart.target
new file mode 100755
index 0000000000..eb82f47aa3
--- /dev/null
+++ b/test/testsuite-03.units/succeeds-on-restart.target
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=succeeds-on-restart.service
diff --git a/test/units/testsuite-03.sh b/test/units/testsuite-03.sh
index 1d4bf3aaaa..ec51b20bf0 100755
--- a/test/units/testsuite-03.sh
+++ b/test/units/testsuite-03.sh
@@ -110,4 +110,17 @@ ELAPSED=$((END_SEC-START_SEC))
[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
[[ "$RESULT" -ne 0 ]] || exit 1
+# Test restart mode direct
+systemctl start succeeds-on-restart-restartdirect.target
+assert_rc 0 systemctl --quiet is-active succeeds-on-restart-restartdirect.target
+
+systemctl start fails-on-restart-restartdirect.target || :
+assert_rc 3 systemctl --quiet is-active fails-on-restart-restartdirect.target
+
+systemctl start succeeds-on-restart.target || :
+assert_rc 3 systemctl --quiet is-active succeeds-on-restart.target
+
+systemctl start fails-on-restart.target || :
+assert_rc 3 systemctl --quiet is-active fails-on-restart.target
+
touch /testok

View File

@ -0,0 +1,37 @@
From 01efd088ea54f56ec5e8c7077be2e05dc5b316e4 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Wed, 12 Feb 2025 17:38:47 +0100
Subject: [PATCH] core/service: drop unneeded unit_add_to_gc_queue()
Follow-up for a1d315730ffddf283d4bb9d73878fbcd97a4d244
and 6ac62d61db737b01ad3776a7688d8a4c57b3f7d9
With the aforementioned commits, unit_release_resources()
is dispatched in a dedicated queue, and Service.n_keep_fd_store
has been dropped, hence the comment is outdated. Moreover,
the unit is added to GC queue in unit_notify() already.
No other unit types do this in corresponding _enter_dead()
functions, nor does Service need it anymore.
(cherry picked from commit 818315ae61370998a4df7b92d89c338c9442c6df)
Related: RHEL-137251
---
src/core/service.c | 4 ----
1 file changed, 4 deletions(-)
diff --git a/src/core/service.c b/src/core/service.c
index 96f419bb47..0da2af7c35 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1900,10 +1900,6 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
s->flush_n_restarts = true;
}
- /* The new state is in effect, let's decrease the fd store ref counter again. Let's also re-add us to the GC
- * queue, so that the fd store is possibly gc'ed again */
- unit_add_to_gc_queue(UNIT(s));
-
/* The next restart might not be a manual stop, hence reset the flag indicating manual stops */
s->forbid_restart = false;

View File

@ -0,0 +1,119 @@
From 3907ede0d5bcb7e514b4875b8c01d67ea761a412 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Mon, 22 May 2023 08:30:30 +0800
Subject: [PATCH] core: get rid of unused Service.will_auto_restart logic
The announced new behavior for OnFailure= never worked properly,
and we've fixed the document instead in #27675.
Therefore, let's get rid of the unused logic completely. More at #27594.
The to-be-added RestartMode= option should cover the use case hopefully.
Closes #27594
(cherry picked from commit 49b34f75e7c801210624e0c7dd00be990873628a)
Resolves: RHEL-118224
---
src/core/service.c | 16 ++++------------
src/core/service.h | 2 --
src/core/unit.c | 7 ++-----
src/core/unit.h | 1 -
4 files changed, 6 insertions(+), 20 deletions(-)
diff --git a/src/core/service.c b/src/core/service.c
index 0da2af7c35..55e8bf5182 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1160,8 +1160,7 @@ static void service_set_state(Service *s, ServiceState state) {
log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
unit_notify(UNIT(s), table[old_state], table[state],
- (s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE) |
- (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0));
+ s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE);
}
static usec_t service_coldplug_timeout(Service *s) {
@@ -1823,8 +1822,6 @@ static bool service_will_restart(Unit *u) {
assert(s);
- if (s->will_auto_restart)
- return true;
if (IN_SET(s->state, SERVICE_DEAD_BEFORE_AUTO_RESTART, SERVICE_FAILED_BEFORE_AUTO_RESTART, SERVICE_AUTO_RESTART))
return true;
@@ -1864,19 +1861,14 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
log_unit_debug(UNIT(s), "Service restart not allowed.");
else {
const char *reason;
- bool shall_restart;
- shall_restart = service_shall_restart(s, &reason);
+ allow_restart = service_shall_restart(s, &reason);
log_unit_debug(UNIT(s), "Service will %srestart (%s)",
- shall_restart ? "" : "not ",
+ allow_restart ? "" : "not ",
reason);
- if (shall_restart)
- s->will_auto_restart = true;
}
- if (s->will_auto_restart) {
- s->will_auto_restart = false;
-
+ if (allow_restart) {
/* We make two state changes here: one that maps to the high-level UNIT_INACTIVE/UNIT_FAILED
* state (i.e. a state indicating deactivation), and then one that that maps to the
* high-level UNIT_STARTING state (i.e. a state indicating activation). We do this so that
diff --git a/src/core/service.h b/src/core/service.h
index bfbb3261a8..0ca04caf24 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -187,8 +187,6 @@ struct Service {
bool main_pid_alien:1;
bool bus_name_good:1;
bool forbid_restart:1;
- /* Keep restart intention between UNIT_FAILED and UNIT_ACTIVATING */
- bool will_auto_restart:1;
bool start_timeout_defined:1;
bool exec_fd_hot:1;
diff --git a/src/core/unit.c b/src/core/unit.c
index 0b58d0498b..c076b5126f 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2703,9 +2703,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
if (ns != os && ns == UNIT_FAILED) {
log_unit_debug(u, "Unit entered failed state.");
-
- if (!(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
- unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
+ unit_start_on_failure(u, "OnFailure=", UNIT_ATOM_ON_FAILURE, u->on_failure_job_mode);
}
if (UNIT_IS_ACTIVE_OR_RELOADING(ns) && !UNIT_IS_ACTIVE_OR_RELOADING(os)) {
@@ -2722,8 +2720,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
unit_log_resources(u);
}
- if (ns == UNIT_INACTIVE && !IN_SET(os, UNIT_FAILED, UNIT_INACTIVE, UNIT_MAINTENANCE) &&
- !(flags & UNIT_NOTIFY_WILL_AUTO_RESTART))
+ if (ns == UNIT_INACTIVE && !IN_SET(os, UNIT_FAILED, UNIT_INACTIVE, UNIT_MAINTENANCE))
unit_start_on_failure(u, "OnSuccess=", UNIT_ATOM_ON_SUCCESS, u->on_success_job_mode);
}
diff --git a/src/core/unit.h b/src/core/unit.h
index acbf74477e..77cc429803 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -903,7 +903,6 @@ void unit_notify_cgroup_oom(Unit *u, bool managed_oom);
typedef enum UnitNotifyFlags {
UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0,
- UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1,
} UnitNotifyFlags;
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags);

View File

@ -0,0 +1,239 @@
From 762a8cff390322bb578b5844326a2b654e9d2c23 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Mon, 22 May 2023 08:35:53 +0800
Subject: [PATCH] core: drop UnitNotifyFlags
This essentially reverts 2ad2e41a72ec19159c0746a78e15ff880fe32a63.
No longer needed after dropping UNIT_NOTIFY_WILL_AUTO_RESTART.
(cherry picked from commit 96b09de500f9d658b2e49abf3be15e06f9bd1ca6)
Related: RHEL-118224
---
src/core/automount.c | 2 +-
src/core/device.c | 2 +-
src/core/mount.c | 3 +--
src/core/path.c | 2 +-
src/core/scope.c | 2 +-
src/core/service.c | 3 +--
src/core/slice.c | 2 +-
src/core/socket.c | 2 +-
src/core/swap.c | 2 +-
src/core/target.c | 2 +-
src/core/timer.c | 2 +-
src/core/unit.c | 10 +++++-----
src/core/unit.h | 6 +-----
13 files changed, 17 insertions(+), 23 deletions(-)
diff --git a/src/core/automount.c b/src/core/automount.c
index ae8399d1af..f559454a88 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -283,7 +283,7 @@ static void automount_set_state(Automount *a, AutomountState state) {
if (state != old_state)
log_unit_debug(UNIT(a), "Changed %s -> %s", automount_state_to_string(old_state), automount_state_to_string(state));
- unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(a), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int automount_coldplug(Unit *u) {
diff --git a/src/core/device.c b/src/core/device.c
index f007bdfd9b..31f014d94b 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -174,7 +174,7 @@ static void device_set_state(Device *d, DeviceState state) {
if (state != old_state)
log_unit_debug(UNIT(d), "Changed %s -> %s", device_state_to_string(old_state), device_state_to_string(state));
- unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(d), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static void device_found_changed(Device *d, DeviceFound previous, DeviceFound now) {
diff --git a/src/core/mount.c b/src/core/mount.c
index 5789a253cd..1a6aca0aa8 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -753,8 +753,7 @@ static void mount_set_state(Mount *m, MountState state) {
if (state != old_state)
log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state));
- unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state],
- m->reload_result == MOUNT_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE);
+ unit_notify(UNIT(m), state_translation_table[old_state], state_translation_table[state], m->reload_result == MOUNT_SUCCESS);
}
static int mount_coldplug(Unit *u) {
diff --git a/src/core/path.c b/src/core/path.c
index 6f850244f1..6b548d40bc 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -475,7 +475,7 @@ static void path_set_state(Path *p, PathState state) {
if (state != old_state)
log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
- unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
diff --git a/src/core/scope.c b/src/core/scope.c
index 4e1a954c6c..1be18c21d6 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -126,7 +126,7 @@ static void scope_set_state(Scope *s, ScopeState state) {
if (state != old_state)
log_debug("%s changed %s -> %s", UNIT(s)->id, scope_state_to_string(old_state), scope_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int scope_add_default_dependencies(Scope *s) {
diff --git a/src/core/service.c b/src/core/service.c
index 55e8bf5182..a3c6b1380f 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1159,8 +1159,7 @@ static void service_set_state(Service *s, ServiceState state) {
if (old_state != state)
log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
- unit_notify(UNIT(s), table[old_state], table[state],
- s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE);
+ unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
}
static usec_t service_coldplug_timeout(Service *s) {
diff --git a/src/core/slice.c b/src/core/slice.c
index 8f913a8d45..cb6670ff45 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -43,7 +43,7 @@ static void slice_set_state(Slice *t, SliceState state) {
slice_state_to_string(old_state),
slice_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int slice_add_parent_slice(Slice *s) {
diff --git a/src/core/socket.c b/src/core/socket.c
index ebb83cd5b0..c916ff7ee5 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1838,7 +1838,7 @@ static void socket_set_state(Socket *s, SocketState state) {
if (state != old_state)
log_unit_debug(UNIT(s), "Changed %s -> %s", socket_state_to_string(old_state), socket_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int socket_coldplug(Unit *u) {
diff --git a/src/core/swap.c b/src/core/swap.c
index 458c935b0c..535657d3b5 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -586,7 +586,7 @@ static void swap_set_state(Swap *s, SwapState state) {
if (state != old_state)
log_unit_debug(UNIT(s), "Changed %s -> %s", swap_state_to_string(old_state), swap_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
/* If there other units for the same device node have a job
queued it might be worth checking again if it is runnable
diff --git a/src/core/target.c b/src/core/target.c
index 6225df5b0d..3519b4b653 100644
--- a/src/core/target.c
+++ b/src/core/target.c
@@ -31,7 +31,7 @@ static void target_set_state(Target *t, TargetState state) {
target_state_to_string(old_state),
target_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static int target_add_default_dependencies(Target *t) {
diff --git a/src/core/timer.c b/src/core/timer.c
index b96e88af90..310bc79903 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -298,7 +298,7 @@ static void timer_set_state(Timer *t, TimerState state) {
if (state != old_state)
log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
- unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
+ unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
}
static void timer_enter_waiting(Timer *t, bool time_change);
diff --git a/src/core/unit.c b/src/core/unit.c
index c076b5126f..53098c5b08 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -2017,7 +2017,7 @@ int unit_reload(Unit *u) {
if (!UNIT_VTABLE(u)->reload) {
/* Unit doesn't have a reload function, but we need to propagate the reload anyway */
- unit_notify(u, unit_active_state(u), unit_active_state(u), 0);
+ unit_notify(u, unit_active_state(u), unit_active_state(u), /* reload_success = */ true);
return 0;
}
@@ -2547,7 +2547,7 @@ static void unit_emit_audit_stop(Unit *u, UnitActiveState state) {
}
}
-static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) {
+static bool unit_process_job(Job *j, UnitActiveState ns, bool reload_success) {
bool unexpected = false;
JobResult result;
@@ -2590,7 +2590,7 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags)
if (j->state == JOB_RUNNING) {
if (ns == UNIT_ACTIVE)
- job_finish_and_invalidate(j, (flags & UNIT_NOTIFY_RELOAD_FAILURE) ? JOB_FAILED : JOB_DONE, true, false);
+ job_finish_and_invalidate(j, reload_success ? JOB_DONE : JOB_FAILED, true, false);
else if (!IN_SET(ns, UNIT_ACTIVATING, UNIT_RELOADING)) {
unexpected = true;
@@ -2621,7 +2621,7 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags)
return unexpected;
}
-void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags) {
+void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
const char *reason;
Manager *m;
@@ -2686,7 +2686,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
/* Let's propagate state changes to the job */
if (u->job)
- unexpected = unit_process_job(u->job, ns, flags);
+ unexpected = unit_process_job(u->job, ns, reload_success);
else
unexpected = true;
diff --git a/src/core/unit.h b/src/core/unit.h
index 77cc429803..08969d4cb9 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -901,11 +901,7 @@ int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t cont
void unit_notify_cgroup_oom(Unit *u, bool managed_oom);
-typedef enum UnitNotifyFlags {
- UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0,
-} UnitNotifyFlags;
-
-void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags);
+void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success);
int unit_watch_pid(Unit *u, pid_t pid, bool exclusive);
void unit_unwatch_pid(Unit *u, pid_t pid);

View File

@ -0,0 +1,128 @@
From b8dabd7117205eadc185c9b86fcaa10e1d0e5f84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Wed, 15 Nov 2023 17:17:12 +0100
Subject: [PATCH] core: split out the helper to serialize/deserialize
ratelimits
(cherry picked from commit 07a6647abbd74288a00b1b8f17ec9a9cce080ae4)
Related: RHEL-213655
---
src/core/manager-serialize.c | 23 ++---------------------
src/shared/serialize.c | 27 +++++++++++++++++++++++++++
src/shared/serialize.h | 3 +++
3 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
index a87d490219..74853181b7 100644
--- a/src/core/manager-serialize.c
+++ b/src/core/manager-serialize.c
@@ -164,13 +164,7 @@ int manager_serialize(
(void) serialize_item_format(f, "user-lookup", "%i %i", copy0, copy1);
}
- (void) serialize_item_format(f,
- "dump-ratelimit",
- USEC_FMT " " USEC_FMT " %u %u",
- m->dump_ratelimit.begin,
- m->dump_ratelimit.interval,
- m->dump_ratelimit.num,
- m->dump_ratelimit.burst);
+ (void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
bus_track_serialize(m->subscribed, f, "subscribed");
@@ -558,20 +552,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
if (deserialize_varlink_sockets)
(void) varlink_server_deserialize_one(m->varlink_server, val, fds);
} else if ((val = startswith(l, "dump-ratelimit="))) {
- usec_t begin, interval;
- unsigned num, burst;
-
- if (sscanf(val, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4)
- log_notice("Failed to parse dump ratelimit, ignoring: %s", val);
- else {
- /* If we changed the values across versions, flush the counter */
- if (interval != m->dump_ratelimit.interval || burst != m->dump_ratelimit.burst)
- m->dump_ratelimit.num = 0;
- else
- m->dump_ratelimit.num = num;
- m->dump_ratelimit.begin = begin;
- }
-
+ deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val);
} else {
ManagerTimestamp q;
diff --git a/src/shared/serialize.c b/src/shared/serialize.c
index cd48286355..5c73fc5f8e 100644
--- a/src/shared/serialize.c
+++ b/src/shared/serialize.c
@@ -130,6 +130,17 @@ int serialize_strv(FILE *f, const char *key, char **l) {
return ret;
}
+int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
+ assert(rl);
+
+ return serialize_item_format(f, key,
+ USEC_FMT " " USEC_FMT " %u %u",
+ rl->begin,
+ rl->interval,
+ rl->num,
+ rl->burst);
+}
+
int deserialize_usec(const char *value, usec_t *ret) {
int r;
@@ -194,6 +205,22 @@ int deserialize_environment(const char *value, char ***list) {
return 0;
}
+void deserialize_ratelimit(RateLimit *rl, const char *name, const char *value) {
+ usec_t begin, interval;
+ unsigned num, burst;
+
+ assert(rl);
+ assert(name);
+ assert(value);
+
+ if (sscanf(value, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4)
+ return log_notice("Failed to parse %s, ignoring: %s", name, value);
+
+ /* Preserve the counter only if the configuration didn't change. */
+ rl->num = (interval == rl->interval && burst == rl->burst) ? num : 0;
+ rl->begin = begin;
+}
+
int open_serialization_fd(const char *ident) {
int fd;
diff --git a/src/shared/serialize.h b/src/shared/serialize.h
index 6d4f1ef418..3fc1e60911 100644
--- a/src/shared/serialize.h
+++ b/src/shared/serialize.h
@@ -5,6 +5,7 @@
#include "fdset.h"
#include "macro.h"
+#include "ratelimit.h"
#include "string-util.h"
#include "time-util.h"
@@ -15,6 +16,7 @@ int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd);
int serialize_usec(FILE *f, const char *key, usec_t usec);
int serialize_dual_timestamp(FILE *f, const char *key, const dual_timestamp *t);
int serialize_strv(FILE *f, const char *key, char **l);
+int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl);
static inline int serialize_bool(FILE *f, const char *key, bool b) {
return serialize_item(f, key, yes_no(b));
@@ -23,5 +25,6 @@ static inline int serialize_bool(FILE *f, const char *key, bool b) {
int deserialize_usec(const char *value, usec_t *timestamp);
int deserialize_dual_timestamp(const char *value, dual_timestamp *t);
int deserialize_environment(const char *value, char ***environment);
+void deserialize_ratelimit(RateLimit *rl, const char *name, const char *value);
int open_serialization_fd(const char *ident);

View File

@ -0,0 +1,244 @@
From 9d258e84de4235fe807990782752f8b9d8e54c59 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Wed, 13 May 2026 16:20:55 +0200
Subject: [PATCH] core: make manager event loop rate limit configurable
Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com>
(cherry picked from commit e1c63fd4c0657a7fc7b749c64283ab4dbc6fb39e)
Resolves: RHEL-213655
---
man/org.freedesktop.systemd1.xml | 12 ++++++++++++
man/systemd-system.conf.xml | 15 +++++++++++++++
src/core/dbus-manager.c | 2 ++
src/core/main.c | 31 +++++++++++++++++++++++++++++++
src/core/manager-serialize.c | 4 ++++
src/core/manager.c | 3 +--
src/core/manager.h | 3 +++
src/core/system.conf.in | 2 ++
src/core/user.conf.in | 2 ++
9 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 8298b726b7..51e017bf10 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -524,6 +524,10 @@ node /org/freedesktop/systemd1 {
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly t DefaultTasksMax = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly t EventLoopRateLimitIntervalUSec = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly u EventLoopRateLimitBurst = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t TimerSlackNSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s DefaultOOMPolicy = '...';
@@ -776,6 +780,10 @@ node /org/freedesktop/systemd1 {
<!--property DefaultTasksMax is not documented!-->
+ <!--property EventLoopRateLimitIntervalUSec is not documented!-->
+
+ <!--property EventLoopRateLimitBurst is not documented!-->
+
<!--property TimerSlackNSec is not documented!-->
<!--property DefaultOOMPolicy is not documented!-->
@@ -1200,6 +1208,10 @@ node /org/freedesktop/systemd1 {
<variablelist class="dbus-property" generated="True" extra-ref="DefaultTasksMax"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="EventLoopRateLimitIntervalUSec"/>
+
+ <variablelist class="dbus-property" generated="True" extra-ref="EventLoopRateLimitBurst"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="TimerSlackNSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="DefaultOOMPolicy"/>
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
index 00de04d426..e563be8615 100644
--- a/man/systemd-system.conf.xml
+++ b/man/systemd-system.conf.xml
@@ -550,6 +550,21 @@
<para>If the value is <literal>/</literal>, only labels specified with <varname>SmackProcessLabel=</varname>
are assigned and the compile-time default is ignored.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>EventLoopRateLimitIntervalSec=</varname></term>
+ <term><varname>EventLoopRateLimitBurst=</varname></term>
+
+ <listitem><para>Configures the rate limiting applied to the manager's main event loop. If the event
+ loop iterates more than <varname>EventLoopRateLimitBurst=</varname> times within
+ <varname>EventLoopRateLimitIntervalSec=</varname>, event processing is briefly paused to prevent
+ excessive CPU usage. <varname>EventLoopRateLimitIntervalSec=</varname> defaults to 1s.
+ <varname>EventLoopRateLimitBurst=</varname> defaults to 50000. These settings can also be set on the
+ kernel command line via
+ <varname>systemd.event_loop_ratelimit_interval_sec=</varname> and
+ <varname>systemd.event_loop_ratelimit_burst=</varname>.</para></listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 16c9680d80..2537c4adf8 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -2911,6 +2911,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultLimitRTTIME", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultLimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(Manager, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTasksMax", "t", bus_property_get_tasks_max, offsetof(Manager, default_tasks_max), 0),
+ SD_BUS_PROPERTY("EventLoopRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, event_loop_ratelimit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("EventLoopRateLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, event_loop_ratelimit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMPolicy", "s", bus_property_get_oom_policy, offsetof(Manager, default_oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultOOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/main.c b/src/core/main.c
index 18f5781126..1e45ed6bd6 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -173,6 +173,8 @@ static size_t arg_random_seed_size;
static int arg_default_oom_score_adjust;
static bool arg_default_oom_score_adjust_set;
static char *arg_default_smack_process_label;
+static usec_t arg_event_loop_ratelimit_interval_sec;
+static unsigned arg_event_loop_ratelimit_burst;
/* A copy of the original environment block */
static char **saved_env = NULL;
@@ -483,6 +485,28 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
arg_random_seed = sz > 0 ? p : mfree(p);
arg_random_seed_size = sz;
+ } else if (proc_cmdline_key_streq(key, "systemd.event_loop_ratelimit_interval_sec")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = parse_sec(value, &arg_event_loop_ratelimit_interval_sec);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse systemd.event_loop_ratelimit_interval_sec= argument '%s', ignoring: %m", value);
+ return 0;
+ }
+
+ } else if (proc_cmdline_key_streq(key, "systemd.event_loop_ratelimit_burst")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = safe_atou(value, &arg_event_loop_ratelimit_burst);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to parse systemd.event_loop_ratelimit_burst= argument '%s', ignoring: %m", value);
+ return 0;
+ }
+
} else if (streq(key, "quiet") && !value) {
if (arg_show_status == _SHOW_STATUS_INVALID)
@@ -662,6 +686,8 @@ static int parse_config_file(void) {
{ "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, arg_runtime_scope, &arg_cad_burst_action },
{ "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_default_oom_policy },
{ "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL },
+ { "Manager", "EventLoopRateLimitIntervalSec", config_parse_sec, 0, &arg_event_loop_ratelimit_interval_sec },
+ { "Manager", "EventLoopRateLimitBurst", config_parse_unsigned, 0, &arg_event_loop_ratelimit_burst },
#if ENABLE_SMACK
{ "Manager", "DefaultSmackProcessLabel", config_parse_string, 0, &arg_default_smack_process_label },
#else
@@ -764,6 +790,8 @@ static void set_manager_settings(Manager *m) {
m->confirm_spawn = arg_confirm_spawn;
m->service_watchdogs = arg_service_watchdogs;
m->cad_burst_action = arg_cad_burst_action;
+ m->event_loop_ratelimit.interval = arg_event_loop_ratelimit_interval_sec;
+ m->event_loop_ratelimit.burst = arg_event_loop_ratelimit_burst;
manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog);
manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog);
@@ -2495,6 +2523,9 @@ static void reset_arguments(void) {
arg_default_oom_score_adjust_set = false;
arg_default_smack_process_label = mfree(arg_default_smack_process_label);
+ arg_event_loop_ratelimit_interval_sec = 1 * USEC_PER_SEC;
+ arg_event_loop_ratelimit_burst = 50000;
+
}
static void determine_default_oom_score_adjust(void) {
diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c
index 74853181b7..125783a48f 100644
--- a/src/core/manager-serialize.c
+++ b/src/core/manager-serialize.c
@@ -166,6 +166,8 @@ int manager_serialize(
(void) serialize_ratelimit(f, "dump-ratelimit", &m->dump_ratelimit);
+ (void) serialize_ratelimit(f, "event-loop-ratelimit", &m->event_loop_ratelimit);
+
bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
@@ -553,6 +555,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
(void) varlink_server_deserialize_one(m->varlink_server, val, fds);
} else if ((val = startswith(l, "dump-ratelimit="))) {
deserialize_ratelimit(&m->dump_ratelimit, "dump-ratelimit", val);
+ } else if ((val = startswith(l, "event-loop-ratelimit="))) {
+ deserialize_ratelimit(&m->event_loop_ratelimit, "event-loop-ratelimit", val);
} else {
ManagerTimestamp q;
diff --git a/src/core/manager.c b/src/core/manager.c
index 79408b18dc..ebfc4d3a9a 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -3024,7 +3024,6 @@ static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t use
}
int manager_loop(Manager *m) {
- RateLimit rl = { .interval = 1*USEC_PER_SEC, .burst = 50000 };
int r;
assert(m);
@@ -3041,7 +3040,7 @@ int manager_loop(Manager *m) {
(void) watchdog_ping();
- if (!ratelimit_below(&rl)) {
+ if (!ratelimit_below(&m->event_loop_ratelimit)) {
/* Yay, something is going seriously wrong, pause a little */
log_warning("Looping too fast. Throttling execution a little.");
sleep(1);
diff --git a/src/core/manager.h b/src/core/manager.h
index 4d5b2e0602..b7917732b5 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -465,6 +465,9 @@ struct Manager {
/* Dump*() are slow, so always rate limit them to 10 per 10 minutes */
RateLimit dump_ratelimit;
+
+ /* Rate limit for the manager event loop */
+ RateLimit event_loop_ratelimit;
};
static inline usec_t manager_default_timeout_abort_usec(Manager *m) {
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index 5d1f6d24f0..31f0f95b7a 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -75,3 +75,5 @@
#DefaultLimitRTTIME=
#DefaultOOMPolicy=stop
#DefaultSmackProcessLabel=
+#EventLoopRateLimitIntervalSec=1s
+#EventLoopRateLimitBurst=50000
diff --git a/src/core/user.conf.in b/src/core/user.conf.in
index b69974978e..0da7ae8be3 100644
--- a/src/core/user.conf.in
+++ b/src/core/user.conf.in
@@ -48,3 +48,5 @@
#DefaultLimitRTPRIO=
#DefaultLimitRTTIME=
#DefaultSmackProcessLabel=
+#EventLoopRateLimitIntervalSec=1s
+#EventLoopRateLimitBurst=50000

View File

@ -0,0 +1,53 @@
From c34d349f0e6467f06484aa93f4b422437b9ee2ca Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 19 Oct 2023 17:04:04 +0200
Subject: [PATCH] iovec-util: add some useful helpers for dealing with iovecs
that refer to dynamic memory
(cherry picked from commit 3a856171c2dc78f040c560142d3b275de1c1bb0a)
Related: RHEL-212610
---
src/basic/io-util.h | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/basic/io-util.h b/src/basic/io-util.h
index 3afb134266..4646666a99 100644
--- a/src/basic/io-util.h
+++ b/src/basic/io-util.h
@@ -8,6 +8,7 @@
#include <sys/types.h>
#include <sys/uio.h>
+#include "alloc-util.h"
#include "macro.h"
#include "time-util.h"
@@ -79,6 +80,27 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
#define IOVEC_INIT_STRING(string) IOVEC_INIT((char*) string, strlen(string))
#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
+#define TAKE_IOVEC(p) TAKE_GENERIC((p), struct iovec, IOVEC_NULL)
+
+static inline void iovec_done(struct iovec *iovec) {
+ /* A _cleanup_() helper that frees the iov_base in the iovec */
+ assert(iovec);
+
+ iovec->iov_base = mfree(iovec->iov_base);
+ iovec->iov_len = 0;
+}
+
+static inline void iovec_done_erase(struct iovec *iovec) {
+ assert(iovec);
+
+ iovec->iov_base = erase_and_free(iovec->iov_base);
+ iovec->iov_len = 0;
+}
+
+static inline bool iovec_is_set(const struct iovec *iov) {
+ return iov && iov->iov_len > 0 && iov->iov_base;
+}
+
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);

View File

@ -0,0 +1,78 @@
From 8159189ecc79ff507976502620034938da98400d Mon Sep 17 00:00:00 2001
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Date: Mon, 14 Jul 2025 07:56:49 -0400
Subject: [PATCH] repart: use iovec structure for --key-file
Use the iovec structure for --key-file, instead of a char pointer and a size.
(cherry picked from commit d4397999324c5093380fc1e6c8ce430a58e57145)
Related: RHEL-212610
---
src/partition/repart.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 5db931e7bc..08d43ce6c9 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -116,8 +116,7 @@ static bool arg_size_auto = false;
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
static PagerFlags arg_pager_flags = 0;
static bool arg_legend = true;
-static void *arg_key = NULL;
-static size_t arg_key_size = 0;
+static struct iovec arg_key = {};
static EVP_PKEY *arg_private_key = NULL;
static X509 *arg_certificate = NULL;
static char *arg_tpm2_device = NULL;
@@ -132,7 +131,7 @@ static bool arg_split = false;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
-STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_key, iovec_done_erase);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep);
STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
@@ -3011,8 +3010,8 @@ static int partition_encrypt(
CRYPT_ANY_SLOT,
volume_key,
volume_key_size,
- strempty(arg_key),
- arg_key_size);
+ strempty(arg_key.iov_base),
+ arg_key.iov_len);
if (r < 0)
return log_error_errno(r, "Failed to add LUKS2 key: %m");
}
@@ -5237,20 +5236,21 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_KEY_FILE: {
- _cleanup_(erase_and_freep) char *k = NULL;
- size_t n = 0;
+ struct iovec key = {};
r = read_full_file_full(
- AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
+ AT_FDCWD, optarg,
+ /* offset= */ UINT64_MAX,
+ /* size= */ SIZE_MAX,
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
- NULL,
- &k, &n);
+ /* bind_name= */ NULL,
+ (char **) &key.iov_base,
+ &key.iov_len);
if (r < 0)
return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
- erase_and_free(arg_key);
- arg_key = TAKE_PTR(k);
- arg_key_size = n;
+ iovec_done_erase(&arg_key);
+ arg_key = key;
break;
}

View File

@ -0,0 +1,159 @@
From 53bd7705fa61caef84ac8cbbda27d5b198de9841 Mon Sep 17 00:00:00 2001
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Date: Thu, 3 Jul 2025 08:08:53 -0400
Subject: [PATCH] repart: make --tpm2-pcrs also configurable in repart.d/*
Add repart.d TPM2PCRs= option with the same syntax as --tpm2-pcrs.
This allows a per-partition pcr binding, and not rely on a global config
applicable to all partitions.
The global --tpm2-pcrs overrides TPM2PCRs config. If none of them
is defined, rely on default.
(cherry picked from commit 49dcc89ddc15651ebca8da7a13e5c5b08ec247cb)
Resolves: RHEL-212610
---
man/repart.d.xml | 12 +++++++++++
src/partition/repart.c | 46 ++++++++++++++++++++++++++++++++++++------
2 files changed, 52 insertions(+), 6 deletions(-)
diff --git a/man/repart.d.xml b/man/repart.d.xml
index ebbb31cc20..6cc60e53a2 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -581,6 +581,18 @@
below. Defaults to <literal>%t</literal>. To disable split artifact generation for a partition, set
<varname>SplitName=</varname> to <literal>-</literal>.</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>TPM2PCRs=</varname></term>
+
+ <listitem><para>Configures the list of PCRs to use for LUKS2 volumes configured with
+ the <varname>Encrypt=tpm2</varname> setting in partition files.
+ This option take the same parameters as the similary named options to
+ <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ and have the same effect on partitions where TPM2 enrollment is requested.
+ This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 08d43ce6c9..d3fcf3b86b 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -198,6 +198,8 @@ struct Partition {
char **copy_files;
char **make_directories;
EncryptMode encrypt;
+ Tpm2PCRValue *tpm2_hash_pcr_values;
+ size_t tpm2_n_hash_pcr_values;
VerityMode verity;
char *verity_match_key;
@@ -328,6 +330,7 @@ static Partition* partition_free(Partition *p) {
free(p->format);
strv_free(p->copy_files);
strv_free(p->make_directories);
+ free(p->tpm2_hash_pcr_values);
free(p->verity_match_key);
free(p->roothash);
@@ -354,6 +357,7 @@ static void partition_foreignize(Partition *p) {
p->format = mfree(p->format);
p->copy_files = strv_free(p->copy_files);
p->make_directories = strv_free(p->make_directories);
+ p->tpm2_hash_pcr_values = mfree(p->tpm2_hash_pcr_values);
p->verity_match_key = mfree(p->verity_match_key);
p->new_uuid = SD_ID128_NULL;
@@ -1470,6 +1474,33 @@ static int config_parse_uuid(
return 0;
}
+static int config_parse_tpm2_pcrs(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Partition *partition = ASSERT_PTR(data);
+
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ /* Clear existing PCR values if empty */
+ partition->tpm2_hash_pcr_values = mfree(partition->tpm2_hash_pcr_values);
+ partition->tpm2_n_hash_pcr_values = 0;
+ return 0;
+ }
+
+ return tpm2_parse_pcr_argument_append(rvalue, &partition->tpm2_hash_pcr_values,
+ &partition->tpm2_n_hash_pcr_values);
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
@@ -1498,6 +1529,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "NoAuto", config_parse_tristate, 0, &p->no_auto },
{ "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
{ "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
+ { "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p },
{}
};
int r;
@@ -3026,6 +3058,8 @@ static int partition_encrypt(
size_t secret_size, blob_size, pubkey_size = 0, srk_buf_size = 0;
ssize_t base64_encoded_size;
int keyslot;
+ Tpm2PCRValue *pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_hash_pcr_values : p->tpm2_hash_pcr_values;
+ size_t n_pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_n_hash_pcr_values : p->tpm2_n_hash_pcr_values;
if (arg_tpm2_public_key_pcr_mask != 0) {
r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey, &pubkey_size);
@@ -3050,29 +3084,29 @@ static int partition_encrypt(
return log_error_errno(r, "Could not convert public key to TPM2B_PUBLIC: %m");
}
- r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
+ r = tpm2_pcr_read_missing_values(tpm2_context, pcr_values, n_pcr_values);
if (r < 0)
return log_error_errno(r, "Could not read pcr values: %m");
uint16_t hash_pcr_bank = 0;
uint32_t hash_pcr_mask = 0;
- if (arg_tpm2_n_hash_pcr_values > 0) {
+ if (n_pcr_values > 0) {
size_t hash_count;
- r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count);
+ r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count);
if (r < 0)
return log_error_errno(r, "Could not get hash count: %m");
if (hash_count > 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
- hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash;
- r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
+ hash_pcr_bank = pcr_values[0].hash;
+ r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, hash_pcr_bank, &hash_pcr_mask);
if (r < 0)
return log_error_errno(r, "Could not get hash mask: %m");
}
TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE);
- r = tpm2_calculate_sealing_policy(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, pubkey ? &public : NULL, /* use_pin= */ false, &policy);
+ r = tpm2_calculate_sealing_policy(pcr_values, n_pcr_values, pubkey ? &public : NULL, /* use_pin= */ false, &policy);
if (r < 0)
return log_error_errno(r, "Could not calculate sealing policy digest: %m");

View File

@ -0,0 +1,196 @@
From 068788ddbadbfa7fe3583b732a2957dc4bfc60c5 Mon Sep 17 00:00:00 2001
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Date: Mon, 14 Jul 2025 05:51:49 -0400
Subject: [PATCH] repart: make --key-file also configurable in repart.d/*
Add repart.d KeyFile= option with the same syntax as --key-file.
This allows a per-partition key file encryption, and not rely on a global key
applicable to all partitions.
The global --key-file overrides KeyFile config. If none of them is
defined, rely on default.
(cherry picked from commit eb44fa4d198d1da11c998f77bc88f95aaf67e186)
Resolves: RHEL-212610
---
man/repart.d.xml | 11 +++++++
man/systemd-repart.xml | 9 ++---
src/partition/repart.c | 74 +++++++++++++++++++++++++++++++++---------
3 files changed, 74 insertions(+), 20 deletions(-)
diff --git a/man/repart.d.xml b/man/repart.d.xml
index 6cc60e53a2..a4f49af39b 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -593,6 +593,17 @@
This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>KeyFile=</varname></term>
+
+ <listitem><para>Takes a file system path. This path must be absolute, otherwise the option is ignored.
+ Configures the encryption key to use when setting up LUKS2 volumes configured with the
+ <varname>Encrypt=key-file</varname> setting in partition files. Please refer to the documentation of
+ <varname>--key-file=</varname> for more details. This option will be overridden by the global
+ <varname>--key-file=</varname> option.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml
index 3585cbf107..f33e2d772a 100644
--- a/man/systemd-repart.xml
+++ b/man/systemd-repart.xml
@@ -306,10 +306,11 @@
<listitem><para>Takes a file system path. Configures the encryption key to use when setting up LUKS2
volumes configured with the <varname>Encrypt=key-file</varname> setting in partition files. Should
refer to a regular file containing the key, or an <constant>AF_UNIX</constant> stream socket in the
- file system. In the latter case a connection is made to it and the key read from it. If this switch
- is not specified the empty key (i.e. zero length key) is used. This behaviour is useful for setting
- up encrypted partitions during early first boot that receive their user-supplied password only in a
- later setup step.</para></listitem>
+ file system. In the latter case, a connection is made to it and the key read from it. If this switch
+ is not specified, and no <varname>KeyFile=</varname> is specified in the partition file, the empty
+ key (i.e. zero length key) is used. This behaviour is useful for setting up encrypted partitions during
+ early first boot that receive their user-supplied password only in a later setup step.</para>
+ </listitem>
</varlistentry>
<varlistentry>
diff --git a/src/partition/repart.c b/src/partition/repart.c
index d3fcf3b86b..c2643231c3 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -198,6 +198,7 @@ struct Partition {
char **copy_files;
char **make_directories;
EncryptMode encrypt;
+ struct iovec key;
Tpm2PCRValue *tpm2_hash_pcr_values;
size_t tpm2_n_hash_pcr_values;
VerityMode verity;
@@ -333,6 +334,8 @@ static Partition* partition_free(Partition *p) {
free(p->tpm2_hash_pcr_values);
free(p->verity_match_key);
+ iovec_done_erase(&p->key);
+
free(p->roothash);
free(p->split_name_format);
@@ -360,6 +363,8 @@ static void partition_foreignize(Partition *p) {
p->tpm2_hash_pcr_values = mfree(p->tpm2_hash_pcr_values);
p->verity_match_key = mfree(p->verity_match_key);
+ iovec_done_erase(&p->key);
+
p->new_uuid = SD_ID128_NULL;
p->new_uuid_is_set = false;
p->priority = 0;
@@ -1501,6 +1506,51 @@ static int config_parse_tpm2_pcrs(
&partition->tpm2_n_hash_pcr_values);
}
+static int parse_key_file(const char *filename, struct iovec *key) {
+ _cleanup_(erase_and_freep) char *k = NULL;
+ size_t n = 0;
+ int r;
+
+ r = read_full_file_full(
+ AT_FDCWD, filename,
+ /* offset= */ UINT64_MAX,
+ /* size= */ SIZE_MAX,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
+ /* bind_name= */ NULL,
+ &k, &n);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read key file '%s': %m", filename);
+
+ iovec_done_erase(key);
+ *key = IOVEC_MAKE(TAKE_PTR(k), n);
+
+ return 0;
+}
+
+static int config_parse_key_file(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Partition *partition = ASSERT_PTR(userdata);
+
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ iovec_done_erase(&partition->key);
+ return 0;
+ }
+
+ return parse_key_file(rvalue, &partition->key);
+}
+
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF, "Invalid verity mode");
static int partition_read_definition(Partition *p, const char *path, const char *const *conf_file_dirs) {
@@ -1530,6 +1580,7 @@ static int partition_read_definition(Partition *p, const char *path, const char
{ "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
{ "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
{ "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p },
+ { "Partition", "KeyFile", config_parse_key_file, 0, p },
{}
};
int r;
@@ -3037,13 +3088,16 @@ static int partition_encrypt(
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
+ /* Use partition-specific key if available, otherwise fall back to global key */
+ struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key;
+
r = sym_crypt_keyslot_add_by_volume_key(
cd,
CRYPT_ANY_SLOT,
volume_key,
volume_key_size,
- strempty(arg_key.iov_base),
- arg_key.iov_len);
+ strempty(iovec_key->iov_base),
+ iovec_key->iov_len);
if (r < 0)
return log_error_errno(r, "Failed to add LUKS2 key: %m");
}
@@ -5270,21 +5324,9 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_KEY_FILE: {
- struct iovec key = {};
-
- r = read_full_file_full(
- AT_FDCWD, optarg,
- /* offset= */ UINT64_MAX,
- /* size= */ SIZE_MAX,
- READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
- /* bind_name= */ NULL,
- (char **) &key.iov_base,
- &key.iov_len);
+ r = parse_key_file(optarg, &arg_key);
if (r < 0)
- return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
-
- iovec_done_erase(&arg_key);
- arg_key = key;
+ return r;
break;
}

View File

@ -0,0 +1,27 @@
From 77567a88f1d9767fed57ad520d9ff1956f3401c6 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Fri, 19 Sep 2025 20:24:06 +0900
Subject: [PATCH] man/repart: fix typo
Follow-up for 49dcc89ddc15651ebca8da7a13e5c5b08ec247cb.
(cherry picked from commit cbdbf68a7266c515db12c87542483b907fe7becf)
Related: RHEL-212610
---
man/repart.d.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/man/repart.d.xml b/man/repart.d.xml
index a4f49af39b..6ce2e0eaf4 100644
--- a/man/repart.d.xml
+++ b/man/repart.d.xml
@@ -587,7 +587,7 @@
<listitem><para>Configures the list of PCRs to use for LUKS2 volumes configured with
the <varname>Encrypt=tpm2</varname> setting in partition files.
- This option take the same parameters as the similary named options to
+ This option take the same parameters as the similarly named options to
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
and have the same effect on partitions where TPM2 enrollment is requested.
This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>

View File

@ -0,0 +1,33 @@
From 9b0799c1faaf2e7fde0625e0f915b8a4bc83728d Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Tue, 11 Aug 2026 19:18:23 +0200
Subject: [PATCH] test: include util.sh needed for assert_*() functions
This is needed by the test backported by
219f89250a9d41c357eb1224f624ae3cf08cf4e9.
In upstream the include was introduced as part of
5db456d06511ed537e5f6870e9d6b1afe50587c8, but we don't have the
necessary prerequisites in RHEL 9, so let's add just the include
manually.
rhel-only: ci
Related: RHEL-155457
---
test/units/testsuite-03.sh | 3 +++
1 file changed, 3 insertions(+)
diff --git a/test/units/testsuite-03.sh b/test/units/testsuite-03.sh
index ec51b20bf0..a08a0b1f0e 100755
--- a/test/units/testsuite-03.sh
+++ b/test/units/testsuite-03.sh
@@ -3,6 +3,9 @@
set -eux
set -o pipefail
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
# Simple test for that daemon-reexec works in container.
# See: https://github.com/systemd/systemd/pull/23883
systemctl daemon-reexec

View File

@ -0,0 +1,44 @@
From 598acc4d04be90d4c88efabec5063955992eaa36 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 11 Aug 2026 14:18:32 +0200
Subject: [PATCH] test: drop forgotten `set +e`
This was introduced all the way back in 2019 where this particular test
case was the last one in the file, so it worked as intended.
Unfortunately, since then we added new tests after this one, which means
this particular `set +e` masked fails in any of them (except for
assert_*() calls, which call exit on fail).
(cherry picked from commit 143c2845676fa5838ac4abf6b6fb9de80e12915e)
Related: RHEL-155457
---
test/units/testsuite-03.sh | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/test/units/testsuite-03.sh b/test/units/testsuite-03.sh
index a08a0b1f0e..7dee3cb707 100755
--- a/test/units/testsuite-03.sh
+++ b/test/units/testsuite-03.sh
@@ -105,13 +105,10 @@ ELAPSED=$((END_SEC-START_SEC))
# Test time-limited scopes
START_SEC=$(date -u '+%s')
-set +e
-systemd-run --scope --property=RuntimeMaxSec=3s sleep 10
-RESULT=$?
+(! systemd-run --scope --property=RuntimeMaxSec=3s sleep 10)
END_SEC=$(date -u '+%s')
ELAPSED=$((END_SEC-START_SEC))
[[ "$ELAPSED" -ge 3 ]] && [[ "$ELAPSED" -le 5 ]] || exit 1
-[[ "$RESULT" -ne 0 ]] || exit 1
# Test restart mode direct
systemctl start succeeds-on-restart-restartdirect.target
@@ -126,4 +123,6 @@ assert_rc 3 systemctl --quiet is-active succeeds-on-restart.target
systemctl start fails-on-restart.target || :
assert_rc 3 systemctl --quiet is-active fails-on-restart.target
+systemctl stop fails-on-restart.service
+
touch /testok

View File

@ -0,0 +1,243 @@
From b9e506b15b3469bb2f19b71697ef0a28eb409a5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Wed, 30 Jul 2025 11:52:26 +0200
Subject: [PATCH] journal: treble field hash table size
As discussed in https://github.com/systemd/systemd/issues/38399, "ordinary"
systems can have the field table with a large number of values, causing journal
rotation to occur early. For example, audit generates a log of fields:
$ journalctl --fields | rg -c '^_?AUDIT'
114
It seems that the "structured log" capabilities of the journal are being use
more than in the past. Looking at some journal files on my system, it seems
the field hash table field is quite high in many cases:
$ build/test-journal-dump /var/log/journal/*/* | rg 'table fill'
Data hash table fill: 15.1%
Field hash table fill: 69.1%
Data hash table fill: 4.9%
Field hash table fill: 32.4%
Data hash table fill: 10.2%
Field hash table fill: 34.2%
Data hash table fill: 9.9%
Field hash table fill: 37.2%
Data hash table fill: 26.8%
Field hash table fill: 21.9%
Data hash table fill: 35.6%
Field hash table fill: 22.8%
Data hash table fill: 25.5%
Field hash table fill: 54.1%
Data hash table fill: 3.4%
Field hash table fill: 43.8%
Data hash table fill: 75.0%
Field hash table fill: 70.3%
Data hash table fill: 75.0%
Field hash table fill: 63.1%
Data hash table fill: 75.0%
Field hash table fill: 74.2%
Data hash table fill: 35.6%
Field hash table fill: 43.2%
Data hash table fill: 35.5%
Field hash table fill: 75.4%
Data hash table fill: 75.0%
Field hash table fill: 59.8%
Data hash table fill: 75.0%
Field hash table fill: 56.5%
Data hash table fill: 16.9%
Field hash table fill: 76.3%
Data hash table fill: 18.1%
Field hash table fill: 76.9%
Data hash table fill: 75.0%
Field hash table fill: 42.0%
Data hash table fill: 75.0%
Field hash table fill: 22.8%
Data hash table fill: 75.0%
Field hash table fill: 22.8%
Data hash table fill: 75.0%
Field hash table fill: 22.8%
Data hash table fill: 75.0%
Field hash table fill: 22.8%
Data hash table fill: 75.0%
Field hash table fill: 32.1%
Data hash table fill: 75.0%
Field hash table fill: 21.9%
Data hash table fill: 75.0%
Field hash table fill: 21.9%
Data hash table fill: 75.0%
Field hash table fill: 21.9%
Data hash table fill: 75.0%
Field hash table fill: 22.8%
Data hash table fill: 75.0%
Field hash table fill: 22.8%
Data hash table fill: 75.0%
Field hash table fill: 21.9%
Data hash table fill: 75.0%
Field hash table fill: 22.5%
Data hash table fill: 9.6%
Field hash table fill: 53.8%
Data hash table fill: 75.0%
Field hash table fill: 22.2%
Data hash table fill: 75.0%
Field hash table fill: 22.2%
Data hash table fill: 75.0%
Field hash table fill: 22.2%
Data hash table fill: 35.6%
Field hash table fill: 75.1%
Data hash table fill: 33.6%
Field hash table fill: 50.2%
Data hash table fill: 75.0%
Field hash table fill: 26.7%
Data hash table fill: 75.0%
Field hash table fill: 25.8%
Data hash table fill: 75.0%
Field hash table fill: 29.1%
Data hash table fill: 75.0%
Field hash table fill: 25.8%
Data hash table fill: 75.0%
Field hash table fill: 31.8%
Data hash table fill: 75.0%
Field hash table fill: 18.9%
Data hash table fill: 75.0%
Field hash table fill: 22.2%
Data hash table fill: 75.0%
Field hash table fill: 20.1%
Data hash table fill: 75.0%
Field hash table fill: 29.1%
Data hash table fill: 75.0%
Field hash table fill: 30.9%
Data hash table fill: 75.0%
Field hash table fill: 28.5%
Data hash table fill: 75.0%
Field hash table fill: 28.5%
Data hash table fill: 75.0%
Field hash table fill: 25.8%
Data hash table fill: 75.0%
Field hash table fill: 25.2%
Data hash table fill: 75.0%
Field hash table fill: 39.3%
Data hash table fill: 50.2%
Field hash table fill: 75.1%
Data hash table fill: 75.0%
Field hash table fill: 61.9%
Data hash table fill: 75.0%
Field hash table fill: 56.5%
Data hash table fill: 75.0%
Field hash table fill: 58.6%
Data hash table fill: 48.9%
Field hash table fill: 79.6%
Data hash table fill: 75.0%
Field hash table fill: 71.5%
Data hash table fill: 75.0%
Field hash table fill: 60.1%
Data hash table fill: 31.4%
Field hash table fill: 75.7%
Data hash table fill: 27.0%
Field hash table fill: 69.4%
Data hash table fill: 28.9%
Field hash table fill: 76.6%
Data hash table fill: 60.2%
Field hash table fill: 79.9%
Data hash table fill: 8.8%
Field hash table fill: 78.7%
Data hash table fill: 5.8%
Field hash table fill: 61.3%
Data hash table fill: 75.0%
Field hash table fill: 64.0%
Data hash table fill: 61.4%
Field hash table fill: 63.4%
Data hash table fill: 29.7%
Field hash table fill: 61.9%
Data hash table fill: 18.9%
Field hash table fill: 30.9%
Data hash table fill: 1.4%
Field hash table fill: 22.2%
Data hash table fill: 0.4%
Field hash table fill: 13.5%
Data hash table fill: 2.6%
Field hash table fill: 37.5%
Data hash table fill: 1.3%
Field hash table fill: 23.4%
Data hash table fill: 0.6%
Field hash table fill: 15.3%
Data hash table fill: 18.7%
Field hash table fill: 33.9%
Data hash table fill: 7.4%
Field hash table fill: 37.5%
Data hash table fill: 20.2%
Field hash table fill: 44.1%
Data hash table fill: 1.3%
Field hash table fill: 33.0%
Data hash table fill: 75.0%
Field hash table fill: 19.2%
Data hash table fill: 42.2%
Field hash table fill: 23.4%
Data hash table fill: 1.6%
Field hash table fill: 87.1%
Data hash table fill: 0.1%
Field hash table fill: 98.8%
Data hash table fill: 0.2%
Field hash table fill: 128.8%
Data hash table fill: 15.4%
Field hash table fill: 31.2%
Data hash table fill: 7.4%
Field hash table fill: 22.5%
Data hash table fill: 10.5%
Field hash table fill: 38.7%
Data hash table fill: 2.8%
Field hash table fill: 18.0%
Data hash table fill: 1.5%
Field hash table fill: 15.9%
Data hash table fill: 0.0%
Field hash table fill: 7.5%
Data hash table fill: 0.1%
Field hash table fill: 12.0%
Data hash table fill: 0.2%
Field hash table fill: 10.8%
Data hash table fill: 0.2%
Field hash table fill: 15.6%
Data hash table fill: 0.1%
Field hash table fill: 11.7%
Data hash table fill: 0.1%
Field hash table fill: 12.0%
Data hash table fill: 0.0%
Field hash table fill: 6.6%
Data hash table fill: 1.4%
Field hash table fill: 18.0%
Data hash table fill: 0.7%
Field hash table fill: 16.8%
Data hash table fill: 1.1%
Field hash table fill: 18.0%
Data hash table fill: 0.2%
Field hash table fill: 10.8%
Data hash table fill: 0.1%
Field hash table fill: 10.8%
Data hash table fill: 0.4%
Field hash table fill: 11.1%
Since filling of the field hash table to 75% normally causes file rotation,
let's double the default to make rotation happen less often.
We'll use 11kB more for the hash table, which should be fine, considering
that journal files are usually at least 8 MB.
Closes https://github.com/systemd/systemd/issues/38399.
(cherry picked from commit e8962d77ac5a10926d0216246cfb5ad5dc25a533)
Resolves: RHEL-105520
---
src/libsystemd/sd-journal/journal-file.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c
index e8c9d4dc3d..3ac6abef1d 100644
--- a/src/libsystemd/sd-journal/journal-file.c
+++ b/src/libsystemd/sd-journal/journal-file.c
@@ -38,7 +38,7 @@
#include "xattr-util.h"
#define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem))
-#define DEFAULT_FIELD_HASH_TABLE_SIZE (333ULL*sizeof(HashItem))
+#define DEFAULT_FIELD_HASH_TABLE_SIZE (1023ULL*sizeof(HashItem))
#define DEFAULT_COMPRESS_THRESHOLD (512ULL)
#define MIN_COMPRESS_THRESHOLD (8ULL)

View File

@ -0,0 +1,37 @@
From 9c200c01eeb947d2300da421e8994ef9d286183e Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Wed, 19 Aug 2026 09:56:52 +0200
Subject: [PATCH] test: rename the start limit subtest
It uses the current upstream naming, unfortunately in RHEL 9 we still
use the previous one, so the test was not picked up by the
TEST-07-PID1's main test script.
Also, unmask & enable the systemd-resolved service, as the old testing
framework masks it by default.
rhel-only: ci
Related: RHEL-164539
Follow-up for 4060cdad388b0ae658f2024633b842a46c37962e.
---
...{TEST-07-PID1.start-limit.sh => testsuite-07.start-limit.sh} | 2 ++
1 file changed, 2 insertions(+)
rename test/units/{TEST-07-PID1.start-limit.sh => testsuite-07.start-limit.sh} (93%)
diff --git a/test/units/TEST-07-PID1.start-limit.sh b/test/units/testsuite-07.start-limit.sh
similarity index 93%
rename from test/units/TEST-07-PID1.start-limit.sh
rename to test/units/testsuite-07.start-limit.sh
index 93447452da..ebd8ac50ac 100755
--- a/test/units/TEST-07-PID1.start-limit.sh
+++ b/test/units/testsuite-07.start-limit.sh
@@ -15,6 +15,8 @@ at_exit() {
trap at_exit EXIT
+systemctl unmask systemd-resolved.service
+systemctl enable systemd-resolved.service
mkdir -p /run/systemd/system/systemd-resolved.service.d/
cat >/run/systemd/system/systemd-resolved.service.d/99-start-limit.conf <<EOF
[Unit]

View File

@ -0,0 +1,37 @@
From 01a9f0f0489d0744b24ffbe0badba04a02c21dc4 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Wed, 19 Aug 2026 12:24:43 +0200
Subject: [PATCH] ci: install GPG keys for Fedora 42
Fedora 42 is EOL and the official keyring doesn't have its keys anymore.
This, in combination with a pretty old version of mkosi means we have to
improvise a bit to get the old keys at the correct place, so we can
bootstrap the tools tree with the old Fedora release.
rhel-only: ci
Related: RHEL-155457
---
.github/workflows/mkosi.yml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml
index 0fa3141b5f..23375bdb13 100644
--- a/.github/workflows/mkosi.yml
+++ b/.github/workflows/mkosi.yml
@@ -66,6 +66,16 @@ jobs:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- uses: systemd/mkosi@857838464970f1092cc0107f4b1df714d0744990
+ - name: Install GPG keys for Fedora 42
+ run: |
+ sudo mkdir -p /usr/share/distribution-gpg-keys
+ sudo apt-get install -y curl cpio rpm2cpio
+ tmpdir=$(mktemp -d)
+ curl -sL https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/42/Server/x86_64/os/Packages/f/fedora-gpg-keys-42-1.noarch.rpm |
+ rpm2cpio | cpio -idmv -D "$tmpdir"
+ sudo mv "$tmpdir/etc/pki/rpm-gpg" /usr/share/distribution-gpg-keys/fedora
+ rm -rf "$tmpdir"
+
- name: Configure
run: |
tee mkosi.local.conf <<EOF

View File

@ -21,7 +21,7 @@
Name: systemd
Url: https://systemd.io
Version: 252
Release: 73%{?dist}.alma.1
Release: 78%{?dist}.alma.1
# For a breakdown of the licensing, see README
License: LGPLv2+ and MIT and GPLv2+
Summary: System and Service Manager
@ -1440,6 +1440,24 @@ Patch1354: 1354-ci-bump-super-linter-to-v8.7.0.patch
Patch1355: 1355-ci-explicitly-disable-multi-status-for-Super-Linter.patch
Patch1356: 1356-github-linter-disable-ENABLE_GITHUB_PULL_REQUEST_SUM.patch
Patch1357: 1357-test-install-iscsi-gen-initiatorname-from-iscsi-init.patch
Patch1358: 1358-resolved-replace-assert-with-error-return-in-DNSSEC-.patch
Patch1359: 1359-pid1-introduce-new-SERVICE_-DEAD-FAILED-_BEFORE_AUTO.patch
Patch1360: 1360-service-add-new-RestartMode-option.patch
Patch1361: 1361-core-service-drop-unneeded-unit_add_to_gc_queue.patch
Patch1362: 1362-core-get-rid-of-unused-Service.will_auto_restart-log.patch
Patch1363: 1363-core-drop-UnitNotifyFlags.patch
Patch1364: 1364-core-split-out-the-helper-to-serialize-deserialize-r.patch
Patch1365: 1365-core-make-manager-event-loop-rate-limit-configurable.patch
Patch1366: 1366-iovec-util-add-some-useful-helpers-for-dealing-with-.patch
Patch1367: 1367-repart-use-iovec-structure-for-key-file.patch
Patch1368: 1368-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch
Patch1369: 1369-repart-make-key-file-also-configurable-in-repart.d.patch
Patch1370: 1370-man-repart-fix-typo.patch
Patch1371: 1371-test-include-util.sh-needed-for-assert_-functions.patch
Patch1372: 1372-test-drop-forgotten-set-e.patch
Patch1373: 1373-journal-treble-field-hash-table-size.patch
Patch1374: 1374-test-rename-the-start-limit-subtest.patch
Patch1375: 1375-ci-install-GPG-keys-for-Fedora-42.patch
# Downstream-only patches (90009999)
@ -2317,9 +2335,33 @@ systemd-hwdb update &>/dev/null || :
%{_prefix}/lib/dracut/modules.d/70rhel-net-naming-sysattrs/*
%changelog
* Mon Aug 24 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 252-73.alma.1
* Thu Sep 03 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 252-78.alma.1
- Debrand for AlmaLinux
* Wed Aug 19 2026 systemd maintenance team <systemd-maint@redhat.com> - 252-78
- test: rename the start limit subtest (RHEL-164539)
- ci: install GPG keys for Fedora 42 (RHEL-155457)
* Tue Aug 18 2026 systemd maintenance team <systemd-maint@redhat.com> - 252-77
- test: include util.sh needed for assert_*() functions (RHEL-155457)
- test: drop forgotten `set +e` (RHEL-155457)
- journal: treble field hash table size (RHEL-105520)
* Wed Aug 05 2026 systemd maintenance team <systemd-maint@redhat.com> - 252-76
- resolved: replace assert() with error return in DNSSEC verify functions (RHEL-208860)
- pid1: introduce new SERVICE_{DEAD|FAILED}_BEFORE_AUTO_RESTART service substates (RHEL-137251)
- service: add new RestartMode option (RHEL-137251)
- core/service: drop unneeded unit_add_to_gc_queue() (RHEL-137251)
- core: get rid of unused Service.will_auto_restart logic (RHEL-118224)
- core: drop UnitNotifyFlags (RHEL-118224)
- core: split out the helper to serialize/deserialize ratelimits (RHEL-213655)
- core: make manager event loop rate limit configurable (RHEL-213655)
- iovec-util: add some useful helpers for dealing with iovecs that refer to dynamic memory (RHEL-212610)
- repart: use iovec structure for --key-file (RHEL-212610)
- repart: make --tpm2-pcrs also configurable in repart.d/* (RHEL-212610)
- repart: make --key-file also configurable in repart.d/* (RHEL-212610)
- man/repart: fix typo (RHEL-212610)
* Tue Jul 21 2026 systemd maintenance team <systemd-maint@redhat.com> - 252-73
- hwdb,rules: add 82-net-auto-link-local.{hwdb,rules} to build (RHEL-180938)
- test: reenable test for cg_get_keyed_attribute() (RHEL-180940)