tree-wide: Remove unnecessary SIGCHLD blocking

Now that sd-event doesn't insist on blocking
SIGCHLD anymore when watching for process exit,
let's stop blocking SIGCHLD tree-wide where not
required anymore.
This commit is contained in:
Daan De Meyer
2025-12-20 15:50:47 +01:00
parent fbd276cb86
commit 29d1df4b03
27 changed files with 34 additions and 87 deletions
+1 -1
View File
@@ -1157,7 +1157,7 @@ static int manager_listen_notify(Manager *m) {
r = notify_socket_prepare_full(
m->event,
SD_EVENT_PRIORITY_NORMAL - 5, /* Make sure we process sd_notify() before SIGCHLD for
SD_EVENT_PRIORITY_NORMAL - 5, /* Make sure we process sd_notify() before child exit for
* any worker, so that we always know the error number
* of a client before it exits. */
on_notify_socket,
-3
View File
@@ -12,7 +12,6 @@
#include "log.h"
#include "main-func.h"
#include "service-util.h"
#include "signal-util.h"
static int run(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *m = NULL;
@@ -32,8 +31,6 @@ static int run(int argc, char *argv[]) {
umask(0022);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");
+3 -5
View File
@@ -409,7 +409,7 @@ static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *u
log_error_errno(errno, "Failed to read log message: %m");
if (l <= 0) {
/* EOF/read error. We just close the pipe here, and
* close the watch, waiting for the SIGCHLD to arrive,
* close the watch, waiting for the child to exit,
* before we do anything else. */
t->log_event_source = sd_event_source_unref(t->log_event_source);
return 0;
@@ -600,7 +600,7 @@ static int transfer_start(Transfer *t) {
if (r < 0)
return r;
/* Make sure always process logging before SIGCHLD */
/* Make sure always process logging before child exit */
r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
if (r < 0)
return r;
@@ -724,7 +724,7 @@ static int manager_new(RuntimeScope scope, Manager **ret) {
r = notify_socket_prepare(
m->event,
SD_EVENT_PRIORITY_NORMAL - 1, /* Make this processed before SIGCHLD. */
SD_EVENT_PRIORITY_NORMAL - 1, /* Make this processed before child exit. */
manager_on_notify,
m,
&m->notify_socket_path);
@@ -2062,8 +2062,6 @@ static int run(int argc, char *argv[]) {
umask(0022);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = manager_new(scope, &m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
+1 -7
View File
@@ -93,7 +93,7 @@ static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si,
ASSERT_PTR_EQ(userdata, INT_TO_PTR('e'));
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGUSR2));
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGUSR2));
ASSERT_OK_ERRNO(pid = fork());
@@ -568,8 +568,6 @@ TEST(pidfd) {
int pidfd;
pid_t pid, pid2;
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
ASSERT_OK_ERRNO(pid = fork());
if (pid == 0)
/* child */
@@ -1001,8 +999,6 @@ static int child_handler_wnowait(sd_event_source *s, const siginfo_t *si, void *
TEST(child_wnowait) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
ASSERT_OK(sd_event_default(&e));
/* Fork a subprocess */
@@ -1034,8 +1030,6 @@ TEST(child_wnowait) {
TEST(child_pidfd_wnowait) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
ASSERT_OK(sd_event_default(&e));
/* Fork a subprocess */
-2
View File
@@ -1372,8 +1372,6 @@ static int run(int argc, char *argv[]) {
(void) mkdir_label("/run/systemd/users", 0755);
(void) mkdir_label("/run/systemd/sessions", 0755);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
-2
View File
@@ -366,8 +366,6 @@ static int run(int argc, char *argv[]) {
if (scope == RUNTIME_SCOPE_SYSTEM)
(void) mkdir_label("/run/systemd/machines", 0755);
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
r = manager_new(scope, &m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
+1 -1
View File
@@ -51,7 +51,7 @@ static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdat
else if (si->si_code == CLD_DUMPED)
log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
else
log_warning("Got unexpected exit code via SIGCHLD, ignoring.");
log_warning("Got unexpected exit code from child, ignoring.");
(void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
return 0;
-3
View File
@@ -8,7 +8,6 @@
#include "log.h"
#include "main-func.h"
#include "mountfsd-manager.h"
#include "signal-util.h"
static int run(int argc, char *argv[]) {
_unused_ _cleanup_(notify_on_cleanup) const char *notify_stop = NULL;
@@ -22,8 +21,6 @@ static int run(int argc, char *argv[]) {
if (argc != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");
+5 -6
View File
@@ -506,9 +506,10 @@ static int action_fork(char *const *_command) {
_cleanup_free_ char *addr_string = NULL;
r = notify_socket_prepare(
event,
SD_EVENT_PRIORITY_NORMAL - 10, /* If we receive both the sd_notify() message and a
* SIGCHLD always process sd_notify() first, it's the
* more interesting, "positive" information. */
SD_EVENT_PRIORITY_NORMAL - 10, /* If we receive both the sd_notify() message and
* child exit notification, always process sd_notify()
* first, it's the more interesting, "positive"
* information. */
on_notify_socket,
&child,
&addr_string);
@@ -541,14 +542,12 @@ static int action_fork(char *const *_command) {
fflush(stdout);
}
BLOCK_SIGNALS(SIGCHLD);
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *child_event_source = NULL;
r = event_add_child_pidref(event, &child_event_source, &child, WEXITED, on_child, /* userdata= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate child source: %m");
/* Handle SIGCHLD before propagating the other signals below */
/* Handle child exit before propagating the other signals below */
r = sd_event_source_set_priority(child_event_source, SD_EVENT_PRIORITY_NORMAL - 5);
if (r < 0)
return log_error_errno(r, "Failed to change child event source priority: %m");
+1 -1
View File
@@ -58,7 +58,7 @@ static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdat
else if (si->si_code == CLD_DUMPED)
log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
else
log_warning("Got unexpected exit code via SIGCHLD, ignoring.");
log_warning("Got unexpected exit code from child, ignoring.");
(void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
return 0;
-3
View File
@@ -9,7 +9,6 @@
#include "log.h"
#include "main-func.h"
#include "nsresourced-manager.h"
#include "signal-util.h"
static int run(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *m = NULL;
@@ -25,8 +24,6 @@ static int run(int argc, char *argv[]) {
if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NamespaceResource", 1) < 0)
return log_error_errno(errno, "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");
-2
View File
@@ -143,8 +143,6 @@ static int run(int argc, char *argv[]) {
if (argc != 1)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
-3
View File
@@ -14,7 +14,6 @@
#include "pretty-print.h"
#include "process-util.h"
#include "ptyfwd.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@@ -156,8 +155,6 @@ static int run(int argc, char *argv[]) {
log_setup();
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
r = parse_argv(argc, argv);
if (r <= 0)
return r;
+2 -4
View File
@@ -35,7 +35,7 @@ static int on_child_exit(sd_event_source *s, const siginfo_t *si, void *userdata
else if (si->si_code == CLD_DUMPED)
log_debug("Child process " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
else
log_debug("Got unexpected exit code %i via SIGCHLD, ignoring.", si->si_code);
log_debug("Got unexpected exit code %i from child, ignoring.", si->si_code);
/* And let's then fail the whole thing, because regardless what the exit status of the child is
* (i.e. even if successful), if it exits before sending READY=1 something is wrong. */
@@ -106,7 +106,7 @@ int fork_notify(char * const *argv, PidRef *ret_pidref) {
_cleanup_free_ char *addr_string = NULL;
r = notify_socket_prepare_full(
event,
SD_EVENT_PRIORITY_NORMAL-10, /* We want the notification message from the child before the SIGCHLD */
SD_EVENT_PRIORITY_NORMAL-10, /* We want the notification message from the child before the child exit */
on_child_notify,
&child,
/* accept_fds= */ false,
@@ -124,8 +124,6 @@ int fork_notify(char * const *argv, PidRef *ret_pidref) {
log_debug("Invoking '%s' as child.", strnull(l));
}
BLOCK_SIGNALS(SIGCHLD);
r = pidref_safe_fork_full(
"(fork-notify)",
(const int[3]) { -EBADF, STDOUT_FILENO, STDERR_FILENO },
-4
View File
@@ -23,7 +23,6 @@
#include "path-util.h"
#include "pretty-print.h"
#include "set.h"
#include "signal-util.h"
#include "sort-util.h"
#include "specifier.h"
#include "string-util.h"
@@ -1877,9 +1876,6 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
/* SIGCHLD signal must be blocked for sd_event_add_child to work */
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
return sysupdate_main(argc, argv);
}
+1 -4
View File
@@ -1738,7 +1738,7 @@ static int manager_new(Manager **ret) {
r = notify_socket_prepare(
m->event,
SD_EVENT_PRIORITY_NORMAL - 1, /* Make this processed before SIGCHLD. */
SD_EVENT_PRIORITY_NORMAL - 1, /* Make this processed before worker exit. */
manager_on_notify,
m,
&m->notify_socket_path);
@@ -2078,9 +2078,6 @@ static int run(int argc, char *argv[]) {
umask(0022);
/* SIGCHLD signal must be blocked for sd_event_add_child to work */
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Failed to allocate manager object: %m");
+3 -5
View File
@@ -680,7 +680,7 @@ static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
return 1;
}
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
static int on_spawn_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
int ret = -EIO;
ASSERT_NOT_NULL(si);
@@ -706,8 +706,6 @@ static int find_libraries(const char *exec, char ***ret) {
ASSERT_NOT_NULL(exec);
ASSERT_NOT_NULL(ret);
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
ASSERT_OK_ERRNO(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC));
ASSERT_OK_ERRNO(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC));
@@ -732,8 +730,8 @@ static int find_libraries(const char *exec, char ***ret) {
ASSERT_OK(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT));
ASSERT_OK(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL));
ASSERT_OK(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT));
ASSERT_OK(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_sigchld, NULL));
/* SIGCHLD should be processed after IO is complete */
ASSERT_OK(sd_event_add_child(e, &sigchld_source, pid, WEXITED, on_spawn_exit, NULL));
/* Child exit should be processed after IO is complete */
ASSERT_OK(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1));
ASSERT_OK(sd_event_loop(e));
-1
View File
@@ -234,7 +234,6 @@ TEST(real_pressure) {
_exit(EXIT_SUCCESS);
}
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
assert_se(sd_event_add_child(e, &cs, pid, WEXITED, real_pressure_child_callback, NULL) >= 0);
assert_se(sd_event_source_set_child_process_own(cs, true) >= 0);
-2
View File
@@ -90,8 +90,6 @@ TEST(notify_socket_prepare) {
_cleanup_free_ char *path = NULL;
ASSERT_OK(notify_socket_prepare_full(e, SD_EVENT_PRIORITY_NORMAL - 10, on_recv, &c, true, &path, NULL));
ASSERT_OK(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD));
ASSERT_OK(r = pidref_safe_fork("(test-notify-recv-child)", FORK_DEATHSIG_SIGTERM|FORK_LOG, &c.pidref));
if (r == 0) {
ASSERT_OK_ERRNO(setenv("NOTIFY_SOCKET", path, /* overwrite= */ true));
+1 -1
View File
@@ -143,7 +143,7 @@ static int run(int argc, char *argv[]) {
assert_se(event = udev_event_new(dev, NULL, EVENT_TEST_RULE_RUNNER));
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP, SIGCHLD) >= 0);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGHUP) >= 0);
/* do what devtmpfs usually provides us */
if (sd_device_get_devname(dev, &devname) >= 0) {
-3
View File
@@ -7,7 +7,6 @@
#include "alloc-util.h"
#include "mountpoint-util.h"
#include "path-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
#include "tests.h"
@@ -104,8 +103,6 @@ int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
test_event_spawn_cat(true, SIZE_MAX);
test_event_spawn_cat(false, SIZE_MAX);
test_event_spawn_cat(true, 5);
+3 -6
View File
@@ -390,7 +390,7 @@ void manager_revert(Manager *manager) {
manager_kill_workers(manager, SIGTERM);
}
static int on_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
_cleanup_(worker_freep) Worker *worker = ASSERT_PTR(userdata);
sd_device *dev = worker->event ? ASSERT_PTR(worker->event->dev) : NULL;
@@ -452,11 +452,11 @@ static int worker_new(Worker **ret, Manager *manager, sd_device_monitor *worker_
if (r < 0)
return r;
r = event_add_child_pidref(manager->event, &worker->child_event_source, &worker->pidref, WEXITED, on_sigchld, worker);
r = event_add_child_pidref(manager->event, &worker->child_event_source, &worker->pidref, WEXITED, on_worker_exit, worker);
if (r < 0)
return r;
r = sd_event_source_set_priority(worker->child_event_source, EVENT_PRIORITY_WORKER_SIGCHLD);
r = sd_event_source_set_priority(worker->child_event_source, EVENT_PRIORITY_WORKER_EXIT);
if (r < 0)
return r;
@@ -1363,9 +1363,6 @@ static int manager_setup_event(Manager *manager) {
assert(manager);
/* block SIGCHLD for listening child events. */
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = sd_event_default(&e);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
+4 -4
View File
@@ -17,13 +17,13 @@
/* This must have a higher priority than the worker notification, to make IN_IGNORED event received earlier
* than notifications about requests of adding/removing inotify watches. */
#define EVENT_PRIORITY_INOTIFY_WATCH (SD_EVENT_PRIORITY_NORMAL - 4)
/* This must have a higher priority than the worker SIGCHLD event, to make notifications about completions of
* processing events received before SIGCHLD. */
/* This must have a higher priority than the worker exit event, to make notifications about completions of
* processing events received before exit. */
#define EVENT_PRIORITY_WORKER_NOTIFY (SD_EVENT_PRIORITY_NORMAL - 3)
/* This should have a higher priority than timer events about killing long running worker processes or idle
* worker processes. */
#define EVENT_PRIORITY_WORKER_SIGCHLD (SD_EVENT_PRIORITY_NORMAL - 2)
/* As said in the above, this should have a lower proority than the SIGCHLD event source. */
#define EVENT_PRIORITY_WORKER_EXIT (SD_EVENT_PRIORITY_NORMAL - 2)
/* As said in the above, this should have a lower proority than the exit event source. */
#define EVENT_PRIORITY_WORKER_TIMER (SD_EVENT_PRIORITY_NORMAL - 1)
/* This should have a lower priority than most event sources, but let's process earlier than varlink and the
* legacy control socket. */
+6 -5
View File
@@ -146,7 +146,7 @@ static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *use
return 1;
}
static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
static int on_spawn_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
Spawn *spawn = ASSERT_PTR(userdata);
int ret = -EIO;
@@ -175,7 +175,7 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd
static int spawn_wait(Spawn *spawn) {
_cleanup_(sd_event_unrefp) sd_event *e = NULL;
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *sigchld_source = NULL;
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *child_source = NULL;
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *stdout_source = NULL;
_cleanup_(sd_event_source_disable_unrefp) sd_event_source *stderr_source = NULL;
int r;
@@ -214,11 +214,12 @@ static int spawn_wait(Spawn *spawn) {
return log_device_debug_errno(spawn->device, r, "Failed to create stderr event source: %m");
}
r = event_add_child_pidref(e, &sigchld_source, &spawn->pidref, WEXITED, on_spawn_sigchld, spawn);
r = event_add_child_pidref(e, &child_source, &spawn->pidref, WEXITED, on_spawn_exit, spawn);
if (r < 0)
return log_device_debug_errno(spawn->device, r, "Failed to create sigchild event source: %m");
/* SIGCHLD should be processed after IO is complete */
r = sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1);
/* Child exit should be processed after IO is complete */
r = sd_event_source_set_priority(child_source, SD_EVENT_PRIORITY_NORMAL + 1);
if (r < 0)
return log_device_debug_errno(spawn->device, r, "Failed to set priority to sigchild event source: %m");
+1 -1
View File
@@ -48,7 +48,7 @@ static int on_worker_exit(sd_event_source *s, const siginfo_t *si, void *userdat
else if (si->si_code == CLD_DUMPED)
log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", si->si_pid, signal_to_string(si->si_status));
else
log_warning("Can't handle SIGCHLD of this type");
log_warning("Can't handle exit code of this type");
(void) start_workers(m, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
return 0;
-3
View File
@@ -8,7 +8,6 @@
#include "daemon-util.h"
#include "log.h"
#include "main-func.h"
#include "signal-util.h"
#include "userdbd-manager.h"
/* This service offers two Varlink services, both implementing io.systemd.UserDatabase:
@@ -39,8 +38,6 @@ static int run(int argc, char *argv[]) {
if (setenv("SYSTEMD_BYPASS_USERDB", "io.systemd.NameServiceSwitch:io.systemd.Multiplexer:io.systemd.DropIn", 1) < 0)
return log_error_errno(errno, "Failed to set $SYSTEMD_BYPASS_USERDB: %m");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
r = manager_new(&m);
if (r < 0)
return log_error_errno(r, "Could not create manager: %m");
+1 -5
View File
@@ -1085,7 +1085,7 @@ static int on_child_exit(sd_event_source *s, const siginfo_t *si, void *userdata
si->si_pid, signal_to_string(si->si_status));
else
ret = log_error_errno(SYNTHETIC_ERRNO(EPROTO),
"Got unexpected exit code %i via SIGCHLD,",
"Got unexpected exit code %i from child,",
si->si_code);
/* Regardless of whether the main qemu process or an auxiliary process died, let's exit either way
@@ -2288,8 +2288,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
return r;
}
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
r = sd_event_new(&event);
if (r < 0)
@@ -2719,8 +2717,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
log_debug("Executing: %s", joined);
}
assert_se(sigprocmask_many(SIG_BLOCK, /* ret_old_mask= */ NULL, SIGCHLD) >= 0);
_cleanup_(pidref_done) PidRef child_pidref = PIDREF_NULL;
r = pidref_safe_fork_full(
qemu_binary,