• 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 <libgen.h>
  7 #include <stdlib.h>
  8 #include <stdio.h>
  9 #include <unistd.h>
 10 #include <fcntl.h>
 11 
 12 #include <errno.h>
 13 #include <glob.h>
 14 
 15 #include <linux/limits.h>
 16 #include <linux/auto_fs4.h>
 17 
 18 #include <libubox/uloop.h>
 19 #include <libubox/utils.h>
 20 #include <libubox/vlist.h>
 21 #include <libubox/ulog.h>
 22 #include <libubox/avl-cmp.h>
 23 #include <libubus.h>
 24 
 25 #include "libfstools/libfstools.h"
 26 
 27 #define AUTOFS_MOUNT_PATH       "/tmp/run/blockd/"
 28 #define AUTOFS_TIMEOUT          30
 29 #define AUTOFS_EXPIRE_TIMER     (5 * 1000)
 30 #define STARTUP_TIMEOUT         (10 * 1000)
 31 
 32 struct hotplug_context {
 33         struct uloop_process process;
 34         void *priv;
 35 };
 36 
 37 struct device {
 38         struct vlist_node node;
 39         struct blob_attr *msg;
 40         char *name;
 41         char *target;
 42         int autofs;
 43         int anon;
 44 };
 45 
 46 static struct uloop_fd fd_autofs_read;
 47 static int fd_autofs_write = 0;
 48 static struct ubus_auto_conn conn;
 49 struct blob_buf bb = { 0 };
 50 static int blockd_ready;
 51 
 52 enum {
 53         MOUNT_UUID,
 54         MOUNT_LABEL,
 55         MOUNT_ENABLE,
 56         MOUNT_TARGET,
 57         MOUNT_DEVICE,
 58         MOUNT_OPTIONS,
 59         MOUNT_AUTOFS,
 60         MOUNT_ANON,
 61         MOUNT_REMOVE,
 62         MOUNT_CHECK_FS,
 63         __MOUNT_MAX
 64 };
 65 
 66 static const struct blobmsg_policy mount_policy[__MOUNT_MAX] = {
 67         [MOUNT_UUID] = { .name = "uuid", .type = BLOBMSG_TYPE_STRING },
 68         [MOUNT_LABEL] = { .name = "label", .type = BLOBMSG_TYPE_STRING },
 69         [MOUNT_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
 70         [MOUNT_TARGET] = { .name = "target", .type = BLOBMSG_TYPE_STRING },
 71         [MOUNT_OPTIONS] = { .name = "options", .type = BLOBMSG_TYPE_STRING },
 72         [MOUNT_ENABLE] = { .name = "enabled", .type = BLOBMSG_TYPE_INT32 },
 73         [MOUNT_AUTOFS] = { .name = "autofs", .type = BLOBMSG_TYPE_INT32 },
 74         [MOUNT_ANON] = { .name = "anon", .type = BLOBMSG_TYPE_INT32 },
 75         [MOUNT_REMOVE] = { .name = "remove", .type = BLOBMSG_TYPE_INT32 },
 76         [MOUNT_CHECK_FS] = { .name = "check_fs", .type = BLOBMSG_TYPE_INT32 },
 77 };
 78 
 79 enum {
 80         INFO_DEVICE,
 81         __INFO_MAX
 82 };
 83 
 84 static const struct blobmsg_policy info_policy[__INFO_MAX] = {
 85         [INFO_DEVICE] = { .name = "device", .type = BLOBMSG_TYPE_STRING },
 86 };
 87 
 88 static char*
 89 _find_mount_point(char *device)
 90 {
 91         char *dev, *mp;
 92 
 93         if (asprintf(&dev, "/dev/%s", device) == -1)
 94                 exit(ENOMEM);
 95 
 96         mp = find_mount_point(dev, 0);
 97         free(dev);
 98 
 99         return mp;
100 }
101 
102 static int
103 block(char *cmd, char *action, char *device, char *options, int sync, struct uloop_process *process)
104 {
105         pid_t pid = fork();
106         int ret = sync;
107         int status = 0;
108         char *argv[6] = { 0 };
109         int a = 0;
110 
111         switch (pid) {
112         case -1:
113                 ULOG_ERR("failed to fork block process\n");
114                 break;
115 
116         case 0:
117                 uloop_end();
118 
119                 argv[a++] = "/sbin/block";
120                 argv[a++] = cmd;
121                 argv[a++] = action;
122                 argv[a++] = device;
123                 if (options)
124                         argv[a++] = options;
125                 execvp(argv[0], argv);
126                 ULOG_ERR("failed to spawn %s %s %s\n", *argv, action, device);
127                 exit(EXIT_FAILURE);
128 
129         default:
130                 if (!sync && process) {
131                         process->pid = pid;
132                         uloop_process_add(process);
133                 } else if (sync) {
134                         while (waitpid(pid, &status, 0) < 0 && errno == EINTR)
135                                 ;
136                         ret = WEXITSTATUS(status);
137                         if (ret)
138                                 ULOG_ERR("failed to run block. %s/%s\n", action, device);
139                 }
140                 break;
141         }
142 
143         return ret;
144 }
145 
146 static int send_block_notification(struct ubus_context *ctx, const char *action,
147                             const char *devname, const char *target);
148 static int hotplug_call_mount(struct ubus_context *ctx, const char *action,
149                               const char *devname, uloop_process_handler cb, void *priv)
150 {
151         char * const argv[] = { "hotplug-call", "mount", NULL };
152         struct hotplug_context *c = NULL;
153         pid_t pid;
154         int err;
155 
156         if (cb) {
157                 c = calloc(1, sizeof(*c));
158                 if (!c)
159                         return -ENOMEM;
160         }
161 
162         pid = fork();
163         switch (pid) {
164         case -1:
165                 if (c)
166                         free(c);
167 
168                 err = -errno;
169                 ULOG_ERR("fork() failed\n");
170                 return err;
171         case 0:
172                 uloop_end();
173 
174                 setenv("ACTION", action, 1);
175                 setenv("DEVICE", devname, 1);
176 
177                 execv("/sbin/hotplug-call", argv);
178                 exit(-1);
179                 break;
180         default:
181                 if (c) {
182                         c->process.pid = pid;
183                         c->process.cb = cb;
184                         c->priv = priv;
185                         uloop_process_add(&c->process);
186                 }
187                 break;
188         }
189 
190         return 0;
191 }
192 
193 static void device_mount_remove_hotplug_cb(struct uloop_process *p, int stat)
194 {
195         struct hotplug_context *hctx = container_of(p, struct hotplug_context, process);
196         struct device *device = hctx->priv;
197         char *mp;
198 
199         if (device->target)
200                 unlink(device->target);
201 
202         mp = _find_mount_point(device->name);
203         if (mp) {
204                 block("autofs", "remove", device->name, NULL, 0, NULL);
205                 free(mp);
206         }
207 
208         free(device);
209         free(hctx);
210 }
211 
212 static void device_mount_remove(struct ubus_context *ctx, struct device *device)
213 {
214         static const char *action = "remove";
215 
216         hotplug_call_mount(ctx, action, device->name,
217                            device_mount_remove_hotplug_cb, device);
218 
219         send_block_notification(ctx, action, device->name, device->target);
220 }
221 
222 static void device_mount_add(struct ubus_context *ctx, struct device *device)
223 {
224         struct stat st;
225         char *path, *tmp;
226 
227         if (asprintf(&path, "/tmp/run/blockd/%s", device->name) == -1)
228                 exit(ENOMEM);
229 
230         if (!lstat(device->target, &st)) {
231                 if (S_ISLNK(st.st_mode))
232                         unlink(device->target);
233                 else if (S_ISDIR(st.st_mode))
234                         rmdir(device->target);
235         }
236 
237         tmp = strrchr(device->target, '/');
238         if (tmp && tmp != device->target && tmp != &device->target[strlen(path)-1]) {
239                 *tmp = '\0';
240                 mkdir_p(device->target, 0755);
241                 *tmp = '/';
242         }
243 
244         if (symlink(path, device->target)) {
245                 ULOG_ERR("failed to symlink %s->%s (%d) - %m\n", device->target, path, errno);
246         } else {
247                 static const char *action = "add";
248                 hotplug_call_mount(ctx, action, device->name, NULL, NULL);
249                 send_block_notification(ctx, action, device->name, device->target);
250         }
251         free(path);
252 }
253 
254 static int
255 device_move(struct device *device_o, struct device *device_n)
256 {
257         char *path;
258 
259         if (device_o->autofs != device_n->autofs)
260                 return -1;
261 
262         if (device_o->anon || device_n->anon)
263                 return -1;
264 
265         if (device_o->autofs) {
266                 unlink(device_o->target);
267                 if (asprintf(&path, "/tmp/run/blockd/%s", device_n->name) == -1)
268                         exit(ENOMEM);
269 
270                 if (symlink(path, device_n->target))
271                         ULOG_ERR("failed to symlink %s->%s (%d) - %m\n", device_n->target, path, errno);
272 
273                 free(path);
274         } else {
275                 mkdir(device_n->target, 0755);
276                 if (mount(device_o->target, device_n->target, NULL, MS_MOVE, NULL))
277                         rmdir(device_n->target);
278                 else
279                         rmdir(device_o->target);
280         }
281 
282         return 0;
283 }
284 
285 static void vlist_nop_update(struct vlist_tree *tree,
286                              struct vlist_node *node_new,
287                              struct vlist_node *node_old)
288 {
289 }
290 
291 VLIST_TREE(devices, avl_strcmp, vlist_nop_update, false, false);
292 
293 static int
294 block_hotplug(struct ubus_context *ctx, struct ubus_object *obj,
295               struct ubus_request_data *req, const char *method,
296               struct blob_attr *msg)
297 {
298         struct blob_attr *data[__MOUNT_MAX];
299         struct device *device;
300         struct blob_attr *_msg;
301         char *devname, *_name;
302         char *target = NULL, *__target;
303         char *_target = NULL;
304 
305         blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
306 
307         if (!data[MOUNT_DEVICE])
308                 return UBUS_STATUS_INVALID_ARGUMENT;
309 
310         devname = blobmsg_get_string(data[MOUNT_DEVICE]);
311 
312         if (data[MOUNT_TARGET]) {
313                 target = blobmsg_get_string(data[MOUNT_TARGET]);
314         } else {
315                 if (asprintf(&_target, "/mnt/%s",
316                              blobmsg_get_string(data[MOUNT_DEVICE])) == -1)
317                         exit(ENOMEM);
318 
319                 target = _target;
320         }
321 
322         if (data[MOUNT_REMOVE])
323                 device = vlist_find(&devices, devname, device, node);
324         else
325                 device = calloc_a(sizeof(*device), &_msg, blob_raw_len(msg),
326                                   &_name, strlen(devname) + 1, &__target, strlen(target) + 1);
327 
328         if (!device) {
329                 if (_target)
330                         free(_target);
331 
332                 return UBUS_STATUS_UNKNOWN_ERROR;
333         }
334 
335         if (data[MOUNT_REMOVE]) {
336                 vlist_delete(&devices, &device->node);
337 
338                 if (device->autofs)
339                         device_mount_remove(ctx, device);
340                 else
341                         free(device);
342 
343                 if (_target)
344                         free(_target);
345         } else {
346                 struct device *old = vlist_find(&devices, devname, device, node);
347 
348                 device->autofs = data[MOUNT_AUTOFS] ? blobmsg_get_u32(data[MOUNT_AUTOFS]) : 0;
349                 device->anon = data[MOUNT_ANON] ? blobmsg_get_u32(data[MOUNT_ANON]) : 0;
350                 device->msg = _msg;
351                 memcpy(_msg, msg, blob_raw_len(msg));
352                 device->name = _name;
353                 strcpy(_name, devname);
354                 device->target = __target;
355                 strcpy(__target, target);
356                 if (_target)
357                         free(_target);
358 
359                 vlist_add(&devices, &device->node, device->name);
360 
361                 if (old && device_move(old, device)) {
362                         device_mount_remove(ctx, old);
363                         device_mount_add(ctx, device);
364                         if (!device->autofs)
365                                 block("mount", NULL, NULL, NULL, 0, NULL);
366                 } else if (device->autofs) {
367                         device_mount_add(ctx, device);
368                 }
369         }
370 
371         return 0;
372 }
373 
374 static int blockd_mount(struct ubus_context *ctx, struct ubus_object *obj,
375                         struct ubus_request_data *req, const char *method,
376                         struct blob_attr *msg)
377 {
378         static const char *action = "add";
379         struct blob_attr *data[__MOUNT_MAX];
380         struct device *device;
381         char *devname;
382 
383         blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
384 
385         if (!data[MOUNT_DEVICE])
386                 return UBUS_STATUS_INVALID_ARGUMENT;
387 
388         devname = blobmsg_get_string(data[MOUNT_DEVICE]);
389 
390         device = vlist_find(&devices, devname, device, node);
391         if (!device)
392                 return UBUS_STATUS_UNKNOWN_ERROR;
393 
394         hotplug_call_mount(ctx, action, device->name, NULL, NULL);
395         send_block_notification(ctx, action, device->name, device->target);
396 
397         return 0;
398 }
399 
400 struct blockd_umount_context {
401         struct ubus_context *ctx;
402         struct ubus_request_data req;
403 };
404 
405 static void blockd_umount_hotplug_cb(struct uloop_process *p, int stat)
406 {
407         struct hotplug_context *hctx = container_of(p, struct hotplug_context, process);
408         struct blockd_umount_context *c = hctx->priv;
409 
410         ubus_complete_deferred_request(c->ctx, &c->req, 0);
411 
412         free(c);
413         free(hctx);
414 }
415 
416 static int blockd_umount(struct ubus_context *ctx, struct ubus_object *obj,
417                          struct ubus_request_data *req, const char *method,
418                          struct blob_attr *msg)
419 {
420         struct blob_attr *data[__MOUNT_MAX];
421         struct blockd_umount_context *c;
422         static const char *action = "remove";
423         char *devname;
424         static char oldtarget[PATH_MAX];
425         struct device *device;
426         int err;
427 
428         blobmsg_parse(mount_policy, __MOUNT_MAX, data, blob_data(msg), blob_len(msg));
429 
430         if (!data[MOUNT_DEVICE])
431                 return UBUS_STATUS_INVALID_ARGUMENT;
432 
433         devname = blobmsg_get_string(data[MOUNT_DEVICE]);
434         device = vlist_find(&devices, devname, device, node);
435         if (device) {
436                 strncpy(oldtarget, device->target, sizeof(oldtarget)-1);
437                 oldtarget[PATH_MAX - 1] = '\0';
438         }
439 
440         c = calloc(1, sizeof(*c));
441         if (!c)
442                 return UBUS_STATUS_UNKNOWN_ERROR;
443 
444         c->ctx = ctx;
445         ubus_defer_request(ctx, req, &c->req);
446 
447         err = hotplug_call_mount(ctx, action, devname, blockd_umount_hotplug_cb, c);
448         if (err) {
449                 free(c);
450                 return UBUS_STATUS_UNKNOWN_ERROR;
451         }
452 
453         send_block_notification(ctx, action, devname, oldtarget);
454 
455         return 0;
456 }
457 
458 static void block_info_dump(struct blob_buf *b, struct device *device)
459 {
460         struct blob_attr *v;
461         char *mp;
462         int rem;
463 
464         blob_for_each_attr(v, device->msg, rem)
465                 blobmsg_add_blob(b, v);
466 
467         mp = _find_mount_point(device->name);
468         if (mp) {
469                 blobmsg_add_string(b, "mount", mp);
470                 free(mp);
471         } else if (device->autofs && device->target) {
472                 blobmsg_add_string(b, "mount", device->target);
473         }
474 }
475 
476 static int
477 block_info(struct ubus_context *ctx, struct ubus_object *obj,
478            struct ubus_request_data *req, const char *method,
479            struct blob_attr *msg)
480 {
481         struct blob_attr *data[__INFO_MAX];
482         struct device *device = NULL;
483 
484         blobmsg_parse(info_policy, __INFO_MAX, data, blob_data(msg), blob_len(msg));
485 
486         if (data[INFO_DEVICE]) {
487                 device = vlist_find(&devices, blobmsg_get_string(data[INFO_DEVICE]), device, node);
488                 if (!device)
489                         return UBUS_STATUS_INVALID_ARGUMENT;
490         }
491 
492         blob_buf_init(&bb, 0);
493         if (device) {
494                 block_info_dump(&bb, device);
495         } else {
496                 void *a;
497 
498                 blobmsg_add_u8(&bb, "ready", blockd_ready);
499                 a = blobmsg_open_array(&bb, "devices");
500                 vlist_for_each_element(&devices, device, node) {
501                         void *t;
502 
503                         t = blobmsg_open_table(&bb, "");
504                         block_info_dump(&bb, device);
505                         blobmsg_close_table(&bb, t);
506                 }
507                 blobmsg_close_array(&bb, a);
508         }
509         ubus_send_reply(ctx, req, bb.head);
510 
511         return 0;
512 }
513 
514 static int
515 block_status(struct ubus_context *ctx, struct ubus_object *obj,
516              struct ubus_request_data *req, const char *method,
517              struct blob_attr *msg)
518 {
519         blob_buf_init(&bb, 0);
520         blobmsg_add_u8(&bb, "ready", blockd_ready);
521         ubus_send_reply(ctx, req, bb.head);
522 
523         return 0;
524 }
525 
526 static const struct ubus_method block_methods[] = {
527         UBUS_METHOD("hotplug", block_hotplug, mount_policy),
528         UBUS_METHOD("mount", blockd_mount, mount_policy),
529         UBUS_METHOD("umount", blockd_umount, mount_policy),
530         UBUS_METHOD("info", block_info, info_policy),
531         UBUS_METHOD_NOARG("status", block_status),
532 };
533 
534 static struct ubus_object_type block_object_type =
535         UBUS_OBJECT_TYPE("block", block_methods);
536 
537 static struct ubus_object block_object = {
538         .name = "block",
539         .type = &block_object_type,
540         .methods = block_methods,
541         .n_methods = ARRAY_SIZE(block_methods),
542 };
543 
544 /* send ubus event for successful mounts, useful for procd triggers */
545 static int send_block_notification(struct ubus_context *ctx, const char *action,
546                             const char *devname, const char *target)
547 {
548         struct blob_buf buf = { 0 };
549         char evname[16];
550         int err;
551 
552         if (!ctx)
553                 return -ENXIO;
554 
555         snprintf(evname, sizeof(evname), "mount.%s", action);
556 
557         blob_buf_init(&buf, 0);
558 
559         if (devname)
560                 blobmsg_add_string(&buf, "device", devname);
561 
562         if (target)
563                 blobmsg_add_string(&buf, "target", target);
564 
565         err = ubus_notify(ctx, &block_object, evname, buf.head, -1);
566 
567         return err;
568 }
569 
570 static void
571 ubus_connect_handler(struct ubus_context *ctx)
572 {
573         int ret;
574 
575         ret = ubus_add_object(ctx, &block_object);
576         if (ret)
577                 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
578 }
579 
580 static int autofs_umount(void)
581 {
582         umount2(AUTOFS_MOUNT_PATH, MNT_DETACH);
583         return 0;
584 }
585 
586 static void autofs_read_handler(struct uloop_fd *u, unsigned int events)
587 {
588         union autofs_v5_packet_union pktu;
589         const struct autofs_v5_packet *pkt;
590         int cmd = AUTOFS_IOC_READY;
591         struct stat st;
592         struct device *device;
593         struct blob_attr *data[__MOUNT_MAX];
594         char *options = NULL;
595         char optbuf[256];
596 
597         while (read(u->fd, &pktu, sizeof(pktu)) == -1) {
598                 if (errno != EINTR)
599                         return;
600                 continue;
601         }
602 
603         if (pktu.hdr.type != autofs_ptype_missing_indirect) {
604                 ULOG_ERR("unknown packet type %d\n", pktu.hdr.type);
605                 return;
606         }
607 
608         pkt = &pktu.missing_indirect;
609         ULOG_ERR("kernel is requesting a mount -> %s\n", pkt->name);
610 
611         /* pass the registrant's mount options (e.g. ro) down to block */
612         device = vlist_find(&devices, pkt->name, device, node);
613         if (device && device->msg) {
614                 blobmsg_parse(mount_policy, __MOUNT_MAX, data,
615                               blob_data(device->msg), blob_len(device->msg));
616                 if (data[MOUNT_OPTIONS])
617                         options = blobmsg_get_string(data[MOUNT_OPTIONS]);
618                 if (data[MOUNT_CHECK_FS] && blobmsg_get_u32(data[MOUNT_CHECK_FS])) {
619                         snprintf(optbuf, sizeof(optbuf), "%s%scheck_fs",
620                                  options ? options : "", options ? "," : "");
621                         options = optbuf;
622                 }
623         }
624 
625         if (lstat(pkt->name, &st) == -1)
626                 if (block("autofs", "add", (char *)pkt->name, options, 1, NULL))
627                         cmd = AUTOFS_IOC_FAIL;
628 
629         if (ioctl(fd_autofs_write, cmd, pkt->wait_queue_token) < 0)
630                 ULOG_ERR("failed to report back to kernel\n");
631 }
632 
633 static void autofs_expire(struct uloop_timeout *t)
634 {
635         struct autofs_packet_expire pkt;
636 
637         while (ioctl(fd_autofs_write, AUTOFS_IOC_EXPIRE, &pkt) == 0) {
638                 block("autofs", "remove", pkt.name, NULL, 1, NULL);
639                 /* the idle mount has been torn down, so its backing block device
640                  * now has no holder; signal subscribers (e.g. uvol's deferred reap)
641                  * that the device became free */
642                 send_block_notification(&conn.ctx, "umount", pkt.name, NULL);
643         }
644 
645         uloop_timeout_set(t, AUTOFS_EXPIRE_TIMER);
646 }
647 
648 struct uloop_timeout autofs_expire_timer = {
649         .cb = autofs_expire,
650 };
651 
652 static int autofs_mount(void)
653 {
654         unsigned long autofs_timeout = AUTOFS_TIMEOUT;
655         int kproto_version;
656         int pipefd[2];
657         char source[64];
658         char opts[64];
659 
660         if (pipe(pipefd) < 0) {
661                 ULOG_ERR("failed to get kernel pipe\n");
662                 return -1;
663         }
664 
665         snprintf(source, sizeof(source), "mountd(pid%u)", getpid());
666         snprintf(opts, sizeof(opts), "fd=%d,pgrp=%u,minproto=5,maxproto=5", pipefd[1], (unsigned) getpgrp());
667         mkdir(AUTOFS_MOUNT_PATH, 0555);
668         if (mount(source, AUTOFS_MOUNT_PATH, "autofs", 0, opts)) {
669                 ULOG_ERR("unable to mount autofs on %s\n", AUTOFS_MOUNT_PATH);
670                 close(pipefd[0]);
671                 close(pipefd[1]);
672                 return -1;
673         }
674         close(pipefd[1]);
675         fd_autofs_read.fd = pipefd[0];
676         fd_autofs_read.cb = autofs_read_handler;
677         uloop_fd_add(&fd_autofs_read, ULOOP_READ);
678 
679         fd_autofs_write = open(AUTOFS_MOUNT_PATH, O_RDONLY);
680         if(fd_autofs_write < 0) {
681                 autofs_umount();
682                 ULOG_ERR("failed to open direcory\n");
683                 return -1;
684         }
685 
686         ioctl(fd_autofs_write, AUTOFS_IOC_PROTOVER, &kproto_version);
687         if (kproto_version != 5) {
688                 ULOG_ERR("only kernel protocol version 5 is tested. You have %d.\n",
689                         kproto_version);
690                 exit(EXIT_FAILURE);
691         }
692         if (ioctl(fd_autofs_write, AUTOFS_IOC_SETTIMEOUT, &autofs_timeout))
693                 ULOG_ERR("failed to set autofs timeout\n");
694 
695         uloop_timeout_set(&autofs_expire_timer, AUTOFS_EXPIRE_TIMER);
696 
697         fcntl(fd_autofs_write, F_SETFD, fcntl(fd_autofs_write, F_GETFD) | FD_CLOEXEC);
698         fcntl(fd_autofs_read.fd, F_SETFD, fcntl(fd_autofs_read.fd, F_GETFD) | FD_CLOEXEC);
699 
700         return 0;
701 }
702 
703 static glob_t coldplug_gl;
704 static size_t coldplug_next_idx;
705 
706 static void startup_expire(struct uloop_timeout *t);
707 
708 static struct uloop_timeout startup_timer = {
709         .cb = startup_expire,
710 };
711 
712 static void startup_ready(void)
713 {
714         if (blockd_ready)
715                 return;
716 
717         uloop_timeout_cancel(&startup_timer);
718         blockd_ready = 1;
719         send_block_notification(&conn.ctx, "ready", NULL, NULL);
720 }
721 
722 /*
723  * A coldplug helper is allowed to take as long as it needs, e.g. while fsck
724  * repairs a large volume, and interrupting it would leave that volume in a worse
725  * state than not mounting it at all. Announce readiness anyway once the deadline
726  * passes and let the remaining helpers run to completion: the devices they do
727  * register still emit their own notifications, and a consumer which has to wait
728  * for a specific volume can wait for that instead of for a level which may never
729  * be reached.
730  */
731 static void startup_expire(struct uloop_timeout *t)
732 {
733         ULOG_WARN("startup still busy after %ds, announcing readiness anyway\n",
734                   STARTUP_TIMEOUT / 1000);
735 
736         startup_ready();
737 }
738 
739 /*
740  * block's autofs and hotplug actions call back into this daemon over ubus, so
741  * waiting for a helper synchronously would starve our own ubus loop and make
742  * the call time out. Reap the child via uloop instead and run the next one from
743  * its callback, keeping the loop alive while at most one helper is in flight.
744  */
745 static bool startup_spawn(char *const argv[], char *const envp[],
746                           uloop_process_handler cb)
747 {
748         struct uloop_process *p;
749         pid_t pid;
750 
751         p = calloc(1, sizeof(*p));
752         if (!p)
753                 return false;
754 
755         pid = fork();
756         if (pid < 0) {
757                 ULOG_ERR("failed to fork block %s\n", argv[1]);
758                 free(p);
759                 return false;
760         }
761 
762         if (!pid) {
763                 uloop_end();
764                 if (envp)
765                         execve(argv[0], argv, envp);
766                 else
767                         execvp(argv[0], argv);
768                 _exit(EXIT_FAILURE);
769         }
770 
771         p->pid = pid;
772         p->cb = cb;
773         uloop_process_add(p);
774 
775         return true;
776 }
777 
778 static void coldplug_next(void);
779 
780 static void coldplug_cb(struct uloop_process *p, int stat)
781 {
782         free(p);
783         coldplug_next();
784 }
785 
786 static void coldplug_next(void)
787 {
788         char *argv[] = { "/sbin/block", "hotplug", NULL };
789         char *env[] = { "ACTION=add", NULL, NULL };
790 
791         while (coldplug_next_idx < coldplug_gl.gl_pathc) {
792                 char *dev = coldplug_gl.gl_pathv[coldplug_next_idx++];
793                 bool spawned;
794 
795                 if (asprintf(&env[1], "DEVNAME=%s", basename(dev)) == -1)
796                         continue;
797 
798                 spawned = startup_spawn(argv, env, coldplug_cb);
799                 free(env[1]);
800 
801                 if (spawned)
802                         return;
803         }
804 
805         globfree(&coldplug_gl);
806         startup_ready();
807 }
808 
809 static void coldplug(void)
810 {
811         if (glob("/sys/class/block/*", GLOB_NOESCAPE | GLOB_MARK, NULL,
812                  &coldplug_gl)) {
813                 startup_ready();
814                 return;
815         }
816 
817         coldplug_next();
818 }
819 
820 static void autofs_start_cb(struct uloop_process *p, int stat)
821 {
822         free(p);
823         coldplug();
824 }
825 
826 static void blockd_startup(struct uloop_timeout *t)
827 {
828         char *argv[] = { "/sbin/block", "autofs", "start", NULL };
829 
830         uloop_timeout_set(&startup_timer, STARTUP_TIMEOUT);
831 
832         if (!startup_spawn(argv, NULL, autofs_start_cb))
833                 coldplug();
834 }
835 
836 struct uloop_timeout startup = {
837         .cb = blockd_startup,
838 };
839 
840 int main(int argc, char **argv)
841 {
842         /* make sure blockd is in it's own POSIX process group */
843         setpgrp();
844 
845         ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "blockd");
846         uloop_init();
847 
848         autofs_mount();
849 
850         conn.cb = ubus_connect_handler;
851         ubus_auto_connect(&conn);
852 
853         uloop_timeout_set(&startup, 1000);
854 
855         uloop_run();
856         uloop_done();
857 
858         autofs_umount();
859 
860         vlist_flush_all(&devices);
861 
862         return 0;
863 }
864 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt