]> git.sur5r.net Git - i3/i3/blob - src/ipc.c
Touch each log message and classify it as DLOG (debug), ELOG (error) or LOG (verbose)
[i3/i3] / src / ipc.c
1 /*
2  * vim:ts=8:expandtab
3  *
4  * i3 - an improved dynamic tiling window manager
5  *
6  * © 2009 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
26 #include "queue.h"
27 #include "i3/ipc.h"
28 #include "i3.h"
29 #include "util.h"
30 #include "commands.h"
31 #include "log.h"
32
33 typedef struct ipc_client {
34         int fd;
35
36         TAILQ_ENTRY(ipc_client) clients;
37 } ipc_client;
38
39 TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
40
41 /*
42  * Puts the given socket file descriptor into non-blocking mode or dies if
43  * setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our
44  * IPC model because we should by no means block the window manager.
45  *
46  */
47 static void set_nonblock(int sockfd) {
48         int flags = fcntl(sockfd, F_GETFL, 0);
49         flags |= O_NONBLOCK;
50         if (fcntl(sockfd, F_SETFL, flags) < 0)
51                 err(-1, "Could not set O_NONBLOCK");
52 }
53
54 #if 0
55 void broadcast(EV_P_ struct ev_timer *t, int revents) {
56         ipc_client *current;
57         TAILQ_FOREACH(current, &all_clients, clients) {
58                 write(current->fd, "hi there!\n", strlen("hi there!\n"));
59         }
60 }
61 #endif
62
63 /*
64  * Decides what to do with the received message.
65  *
66  * message is the raw packet, as received from the UNIX domain socket. size
67  * is the remaining size of bytes for this packet.
68  *
69  * message_size is the size of the message as the sender specified it.
70  * message_type is the type of the message as the sender specified it.
71  *
72  */
73 static void ipc_handle_message(uint8_t *message, int size,
74                                 uint32_t message_size, uint32_t message_type) {
75         DLOG("handling message of size %d\n", size);
76         DLOG("sender specified size %d\n", message_size);
77         DLOG("sender specified type %d\n", message_type);
78         DLOG("payload as a string = %s\n", message);
79
80         switch (message_type) {
81                 case I3_IPC_MESSAGE_TYPE_COMMAND: {
82                         /* To get a properly terminated buffer, we copy
83                          * message_size bytes out of the buffer */
84                         char *command = scalloc(message_size);
85                         strncpy(command, (const char*)message, message_size);
86                         parse_command(global_conn, (const char*)command);
87                         free(command);
88
89                         break;
90                 }
91                 default:
92                         DLOG("unhandled ipc message\n");
93                         break;
94         }
95 }
96
97 /*
98  * Handler for activity on a client connection, receives a message from a
99  * client.
100  *
101  * For now, the maximum message size is 2048. I’m not sure for what the
102  * IPC interface will be used in the future, thus I’m not implementing a
103  * mechanism for arbitrarily long messages, as it seems like overkill
104  * at the moment.
105  *
106  */
107 static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
108         char buf[2048];
109         int n = read(w->fd, buf, sizeof(buf));
110
111         /* On error or an empty message, we close the connection */
112         if (n <= 0) {
113 #if 0
114                 /* FIXME: I get these when closing a client socket,
115                  * therefore we just treat them as an error. Is this
116                  * correct? */
117                 if (errno == EAGAIN || errno == EWOULDBLOCK)
118                         return;
119 #endif
120
121                 /* If not, there was some kind of error. We don’t bother
122                  * and close the connection */
123                 close(w->fd);
124
125                 /* Delete the client from the list of clients */
126                 struct ipc_client *current;
127                 TAILQ_FOREACH(current, &all_clients, clients) {
128                         if (current->fd != w->fd)
129                                 continue;
130
131                         /* We can call TAILQ_REMOVE because we break out of the
132                          * TAILQ_FOREACH afterwards */
133                         TAILQ_REMOVE(&all_clients, current, clients);
134                         break;
135                 }
136
137                 ev_io_stop(EV_A_ w);
138
139                 DLOG("IPC: client disconnected\n");
140                 return;
141         }
142
143         /* Terminate the message correctly */
144         buf[n] = '\0';
145
146         /* Check if the message starts with the i3 IPC magic code */
147         if (n < strlen(I3_IPC_MAGIC)) {
148                 DLOG("IPC: message too short, ignoring\n");
149                 return;
150         }
151
152         if (strncmp(buf, I3_IPC_MAGIC, strlen(I3_IPC_MAGIC)) != 0) {
153                 DLOG("IPC: message does not start with the IPC magic\n");
154                 return;
155         }
156
157         uint8_t *message = (uint8_t*)buf;
158         while (n > 0) {
159                 DLOG("IPC: n = %d\n", n);
160                 message += strlen(I3_IPC_MAGIC);
161                 n -= strlen(I3_IPC_MAGIC);
162
163                 /* The next 32 bit after the magic are the message size */
164                 uint32_t message_size = *((uint32_t*)message);
165                 message += sizeof(uint32_t);
166                 n -= sizeof(uint32_t);
167
168                 if (message_size > n) {
169                         DLOG("IPC: Either the message size was wrong or the message was not read completely, dropping\n");
170                         return;
171                 }
172
173                 /* The last 32 bits of the header are the message type */
174                 uint32_t message_type = *((uint32_t*)message);
175                 message += sizeof(uint32_t);
176                 n -= sizeof(uint32_t);
177
178                 ipc_handle_message(message, n, message_size, message_type);
179                 n -= message_size;
180                 message += message_size;
181         }
182 }
183
184 /*
185  * Handler for activity on the listening socket, meaning that a new client
186  * has just connected and we should accept() him. Sets up the event handler
187  * for activity on the new connection and inserts the file descriptor into
188  * the list of clients.
189  *
190  */
191 void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
192         struct sockaddr_un peer;
193         socklen_t len = sizeof(struct sockaddr_un);
194         int client;
195         if ((client = accept(w->fd, (struct sockaddr*)&peer, &len)) < 0) {
196                 if (errno == EINTR)
197                         return;
198                 else perror("accept()");
199                 return;
200         }
201
202         set_nonblock(client);
203
204         struct ev_io *package = calloc(sizeof(struct ev_io), 1);
205         ev_io_init(package, ipc_receive_message, client, EV_READ);
206         ev_io_start(EV_A_ package);
207
208         DLOG("IPC: new client connected\n");
209
210         struct ipc_client *new = calloc(sizeof(struct ipc_client), 1);
211         new->fd = client;
212
213         TAILQ_INSERT_TAIL(&all_clients, new, clients);
214 }
215
216 /*
217  * Creates the UNIX domain socket at the given path, sets it to non-blocking
218  * mode, bind()s and listen()s on it.
219  *
220  */
221 int ipc_create_socket(const char *filename) {
222         int sockfd;
223
224         /* Unlink the unix domain socket before */
225         unlink(filename);
226
227         if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
228                 perror("socket()");
229                 return -1;
230         }
231
232         struct sockaddr_un addr;
233         memset(&addr, 0, sizeof(struct sockaddr_un));
234         addr.sun_family = AF_LOCAL;
235         strcpy(addr.sun_path, filename);
236         if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) {
237                 perror("bind()");
238                 return -1;
239         }
240
241         set_nonblock(sockfd);
242
243         if (listen(sockfd, 5) < 0) {
244                 perror("listen()");
245                 return -1;
246         }
247
248         return sockfd;
249 }