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