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