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