diff --git a/include/sway/commands.h b/include/sway/commands.h index 2746ef28f..116c8681e 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -307,4 +307,6 @@ sway_cmd cmd_ipc_cmd; sway_cmd cmd_ipc_events; sway_cmd cmd_ipc_event_cmd; +sway_cmd cmd_sandbox_socket; + #endif diff --git a/sway/commands.c b/sway/commands.c index 5a1fd32ef..ade234c92 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -81,6 +81,7 @@ static const struct cmd_handler handlers[] = { { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "popup_during_fullscreen", cmd_popup_during_fullscreen }, + { "sandbox_socket", cmd_sandbox_socket }, { "seat", cmd_seat }, { "set", cmd_set }, { "show_marks", cmd_show_marks }, diff --git a/sway/commands/sandbox_socket.c b/sway/commands/sandbox_socket.c new file mode 100644 index 000000000..d997f473d --- /dev/null +++ b/sway/commands/sandbox_socket.c @@ -0,0 +1,147 @@ +#define _XOPEN_SOURCE 700 // for strdup +#include +#include +#include +#include +#include +#include +#include +#include +#include "sway/client_label.h" +#include "sway/commands.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" +#include "list.h" +#include "log.h" +#include "util.h" + +struct sandbox_socket { + char* path; + struct wl_event_source *src; + int fd; + char* label; +}; + +static list_t *sandbox_sockets; + +static int fd_accept(int srv_fd, uint32_t mask, void *data) { + struct sandbox_socket *sock = data; + + int cli_fd = accept(srv_fd, NULL, NULL); + if (cli_fd < 0) { + if (errno == EINTR || errno == ECONNABORTED || errno == EAGAIN || errno == EWOULDBLOCK) { + return 1; + } else { + int i; + wl_event_source_remove(sock->src); + unlink(sock->path); + free(sock->path); + close(srv_fd); + for(i = 0; i < sandbox_sockets->length; ++i) { + if (sock != sandbox_sockets->items[i]) + continue; + list_del(sandbox_sockets, i); + break; + } + free(sock); + return 0; + } + } + + sway_set_cloexec(cli_fd, true); + struct wl_client* client = wl_client_create(server.wl_display, cli_fd); + if (client) { + wl_client_label_set(client, strdup(sock->label)); + } else { + close(cli_fd); + } + + return 1; +} + +struct cmd_results *cmd_sandbox_socket(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "sandbox_socket", EXPECTED_AT_LEAST, 2))) { + return error; + } + + if (!sandbox_sockets) + sandbox_sockets = create_list(); + + char* op = argv[0]; + + if (strcmp(op, "create") == 0) { + struct sockaddr_un name = {}; + char* label = NULL; + int i = 1; + while (i < argc) { + if (strcmp(argv[i], "--label") == 0) { + if (i + 1 >= argc) + return cmd_results_new(CMD_INVALID, "--label requires an argument"); + label = argv[i + 1]; + i += 2; + } else if (strcmp(argv[i], "--") == 0) { // after this any argument should be treated as positional + ++i; + break; + } else if (strncmp(argv[i], "-", 1) == 0) { + return cmd_results_new(CMD_INVALID, "Unknown option to sandbox_socket"); + } else { + break; // end of options, now only positional arguments + } + } + + if ((error = checkarg(argc, "sandbox_socket", EXPECTED_EQUAL_TO, i + 1))) { + return error; + } + char *path = argv[i]; + size_t path_len = strlen(path) + 1; + + if (path_len > sizeof(name.sun_path)) { + return cmd_results_new(CMD_INVALID, "Invalid socket path: %s", path); + } + unlink(path); + + name.sun_family = AF_UNIX; + memcpy(name.sun_path, path, path_len); + size_t name_len = offsetof(struct sockaddr_un, sun_path) + path_len; + + int srv_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (srv_fd < 0 || + !sway_set_cloexec(srv_fd, true) || + fcntl(srv_fd, F_SETFL, O_NONBLOCK) != 0 || + bind(srv_fd, (struct sockaddr *)&name, name_len) != 0 || + listen(srv_fd, 5) != 0) { + close(srv_fd); + return cmd_results_new(CMD_FAILURE, "Error creating socket: %s", strerror(errno)); + } + + struct sandbox_socket *sock = calloc(1, sizeof(*sock)); + sock->path = strdup(path); + sock->src = wl_event_loop_add_fd(server.wl_event_loop, srv_fd, WL_EVENT_READABLE, fd_accept, sock); + sock->fd = srv_fd; + if (label) + sock->label = strdup(label); + + list_add(sandbox_sockets, sock); + return cmd_results_new(CMD_SUCCESS, NULL); + } else if (strcmp(op, "delete") == 0) { + char* path = argv[1]; + int i; + for(i = 0; i < sandbox_sockets->length; ++i) { + struct sandbox_socket *sock = sandbox_sockets->items[i]; + if (strcmp(sock->path, path) == 0) { + wl_event_source_remove(sock->src); + unlink(path); + close(sock->fd); + free(sock->path); + free(sock->label); + free(sock); + list_del(sandbox_sockets, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + return cmd_results_new(CMD_FAILURE, "sandbox_socket: %s not found", path); + } else { + return cmd_results_new(CMD_INVALID, "Unknown command sandbox_socket %s", op); + } +} diff --git a/sway/meson.build b/sway/meson.build index 5f34ce6b0..b990fc36f 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -86,6 +86,7 @@ sway_sources = files( 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', + 'commands/sandbox_socket.c', 'commands/scratchpad.c', 'commands/seat.c', 'commands/seat/attach.c',