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