365755a537e2a3a9084c346bba356aa243dde183
[project/fstools.git] / blockd.c
1 #define _GNU_SOURCE
2 #include <sys/stat.h>
3 #include <sys/mount.h>
4 #include <sys/wait.h>
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <fcntl.h>
10
11 #include <errno.h>
12
13 #include <linux/limits.h>
14 #include <linux/auto_fs4.h>
15
16 #include <libubox/uloop.h>
17 #include <libubox/vlist.h>
18 #include <libubox/ulog.h>
19 #include <libubox/avl-cmp.h>
20 #include <libubus.h>
21
22 #include "libfstools/libfstools.h"
23
24 #define AUTOFS_MOUNT_PATH "/tmp/run/blockd/"
25 #define AUTOFS_TIMEOUT 30
26 #define AUTOFS_EXPIRE_TIMER (5 * 1000)
27
28 struct hotplug_context {
29 struct uloop_process process;
30 void *priv;
31 };
32
33 struct device {
34 struct vlist_node node;
35 struct blob_attr *msg;
36 char *name;
37 char *target;
38 int autofs;
39 int anon;
40 };
41
42 static struct uloop_fd fd_autofs_read;
43 static int fd_autofs_write = 0;
44 static struct ubus_auto_conn conn;
45 struct blob_buf bb = { 0 };
46
47 enum {
48 MOUNT_UUID,
49 MOUNT_LABEL,
50 MOUNT_ENABLE,
51 MOUNT_TARGET,
52 MOUNT_DEVICE,
53 MOUNT_OPTIONS,
54 MOUNT_AUTOFS,
55 MOUNT_ANON,
56 MOUNT_REMOVE,
57 __MOUNT_MAX
58 };
59
60 static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = {
61 [MOUNT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
62 [MOUNT_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING },
63 [MOUNT_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
64 [MOUNT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
65 [MOUNT_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_STRING },
66 [MOUNT_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
67 [MOUNT_AUTOFS] = { .name = "autofs", .type = BLOBMSG_TYPE_INT32 },
68 [MOUNT_ANON] = { .name = "anon", .type = BLOBMSG_TYPE_INT32 },
69 [MOUNT_REMOVE] = { .name = "remove", .type = BLOBMSG_TYPE_INT32 },
70 };
71
72 enum {
73 INFO_DEVICE,
74 __INFO_MAX
75 };
76
77 static const struct blobmsg_policy info_policy[__INFO_MAX] = {
78 [INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
79 };
80
81 static char*
82 _find_mount_point(char *device)
83 {
84 char *dev, *mp;
85
86 if (asprintf(&dev, "/dev/%s", device) == -1)
87 exit(ENOMEM);
88
89 mp = find_mount_point(dev, 0);
90 free(dev);
91
92 return mp;
93 }
94
95 static int
96 block(char *cmd, char *action, char *device, int sync, struct uloop_process *process)
97 {
98 pid_t pid = fork();
99 int ret = sync;
100 int status;
101 char *argv[5] = { 0 };
102 int a = 0;
103
104 switch (pid) {
105 case -1:
106 ULOG_ERR("failed to fork block process\n");
107 break;
108
109 case 0:
110 uloop_end();
111
112 argv[a++] = "/sbin/block";
113 argv[a++] = cmd;
114 argv[a++] = action;
115 argv[a++] = device;
116 execvp(argv[0], argv);
117 ULOG_ERR("failed to spawn %s %s %s\n", *argv, action, device);
118 exit(EXIT_FAILURE);
119
120 default:
121 if (!sync && process) {
122 process->pid = pid;
123 uloop_process_add(process);
124 } else if (sync) {
125 waitpid(pid, &status, 0);
126 ret = WEXITSTATUS(status);
127 if (ret)
128 ULOG_ERR("failed to run block. %s/%s\n", action, device);
129 }
130 break;
131 }
132
133 return ret;
134 }
135
136 static int send_block_notification(struct ubus_context *ctx, const char *action,
137 const char *devname, const char *target);
138 static int hotplug_call_mount(struct ubus_context *ctx, const char *action,
139 const char *devname, uloop_process_handler cb, void *priv)
140 {
141 char * const argv[] = { "hotplug-call", "mount", NULL };
142 struct hotplug_context *c = NULL;
143 pid_t pid;
144 int err;
145
146 if (cb) {
147 c = calloc(1, sizeof(*c));
148 if (!c)
149 return -ENOMEM;
150 }
151
152 pid = fork();
153 switch (pid) {
154 case -1:
155 if (c)
156 free(c);
157
158 err = -errno;
159 ULOG_ERR("fork() failed\n");
160 return err;
161 case 0:
162 uloop_end();
163
164 setenv("ACTION", action, 1);
165 setenv("DEVICE", devname, 1);
166
167 execv("/sbin/hotplug-call", argv);
168 exit(-1);
169 break;
170 default:
171 if (c) {
172 c->process.pid = pid;
173 c->process.cb = cb;
174 c->priv = priv;
175 uloop_process_add(&c->process);
176 }
177 break;
178 }
179
180 return 0;
181 }
182
183 static void device_mount_remove_hotplug_cb(struct uloop_process *p, int stat)
184 {
185 struct hotplug_context *hctx = container_of(p, struct hotplug_context, process);
186 struct device *device = hctx->priv;
187 char *mp;
188
189 if (device->target)
190 unlink(device->target);
191
192 mp = _find_mount_point(device->name);
193 if (mp) {
194 block("autofs", "remove", device->name, 0, NULL);
195 free(mp);
196 }
197
198 free(device);
199 free(hctx);
200 }
201
202 static void device_mount_remove(struct ubus_context *ctx, struct device *device)
203 {
204 static const char *action = "remove";
205
206 hotplug_call_mount(ctx, action, device->name,
207 device_mount_remove_hotplug_cb, device);
208
209 send_block_notification(ctx, action, device->name, device->target);
210 }
211
212 static void device_mount_add(struct ubus_context *ctx, struct device *device)
213 {
214 struct stat st;
215 char *path, *tmp;
216
217 if (asprintf(&path, "/tmp/run/blockd/%s", device->name) == -1)
218 exit(ENOMEM);
219
220 if (!lstat(device->target, &st)) {
221 if (S_ISLNK(st.st_mode))
222 unlink(device->target);
223 else if (S_ISDIR(st.st_mode))
224 rmdir(device->target);
225 }
226
227 tmp = strrchr(device->target, '/');
228 if (tmp && tmp != device->target && tmp != &device->target[strlen(path)-1]) {
229 *tmp = '\0';
230 mkdir_p(device->target, 0755);
231 *tmp = '/';
232 }
233
234 if (symlink(path, device->target)) {
235 ULOG_ERR("failed to symlink %s->%s (%d) - %m\n", device->target, path, errno);
236 } else {
237 static const char *action = "add";
238 hotplug_call_mount(ctx, action, device->name, NULL, NULL);
239 send_block_notification(ctx, action, device->name, device->target);
240 }
241 free(path);
242 }
243
244 static int
245 device_move(struct device *device_o, struct device *device_n)
246 {
247 char *path;
248
249 if (device_o->autofs != device_n->autofs)
250 return -1;
251
252 if (device_o->anon || device_n->anon)
253 return -1;
254
255 if (device_o->autofs) {
256 unlink(device_o->target);
257 if (asprintf(&path, "/tmp/run/blockd/%s", device_n->name) == -1)
258 exit(ENOMEM);
259
260 if (symlink(path, device_n->target))
261 ULOG_ERR("failed to symlink %s->%s (%d) - %m\n", device_n->target, path, errno);
262
263 free(path);
264 } else {
265 mkdir(device_n->target, 0755);
266 if (mount(device_o->target, device_n->target, NULL, MS_MOVE, NULL))
267 rmdir(device_n->target);
268 else
269 rmdir(device_o->target);
270 }
271
272 return 0;
273 }
274
275 static void vlist_nop_update(struct vlist_tree *tree,
276 struct vlist_node *node_new,
277 struct vlist_node *node_old)
278 {
279 }
280
281 VLIST_TREE(devices, avl_strcmp, vlist_nop_update, false, false);
282
283 static int
284 block_hotplug(struct ubus_context *ctx, struct ubus_object *obj,
285 struct ubus_request_data *req, const char *method,
286 struct blob_attr *msg)
287 {
288 struct blob_attr *data[__MOUNT_MAX];
289 struct device *device;
290 struct blob_attr *_msg;
291 char *devname, *_name;
292 char *target = NULL, *__target;
293 char *_target = NULL;
294
295 blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
296
297 if (!data[MOUNT_DEVICE])
298 return UBUS_STATUS_INVALID_ARGUMENT;
299
300 devname = blobmsg_get_string(data[MOUNT_DEVICE]);
301
302 if (data[MOUNT_TARGET]) {
303 target = blobmsg_get_string(data[MOUNT_TARGET]);
304 } else {
305 if (asprintf(&_target, "/mnt/%s",
306 blobmsg_get_string(data[MOUNT_DEVICE])) == -1)
307 exit(ENOMEM);
308
309 target = _target;
310 }
311
312 if (data[MOUNT_REMOVE])
313 device = vlist_find(&devices, devname, device, node);
314 else
315 device = calloc_a(sizeof(*device), &_msg, blob_raw_len(msg),
316 &_name, strlen(devname) + 1, &__target, strlen(target) + 1);
317
318 if (!device) {
319 if (_target)
320 free(_target);
321
322 return UBUS_STATUS_UNKNOWN_ERROR;
323 }
324
325 if (data[MOUNT_REMOVE]) {
326 vlist_delete(&devices, &device->node);
327
328 if (device->autofs)
329 device_mount_remove(ctx, device);
330 else
331 free(device);
332
333 if (_target)
334 free(_target);
335 } else {
336 struct device *old = vlist_find(&devices, devname, device, node);
337
338 device->autofs = data[MOUNT_AUTOFS] ? blobmsg_get_u32(data[MOUNT_AUTOFS]) : 0;
339 device->anon = data[MOUNT_ANON] ? blobmsg_get_u32(data[MOUNT_ANON]) : 0;
340 device->msg = _msg;
341 memcpy(_msg, msg, blob_raw_len(msg));
342 device->name = _name;
343 strcpy(_name, devname);
344 device->target = __target;
345 strcpy(__target, target);
346 if (_target)
347 free(_target);
348
349 vlist_add(&devices, &device->node, device->name);
350
351 if (old && !device_move(old, device)) {
352 if (device->autofs) {
353 device_mount_remove(ctx, old);
354 device_mount_add(ctx, device);
355 } else {
356 block("mount", NULL, NULL, 0, NULL);
357 }
358 } else if (device->autofs) {
359 device_mount_add(ctx, device);
360 }
361 }
362
363 return 0;
364 }
365
366 static int blockd_mount(struct ubus_context *ctx, struct ubus_object *obj,
367 struct ubus_request_data *req, const char *method,
368 struct blob_attr *msg)
369 {
370 static const char *action = "add";
371 struct blob_attr *data[__MOUNT_MAX];
372 struct device *device;
373 char *devname;
374
375 blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
376
377 if (!data[MOUNT_DEVICE])
378 return UBUS_STATUS_INVALID_ARGUMENT;
379
380 devname = blobmsg_get_string(data[MOUNT_DEVICE]);
381
382 device = vlist_find(&devices, devname, device, node);
383 if (!device)
384 return UBUS_STATUS_UNKNOWN_ERROR;
385
386 hotplug_call_mount(ctx, action, device->name, NULL, NULL);
387 send_block_notification(ctx, action, device->name, device->target);
388
389 return 0;
390 }
391
392 struct blockd_umount_context {
393 struct ubus_context *ctx;
394 struct ubus_request_data req;
395 };
396
397 static void blockd_umount_hotplug_cb(struct uloop_process *p, int stat)
398 {
399 struct hotplug_context *hctx = container_of(p, struct hotplug_context, process);
400 struct blockd_umount_context *c = hctx->priv;
401
402 ubus_complete_deferred_request(c->ctx, &c->req, 0);
403
404 free(c);
405 free(hctx);
406 }
407
408 static int blockd_umount(struct ubus_context *ctx, struct ubus_object *obj,
409 struct ubus_request_data *req, const char *method,
410 struct blob_attr *msg)
411 {
412 struct blob_attr *data[__MOUNT_MAX];
413 struct blockd_umount_context *c;
414 static const char *action = "remove";
415 char *devname;
416 static char oldtarget[PATH_MAX];
417 struct device *device;
418 int err;
419
420 blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
421
422 if (!data[MOUNT_DEVICE])
423 return UBUS_STATUS_INVALID_ARGUMENT;
424
425 devname = blobmsg_get_string(data[MOUNT_DEVICE]);
426 device = vlist_find(&devices, devname, device, node);
427 if (device) {
428 strncpy(oldtarget, device->target, sizeof(oldtarget)-1);
429 oldtarget[PATH_MAX - 1] = '\0';
430 }
431
432 c = calloc(1, sizeof(*c));
433 if (!c)
434 return UBUS_STATUS_UNKNOWN_ERROR;
435
436 c->ctx = ctx;
437 ubus_defer_request(ctx, req, &c->req);
438
439 err = hotplug_call_mount(ctx, action, devname, blockd_umount_hotplug_cb, c);
440 if (err) {
441 free(c);
442 return UBUS_STATUS_UNKNOWN_ERROR;
443 }
444
445 send_block_notification(ctx, action, devname, oldtarget);
446
447 return 0;
448 }
449
450 static void block_info_dump(struct blob_buf *b, struct device *device)
451 {
452 struct blob_attr *v;
453 char *mp;
454 int rem;
455
456 blob_for_each_attr(v, device->msg, rem)
457 blobmsg_add_blob(b, v);
458
459 mp = _find_mount_point(device->name);
460 if (mp) {
461 blobmsg_add_string(b, "mount", mp);
462 free(mp);
463 } else if (device->autofs && device->target) {
464 blobmsg_add_string(b, "mount", device->target);
465 }
466 }
467
468 static int
469 block_info(struct ubus_context *ctx, struct ubus_object *obj,
470 struct ubus_request_data *req, const char *method,
471 struct blob_attr *msg)
472 {
473 struct blob_attr *data[__INFO_MAX];
474 struct device *device = NULL;
475
476 blobmsg_parse(info_policy, __INFO_MAX, data, blob_data(msg), blob_len(msg));
477
478 if (data[INFO_DEVICE]) {
479 device = vlist_find(&devices, blobmsg_get_string(data[INFO_DEVICE]), device, node);
480 if (!device)
481 return UBUS_STATUS_INVALID_ARGUMENT;
482 }
483
484 blob_buf_init(&bb, 0);
485 if (device) {
486 block_info_dump(&bb, device);
487 } else {
488 void *a;
489
490 a = blobmsg_open_array(&bb, "devices");
491 vlist_for_each_element(&devices, device, node) {
492 void *t;
493
494 t = blobmsg_open_table(&bb, "");
495 block_info_dump(&bb, device);
496 blobmsg_close_table(&bb, t);
497 }
498 blobmsg_close_array(&bb, a);
499 }
500 ubus_send_reply(ctx, req, bb.head);
501
502 return 0;
503 }
504
505 static const struct ubus_method block_methods[] = {
506 UBUS_METHOD("hotplug", block_hotplug, mount_policy),
507 UBUS_METHOD("mount", blockd_mount, mount_policy),
508 UBUS_METHOD("umount", blockd_umount, mount_policy),
509 UBUS_METHOD("info", block_info, info_policy),
510 };
511
512 static struct ubus_object_type block_object_type =
513 UBUS_OBJECT_TYPE("block", block_methods);
514
515 static struct ubus_object block_object = {
516 .name = "block",
517 .type = &block_object_type,
518 .methods = block_methods,
519 .n_methods = ARRAY_SIZE(block_methods),
520 };
521
522 /* send ubus event for successful mounts, useful for procd triggers */
523 static int send_block_notification(struct ubus_context *ctx, const char *action,
524 const char *devname, const char *target)
525 {
526 struct blob_buf buf = { 0 };
527 char evname[16] = "mount.";
528 int err;
529
530 if (!ctx)
531 return -ENXIO;
532
533 strncat(evname, action, sizeof(evname) - 1);
534
535 blob_buf_init(&buf, 0);
536
537 if (devname)
538 blobmsg_add_string(&buf, "device", devname);
539
540 if (target)
541 blobmsg_add_string(&buf, "target", target);
542
543 err = ubus_notify(ctx, &block_object, evname, buf.head, -1);
544
545 return err;
546 }
547
548 static void
549 ubus_connect_handler(struct ubus_context *ctx)
550 {
551 int ret;
552
553 ret = ubus_add_object(ctx, &block_object);
554 if (ret)
555 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
556 }
557
558 static int autofs_umount(void)
559 {
560 umount2(AUTOFS_MOUNT_PATH, MNT_DETACH);
561 return 0;
562 }
563
564 static void autofs_read_handler(struct uloop_fd *u, unsigned int events)
565 {
566 union autofs_v5_packet_union pktu;
567 const struct autofs_v5_packet *pkt;
568 int cmd = AUTOFS_IOC_READY;
569 struct stat st;
570
571 while (read(u->fd, &pktu, sizeof(pktu)) == -1) {
572 if (errno != EINTR)
573 return;
574 continue;
575 }
576
577 if (pktu.hdr.type != autofs_ptype_missing_indirect) {
578 ULOG_ERR("unknown packet type %d\n", pktu.hdr.type);
579 return;
580 }
581
582 pkt = &pktu.missing_indirect;
583 ULOG_ERR("kernel is requesting a mount -> %s\n", pkt->name);
584 if (lstat(pkt->name, &st) == -1)
585 if (block("autofs", "add", (char *)pkt->name, 1, NULL))
586 cmd = AUTOFS_IOC_FAIL;
587
588 if (ioctl(fd_autofs_write, cmd, pkt->wait_queue_token) < 0)
589 ULOG_ERR("failed to report back to kernel\n");
590 }
591
592 static void autofs_expire(struct uloop_timeout *t)
593 {
594 struct autofs_packet_expire pkt;
595
596 while (ioctl(fd_autofs_write, AUTOFS_IOC_EXPIRE, &pkt) == 0)
597 block("autofs", "remove", pkt.name, 1, NULL);
598
599 uloop_timeout_set(t, AUTOFS_EXPIRE_TIMER);
600 }
601
602 struct uloop_timeout autofs_expire_timer = {
603 .cb = autofs_expire,
604 };
605
606 static int autofs_mount(void)
607 {
608 unsigned long autofs_timeout = AUTOFS_TIMEOUT;
609 int kproto_version;
610 int pipefd[2];
611 char source[64];
612 char opts[64];
613
614 if (pipe(pipefd) < 0) {
615 ULOG_ERR("failed to get kernel pipe\n");
616 return -1;
617 }
618
619 snprintf(source, sizeof(source), "mountd(pid%u)", getpid());
620 snprintf(opts, sizeof(opts), "fd=%d,pgrp=%u,minproto=5,maxproto=5", pipefd[1], (unsigned) getpgrp());
621 mkdir(AUTOFS_MOUNT_PATH, 0555);
622 if (mount(source, AUTOFS_MOUNT_PATH, "autofs", 0, opts)) {
623 ULOG_ERR("unable to mount autofs on %s\n", AUTOFS_MOUNT_PATH);
624 close(pipefd[0]);
625 close(pipefd[1]);
626 return -1;
627 }
628 close(pipefd[1]);
629 fd_autofs_read.fd = pipefd[0];
630 fd_autofs_read.cb = autofs_read_handler;
631 uloop_fd_add(&fd_autofs_read, ULOOP_READ);
632
633 fd_autofs_write = open(AUTOFS_MOUNT_PATH, O_RDONLY);
634 if(fd_autofs_write < 0) {
635 autofs_umount();
636 ULOG_ERR("failed to open direcory\n");
637 return -1;
638 }
639
640 ioctl(fd_autofs_write, AUTOFS_IOC_PROTOVER, &kproto_version);
641 if (kproto_version != 5) {
642 ULOG_ERR("only kernel protocol version 5 is tested. You have %d.\n",
643 kproto_version);
644 exit(EXIT_FAILURE);
645 }
646 if (ioctl(fd_autofs_write, AUTOFS_IOC_SETTIMEOUT, &autofs_timeout))
647 ULOG_ERR("failed to set autofs timeout\n");
648
649 uloop_timeout_set(&autofs_expire_timer, AUTOFS_EXPIRE_TIMER);
650
651 fcntl(fd_autofs_write, F_SETFD, fcntl(fd_autofs_write, F_GETFD) | FD_CLOEXEC);
652 fcntl(fd_autofs_read.fd, F_SETFD, fcntl(fd_autofs_read.fd, F_GETFD) | FD_CLOEXEC);
653
654 return 0;
655 }
656
657 static void blockd_startup_cb(struct uloop_process *p, int stat)
658 {
659 send_block_notification(&conn.ctx, "ready", NULL, NULL);
660 }
661
662 static struct uloop_process startup_process = {
663 .cb = blockd_startup_cb,
664 };
665
666 static void blockd_startup(struct uloop_timeout *t)
667 {
668 block("autofs", "start", NULL, 0, &startup_process);
669 }
670
671 struct uloop_timeout startup = {
672 .cb = blockd_startup,
673 };
674
675 int main(int argc, char **argv)
676 {
677 /* make sure blockd is in it's own POSIX process group */
678 setpgrp();
679
680 ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "blockd");
681 uloop_init();
682
683 autofs_mount();
684
685 conn.cb = ubus_connect_handler;
686 ubus_auto_connect(&conn);
687
688 uloop_timeout_set(&startup, 1000);
689
690 uloop_run();
691 uloop_done();
692
693 autofs_umount();
694
695 vlist_flush_all(&devices);
696
697 return 0;
698 }