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