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