From 8981b48cd2cdd0a59c1546f2816587b303538d77 Mon Sep 17 00:00:00 2001 From: minus Date: Sun, 16 Aug 2015 20:24:18 +0200 Subject: very basic IPC implementation simply executes the received data as command --- sway/ipc.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 sway/ipc.c (limited to 'sway/ipc.c') diff --git a/sway/ipc.c b/sway/ipc.c new file mode 100644 index 00000000..ac5246d2 --- /dev/null +++ b/sway/ipc.c @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "config.h" +#include "commands.h" + +static int ipc_socket = -1; + +int ipc_handle_connection(int fd, uint32_t mask, void *data); + +void init_ipc() { + ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (ipc_socket == -1) { + sway_abort("Unable to create IPC socket"); + } + + struct sockaddr_un ipc_sockaddr = { + .sun_family = AF_UNIX, + // TODO: use a proper socket path + .sun_path = "/tmp/sway.sock" + }; + + unlink(ipc_sockaddr.sun_path); + if (bind(ipc_socket, (struct sockaddr *)&ipc_sockaddr, sizeof(ipc_sockaddr)) == -1) { + sway_abort("Unable to bind IPC socket"); + } + + if (listen(ipc_socket, 3) == -1) { + sway_abort("Unable to listen on IPC socket"); + } + + wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); +} + +int ipc_handle_connection(int /*fd*/, uint32_t /*mask*/, void */*data*/) { + int client_socket = accept(ipc_socket, NULL, NULL); + if (client_socket == -1) { + char error[256]; + strerror_r(errno, error, sizeof(error)); + sway_log(L_INFO, "Unable to accept IPC client connection: %s", error); + return 0; + } + + char buf[1024]; + if (recv(client_socket, buf, sizeof(buf), 0) == -1) { + char error[256]; + strerror_r(errno, error, sizeof(error)); + sway_log(L_INFO, "Unable to receive from IPC client: %s", error); + close(client_socket); + return 0; + } + + sway_log(L_INFO, "Executing IPC command: %s", buf); + + bool success = handle_command(config, buf); + snprintf(buf, sizeof(buf), "{\"success\":%s}\n", success ? "true" : "false"); + + if (send(client_socket, buf, strlen(buf), 0) == -1) { + char error[256]; + strerror_r(errno, error, sizeof(error)); + sway_log(L_INFO, "Unable to send to IPC client: %s", error); + } + + close(client_socket); + return 0; +} -- cgit v1.2.3 From 5d99215469088790d9b07a2932fd266133c8dc0b Mon Sep 17 00:00:00 2001 From: minus Date: Sun, 16 Aug 2015 22:02:16 +0200 Subject: added i3-ipc support/parsing --- include/ipc.h | 11 ++++++++ sway/ipc.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 84 insertions(+), 7 deletions(-) (limited to 'sway/ipc.c') diff --git a/include/ipc.h b/include/ipc.h index aab9cf0c..25d2fc61 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -1,6 +1,17 @@ #ifndef _SWAY_IPC_H #define _SWAY_IPC_H +enum ipc_command_type { + IPC_COMMAND = 0, + IPC_GET_WORKSPACES = 1, + IPC_SUBSCRIBE = 2, + IPC_GET_OUTPUTS = 3, + IPC_GET_TREE = 4, + IPC_GET_MARKS = 5, + IPC_GET_BAR_CONFIG = 6, + IPC_GET_VERSION = 7, +}; + void init_ipc(void); #endif diff --git a/sway/ipc.c b/sway/ipc.c index ac5246d2..1a074788 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -2,15 +2,23 @@ #include #include #include +#include #include #include +#include +#include "ipc.h" #include "log.h" #include "config.h" #include "commands.h" static int ipc_socket = -1; +static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; + int ipc_handle_connection(int fd, uint32_t mask, void *data); +size_t ipc_handle_command(char **reply_data, char *data, ssize_t length); +size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length); + void init_ipc() { ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); @@ -36,7 +44,8 @@ void init_ipc() { wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); } -int ipc_handle_connection(int /*fd*/, uint32_t /*mask*/, void */*data*/) { +int ipc_handle_connection(int fd, uint32_t mask, void *data) { + sway_log(L_DEBUG, "Event on IPC listening socket"); int client_socket = accept(ipc_socket, NULL, NULL); if (client_socket == -1) { char error[256]; @@ -46,7 +55,9 @@ int ipc_handle_connection(int /*fd*/, uint32_t /*mask*/, void */*data*/) { } char buf[1024]; - if (recv(client_socket, buf, sizeof(buf), 0) == -1) { + // Leave one byte of space at the end of the buffer for NULL terminator + ssize_t received = recv(client_socket, buf, sizeof(buf) - 1, 0); + if (received == -1) { char error[256]; strerror_r(errno, error, sizeof(error)); sway_log(L_INFO, "Unable to receive from IPC client: %s", error); @@ -54,17 +65,72 @@ int ipc_handle_connection(int /*fd*/, uint32_t /*mask*/, void */*data*/) { return 0; } - sway_log(L_INFO, "Executing IPC command: %s", buf); - - bool success = handle_command(config, buf); - snprintf(buf, sizeof(buf), "{\"success\":%s}\n", success ? "true" : "false"); + char *reply_buf; + size_t reply_length = ipc_handle_command(&reply_buf, buf, received); + sway_log(L_DEBUG, "IPC reply: %s", reply_buf); - if (send(client_socket, buf, strlen(buf), 0) == -1) { + if (send(client_socket, reply_buf, reply_length, 0) == -1) { char error[256]; strerror_r(errno, error, sizeof(error)); sway_log(L_INFO, "Unable to send to IPC client: %s", error); } + free(reply_buf); close(client_socket); return 0; } + +static const int ipc_header_size = sizeof(ipc_magic)+8; + +size_t ipc_handle_command(char **reply_data, char *data, ssize_t length) { + // See https://i3wm.org/docs/ipc.html for protocol details + + if (length < ipc_header_size) { + sway_log(L_DEBUG, "IPC data too short"); + return false; + } + + if (memcmp(data, ipc_magic, sizeof(ipc_magic)) != 0) { + sway_log(L_DEBUG, "IPC header check failed"); + return false; + } + + uint32_t payload_length = *(uint32_t *)&data[sizeof(ipc_magic)]; + uint32_t command_type = *(uint32_t *)&data[sizeof(ipc_magic)+4]; + + if (length != payload_length + ipc_header_size) { + // TODO: try to read enough data + sway_log(L_DEBUG, "IPC payload size mismatch"); + return false; + } + + switch (command_type) { + case IPC_COMMAND: + { + char *cmd = &data[ipc_header_size]; + data[ipc_header_size + payload_length] = '\0'; + bool success = handle_command(config, cmd); + char buf[64]; + int length = snprintf(buf, sizeof(buf), "{\"success\":%s}", success ? "true" : "false"); + return ipc_format_reply(reply_data, IPC_COMMAND, buf, (uint32_t) length); + } + default: + sway_log(L_INFO, "Unknown IPC command type %i", command_type); + return false; + } +} + +size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length) { + assert(data); + assert(payload); + + size_t length = ipc_header_size + payload_length; + *data = malloc(length); + + memcpy(*data, ipc_magic, sizeof(ipc_magic)); + *(uint32_t *)&((*data)[sizeof(ipc_magic)]) = payload_length; + *(uint32_t *)&((*data)[sizeof(ipc_magic)+4]) = command_type; + memcpy(&(*data)[ipc_header_size], payload, payload_length); + + return length; +} -- cgit v1.2.3 From 773e85c681ee4faecf353de7066b536f1a50ff61 Mon Sep 17 00:00:00 2001 From: minus Date: Wed, 19 Aug 2015 01:35:01 +0200 Subject: properly handle IPC clients --- include/log.h | 1 + sway/ipc.c | 191 ++++++++++++++++++++++++++++++++++++++++------------------ sway/log.c | 30 +++++++++ 3 files changed, 162 insertions(+), 60 deletions(-) (limited to 'sway/ipc.c') diff --git a/include/log.h b/include/log.h index 7aea2ded..47a83321 100644 --- a/include/log.h +++ b/include/log.h @@ -13,6 +13,7 @@ typedef enum { void init_log(int verbosity); void sway_log_colors(int mode); void sway_log(int verbosity, const char* format, ...) __attribute__((format(printf,2,3))); +void sway_log_errno(int verbosity, char* format, ...) __attribute__((format(printf,2,3))); void sway_abort(const char* format, ...) __attribute__((format(printf,1,2))); bool sway_assert(bool condition, const char* format, ...) __attribute__((format(printf,2,3))); diff --git a/sway/ipc.c b/sway/ipc.c index 1a074788..292d9593 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -1,3 +1,5 @@ +// See https://i3wm.org/docs/ipc.html for protocol information + #include #include #include @@ -6,6 +8,8 @@ #include #include #include +#include +#include #include "ipc.h" #include "log.h" #include "config.h" @@ -15,10 +19,18 @@ static int ipc_socket = -1; static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; -int ipc_handle_connection(int fd, uint32_t mask, void *data); -size_t ipc_handle_command(char **reply_data, char *data, ssize_t length); -size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length); +struct ipc_client { + struct wlc_event_source *event_source; + int fd; + uint32_t payload_length; + enum ipc_command_type current_command; +}; +int ipc_handle_connection(int fd, uint32_t mask, void *data); +int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); +void ipc_client_disconnect(struct ipc_client *client); +void ipc_client_handle_command(struct ipc_client *client); +bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); void init_ipc() { ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); @@ -46,91 +58,150 @@ void init_ipc() { int ipc_handle_connection(int fd, uint32_t mask, void *data) { sway_log(L_DEBUG, "Event on IPC listening socket"); - int client_socket = accept(ipc_socket, NULL, NULL); - if (client_socket == -1) { - char error[256]; - strerror_r(errno, error, sizeof(error)); - sway_log(L_INFO, "Unable to accept IPC client connection: %s", error); - return 0; - } + assert(mask == WLC_EVENT_READABLE); - char buf[1024]; - // Leave one byte of space at the end of the buffer for NULL terminator - ssize_t received = recv(client_socket, buf, sizeof(buf) - 1, 0); - if (received == -1) { - char error[256]; - strerror_r(errno, error, sizeof(error)); - sway_log(L_INFO, "Unable to receive from IPC client: %s", error); - close(client_socket); + int client_fd = accept(ipc_socket, NULL, NULL); + if (client_fd == -1) { + sway_log_errno(L_INFO, "Unable to accept IPC client connection"); return 0; } - char *reply_buf; - size_t reply_length = ipc_handle_command(&reply_buf, buf, received); - sway_log(L_DEBUG, "IPC reply: %s", reply_buf); + struct ipc_client* client = malloc(sizeof(struct ipc_client)); + client->payload_length = 0; + client->fd = client_fd; + client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client); - if (send(client_socket, reply_buf, reply_length, 0) == -1) { - char error[256]; - strerror_r(errno, error, sizeof(error)); - sway_log(L_INFO, "Unable to send to IPC client: %s", error); - } - - free(reply_buf); - close(client_socket); return 0; } static const int ipc_header_size = sizeof(ipc_magic)+8; -size_t ipc_handle_command(char **reply_data, char *data, ssize_t length) { - // See https://i3wm.org/docs/ipc.html for protocol details +int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { + struct ipc_client *client = data; + sway_log(L_DEBUG, "Event on IPC client socket %d", client_fd); - if (length < ipc_header_size) { - sway_log(L_DEBUG, "IPC data too short"); - return false; + if (mask & WLC_EVENT_ERROR) { + sway_log(L_INFO, "IPC Client socket error, removing client"); + ipc_client_disconnect(client); + return 0; + } + + if (mask & WLC_EVENT_HANGUP) { + ipc_client_disconnect(client); + return 0; } - if (memcmp(data, ipc_magic, sizeof(ipc_magic)) != 0) { + int read_available; + ioctl(client_fd, FIONREAD, &read_available); + + // Wait for the rest of the command payload in case the header has already been read + if (client->payload_length > 0) { + if (read_available >= client->payload_length) { + ipc_client_handle_command(client); + } + else { + sway_log(L_DEBUG, "Too little data to read payload on IPC Client socket, waiting for more (%d < %d)", read_available, client->payload_length); + } + return 0; + } + + if (read_available < ipc_header_size) { + sway_log(L_DEBUG, "Too little data to read header on IPC Client socket, waiting for more (%d < %d)", read_available, ipc_header_size); + return 0; + } + + char buf[ipc_header_size]; + ssize_t received = recv(client_fd, buf, ipc_header_size, 0); + if (received == -1) { + sway_log_errno(L_INFO, "Unable to receive header from IPC client"); + ipc_client_disconnect(client); + return 0; + } + + if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) { sway_log(L_DEBUG, "IPC header check failed"); - return false; + ipc_client_disconnect(client); + return 0; } - uint32_t payload_length = *(uint32_t *)&data[sizeof(ipc_magic)]; - uint32_t command_type = *(uint32_t *)&data[sizeof(ipc_magic)+4]; + client->payload_length = *(uint32_t *)&buf[sizeof(ipc_magic)]; + client->current_command = (enum ipc_command_type) *(uint32_t *)&buf[sizeof(ipc_magic)+4]; - if (length != payload_length + ipc_header_size) { - // TODO: try to read enough data - sway_log(L_DEBUG, "IPC payload size mismatch"); - return false; + if (read_available - received >= client->payload_length) { + ipc_client_handle_command(client); + } + + return 0; +} + +void ipc_client_disconnect(struct ipc_client *client) +{ + if (!sway_assert(client != NULL, "client != NULL")) { + return; + } + + sway_log(L_INFO, "IPC Client %d disconnected", client->fd); + wlc_event_source_remove(client->event_source); + close(client->fd); + free(client); +} + +void ipc_client_handle_command(struct ipc_client *client) { + if (!sway_assert(client != NULL, "client != NULL")) { + return; + } + + char buf[client->payload_length + 1]; + if (client->payload_length > 0) + { + ssize_t received = recv(client->fd, buf, client->payload_length, 0); + if (received == -1) + { + sway_log_errno(L_INFO, "Unable to receive payload from IPC client"); + ipc_client_disconnect(client); + return; + } } - switch (command_type) { + switch (client->current_command) { case IPC_COMMAND: { - char *cmd = &data[ipc_header_size]; - data[ipc_header_size + payload_length] = '\0'; - bool success = handle_command(config, cmd); - char buf[64]; - int length = snprintf(buf, sizeof(buf), "{\"success\":%s}", success ? "true" : "false"); - return ipc_format_reply(reply_data, IPC_COMMAND, buf, (uint32_t) length); + buf[client->payload_length] = '\0'; + bool success = handle_command(config, buf); + char reply[64]; + int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); + ipc_send_reply(client, reply, (uint32_t) length); + break; } default: - sway_log(L_INFO, "Unknown IPC command type %i", command_type); - return false; + sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); + ipc_client_disconnect(client); + break; } + + client->payload_length = 0; } -size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length) { - assert(data); +bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) { assert(payload); - size_t length = ipc_header_size + payload_length; - *data = malloc(length); + char data[ipc_header_size]; - memcpy(*data, ipc_magic, sizeof(ipc_magic)); - *(uint32_t *)&((*data)[sizeof(ipc_magic)]) = payload_length; - *(uint32_t *)&((*data)[sizeof(ipc_magic)+4]) = command_type; - memcpy(&(*data)[ipc_header_size], payload, payload_length); + memcpy(data, ipc_magic, sizeof(ipc_magic)); + *(uint32_t *)&(data[sizeof(ipc_magic)]) = payload_length; + *(uint32_t *)&(data[sizeof(ipc_magic)+4]) = client->current_command; + + if (write(client->fd, data, ipc_header_size) == -1) { + sway_log_errno(L_INFO, "Unable to send header to IPC client"); + ipc_client_disconnect(client); + return false; + } + + if (write(client->fd, payload, payload_length) == -1) { + sway_log_errno(L_INFO, "Unable to send payload to IPC client"); + ipc_client_disconnect(client); + return false; + } - return length; + return true; } diff --git a/sway/log.c b/sway/log.c index 2d633abb..e8c1b78f 100644 --- a/sway/log.c +++ b/sway/log.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include int colored = 1; int v = 0; @@ -66,6 +68,34 @@ void sway_log(int verbosity, const char* format, ...) { } } +void sway_log_errno(int verbosity, char* format, ...) { + if (verbosity <= v) { + int c = verbosity; + if (c > sizeof(verbosity_colors) / sizeof(char *)) { + c = sizeof(verbosity_colors) / sizeof(char *) - 1; + } + + if (colored) { + fprintf(stderr, verbosity_colors[c]); + } + + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + fprintf(stderr, ": "); + char error[256]; + strerror_r(errno, error, sizeof(error)); + fprintf(stderr, error); + + if (colored) { + fprintf(stderr, "\x1B[0m"); + } + fprintf(stderr, "\n"); + } +} + bool sway_assert(bool condition, const char* format, ...) { if (condition) { return true; -- cgit v1.2.3 From bfbadadf702c6c7f078cc2e854afa02686b91c22 Mon Sep 17 00:00:00 2001 From: minus Date: Wed, 19 Aug 2015 01:40:49 +0200 Subject: use env var SWAYSOCK if available or fall back to /tmp/sway-ipc.sock --- sway/ipc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sway/ipc.c') diff --git a/sway/ipc.c b/sway/ipc.c index 292d9593..ba01a679 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -40,10 +40,13 @@ void init_ipc() { struct sockaddr_un ipc_sockaddr = { .sun_family = AF_UNIX, - // TODO: use a proper socket path - .sun_path = "/tmp/sway.sock" + .sun_path = "/tmp/sway-ipc.sock" }; + if (getenv("SWAYSOCK") != NULL) { + strncpy(ipc_sockaddr.sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr.sun_path)); + } + unlink(ipc_sockaddr.sun_path); if (bind(ipc_socket, (struct sockaddr *)&ipc_sockaddr, sizeof(ipc_sockaddr)) == -1) { sway_abort("Unable to bind IPC socket"); -- cgit v1.2.3 From 91c08772645e2162015c3acf8a8ae7187502adb4 Mon Sep 17 00:00:00 2001 From: minus Date: Wed, 19 Aug 2015 01:52:46 +0200 Subject: properly exit sway - wlc_terminate() instead of exit(0) - unlink IPC socket --- include/ipc.h | 3 ++- sway/commands.c | 2 +- sway/ipc.c | 23 ++++++++++++++++------- sway/main.c | 4 +++- 4 files changed, 22 insertions(+), 10 deletions(-) (limited to 'sway/ipc.c') diff --git a/include/ipc.h b/include/ipc.h index 25d2fc61..606c47ba 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -12,6 +12,7 @@ enum ipc_command_type { IPC_GET_VERSION = 7, }; -void init_ipc(void); +void ipc_init(void); +void ipc_shutdown(void); #endif diff --git a/sway/commands.c b/sway/commands.c index 803d9a21..38557b62 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -186,7 +186,7 @@ static bool cmd_exit(struct sway_config *config, int argc, char **argv) { } // Close all views container_map(&root_container, kill_views, NULL); - exit(0); + wlc_terminate(); return true; } diff --git a/sway/ipc.c b/sway/ipc.c index ba01a679..a6c4eb1a 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -16,6 +16,11 @@ #include "commands.h" static int ipc_socket = -1; +static struct wlc_event_source *ipc_event_source = NULL; +static struct sockaddr_un ipc_sockaddr = { + .sun_family = AF_UNIX, + .sun_path = "/tmp/sway-ipc.sock" +}; static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; @@ -32,17 +37,12 @@ void ipc_client_disconnect(struct ipc_client *client); void ipc_client_handle_command(struct ipc_client *client); bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); -void init_ipc() { +void ipc_init(void) { ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); if (ipc_socket == -1) { sway_abort("Unable to create IPC socket"); } - struct sockaddr_un ipc_sockaddr = { - .sun_family = AF_UNIX, - .sun_path = "/tmp/sway-ipc.sock" - }; - if (getenv("SWAYSOCK") != NULL) { strncpy(ipc_sockaddr.sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr.sun_path)); } @@ -56,10 +56,19 @@ void init_ipc() { sway_abort("Unable to listen on IPC socket"); } - wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); + ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); +} + +void ipc_shutdown(void) { + if (ipc_event_source) { + wlc_event_source_remove(ipc_event_source); + } + close(ipc_socket); + unlink(ipc_sockaddr.sun_path); } int ipc_handle_connection(int fd, uint32_t mask, void *data) { + (void) fd; (void) data; sway_log(L_DEBUG, "Event on IPC listening socket"); assert(mask == WLC_EVENT_READABLE); diff --git a/sway/main.c b/sway/main.c index 1af1278d..a42fbcb7 100644 --- a/sway/main.c +++ b/sway/main.c @@ -100,13 +100,15 @@ int main(int argc, char **argv) { free(config_path); } - init_ipc(); + ipc_init(); wlc_run(); if (devnull) { fclose(devnull); } + ipc_shutdown(); + return 0; } -- cgit v1.2.3 From 70f046c87a1fc03c3b3132bf3b05d2e4c4495805 Mon Sep 17 00:00:00 2001 From: minus Date: Thu, 20 Aug 2015 14:49:54 +0200 Subject: set IPC client sockets to close on exec --- sway/ipc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sway/ipc.c') diff --git a/sway/ipc.c b/sway/ipc.c index a6c4eb1a..69f4a4f3 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "ipc.h" #include "log.h" #include "config.h" @@ -78,6 +79,12 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { return 0; } + int flags; + if ((flags=fcntl(client_fd, F_GETFD)) == -1 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) { + sway_log_errno(L_INFO, "Unable to set CLOEXEC on IPC client socket"); + return 0; + } + struct ipc_client* client = malloc(sizeof(struct ipc_client)); client->payload_length = 0; client->fd = client_fd; -- cgit v1.2.3 From f26ed32e460f3007e623c529d28562f4a0b261cd Mon Sep 17 00:00:00 2001 From: minus Date: Thu, 20 Aug 2015 15:12:34 +0200 Subject: added sway_terminate to exit cleanly --- include/ipc.h | 2 +- sway/commands.c | 3 ++- sway/ipc.c | 2 +- sway/log.c | 3 ++- sway/main.c | 15 +++++++++++++-- 5 files changed, 19 insertions(+), 6 deletions(-) (limited to 'sway/ipc.c') diff --git a/include/ipc.h b/include/ipc.h index 606c47ba..0b6441f6 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -13,6 +13,6 @@ enum ipc_command_type { }; void ipc_init(void); -void ipc_shutdown(void); +void ipc_terminate(void); #endif diff --git a/sway/commands.c b/sway/commands.c index 38557b62..644b8005 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -15,6 +15,7 @@ #include "commands.h" #include "container.h" #include "handlers.h" +#include "sway.h" struct modifier_key { char *name; @@ -186,7 +187,7 @@ static bool cmd_exit(struct sway_config *config, int argc, char **argv) { } // Close all views container_map(&root_container, kill_views, NULL); - wlc_terminate(); + sway_terminate(); return true; } diff --git a/sway/ipc.c b/sway/ipc.c index 69f4a4f3..d55469ed 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -60,7 +60,7 @@ void ipc_init(void) { ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); } -void ipc_shutdown(void) { +void ipc_terminate(void) { if (ipc_event_source) { wlc_event_source_remove(ipc_event_source); } diff --git a/sway/log.c b/sway/log.c index e8c1b78f..6e01421b 100644 --- a/sway/log.c +++ b/sway/log.c @@ -1,4 +1,5 @@ #include "log.h" +#include "sway.h" #include #include #include @@ -42,7 +43,7 @@ void sway_abort(const char *format, ...) { vfprintf(stderr, format, args); va_end(args); fprintf(stderr, "\n"); - exit(1); + sway_terminate(); } void sway_log(int verbosity, const char* format, ...) { diff --git a/sway/main.c b/sway/main.c index a42fbcb7..f37f086d 100644 --- a/sway/main.c +++ b/sway/main.c @@ -10,6 +10,14 @@ #include "log.h" #include "handlers.h" #include "ipc.h" +#include "sway.h" + +static bool terminate_request = false; + +void sway_terminate(void) { + terminate_request = true; + wlc_terminate(); +} static void sigchld_handle(int signal); @@ -102,12 +110,15 @@ int main(int argc, char **argv) { ipc_init(); - wlc_run(); + if (!terminate_request) { + wlc_run(); + } + if (devnull) { fclose(devnull); } - ipc_shutdown(); + ipc_terminate(); return 0; } -- cgit v1.2.3 From 6ab968e63eb8c59abda6ddf9c5ae6d5a15d4c25e Mon Sep 17 00:00:00 2001 From: minus Date: Thu, 20 Aug 2015 15:15:04 +0200 Subject: fixed formatting --- sway/ipc.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'sway/ipc.c') diff --git a/sway/ipc.c b/sway/ipc.c index d55469ed..074f2a78 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -183,19 +183,19 @@ void ipc_client_handle_command(struct ipc_client *client) { } switch (client->current_command) { - case IPC_COMMAND: - { - buf[client->payload_length] = '\0'; - bool success = handle_command(config, buf); - char reply[64]; - int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); - ipc_send_reply(client, reply, (uint32_t) length); - break; - } - default: - sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); - ipc_client_disconnect(client); - break; + case IPC_COMMAND: + { + buf[client->payload_length] = '\0'; + bool success = handle_command(config, buf); + char reply[64]; + int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false"); + ipc_send_reply(client, reply, (uint32_t) length); + break; + } + default: + sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); + ipc_client_disconnect(client); + break; } client->payload_length = 0; -- cgit v1.2.3 From 4c56cd0ed638cc59f4412b7091220d2d32ba130c Mon Sep 17 00:00:00 2001 From: minus Date: Thu, 20 Aug 2015 15:22:38 +0200 Subject: set I3SOCK for i3-msg compatibility --- sway/ipc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sway/ipc.c') diff --git a/sway/ipc.c b/sway/ipc.c index 074f2a78..505c17f8 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -57,6 +57,9 @@ void ipc_init(void) { sway_abort("Unable to listen on IPC socket"); } + // Set i3 IPC socket path so that i3-msg works out of the box + setenv("I3SOCK", ipc_sockaddr.sun_path, 1); + ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); } -- cgit v1.2.3 From 754793aad4c0f0b93e30804c70807e9984741ae0 Mon Sep 17 00:00:00 2001 From: minus Date: Thu, 20 Aug 2015 21:08:13 +0200 Subject: added IPC messages get_workspaces and get_outputs No escaping on container names is done yet, as well as some values are hardcoded because they don't exist yet. --- include/stringop.h | 1 + sway/ipc.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sway/stringop.c | 39 +++++++++++++++++++++++ 3 files changed, 132 insertions(+) (limited to 'sway/ipc.c') diff --git a/include/stringop.h b/include/stringop.h index a5346829..4300f9ed 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -10,5 +10,6 @@ char *code_strchr(const char *string, char delimiter); char *code_strstr(const char *haystack, const char *needle); int unescape_string(char *string); char *join_args(char **argv, int argc); +char *join_list(list_t *list, char *separator); #endif diff --git a/sway/ipc.c b/sway/ipc.c index 505c17f8..39e580cd 100644 --- a/sway/ipc.c +++ b/sway/ipc.c @@ -11,10 +11,13 @@ #include #include #include +#include #include "ipc.h" #include "log.h" #include "config.h" #include "commands.h" +#include "list.h" +#include "stringop.h" static int ipc_socket = -1; static struct wlc_event_source *ipc_event_source = NULL; @@ -37,6 +40,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); void ipc_client_disconnect(struct ipc_client *client); void ipc_client_handle_command(struct ipc_client *client); bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); +void ipc_get_workspaces_callback(swayc_t *container, void *data); +void ipc_get_outputs_callback(swayc_t *container, void *data); + +char *json_list(list_t *items); void ipc_init(void) { ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); @@ -195,6 +202,26 @@ void ipc_client_handle_command(struct ipc_client *client) { ipc_send_reply(client, reply, (uint32_t) length); break; } + case IPC_GET_WORKSPACES: + { + list_t *workspaces = create_list(); + container_map(&root_container, ipc_get_workspaces_callback, workspaces); + char *json = json_list(workspaces); + free_flat_list(workspaces); + ipc_send_reply(client, json, strlen(json)); + free(json); + break; + } + case IPC_GET_OUTPUTS: + { + list_t *outputs = create_list(); + container_map(&root_container, ipc_get_outputs_callback, outputs); + char *json = json_list(outputs); + free_flat_list(outputs); + ipc_send_reply(client, json, strlen(json)); + free(json); + break; + } default: sway_log(L_INFO, "Unknown IPC command type %i", client->current_command); ipc_client_disconnect(client); @@ -227,3 +254,68 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay return true; } + +char *json_list(list_t *items) { + char *json_elements = join_list(items, ","); + size_t len = strlen(json_elements); + char *json = malloc(len + 3); + json[0] = '['; + memcpy(json + 1, json_elements, len); + json[len+1] = ']'; + json[len+2] = '\0'; + free(json_elements); + return json; +} + +void ipc_get_workspaces_callback(swayc_t *container, void *data) { + if (container->type == C_WORKSPACE) { + char *json = malloc(512); // Output should usually be around 180 chars + int num = isdigit(container->name[0]) ? atoi(container->name) : -1; + // TODO: escape the name (quotation marks, unicode) + sprintf(json, + "{" + "\"num\":%d," + "\"name\":\"%s\"," + "\"visible\":%s," + "\"focused\":%s," + "\"rect\":{" + "\"x\":%d," + "\"y\":%d," + "\"width\":%d," + "\"height\":%d" + "}," + "\"output\":\"%s\"," + "\"urgent\":%s" + "}", + num, container->name, container->visible ? "true" : "false", container->is_focused ? "true" : "false", + container->x, container->y, container->width, container->height, + container->parent->name, "false" // TODO: urgent hint + ); + list_add((list_t *)data, json); + } +} + +void ipc_get_outputs_callback(swayc_t *container, void *data) { + if (container->type == C_OUTPUT) { + char *json = malloc(512); // Output should usually be around 130 chars + // TODO: escape the name (quotation marks, unicode) + sprintf(json, + "{" + "\"name\":\"%s\"," + "\"active\":%s," + "\"primary\":%s," + "\"rect\":{" + "\"x\":%d," + "\"y\":%d," + "\"width\":%d," + "\"height\":%d" + "}," + "\"current_workspace\":\"%s\"" + "}", + container->name, "true", "false", // TODO: active, primary + container->x, container->y, container->width, container->height, + container->focused ? container->focused->name : "" + ); + list_add((list_t *)data, json); + } +} diff --git a/sway/stringop.c b/sway/stringop.c index 1dff97bf..c39e2c34 100644 --- a/sway/stringop.c +++ b/sway/stringop.c @@ -4,6 +4,7 @@ #include "string.h" #include "list.h" #include +#include /* Note: This returns 8 characters for trimmed_start per tab character. */ char *strip_whitespace(char *_str, int *trimmed_start) { @@ -197,3 +198,41 @@ char *join_args(char **argv, int argc) { res[len - 1] = '\0'; return res; } + +/* + * Join a list of strings, adding separator in between. Separator can be NULL. + */ +char *join_list(list_t *list, char *separator) { + if (!sway_assert(list != NULL, "list != NULL") || list->length == 0) { + return NULL; + } + + size_t len = 1; // NULL terminator + size_t sep_len = 0; + if (separator != NULL) { + sep_len = strlen(separator); + len += (list->length - 1) * sep_len; + } + + for (int i = 0; i < list->length; i++) { + len += strlen(list->items[i]); + } + + char *res = malloc(len); + + char *p = res + strlen(list->items[0]); + strcpy(res, list->items[0]); + + for (int i = 1; i < list->length; i++) { + if (sep_len) { + memcpy(p, separator, sep_len); + p += sep_len; + } + strcpy(p, list->items[i]); + p += strlen(list->items[i]); + } + + *p = '\0'; + + return res; +} -- cgit v1.2.3