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