From 0794c2d6b9d21007052c5842c0975d6d8425b278 Mon Sep 17 00:00:00 2001 From: zhangyao Date: Mon, 17 Jun 2024 14:15:37 +0800 Subject: [PATCH] Add the FileDescriptorStorePreserve= option to the service --- ...-implicit-sentinel-to-strv_env_merge.patch | 310 ++++++++++++ ...ronize-explicitly-instead-of-by-time.patch | 166 ++++++ ...e-variables-and-modernize-style-a-bi.patch | 453 +++++++++++++++++ ...-unit-if-it-is-in-cgroup_empty_queue.patch | 92 ++++ ...nits-jobs-that-are-in-the-D-Bus-queu.patch | 84 ++++ ...y-getter-method-for-NFileDescriptorS.patch | 72 +++ ...e-directory-removal-into-release_res.patch | 90 ++++ ...rriding-NOTIFYACCESS-through-sd-noti.patch | 294 +++++++++++ ...util-introduce-dir_fd_is_root_or_cwd.patch | 27 + ...t-fdset-add-new-fdset_consume-helper.patch | 52 ++ ...lper-to-convert-an-fdset-to-an-array.patch | 86 ++++ ...itattributes-when-using-install_subd.patch | 95 ++++ ...xec-switch-for-chaining-other-comman.patch | 150 ++++++ ...rt-for-sending-fds-with-notification.patch | 258 ++++++++++ ...bug-logging-when-stashing-ds-into-th.patch | 54 ++ ...-service-add-ability-to-pin-fd-store.patch | 476 ++++++++++++++++++ ...low-freeing-the-fdstore-via-cleaning.patch | 283 +++++++++++ ...service-close-fdstore-asynchronously.patch | 90 ++++ ...e-drop-redundant-unit_ref_unset-call.patch | 31 ++ ...resources-from-a-seperate-queue-not-.patch | 286 +++++++++++ ...ervice_close_socket_fd-service_relea.patch | 95 ++++ ...vice-rework-how-we-release-resources.patch | 137 +++++ ...ss-socket-fd-and-SocketPeer-ownershi.patch | 174 +++++++ backport-socket-various-modernizations.patch | 94 ++++ ...py_n-helper-for-copying-part-of-a-n-.patch | 125 +++++ ...v_copy-with-cleanup-attribute-and-ST.patch | 56 +++ ...or-NOTIFYACCESS-override-through-sd_.patch | 160 ++++++ ...-validate-that-fdstore-pinning-works.patch | 186 +++++++ ...port-unit-don-t-gc-unit-in-oom-queue.patch | 49 ++ systemd.spec | 34 +- 30 files changed, 4558 insertions(+), 1 deletion(-) create mode 100644 backport-Add-implicit-sentinel-to-strv_env_merge.patch create mode 100644 backport-TEST-80-synchronize-explicitly-instead-of-by-time.patch create mode 100644 backport-basic-strv-inline-variables-and-modernize-style-a-bi.patch create mode 100644 backport-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch create mode 100644 backport-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch create mode 100644 backport-core-fix-property-getter-method-for-NFileDescriptorS.patch create mode 100644 backport-core-move-runtime-directory-removal-into-release_res.patch create mode 100644 backport-core-support-overriding-NOTIFYACCESS-through-sd-noti.patch create mode 100644 backport-fd-util-introduce-dir_fd_is_root_or_cwd.patch create mode 100644 backport-fdset-add-new-fdset_consume-helper.patch create mode 100644 backport-fdset-add-new-helper-to-convert-an-fdset-to-an-array.patch create mode 100644 backport-meson-exclude-.gitattributes-when-using-install_subd.patch create mode 100644 backport-notify-add-new-exec-switch-for-chaining-other-comman.patch create mode 100644 backport-notify-add-support-for-sending-fds-with-notification.patch create mode 100644 backport-pid1-add-some-debug-logging-when-stashing-ds-into-th.patch create mode 100644 backport-service-add-ability-to-pin-fd-store.patch create mode 100644 backport-service-allow-freeing-the-fdstore-via-cleaning.patch create mode 100644 backport-service-close-fdstore-asynchronously.patch create mode 100644 backport-service-drop-redundant-unit_ref_unset-call.patch create mode 100644 backport-service-release-resources-from-a-seperate-queue-not-.patch create mode 100644 backport-service-rename-service_close_socket_fd-service_relea.patch create mode 100644 backport-service-rework-how-we-release-resources.patch create mode 100644 backport-socket-always-pass-socket-fd-and-SocketPeer-ownershi.patch create mode 100644 backport-socket-various-modernizations.patch create mode 100644 backport-strv-add-strv_copy_n-helper-for-copying-part-of-a-n-.patch create mode 100644 backport-strv-rewrite-strv_copy-with-cleanup-attribute-and-ST.patch create mode 100644 backport-test-add-tests-for-NOTIFYACCESS-override-through-sd_.patch create mode 100644 backport-test-validate-that-fdstore-pinning-works.patch create mode 100644 backport-unit-don-t-gc-unit-in-oom-queue.patch diff --git a/backport-Add-implicit-sentinel-to-strv_env_merge.patch b/backport-Add-implicit-sentinel-to-strv_env_merge.patch new file mode 100644 index 0000000..444fc63 --- /dev/null +++ b/backport-Add-implicit-sentinel-to-strv_env_merge.patch @@ -0,0 +1,310 @@ +From 4ab3d29ff03c0508f47846875d9310cbc5b8cd0f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 7 Aug 2021 10:16:19 +0200 +Subject: [PATCH] Add implicit sentinel to strv_env_merge() + +Just to make it a tiny bit nicer to use. + +--- + src/basic/env-util.c | 40 ++++++++++++++++++++++++++-------------- + src/basic/env-util.h | 3 ++- + src/core/dbus-execute.c | 4 ++-- + src/core/execute.c | 5 ++--- + src/core/locale-setup.c | 2 +- + src/core/manager.c | 6 +++--- + src/core/service.c | 2 +- + src/notify/notify.c | 2 +- + src/nspawn/nspawn.c | 20 ++++++++++---------- + src/run/run.c | 2 +- + src/test/test-env-util.c | 2 +- + 11 files changed, 50 insertions(+), 38 deletions(-) + +diff --git a/src/basic/env-util.c b/src/basic/env-util.c +index 81b1e3f10e..0c30ddc277 100644 +--- a/src/basic/env-util.c ++++ b/src/basic/env-util.c +@@ -183,39 +183,51 @@ static int env_append(char **r, char ***k, char **a) { + return 0; + } + +-char **strv_env_merge(size_t n_lists, ...) { +- _cleanup_strv_free_ char **ret = NULL; +- size_t n = 0; +- char **l, **k; ++char** _strv_env_merge(char **first, ...) { ++ _cleanup_strv_free_ char **merged = NULL; ++ char **k; + va_list ap; + + /* Merges an arbitrary number of environment sets */ + +- va_start(ap, n_lists); +- for (size_t i = 0; i < n_lists; i++) { ++ size_t n = strv_length(first); ++ ++ va_start(ap, first); ++ for (;;) { ++ char **l; ++ + l = va_arg(ap, char**); ++ if (l == POINTER_MAX) ++ break; ++ + n += strv_length(l); + } + va_end(ap); + +- ret = new(char*, n+1); +- if (!ret) ++ k = merged = new(char*, n + 1); ++ if (!merged) + return NULL; ++ merged[0] = NULL; + +- *ret = NULL; +- k = ret; ++ if (env_append(merged, &k, first) < 0) ++ return NULL; ++ ++ va_start(ap, first); ++ for (;;) { ++ char **l; + +- va_start(ap, n_lists); +- for (size_t i = 0; i < n_lists; i++) { + l = va_arg(ap, char**); +- if (env_append(ret, &k, l) < 0) { ++ if (l == POINTER_MAX) ++ break; ++ ++ if (env_append(merged, &k, l) < 0) { + va_end(ap); + return NULL; + } + } + va_end(ap); + +- return TAKE_PTR(ret); ++ return TAKE_PTR(merged); + } + + static bool env_match(const char *t, const char *pattern) { +diff --git a/src/basic/env-util.h b/src/basic/env-util.h +index 1fbe7e4270..18d10ebab8 100644 +--- a/src/basic/env-util.h ++++ b/src/basic/env-util.h +@@ -39,7 +39,8 @@ char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const cha + bool strv_env_name_is_valid(char **l); + bool strv_env_name_or_assignment_is_valid(char **l); + +-char **strv_env_merge(size_t n_lists, ...); ++char** _strv_env_merge(char **first, ...); ++#define strv_env_merge(first, ...) _strv_env_merge(first, __VA_ARGS__, POINTER_MAX) + char **strv_env_delete(char **x, size_t n_lists, ...); /* New copy */ + + char **strv_env_unset(char **l, const char *p); /* In place ... */ +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index f6783e924a..5ea97b9194 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -2888,7 +2888,7 @@ int bus_exec_context_set_transient_property( + if (!joined) + return -ENOMEM; + +- e = strv_env_merge(2, c->environment, l); ++ e = strv_env_merge(c->environment, l); + if (!e) + return -ENOMEM; + +@@ -2922,7 +2922,7 @@ int bus_exec_context_set_transient_property( + if (!joined) + return -ENOMEM; + +- e = strv_env_merge(2, c->unset_environment, l); ++ e = strv_env_merge(c->unset_environment, l); + if (!e) + return -ENOMEM; + +diff --git a/src/core/execute.c b/src/core/execute.c +index 4608956259..5bee44ac7b 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -4158,8 +4158,7 @@ static int exec_child( + return log_oom(); + } + +- accum_env = strv_env_merge(5, +- params->environment, ++ accum_env = strv_env_merge(params->environment, + our_env, + pass_env, + context->environment, +@@ -5214,7 +5213,7 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c, + else { + char **m; + +- m = strv_env_merge(2, r, p); ++ m = strv_env_merge(r, p); + strv_free(r); + strv_free(p); + if (!m) +diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c +index 64761ddb11..59ddb9c487 100644 +--- a/src/core/locale-setup.c ++++ b/src/core/locale-setup.c +@@ -85,7 +85,7 @@ int locale_setup(char ***environment) { + else { + char **merged; + +- merged = strv_env_merge(2, *environment, add); ++ merged = strv_env_merge(*environment, add); + if (!merged) + return -ENOMEM; + +diff --git a/src/core/manager.c b/src/core/manager.c +index 24dfe9fc06..d76b7b2b16 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -3672,7 +3672,7 @@ int manager_transient_environment_add(Manager *m, char **plus) { + if (strv_isempty(plus)) + return 0; + +- a = strv_env_merge(2, m->transient_environment, plus); ++ a = strv_env_merge(m->transient_environment, plus); + if (!a) + return log_oom(); + +@@ -3704,7 +3704,7 @@ int manager_client_environment_modify( + } + + if (!strv_isempty(plus)) { +- b = strv_env_merge(2, l, plus); ++ b = strv_env_merge(l, plus); + if (!b) { + strv_free(a); + return -ENOMEM; +@@ -3731,7 +3731,7 @@ int manager_get_effective_environment(Manager *m, char ***ret) { + assert(m); + assert(ret); + +- l = strv_env_merge(2, m->transient_environment, m->client_environment); ++ l = strv_env_merge(m->transient_environment, m->client_environment); + if (!l) + return -ENOMEM; + +diff --git a/src/core/service.c b/src/core/service.c +index ddcfeb8523..4115db0a30 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -1546,7 +1546,7 @@ static int service_spawn( + if (r < 0) + return r; + +- final_env = strv_env_merge(2, exec_params.environment, our_env, NULL); ++ final_env = strv_env_merge(exec_params.environment, our_env); + if (!final_env) + return -ENOMEM; + +diff --git a/src/notify/notify.c b/src/notify/notify.c +index 49d5f3ec92..b468a5bc44 100644 +--- a/src/notify/notify.c ++++ b/src/notify/notify.c +@@ -232,7 +232,7 @@ static int run(int argc, char* argv[]) { + + our_env[i++] = NULL; + +- final_env = strv_env_merge(2, our_env, argv + optind); ++ final_env = strv_env_merge(our_env, argv + optind); + if (!final_env) + return log_oom(); + +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index d75cce408e..cf89b27dfa 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -3189,8 +3189,8 @@ static int inner_child( + _cleanup_free_ char *home = NULL; + char as_uuid[ID128_UUID_STRING_MAX]; + size_t n_env = 1; +- const char *envp[] = { +- "PATH=" DEFAULT_PATH_COMPAT, ++ char *envp[] = { ++ (char*) "PATH=" DEFAULT_PATH_COMPAT, + NULL, /* container */ + NULL, /* TERM */ + NULL, /* HOME */ +@@ -3426,17 +3426,17 @@ static int inner_child( + n_env++; + + if (home || !uid_is_valid(arg_uid) || arg_uid == 0) +- if (asprintf((char**)(envp + n_env++), "HOME=%s", home ?: "/root") < 0) ++ if (asprintf(envp + n_env++, "HOME=%s", home ?: "/root") < 0) + return log_oom(); + + if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0) +- if (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ?: "root") < 0 || +- asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0) ++ if (asprintf(envp + n_env++, "USER=%s", arg_user ?: "root") < 0 || ++ asprintf(envp + n_env++, "LOGNAME=%s", arg_user ? arg_user : "root") < 0) + return log_oom(); + + assert(!sd_id128_is_null(arg_uuid)); + +- if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0) ++ if (asprintf(envp + n_env++, "container_uuid=%s", id128_to_uuid_string(arg_uuid, as_uuid)) < 0) + return log_oom(); + + if (fdset_size(fds) > 0) { +@@ -3444,11 +3444,11 @@ static int inner_child( + if (r < 0) + return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors."); + +- if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) || +- (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) ++ if ((asprintf(envp + n_env++, "LISTEN_FDS=%u", fdset_size(fds)) < 0) || ++ (asprintf(envp + n_env++, "LISTEN_PID=1") < 0)) + return log_oom(); + } +- if (asprintf((char **)(envp + n_env++), "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) ++ if (asprintf(envp + n_env++, "NOTIFY_SOCKET=%s", NSPAWN_NOTIFY_SOCKET_PATH) < 0) + return log_oom(); + + if (arg_n_credentials > 0) { +@@ -3458,7 +3458,7 @@ static int inner_child( + n_env++; + } + +- env_use = strv_env_merge(3, envp, os_release_pairs, arg_setenv); ++ env_use = strv_env_merge(envp, os_release_pairs, arg_setenv); + if (!env_use) + return log_oom(); + +diff --git a/src/run/run.c b/src/run/run.c +index 0be974f03a..993f1bc4f4 100644 +--- a/src/run/run.c ++++ b/src/run/run.c +@@ -1526,7 +1526,7 @@ static int start_transient_scope(sd_bus *bus) { + return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid); + } + +- env = strv_env_merge(3, environ, user_env, arg_environment); ++ env = strv_env_merge(environ, user_env, arg_environment); + if (!env) + return log_oom(); + +diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c +index ed4580e4af..5bf130ed86 100644 +--- a/src/test/test-env-util.c ++++ b/src/test/test-env-util.c +@@ -81,7 +81,7 @@ static void test_strv_env_merge(void) { + b = strv_new("FOO=KKK", "FOO=", "PIEP=", "SCHLUMPF=SMURFF", "NANANANA=YES"); + assert_se(b); + +- r = strv_env_merge(2, a, b); ++ r = strv_env_merge(a, b); + assert_se(r); + assert_se(streq(r[0], "FOO=")); + assert_se(streq(r[1], "WALDO=")); +-- +2.33.0 + diff --git a/backport-TEST-80-synchronize-explicitly-instead-of-by-time.patch b/backport-TEST-80-synchronize-explicitly-instead-of-by-time.patch new file mode 100644 index 0000000..d53e2cc --- /dev/null +++ b/backport-TEST-80-synchronize-explicitly-instead-of-by-time.patch @@ -0,0 +1,166 @@ +From 09ba6d1a14ba027f2bc4e3426c7dd85db19e720e Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 28 Mar 2023 16:35:35 +0200 +Subject: [PATCH] TEST-80: synchronize explicitly instead of by time + +This removes "sleep" invocations, and makes the notify access testcase a +lot more robust to runtime jitter. We use a pair of fifos in the fs to +sync instead. + +Also various other improvoements, including comments. + +(Also removes the unnecessary "no-qemu" restriction) + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/09ba6d1a14ba027f2bc4e3426c7dd85db19e720e + +--- + test/TEST-80-NOTIFYACCESS/test.sh | 1 - + test/testsuite-80.units/test.sh | 59 +++++++++++++++++++++++++------ + test/units/testsuite-80.sh | 30 +++++++++++++--- + 3 files changed, 73 insertions(+), 17 deletions(-) + +diff --git a/test/TEST-80-NOTIFYACCESS/test.sh b/test/TEST-80-NOTIFYACCESS/test.sh +index b4d2452b75..8ec5b1bc5f 100755 +--- a/test/TEST-80-NOTIFYACCESS/test.sh ++++ b/test/TEST-80-NOTIFYACCESS/test.sh +@@ -3,7 +3,6 @@ + set -e + + TEST_DESCRIPTION="test NotifyAccess through sd-notify" +-TEST_NO_QEMU=1 + + # shellcheck source=test/test-functions + . "${TEST_BASE_DIR:?}/test-functions" +diff --git a/test/testsuite-80.units/test.sh b/test/testsuite-80.units/test.sh +index 3ca71d5648..565ed8d35a 100755 +--- a/test/testsuite-80.units/test.sh ++++ b/test/testsuite-80.units/test.sh +@@ -4,23 +4,60 @@ + set -eux + set -o pipefail + +-systemd-notify --status="Test starts, waiting for 5 seconds" +-sleep 5 ++sync_in() { ++ read -r x < /tmp/syncfifo2 ++ test "$x" = "$1" ++} + ++sync_out() { ++ echo "$1" > /tmp/syncfifo1 ++} ++ ++export SYSTEMD_LOG_LEVEL=debug ++ ++echo "toplevel PID: $BASHPID" ++ ++systemd-notify --status="Test starts" ++sync_out a ++sync_in b + ( +- systemd-notify --pid=auto ++ echo "subshell PID: $BASHPID" ++ ++ # Make us main process ++ systemd-notify --pid="$BASHPID" ++ ++ # Lock down access to just us + systemd-notify "NOTIFYACCESS=main" + +- systemd-notify --status="Sending READY=1 in an unpriviledged process" +- ( +- sleep 0.1 +- systemd-notify --ready +- ) +- sleep 10 ++ # This should still work ++ systemd-notify --status="Sending READY=1 in an unprivileged process" ++ ++ # Send as subprocess of the subshell, this should not work ++ systemd-notify --ready --pid=self --status "BOGUS1" + +- systemd-notify "MAINPID=$$" ++ sync_out c ++ sync_in d ++ ++ # Move main process back to toplevel ++ systemd-notify --pid=parent "MAINPID=$$" ++ ++ # Should be dropped again ++ systemd-notify --status="BOGUS2" --pid=parent ++ ++ # Apparently, bash will automatically invoke the last command in a subshell ++ # via a simple execve() rather than fork()ing first. But we want that the ++ # previous command uses the subshell's PID, hence let's insert a final, ++ # bogus redundant command as last command to run in the subshell, so that ++ # bash can't optimize things like that. ++ echo "bye" + ) + ++echo "toplevel again: $BASHPID" ++ + systemd-notify --ready --status="OK" + systemd-notify "NOTIFYACCESS=none" +-sleep infinity ++systemd-notify --status="BOGUS3" ++ ++sync_out e ++ ++exec sleep infinity +diff --git a/test/units/testsuite-80.sh b/test/units/testsuite-80.sh +index 5f57569b07..43647a707f 100755 +--- a/test/units/testsuite-80.sh ++++ b/test/units/testsuite-80.sh +@@ -9,17 +9,35 @@ set -o pipefail + + : >/failed + ++mkfifo /tmp/syncfifo1 /tmp/syncfifo2 ++ ++sync_in() { ++ read -r x < /tmp/syncfifo1 ++ test "$x" = "$1" ++} ++ ++sync_out() { ++ echo "$1" > /tmp/syncfifo2 ++} ++ ++export SYSTEMD_LOG_LEVEL=debug ++ + systemctl --no-block start notify.service +-sleep 2 + +-assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts, waiting for 5 seconds" ++sync_in a ++ + assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" +-sleep 5 ++assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts" ++ ++sync_out b ++sync_in c + + assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main" +-assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unpriviledged process" ++assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unprivileged process" + assert_rc 3 systemctl --quiet is-active notify.service +-sleep 10 ++ ++sync_out d ++sync_in e + + systemctl --quiet is-active notify.service + assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK" +@@ -28,5 +46,7 @@ assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none" + systemctl stop notify.service + assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" + ++rm /tmp/syncfifo1 /tmp/syncfifo2 ++ + touch /testok + rm /failed +-- +2.33.0 + diff --git a/backport-basic-strv-inline-variables-and-modernize-style-a-bi.patch b/backport-basic-strv-inline-variables-and-modernize-style-a-bi.patch new file mode 100644 index 0000000..3ed2c9b --- /dev/null +++ b/backport-basic-strv-inline-variables-and-modernize-style-a-bi.patch @@ -0,0 +1,453 @@ +From 14337c374a641badf55cf9ce6ec1fb387886b651 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 26 Nov 2021 09:47:44 +0100 +Subject: [PATCH] basic/strv: inline variables and modernize style a bit + +Conflict:Context Adaptation. +Reference:https://github.com/systemd/systemd/commit/14337c374a641badf55cf9ce6ec1fb387886b651 + +--- + src/basic/strv.c | 82 +++++++++++++++++++++--------------------------- + src/basic/strv.h | 42 ++++++++++++------------- + 2 files changed, 56 insertions(+), 68 deletions(-) + +diff --git a/src/basic/strv.c b/src/basic/strv.c +index 3adf3c5..e10af33 100644 +--- a/src/basic/strv.c ++++ b/src/basic/strv.c +@@ -16,7 +16,7 @@ + #include "string-util.h" + #include "strv.h" + +-char *strv_find(char * const *l, const char *name) { ++char* strv_find(char * const *l, const char *name) { + char * const *i; + + assert(name); +@@ -28,7 +28,7 @@ char *strv_find(char * const *l, const char *name) { + return NULL; + } + +-char *strv_find_case(char * const *l, const char *name) { ++char* strv_find_case(char * const *l, const char *name) { + char * const *i; + + assert(name); +@@ -40,7 +40,7 @@ char *strv_find_case(char * const *l, const char *name) { + return NULL; + } + +-char *strv_find_prefix(char * const *l, const char *name) { ++char* strv_find_prefix(char * const *l, const char *name) { + char * const *i; + + assert(name); +@@ -52,7 +52,7 @@ char *strv_find_prefix(char * const *l, const char *name) { + return NULL; + } + +-char *strv_find_startswith(char * const *l, const char *name) { ++char* strv_find_startswith(char * const *l, const char *name) { + char * const *i, *e; + + assert(name); +@@ -69,19 +69,17 @@ char *strv_find_startswith(char * const *l, const char *name) { + return NULL; + } + +-char **strv_free(char **l) { +- char **k; +- ++char** strv_free(char **l) { + if (!l) + return NULL; + +- for (k = l; *k; k++) ++ for (char **k = l; *k; k++) + free(*k); + + return mfree(l); + } + +-char **strv_free_erase(char **l) { ++char** strv_free_erase(char **l) { + char **i; + + STRV_FOREACH(i, l) +@@ -90,7 +88,7 @@ char **strv_free_erase(char **l) { + return mfree(l); + } + +-char **strv_copy(char * const *l) { ++char** strv_copy(char * const *l) { + char **r, **k; + + k = r = new(char*, strv_length(l) + 1); +@@ -122,7 +120,7 @@ size_t strv_length(char * const *l) { + return n; + } + +-char **strv_new_ap(const char *x, va_list ap) { ++char** strv_new_ap(const char *x, va_list ap) { + _cleanup_strv_free_ char **a = NULL; + size_t n = 0, i = 0; + va_list aq; +@@ -161,7 +159,7 @@ char **strv_new_ap(const char *x, va_list ap) { + return TAKE_PTR(a); + } + +-char **strv_new_internal(const char *x, ...) { ++char** strv_new_internal(const char *x, ...) { + char **r; + va_list ap; + +@@ -174,7 +172,7 @@ char **strv_new_internal(const char *x, ...) { + + int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { + char * const *s, **t; +- size_t p, q, i = 0, j; ++ size_t p, q, i = 0; + + assert(a); + +@@ -195,7 +193,6 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { + *a = t; + + STRV_FOREACH(s, b) { +- + if (filter_duplicates && strv_contains(t, *s)) + continue; + +@@ -212,7 +209,7 @@ int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { + return (int) i; + + rollback: +- for (j = 0; j < i; j++) ++ for (size_t j = 0; j < i; j++) + free(t[p + j]); + + t[p] = NULL; +@@ -285,7 +282,6 @@ int strv_split_full(char ***t, const char *s, const char *separators, ExtractFla + return -ENOMEM; + + l[n++] = TAKE_PTR(word); +- + l[n] = NULL; + } + +@@ -352,7 +348,7 @@ int strv_split_colon_pairs(char ***t, const char *s) { + return (int) n; + } + +-char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { ++char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { + char * const *s; + char *r, *e; + size_t n, k, m; +@@ -459,7 +455,7 @@ int strv_push_pair(char ***l, char *a, char *b) { + + int strv_insert(char ***l, size_t position, char *value) { + char **c; +- size_t n, m, i; ++ size_t n, m; + + if (!value) + return 0; +@@ -476,18 +472,14 @@ int strv_insert(char ***l, size_t position, char *value) { + if (!c) + return -ENOMEM; + +- for (i = 0; i < position; i++) ++ for (size_t i = 0; i < position; i++) + c[i] = (*l)[i]; + c[position] = value; +- for (i = position; i < n; i++) ++ for (size_t i = position; i < n; i++) + c[i+1] = (*l)[i]; +- + c[n+1] = NULL; + +- free(*l); +- *l = c; +- +- return 0; ++ return free_and_replace(*l, c); + } + + int strv_consume(char ***l, char *value) { +@@ -584,7 +576,7 @@ int strv_extend_front(char ***l, const char *value) { + return 0; + } + +-char **strv_uniq(char **l) { ++char** strv_uniq(char **l) { + char **i; + + /* Drops duplicate entries. The first identical string will be +@@ -606,7 +598,7 @@ bool strv_is_uniq(char * const *l) { + return true; + } + +-char **strv_remove(char **l, const char *s) { ++char** strv_remove(char **l, const char *s) { + char **f, **t; + + if (!l) +@@ -627,7 +619,7 @@ char **strv_remove(char **l, const char *s) { + return l; + } + +-char **strv_parse_nulstr(const char *s, size_t l) { ++char** strv_parse_nulstr(const char *s, size_t l) { + /* l is the length of the input data, which will be split at NULs into + * elements of the resulting strv. Hence, the number of items in the resulting strv + * will be equal to one plus the number of NUL bytes in the l bytes starting at s, +@@ -639,7 +631,6 @@ char **strv_parse_nulstr(const char *s, size_t l) { + * empty strings in s. + */ + +- const char *p; + size_t c = 0, i = 0; + char **v; + +@@ -648,7 +639,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { + if (l <= 0) + return new0(char*, 1); + +- for (p = s; p < s + l; p++) ++ for (const char *p = s; p < s + l; p++) + if (*p == 0) + c++; + +@@ -659,8 +650,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { + if (!v) + return NULL; + +- p = s; +- while (p < s + l) { ++ for (const char *p = s; p < s + l; ) { + const char *e; + + e = memchr(p, 0, s + l - p); +@@ -684,7 +674,7 @@ char **strv_parse_nulstr(const char *s, size_t l) { + return v; + } + +-char **strv_split_nulstr(const char *s) { ++char** strv_split_nulstr(const char *s) { + const char *i; + char **r = NULL; + +@@ -759,7 +749,7 @@ static int str_compare(char * const *a, char * const *b) { + return strcmp(*a, *b); + } + +-char **strv_sort(char **l) { ++char** strv_sort(char **l) { + typesafe_qsort(l, strv_length(l), str_compare); + return l; + } +@@ -808,20 +798,20 @@ int strv_extendf(char ***l, const char *format, ...) { + return strv_consume(l, x); + } + +-char **strv_reverse(char **l) { +- size_t n, i; ++char** strv_reverse(char **l) { ++ size_t n; + + n = strv_length(l); + if (n <= 1) + return l; + +- for (i = 0; i < n / 2; i++) ++ for (size_t i = 0; i < n / 2; i++) + SWAP_TWO(l[i], l[n-1-i]); + + return l; + } + +-char **strv_shell_escape(char **l, const char *bad) { ++char** strv_shell_escape(char **l, const char *bad) { + char **s; + + /* Escapes every character in every string in l that is in bad, +@@ -852,19 +842,17 @@ bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t * + return false; + } + +-char ***strv_free_free(char ***l) { +- char ***i; +- ++char*** strv_free_free(char ***l) { + if (!l) + return NULL; + +- for (i = l; *i; i++) ++ for (char ***i = l; *i; i++) + strv_free(*i); + + return mfree(l); + } + +-char **strv_skip(char **l, size_t n) { ++char** strv_skip(char **l, size_t n) { + + while (n > 0) { + if (strv_isempty(l)) +@@ -877,7 +865,7 @@ char **strv_skip(char **l, size_t n) { + } + + int strv_extend_n(char ***l, const char *value, size_t n) { +- size_t i, j, k; ++ size_t i, k; + char **nl; + + assert(l); +@@ -904,15 +892,15 @@ int strv_extend_n(char ***l, const char *value, size_t n) { + if (!nl[i]) + goto rollback; + } +- + nl[i] = NULL; ++ + return 0; + + rollback: +- for (j = k; j < i; j++) ++ for (size_t j = k; j < i; j++) + free(nl[j]); +- + nl[k] = NULL; ++ + return -ENOMEM; + } + +diff --git a/src/basic/strv.h b/src/basic/strv.h +index 911528f..8674bfd 100644 +--- a/src/basic/strv.h ++++ b/src/basic/strv.h +@@ -13,23 +13,23 @@ + #include "macro.h" + #include "string-util.h" + +-char *strv_find(char * const *l, const char *name) _pure_; +-char *strv_find_case(char * const *l, const char *name) _pure_; +-char *strv_find_prefix(char * const *l, const char *name) _pure_; +-char *strv_find_startswith(char * const *l, const char *name) _pure_; ++char* strv_find(char * const *l, const char *name) _pure_; ++char* strv_find_case(char * const *l, const char *name) _pure_; ++char* strv_find_prefix(char * const *l, const char *name) _pure_; ++char* strv_find_startswith(char * const *l, const char *name) _pure_; + + #define strv_contains(l, s) (!!strv_find((l), (s))) + #define strv_contains_case(l, s) (!!strv_find_case((l), (s))) + +-char **strv_free(char **l); ++char** strv_free(char **l); + DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); + #define _cleanup_strv_free_ _cleanup_(strv_freep) + +-char **strv_free_erase(char **l); ++char** strv_free_erase(char **l); + DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); + #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) + +-char **strv_copy(char * const *l); ++char** strv_copy(char * const *l); + size_t strv_length(char * const *l) _pure_; + + int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); +@@ -50,8 +50,8 @@ int strv_consume(char ***l, char *value); + int strv_consume_pair(char ***l, char *a, char *b); + int strv_consume_prepend(char ***l, char *value); + +-char **strv_remove(char **l, const char *s); +-char **strv_uniq(char **l); ++char** strv_remove(char **l, const char *s); ++char** strv_uniq(char **l); + bool strv_is_uniq(char * const *l); + + int strv_compare(char * const *a, char * const *b); +@@ -59,8 +59,8 @@ static inline bool strv_equal(char * const *a, char * const *b) { + return strv_compare(a, b) == 0; + } + +-char **strv_new_internal(const char *x, ...) _sentinel_; +-char **strv_new_ap(const char *x, va_list ap); ++char** strv_new_internal(const char *x, ...) _sentinel_; ++char** strv_new_ap(const char *x, va_list ap); + #define strv_new(...) strv_new_internal(__VA_ARGS__, NULL) + + #define STRV_IGNORE ((const char *) POINTER_MAX) +@@ -74,7 +74,7 @@ static inline bool strv_isempty(char * const *l) { + } + + int strv_split_full(char ***t, const char *s, const char *separators, ExtractFlags flags); +-static inline char **strv_split(const char *s, const char *separators) { ++static inline char** strv_split(const char *s, const char *separators) { + char **ret; + + if (strv_split_full(&ret, s, separators, 0) < 0) +@@ -84,7 +84,7 @@ static inline char **strv_split(const char *s, const char *separators) { + } + + int strv_split_newlines_full(char ***ret, const char *s, ExtractFlags flags); +-static inline char **strv_split_newlines(const char *s) { ++static inline char** strv_split_newlines(const char *s) { + char **ret; + + if (strv_split_newlines_full(&ret, s, 0) < 0) +@@ -98,13 +98,13 @@ static inline char **strv_split_newlines(const char *s) { + * string in the vector is an empty string. */ + int strv_split_colon_pairs(char ***t, const char *s); + +-char *strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); ++char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool escape_separtor); + static inline char *strv_join(char * const *l, const char *separator) { + return strv_join_full(l, separator, NULL, false); + } + +-char **strv_parse_nulstr(const char *s, size_t l); +-char **strv_split_nulstr(const char *s); ++char** strv_parse_nulstr(const char *s, size_t l); ++char** strv_split_nulstr(const char *s); + int strv_make_nulstr(char * const *l, char **p, size_t *n); + + static inline int strv_from_nulstr(char ***a, const char *nulstr) { +@@ -133,7 +133,7 @@ bool strv_overlap(char * const *a, char * const *b) _pure_; + #define STRV_FOREACH_PAIR(x, y, l) \ + for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) + +-char **strv_sort(char **l); ++char** strv_sort(char **l); + void strv_print(char * const *l); + + #define strv_from_stdarg_alloca(first) \ +@@ -208,8 +208,8 @@ void strv_print(char * const *l); + x; \ + x = *(++_l)) + +-char **strv_reverse(char **l); +-char **strv_shell_escape(char **l, const char *bad); ++char** strv_reverse(char **l); ++char** strv_shell_escape(char **l, const char *bad); + + bool strv_fnmatch_full(char* const* patterns, const char *s, int flags, size_t *matched_pos); + static inline bool strv_fnmatch(char* const* patterns, const char *s) { +@@ -222,10 +222,10 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i + strv_fnmatch_full(patterns, s, flags, NULL); + } + +-char ***strv_free_free(char ***l); ++char*** strv_free_free(char ***l); + DEFINE_TRIVIAL_CLEANUP_FUNC(char***, strv_free_free); + +-char **strv_skip(char **l, size_t n); ++char** strv_skip(char **l, size_t n); + + int strv_extend_n(char ***l, const char *value, size_t n); + +-- +2.33.0 + diff --git a/backport-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch b/backport-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch new file mode 100644 index 0000000..659f88f --- /dev/null +++ b/backport-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch @@ -0,0 +1,92 @@ +From 8db998981a4fefd0122bcf5f965726b63c9045c2 Mon Sep 17 00:00:00 2001 +From: Richard Phibel +Date: Tue, 23 May 2023 16:09:40 +0200 +Subject: [PATCH] core: Don't GC unit if it is in cgroup_empty_queue + +The gc_unit_queue is dispatched before the cgroup_empty_queue. Because +of this, when we enter in on_cgroup_empty_event, the unit in +cgroup_empty_queue may already have been freed and we don't clean up the +corresponding cgroup. With this change, we prevent the unit from being +garbage collected if it is in the cgroup_empty_queue. + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/8db998981a4fefd0122bcf5f965726b63c9045c2 + +--- + src/core/unit.c | 3 ++ + test/units/testsuite-19.cleanup-slice.sh | 49 ++++++++++++++++++++++++ + 2 files changed, 52 insertions(+) + create mode 100755 test/units/testsuite-19.cleanup-slice.sh + +diff --git a/src/core/unit.c b/src/core/unit.c +index 90f87a95f5..84e9185e82 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -441,6 +441,9 @@ bool unit_may_gc(Unit *u) { + if (u->perpetual) + return false; + ++ if (u->in_cgroup_empty_queue) ++ return false; ++ + if (sd_bus_track_count(u->bus_track) > 0) + return false; + +diff --git a/test/units/testsuite-19.cleanup-slice.sh b/test/units/testsuite-19.cleanup-slice.sh +new file mode 100755 +index 0000000000..5d63160334 +--- /dev/null ++++ b/test/units/testsuite-19.cleanup-slice.sh +@@ -0,0 +1,49 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++set -eux ++set -o pipefail ++ ++# shellcheck source=test/units/util.sh ++. "$(dirname "$0")"/util.sh ++ ++export SYSTEMD_LOG_LEVEL=debug ++ ++# Create service with KillMode=none inside a slice ++cat </run/systemd/system/test19cleanup.service ++[Unit] ++Description=Test 19 cleanup Service ++[Service] ++Slice=test19cleanup.slice ++Type=exec ++ExecStart=sleep infinity ++KillMode=none ++EOF ++cat </run/systemd/system/test19cleanup.slice ++[Unit] ++Description=Test 19 cleanup Slice ++EOF ++ ++# Start service ++systemctl start test19cleanup.service ++assert_rc 0 systemd-cgls /test19cleanup.slice ++ ++pid=$(systemctl show --property MainPID --value test19cleanup) ++ps "$pid" ++ ++# Stop slice ++# The sleep process will not be killed because of KillMode=none ++# Since there is still a process running under it, the /test19cleanup.slice cgroup won't be removed ++systemctl stop test19cleanup.slice ++ ++ps "$pid" ++ ++# Kill sleep process manually ++kill -s TERM "$pid" ++while kill -0 "$pid" 2>/dev/null; do sleep 0.1; done ++ ++timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice/test19cleanup.service >& /dev/null; do sleep .5; done' ++assert_rc 1 systemd-cgls /test19cleanup.slice/test19cleanup.service ++ ++# Check that empty cgroup /test19cleanup.slice has been removed ++timeout 30 bash -c 'while systemd-cgls /test19cleanup.slice >& /dev/null; do sleep .5; done' ++assert_rc 1 systemd-cgls /test19cleanup.slice +-- +2.33.0 + diff --git a/backport-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch b/backport-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch new file mode 100644 index 0000000..e475fc0 --- /dev/null +++ b/backport-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch @@ -0,0 +1,84 @@ +From af05bb971731fe7280e4e85fde71c2e671772c18 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Jun 2023 11:11:49 +0200 +Subject: [PATCH] core: do not GC units/jobs that are in the D-Bus queue + +Let's make sure that D-Bus messages are always sent out when pending, +before we might GC a unit/job. + +This is kinda a follow-up for 8db998981a4fefd0122bcf5f965726b63c9045c2, +and a similar logic really applies: GC should only be done if we +processed everything else, generated evertyhing else and really don't +need it anymore. + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/af05bb971731fe7280e4e85fde71c2e671772c18 + +--- + src/core/dbus-job.c | 3 +++ + src/core/dbus-unit.c | 3 +++ + src/core/job.c | 4 ++++ + src/core/unit.c | 4 ++++ + 4 files changed, 14 insertions(+) + +diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c +index 9792a5c44a..c88d8c2dd5 100644 +--- a/src/core/dbus-job.c ++++ b/src/core/dbus-job.c +@@ -241,6 +241,9 @@ void bus_job_send_change_signal(Job *j) { + if (j->in_dbus_queue) { + LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j); + j->in_dbus_queue = false; ++ ++ /* The job might be good to be GC once its pending signals have been sent */ ++ job_add_to_gc_queue(j); + } + + r = bus_foreach_bus(j->manager, j->bus_track, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index 59d541ebfe..629f08ebcc 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -1648,6 +1648,9 @@ void bus_unit_send_change_signal(Unit *u) { + if (u->in_dbus_queue) { + LIST_REMOVE(dbus_queue, u->manager->dbus_unit_queue, u); + u->in_dbus_queue = false; ++ ++ /* The unit might be good to be GC once its pending signals have been sent */ ++ unit_add_to_gc_queue(u); + } + + if (!u->id) +diff --git a/src/core/job.c b/src/core/job.c +index f87b0f7c74..50f9581d72 100644 +--- a/src/core/job.c ++++ b/src/core/job.c +@@ -1444,6 +1444,10 @@ bool job_may_gc(Job *j) { + if (!UNIT_VTABLE(j->unit)->gc_jobs) + return false; + ++ /* Make sure to send out pending D-Bus events before we unload the unit */ ++ if (j->in_dbus_queue) ++ return false; ++ + if (sd_bus_track_count(j->bus_track) > 0) + return false; + +diff --git a/src/core/unit.c b/src/core/unit.c +index 80f398c309..7b2e8c5f5c 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -447,6 +447,10 @@ bool unit_may_gc(Unit *u) { + if (u->in_cgroup_empty_queue || u->in_cgroup_oom_queue) + return false; + ++ /* Make sure to send out D-Bus events before we unload the unit */ ++ if (u->in_dbus_queue) ++ return false; ++ + if (sd_bus_track_count(u->bus_track) > 0) + return false; + +-- +2.33.0 + diff --git a/backport-core-fix-property-getter-method-for-NFileDescriptorS.patch b/backport-core-fix-property-getter-method-for-NFileDescriptorS.patch new file mode 100644 index 0000000..a0cba00 --- /dev/null +++ b/backport-core-fix-property-getter-method-for-NFileDescriptorS.patch @@ -0,0 +1,72 @@ +From 47226e893b24f1aa84caaa6be266fb3c03442904 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 12 Apr 2023 20:51:23 +0200 +Subject: [PATCH] core: fix property getter method for NFileDescriptorStore bus + property + +Since da6053d0a7c16795e7fac1f9ba6694863918a597 this is a size_t, not an +unsigned. The difference doesn't matter on LE archs, but it matters on +BE (i.e. s390x), since we'll return entirely nonsensical data. + +Let's fix that. + +Follow-up-for: da6053d0a7c16795e7fac1f9ba6694863918a597 + +An embarassing bug introduced in 2018... That made me scratch my head +for way too long, as it made #27135 fail on s390x while it passed +everywhere else. + +Conflict:Adaptation Context. ASSERT_PTR function adaptation. +Reference:https://github.com/systemd/systemd/commit/47226e893b24f1aa84caaa6be266fb3c03442904 + +--- + src/core/dbus-service.c | 26 +++++++++++++++++++++++++- + 1 file changed, 25 insertions(+), 1 deletion(-) + +diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c +index 02628cd..c0ec277 100644 +--- a/src/core/dbus-service.c ++++ b/src/core/dbus-service.c +@@ -189,6 +189,30 @@ int bus_service_method_mount_image(sd_bus_message *message, void *userdata, sd_b + return bus_service_method_mount(message, userdata, error, true); + } + ++#if __SIZEOF_SIZE_T__ == 8 ++static int property_get_size_as_uint32( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ ++ size_t *value = userdata; ++ assert(value); ++ uint32_t sz = *value >= UINT32_MAX ? UINT32_MAX : (uint32_t) *value; ++ ++ /* Returns a size_t as a D-Bus "u" type, i.e. as 32bit value, even if size_t is 64bit. We'll saturate if it doesn't fit. */ ++ ++ return sd_bus_message_append_basic(reply, 'u', &sz); ++} ++#elif __SIZEOF_SIZE_T__ == 4 ++#define property_get_size_as_uint32 ((sd_bus_property_get_t) NULL) ++#else ++#error "Unexpected size of size_t" ++#endif ++ + const sd_bus_vtable bus_service_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -215,7 +239,7 @@ const sd_bus_vtable bus_service_vtable[] = { + SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), +- SD_BUS_PROPERTY("NFileDescriptorStore", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store), 0), ++ SD_BUS_PROPERTY("NFileDescriptorStore", "u", property_get_size_as_uint32, offsetof(Service, n_fd_store), 0), + SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), +-- +2.33.0 + diff --git a/backport-core-move-runtime-directory-removal-into-release_res.patch b/backport-core-move-runtime-directory-removal-into-release_res.patch new file mode 100644 index 0000000..615a212 --- /dev/null +++ b/backport-core-move-runtime-directory-removal-into-release_res.patch @@ -0,0 +1,90 @@ +From 1ba84fef3c1e505cb1413ef446a85c0c7ec439c6 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 4 Apr 2023 13:42:08 +0200 +Subject: [PATCH] core: move runtime directory removal into release_resource + handler + +We already clear the various fds we keep from the release_resources() +handler, let's also destroy the runtime dir from there if this +preservation mode is selected. + +This makes a minor semantic change: previously we'd keep a runtime +directory around if RuntimeDirectoryPreserve=restart is selected and at +least one JOB_START job was around. With this logic we'll keep it around +a tiny bit longer: as long as any job for the unit is around. + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/1ba84fef3c1e505cb1413ef446a85c0c7ec439c6 + +--- + src/core/unit.c | 26 +++++++++++++++++++++++--- + 1 file changed, 23 insertions(+), 3 deletions(-) + +diff --git a/src/core/unit.c b/src/core/unit.c +index 089213cf88..d632c34f98 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -396,6 +396,7 @@ static bool unit_success_failure_handler_has_jobs(Unit *unit) { + + void unit_release_resources(Unit *u) { + UnitActiveState state; ++ ExecContext *ec; + + assert(u); + +@@ -412,6 +413,10 @@ void unit_release_resources(Unit *u) { + if (unit_will_restart(u)) + return; + ++ ec = unit_get_exec_context(u); ++ if (ec && ec->runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART) ++ exec_context_destroy_runtime_directory(ec, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]); ++ + if (UNIT_VTABLE(u)->release_resources) + UNIT_VTABLE(u)->release_resources(u); + } +@@ -582,6 +587,21 @@ void unit_submit_to_stop_when_bound_queue(Unit *u) { + u->in_stop_when_bound_queue = true; + } + ++static bool unit_can_release_resources(Unit *u) { ++ ExecContext *ec; ++ ++ assert(u); ++ ++ if (UNIT_VTABLE(u)->release_resources) ++ return true; ++ ++ ec = unit_get_exec_context(u); ++ if (ec && ec->runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART) ++ return true; ++ ++ return false; ++} ++ + void unit_submit_to_release_resources_queue(Unit *u) { + assert(u); + +@@ -594,7 +614,7 @@ void unit_submit_to_release_resources_queue(Unit *u) { + if (u->perpetual) + return; + +- if (!UNIT_VTABLE(u)->release_resources) ++ if (!unit_can_release_resources(u)) + return; + + LIST_PREPEND(release_resources_queue, u->manager->release_resources_queue, u); +@@ -5863,8 +5883,8 @@ void unit_destroy_runtime_data(Unit *u, const ExecContext *context) { + assert(u); + assert(context); + +- if (context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO || +- (context->runtime_directory_preserve_mode == EXEC_PRESERVE_RESTART && !unit_will_restart(u))) ++ /* EXEC_PRESERVE_RESTART is handled via unit_release_resources()! */ ++ if (context->runtime_directory_preserve_mode == EXEC_PRESERVE_NO) + exec_context_destroy_runtime_directory(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]); + + exec_context_destroy_credentials(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id); +-- +2.33.0 + diff --git a/backport-core-support-overriding-NOTIFYACCESS-through-sd-noti.patch b/backport-core-support-overriding-NOTIFYACCESS-through-sd-noti.patch new file mode 100644 index 0000000..ffd8d6e --- /dev/null +++ b/backport-core-support-overriding-NOTIFYACCESS-through-sd-noti.patch @@ -0,0 +1,294 @@ +From 19dff6914dee94b36320dbfda94f60af30ac65c1 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +Date: Thu, 26 Jan 2023 17:44:03 +0800 +Subject: [PATCH] core: support overriding NOTIFYACCESS= through sd-notify + during runtime + +Closes #25963 + +Conflict:Adaptation Context. +Reference:https://github.com/systemd/systemd/commit/19dff6914dee94b36320dbfda94f60af30ac65c1 + +--- + man/org.freedesktop.systemd1.xml | 1 - + man/sd_notify.xml | 10 +++++ + src/core/dbus-service.c | 4 +- + src/core/service.c | 67 +++++++++++++++++++++++++++----- + src/core/service.h | 6 +++ + src/systemd/sd-daemon.h | 4 ++ + 6 files changed, 80 insertions(+), 12 deletions(-) + +diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml +index ec2148d..40b5223 100644 +--- a/man/org.freedesktop.systemd1.xml ++++ b/man/org.freedesktop.systemd1.xml +@@ -2301,7 +2301,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + readonly s Restart = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s PIDFile = '...'; +- @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s NotifyAccess = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly t RestartUSec = ...; +diff --git a/man/sd_notify.xml b/man/sd_notify.xml +index 69e1b02..c00889b 100644 +--- a/man/sd_notify.xml ++++ b/man/sd_notify.xml +@@ -142,6 +142,16 @@ + system check… + + ++ ++ NOTIFYACCESS=… ++ ++ Reset the access to the service status notification ++ socket during runtime, overriding NotifyAccess= setting ++ in the service unit file. See systemd.service5 ++ for details, specifically NotifyAccess= for a list of ++ accepted values. ++ ++ + + ERRNO=… + +diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c +index c301948..115c256 100644 +--- a/src/core/dbus-service.c ++++ b/src/core/dbus-service.c +@@ -29,8 +29,8 @@ + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType); + 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_emergency_action, emergency_action, EmergencyAction); ++static BUS_DEFINE_PROPERTY_GET2(property_get_notify_access, "s", Service, service_get_notify_access, notify_access_to_string); + 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); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode); +@@ -194,7 +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("Restart", "s", property_get_restart, offsetof(Service, restart), 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("NotifyAccess", "s", property_get_notify_access, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("RestartUSec", "t", bus_property_get_usec, offsetof(Service, restart_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), +diff --git a/src/core/service.c b/src/core/service.c +index 9426060..f05cac8 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -124,6 +124,8 @@ static void service_init(Unit *u) { + s->exec_context.keyring_mode = MANAGER_IS_SYSTEM(u->manager) ? + EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT; + ++ s->notify_access_override = _NOTIFY_ACCESS_INVALID; ++ + s->watchdog_original_usec = USEC_INFINITY; + + s->oom_policy = _OOM_POLICY_INVALID; +@@ -208,6 +210,15 @@ void service_release_socket_fd(Service *s) { + s->socket_peer = socket_peer_unref(s->socket_peer); + } + ++static void service_override_notify_access(Service *s, NotifyAccess notify_access_override) { ++ assert(s); ++ ++ s->notify_access_override = notify_access_override; ++ ++ log_unit_debug(UNIT(s), "notify_access=%s", notify_access_to_string(s->notify_access)); ++ log_unit_debug(UNIT(s), "notify_access_override=%s", notify_access_to_string(s->notify_access_override)); ++} ++ + static void service_stop_watchdog(Service *s) { + assert(s); + +@@ -839,7 +850,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { + prefix, yes_no(s->guess_main_pid), + prefix, service_type_to_string(s->type), + prefix, service_restart_to_string(s->restart), +- prefix, notify_access_to_string(s->notify_access), ++ prefix, notify_access_to_string(service_get_notify_access(s)), + prefix, notify_state_to_string(s->notify_state), + prefix, oom_policy_to_string(s->oom_policy)); + +@@ -1435,11 +1446,11 @@ static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) { + + if (flags & EXEC_IS_CONTROL) + /* A control process */ +- return IN_SET(s->notify_access, NOTIFY_EXEC, NOTIFY_ALL); ++ return IN_SET(service_get_notify_access(s), NOTIFY_EXEC, NOTIFY_ALL); + + /* We only spawn main processes and control processes, so any + * process that is not a control process is a main process */ +- return s->notify_access != NOTIFY_NONE; ++ return service_get_notify_access(s) != NOTIFY_NONE; + } + + static int service_spawn( +@@ -1820,6 +1831,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + /* The next restart might not be a manual stop, hence reset the flag indicating manual stops */ + s->forbid_restart = false; + ++ /* Reset NotifyAccess override */ ++ s->notify_access_override = _NOTIFY_ACCESS_INVALID; ++ + /* We want fresh tmpdirs in case service is started again immediately */ + s->exec_runtime = exec_runtime_unref(s->exec_runtime, true); + +@@ -2318,6 +2332,8 @@ static void service_enter_restart(Service *s) { + s->n_restarts ++; + s->flush_n_restarts = false; + ++ s->notify_access_override = _NOTIFY_ACCESS_INVALID; ++ + log_unit_struct(UNIT(s), LOG_INFO, + "MESSAGE_ID=" SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR, + LOG_UNIT_INVOCATION_ID(UNIT(s)), +@@ -2498,6 +2514,7 @@ static int service_start(Unit *u) { + s->status_text = mfree(s->status_text); + s->status_errno = 0; + ++ s->notify_access_override = _NOTIFY_ACCESS_INVALID; + s->notify_state = NOTIFY_UNKNOWN; + + s->watchdog_original_usec = s->watchdog_usec; +@@ -2760,6 +2777,9 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { + } + } + ++ if (s->notify_access_override >= 0) ++ (void) serialize_item(f, "notify-access-override", notify_access_to_string(s->notify_access_override)); ++ + (void) serialize_dual_timestamp(f, "watchdog-timestamp", &s->watchdog_timestamp); + (void) serialize_bool(f, "forbid-restart", s->forbid_restart); + +@@ -3028,7 +3048,15 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, + deserialize_dual_timestamp(value, &s->main_exec_status.start_timestamp); + else if (streq(key, "main-exec-status-exit")) + deserialize_dual_timestamp(value, &s->main_exec_status.exit_timestamp); +- else if (streq(key, "watchdog-timestamp")) ++ else if (streq(key, "notify-access-override")) { ++ NotifyAccess notify_access; ++ ++ notify_access = notify_access_from_string(value); ++ if (notify_access < 0) ++ log_unit_debug(u, "Failed to parse notify-access-override value: %s", value); ++ else ++ s->notify_access_override = notify_access; ++ } else if (streq(key, "watchdog-timestamp")) + deserialize_dual_timestamp(value, &s->watchdog_timestamp); + else if (streq(key, "forbid-restart")) { + int b; +@@ -3549,7 +3577,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + * has been received */ + if (f != SERVICE_SUCCESS) + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); +- else if (!s->remain_after_exit || s->notify_access == NOTIFY_MAIN) ++ else if (!s->remain_after_exit || service_get_notify_access(s) == NOTIFY_MAIN) + /* The service has never been and will never be active */ + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_PROTOCOL); + break; +@@ -4007,12 +4035,14 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void + static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) { + assert(s); + +- if (s->notify_access == NOTIFY_NONE) { ++ NotifyAccess notify_access = service_get_notify_access(s); ++ ++ if (notify_access == NOTIFY_NONE) { + log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception is disabled.", pid); + return false; + } + +- if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { ++ if (notify_access == NOTIFY_MAIN && pid != s->main_pid) { + if (s->main_pid != 0) + log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, pid, s->main_pid); + else +@@ -4021,7 +4051,7 @@ static bool service_notify_message_authorized(Service *s, pid_t pid, FDSet *fds) + return false; + } + +- if (s->notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) { ++ if (notify_access == NOTIFY_EXEC && pid != s->main_pid && pid != s->control_pid) { + if (s->main_pid != 0 && s->control_pid != 0) + log_unit_warning(UNIT(s), "Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT" and control PID "PID_FMT, + pid, s->main_pid, s->control_pid); +@@ -4063,7 +4093,7 @@ static void service_notify_message( + assert(u); + assert(ucred); + +- if (!service_notify_message_authorized(SERVICE(u), ucred->pid, fds)) ++ if (!service_notify_message_authorized(s, ucred->pid, fds)) + return; + + if (DEBUG_LOGGING) { +@@ -4168,6 +4198,25 @@ static void service_notify_message( + } + } + ++ /* Interpret NOTIFYACCESS= */ ++ e = strv_find_startswith(tags, "NOTIFYACCESS="); ++ if (e) { ++ NotifyAccess notify_access; ++ ++ notify_access = notify_access_from_string(e); ++ if (notify_access < 0) ++ log_unit_warning_errno(u, notify_access, ++ "Failed to parse NOTIFYACCESS= field value '%s' in notification message, ignoring: %m", e); ++ ++ /* We don't need to check whether the new access mode is more strict than what is ++ * already in use, since only the privileged process is allowed to change it ++ * in the first place. */ ++ if (service_get_notify_access(s) != notify_access) { ++ service_override_notify_access(s, notify_access); ++ notify_dbus = true; ++ } ++ } ++ + /* Interpret ERRNO= */ + e = strv_find_startswith(tags, "ERRNO="); + if (e) { +diff --git a/src/core/service.h b/src/core/service.h +index 4a4ab8a..a65ade3 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -184,6 +184,7 @@ struct Service { + PathSpec *pid_file_pathspec; + + NotifyAccess notify_access; ++ NotifyAccess notify_access_override; + NotifyState notify_state; + + sd_bus_slot *bus_name_pid_lookup_slot; +@@ -213,6 +214,11 @@ static inline usec_t service_timeout_abort_usec(Service *s) { + return s->timeout_abort_set ? s->timeout_abort_usec : s->timeout_stop_usec; + } + ++static inline NotifyAccess service_get_notify_access(Service *s) { ++ assert(s); ++ return s->notify_access_override < 0 ? s->notify_access : s->notify_access_override; ++} ++ + static inline usec_t service_get_watchdog_usec(Service *s) { + assert(s); + return s->watchdog_override_enable ? s->watchdog_override_usec : s->watchdog_original_usec; +diff --git a/src/systemd/sd-daemon.h b/src/systemd/sd-daemon.h +index f42a5d8..a93ce9c 100644 +--- a/src/systemd/sd-daemon.h ++++ b/src/systemd/sd-daemon.h +@@ -195,6 +195,10 @@ int sd_is_mq(int fd, const char *path); + readable error message. Example: "STATUS=Completed + 66% of file system check..." + ++ NOTIFYACCESS=... ++ Reset the access to the service status notification socket. ++ Example: "NOTIFYACCESS=main" ++ + ERRNO=... If a daemon fails, the errno-style error code, + formatted as string. Example: "ERRNO=2" for ENOENT. + +-- +2.33.0 + diff --git a/backport-fd-util-introduce-dir_fd_is_root_or_cwd.patch b/backport-fd-util-introduce-dir_fd_is_root_or_cwd.patch new file mode 100644 index 0000000..22daf99 --- /dev/null +++ b/backport-fd-util-introduce-dir_fd_is_root_or_cwd.patch @@ -0,0 +1,27 @@ +From e212f422796da9e626030289faf083407c8955df Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Sun, 2 Apr 2023 01:25:46 +0900 +Subject: [PATCH] fd-util: introduce dir_fd_is_root_or_cwd() + +Conflict:Import only fcntl.h to prevent dependency failures. +Reference:https://github.com/systemd/systemd/commit/e212f422796da9e626030289faf083407c8955df + +--- + src/basic/fd-util.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h +index 9529a47..a08907e 100644 +--- a/src/basic/fd-util.h ++++ b/src/basic/fd-util.h +@@ -2,6 +2,7 @@ + #pragma once + + #include ++#include + #include + #include + #include +-- +2.33.0 + diff --git a/backport-fdset-add-new-fdset_consume-helper.patch b/backport-fdset-add-new-fdset_consume-helper.patch new file mode 100644 index 0000000..c45caef --- /dev/null +++ b/backport-fdset-add-new-fdset_consume-helper.patch @@ -0,0 +1,52 @@ +From e829f28c1bc6e6865261bfb3bc26089f50e0c7bd Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Mar 2023 18:52:25 +0200 +Subject: [PATCH] fdset: add new fdset_consume() helper + +Conflict:Context Adaptation. Delete the code that is not involved. +Reference:https://github.com/systemd/systemd/commit/e829f28c1bc6e6865261bfb3bc26089f50e0c7bd + +--- + src/shared/fdset.c | 13 +++++++++++++ + src/shared/fdset.h | 1 + + 2 files changed, 14 insertions(+) + +diff --git a/src/shared/fdset.c b/src/shared/fdset.c +index 443aa7f..68a465f 100644 +--- a/src/shared/fdset.c ++++ b/src/shared/fdset.c +@@ -81,6 +81,19 @@ int fdset_put(FDSet *s, int fd) { + return set_put(MAKE_SET(s), FD_TO_PTR(fd)); + } + ++int fdset_consume(FDSet *s, int fd) { ++ int r; ++ ++ assert(s); ++ assert(fd >= 0); ++ ++ r = fdset_put(s, fd); ++ if (r < 0) ++ safe_close(fd); ++ ++ return r; ++} ++ + int fdset_put_dup(FDSet *s, int fd) { + int copy, r; + +diff --git a/src/shared/fdset.h b/src/shared/fdset.h +index e8a6b48..ac5ec76 100644 +--- a/src/shared/fdset.h ++++ b/src/shared/fdset.h +@@ -13,6 +13,7 @@ FDSet* fdset_new(void); + FDSet* fdset_free(FDSet *s); + + int fdset_put(FDSet *s, int fd); ++int fdset_consume(FDSet *s, int fd); + int fdset_put_dup(FDSet *s, int fd); + + bool fdset_contains(FDSet *s, int fd); +-- +2.33.0 + diff --git a/backport-fdset-add-new-helper-to-convert-an-fdset-to-an-array.patch b/backport-fdset-add-new-helper-to-convert-an-fdset-to-an-array.patch new file mode 100644 index 0000000..b78471b --- /dev/null +++ b/backport-fdset-add-new-helper-to-convert-an-fdset-to-an-array.patch @@ -0,0 +1,86 @@ +From bdcad22e8e508308032cba5f8c2d5c093adb7d87 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 28 Mar 2023 11:17:23 +0200 +Subject: [PATCH] fdset: add new helper to convert an fdset to an array + +Conflict:fdset_close_others removes different code, but the end result is the same. +Reference:https://github.com/systemd/systemd/commit/bdcad22e8e508308032cba5f8c2d5c093adb7d87 + +--- + src/shared/fdset.c | 39 ++++++++++++++++++++++++++++++--------- + src/shared/fdset.h | 2 ++ + 2 files changed, 32 insertions(+), 9 deletions(-) + +diff --git a/src/shared/fdset.c b/src/shared/fdset.c +index 443aa7f..cb7a340 100644 +--- a/src/shared/fdset.c ++++ b/src/shared/fdset.c +@@ -236,22 +236,43 @@ fail: + return r; + } + +-int fdset_close_others(FDSet *fds) { ++int fdset_to_array(FDSet *fds, int **ret) { ++ unsigned j = 0, m; + void *e; +- int *a = NULL; +- size_t j = 0, m; ++ int *a; + +- m = fdset_size(fds); ++ assert(ret); + +- if (m > 0) { +- a = newa(int, m); +- SET_FOREACH(e, MAKE_SET(fds)) +- a[j++] = PTR_TO_FD(e); ++ m = fdset_size(fds); ++ if (m > INT_MAX) /* We want to be able to return an "int" */ ++ return -ENOMEM; ++ if (m == 0) { ++ *ret = NULL; /* suppress array allocation if empty */ ++ return 0; + } + ++ a = new(int, m); ++ if (!a) ++ return -ENOMEM; ++ ++ SET_FOREACH(e, MAKE_SET(fds)) ++ a[j++] = PTR_TO_FD(e); ++ + assert(j == m); + +- return close_all_fds(a, j); ++ *ret = TAKE_PTR(a); ++ return (int) m; ++} ++ ++int fdset_close_others(FDSet *fds) { ++ _cleanup_free_ int *a = NULL; ++ int n; ++ ++ n = fdset_to_array(fds, &a); ++ if (n < 0) ++ return n; ++ ++ return close_all_fds(a, n); + } + + unsigned fdset_size(FDSet *fds) { +diff --git a/src/shared/fdset.h b/src/shared/fdset.h +index e8a6b48..5f4304e 100644 +--- a/src/shared/fdset.h ++++ b/src/shared/fdset.h +@@ -24,6 +24,8 @@ int fdset_new_listen_fds(FDSet **ret, bool unset); + + int fdset_cloexec(FDSet *fds, bool b); + ++int fdset_to_array(FDSet *fds, int **ret); ++ + int fdset_close_others(FDSet *fds); + + unsigned fdset_size(FDSet *fds); +-- +2.33.0 + diff --git a/backport-meson-exclude-.gitattributes-when-using-install_subd.patch b/backport-meson-exclude-.gitattributes-when-using-install_subd.patch new file mode 100644 index 0000000..4d22868 --- /dev/null +++ b/backport-meson-exclude-.gitattributes-when-using-install_subd.patch @@ -0,0 +1,95 @@ +From 082c67616511c7c08627eaf19af7e59a7f360479 Mon Sep 17 00:00:00 2001 +From: Luca Boccassi +Date: Thu, 9 Dec 2021 22:16:19 +0000 +Subject: [PATCH] meson: exclude .gitattributes when using install_subdir + +It picks the whole content of the directory by default, but we don't +want to install .gitattributes files. Add it to all invocations, not +just the ones on subdirs with .gitattributes, so that we don't regress +in the future. + +Fixes #21715 + +Conflict:Adaptation Context. +Reference:https://github.com/systemd/systemd/commit/082c67616511c7c08627eaf19af7e59a7f360479 + +--- + test/meson.build | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/test/meson.build b/test/meson.build +index 6f8f257..8727e66 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -4,36 +4,52 @@ if install_tests + testdata_dir = testsdir + '/testdata/' + + install_subdir('journal-data', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('test-execute', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('test-path', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('test-path-util', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('test-umount', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('test-network-generator-conversion', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-04.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-06.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-10.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-11.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-16.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-28.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-30.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-52.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + install_subdir('testsuite-63.units', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + + testsuite08_dir = testdata_dir + '/testsuite-08.units' +@@ -50,6 +66,7 @@ if install_tests + + if conf.get('ENABLE_RESOLVE') == 1 + install_subdir('test-resolve', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + endif + +@@ -72,6 +89,7 @@ if install_tests and conf.get('ENABLE_SYSUSERS') == 1 + install_data(test_sysusers_sh, + install_dir : testsdir) + install_subdir('test-sysusers', ++ exclude_files : '.gitattributes', + install_dir : testdata_dir) + endif + +-- +2.33.0 + diff --git a/backport-notify-add-new-exec-switch-for-chaining-other-comman.patch b/backport-notify-add-new-exec-switch-for-chaining-other-comman.patch new file mode 100644 index 0000000..1c02866 --- /dev/null +++ b/backport-notify-add-new-exec-switch-for-chaining-other-comman.patch @@ -0,0 +1,150 @@ +From 9175338e09de56c99cebf0b9f215f3e9e0016cb9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 16 Feb 2023 15:42:49 +0100 +Subject: [PATCH] notify: add new --exec switch for chaining other commands to + systemd-notify + +This is useful in tests, so that we can first send a READY message and +then continue doing something else without changing PID. + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/9175338e09de56c99cebf0b9f215f3e9e0016cb9 + +--- + src/notify/notify.c | 59 ++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 56 insertions(+), 3 deletions(-) + +diff --git a/src/notify/notify.c b/src/notify/notify.c +index b1348383f5..7320ffebde 100644 +--- a/src/notify/notify.c ++++ b/src/notify/notify.c +@@ -31,6 +31,11 @@ static bool arg_booted = false; + static uid_t arg_uid = UID_INVALID; + static gid_t arg_gid = GID_INVALID; + static bool arg_no_block = false; ++static char **arg_env = NULL; ++static char **arg_exec = NULL; ++ ++STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep); ++STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep); + + static int help(void) { + _cleanup_free_ char *link = NULL; +@@ -41,6 +46,7 @@ static int help(void) { + return log_oom(); + + printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n" ++ "%s [OPTIONS...] --exec [VARIABLE=VALUE...] ; CMDLINE...\n" + "\n%sNotify the init system about service status updates.%s\n\n" + " -h --help Show this help\n" + " --version Show package version\n" +@@ -53,8 +59,10 @@ static int help(void) { + " --status=TEXT Set status text\n" + " --booted Check if the system was booted up with systemd\n" + " --no-block Do not wait until operation finished\n" ++ " --exec Execute command line separated by ';' once done\n" + "\nSee the %s for details.\n", + program_invocation_short_name, ++ program_invocation_short_name, + ansi_highlight(), + ansi_normal(), + link); +@@ -93,7 +101,8 @@ static int parse_argv(int argc, char *argv[]) { + ARG_STATUS, + ARG_BOOTED, + ARG_UID, +- ARG_NO_BLOCK ++ ARG_NO_BLOCK, ++ ARG_EXEC, + }; + + static const struct option options[] = { +@@ -107,10 +116,12 @@ static int parse_argv(int argc, char *argv[]) { + { "booted", no_argument, NULL, ARG_BOOTED }, + { "uid", required_argument, NULL, ARG_UID }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, ++ { "exec", no_argument, NULL, ARG_EXEC }, + {} + }; + +- int c, r; ++ bool do_exec = false; ++ int c, r, n_env; + + assert(argc >= 0); + assert(argv); +@@ -183,6 +194,10 @@ static int parse_argv(int argc, char *argv[]) { + arg_no_block = true; + break; + ++ case ARG_EXEC: ++ do_exec = true; ++ break; ++ + case '?': + return -EINVAL; + +@@ -202,6 +217,32 @@ static int parse_argv(int argc, char *argv[]) { + return -EINVAL; + } + ++ if (do_exec) { ++ int i; ++ ++ for (i = optind; i < argc; i++) ++ if (streq(argv[i], ";")) ++ break; ++ ++ if (i >= argc) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "If --exec is used argument list must contain ';' separator, refusing."); ++ if (i+1 == argc) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty command line specified after ';' separator, refusing"); ++ ++ arg_exec = strv_copy_n(argv + i + 1, argc - i - 1); ++ if (!arg_exec) ++ return log_oom(); ++ ++ n_env = i - optind; ++ } else ++ n_env = argc - optind; ++ ++ if (n_env > 0) { ++ arg_env = strv_copy_n(argv + optind, n_env); ++ if (!arg_env) ++ return log_oom(); ++ } ++ + return 1; + } + +@@ -263,7 +304,7 @@ static int run(int argc, char* argv[]) { + + our_env[i++] = NULL; + +- final_env = strv_env_merge(our_env, argv + optind); ++ final_env = strv_env_merge(our_env, arg_env); + if (!final_env) + return log_oom(); + +@@ -313,6 +354,18 @@ static int run(int argc, char* argv[]) { + "No status data could be sent: $NOTIFY_SOCKET was not set"); + } + ++ if (arg_exec) { ++ _cleanup_free_ char *cmdline = NULL; ++ ++ execvp(arg_exec[0], arg_exec); ++ ++ cmdline = strv_join(arg_exec, " "); ++ if (!cmdline) ++ return log_oom(); ++ ++ return log_error_errno(errno, "Failed to execute command line: %s", cmdline); ++ } ++ + return 0; + } + +-- +2.33.0 + diff --git a/backport-notify-add-support-for-sending-fds-with-notification.patch b/backport-notify-add-support-for-sending-fds-with-notification.patch new file mode 100644 index 0000000..d7d8b38 --- /dev/null +++ b/backport-notify-add-support-for-sending-fds-with-notification.patch @@ -0,0 +1,258 @@ +From 6e4a324574d5f1b2296799324dbdb54326078233 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 28 Mar 2023 11:17:44 +0200 +Subject: [PATCH] notify: add support for sending fds with notification + messages + +This exposes the fd passing we support via sd_pid_notify_with_fds() also +via the command line tool systemd-notify. + +Conflict:Context Adaptation. fdset_new_fill adaptation. +Reference:https://github.com/systemd/systemd/commit/6e4a324574d5f1b2296799324dbdb54326078233 + +--- + man/systemd-notify.xml | 23 +++++++++ + src/notify/notify.c | 109 +++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 128 insertions(+), 4 deletions(-) + +diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml +index 3fed92e..586d09e 100644 +--- a/man/systemd-notify.xml ++++ b/man/systemd-notify.xml +@@ -143,6 +143,29 @@ + this option set is prone to race conditions in all other cases. + + ++ ++ ++ ++ Send a file descriptor along with the notification message. This is useful when ++ invoked in services that have the FileDescriptorStoreMax= setting enabled, see ++ systemd.service5 ++ for details. The specified file descriptor must be passed to systemd-notify when ++ invoked. This option may be used multiple times to pass multiple file descriptors in a single ++ notification message. ++ ++ To use this functionality from a bash shell, use an expression like the following: ++ systemd-notify --fd=4 --fd=5 4</some/file 5</some/other/file ++ ++ ++ ++ ++ ++ Set a name to assign to the file descriptors passed via (see ++ above). This controls the FDNAME= field. This setting may only be specified once, ++ and applies to all file descriptors passed. Invoke this tool multiple times in case multiple file ++ descriptors with different file descriptor names shall be submitted. ++ ++ + + + +diff --git a/src/notify/notify.c b/src/notify/notify.c +index 5afea7f..ad0ae89 100644 +--- a/src/notify/notify.c ++++ b/src/notify/notify.c +@@ -10,6 +10,8 @@ + + #include "alloc-util.h" + #include "env-util.h" ++#include "fd-util.h" ++#include "fdset.h" + #include "format-util.h" + #include "log.h" + #include "main-func.h" +@@ -31,9 +33,13 @@ static gid_t arg_gid = GID_INVALID; + static bool arg_no_block = false; + static char **arg_env = NULL; + static char **arg_exec = NULL; ++static FDSet *arg_fds = NULL; ++static char *arg_fdname = NULL; + + STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep); + STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep); ++STATIC_DESTRUCTOR_REGISTER(arg_fds, fdset_freep); ++STATIC_DESTRUCTOR_REGISTER(arg_fdname, freep); + + static int help(void) { + _cleanup_free_ char *link = NULL; +@@ -55,6 +61,8 @@ static int help(void) { + " --booted Check if the system was booted up with systemd\n" + " --no-block Do not wait until operation finished\n" + " --exec Execute command line separated by ';' once done\n" ++ " --fd=FD Pass specified file descriptor with along with message\n" ++ " --fdname=NAME Name to assign to passed file descriptor(s)\n" + "\nSee the %s for details.\n", + program_invocation_short_name, + program_invocation_short_name, +@@ -96,6 +104,8 @@ static int parse_argv(int argc, char *argv[]) { + ARG_UID, + ARG_NO_BLOCK, + ARG_EXEC, ++ ARG_FD, ++ ARG_FDNAME, + }; + + static const struct option options[] = { +@@ -108,9 +118,12 @@ static int parse_argv(int argc, char *argv[]) { + { "uid", required_argument, NULL, ARG_UID }, + { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "exec", no_argument, NULL, ARG_EXEC }, ++ { "fd", required_argument, NULL, ARG_FD }, ++ { "fdname", required_argument, NULL, ARG_FDNAME }, + {} + }; + ++ _cleanup_(fdset_freep) FDSet *passed = NULL; + bool do_exec = false; + int c, r, n_env; + +@@ -181,6 +194,60 @@ static int parse_argv(int argc, char *argv[]) { + do_exec = true; + break; + ++ case ARG_FD: { ++ _cleanup_close_ int owned_fd = -EBADF; ++ int fdnr; ++ ++ r = safe_atoi(optarg, &fdnr); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse file descriptor: %s", optarg); ++ if (fdnr < 0) ++ return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "File descriptor can't be negative: %i", fdnr); ++ ++ if (!passed) { ++ /* Take possession of all passed fds */ ++ r = fdset_new_fill(/* filter_cloexec= */ 0, &passed); ++ if (r < 0) ++ return log_error_errno(r, "Failed to take possession of passed file descriptors: %m"); ++ ++ r = fdset_cloexec(passed, true); ++ if (r < 0) ++ return log_error_errno(r, "Failed to enable O_CLOEXEC for passed file descriptors: %m"); ++ } ++ ++ if (fdnr < 3) { ++ /* For stdin/stdout/stderr we want to keep the fd, too, hence make a copy */ ++ owned_fd = fcntl(fdnr, F_DUPFD_CLOEXEC, 3); ++ if (owned_fd < 0) ++ return log_error_errno(errno, "Failed to duplicate file descriptor: %m"); ++ } else { ++ /* Otherwise, move the fd over */ ++ owned_fd = fdset_remove(passed, fdnr); ++ if (owned_fd < 0) ++ return log_error_errno(owned_fd, "Specified file descriptor '%i' not passed or specified more than once: %m", fdnr); ++ } ++ ++ if (!arg_fds) { ++ arg_fds = fdset_new(); ++ if (!arg_fds) ++ return log_oom(); ++ } ++ ++ r = fdset_consume(arg_fds, TAKE_FD(owned_fd)); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add file descriptor to set: %m"); ++ break; ++ } ++ ++ case ARG_FDNAME: ++ if (!fdname_is_valid(optarg)) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File descriptor name invalid: %s", optarg); ++ ++ if (free_and_strdup(&arg_fdname, optarg) < 0) ++ return log_oom(); ++ ++ break; ++ + case '?': + return -EINVAL; + +@@ -193,11 +260,15 @@ static int parse_argv(int argc, char *argv[]) { + !arg_ready && + !arg_status && + !arg_pid && +- !arg_booted) { ++ !arg_booted && ++ fdset_isempty(arg_fds)) { + help(); + return -EINVAL; + } + ++ if (arg_fdname && fdset_isempty(arg_fds)) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No file descriptors passed, but --fdname= set, refusing."); ++ + if (do_exec) { + int i; + +@@ -224,13 +295,16 @@ static int parse_argv(int argc, char *argv[]) { + return log_oom(); + } + ++ if (!fdset_isempty(passed)) ++ log_warning("Warning: %u more file descriptors passed than referenced with --fd=.", fdset_size(passed)); ++ + return 1; + } + + static int run(int argc, char* argv[]) { +- _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL; ++ _cleanup_free_ char *status = NULL, *cpid = NULL, *n = NULL, *fdn = NULL; + _cleanup_strv_free_ char **final_env = NULL; +- char* our_env[4]; ++ char* our_env[6]; + unsigned i = 0; + pid_t source_pid; + int r; +@@ -271,6 +345,18 @@ static int run(int argc, char* argv[]) { + our_env[i++] = cpid; + } + ++ if (!fdset_isempty(arg_fds)) { ++ our_env[i++] = (char*) "FDSTORE=1"; ++ ++ if (arg_fdname) { ++ fdn = strjoin("FDNAME=", arg_fdname); ++ if (!fdn) ++ return log_oom(); ++ ++ our_env[i++] = fdn; ++ } ++ } ++ + our_env[i++] = NULL; + + final_env = strv_env_merge(our_env, arg_env); +@@ -307,13 +393,28 @@ static int run(int argc, char* argv[]) { + * or the service manager itself */ + source_pid = 0; + } +- r = sd_pid_notify(source_pid, false, n); ++ ++ if (fdset_isempty(arg_fds)) ++ r = sd_pid_notify(source_pid, /* unset_environment= */ false, n); ++ else { ++ _cleanup_free_ int *a = NULL; ++ int k; ++ ++ k = fdset_to_array(arg_fds, &a); ++ if (k < 0) ++ return log_error_errno(k, "Failed to convert file descriptor set to array: %m"); ++ ++ r = sd_pid_notify_with_fds(source_pid, /* unset_environment= */ false, n, a, k); ++ ++ } + if (r < 0) + return log_error_errno(r, "Failed to notify init system: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "No status data could be sent: $NOTIFY_SOCKET was not set"); + ++ arg_fds = fdset_free(arg_fds); /* Close before we execute anything */ ++ + if (!arg_no_block) { + r = sd_notify_barrier(0, 5 * USEC_PER_SEC); + if (r < 0) +-- +2.33.0 + diff --git a/backport-pid1-add-some-debug-logging-when-stashing-ds-into-th.patch b/backport-pid1-add-some-debug-logging-when-stashing-ds-into-th.patch new file mode 100644 index 0000000..325d7f8 --- /dev/null +++ b/backport-pid1-add-some-debug-logging-when-stashing-ds-into-th.patch @@ -0,0 +1,54 @@ +From e8783d7620e4811738be078480aa5ffc9bbdcf9b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 12 Apr 2023 21:07:29 +0200 +Subject: [PATCH] pid1: add some debug logging when stashing ds into the + fdstore + +Conflict:Context Adaptation. Log Adaptation. +Reference:https://github.com/systemd/systemd/commit/e8783d7620e4811738be078480aa5ffc9bbdcf9b + +--- + src/core/service.c | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index a7d5f3f..103c8a5 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -427,6 +427,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us + } + + static int service_add_fd_store(Service *s, int fd, const char *name, bool do_poll) { ++ struct stat st; + ServiceFDStore *fs; + int r; + +@@ -435,16 +436,22 @@ static int service_add_fd_store(Service *s, int fd, const char *name, bool do_po + assert(s); + assert(fd >= 0); + ++ if (fstat(fd, &st) < 0) ++ return -errno; ++ ++ log_unit_debug(UNIT(s), "Trying to stash fd for dev=" "%u:%u" "/inode=%" PRIu64, major(st.st_dev), minor(st.st_dev), (uint64_t) st.st_ino); ++ + if (s->n_fd_store >= s->n_fd_store_max) +- return -EXFULL; /* Our store is full. +- * Use this errno rather than E[NM]FILE to distinguish from +- * the case where systemd itself hits the file limit. */ ++ /* Our store is full. Use this errno rather than E[NM]FILE to distinguish from the case ++ * where systemd itself hits the file limit. */ ++ return log_unit_debug_errno(UNIT(s), SYNTHETIC_ERRNO(EXFULL), "Hit fd store limit."); + + LIST_FOREACH(fd_store, fs, s->fd_store) { + r = same_fd(fs->fd, fd); + if (r < 0) + return r; + if (r > 0) { ++ log_unit_debug(UNIT(s), "Suppressing duplicate fd in fd store."); + asynchronous_close(fd); + return 0; /* fd already included */ + } +-- +2.33.0 + diff --git a/backport-service-add-ability-to-pin-fd-store.patch b/backport-service-add-ability-to-pin-fd-store.patch new file mode 100644 index 0000000..86cc1ff --- /dev/null +++ b/backport-service-add-ability-to-pin-fd-store.patch @@ -0,0 +1,476 @@ +From b9c1883a9cd9b5126fe648f3e198143dc19a222d Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Mar 2023 22:07:22 +0200 +Subject: [PATCH] service: add ability to pin fd store +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Oftentimes it is useful to allow the per-service fd store to survive +longer than for a restart. This is useful in various scenarios: + +1. An fd to some security relevant object needs to be stashed somewhere, + that should not be cleaned automatically, because the security + enforcement would be dropped then. + +2. A user namespace fd should be allocated on first invocation and be + kept around until the user logs out (i.e. systemd --user ends), á la + #16328 (This does not implement what #16318 asks for, but should + solve the use-case discussed there.) + +3. There's interest in allow a concept of "userspace reboots" where the + kernel stays running, and userspace is swapped out (i.e. all services + exit, and the rootfs transitioned into a new version of it) while + keeping some select resources pinned, very similar to how we + implement a switch root. Thus it is useful to allow services to exit, + while leaving their fds around till the very end. + +This is exposed through a new FileDescriptorStorePreserve= setting that +is closely modelled after RuntimeDirectoryPreserve= (in fact it reused +the same internal type), since we want similar behaviour in the end, and +quite often they probably want to be used together. + +Conflict:Adaptation Context.The FileDescriptorStorePreserve= field is added to the directives.service file to prevent test case failures. +Reference:https://github.com/systemd/systemd/commit/b9c1883a9cd9b5126fe648f3e198143dc19a222d + +--- + man/org.freedesktop.systemd1.xml | 6 +++ + man/systemd.service.xml | 21 +++++++++- + src/basic/unit-def.c | 1 + + src/basic/unit-def.h | 1 + + src/core/dbus-execute.c | 8 ++-- + src/core/dbus-execute.h | 2 + + src/core/dbus-service.c | 4 ++ + src/core/load-fragment-gperf.gperf.in | 3 +- + src/core/load-fragment.c | 2 +- + src/core/load-fragment.h | 2 +- + src/core/service.c | 43 ++++++++++++++++----- + src/core/service.h | 1 + + src/shared/bus-unit-util.c | 3 +- + test/fuzz/fuzz-unit-file/directives.service | 1 + + 14 files changed, 78 insertions(+), 20 deletions(-) + +diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml +index 74a9202..ec2148d 100644 +--- a/man/org.freedesktop.systemd1.xml ++++ b/man/org.freedesktop.systemd1.xml +@@ -2343,6 +2343,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + readonly u FileDescriptorStoreMax = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly u NFileDescriptorStore = ...; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly s FileDescriptorStorePreserve = '...'; + readonly s StatusText = '...'; + readonly i StatusErrno = ...; + readonly s Result = '...'; +@@ -2898,6 +2900,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + + ++ ++ + + + +@@ -3430,6 +3434,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + + ++ ++ + + + +diff --git a/man/systemd.service.xml b/man/systemd.service.xml +index 350bc5f..ed9b410 100644 +--- a/man/systemd.service.xml ++++ b/man/systemd.service.xml +@@ -1050,7 +1050,7 @@ + FDSTORE=1 messages. This is useful for implementing services that can restart + after an explicit request or a crash without losing state. Any open sockets and other file + descriptors which should not be closed during the restart may be stored this way. Application state +- can either be serialized to a file in /run/, or better, stored in a ++ can either be serialized to a file in RuntimeDirectory=, or stored in a + memfd_create2 + memory file descriptor. Defaults to 0, i.e. no file descriptors may be stored in the service + manager. All file descriptors passed to the service manager from a specific service are passed back +@@ -1059,12 +1059,29 @@ + details about the precise protocol used and the order in which the file descriptors are passed). Any + file descriptors passed to the service manager are automatically closed when + POLLHUP or POLLERR is seen on them, or when the service is +- fully stopped and no job is queued or being executed for it. If this option is used, ++ fully stopped and no job is queued or being executed for it (the latter can be tweaked with ++ FileDescriptorStorePreserve=, see below). If this option is used, + NotifyAccess= (see above) should be set to open access to the notification socket + provided by systemd. If NotifyAccess= is not set, it will be implicitly set to + . + + ++ ++ FileDescriptorStorePreserve= ++ Takes one of no, yes, ++ restart and controls when to release the service's file descriptor store ++ (i.e. when to close the contained file descriptors, if any). If set to no the ++ file descriptor store is automatically released when the service is stopped; if ++ restart (the default) it is kept around as long as the unit is neither inactive ++ nor failed, or a job is queued for the service, or the service is expected to be restarted. If ++ yes the file descriptor store is kept around until the unit is removed from ++ memory (i.e. is not referenced anymore and inactive). The latter is useful to keep entries in the ++ file descriptor store pinned until the service manage exits. ++ ++ Use systemctl clean --what=fdstore … to release the file descriptor store ++ explicitly. ++ ++ + + USBFunctionDescriptors= + Configure the location of a file containing +diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c +index 2667e61..09b2747 100644 +--- a/src/basic/unit-def.c ++++ b/src/basic/unit-def.c +@@ -196,6 +196,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = { + [SERVICE_FINAL_SIGTERM] = "final-sigterm", + [SERVICE_FINAL_SIGKILL] = "final-sigkill", + [SERVICE_FAILED] = "failed", ++ [SERVICE_DEAD_RESOURCES_PINNED] = "dead-resources-pinned", + [SERVICE_AUTO_RESTART] = "auto-restart", + [SERVICE_CLEANING] = "cleaning", + }; +diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h +index 08651ef..c9e41bd 100644 +--- a/src/basic/unit-def.h ++++ b/src/basic/unit-def.h +@@ -141,6 +141,7 @@ 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_RESOURCES_PINNED, /* Like SERVICE_DEAD, but with pinned resources */ + SERVICE_AUTO_RESTART, + SERVICE_CLEANING, + _SERVICE_STATE_MAX, +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index d8931c1..97277ca 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -46,7 +46,7 @@ + BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); +-static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode); ++BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_protect_proc, protect_proc, ProtectProc); + static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_proc_subset, proc_subset, ProcSubset); +@@ -1181,7 +1181,7 @@ const sd_bus_vtable bus_exec_vtable[] = { + SD_BUS_PROPERTY("Personality", "s", property_get_personality, offsetof(ExecContext, personality), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("LockPersonality", "b", bus_property_get_bool, offsetof(ExecContext, lock_personality), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestrictAddressFamilies", "(bas)", property_get_address_families, 0, SD_BUS_VTABLE_PROPERTY_CONST), +- SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("RuntimeDirectoryPreserve", "s", bus_property_get_exec_preserve_mode, offsetof(ExecContext, runtime_directory_preserve_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_RUNTIME].paths), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StateDirectoryMode", "u", bus_property_get_mode, offsetof(ExecContext, directories[EXEC_DIRECTORY_STATE].mode), SD_BUS_VTABLE_PROPERTY_CONST), +@@ -1551,7 +1551,7 @@ static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_home, ProtectHome, protect_home_fr + static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyring_mode_from_string); + static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_proc, ProtectProc, protect_proc_from_string); + static BUS_DEFINE_SET_TRANSIENT_PARSE(proc_subset, ProcSubset, proc_subset_from_string); +-static BUS_DEFINE_SET_TRANSIENT_PARSE(preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string); ++BUS_DEFINE_SET_TRANSIENT_PARSE(exec_preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string); + static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(personality, unsigned long, parse_personality); + static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(secure_bits, "i", int32_t, int, "%" PRIi32, secure_bits_to_string_alloc_with_check); + static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint64_t, "%" PRIu64, capability_set_to_string_alloc); +@@ -1842,7 +1842,7 @@ int bus_exec_context_set_transient_property( + return bus_set_transient_proc_subset(u, name, &c->proc_subset, message, flags, error); + + if (streq(name, "RuntimeDirectoryPreserve")) +- return bus_set_transient_preserve_mode(u, name, &c->runtime_directory_preserve_mode, message, flags, error); ++ return bus_set_transient_exec_preserve_mode(u, name, &c->runtime_directory_preserve_mode, message, flags, error); + + if (streq(name, "UMask")) + return bus_set_transient_mode_t(u, name, &c->umask, message, flags, error); +diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h +index c538341..5926bdb 100644 +--- a/src/core/dbus-execute.h ++++ b/src/core/dbus-execute.h +@@ -28,6 +28,8 @@ int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *inte + int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); + int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); + int bus_property_get_exec_ex_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); ++int bus_property_get_exec_preserve_mode(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); + + int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); + int bus_set_transient_exec_command(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); ++int bus_set_transient_exec_preserve_mode(Unit *u, const char *name, ExecPreserveMode *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c +index c0ec277..4b05968 100644 +--- a/src/core/dbus-service.c ++++ b/src/core/dbus-service.c +@@ -240,6 +240,7 @@ const sd_bus_vtable bus_service_vtable[] = { + SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NFileDescriptorStore", "u", property_get_size_as_uint32, offsetof(Service, n_fd_store), 0), ++ SD_BUS_PROPERTY("FileDescriptorStorePreserve", "s", bus_property_get_exec_preserve_mode, offsetof(Service, fd_store_preserve_mode), 0), + SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), +@@ -477,6 +478,9 @@ static int bus_service_set_transient_property( + if (streq(name, "FileDescriptorStoreMax")) + return bus_set_transient_unsigned(u, name, &s->n_fd_store_max, message, flags, error); + ++ if (streq(name, "FileDescriptorStorePreserve")) ++ return bus_set_transient_exec_preserve_mode(u, name, &s->fd_store_preserve_mode, message, flags, error); ++ + if (streq(name, "NotifyAccess")) + return bus_set_transient_notify_access(u, name, &s->notify_access, message, flags, error); + +diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in +index 42441ea..49e036f 100644 +--- a/src/core/load-fragment-gperf.gperf.in ++++ b/src/core/load-fragment-gperf.gperf.in +@@ -127,7 +127,7 @@ + {{type}}.MountFlags, config_parse_exec_mount_flags, 0, offsetof({{type}}, exec_context.mount_flags) + {{type}}.MountAPIVFS, config_parse_exec_mount_apivfs, 0, offsetof({{type}}, exec_context) + {{type}}.Personality, config_parse_personality, 0, offsetof({{type}}, exec_context.personality) +-{{type}}.RuntimeDirectoryPreserve, config_parse_runtime_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode) ++{{type}}.RuntimeDirectoryPreserve, config_parse_exec_preserve_mode, 0, offsetof({{type}}, exec_context.runtime_directory_preserve_mode) + {{type}}.RuntimeDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].mode) + {{type}}.RuntimeDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_RUNTIME].paths) + {{type}}.StateDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_STATE].mode) +@@ -395,6 +395,7 @@ Service.SysVStartPriority, config_parse_warn_compat, + Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking) + Service.BusName, config_parse_bus_name, 0, offsetof(Service, bus_name) + Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max) ++Service.FileDescriptorStorePreserve, config_parse_exec_preserve_mode, 0, offsetof(Service, fd_store_preserve_mode) + Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) + Service.Sockets, config_parse_service_sockets, 0, 0 + Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0 +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index f357408..7e5f919 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -133,7 +133,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to pa + DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_home, protect_home, ProtectHome, "Failed to parse protect home value"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_protect_system, protect_system, ProtectSystem, "Failed to parse protect system value"); +-DEFINE_CONFIG_PARSE_ENUM(config_parse_runtime_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse runtime directory preserve mode"); ++DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_preserve_mode, exec_preserve_mode, ExecPreserveMode, "Failed to parse resource 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_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_service_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode, "Failed to parse timeout failure mode"); +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index 45e9c39..974bb42 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -93,7 +93,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_selinux_context); + CONFIG_PARSER_PROTOTYPE(config_parse_exec_apparmor_profile); + CONFIG_PARSER_PROTOTYPE(config_parse_exec_smack_process_label); + CONFIG_PARSER_PROTOTYPE(config_parse_address_families); +-CONFIG_PARSER_PROTOTYPE(config_parse_runtime_preserve_mode); ++CONFIG_PARSER_PROTOTYPE(config_parse_exec_preserve_mode); + CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories); + CONFIG_PARSER_PROTOTYPE(config_parse_set_credential); + CONFIG_PARSER_PROTOTYPE(config_parse_load_credential); +diff --git a/src/core/service.c b/src/core/service.c +index af6360e..9f16790 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -60,6 +60,7 @@ 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_RESOURCES_PINNED] = UNIT_INACTIVE, + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING, + [SERVICE_CLEANING] = UNIT_MAINTENANCE, + }; +@@ -84,6 +85,7 @@ 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_RESOURCES_PINNED] = UNIT_INACTIVE, + [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING, + [SERVICE_CLEANING] = UNIT_MAINTENANCE, + }; +@@ -121,6 +123,8 @@ static void service_init(Unit *u) { + s->watchdog_original_usec = USEC_INFINITY; + + s->oom_policy = _OOM_POLICY_INVALID; ++ ++ s->fd_store_preserve_mode = EXEC_PRESERVE_RESTART; + } + + static void service_unwatch_control_pid(Service *s) { +@@ -906,8 +910,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { + if (s->n_fd_store_max > 0) + fprintf(f, + "%sFile Descriptor Store Max: %u\n" ++ "%sFile Descriptor Store Pin: %s\n" + "%sFile Descriptor Store Current: %zu\n", + prefix, s->n_fd_store_max, ++ prefix, exec_preserve_mode_to_string(s->fd_store_preserve_mode), + prefix, s->n_fd_store); + + cgroup_context_dump(UNIT(s), f, prefix); +@@ -1101,7 +1107,7 @@ 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_AUTO_RESTART, SERVICE_DEAD_RESOURCES_PINNED)) { + unit_unwatch_all_pids(UNIT(s)); + unit_dequeue_rewatch_pids(UNIT(s)); + } +@@ -1202,7 +1208,8 @@ 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_AUTO_RESTART, SERVICE_CLEANING, ++ SERVICE_DEAD_RESOURCES_PINNED)) { + (void) unit_enqueue_rewatch_pids(u); + (void) unit_setup_dynamic_creds(u); + (void) unit_setup_exec_runtime(u); +@@ -1718,6 +1725,12 @@ static bool service_will_restart(Unit *u) { + return unit_will_restart_default(u); + } + ++static ServiceState service_determine_dead_state(Service *s) { ++ assert(s); ++ ++ return s->fd_store && s->fd_store_preserve_mode == EXEC_PRESERVE_YES ? SERVICE_DEAD_RESOURCES_PINNED : SERVICE_DEAD; ++} ++ + static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) { + ServiceState end_state; + int r; +@@ -1734,10 +1747,10 @@ 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; ++ end_state = service_determine_dead_state(s); + } else if (s->result == SERVICE_SKIP_CONDITION) { + unit_log_skip(UNIT(s), service_result_to_string(s->result)); +- end_state = SERVICE_DEAD; ++ end_state = service_determine_dead_state(s); + } else { + unit_log_failure(UNIT(s), service_result_to_string(s->result)); + end_state = SERVICE_FAILED; +@@ -1793,6 +1806,10 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) + /* Also, remove the runtime directory */ + unit_destroy_runtime_data(UNIT(s), &s->exec_context); + ++ /* Also get rid of the fd store, if that's configured. */ ++ if (s->fd_store_preserve_mode == EXEC_PRESERVE_NO) ++ service_release_fd_store(s); ++ + /* Get rid of the IPC bits of the user */ + unit_unref_uid_gid(UNIT(s), true); + +@@ -2449,7 +2466,7 @@ static int service_start(Unit *u) { + if (s->state == SERVICE_AUTO_RESTART) + return -EAGAIN; + +- assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); ++ assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED)); + + r = unit_acquire_invocation_id(u); + if (r < 0) +@@ -2501,7 +2518,7 @@ static int service_stop(Unit *u) { + + /* A restart will be scheduled or is in progress. */ + if (s->state == SERVICE_AUTO_RESTART) { +- service_set_state(s, SERVICE_DEAD); ++ service_set_state(s, service_determine_dead_state(s)); + return 0; + } + +@@ -3312,6 +3332,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { + * up the cgroup earlier and should do it now. */ + case SERVICE_DEAD: + case SERVICE_FAILED: ++ case SERVICE_DEAD_RESOURCES_PINNED: + unit_prune_cgroup(u); + break; + +@@ -4332,7 +4353,7 @@ int service_set_socket_fd( + + assert(!s->socket_peer); + +- if (s->state != SERVICE_DEAD) ++ if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_DEAD_RESOURCES_PINNED)) + return -EAGAIN; + + if (getpeername_pretty(fd, true, &peer_text) >= 0) { +@@ -4369,7 +4390,7 @@ static void service_reset_failed(Unit *u) { + assert(s); + + if (s->state == SERVICE_FAILED) +- service_set_state(s, SERVICE_DEAD); ++ service_set_state(s, service_determine_dead_state(s)); + + s->result = SERVICE_SUCCESS; + s->reload_result = SERVICE_SUCCESS; +@@ -4531,14 +4552,19 @@ static void service_release_resources(Unit *u) { + /* Don't release resources if this is a transitionary failed/dead state + * (i.e. SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART), insist on a permanent + * failure state. */ +- if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)) ++ if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_DEAD_RESOURCES_PINNED)) + return; + + log_unit_debug(u, "Releasing resources..."); + + service_close_socket_fd(s); + service_release_stdio_fd(s); +- service_release_fd_store(s); ++ ++ if (s->fd_store_preserve_mode != EXEC_PRESERVE_YES) ++ service_release_fd_store(s); ++ ++ if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !s->fd_store) ++ service_set_state(s, SERVICE_DEAD); + } + + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { +diff --git a/src/core/service.h b/src/core/service.h +index 2e803a3..04bdcb2 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -194,6 +194,7 @@ struct Service { + size_t n_fd_store; + unsigned n_fd_store_max; + unsigned n_keep_fd_store; ++ ExecPreserveMode fd_store_preserve_mode; + + char *usb_function_descriptors; + char *usb_function_strings; +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index d3a5b25..b944cd0 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -1999,7 +1999,8 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con + "USBFunctionStrings", + "OOMPolicy", + "TimeoutStartFailureMode", +- "TimeoutStopFailureMode")) ++ "TimeoutStopFailureMode", ++ "FileDescriptorStorePreserve")) + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, "PermissionsStartOnly", +diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service +index de7d2c7..0509c36 100644 +--- a/test/fuzz/fuzz-unit-file/directives.service ++++ b/test/fuzz/fuzz-unit-file/directives.service +@@ -163,6 +163,7 @@ ExecStopPost= + ExtensionImages= + FailureAction= + FileDescriptorStoreMax= ++FileDescriptorStorePreserve= + FinalKillSignal= + Group= + GuessMainPID= +-- +2.33.0 + diff --git a/backport-service-allow-freeing-the-fdstore-via-cleaning.patch b/backport-service-allow-freeing-the-fdstore-via-cleaning.patch new file mode 100644 index 0000000..86dd6be --- /dev/null +++ b/backport-service-allow-freeing-the-fdstore-via-cleaning.patch @@ -0,0 +1,283 @@ +From 4fb8f1e88322b94b0fa051d3c6fd19cac0227aaa Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Mar 2023 22:10:01 +0200 +Subject: [PATCH] service: allow freeing the fdstore via cleaning + +Now that we have a potentially pinned fdstore let's add a concept for +cleaning it explicitly on user requested. Let's expose this via +"systemctl clean", i.e. the same way as user directories are cleaned. + +Conflict:Adaptation context. +Reference:https://github.com/systemd/systemd/commit/4fb8f1e88322b94b0fa051d3c6fd19cac0227aaa + +--- + man/systemctl.xml | 28 +++++++++++------- + src/core/dbus-unit.c | 21 ++++++------- + src/core/execute.c | 17 +++++++++++ + src/core/execute.h | 5 +++- + src/core/service.c | 36 ++++++++++++++++++++--- + src/core/timer.c | 1 + + src/systemctl/systemctl-clean-or-freeze.c | 2 +- + src/systemctl/systemctl.c | 3 +- + 8 files changed, 86 insertions(+), 27 deletions(-) + +diff --git a/man/systemctl.xml b/man/systemctl.xml +index 8402b95..a2cc54c 100644 +--- a/man/systemctl.xml ++++ b/man/systemctl.xml +@@ -484,12 +484,16 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + StateDirectory=, CacheDirectory=, + LogsDirectory= and RuntimeDirectory=, see + systemd.exec5 +- for details. For timer units this may be used to clear out the persistent timestamp data if ++ for details. It may also be used to clear the file decriptor store as enabled via ++ FileDescriptorStoreMax=, see ++ systemd.service5 ++ for details. For timer units this may be used to clear out the persistent timestamp data if + Persistent= is used and is selected, see + systemd.timer5. This + command only applies to units that use either of these settings. If is +- not specified, both the cache and runtime data are removed (as these two types of data are +- generally redundant and reproducible on the next invocation of the unit). ++ not specified, the cache and runtime data as well as the file descriptor store are removed (as ++ these three types of resources are generally redundant and reproducible on the next invocation of ++ the unit). Note that the specified units must be stopped to invoke this operation. + + + +@@ -2076,13 +2080,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + Select what type of per-unit resources to remove when the clean command is +- invoked, see below. Takes one of configuration, state, +- cache, logs, runtime to select the +- type of resource. This option may be specified more than once, in which case all specified resource +- types are removed. Also accepts the special value all as a shortcut for +- specifying all five resource types. If this option is not specified defaults to the combination of +- cache and runtime, i.e. the two kinds of resources that +- are generally considered to be redundant and can be reconstructed on next invocation. ++ invoked, see above. Takes one of configuration, state, ++ cache, logs, runtime, ++ fdstore to select the type of resource. This option may be specified more than ++ once, in which case all specified resource types are removed. Also accepts the special value ++ all as a shortcut for specifying all six resource types. If this option is not ++ specified defaults to the combination of cache, runtime ++ and fdstore, i.e. the three kinds of resources that are generally considered ++ to be redundant and can be reconstructed on next invocation. Note that the explicit removal of the ++ fdstore resource type is only useful if the ++ FileDescriptorStorePreserve= option is enabled, since the file descriptor store ++ is otherwise cleaned automatically when the unit is stopped. + + + +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index 24e4d25..eefb08e 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -91,6 +91,12 @@ static int property_get_can_clean( + return r; + } + ++ if (FLAGS_SET(mask, EXEC_CLEAN_FDSTORE)) { ++ r = sd_bus_message_append(reply, "s", "fdstore"); ++ if (r < 0) ++ return r; ++ } ++ + return sd_bus_message_close_container(reply); + } + +@@ -684,6 +690,7 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error + return r; + + for (;;) { ++ ExecCleanMask m; + const char *i; + + r = sd_bus_message_read(message, "s", &i); +@@ -692,17 +699,11 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error + if (r == 0) + break; + +- if (streq(i, "all")) +- mask |= EXEC_CLEAN_ALL; +- else { +- ExecDirectoryType t; +- +- t = exec_resource_type_from_string(i); +- if (t < 0) +- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid resource type: %s", i); ++ m = exec_clean_mask_from_string(i); ++ if (m < 0) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid resource type: %s", i); + +- mask |= 1U << t; +- } ++ mask |= m; + } + + r = sd_bus_message_exit_container(message); +diff --git a/src/core/execute.c b/src/core/execute.c +index 9185a6f..e4c9e94 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -6584,6 +6584,23 @@ ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) { + return mfree(sc); + } + ++ExecCleanMask exec_clean_mask_from_string(const char *s) { ++ ExecDirectoryType t; ++ ++ assert(s); ++ ++ if (streq(s, "all")) ++ return EXEC_CLEAN_ALL; ++ if (streq(s, "fdstore")) ++ return EXEC_CLEAN_FDSTORE; ++ ++ t = exec_resource_type_from_string(s); ++ if (t < 0) ++ return (ExecCleanMask) t; ++ ++ return 1U << t; ++} ++ + DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free); + + static const char* const exec_input_table[_EXEC_INPUT_MAX] = { +diff --git a/src/core/execute.h b/src/core/execute.h +index f1f0ee4..fd83a8c 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -145,8 +145,9 @@ typedef enum ExecCleanMask { + EXEC_CLEAN_CACHE = 1U << EXEC_DIRECTORY_CACHE, + EXEC_CLEAN_LOGS = 1U << EXEC_DIRECTORY_LOGS, + EXEC_CLEAN_CONFIGURATION = 1U << EXEC_DIRECTORY_CONFIGURATION, ++ EXEC_CLEAN_FDSTORE = 1U << _EXEC_DIRECTORY_TYPE_MAX, + EXEC_CLEAN_NONE = 0, +- EXEC_CLEAN_ALL = (1U << _EXEC_DIRECTORY_TYPE_MAX) - 1, ++ EXEC_CLEAN_ALL = (1U << (_EXEC_DIRECTORY_TYPE_MAX+1)) - 1, + _EXEC_CLEAN_MASK_INVALID = -EINVAL, + } ExecCleanMask; + +@@ -459,6 +460,8 @@ bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c); + ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc); + DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free); + ++ExecCleanMask exec_clean_mask_from_string(const char *s); ++ + extern const struct hash_ops exec_set_credential_hash_ops; + + const char* exec_output_to_string(ExecOutput i) _const_; +diff --git a/src/core/service.c b/src/core/service.c +index 25aec40..d52ffe7 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -4500,22 +4500,39 @@ static int service_exit_status(Unit *u) { + + static int service_clean(Unit *u, ExecCleanMask mask) { + _cleanup_strv_free_ char **l = NULL; ++ bool may_clean_fdstore = false; + Service *s = SERVICE(u); + int r; + + assert(s); + assert(mask != 0); + +- if (s->state != SERVICE_DEAD) ++ if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_DEAD_RESOURCES_PINNED)) + return -EBUSY; + ++ /* Determine if there's anything we could potentially clean */ + r = exec_context_get_clean_directories(&s->exec_context, u->manager->prefix, mask, &l); + if (r < 0) + return r; + +- if (strv_isempty(l)) +- return -EUNATCH; ++ if (mask & EXEC_CLEAN_FDSTORE) ++ may_clean_fdstore = s->n_fd_store > 0 || s->n_fd_store_max > 0; ++ ++ if (strv_isempty(l) && !may_clean_fdstore) ++ return -EUNATCH; /* Nothing to potentially clean */ ++ ++ /* Let's clean the stuff we can clean quickly */ ++ if (may_clean_fdstore) ++ service_release_fd_store(s); ++ ++ /* If we are done, leave quickly */ ++ if (strv_isempty(l)) { ++ if (s->state == SERVICE_DEAD_RESOURCES_PINNED && !s->fd_store) ++ service_set_state(s, SERVICE_DEAD); ++ return 0; ++ } + ++ /* We need to clean disk stuff. This is slow, hence do it out of process, and change state */ + service_unwatch_control_pid(s); + s->clean_result = SERVICE_SUCCESS; + s->control_command = NULL; +@@ -4542,10 +4559,21 @@ fail: + + static int service_can_clean(Unit *u, ExecCleanMask *ret) { + Service *s = SERVICE(u); ++ ExecCleanMask mask = 0; ++ int r; + + assert(s); ++ assert(ret); ++ ++ r = exec_context_get_clean_mask(&s->exec_context, &mask); ++ if (r < 0) ++ return r; + +- return exec_context_get_clean_mask(&s->exec_context, ret); ++ if (s->n_fd_store_max > 0) ++ mask |= EXEC_CLEAN_FDSTORE; ++ ++ *ret = mask; ++ return 0; + } + + static const char *service_finished_job(Unit *u, JobType t, JobResult result) { +diff --git a/src/core/timer.c b/src/core/timer.c +index 77c8ce4..b038004 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -891,6 +891,7 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) { + Timer *t = TIMER(u); + + assert(t); ++ assert(ret); + + *ret = t->persistent ? EXEC_CLEAN_STATE : 0; + return 0; +diff --git a/src/systemctl/systemctl-clean-or-freeze.c b/src/systemctl/systemctl-clean-or-freeze.c +index eca3a6d..0fe9441 100644 +--- a/src/systemctl/systemctl-clean-or-freeze.c ++++ b/src/systemctl/systemctl-clean-or-freeze.c +@@ -22,7 +22,7 @@ int clean_or_freeze_unit(int argc, char *argv[], void *userdata) { + polkit_agent_open_maybe(); + + if (!arg_clean_what) { +- arg_clean_what = strv_new("cache", "runtime"); ++ arg_clean_what = strv_new("cache", "runtime", "fdstore"); + if (!arg_clean_what) + return log_oom(); + } +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 2f6f581..887bb47 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -870,7 +870,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { + "state\n" + "cache\n" + "logs\n" +- "configuration"); ++ "configuration\n" ++ "fdstore"); + return 0; + } + +-- +2.33.0 + diff --git a/backport-service-close-fdstore-asynchronously.patch b/backport-service-close-fdstore-asynchronously.patch new file mode 100644 index 0000000..3140062 --- /dev/null +++ b/backport-service-close-fdstore-asynchronously.patch @@ -0,0 +1,90 @@ +From 99620f457ed0886852ba18c9093b59767299121c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 4 Apr 2023 12:17:16 +0200 +Subject: [PATCH] service: close fdstore asynchronously + +The file descriptors we keep in the fdstore might be basically anything, +let's clean it up with our asynchronous closing feature, to not +deadlock on close(). + +(Let's also do the same for stdin/stdout/stderr fds, since they might +point to network services these days.) + +Conflict:fd adaptation, the actual code function does not change. +Reference:https://github.com/systemd/systemd/commit/99620f457ed0886852ba18c9093b59767299121c + +--- + TODO | 2 -- + src/core/service.c | 15 ++++++++------- + 2 files changed, 8 insertions(+), 9 deletions(-) + +diff --git a/TODO b/TODO +index 75f885c..27b85ea 100644 +--- a/TODO ++++ b/TODO +@@ -531,8 +531,6 @@ Features: + * maybe rework get_user_creds() to query the user database if $SHELL is used + for root, but only then. + +-* be stricter with fds we receive for the fdstore: close them asynchronously +- + * calenderspec: add support for week numbers and day numbers within a + year. This would allow us to define "bi-weekly" triggers safely. + +diff --git a/src/core/service.c b/src/core/service.c +index d52ffe7..1b18a83 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -334,7 +334,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { + sd_event_source_disable_unref(fs->event_source); + + free(fs->fdname); +- safe_close(fs->fd); ++ asynchronous_close(fs->fd); + free(fs); + } + +@@ -360,9 +360,9 @@ static void service_release_stdio_fd(Service *s) { + + log_unit_debug(UNIT(s), "Releasing stdin/stdout/stderr file descriptors."); + +- s->stdin_fd = safe_close(s->stdin_fd); +- s->stdout_fd = safe_close(s->stdout_fd); +- s->stderr_fd = safe_close(s->stderr_fd); ++ s->stdin_fd = asynchronous_close(s->stdin_fd); ++ s->stdout_fd = asynchronous_close(s->stdout_fd); ++ s->stderr_fd = asynchronous_close(s->stderr_fd); + } + static void service_done(Unit *u) { + Service *s = SERVICE(u); +@@ -448,7 +448,7 @@ static int service_add_fd_store(Service *s, int fd, const char *name, bool do_po + if (r < 0) + return r; + if (r > 0) { +- safe_close(fd); ++ asynchronous_close(fd); + return 0; /* fd already included */ + } + } +@@ -491,7 +491,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bo + assert(s); + + while (fdset_size(fds) > 0) { +- _cleanup_close_ int fd = -1; ++ _cleanup_(asynchronous_closep) int fd = -EBADF; + + fd = fdset_steal_first(fds); + if (fd < 0) +@@ -506,7 +506,8 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bo + return log_unit_error_errno(UNIT(s), r, "Failed to add fd to store: %m"); + if (r > 0) + log_unit_debug(UNIT(s), "Added fd %u (%s) to fd store.", fd, strna(name)); +- fd = -1; ++ ++ TAKE_FD(fd); + } + + return 0; +-- +2.33.0 + diff --git a/backport-service-drop-redundant-unit_ref_unset-call.patch b/backport-service-drop-redundant-unit_ref_unset-call.patch new file mode 100644 index 0000000..b7e95b9 --- /dev/null +++ b/backport-service-drop-redundant-unit_ref_unset-call.patch @@ -0,0 +1,31 @@ +From d53bda316be6ba226788b40e101a3bff5aa75a5f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Mar 2023 22:04:26 +0200 +Subject: [PATCH] service: drop redundant unit_ref_unset() call + +The immediately preceeding service_close_socket_fd() call does that +internally anyway. No need to do this again right after. + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/d53bda316be6ba226788b40e101a3bff5aa75a5f + +--- + src/core/service.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index d73feb363e..c4a7be890d 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -452,8 +452,6 @@ static void service_done(Unit *u) { + + service_close_socket_fd(s); + +- unit_ref_unset(&s->accept_socket); +- + service_stop_watchdog(s); + + s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source); +-- +2.33.0 + diff --git a/backport-service-release-resources-from-a-seperate-queue-not-.patch b/backport-service-release-resources-from-a-seperate-queue-not-.patch new file mode 100644 index 0000000..0fb3a54 --- /dev/null +++ b/backport-service-release-resources-from-a-seperate-queue-not-.patch @@ -0,0 +1,286 @@ +From 6ac62d61db737b01ad3776a7688d8a4c57b3f7d9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Mar 2023 21:52:41 +0200 +Subject: [PATCH] service: release resources from a seperate queue, not + unit_check_gc() + +The per-unit-type release_resources() hook (most prominent use: to +release a service unit's fdstore once a unit is entirely dead and has no +jobs more) was currently invoked as part of unit_check_gc(), whose +primary purpose is to determine if a unit should be GC'ed. This was +always a bit ugly, as release_resources() changes state of the unit, +while unit_check_gc() is otherwise (and was before release_resources() +was added) a "passive" function that just checks for a couple of +conditions. + +unit_check_gc() is called at various places, including when we wonder if +we should add a unit to the gc queue, and then again when we take it out +of the gc queue to dtermine whether to really gc it now. The fact that +these checks have side effects so far wasn't too problematic, as the +state changes (primarily: that services would empty their fdstores) were +relatively limited and scope. + +A later patch in this series is supposed to extend the service state +engine with a separate state distinct from SERVICE_DEAD that is very +much like it but indicates that the service still has active resources +(specifically the fdstore). For cases like that the releasing of the +fdstore would result in state changes (as we'd then return to a classic +SERVICE_DEAD state). And this is where the fact that the +release_resources() is called as side-effect becomes problematic: it +would mean that unit state changes would instantly propagate to state +changes elsewhere, though we usually want this to be done through the +run queue for coalescing and avoidance of recursion. + +Hence, let's clean this up: let's move the release_resources() logic +into a queue of its own, and then enqueue items into it from the general +state change notification handle in unit_notify(). + +Conflict:Adapt to context +Reference:https://github.com/systemd/systemd/commit/6ac62d61db737b01ad3776a7688d8a4c57b3f7d9 + +--- + src/core/manager.c | 23 ++++++++++++++ + src/core/manager.h | 3 ++ + src/core/unit.c | 75 +++++++++++++++++++++++++++++++++++----------- + src/core/unit.h | 7 +++++ + 4 files changed, 91 insertions(+), 17 deletions(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index 3c83c82..3245acd 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1108,6 +1108,26 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) { + return n; + } + ++static unsigned manager_dispatch_release_resources_queue(Manager *m) { ++ unsigned n = 0; ++ Unit *u; ++ ++ assert(m); ++ ++ while ((u = m->release_resources_queue)) { ++ assert(u->in_release_resources_queue); ++ ++ LIST_REMOVE(release_resources_queue, m->release_resources_queue, u); ++ u->in_release_resources_queue = false; ++ ++ n++; ++ ++ unit_release_resources(u); ++ } ++ ++ return n; ++} ++ + enum { + GC_OFFSET_IN_PATH, /* This one is on the path we were traveling */ + GC_OFFSET_UNSURE, /* No clue */ +@@ -2966,6 +2986,9 @@ int manager_loop(Manager *m) { + if (manager_dispatch_stop_when_unneeded_queue(m) > 0) + continue; + ++ if (manager_dispatch_release_resources_queue(m) > 0) ++ continue; ++ + if (manager_dispatch_dbus_queue(m) > 0) + continue; + +diff --git a/src/core/manager.h b/src/core/manager.h +index 226cfc9..f53e5d5 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -193,6 +193,9 @@ struct Manager { + /* Units that have BindsTo= another unit, and might need to be shutdown because the bound unit is not active. */ + LIST_HEAD(Unit, stop_when_bound_queue); + ++ /* Units that have resources open, and where it might be good to check if they can be released now */ ++ LIST_HEAD(Unit, release_resources_queue); ++ + sd_event *event; + + /* This maps PIDs we care about to units that are interested in. We allow multiple units to be interested in +diff --git a/src/core/unit.c b/src/core/unit.c +index d75c8a9..af225e2 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -370,18 +370,39 @@ int unit_set_description(Unit *u, const char *description) { + return 0; + } + ++void unit_release_resources(Unit *u) { ++ UnitActiveState state; ++ ++ assert(u); ++ ++ if (u->job || u->nop_job) ++ return; ++ ++ if (u->perpetual) ++ return; ++ ++ state = unit_active_state(u); ++ if (!IN_SET(state, UNIT_INACTIVE, UNIT_FAILED)) ++ return; ++ ++ if (unit_will_restart(u)) ++ return; ++ ++ if (UNIT_VTABLE(u)->release_resources) ++ UNIT_VTABLE(u)->release_resources(u); ++} ++ + bool unit_may_gc(Unit *u) { + UnitActiveState state; + int r; + + assert(u); + +- /* Checks whether the unit is ready to be unloaded for garbage collection. +- * Returns true when the unit may be collected, and false if there's some +- * reason to keep it loaded. ++ /* Checks whether the unit is ready to be unloaded for garbage collection. Returns true when the ++ * unit may be collected, and false if there's some reason to keep it loaded. + * +- * References from other units are *not* checked here. Instead, this is done +- * in unit_gc_sweep(), but using markers to properly collect dependency loops. ++ * References from other units are *not* checked here. Instead, this is done in unit_gc_sweep(), but ++ * using markers to properly collect dependency loops. + */ + + if (u->job) +@@ -390,20 +411,16 @@ bool unit_may_gc(Unit *u) { + if (u->nop_job) + return false; + +- state = unit_active_state(u); +- +- /* If the unit is inactive and failed and no job is queued for it, then release its runtime resources */ +- if (UNIT_IS_INACTIVE_OR_FAILED(state) && +- UNIT_VTABLE(u)->release_resources) +- UNIT_VTABLE(u)->release_resources(u); +- + if (u->perpetual) + return false; + + if (sd_bus_track_count(u->bus_track) > 0) + return false; + +- /* But we keep the unit object around for longer when it is referenced or configured to not be gc'ed */ ++ state = unit_active_state(u); ++ ++ /* But we keep the unit object around for longer when it is referenced or configured to not be ++ * gc'ed */ + switch (u->collect_mode) { + + case COLLECT_INACTIVE: +@@ -433,10 +450,10 @@ bool unit_may_gc(Unit *u) { + return false; + } + +- if (UNIT_VTABLE(u)->may_gc && !UNIT_VTABLE(u)->may_gc(u)) +- return false; ++ if (!UNIT_VTABLE(u)->may_gc) ++ return true; + +- return true; ++ return UNIT_VTABLE(u)->may_gc(u); + } + + void unit_add_to_load_queue(Unit *u) { +@@ -540,6 +557,25 @@ void unit_submit_to_stop_when_bound_queue(Unit *u) { + u->in_stop_when_bound_queue = true; + } + ++void unit_submit_to_release_resources_queue(Unit *u) { ++ assert(u); ++ ++ if (u->in_release_resources_queue) ++ return; ++ ++ if (u->job || u->nop_job) ++ return; ++ ++ if (u->perpetual) ++ return; ++ ++ if (!UNIT_VTABLE(u)->release_resources) ++ return; ++ ++ LIST_PREPEND(release_resources_queue, u->manager->release_resources_queue, u); ++ u->in_release_resources_queue = true; ++} ++ + static void unit_clear_dependencies(Unit *u) { + assert(u); + +@@ -761,6 +797,9 @@ Unit* unit_free(Unit *u) { + if (u->in_stop_when_bound_queue) + LIST_REMOVE(stop_when_bound_queue, u->manager->stop_when_bound_queue, u); + ++ if (u->in_release_resources_queue) ++ LIST_REMOVE(release_resources_queue, u->manager->release_resources_queue, u); ++ + bpf_firewall_close(u); + + hashmap_free(u->bpf_foreign_by_key); +@@ -2494,7 +2533,6 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) + assert(j); + + if (j->state == JOB_WAITING) +- + /* So we reached a different state for this job. Let's see if we can run it now if it failed previously + * due to EAGAIN. */ + job_add_to_run_queue(j); +@@ -2698,6 +2736,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag + + /* Maybe the unit should be GC'ed now? */ + unit_add_to_gc_queue(u); ++ ++ /* Maybe we can release some resources now? */ ++ unit_submit_to_release_resources_queue(u); + } + + if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) { +diff --git a/src/core/unit.h b/src/core/unit.h +index 6485858..16c65d9 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -241,6 +241,9 @@ typedef struct Unit { + /* Queue of units that have a BindTo= dependency on some other unit, and should possibly be shut down */ + LIST_FIELDS(Unit, stop_when_bound_queue); + ++ /* Queue of units that should be checked if they can release resources now */ ++ LIST_FIELDS(Unit, release_resources_queue); ++ + /* PIDs we keep an eye on. Note that a unit might have many + * more, but these are the ones we care enough about to + * process SIGCHLD for */ +@@ -393,6 +396,7 @@ typedef struct Unit { + bool in_stop_when_unneeded_queue:1; + bool in_start_when_upheld_queue:1; + bool in_stop_when_bound_queue:1; ++ bool in_release_resources_queue:1; + + bool sent_dbus_new_signal:1; + +@@ -744,6 +748,8 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c); + int unit_choose_id(Unit *u, const char *name); + int unit_set_description(Unit *u, const char *description); + ++void unit_release_resources(Unit *u); ++ + bool unit_may_gc(Unit *u); + + static inline bool unit_is_extrinsic(Unit *u) { +@@ -759,6 +765,7 @@ void unit_add_to_target_deps_queue(Unit *u); + void unit_submit_to_stop_when_unneeded_queue(Unit *u); + void unit_submit_to_start_when_upheld_queue(Unit *u); + void unit_submit_to_stop_when_bound_queue(Unit *u); ++void unit_submit_to_release_resources_queue(Unit *u); + + int unit_merge(Unit *u, Unit *other); + int unit_merge_by_name(Unit *u, const char *other); +-- +2.33.0 + diff --git a/backport-service-rename-service_close_socket_fd-service_relea.patch b/backport-service-rename-service_close_socket_fd-service_relea.patch new file mode 100644 index 0000000..edcc768 --- /dev/null +++ b/backport-service-rename-service_close_socket_fd-service_relea.patch @@ -0,0 +1,95 @@ +From 81a1d6d6790ad6ae8ff63147dfab68e6835178c3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 4 Apr 2023 15:51:07 +0200 +Subject: [PATCH] =?UTF-8?q?service:=20rename=20service=5Fclose=5Fsocket=5F?= + =?UTF-8?q?fd()=20=E2=86=92=20service=5Frelease=5Fsocket=5Ffd()?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Just to match service_release_stdio_fd() and service_release_fd_store() +in the name, since they do similar things. + +This follows the concept that we "release" resources, and this is all +generically wrapped in "service_release_resources()". + +Conflict:Context Adaptation. +Reference:https://github.com/systemd/systemd/commit/81a1d6d6790ad6ae8ff63147dfab68e6835178c3 + +--- + src/core/service.c | 9 ++++----- + src/core/service.h | 2 +- + src/core/socket.c | 2 +- + 3 files changed, 6 insertions(+), 7 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 9bd82fc..a7d5f3f 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -188,7 +188,7 @@ static int service_set_main_pid(Service *s, pid_t pid) { + return 0; + } + +-void service_close_socket_fd(Service *s) { ++void service_release_socket_fd(Service *s) { + assert(s); + + if (s->socket_fd < 0 && !UNIT_ISSET(s->accept_socket) && !s->socket_peer) +@@ -399,8 +399,6 @@ static void service_done(Unit *u) { + s->usb_function_descriptors = mfree(s->usb_function_descriptors); + s->usb_function_strings = mfree(s->usb_function_strings); + +- service_close_socket_fd(s); +- + service_stop_watchdog(s); + + s->timer_event_source = sd_event_source_disable_unref(s->timer_event_source); +@@ -408,8 +406,9 @@ static void service_done(Unit *u) { + + s->bus_name_pid_lookup_slot = sd_bus_slot_unref(s->bus_name_pid_lookup_slot); + +- service_release_fd_store(s); ++ service_release_socket_fd(s); + service_release_stdio_fd(s); ++ service_release_fd_store(s); + } + + static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { +@@ -4616,7 +4615,7 @@ static void service_release_resources(Unit *u) { + + log_unit_debug(u, "Releasing resources..."); + +- service_close_socket_fd(s); ++ service_release_socket_fd(s); + service_release_stdio_fd(s); + + if (s->fd_store_preserve_mode != EXEC_PRESERVE_YES) +diff --git a/src/core/service.h b/src/core/service.h +index 78a0f97..4a4ab8a 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -221,7 +221,7 @@ static inline usec_t service_get_watchdog_usec(Service *s) { + extern const UnitVTable service_vtable; + + int service_set_socket_fd(Service *s, int fd, struct Socket *socket, struct SocketPeer *peer, bool selinux_context_net); +-void service_close_socket_fd(Service *s); ++void service_release_socket_fd(Service *s); + + const char* service_restart_to_string(ServiceRestart i) _const_; + ServiceRestart service_restart_from_string(const char *s) _pure_; +diff --git a/src/core/socket.c b/src/core/socket.c +index b1c534b..f61c302 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2426,7 +2426,7 @@ static void socket_enter_running(Socket *s, int cfd_in) { + if (r < 0) { + /* We failed to activate the new service, but it still exists. Let's make sure the + * service closes and forgets the connection fd again, immediately. */ +- service_close_socket_fd(SERVICE(service)); ++ service_release_socket_fd(SERVICE(service)); + goto fail; + } + +-- +2.33.0 + diff --git a/backport-service-rework-how-we-release-resources.patch b/backport-service-rework-how-we-release-resources.patch new file mode 100644 index 0000000..cd88cd9 --- /dev/null +++ b/backport-service-rework-how-we-release-resources.patch @@ -0,0 +1,137 @@ +From c25fac9a17b95271bb6f8d967d33c5a9aa9e4bc9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Mar 2023 22:06:39 +0200 +Subject: [PATCH] service: rework how we release resources + +Let's normalize how we release service resources, i.e. the three types +of fds we maintain for each service: + +1. the fdstore +2. the socket fd for per-connection socket activated services +3. stdin/stdout/stderr + +The generic service_release_resources() hook now calls into +service_release_fd_store() + service_close_socket_fd() +service_release_stdio_fd() one after the other, releasing them all for +the generic "release_resources" infra of the unit lifecycle. + +We do no longer close the socket fd from service_set_state(), moving +this exclusively into service_release_resources(), so that all fds are +closed the same way. + +Conflict:Adaptation context. Deleted the features that are not introduced. +Reference:https://github.com/systemd/systemd/commit/c25fac9a17b95271bb6f8d967d33c5a9aa9e4bc9 + +--- + src/core/service.c | 51 ++++++++++++++++++++++++++++++---------------- + 1 file changed, 34 insertions(+), 17 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 7d823dd..af6360e 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -183,6 +183,11 @@ static int service_set_main_pid(Service *s, pid_t pid) { + void service_close_socket_fd(Service *s) { + assert(s); + ++ if (s->socket_fd < 0 && !UNIT_ISSET(s->accept_socket) && !s->socket_peer) ++ return; ++ ++ log_unit_debug(UNIT(s), "Closing connection socket."); ++ + /* Undo the effect of service_set_socket_fd(). */ + + s->socket_fd = asynchronous_close(s->socket_fd); +@@ -331,30 +336,29 @@ static void service_release_fd_store(Service *s) { + if (s->n_keep_fd_store > 0) + return; + ++ if (!s->fd_store) ++ return; ++ + log_unit_debug(UNIT(s), "Releasing all stored fds"); ++ + while (s->fd_store) + service_fd_store_unlink(s->fd_store); + + assert(s->n_fd_store == 0); + } + +-static void service_release_resources(Unit *u) { +- Service *s = SERVICE(u); +- ++static void service_release_stdio_fd(Service *s) { + assert(s); + +- if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0) ++ if (s->stdin_fd < 0 && s->stdout_fd < 0 && s->stdout_fd < 0) + return; + +- log_unit_debug(u, "Releasing resources."); ++ log_unit_debug(UNIT(s), "Releasing stdin/stdout/stderr file descriptors."); + + s->stdin_fd = safe_close(s->stdin_fd); + s->stdout_fd = safe_close(s->stdout_fd); + s->stderr_fd = safe_close(s->stderr_fd); +- +- service_release_fd_store(s); + } +- + static void service_done(Unit *u) { + Service *s = SERVICE(u); + +@@ -399,7 +403,8 @@ static void service_done(Unit *u) { + + s->bus_name_pid_lookup_slot = sd_bus_slot_unref(s->bus_name_pid_lookup_slot); + +- service_release_resources(u); ++ service_release_fd_store(s); ++ service_release_stdio_fd(s); + } + + static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { +@@ -1101,14 +1106,6 @@ static void service_set_state(Service *s, ServiceState state) { + unit_dequeue_rewatch_pids(UNIT(s)); + } + +- if (!IN_SET(state, +- SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST, +- SERVICE_RUNNING, SERVICE_RELOAD, +- SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, +- SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && +- !(state == SERVICE_DEAD && UNIT(s)->job)) +- service_close_socket_fd(s); +- + if (state != SERVICE_START) + s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source); + +@@ -4524,6 +4521,26 @@ static int service_can_start(Unit *u) { + return 1; + } + ++static void service_release_resources(Unit *u) { ++ Service *s = SERVICE(u); ++ assert(s); ++ ++ /* Invoked by the unit state engine, whenever it realizes that unit is dead and there's no job ++ * anymore for it, and it hence is a good idea to release resources */ ++ ++ /* Don't release resources if this is a transitionary failed/dead state ++ * (i.e. SERVICE_DEAD_BEFORE_AUTO_RESTART/SERVICE_FAILED_BEFORE_AUTO_RESTART), insist on a permanent ++ * failure state. */ ++ if (!IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)) ++ return; ++ ++ log_unit_debug(u, "Releasing resources..."); ++ ++ service_close_socket_fd(s); ++ service_release_stdio_fd(s); ++ service_release_fd_store(s); ++} ++ + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { + [SERVICE_RESTART_NO] = "no", + [SERVICE_RESTART_ON_SUCCESS] = "on-success", +-- +2.33.0 + diff --git a/backport-socket-always-pass-socket-fd-and-SocketPeer-ownershi.patch b/backport-socket-always-pass-socket-fd-and-SocketPeer-ownershi.patch new file mode 100644 index 0000000..9cd4289 --- /dev/null +++ b/backport-socket-always-pass-socket-fd-and-SocketPeer-ownershi.patch @@ -0,0 +1,174 @@ +From 3fabebf45e268a0e1e8f9e7688b2428f8ee8a802 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 24 Nov 2021 23:50:07 +0100 +Subject: [PATCH] socket: always pass socket, fd and SocketPeer ownership to + service together + +Per-connection socket instances we currently maintain three fields +related to the socket: a reference to the Socket unit, the connection fd, +and a reference to the SocketPeer object that counts socket peers. + +Let's synchronize their lifetime, i.e. always set them all three +together or unset them together, so that their reference counters stay +synchronous. + +THis will in particuar ensure that we'll drop the SocketPeer reference +whenever we leave an active state of the service unit, i.e. at the same +time we close the fd for it. + +Fixes: #20685 + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/3fabebf45e268a0e1e8f9e7688b2428f8ee8a802 + +--- + src/core/service.c | 27 ++++++++++++++++++--------- + src/core/service.h | 9 +++++---- + src/core/socket.c | 4 +--- + 3 files changed, 24 insertions(+), 16 deletions(-) + +diff --git a/src/core/service.c b/src/core/service.c +index 17c19a2c4a..49579f7998 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -190,6 +190,8 @@ void service_close_socket_fd(Service *s) { + socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); + unit_ref_unset(&s->accept_socket); + } ++ ++ s->socket_peer = socket_peer_unref(s->socket_peer); + } + + static void service_stop_watchdog(Service *s) { +@@ -388,7 +390,6 @@ static void service_done(Unit *u) { + s->usb_function_strings = mfree(s->usb_function_strings); + + service_close_socket_fd(s); +- s->peer = socket_peer_unref(s->peer); + + unit_ref_unset(&s->accept_socket); + +@@ -1237,8 +1238,8 @@ static int service_coldplug(Unit *u) { + + /* Make a best-effort attempt at bumping the connection count */ + if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) { +- socket_peer_unref(s->peer); +- s->peer = peer; ++ socket_peer_unref(s->socket_peer); ++ s->socket_peer = peer; + } + } + } +@@ -4285,8 +4286,14 @@ static void service_bus_name_owner_change(Unit *u, const char *new_owner) { + } + } + +-int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context_net) { +- _cleanup_free_ char *peer = NULL; ++int service_set_socket_fd( ++ Service *s, ++ int fd, ++ Socket *sock, ++ SocketPeer *peer, ++ bool selinux_context_net) { ++ ++ _cleanup_free_ char *peer_text = NULL; + int r; + + assert(s); +@@ -4301,22 +4308,23 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context + if (s->socket_fd >= 0) + return -EBUSY; + ++ assert(!s->socket_peer); ++ + if (s->state != SERVICE_DEAD) + return -EAGAIN; + +- if (getpeername_pretty(fd, true, &peer) >= 0) { ++ if (getpeername_pretty(fd, true, &peer_text) >= 0) { + + if (UNIT(s)->description) { + _cleanup_free_ char *a = NULL; + +- a = strjoin(UNIT(s)->description, " (", peer, ")"); ++ a = strjoin(UNIT(s)->description, " (", peer_text, ")"); + if (!a) + return -ENOMEM; + + r = unit_set_description(UNIT(s), a); + } else +- r = unit_set_description(UNIT(s), peer); +- ++ r = unit_set_description(UNIT(s), peer_text); + if (r < 0) + return r; + } +@@ -4326,6 +4334,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context + return r; + + s->socket_fd = fd; ++ s->socket_peer = socket_peer_ref(peer); + s->socket_fd_selinux_context_net = selinux_context_net; + + unit_ref_set(&s->accept_socket, UNIT(s), UNIT(sock)); +diff --git a/src/core/service.h b/src/core/service.h +index 70ce70fba5..778551d844 100644 +--- a/src/core/service.h ++++ b/src/core/service.h +@@ -157,8 +157,11 @@ struct Service { + DynamicCreds dynamic_creds; + + pid_t main_pid, control_pid; ++ ++ /* if we are a socket activated service instance, store information of the connection/peer/socket */ + int socket_fd; +- SocketPeer *peer; ++ SocketPeer *socket_peer; ++ UnitRef accept_socket; + bool socket_fd_selinux_context_net; + + bool permissions_start_only; +@@ -186,8 +189,6 @@ struct Service { + char *status_text; + int status_errno; + +- UnitRef accept_socket; +- + sd_event_source *timer_event_source; + PathSpec *pid_file_pathspec; + +@@ -226,7 +227,7 @@ static inline usec_t service_get_watchdog_usec(Service *s) { + + extern const UnitVTable service_vtable; + +-int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net); ++int service_set_socket_fd(Service *s, int fd, struct Socket *socket, struct SocketPeer *peer, bool selinux_context_net); + void service_close_socket_fd(Service *s); + + const char* service_restart_to_string(ServiceRestart i) _const_; +diff --git a/src/core/socket.c b/src/core/socket.c +index e6d168188a..d9db1edd3c 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -2409,7 +2409,7 @@ static void socket_enter_running(Socket *s, int cfd_in) { + + s->n_accepted++; + +- r = service_set_socket_fd(SERVICE(service), cfd, s, s->selinux_context_from_net); ++ r = service_set_socket_fd(SERVICE(service), cfd, s, p, s->selinux_context_from_net); + if (ERRNO_IS_DISCONNECT(r)) + return; + if (r < 0) +@@ -2418,8 +2418,6 @@ static void socket_enter_running(Socket *s, int cfd_in) { + TAKE_FD(cfd); /* We passed ownership of the fd to the service now. Forget it here. */ + s->n_connections++; + +- SERVICE(service)->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ +- + r = manager_add_job(UNIT(s)->manager, JOB_START, service, JOB_REPLACE, NULL, &error, NULL); + if (r < 0) { + /* We failed to activate the new service, but it still exists. Let's make sure the +-- +2.33.0 + diff --git a/backport-socket-various-modernizations.patch b/backport-socket-various-modernizations.patch new file mode 100644 index 0000000..6a0fc42 --- /dev/null +++ b/backport-socket-various-modernizations.patch @@ -0,0 +1,94 @@ +From 000b61b980abba1a69d7a5e2a2a073930eca5e08 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 24 Nov 2021 23:53:10 +0100 +Subject: [PATCH] socket: various modernizations + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/000b61b980abba1a69d7a5e2a2a073930eca5e08 + +--- + src/core/socket.c | 25 +++++++++++++------------ + 1 file changed, 13 insertions(+), 12 deletions(-) + +diff --git a/src/core/socket.c b/src/core/socket.c +index d9db1edd3c..6b5ec9d987 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -463,10 +463,6 @@ static int socket_load(Unit *u) { + assert(u); + assert(u->load_state == UNIT_STUB); + +- r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); +- if (r < 0) +- return r; +- + r = unit_load_fragment_and_dropin(u, true); + if (r < 0) + return r; +@@ -485,12 +481,13 @@ static int socket_load(Unit *u) { + static SocketPeer *socket_peer_new(void) { + SocketPeer *p; + +- p = new0(SocketPeer, 1); ++ p = new(SocketPeer, 1); + if (!p) + return NULL; + +- p->n_ref = 1; +- ++ *p = (SocketPeer) { ++ .n_ref = 1, ++ }; + return p; + } + +@@ -507,14 +504,15 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(SocketPeer, socket_peer, socket_peer_free); + + int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; +- SocketPeer sa = {}, *i; +- socklen_t salen = sizeof(sa.peer); ++ SocketPeer sa = { ++ .peer_salen = sizeof(union sockaddr_union), ++ }, *i; + int r; + + assert(fd >= 0); + assert(s); + +- if (getpeername(fd, &sa.peer.sa, &salen) < 0) ++ if (getpeername(fd, &sa.peer.sa, &sa.peer_salen) < 0) + return log_unit_error_errno(UNIT(s), errno, "getpeername failed: %m"); + + if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6, AF_VSOCK)) { +@@ -522,6 +520,10 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + return 0; + } + ++ r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); ++ if (r < 0) ++ return r; ++ + i = set_get(s->peers_by_address, &sa); + if (i) { + *p = socket_peer_ref(i); +@@ -533,7 +535,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + return log_oom(); + + remote->peer = sa.peer; +- remote->peer_salen = salen; ++ remote->peer_salen = sa.peer_salen; + + r = set_put(s->peers_by_address, remote); + if (r < 0) +@@ -542,7 +544,6 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + remote->socket = s; + + *p = TAKE_PTR(remote); +- + return 1; + } + +-- +2.33.0 + diff --git a/backport-strv-add-strv_copy_n-helper-for-copying-part-of-a-n-.patch b/backport-strv-add-strv_copy_n-helper-for-copying-part-of-a-n-.patch new file mode 100644 index 0000000..8f7981d --- /dev/null +++ b/backport-strv-add-strv_copy_n-helper-for-copying-part-of-a-n-.patch @@ -0,0 +1,125 @@ +From 4ea517a6e07f47117348c68c6fe087bf6d401558 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 16 Feb 2023 15:41:55 +0100 +Subject: [PATCH] strv: add strv_copy_n() helper for copying part of a n strv + +Conflict:Context Adaptation. Test case adaptation. +Reference:https://github.com/systemd/systemd/commit/4ea517a6e07f47117348c68c6fe087bf6d401558 + +--- + src/basic/strv.c | 10 ++++++++-- + src/basic/strv.h | 5 ++++- + src/test/test-strv.c | 41 +++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 53 insertions(+), 3 deletions(-) + +diff --git a/src/basic/strv.c b/src/basic/strv.c +index 6a2e44c..d6c50f5 100644 +--- a/src/basic/strv.c ++++ b/src/basic/strv.c +@@ -88,20 +88,26 @@ char** strv_free_erase(char **l) { + return mfree(l); + } + +-char** strv_copy(char * const *l) { ++char** strv_copy_n(char * const *l, size_t m) { + _cleanup_strv_free_ char **result = NULL; + char **k, * const *i; + +- result = new(char*, strv_length(l) + 1); ++ result = new(char*, MIN(strv_length(l), m) + 1); + if (!result) + return NULL; + + k = result; + STRV_FOREACH(i, l) { ++ if (m == 0) ++ break; ++ + *k = strdup(*i); + if (!*k) + return NULL; + k++; ++ ++ if (m != SIZE_MAX) ++ m--; + } + + *k = NULL; +diff --git a/src/basic/strv.h b/src/basic/strv.h +index 8674bfd..108ec25 100644 +--- a/src/basic/strv.h ++++ b/src/basic/strv.h +@@ -29,7 +29,10 @@ char** strv_free_erase(char **l); + DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); + #define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) + +-char** strv_copy(char * const *l); ++char** strv_copy_n(char * const *l, size_t n); ++static inline char** strv_copy(char * const *l) { ++ return strv_copy_n(l, SIZE_MAX); ++} + size_t strv_length(char * const *l) _pure_; + + int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates); +diff --git a/src/test/test-strv.c b/src/test/test-strv.c +index 1345252..dd119a8 100644 +--- a/src/test/test-strv.c ++++ b/src/test/test-strv.c +@@ -988,6 +988,46 @@ static void test_strv_fnmatch(void) { + assert(pos == 1); + } + ++static void test_strv_copy_n(void) { ++ char **x = STRV_MAKE("a", "b", "c", "d", "e"); ++ _cleanup_strv_free_ char **l = NULL; ++ ++ l = strv_copy_n(x, 0); ++ assert_se(strv_equal(l, NULL)); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 0); ++ assert_se(strv_equal(l, (char**) { NULL })); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 1); ++ assert_se(strv_equal(l, STRV_MAKE("a"))); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 2); ++ assert_se(strv_equal(l, STRV_MAKE("a", "b"))); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 3); ++ assert_se(strv_equal(l, STRV_MAKE("a", "b", "c"))); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 4); ++ assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d"))); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 5); ++ assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d", "e"))); ++ strv_free(l); ++ ++ l = strv_copy_n(x, 6); ++ assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d", "e"))); ++ strv_free(l); ++ ++ l = strv_copy_n(x, SIZE_MAX); ++ assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d", "e"))); ++} ++ + int main(int argc, char *argv[]) { + test_str_in_set(); + test_strptr_in_set(); +@@ -1054,6 +1094,7 @@ int main(int argc, char *argv[]) { + + test_foreach_string(); + test_strv_fnmatch(); ++ test_strv_copy_n(); + + return 0; + } +-- +2.33.0 + diff --git a/backport-strv-rewrite-strv_copy-with-cleanup-attribute-and-ST.patch b/backport-strv-rewrite-strv_copy-with-cleanup-attribute-and-ST.patch new file mode 100644 index 0000000..5ad4b58 --- /dev/null +++ b/backport-strv-rewrite-strv_copy-with-cleanup-attribute-and-ST.patch @@ -0,0 +1,56 @@ +From 9eb814818d0c35f0c9e05cb596508c1536b0e654 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Wed, 16 Mar 2022 22:29:32 +0900 +Subject: [PATCH] strv: rewrite strv_copy() with cleanup attribute and + STRV_FOREACH() + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/9eb814818d0c35f0c9e05cb596508c1536b0e654 + +--- + src/basic/strv.c | 24 ++++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +diff --git a/src/basic/strv.c b/src/basic/strv.c +index cf573a3783..07a6c49b50 100644 +--- a/src/basic/strv.c ++++ b/src/basic/strv.c +@@ -89,23 +89,23 @@ char** strv_free_erase(char **l) { + } + + char** strv_copy(char * const *l) { +- char **r, **k; ++ _cleanup_strv_free_ char **result = NULL; ++ char **k, * const *i; + +- k = r = new(char*, strv_length(l) + 1); +- if (!r) ++ result = new(char*, strv_length(l) + 1); ++ if (!result) + return NULL; + +- if (l) +- for (; *l; k++, l++) { +- *k = strdup(*l); +- if (!*k) { +- strv_free(r); +- return NULL; +- } +- } ++ k = result; ++ STRV_FOREACH(i, l) { ++ *k = strdup(*i); ++ if (!*k) ++ return NULL; ++ k++; ++ } + + *k = NULL; +- return r; ++ return TAKE_PTR(result); + } + + size_t strv_length(char * const *l) { +-- +2.33.0 + diff --git a/backport-test-add-tests-for-NOTIFYACCESS-override-through-sd_.patch b/backport-test-add-tests-for-NOTIFYACCESS-override-through-sd_.patch new file mode 100644 index 0000000..ba17612 --- /dev/null +++ b/backport-test-add-tests-for-NOTIFYACCESS-override-through-sd_.patch @@ -0,0 +1,160 @@ +From b64f5ddacad725d5e4e021691fad52aa2eb32c46 Mon Sep 17 00:00:00 2001 +From: Mike Yuan +Date: Wed, 22 Mar 2023 03:40:52 +0800 +Subject: [PATCH] test: add tests for NOTIFYACCESS override through sd_notify + +Conflict:Adaptation Context. +Reference:https://github.com/systemd/systemd/commit/b64f5ddacad725d5e4e021691fad52aa2eb32c46 + +--- + test/TEST-80-NOTIFYACCESS/Makefile | 1 + + test/TEST-80-NOTIFYACCESS/test.sh | 11 +++++++++ + test/meson.build | 3 +++ + test/testsuite-80.units/notify.service | 4 ++++ + test/testsuite-80.units/test.sh | 26 +++++++++++++++++++++ + test/units/testsuite-80.service | 8 +++++++ + test/units/testsuite-80.sh | 32 ++++++++++++++++++++++++++ + 7 files changed, 85 insertions(+) + create mode 120000 test/TEST-80-NOTIFYACCESS/Makefile + create mode 100755 test/TEST-80-NOTIFYACCESS/test.sh + create mode 100644 test/testsuite-80.units/notify.service + create mode 100755 test/testsuite-80.units/test.sh + create mode 100644 test/units/testsuite-80.service + create mode 100755 test/units/testsuite-80.sh + +diff --git a/test/TEST-80-NOTIFYACCESS/Makefile b/test/TEST-80-NOTIFYACCESS/Makefile +new file mode 120000 +index 0000000..e9f93b1 +--- /dev/null ++++ b/test/TEST-80-NOTIFYACCESS/Makefile +@@ -0,0 +1 @@ ++../TEST-01-BASIC/Makefile +\ No newline at end of file +diff --git a/test/TEST-80-NOTIFYACCESS/test.sh b/test/TEST-80-NOTIFYACCESS/test.sh +new file mode 100755 +index 0000000..b4d2452 +--- /dev/null ++++ b/test/TEST-80-NOTIFYACCESS/test.sh +@@ -0,0 +1,11 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++set -e ++ ++TEST_DESCRIPTION="test NotifyAccess through sd-notify" ++TEST_NO_QEMU=1 ++ ++# shellcheck source=test/test-functions ++. "${TEST_BASE_DIR:?}/test-functions" ++ ++do_test "$@" +diff --git a/test/meson.build b/test/meson.build +index 8727e66..8eb9bc4 100644 +--- a/test/meson.build ++++ b/test/meson.build +@@ -51,6 +51,9 @@ if install_tests + install_subdir('testsuite-63.units', + exclude_files : '.gitattributes', + install_dir : testdata_dir) ++ install_subdir('testsuite-80.units', ++ exclude_files : '.gitattributes', ++ install_dir : testdata_dir) + + testsuite08_dir = testdata_dir + '/testsuite-08.units' + install_data('testsuite-08.units/-.mount', +diff --git a/test/testsuite-80.units/notify.service b/test/testsuite-80.units/notify.service +new file mode 100644 +index 0000000..196b076 +--- /dev/null ++++ b/test/testsuite-80.units/notify.service +@@ -0,0 +1,4 @@ ++[Service] ++Type=notify ++NotifyAccess=all ++ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/test.sh +diff --git a/test/testsuite-80.units/test.sh b/test/testsuite-80.units/test.sh +new file mode 100755 +index 0000000..3ca71d5 +--- /dev/null ++++ b/test/testsuite-80.units/test.sh +@@ -0,0 +1,26 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++# shellcheck disable=SC2016 ++set -eux ++set -o pipefail ++ ++systemd-notify --status="Test starts, waiting for 5 seconds" ++sleep 5 ++ ++( ++ systemd-notify --pid=auto ++ systemd-notify "NOTIFYACCESS=main" ++ ++ systemd-notify --status="Sending READY=1 in an unpriviledged process" ++ ( ++ sleep 0.1 ++ systemd-notify --ready ++ ) ++ sleep 10 ++ ++ systemd-notify "MAINPID=$$" ++) ++ ++systemd-notify --ready --status="OK" ++systemd-notify "NOTIFYACCESS=none" ++sleep infinity +diff --git a/test/units/testsuite-80.service b/test/units/testsuite-80.service +new file mode 100644 +index 0000000..4c7f5d5 +--- /dev/null ++++ b/test/units/testsuite-80.service +@@ -0,0 +1,8 @@ ++# SPDX-License-Identifier: LGPL-2.1-or-later ++[Unit] ++Description=TEST-80-NOTIFYACCESS ++ ++[Service] ++ExecStartPre=rm -f /failed /testok ++ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh ++Type=oneshot +diff --git a/test/units/testsuite-80.sh b/test/units/testsuite-80.sh +new file mode 100755 +index 0000000..5f57569 +--- /dev/null ++++ b/test/units/testsuite-80.sh +@@ -0,0 +1,32 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++# shellcheck disable=SC2016 ++set -eux ++set -o pipefail ++ ++# shellcheck source=test/units/assert.sh ++. "$(dirname "$0")"/assert.sh ++ ++: >/failed ++ ++systemctl --no-block start notify.service ++sleep 2 ++ ++assert_eq "$(systemctl show notify.service -p StatusText --value)" "Test starts, waiting for 5 seconds" ++assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" ++sleep 5 ++ ++assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "main" ++assert_eq "$(systemctl show notify.service -p StatusText --value)" "Sending READY=1 in an unpriviledged process" ++assert_rc 3 systemctl --quiet is-active notify.service ++sleep 10 ++ ++systemctl --quiet is-active notify.service ++assert_eq "$(systemctl show notify.service -p StatusText --value)" "OK" ++assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "none" ++ ++systemctl stop notify.service ++assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" ++ ++touch /testok ++rm /failed +-- +2.33.0 + diff --git a/backport-test-validate-that-fdstore-pinning-works.patch b/backport-test-validate-that-fdstore-pinning-works.patch new file mode 100644 index 0000000..17159d8 --- /dev/null +++ b/backport-test-validate-that-fdstore-pinning-works.patch @@ -0,0 +1,186 @@ +From 3540ce8587cbd21ce9c2dbec72ea7fa3d1b38a5f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 4 Apr 2023 11:41:55 +0200 +Subject: [PATCH] test: validate that fdstore pinning works + +Conflict:Adaptation Context. +Reference:https://github.com/systemd/systemd/commit/3540ce8587cbd21ce9c2dbec72ea7fa3d1b38a5f + +--- + test/testsuite-80.units/fdstore-nopin.service | 8 +++ + test/testsuite-80.units/fdstore-pin.service | 8 +++ + test/testsuite-80.units/fdstore-pin.sh | 47 +++++++++++++++++ + test/testsuite-80.units/fdstore-pin.target | 3 ++ + test/units/testsuite-80.service | 2 + + test/units/testsuite-80.sh | 51 +++++++++++++++++++ + 6 files changed, 119 insertions(+) + create mode 100644 test/testsuite-80.units/fdstore-nopin.service + create mode 100644 test/testsuite-80.units/fdstore-pin.service + create mode 100755 test/testsuite-80.units/fdstore-pin.sh + create mode 100644 test/testsuite-80.units/fdstore-pin.target + +diff --git a/test/testsuite-80.units/fdstore-nopin.service b/test/testsuite-80.units/fdstore-nopin.service +new file mode 100644 +index 0000000..58a687a +--- /dev/null ++++ b/test/testsuite-80.units/fdstore-nopin.service +@@ -0,0 +1,8 @@ ++[Service] ++Type=notify ++NotifyAccess=all ++FileDescriptorStoreMax=10 ++FileDescriptorStorePreserve=restart ++ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 0 ++StandardOutput=journal+console ++StandardError=journal+console +diff --git a/test/testsuite-80.units/fdstore-pin.service b/test/testsuite-80.units/fdstore-pin.service +new file mode 100644 +index 0000000..bc78ee0 +--- /dev/null ++++ b/test/testsuite-80.units/fdstore-pin.service +@@ -0,0 +1,8 @@ ++[Service] ++Type=notify ++NotifyAccess=all ++FileDescriptorStoreMax=10 ++FileDescriptorStorePreserve=yes ++ExecStart=/usr/lib/systemd/tests/testdata/testsuite-80.units/fdstore-pin.sh 1 ++StandardOutput=journal+console ++StandardError=journal+console +diff --git a/test/testsuite-80.units/fdstore-pin.sh b/test/testsuite-80.units/fdstore-pin.sh +new file mode 100755 +index 0000000..4cb041a +--- /dev/null ++++ b/test/testsuite-80.units/fdstore-pin.sh +@@ -0,0 +1,47 @@ ++#!/usr/bin/env bash ++# SPDX-License-Identifier: LGPL-2.1-or-later ++set -eux ++set -o pipefail ++ ++PINNED="$1" ++COUNTER="/tmp/fdstore-invoked.$PINNED" ++FILE="/tmp/fdstore-data.$PINNED" ++ ++# This script is called six times: thrice from a service unit where the fdstore ++# is pinned, and thrice where it isn't. The second iteration of each series is ++# a restart, the third a stop followed by a start ++ ++if [ -e "$COUNTER" ] ; then ++ read -r N < "$COUNTER" ++else ++ N=0 ++fi ++ ++echo "Invocation #$N with PINNED=$PINNED." ++ ++if [ "$N" -eq 0 ] ; then ++ # First iteration ++ test "${LISTEN_FDS:-0}" -eq 0 ++ test ! -e "$FILE" ++ echo waldi > "$FILE" ++ systemd-notify --fd=3 --fdname="fd-$N-$PINNED" 3< "$FILE" ++elif [ "$N" -eq 1 ] || { [ "$N" -eq 2 ] && [ "$PINNED" -eq 1 ]; } ; then ++ # Second iteration, or iteration with pinning on ++ test "${LISTEN_FDS:-0}" -eq 1 ++ # We reopen fd #3 here, so that the read offset is at zero each time (hence no <&3 here…) ++ read -r word < /proc/self/fd/3 ++ test "$word" = "waldi" ++else ++ test "${LISTEN_FDS:-0}" -eq 0 ++ test -e "$FILE" ++fi ++ ++if [ "$N" -ge 2 ] ; then ++ rm "$COUNTER" "$FILE" ++else ++ echo $((N + 1)) > "$COUNTER" ++fi ++ ++systemd-notify --ready --status="Ready" ++ ++exec sleep infinity +diff --git a/test/testsuite-80.units/fdstore-pin.target b/test/testsuite-80.units/fdstore-pin.target +new file mode 100644 +index 0000000..319b7e1 +--- /dev/null ++++ b/test/testsuite-80.units/fdstore-pin.target +@@ -0,0 +1,3 @@ ++[Unit] ++After=fdstore-pin.service fdstore-nopin.service ++Wants=fdstore-pin.service fdstore-nopin.service +diff --git a/test/units/testsuite-80.service b/test/units/testsuite-80.service +index 4c7f5d5..82b08a1 100644 +--- a/test/units/testsuite-80.service ++++ b/test/units/testsuite-80.service +@@ -6,3 +6,5 @@ Description=TEST-80-NOTIFYACCESS + ExecStartPre=rm -f /failed /testok + ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh + Type=oneshot ++StandardOutput=journal+console ++StandardError=journal+console +diff --git a/test/units/testsuite-80.sh b/test/units/testsuite-80.sh +index 43647a7..9a979b9 100755 +--- a/test/units/testsuite-80.sh ++++ b/test/units/testsuite-80.sh +@@ -48,5 +48,56 @@ assert_eq "$(systemctl show notify.service -p NotifyAccess --value)" "all" + + rm /tmp/syncfifo1 /tmp/syncfifo2 + ++# Now test basic fdstore behaviour ++ ++systemd-analyze log-level debug ++ ++# Test fdstore pinning (this will pull in fdstore-pin.service fdstore-nopin.service) ++systemctl start fdstore-pin.target ++ ++assert_eq "$(systemctl show fdstore-pin.service -P FileDescriptorStorePreserve)" yes ++assert_eq "$(systemctl show fdstore-nopin.service -P FileDescriptorStorePreserve)" restart ++assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running ++assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running ++assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 ++assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1 ++ ++# The file descriptor store should survive service restarts ++systemctl restart fdstore-pin.service fdstore-nopin.service ++ ++assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 ++assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 1 ++assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running ++assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running ++ ++# It should not survive the service stop plus a later start (unless pinned) ++systemctl stop fdstore-pin.service fdstore-nopin.service ++ ++assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 ++assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 ++assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned ++assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead ++ ++systemctl start fdstore-pin.service fdstore-nopin.service ++ ++assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 ++assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 ++assert_eq "$(systemctl show fdstore-pin.service -P SubState)" running ++assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" running ++ ++systemctl stop fdstore-pin.service fdstore-nopin.service ++ ++assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 1 ++assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 ++assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead-resources-pinned ++assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead ++ ++systemctl clean fdstore-pin.service --what=fdstore ++ ++assert_eq "$(systemctl show fdstore-pin.service -P NFileDescriptorStore)" 0 ++assert_eq "$(systemctl show fdstore-nopin.service -P NFileDescriptorStore)" 0 ++assert_eq "$(systemctl show fdstore-pin.service -P SubState)" dead ++assert_eq "$(systemctl show fdstore-nopin.service -P SubState)" dead ++ + touch /testok + rm /failed + +-- +2.33.0 + diff --git a/backport-unit-don-t-gc-unit-in-oom-queue.patch b/backport-unit-don-t-gc-unit-in-oom-queue.patch new file mode 100644 index 0000000..a11d30d --- /dev/null +++ b/backport-unit-don-t-gc-unit-in-oom-queue.patch @@ -0,0 +1,49 @@ +From 935f80428fd3220c83163cc4b5a637873e68babb Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Jun 2023 11:11:28 +0200 +Subject: [PATCH] unit: don't gc unit in oom queue + +This is a follow-up for 8db998981a4fefd0122bcf5f965726b63c9045c2, and +follows a similar logic: a pending OOM event really trumps everything: +we should not GC a unit while it is pending. + +Conflict:NA +Reference:https://github.com/systemd/systemd/commit/935f80428fd3220c83163cc4b5a637873e68babb + +--- + src/core/cgroup.c | 2 ++ + src/core/unit.c | 5 ++++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 839b1676c8..34643b242c 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -3319,6 +3319,8 @@ static int on_cgroup_oom_event(sd_event_source *s, void *userdata) { + } + + (void) unit_check_oom(u); ++ unit_add_to_gc_queue(u); ++ + return 0; + } + +diff --git a/src/core/unit.c b/src/core/unit.c +index 7b6d50e0ea..80f398c309 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -441,7 +441,10 @@ bool unit_may_gc(Unit *u) { + if (u->perpetual) + return false; + +- if (u->in_cgroup_empty_queue) ++ /* if we saw a cgroup empty event for this unit, stay around until we processed it so that we remove ++ * the empty cgroup if possible. Similar, process any pending OOM events if they are already queued ++ * before we release the unit. */ ++ if (u->in_cgroup_empty_queue || u->in_cgroup_oom_queue) + return false; + + if (sd_bus_track_count(u->bus_track) > 0) +-- +2.33.0 + diff --git a/systemd.spec b/systemd.spec index aa7f3f8..2eb8db7 100644 --- a/systemd.spec +++ b/systemd.spec @@ -21,7 +21,7 @@ Name: systemd Url: https://systemd.io/ Version: 249 -Release: 79 +Release: 80 License: MIT and LGPLv2+ and GPLv2+ Summary: System and Service Manager @@ -642,6 +642,35 @@ Patch6593: backport-CVE-2023-50387.patch Patch6594: backport-CVE-2023-50868.patch Patch6595: backport-login-user-runtime-dir-properly-check-for-mount-poin.patch Patch6596: backport-user-util-validate-the-right-field.patch +Patch6597: backport-core-fix-property-getter-method-for-NFileDescriptorS.patch +Patch6598: backport-Add-implicit-sentinel-to-strv_env_merge.patch +Patch6599: backport-basic-strv-inline-variables-and-modernize-style-a-bi.patch +Patch6600: backport-strv-rewrite-strv_copy-with-cleanup-attribute-and-ST.patch +Patch6601: backport-strv-add-strv_copy_n-helper-for-copying-part-of-a-n-.patch +Patch6602: backport-fd-util-introduce-dir_fd_is_root_or_cwd.patch +Patch6603: backport-notify-add-new-exec-switch-for-chaining-other-comman.patch +Patch6604: backport-fdset-add-new-fdset_consume-helper.patch +Patch6605: backport-notify-add-support-for-sending-fds-with-notification.patch +Patch6606: backport-socket-various-modernizations.patch +Patch6607: backport-socket-always-pass-socket-fd-and-SocketPeer-ownershi.patch +Patch6608: backport-fdset-add-new-helper-to-convert-an-fdset-to-an-array.patch +Patch6609: backport-service-release-resources-from-a-seperate-queue-not-.patch +Patch6610: backport-service-drop-redundant-unit_ref_unset-call.patch +Patch6611: backport-service-rework-how-we-release-resources.patch +Patch6612: backport-service-add-ability-to-pin-fd-store.patch +Patch6613: backport-service-allow-freeing-the-fdstore-via-cleaning.patch +Patch6614: backport-service-close-fdstore-asynchronously.patch +Patch6615: backport-core-move-runtime-directory-removal-into-release_res.patch +Patch6616: backport-service-rename-service_close_socket_fd-service_relea.patch +Patch6617: backport-pid1-add-some-debug-logging-when-stashing-ds-into-th.patch +Patch6618: backport-meson-exclude-.gitattributes-when-using-install_subd.patch +Patch6619: backport-core-support-overriding-NOTIFYACCESS-through-sd-noti.patch +Patch6620: backport-test-add-tests-for-NOTIFYACCESS-override-through-sd_.patch +Patch6621: backport-TEST-80-synchronize-explicitly-instead-of-by-time.patch +Patch6622: backport-test-validate-that-fdstore-pinning-works.patch +Patch6623: backport-core-Don-t-GC-unit-if-it-is-in-cgroup_empty_queue.patch +Patch6624: backport-unit-don-t-gc-unit-in-oom-queue.patch +Patch6625: backport-core-do-not-GC-units-jobs-that-are-in-the-D-Bus-queu.patch Patch9001: update-rtc-with-system-clock-when-shutdown.patch Patch9002: udev-add-actions-while-rename-netif-failed.patch @@ -2156,6 +2185,9 @@ grep -q -E '^KEYMAP="?fi-latin[19]"?' /etc/vconsole.conf 2>/dev/null && /usr/bin/systemd-cryptenroll %changelog +* Mon Jun 17 2024 zhangyao - 249-80 +- Add the FileDescriptorStorePreserve= option to the service + * Tue Jun 11 2024 wangyuhang - 249-79 - extract systemd-cryptsetup -- Gitee