• source navigation  • diff markup  • identifier search  • freetext search  • 

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt