mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
common/spawn.c: add spawn_piped()
This commit is contained in:
parent
0671a3bfd3
commit
e5488fefcb
3 changed files with 180 additions and 6 deletions
|
|
@ -2,10 +2,34 @@
|
|||
#ifndef LABWC_SPAWN_H
|
||||
#define LABWC_SPAWN_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
/**
|
||||
* spawn_async_no_shell - execute asynchronously
|
||||
* @command: command to be executed
|
||||
*/
|
||||
void spawn_async_no_shell(char const *command);
|
||||
|
||||
/**
|
||||
* spawn_piped - execute asyncronously
|
||||
* @command: command to be executed
|
||||
* @pipe_fd: set to the read end of a pipe
|
||||
* connected to stdout of the command
|
||||
*
|
||||
* Notes:
|
||||
* The returned pid_t has to be waited for to
|
||||
* not produce zombies and the pipe_fd has to
|
||||
* be closed. spawn_piped_close() can be used
|
||||
* to ensure both.
|
||||
*/
|
||||
pid_t spawn_piped(const char *command, int *pipe_fd);
|
||||
|
||||
/**
|
||||
* spawn_piped_close - clean up a previous
|
||||
* spawn_piped() process
|
||||
* @pid: will be waitpid()'d for
|
||||
* @pipe_fd: will be close()'d
|
||||
*/
|
||||
void spawn_piped_close(pid_t pid, int pipe_fd);
|
||||
|
||||
#endif /* LABWC_SPAWN_H */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
|||
61
src/server.c
61
src/server.c
|
|
@ -2,6 +2,7 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "config.h"
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <wlr/backend/headless.h>
|
||||
#include <wlr/backend/multi.h>
|
||||
|
|
@ -42,6 +43,7 @@ static struct wlr_compositor *compositor;
|
|||
static struct wl_event_source *sighup_source;
|
||||
static struct wl_event_source *sigint_source;
|
||||
static struct wl_event_source *sigterm_source;
|
||||
static struct wl_event_source *sigchld_source;
|
||||
|
||||
static struct server *g_server;
|
||||
|
||||
|
|
@ -83,6 +85,63 @@ handle_sigterm(int signal, void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_sigchld(int signal, void *data)
|
||||
{
|
||||
siginfo_t info;
|
||||
info.si_pid = 0;
|
||||
|
||||
/* First call waitid() with NOWAIT which doesn't consume the zombie */
|
||||
if (waitid(P_ALL, /*id*/ 0, &info, WEXITED | WNOHANG | WNOWAIT) == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (info.si_pid == 0) {
|
||||
/* No children in waitable state */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if HAVE_XWAYLAND
|
||||
/* Verify that we do not break xwayland lazy initialization */
|
||||
struct server *server = data;
|
||||
if (server->xwayland && server->xwayland->server
|
||||
&& info.si_pid == server->xwayland->server->pid) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* And then do the actual (consuming) lookup again */
|
||||
int ret = waitid(P_PID, info.si_pid, &info, WEXITED);
|
||||
if (ret == -1) {
|
||||
wlr_log(WLR_ERROR, "blocking waitid() for %ld failed: %d",
|
||||
(long)info.si_pid, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (info.si_code) {
|
||||
case CLD_EXITED:
|
||||
wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR,
|
||||
"spawned child %ld exited with %d",
|
||||
(long)info.si_pid, info.si_status);
|
||||
break;
|
||||
case CLD_KILLED:
|
||||
case CLD_DUMPED:
|
||||
; /* works around "a label can only be part of a statement" */
|
||||
const char *signame = strsignal(info.si_status);
|
||||
wlr_log(WLR_ERROR,
|
||||
"spawned child %ld terminated with signal %d (%s)",
|
||||
(long)info.si_pid, info.si_status,
|
||||
signame ? signame : "unknown");
|
||||
break;
|
||||
default:
|
||||
wlr_log(WLR_ERROR,
|
||||
"spawned child %ld terminated unexpectedly: %d"
|
||||
" please report", (long)info.si_pid, info.si_code);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
seat_inhibit_input(struct seat *seat, struct wl_client *active_client)
|
||||
{
|
||||
|
|
@ -239,6 +298,8 @@ server_init(struct server *server)
|
|||
event_loop, SIGINT, handle_sigterm, server->wl_display);
|
||||
sigterm_source = wl_event_loop_add_signal(
|
||||
event_loop, SIGTERM, handle_sigterm, server->wl_display);
|
||||
sigchld_source = wl_event_loop_add_signal(
|
||||
event_loop, SIGCHLD, handle_sigchld, server);
|
||||
server->wl_event_loop = event_loop;
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue