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