From b30f77cd125db1cca0eca95ba2d6dcbc5fffe45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 17 Dec 2025 22:51:26 +0100 Subject: [PATCH 01/10] ssh-generator: reword error message We have two error messages with exactly the same message. Let's change one so that it is possible to distinguish them in logs. --- src/ssh-generator/ssh-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssh-generator/ssh-util.c b/src/ssh-generator/ssh-util.c index 48cb662b71026..b7af0454870ed 100644 --- a/src/ssh-generator/ssh-util.c +++ b/src/ssh-generator/ssh-util.c @@ -35,6 +35,6 @@ int vsock_get_local_cid_or_warn(unsigned *ret) { return 0; } if (r < 0) - return log_error_errno(r, "Failed to query local AF_VSOCK CID: %m"); + return log_error_errno(r, "Failed to query host's AF_VSOCK CID: %m"); return 1; } From 1ee73c884e4fecfae57433b9d50caa72816403d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 4 Jan 2026 12:18:38 +0100 Subject: [PATCH 02/10] basic/process-util: use synthetic errno in two more places --- src/basic/process-util.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 42709202a537c..92b851573f08a 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -894,14 +894,12 @@ int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFl return status.si_status; - } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { + } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) + return log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO), + "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); - log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); - return -EPROTO; - } - - log_full(prio, "%s failed due to unknown reason.", strna(name)); - return -EPROTO; + return log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO), + "%s failed due to unknown reason.", strna(name)); } int kill_and_sigcont(pid_t pid, int sig) { From 22aa8c48799b51df7e8b176bbec5645174fd9261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 4 Jan 2026 12:21:52 +0100 Subject: [PATCH 03/10] basic/process-util: reduce scope of variables --- src/basic/process-util.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 92b851573f08a..e63f9fad6716e 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -389,11 +389,6 @@ int container_get_leader(const char *machine, pid_t *pid) { } int pid_is_kernel_thread(pid_t pid) { - _cleanup_free_ char *line = NULL; - unsigned long long flags; - size_t l, i; - const char *p; - char *q; int r; if (IN_SET(pid, 0, 1) || pid == getpid_cached()) /* pid 1, and we ourselves certainly aren't a kernel thread */ @@ -401,7 +396,8 @@ int pid_is_kernel_thread(pid_t pid) { if (!pid_is_valid(pid)) return -EINVAL; - p = procfs_file_alloca(pid, "stat"); + const char *p = procfs_file_alloca(pid, "stat"); + _cleanup_free_ char *line = NULL; r = read_one_line_file(p, &line); if (r == -ENOENT) return -ESRCH; @@ -409,14 +405,14 @@ int pid_is_kernel_thread(pid_t pid) { return r; /* Skip past the comm field */ - q = strrchr(line, ')'); + char *q = strrchr(line, ')'); if (!q) return -EINVAL; q++; /* Skip 6 fields to reach the flags field */ - for (i = 0; i < 6; i++) { - l = strspn(q, WHITESPACE); + for (size_t i = 0; i < 6; i++) { + size_t l = strspn(q, WHITESPACE); if (l < 1) return -EINVAL; q += l; @@ -428,7 +424,7 @@ int pid_is_kernel_thread(pid_t pid) { } /* Skip preceding whitespace */ - l = strspn(q, WHITESPACE); + size_t l = strspn(q, WHITESPACE); if (l < 1) return -EINVAL; q += l; @@ -439,6 +435,7 @@ int pid_is_kernel_thread(pid_t pid) { return -EINVAL; q[l] = 0; + unsigned long long flags; r = safe_atollu(q, &flags); if (r < 0) return r; From 4a4be1015b1a3c9efa974cbb1dbe1c776aab4f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 4 Jan 2026 12:25:32 +0100 Subject: [PATCH 04/10] inhibit: fix borked double logging on error Previously, if execution failed, we'd log at error level both from the child and the parent, and we were using a bogus variable for the argument name: $ build/systemd-inhibit list Failed to execute : No such file or directory list failed with exit status 1. In general, we can and should assume that the program the user is calling is well behaved, so it'll log the error on its own if appropriate. So we shouldn't log on "normal errors", but only if the child is terminated by a signal. And since the program name is controlled by the user, use quotes everywhere to avoid ambiguity. Now: $ build/systemd-inhibit false (nothing) $ build/systemd-inhibit bash -c 'kill -SEGV $$' src/basic/process-util.c:895: 'bash' terminated by signal SEGV. --- src/basic/process-util.c | 10 +++++----- src/login/inhibit.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index e63f9fad6716e..1ff59dd374a50 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -880,23 +880,23 @@ int pidref_wait_for_terminate_and_check(const char *name, PidRef *pidref, WaitFl siginfo_t status; r = pidref_wait_for_terminate(pidref, &status); if (r < 0) - return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); + return log_full_errno(prio, r, "Failed to wait for '%s': %m", strna(name)); if (status.si_code == CLD_EXITED) { if (status.si_status != EXIT_SUCCESS) log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, - "%s failed with exit status %i.", strna(name), status.si_status); + "'%s' failed with exit status %i.", strna(name), status.si_status); else - log_debug("%s succeeded.", name); + log_debug("'%s' succeeded.", name); return status.si_status; } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) return log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO), - "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); + "'%s' terminated by signal %s.", strna(name), signal_to_string(status.si_status)); return log_full_errno(prio, SYNTHETIC_ERRNO(EPROTO), - "%s failed due to unknown reason.", strna(name)); + "'%s' failed due to unknown reason.", strna(name)); } int kill_and_sigcont(pid_t pid, int sig) { diff --git a/src/login/inhibit.c b/src/login/inhibit.c index 786594c9ee33d..4df17d2d69973 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -368,11 +368,11 @@ static int run(int argc, char *argv[]) { /* Child */ execvp(arguments[0], arguments); log_open(); - log_error_errno(errno, "Failed to execute %s: %m", argv[optind]); + log_error_errno(errno, "Failed to execute '%s': %m", arguments[0]); _exit(EXIT_FAILURE); } - return pidref_wait_for_terminate_and_check(argv[optind], &pidref, WAIT_LOG); + return pidref_wait_for_terminate_and_check(argv[optind], &pidref, WAIT_LOG_ABNORMAL); } } From 239903d44c12f10b5fe7c1f8457ae5203e47d8cc Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Sat, 27 Dec 2025 15:24:45 +0100 Subject: [PATCH 05/10] nss-systemd: set sg_adm/sg_mem in intrinsic groups The sg_adm and sg_mem fields are supposed to point to a NULL terminated string array. If these are NULL, some foreign tools like shadow's sg trigger NULL pointer dereferences (or fortunately their asset() calls). --- src/nss-systemd/nss-systemd.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index ae0b5c803df0e..11384dbad8446 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -75,6 +75,8 @@ static const struct group root_group = { static const struct sgrp root_sgrp = { .sg_namp = (char*) "root", .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID, + .sg_adm = (char*[]) { NULL }, + .sg_mem = (char*[]) { NULL }, }; static const struct group nobody_group = { @@ -87,6 +89,8 @@ static const struct group nobody_group = { static const struct sgrp nobody_sgrp = { .sg_namp = (char*) NOBODY_GROUP_NAME, .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID, + .sg_adm = (char*[]) { NULL }, + .sg_mem = (char*[]) { NULL }, }; typedef struct GetentData { @@ -257,12 +261,18 @@ static enum nss_status copy_synthesized_sgrp( assert(src); assert(src->sg_namp); assert(src->sg_passwd); + assert(src->sg_adm); + assert(!*src->sg_adm); /* Our synthesized records' sg_adm is always just NULL... */ + assert(src->sg_mem); + assert(!*src->sg_mem); /* Our synthesized records' sg_mem is always just NULL... */ size_t required = strlen(src->sg_namp) + 1 + - strlen(src->sg_passwd) + 1; + strlen(src->sg_passwd) + 1 + + sizeof(char*) + /* NULL terminator storage for src->sg_adm */ + sizeof(char*); /* NULL terminator storage for src->sg_mem */ - if (buflen < required) { + if (buflen < ALIGN(required)) { *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -274,7 +284,10 @@ static enum nss_status copy_synthesized_sgrp( /* String fields point into the user-provided buffer */ dest->sg_namp = buffer; dest->sg_passwd = stpcpy(dest->sg_namp, src->sg_namp) + 1; - strcpy(dest->sg_passwd, src->sg_passwd); + dest->sg_adm = ALIGN_PTR(stpcpy(dest->sg_passwd, src->sg_passwd) + 1); + *dest->sg_adm = NULL; + dest->sg_mem = dest->sg_adm + 1; + *dest->sg_mem = NULL; return NSS_STATUS_SUCCESS; } From 57682793dac269994d6e69f7a5a937f5ad459cc8 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Sat, 27 Dec 2025 15:27:05 +0100 Subject: [PATCH 06/10] nss-systemd: set sg_adm/sg_mem for all groups Fill sg_adm and sg_mem in nss_pack_group_record_shadow to stay compatible with other NSS getsgnam implementations which set these members to NULL terminated string arrays. Tools like shadow's sg would trigger a NULL pointer dereference with groups only found through nss-systemd otherwise. --- src/nss-systemd/userdb-glue.c | 56 +++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 70f0ecfbd5f4c..1d5e311ce8653 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -411,6 +411,40 @@ enum nss_status userdb_getgrgid( return NSS_STATUS_SUCCESS; } +/* Counts string pointers (including terminating NULL element) of given + * string vector strv and stores amount of pointers in n and total + * length of all contained strings including NUL bytes in len. */ +static void nss_count_strv(char * const *strv, size_t *n, size_t *len) { + STRV_FOREACH(str, strv) { + (*len) += sizeof(char*); /* space for array entry */ + (*len) += strlen(*str) + 1; + (*n)++; + } + (*len) += sizeof(char*); /* trailing NULL in array entry */ + (*n)++; +} + +/* Performs deep copy of given string vector src and stores content + * of contained strings into buf with references to these strings + * in dst. At dst location, a new NULL-terminated string vector is + * created. The dst and buf locations are updated to point just behind + * the last pointer or char respectively. Returns total amount of + * pointers in newly created string vector in dst, including the + * terminating NULL element. */ +static size_t nss_deep_copy_strv(char * const *src, char ***dst, char **buf) { + char *p = *buf; + size_t i = 0; + + STRV_FOREACH(str, src) { + (*dst)[i++] = p; + p = stpcpy(p, *str) + 1; + } + (*dst)[i++] = NULL; + *dst += i; + *buf = p; + return i; +} + int nss_pack_group_record_shadow( GroupRecord *hr, struct sgrp *sgrp, @@ -418,7 +452,8 @@ int nss_pack_group_record_shadow( size_t buflen) { const char *hashed; - size_t required; + char **array = NULL, *p; + size_t i = 0, n = 0, required; assert(hr); assert(sgrp); @@ -429,15 +464,26 @@ int nss_pack_group_record_shadow( assert_se(hashed = strv_isempty(hr->hashed_password) ? PASSWORD_LOCKED_AND_INVALID : hr->hashed_password[0]); required += strlen(hashed) + 1; + nss_count_strv(hr->administrators, &n, &required); + nss_count_strv(hr->members, &n, &required); + if (buflen < required) return -ERANGE; - *sgrp = (struct sgrp) { - .sg_namp = buffer, - }; - assert(buffer); + p = buffer + sizeof(void*) * (n + 1); /* place member strings right after the ptr array */ + array = (char**) buffer; /* place ptr array at beginning of buffer, under assumption buffer is aligned */ + + sgrp->sg_mem = array; + i += nss_deep_copy_strv(hr->members, &array, &p); + + sgrp->sg_adm = array; + i += nss_deep_copy_strv(hr->administrators, &array, &p); + + assert_se(i == n); + + sgrp->sg_namp = p; sgrp->sg_passwd = stpcpy(sgrp->sg_namp, hr->group_name) + 1; strcpy(sgrp->sg_passwd, hashed); From 2eaca3ea5f0de702382d388d3bbc753c000ada4b Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Sun, 4 Jan 2026 12:19:22 +0100 Subject: [PATCH 07/10] nss-systemd: add unit test for sg_adm/sg_mem Add a test for getsgnam_r to verify that sg_adm and sg_mem always point to a NULL-terminated string vector. Extend the gr_mem check of struct group for non-NULL values as well. --- src/shared/nss-util.h | 7 ++++++ src/test/test-nss-users.c | 45 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/shared/nss-util.h b/src/shared/nss-util.h index c182237368afc..8f9c4e1ff30bf 100644 --- a/src/shared/nss-util.h +++ b/src/shared/nss-util.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include #include #include @@ -291,3 +292,9 @@ typedef enum nss_status (*_nss_getgrgid_r_t)( struct group *gr, char *buffer, size_t buflen, int *errnop); + +typedef enum nss_status (*_nss_getsgnam_r_t)( + const char *name, + struct sgrp *sg, + char *buffer, size_t buflen, + int *errnop); diff --git a/src/test/test-nss-users.c b/src/test/test-nss-users.c index f253b53c1f015..b9078f745e620 100644 --- a/src/test/test-nss-users.c +++ b/src/test/test-nss-users.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #include @@ -35,7 +36,22 @@ static void print_struct_group(const struct group *gr) { gr->gr_name, gr->gr_gid); log_info(" passwd=\"%s\"", gr->gr_passwd); - assert_se(members = strv_join(gr->gr_mem, ", ")); + assert_se(members = strv_join(ASSERT_PTR(gr->gr_mem), ", ")); + // FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available + log_info(" members=%s", members); +} + +static void print_struct_sgrp(const struct sgrp *sg) { + _cleanup_free_ char *administrators = NULL, *members = NULL; + + log_info(" \"%s\"", sg->sg_namp); + log_info(" passwd=\"%s\"", sg->sg_passwd); + + assert_se(administrators = strv_join(ASSERT_PTR(sg->sg_adm), ", ")); + // FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available + log_info(" administrators=%s", administrators); + + assert_se(members = strv_join(ASSERT_PTR(sg->sg_mem), ", ")); // FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available log_info(" members=%s", members); } @@ -92,6 +108,32 @@ static void test_getgrnam_r(void *handle, const char *module, const char *name) print_struct_group(&gr); } +static void test_getsgnam_r(void *handle, const char *module, const char *name) { + const char *fname; + _nss_getsgnam_r_t f; + char buffer[arg_bufsize]; + int errno1 = 999; /* nss-dns doesn't set those */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + struct sgrp sg; + + fname = strjoina("_nss_", module, "_getsgnam_r"); + f = dlsym(handle, fname); + log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); + if (!f) { + log_info("%s not defined", fname); + return; + } + + status = f(name, &sg, buffer, sizeof buffer, &errno1); + log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s", + fname, name, + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + errno1, errno1 > 0 ? ERRNO_NAME(errno1) : "---"); + if (status == NSS_STATUS_SUCCESS) + print_struct_sgrp(&sg); +} + static void test_getpwuid_r(void *handle, const char *module, uid_t uid) { const char *fname; _nss_getpwuid_r_t f; @@ -147,6 +189,7 @@ static void test_getgrgid_r(void *handle, const char *module, gid_t gid) { static void test_byname(void *handle, const char *module, const char *name) { test_getpwnam_r(handle, module, name); test_getgrnam_r(handle, module, name); + test_getsgnam_r(handle, module, name); puts(""); } From 8a27100d0696d971525efcd8e59b504f56a0b6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 4 Jan 2026 15:10:42 +0100 Subject: [PATCH 08/10] shared/install: ignore aliasing failure when doing presets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In recent Fedora, preset-all fails: [ 155s] Failed to preset unit: File '/buildroot/etc/systemd/user/dbus.service' already exists and is a symlink to /usr/lib/systemd/user/dbus-broker.service [ 155s] ‣ "systemctl --root=/buildroot --global preset-all" returned non-zero exit code 1. Strictly speaking, this is an error in configuration. The presets specify that both dbus-broker.service and dbus-daemon.service shall be enabled and they both claim the 'dbus.service' alias. But this kind of error is very easy to make. Failing the preset operation is too harsh, since in most cases the system will work fine without an alias and changes in unrelated components can cause the conflict. Let's reuse the same logic that was added in ad5fdd391248432e0c105003a8a13f821bde0b8e: when enabling the unit through 'preset' or 'preset-all', print the message, but suppress the error. When enabling through 'enable', fail the operation. --- src/shared/install.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/shared/install.c b/src/shared/install.c index a22c6df2f7b1b..d85ac2df6283a 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -1942,6 +1942,7 @@ int unit_file_verify_alias( static int install_info_symlink_alias( RuntimeScope scope, + UnitFileFlags file_flags, InstallInfo *info, const LookupPaths *lp, const char *config_path, @@ -1996,6 +1997,10 @@ static int install_info_symlink_alias( broken = r == 0; /* symlink target does not exist? */ r = create_symlink(lp, alias_target ?: info->path, alias_path, force || broken, changes, n_changes); + if (r == -EEXIST && FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE)) + /* We cannot realize the alias because a conflicting alias exists. + * Do not propagate this as error. */ + continue; if (r != 0 && ret >= 0) ret = r; } @@ -2160,7 +2165,7 @@ static int install_info_apply( * because they might would pointing to a non-existent or wrong unit. */ return r; - r = install_info_symlink_alias(scope, info, lp, config_path, force, changes, n_changes); + r = install_info_symlink_alias(scope, file_flags, info, lp, config_path, force, changes, n_changes); q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes); if (q != 0 && r >= 0) From f8e1a7a66e248c758bb3741858ccf9838f06d735 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Sun, 4 Jan 2026 14:19:14 +0100 Subject: [PATCH 09/10] man/systemd.socket: Document JoinsNamespaceOf= support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This has been supported since systemd v242 (specifically commit 7619cb32f0 if I’m not mistaken; added to NEWS in commit 4107452e51), but the man page still claimed otherwise. --- man/systemd.socket.xml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 213ceffcdbd74..0de281f02eca7 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -90,7 +90,7 @@ socket passing (i.e. sockets passed in via standard input and output, using StandardInput=socket in the service file). - All network sockets allocated through .socket units are allocated in the host's network + By default, network sockets allocated through .socket units are allocated in the host's network namespace (see network_namespaces7). This does not mean however that the service activated by a configured socket unit has to be part of the host's network @@ -101,6 +101,11 @@ the host's network namespace is only permitted through the activation sockets passed in while all sockets allocated from the service code itself will be associated with the service's own namespace, and thus possibly subject to a restrictive configuration. + + Alternatively, it is possible to run a .socket unit in another network namespace + by setting in combination with JoinsNamespaceOf=, see + systemd.exec5 and + systemd.unit5 for details. From 75f04abf16e0b167803b47acf8c59dd08fde27f0 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Wed, 17 Dec 2025 14:40:48 +0100 Subject: [PATCH 10/10] core: move several checks from _start() to _test_startable() where appropriate If these basic sanity checks fail, there's no point in bumping ratelimit. --- src/core/automount.c | 8 ++++---- src/core/path.c | 8 ++++---- src/core/socket.c | 28 ++++++++++++++-------------- src/core/timer.c | 8 ++++---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/core/automount.c b/src/core/automount.c index 79ccdd3fc70e0..5fd9c8f4ccbc2 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -804,10 +804,6 @@ static int automount_start(Unit *u) { if (path_is_mount_point(a->where) > 0) return log_unit_error_errno(u, SYNTHETIC_ERRNO(EEXIST), "Path %s is already a mount point, refusing start.", a->where); - r = unit_test_trigger_loaded(u); - if (r < 0) - return r; - r = unit_acquire_invocation_id(u); if (r < 0) return r; @@ -1047,6 +1043,10 @@ static int automount_test_startable(Unit *u) { Automount *a = ASSERT_PTR(AUTOMOUNT(u)); int r; + r = unit_test_trigger_loaded(u); + if (r < 0) + return r; + r = unit_test_start_limit(u); if (r < 0) { automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); diff --git a/src/core/path.c b/src/core/path.c index 764dea03dc46e..789ef9e25d6e9 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -632,10 +632,6 @@ static int path_start(Unit *u) { assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED)); - r = unit_test_trigger_loaded(u); - if (r < 0) - return r; - r = unit_acquire_invocation_id(u); if (r < 0) return r; @@ -902,6 +898,10 @@ static int path_test_startable(Unit *u) { Path *p = ASSERT_PTR(PATH(u)); int r; + r = unit_test_trigger_loaded(u); + if (r < 0) + return r; + r = unit_test_start_limit(u); if (r < 0) { path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); diff --git a/src/core/socket.c b/src/core/socket.c index 86e767452843a..6ce307a00f0b1 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2623,20 +2623,6 @@ static int socket_start(Unit *u) { Socket *s = ASSERT_PTR(SOCKET(u)); int r; - /* Cannot run this without the service being around */ - if (UNIT_ISSET(s->service)) { - Service *service = ASSERT_PTR(SERVICE(UNIT_DEREF(s->service))); - - if (UNIT(service)->load_state != UNIT_LOADED) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), - "Socket service %s not loaded, refusing.", UNIT(service)->id); - - /* If the service is already active we cannot start the socket */ - if (SOCKET_SERVICE_IS_ACTIVE(service, /* allow_finalize= */ false)) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), - "Socket service %s already active, refusing.", UNIT(service)->id); - } - assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED)); r = unit_acquire_invocation_id(u); @@ -3642,6 +3628,20 @@ static int socket_test_startable(Unit *u) { SOCKET_START_POST)) return false; + /* Cannot run this without the service being around */ + if (UNIT_ISSET(s->service)) { + Service *service = ASSERT_PTR(SERVICE(UNIT_DEREF(s->service))); + + if (UNIT(service)->load_state != UNIT_LOADED) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "Socket service %s not loaded, refusing.", UNIT(service)->id); + + /* If the service is already active we cannot start the socket */ + if (SOCKET_SERVICE_IS_ACTIVE(service, /* allow_finalize= */ false)) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), + "Socket service %s already active, refusing.", UNIT(service)->id); + } + r = unit_test_start_limit(u); if (r < 0) { socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); diff --git a/src/core/timer.c b/src/core/timer.c index f2d4b5cc3dff9..c591fcd469c7a 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -668,10 +668,6 @@ static int timer_start(Unit *u) { assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED)); - r = unit_test_trigger_loaded(u); - if (r < 0) - return r; - r = unit_acquire_invocation_id(u); if (r < 0) return r; @@ -917,6 +913,10 @@ static int timer_test_startable(Unit *u) { Timer *t = ASSERT_PTR(TIMER(u)); int r; + r = unit_test_trigger_loaded(u); + if (r < 0) + return r; + r = unit_test_start_limit(u); if (r < 0) { timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);