Merge "Added state snapshot support for QemuD service "boot properties"."
diff --git a/android/boot-properties.c b/android/boot-properties.c
index c094202..1a86010 100644
--- a/android/boot-properties.c
+++ b/android/boot-properties.c
@@ -16,6 +16,8 @@
 #include "android/hw-qemud.h"
 #include "android/globals.h"
 
+#include "hw/hw.h"
+
 #define  D(...)  VERBOSE_PRINT(init,__VA_ARGS__)
 
 /* define T_ACTIVE to 1 to debug transport communications */
@@ -59,10 +61,32 @@
     return prop;
 }
 
-static BootProperty*   _boot_properties;
+static BootProperty*   _boot_properties = NULL;
+/* address to store pointer to next new list element */
 static BootProperty**  _boot_properties_tail = &_boot_properties;
 static int             _inited;
 
+/* Clears all existing boot properties
+ */
+static void
+boot_property_clear_all()
+{
+    /* free all elements of the linked list */
+    BootProperty *p = _boot_properties;
+    BootProperty *next = NULL;
+    while (p) {
+        next = p->next;
+        AFREE(p);
+        p = next;
+    }
+
+    /* reset list administration to initial state */
+    _boot_properties = NULL;
+    _boot_properties_tail = &_boot_properties;
+}
+
+/* Appends a new boot property to the end of the internal list.
+ */
 int
 boot_property_add2( const char*  name, int  namelen,
                     const char*  value, int  valuelen )
@@ -97,7 +121,7 @@
     D("Adding boot property: '%.*s' = '%.*s'",
       namelen, name, valuelen, value);
 
-    /* add to the internal list */
+    /* add to the end of the internal list */
     prop = boot_property_alloc(name, namelen, value, valuelen);
 
     *_boot_properties_tail = prop;
@@ -106,6 +130,28 @@
     return 0;
 }
 
+/* Prints the warning string corresponding to the error code returned by
+ * boot_propery_add2().
+ */
+static void
+boot_property_raise_warning( int ret, const char*  name, int  namelen,
+                             const char*  value, int  valuelen )
+{
+    switch (ret) {
+    case -1:
+        dwarning("boot property name too long: '%.*s'",
+                    namelen, name);
+        break;
+    case -2:
+        dwarning("boot property value too long: '%.*s'",
+                    valuelen, value);
+        break;
+    case -3:
+        dwarning("boot property name contains invalid chars: %.*s",
+                    namelen, name);
+        break;
+    }
+}
 
 int
 boot_property_add( const char*  name, const char*  value )
@@ -116,7 +162,131 @@
     return boot_property_add2(name, namelen, value, valuelen);
 }
 
