diff --git a/src/basic/process-util.c b/src/basic/process-util.c index c9d968dee0a..648b160c87a 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1457,11 +1457,20 @@ int safe_fork_full( sigset_t saved_ss, ss; _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; bool block_signals = false, block_all = false, intermediary = false; + _cleanup_free_ char *prefixed_name = NULL; int prio, r; assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid); assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT)); + if (name && FLAGS_SET(flags, FORK_ARGV00_AT)) { + /* Optionally, ensure argv[0][0] is '@', in order to implement https://systemd.io/ROOT_STORAGE_DAEMONS */ + prefixed_name = strjoin("@", name); + if (!prefixed_name) + return -ENOMEM; + name = prefixed_name; + } + /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 83084028478..960df87a38e 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -186,6 +186,7 @@ typedef enum ForkFlags { FORK_DETACH = 1 << 18, /* Double fork if needed to ensure PID1/subreaper is parent */ FORK_NEW_NETNS = 1 << 19, /* Run child in its own network namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_PACK_FDS = 1 << 20, /* Rearrange the passed FDs to be FD 3,4,5,etc. Updates the array in place (combine with FORK_CLOSE_ALL_FDS!) */ + FORK_ARGV00_AT = 1 << 21, /* Prefix supplied name with '@', to exclude child process from final killing spree */ } ForkFlags; int safe_fork_full( diff --git a/src/shared/async.c b/src/shared/async.c index bbb8b81011a..924081cfadf 100644 --- a/src/shared/async.c +++ b/src/shared/async.c @@ -6,6 +6,7 @@ #include #include +#include "argv-util.h" #include "async.h" #include "errno-util.h" #include "fd-util.h" @@ -22,7 +23,14 @@ int asynchronous_sync(pid_t *ret_pid) { * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking * syscalls. */ - r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH), ret_pid); + /* Note that we set argv[0][0] = '@' here if we are called from PID 1, which tells the + * switch-root/soft-reboot/shutdown killing spree to leave this process around. After all the killing + * is likely not going to work anyway, and given the limited scope of the child it's really not worth + * killing it. */ + + r = safe_fork("(sd-sync)", + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|(ret_pid ? 0 : FORK_DETACH)|(getpid_cached() == 1 ? FORK_ARGV00_AT : 0), + ret_pid); if (r < 0) return r; if (r == 0) { @@ -41,12 +49,23 @@ int asynchronous_sync(pid_t *ret_pid) { static int close_func(void *p) { unsigned v = PTR_TO_UINT(p); - (void) prctl(PR_SET_NAME, (unsigned long*) "(sd-close)"); - /* Note: 💣 This function is invoked in a child process created via glibc's clone() wrapper. In such * children memory allocation is not allowed, since glibc does not release malloc mutexes in * clone() 💣 */ + /* This is a poor man's version of rename_process(). We don't use the real thing to avoid any memory + * allocations. We set argv[0][0] to '@' if invoked by PID 1 to exclude us from the + * switch-root/soft-reboot/shutdown killing spree, since it would likely fail anyway, and with a + * process of such minimal scope is not really worh the effort anyway. */ + const char *comm; + if (getppid() == 1) { + if (saved_argc >= 1 && !isempty(saved_argv[0])) + saved_argv[0][0] = '@'; + comm = "@(sd-close)"; + } else + comm = "(sd-close)"; + (void) prctl(PR_SET_NAME, comm); + if (v & NEED_DOUBLE_FORK) { pid_t pid; @@ -117,9 +136,15 @@ int asynchronous_rm_rf(const char *p, RemoveFlags flags) { assert(p); /* Forks off a child that destroys the specified path. This will be best effort only, i.e. the child - * will attempt to do its thing, but we won't wait for it or check its success. */ + * will attempt to do its thing, but we won't wait for it or check its success. + * + * We do set argv[0][0] = '@' here however (if we are invoked as PID 1), to ensure that the this is + * excluded from the switch-root/soft-reboot/poweroff killing spree, and can definitely complete it's + * job. */ - r = safe_fork("(sd-rmrf)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DETACH, NULL); + r = safe_fork("(sd-rmrf)", + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DETACH|(getpid_cached() == 1 ? FORK_ARGV00_AT : 0), + /* ret_pid= */ NULL); if (r != 0) return r;