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