+/* Saves a single BootProperty to file.
+ */
+static int
+boot_property_save_property( QEMUFile  *f, BootProperty  *p )
+{
+    /* split in key and value, so we can re-use boot_property_add (and its
+     * sanity checks) when loading
+     */
 
+    char *split = strchr(p->property, '=');
+    if (split == NULL) {
+        D("%s: save failed: illegal key/value pair \"%s\" (missing '=')\n",
+          __FUNCTION__, p->property);
+        qemu_file_set_error(f);
+        return -1;
+    }
+
+    *split = '\0';  /* p->property is now "<key>\0<value>\0" */
+
+    uint32_t key_buf_len = (split - p->property) + 1; // +1: '\0' terminator
+    qemu_put_be32(f, key_buf_len);
+    qemu_put_buffer(f, (uint8_t*) p->property, key_buf_len);
+
+    uint32_t value_buf_len = p->length - key_buf_len + 1; // +1: '\0' terminator
+    qemu_put_be32(f, value_buf_len);
+    qemu_put_buffer(f, (uint8_t*) split + 1, value_buf_len);
+
+    *split = '=';  /* restore property to "<key>=<value>\0" */
+
+    return 0;
+}
+
+/* Loads a single boot property from a snapshot file
+ */
+static int
+boot_property_load_property( QEMUFile  *f )
+{
+    int ret;
+
+    /* load key */
+    uint32_t key_buf_len = qemu_get_be32(f);
+    char* key = android_alloc(key_buf_len);
+    if ((ret = qemu_get_buffer(f, (uint8_t*)key, key_buf_len) != key_buf_len)) {
+        D("%s: key load failed: expected %d bytes, got %d\n",
+          __FUNCTION__, key_buf_len, ret);
+        goto fail_key;
+    }
+
+    /* load value */
+    uint32_t value_buf_len = qemu_get_be32(f);
+    char* value = android_alloc(value_buf_len);
+    if ((ret = qemu_get_buffer(f, (uint8_t*)value, value_buf_len) != value_buf_len)) {
+        D("%s: value load failed: expected %d bytes, got %d\n",
+          __FUNCTION__, value_buf_len, ret);
+        goto fail_value;
+    }
+
+    /* add the property */
+    ret = boot_property_add2(key, key_buf_len - 1, value, value_buf_len - 1);
+    if (ret < 0) {
+        D("%s: load failed: cannot add boot property (details follow)\n",
+          __FUNCTION__);
+        boot_property_raise_warning(ret, key, key_buf_len - 1, value, value_buf_len - 1);
+        goto fail_value;
+    }
+
+    return 0;
+
+    /* in case of errors, clean up before return */
+    fail_value:
+        AFREE(value);
+    fail_key:
+        AFREE(key);
+        return -EIO;
+}
+
+/* Saves the number of available boot properties to file
+ */
+static void
+boot_property_save_count( QEMUFile*  f, BootProperty*  p )
+{
+    uint32_t property_count = 0;
+    for (; p; p = p->next) {
+        property_count++;
+    }
+
+    qemu_put_be32(f, property_count);
+}
+
+/* Saves all available boot properties to snapshot.
+ */
+static void
+boot_property_save( QEMUFile*  f, QemudService*  service, void*  opaque )
+{
+    boot_property_save_count(f, _boot_properties);
+
+    BootProperty *p = _boot_properties;
+    for ( ; p; p = p->next) {
+        if (boot_property_save_property(f, p)) {
+            break;  /* abort on error */
+        }
+    }
+}
+
+/* Replaces the currently available boot properties by those stored
+ * in a snapshot.
+ */
+static int
+boot_property_load( QEMUFile*  f, QemudService*  service, void*  opaque )
+{
+    int ret;
+
+    /* remove properties from old run */
+    boot_property_clear_all();
+
+    /* load properties from snapshot */
+    uint32_t i, property_count = qemu_get_be32(f);
+    for (i = 0; i < property_count; i++) {
+        if ((ret = boot_property_load_property(f))) {
+            return ret;
+        }
+    }
+
+    return 0;
+}
 
 #define SERVICE_NAME  "boot-properties"
 
@@ -167,9 +337,10 @@
 {
     if (!_inited) {
         QemudService*  serv = qemud_service_register( SERVICE_NAME,
-                                                    1, NULL,
-                                                    boot_property_service_connect,
-                                                    NULL, NULL);
+                                                      1, NULL,
+                                                      boot_property_service_connect,
+                                                      boot_property_save,
+                                                      boot_property_load);
         if (serv == NULL) {
             derror("could not register '%s' service", SERVICE_NAME);
             return;
@@ -201,19 +372,6 @@
 
     ret = boot_property_add2(name, namelen, value, valuelen);
     if (ret < 0) {
-        switch (ret) {
-        case -1: 
-            dwarning("boot property name too long: '%.*s'",
-                        namelen, name);
-            break;
-        case -2:
-            dwarning("boot property value too long: '%.*s'",
-                        valuelen, value);
-            break;
-        case -3:
-            dwarning("boot property name contains invalid chars: %.*s",
-                        namelen, name);
-            break;
-        }
+        boot_property_raise_warning(ret, name, namelen, value, valuelen);
     }
 }