]> git.sur5r.net Git - i3/i3/blob - src/ipc.c
ipc: implement GET_WORKSPACES message type
[i3/i3] / src / ipc.c
1 /*
2  * vim:ts=8:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  *
6  * © 2009-2010 Michael Stapelberg and contributors
7  *
8  * See file LICENSE for license information.
9  *
10  * ipc.c: Everything about the UNIX domain sockets for IPC
11  *
12  */
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <err.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <ev.h>
25 #include <yajl/yajl_gen.h>
26
27 #include "queue.h"
28 #include "i3/ipc.h"
29 #include "i3.h"
30 #include "util.h"
31 #include "commands.h"
32 #include "log.h"
33 #include "table.h"
34
35 /* Shorter names for all those yajl_gen_* functions */
36 #define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
37 #define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
38
39 typedef struct ipc_client {
40         int fd;
41
42         TAILQ_ENTRY(ipc_client) clients;
43 } ipc_client;
44
45 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
46
47 /*
48  * Puts the given socket file descriptor into non-blocking mode or dies if
49  * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our
50  * IPC model because we should by no means block the window manager.
51  *
52  */
53 static void set_nonblock(int sockfd) {
54         int flags = fcntl(sockfd, F_GETFL, 0);
55         flags |= O_NONBLOCK;
56         if (fcntl(sockfd, F_SETFL, flags) < 0)
57                 err(-1, "Could not set O_NONBLOCK");
58 }
59
60 #if 0
61 void broadcast(EV_P_ struct ev_timer *t, int revents) {
62         ipc_client *current;
63         TAILQ_FOREACH(current, &all_clients, clients) {
64                 write(current->fd, "hi there!\n", strlen("hi there!\n"));
65         }
66 }
67 #endif
68
69 static void ipc_send_message(int fd, const unsigned char *payload,
70                              int message_type, int message_size) {
71         int buffer_size = strlen("i3-ipc") + sizeof(uint32_t) +
72                           sizeof(uint32_t) + message_size;
73         char msg[buffer_size];
74         char *walk = msg;
75
76         strcpy(walk, "i3-ipc");
77         walk += strlen("i3-ipc");
78         memcpy(walk, &message_size, sizeof(uint32_t));
79         walk += sizeof(uint32_t);
80         memcpy(walk, &message_type, sizeof(uint32_t));
81         walk += sizeof(uint32_t);
82         memcpy(walk, payload, message_size);
83
84         int sent_bytes = 0;
85         int bytes_to_go = buffer_size;
86         while (sent_bytes < bytes_to_go) {
87                 int n = write(fd, msg + sent_bytes, bytes_to_go);
88                 if (n == -1)
89                         err(EXIT_FAILURE, "write() failed");
90
91                 sent_bytes += n;
92                 bytes_to_go -= n;
93         }
94 }
95
96 /*
97  * Formats the reply message for a GET_WORKSPACES request and sends it to the
98  * client
99  *
100  */
101 static void ipc_send_workspaces(int fd) {
102         Workspace *ws;
103
104         yajl_gen gen = yajl_gen_alloc(NULL, NULL);
105         y(array_open);
106
107         TAILQ_FOREACH(ws, workspaces, workspaces) {
108                 if (ws->output == NULL)
109                         continue;
110
111                 y(map_open);
112                 ystr("num");
113                 y(integer, ws->num);
114
115                 ystr("name");
116                 ystr(ws->utf8_name);
117
118                 ystr("rect");
119                 y(map_open);
120                 ystr("x");
121                 y(integer, ws->rect.x);
122                 ystr("y");
123                 y(integer, ws->rect.y);
124                 ystr("width");
125                 y(integer, ws->rect.width);
126                 ystr("height");
127                 y(integer, ws->rect.height);
128                 y(map_close);
129
130                 ystr("output");
131                 ystr(ws->output->name);
132
133                 y(map_close);
134         }
135
136         y(array_close);
137
138         const unsigned char *payload;
139         unsigned int length;
140         y(get_buf, &payload, &length);
141
142         ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_WORKSPACES, length);
143         y(free);
144 }
145
146 /*
147  * Decides what to do with the received message.
148  *
149  * message is the raw packet, as received from the UNIX domain socket. size
150  * is the remaining size of bytes for this packet.
151  *
152  * message_size is the size of the message as the sender specified it.
153  * message_type is the type of the message as the sender specified it.
154  *
155  */
156 static void ipc_handle_message(int fd, uint8_t *message, int size,
157                                uint32_t message_size, uint32_t message_type) {
158         DLOG("handling message of size %d\n", size);
159         DLOG("sender specified size %d\n", message_size);
160         DLOG("sender specified type %d\n", message_type);
161         DLOG("payload as a string = %s\n", message);
162
163         switch (message_type) {
164                 case I3_IPC_MESSAGE_TYPE_COMMAND: {
165                         /* To get a properly terminated buffer, we copy
166                          * message_size bytes out of the buffer */
167                         char *command = scalloc(message_size);
168                         strncpy(command, (const char*)message, message_size);
169                         parse_command(global_conn, (const char*)command);
170                         free(command);
171
172                         break;
173                 }
174                 case I3_IPC_MESSAGE_TYPE_GET_WORKSPACES:
175                         ipc_send_workspaces(fd);
176                         break;
177                 default:
178                         DLOG("unhandled ipc message\n");
179                         break;
180         }
181 }
182
183 /*
184  * Handler for activity on a client connection, receives a message from a
185  * client.
186  *
187  * For now, the maximum message size is 2048. I’m not sure for what the
188  * IPC interface will be used in the future, thus I’m not implementing a
189  * mechanism for arbitrarily long messages, as it seems like overkill
190  * at the moment.
191  *
192  */
193 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
194         char buf[2048];
195         int n = read(w->fd, buf, sizeof(buf));
196
197         /* On error or an empty message, we close the connection */
198         if (n <= 0) {
199 #if 0
200                 /* FIXME: I get these when closing a client socket,
201                  * therefore we just treat them as an error. Is this
202                  * correct? */
203                 if (errno == EAGAIN || errno == EWOULDBLOCK)
204                         return;
205 #endif
206
207                 /* If not, there was some kind of error. We don’t bother
208                  * and close the connection */
209                 close(w->fd);
210
211                 /* Delete the client from the list of clients */
212                 struct ipc_client *current;
213                 TAILQ_FOREACH(current, &all_clients, clients) {
214                         if (current->fd != w->fd)
215                                 continue;
216
217                         /* We can call TAILQ_REMOVE because we break out of the
218                          * TAILQ_FOREACH afterwards */
219                         TAILQ_REMOVE(&all_clients, current, clients);
220                         break;
221                 }
222
223                 ev_io_stop(EV_A_ w);
224
225                 DLOG("IPC: client disconnected\n");
226                 return;
227         }
228
229         /* Terminate the message correctly */
230         buf[n] = '\0';
231
232         /* Check if the message starts with the i3 IPC magic code */
233         if (n < strlen(I3_IPC_MAGIC)) {
234                 DLOG("IPC: message too short, ignoring\n");
235                 return;
236         }
237
238         if (strncmp(buf, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) {
239                 DLOG("IPC: message does not start with the IPC magic\n");
240                 return;
241         }
242
243         uint8_t *message = (uint8_t*)buf;
244         while (n > 0) {
245                 DLOG("IPC: n = %d\n", n);
246                 message += strlen(I3_IPC_MAGIC);
247                 n -= strlen(I3_IPC_MAGIC);
248
249                 /* The next 32 bit after the magic are the message size */
250                 uint32_t message_size = *((uint32_t*)message);
251                 message += sizeof(uint32_t);
252                 n -= sizeof(uint32_t);
253
254                 if (message_size > n) {
255                         DLOG("IPC: Either the message size was wrong or the message was not read completely, dropping\n");
256                         return;
257                 }
258
259                 /* The last 32 bits of the header are the message type */
260                 uint32_t message_type = *((uint32_t*)message);
261                 message += sizeof(uint32_t);
262                 n -= sizeof(uint32_t);
263
264                 ipc_handle_message(w->fd, message, n, message_size, message_type);
265                 n -= message_size;
266                 message += message_size;
267         }
268 }
269
270 /*
271  * Handler for activity on the listening socket, meaning that a new client
272  * has just connected and we should accept() him. Sets up the event handler
273  * for activity on the new connection and inserts the file descriptor into
274  * the list of clients.
275  *
276  */
277 void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
278         struct sockaddr_un peer;
279         socklen_t len = sizeof(struct sockaddr_un);
280         int client;
281         if ((client = accept(w->fd, (struct sockaddr*)&peer, &len)) < 0) {
282                 if (errno == EINTR)
283                         return;
284                 else perror("accept()");
285                 return;
286         }
287
288         set_nonblock(client);
289
290         struct ev_io *package = scalloc(sizeof(struct ev_io));
291         ev_io_init(package, ipc_receive_message, client, EV_READ);
292         ev_io_start(EV_A_ package);
293
294         DLOG("IPC: new client connected\n");
295
296         struct ipc_client *new = scalloc(sizeof(struct ipc_client));
297         new->fd = client;
298
299         TAILQ_INSERT_TAIL(&all_clients, new, clients);
300 }
301
302 /*
303  * Creates the UNIX domain socket at the given path, sets it to non-blocking
304  * mode, bind()s and listen()s on it.
305  *
306  */
307 int ipc_create_socket(const char *filename) {
308         int sockfd;
309
310         /* Unlink the unix domain socket before */
311         unlink(filename);
312
313         if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
314                 perror("socket()");
315                 return -1;
316         }
317
318         (void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
319
320         struct sockaddr_un addr;
321         memset(&addr, 0, sizeof(struct sockaddr_un));
322         addr.sun_family = AF_LOCAL;
323         strcpy(addr.sun_path, filename);
324         if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) {
325                 perror("bind()");
326                 return -1;
327         }
328
329         set_nonblock(sockfd);
330
331         if (listen(sockfd, 5) < 0) {
332                 perror("listen()");
333                 return -1;
334         }
335
336         return sockfd;
337 }