• 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                         device_mount_remove(ctx, old);
354                         device_mount_add(ctx, device);
355                         if (!device->autofs)
356                                 block("mount", NULL, NULL, 0, NULL);
357                 } else if (device->autofs) {
358                         device_mount_add(ctx, device);
359                 }
360         }
361 
362         return 0;
363 }
364 
365 static int blockd_mount(struct ubus_context *ctx, struct ubus_object *obj,
366                         struct ubus_request_data *req, const char *method,
367                         struct blob_attr *msg)
368 {
369         static const char *action = "add";
370         struct blob_attr *data[__MOUNT_MAX];
371         struct device *device;
372         char *devname;
373 
374         blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
375 
376         if (!data[MOUNT_DEVICE])
377                 return UBUS_STATUS_INVALID_ARGUMENT;
378 
379         devname = blobmsg_get_string(data[MOUNT_DEVICE]);
380 
381         device = vlist_find(&devices, devname, device, node);
382         if (!device)
383                 return UBUS_STATUS_UNKNOWN_ERROR;
384 
385         hotplug_call_mount(ctx, action, device->name, NULL, NULL);
386         send_block_notification(ctx, action, device->name, device->target);
387 
388         return 0;
389 }
390 
391 struct blockd_umount_context {
392         struct ubus_context *ctx;
393         struct ubus_request_data req;
394 };
395 
396 static void blockd_umount_hotplug_cb(struct uloop_process *p, int stat)
397 {
398         struct hotplug_context *hctx = container_of(p, struct hotplug_context, process);
399         struct blockd_umount_context *c = hctx->priv;
400 
401         ubus_complete_deferred_request(c->ctx, &c->req, 0);
402 
403         free(c);
404         free(hctx);
405 }
406 
407 static int blockd_umount(struct ubus_context *ctx, struct ubus_object *obj,
408                          struct ubus_request_data *req, const char *method,
409                          struct blob_attr *msg)
410 {
411         struct blob_attr *data[__MOUNT_MAX];
412         struct blockd_umount_context *c;
413         static const char *action = "remove";
414         char *devname;
415         static char oldtarget[PATH_MAX];
416         struct device *device;
417         int err;
418 
419         blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
420 
421         if (!data[MOUNT_DEVICE])
422                 return UBUS_STATUS_INVALID_ARGUMENT;
423 
424         devname = blobmsg_get_string(data[MOUNT_DEVICE]);
425         device = vlist_find(&devices, devname, device, node);
426         if (device) {
427                 strncpy(oldtarget, device->target, sizeof(oldtarget)-1);
428                 oldtarget[PATH_MAX - 1] = '\0';
429         }
430 
431         c = calloc(1, sizeof(*c));
432         if (!c)
433                 return UBUS_STATUS_UNKNOWN_ERROR;
434 
435         c->ctx = ctx;
436         ubus_defer_request(ctx, req, &c->req);
437 
438         err = hotplug_call_mount(ctx, action, devname, blockd_umount_hotplug_cb, c);
439         if (err) {
440                 free(c);
441                 return UBUS_STATUS_UNKNOWN_ERROR;
442         }
443 
444         send_block_notification(ctx, action, devname, oldtarget);
445 
446         return 0;
447 }
448 
449 static void block_info_dump(struct blob_buf *b, struct device *device)
450 {
451         struct blob_attr *v;
452         char *mp;
453         int rem;
454 
455         blob_for_each_attr(v, device->msg, rem)
456                 blobmsg_add_blob(b, v);
457 
458         mp = _find_mount_point(device->name);
459         if (mp) {
460                 blobmsg_add_string(b, "mount", mp);
461                 free(mp);
462         } else if (device->autofs && device->target) {
463                 blobmsg_add_string(b, "mount", device->target);
464         }
465 }
466 
467 static int
468 block_info(struct ubus_context *ctx, struct ubus_object *obj,
469            struct ubus_request_data *req, const char *method,
470            struct blob_attr *msg)
471 {
472         struct blob_attr *data[__INFO_MAX];
473         struct device *device = NULL;
474 
475         blobmsg_parse(info_policy, __INFO_MAX, data, blob_data(msg), blob_len(msg));
476 
477         if (data[INFO_DEVICE]) {
478                 device = vlist_find(&devices, blobmsg_get_string(data[INFO_DEVICE]), device, node);
479                 if (!device)
480                         return UBUS_STATUS_INVALID_ARGUMENT;
481         }
482 
483         blob_buf_init(&bb, 0);
484         if (device) {
485                 block_info_dump(&bb, device);
486         } else {
487                 void *a;
488 
489                 a = blobmsg_open_array(&bb, "devices");
490                 vlist_for_each_element(&devices, device, node) {
491                         void *t;
492 
493                         t = blobmsg_open_table(&bb, "");
494                         block_info_dump(&bb, device);
495                         blobmsg_close_table(&bb, t);
496                 }
497                 blobmsg_close_array(&bb, a);
498         }
499         ubus_send_reply(ctx, req, bb.head);
500 
501         return 0;
502 }
503 
504 static const struct ubus_method block_methods[] = {
505         UBUS_METHOD("hotplug", block_hotplug, mount_policy),
506         UBUS_METHOD("mount", blockd_mount, mount_policy),
507         UBUS_METHOD("umount", blockd_umount, mount_policy),
508         UBUS_METHOD("info", block_info, info_policy),
509 };
510 
511 static struct ubus_object_type block_object_type =
512         UBUS_OBJECT_TYPE("block", block_methods);
513 
514 static struct ubus_object block_object = {
515         .name = "block",
516         .type = &block_object_type,
517         .methods = block_methods,
518         .n_methods = ARRAY_SIZE(block_methods),
519 };
520 
521 /* send ubus event for successful mounts, useful for procd triggers */
522 static int send_block_notification(struct ubus_context *ctx, const char *action,
523                             const char *devname, const char *target)
524 {
525         struct blob_buf buf = { 0 };
526         char evname[16] = "mount.";
527         int err;
528 
529         if (!ctx)
530                 return -ENXIO;
531 
532         strncat(evname, action, sizeof(evname) - 1);
533 
534         blob_buf_init(&buf, 0);
535 
536         if (devname)
537                 blobmsg_add_string(&buf, "device", devname);
538 
539         if (target)
540                 blobmsg_add_string(&buf, "target", target);
541 
542         err = ubus_notify(ctx, &block_object, evname, buf.head, -1);
543 
544         return err;
545 }
546 
547 static void
548 ubus_connect_handler(struct ubus_context *ctx)
549 {
550         int ret;
551 
552         ret = ubus_add_object(ctx, &block_object);
553         if (ret)
554                 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
555 }
556 
557 static int autofs_umount(void)
558 {
559         umount2(AUTOFS_MOUNT_PATH, MNT_DETACH);
560         return 0;
561 }
562 
563 static void autofs_read_handler(struct uloop_fd *u, unsigned int events)
564 {
565         union autofs_v5_packet_union pktu;
566         const struct autofs_v5_packet *pkt;
567         int cmd = AUTOFS_IOC_READY;
568         struct stat st;
569 
570         while (read(u->fd, &pktu, sizeof(pktu)) == -1) {
571                 if (errno != EINTR)
572                         return;
573                 continue;
574         }
575 
576         if (pktu.hdr.type != autofs_ptype_missing_indirect) {
577                 ULOG_ERR("unknown packet type %d\n", pktu.hdr.type);
578                 return;
579         }
580 
581         pkt = &pktu.missing_indirect;
582         ULOG_ERR("kernel is requesting a mount -> %s\n", pkt->name);
583         if (lstat(pkt->name, &st) == -1)
584                 if (block("autofs", "add", (char *)pkt->name, 1, NULL))
585                         cmd = AUTOFS_IOC_FAIL;
586 
587         if (ioctl(fd_autofs_write, cmd, pkt->wait_queue_token) < 0)
588                 ULOG_ERR("failed to report back to kernel\n");
589 }
590 
591 static void autofs_expire(struct uloop_timeout *t)
592 {
593         struct autofs_packet_expire pkt;
594 
595         while (ioctl(fd_autofs_write, AUTOFS_IOC_EXPIRE, &pkt) == 0)
596                 block("autofs", "remove", pkt.name, 1, NULL);
597 
598         uloop_timeout_set(t, AUTOFS_EXPIRE_TIMER);
599 }
600 
601 struct uloop_timeout autofs_expire_timer = {
602         .cb = autofs_expire,
603 };
604 
605 static int autofs_mount(void)
606 {
607         unsigned long autofs_timeout = AUTOFS_TIMEOUT;
608         int kproto_version;
609         int pipefd[2];
610         char source[64];
611         char opts[64];
612 
613         if (pipe(pipefd) < 0) {
614                 ULOG_ERR("failed to get kernel pipe\n");
615                 return -1;
616         }
617 
618         snprintf(source, sizeof(source), "mountd(pid%u)", getpid());
619         snprintf(opts, sizeof(opts), "fd=%d,pgrp=%u,minproto=5,maxproto=5", pipefd[1], (unsigned) getpgrp());
620         mkdir(AUTOFS_MOUNT_PATH, 0555);
621         if (mount(source, AUTOFS_MOUNT_PATH, "autofs", 0, opts)) {
622                 ULOG_ERR("unable to mount autofs on %s\n", AUTOFS_MOUNT_PATH);
623                 close(pipefd[0]);
624                 close(pipefd[1]);
625                 return -1;
626         }
627         close(pipefd[1]);
628         fd_autofs_read.fd = pipefd[0];
629         fd_autofs_read.cb = autofs_read_handler;
630         uloop_fd_add(&fd_autofs_read, ULOOP_READ);
631 
632         fd_autofs_write = open(AUTOFS_MOUNT_PATH, O_RDONLY);
633         if(fd_autofs_write < 0) {
634                 autofs_umount();
635                 ULOG_ERR("failed to open direcory\n");
636                 return -1;
637         }
638 
639         ioctl(fd_autofs_write, AUTOFS_IOC_PROTOVER, &kproto_version);
640         if (kproto_version != 5) {
641                 ULOG_ERR("only kernel protocol version 5 is tested. You have %d.\n",
642                         kproto_version);
643                 exit(EXIT_FAILURE);
644         }
645         if (ioctl(fd_autofs_write, AUTOFS_IOC_SETTIMEOUT, &autofs_timeout))
646                 ULOG_ERR("failed to set autofs timeout\n");
647 
648         uloop_timeout_set(&autofs_expire_timer, AUTOFS_EXPIRE_TIMER);
649 
650         fcntl(fd_autofs_write, F_SETFD, fcntl(fd_autofs_write, F_GETFD) | FD_CLOEXEC);
651         fcntl(fd_autofs_read.fd, F_SETFD, fcntl(fd_autofs_read.fd, F_GETFD) | FD_CLOEXEC);
652 
653         return 0;
654 }
655 
656 static void blockd_startup_cb(struct uloop_process *p, int stat)
657 {
658         send_block_notification(&conn.ctx, "ready", NULL, NULL);
659 }
660 
661 static struct uloop_process startup_process = {
662         .cb = blockd_startup_cb,
663 };
664 
665 static void blockd_startup(struct uloop_timeout *t)
666 {
667         block("autofs", "start", NULL, 0, &startup_process);
668 }
669 
670 struct uloop_timeout startup = {
671         .cb = blockd_startup,
672 };
673 
674 int main(int argc, char **argv)
675 {
676         /* make sure blockd is in it's own POSIX process group */
677         setpgrp();
678 
679         ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "blockd");
680         uloop_init();
681 
682         autofs_mount();
683 
684         conn.cb = ubus_connect_handler;
685         ubus_auto_connect(&conn);
686 
687         uloop_timeout_set(&startup, 1000);
688 
689         uloop_run();
690         uloop_done();
691 
692         autofs_umount();
693 
694         vlist_flush_all(&devices);
695 
696         return 0;
697 }
698 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt