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. diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 42709202a537c..1ff59dd374a50 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; @@ -883,25 +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)) { - - log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); - return -EPROTO; - } + } 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 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) { 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); 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); } } 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; } 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); 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) 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/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; } 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(""); }