bionic: move system property writing from init to bionic

Move the implementation of writing to the system property area
from init to bionic, next to the reader implementation.  This
will allow full property testing to be added to bionic tests.

Add new accessor and waiting functions to hide the implementation
from watchprops and various bionic users.

Also hide some of the implementation details of the property area
from init by moving them into _system_properties.h, and other details
from everybody by moving them into system_properties.h.

Change-Id: I9026e604109e30546b2849b60cab2e7e5ff00ba5
diff --git a/libc/bionic/system_properties.c b/libc/bionic/system_properties.c
index 0587430..d969507 100644
--- a/libc/bionic/system_properties.c
+++ b/libc/bionic/system_properties.c
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 #include <stdio.h>
+#include <stdint.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stddef.h>
@@ -49,6 +50,25 @@
 
 #include <sys/atomics.h>
 
+struct prop_area {
+    unsigned volatile count;
+    unsigned volatile serial;
+    unsigned magic;
+    unsigned version;
+    unsigned reserved[4];
+    unsigned toc[1];
+};
+
+typedef struct prop_area prop_area;
+
+struct prop_info {
+    char name[PROP_NAME_MAX];
+    unsigned volatile serial;
+    char value[PROP_VALUE_MAX];
+};
+
+typedef struct prop_info prop_info;
+
 static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
 
 static unsigned dummy_props = 0;
@@ -66,6 +86,17 @@
     return atoi(env);
 }
 
+void __system_property_area_init(void *data)
+{
+    prop_area *pa = data;
+    memset(pa, 0, PA_SIZE);
+    pa->magic = PROP_AREA_MAGIC;
+    pa->version = PROP_AREA_VERSION;
+
+    /* plug into the lib property services */
+    __system_property_area__ = pa;
+}
+
 int __system_properties_init(void)
 {
     bool fromFile = true;
@@ -147,6 +178,11 @@
     unsigned len = strlen(name);
     prop_info *pi;
 
+    if (len >= PROP_NAME_MAX)
+        return 0;
+    if (len < 1)
+        return 0;
+
     while(count--) {
         unsigned entry = *toc++;
         if(TOC_NAME_LEN(entry) != len) continue;
@@ -294,3 +330,68 @@
     }
     return 0;
 }
+
+int __system_property_update(prop_info *pi, const char *value, unsigned int len)
+{
+    prop_area *pa = __system_property_area__;
+
+    if (len >= PROP_VALUE_MAX)
+        return -1;
+
+    pi->serial = pi->serial | 1;
+    memcpy(pi->value, value, len + 1);
+    pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
+    __futex_wake(&pi->serial, INT32_MAX);
+
+    pa->serial++;
+    __futex_wake(&pa->serial, INT32_MAX);
+
+    return 0;
+}
+
+int __system_property_add(const char *name, unsigned int namelen,
+            const char *value, unsigned int valuelen)
+{
+    prop_area *pa = __system_property_area__;
+    prop_info *pa_info_array = (void*) (((char*) pa) + PA_INFO_START);
+    prop_info *pi;
+
+    if (pa->count == PA_COUNT_MAX)
+        return -1;
+    if (namelen >= PROP_NAME_MAX)
+        return -1;
+    if (valuelen >= PROP_VALUE_MAX)
+        return -1;
+    if (namelen < 1)
+        return -1;
+
+    pi = pa_info_array + pa->count;
+    pi->serial = (valuelen << 24);
+    memcpy(pi->name, name, namelen + 1);
+    memcpy(pi->value, value, valuelen + 1);
+
+    pa->toc[pa->count] =
+        (namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
+
+    pa->count++;
+    pa->serial++;
+    __futex_wake(&pa->serial, INT32_MAX);
+
+    return 0;
+}
+
+unsigned int __system_property_serial(const prop_info *pi)
+{
+    return pi->serial;
+}
+
+unsigned int __system_property_wait_any(unsigned int serial)
+{
+    prop_area *pa = __system_property_area__;
+
+    do {
+        __futex_wait(&pa->serial, serial, 0);
+    } while(pa->serial == serial);
+
+    return pa->serial;
+}