1 /* 2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org> 3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public License version 2.1 7 * as published by the Free Software Foundation 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15 #include <sys/stat.h> 16 #include <sys/socket.h> 17 #include <sys/types.h> 18 #include <sys/sysmacros.h> 19 20 #include <linux/types.h> 21 #include <linux/netlink.h> 22 23 #include <libubox/blobmsg_json.h> 24 #include <libubox/json_script.h> 25 #include <libubox/uloop.h> 26 #include <libubox/utils.h> 27 #include <json-c/json.h> 28 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <stdlib.h> 33 #include <libgen.h> 34 #include <grp.h> 35 36 #include "../procd.h" 37 38 #include "hotplug.h" 39 40 #define HOTPLUG_WAIT 500 41 42 struct cmd_handler; 43 struct cmd_queue { 44 struct list_head list; 45 46 struct blob_attr *msg; 47 struct blob_attr *data; 48 int timeout; 49 50 void (*handler)(struct blob_attr *msg, struct blob_attr *data); 51 void (*start)(struct blob_attr *msg, struct blob_attr *data); 52 void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret); 53 }; 54 55 struct button_timeout { 56 struct list_head list; 57 struct uloop_timeout timeout; 58 char *name; 59 int seen; 60 struct blob_attr *data; 61 }; 62 63 static LIST_HEAD(cmd_queue); 64 static LIST_HEAD(button_timer); 65 static struct uloop_process queue_proc; 66 static struct uloop_timeout last_event; 67 static struct blob_buf b, button_buf; 68 static char *rule_file; 69 static struct blob_buf script; 70 static struct cmd_queue *current; 71 72 static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data); 73 static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret); 74 75 static void button_free(struct button_timeout *b) 76 { 77 uloop_timeout_cancel(&b->timeout); 78 list_del(&b->list); 79 free(b->data); 80 free(b->name); 81 free(b); 82 } 83 84 static void button_timeout_remove(char *button) 85 { 86 struct button_timeout *b, *c; 87 88 if (!list_empty(&button_timer)) list_for_each_entry_safe(b, c, &button_timer, list) { 89 if (!strcmp(b->name, button)) 90 button_free(b); 91 } 92 } 93 94 static char *hotplug_msg_find_var(struct blob_attr *msg, const char *name) 95 { 96 struct blob_attr *cur; 97 int rem; 98 99 blobmsg_for_each_attr(cur, msg, rem) { 100 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 101 continue; 102 103 if (strcmp(blobmsg_name(cur), name) != 0) 104 continue; 105 106 return blobmsg_data(cur); 107 } 108 109 return NULL; 110 } 111 112 static void chgrp_error(const char *group, const char *target, const char *failed) 113 { 114 ERROR("cannot set group %s for %s (%s: %d)\n", 115 group, target, failed, errno); 116 } 117 118 static void chgrp_target(struct blob_attr *bgroup, struct blob_attr *btarget) 119 { 120 int ret = 0; 121 struct group *g = NULL; 122 const char *group = blobmsg_get_string(bgroup); 123 const char *target = blobmsg_get_string(btarget); 124 125 errno = 0; 126 127 g = getgrnam(group); 128 if (!g) 129 return chgrp_error(group, target, "getgrnam"); 130 131 ret = chown(target, 0, g->gr_gid); 132 if (ret < 0) 133 return chgrp_error(group, target, "chown"); 134 } 135 136 static void handle_makedev(struct blob_attr *msg, struct blob_attr *data) 137 { 138 unsigned int oldumask = umask(0); 139 static struct blobmsg_policy mkdev_policy[3] = { 140 { .type = BLOBMSG_TYPE_STRING }, 141 { .type = BLOBMSG_TYPE_STRING }, 142 { .type = BLOBMSG_TYPE_STRING }, 143 }; 144 struct blob_attr *tb[3]; 145 char *minor = hotplug_msg_find_var(msg, "MINOR"); 146 char *major = hotplug_msg_find_var(msg, "MAJOR"); 147 char *subsystem = hotplug_msg_find_var(msg, "SUBSYSTEM"); 148 149 blobmsg_parse_array(mkdev_policy, 3, tb, blobmsg_data(data), blobmsg_data_len(data)); 150 if (tb[0] && tb[1] && minor && major && subsystem) { 151 mode_t m = S_IFCHR; 152 char *d = strdup(blobmsg_get_string(tb[0])); 153 154 d = dirname(d); 155 mkdir_p(d, 0755); 156 free(d); 157 158 if (!strcmp(subsystem, "block")) 159 m = S_IFBLK; 160 mknod(blobmsg_get_string(tb[0]), 161 m | strtoul(blobmsg_data(tb[1]), NULL, 8), 162 makedev(atoi(major), atoi(minor))); 163 if (tb[2]) 164 chgrp_target(tb[2], tb[0]); 165 } 166 umask(oldumask); 167 } 168 169 static void handle_rm(struct blob_attr *msg, struct blob_attr *data) 170 { 171 static struct blobmsg_policy rm_policy = { 172 .type = BLOBMSG_TYPE_STRING, 173 }; 174 struct blob_attr *tb; 175 176 blobmsg_parse_array(&rm_policy, 1, &tb, blobmsg_data(data), blobmsg_data_len(data)); 177 if (tb) 178 unlink(blobmsg_data(tb)); 179 } 180 181 static void handle_exec(struct blob_attr *msg, struct blob_attr *data) 182 { 183 char *argv[8]; 184 struct blob_attr *cur; 185 int rem, fd; 186 int i = 0; 187 188 blobmsg_for_each_attr(cur, msg, rem) 189 setenv(blobmsg_name(cur), blobmsg_data(cur), 1); 190 191 blobmsg_for_each_attr(cur, data, rem) { 192 argv[i] = blobmsg_data(cur); 193 i++; 194 if (i == 7) 195 break; 196 } 197 198 if (debug < 3) { 199 fd = open("/dev/null", O_RDWR); 200 if (fd > -1) { 201 dup2(fd, STDIN_FILENO); 202 dup2(fd, STDOUT_FILENO); 203 dup2(fd, STDERR_FILENO); 204 if (fd > STDERR_FILENO) 205 close(fd); 206 } 207 } 208 209 if (i > 0) { 210 argv[i] = NULL; 211 execvp(argv[0], &argv[0]); 212 } 213 exit(EXIT_FAILURE); 214 } 215 216 static void handle_button_start(struct blob_attr *msg, struct blob_attr *data) 217 { 218 char *button = hotplug_msg_find_var(msg, "BUTTON"); 219 220 if (button) 221 button_timeout_remove(button); 222 } 223 224 static void handle_firmware(struct blob_attr *msg, struct blob_attr *data) 225 { 226 char *dir = blobmsg_get_string(blobmsg_data(data)); 227 char *file = hotplug_msg_find_var(msg, "FIRMWARE"); 228 char *dev = hotplug_msg_find_var(msg, "DEVPATH"); 229 struct stat s = { 0 }; 230 char *path, loadpath[256], syspath[256]; 231 int fw, src, load, len; 232 static char buf[4096]; 233 234 P_DEBUG(2, "Firmware request for %s/%s\n", dir, file); 235 236 if (!file || !dir || !dev) { 237 ERROR("Request for unknown firmware %s/%s\n", dir, file); 238 exit(EXIT_FAILURE); 239 } 240 241 path = alloca(strlen(dir) + strlen(file) + 2); 242 sprintf(path, "%s/%s", dir, file); 243 244 if (stat(path, &s)) { 245 ERROR("Could not find firmware %s: %m\n", path); 246 src = -1; 247 s.st_size = 0; 248 goto send_to_kernel; 249 } 250 251 src = open(path, O_RDONLY); 252 if (src < 0) { 253 ERROR("Failed to open %s: %m\n", path); 254 s.st_size = 0; 255 goto send_to_kernel; 256 } 257 258 send_to_kernel: 259 snprintf(loadpath, sizeof(loadpath), "/sys/%s/loading", dev); 260 load = open(loadpath, O_WRONLY); 261 if (!load) { 262 ERROR("Failed to open %s: %m\n", loadpath); 263 exit(EXIT_FAILURE); 264 } 265 if (write(load, "1", 1) == -1) { 266 ERROR("Failed to write to %s: %m\n", loadpath); 267 exit(EXIT_FAILURE); 268 } 269 close(load); 270 271 snprintf(syspath, sizeof(syspath), "/sys/%s/data", dev); 272 fw = open(syspath, O_WRONLY); 273 if (fw < 0) { 274 ERROR("Failed to open %s: %m\n", syspath); 275 exit(EXIT_FAILURE); 276 } 277 278 len = s.st_size; 279 while (len) { 280 len = read(src, buf, sizeof(buf)); 281 if (len <= 0) 282 break; 283 284 if (write(fw, buf, len) == -1) { 285 ERROR("failed to write firmware file %s/%s to %s: %m\n", dir, file, dev); 286 break; 287 } 288 } 289 290 if (src >= 0) 291 close(src); 292 close(fw); 293 294 load = open(loadpath, O_WRONLY); 295 if (write(load, "", 1) == -1) 296 ERROR("failed to write to %s: %m\n", loadpath); 297 close(load); 298 299 P_DEBUG(2, "Done loading %s\n", path); 300 301 exit(EXIT_FAILURE); 302 } 303 304 static void handle_start_console(struct blob_attr *msg, struct blob_attr *data) 305 { 306 char *dev = blobmsg_get_string(blobmsg_data(data)); 307 308 P_DEBUG(2, "Start console request for %s\n", dev); 309 310 procd_inittab_run("respawn"); 311 procd_inittab_run("askfirst"); 312 313 P_DEBUG(2, "Done starting console for %s\n", dev); 314 315 exit(EXIT_FAILURE); 316 } 317 318 enum { 319 HANDLER_MKDEV = 0, 320 HANDLER_RM, 321 HANDLER_EXEC, 322 HANDLER_BUTTON, 323 HANDLER_FW, 324 HANDLER_START_CONSOLE, 325 }; 326 327 static struct cmd_handler { 328 char *name; 329 int atomic; 330 void (*handler)(struct blob_attr *msg, struct blob_attr *data); 331 void (*start)(struct blob_attr *msg, struct blob_attr *data); 332 void (*complete)(struct blob_attr *msg, struct blob_attr *data, int ret); 333 } handlers[] = { 334 [HANDLER_MKDEV] = { 335 .name = "makedev", 336 .atomic = 1, 337 .handler = handle_makedev, 338 }, 339 [HANDLER_RM] = { 340 .name = "rm", 341 .atomic = 1, 342 .handler = handle_rm, 343 }, 344 [HANDLER_EXEC] = { 345 .name = "exec", 346 .handler = handle_exec, 347 }, 348 [HANDLER_BUTTON] = { 349 .name = "button", 350 .handler = handle_exec, 351 .start = handle_button_start, 352 .complete = handle_button_complete, 353 }, 354 [HANDLER_FW] = { 355 .name = "load-firmware", 356 .handler = handle_firmware, 357 }, 358 [HANDLER_START_CONSOLE] = { 359 .name = "start-console", 360 .handler = handle_start_console, 361 }, 362 }; 363 364 static void queue_next(void) 365 { 366 struct cmd_queue *c; 367 368 if (queue_proc.pending || list_empty(&cmd_queue)) 369 return; 370 371 c = list_first_entry(&cmd_queue, struct cmd_queue, list); 372 373 queue_proc.pid = fork(); 374 if (!queue_proc.pid) { 375 uloop_done(); 376 c->handler(c->msg, c->data); 377 exit(0); 378 } 379 if (c->start) 380 c->start(c->msg, c->data); 381 list_del(&c->list); 382 if (c->complete) 383 current = c; 384 else 385 free(c); 386 if (queue_proc.pid <= 0) { 387 queue_next(); 388 return; 389 } 390 391 uloop_process_add(&queue_proc); 392 393 P_DEBUG(4, "Launched hotplug exec instance, pid=%d\n", (int) queue_proc.pid); 394 } 395 396 static void queue_proc_cb(struct uloop_process *c, int ret) 397 { 398 P_DEBUG(4, "Finished hotplug exec instance, pid=%d\n", (int) c->pid); 399 400 if (current) { 401 current->complete(current->msg, current->data, ret); 402 free(current); 403 current = NULL; 404 } 405 queue_next(); 406 } 407 408 static void queue_add(struct cmd_handler *h, struct blob_attr *msg, struct blob_attr *data) 409 { 410 struct cmd_queue *c = NULL; 411 struct blob_attr *_msg, *_data; 412 413 c = calloc_a(sizeof(struct cmd_queue), 414 &_msg, blob_pad_len(msg), 415 &_data, blob_pad_len(data), 416 NULL); 417 418 if (!c) 419 return; 420 421 c->msg = _msg; 422 c->data = _data; 423 424 memcpy(c->msg, msg, blob_pad_len(msg)); 425 memcpy(c->data, data, blob_pad_len(data)); 426 c->handler = h->handler; 427 c->complete = h->complete; 428 c->start = h->start; 429 list_add_tail(&c->list, &cmd_queue); 430 queue_next(); 431 } 432 433 static void handle_button_timeout(struct uloop_timeout *t) 434 { 435 struct button_timeout *b; 436 char seen[16]; 437 438 b = container_of(t, struct button_timeout, timeout); 439 blob_buf_init(&button_buf, 0); 440 blobmsg_add_string(&button_buf, "BUTTON", b->name); 441 blobmsg_add_string(&button_buf, "ACTION", "timeout"); 442 snprintf(seen, sizeof(seen), "%d", b->seen); 443 blobmsg_add_string(&button_buf, "SEEN", seen); 444 queue_add(&handlers[HANDLER_EXEC], button_buf.head, b->data); 445 button_free(b); 446 } 447 448 static void handle_button_complete(struct blob_attr *msg, struct blob_attr *data, int ret) 449 { 450 char *name = hotplug_msg_find_var(msg, "BUTTON"); 451 struct button_timeout *b; 452 int timeout = ret >> 8; 453 454 if (!timeout) 455 return; 456 457 if (!name) 458 return; 459 460 b = calloc(1, sizeof(*b)); 461 if (!b) 462 return; 463 464 b->data = malloc(blob_pad_len(data)); 465 b->name = strdup(name); 466 b->seen = timeout; 467 468 memcpy(b->data, data, blob_pad_len(data)); 469 b->timeout.cb = handle_button_timeout; 470 471 uloop_timeout_set(&b->timeout, timeout * 1000); 472 list_add(&b->list, &button_timer); 473 } 474 475 static const char* rule_handle_var(struct json_script_ctx *ctx, const char *name, struct blob_attr *vars) 476 { 477 const char *str, *sep; 478 479 if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) { 480 str = json_script_find_var(ctx, vars, "DEVPATH"); 481 if (!str) 482 return NULL; 483 484 sep = strrchr(str, '/'); 485 if (sep) 486 return sep + 1; 487 488 return str; 489 } 490 491 return NULL; 492 } 493 494 static struct json_script_file * 495 rule_handle_file(struct json_script_ctx *ctx, const char *name) 496 { 497 json_object *obj; 498 499 obj = json_object_from_file((char*)name); 500 if (!obj) 501 return NULL; 502 503 blob_buf_init(&script, 0); 504 blobmsg_add_json_element(&script, "", obj); 505 506 return json_script_file_from_blobmsg(name, blob_data(script.head), blob_len(script.head)); 507 } 508 509 static void rule_handle_command(struct json_script_ctx *ctx, const char *name, 510 struct blob_attr *data, struct blob_attr *vars) 511 { 512 struct blob_attr *cur; 513 int rem, i; 514 515 if (debug > 3) { 516 P_DEBUG(4, "Command: %s\n", name); 517 blobmsg_for_each_attr(cur, data, rem) 518 P_DEBUG(4, " %s\n", (char *) blobmsg_data(cur)); 519 520 P_DEBUG(4, "Message:\n"); 521 blobmsg_for_each_attr(cur, vars, rem) 522 P_DEBUG(4, " %s=%s\n", blobmsg_name(cur), (char *) blobmsg_data(cur)); 523 } 524 525 for (i = 0; i < ARRAY_SIZE(handlers); i++) 526 if (!strcmp(handlers[i].name, name)) { 527 if (handlers[i].atomic) 528 handlers[i].handler(vars, data); 529 else 530 queue_add(&handlers[i], vars, data); 531 break; 532 } 533 534 if (last_event.cb) 535 uloop_timeout_set(&last_event, HOTPLUG_WAIT); 536 } 537 538 static void rule_handle_error(struct json_script_ctx *ctx, const char *msg, 539 struct blob_attr *context) 540 { 541 char *s; 542 543 s = blobmsg_format_json(context, false); 544 ERROR("ERROR: %s in block: %s\n", msg, s); 545 free(s); 546 } 547 548 static struct json_script_ctx jctx = { 549 .handle_var = rule_handle_var, 550 .handle_error = rule_handle_error, 551 .handle_command = rule_handle_command, 552 .handle_file = rule_handle_file, 553 }; 554 555 static void hotplug_handler_debug(struct blob_attr *data) 556 { 557 char *str; 558 559 if (debug < 3) 560 return; 561 562 str = blobmsg_format_json(data, true); 563 P_DEBUG(3, "%s\n", str); 564 free(str); 565 } 566 567 static void hotplug_handler(struct uloop_fd *u, unsigned int ev) 568 { 569 int i = 0; 570 static char buf[4096]; 571 int len = recv(u->fd, buf, sizeof(buf) - 1, MSG_DONTWAIT); 572 void *index; 573 if (len < 1) 574 return; 575 576 buf[len] = '\0'; 577 578 blob_buf_init(&b, 0); 579 index = blobmsg_open_table(&b, NULL); 580 while (i < len) { 581 int l = strlen(buf + i) + 1; 582 char *e = strstr(&buf[i], "="); 583 584 if (e) { 585 *e = '\0'; 586 blobmsg_add_string(&b, &buf[i], &e[1]); 587 } 588 i += l; 589 } 590 blobmsg_close_table(&b, index); 591 hotplug_handler_debug(b.head); 592 json_script_run(&jctx, rule_file, blob_data(b.head)); 593 } 594 595 static struct uloop_fd hotplug_fd = { 596 .cb = hotplug_handler, 597 }; 598 599 void hotplug_last_event(uloop_timeout_handler handler) 600 { 601 last_event.cb = handler; 602 if (handler) 603 uloop_timeout_set(&last_event, HOTPLUG_WAIT); 604 else 605 uloop_timeout_cancel(&last_event); 606 } 607 608 void hotplug(char *rules) 609 { 610 struct sockaddr_nl nls = {}; 611 int nlbufsize = 512 * 1024; 612 613 rule_file = strdup(rules); 614 nls.nl_family = AF_NETLINK; 615 nls.nl_pid = 0; 616 nls.nl_groups = -1; 617 618 if ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) { 619 ERROR("Failed to open hotplug socket: %m\n"); 620 exit(1); 621 } 622 if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) { 623 ERROR("Failed to bind hotplug socket: %m\n"); 624 exit(1); 625 } 626 627 if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize))) 628 ERROR("Failed to resize receive buffer: %m\n"); 629 630 json_script_init(&jctx); 631 queue_proc.cb = queue_proc_cb; 632 uloop_fd_add(&hotplug_fd, ULOOP_READ); 633 } 634 635 int hotplug_run(char *rules) 636 { 637 uloop_init(); 638 hotplug(rules); 639 uloop_run(); 640 641 return 0; 642 } 643 644 void hotplug_shutdown(void) 645 { 646 uloop_fd_delete(&hotplug_fd); 647 close(hotplug_fd.fd); 648 } 649
This page was automatically generated by LXR 0.3.1. • OpenWrt