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