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