From: Lennart Poettering Date: Thu, 22 Jun 2023 09:55:59 +0000 (+0200) Subject: async: add generic implementation of asynchronous_rm_rf() X-Git-Tag: v254-rc1~133^2~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=437f3e35b4d580ac99a52e307542aa4370854768;p=thirdparty%2Fsystemd.git async: add generic implementation of asynchronous_rm_rf() This one doesn't use threads anymore. This is the last use of threads in PID 1. Yay! Fixes: #27287 --- diff --git a/src/core/execute.c b/src/core/execute.c index b7fe922c7a9..90af8fa619d 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -7318,28 +7318,17 @@ int exec_command_append(ExecCommand *c, const char *path, ...) { return 0; } -static void *rm_rf_thread(void *p) { - _cleanup_free_ char *path = p; - - (void) rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL); - return NULL; -} - -static void asynchronous_rm_rf(char **path) { - int r; - - assert(path); +static char *destroy_tree(char *path) { + if (!path) + return NULL; - if (!*path || streq(*path, RUN_SYSTEMD_EMPTY)) - return; + if (!path_equal(path, RUN_SYSTEMD_EMPTY)) { + log_debug("Spawning process to nuke '%s'", path); - log_debug("Spawning thread to nuke %s", *path); + (void) asynchronous_rm_rf(path, REMOVE_ROOT|REMOVE_SUBVOLUME|REMOVE_PHYSICAL); + } - r = asynchronous_job(rm_rf_thread, *path); - if (r < 0) - log_warning_errno(r, "Failed to nuke %s: %m", *path); - else - *path = NULL; + return mfree(path); } static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) { @@ -7370,8 +7359,8 @@ ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) { if (rt->n_ref > 0) return NULL; - asynchronous_rm_rf(&rt->tmp_dir); - asynchronous_rm_rf(&rt->var_tmp_dir); + rt->tmp_dir = destroy_tree(rt->tmp_dir); + rt->var_tmp_dir = destroy_tree(rt->var_tmp_dir); return exec_shared_runtime_free(rt); } @@ -7862,9 +7851,8 @@ ExecRuntime* exec_runtime_free(ExecRuntime *rt) { exec_shared_runtime_unref(rt->shared); dynamic_creds_unref(rt->dynamic_creds); - asynchronous_rm_rf(&rt->ephemeral_copy); + rt->ephemeral_copy = destroy_tree(rt->ephemeral_copy); - free(rt->ephemeral_copy); safe_close_pair(rt->ephemeral_storage_socket); return mfree(rt); } diff --git a/src/shared/async.c b/src/shared/async.c index c0e1641cb2b..a98f31d3b85 100644 --- a/src/shared/async.c +++ b/src/shared/async.c @@ -158,3 +158,28 @@ int asynchronous_close(int fd) { return -EBADF; /* return an invalidated fd */ } + +int asynchronous_rm_rf(const char *p, RemoveFlags flags) { + int r; + + 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. */ + + r = safe_fork("(sd-rmrf)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DETACH, NULL); + if (r != 0) + return r; + + /* Child */ + + r = rm_rf(p, flags); + if (r < 0) { + log_debug_errno(r, "Failed to rm -rf '%s', ignoring: %m", p); + _exit(EXIT_FAILURE); /* This is a detached process, hence noone really cares, but who knows + * maybe it's good for debugging/tracing to return an exit code + * indicative of our failure here. */ + } + + _exit(EXIT_SUCCESS); +} diff --git a/src/shared/async.h b/src/shared/async.h index e0bbaa56583..2c7def993d2 100644 --- a/src/shared/async.h +++ b/src/shared/async.h @@ -4,10 +4,12 @@ #include #include "macro.h" +#include "rm-rf.h" int asynchronous_job(void* (*func)(void *p), void *arg); int asynchronous_sync(pid_t *ret_pid); int asynchronous_close(int fd); +int asynchronous_rm_rf(const char *p, RemoveFlags flags); DEFINE_TRIVIAL_CLEANUP_FUNC(int, asynchronous_close); diff --git a/src/test/test-async.c b/src/test/test-async.c index c2bf8011969..02028ce323f 100644 --- a/src/test/test-async.c +++ b/src/test/test-async.c @@ -2,11 +2,14 @@ #include #include +#include #include #include "async.h" #include "fs-util.h" +#include "path-util.h" #include "process-util.h" +#include "signal-util.h" #include "tests.h" #include "tmpfile-util.h" @@ -59,4 +62,50 @@ TEST(asynchronous_close) { } } +TEST(asynchronous_rm_rf) { + _cleanup_free_ char *t = NULL, *k = NULL; + int r; + + assert_se(mkdtemp_malloc(NULL, &t) >= 0); + assert_se(k = path_join(t, "somefile")); + assert_se(touch(k) >= 0); + assert_se(asynchronous_rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + /* 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); + assert_se(r >= 0); + + if (r == 0) { + _cleanup_free_ char *tt = NULL, *kk = NULL; + + /* child */ + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); + assert_se(prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0) >= 0); + + assert_se(mkdtemp_malloc(NULL, &tt) >= 0); + assert_se(kk = path_join(tt, "somefile")); + assert_se(touch(kk) >= 0); + assert_se(asynchronous_rm_rf(tt, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + for (;;) { + siginfo_t si = {}; + + assert_se(waitid(P_ALL, 0, &si, WEXITED) >= 0); + + if (access(tt, F_OK) < 0) { + assert_se(errno == ENOENT); + break; + } + + /* wasn't the rm_rf() call. let's wait longer */ + } + + _exit(EXIT_SUCCESS); + } +} + + DEFINE_TEST_MAIN(LOG_DEBUG);