]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
async: add generic implementation of asynchronous_rm_rf()
authorLennart Poettering <lennart@poettering.net>
Thu, 22 Jun 2023 09:55:59 +0000 (11:55 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 23 Jun 2023 08:05:16 +0000 (10:05 +0200)
This one doesn't use threads anymore. This is the last use of threads in
PID 1. Yay!

Fixes: #27287
src/core/execute.c
src/shared/async.c
src/shared/async.h
src/test/test-async.c

index b7fe922c7a950c4bb63f56776afe6c9d52c74ded..90af8fa619d9b0ed64c9290b308ec0aeb0640e3d 100644 (file)
@@ -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);
 }
index c0e1641cb2b048ed717972f380eda4497ea0c0e2..a98f31d3b85d30fc3e2243dd2753902c46af0a0b 100644 (file)
@@ -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);
+}
index e0bbaa5658319dd07ff89ddad98384e4a763412d..2c7def993d2c236f49097577b5e953e7587af3e6 100644 (file)
@@ -4,10 +4,12 @@
 #include <sys/types.h>
 
 #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);
index c2bf8011969ff840ba9458afb4ecea3b5d08f3ba..02028ce323ff1e11043723178e94e3559a4dac03 100644 (file)
@@ -2,11 +2,14 @@
 
 #include <fcntl.h>
 #include <sys/prctl.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #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);