diff --git a/common/util.c b/common/util.c index c43c5ddf..3a807edb 100644 --- a/common/util.c +++ b/common/util.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -75,3 +76,21 @@ const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) sway_assert(false, "Unknown value for wl_output_subpixel."); return NULL; } + +bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 5a178739..74d9ea18 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,9 +1,12 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H +#include struct swaynag_instance { + struct wl_client *client; + struct wl_listener client_destroy; + const char *args; - pid_t pid; int fd[2]; bool detailed; }; @@ -15,9 +18,6 @@ struct swaynag_instance { bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag); -// Kill the swaynag instance -void swaynag_kill(struct swaynag_instance *swaynag); - // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed // is false. void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, diff --git a/include/util.h b/include/util.h index 6a668fd6..6d9454e0 100644 --- a/include/util.h +++ b/include/util.h @@ -32,4 +32,6 @@ float parse_float(const char *value); const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); +bool set_cloexec(int fd, bool cloexec); + #endif diff --git a/sway/config.c b/sway/config.c index d5bfe105..4944ec02 100644 --- a/sway/config.c +++ b/sway/config.c @@ -175,15 +175,13 @@ static void set_color(float dest[static 4], uint32_t color) { static void config_defaults(struct sway_config *config) { if (!(config->swaynag_command = strdup("swaynag"))) goto cleanup; - config->swaynag_config_errors = (struct swaynag_instance){ - .args = "--type error " + config->swaynag_config_errors = (struct swaynag_instance){0}; + config->swaynag_config_errors.args = "--type error " "--message 'There are errors in your config file' " "--detailed-message " - "--button 'Exit sway' 'swaymsg exit' " - "--button 'Reload sway' 'swaymsg reload'", - .pid = -1, - .detailed = true, - }; + "--button-no-terminal 'Exit sway' 'swaymsg exit' " + "--button-no-terminal 'Reload sway' 'swaymsg reload'"; + config->swaynag_config_errors.detailed = true; if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; @@ -411,10 +409,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { config->reloading = true; config->active = true; - swaynag_kill(&old_config->swaynag_config_errors); - memcpy(&config->swaynag_config_errors, - &old_config->swaynag_config_errors, - sizeof(struct swaynag_instance)); + if (old_config->swaynag_config_errors.client != NULL) { + wl_client_destroy(old_config->swaynag_config_errors.client); + } input_manager_reset_all_inputs(); } @@ -486,7 +483,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/config/output.c b/sway/config/output.c index 747ab28b..fb8a9ee5 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,6 +1,5 @@ #define _POSIX_C_SOURCE 200809L #include -#include #include #include #include @@ -488,24 +487,6 @@ static void handle_swaybg_client_destroy(struct wl_listener *listener, config->swaybg_client = NULL; } -static bool set_cloexec(int fd, bool cloexec) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - if (cloexec) { - flags = flags | FD_CLOEXEC; - } else { - flags = flags & ~FD_CLOEXEC; - } - if (fcntl(fd, F_SETFD, flags) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - return true; -} - static bool _spawn_swaybg(char **command) { if (config->swaybg_client != NULL) { wl_client_destroy(config->swaybg_client); diff --git a/sway/main.c b/sway/main.c index ba4e2562..96f67b36 100644 --- a/sway/main.c +++ b/sway/main.c @@ -391,7 +391,7 @@ int main(int argc, char **argv) { load_swaybars(); run_deferred_commands(); - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/swaynag.c b/sway/swaynag.c index 49027f5d..0fca6c71 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -1,16 +1,32 @@ #define _POSIX_C_SOURCE 200809L -#include #include #include #include #include +#include #include +#include #include #include "log.h" +#include "sway/server.h" #include "sway/swaynag.h" +#include "util.h" + +static void handle_swaynag_client_destroy(struct wl_listener *listener, + void *data) { + struct swaynag_instance *swaynag = + wl_container_of(listener, swaynag, client_destroy); + wl_list_remove(&swaynag->client_destroy.link); + wl_list_init(&swaynag->client_destroy.link); + swaynag->client = NULL; +} bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag) { + if (swaynag->client != NULL) { + wl_client_destroy(swaynag->client); + } + if (!swaynag_command) { return true; } @@ -20,44 +36,94 @@ bool swaynag_spawn(const char *swaynag_command, sway_log(SWAY_ERROR, "Failed to create pipe for swaynag"); return false; } - fcntl(swaynag->fd[1], F_SETFD, FD_CLOEXEC); + if (!set_cloexec(swaynag->fd[1], true)) { + goto failed; + } } - pid_t pid; - if ((pid = fork()) == 0) { - if (swaynag->detailed) { - close(swaynag->fd[1]); - dup2(swaynag->fd[0], STDIN_FILENO); - close(swaynag->fd[0]); - } + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + goto failed; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + goto failed; + } - size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; - char *cmd = malloc(length); - snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); - execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); - _exit(0); - } else if (pid < 0) { + swaynag->client = wl_client_create(server.wl_display, sockets[0]); + if (swaynag->client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + goto failed; + } + + swaynag->client_destroy.notify = handle_swaynag_client_destroy; + wl_client_add_destroy_listener(swaynag->client, &swaynag->client_destroy); + + pid_t pid = fork(); + if (pid < 0) { sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); - if (swaynag->detailed) { - close(swaynag->fd[0]); - close(swaynag->fd[1]); + goto failed; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + if (swaynag->detailed) { + close(swaynag->fd[1]); + dup2(swaynag->fd[0], STDIN_FILENO); + close(swaynag->fd[0]); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; + char *cmd = malloc(length); + snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + sway_log_errno(SWAY_ERROR, "execl failed"); + _exit(EXIT_FAILURE); } - return false; + _exit(EXIT_SUCCESS); } if (swaynag->detailed) { - close(swaynag->fd[0]); + if (close(swaynag->fd[0]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } } - swaynag->pid = pid; + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + return true; -} - -void swaynag_kill(struct swaynag_instance *swaynag) { - if (swaynag->pid > 0) { - kill(swaynag->pid, SIGTERM); - swaynag->pid = -1; +failed: + if (swaynag->detailed) { + if (close(swaynag->fd[0]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (close(swaynag->fd[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + } } + return false; } void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, @@ -71,7 +137,7 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, return; } - if (swaynag->pid <= 0 && !swaynag_spawn(swaynag_command, swaynag)) { + if (swaynag->client == NULL && !swaynag_spawn(swaynag_command, swaynag)) { return; } @@ -96,7 +162,7 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, } void swaynag_show(struct swaynag_instance *swaynag) { - if (swaynag->detailed && swaynag->pid > 0) { + if (swaynag->detailed && swaynag->client != NULL) { close(swaynag->fd[1]); } } diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index eb31da57..26411ab3 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -45,17 +45,24 @@ static void swaynag_button_execute(struct swaynag *swaynag, swaynag->details.visible = !swaynag->details.visible; render_frame(swaynag); } else { - if (fork() == 0) { + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { // Child process. Will be used to prevent zombie processes - setsid(); - if (fork() == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { // Child of the child. Will be reparented to the init process char *terminal = getenv("TERMINAL"); if (button->terminal && terminal && strlen(terminal)) { sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); if (!terminal_execute(terminal, button->action)) { swaynag_destroy(swaynag); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } } else { if (button->terminal) { @@ -63,12 +70,16 @@ static void swaynag_button_execute(struct swaynag *swaynag, "$TERMINAL not found. Running directly"); } execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); + sway_log_errno(SWAY_DEBUG, "execl failed"); + _exit(EXIT_FAILURE); } } - exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_DEBUG, "waitpid failed"); } } - wait(0); } static void layer_surface_configure(void *data,