Add the FileDescriptorStorePreserve= option to the service

This commit is contained in:
zhangyao 2024-06-17 14:15:37 +08:00
parent 808da02699
commit 995e1ad82b
30 changed files with 4558 additions and 1 deletions

View File

@ -0,0 +1,310 @@
From 4ab3d29ff03c0508f47846875d9310cbc5b8cd0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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

View File

@ -0,0 +1,166 @@
From 09ba6d1a14ba027f2bc4e3426c7dd85db19e720e Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,453 @@
From 14337c374a641badf55cf9ce6ec1fb387886b651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
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

View File

@ -0,0 +1,92 @@
From 8db998981a4fefd0122bcf5f965726b63c9045c2 Mon Sep 17 00:00:00 2001
From: Richard Phibel <rphibel@googlemail.com>
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 <<EOF >/run/systemd/system/test19cleanup.service
+[Unit]
+Description=Test 19 cleanup Service
+[Service]
+Slice=test19cleanup.slice
+Type=exec
+ExecStart=sleep infinity
+KillMode=none
+EOF
+cat <<EOF >/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

View File

@ -0,0 +1,84 @@
From af05bb971731fe7280e4e85fde71c2e671772c18 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,72 @@
From 47226e893b24f1aa84caaa6be266fb3c03442904 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,90 @@
From 1ba84fef3c1e505cb1413ef446a85c0c7ec439c6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,294 @@
From 19dff6914dee94b36320dbfda94f60af30ac65c1 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
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…</literal></para></listitem>
</varlistentry>
+ <varlistentry>
+ <term>NOTIFYACCESS=…</term>
+
+ <listitem><para>Reset the access to the service status notification
+ socket during runtime, overriding <varname>NotifyAccess=</varname> setting
+ in the service unit file. See <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details, specifically <literal>NotifyAccess=</literal> for a list of
+ accepted values.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term>ERRNO=…</term>
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

View File

@ -0,0 +1,27 @@
From e212f422796da9e626030289faf083407c8955df Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
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 <dirent.h>
+#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/socket.h>
--
2.33.0

View File

@ -0,0 +1,52 @@
From e829f28c1bc6e6865261bfb3bc26089f50e0c7bd Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,86 @@
From bdcad22e8e508308032cba5f8c2d5c093adb7d87 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,95 @@
From 082c67616511c7c08627eaf19af7e59a7f360479 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <luca.boccassi@microsoft.com>
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

View File

@ -0,0 +1,150 @@
From 9175338e09de56c99cebf0b9f215f3e9e0016cb9 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,258 @@
From 6e4a324574d5f1b2296799324dbdb54326078233 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--fd=</option></term>
+
+ <listitem><para>Send a file descriptor along with the notification message. This is useful when
+ invoked in services that have the <varname>FileDescriptorStoreMax=</varname> setting enabled, see
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details. The specified file descriptor must be passed to <command>systemd-notify</command> when
+ invoked. This option may be used multiple times to pass multiple file descriptors in a single
+ notification message.</para>
+
+ <para>To use this functionality from a <command>bash</command> shell, use an expression like the following:</para>
+ <programlisting>systemd-notify --fd=4 --fd=5 4&lt;/some/file 5&lt;/some/other/file</programlisting></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><option>--fdname=</option></term>
+
+ <listitem><para>Set a name to assign to the file descriptors passed via <option>--fd=</option> (see
+ above). This controls the <literal>FDNAME=</literal> 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.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
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

View File

