diff --git a/include/sway/util.h b/include/sway/util.h new file mode 100644 index 000000000..500421f94 --- /dev/null +++ b/include/sway/util.h @@ -0,0 +1,14 @@ +#ifndef _SWAY_SWAY_UTIL_H +#define _SWAY_SWAY_UTIL_H + +#include + +/** + * Close fd and log theoretical case when close(2) failed + */ +void close_warn(int fd); + +struct wl_client *spawn_wl_client(char * const cmd[], struct wl_display *display); +struct wl_client *spawn_wl_client_fa(char * const cmd[], struct wl_display *display, posix_spawn_file_actions_t *fa); + +#endif//_SWAY_SWAY_UTIL_H diff --git a/sway/config/bar.c b/sway/config/bar.c index 9c30204e2..63f06ecd5 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -13,6 +13,7 @@ #include "sway/config.h" #include "sway/input/keyboard.h" #include "sway/output.h" +#include "sway/util.h" #include "config.h" #include "list.h" #include "log.h" @@ -186,79 +187,25 @@ static void handle_swaybar_client_destroy(struct wl_listener *listener, bar->client = NULL; } -static void invoke_swaybar(struct bar_config *bar) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return; - } - if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { - return; - } - - bar->client = wl_client_create(server.wl_display, sockets[0]); - if (bar->client == NULL) { - sway_log_errno(SWAY_ERROR, "wl_client_create failed"); - return; - } - - bar->client_destroy.notify = handle_swaybar_client_destroy; - wl_client_add_destroy_listener(bar->client, &bar->client_destroy); - - pid_t pid = fork(); - if (pid < 0) { - sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); - return; - } else if (pid == 0) { - // Remove the SIGUSR1 handler that wlroots adds for xwayland - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - - 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); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - // run custom swaybar - char *const cmd[] = { - bar->swaybar_command ? bar->swaybar_command : "swaybar", - "-b", bar->id, NULL}; - execvp(cmd[0], cmd); - _exit(EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return; - } - - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return; - } - - sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); - return; -} - void load_swaybar(struct bar_config *bar) { if (bar->client != NULL) { wl_client_destroy(bar->client); } sway_log(SWAY_DEBUG, "Invoking swaybar for bar id '%s'", bar->id); - invoke_swaybar(bar); + + char *const cmd[] = { + bar->swaybar_command ? bar->swaybar_command : "swaybar", + "-b", bar->id, NULL}; + bar->client = spawn_wl_client(cmd, server.wl_display); + + if (bar->client == NULL) { + sway_log(SWAY_ERROR, "Failed to spawn swaybar %s", bar->id); + return; + } + + sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); + bar->client_destroy.notify = handle_swaybar_client_destroy; + wl_client_add_destroy_listener(bar->client, &bar->client_destroy); } void load_swaybars(void) { diff --git a/sway/config/output.c b/sway/config/output.c index 50bf1155f..27312a9d0 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -12,6 +12,7 @@ #include "sway/input/cursor.h" #include "sway/output.h" #include "sway/tree/root.h" +#include "sway/util.h" #include "log.h" #include "util.h" @@ -496,67 +497,6 @@ static void handle_swaybg_client_destroy(struct wl_listener *listener, sway_config->swaybg_client = NULL; } -static bool _spawn_swaybg(char **command) { - if (config->swaybg_client != NULL) { - wl_client_destroy(config->swaybg_client); - } - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return false; - } - if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { - return false; - } - - config->swaybg_client = wl_client_create(server.wl_display, sockets[0]); - if (config->swaybg_client == NULL) { - sway_log_errno(SWAY_ERROR, "wl_client_create failed"); - return false; - } - - config->swaybg_client_destroy.notify = handle_swaybg_client_destroy; - wl_client_add_destroy_listener(config->swaybg_client, - &config->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) { - if (!set_cloexec(sockets[1], false)) { - _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(command[0], command); - sway_log_errno(SWAY_ERROR, "execvp failed"); - _exit(EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } - - 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; -} - bool spawn_swaybg(void) { if (!config->swaybg_command) { return true; @@ -614,7 +554,17 @@ bool spawn_swaybg(void) { sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%zd] = %s", k, cmd[k]); } - bool result = _spawn_swaybg(cmd); + if (config->swaybg_client) { + wl_client_destroy(config->swaybg_client); + } + + config->swaybg_client = spawn_wl_client(cmd, server.wl_display); + + if (config->swaybg_client) { + config->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(config->swaybg_client, &config->swaybg_client_destroy); + } + free(cmd); - return result; + return config->swaybg_client != NULL; } diff --git a/sway/meson.build b/sway/meson.build index 24628100f..98d3e577c 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -9,6 +9,7 @@ sway_sources = files( 'security.c', 'server.c', 'swaynag.c', + 'util.c', 'xdg_decoration.c', 'desktop/desktop.c', diff --git a/sway/swaynag.c b/sway/swaynag.c index 0fca6c710..b73b518fe 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -10,6 +10,7 @@ #include "log.h" #include "sway/server.h" #include "sway/swaynag.h" +#include "sway/util.h" #include "util.h" static void handle_swaynag_client_destroy(struct wl_listener *listener, @@ -21,6 +22,40 @@ static void handle_swaynag_client_destroy(struct wl_listener *listener, swaynag->client = NULL; } +static struct wl_client *swaynag_spawn_detailed(char *cmd[], struct swaynag_instance *swaynag) { + if (pipe(swaynag->fd) != 0) { + sway_log(SWAY_ERROR, "Failed to create pipe for swaynag"); + return NULL; + } + + if (!set_cloexec(swaynag->fd[1], true)) { + goto failed; + } + + posix_spawn_file_actions_t fa; + posix_spawn_file_actions_init(&fa); + posix_spawn_file_actions_adddup2(&fa, swaynag->fd[0], STDIN_FILENO); + posix_spawn_file_actions_addclose(&fa, swaynag->fd[0]); + + struct wl_client *client = spawn_wl_client_fa(cmd, server.wl_display, &fa); + + posix_spawn_file_actions_destroy(&fa); + + if (client == NULL) { + goto failed; + } + + close_warn(swaynag->fd[0]); + + return client; + +failed: + close_warn(swaynag->fd[0]); + close_warn(swaynag->fd[1]); + + return NULL; +} + bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag) { if (swaynag->client != NULL) { @@ -31,99 +66,28 @@ bool swaynag_spawn(const char *swaynag_command, return true; } + size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; + char *swaynag_cmd = malloc(length); + snprintf(swaynag_cmd, length, "%s %s", swaynag_command, swaynag->args); + char *cmd[] = {"/bin/sh", "-c", swaynag_cmd, NULL}; + if (swaynag->detailed) { - if (pipe(swaynag->fd) != 0) { - sway_log(SWAY_ERROR, "Failed to create pipe for swaynag"); - return false; - } - if (!set_cloexec(swaynag->fd[1], true)) { - goto failed; - } + swaynag->client = swaynag_spawn_detailed(cmd, swaynag); + } else { + swaynag->client = spawn_wl_client(cmd, server.wl_display); } - 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; - } + free(swaynag_cmd); - 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; + sway_log(SWAY_ERROR, "Failed to spawn process for swaynag"); + return false; } 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"); - 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); - } - _exit(EXIT_SUCCESS); - } - - if (swaynag->detailed) { - if (close(swaynag->fd[0]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return false; - } - } - - 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; - -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, diff --git a/sway/util.c b/sway/util.c new file mode 100644 index 000000000..0cf77179a --- /dev/null +++ b/sway/util.c @@ -0,0 +1,96 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "sway/util.h" +#include "util.h" + +extern char **environ; + +void close_warn(int fd) { + if (close(fd) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + } +} + +struct wl_client *spawn_wl_client(char * const cmd[], struct wl_display *display) { + return spawn_wl_client_fa(cmd, display, NULL); +} + +struct wl_client *spawn_wl_client_fa(char * const cmd[], struct wl_display *display, posix_spawn_file_actions_t *fa) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return NULL; + } + + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + goto cleanup_sockets; + } + + struct wl_client *client = wl_client_create(display, sockets[0]); + if (client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + goto cleanup_sockets; + } + + pid_t pid = fork(); + if (pid < 0) { + sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); + goto cleanup_client; + } else if (pid == 0) { + posix_spawnattr_t attr; + posix_spawnattr_init(&attr); + + // Remove the SIGUSR1 handler that wlroots adds for xwayland + sigset_t set; + sigfillset(&set); + posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGDEF); + posix_spawnattr_setsigdefault(&attr, &set); + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + int r = posix_spawnp(&pid, cmd[0], fa, &attr, cmd, environ); + if (r) { + sway_log_errno(SWAY_ERROR, "posix_spawnp failed"); + _exit(EXIT_FAILURE); + } + + posix_spawnattr_destroy(&attr); + + _exit(EXIT_SUCCESS); + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + goto cleanup_client; + } + + close_warn(sockets[1]); + + return client; + +cleanup_client: + wl_client_destroy(client); + +cleanup_sockets: + close(sockets[0]); + close(sockets[1]); + + return NULL; +}