bionic/libc/bionic/system_properties.c
satok 0b3c5c50f7 do not merge. Move property setting from libcutils to bionic.
Backport I110b653a58f3

All the other property stuff is already here.  Property setting was
only in libcutils previously to leverage a utility function / constant
or two.

Unfortunately in the process of fixing a race condition we would've
had to do break abstraction boundaries and put some libc-internal
details into libcutils so instead of that we'll just move this
into bionic.

Along with Iee1ca9b7, this now passes:

$ adb shell am instrument -w -e class android.os.SystemPropertiesTest \
  com.android.frameworks.coretests.systemproperties/android.test.InstrumentationTestRunner

  Bug: 3511230

Change-Id: I1b588db3344169621e1279ecc0b660cf4e1015d7
2011-03-15 23:06:58 -07:00

281 lines
7.2 KiB
C

/*
* Copyright (C) 2008 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
#include <sys/atomics.h>
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static unsigned dummy_props = 0;
prop_area *__system_property_area__ = (void*) &dummy_props;
int __system_properties_init(void)
{
prop_area *pa;
int s, fd;
unsigned sz;
char *env;
if(__system_property_area__ != ((void*) &dummy_props)) {
return 0;
}
env = getenv("ANDROID_PROPERTY_WORKSPACE");
if (!env) {
return -1;
}
fd = atoi(env);
env = strchr(env, ',');
if (!env) {
return -1;
}
sz = atoi(env + 1);
pa = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);
if(pa == MAP_FAILED) {
return -1;
}
if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION)) {
munmap(pa, sz);
return -1;
}
__system_property_area__ = pa;
return 0;
}
const prop_info *__system_property_find_nth(unsigned n)
{
prop_area *pa = __system_property_area__;
if(n >= pa->count) {
return 0;
} else {
return TOC_TO_INFO(pa, pa->toc[n]);
}
}
const prop_info *__system_property_find(const char *name)
{
prop_area *pa = __system_property_area__;
unsigned count = pa->count;
unsigned *toc = pa->toc;
unsigned len = strlen(name);
prop_info *pi;
while(count--) {
unsigned entry = *toc++;
if(TOC_NAME_LEN(entry) != len) continue;
pi = TOC_TO_INFO(pa, entry);
if(memcmp(name, pi->name, len)) continue;
return pi;
}
return 0;
}
int __system_property_read(const prop_info *pi, char *name, char *value)
{
unsigned serial, len;
for(;;) {
serial = pi->serial;
while(SERIAL_DIRTY(serial)) {
__futex_wait((volatile void *)&pi->serial, serial, 0);
serial = pi->serial;
}
len = SERIAL_VALUE_LEN(serial);
memcpy(value, pi->value, len + 1);
if(serial == pi->serial) {
if(name != 0) {
strcpy(name, pi->name);
}
return len;
}
}
}
int __system_property_get(const char *name, char *value)
{
const prop_info *pi = __system_property_find(name);
if(pi != 0) {
return __system_property_read(pi, 0, value);
} else {
value[0] = 0;
return 0;
}
}
static int send_prop_msg(prop_msg *msg)
{
struct sockaddr_un addr;
socklen_t alen;
size_t namelen;
int s;
int r;
s = socket(AF_LOCAL, SOCK_STREAM, 0);
if(s < 0) {
return -1;
}
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 -1;
}
r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
if(r == sizeof(prop_msg)) {
r = 0;
} else {
r = -1;
}
close(s);
return r;
}
int __system_property_set(const char *key, const char *value)
{
unsigned old_serial;
volatile unsigned *serial;
prop_msg msg;
int err;
prop_area *pa = __system_property_area__;
int tries = 0;
int update_seen = 0;
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);
/* Note the system properties serial number before we do our update. */
const prop_info *pi = __system_property_find(key);
if(pi != NULL) {
serial = &pi->serial;
} else {
serial = &pa->serial;
}
old_serial = *serial;
err = send_prop_msg(&msg);
if(err < 0) {
return err;
}
/**
* Wait for the shared memory page to be written back and be
* visible in our address space before returning to the caller
* who might reasonably expect subsequent reads to match what was
* just written.
*
* Sleep 5 ms after failed checks and only wait up to a 500 ms
* total, just in case the system property server fails to update
* for whatever reason.
*/
do {
struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = 2500000; // 2.5 ms
if(tries++ > 0) {
usleep(2500); // 2.5 ms
}
__futex_wait(serial, old_serial, &timeout);
if(pi != NULL) {
unsigned new_serial = *serial;
/* Waiting on a specific prop_info to be updated. */
if (old_serial != new_serial && !SERIAL_DIRTY(new_serial)) {
update_seen = 1;
}
} else {
/* Waiting for a prop_info to be created. */
const prop_info *new_pi = __system_property_find(key);
if(new_pi != NULL && !SERIAL_DIRTY(new_pi->serial)) {
update_seen = 1;
}
}
} while (!update_seen && tries < 100);
return 0;
}
int __system_property_wait(const prop_info *pi)
{
unsigned n;
if(pi == 0) {
prop_area *pa = __system_property_area__;
n = pa->serial;
do {
__futex_wait(&pa->serial, n, 0);
} while(n == pa->serial);
} else {
n = pi->serial;
do {
__futex_wait((volatile void *)&pi->serial, n, 0);
} while(n == pi->serial);
}
return 0;
}