common/spawn.c: add spawn_piped()

This commit is contained in:
Consolatis 2024-03-18 06:15:15 +01:00 committed by Johan Malm
parent 0671a3bfd3
commit e5488fefcb
3 changed files with 180 additions and 6 deletions

View file

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <fcntl.h>
#include <glib.h>
#include <signal.h>
#include <stdio.h>
@ -11,6 +12,26 @@
#include "common/spawn.h"
#include "common/fd_util.h"
static void
reset_signals_and_limits(void)
{
restore_nofile_limit();
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
/* Restore ignored signals */
signal(SIGPIPE, SIG_DFL);
}
static void
set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
void
spawn_async_no_shell(char const *command)
{
@ -39,14 +60,9 @@ spawn_async_no_shell(char const *command)
wlr_log(WLR_ERROR, "unable to fork()");
goto out;
case 0:
restore_nofile_limit();
reset_signals_and_limits();
setsid();
sigset_t set;
sigemptyset(&set);
sigprocmask(SIG_SETMASK, &set, NULL);
/* Restore ignored signals */
signal(SIGPIPE, SIG_DFL);
grandchild = fork();
if (grandchild == 0) {
execvp(argv[0], argv);
@ -63,3 +79,76 @@ out:
g_strfreev(argv);
}
pid_t
spawn_piped(const char *command, int *pipe_fd)
{
assert(command);
int pipe_rw[2];
if (pipe(pipe_rw) != 0) {
wlr_log(WLR_ERROR, "unable to pipe()");
return -1;
}
pid_t pid = fork();
if (pid < 0) {
close(pipe_rw[0]);
close(pipe_rw[1]);
wlr_log(WLR_ERROR, "unable to fork()");
return pid;
}
if (pid == 0) {
/* child */
reset_signals_and_limits();
/*
* replace stdin and stderr with /dev/null
* and stdout with the write end of the pipe
*/
dup2(pipe_rw[1], STDOUT_FILENO);
close(pipe_rw[0]);
close(pipe_rw[1]);
int dev_null = open("/dev/null", O_RDWR);
if (dev_null < 0) {
wlr_log_errno(WLR_ERROR, "opening /dev/null failed");
/*
* Just close stdin and stderr and
* hope $command can deal with that.
*/
close(STDIN_FILENO);
close(STDERR_FILENO);
} else {
dup2(dev_null, STDIN_FILENO);
dup2(dev_null, STDERR_FILENO);
close(dev_null);
}
execl("/bin/sh", "sh", "-c", command, NULL);
/*
* Our stderr points to /dev/null or is closed
* at this point so logging is pretty useless.
*/
_exit(1);
}
/* labwc */
close(pipe_rw[1]);
/*
* Prevent leaking the read end of the pipe to further
* children forked during the lifetime of the descriptor.
*/
set_cloexec(pipe_rw[0]);
*pipe_fd = pipe_rw[0];
return pid;
}
void
spawn_piped_close(pid_t pid, int pipe_fd)
{
close(pipe_fd);
/* waitpid() is done in a generic SIGCHLD handler in src/server.c */
}