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) {
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);
}
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);
}
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);
+}
#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);
#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"
}
}
+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);