From c9ae21a5c3b2e1baafe50f752e2e07e343d39530 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Wed, 19 Feb 2014 17:59:05 +0000 Subject: [PATCH] Move system_properties over to C++. This change constitutes the minimum amount of work required to move the code over to C++, address compiler warnings, and to make it const correct and idiomatic (within the constraints of being called from C code). bug: 13058886 Change-Id: Ic78cf91b7c8e8f07b4ab0781333a9e243763298c --- libc/Android.mk | 2 +- ...tem_properties.c => system_properties.cpp} | 887 +++++++++--------- libc/include/sys/_system_properties.h | 8 + 3 files changed, 477 insertions(+), 420 deletions(-) rename libc/bionic/{system_properties.c => system_properties.cpp} (58%) diff --git a/libc/Android.mk b/libc/Android.mk index 4e140f038..c060ff6d3 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -79,7 +79,6 @@ libc_common_src_files := \ bionic/strntoimax.c \ bionic/strntoumax.c \ bionic/strtotimeval.c \ - bionic/system_properties.c \ bionic/system_properties_compat.c \ bionic/tcgetpgrp.c \ bionic/tcsetpgrp.c \ @@ -223,6 +222,7 @@ libc_bionic_src_files := \ bionic/stubs.cpp \ bionic/symlink.cpp \ bionic/sysconf.cpp \ + bionic/system_properties.cpp \ bionic/sys_siglist.c \ bionic/sys_signame.c \ bionic/tdestroy.cpp \ diff --git a/libc/bionic/system_properties.c b/libc/bionic/system_properties.cpp similarity index 58% rename from libc/bionic/system_properties.c rename to libc/bionic/system_properties.cpp index 825894f75..7c2f8dc4f 100644 --- a/libc/bionic/system_properties.c +++ b/libc/bionic/system_properties.cpp @@ -25,6 +25,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ +#include #include #include #include @@ -48,6 +49,7 @@ #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include +#include #include @@ -55,24 +57,9 @@ #define ALIGN(x, a) (((x) + (a - 1)) & ~(a - 1)) -struct prop_area { - unsigned bytes_used; - unsigned volatile serial; - unsigned magic; - unsigned version; - unsigned reserved[28]; - char data[0]; -}; -typedef struct prop_area prop_area; +static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; -struct prop_info { - unsigned volatile serial; - char value[PROP_VALUE_MAX]; - char name[0]; -}; - -typedef struct prop_info prop_info; /* * Properties are stored in a hybrid trie/binary tree structure. @@ -91,34 +78,98 @@ typedef struct prop_info prop_info; * +-----+ +-----+ +-----+ +===========+ */ -typedef volatile uint32_t prop_off_t; +// Represents a node in the trie. struct prop_bt { uint8_t namelen; uint8_t reserved[3]; - prop_off_t prop; + volatile uint32_t prop; - prop_off_t left; - prop_off_t right; + volatile uint32_t left; + volatile uint32_t right; - prop_off_t children; + volatile uint32_t children; char name[0]; + + prop_bt(const char *name, const uint8_t name_length) { + this->namelen = name_length; + memcpy(this->name, name, name_length); + this->name[name_length] = '\0'; + ANDROID_MEMBAR_FULL(); + } + +private: + // Disallow copy and assign. + prop_bt(const prop_bt&); + prop_bt& operator=(const prop_bt&); }; -typedef struct prop_bt prop_bt; +struct prop_area { + uint32_t bytes_used; + volatile uint32_t serial; + uint32_t magic; + uint32_t version; + uint32_t reserved[28]; + char data[0]; + + prop_area(const uint32_t magic, const uint32_t version) : + serial(0), magic(magic), version(version) { + memset(reserved, 0, sizeof(reserved)); + // Allocate enough space for the root node. + bytes_used = sizeof(prop_bt); + } + +private: + // Disallow copy and assign. + prop_area(const prop_area&); + prop_area& operator=(const prop_area&); +}; + +struct prop_info { + volatile uint32_t serial; + char value[PROP_VALUE_MAX]; + char name[0]; + + prop_info(const char *name, const uint8_t namelen, const char *value, + const uint8_t valuelen) { + memcpy(this->name, name, namelen); + this->name[namelen] = '\0'; + this->serial = (valuelen << 24); + memcpy(this->value, value, valuelen); + this->value[valuelen] = '\0'; + ANDROID_MEMBAR_FULL(); + } +private: + // Disallow copy and assign. + prop_info(const prop_info&); + prop_info& operator=(const prop_info&); +}; + +struct find_nth_cookie { + uint32_t count; + const uint32_t n; + const prop_info *pi; + + find_nth_cookie(uint32_t n) : count(0), n(n), pi(NULL) { + } +}; -static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; static char property_filename[PATH_MAX] = PROP_FILENAME; static bool compat_mode = false; +static size_t pa_data_size; +static size_t pa_size; +// NOTE: This isn't static because system_properties_compat.c +// requires it. prop_area *__system_property_area__ = NULL; -size_t pa_data_size; -size_t pa_size; - static int get_fd_from_env(void) { + // This environment variable consistes of two decimal integer + // values separated by a ",". The first value is a file descriptor + // and the second is the size of the system properties area. The + // size is currently unused. char *env = getenv("ANDROID_PROPERTY_WORKSPACE"); if (!env) { @@ -130,15 +181,12 @@ static int get_fd_from_env(void) static int map_prop_area_rw() { - prop_area *pa; - int fd; - int ret; - /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let's do that... */ - fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | - O_EXCL, 0444); + const int fd = open(property_filename, + O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); + if (fd < 0) { if (errno == EACCES) { /* for consistency with the case where the process has already @@ -149,36 +197,391 @@ static int map_prop_area_rw() return -1; } - ret = fcntl(fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) - goto out; + // TODO: Is this really required ? Does android run on any kernels that + // don't support O_CLOEXEC ? + const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + close(fd); + return -1; + } - if (ftruncate(fd, PA_SIZE) < 0) - goto out; + if (ftruncate(fd, PA_SIZE) < 0) { + close(fd); + return -1; + } pa_size = PA_SIZE; pa_data_size = pa_size - sizeof(prop_area); compat_mode = false; - pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if(pa == MAP_FAILED) - goto out; + void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (memory_area == MAP_FAILED) { + close(fd); + return -1; + } - memset(pa, 0, pa_size); - pa->magic = PROP_AREA_MAGIC; - pa->version = PROP_AREA_VERSION; - /* reserve root node */ - pa->bytes_used = sizeof(prop_bt); + prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); /* plug into the lib property services */ __system_property_area__ = pa; close(fd); return 0; +} + +static int map_fd_ro(const int fd) { + struct stat fd_stat; + if (fstat(fd, &fd_stat) < 0) { + return -1; + } + + if ((fd_stat.st_uid != 0) + || (fd_stat.st_gid != 0) + || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) + || (fd_stat.st_size < sizeof(prop_area)) ) { + return -1; + } + + pa_size = fd_stat.st_size; + pa_data_size = pa_size - sizeof(prop_area); + + void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0); + if (map_result == MAP_FAILED) { + return -1; + } + + prop_area* pa = reinterpret_cast(map_result); + if ((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION && + pa->version != PROP_AREA_VERSION_COMPAT)) { + munmap(pa, pa_size); + return -1; + } + + if (pa->version == PROP_AREA_VERSION_COMPAT) { + compat_mode = true; + } + + __system_property_area__ = pa; + return 0; +} + +static int map_prop_area() +{ + int fd(open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)); + if (fd >= 0) { + /* For old kernels that don't support O_CLOEXEC */ + const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + close(fd); + return -1; + } + } + + bool close_fd = true; + if ((fd < 0) && (errno == ENOENT)) { + /* + * For backwards compatibility, if the file doesn't + * exist, we use the environment to get the file descriptor. + * For security reasons, we only use this backup if the kernel + * returns ENOENT. We don't want to use the backup if the kernel + * returns other errors such as ENOMEM or ENFILE, since it + * might be possible for an external program to trigger this + * condition. + */ + fd = get_fd_from_env(); + close_fd = false; + } + + if (fd < 0) { + return -1; + } + + const int map_result = map_fd_ro(fd); + if (close_fd) { + close(fd); + } + + return map_result; +} + +static void *allocate_obj(const size_t size, uint32_t *const off) +{ + prop_area *pa = __system_property_area__; + const size_t aligned = ALIGN(size, sizeof(uint32_t)); + if (pa->bytes_used + aligned > pa_data_size) { + return NULL; + } + + *off = pa->bytes_used; + pa->bytes_used += aligned; + return pa->data + *off; +} + +static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint32_t *const off) +{ + uint32_t new_offset; + void *const offset = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset); + if (offset) { + prop_bt* bt = new(offset) prop_bt(name, namelen); + *off = new_offset; + return bt; + } + + return NULL; +} + +static prop_info *new_prop_info(const char *name, uint8_t namelen, + const char *value, uint8_t valuelen, uint32_t *const off) +{ + uint32_t off_tmp; + void* const offset = allocate_obj(sizeof(prop_info) + namelen + 1, &off_tmp); + if (offset) { + prop_info* info = new(offset) prop_info(name, namelen, value, valuelen); + *off = off_tmp; + return info; + } + + return NULL; +} + +static void *to_prop_obj(const uint32_t off) +{ + if (off > pa_data_size) + return NULL; + if (!__system_property_area__) + return NULL; + + return (__system_property_area__->data + off); +} + +static prop_bt *root_node() +{ + return reinterpret_cast(to_prop_obj(0)); +} + +static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, + uint8_t two_len) +{ + if (one_len < two_len) + return -1; + else if (one_len > two_len) + return 1; + else + return strncmp(one, two, one_len); +} + +static prop_bt *find_prop_bt(prop_bt *const bt, const char *name, + uint8_t namelen, bool alloc_if_needed) +{ + + prop_bt* current = bt; + while (true) { + if (!current) { + return NULL; + } + + const int ret = cmp_prop_name(name, namelen, current->name, current->namelen); + if (ret == 0) { + return current; + } + + if (ret < 0) { + if (current->left) { + current = reinterpret_cast(to_prop_obj(current->left)); + } else { + if (!alloc_if_needed) { + return NULL; + } + + // Note that there isn't a race condition here. "clients" never + // reach this code-path since It's only the (single threaded) server + // that allocates new nodes. Though "bt->left" is volatile, it can't + // have changed since the last value was last read. + uint32_t new_offset = 0; + prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); + if (new_bt) { + current->left = new_offset; + } + return new_bt; + } + } else { + if (current->right) { + current = reinterpret_cast(to_prop_obj(current->right)); + } else { + if (!alloc_if_needed) { + return NULL; + } + + uint32_t new_offset; + prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); + if (new_bt) { + current->right = new_offset; + } + return new_bt; + } + } + } +} + +static const prop_info *find_property(prop_bt *const trie, const char *name, + uint8_t namelen, const char *value, uint8_t valuelen, + bool alloc_if_needed) +{ + if (!trie) return NULL; + + const char *remaining_name = name; + prop_bt* current = trie; + while (true) { + const char *sep = strchr(remaining_name, '.'); + const bool want_subtree = (sep != NULL); + const uint8_t substr_size = (want_subtree) ? + sep - remaining_name : strlen(remaining_name); + + if (!substr_size) { + return NULL; + } + + prop_bt* root = NULL; + if (current->children) { + root = reinterpret_cast(to_prop_obj(current->children)); + } else if (alloc_if_needed) { + uint32_t new_bt_offset; + root = new_prop_bt(remaining_name, substr_size, &new_bt_offset); + if (root) { + current->children = new_bt_offset; + } + } + + if (!root) { + return NULL; + } + + current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); + if (!current) { + return NULL; + } + + if (!want_subtree) + break; + + remaining_name = sep + 1; + } + + if (current->prop) { + return reinterpret_cast(to_prop_obj(current->prop)); + } else if (alloc_if_needed) { + uint32_t new_info_offset; + prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_info_offset); + if (new_info) { + current->prop = new_info_offset; + } + + return new_info; + } else { + return NULL; + } +} + +static int send_prop_msg(const prop_msg *msg) +{ + const int fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { + return -1; + } + + const size_t namelen = strlen(property_service_socket); + + sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path)); + addr.sun_family = AF_LOCAL; + socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1; + if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast(&addr), alen)) < 0) { + close(fd); + return -1; + } + + const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0)); + + int result = -1; + if (num_bytes == sizeof(prop_msg)) { + // We successfully wrote to the property server but now we + // wait for the property server to finish its work. It + // acknowledges its completion by closing the socket so we + // poll here (on nothing), waiting for the socket to close. + // If you 'adb shell setprop foo bar' you'll see the POLLHUP + // once the socket closes. Out of paranoia we cap our poll + // at 250 ms. + pollfd pollfds[1]; + pollfds[0].fd = fd; + pollfds[0].events = 0; + const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); + if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) { + result = 0; + } else { + // Ignore the timeout and treat it like a success anyway. + // The init process is single-threaded and its property + // service is sometimes slow to respond (perhaps it's off + // starting a child process or something) and thus this + // times out and the caller thinks it failed, even though + // it's still getting around to it. So we fake it here, + // mostly for ctl.* properties, but we do try and wait 250 + // ms so callers who do read-after-write can reliably see + // what they've written. Most of the time. + // TODO: fix the system properties design. + result = 0; + } + } -out: close(fd); - return -1; + return result; +} + +static void find_nth_fn(const prop_info *pi, void *ptr) +{ + find_nth_cookie *cookie = reinterpret_cast(ptr); + + if (cookie->n == cookie->count) + cookie->pi = pi; + + cookie->count++; +} + +static int foreach_property(const uint32_t off, + void (*propfn)(const prop_info *pi, void *cookie), void *cookie) +{ + prop_bt *trie = reinterpret_cast(to_prop_obj(off)); + if (!trie) + return -1; + + if (trie->left) { + const int err = foreach_property(trie->left, propfn, cookie); + if (err < 0) + return -1; + } + if (trie->prop) { + prop_info *info = reinterpret_cast(to_prop_obj(trie->prop)); + if (!info) + return -1; + propfn(info, cookie); + } + if (trie->children) { + const int err = foreach_property(trie->children, propfn, cookie); + if (err < 0) + return -1; + } + if (trie->right) { + const int err = foreach_property(trie->right, propfn, cookie); + if (err < 0) + return -1; + } + + return 0; +} + +int __system_properties_init() +{ + return map_prop_area(); } int __system_property_set_filename(const char *filename) @@ -196,245 +599,6 @@ int __system_property_area_init() return map_prop_area_rw(); } -static int map_prop_area() -{ - bool fromFile = true; - int result = -1; - int fd; - int ret; - - fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); - if (fd >= 0) { - /* For old kernels that don't support O_CLOEXEC */ - ret = fcntl(fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) - goto cleanup; - } - - if ((fd < 0) && (errno == ENOENT)) { - /* - * For backwards compatibility, if the file doesn't - * exist, we use the environment to get the file descriptor. - * For security reasons, we only use this backup if the kernel - * returns ENOENT. We don't want to use the backup if the kernel - * returns other errors such as ENOMEM or ENFILE, since it - * might be possible for an external program to trigger this - * condition. - */ - fd = get_fd_from_env(); - fromFile = false; - } - - if (fd < 0) { - return -1; - } - - struct stat fd_stat; - if (fstat(fd, &fd_stat) < 0) { - goto cleanup; - } - - if ((fd_stat.st_uid != 0) - || (fd_stat.st_gid != 0) - || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) - || (fd_stat.st_size < sizeof(prop_area)) ) { - goto cleanup; - } - - pa_size = fd_stat.st_size; - pa_data_size = pa_size - sizeof(prop_area); - prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0); - - if (pa == MAP_FAILED) { - goto cleanup; - } - - if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION && - pa->version != PROP_AREA_VERSION_COMPAT)) { - munmap(pa, pa_size); - goto cleanup; - } - - if (pa->version == PROP_AREA_VERSION_COMPAT) { - compat_mode = true; - } - - result = 0; - - __system_property_area__ = pa; - -cleanup: - if (fromFile) { - close(fd); - } - - return result; -} - -int __system_properties_init() -{ - return map_prop_area(); -} - -static void *new_prop_obj(size_t size, prop_off_t *off) -{ - prop_area *pa = __system_property_area__; - size = ALIGN(size, sizeof(uint32_t)); - - if (pa->bytes_used + size > pa_data_size) - return NULL; - - *off = pa->bytes_used; - __system_property_area__->bytes_used += size; - return __system_property_area__->data + *off; -} - -static prop_bt *new_prop_bt(const char *name, uint8_t namelen, prop_off_t *off) -{ - prop_off_t off_tmp; - prop_bt *bt = new_prop_obj(sizeof(prop_bt) + namelen + 1, &off_tmp); - if (bt) { - memcpy(bt->name, name, namelen); - bt->name[namelen] = '\0'; - bt->namelen = namelen; - ANDROID_MEMBAR_FULL(); - *off = off_tmp; - } - - return bt; -} - -static prop_info *new_prop_info(const char *name, uint8_t namelen, - const char *value, uint8_t valuelen, prop_off_t *off) -{ - prop_off_t off_tmp; - prop_info *info = new_prop_obj(sizeof(prop_info) + namelen + 1, &off_tmp); - if (info) { - memcpy(info->name, name, namelen); - info->name[namelen] = '\0'; - info->serial = (valuelen << 24); - memcpy(info->value, value, valuelen); - info->value[valuelen] = '\0'; - ANDROID_MEMBAR_FULL(); - *off = off_tmp; - } - - return info; -} - -static void *to_prop_obj(prop_off_t off) -{ - if (off > pa_data_size) - return NULL; - if (!__system_property_area__) - return NULL; - - return __system_property_area__->data + off; -} - -static prop_bt *root_node() -{ - return to_prop_obj(0); -} - -static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, - uint8_t two_len) -{ - if (one_len < two_len) - return -1; - else if (one_len > two_len) - return 1; - else - return strncmp(one, two, one_len); -} - -static prop_bt *find_prop_bt(prop_bt *bt, const char *name, uint8_t namelen, - bool alloc_if_needed) -{ - while (true) { - int ret; - if (!bt) - return bt; - ret = cmp_prop_name(name, namelen, bt->name, bt->namelen); - - if (ret == 0) { - return bt; - } else if (ret < 0) { - if (bt->left) { - bt = to_prop_obj(bt->left); - } else { - if (!alloc_if_needed) - return NULL; - - bt = new_prop_bt(name, namelen, &bt->left); - } - } else { - if (bt->right) { - bt = to_prop_obj(bt->right); - } else { - if (!alloc_if_needed) - return NULL; - - bt = new_prop_bt(name, namelen, &bt->right); - } - } - } -} - -static const prop_info *find_property(prop_bt *trie, const char *name, - uint8_t namelen, const char *value, uint8_t valuelen, - bool alloc_if_needed) -{ - const char *remaining_name = name; - - if (!trie) return NULL; - - while (true) { - char *sep = strchr(remaining_name, '.'); - bool want_subtree = (sep != NULL); - uint8_t substr_size; - - prop_bt *root; - - if (want_subtree) { - substr_size = sep - remaining_name; - } else { - substr_size = strlen(remaining_name); - } - - if (!substr_size) - return NULL; - - if (trie->children) { - root = to_prop_obj(trie->children); - } else if (alloc_if_needed) { - root = new_prop_bt(remaining_name, substr_size, &trie->children); - } else { - root = NULL; - } - - if (!root) - return NULL; - - trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); - if (!trie) - return NULL; - - if (!want_subtree) - break; - - remaining_name = sep + 1; - } - - if (trie->prop) { - return to_prop_obj(trie->prop); - } else if (alloc_if_needed) { - return new_prop_info(name, namelen, value, valuelen, &trie->prop); - } else { - return NULL; - } -} - const prop_info *__system_property_find(const char *name) { if (__predict_false(compat_mode)) { @@ -473,7 +637,7 @@ int __system_property_get(const char *name, char *value) { const prop_info *pi = __system_property_find(name); - if(pi != 0) { + if (pi != 0) { return __system_property_read(pi, 0, value); } else { value[0] = 0; @@ -481,84 +645,21 @@ int __system_property_get(const char *name, char *value) } } - -static int send_prop_msg(prop_msg *msg) -{ - struct pollfd pollfds[1]; - struct sockaddr_un addr; - socklen_t alen; - size_t namelen; - int s; - int r; - int result = -1; - - s = socket(AF_LOCAL, SOCK_STREAM, 0); - if(s < 0) { - return result; - } - - memset(&addr, 0, sizeof(addr)); - namelen = strlen(property_service_socket); - strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path); - addr.sun_family = AF_LOCAL; - alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; - - if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) { - close(s); - return result; - } - - r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0)); - - if(r == sizeof(prop_msg)) { - // We successfully wrote to the property server but now we - // wait for the property server to finish its work. It - // acknowledges its completion by closing the socket so we - // poll here (on nothing), waiting for the socket to close. - // If you 'adb shell setprop foo bar' you'll see the POLLHUP - // once the socket closes. Out of paranoia we cap our poll - // at 250 ms. - pollfds[0].fd = s; - pollfds[0].events = 0; - r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); - if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) { - result = 0; - } else { - // Ignore the timeout and treat it like a success anyway. - // The init process is single-threaded and its property - // service is sometimes slow to respond (perhaps it's off - // starting a child process or something) and thus this - // times out and the caller thinks it failed, even though - // it's still getting around to it. So we fake it here, - // mostly for ctl.* properties, but we do try and wait 250 - // ms so callers who do read-after-write can reliably see - // what they've written. Most of the time. - // TODO: fix the system properties design. - result = 0; - } - } - - close(s); - return result; -} - int __system_property_set(const char *key, const char *value) { - int err; + if (key == 0) return -1; + if (value == 0) value = ""; + if (strlen(key) >= PROP_NAME_MAX) return -1; + if (strlen(value) >= PROP_VALUE_MAX) return -1; + prop_msg msg; - - if(key == 0) return -1; - if(value == 0) value = ""; - if(strlen(key) >= PROP_NAME_MAX) return -1; - if(strlen(value) >= PROP_VALUE_MAX) return -1; - memset(&msg, 0, sizeof msg); msg.cmd = PROP_MSG_SETPROP; strlcpy(msg.name, key, sizeof msg.name); strlcpy(msg.value, value, sizeof msg.value); - err = send_prop_msg(&msg); - if(err < 0) { + const int err = send_prop_msg(&msg); + if (err < 0) { return err; } @@ -567,15 +668,14 @@ int __system_property_set(const char *key, const char *value) int __system_property_wait(const prop_info *pi) { - unsigned n; - if(pi == 0) { + if (pi == 0) { prop_area *pa = __system_property_area__; - n = pa->serial; + const uint32_t n = pa->serial; do { __futex_wait(&pa->serial, n, NULL); - } while(n == pa->serial); + } while (n == pa->serial); } else { - n = pi->serial; + const uint32_t n = pi->serial; do { __futex_wait((volatile void *)&pi->serial, n, NULL); } while(n == pi->serial); @@ -602,7 +702,6 @@ int __system_property_update(prop_info *pi, const char *value, unsigned int len) return 0; } - int __system_property_add(const char *name, unsigned int namelen, const char *value, unsigned int valuelen) { @@ -641,74 +740,24 @@ unsigned int __system_property_wait_any(unsigned int serial) return pa->serial; } -struct find_nth_cookie { - unsigned count; - unsigned n; - const prop_info *pi; -}; - -static void find_nth_fn(const prop_info *pi, void *ptr) -{ - struct find_nth_cookie *cookie = ptr; - - if (cookie->n == cookie->count) - cookie->pi = pi; - - cookie->count++; -} - const prop_info *__system_property_find_nth(unsigned n) { - struct find_nth_cookie cookie; - int err; + find_nth_cookie cookie(n); - memset(&cookie, 0, sizeof(cookie)); - cookie.n = n; - - err = __system_property_foreach(find_nth_fn, &cookie); - if (err < 0) + const int err = __system_property_foreach(find_nth_fn, &cookie); + if (err < 0) { return NULL; + } return cookie.pi; } -static int foreach_property(prop_off_t off, - void (*propfn)(const prop_info *pi, void *cookie), void *cookie) -{ - prop_bt *trie = to_prop_obj(off); - if (!trie) - return -1; - - if (trie->left) { - int err = foreach_property(trie->left, propfn, cookie); - if (err < 0) - return -1; - } - if (trie->prop) { - prop_info *info = to_prop_obj(trie->prop); - if (!info) - return -1; - propfn(info, cookie); - } - if (trie->children) { - int err = foreach_property(trie->children, propfn, cookie); - if (err < 0) - return -1; - } - if (trie->right) { - int err = foreach_property(trie->right, propfn, cookie); - if (err < 0) - return -1; - } - - return 0; -} - int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie), void *cookie) { if (__predict_false(compat_mode)) { return __system_property_foreach_compat(propfn, cookie); - } + } + return foreach_property(0, propfn, cookie); } diff --git a/libc/include/sys/_system_properties.h b/libc/include/sys/_system_properties.h index 5eee7f097..5a681df72 100644 --- a/libc/include/sys/_system_properties.h +++ b/libc/include/sys/_system_properties.h @@ -139,6 +139,14 @@ int __system_property_foreach_compat( void (*propfn)(const prop_info *pi, void *cookie), void *cookie); +/* Initialize the system properties area in read only mode. + * Should be done by all processes that need to read system + * properties. + * + * Returns 0 on success, -1 otherwise. + */ +int __system_properties_init(); + __END_DECLS #endif