system_properties.cpp revision d5ed63a6a8290de88802172ce178656fbafe70c6
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28#include <new> 29#include <stdio.h> 30#include <stdint.h> 31#include <stdlib.h> 32#include <unistd.h> 33#include <stddef.h> 34#include <errno.h> 35#include <poll.h> 36#include <fcntl.h> 37#include <stdbool.h> 38#include <string.h> 39 40#include <sys/mman.h> 41 42#include <sys/socket.h> 43#include <sys/un.h> 44#include <sys/select.h> 45#include <sys/stat.h> 46#include <sys/types.h> 47#include <netinet/in.h> 48#include <unistd.h> 49 50#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 51#include <sys/_system_properties.h> 52#include <sys/system_properties.h> 53 54#include "private/bionic_atomic_inline.h" 55#include "private/bionic_futex.h" 56#include "private/bionic_macros.h" 57 58#define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1)) 59 60 61static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; 62 63 64/* 65 * Properties are stored in a hybrid trie/binary tree structure. 66 * Each property's name is delimited at '.' characters, and the tokens are put 67 * into a trie structure. Siblings at each level of the trie are stored in a 68 * binary tree. For instance, "ro.secure"="1" could be stored as follows: 69 * 70 * +-----+ children +----+ children +--------+ 71 * | |-------------->| ro |-------------->| secure | 72 * +-----+ +----+ +--------+ 73 * / \ / | 74 * left / \ right left / | prop +===========+ 75 * v v v +-------->| ro.secure | 76 * +-----+ +-----+ +-----+ +-----------+ 77 * | net | | sys | | com | | 1 | 78 * +-----+ +-----+ +-----+ +===========+ 79 */ 80 81// Represents a node in the trie. 82struct prop_bt { 83 uint8_t namelen; 84 uint8_t reserved[3]; 85 86 volatile uint32_t prop; 87 88 volatile uint32_t left; 89 volatile uint32_t right; 90 91 volatile uint32_t children; 92 93 char name[0]; 94 95 prop_bt(const char *name, const uint8_t name_length) { 96 this->namelen = name_length; 97 memcpy(this->name, name, name_length); 98 this->name[name_length] = '\0'; 99 ANDROID_MEMBAR_FULL(); 100 } 101 102private: 103 DISALLOW_COPY_AND_ASSIGN(prop_bt); 104}; 105 106struct prop_area { 107 uint32_t bytes_used; 108 volatile uint32_t serial; 109 uint32_t magic; 110 uint32_t version; 111 uint32_t reserved[28]; 112 char data[0]; 113 114 prop_area(const uint32_t magic, const uint32_t version) : 115 serial(0), magic(magic), version(version) { 116 memset(reserved, 0, sizeof(reserved)); 117 // Allocate enough space for the root node. 118 bytes_used = sizeof(prop_bt); 119 } 120 121private: 122 DISALLOW_COPY_AND_ASSIGN(prop_area); 123}; 124 125struct prop_info { 126 volatile uint32_t serial; 127 char value[PROP_VALUE_MAX]; 128 char name[0]; 129 130 prop_info(const char *name, const uint8_t namelen, const char *value, 131 const uint8_t valuelen) { 132 memcpy(this->name, name, namelen); 133 this->name[namelen] = '\0'; 134 this->serial = (valuelen << 24); 135 memcpy(this->value, value, valuelen); 136 this->value[valuelen] = '\0'; 137 ANDROID_MEMBAR_FULL(); 138 } 139private: 140 DISALLOW_COPY_AND_ASSIGN(prop_info); 141}; 142 143struct find_nth_cookie { 144 uint32_t count; 145 const uint32_t n; 146 const prop_info *pi; 147 148 find_nth_cookie(uint32_t n) : count(0), n(n), pi(NULL) { 149 } 150}; 151 152static char property_filename[PATH_MAX] = PROP_FILENAME; 153static bool compat_mode = false; 154static size_t pa_data_size; 155static size_t pa_size; 156 157// NOTE: This isn't static because system_properties_compat.c 158// requires it. 159prop_area *__system_property_area__ = NULL; 160 161static int get_fd_from_env(void) 162{ 163 // This environment variable consistes of two decimal integer 164 // values separated by a ",". The first value is a file descriptor 165 // and the second is the size of the system properties area. The 166 // size is currently unused. 167 char *env = getenv("ANDROID_PROPERTY_WORKSPACE"); 168 169 if (!env) { 170 return -1; 171 } 172 173 return atoi(env); 174} 175 176static int map_prop_area_rw() 177{ 178 /* dev is a tmpfs that we can use to carve a shared workspace 179 * out of, so let's do that... 180 */ 181 const int fd = open(property_filename, 182 O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); 183 184 if (fd < 0) { 185 if (errno == EACCES) { 186 /* for consistency with the case where the process has already 187 * mapped the page in and segfaults when trying to write to it 188 */ 189 abort(); 190 } 191 return -1; 192 } 193 194 // TODO: Is this really required ? Does android run on any kernels that 195 // don't support O_CLOEXEC ? 196 const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); 197 if (ret < 0) { 198 close(fd); 199 return -1; 200 } 201 202 if (ftruncate(fd, PA_SIZE) < 0) { 203 close(fd); 204 return -1; 205 } 206 207 pa_size = PA_SIZE; 208 pa_data_size = pa_size - sizeof(prop_area); 209 compat_mode = false; 210 211 void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 212 if (memory_area == MAP_FAILED) { 213 close(fd); 214 return -1; 215 } 216 217 prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); 218 219 /* plug into the lib property services */ 220 __system_property_area__ = pa; 221 222 close(fd); 223 return 0; 224} 225 226static int map_fd_ro(const int fd) { 227 struct stat fd_stat; 228 if (fstat(fd, &fd_stat) < 0) { 229 return -1; 230 } 231 232 if ((fd_stat.st_uid != 0) 233 || (fd_stat.st_gid != 0) 234 || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) 235 || (fd_stat.st_size < static_cast<off_t>(sizeof(prop_area))) ) { 236 return -1; 237 } 238 239 pa_size = fd_stat.st_size; 240 pa_data_size = pa_size - sizeof(prop_area); 241 242 void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0); 243 if (map_result == MAP_FAILED) { 244 return -1; 245 } 246 247 prop_area* pa = reinterpret_cast<prop_area*>(map_result); 248 if ((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION && 249 pa->version != PROP_AREA_VERSION_COMPAT)) { 250 munmap(pa, pa_size); 251 return -1; 252 } 253 254 if (pa->version == PROP_AREA_VERSION_COMPAT) { 255 compat_mode = true; 256 } 257 258 __system_property_area__ = pa; 259 return 0; 260} 261 262static int map_prop_area() 263{ 264 int fd(open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); 265 if (fd >= 0) { 266 /* For old kernels that don't support O_CLOEXEC */ 267 const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); 268 if (ret < 0) { 269 close(fd); 270 return -1; 271 } 272 } 273 274 bool close_fd = true; 275 if ((fd < 0) && (errno == ENOENT)) { 276 /* 277 * For backwards compatibility, if the file doesn't 278 * exist, we use the environment to get the file descriptor. 279 * For security reasons, we only use this backup if the kernel 280 * returns ENOENT. We don't want to use the backup if the kernel 281 * returns other errors such as ENOMEM or ENFILE, since it 282 * might be possible for an external program to trigger this 283 * condition. 284 */ 285 fd = get_fd_from_env(); 286 close_fd = false; 287 } 288 289 if (fd < 0) { 290 return -1; 291 } 292 293 const int map_result = map_fd_ro(fd); 294 if (close_fd) { 295 close(fd); 296 } 297 298 return map_result; 299} 300 301static void *allocate_obj(const size_t size, uint32_t *const off) 302{ 303 prop_area *pa = __system_property_area__; 304 const size_t aligned = ALIGN(size, sizeof(uint32_t)); 305 if (pa->bytes_used + aligned > pa_data_size) { 306 return NULL; 307 } 308 309 *off = pa->bytes_used; 310 pa->bytes_used += aligned; 311 return pa->data + *off; 312} 313 314static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint32_t *const off) 315{ 316 uint32_t new_offset; 317 void *const offset = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset); 318 if (offset) { 319 prop_bt* bt = new(offset) prop_bt(name, namelen); 320 *off = new_offset; 321 return bt; 322 } 323 324 return NULL; 325} 326 327static prop_info *new_prop_info(const char *name, uint8_t namelen, 328 const char *value, uint8_t valuelen, uint32_t *const off) 329{ 330 uint32_t off_tmp; 331 void* const offset = allocate_obj(sizeof(prop_info) + namelen + 1, &off_tmp); 332 if (offset) { 333 prop_info* info = new(offset) prop_info(name, namelen, value, valuelen); 334 *off = off_tmp; 335 return info; 336 } 337 338 return NULL; 339} 340 341static void *to_prop_obj(const uint32_t off) 342{ 343 if (off > pa_data_size) 344 return NULL; 345 if (!__system_property_area__) 346 return NULL; 347 348 return (__system_property_area__->data + off); 349} 350 351static prop_bt *root_node() 352{ 353 return reinterpret_cast<prop_bt*>(to_prop_obj(0)); 354} 355 356static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, 357 uint8_t two_len) 358{ 359 if (one_len < two_len) 360 return -1; 361 else if (one_len > two_len) 362 return 1; 363 else 364 return strncmp(one, two, one_len); 365} 366 367static prop_bt *find_prop_bt(prop_bt *const bt, const char *name, 368 uint8_t namelen, bool alloc_if_needed) 369{ 370 371 prop_bt* current = bt; 372 while (true) { 373 if (!current) { 374 return NULL; 375 } 376 377 const int ret = cmp_prop_name(name, namelen, current->name, current->namelen); 378 if (ret == 0) { 379 return current; 380 } 381 382 if (ret < 0) { 383 if (current->left) { 384 current = reinterpret_cast<prop_bt*>(to_prop_obj(current->left)); 385 } else { 386 if (!alloc_if_needed) { 387 return NULL; 388 } 389 390 // Note that there isn't a race condition here. "clients" never 391 // reach this code-path since It's only the (single threaded) server 392 // that allocates new nodes. Though "bt->left" is volatile, it can't 393 // have changed since the last value was last read. 394 uint32_t new_offset = 0; 395 prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); 396 if (new_bt) { 397 current->left = new_offset; 398 } 399 return new_bt; 400 } 401 } else { 402 if (current->right) { 403 current = reinterpret_cast<prop_bt*>(to_prop_obj(current->right)); 404 } else { 405 if (!alloc_if_needed) { 406 return NULL; 407 } 408 409 uint32_t new_offset; 410 prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); 411 if (new_bt) { 412 current->right = new_offset; 413 } 414 return new_bt; 415 } 416 } 417 } 418} 419 420static const prop_info *find_property(prop_bt *const trie, const char *name, 421 uint8_t namelen, const char *value, uint8_t valuelen, 422 bool alloc_if_needed) 423{ 424 if (!trie) return NULL; 425 426 const char *remaining_name = name; 427 prop_bt* current = trie; 428 while (true) { 429 const char *sep = strchr(remaining_name, '.'); 430 const bool want_subtree = (sep != NULL); 431 const uint8_t substr_size = (want_subtree) ? 432 sep - remaining_name : strlen(remaining_name); 433 434 if (!substr_size) { 435 return NULL; 436 } 437 438 prop_bt* root = NULL; 439 if (current->children) { 440 root = reinterpret_cast<prop_bt*>(to_prop_obj(current->children)); 441 } else if (alloc_if_needed) { 442 uint32_t new_bt_offset; 443 root = new_prop_bt(remaining_name, substr_size, &new_bt_offset); 444 if (root) { 445 current->children = new_bt_offset; 446 } 447 } 448 449 if (!root) { 450 return NULL; 451 } 452 453 current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); 454 if (!current) { 455 return NULL; 456 } 457 458 if (!want_subtree) 459 break; 460 461 remaining_name = sep + 1; 462 } 463 464 if (current->prop) { 465 return reinterpret_cast<prop_info*>(to_prop_obj(current->prop)); 466 } else if (alloc_if_needed) { 467 uint32_t new_info_offset; 468 prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_info_offset); 469 if (new_info) { 470 current->prop = new_info_offset; 471 } 472 473 return new_info; 474 } else { 475 return NULL; 476 } 477} 478 479static int send_prop_msg(const prop_msg *msg) 480{ 481 const int fd = socket(AF_LOCAL, SOCK_STREAM, 0); 482 if (fd < 0) { 483 return -1; 484 } 485 486 const size_t namelen = strlen(property_service_socket); 487 488 sockaddr_un addr; 489 memset(&addr, 0, sizeof(addr)); 490 strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path)); 491 addr.sun_family = AF_LOCAL; 492 socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1; 493 if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) { 494 close(fd); 495 return -1; 496 } 497 498 const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0)); 499 500 int result = -1; 501 if (num_bytes == sizeof(prop_msg)) { 502 // We successfully wrote to the property server but now we 503 // wait for the property server to finish its work. It 504 // acknowledges its completion by closing the socket so we 505 // poll here (on nothing), waiting for the socket to close. 506 // If you 'adb shell setprop foo bar' you'll see the POLLHUP 507 // once the socket closes. Out of paranoia we cap our poll 508 // at 250 ms. 509 pollfd pollfds[1]; 510 pollfds[0].fd = fd; 511 pollfds[0].events = 0; 512 const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); 513 if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) { 514 result = 0; 515 } else { 516 // Ignore the timeout and treat it like a success anyway. 517 // The init process is single-threaded and its property 518 // service is sometimes slow to respond (perhaps it's off 519 // starting a child process or something) and thus this 520 // times out and the caller thinks it failed, even though 521 // it's still getting around to it. So we fake it here, 522 // mostly for ctl.* properties, but we do try and wait 250 523 // ms so callers who do read-after-write can reliably see 524 // what they've written. Most of the time. 525 // TODO: fix the system properties design. 526 result = 0; 527 } 528 } 529 530 close(fd); 531 return result; 532} 533 534static void find_nth_fn(const prop_info *pi, void *ptr) 535{ 536 find_nth_cookie *cookie = reinterpret_cast<find_nth_cookie*>(ptr); 537 538 if (cookie->n == cookie->count) 539 cookie->pi = pi; 540 541 cookie->count++; 542} 543 544static int foreach_property(const uint32_t off, 545 void (*propfn)(const prop_info *pi, void *cookie), void *cookie) 546{ 547 prop_bt *trie = reinterpret_cast<prop_bt*>(to_prop_obj(off)); 548 if (!trie) 549 return -1; 550 551 if (trie->left) { 552 const int err = foreach_property(trie->left, propfn, cookie); 553 if (err < 0) 554 return -1; 555 } 556 if (trie->prop) { 557 prop_info *info = reinterpret_cast<prop_info*>(to_prop_obj(trie->prop)); 558 if (!info) 559 return -1; 560 propfn(info, cookie); 561 } 562 if (trie->children) { 563 const int err = foreach_property(trie->children, propfn, cookie); 564 if (err < 0) 565 return -1; 566 } 567 if (trie->right) { 568 const int err = foreach_property(trie->right, propfn, cookie); 569 if (err < 0) 570 return -1; 571 } 572 573 return 0; 574} 575 576int __system_properties_init() 577{ 578 return map_prop_area(); 579} 580 581int __system_property_set_filename(const char *filename) 582{ 583 size_t len = strlen(filename); 584 if (len >= sizeof(property_filename)) 585 return -1; 586 587 strcpy(property_filename, filename); 588 return 0; 589} 590 591int __system_property_area_init() 592{ 593 return map_prop_area_rw(); 594} 595 596const prop_info *__system_property_find(const char *name) 597{ 598 if (__predict_false(compat_mode)) { 599 return __system_property_find_compat(name); 600 } 601 return find_property(root_node(), name, strlen(name), NULL, 0, false); 602} 603 604int __system_property_read(const prop_info *pi, char *name, char *value) 605{ 606 if (__predict_false(compat_mode)) { 607 return __system_property_read_compat(pi, name, value); 608 } 609 610 while (true) { 611 uint32_t serial = __system_property_serial(pi); 612 size_t len = SERIAL_VALUE_LEN(serial); 613 memcpy(value, pi->value, len + 1); 614 ANDROID_MEMBAR_FULL(); 615 if (serial == pi->serial) { 616 if (name != 0) { 617 strcpy(name, pi->name); 618 } 619 return len; 620 } 621 } 622} 623 624int __system_property_get(const char *name, char *value) 625{ 626 const prop_info *pi = __system_property_find(name); 627 628 if (pi != 0) { 629 return __system_property_read(pi, 0, value); 630 } else { 631 value[0] = 0; 632 return 0; 633 } 634} 635 636int __system_property_set(const char *key, const char *value) 637{ 638 if (key == 0) return -1; 639 if (value == 0) value = ""; 640 if (strlen(key) >= PROP_NAME_MAX) return -1; 641 if (strlen(value) >= PROP_VALUE_MAX) return -1; 642 643 prop_msg msg; 644 memset(&msg, 0, sizeof msg); 645 msg.cmd = PROP_MSG_SETPROP; 646 strlcpy(msg.name, key, sizeof msg.name); 647 strlcpy(msg.value, value, sizeof msg.value); 648 649 const int err = send_prop_msg(&msg); 650 if (err < 0) { 651 return err; 652 } 653 654 return 0; 655} 656 657int __system_property_update(prop_info *pi, const char *value, unsigned int len) 658{ 659 prop_area *pa = __system_property_area__; 660 661 if (len >= PROP_VALUE_MAX) 662 return -1; 663 664 pi->serial = pi->serial | 1; 665 ANDROID_MEMBAR_FULL(); 666 memcpy(pi->value, value, len + 1); 667 ANDROID_MEMBAR_FULL(); 668 pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); 669 __futex_wake(&pi->serial, INT32_MAX); 670 671 pa->serial++; 672 __futex_wake(&pa->serial, INT32_MAX); 673 674 return 0; 675} 676 677int __system_property_add(const char *name, unsigned int namelen, 678 const char *value, unsigned int valuelen) 679{ 680 prop_area *pa = __system_property_area__; 681 const prop_info *pi; 682 683 if (namelen >= PROP_NAME_MAX) 684 return -1; 685 if (valuelen >= PROP_VALUE_MAX) 686 return -1; 687 if (namelen < 1) 688 return -1; 689 690 pi = find_property(root_node(), name, namelen, value, valuelen, true); 691 if (!pi) 692 return -1; 693 694 pa->serial++; 695 __futex_wake(&pa->serial, INT32_MAX); 696 return 0; 697} 698 699unsigned int __system_property_serial(const prop_info *pi) 700{ 701 uint32_t serial = pi->serial; 702 while (SERIAL_DIRTY(serial)) { 703 __futex_wait(const_cast<volatile uint32_t*>(&pi->serial), serial, NULL); 704 serial = pi->serial; 705 } 706 return serial; 707} 708 709unsigned int __system_property_wait_any(unsigned int serial) 710{ 711 prop_area *pa = __system_property_area__; 712 713 do { 714 __futex_wait(&pa->serial, serial, NULL); 715 } while (pa->serial == serial); 716 717 return pa->serial; 718} 719 720const prop_info *__system_property_find_nth(unsigned n) 721{ 722 find_nth_cookie cookie(n); 723 724 const int err = __system_property_foreach(find_nth_fn, &cookie); 725 if (err < 0) { 726 return NULL; 727 } 728 729 return cookie.pi; 730} 731 732int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie), 733 void *cookie) 734{ 735 if (__predict_false(compat_mode)) { 736 return __system_property_foreach_compat(propfn, cookie); 737 } 738 739 return foreach_property(0, propfn, cookie); 740} 741