From 41bd3379efc0e5b5621c329ff55b99b2825f06c0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Dec 2017 18:24:28 +0100 Subject: [PATCH] sync: fork off sync() in a process instead of a thread Let's fork off sync() ina process instead of a thread, as a safety measure. This is beneficial to ensure that the original process can exit without having to wait for the sync() to finish (note that the kernel will delay process termination until all threads finished their syscalls). In case of hanging NFS this increases the chance that PID 1 can safely transition to the "systemd-shutdown" process as the sync() is initiated early on but definitely not waited for. --- src/basic/async.c | 37 ++++++++++++++++++++++++++++++------- src/core/job.c | 2 +- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/basic/async.c b/src/basic/async.c index d368b92522..c510cbd7f5 100644 --- a/src/basic/async.c +++ b/src/basic/async.c @@ -27,6 +27,8 @@ #include "fd-util.h" #include "log.h" #include "macro.h" +#include "process-util.h" +#include "signal-util.h" #include "util.h" int asynchronous_job(void* (*func)(void *p), void *arg) { @@ -58,15 +60,36 @@ finish: return -r; } -static void *sync_thread(void *p) { - sync(); - return NULL; -} - int asynchronous_sync(void) { - log_debug("Spawning new thread for sync"); + pid_t pid; + + /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to + * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our + * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking + * syscalls. */ + + log_debug("Spawning new process for sync"); + + pid = fork(); + if (pid < 0) + return -errno; + + if (pid == 0) { + /* Child process */ + + (void) rename_process("(sd-sync)"); + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + (void) close_all_fds(NULL, 0); + + (void) sync(); + _exit(EXIT_SUCCESS); + } - return asynchronous_job(sync_thread, NULL); + /* We don' really care about the PID from here on. It will exit when it's done. */ + return 0; } static void *close_thread(void *p) { diff --git a/src/core/job.c b/src/core/job.c index 2ea7834dfd..9ff5b288f9 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -1244,7 +1244,7 @@ void job_shutdown_magic(Job *j) { if (detect_container() > 0) return; - asynchronous_sync(); + (void) asynchronous_sync(); } int job_get_timeout(Job *j, usec_t *timeout) { -- 2.25.1