From 009cca3fa9ce642edd311f99869e64d2bedceca4 Mon Sep 17 00:00:00 2001 From: Jente Hidskes Date: Tue, 28 Jan 2020 21:32:45 +0100 Subject: [PATCH] cage: switch SIGCHLD handling for pipe As explained in [1] and [2], SIGCHLD is a tricky signal to handle. A pipe can be used to signal completion instead. [1]: https://github.com/swaywm/wlroots/issues/2012#issuecomment-578908333 [2]: https://stackoverflow.com/questions/8976004/using-waitpid-or-sigaction/8976461#8976461 --- cage.c | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/cage.c b/cage.c index ecc19dd..b6d537f 100644 --- a/cage.c +++ b/cage.c @@ -49,14 +49,40 @@ #include "xwayland.h" #endif -static bool -spawn_primary_client(char *argv[], pid_t *pid_out) +static int +sigchld_handler(int fd, uint32_t mask, void *data) { + struct wl_display *display = data; + + /* Close Cage's read pipe. */ + close(fd); + + if (mask & WL_EVENT_HANGUP) { + wlr_log(WLR_DEBUG, "Child process closed normally"); + } else if (mask & WL_EVENT_ERROR) { + wlr_log(WLR_DEBUG, "Connection closed by server"); + } + + wl_display_terminate(display); + return 0; +} + +static bool +spawn_primary_client(struct wl_display *display, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source) +{ + int fd[2]; + if (pipe(fd) != 0) { + wlr_log(WLR_ERROR, "Unable to create pipe"); + return false; + } + pid_t pid = fork(); if (pid == 0) { sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); + /* Close read, we only need write in the primary client process. */ + close(fd[0]); execvp(argv[0], argv); _exit(1); } else if (pid == -1) { @@ -64,6 +90,13 @@ spawn_primary_client(char *argv[], pid_t *pid_out) return false; } + /* Close write, we only need read in Cage. */ + close(fd[1]); + + struct wl_event_loop *event_loop = wl_display_get_event_loop(display); + uint32_t mask = WL_EVENT_HANGUP | WL_EVENT_ERROR; + *sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, display); + *pid_out = pid; wlr_log(WLR_DEBUG, "Child process created with pid %d", pid); return true; @@ -111,8 +144,6 @@ handle_signal(int signal, void *data) case SIGINT: /* Fallthrough */ case SIGTERM: - /* Fallthrough */ - case SIGCHLD: wl_display_terminate(display); return 0; default: @@ -230,7 +261,6 @@ main(int argc, char *argv[]) event_loop = wl_display_get_event_loop(server.wl_display); sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server.wl_display); sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server.wl_display); - sigchld_source = wl_event_loop_add_signal(event_loop, SIGCHLD, handle_signal, &server.wl_display); server.backend = wlr_backend_autocreate(server.wl_display, NULL); if (!server.backend) { @@ -420,7 +450,7 @@ main(int argc, char *argv[]) #endif pid_t pid; - if (!spawn_primary_client(argv + optind, &pid)) { + if (!spawn_primary_client(server.wl_display, argv + optind, &pid, &sigchld_source)) { ret = 1; goto end; }