devices.c revision bb738d6727f589240dbd31b59e5747eab62ab59f
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <stddef.h> 19#include <stdio.h> 20#include <stdlib.h> 21#include <sys/stat.h> 22#include <sys/types.h> 23 24#include <fcntl.h> 25#include <dirent.h> 26#include <unistd.h> 27#include <string.h> 28 29#include <sys/socket.h> 30#include <sys/un.h> 31#include <linux/netlink.h> 32#include <private/android_filesystem_config.h> 33#include <sys/time.h> 34#include <asm/page.h> 35#include <sys/wait.h> 36 37#include "devices.h" 38#include "util.h" 39#include "log.h" 40#include "list.h" 41 42#define SYSFS_PREFIX "/sys" 43#define FIRMWARE_DIR1 "/etc/firmware" 44#define FIRMWARE_DIR2 "/vendor/firmware" 45 46static int device_fd = -1; 47 48struct uevent { 49 const char *action; 50 const char *path; 51 const char *subsystem; 52 const char *firmware; 53 const char *partition_name; 54 int partition_num; 55 int major; 56 int minor; 57}; 58 59static int open_uevent_socket(void) 60{ 61 struct sockaddr_nl addr; 62 int sz = 64*1024; // XXX larger? udev uses 16MB! 63 int on = 1; 64 int s; 65 66 memset(&addr, 0, sizeof(addr)); 67 addr.nl_family = AF_NETLINK; 68 addr.nl_pid = getpid(); 69 addr.nl_groups = 0xffffffff; 70 71 s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 72 if(s < 0) 73 return -1; 74 75 setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)); 76 setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); 77 78 if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 79 close(s); 80 return -1; 81 } 82 83 return s; 84} 85 86struct perms_ { 87 char *name; 88 char *attr; 89 mode_t perm; 90 unsigned int uid; 91 unsigned int gid; 92 unsigned short prefix; 93}; 94 95struct perm_node { 96 struct perms_ dp; 97 struct listnode plist; 98}; 99 100struct platform_node { 101 char *name; 102 int name_len; 103 struct listnode list; 104}; 105 106static list_declare(sys_perms); 107static list_declare(dev_perms); 108static list_declare(platform_names); 109 110int add_dev_perms(const char *name, const char *attr, 111 mode_t perm, unsigned int uid, unsigned int gid, 112 unsigned short prefix) { 113 struct perm_node *node = calloc(1, sizeof(*node)); 114 if (!node) 115 return -ENOMEM; 116 117 node->dp.name = strdup(name); 118 if (!node->dp.name) 119 return -ENOMEM; 120 121 if (attr) { 122 node->dp.attr = strdup(attr); 123 if (!node->dp.attr) 124 return -ENOMEM; 125 } 126 127 node->dp.perm = perm; 128 node->dp.uid = uid; 129 node->dp.gid = gid; 130 node->dp.prefix = prefix; 131 132 if (attr) 133 list_add_tail(&sys_perms, &node->plist); 134 else 135 list_add_tail(&dev_perms, &node->plist); 136 137 return 0; 138} 139 140void fixup_sys_perms(const char *upath) 141{ 142 char buf[512]; 143 struct listnode *node; 144 struct perms_ *dp; 145 146 /* upaths omit the "/sys" that paths in this list 147 * contain, so we add 4 when comparing... 148 */ 149 list_for_each(node, &sys_perms) { 150 dp = &(node_to_item(node, struct perm_node, plist))->dp; 151 if (dp->prefix) { 152 if (strncmp(upath, dp->name + 4, strlen(dp->name + 4))) 153 continue; 154 } else { 155 if (strcmp(upath, dp->name + 4)) 156 continue; 157 } 158 159 if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf)) 160 return; 161 162 sprintf(buf,"/sys%s/%s", upath, dp->attr); 163 INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); 164 chown(buf, dp->uid, dp->gid); 165 chmod(buf, dp->perm); 166 } 167} 168 169static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid) 170{ 171 mode_t perm; 172 struct listnode *node; 173 struct perm_node *perm_node; 174 struct perms_ *dp; 175 176 /* search the perms list in reverse so that ueventd.$hardware can 177 * override ueventd.rc 178 */ 179 list_for_each_reverse(node, &dev_perms) { 180 perm_node = node_to_item(node, struct perm_node, plist); 181 dp = &perm_node->dp; 182 183 if (dp->prefix) { 184 if (strncmp(path, dp->name, strlen(dp->name))) 185 continue; 186 } else { 187 if (strcmp(path, dp->name)) 188 continue; 189 } 190 *uid = dp->uid; 191 *gid = dp->gid; 192 return dp->perm; 193 } 194 /* Default if nothing found. */ 195 *uid = 0; 196 *gid = 0; 197 return 0600; 198} 199 200static void make_device(const char *path, 201 const char *upath, 202 int block, int major, int minor) 203{ 204 unsigned uid; 205 unsigned gid; 206 mode_t mode; 207 dev_t dev; 208 209 mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); 210 dev = makedev(major, minor); 211 /* Temporarily change egid to avoid race condition setting the gid of the 212 * device node. Unforunately changing the euid would prevent creation of 213 * some device nodes, so the uid has to be set with chown() and is still 214 * racy. Fixing the gid race at least fixed the issue with system_server 215 * opening dynamic input devices under the AID_INPUT gid. */ 216 setegid(gid); 217 mknod(path, mode, dev); 218 chown(path, uid, -1); 219 setegid(AID_ROOT); 220} 221 222static void add_platform_device(const char *name) 223{ 224 int name_len = strlen(name); 225 struct listnode *node; 226 struct platform_node *bus; 227 228 list_for_each_reverse(node, &platform_names) { 229 bus = node_to_item(node, struct platform_node, list); 230 if ((bus->name_len < name_len) && 231 (name[bus->name_len] == '/') && 232 !strncmp(name, bus->name, bus->name_len)) 233 /* subdevice of an existing platform, ignore it */ 234 return; 235 } 236 237 INFO("adding platform device %s\n", name); 238 239 bus = calloc(1, sizeof(struct platform_node)); 240 bus->name = strdup(name); 241 bus->name_len = name_len; 242 list_add_tail(&platform_names, &bus->list); 243} 244 245/* 246 * given a name that may start with a platform device, find the length of the 247 * platform device prefix. If it doesn't start with a platform device, return 248 * 0. 249 */ 250static const char *find_platform_device(const char *name) 251{ 252 int name_len = strlen(name); 253 struct listnode *node; 254 struct platform_node *bus; 255 256 list_for_each_reverse(node, &platform_names) { 257 bus = node_to_item(node, struct platform_node, list); 258 if ((bus->name_len < name_len) && 259 (name[bus->name_len] == '/') && 260 !strncmp(name, bus->name, bus->name_len)) 261 return bus->name; 262 } 263 264 return NULL; 265} 266 267static void remove_platform_device(const char *name) 268{ 269 struct listnode *node; 270 struct platform_node *bus; 271 272 list_for_each_reverse(node, &platform_names) { 273 bus = node_to_item(node, struct platform_node, list); 274 if (!strcmp(name, bus->name)) { 275 INFO("removing platform device %s\n", name); 276 free(bus->name); 277 list_remove(node); 278 free(bus); 279 return; 280 } 281 } 282} 283 284#if LOG_UEVENTS 285 286static inline suseconds_t get_usecs(void) 287{ 288 struct timeval tv; 289 gettimeofday(&tv, 0); 290 return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec; 291} 292 293#define log_event_print(x...) INFO(x) 294 295#else 296 297#define log_event_print(fmt, args...) do { } while (0) 298#define get_usecs() 0 299 300#endif 301 302static void parse_event(const char *msg, struct uevent *uevent) 303{ 304 uevent->action = ""; 305 uevent->path = ""; 306 uevent->subsystem = ""; 307 uevent->firmware = ""; 308 uevent->major = -1; 309 uevent->minor = -1; 310 uevent->partition_name = NULL; 311 uevent->partition_num = -1; 312 313 /* currently ignoring SEQNUM */ 314 while(*msg) { 315 if(!strncmp(msg, "ACTION=", 7)) { 316 msg += 7; 317 uevent->action = msg; 318 } else if(!strncmp(msg, "DEVPATH=", 8)) { 319 msg += 8; 320 uevent->path = msg; 321 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) { 322 msg += 10; 323 uevent->subsystem = msg; 324 } else if(!strncmp(msg, "FIRMWARE=", 9)) { 325 msg += 9; 326 uevent->firmware = msg; 327 } else if(!strncmp(msg, "MAJOR=", 6)) { 328 msg += 6; 329 uevent->major = atoi(msg); 330 } else if(!strncmp(msg, "MINOR=", 6)) { 331 msg += 6; 332 uevent->minor = atoi(msg); 333 } else if(!strncmp(msg, "PARTN=", 6)) { 334 msg += 6; 335 uevent->partition_num = atoi(msg); 336 } else if(!strncmp(msg, "PARTNAME=", 9)) { 337 msg += 9; 338 uevent->partition_name = msg; 339 } 340 341 /* advance to after the next \0 */ 342 while(*msg++) 343 ; 344 } 345 346 log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n", 347 uevent->action, uevent->path, uevent->subsystem, 348 uevent->firmware, uevent->major, uevent->minor); 349} 350 351static char **get_character_device_symlinks(struct uevent *uevent) 352{ 353 const char *parent; 354 char *slash; 355 char **links; 356 int link_num = 0; 357 int width; 358 359 if (strncmp(uevent->path, "/devices/platform/", 18)) 360 return NULL; 361 362 links = malloc(sizeof(char *) * 2); 363 if (!links) 364 return NULL; 365 memset(links, 0, sizeof(char *) * 2); 366 367 /* skip "/devices/platform/<driver>" */ 368 parent = strchr(uevent->path + 18, '/'); 369 if (!*parent) 370 goto err; 371 372 if (!strncmp(parent, "/usb", 4)) { 373 /* skip root hub name and device. use device interface */ 374 while (*++parent && *parent != '/'); 375 if (*parent) 376 while (*++parent && *parent != '/'); 377 if (!*parent) 378 goto err; 379 slash = strchr(++parent, '/'); 380 if (!slash) 381 goto err; 382 width = slash - parent; 383 if (width <= 0) 384 goto err; 385 386 if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0) 387 link_num++; 388 else 389 links[link_num] = NULL; 390 mkdir("/dev/usb", 0755); 391 } 392 else { 393 goto err; 394 } 395 396 return links; 397err: 398 free(links); 399 return NULL; 400} 401 402static char **parse_platform_block_device(struct uevent *uevent) 403{ 404 const char *device; 405 const char *path; 406 char *slash; 407 int width; 408 char buf[256]; 409 char link_path[256]; 410 int fd; 411 int link_num = 0; 412 int ret; 413 char *p; 414 unsigned int size; 415 struct stat info; 416 417 char **links = malloc(sizeof(char *) * 4); 418 if (!links) 419 return NULL; 420 memset(links, 0, sizeof(char *) * 4); 421 422 /* Drop "/devices/platform/" */ 423 path = uevent->path; 424 device = path + 18; 425 device = find_platform_device(device); 426 if (!device) 427 goto err; 428 429 INFO("found platform device %s\n", device); 430 431 snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device); 432 433 if (uevent->partition_name) { 434 p = strdup(uevent->partition_name); 435 sanitize(p); 436 if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0) 437 link_num++; 438 else 439 links[link_num] = NULL; 440 free(p); 441 } 442 443 if (uevent->partition_num >= 0) { 444 if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0) 445 link_num++; 446 else 447 links[link_num] = NULL; 448 } 449 450 slash = strrchr(path, '/'); 451 if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) 452 link_num++; 453 else 454 links[link_num] = NULL; 455 456 return links; 457 458err: 459 free(links); 460 return NULL; 461} 462 463static void handle_device(const char *action, const char *devpath, 464 const char *path, int block, int major, int minor, char **links) 465{ 466 int i; 467 468 if(!strcmp(action, "add")) { 469 make_device(devpath, path, block, major, minor); 470 if (links) { 471 for (i = 0; links[i]; i++) 472 make_link(devpath, links[i]); 473 } 474 } 475 476 if(!strcmp(action, "remove")) { 477 if (links) { 478 for (i = 0; links[i]; i++) 479 remove_link(devpath, links[i]); 480 } 481 unlink(devpath); 482 } 483 484 if (links) { 485 for (i = 0; links[i]; i++) 486 free(links[i]); 487 free(links); 488 } 489} 490 491static void handle_platform_device_event(struct uevent *uevent) 492{ 493 const char *name = uevent->path + 18; /* length of /devices/platform/ */ 494 495 if (!strcmp(uevent->action, "add")) 496 add_platform_device(name); 497 else if (!strcmp(uevent->action, "remove")) 498 remove_platform_device(name); 499} 500 501static const char *parse_device_name(struct uevent *uevent, unsigned int len) 502{ 503 const char *name; 504 505 /* if it's not a /dev device, nothing else to do */ 506 if((uevent->major < 0) || (uevent->minor < 0)) 507 return NULL; 508 509 /* do we have a name? */ 510 name = strrchr(uevent->path, '/'); 511 if(!name) 512 return NULL; 513 name++; 514 515 /* too-long names would overrun our buffer */ 516 if(strlen(name) > len) 517 return NULL; 518 519 return name; 520} 521 522static void handle_block_device_event(struct uevent *uevent) 523{ 524 const char *base = "/dev/block/"; 525 const char *name; 526 char devpath[96]; 527 char **links = NULL; 528 529 name = parse_device_name(uevent, 64); 530 if (!name) 531 return; 532 533 snprintf(devpath, sizeof(devpath), "%s%s", base, name); 534 mkdir(base, 0755); 535 536 if (!strncmp(uevent->path, "/devices/platform/", 18)) 537 links = parse_platform_block_device(uevent); 538 539 handle_device(uevent->action, devpath, uevent->path, 1, 540 uevent->major, uevent->minor, links); 541} 542 543static void handle_generic_device_event(struct uevent *uevent) 544{ 545 char *base; 546 const char *name; 547 char devpath[96] = {0}; 548 char **links = NULL; 549 550 name = parse_device_name(uevent, 64); 551 if (!name) 552 return; 553 554 if (!strncmp(uevent->subsystem, "usb", 3)) { 555 if (!strcmp(uevent->subsystem, "usb")) { 556 /* This imitates the file system that would be created 557 * if we were using devfs instead. 558 * Minors are broken up into groups of 128, starting at "001" 559 */ 560 int bus_id = uevent->minor / 128 + 1; 561 int device_id = uevent->minor % 128 + 1; 562 /* build directories */ 563 mkdir("/dev/bus", 0755); 564 mkdir("/dev/bus/usb", 0755); 565 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); 566 mkdir(devpath, 0755); 567 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); 568 } else { 569 /* ignore other USB events */ 570 return; 571 } 572 } else if (!strncmp(uevent->subsystem, "graphics", 8)) { 573 base = "/dev/graphics/"; 574 mkdir(base, 0755); 575 } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { 576 base = "/dev/oncrpc/"; 577 mkdir(base, 0755); 578 } else if (!strncmp(uevent->subsystem, "adsp", 4)) { 579 base = "/dev/adsp/"; 580 mkdir(base, 0755); 581 } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { 582 base = "/dev/msm_camera/"; 583 mkdir(base, 0755); 584 } else if(!strncmp(uevent->subsystem, "input", 5)) { 585 base = "/dev/input/"; 586 mkdir(base, 0755); 587 } else if(!strncmp(uevent->subsystem, "mtd", 3)) { 588 base = "/dev/mtd/"; 589 mkdir(base, 0755); 590 } else if(!strncmp(uevent->subsystem, "sound", 5)) { 591 base = "/dev/snd/"; 592 mkdir(base, 0755); 593 } else if(!strncmp(uevent->subsystem, "misc", 4) && 594 !strncmp(name, "log_", 4)) { 595 base = "/dev/log/"; 596 mkdir(base, 0755); 597 name += 4; 598 } else 599 base = "/dev/"; 600 links = get_character_device_symlinks(uevent); 601 602 if (!devpath[0]) 603 snprintf(devpath, sizeof(devpath), "%s%s", base, name); 604 605 handle_device(uevent->action, devpath, uevent->path, 0, 606 uevent->major, uevent->minor, links); 607} 608 609static void handle_device_event(struct uevent *uevent) 610{ 611 if (!strcmp(uevent->action,"add")) 612 fixup_sys_perms(uevent->path); 613 614 if (!strncmp(uevent->subsystem, "block", 5)) { 615 handle_block_device_event(uevent); 616 } else if (!strncmp(uevent->subsystem, "platform", 8)) { 617 handle_platform_device_event(uevent); 618 } else { 619 handle_generic_device_event(uevent); 620 } 621} 622 623static int load_firmware(int fw_fd, int loading_fd, int data_fd) 624{ 625 struct stat st; 626 long len_to_copy; 627 int ret = 0; 628 629 if(fstat(fw_fd, &st) < 0) 630 return -1; 631 len_to_copy = st.st_size; 632 633 write(loading_fd, "1", 1); /* start transfer */ 634 635 while (len_to_copy > 0) { 636 char buf[PAGE_SIZE]; 637 ssize_t nr; 638 639 nr = read(fw_fd, buf, sizeof(buf)); 640 if(!nr) 641 break; 642 if(nr < 0) { 643 ret = -1; 644 break; 645 } 646 647 len_to_copy -= nr; 648 while (nr > 0) { 649 ssize_t nw = 0; 650 651 nw = write(data_fd, buf + nw, nr); 652 if(nw <= 0) { 653 ret = -1; 654 goto out; 655 } 656 nr -= nw; 657 } 658 } 659 660out: 661 if(!ret) 662 write(loading_fd, "0", 1); /* successful end of transfer */ 663 else 664 write(loading_fd, "-1", 2); /* abort transfer */ 665 666 return ret; 667} 668 669static int is_booting(void) 670{ 671 return access("/dev/.booting", F_OK) == 0; 672} 673 674static void process_firmware_event(struct uevent *uevent) 675{ 676 char *root, *loading, *data, *file1 = NULL, *file2 = NULL; 677 int l, loading_fd, data_fd, fw_fd; 678 int booting = is_booting(); 679 680 INFO("firmware: loading '%s' for '%s'\n", 681 uevent->firmware, uevent->path); 682 683 l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); 684 if (l == -1) 685 return; 686 687 l = asprintf(&loading, "%sloading", root); 688 if (l == -1) 689 goto root_free_out; 690 691 l = asprintf(&data, "%sdata", root); 692 if (l == -1) 693 goto loading_free_out; 694 695 l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware); 696 if (l == -1) 697 goto data_free_out; 698 699 l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware); 700 if (l == -1) 701 goto data_free_out; 702 703 loading_fd = open(loading, O_WRONLY); 704 if(loading_fd < 0) 705 goto file_free_out; 706 707 data_fd = open(data, O_WRONLY); 708 if(data_fd < 0) 709 goto loading_close_out; 710 711try_loading_again: 712 fw_fd = open(file1, O_RDONLY); 713 if(fw_fd < 0) { 714 fw_fd = open(file2, O_RDONLY); 715 if (fw_fd < 0) { 716 if (booting) { 717 /* If we're not fully booted, we may be missing 718 * filesystems needed for firmware, wait and retry. 719 */ 720 usleep(100000); 721 booting = is_booting(); 722 goto try_loading_again; 723 } 724 INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); 725 write(loading_fd, "-1", 2); 726 goto data_close_out; 727 } 728 } 729 730 if(!load_firmware(fw_fd, loading_fd, data_fd)) 731 INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); 732 else 733 INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); 734 735 close(fw_fd); 736data_close_out: 737 close(data_fd); 738loading_close_out: 739 close(loading_fd); 740file_free_out: 741 free(file1); 742 free(file2); 743data_free_out: 744 free(data); 745loading_free_out: 746 free(loading); 747root_free_out: 748 free(root); 749} 750 751static void handle_firmware_event(struct uevent *uevent) 752{ 753 pid_t pid; 754 int ret; 755 756 if(strcmp(uevent->subsystem, "firmware")) 757 return; 758 759 if(strcmp(uevent->action, "add")) 760 return; 761 762 /* we fork, to avoid making large memory allocations in init proper */ 763 pid = fork(); 764 if (!pid) { 765 process_firmware_event(uevent); 766 exit(EXIT_SUCCESS); 767 } 768} 769 770#define UEVENT_MSG_LEN 1024 771void handle_device_fd() 772{ 773 for(;;) { 774 char msg[UEVENT_MSG_LEN+2]; 775 char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; 776 struct iovec iov = {msg, sizeof(msg)}; 777 struct sockaddr_nl snl; 778 struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0}; 779 780 ssize_t n = recvmsg(device_fd, &hdr, 0); 781 if (n <= 0) { 782 break; 783 } 784 785 if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) { 786 /* ignoring non-kernel netlink multicast message */ 787 continue; 788 } 789 790 struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr); 791 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { 792 /* no sender credentials received, ignore message */ 793 continue; 794 } 795 796 struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg); 797 if (cred->uid != 0) { 798 /* message from non-root user, ignore */ 799 continue; 800 } 801 802 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ 803 continue; 804 805 msg[n] = '\0'; 806 msg[n+1] = '\0'; 807 808 struct uevent uevent; 809 parse_event(msg, &uevent); 810 811 handle_device_event(&uevent); 812 handle_firmware_event(&uevent); 813 } 814} 815 816/* Coldboot walks parts of the /sys tree and pokes the uevent files 817** to cause the kernel to regenerate device add events that happened 818** before init's device manager was started 819** 820** We drain any pending events from the netlink socket every time 821** we poke another uevent file to make sure we don't overrun the 822** socket's buffer. 823*/ 824 825static void do_coldboot(DIR *d) 826{ 827 struct dirent *de; 828 int dfd, fd; 829 830 dfd = dirfd(d); 831 832 fd = openat(dfd, "uevent", O_WRONLY); 833 if(fd >= 0) { 834 write(fd, "add\n", 4); 835 close(fd); 836 handle_device_fd(); 837 } 838 839 while((de = readdir(d))) { 840 DIR *d2; 841 842 if(de->d_type != DT_DIR || de->d_name[0] == '.') 843 continue; 844 845 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); 846 if(fd < 0) 847 continue; 848 849 d2 = fdopendir(fd); 850 if(d2 == 0) 851 close(fd); 852 else { 853 do_coldboot(d2); 854 closedir(d2); 855 } 856 } 857} 858 859static void coldboot(const char *path) 860{ 861 DIR *d = opendir(path); 862 if(d) { 863 do_coldboot(d); 864 closedir(d); 865 } 866} 867 868void device_init(void) 869{ 870 suseconds_t t0, t1; 871 struct stat info; 872 int fd; 873 874 device_fd = open_uevent_socket(); 875 if(device_fd < 0) 876 return; 877 878 fcntl(device_fd, F_SETFD, FD_CLOEXEC); 879 fcntl(device_fd, F_SETFL, O_NONBLOCK); 880 881 if (stat(coldboot_done, &info) < 0) { 882 t0 = get_usecs(); 883 coldboot("/sys/class"); 884 coldboot("/sys/block"); 885 coldboot("/sys/devices"); 886 t1 = get_usecs(); 887 fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000); 888 close(fd); 889 log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); 890 } else { 891 log_event_print("skipping coldboot, already done\n"); 892 } 893} 894 895int get_device_fd() 896{ 897 return device_fd; 898} 899