From 00712f2c9ce5f323cbc43907db73a92469ed7384 Mon Sep 17 00:00:00 2001 From: Jan Macku Date: Wed, 5 Aug 2026 09:38:59 +0200 Subject: [PATCH] systemd-252-76 Resolves: RHEL-208860, RHEL-137251, RHEL-118224, RHEL-213655, RHEL-212610 --- ...-assert-with-error-return-in-DNSSEC-.patch | 53 +++ ...ew-SERVICE_-DEAD-FAILED-_BEFORE_AUTO.patch | 399 +++++++++++++++++ 1360-service-add-new-RestartMode-option.patch | 423 ++++++++++++++++++ ...e-drop-unneeded-unit_add_to_gc_queue.patch | 37 ++ ...unused-Service.will_auto_restart-log.patch | 119 +++++ 1363-core-drop-UnitNotifyFlags.patch | 239 ++++++++++ ...he-helper-to-serialize-deserialize-r.patch | 128 ++++++ ...r-event-loop-rate-limit-configurable.patch | 244 ++++++++++ ...ome-useful-helpers-for-dealing-with-.patch | 53 +++ ...art-use-iovec-structure-for-key-file.patch | 78 ++++ ...2-pcrs-also-configurable-in-repart.d.patch | 159 +++++++ ...y-file-also-configurable-in-repart.d.patch | 196 ++++++++ 1370-man-repart-fix-typo.patch | 27 ++ systemd.spec | 30 +- 14 files changed, 2184 insertions(+), 1 deletion(-) create mode 100644 1358-resolved-replace-assert-with-error-return-in-DNSSEC-.patch create mode 100644 1359-pid1-introduce-new-SERVICE_-DEAD-FAILED-_BEFORE_AUTO.patch create mode 100644 1360-service-add-new-RestartMode-option.patch create mode 100644 1361-core-service-drop-unneeded-unit_add_to_gc_queue.patch create mode 100644 1362-core-get-rid-of-unused-Service.will_auto_restart-log.patch create mode 100644 1363-core-drop-UnitNotifyFlags.patch create mode 100644 1364-core-split-out-the-helper-to-serialize-deserialize-r.patch create mode 100644 1365-core-make-manager-event-loop-rate-limit-configurable.patch create mode 100644 1366-iovec-util-add-some-useful-helpers-for-dealing-with-.patch create mode 100644 1367-repart-use-iovec-structure-for-key-file.patch create mode 100644 1368-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch create mode 100644 1369-repart-make-key-file-also-configurable-in-repart.d.patch create mode 100644 1370-man-repart-fix-typo.patch diff --git a/1358-resolved-replace-assert-with-error-return-in-DNSSEC-.patch b/1358-resolved-replace-assert-with-error-return-in-DNSSEC-.patch new file mode 100644 index 0000000..f11730c --- /dev/null +++ b/1358-resolved-replace-assert-with-error-return-in-DNSSEC-.patch @@ -0,0 +1,53 @@ +From 13549c3d3be415eafa0104199cea247706dfa452 Mon Sep 17 00:00:00 2001 +From: Milan Kyselica +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) diff --git a/1359-pid1-introduce-new-SERVICE_-DEAD-FAILED-_BEFORE_AUTO.patch b/1359-pid1-introduce-new-SERVICE_-DEAD-FAILED-_BEFORE_AUTO.patch new file mode 100644 index 0000000..24fdcb6 --- /dev/null +++ b/1359-pid1-introduce-new-SERVICE_-DEAD-FAILED-_BEFORE_AUTO.patch @@ -0,0 +1,399 @@ +From 3c1b490d6c754b8f91c3b7860a75ab59f8c3e6c8 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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); diff --git a/1360-service-add-new-RestartMode-option.patch b/1360-service-add-new-RestartMode-option.patch new file mode 100644 index 0000000..c875eef --- /dev/null +++ b/1360-service-add-new-RestartMode-option.patch @@ -0,0 +1,423 @@ +From 219f89250a9d41c357eb1224f624ae3cf08cf4e9 Mon Sep 17 00:00:00 2001 +From: Richard Phibel +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 { + + + ++ ++ + + + +@@ -3666,6 +3670,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + + ++ ++ + + + +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 @@ + + + ++ ++ RestartMode= ++ ++ ++ Takes a string value that specifies how a service should restart: ++ ++ If set to (the default), the service restarts by ++ going through a failed/inactive state. ++ ++ If set to , the service transitions to the activating ++ state directly during auto-restart, skipping failed/inactive state. ++ ExecStopPost= is invoked. ++ OnSuccess= and OnFailure= are skipped. ++ ++ ++ ++ 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 , dependent units are not notified of these temporary failures. ++ ++ ++ + + SuccessExitStatus= + +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 diff --git a/1361-core-service-drop-unneeded-unit_add_to_gc_queue.patch b/1361-core-service-drop-unneeded-unit_add_to_gc_queue.patch new file mode 100644 index 0000000..78b4501 --- /dev/null +++ b/1361-core-service-drop-unneeded-unit_add_to_gc_queue.patch @@ -0,0 +1,37 @@ +From 01efd088ea54f56ec5e8c7077be2e05dc5b316e4 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +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; + diff --git a/1362-core-get-rid-of-unused-Service.will_auto_restart-log.patch b/1362-core-get-rid-of-unused-Service.will_auto_restart-log.patch new file mode 100644 index 0000000..2686db0 --- /dev/null +++ b/1362-core-get-rid-of-unused-Service.will_auto_restart-log.patch @@ -0,0 +1,119 @@ +From 3907ede0d5bcb7e514b4875b8c01d67ea761a412 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +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); diff --git a/1363-core-drop-UnitNotifyFlags.patch b/1363-core-drop-UnitNotifyFlags.patch new file mode 100644 index 0000000..6486570 --- /dev/null +++ b/1363-core-drop-UnitNotifyFlags.patch @@ -0,0 +1,239 @@ +From 762a8cff390322bb578b5844326a2b654e9d2c23 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +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); diff --git a/1364-core-split-out-the-helper-to-serialize-deserialize-r.patch b/1364-core-split-out-the-helper-to-serialize-deserialize-r.patch new file mode 100644 index 0000000..29173cc --- /dev/null +++ b/1364-core-split-out-the-helper-to-serialize-deserialize-r.patch @@ -0,0 +1,128 @@ +From b8dabd7117205eadc185c9b86fcaa10e1d0e5f84 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +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); diff --git a/1365-core-make-manager-event-loop-rate-limit-configurable.patch b/1365-core-make-manager-event-loop-rate-limit-configurable.patch new file mode 100644 index 0000000..fd8b466 --- /dev/null +++ b/1365-core-make-manager-event-loop-rate-limit-configurable.patch @@ -0,0 +1,244 @@ +From 9d258e84de4235fe807990782752f8b9d8e54c59 Mon Sep 17 00:00:00 2001 +From: Michal Sekletar +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 +(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 { + + + ++ ++ ++ ++ + + + +@@ -1200,6 +1208,10 @@ node /org/freedesktop/systemd1 { + + + ++ ++ ++ ++ + + + +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 @@ + If the value is /, only labels specified with SmackProcessLabel= + are assigned and the compile-time default is ignored. + ++ ++ ++ EventLoopRateLimitIntervalSec= ++ EventLoopRateLimitBurst= ++ ++ Configures the rate limiting applied to the manager's main event loop. If the event ++ loop iterates more than EventLoopRateLimitBurst= times within ++ EventLoopRateLimitIntervalSec=, event processing is briefly paused to prevent ++ excessive CPU usage. EventLoopRateLimitIntervalSec= defaults to 1s. ++ EventLoopRateLimitBurst= defaults to 50000. These settings can also be set on the ++ kernel command line via ++ systemd.event_loop_ratelimit_interval_sec= and ++ systemd.event_loop_ratelimit_burst=. ++ ++ + + + +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 diff --git a/1366-iovec-util-add-some-useful-helpers-for-dealing-with-.patch b/1366-iovec-util-add-some-useful-helpers-for-dealing-with-.patch new file mode 100644 index 0000000..dbff1d6 --- /dev/null +++ b/1366-iovec-util-add-some-useful-helpers-for-dealing-with-.patch @@ -0,0 +1,53 @@ +From c34d349f0e6467f06484aa93f4b422437b9ee2ca Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +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 + #include + ++#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); + diff --git a/1367-repart-use-iovec-structure-for-key-file.patch b/1367-repart-use-iovec-structure-for-key-file.patch new file mode 100644 index 0000000..709f4af --- /dev/null +++ b/1367-repart-use-iovec-structure-for-key-file.patch @@ -0,0 +1,78 @@ +From 8159189ecc79ff507976502620034938da98400d Mon Sep 17 00:00:00 2001 +From: Emanuele Giuseppe Esposito +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; + } + diff --git a/1368-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch b/1368-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch new file mode 100644 index 0000000..4b8c77f --- /dev/null +++ b/1368-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch @@ -0,0 +1,159 @@ +From 53bd7705fa61caef84ac8cbbda27d5b198de9841 Mon Sep 17 00:00:00 2001 +From: Emanuele Giuseppe Esposito +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 %t. To disable split artifact generation for a partition, set + SplitName= to -. + ++ ++ ++ TPM2PCRs= ++ ++ Configures the list of PCRs to use for LUKS2 volumes configured with ++ the Encrypt=tpm2 setting in partition files. ++ This option take the same parameters as the similary named options to ++ systemd-cryptenroll1 ++ and have the same effect on partitions where TPM2 enrollment is requested. ++ This option will be overridden by the global --tpm2-pcrs= option. ++ ++ + + + +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"); + diff --git a/1369-repart-make-key-file-also-configurable-in-repart.d.patch b/1369-repart-make-key-file-also-configurable-in-repart.d.patch new file mode 100644 index 0000000..45f94fb --- /dev/null +++ b/1369-repart-make-key-file-also-configurable-in-repart.d.patch @@ -0,0 +1,196 @@ +From 068788ddbadbfa7fe3583b732a2957dc4bfc60c5 Mon Sep 17 00:00:00 2001 +From: Emanuele Giuseppe Esposito +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 --tpm2-pcrs= option. + + ++ ++ ++ KeyFile= ++ ++ 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 ++ Encrypt=key-file setting in partition files. Please refer to the documentation of ++ --key-file= for more details. This option will be overridden by the global ++ --key-file= option. ++ ++ + + + +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 @@ + Takes a file system path. Configures the encryption key to use when setting up LUKS2 + volumes configured with the Encrypt=key-file setting in partition files. Should + refer to a regular file containing the key, or an AF_UNIX 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. ++ 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 KeyFile= 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. ++ + + + +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; + } + diff --git a/1370-man-repart-fix-typo.patch b/1370-man-repart-fix-typo.patch new file mode 100644 index 0000000..6f870e7 --- /dev/null +++ b/1370-man-repart-fix-typo.patch @@ -0,0 +1,27 @@ +From 77567a88f1d9767fed57ad520d9ff1956f3401c6 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +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 @@ + + Configures the list of PCRs to use for LUKS2 volumes configured with + the Encrypt=tpm2 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 + systemd-cryptenroll1 + and have the same effect on partitions where TPM2 enrollment is requested. + This option will be overridden by the global --tpm2-pcrs= option. diff --git a/systemd.spec b/systemd.spec index 412f80b..d97404e 100644 --- a/systemd.spec +++ b/systemd.spec @@ -21,7 +21,7 @@ Name: systemd Url: https://systemd.io Version: 252 -Release: 73%{?dist} +Release: 76%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -1440,6 +1440,19 @@ 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 # Downstream-only patches (9000–9999) @@ -2317,6 +2330,21 @@ systemd-hwdb update &>/dev/null || : %{_prefix}/lib/dracut/modules.d/70rhel-net-naming-sysattrs/* %changelog +* Wed Aug 05 2026 systemd maintenance team - 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 - 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)