core/socket: several fixlets (#35637)

Fixes https://github.com/systemd/systemd/issues/35635
This commit is contained in:
Lennart Poettering
2025-01-02 18:20:48 +01:00
committed by GitHub
9 changed files with 218 additions and 142 deletions
+7 -11
View File
@@ -26,14 +26,9 @@ static bool ignore_dirent(const struct dirent *de, RecurseDirFlags flags) {
dot_or_dot_dot(de->d_name);
}
int readdir_all(int dir_fd,
RecurseDirFlags flags,
DirectoryEntries **ret) {
int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret) {
_cleanup_free_ DirectoryEntries *de = NULL;
struct dirent *entry;
DirectoryEntries *nde;
size_t add, sz, j;
int r;
assert(dir_fd >= 0);
@@ -77,11 +72,11 @@ int readdir_all(int dir_fd,
nde = realloc(de, bs);
if (!nde)
return -ENOMEM;
de = nde;
}
de->n_entries = 0;
struct dirent *entry;
FOREACH_DIRENT_IN_BUFFER(entry, de->buffer, de->buffer_size) {
if (ignore_dirent(entry, flags))
continue;
@@ -98,16 +93,17 @@ int readdir_all(int dir_fd,
de->n_entries++;
}
size_t sz, j;
sz = ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size);
add = sizeof(struct dirent*) * de->n_entries;
if (add > SIZE_MAX - add)
if (!INC_SAFE(&sz, sizeof(struct dirent*) * de->n_entries))
return -ENOMEM;
nde = realloc(de, sz + add);
nde = realloc(de, sz);
if (!nde)
return -ENOMEM;
de = nde;
de->entries = (struct dirent**) ((uint8_t*) de + ALIGN(offsetof(DirectoryEntries, buffer) + de->buffer_size));
j = 0;
+22 -22
View File
@@ -8,6 +8,28 @@
#include "stat-util.h"
#include "macro.h"
typedef enum RecurseDirFlags {
/* Interpreted by readdir_all() */
RECURSE_DIR_SORT = 1 << 0, /* sort file directory entries before processing them */
RECURSE_DIR_IGNORE_DOT = 1 << 1, /* ignore all dot files ("." and ".." are always ignored) */
RECURSE_DIR_ENSURE_TYPE = 1 << 2, /* guarantees that 'd_type' field of 'de' is not DT_UNKNOWN */
/* Interpreted by recurse_dir() */
RECURSE_DIR_SAME_MOUNT = 1 << 3, /* skips over subdirectories that are submounts */
RECURSE_DIR_INODE_FD = 1 << 4, /* passes an opened inode fd (O_DIRECTORY fd in case of dirs, O_PATH otherwise) */
RECURSE_DIR_TOPLEVEL = 1 << 5, /* call RECURSE_DIR_ENTER/RECURSE_DIR_LEAVE once for top-level dir, too, with dir_fd=-1 and NULL dirent */
} RecurseDirFlags;
typedef struct DirectoryEntries {
size_t n_entries;
struct dirent **entries;
size_t buffer_size;
struct dirent buffer[];
} DirectoryEntries;
int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);
int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret);
typedef enum RecurseDirEvent {
RECURSE_DIR_ENTER, /* only for dir inodes */
RECURSE_DIR_LEAVE, /* only for dir inodes */
@@ -56,27 +78,5 @@ typedef int (*recurse_dir_func_t)(
const struct statx *sx, /* statx data (only if statx_mask was non-zero) */
void *userdata);
typedef enum RecurseDirFlags {
/* Interpreted by readdir_all() */
RECURSE_DIR_SORT = 1 << 0, /* sort file directory entries before processing them */
RECURSE_DIR_IGNORE_DOT = 1 << 1, /* ignore all dot files ("." and ".." are always ignored) */
RECURSE_DIR_ENSURE_TYPE = 1 << 2, /* guarantees that 'd_type' field of 'de' is not DT_UNKNOWN */
/* Interpreted by recurse_dir() */
RECURSE_DIR_SAME_MOUNT = 1 << 3, /* skips over subdirectories that are submounts */
RECURSE_DIR_INODE_FD = 1 << 4, /* passes an opened inode fd (O_DIRECTORY fd in case of dirs, O_PATH otherwise) */
RECURSE_DIR_TOPLEVEL = 1 << 5, /* call RECURSE_DIR_ENTER/RECURSE_DIR_LEAVE once for top-level dir, too, with dir_fd=-1 and NULL dirent */
} RecurseDirFlags;
typedef struct DirectoryEntries {
size_t n_entries;
struct dirent** entries;
size_t buffer_size;
struct dirent buffer[];
} DirectoryEntries;
int readdir_all(int dir_fd, RecurseDirFlags flags, DirectoryEntries **ret);
int readdir_all_at(int fd, const char *path, RecurseDirFlags flags, DirectoryEntries **ret);
int recurse_dir(int dir_fd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
int recurse_dir_at(int atfd, const char *path, unsigned statx_mask, unsigned n_depth_max, RecurseDirFlags flags, recurse_dir_func_t func, void *userdata);
+1
View File
@@ -251,6 +251,7 @@ DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState);
static const char* const socket_state_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = "dead",
[SOCKET_START_PRE] = "start-pre",
[SOCKET_START_OPEN] = "start-open",
[SOCKET_START_CHOWN] = "start-chown",
[SOCKET_START_POST] = "start-post",
[SOCKET_LISTENING] = "listening",
+1
View File
@@ -168,6 +168,7 @@ typedef enum SliceState {
typedef enum SocketState {
SOCKET_DEAD,
SOCKET_START_PRE,
SOCKET_START_OPEN,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
+112 -107
View File
@@ -32,6 +32,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "recurse-dir.h"
#include "selinux-util.h"
#include "serialize.h"
#include "service.h"
@@ -59,6 +60,7 @@ struct SocketPeer {
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
[SOCKET_START_PRE] = UNIT_ACTIVATING,
[SOCKET_START_OPEN] = UNIT_ACTIVATING,
[SOCKET_START_CHOWN] = UNIT_ACTIVATING,
[SOCKET_START_POST] = UNIT_ACTIVATING,
[SOCKET_LISTENING] = UNIT_ACTIVE,
@@ -75,7 +77,6 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata);
static void flush_ports(Socket *s);
static bool SOCKET_STATE_WITH_PROCESS(SocketState state) {
return IN_SET(state,
@@ -112,9 +113,8 @@ static bool SOCKET_SERVICE_IS_ACTIVE(Service *s, bool allow_finalize) {
}
static void socket_init(Unit *u) {
Socket *s = SOCKET(u);
Socket *s = ASSERT_PTR(SOCKET(u));
assert(u);
assert(u->load_state == UNIT_STUB);
s->backlog = SOMAXCONN_DELUXE;
@@ -145,7 +145,7 @@ static void socket_unwatch_control_pid(Socket *s) {
unit_unwatch_pidref_done(UNIT(s), &s->control_pid);
}
static void socket_cleanup_fd_list(SocketPort *p) {
static void socket_port_close_auxiliary_fds(SocketPort *p) {
assert(p);
close_many(p->auxiliary_fds, p->n_auxiliary_fds);
@@ -153,13 +153,13 @@ static void socket_cleanup_fd_list(SocketPort *p) {
p->n_auxiliary_fds = 0;
}
SocketPort *socket_port_free(SocketPort *p) {
SocketPort* socket_port_free(SocketPort *p) {
if (!p)
return NULL;
sd_event_source_unref(p->event_source);
socket_cleanup_fd_list(p);
socket_port_close_auxiliary_fds(p);
safe_close(p->fd);
free(p->path);
@@ -372,11 +372,13 @@ static int socket_add_extras(Socket *s) {
return 0;
}
static const char *socket_find_symlink_target(Socket *s) {
static const char* socket_find_symlink_target(Socket *s) {
const char *found = NULL;
assert(s);
LIST_FOREACH(port, p, s->ports) {
const char *f = NULL;
const char *f;
switch (p->type) {
@@ -389,7 +391,7 @@ static const char *socket_find_symlink_target(Socket *s) {
break;
default:
break;
f = NULL;
}
if (f) {
@@ -483,7 +485,7 @@ static int socket_load(Unit *u) {
return socket_verify(s);
}
static SocketPeer *socket_peer_dup(const SocketPeer *q) {
static SocketPeer* socket_peer_dup(const SocketPeer *q) {
SocketPeer *p;
assert(q);
@@ -502,7 +504,7 @@ static SocketPeer *socket_peer_dup(const SocketPeer *q) {
return p;
}
static SocketPeer *socket_peer_free(SocketPeer *p) {
static SocketPeer* socket_peer_free(SocketPeer *p) {
assert(p);
if (p->socket)
@@ -578,7 +580,6 @@ static const char* listen_lookup(int family, int type) {
return "ListenSequentialPacket";
assert_not_reached();
return NULL;
}
static void socket_dump(Unit *u, FILE *f, const char *prefix) {
@@ -941,13 +942,11 @@ static void socket_close_fds(Socket *s) {
assert(s);
LIST_FOREACH(port, p, s->ports) {
bool was_open;
was_open = p->fd >= 0;
bool was_open = p->fd >= 0;
p->event_source = sd_event_source_disable_unref(p->event_source);
p->fd = safe_close(p->fd);
socket_cleanup_fd_list(p);
socket_port_close_auxiliary_fds(p);
/* One little note: we should normally not delete any sockets in the file system here! After all some
* other process we spawned might still have a reference of this fd and wants to continue to use
@@ -973,7 +972,7 @@ static void socket_close_fds(Socket *s) {
break;
default:
break;
;
}
}
@@ -984,6 +983,8 @@ static void socket_close_fds(Socket *s) {
/* Note that we don't return NULL here, since s has not been freed. */
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Socket*, socket_close_fds, NULL);
static void socket_apply_socket_options(Socket *s, SocketPort *p, int fd) {
int r;
@@ -1217,13 +1218,14 @@ static int special_address_create(const char *path, bool writable) {
return TAKE_FD(fd);
}
static int usbffs_address_create(const char *path) {
static int usbffs_address_create_at(int dfd, const char *name) {
_cleanup_close_ int fd = -EBADF;
struct stat st;
assert(path);
assert(dfd >= 0);
assert(name);
fd = open(path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
fd = openat(dfd, name, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
if (fd < 0)
return -errno;
@@ -1295,7 +1297,6 @@ static int socket_symlink(Socket *s) {
(void) mkdir_parents_label(*i, s->directory_mode);
r = symlink_idempotent(p, *i, false);
if (r == -EEXIST && s->remove_on_stop) {
/* If there's already something where we want to create the symlink, and the destructive
* RemoveOnStop= mode is set, then we might as well try to remove what already exists and try
@@ -1304,7 +1305,6 @@ static int socket_symlink(Socket *s) {
if (unlink(*i) >= 0)
r = symlink_idempotent(p, *i, false);
}
if (r < 0)
log_unit_warning_errno(UNIT(s), r, "Failed to create symlink %s %s %s, ignoring: %m",
p, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), *i);
@@ -1329,57 +1329,40 @@ static int usbffs_write_descs(int fd, Service *s) {
return copy_file_fd(s->usb_function_strings, fd, 0);
}
static int usbffs_select_ep(const struct dirent *d) {
return d->d_name[0] != '.' && !streq(d->d_name, "ep0");
}
static int usbffs_dispatch_eps(SocketPort *p) {
_cleanup_free_ struct dirent **ent = NULL;
size_t n, k;
static int usbffs_dispatch_eps(SocketPort *p, int dfd) {
_cleanup_free_ DirectoryEntries *des = NULL;
int r;
r = scandir(p->path, &ent, usbffs_select_ep, alphasort);
assert(p);
assert(dfd >= 0);
r = readdir_all(dfd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &des);
if (r < 0)
return -errno;
return r;
n = (size_t) r;
p->auxiliary_fds = new(int, n);
if (!p->auxiliary_fds) {
r = -ENOMEM;
goto clear;
}
p->auxiliary_fds = new(int, des->n_entries);
if (!p->auxiliary_fds)
return -ENOMEM;
p->n_auxiliary_fds = n;
FOREACH_ARRAY(i, des->entries, des->n_entries) {
const struct dirent *de = *i;
k = 0;
for (size_t i = 0; i < n; ++i) {
_cleanup_free_ char *ep = NULL;
if (streq(de->d_name, "ep0"))
continue;
ep = path_make_absolute(ent[i]->d_name, p->path);
if (!ep) {
r = -ENOMEM;
goto fail;
}
path_simplify(ep);
r = usbffs_address_create(ep);
r = usbffs_address_create_at(dfd, de->d_name);
if (r < 0)
goto fail;
p->auxiliary_fds[k++] = r;
p->auxiliary_fds[p->n_auxiliary_fds++] = r;
}
r = 0;
goto clear;
assert(p->n_auxiliary_fds < des->n_entries);
return 0;
fail:
close_many(p->auxiliary_fds, k);
p->auxiliary_fds = mfree(p->auxiliary_fds);
p->n_auxiliary_fds = 0;
clear:
free_many((void**) ent, n);
socket_port_close_auxiliary_fds(p);
return r;
}
@@ -1624,8 +1607,6 @@ static int socket_address_listen_in_cgroup(
return fd;
}
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Socket *, socket_close_fds, NULL);
static int socket_open_fds(Socket *orig_s) {
_cleanup_(socket_close_fdsp) Socket *s = orig_s;
_cleanup_freecon_ char *label = NULL;
@@ -1685,7 +1666,7 @@ static int socket_open_fds(Socket *orig_s) {
p->fd = special_address_create(p->path, s->writable);
if (p->fd < 0)
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open special file %s: %m", p->path);
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open special file '%s': %m", p->path);
break;
case SOCKET_FIFO:
@@ -1695,7 +1676,7 @@ static int socket_open_fds(Socket *orig_s) {
s->directory_mode,
s->socket_mode);
if (p->fd < 0)
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open FIFO %s: %m", p->path);
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open FIFO '%s': %m", p->path);
socket_apply_fifo_options(s, p->fd);
socket_symlink(s);
@@ -1709,36 +1690,38 @@ static int socket_open_fds(Socket *orig_s) {
s->mq_maxmsg,
s->mq_msgsize);
if (p->fd < 0)
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open message queue %s: %m", p->path);
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open message queue '%s': %m", p->path);
break;
case SOCKET_USB_FUNCTION: {
_cleanup_free_ char *ep = NULL;
_cleanup_close_ int dfd = -EBADF;
ep = path_make_absolute("ep0", p->path);
if (!ep)
return -ENOMEM;
dfd = open(p->path, O_DIRECTORY|O_CLOEXEC);
if (dfd < 0)
return log_unit_error_errno(UNIT(s), errno,
"Failed to open USB FunctionFS dir '%s': %m", p->path);
p->fd = usbffs_address_create(ep);
p->fd = usbffs_address_create_at(dfd, "ep0");
if (p->fd < 0)
return p->fd;
return log_unit_error_errno(UNIT(s), p->fd, "Failed to open USB FunctionFS ep0: %m");
r = usbffs_write_descs(p->fd, SERVICE(UNIT_DEREF(s->service)));
if (r < 0)
return r;
return log_unit_error_errno(UNIT(s), r, "Failed to write to USB FunctionFS ep0: %m");
r = usbffs_dispatch_eps(p);
r = usbffs_dispatch_eps(p, dfd);
if (r < 0)
return r;
return log_unit_error_errno(UNIT(s), r, "Failed to dispatch USB FunctionFS eps: %m");
break;
}
default:
assert_not_reached();
}
}
s = NULL;
TAKE_PTR(s);
return 0;
}
@@ -1840,14 +1823,14 @@ static void socket_set_state(Socket *s, SocketState state) {
socket_unwatch_fds(s);
if (!IN_SET(state,
SOCKET_START_OPEN,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
SOCKET_RUNNING,
SOCKET_STOP_PRE,
SOCKET_STOP_PRE_SIGTERM,
SOCKET_STOP_PRE_SIGKILL,
SOCKET_CLEANING))
SOCKET_STOP_PRE_SIGKILL))
socket_close_fds(s);
if (state != old_state)
@@ -1879,6 +1862,7 @@ static int socket_coldplug(Unit *u) {
}
if (IN_SET(s->deserialized_state,
SOCKET_START_OPEN,
SOCKET_START_CHOWN,
SOCKET_START_POST,
SOCKET_LISTENING,
@@ -1887,7 +1871,11 @@ static int socket_coldplug(Unit *u) {
/* Originally, we used to simply reopen all sockets here that we didn't have file descriptors
* for. However, this is problematic, as we won't traverse through the SOCKET_START_CHOWN state for
* them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open,
* and if there's a mismatch, warn loudly. */
* and if there's a mismatch, warn loudly.
*
* Note that SOCKET_START_OPEN requires no special treatment, as it's only intermediate
* between SOCKET_START_PRE and SOCKET_START_CHOWN and shall otherwise not be observed.
* It's listed only for consistency. */
r = socket_check_open(s);
if (r == SOCKET_OPEN_NONE)
@@ -2105,8 +2093,10 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
}
static int state_to_kill_operation(Socket *s, SocketState state) {
if (state == SOCKET_STOP_PRE_SIGTERM && unit_has_job_type(UNIT(s), JOB_RESTART))
return KILL_RESTART;
assert(s);
if (state == SOCKET_STOP_PRE_SIGTERM)
return unit_has_job_type(UNIT(s), JOB_RESTART) ? KILL_RESTART : KILL_TERMINATE;
if (state == SOCKET_FINAL_SIGTERM)
return KILL_TERMINATE;
@@ -2127,7 +2117,6 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
goto fail;
}
if (r > 0) {
r = socket_arm_timer(s, /* relative= */ true, s->timeout_usec);
if (r < 0) {
@@ -2181,6 +2170,21 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
socket_enter_stop_post(s, SOCKET_SUCCESS);
}
static void flush_ports(Socket *s) {
assert(s);
/* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy
* anymore */
LIST_FOREACH(port, p, s->ports) {
if (p->fd < 0)
continue;
(void) flush_accept(p->fd);
(void) flush_fd(p->fd);
}
}
static void socket_enter_listening(Socket *s) {
int r;
@@ -2229,12 +2233,7 @@ static void socket_enter_start_chown(Socket *s) {
int r;
assert(s);
r = socket_open_fds(s);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m");
goto fail;
}
assert(s->state == SOCKET_START_OPEN);
if (!isempty(s->user) || !isempty(s->group)) {
@@ -2245,17 +2244,36 @@ static void socket_enter_start_chown(Socket *s) {
r = socket_chown(s, &s->control_pid);
if (r < 0) {
log_unit_warning_errno(UNIT(s), r, "Failed to spawn 'start-chown' task: %m");
goto fail;
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
return;
}
socket_set_state(s, SOCKET_START_CHOWN);
} else
socket_enter_start_post(s);
}
return;
static void socket_enter_start_open(Socket *s) {
int r;
fail:
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
assert(s);
assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED, SOCKET_START_PRE));
/* We force a state transition here even though we're not spawning any process (i.e. the state is purely
* intermediate), so that failure of socket_open_fds() always causes a state change in unit_notify().
* Otherwise, if no Exec*= is defined, we might go from previous SOCKET_FAILED to SOCKET_FAILED,
* meaning the OnFailure= deps are unexpectedly skipped (#35635). */
socket_set_state(s, SOCKET_START_OPEN);
r = socket_open_fds(s);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to listen on sockets: %m");
socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES);
return;
}
socket_enter_start_chown(s);
}
static void socket_enter_start_pre(Socket *s) {
@@ -2282,22 +2300,7 @@ static void socket_enter_start_pre(Socket *s) {
socket_set_state(s, SOCKET_START_PRE);
} else
socket_enter_start_chown(s);
}
static void flush_ports(Socket *s) {
assert(s);
/* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy
* anymore */
LIST_FOREACH(port, p, s->ports) {
if (p->fd < 0)
continue;
(void) flush_accept(p->fd);
(void) flush_fd(p->fd);
}
socket_enter_start_open(s);
}
static void socket_enter_running(Socket *s, int cfd_in) {
@@ -2485,6 +2488,7 @@ static int socket_start(Unit *u) {
/* Already on it! */
if (IN_SET(s->state,
SOCKET_START_PRE,
SOCKET_START_OPEN,
SOCKET_START_CHOWN,
SOCKET_START_POST))
return 0;
@@ -2536,6 +2540,7 @@ static int socket_stop(Unit *u) {
* kill mode. */
if (IN_SET(s->state,
SOCKET_START_PRE,
SOCKET_START_OPEN,
SOCKET_START_CHOWN,
SOCKET_START_POST)) {
socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_SUCCESS);
@@ -3172,7 +3177,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
case SOCKET_START_PRE:
if (f == SOCKET_SUCCESS)
socket_enter_start_chown(s);
socket_enter_start_open(s);
else
socket_enter_signal(s, SOCKET_FINAL_SIGTERM, f);
break;
+1 -1
View File
@@ -176,7 +176,7 @@ int socket_collect_fds(Socket *s, int **ret);
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
SocketPort *socket_port_free(SocketPort *p);
SocketPort* socket_port_free(SocketPort *p);
DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPort*, socket_port_free);
void socket_free_ports(Socket *s);
+1 -1
View File
@@ -11,7 +11,7 @@ u root 0:0 "Super User" /root
# The nobody user/group for NFS file systems
g {{NOBODY_GROUP_NAME}} 65534 - -
u! {{NOBODY_USER_NAME }} 65534:65534 "Kernel Overflow User" -
u! {{NOBODY_USER_NAME}} 65534:65534 "Kernel Overflow User" -
# Administrator group: can *see* more than normal users
g adm {{ADM_GID }} - -
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
UNIT_NAME="TEST-07-PID1-socket-$RANDOM"
at_exit() {
systemctl stop "$UNIT_NAME.socket"
rm -f /run/systemd/system/"$UNIT_NAME".{socket,service} \
/run/systemd/system/TEST-07-PID1-socket-OnFailure.service
}
trap at_exit EXIT
# Test triggering OnFailure= when fails to listen on socket (#35635)
mkdir -p "/tmp/$UNIT_NAME"
cat >/run/systemd/system/"$UNIT_NAME.socket" <<EOF
[Unit]
OnFailure=TEST-07-PID1-socket-OnFailure.service
[Socket]
ListenStream=/tmp/$UNIT_NAME/test
EOF
cat >/run/systemd/system/"$UNIT_NAME.service" <<EOF
[Service]
ExecStart=true
EOF
cat >/run/systemd/system/TEST-07-PID1-socket-OnFailure.service <<EOF
[Service]
Type=oneshot
ExecStart=rmdir /tmp/$UNIT_NAME/test
RemainAfterExit=yes
EOF
systemctl start "$UNIT_NAME.socket"
systemctl is-active "$UNIT_NAME.socket"
[[ -S "/tmp/$UNIT_NAME/test" ]]
systemctl stop "$UNIT_NAME.socket"
rm "/tmp/$UNIT_NAME/test"
chattr +i "/tmp/$UNIT_NAME"
(! systemctl start "$UNIT_NAME.socket")
systemctl is-failed "$UNIT_NAME.socket"
assert_eq "$(systemctl show "$UNIT_NAME.socket" -P SubState)" "failed"
[[ ! -e "/tmp/$UNIT_NAME/test" ]]
timeout 10 bash -c "until systemctl is-failed TEST-07-PID1-socket-OnFailure.service; do sleep .5; done"
chattr -i "/tmp/$UNIT_NAME"
mkdir "/tmp/$UNIT_NAME/test"
(! systemctl start "$UNIT_NAME.socket")
systemctl is-failed "$UNIT_NAME.socket"
assert_eq "$(systemctl show "$UNIT_NAME.socket" -P SubState)" "failed"
timeout 10 bash -c "while [[ -d '/tmp/$UNIT_NAME/test' ]]; do sleep .5; done"
[[ ! -e "/tmp/$UNIT_NAME/test" ]]
systemctl is-active TEST-07-PID1-socket-OnFailure.service
systemctl start "$UNIT_NAME.socket"
systemctl is-active "$UNIT_NAME.socket"