@ -0,0 +1,54 @@
From e8783d7620e4811738be078480aa5ffc9bbdcf9b Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,476 @@
From b9c1883a9cd9b5126fe648f3e198143dc19a222d Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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 {
<!--property NFileDescriptorStore is not documented!-->
+ <!--property FileDescriptorStorePreserve is not documented!-->
+
<!--property StatusErrno is not documented!-->
<!--property ReloadResult is not documented!-->
@@ -3430,6 +3434,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="NFileDescriptorStore"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="FileDescriptorStorePreserve"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="StatusText"/>
<variablelist class="dbus-property" generated="True" extra-ref="StatusErrno"/>
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 @@
<literal>FDSTORE=1</literal> 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 <filename>/run/</filename>, or better, stored in a
+ can either be serialized to a file in <varname>RuntimeDirectory=</varname>, or stored in a
<citerefentry><refentrytitle>memfd_create</refentrytitle><manvolnum>2</manvolnum></citerefentry>
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
<constant>POLLHUP</constant> or <constant>POLLERR</constant> 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
+ <varname>FileDescriptorStorePreserve=</varname>, see below). If this option is used,
<varname>NotifyAccess=</varname> (see above) should be set to open access to the notification socket
provided by systemd. If <varname>NotifyAccess=</varname> is not set, it will be implicitly set to
<option>main</option>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>FileDescriptorStorePreserve=</varname></term>
+ <listitem><para>Takes one of <constant>no</constant>, <constant>yes</constant>,
+ <constant>restart</constant> 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 <constant>no</constant> the
+ file descriptor store is automatically released when the service is stopped; if
+ <constant>restart</constant> (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
+ <constant>yes</constant> 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.</para>
+
+ <para>Use <command>systemctl clean --what=fdstore …</command> to release the file descriptor store
+ explicitly.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>USBFunctionDescriptors=</varname></term>
<listitem><para>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

View File

@ -0,0 +1,283 @@
From 4fb8f1e88322b94b0fa051d3c6fd19cac0227aaa Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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
<varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>,
<varname>LogsDirectory=</varname> and <varname>RuntimeDirectory=</varname>, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- 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
+ <varname>FileDescriptorStoreMax=</varname>, see
+ <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details. For timer units this may be used to clear out the persistent timestamp data if
<varname>Persistent=</varname> is used and <option>--what=state</option> is selected, see
<citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
command only applies to units that use either of these settings. If <option>--what=</option> 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).</para>
+ 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.</para>
</listitem>
</varlistentry>
<varlistentry>
@@ -2076,13 +2080,17 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
<listitem>
<para>Select what type of per-unit resources to remove when the <command>clean</command> command is
- invoked, see below. Takes one of <constant>configuration</constant>, <constant>state</constant>,
- <constant>cache</constant>, <constant>logs</constant>, <constant>runtime</constant> 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 <constant>all</constant> as a shortcut for
- specifying all five resource types. If this option is not specified defaults to the combination of
- <constant>cache</constant> and <constant>runtime</constant>, i.e. the two kinds of resources that
- are generally considered to be redundant and can be reconstructed on next invocation.</para>
+ invoked, see above. Takes one of <constant>configuration</constant>, <constant>state</constant>,
+ <constant>cache</constant>, <constant>logs</constant>, <constant>runtime</constant>,
+ <constant>fdstore</constant> 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
+ <constant>all</constant> as a shortcut for specifying all six resource types. If this option is not
+ specified defaults to the combination of <constant>cache</constant>, <constant>runtime</constant>
+ and <constant>fdstore</constant>, 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
+ <constant>fdstore</constant> resource type is only useful if the
+ <varname>FileDescriptorStorePreserve=</varname> option is enabled, since the file descriptor store
+ is otherwise cleaned automatically when the unit is stopped.</para>
</listitem>
</varlistentry>
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

View File

@ -0,0 +1,90 @@
From 99620f457ed0886852ba18c9093b59767299121c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,31 @@
From d53bda316be6ba226788b40e101a3bff5aa75a5f Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,286 @@
From 6ac62d61db737b01ad3776a7688d8a4c57b3f7d9 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,95 @@
From 81a1d6d6790ad6ae8ff63147dfab68e6835178c3 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,137 @@
From c25fac9a17b95271bb6f8d967d33c5a9aa9e4bc9 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,174 @@
From 3fabebf45e268a0e1e8f9e7688b2428f8ee8a802 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,94 @@
From 000b61b980abba1a69d7a5e2a2a073930eca5e08 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,125 @@
From 4ea517a6e07f47117348c68c6fe087bf6d401558 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,56 @@
From 9eb814818d0c35f0c9e05cb596508c1536b0e654 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
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

View File

@ -0,0 +1,160 @@
From b64f5ddacad725d5e4e021691fad52aa2eb32c46 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
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

View File

@ -0,0 +1,186 @@
From 3540ce8587cbd21ce9c2dbec72ea7fa3d1b38a5f Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,49 @@
From 935f80428fd3220c83163cc4b5a637873e68babb Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -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 <zhangyao108@huawei.com> - 249-80
- Add the FileDescriptorStorePreserve= option to the service
* Tue Jun 11 2024 wangyuhang <wangyuhang27@huawei.com> - 249-79
- extract systemd-cryptsetup