diff options
-rw-r--r-- | include/ipc.h | 11 | ||||
-rw-r--r-- | sway/ipc.c | 80 |
2 files changed, 84 insertions, 7 deletions
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 @@ | |||
1 | #ifndef _SWAY_IPC_H | 1 | #ifndef _SWAY_IPC_H |
2 | #define _SWAY_IPC_H | 2 | #define _SWAY_IPC_H |
3 | 3 | ||
4 | enum ipc_command_type { | ||
5 | IPC_COMMAND = 0, | ||
6 | IPC_GET_WORKSPACES = 1, | ||
7 | IPC_SUBSCRIBE = 2, | ||
8 | IPC_GET_OUTPUTS = 3, | ||
9 | IPC_GET_TREE = 4, | ||
10 | IPC_GET_MARKS = 5, | ||
11 | IPC_GET_BAR_CONFIG = 6, | ||
12 | IPC_GET_VERSION = 7, | ||
13 | }; | ||
14 | |||
4 | void init_ipc(void); | 15 | void init_ipc(void); |
5 | 16 | ||
6 | #endif | 17 | #endif |
@@ -2,15 +2,23 @@ | |||
2 | #include <string.h> | 2 | #include <string.h> |
3 | #include <sys/socket.h> | 3 | #include <sys/socket.h> |
4 | #include <sys/un.h> | 4 | #include <sys/un.h> |
5 | #include <stdbool.h> | ||
5 | #include <wlc/wlc.h> | 6 | #include <wlc/wlc.h> |
6 | #include <unistd.h> | 7 | #include <unistd.h> |
8 | #include <stdlib.h> | ||
9 | #include "ipc.h" | ||
7 | #include "log.h" | 10 | #include "log.h" |
8 | #include "config.h" | 11 | #include "config.h" |
9 | #include "commands.h" | 12 | #include "commands.h" |
10 | 13 | ||
11 | static int ipc_socket = -1; | 14 | static int ipc_socket = -1; |
12 | 15 | ||
16 | static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'}; | ||
17 | |||
13 | int ipc_handle_connection(int fd, uint32_t mask, void *data); | 18 | int ipc_handle_connection(int fd, uint32_t mask, void *data); |
19 | size_t ipc_handle_command(char **reply_data, char *data, ssize_t length); | ||
20 | size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length); | ||
21 | |||
14 | 22 | ||
15 | void init_ipc() { | 23 | void init_ipc() { |
16 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); | 24 | ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); |
@@ -36,7 +44,8 @@ void init_ipc() { | |||
36 | wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); | 44 | wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL); |
37 | } | 45 | } |
38 | 46 | ||
39 | int ipc_handle_connection(int /*fd*/, uint32_t /*mask*/, void */*data*/) { | 47 | int ipc_handle_connection(int fd, uint32_t mask, void *data) { |
48 | sway_log(L_DEBUG, "Event on IPC listening socket"); | ||
40 | int client_socket = accept(ipc_socket, NULL, NULL); | 49 | int client_socket = accept(ipc_socket, NULL, NULL); |
41 | if (client_socket == -1) { | 50 | if (client_socket == -1) { |
42 | char error[256]; | 51 | char error[256]; |
@@ -46,7 +55,9 @@ int ipc_handle_connection(int /*fd*/, uint32_t /*mask*/, void */*data*/) { | |||
46 | } | 55 | } |
47 | 56 | ||
48 | char buf[1024]; | 57 | char buf[1024]; |
49 | if (recv(client_socket, buf, sizeof(buf), 0) == -1) { | 58 | // Leave one byte of space at the end of the buffer for NULL terminator |
59 | ssize_t received = recv(client_socket, buf, sizeof(buf) - 1, 0); | ||
60 | if (received == -1) { | ||
50 | char error[256]; | 61 | char error[256]; |
51 | strerror_r(errno, error, sizeof(error)); | 62 | strerror_r(errno, error, sizeof(error)); |
52 | sway_log(L_INFO, "Unable to receive from IPC client: %s", error); | 63 | 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*/) { | |||
54 | return 0; | 65 | return 0; |
55 | } | 66 | } |
56 | 67 | ||
57 | sway_log(L_INFO, "Executing IPC command: %s", buf); | 68 | char *reply_buf; |
58 | 69 | size_t reply_length = ipc_handle_command(&reply_buf, buf, received); | |
59 | bool success = handle_command(config, buf); | 70 | sway_log(L_DEBUG, "IPC reply: %s", reply_buf); |
60 | snprintf(buf, sizeof(buf), "{\"success\":%s}\n", success ? "true" : "false"); | ||
61 | 71 | ||
62 | if (send(client_socket, buf, strlen(buf), 0) == -1) { | 72 | if (send(client_socket, reply_buf, reply_length, 0) == -1) { |
63 | char error[256]; | 73 | char error[256]; |
64 | strerror_r(errno, error, sizeof(error)); | 74 | strerror_r(errno, error, sizeof(error)); |
65 | sway_log(L_INFO, "Unable to send to IPC client: %s", error); | 75 | sway_log(L_INFO, "Unable to send to IPC client: %s", error); |
66 | } | 76 | } |
67 | 77 | ||
78 | free(reply_buf); | ||
68 | close(client_socket); | 79 | close(client_socket); |
69 | return 0; | 80 | return 0; |
70 | } | 81 | } |
82 | |||
83 | static const int ipc_header_size = sizeof(ipc_magic)+8; | ||
84 | |||
85 | size_t ipc_handle_command(char **reply_data, char *data, ssize_t length) { | ||
86 | // See https://i3wm.org/docs/ipc.html for protocol details | ||
87 | |||
88 | if (length < ipc_header_size) { | ||
89 | sway_log(L_DEBUG, "IPC data too short"); | ||
90 | return false; | ||
91 | } | ||
92 | |||
93 | if (memcmp(data, ipc_magic, sizeof(ipc_magic)) != 0) { | ||
94 | sway_log(L_DEBUG, "IPC header check failed"); | ||
95 | return false; | ||
96 | } | ||
97 | |||
98 | uint32_t payload_length = *(uint32_t *)&data[sizeof(ipc_magic)]; | ||
99 | uint32_t command_type = *(uint32_t *)&data[sizeof(ipc_magic)+4]; | ||
100 | |||
101 | if (length != payload_length + ipc_header_size) { | ||
102 | // TODO: try to read enough data | ||
103 | sway_log(L_DEBUG, "IPC payload size mismatch"); | ||
104 | return false; | ||
105 | } | ||
106 | |||
107 | switch (command_type) { | ||
108 | case IPC_COMMAND: | ||
109 | { | ||
110 | char *cmd = &data[ipc_header_size]; | ||
111 | data[ipc_header_size + payload_length] = '\0'; | ||
112 | bool success = handle_command(config, cmd); | ||
113 | char buf[64]; | ||
114 | int length = snprintf(buf, sizeof(buf), "{\"success\":%s}", success ? "true" : "false"); | ||
115 | return ipc_format_reply(reply_data, IPC_COMMAND, buf, (uint32_t) length); | ||
116 | } | ||
117 | default: | ||
118 | sway_log(L_INFO, "Unknown IPC command type %i", command_type); | ||
119 | return false; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | size_t ipc_format_reply(char **data, enum ipc_command_type command_type, const char *payload, uint32_t payload_length) { | ||
124 | assert(data); | ||
125 | assert(payload); | ||
126 | |||
127 | size_t length = ipc_header_size + payload_length; | ||
128 | *data = malloc(length); | ||
129 | |||
130 | memcpy(*data, ipc_magic, sizeof(ipc_magic)); | ||
131 | *(uint32_t *)&((*data)[sizeof(ipc_magic)]) = payload_length; | ||
132 | *(uint32_t *)&((*data)[sizeof(ipc_magic)+4]) = command_type; | ||
133 | memcpy(&(*data)[ipc_header_size], payload, payload_length); | ||
134 | |||
135 | return length; | ||
136 | } | ||