summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Drew DeVault <sir@cmpwn.com>2015-08-20 09:34:06 -0400
committerLibravatar Drew DeVault <sir@cmpwn.com>2015-08-20 09:34:06 -0400
commit84f01a67bdaef4dbff787caa4b076ed60e257c51 (patch)
tree0d264e5a6232071a8277c59216f85aac64583459
parentAdd command line parsing (diff)
parentadded missing header file (diff)
downloadsway-84f01a67bdaef4dbff787caa4b076ed60e257c51.tar.gz
sway-84f01a67bdaef4dbff787caa4b076ed60e257c51.tar.zst
sway-84f01a67bdaef4dbff787caa4b076ed60e257c51.zip
Merge pull request #75 from minus7/ipc
i3 IPC
-rw-r--r--include/ipc.h18
-rw-r--r--include/log.h1
-rw-r--r--include/sway.h6
-rw-r--r--sway/commands.c3
-rw-r--r--sway/ipc.c229
-rw-r--r--sway/log.c33
-rw-r--r--sway/main.c18
7 files changed, 305 insertions, 3 deletions
diff --git a/include/ipc.h b/include/ipc.h
new file mode 100644
index 00000000..0b6441f6
--- /dev/null
+++ b/include/ipc.h
@@ -0,0 +1,18 @@
1#ifndef _SWAY_IPC_H
2#define _SWAY_IPC_H
3
4enum 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
15void ipc_init(void);
16void ipc_terminate(void);
17
18#endif
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 {
13void init_log(int verbosity); 13void init_log(int verbosity);
14void sway_log_colors(int mode); 14void sway_log_colors(int mode);
15void sway_log(int verbosity, const char* format, ...) __attribute__((format(printf,2,3))); 15void sway_log(int verbosity, const char* format, ...) __attribute__((format(printf,2,3)));
16void sway_log_errno(int verbosity, char* format, ...) __attribute__((format(printf,2,3)));
16void sway_abort(const char* format, ...) __attribute__((format(printf,1,2))); 17void sway_abort(const char* format, ...) __attribute__((format(printf,1,2)));
17bool sway_assert(bool condition, const char* format, ...) __attribute__((format(printf,2,3))); 18bool sway_assert(bool condition, const char* format, ...) __attribute__((format(printf,2,3)));
18 19
diff --git a/include/sway.h b/include/sway.h
new file mode 100644
index 00000000..6499c81d
--- /dev/null
+++ b/include/sway.h
@@ -0,0 +1,6 @@
1#ifndef _SWAY_SWAY_H
2#define _SWAY_SWAY_H
3
4void sway_terminate(void);
5
6#endif
diff --git a/sway/commands.c b/sway/commands.c
index 803d9a21..644b8005 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -15,6 +15,7 @@
15#include "commands.h" 15#include "commands.h"
16#include "container.h" 16#include "container.h"
17#include "handlers.h" 17#include "handlers.h"
18#include "sway.h"
18 19
19struct modifier_key { 20struct modifier_key {
20 char *name; 21 char *name;
@@ -186,7 +187,7 @@ static bool cmd_exit(struct sway_config *config, int argc, char **argv) {
186 } 187 }
187 // Close all views 188 // Close all views
188 container_map(&root_container, kill_views, NULL); 189 container_map(&root_container, kill_views, NULL);
189 exit(0); 190 sway_terminate();
190 return true; 191 return true;
191} 192}
192 193
diff --git a/sway/ipc.c b/sway/ipc.c
new file mode 100644
index 00000000..505c17f8
--- /dev/null
+++ b/sway/ipc.c
@@ -0,0 +1,229 @@
1// See https://i3wm.org/docs/ipc.html for protocol information
2
3#include <errno.h>
4#include <string.h>
5#include <sys/socket.h>
6#include <sys/un.h>
7#include <stdbool.h>
8#include <wlc/wlc.h>
9#include <unistd.h>
10#include <stdlib.h>
11#include <stropts.h>
12#include <sys/ioctl.h>
13#include <fcntl.h>
14#include "ipc.h"
15#include "log.h"
16#include "config.h"
17#include "commands.h"
18
19static int ipc_socket = -1;
20static struct wlc_event_source *ipc_event_source = NULL;
21static struct sockaddr_un ipc_sockaddr = {
22 .sun_family = AF_UNIX,
23 .sun_path = "/tmp/sway-ipc.sock"
24};
25
26static const char ipc_magic[] = {'i', '3', '-', 'i', 'p', 'c'};
27
28struct ipc_client {
29 struct wlc_event_source *event_source;
30 int fd;
31 uint32_t payload_length;
32 enum ipc_command_type current_command;
33};
34
35int ipc_handle_connection(int fd, uint32_t mask, void *data);
36int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data);
37void ipc_client_disconnect(struct ipc_client *client);
38void ipc_client_handle_command(struct ipc_client *client);
39bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length);
40
41void ipc_init(void) {
42 ipc_socket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
43 if (ipc_socket == -1) {
44 sway_abort("Unable to create IPC socket");
45 }
46
47 if (getenv("SWAYSOCK") != NULL) {
48 strncpy(ipc_sockaddr.sun_path, getenv("SWAYSOCK"), sizeof(ipc_sockaddr.sun_path));
49 }
50
51 unlink(ipc_sockaddr.sun_path);
52 if (bind(ipc_socket, (struct sockaddr *)&ipc_sockaddr, sizeof(ipc_sockaddr)) == -1) {
53 sway_abort("Unable to bind IPC socket");
54 }
55
56 if (listen(ipc_socket, 3) == -1) {
57 sway_abort("Unable to listen on IPC socket");
58 }
59
60 // Set i3 IPC socket path so that i3-msg works out of the box
61 setenv("I3SOCK", ipc_sockaddr.sun_path, 1);
62
63 ipc_event_source = wlc_event_loop_add_fd(ipc_socket, WLC_EVENT_READABLE, ipc_handle_connection, NULL);
64}
65
66void ipc_terminate(void) {
67 if (ipc_event_source) {
68 wlc_event_source_remove(ipc_event_source);
69 }
70 close(ipc_socket);
71 unlink(ipc_sockaddr.sun_path);
72}
73
74int ipc_handle_connection(int fd, uint32_t mask, void *data) {
75 (void) fd; (void) data;
76 sway_log(L_DEBUG, "Event on IPC listening socket");
77 assert(mask == WLC_EVENT_READABLE);
78
79 int client_fd = accept(ipc_socket, NULL, NULL);
80 if (client_fd == -1) {
81 sway_log_errno(L_INFO, "Unable to accept IPC client connection");
82 return 0;
83 }
84
85 int flags;
86 if ((flags=fcntl(client_fd, F_GETFD)) == -1 || fcntl(client_fd, F_SETFD, flags|FD_CLOEXEC) == -1) {
87 sway_log_errno(L_INFO, "Unable to set CLOEXEC on IPC client socket");
88 return 0;
89 }
90
91 struct ipc_client* client = malloc(sizeof(struct ipc_client));
92 client->payload_length = 0;
93 client->fd = client_fd;
94 client->event_source = wlc_event_loop_add_fd(client_fd, WLC_EVENT_READABLE, ipc_client_handle_readable, client);
95
96 return 0;
97}
98
99static const int ipc_header_size = sizeof(ipc_magic)+8;
100
101int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
102 struct ipc_client *client = data;
103 sway_log(L_DEBUG, "Event on IPC client socket %d", client_fd);
104
105 if (mask & WLC_EVENT_ERROR) {
106 sway_log(L_INFO, "IPC Client socket error, removing client");
107 ipc_client_disconnect(client);
108 return 0;
109 }
110
111 if (mask & WLC_EVENT_HANGUP) {
112 ipc_client_disconnect(client);
113 return 0;
114 }
115
116 int read_available;
117 ioctl(client_fd, FIONREAD, &read_available);
118
119 // Wait for the rest of the command payload in case the header has already been read
120 if (client->payload_length > 0) {
121 if (read_available >= client->payload_length) {
122 ipc_client_handle_command(client);
123 }
124 else {
125 sway_log(L_DEBUG, "Too little data to read payload on IPC Client socket, waiting for more (%d < %d)", read_available, client->payload_length);
126 }
127 return 0;
128 }
129
130 if (read_available < ipc_header_size) {
131 sway_log(L_DEBUG, "Too little data to read header on IPC Client socket, waiting for more (%d < %d)", read_available, ipc_header_size);
132 return 0;
133 }
134
135 char buf[ipc_header_size];
136 ssize_t received = recv(client_fd, buf, ipc_header_size, 0);
137 if (received == -1) {
138 sway_log_errno(L_INFO, "Unable to receive header from IPC client");
139 ipc_client_disconnect(client);
140 return 0;
141 }
142
143 if (memcmp(buf, ipc_magic, sizeof(ipc_magic)) != 0) {
144 sway_log(L_DEBUG, "IPC header check failed");
145 ipc_client_disconnect(client);
146 return 0;
147 }
148
149 client->payload_length = *(uint32_t *)&buf[sizeof(ipc_magic)];
150 client->current_command = (enum ipc_command_type) *(uint32_t *)&buf[sizeof(ipc_magic)+4];
151
152 if (read_available - received >= client->payload_length) {
153 ipc_client_handle_command(client);
154 }
155
156 return 0;
157}
158
159void ipc_client_disconnect(struct ipc_client *client)
160{
161 if (!sway_assert(client != NULL, "client != NULL")) {
162 return;
163 }
164
165 sway_log(L_INFO, "IPC Client %d disconnected", client->fd);
166 wlc_event_source_remove(client->event_source);
167 close(client->fd);
168 free(client);
169}
170
171void ipc_client_handle_command(struct ipc_client *client) {
172 if (!sway_assert(client != NULL, "client != NULL")) {
173 return;
174 }
175
176 char buf[client->payload_length + 1];
177 if (client->payload_length > 0)
178 {
179 ssize_t received = recv(client->fd, buf, client->payload_length, 0);
180 if (received == -1)
181 {
182 sway_log_errno(L_INFO, "Unable to receive payload from IPC client");
183 ipc_client_disconnect(client);
184 return;
185 }
186 }
187
188 switch (client->current_command) {
189 case IPC_COMMAND:
190 {
191 buf[client->payload_length] = '\0';
192 bool success = handle_command(config, buf);
193 char reply[64];
194 int length = snprintf(reply, sizeof(reply), "{\"success\":%s}", success ? "true" : "false");
195 ipc_send_reply(client, reply, (uint32_t) length);
196 break;
197 }
198 default:
199 sway_log(L_INFO, "Unknown IPC command type %i", client->current_command);
200 ipc_client_disconnect(client);
201 break;
202 }
203
204 client->payload_length = 0;
205}
206
207bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) {
208 assert(payload);
209
210 char data[ipc_header_size];
211
212 memcpy(data, ipc_magic, sizeof(ipc_magic));
213 *(uint32_t *)&(data[sizeof(ipc_magic)]) = payload_length;
214 *(uint32_t *)&(data[sizeof(ipc_magic)+4]) = client->current_command;
215
216 if (write(client->fd, data, ipc_header_size) == -1) {
217 sway_log_errno(L_INFO, "Unable to send header to IPC client");
218 ipc_client_disconnect(client);
219 return false;
220 }
221
222 if (write(client->fd, payload, payload_length) == -1) {
223 sway_log_errno(L_INFO, "Unable to send payload to IPC client");
224 ipc_client_disconnect(client);
225 return false;
226 }
227
228 return true;
229}
diff --git a/sway/log.c b/sway/log.c
index 2d633abb..6e01421b 100644
--- a/sway/log.c
+++ b/sway/log.c
@@ -1,10 +1,13 @@
1#include "log.h" 1#include "log.h"
2#include "sway.h"
2#include <stdarg.h> 3#include <stdarg.h>
3#include <stdio.h> 4#include <stdio.h>
4#include <stdlib.h> 5#include <stdlib.h>
5#include <fcntl.h> 6#include <fcntl.h>
6#include <unistd.h> 7#include <unistd.h>
7#include <signal.h> 8#include <signal.h>
9#include <errno.h>
10#include <string.h>
8 11
9int colored = 1; 12int colored = 1;
10int v = 0; 13int v = 0;
@@ -40,7 +43,7 @@ void sway_abort(const char *format, ...) {
40 vfprintf(stderr, format, args); 43 vfprintf(stderr, format, args);
41 va_end(args); 44 va_end(args);
42 fprintf(stderr, "\n"); 45 fprintf(stderr, "\n");
43 exit(1); 46 sway_terminate();
44} 47}
45 48
46void sway_log(int verbosity, const char* format, ...) { 49void sway_log(int verbosity, const char* format, ...) {
@@ -66,6 +69,34 @@ void sway_log(int verbosity, const char* format, ...) {
66 } 69 }
67} 70}
68 71
72void sway_log_errno(int verbosity, char* format, ...) {
73 if (verbosity <= v) {
74 int c = verbosity;
75 if (c > sizeof(verbosity_colors) / sizeof(char *)) {
76 c = sizeof(verbosity_colors) / sizeof(char *) - 1;
77 }
78
79 if (colored) {
80 fprintf(stderr, verbosity_colors[c]);
81 }
82
83 va_list args;
84 va_start(args, format);
85 vfprintf(stderr, format, args);
86 va_end(args);
87
88 fprintf(stderr, ": ");
89 char error[256];
90 strerror_r(errno, error, sizeof(error));
91 fprintf(stderr, error);
92
93 if (colored) {
94 fprintf(stderr, "\x1B[0m");
95 }
96 fprintf(stderr, "\n");
97 }
98}
99
69bool sway_assert(bool condition, const char* format, ...) { 100bool sway_assert(bool condition, const char* format, ...) {
70 if (condition) { 101 if (condition) {
71 return true; 102 return true;
diff --git a/sway/main.c b/sway/main.c
index 4a7e13c1..f37f086d 100644
--- a/sway/main.c
+++ b/sway/main.c
@@ -9,6 +9,15 @@
9#include "config.h" 9#include "config.h"
10#include "log.h" 10#include "log.h"
11#include "handlers.h" 11#include "handlers.h"
12#include "ipc.h"
13#include "sway.h"
14
15static bool terminate_request = false;
16
17void sway_terminate(void) {
18 terminate_request = true;
19 wlc_terminate();
20}
12 21
13static void sigchld_handle(int signal); 22static void sigchld_handle(int signal);
14 23
@@ -99,11 +108,18 @@ int main(int argc, char **argv) {
99 free(config_path); 108 free(config_path);
100 } 109 }
101 110
102 wlc_run(); 111 ipc_init();
112
113 if (!terminate_request) {
114 wlc_run();
115 }
116
103 if (devnull) { 117 if (devnull) {
104 fclose(devnull); 118 fclose(devnull);
105 } 119 }
106 120
121 ipc_terminate();
122
107 return 0; 123 return 0;
108} 124}
109 125