From 7baaa3a0f80a28f58a3f95f7c3c832cb109b2aab Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 23:30:19 +0100 Subject: [PATCH] Disconnect swaybg instead of killing it This is much more reliable. This also fixes race conditions when killing swaybg while it's doing a wl_display_roundtrip. --- include/sway/output.h | 8 ++-- sway/config/output.c | 91 ++++++++++++++++++++++++++++++++++--------- sway/desktop/output.c | 2 + sway/tree/output.c | 5 +-- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 479897ef..ea7a2174 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -21,6 +21,7 @@ struct sway_output { struct sway_node node; struct wlr_output *wlr_output; struct sway_server *server; + struct wl_list link; struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; @@ -36,6 +37,8 @@ struct sway_output { struct sway_output_state current; + struct wl_client *swaybg_client; + struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -43,10 +46,7 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - - struct wl_list link; - - pid_t bg_pid; + struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; diff --git a/sway/config/output.c b/sway/config/output.c index 0f238715..513d03e0 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,16 +1,17 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include -#include +#include #include #include -#include #include +#include +#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" -#include "log.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -165,14 +166,71 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -void terminate_swaybg(pid_t pid) { - int ret = kill(pid, SIGTERM); - if (ret != 0) { - sway_log(SWAY_ERROR, "Unable to terminate swaybg [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + struct sway_output *output = + wl_container_of(listener, output, swaybg_client_destroy); + wl_list_remove(&output->swaybg_client_destroy.link); + wl_list_init(&output->swaybg_client_destroy.link); + output->swaybg_client = NULL; +} + +static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; } + + output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (output->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(output->swaybg_client, + &output->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + // Remove the CLOEXEC flag + int flags = fcntl(sockets[1], F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + if (fcntl(sockets[1], F_SETFD, flags & ~FD_CLOEXEC) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(cmd[0], cmd); + sway_log_errno(SWAY_ERROR, "execvp failed"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; } bool apply_output_config(struct output_config *oc, struct sway_output *output) { @@ -243,8 +301,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - if (output->bg_pid != 0) { - terminate_swaybg(output->bg_pid); + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } if (oc && oc->background && config->swaybg_command) { sway_log(SWAY_DEBUG, "Setting background for output %s to %s", @@ -258,13 +316,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { oc->background_fallback ? oc->background_fallback : NULL, NULL, }; - - output->bg_pid = fork(); - if (output->bg_pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - } else if (output->bg_pid == 0) { - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "Failed to execute swaybg"); + if (!spawn_swaybg(output, cmd)) { + return false; } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ad75bb35..c5461ee6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -506,6 +506,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); + wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); } @@ -612,6 +613,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_frame.notify = damage_handle_frame; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; + wl_list_init(&output->swaybg_client_destroy.link); struct output_config *oc = output_find_config(output); if (!oc || oc->enabled) { diff --git a/sway/tree/output.c b/sway/tree/output.c index 138144a7..60e0af9f 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -233,9 +233,8 @@ void output_disable(struct sway_output *output) { root_for_each_container(untrack_output, output); - if (output->bg_pid) { - terminate_swaybg(output->bg_pid); - output->bg_pid = 0; + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } int index = list_find(root->outputs, output);