ubusd: fix systemd socket activation support
[project/ubus.git] / ubusd.c
1 /*
2 * Copyright (C) 2011-2014 Felix Fietkau <nbd@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #include <sys/socket.h>
15 #include <sys/stat.h>
16 #include <sys/uio.h>
17 #ifdef FreeBSD
18 #include <sys/param.h>
19 #endif
20 #include <syslog.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #ifdef ENABLE_SYSTEMD
26 #include <systemd/sd-daemon.h>
27 #endif
28
29 #include <libubox/blob.h>
30 #include <libubox/uloop.h>
31 #include <libubox/usock.h>
32 #include <libubox/list.h>
33
34 #include "ubusd.h"
35
36 static struct ubus_msg_buf *ubus_msg_ref(struct ubus_msg_buf *ub)
37 {
38 if (ub->refcount == ~0)
39 return ubus_msg_new(ub->data, ub->len, false);
40
41 ub->refcount++;
42 return ub;
43 }
44
45 struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared)
46 {
47 struct ubus_msg_buf *ub;
48 int buflen = sizeof(*ub);
49
50 if (!shared)
51 buflen += len;
52
53 ub = calloc(1, buflen);
54 if (!ub)
55 return NULL;
56
57 ub->fd = -1;
58
59 if (shared) {
60 ub->refcount = ~0;
61 ub->data = data;
62 } else {
63 ub->refcount = 1;
64 ub->data = (void *) (ub + 1);
65 if (data)
66 memcpy(ub + 1, data, len);
67 }
68
69 ub->len = len;
70 return ub;
71 }
72
73 void ubus_msg_free(struct ubus_msg_buf *ub)
74 {
75 switch (ub->refcount) {
76 case 1:
77 case ~0:
78 if (ub->fd >= 0)
79 close(ub->fd);
80
81 free(ub);
82 break;
83 default:
84 ub->refcount--;
85 break;
86 }
87 }
88
89 static int ubus_msg_writev(int fd, struct ubus_msg_buf *ub, int offset)
90 {
91 static struct iovec iov[2];
92 static struct {
93 struct cmsghdr h;
94 int fd;
95 } fd_buf = {
96 .h = {
97 .cmsg_len = sizeof(fd_buf),
98 .cmsg_level = SOL_SOCKET,
99 .cmsg_type = SCM_RIGHTS,
100 },
101 };
102 struct msghdr msghdr = {
103 .msg_iov = iov,
104 .msg_iovlen = ARRAY_SIZE(iov),
105 .msg_control = &fd_buf,
106 .msg_controllen = sizeof(fd_buf),
107 };
108
109 fd_buf.fd = ub->fd;
110 if (ub->fd < 0) {
111 msghdr.msg_control = NULL;
112 msghdr.msg_controllen = 0;
113 }
114
115 if (offset < sizeof(ub->hdr)) {
116 struct ubus_msghdr hdr;
117
118 hdr.version = ub->hdr.version;
119 hdr.type = ub->hdr.type;
120 hdr.seq = cpu_to_be16(ub->hdr.seq);
121 hdr.peer = cpu_to_be32(ub->hdr.peer);
122
123 iov[0].iov_base = ((char *) &hdr) + offset;
124 iov[0].iov_len = sizeof(hdr) - offset;
125 iov[1].iov_base = (char *) ub->data;
126 iov[1].iov_len = ub->len;
127
128 return sendmsg(fd, &msghdr, 0);
129 } else {
130 offset -= sizeof(ub->hdr);
131 return write(fd, ((char *) ub->data) + offset, ub->len - offset);
132 }
133 }
134
135 static void ubus_msg_enqueue(struct ubus_client *cl, struct ubus_msg_buf *ub)
136 {
137 if (cl->tx_queue[cl->txq_tail])
138 return;
139
140 cl->tx_queue[cl->txq_tail] = ubus_msg_ref(ub);
141 cl->txq_tail = (cl->txq_tail + 1) % ARRAY_SIZE(cl->tx_queue);
142 }
143
144 /* takes the msgbuf reference */
145 void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free)
146 {
147 int written;
148
149 if (ub->hdr.type != UBUS_MSG_MONITOR)
150 ubusd_monitor_message(cl, ub, true);
151
152 if (!cl->tx_queue[cl->txq_cur]) {
153 written = ubus_msg_writev(cl->sock.fd, ub, 0);
154 if (written >= ub->len + sizeof(ub->hdr))
155 goto out;
156
157 if (written < 0)
158 written = 0;
159
160 cl->txq_ofs = written;
161
162 /* get an event once we can write to the socket again */
163 uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_WRITE | ULOOP_EDGE_TRIGGER);
164 }
165 ubus_msg_enqueue(cl, ub);
166
167 out:
168 if (free)
169 ubus_msg_free(ub);
170 }
171
172 static struct ubus_msg_buf *ubus_msg_head(struct ubus_client *cl)
173 {
174 return cl->tx_queue[cl->txq_cur];
175 }
176
177 static void ubus_msg_dequeue(struct ubus_client *cl)
178 {
179 struct ubus_msg_buf *ub = ubus_msg_head(cl);
180
181 if (!ub)
182 return;
183
184 ubus_msg_free(ub);
185 cl->txq_ofs = 0;
186 cl->tx_queue[cl->txq_cur] = NULL;
187 cl->txq_cur = (cl->txq_cur + 1) % ARRAY_SIZE(cl->tx_queue);
188 }
189
190 static void handle_client_disconnect(struct ubus_client *cl)
191 {
192 while (ubus_msg_head(cl))
193 ubus_msg_dequeue(cl);
194
195 ubusd_monitor_disconnect(cl);
196 ubusd_proto_free_client(cl);
197 if (cl->pending_msg_fd >= 0)
198 close(cl->pending_msg_fd);
199 uloop_fd_delete(&cl->sock);
200 close(cl->sock.fd);
201 free(cl);
202 }
203
204 static void client_cb(struct uloop_fd *sock, unsigned int events)
205 {
206 struct ubus_client *cl = container_of(sock, struct ubus_client, sock);
207 struct ubus_msg_buf *ub;
208 static struct iovec iov;
209 static struct {
210 struct cmsghdr h;
211 int fd;
212 } fd_buf = {
213 .h = {
214 .cmsg_type = SCM_RIGHTS,
215 .cmsg_level = SOL_SOCKET,
216 .cmsg_len = sizeof(fd_buf),
217 }
218 };
219 struct msghdr msghdr = {
220 .msg_iov = &iov,
221 .msg_iovlen = 1,
222 };
223
224 /* first try to tx more pending data */
225 while ((ub = ubus_msg_head(cl))) {
226 int written;
227
228 written = ubus_msg_writev(sock->fd, ub, cl->txq_ofs);
229 if (written < 0) {
230 switch(errno) {
231 case EINTR:
232 case EAGAIN:
233 break;
234 default:
235 goto disconnect;
236 }
237 break;
238 }
239
240 cl->txq_ofs += written;
241 if (cl->txq_ofs < ub->len + sizeof(ub->hdr))
242 break;
243
244 ubus_msg_dequeue(cl);
245 }
246
247 /* prevent further ULOOP_WRITE events if we don't have data
248 * to send anymore */
249 if (!ubus_msg_head(cl) && (events & ULOOP_WRITE))
250 uloop_fd_add(sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
251
252 retry:
253 if (!sock->eof && cl->pending_msg_offset < sizeof(cl->hdrbuf)) {
254 int offset = cl->pending_msg_offset;
255 int bytes;
256
257 fd_buf.fd = -1;
258
259 iov.iov_base = ((char *) &cl->hdrbuf) + offset;
260 iov.iov_len = sizeof(cl->hdrbuf) - offset;
261
262 if (cl->pending_msg_fd < 0) {
263 msghdr.msg_control = &fd_buf;
264 msghdr.msg_controllen = sizeof(fd_buf);
265 } else {
266 msghdr.msg_control = NULL;
267 msghdr.msg_controllen = 0;
268 }
269
270 bytes = recvmsg(sock->fd, &msghdr, 0);
271 if (bytes < 0)
272 goto out;
273
274 if (fd_buf.fd >= 0)
275 cl->pending_msg_fd = fd_buf.fd;
276
277 cl->pending_msg_offset += bytes;
278 if (cl->pending_msg_offset < sizeof(cl->hdrbuf))
279 goto out;
280
281 if (blob_pad_len(&cl->hdrbuf.data) > UBUS_MAX_MSGLEN)
282 goto disconnect;
283
284 cl->pending_msg = ubus_msg_new(NULL, blob_raw_len(&cl->hdrbuf.data), false);
285 if (!cl->pending_msg)
286 goto disconnect;
287
288 cl->hdrbuf.hdr.seq = be16_to_cpu(cl->hdrbuf.hdr.seq);
289 cl->hdrbuf.hdr.peer = be32_to_cpu(cl->hdrbuf.hdr.peer);
290
291 memcpy(&cl->pending_msg->hdr, &cl->hdrbuf.hdr, sizeof(cl->hdrbuf.hdr));
292 memcpy(cl->pending_msg->data, &cl->hdrbuf.data, sizeof(cl->hdrbuf.data));
293 }
294
295 ub = cl->pending_msg;
296 if (ub) {
297 int offset = cl->pending_msg_offset - sizeof(ub->hdr);
298 int len = blob_raw_len(ub->data) - offset;
299 int bytes = 0;
300
301 if (len > 0) {
302 bytes = read(sock->fd, (char *) ub->data + offset, len);
303 if (bytes <= 0)
304 goto out;
305 }
306
307 if (bytes < len) {
308 cl->pending_msg_offset += bytes;
309 goto out;
310 }
311
312 /* accept message */
313 ub->fd = cl->pending_msg_fd;
314 cl->pending_msg_fd = -1;
315 cl->pending_msg_offset = 0;
316 cl->pending_msg = NULL;
317 ubusd_monitor_message(cl, ub, false);
318 ubusd_proto_receive_message(cl, ub);
319 goto retry;
320 }
321
322 out:
323 if (!sock->eof || ubus_msg_head(cl))
324 return;
325
326 disconnect:
327 handle_client_disconnect(cl);
328 }
329
330 static bool get_next_connection(int fd)
331 {
332 struct ubus_client *cl;
333 int client_fd;
334
335 client_fd = accept(fd, NULL, 0);
336 if (client_fd < 0) {
337 switch (errno) {
338 case ECONNABORTED:
339 case EINTR:
340 return true;
341 default:
342 return false;
343 }
344 }
345
346 cl = ubusd_proto_new_client(client_fd, client_cb);
347 if (cl)
348 uloop_fd_add(&cl->sock, ULOOP_READ | ULOOP_EDGE_TRIGGER);
349 else
350 close(client_fd);
351
352 return true;
353 }
354
355 static void server_cb(struct uloop_fd *fd, unsigned int events)
356 {
357 bool next;
358
359 do {
360 next = get_next_connection(fd->fd);
361 } while (next);
362 }
363
364 static struct uloop_fd server_fd = {
365 .cb = server_cb,
366 };
367
368 static int usage(const char *progname)
369 {
370 fprintf(stderr, "Usage: %s [<options>]\n"
371 "Options: \n"
372 " -A <path>: Set the path to ACL files\n"
373 " -s <socket>: Set the unix domain socket to listen on\n"
374 "\n", progname);
375 return 1;
376 }
377
378 static void sighup_handler(int sig)
379 {
380 ubusd_acl_load();
381 }
382
383 int main(int argc, char **argv)
384 {
385 const char *ubus_socket = UBUS_UNIX_SOCKET;
386 bool remove_socket = true;
387 int ret = 0;
388 int ch;
389 #ifdef ENABLE_SYSTEMD
390 int n_fds;
391 #endif
392
393 signal(SIGPIPE, SIG_IGN);
394 signal(SIGHUP, sighup_handler);
395
396 openlog("ubusd", LOG_PID, LOG_DAEMON);
397 uloop_init();
398
399 while ((ch = getopt(argc, argv, "A:s:")) != -1) {
400 switch (ch) {
401 case 's':
402 ubus_socket = optarg;
403 break;
404 case 'A':
405 ubusd_acl_dir = optarg;
406 break;
407 default:
408 return usage(argv[0]);
409 }
410 }
411
412 #ifdef ENABLE_SYSTEMD
413 n_fds = sd_listen_fds(1);
414 if (n_fds > 1) {
415 fprintf(stderr, "Too many file descriptors received.\n");
416 ret = -1;
417 goto out;
418 } else if (n_fds == 1) {
419 server_fd.fd = SD_LISTEN_FDS_START + 0;
420 fcntl(server_fd.fd, F_SETFD, fcntl(server_fd.fd, F_GETFD) | FD_CLOEXEC);
421 fcntl(server_fd.fd, F_SETFL, fcntl(server_fd.fd, F_GETFL) | O_NONBLOCK);
422
423 remove_socket = false;
424 } else
425 #endif
426 {
427 unlink(ubus_socket);
428 umask(0111);
429 server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL);
430 if (server_fd.fd < 0) {
431 perror("usock");
432 ret = -1;
433 goto out;
434 }
435 }
436 uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER);
437 ubusd_acl_load();
438
439 uloop_run();
440
441 if (remove_socket)
442 unlink(ubus_socket);
443
444 out:
445 uloop_done();
446 return ret;
447 }