subprocess-posix: handle detach inside spawn_process

This commit is contained in:
Misaki Kasumi authored and sfan5 committed 2026-06-17 10:47:16 +02:00
1 parent 5fb6a76ddf
commit de1c73a44b
1 file changed
+41 -41
+41 -41
View File
@@ -95,8 +95,12 @@ struct child_args {
int *src_fds;
int pipe_end;
bool failed;
bool detach;
};
static pid_t spawn_process_inner(const char *path, struct mp_subprocess_opts *opts,
int src_fds[], bool detach);
// This function is called from a clone(CLONE_VM) context where the child
// shares the parent's address space. Use MP_NO_ASAN to avoid false positives
// from ASan when the child writes to shared memory.
@@ -107,6 +111,14 @@ MP_NO_ASAN static int child_main(void* args)
struct mp_subprocess_opts *opts = child_args->opts;
int *src_fds = child_args->src_fds;
int pipe_end = child_args->pipe_end;
bool detach = child_args->detach;
if (detach) {
setsid();
if (!spawn_process_inner(path, opts, src_fds, false))
goto child_failed;
return 0;
}
reset_signals_child();
@@ -132,24 +144,19 @@ child_failed:
return 1;
}
// Returns 0 on any error, valid PID on success.
// This function must be async-signal-safe, as it may be called from a fork().
static pid_t spawn_process(const char *path, struct mp_subprocess_opts *opts,
int src_fds[])
static pid_t spawn_process_inner(const char *path, struct mp_subprocess_opts *opts,
int src_fds[], bool detach)
{
pid_t fres = 0;
sigset_t sigmask, oldmask;
int r = 0;
sigfillset(&sigmask);
pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
struct child_args child_args = {
.path = path,
.opts = opts,
.src_fds = src_fds,
.pipe_end = -1,
.failed = false,
.detach = detach,
};
int p[2] = {-1, -1};
@@ -196,13 +203,14 @@ static pid_t spawn_process(const char *path, struct mp_subprocess_opts *opts,
#endif
// If exec()ing child failed, collect it immediately.
if (child_args.failed || r != 0) {
while (waitpid(fres, &(int){0}, 0) < 0 && errno == EINTR) {}
fres = 0;
if (detach || child_args.failed || r != 0) {
int child_status = 0;
while (waitpid(fres, &child_status, 0) < 0 && errno == EINTR) {}
if (r != 0 || !WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0)
fres = 0;
}
done:
pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
#if HAVE_CLONE
munmap(stack, stack_size);
#endif
@@ -212,6 +220,22 @@ done:
return fres;
}
// Returns 0 on any error, valid PID on success.
static pid_t spawn_process(const char *path, struct mp_subprocess_opts *opts,
int src_fds[])
{
sigset_t sigmask, oldmask;
sigfillset(&sigmask);
pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
pid_t fres = spawn_process_inner(path, opts, src_fds, opts->detach);
pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
return fres;
}
void mp_subprocess2(struct mp_subprocess_opts *opts,
struct mp_subprocess_result *res)
{
@@ -270,35 +294,11 @@ void mp_subprocess2(struct mp_subprocess_opts *opts,
src_fds[n] = src_fd;
}
if (opts->detach) {
// If we run it detached, we fork a child to start the process; then
// it exits immediately, letting PID 1 inherit it. So we don't need
// anything else to collect these child PIDs.
sigset_t sigmask, oldmask;
sigfillset(&sigmask);
pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask);
pid_t fres = fork();
if (fres < 0) {
pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
goto done;
}
if (fres == 0) {
// child
setsid();
if (!spawn_process(path, opts, src_fds))
_exit(1);
_exit(0);
}
pthread_sigmask(SIG_SETMASK, &oldmask, NULL);
int child_status = 0;
while (waitpid(fres, &child_status, 0) < 0 && errno == EINTR) {}
if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0)
goto done;
} else {
pid = spawn_process(path, opts, src_fds);
if (!pid)
goto done;
}
pid = spawn_process(path, opts, src_fds);
if (!pid)
goto done;
if (opts->detach)
pid = 0;
spawned = true;