From 61c062f896a9a59cab6899d3c8ba3e3d1e4e0c61 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 2 Nov 2023 11:31:13 +0100 Subject: [PATCH 01/12] fd-util: add new FORMAT_PROC_PID_FD_PATH() helper This is just like FORMAT_PROC_FD_PATH() but goes via the PID number rather than the "self" symlink. This is useful whenever we want to generate a path that is useful outside of our local scope. --- src/basic/fd-util.c | 8 ++++++++ src/basic/fd-util.h | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index f2b606ea9b..32289cfd09 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -988,3 +988,11 @@ const char *accmode_to_string(int flags) { return NULL; } } + +char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd) { + assert(buf); + assert(fd >= 0); + assert(pid >= 0); + assert_se(snprintf_ok(buf, PROC_PID_FD_PATH_MAX, "/proc/" PID_FMT "/fd/%i", pid == 0 ? getpid_cached() : pid, fd)); + return buf; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index aba8f5f5af..5061e32196 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -131,6 +131,16 @@ static inline char *format_proc_fd_path(char buf[static PROC_FD_PATH_MAX], int f #define FORMAT_PROC_FD_PATH(fd) \ format_proc_fd_path((char[PROC_FD_PATH_MAX]) {}, (fd)) +/* The maximum length a buffer for a /proc//fd/ path needs */ +#define PROC_PID_FD_PATH_MAX \ + (STRLEN("/proc//fd/") + DECIMAL_STR_MAX(pid_t) + DECIMAL_STR_MAX(int)) + +char *format_proc_pid_fd_path(char buf[static PROC_PID_FD_PATH_MAX], pid_t pid, int fd); + +/* Kinda the same as FORMAT_PROC_FD_PATH(), but goes by PID rather than "self" symlink */ +#define FORMAT_PROC_PID_FD_PATH(pid, fd) \ + format_proc_pid_fd_path((char[PROC_PID_FD_PATH_MAX]) {}, (pid), (fd)) + const char *accmode_to_string(int flags); /* Like ASSERT_PTR, but for fds */ From e9ccae31355b8d4fd7496b0548470c8c977897d8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 2 Nov 2023 11:04:36 +0100 Subject: [PATCH 02/12] =?UTF-8?q?process-util:=20add=20new=20FORK=5FDEATHS?= =?UTF-8?q?IG=5FSIGKILL=20flag,=20rename=20FORK=5FDEATHSIG=20=E2=86=92=20F?= =?UTF-8?q?ORK=5FDEATHSIG=5FSIGTERM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes it makes sense to hard kill a client if we die. Let's hence add a third FORK_DEATHSIG flag for this purpose: FORK_DEATHSIG_SIGKILL. To make things less confusing this also renames FORK_DEATHSIG to FORK_DEATHSIG_SIGTERM to make clear it sends SIGTERM. We already had FORK_DEATHSIG_SIGINT, hence this makes things nicely symmetric. A bunch of users are switched over for FORK_DEATHSIG_SIGKILL where we know it's safe to abort things abruptly. This should make some kernel cases more robust, since we cannot get confused by signal masks or such. While we are at it, also fix a bunch of bugs where we didn't take FORK_DEATHSIG_SIGINT into account in safe_fork() --- src/basic/namespace-util.c | 2 +- src/basic/process-util.c | 26 +++++++++++++------- src/basic/process-util.h | 31 ++++++++++++------------ src/basic/terminal-util.c | 4 +-- src/basic/time-util.c | 2 +- src/core/exec-credential.c | 2 +- src/core/exec-invoke.c | 2 +- src/core/unit.c | 2 +- src/coredump/coredump.c | 2 +- src/coredump/coredumpctl.c | 2 +- src/delta/delta.c | 2 +- src/fsck/fsck.c | 2 +- src/home/homectl.c | 2 +- src/home/homed-home.c | 4 +-- src/home/homework-cifs.c | 2 +- src/home/homework-fscrypt.c | 2 +- src/home/homework-luks.c | 6 ++--- src/import/import-common.c | 4 +-- src/import/importd.c | 2 +- src/import/pull-common.c | 2 +- src/journal-remote/journal-remote-main.c | 2 +- src/libsystemd/sd-bus/bus-container.c | 2 +- src/login/inhibit.c | 2 +- src/login/logind-brightness.c | 2 +- src/machine/machine-dbus.c | 6 ++--- src/nspawn/nspawn-setuid.c | 2 +- src/nspawn/nspawn.c | 2 +- src/partition/repart.c | 2 +- src/portable/portable.c | 2 +- src/quotacheck/quotacheck.c | 2 +- src/remount-fs/remount-fs.c | 2 +- src/resolve/test-resolved-stream.c | 4 +-- src/shared/calendarspec.c | 2 +- src/shared/dissect-image.c | 4 +-- src/shared/edit-util.c | 2 +- src/shared/exec-util.c | 6 ++--- src/shared/logs-show.c | 2 +- src/shared/mkfs-util.c | 4 +-- src/shared/mount-util.c | 4 +-- src/shared/pager.c | 2 +- src/shared/varlink.c | 2 +- src/socket-activate/socket-activate.c | 2 +- src/sulogin-shell/sulogin-shell.c | 2 +- src/sysext/sysext.c | 2 +- src/systemctl/systemctl-start-special.c | 2 +- src/systemctl/systemctl-sysv-compat.c | 2 +- src/sysupdate/sysupdate-resource.c | 2 +- src/sysupdate/sysupdate-transfer.c | 2 +- src/test/test-async.c | 4 +-- src/test/test-capability.c | 2 +- src/test/test-data-fd-util.c | 2 +- src/test/test-env-util.c | 2 +- src/test/test-execute.c | 4 +-- src/test/test-fd-util.c | 8 +++--- src/test/test-fileio.c | 2 +- src/test/test-mempress.c | 2 +- src/test/test-mkdir.c | 2 +- src/test/test-mount-util.c | 6 ++--- src/test/test-process-util.c | 6 ++--- src/test/test-rm-rf.c | 2 +- src/test/test-socket-util.c | 14 +++++------ src/udev/udev-manager.c | 2 +- src/udev/udev-spawn.c | 2 +- src/udev/udevadm-lock.c | 4 +-- src/userdb/userdbd-manager.c | 2 +- 65 files changed, 125 insertions(+), 116 deletions(-) diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c index 0a0de85aad..2101f617ad 100644 --- a/src/basic/namespace-util.c +++ b/src/basic/namespace-util.c @@ -219,7 +219,7 @@ int userns_acquire(const char *uid_map, const char *gid_map) { * and then kills the process again. This way we have a userns fd that is not bound to any * process. We can use that for file system mounts and similar. */ - r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NEW_USERNS, &pid); + r = safe_fork("(sd-mkuserns)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_NEW_USERNS, &pid); if (r < 0) return r; if (r == 0) diff --git a/src/basic/process-util.c b/src/basic/process-util.c index c5883b05d1..201c5596ae 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1380,6 +1380,12 @@ pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata) { return pid; } +static int fork_flags_to_signal(ForkFlags flags) { + return (flags & FORK_DEATHSIG_SIGTERM) ? SIGTERM : + (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : + SIGKILL; +} + int safe_fork_full( const char *name, const int stdio_fds[3], @@ -1409,9 +1415,10 @@ int safe_fork_full( fflush(stderr); /* This one shouldn't be necessary, stderr should be unbuffered anyway, but let's better be safe than sorry */ } - if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { - /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can - * be sure that SIGTERMs are not lost we might send to the child. */ + if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT)) { + /* We temporarily block all signals, so that the new child has them blocked initially. This + * way, we can be sure that SIGTERMs are not lost we might send to the child. (Note that for + * FORK_DEATHSIG_SIGKILL we don't bother, since it cannot be blocked anyway.) */ assert_se(sigfillset(&ss) >= 0); block_signals = block_all = true; @@ -1512,8 +1519,8 @@ int safe_fork_full( r, "Failed to rename process, ignoring: %m"); } - if (flags & (FORK_DEATHSIG|FORK_DEATHSIG_SIGINT)) - if (prctl(PR_SET_PDEATHSIG, (flags & FORK_DEATHSIG_SIGINT) ? SIGINT : SIGTERM) < 0) { + if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGKILL)) + if (prctl(PR_SET_PDEATHSIG, fork_flags_to_signal(flags)) < 0) { log_full_errno(prio, errno, "Failed to set death signal: %m"); _exit(EXIT_FAILURE); } @@ -1538,7 +1545,7 @@ int safe_fork_full( } } - if (flags & FORK_DEATHSIG) { + if (flags & (FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL|FORK_DEATHSIG_SIGINT)) { pid_t ppid; /* Let's see if the parent PID is still the one we started from? If not, then the parent * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ @@ -1547,8 +1554,9 @@ int safe_fork_full( if (ppid == 0) /* Parent is in a different PID namespace. */; else if (ppid != original_pid) { - log_debug("Parent died early, raising SIGTERM."); - (void) raise(SIGTERM); + int sig = fork_flags_to_signal(flags); + log_debug("Parent died early, raising %s.", signal_to_string(sig)); + (void) raise(sig); _exit(EXIT_FAILURE); } } @@ -1664,7 +1672,7 @@ int namespace_fork( r = safe_fork_full(outer_name, NULL, except_fds, n_except_fds, - (flags|FORK_DEATHSIG) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); + (flags|FORK_DEATHSIG_SIGINT|FORK_DEATHSIG_SIGTERM|FORK_DEATHSIG_SIGKILL) & ~(FORK_REOPEN_LOG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE), ret_pid); if (r < 0) return r; if (r == 0) { diff --git a/src/basic/process-util.h b/src/basic/process-util.h index a18a730936..af6cba13eb 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -160,22 +160,23 @@ pid_t clone_with_nested_stack(int (*fn)(void *), int flags, void *userdata); typedef enum ForkFlags { FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */ FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */ - FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */ + FORK_DEATHSIG_SIGTERM = 1 << 2, /* Set PR_DEATHSIG in the child to SIGTERM */ FORK_DEATHSIG_SIGINT = 1 << 3, /* Set PR_DEATHSIG in the child to SIGINT */ - FORK_REARRANGE_STDIO = 1 << 4, /* Connect 0,1,2 to specified fds or /dev/null */ - FORK_REOPEN_LOG = 1 << 5, /* Reopen log connection */ - FORK_LOG = 1 << 6, /* Log above LOG_DEBUG log level about failures */ - FORK_WAIT = 1 << 7, /* Wait until child exited */ - FORK_NEW_MOUNTNS = 1 << 8, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ - FORK_MOUNTNS_SLAVE = 1 << 9, /* Make child's mount namespace MS_SLAVE */ - FORK_PRIVATE_TMP = 1 << 10, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */ - FORK_RLIMIT_NOFILE_SAFE = 1 << 11, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ - FORK_STDOUT_TO_STDERR = 1 << 12, /* Make stdout a copy of stderr */ - FORK_FLUSH_STDIO = 1 << 13, /* fflush() stdout (and stderr) before forking */ - FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ - FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */ - FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */ - FORK_DETACH = 1 << 17, /* Double fork if needed to ensure PID1/subreaper is parent */ + FORK_DEATHSIG_SIGKILL = 1 << 4, /* Set PR_DEATHSIG in the child to SIGKILL */ + FORK_REARRANGE_STDIO = 1 << 5, /* Connect 0,1,2 to specified fds or /dev/null */ + FORK_REOPEN_LOG = 1 << 6, /* Reopen log connection */ + FORK_LOG = 1 << 7, /* Log above LOG_DEBUG log level about failures */ + FORK_WAIT = 1 << 8, /* Wait until child exited */ + FORK_NEW_MOUNTNS = 1 << 9, /* Run child in its own mount namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ + FORK_MOUNTNS_SLAVE = 1 << 10, /* Make child's mount namespace MS_SLAVE */ + FORK_PRIVATE_TMP = 1 << 11, /* Mount new /tmp/ in the child (combine with FORK_NEW_MOUNTNS!) */ + FORK_RLIMIT_NOFILE_SAFE = 1 << 12, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */ + FORK_STDOUT_TO_STDERR = 1 << 13, /* Make stdout a copy of stderr */ + FORK_FLUSH_STDIO = 1 << 14, /* fflush() stdout (and stderr) before forking */ + FORK_NEW_USERNS = 1 << 15, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ + FORK_CLOEXEC_OFF = 1 << 16, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */ + FORK_KEEP_NOTIFY_SOCKET = 1 << 17, /* Unless this specified, $NOTIFY_SOCKET will be unset. */ + FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */ } ForkFlags; int safe_fork_full( diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 6cc6ff424a..b505a3ad0e 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1204,7 +1204,7 @@ int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) { if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-openptns)", "(sd-openpt)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child); if (r < 0) return r; @@ -1255,7 +1255,7 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-terminalns)", "(sd-terminal)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child); if (r < 0) return r; diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 19d8602faf..695b998b57 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1044,7 +1044,7 @@ int parse_timestamp(const char *t, usec_t *ret) { if (shared == MAP_FAILED) return negative_errno(); - r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); + r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL); if (r < 0) { (void) munmap(shared, sizeof *shared); return r; diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index ff2d795c7d..6bcfb68d8f 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -930,7 +930,7 @@ int exec_setup_credentials( if (r < 0 && r != -EEXIST) return r; - r = safe_fork("(sd-mkdcreds)", FORK_DEATHSIG|FORK_WAIT|FORK_NEW_MOUNTNS, NULL); + r = safe_fork("(sd-mkdcreds)", FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_NEW_MOUNTNS, NULL); if (r < 0) { _cleanup_(rmdir_and_freep) char *u = NULL; /* remove the temporary workspace if we can */ _cleanup_free_ char *t = NULL; diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 4cc303737e..b1467947e5 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -2136,7 +2136,7 @@ static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) { if (pipe2(errno_pipe, O_CLOEXEC) < 0) return -errno; - r = safe_fork("(sd-userns)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(sd-userns)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/core/unit.c b/src/core/unit.c index d12829c31f..c3613ca8fb 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5426,7 +5426,7 @@ int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret) { (void) unit_realize_cgroup(u); - r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG, &pid); + r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM, &pid); if (r < 0) return r; if (r > 0) { diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 4340d07df4..32c17664fd 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -1443,7 +1443,7 @@ static int forward_coredump_to_container(Context *context) { return log_debug_errno(r, "Failed to join namespaces of PID " PID_FMT ": %m", pid); r = namespace_fork("(sd-coredumpns)", "(sd-coredump)", NULL, 0, - FORK_RESET_SIGNALS|FORK_DEATHSIG, + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, pidnsfd, mntnsfd, netnsfd, usernsfd, rootfd, &child); if (r < 0) return log_debug_errno(r, "Failed to fork into namespaces of PID " PID_FMT ": %m", pid); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 16abb8f62a..2245b350e4 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -1253,7 +1253,7 @@ static int run_debug(int argc, char **argv, void *userdata) { fork_name = strjoina("(", debugger_call[0], ")"); - r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_FLUSH_STDIO, &pid); + r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_FLUSH_STDIO, &pid); if (r < 0) goto finish; if (r == 0) { diff --git a/src/delta/delta.c b/src/delta/delta.c index d830c5e162..3337b7f081 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -168,7 +168,7 @@ static int found_override(const char *top, const char *bottom) { fflush(stdout); - r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index a09e7950cf..000ed69667 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -339,7 +339,7 @@ static int run(int argc, char *argv[]) { pipe(progress_pipe) < 0) return log_error_errno(errno, "pipe(): %m"); - r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &pid); + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/home/homectl.c b/src/home/homectl.c index 56b3de4a95..a6951c8562 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -2025,7 +2025,7 @@ static int with_home(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid); + r = safe_fork("(with)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE|FORK_REOPEN_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 749670f94b..4ff35400c4 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -1184,7 +1184,7 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord r = safe_fork_full("(sd-homework)", (int[]) { stdin_fd, stdout_fd, STDERR_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG|FORK_REOPEN_LOG, &pid); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_REOPEN_LOG, &pid); if (r < 0) return r; if (r == 0) { @@ -2080,7 +2080,7 @@ int home_killall(Home *h) { /* Let's kill everything matching the specified UID */ r = safe_fork("(sd-killer)", - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG|FORK_REOPEN_LOG, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT|FORK_LOG|FORK_REOPEN_LOG, NULL); if (r < 0) return r; diff --git a/src/home/homework-cifs.c b/src/home/homework-cifs.c index dff99e5a86..19f1cd5b85 100644 --- a/src/home/homework-cifs.c +++ b/src/home/homework-cifs.c @@ -91,7 +91,7 @@ int home_setup_cifs( if (!strextend_with_separator(&options, ",", h->cifs_extra_mount_options)) return log_oom(); - r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid); + r = safe_fork("(mount)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR, &mount_pid); if (r < 0) return r; if (r == 0) { diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index 1b695309c0..6aae1d2626 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -314,7 +314,7 @@ int home_setup_fscrypt( if (uid_is_valid(h->uid)) { r = safe_fork("(sd-addkey)", - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_REOPEN_LOG, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_REOPEN_LOG, NULL); if (r < 0) return log_error_errno(r, "Failed install encryption key in user's keyring: %m"); diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 63d6c47ac1..5bd78a03ed 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -228,7 +228,7 @@ static int run_fsck(const char *node, const char *fstype) { } r = safe_fork("(fsck)", - FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, + FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, &fsck_pid); if (r < 0) return r; @@ -2596,7 +2596,7 @@ static int ext4_offline_resize_fs( /* resize2fs requires that the file system is force checked first, do so. */ r = safe_fork("(e2fsck)", - FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, + FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, &fsck_pid); if (r < 0) return r; @@ -2628,7 +2628,7 @@ static int ext4_offline_resize_fs( /* Resize the thing */ r = safe_fork("(e2resize)", - FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, + FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, &resize_pid); if (r < 0) return r; diff --git a/src/import/import-common.c b/src/import/import-common.c index 3a7a42c6b3..319aa078a7 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -39,7 +39,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) { r = safe_fork_full("(tar)", (int[]) { pipefd[0], -EBADF, STDERR_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG, &pid); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { @@ -108,7 +108,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) { r = safe_fork_full("(tar)", (int[]) { -EBADF, pipefd[1], STDERR_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG, &pid); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/import/importd.c b/src/import/importd.c index c5b60c2604..cb5156bf06 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -369,7 +369,7 @@ static int transfer_start(Transfer *t) { r = safe_fork_full("(sd-transfer)", (int[]) { t->stdin_fd, t->stdout_fd < 0 ? pipefd[1] : t->stdout_fd, pipefd[1] }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO, &t->pid); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO, &t->pid); if (r < 0) return r; if (r == 0) { diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 44919a19c5..5e1ea20a03 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -417,7 +417,7 @@ static int verify_gpg( r = safe_fork_full("(gpg)", (int[]) { gpg_pipe[0], -EBADF, STDERR_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &pid); if (r < 0) return r; diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 3ffac96d7a..da0f20d3ce 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -91,7 +91,7 @@ static int spawn_child(const char* child, char** argv) { r = safe_fork_full("(remote)", (int[]) {STDIN_FILENO, fd[1], STDERR_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &child_pid); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &child_pid); if (r < 0) { safe_close_pair(fd); return r; diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 71581d718c..4146a6efd3 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -51,7 +51,7 @@ int bus_container_connect_socket(sd_bus *b) { if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) return log_debug_errno(errno, "Failed to create a socket pair: %m"); - r = namespace_fork("(sd-buscntrns)", "(sd-buscntr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-buscntrns)", "(sd-buscntr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, pidnsfd, mntnsfd, -1, usernsfd, rootfd, &child); if (r < 0) return log_debug_errno(r, "Failed to create namespace for (sd-buscntr): %m"); diff --git a/src/login/inhibit.c b/src/login/inhibit.c index 61fbd5df14..ad73c4bde3 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -300,7 +300,7 @@ static int run(int argc, char *argv[]) { if (!arguments) return log_oom(); - r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/login/logind-brightness.c b/src/login/logind-brightness.c index d238cb74c9..40bcb39ce0 100644 --- a/src/login/logind-brightness.c +++ b/src/login/logind-brightness.c @@ -136,7 +136,7 @@ static int brightness_writer_fork(BrightnessWriter *w) { assert(w->child == 0); assert(!w->child_event_source); - r = safe_fork("(sd-bright)", FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &w->child); + r = safe_fork("(sd-bright)", FORK_DEATHSIG_SIGKILL|FORK_REARRANGE_STDIO|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_REOPEN_LOG, &w->child); if (r < 0) return r; if (r == 0) { diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b9ac931cd4..4620f32d62 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -248,7 +248,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) return -errno; - r = namespace_fork("(sd-addrns)", "(sd-addr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-addrns)", "(sd-addr)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, -1, -1, netns_fd, -1, -1, &child); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); @@ -382,7 +382,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) return -errno; - r = namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, pidns_fd, mntns_fd, -1, -1, root_fd, &child); if (r < 0) @@ -1103,7 +1103,7 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - r = namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, -1, mntns_fd, -1, -1, root_fd, &child); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index 3c12648b9d..2d67c3d9de 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -30,7 +30,7 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { r = safe_fork_full("(getent)", (int[]) { -EBADF, pipe_fds[1], -EBADF }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &pid); if (r < 0) { safe_close_pair(pipe_fds); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index ca97453283..51791d9729 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -5276,7 +5276,7 @@ static int run_container( if (arg_private_network) { /* Move network interfaces back to the parent network namespace. We use `safe_fork` * to avoid having to move the parent to the child network namespace. */ - r = safe_fork(NULL, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL); + r = safe_fork(NULL, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL); if (r < 0) return r; diff --git a/src/partition/repart.c b/src/partition/repart.c index 94c6d8e42c..c99168b08f 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -4673,7 +4673,7 @@ static int partition_populate_filesystem(Context *context, Partition *p, const c * appear in the host namespace. Hence we fork a child that has its own file system namespace and * detached mount propagation. */ - r = safe_fork("(sd-copy)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); + r = safe_fork("(sd-copy)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); if (r < 0) return r; if (r == 0) { diff --git a/src/portable/portable.c b/src/portable/portable.c index f283530dd9..d72f3a02db 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -411,7 +411,7 @@ static int portable_extract_by_path( if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, seq) < 0) return log_debug_errno(errno, "Failed to allocated SOCK_SEQPACKET socket: %m"); - r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_LOG, &child); + r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_LOG, &child); if (r < 0) return r; if (r == 0) { diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index ef5fec3f10..27a914d737 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -79,7 +79,7 @@ static int run(int argc, char *argv[]) { return 0; } - r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_WAIT|FORK_LOG, NULL); + r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_WAIT|FORK_LOG, NULL); if (r < 0) return r; if (r == 0) { diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index 2874a0390d..37c7b389b4 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -51,7 +51,7 @@ static int do_remount(const char *path, bool force_rw, Hashmap **pids) { log_debug("Remounting %s...", path); r = safe_fork(force_rw ? "(remount-rw)" : "(remount)", - FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/resolve/test-resolved-stream.c b/src/resolve/test-resolved-stream.c index d1b38b3aad..847de04f90 100644 --- a/src/resolve/test-resolved-stream.c +++ b/src/resolve/test-resolved-stream.c @@ -151,7 +151,7 @@ static void *tls_dns_server(void *p) { r = safe_fork_full("(test-resolved-stream-tls-openssl)", (int[]) { fd_tls, fd_tls, STDOUT_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG|FORK_REOPEN_LOG, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_REOPEN_LOG, &openssl_pid); assert_se(r >= 0); if (r == 0) { @@ -336,7 +336,7 @@ static void try_isolate_network(void) { /* First test if CLONE_NEWUSER/CLONE_NEWNET can actually work for us, i.e. we can open the namespaces * and then still access the build dir we are run from. We do that in a child process since it's * nasty if we have to go back from the namespace once we entered it and realized it cannot work. */ - r = safe_fork("(usernstest)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(usernstest)", FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { /* child */ _cleanup_free_ char *rt = NULL, *d = NULL; diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 2665930209..039080f052 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -1401,7 +1401,7 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *ret_n if (shared == MAP_FAILED) return negative_errno(); - r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); + r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL); if (r < 0) { (void) munmap(shared, sizeof *shared); return r; diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index ef23c4a255..2687eafaf6 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1718,7 +1718,7 @@ static int run_fsck(int node_fd, const char *fstype) { "(fsck)", NULL, &node_fd, 1, /* Leave the node fd open */ - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_CLOEXEC_OFF, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_CLOEXEC_OFF, &pid); if (r < 0) return log_debug_errno(r, "Failed to fork off fsck: %m"); @@ -3410,7 +3410,7 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ goto finish; } - r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child); + r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child); if (r < 0) goto finish; if (r == 0) { diff --git a/src/shared/edit-util.c b/src/shared/edit-util.c index 96ad721343..045839b663 100644 --- a/src/shared/edit-util.c +++ b/src/shared/edit-util.c @@ -270,7 +270,7 @@ static int run_editor(const EditFileContext *context) { assert(context); - r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_WAIT, NULL); if (r < 0) return r; if (r == 0) { /* Child */ diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 5da613bc17..852b3c0e3d 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -45,7 +45,7 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid, b return 0; } - r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &_pid); + r = safe_fork("(direxec)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &_pid); if (r < 0) return r; if (r == 0) { @@ -250,7 +250,7 @@ int execute_strv( * them to finish. Optionally a timeout is applied. If a file with the same name * exists in more than one directory, the earliest one wins. */ - r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid); + r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG, &executor_pid); if (r < 0) return r; if (r == 0) { @@ -541,7 +541,7 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret NULL, except, n_except, - FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_RLIMIT_NOFILE_SAFE, + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_RLIMIT_NOFILE_SAFE, ret_pid); if (r < 0) return r; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index d3053ceabb..a5d04003bd 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1692,7 +1692,7 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, pidnsfd, mntnsfd, -1, -1, rootfd, &child); if (r < 0) return r; diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 6ea080869f..4e58b6e871 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -155,7 +155,7 @@ static int do_mcopy(const char *node, const char *root) { if (strv_extend(&argv, "::") < 0) return log_oom(); - r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, NULL); + r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_CLOSE_ALL_FDS, NULL); if (r < 0) return r; if (r == 0) { @@ -336,7 +336,7 @@ int make_filesystem( _cleanup_(unlink_and_freep) char *protofile = NULL; char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {}; int stdio_fds[3] = { -EBADF, STDERR_FILENO, STDERR_FILENO}; - ForkFlags flags = FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT| + ForkFlags flags = FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT| FORK_CLOSE_ALL_FDS|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG; int r; diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index a7493a3814..af4729b9dc 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -981,7 +981,7 @@ static int mount_in_namespace_legacy( goto finish; } - r = namespace_fork("(sd-bindmnt)", "(sd-bindmnt-inner)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG, + r = namespace_fork("(sd-bindmnt)", "(sd-bindmnt-inner)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, pidns_fd, mntns_fd, -1, -1, root_fd, &child); if (r < 0) goto finish; @@ -1184,7 +1184,7 @@ static int mount_in_namespace( "(sd-bindmnt-inner)", /* except_fds= */ NULL, /* n_except_fds= */ 0, - FORK_RESET_SIGNALS|FORK_DEATHSIG, + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, pidns_fd, mntns_fd, /* netns_fd= */ -1, diff --git a/src/shared/pager.c b/src/shared/pager.c index 2e9c82cde7..19deefab56 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -316,7 +316,7 @@ int show_man_page(const char *desc, bool null_stdio) { } else args[1] = desc; - r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_REARRANGE_STDIO : 0)|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|(null_stdio ? FORK_REARRANGE_STDIO : 0)|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/shared/varlink.c b/src/shared/varlink.c index 23ce56ca9f..2ba942f30e 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -452,7 +452,7 @@ int varlink_connect_exec(Varlink **ret, const char *_command, char **_argv) { /* stdio_fds= */ NULL, /* except_fds= */ (int[]) { pair[1] }, /* n_except_fds= */ 1, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REOPEN_LOG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &pid); if (r < 0) return log_debug_errno(r, "Failed to spawn process: %m"); diff --git a/src/socket-activate/socket-activate.c b/src/socket-activate/socket-activate.c index 644bed0dcc..78ecb29e60 100644 --- a/src/socket-activate/socket-activate.c +++ b/src/socket-activate/socket-activate.c @@ -223,7 +223,7 @@ static int fork_and_exec_process(const char *child, char **argv, int fd) { return log_oom(); r = safe_fork("(activate)", - FORK_RESET_SIGNALS | FORK_DEATHSIG | FORK_RLIMIT_NOFILE_SAFE | FORK_LOG, + FORK_RESET_SIGNALS | FORK_DEATHSIG_SIGTERM | FORK_RLIMIT_NOFILE_SAFE | FORK_LOG, &child_pid); if (r < 0) return r; diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c index f2de30cc3f..b26663df08 100644 --- a/src/sulogin-shell/sulogin-shell.c +++ b/src/sulogin-shell/sulogin-shell.c @@ -70,7 +70,7 @@ static int fork_wait(const char* const cmdline[]) { pid_t pid; int r; - r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 858868c0c5..8dc515e4d5 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -973,7 +973,7 @@ static int merge(ImageClass image_class, pid_t pid; int r; - r = safe_fork("(sd-merge)", FORK_DEATHSIG|FORK_LOG|FORK_NEW_MOUNTNS, &pid); + r = safe_fork("(sd-merge)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_NEW_MOUNTNS, &pid); if (r < 0) return log_error_errno(r, "Failed to fork off child: %m"); if (r == 0) { diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index 6e2238f2a5..d93bffb759 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -84,7 +84,7 @@ static int load_kexec_kernel(void) { if (arg_dry_run) return 0; - r = safe_fork("(kexec)", FORK_WAIT|FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(kexec)", FORK_WAIT|FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/systemctl/systemctl-sysv-compat.c b/src/systemctl/systemctl-sysv-compat.c index 37fc6347fe..2aa1ec6d83 100644 --- a/src/systemctl/systemctl-sysv-compat.c +++ b/src/systemctl/systemctl-sysv-compat.c @@ -210,7 +210,7 @@ int enable_sysv_units(const char *verb, char **args) { if (!arg_quiet) log_info("Executing: %s", l); - j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (j < 0) return j; if (j == 0) { diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index 15e89336de..e4bdd88284 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -292,7 +292,7 @@ static int download_manifest( r = safe_fork_full("(sd-pull)", (int[]) { -EBADF, pfd[1], STDERR_FILENO }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG, &pid); if (r < 0) return r; diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index acf75e51c7..f8f4a154f9 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -793,7 +793,7 @@ static int run_helper( assert(path); assert(cmdline); - r = safe_fork(name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork(name, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); if (r < 0) return r; if (r == 0) { diff --git a/src/test/test-async.c b/src/test/test-async.c index b5a4f5904b..75bc4d8238 100644 --- a/src/test/test-async.c +++ b/src/test/test-async.c @@ -30,7 +30,7 @@ TEST(asynchronous_close) { assert_se(fcntl(fd, F_GETFD) == -1); assert_se(errno == EBADF); - r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_LOG|FORK_WAIT, NULL); assert(r >= 0); if (r == 0) { @@ -63,7 +63,7 @@ TEST(asynchronous_rm_rf) { /* Do this once more, form a subreaper. Which is nice, because we can watch the async child even * though detached */ - r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(subreaper)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { diff --git a/src/test/test-capability.c b/src/test/test-capability.c index 2f93fbeede..e8a0569737 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -254,7 +254,7 @@ static void test_capability_get_ambient(void) { assert_se(capability_get_ambient(&c) >= 0); - r = safe_fork("(getambient)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL); + r = safe_fork("(getambient)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL); assert_se(r >= 0); if (r == 0) { diff --git a/src/test/test-data-fd-util.c b/src/test/test-data-fd-util.c index d3bb0e3f5f..aa68132e45 100644 --- a/src/test/test-data-fd-util.c +++ b/src/test/test-data-fd-util.c @@ -115,7 +115,7 @@ TEST(copy_data_fd) { assert_se(socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, sfd) >= 0); - r = safe_fork("(sd-pipe)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + r = safe_fork("(sd-pipe)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG, &pid); assert_se(r >= 0); if (r == 0) { diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index a6b5f2f347..dffbad6327 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -475,7 +475,7 @@ TEST(setenv_systemd_exec_pid) { TEST(getenv_steal_erase) { int r; - r = safe_fork("(sd-getenvstealerase)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(sd-getenvstealerase)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 6156c9caa7..ab0c4e905d 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -602,7 +602,7 @@ static int find_libraries(const char *exec, char ***ret) { r = safe_fork_full("(spawn-ldd)", (int[]) { -EBADF, outpipe[1], errpipe[1] }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG, &pid); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG, &pid); assert_se(r >= 0); if (r == 0) { execlp("ldd", "ldd", exec, NULL); @@ -1299,7 +1299,7 @@ static int prepare_ns(const char *process_name) { r = safe_fork(process_name, FORK_RESET_SIGNALS | FORK_CLOSE_ALL_FDS | - FORK_DEATHSIG | + FORK_DEATHSIG_SIGTERM | FORK_WAIT | FORK_REOPEN_LOG | FORK_LOG | diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 9c74d88abc..021d4b47c2 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -337,7 +337,7 @@ TEST(close_all_fds) { * with /proc/ overmounted, and once with the combination of both. This should trigger all fallbacks * in the close_range_all() function. */ - r = safe_fork("(caf-plain)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(caf-plain)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { test_close_all_fds_inner(); _exit(EXIT_SUCCESS); @@ -347,7 +347,7 @@ TEST(close_all_fds) { if (geteuid() != 0) return (void) log_tests_skipped("Lacking privileges for test with close_range() blocked and /proc/ overmounted"); - r = safe_fork("(caf-noproc)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); + r = safe_fork("(caf-noproc)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); if (r == 0) { r = mount_nofollow_verbose(LOG_WARNING, "tmpfs", "/proc", "tmpfs", 0, NULL); if (r < 0) @@ -361,7 +361,7 @@ TEST(close_all_fds) { if (!is_seccomp_available()) return (void) log_tests_skipped("Seccomp not available"); - r = safe_fork("(caf-seccomp)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(caf-seccomp)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { r = seccomp_prohibit_close_range(); if (r < 0) @@ -373,7 +373,7 @@ TEST(close_all_fds) { } assert_se(r >= 0); - r = safe_fork("(caf-scnp)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); + r = safe_fork("(caf-scnp)", FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, NULL); if (r == 0) { r = seccomp_prohibit_close_range(); if (r < 0) diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index d76a4ced91..ad98a92060 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -969,7 +969,7 @@ TEST(read_full_file_socket) { /* Bind the *client* socket to some randomized name, to verify that this works correctly. */ assert_se(asprintf(&clientname, "@%" PRIx64 "/test-bindname", random_u64()) >= 0); - r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid); + r = safe_fork("(server)", FORK_DEATHSIG_SIGTERM|FORK_LOG, &pid); assert_se(r >= 0); if (r == 0) { union sockaddr_union peer = {}; diff --git a/src/test/test-mempress.c b/src/test/test-mempress.c index 69928f3de0..26ce4cee79 100644 --- a/src/test/test-mempress.c +++ b/src/test/test-mempress.c @@ -226,7 +226,7 @@ TEST(real_pressure) { assert_se(pipe2(pipe_fd, O_CLOEXEC) >= 0); - r = safe_fork("(eat-memory)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + r = safe_fork("(eat-memory)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, &pid); assert_se(r >= 0); if (r == 0) { real_pressure_eat_memory(pipe_fd[0]); diff --git a/src/test/test-mkdir.c b/src/test/test-mkdir.c index 03e5a4997c..4820b3251a 100644 --- a/src/test/test-mkdir.c +++ b/src/test/test-mkdir.c @@ -80,7 +80,7 @@ TEST(mkdir_p_safe) { p = mfree(p); assert_se(p = path_join(tmp, "zero-mode/should-fail-to-create-child")); assert_se(mkdir_parents_safe(tmp, p, 0000, UID_INVALID, GID_INVALID, 0) >= 0); - r = safe_fork("(test-mkdir-no-cap)", FORK_DEATHSIG | FORK_WAIT | FORK_LOG, NULL); + r = safe_fork("(test-mkdir-no-cap)", FORK_DEATHSIG_SIGTERM | FORK_WAIT | FORK_LOG, NULL); if (r == 0) { (void) capability_bounding_set_drop(0, /* right_now = */ true); assert_se(mkdir_p_safe(tmp, p, 0000, UID_INVALID, GID_INVALID, 0) == -EACCES); diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index 1cfc1f3ae5..c3d0acb6af 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -280,7 +280,7 @@ TEST(make_mount_switch_root) { r = safe_fork("(switch-root)", FORK_RESET_SIGNALS | FORK_CLOSE_ALL_FDS | - FORK_DEATHSIG | + FORK_DEATHSIG_SIGTERM | FORK_WAIT | FORK_REOPEN_LOG | FORK_LOG | @@ -332,7 +332,7 @@ TEST(umount_recursive) { r = safe_fork("(umount-rec)", FORK_RESET_SIGNALS | FORK_CLOSE_ALL_FDS | - FORK_DEATHSIG | + FORK_DEATHSIG_SIGTERM | FORK_WAIT | FORK_REOPEN_LOG | FORK_LOG | @@ -406,7 +406,7 @@ TEST(fd_make_mount_point) { r = safe_fork("(make_mount-point)", FORK_RESET_SIGNALS | FORK_CLOSE_ALL_FDS | - FORK_DEATHSIG | + FORK_DEATHSIG_SIGTERM | FORK_WAIT | FORK_REOPEN_LOG | FORK_LOG | diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 22fb1fd278..09ad82d239 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -634,7 +634,7 @@ TEST(safe_fork) { BLOCK_SIGNALS(SIGCHLD); - r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG, &pid); + r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG, &pid); assert_se(r >= 0); if (r == 0) { @@ -701,7 +701,7 @@ TEST(setpriority_closest) { int r; r = safe_fork("(test-setprio)", - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT|FORK_LOG, NULL); + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_WAIT|FORK_LOG, NULL); assert_se(r >= 0); if (r == 0) { @@ -855,7 +855,7 @@ TEST(get_process_threads) { int r; /* Run this test in a child, so that we can guarantee there's exactly one thread around in the child */ - r = safe_fork("(nthreads)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_REOPEN_LOG|FORK_WAIT|FORK_LOG, NULL); + r = safe_fork("(nthreads)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_WAIT|FORK_LOG, NULL); assert_se(r >= 0); if (r == 0) { diff --git a/src/test/test-rm-rf.c b/src/test/test-rm-rf.c index 6a8b7d823f..4c69bd28c9 100644 --- a/src/test/test-rm-rf.c +++ b/src/test/test-rm-rf.c @@ -93,7 +93,7 @@ TEST(rm_rf_chmod) { /* This test only works unpriv (as only then the access mask for the owning user matters), * hence drop privs here */ - r = safe_fork("(setresuid)", FORK_DEATHSIG|FORK_WAIT, NULL); + r = safe_fork("(setresuid)", FORK_DEATHSIG_SIGTERM|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index b5fb437c5b..e9c776a8c5 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -158,7 +158,7 @@ TEST(in_addr_is_multicast) { TEST(getpeercred_getpeergroups) { int r; - r = safe_fork("(getpeercred)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(getpeercred)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { @@ -228,7 +228,7 @@ TEST(passfd_read) { assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); - r = safe_fork("(passfd_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(passfd_read)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { @@ -270,7 +270,7 @@ TEST(passfd_contents_read) { assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); - r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { @@ -322,7 +322,7 @@ TEST(pass_many_fds_contents_read) { assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); - r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(passfd_contents_read)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { @@ -382,7 +382,7 @@ TEST(receive_nopassfd) { assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); - r = safe_fork("(receive_nopassfd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(receive_nopassfd)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { @@ -418,7 +418,7 @@ TEST(send_nodata_nofd) { assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); - r = safe_fork("(send_nodata_nofd)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(send_nodata_nofd)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { @@ -451,7 +451,7 @@ TEST(send_emptydata) { assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) >= 0); - r = safe_fork("(send_emptydata)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(send_emptydata)", FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT, NULL); assert_se(r >= 0); if (r == 0) { diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 697dc2f14f..aca241c0c9 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -381,7 +381,7 @@ static int worker_spawn(Manager *manager, Event *event) { if (r < 0) return log_error_errno(r, "Worker: Failed to enable receiving of device: %m"); - r = safe_fork("(udev-worker)", FORK_DEATHSIG, &pid); + r = safe_fork("(udev-worker)", FORK_DEATHSIG_SIGTERM, &pid); if (r < 0) { event->state = EVENT_QUEUED; return log_error_errno(r, "Failed to fork() worker: %m"); diff --git a/src/udev/udev-spawn.c b/src/udev/udev-spawn.c index 9501013d1c..1d0b8548e3 100644 --- a/src/udev/udev-spawn.c +++ b/src/udev/udev-spawn.c @@ -280,7 +280,7 @@ int udev_event_spawn( r = safe_fork_full("(spawn)", (int[]) { -EBADF, outpipe[WRITE_END], errpipe[WRITE_END] }, NULL, 0, - FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &pid); if (r < 0) return log_device_error_errno(event->dev, r, diff --git a/src/udev/udevadm-lock.c b/src/udev/udevadm-lock.c index 89d7d256d2..8993cc4571 100644 --- a/src/udev/udevadm-lock.c +++ b/src/udev/udevadm-lock.c @@ -226,7 +226,7 @@ static int lock_device( BLOCK_SIGNALS(SIGCHLD); - r = safe_fork("(timed-flock)", FORK_DEATHSIG|FORK_LOG, &flock_pid); + r = safe_fork("(timed-flock)", FORK_DEATHSIG_SIGKILL|FORK_LOG, &flock_pid); if (r < 0) return r; if (r == 0) { @@ -349,7 +349,7 @@ int lock_main(int argc, char *argv[], void *userdata) { /* Ignore SIGINT and allow the forked process to receive it */ (void) ignore_signals(SIGINT); - r = safe_fork("(lock)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(lock)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { diff --git a/src/userdb/userdbd-manager.c b/src/userdb/userdbd-manager.c index 73da3fb1e8..62a1fa5f4b 100644 --- a/src/userdb/userdbd-manager.c +++ b/src/userdb/userdbd-manager.c @@ -149,7 +149,7 @@ static int start_one_worker(Manager *m) { "(sd-worker)", /* stdio_fds= */ NULL, &m->listen_fd, 1, - FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_REOPEN_LOG|FORK_LOG|FORK_CLOSE_ALL_FDS, + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_REOPEN_LOG|FORK_LOG|FORK_CLOSE_ALL_FDS, &pid); if (r < 0) return log_error_errno(r, "Failed to fork new worker child: %m"); From e5f1e8b89432724ba5c4772d1394eefe799c5504 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 2 Nov 2023 11:07:14 +0100 Subject: [PATCH 03/12] lock-util: add a new lock_generic_with_timeout() helper This is just like lock_generic(), but applies the lock with a timeout. This requires jumping through some hoops by executing things in a child process, so that we can abort if necessary via a timer. Linux after all has no native way to take file locks with a timeout. --- src/basic/lock-util.c | 89 +++++++++++++++++++++++++++++++++++++++ src/basic/lock-util.h | 2 + src/basic/meson.build | 1 + src/test/test-lock-util.c | 27 ++++++++++++ 4 files changed, 119 insertions(+) diff --git a/src/basic/lock-util.c b/src/basic/lock-util.c index 7e67c2d583..047fd0184d 100644 --- a/src/basic/lock-util.c +++ b/src/basic/lock-util.c @@ -2,10 +2,12 @@ #include #include +#include #include #include #include #include +#include #include "alloc-util.h" #include "fd-util.h" @@ -14,6 +16,7 @@ #include "macro.h" #include "missing_fcntl.h" #include "path-util.h" +#include "process-util.h" int make_lock_file_at(int dir_fd, const char *p, int operation, LockFile *ret) { _cleanup_close_ int fd = -EBADF, dfd = -EBADF; @@ -186,3 +189,89 @@ int lock_generic(int fd, LockType type, int operation) { assert_not_reached(); } } + +int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout) { + _cleanup_(sigkill_waitp) pid_t pid = 0; + int r; + + assert(fd >= 0); + + /* A version of lock_generic(), but with a time-out. We do this in a child process, since the kernel + * APIs natively don't support a timeout. We set a SIGALRM timer that will kill the child after the + * timeout is hit. Returns -ETIMEDOUT if the time-out is hit, and 0 on success. + * + * This only works for BSD and UNPOSIX locks, as only those are fd-bound, and hence can be acquired + * from any process that has access to the fd. POSIX locks OTOH are process-bound, and hence if we'd + * acquire them in a child process they'd remain unlocked in the parent. */ + + if (type == LOCK_NONE) + return 0; + if (!IN_SET(type, LOCK_BSD, LOCK_UNPOSIX)) /* Not for POSIX locks, see above. */ + return -EOPNOTSUPP; + + /* First, try without forking anything off */ + r = lock_generic(fd, type, operation | (timeout == USEC_INFINITY ? 0 : LOCK_NB)); + if (r != -EAGAIN || timeout == 0 || FLAGS_SET(operation, LOCK_NB)) + return r; + + /* If that didn't work, try with a child */ + + r = safe_fork("(sd-flock)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, &pid); + if (r < 0) + return log_error_errno(r, "Failed to flock block device in child process: %m"); + if (r == 0) { + struct sigevent sev = { + .sigev_notify = SIGEV_SIGNAL, + .sigev_signo = SIGALRM, + }; + timer_t id = 0; + + if (timer_create(CLOCK_MONOTONIC, &sev, &id) < 0) { + log_error_errno(errno, "Failed to allocate CLOCK_MONOTONIC timer: %m"); + _exit(EXIT_FAILURE); + } + + struct itimerspec its = {}; + timespec_store(&its.it_value, timeout); + + if (timer_settime(id, /* flags= */ 0, &its, NULL) < 0) { + log_error_errno(errno, "Failed to start CLOCK_MONOTONIC timer: %m"); + _exit(EXIT_FAILURE); + } + + if (lock_generic(fd, type, operation) < 0) { + log_error_errno(errno, "Unable to get an exclusive lock on the device: %m"); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + siginfo_t status; + r = wait_for_terminate(pid, &status); + if (r < 0) + return r; + + TAKE_PID(pid); + + switch (status.si_code) { + + case CLD_EXITED: + if (status.si_status != EXIT_SUCCESS) + return -EPROTO; + + return 0; + + case CLD_KILLED: + if (status.si_status == SIGALRM) + return -ETIMEDOUT; + + _fallthrough_; + + case CLD_DUMPED: + return -EPROTO; + + default: + assert_not_reached(); + } +} diff --git a/src/basic/lock-util.h b/src/basic/lock-util.h index b96ad85e21..91b332f803 100644 --- a/src/basic/lock-util.h +++ b/src/basic/lock-util.h @@ -41,3 +41,5 @@ typedef enum LockType { } LockType; int lock_generic(int fd, LockType type, int operation); + +int lock_generic_with_timeout(int fd, LockType type, int operation, usec_t timeout); diff --git a/src/basic/meson.build b/src/basic/meson.build index 438e8493b8..d7450d8b44 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -273,6 +273,7 @@ libbasic = static_library( include_directories : basic_includes, dependencies : [libcap, libm, + librt, threads, userspace], c_args : ['-fvisibility=default'], diff --git a/src/test/test-lock-util.c b/src/test/test-lock-util.c index 28fc54a5d6..5edd087582 100644 --- a/src/test/test-lock-util.c +++ b/src/test/test-lock-util.c @@ -35,4 +35,31 @@ TEST(make_lock_file) { assert_se(make_lock_file("lock", LOCK_EX|LOCK_NB, &lock2) == -EBUSY); } +static void test_lock_generic_with_timeout_for_type(LockType type) { + _cleanup_(rm_rf_physical_and_freep) char *t = NULL; + _cleanup_close_ int tfd = -EBADF, tfd2 = -EBADF; + + tfd = mkdtemp_open(NULL, 0, &t); + assert_se(tfd >= 0); + + tfd2 = fd_reopen(tfd, O_CLOEXEC|O_DIRECTORY); + assert_se(tfd2 >= 0); + + assert_se(lock_generic(tfd, LOCK_BSD, LOCK_EX) >= 0); + assert_se(lock_generic(tfd2, LOCK_BSD, LOCK_EX|LOCK_NB) == -EWOULDBLOCK); + + usec_t start = now(CLOCK_MONOTONIC); + assert_se(lock_generic_with_timeout(tfd2, LOCK_BSD, LOCK_EX, 200 * USEC_PER_MSEC) == -ETIMEDOUT); + assert_se(usec_sub_unsigned(now(CLOCK_MONOTONIC), start) >= 200 * USEC_PER_MSEC); + + assert_se(lock_generic(tfd, LOCK_BSD, LOCK_UN) >= 0); + assert_se(lock_generic_with_timeout(tfd2, LOCK_BSD, LOCK_EX, 200 * USEC_PER_MSEC) == 0); + assert_se(lock_generic(tfd, LOCK_BSD, LOCK_EX|LOCK_NB) == -EWOULDBLOCK); +} + +TEST(lock_generic_with_timeout) { + test_lock_generic_with_timeout_for_type(LOCK_BSD); + test_lock_generic_with_timeout_for_type(LOCK_UNPOSIX); +} + DEFINE_TEST_MAIN(LOG_INFO); From de7e033286520caa1824b376b1afa143b3d71aa1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 2 Nov 2023 11:08:29 +0100 Subject: [PATCH 04/12] udevadm-lock: switch things over to lock_generic_with_timeout() This replaces the local implementation of a timeout file lock with our new generic one. Note that a comment in the old code claimed we couldn't use alarm()-like timeouts, but htat's not entirely true: we can if we use SIGKILL, and thus know for sure that the process will be dead in case the timer is hit before we actually enter the file lock syscall. But we also know it will be delivered if we hit after. --- src/udev/udevadm-lock.c | 85 +++++++---------------------------------- 1 file changed, 13 insertions(+), 72 deletions(-) diff --git a/src/udev/udevadm-lock.c b/src/udev/udevadm-lock.c index 8993cc4571..bc2d5e76bd 100644 --- a/src/udev/udevadm-lock.c +++ b/src/udev/udevadm-lock.c @@ -10,6 +10,7 @@ #include "device-util.h" #include "fd-util.h" #include "fdset.h" +#include "lock-util.h" #include "main-func.h" #include "parse-util.h" #include "path-util.h" @@ -194,91 +195,31 @@ static int lock_device( if (!S_ISBLK(st.st_mode) || st.st_rdev != devno) return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Path '%s' no longer refers to specified block device %u:%u: %m", path, major(devno), minor(devno)); - if (flock(fd, LOCK_EX|LOCK_NB) < 0) { - - if (errno != EAGAIN) - return log_error_errno(errno, "Failed to lock device '%s': %m", path); + r = lock_generic(fd, LOCK_BSD, LOCK_EX|LOCK_NB); + if (r < 0) { + if (r != -EAGAIN) + return log_error_errno(r, "Failed to lock device '%s': %m", path); if (deadline == 0) return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Device '%s' is currently locked.", path); if (deadline == USEC_INFINITY) { - log_info("Device '%s' is currently locked, waiting%s", path, special_glyph(SPECIAL_GLYPH_ELLIPSIS)); - if (flock(fd, LOCK_EX) < 0) - return log_error_errno(errno, "Failed to lock device '%s': %m", path); - + r = lock_generic(fd, LOCK_BSD, LOCK_EX); } else { - _cleanup_(sigkill_waitp) pid_t flock_pid = 0; - - /* flock() doesn't support a time-out. Let's fake one then. The traditional way to do - * this is via alarm()/setitimer()/timer_create(), but that's racy, given that the - * SIGALRM might already fire between the alarm() and the flock() in which case the - * flock() is never cancelled and we lock up (this is a short time window, but with - * short timeouts on a loaded machine we might run into it, who knows?). Let's - * instead do the lock out-of-process: fork off a child that does the locking, and - * that we'll wait on and kill if it takes too long. */ + usec_t left = usec_sub_unsigned(deadline, now(CLOCK_MONOTONIC)); log_info("Device '%s' is currently locked, waiting %s%s", - path, FORMAT_TIMESPAN(usec_sub_unsigned(deadline, now(CLOCK_MONOTONIC)), 0), + path, FORMAT_TIMESPAN(left, 0), special_glyph(SPECIAL_GLYPH_ELLIPSIS)); - BLOCK_SIGNALS(SIGCHLD); - - r = safe_fork("(timed-flock)", FORK_DEATHSIG_SIGKILL|FORK_LOG, &flock_pid); - if (r < 0) - return r; - if (r == 0) { - /* Child */ - - if (flock(fd, LOCK_EX) < 0) { - log_error_errno(errno, "Failed to lock device '%s': %m", path); - _exit(EXIT_FAILURE); - } - - _exit(EXIT_SUCCESS); - } - - for (;;) { - siginfo_t si; - sigset_t ss; - usec_t n; - - assert_se(sigemptyset(&ss) >= 0); - assert_se(sigaddset(&ss, SIGCHLD) >= 0); - - n = now(CLOCK_MONOTONIC); - if (n >= deadline) - return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Timeout reached."); - - r = sigtimedwait(&ss, NULL, TIMESPEC_STORE(deadline - n)); - if (r < 0) { - if (errno != EAGAIN) - return log_error_errno(errno, "Failed to wait for SIGCHLD: %m"); - - return log_error_errno(SYNTHETIC_ERRNO(ETIMEDOUT), "Timeout reached."); - } - - assert(r == SIGCHLD); - - zero(si); - - if (waitid(P_PID, flock_pid, &si, WEXITED|WNOHANG|WNOWAIT) < 0) - return log_error_errno(errno, "Failed to wait for child: %m"); - - if (si.si_pid != 0) { - assert(si.si_pid == flock_pid); - - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Unexpected exit status of file lock child."); - - break; - } - - log_debug("Got SIGCHLD for other child, continuing."); - } + r = lock_generic_with_timeout(fd, LOCK_BSD, LOCK_EX, left); + if (r == -ETIMEDOUT) + return log_error_errno(r, "Timeout reached."); } + if (r < 0) + return log_error_errno(r, "Failed to lock device '%s': %m", path); } log_debug("Successfully locked %s (%u:%u)%s", path, major(devno), minor(devno), special_glyph(SPECIAL_GLYPH_ELLIPSIS)); From 50ab93eb0e82b7c6a5f0d7796cbc32f2a3682421 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 27 Oct 2023 18:16:19 +0200 Subject: [PATCH 05/12] blockdev-util: export block_device_get_originating() --- src/shared/blockdev-util.c | 2 +- src/shared/blockdev-util.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shared/blockdev-util.c b/src/shared/blockdev-util.c index c6ccab081e..c906aec109 100644 --- a/src/shared/blockdev-util.c +++ b/src/shared/blockdev-util.c @@ -104,7 +104,7 @@ int block_device_get_whole_disk(sd_device *dev, sd_device **ret) { return 0; } -static int block_device_get_originating(sd_device *dev, sd_device **ret) { +int block_device_get_originating(sd_device *dev, sd_device **ret) { _cleanup_(sd_device_unrefp) sd_device *first_found = NULL; const char *suffix; dev_t devnum = 0; /* avoid false maybe-uninitialized warning */ diff --git a/src/shared/blockdev-util.h b/src/shared/blockdev-util.h index ea093c49a6..954a23df3e 100644 --- a/src/shared/blockdev-util.h +++ b/src/shared/blockdev-util.h @@ -27,6 +27,7 @@ int block_device_new_from_path(const char *path, BlockDeviceLookupFlag flag, sd_ int block_device_is_whole_disk(sd_device *dev); int block_device_get_whole_disk(sd_device *dev, sd_device **ret); +int block_device_get_originating(sd_device *dev, sd_device **ret); int block_get_whole_disk(dev_t d, dev_t *ret); int block_get_originating(dev_t d, dev_t *ret); From 0adb58c3f19a347cd85d99c7dfd7f29965ec2ff8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 27 Oct 2023 23:16:06 +0200 Subject: [PATCH 06/12] sd-netlink: add call to query sd_event object an sd_netlink object is attached to This mimics a similar call sd_bus_get_event() that already exists for sd-bus. --- src/libsystemd/sd-netlink/sd-netlink.c | 6 ++++++ src/systemd/sd-netlink.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 21a65e4dac..898f11d1ab 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -761,6 +761,12 @@ int sd_netlink_detach_event(sd_netlink *nl) { return 0; } +sd_event* sd_netlink_get_event(sd_netlink *nl) { + assert_return(nl, NULL); + + return nl->event; +} + int netlink_add_match_internal( sd_netlink *nl, sd_netlink_slot **ret_slot, diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 7edfeb7687..4119c453fb 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -67,6 +67,7 @@ int sd_netlink_add_match(sd_netlink *nl, sd_netlink_slot **ret_slot, uint16_t ma int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority); int sd_netlink_detach_event(sd_netlink *nl); +sd_event* sd_netlink_get_event(sd_netlink *nl); int sd_netlink_attach_filter(sd_netlink *nl, size_t len, const struct sock_filter *filter); /* Message construction */ From 3b516db71d7e394b74653dfd81c27a7b6a65d453 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 30 Oct 2023 09:48:44 +0100 Subject: [PATCH 07/12] glyph-util: add computer disk + world emoji --- src/basic/glyph-util.c | 2 ++ src/basic/glyph-util.h | 2 ++ src/test/test-locale-util.c | 4 +++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/basic/glyph-util.c b/src/basic/glyph-util.c index bac75a053a..803bdd90e2 100644 --- a/src/basic/glyph-util.c +++ b/src/basic/glyph-util.c @@ -134,6 +134,8 @@ const char *special_glyph_full(SpecialGlyph code, bool force_utf) { [SPECIAL_GLYPH_SPARKLES] = u8"✨", [SPECIAL_GLYPH_LOW_BATTERY] = u8"🪫", [SPECIAL_GLYPH_WARNING_SIGN] = u8"⚠️", + [SPECIAL_GLYPH_COMPUTER_DISK] = u8"💽", + [SPECIAL_GLYPH_WORLD] = u8"🌍", }, }; diff --git a/src/basic/glyph-util.h b/src/basic/glyph-util.h index 451ec9af25..a7709976e1 100644 --- a/src/basic/glyph-util.h +++ b/src/basic/glyph-util.h @@ -47,6 +47,8 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_SPARKLES, SPECIAL_GLYPH_LOW_BATTERY, SPECIAL_GLYPH_WARNING_SIGN, + SPECIAL_GLYPH_COMPUTER_DISK, + SPECIAL_GLYPH_WORLD, _SPECIAL_GLYPH_MAX, _SPECIAL_GLYPH_INVALID = -EINVAL, } SpecialGlyph; diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index 9cf0bf42bf..39f71c67d3 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -82,7 +82,7 @@ TEST(keymaps) { #define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x)) TEST(dump_special_glyphs) { - assert_cc(SPECIAL_GLYPH_WARNING_SIGN + 1 == _SPECIAL_GLYPH_MAX); + assert_cc(SPECIAL_GLYPH_WORLD + 1 == _SPECIAL_GLYPH_MAX); log_info("is_locale_utf8: %s", yes_no(is_locale_utf8())); @@ -125,6 +125,8 @@ TEST(dump_special_glyphs) { dump_glyph(SPECIAL_GLYPH_SPARKLES); dump_glyph(SPECIAL_GLYPH_LOW_BATTERY); dump_glyph(SPECIAL_GLYPH_WARNING_SIGN); + dump_glyph(SPECIAL_GLYPH_COMPUTER_DISK); + dump_glyph(SPECIAL_GLYPH_WORLD); } DEFINE_TEST_MAIN(LOG_INFO); From 1761066b135f1a322c446f102343ea4aa61fe3ee Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 27 Oct 2023 14:25:49 +0200 Subject: [PATCH 08/12] storagetm: add new systemd-storagetm component This implements a "storage target mode", similar to what MacOS provides since a long time as "Target Disk Mode": https://en.wikipedia.org/wiki/Target_Disk_Mode This implementation is relatively simple: 1. a new generic target "storage-target-mode.target" is added, which when booted into defines the target mode. 2. a small tool and service "systemd-storagetm.service" is added which exposes a specific device or all devices as NVMe-TCP devices over the network. NVMe-TCP appears to be hot shit right now how to expose block devices over the network. And it's really simple to set up via configs, hence our code is relatively short and neat. The idea is that systemd-storagetm.target can be extended sooner or later, for example to expose block devices also as USB mass storage devices and similar, in case the system has "dual mode" USB controller that can also work as device, not just as host. (And people could also plug in sharing as NBD, iSCSI, whatever they want.) How to use this? Boot into your system with a kernel cmdline of "rd.systemd.unit=storage-target-mode.target ip=link-local", and you'll see on screen the precise "nvme connect" command line to make the relevant block devices available locally on some other machine. This all requires that the target mode stuff is included in the initrd of course. And the system will the stay in the initrd forever. Why bother? Primarily three use-cases: 1. Debug a broken system: with very few dependencies during boot get access to the raw block device of a broken machine. 2. Migrate from system to another system, by dd'ing the old to the new directly. 3. Installing an OS remotely on some device (for example via Thunderbolt networking) (And there might be more, for example the ability to boot from a laptop's disk on another system) Limitations: 1. There's no authentication/encryption. Hence: use this on local links only. 2. NVMe target mode on Linux supports r/w operation only. Ideally, we'd have a read-only mode, for security reasons, and default to it. Future love: 1. We should have another mode, where we simply expose the homed LUKS home dirs like that. 2. Some lightweight hookup with plymouth, to display a (shortened) version of the info we write to the console. To test all this, just run: mkosi --kernel-command-line-extra="rd.systemd.unit=storage-target-mode.target" qemu --- meson.build | 6 +- meson_options.txt | 2 + src/storagetm/meson.build | 11 + src/storagetm/storagetm.c | 1047 ++++++++++++++++++++++++++++ units/meson.build | 8 + units/storage-target-mode.target | 16 + units/systemd-storagetm.service.in | 27 + 7 files changed, 1116 insertions(+), 1 deletion(-) create mode 100644 src/storagetm/meson.build create mode 100644 src/storagetm/storagetm.c create mode 100644 units/storage-target-mode.target create mode 100644 units/systemd-storagetm.service.in diff --git a/meson.build b/meson.build index 5f1331e6f3..5ac15d8356 100644 --- a/meson.build +++ b/meson.build @@ -1533,6 +1533,8 @@ have = get_option('sysupdate').require( error_message : 'fdisk and openssl required').allowed() conf.set10('ENABLE_SYSUPDATE', have) +conf.set10('ENABLE_STORAGETM', get_option('storagetm')) + have = get_option('importd').require( conf.get('HAVE_LIBCURL') == 1 and conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and @@ -2196,10 +2198,11 @@ subdir('src/systemctl') subdir('src/sysupdate') subdir('src/sysusers') subdir('src/sysv-generator') +subdir('src/storagetm') subdir('src/timedate') subdir('src/timesync') -subdir('src/tpm2-setup') subdir('src/tmpfiles') +subdir('src/tpm2-setup') subdir('src/tty-ask-password-agent') subdir('src/update-done') subdir('src/update-utmp') @@ -2793,6 +2796,7 @@ foreach tuple : [ ['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1], ['sysupdate'], ['sysusers'], + ['storagetm'], ['timedated'], ['timesyncd'], ['tmpfiles'], diff --git a/meson_options.txt b/meson_options.txt index 831d23e299..c31e3cd4d7 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -158,6 +158,8 @@ option('quotacheck', type : 'boolean', description : 'support for the quotacheck tools') option('sysusers', type : 'boolean', description : 'support for the sysusers configuration') +option('storagetm', type : 'boolean', + description : 'support for Storage Target Mode') option('tmpfiles', type : 'boolean', description : 'support for tmpfiles.d') option('importd', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' }, diff --git a/src/storagetm/meson.build b/src/storagetm/meson.build new file mode 100644 index 0000000000..f95210af4c --- /dev/null +++ b/src/storagetm/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +executables += [ + libexec_template + { + 'name' : 'systemd-storagetm', + 'conditions' : [ + 'ENABLE_STORAGETM', + ], + 'sources' : files('storagetm.c'), + }, +] diff --git a/src/storagetm/storagetm.c b/src/storagetm/storagetm.c new file mode 100644 index 0000000000..dfde848434 --- /dev/null +++ b/src/storagetm/storagetm.c @@ -0,0 +1,1047 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "af-list.h" +#include "alloc-util.h" +#include "blockdev-util.h" +#include "build.h" +#include "daemon-util.h" +#include "device-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "format-util.h" +#include "fs-util.h" +#include "local-addresses.h" +#include "loop-util.h" +#include "main-func.h" +#include "parse-argument.h" +#include "path-util.h" +#include "pretty-print.h" +#include "process-util.h" +#include "random-util.h" +#include "recurse-dir.h" +#include "socket-util.h" +#include "terminal-util.h" +#include "udev-util.h" + +static char **arg_devices = NULL; +static char *arg_nqn = NULL; +static int arg_all = 0; + +STATIC_DESTRUCTOR_REGISTER(arg_devices, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_nqn, freep); + +static int help(void) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-storagetm", "8", &link); + if (r < 0) + return log_oom(); + + printf("%s [OPTIONS...] [DEVICE...]\n" + "\n%sExpose a block device or regular file as NVMe-TCP volume.%s\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --nqn=STRING Select NQN (NVMe Qualified Name)\n" + " -a --all Expose all devices\n" + "\nSee the %s for details.\n", + program_invocation_short_name, + ansi_highlight(), + ansi_normal(), + link); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_NQN = 0x100, + ARG_VERSION, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "nqn", required_argument, NULL, ARG_NQN }, + { "all", no_argument, NULL, 'a' }, + {} + }; + + int r, c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "ha", options, NULL)) >= 0) + + switch (c) { + + case 'h': + return help(); + + case ARG_VERSION: + return version(); + + case ARG_NQN: + if (!filename_is_valid(optarg)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "NQN invalid: %s", optarg); + + if (free_and_strdup(&arg_nqn, optarg) < 0) + return log_oom(); + + break; + + case 'a': + arg_all++; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + + if (arg_all > 0) { + if (argc > optind) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expects no further arguments if --all/-a is specified."); + } else { + if (optind >= argc) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expecting device name or --all/-a."); + + for (int i = optind; i < argc; i++) + if (!path_is_valid(argv[i])) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid path: %s", argv[i]); + + arg_devices = strv_copy(argv + optind); + } + + if (!arg_nqn) { + sd_id128_t id; + + r = sd_id128_get_machine_app_specific(SD_ID128_MAKE(b4,f9,4e,52,b8,e2,45,db,88,84,6e,2e,c3,f4,ef,18), &id); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + /* See NVM Express Base Specification 2.0c, 4.5 "NVMe Qualified Names" */ + if (asprintf(&arg_nqn, "nqn.2023-10.io.systemd:storagetm." SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id)) < 0) + return log_oom(); + } + + return 1; +} + +typedef struct NvmeSubsystem { + char *name; + struct stat device_stat; + int device_fd; + int nvme_all_subsystems_fd; /* The /sys/kernel/config/nvmet/subsystems/ dir, that contains all subsystems */ + int nvme_our_subsystem_fd; /* Our private subsystem dir below it. */ + char *device; +} NvmeSubsystem; + +static NvmeSubsystem* nvme_subsystem_free(NvmeSubsystem *s) { + if (!s) + return NULL; + + free(s->name); + safe_close(s->nvme_all_subsystems_fd); + safe_close(s->nvme_our_subsystem_fd); + safe_close(s->device_fd); + free(s->device); + + return mfree(s); +} + +static int nvme_subsystem_unlink(NvmeSubsystem *s) { + int r; + + assert(s); + + if (s->nvme_our_subsystem_fd >= 0) { + _cleanup_close_ int namespaces_fd = -EBADF; + + namespaces_fd = openat(s->nvme_our_subsystem_fd, "namespaces", O_CLOEXEC|O_DIRECTORY|O_RDONLY); + if (namespaces_fd < 0) + log_warning_errno(errno, "Failed to open 'namespaces' directory of subsystem '%s': %m", s->name); + else { + _cleanup_free_ DirectoryEntries *de = NULL; + + r = readdir_all(namespaces_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); + if (r < 0) + log_warning_errno(r, "Failed to read 'namespaces' dir of subsystem '%s', ignoring: %m", s->name); + else { + FOREACH_ARRAY(ee, de->entries, de->n_entries) { + _cleanup_free_ char *enable_fn = NULL; + const struct dirent *e = *ee; + + enable_fn = path_join(e->d_name, "enable"); + if (!enable_fn) + return log_oom(); + + r = write_string_file_at(namespaces_fd, enable_fn, "0", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + log_warning_errno(r, "Failed to disable namespace '%s' of NVME subsystem '%s', ignoring: %m", e->d_name, s->name); + + if (unlinkat(namespaces_fd, e->d_name, AT_REMOVEDIR) < 0 && errno != ENOENT) + log_warning_errno(errno, "Failed to remove namespace '%s' of NVME subsystem '%s', ignoring: %m", e->d_name, s->name); + } + } + } + + s->nvme_our_subsystem_fd = safe_close(s->nvme_our_subsystem_fd); + } + + if (s->nvme_all_subsystems_fd >= 0 && s->name) { + if (unlinkat(s->nvme_all_subsystems_fd, s->name, AT_REMOVEDIR) < 0 && errno != ENOENT) + log_warning_errno(errno, "Failed to remove NVME subsystem '%s', ignoring: %m", s->name); + + s->nvme_all_subsystems_fd = safe_close(s->nvme_all_subsystems_fd); /* Invalidate the subsystems/ dir fd, to remember we unlinked the thing already */ + + log_info("NVME subsystem '%s' removed.", s->name); + } + + return 0; +} + +static NvmeSubsystem *nvme_subsystem_destroy(NvmeSubsystem *s) { + if (!s) + return NULL; + + (void) nvme_subsystem_unlink(s); + + return nvme_subsystem_free(s); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(NvmeSubsystem*, nvme_subsystem_destroy); + +static int nvme_subsystem_add(const char *node, int consumed_fd, NvmeSubsystem **ret) { + _cleanup_close_ int fd = consumed_fd; /* always take possession of the fd */ + int r; + + assert(node); + assert(ret); + + _cleanup_free_ char *fname = NULL; + r = path_extract_filename(node, &fname); + if (r < 0) + return log_error_errno(r, "Failed to extract file name from path: %s", node); + + _cleanup_free_ char *j = NULL; + j = strjoin(arg_nqn, ".", fname); + if (!j) + return log_oom(); + + if (fd < 0) { + fd = RET_NERRNO(open(node, O_RDONLY|O_CLOEXEC|O_NONBLOCK)); + if (fd < 0) + return log_error_errno(fd, "Failed to open '%s': %m", node); + } + + struct stat st; + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to fstat '%s': %m", node); + if (!S_ISBLK(st.st_mode)) { + r = stat_verify_regular(&st); + if (r < 0) + return log_error_errno(r, "Not a block device or regular file, refusing: %s", node); + } + + /* Let's lock this device continuously while we are operating on it */ + r = lock_generic_with_timeout(fd, LOCK_BSD, LOCK_EX, 10 * USEC_PER_SEC); + if (r < 0) + return log_error_errno(r, "Failed to lock block device: %m"); + + _cleanup_close_ int subsystems_fd = -EBADF; + subsystems_fd = RET_NERRNO(open("/sys/kernel/config/nvmet/subsystems", O_DIRECTORY|O_CLOEXEC|O_RDONLY)); + if (subsystems_fd < 0) + return log_error_errno(subsystems_fd, "Failed to open /sys/kernel/config/nvmet/subsystems: %m"); + + _cleanup_close_ int subsystem_fd = -EBADF; + subsystem_fd = open_mkdir_at(subsystems_fd, j, O_EXCL|O_RDONLY|O_CLOEXEC, 0777); + if (subsystem_fd < 0) + return log_error_errno(subsystem_fd, "Failed to create NVME subsystem '%s': %m", j); + + r = write_string_file_at(subsystem_fd, "attr_allow_any_host", "1", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to set 'attr_allow_any_host' flag: %m"); + + _cleanup_close_ int namespace_fd = -EBADF; + namespace_fd = open_mkdir_at(subsystem_fd, "namespaces/1", O_EXCL|O_RDONLY|O_CLOEXEC, 0777); + if (namespace_fd < 0) + return log_error_errno(namespace_fd, "Failed to create NVME namespace '1': %m"); + + /* We use /proc/$PID/fd/$FD rather than /proc/self/fd/$FD, because this string is visible to others + * via configfs, and by including the PID it's clear to who the stuff belongs. */ + r = write_string_file_at(namespace_fd, "device_path", FORMAT_PROC_PID_FD_PATH(0, fd), WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to write 'device_path' attribute: %m"); + + r = write_string_file_at(namespace_fd, "enable", "1", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to write 'enable' attribute: %m"); + + _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *subsys = NULL; + + subsys = new(NvmeSubsystem, 1); + if (!subsys) + return log_oom(); + + *subsys = (NvmeSubsystem) { + .name = TAKE_PTR(j), + .device_fd = TAKE_FD(fd), + .nvme_all_subsystems_fd = TAKE_FD(subsystems_fd), + .nvme_our_subsystem_fd = TAKE_FD(subsystem_fd), + .device_stat = st, + }; + + subsys->device = strdup(node); + if (!subsys->device) + return log_oom(); + + *ret = TAKE_PTR(subsys); + return 0; +} + +typedef struct NvmePort { + uint16_t portnr; /* used for both the IP and the NVME port numer */ + + int nvme_port_fd; + int nvme_ports_fd; + + int ip_family; +} NvmePort; + +static NvmePort *nvme_port_free(NvmePort *p) { + if (!p) + return NULL; + + safe_close(p->nvme_port_fd); + safe_close(p->nvme_ports_fd); + + return mfree(p); +} + +static int nvme_port_unlink(NvmePort *p) { + int r, ret = 0; + + assert(p); + + if (p->nvme_port_fd >= 0) { + _cleanup_close_ int subsystems_dir_fd = -EBADF; + + subsystems_dir_fd = openat(p->nvme_port_fd, "subsystems", O_DIRECTORY|O_RDONLY|O_CLOEXEC); + if (subsystems_dir_fd < 0) + log_warning_errno(errno, "Failed to open 'subsystems' dir of port %" PRIu16 ", ignoring: %m", p->portnr); + else { + _cleanup_free_ DirectoryEntries *de = NULL; + + r = readdir_all(subsystems_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de); + if (r < 0) + log_warning_errno(r, "Failed to read 'subsystems' dir of port %" PRIu16 ", ignoring: %m", p->portnr); + else + FOREACH_ARRAY(ee, de->entries, de->n_entries) { + const struct dirent *e = *ee; + + if (unlinkat(subsystems_dir_fd, e->d_name, 0) < 0 && errno != ENOENT) + log_warning_errno(errno, "Failed to remove 'subsystems' symlink '%s' of port %" PRIu16 ", ignoring: %m", e->d_name, p->portnr); + } + } + + p->nvme_port_fd = safe_close(p->nvme_port_fd); + } + + if (p->nvme_ports_fd >= 0) { + _cleanup_free_ char *fn = NULL; + if (asprintf(&fn, "%" PRIu16, p->portnr) < 0) + return log_oom(); + + if (unlinkat(p->nvme_ports_fd, fn, AT_REMOVEDIR) < 0) { + if (errno == ENOENT) + ret = 0; + else + ret = log_warning_errno(errno, "Failed to remove port '%" PRIu16 ", ignoring: %m", p->portnr); + } else + ret = 1; + + p->nvme_ports_fd = safe_close(p->nvme_ports_fd); + } + + return ret; +} + +static NvmePort *nvme_port_destroy(NvmePort *p) { + if (!p) + return NULL; + + (void) nvme_port_unlink(p); + + return nvme_port_free(p); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(NvmePort*, nvme_port_destroy); + +static int nvme_port_add_portnr( + int ports_fd, + uint16_t portnr, + int ip_family, + int *ret_fd) { + + int r; + + assert(ports_fd >= 0); + assert(IN_SET(ip_family, AF_INET, AF_INET6)); + assert(ret_fd); + + _cleanup_free_ char *fname = NULL; + if (asprintf(&fname, "%" PRIu16, portnr) < 0) + return log_oom(); + + _cleanup_close_ int port_fd = -EBADF; + port_fd = open_mkdir_at(ports_fd, fname, O_EXCL|O_RDONLY|O_CLOEXEC, 0777); + if (port_fd < 0) { + if (port_fd != -EEXIST) + return log_error_errno(port_fd, "Failed to create port %" PRIu16 ": %m", portnr); + + *ret_fd = -EBADF; + return 0; + } + + r = write_string_file_at(port_fd, "addr_adrfam", af_to_ipv4_ipv6(ip_family), WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to set address family on NVME port %" PRIu16 ": %m", portnr); + + r = write_string_file_at(port_fd, "addr_trtype", "tcp", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to set transport type on NVME port %" PRIu16 ": %m", portnr); + + r = write_string_file_at(port_fd, "addr_trsvcid", fname, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to set IP port on NVME port %" PRIu16 ": %m", portnr); + + r = write_string_file_at(port_fd, "addr_traddr", ip_family == AF_INET6 ? "::" : "0.0.0.0", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_error_errno(r, "Failed to set IP address on NVME port %" PRIu16 ": %m", portnr); + + *ret_fd = TAKE_FD(port_fd); + return 1; +} + +static uint16_t calculate_start_port(const char *name, int ip_family) { + struct siphash state; + uint16_t nr; + + assert(name); + assert(IN_SET(ip_family, AF_INET, AF_INET6)); + + /* Use some fixed key Lennart pulled from /dev/urandom, so that we are deterministic */ + siphash24_init(&state, SD_ID128_MAKE(d1,0b,67,b5,e2,b7,4a,91,8d,6b,27,b6,35,c1,9f,d9).bytes); + siphash24_compress_string(name, &state); + siphash24_compress(&ip_family, sizeof(ip_family), &state); + + nr = 1024U + siphash24_finalize(&state) % (0xFFFFU - 1024U); + SET_FLAG(nr, 1, ip_family == AF_INET6); /* Lowest bit reflects family */ + + return nr; +} + +static uint16_t calculate_next_port(int ip_family) { + uint16_t nr; + + assert(IN_SET(ip_family, AF_INET, AF_INET6)); + + nr = 1024U + random_u64_range(0xFFFFU - 1024U); + SET_FLAG(nr, 1, ip_family == AF_INET6); /* Lowest bit reflects family */ + + return nr; +} + +static int nvme_port_add(const char *name, int ip_family, NvmePort **ret) { + int r; + + assert(name); + assert(IN_SET(ip_family, AF_INET, AF_INET6)); + assert(ret); + + _cleanup_close_ int ports_fd = -EBADF; + ports_fd = RET_NERRNO(open("/sys/kernel/config/nvmet/ports", O_DIRECTORY|O_RDONLY|O_CLOEXEC)); + if (ports_fd < 0) + return log_error_errno(ports_fd, "Failed to open /sys/kernel/config/nvmet/ports: %m"); + + _cleanup_close_ int port_fd = -EBADF; + uint16_t portnr = calculate_start_port(name, ip_family); + for (unsigned attempt = 0;; attempt++) { + r = nvme_port_add_portnr(ports_fd, portnr, ip_family, &port_fd); + if (r < 0) + return r; + if (r > 0) + break; + + if (attempt > 16) + return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find free NVME port after %u attempts.", attempt); + + log_debug_errno(port_fd, "NVME port %" PRIu16 " exists already, randomizing port.", portnr); + + portnr = calculate_next_port(ip_family); + } + + _cleanup_(nvme_port_destroyp) NvmePort *p = new(NvmePort, 1); + if (!p) + return log_oom(); + + *p = (NvmePort) { + .portnr = portnr, + .nvme_ports_fd = TAKE_FD(ports_fd), + .nvme_port_fd = TAKE_FD(port_fd), + .ip_family = ip_family, + }; + + *ret = TAKE_PTR(p); + return 0; +} + +static int nvme_port_link_subsystem(NvmePort *port, NvmeSubsystem *subsys) { + assert(port); + assert(subsys); + + _cleanup_free_ char *target = NULL, *linkname = NULL; + target = path_join("/sys/kernel/config/nvmet/subsystems", subsys->name); + if (!target) + return log_oom(); + + linkname = path_join("subsystems", subsys->name); + if (!linkname) + return log_oom(); + + if (symlinkat(target, port->nvme_port_fd, linkname) < 0) + return log_error_errno(errno, "Failed to link subsystem '%s' to port %" PRIu16 ": %m", subsys->name, port->portnr); + + return 0; +} + +static int nvme_port_unlink_subsystem(NvmePort *port, NvmeSubsystem *subsys) { + assert(port); + assert(subsys); + + _cleanup_free_ char *linkname = NULL; + linkname = path_join("subsystems", subsys->name); + if (!linkname) + return log_oom(); + + if (unlinkat(port->nvme_port_fd, linkname, 0) < 0 && errno != ENOENT) + return log_error_errno(errno, "Failed to unlink subsystem '%s' to port %" PRIu16 ": %m", subsys->name, port->portnr); + + return 0; +} + +static int nvme_subsystem_report(NvmeSubsystem *subsystem, NvmePort *ipv4, NvmePort *ipv6) { + assert(subsystem); + + _cleanup_free_ struct local_address *addresses = NULL; + int n_addresses; + n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n_addresses < 0) + return log_error_errno(n_addresses, "Failed to determine local IP addresses: %m"); + + log_notice("NVMe-TCP: %s %s%s%s (%s)", + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_COMPUTER_DISK) : "", emoji_enabled() ? " " : "", + subsystem->name, subsystem->device); + + FOREACH_ARRAY(a, addresses, n_addresses) { + NvmePort *port = a->family == AF_INET ? ipv4 : ipv6; + + if (!port) + continue; + + log_info(" %s Try for specific device: nvme connect -t tcp -n '%s' -a %s -s %" PRIu16, + special_glyph(a >= addresses + (n_addresses - 1) ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH), + subsystem->name, + IN_ADDR_TO_STRING(a->family, &a->address), + port->portnr); + } + + return 0; +} + +static int nvme_port_report(NvmePort *port) { + if (!port) + return 0; + + _cleanup_free_ struct local_address *addresses = NULL; + int n_addresses; + n_addresses = local_addresses(NULL, 0, port->ip_family, &addresses); + if (n_addresses < 0) + return log_error_errno(n_addresses, "Failed to determine local IP addresses: %m"); + + log_notice("NVMe-TCP: %s %s%sListening on %s (port %" PRIu16 ")", + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), + emoji_enabled() ? special_glyph(SPECIAL_GLYPH_WORLD) : "", emoji_enabled() ? " " : "", + af_to_ipv4_ipv6(port->ip_family), + port->portnr); + + FOREACH_ARRAY(a, addresses, n_addresses) + log_info(" %s Try for all devices: nvme connect-all -t tcp -a %s -s %" PRIu16, + special_glyph(a >= addresses + (n_addresses - 1) ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH), + IN_ADDR_TO_STRING(a->family, &a->address), + port->portnr); + + return 0; +} + +typedef struct Context { + Hashmap *subsystems; + NvmePort *ipv4_port, *ipv6_port; + + bool display_refresh_scheduled; +} Context; + +static void device_hash_func(const struct stat *q, struct siphash *state) { + assert(q); + + if (S_ISBLK(q->st_mode) || S_ISCHR(q->st_mode)) { + mode_t m = q->st_mode & S_IFMT; + siphash24_compress(&m, sizeof(m), state); + siphash24_compress(&q->st_rdev, sizeof(q->st_rdev), state); + return; + } + + return inode_hash_func(q, state); +} + +static int device_compare_func(const struct stat *a, const struct stat *b) { + int r; + + assert(a); + assert(b); + + r = CMP(a->st_mode & S_IFMT, b->st_mode & S_IFMT); + if (r != 0) + return r; + + if (S_ISBLK(a->st_mode) || S_ISCHR(a->st_mode)) { + r = CMP(major(a->st_rdev), major(b->st_rdev)); + if (r != 0) + return r; + + r = CMP(minor(a->st_rdev), minor(b->st_rdev)); + if (r != 0) + return r; + + return 0; + } + + return inode_compare_func(a, b); +} + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( + nvme_subsystem_hash_ops, + struct stat, + device_hash_func, + device_compare_func, + NvmeSubsystem, + nvme_subsystem_destroy); + +static void context_done(Context *c) { + assert(c); + + c->ipv4_port = nvme_port_destroy(c->ipv4_port); + c->ipv6_port = nvme_port_destroy(c->ipv6_port); + + c->subsystems = hashmap_free(c->subsystems); +} + +static void device_track_back(sd_device *d, sd_device **ret) { + int r; + + assert(d); + assert(ret); + + const char *devname = NULL; + (void) sd_device_get_devname(d, &devname); + + _cleanup_(sd_device_unrefp) sd_device *d_originating = NULL; + r = block_device_get_originating(d, &d_originating); + if (r < 0) + log_device_debug_errno(d, r, "Failed to get originating device for '%s', ignoring: %m", strna(devname)); + + sd_device *d_whole = NULL; + r = block_device_get_whole_disk(d_originating ?: d, &d_whole); /* does not ref returned device */ + if (r < 0) + log_device_debug_errno(d, r, "Failed to get whole device for '%s', ignoring: %m", strna(devname)); + + *ret = d_whole ? sd_device_ref(d_whole) : d_originating ? TAKE_PTR(d_originating) : sd_device_ref(d); +} + +static int device_is_same(sd_device *a, sd_device *b) { + dev_t devnum_a, devnum_b; + int r; + + assert(a); + assert(b); + + r = sd_device_get_devnum(a, &devnum_a); + if (r < 0) + return r; + + r = sd_device_get_devnum(b, &devnum_b); + if (r < 0) + return r; + + return devnum_a == devnum_b; +} + +static bool device_is_allowed(sd_device *d) { + int r; + + assert(d); + + if (arg_all >= 2) /* If --all is specified twice we allow even the root fs to shared */ + return true; + + const char *devname; + r = sd_device_get_devname(d, &devname); + if (r < 0) + return log_device_error_errno(d, r, "Failed to get device name: %m"); + + dev_t root_devnum; + r = get_block_device("/", &root_devnum); + if (r < 0) { + log_warning_errno(r, "Failed to get backing device of the root file system: %m"); + return false; /* Better safe */ + } + if (root_devnum == 0) /* Not backed by a block device? */ + return true; + + _cleanup_(sd_device_unrefp) sd_device *root_device = NULL; + r = sd_device_new_from_devnum(&root_device, 'b', root_devnum); + if (r < 0) { + log_warning_errno(r, "Failed to get root block device, assuming device '%s' is same as root device: %m", devname); + return false; + } + + _cleanup_(sd_device_unrefp) sd_device *whole_root_device = NULL; + device_track_back(root_device, &whole_root_device); + + _cleanup_(sd_device_unrefp) sd_device *whole_d = NULL; + device_track_back(d, &whole_d); + + r = device_is_same(whole_root_device, whole_d); + if (r < 0) { + log_warning_errno(r, "Failed to determine if root device and device '%s' are the same, assuming they are: %m", devname); + return false; /* Better safe */ + } + + return !r; +} + +static int device_added(Context *c, sd_device *device) { + _cleanup_close_ int fd = -EBADF; + int r; + + assert(c); + assert(device); + + const char *sysname; + r = sd_device_get_sysname(device, &sysname); + if (r < 0) + return log_device_error_errno(device, r, "Failed to get device name: %m"); + + log_device_debug(device, "new block device '%s'", sysname); + + if (STARTSWITH_SET(sysname, "loop", "zram")) /* Ignore some devices */ + return 0; + + const char *devname; + r = sd_device_get_devname(device, &devname); + if (r < 0) + return log_device_error_errno(device, r, "Failed to get device node path: %m"); + + struct stat lookup_key = { + .st_mode = S_IFBLK, + }; + + r = sd_device_get_devnum(device, &lookup_key.st_rdev); + if (r < 0) + return log_device_error_errno(device, r, "Failed to get major/minor from device: %m"); + + if (hashmap_contains(c->subsystems, &lookup_key)) { + log_debug("Device '%s' already seen.", devname); + return 0; + } + + if (!device_is_allowed(device)) { + log_device_debug(device, "Not exposing device '%s', as it is backed by root disk.", devname); + return 0; + } + + fd = sd_device_open(device, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) { + log_device_warning_errno(device, fd, "Failed to open newly acquired device '%s', ignoring device: %m", devname); + return 0; + } + + _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *s = NULL; + r = nvme_subsystem_add(devname, TAKE_FD(fd), &s); + if (r < 0) + return r; + + if (c->ipv4_port) { + r = nvme_port_link_subsystem(c->ipv4_port, s); + if (r < 0) + return r; + } + + if (c->ipv6_port) { + r = nvme_port_link_subsystem(c->ipv6_port, s); + if (r < 0) + return r; + } + + r = hashmap_ensure_put(&c->subsystems, &nvme_subsystem_hash_ops, &s->device_stat, s); + if (r < 0) + return log_error_errno(r, "Failed to add subsystem to hash table: %m"); + + (void) nvme_subsystem_report(s, c->ipv4_port, c->ipv6_port); + + TAKE_PTR(s); + return 1; +} + +static int device_removed(Context *c, sd_device *device) { + int r; + + assert(device); + + struct stat lookup_key = { + .st_mode = S_IFBLK, + }; + + r = sd_device_get_devnum(device, &lookup_key.st_rdev); + if (r < 0) + return log_device_error_errno(device, r, "Failed to get major/minor from device: %m"); + + NvmeSubsystem *s = hashmap_remove(c->subsystems, &lookup_key); + if (!s) + return 0; + + log_device_debug(device, "removed block device '%s'", s->name); + + if (c->ipv4_port) + (void) nvme_port_unlink_subsystem(c->ipv4_port, s); + if (c->ipv6_port) + (void) nvme_port_unlink_subsystem(c->ipv6_port, s); + + s = nvme_subsystem_destroy(s); + return 1; +} + +static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) { + Context *c = ASSERT_PTR(userdata); + + if (device_for_action(device, SD_DEVICE_REMOVE)) + device_removed(c, device); + else + device_added(c, device); + + return 0; +} + +static int on_display_refresh(sd_event_source *s, uint64_t usec, void *userdata) { + Context *c = ASSERT_PTR(userdata); + + assert(s); + + c->display_refresh_scheduled = false; + + if (isatty(STDERR_FILENO) > 0) + fputs(ANSI_HOME_CLEAR, stderr); + + (void) nvme_port_report(c->ipv4_port); + (void) nvme_port_report(c->ipv6_port); + + NvmeSubsystem *i; + HASHMAP_FOREACH(i, c->subsystems) + (void) nvme_subsystem_report(i, c->ipv4_port, c->ipv6_port); + + return 0; +} + +static int on_address_change(sd_netlink *rtnl, sd_netlink_message *mm, void *userdata) { + Context *c = ASSERT_PTR(userdata); + int r, family; + + assert(rtnl); + assert(mm); + + r = sd_rtnl_message_addr_get_family(mm, &family); + if (r < 0) { + log_warning_errno(r, "Failed to get address family from netlink address message, ignoring: %m"); + return 0; + } + + if (!c->display_refresh_scheduled) { + r = sd_event_add_time_relative( + sd_netlink_get_event(rtnl), + /* ret_slot= */ NULL, + CLOCK_MONOTONIC, + 750 * USEC_PER_MSEC, + 0, + on_display_refresh, + c); + if (r < 0) + log_warning_errno(r, "Failed to schedule display refresh, ignoring: %m"); + else + c->display_refresh_scheduled = true; + } + + return 0; +} + +static int run(int argc, char* argv[]) { + _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + _cleanup_(context_done) Context context = {}; + int r; + + log_show_color(true); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r; + + r = sd_event_new(&event); + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); + + r = sd_event_set_signal_exit(event, true); + if (r < 0) + return log_error_errno(r, "Failed to install exit signal handlers: %m"); + + STRV_FOREACH(i, arg_devices) { + _cleanup_(nvme_subsystem_destroyp) NvmeSubsystem *subsys = NULL; + + r = nvme_subsystem_add(*i, -EBADF, &subsys); + if (r < 0) + return r; + + r = hashmap_ensure_put(&context.subsystems, &nvme_subsystem_hash_ops, &subsys->device_stat, subsys); + if (r == -EEXIST) { + log_warning_errno(r, "Duplicate device '%s' specified, skipping: %m", *i); + continue; + } + if (r < 0) + return log_error_errno(r, "Failed to add subsystem to hash table: %m"); + + TAKE_PTR(subsys); + } + + r = nvme_port_add(arg_nqn, AF_INET, &context.ipv4_port); + if (r < 0) + return r; + + nvme_port_report(context.ipv4_port); + + if (socket_ipv6_is_enabled()) { + r = nvme_port_add(arg_nqn, AF_INET6, &context.ipv6_port); + if (r < 0) + return r; + + nvme_port_report(context.ipv6_port); + } + + NvmeSubsystem *i; + HASHMAP_FOREACH(i, context.subsystems) { + if (context.ipv4_port) { + r = nvme_port_link_subsystem(context.ipv4_port, i); + if (r < 0) + return r; + } + + if (context.ipv6_port) { + r = nvme_port_link_subsystem(context.ipv6_port, i); + if (r < 0) + return r; + } + + (void) nvme_subsystem_report(i, context.ipv4_port, context.ipv6_port); + } + + if (arg_all > 0) { + r = sd_device_monitor_new(&monitor); + if (r < 0) + return log_error_errno(r, "Failed to allocate device monitor: %m"); + + r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "block", "disk"); + if (r < 0) + return log_error_errno(r, "Failed to configure device monitor match: %m"); + + r = sd_device_monitor_attach_event(monitor, event); + if (r < 0) + return log_error_errno(r, "Failed to attach device monitor to event loop: %m"); + + r = sd_device_monitor_start(monitor, device_monitor_handler, &context); + if (r < 0) + return log_error_errno(r, "Failed to start device monitor: %m"); + + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *enumerator = NULL; + r = sd_device_enumerator_new(&enumerator); + if (r < 0) + return log_error_errno(r, "Failed to allocate enumerator: %m"); + + r = sd_device_enumerator_add_match_subsystem(enumerator, "block", /* match= */ true); + if (r < 0) + return log_error_errno(r, "Failed to match block devices: %m"); + + r = sd_device_enumerator_add_match_property(enumerator, "DEVTYPE", "disk"); + if (r < 0) + return log_error_errno(r, "Failed to match whole block devices: %m"); + + r = sd_device_enumerator_add_nomatch_sysname(enumerator, "loop*"); + if (r < 0) + return log_error_errno(r, "Failed to exclude loop devices: %m"); + + FOREACH_DEVICE(enumerator, device) + device_added(&context, device); + } + + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + r = sd_netlink_attach_event(rtnl, event, SD_EVENT_PRIORITY_NORMAL); + if (r < 0) + return log_error_errno(r, "Failed to attach netlink socket to event loop: %m"); + + r = sd_netlink_add_match(rtnl, /* ret_slot= */ NULL, RTM_NEWADDR, on_address_change, /* destroy_callback= */ NULL, &context, "storagetm-newaddr"); + if (r < 0) + return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR events: %m"); + + r = sd_netlink_add_match(rtnl, /* ret_slot= */ NULL, RTM_DELADDR, on_address_change, /* destroy_callback= */ NULL, &context, "storagetm-deladdr"); + if (r < 0) + return log_error_errno(r, "Failed to subscribe to RTM_DELADDR events: %m"); + + if (isatty(0) > 0) + log_info("Hit Ctrl-C to exit target mode."); + + _unused_ _cleanup_(notify_on_cleanup) const char *notify_message = + notify_start("READY=1\n" + "STATUS=Exposing disks in target mode...", + NOTIFY_STOPPING); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + log_info("Exiting target mode."); + return r; +} + +DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/units/meson.build b/units/meson.build index 493e294002..51ae9cee5d 100644 --- a/units/meson.build +++ b/units/meson.build @@ -559,6 +559,14 @@ units = [ 'conditions' : ['ENABLE_SYSUSERS'], 'symlinks' : ['sysinit.target.wants/'], }, + { + 'file' : 'systemd-storagetm.service.in', + 'conditions' : ['ENABLE_STORAGETM'], + }, + { + 'file' : 'storage-target-mode.target', + 'conditions' : ['ENABLE_STORAGETM'], + }, { 'file' : 'systemd-time-wait-sync.service.in', 'conditions' : ['ENABLE_TIMESYNCD'], diff --git a/units/storage-target-mode.target b/units/storage-target-mode.target new file mode 100644 index 0000000000..38f78a286e --- /dev/null +++ b/units/storage-target-mode.target @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Storage Target Mode +Documentation=man:systemd.special(7) +Wants=systemd-storagetm.service systemd-udevd.service systemd-udev-trigger.service systemd-networkd.service systemd-network-generator.service systemd-journald.socket systemd-journald-dev-log.socket +Conflicts=rescue.service rescue.target +After=systemd-storagetm.service systemd-udevd.service systemd-udev-trigger.service systemd-networkd.service systemd-network-generator.service systemd-journald.socket systemd-journald-dev-log.socket rescue.service rescue.target +AllowIsolate=yes diff --git a/units/systemd-storagetm.service.in b/units/systemd-storagetm.service.in new file mode 100644 index 0000000000..0fe91eff27 --- /dev/null +++ b/units/systemd-storagetm.service.in @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Storage Target Mode (NVMe-TCP) +Documentation=man:systemd-storagetm.service(8) +ConditionVirtualization=!container +DefaultDependencies=no +Wants=modprobe@nvmet_tcp.service modprobe@thunderbolt_net.service sys-kernel-config.mount +After=modprobe@nvmet_tcp.service modprobe@thunderbolt_net.service sys-kernel-config.mount +Conflicts=shutdown.target +Before=shutdown.target +FailureAction=reboot +SuccessAction=reboot + +[Service] +Type=notify +RemainAfterExit=yes +StandardInput=tty +StandardOutput=tty +ExecStart={{LIBEXECDIR}}/systemd-storagetm --all From eae4943afa0f5cb15a7d68d046c67de9c463fde5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 30 Oct 2023 12:12:59 +0100 Subject: [PATCH 09/12] man: add docs for new storagetm service --- man/rules/meson.build | 1 + man/systemd-storagetm.service.xml | 104 ++++++++++++++++++++++++++++++ man/systemd.special.xml | 13 ++++ 3 files changed, 118 insertions(+) create mode 100644 man/systemd-storagetm.service.xml diff --git a/man/rules/meson.build b/man/rules/meson.build index 525bea94fd..45a1780fb2 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -1045,6 +1045,7 @@ manpages = [ ['systemd-socket-proxyd', '8', [], ''], ['systemd-soft-reboot.service', '8', [], ''], ['systemd-stdio-bridge', '1', [], ''], + ['systemd-storagetm.service', '8', ['systemd-storagetm'], 'ENABLE_STORAGETM'], ['systemd-stub', '7', ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub', 'sd-stub'], diff --git a/man/systemd-storagetm.service.xml b/man/systemd-storagetm.service.xml new file mode 100644 index 0000000000..4fa7958737 --- /dev/null +++ b/man/systemd-storagetm.service.xml @@ -0,0 +1,104 @@ + + + + + + + + systemd-storagetm.service + systemd + + + + systemd-storagetm.service + 8 + + + + systemd-storagetm.service + systemd-storagetm + Exposes all local block devices as NVMe-TCP mass storage devices + + + + systemd-storagetm.service + + + /usr/lib/systemd/systemd-storagetm + OPTIONS + DEVICE + + + + + Description + + systemd-storagetm.service is a service that exposes all local block devices as + NVMe-TCP mass storage devices. Its primary use-case is to be invoked by the + storage-target-mode.target unit that can be booted into. + + Warning: the NVMe disks are currently exposed without authentication or encryption, in read/write + mode. This means network peers may read from and write to the device without any restrictions. This + functionality should hence only be used in a local setup. + + Note that to function properly networking must be configured too. The recommended mechanism to boot + into a storage target mode is by adding rd.systemd.unit=storage-target-mode.target + ip=link-local on the kernel command line. Note that ip=link-local only + configures link-local IP, i.e. IPv4LL and IPv6LL, which means non-routable addresses. This is done for + security reasons, so that only systems on the local link can access the devices. Use + ip=dhcp to assign routable addresses too. For further details see + systemd-network-generator.service8. + + Unless the switch is used expects one or more block devices or regular files to expose + via NVMe-TCP as argument. + + + + Options + + The following options are understood: + + + + + Takes a string. If specified configures the NVMe Qualified Name to use for the + exposed NVMe-TCP mass storage devices. The NQN should follow the syntax described in NVM + Express Base Specification 2.0c, section 4.5 "NVMe Qualified Names". Note that the NQN + specified here will be suffixed with a dot and the the block device name before it is exposed on the + NVMe target. If not specified defaults to + nqn.2023-10.io.systemd:storagetm.ID, where ID is + replaced by a 128bit ID derived from + machine-id5. + + + + + + + + + If specified exposes all local block devices via NVMe-TCP, current and future + (i.e. it watches block devices come and go and updates the NVMe-TCP list as needed). Note that by + default any block devices that originate on the same block device as the block device backing the + current root file system are excluded. If the switch is specified twice this safety mechanism is + disabled. + + + + + + + + + + See Also + + systemd1, + systemd.special7 + + + + diff --git a/man/systemd.special.xml b/man/systemd.special.xml index 1d1796154e..8acad5c83e 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -83,6 +83,7 @@ sockets.target, soft-reboot.target, sound.target, + storage-target-mode.target, suspend.target, swap.target, sysinit.target, @@ -767,6 +768,18 @@ + + storage-target-mode.target + + A special target unit that can be booted into that selects the "Storage Target Mode" for + the OS. In this mode all local storage disks are exposed to external systems as block + devices. This invokes + systemd-storagetm.service8 + which exposes all local disks as NVMe-TCP devices for access over the network. It might as well + invoke other services too that make local disks available via other mechanisms. + + + suspend.target From 565b7c90eb4ecf69758b6fb2cee41d9892dd77be Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 1 Nov 2023 19:17:02 +0100 Subject: [PATCH 10/12] test: add integration test for systemd-storagetm --- test/TEST-84-STORAGETM/Makefile | 1 + test/TEST-84-STORAGETM/test.sh | 25 +++++++++++++++++++++++++ test/units/testsuite-84.service | 9 +++++++++ test/units/testsuite-84.sh | 25 +++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 120000 test/TEST-84-STORAGETM/Makefile create mode 100755 test/TEST-84-STORAGETM/test.sh create mode 100644 test/units/testsuite-84.service create mode 100755 test/units/testsuite-84.sh diff --git a/test/TEST-84-STORAGETM/Makefile b/test/TEST-84-STORAGETM/Makefile new file mode 120000 index 0000000000..e9f93b1104 --- /dev/null +++ b/test/TEST-84-STORAGETM/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-84-STORAGETM/test.sh b/test/TEST-84-STORAGETM/test.sh new file mode 100755 index 0000000000..8a9730c957 --- /dev/null +++ b/test/TEST-84-STORAGETM/test.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -e + +TEST_DESCRIPTION="test storagetm" + +TEST_NO_NSPAWN=1 + +# shellcheck source=test/test-functions +. "${TEST_BASE_DIR:?}/test-functions" + +test_require_bin nvme + +modprobe -nv nvmet-tcp || exit 0 + +test_append_files() { + inst_binary nvme + + instmods "=nvme" + instmods configfs + + generate_module_dependencies +} + +do_test "$@" diff --git a/test/units/testsuite-84.service b/test/units/testsuite-84.service new file mode 100644 index 0000000000..2c2577019c --- /dev/null +++ b/test/units/testsuite-84.service @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-84-STORAGETM +After=multi-user.target + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-84.sh b/test/units/testsuite-84.sh new file mode 100755 index 0000000000..954dce2d04 --- /dev/null +++ b/test/units/testsuite-84.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -eux +set -o pipefail + +modprobe -v nvmet-tcp +modprobe -v nvme-tcp + +systemctl start sys-kernel-config.mount + +dd if=/dev/urandom of=/var/tmp/storagetm.test bs=1024 count=10240 + +systemd-run -u teststoragetm.service -p Type=notify /usr/lib/systemd/systemd-storagetm /var/tmp/storagetm.test --nqn=quux + +nvme connect-all -t tcp -a 127.0.0.1 -s 16858 --hostid="$(cat /proc/sys/kernel/random/uuid)" + +dd if=/dev/nvme1n1 bs=1024 | cmp /var/tmp/storagetm.test - + +nvme disconnect --device=nvme1 + +systemctl stop teststoragetm.service + +rm /var/tmp/storagetm.test + +touch /testok From 08b939a6a92d35dbc26f66e4189fbcc754f0a2c5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 2 Nov 2023 11:35:39 +0100 Subject: [PATCH 11/12] update NEWS --- NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NEWS b/NEWS index a5b539ee19..8e5ae59119 100644 --- a/NEWS +++ b/NEWS @@ -379,6 +379,15 @@ CHANGES WITH 255 in spe: * Options and verbs in man pages are now tagged with the version they were first introduced in. + * A new component "systemd-storagetm" has been added, which exposes all + local block devices as NVMe-TCP devices, fully automatically. It's + hooked into a new target unit storage-target-mode.target that is + suppsoed to be booted into via + rd.systemd.unit=storage-target-mode.target on the kernel command + line. This is intended to be used for installers and debugging to + quickly get access to the local disk. It's inspired by MacOS "target + disk mode". + Contributions from: 김인수, Abderrahim Kitouni, Adam Williamson, Alexandre Peixoto Ferreira, Alex Hudspith, Alvin Alvarado, André Paiusco, Antonio Alvarez Feijoo, Anton Lundin, From d2c5b50ae36aab084ef6f757cea9c4a90be7c357 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 30 Oct 2023 12:28:57 +0100 Subject: [PATCH 12/12] update TODO --- TODO | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index 3d30fb2982..021994a770 100644 --- a/TODO +++ b/TODO @@ -175,18 +175,18 @@ Features: * systemd-repart: add support for formatting dm-crypt + dm-integrity file systems. -* homed: add small tool that exposes a homed home dir via nvme-over-tcp (just a - bunch of sysfs writes). Then, teach homed/pam_systemd_homed with a user name - such as lennart%nvmettcp_192.168.100.77_8787_nqn to log in from any linux - host with the same home dir. Similar maybe for nbd, iscsi? this should then - first ask for the local root pw, to authenticate that logging in like this is - ok, and would then be followed by another password prompt asking for the - user's own password. Also, do something similar for CIFS: if you log in via +* homed: use systemd-storagetm to expose home dirs via nvme-tcp. Then, + teach homed/pam_systemd_homed with a user name such as + lennart%nvme_tcp_192.168.100.77_8787 to log in from any linux host with the + same home dir. Similar maybe for nbd, iscsi? this should then first ask for + the local root pw, to authenticate that logging in like this is ok, and would + then be followed by another password prompt asking for the user's own + password. Also, do something similar for CIFS: if you log in via lennart%cifs-someserver_someshare, then set up the homed dir for it - automatically. The PAM module should update the user name used for login to the - short version once it set up the user. Some care should be taken, so that the - long version can be still be resolved via NSS afterwards, to deal with PAM - clients that do not support PAM sessions where PAM_USER changes half-way. + automatically. The PAM module should update the user name used for login to + the short version once it set up the user. Some care should be taken, so that + the long version can be still be resolved via NSS afterwards, to deal with + PAM clients that do not support PAM sessions where PAM_USER changes half-way. * redefine /var/lib/extensions/ as the dir one can place all three of sysext, confext as well is multi-modal DDIs that qualify as both. Then introduce