This adds the '-prop <name>=<value>' option which is used to set
boot-time system properties from the command line. This is done
by implementing a new 'boot-properties' qemud service in the emulator.

This is to be used by the 'qemu-props' helper program that will be
invoked by /system/etc/init.goldfish.rc to read a list of system
properties from the emulator and set them in the emulated system
during boot.
diff --git a/android/boot-properties.c b/android/boot-properties.c
new file mode 100644
index 0000000..1c714e9
--- /dev/null
+++ b/android/boot-properties.c
@@ -0,0 +1,214 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/boot-properties.h"
+#include "android/utils/debug.h"
+#include "android/utils/system.h"
+#include "android/hw-qemud.h"
+#include "android/globals.h"
+
+#define  D(...)  VERBOSE_PRINT(init,__VA_ARGS__)
+
+/* define T_ACTIVE to 1 to debug transport communications */
+#define  T_ACTIVE  0
+
+#if T_ACTIVE
+#define  T(...)  VERBOSE_PRINT(init,__VA_ARGS__)
+#else
+#define  T(...)   ((void)0)
+#endif
+
+typedef struct BootProperty {
+    struct BootProperty*  next;
+    char*                 property;
+    int                   length;
+} BootProperty;
+
+static BootProperty*
+boot_property_alloc( const char*  name,  int  namelen,
+                     const char*  value, int  valuelen )
+{
+    int            length = namelen + 1 + valuelen;
+    BootProperty*  prop = android_alloc( sizeof(*prop) + length + 1 );
+    char*          p;
+
+    prop->next     = NULL;
+    prop->property = p = (char*)(prop + 1);
+    prop->length   = length;
+
+    memcpy( p, name, namelen );
+    p += namelen;
+    *p++ = '=';
+    memcpy( p, value, valuelen );
+    p += valuelen;
+    *p = '\0';
+
+    return prop;
+}
+
+static BootProperty*   _boot_properties;
+static BootProperty**  _boot_properties_tail = &_boot_properties;
+static int             _inited;
+
+/* this code supports the list of system properties that will
+ * be set on boot in the emulated system.
+ */
+
+int
+boot_property_add2( const char*  name, int  namelen,
+                    const char*  value, int  valuelen )
+{
+    BootProperty*  prop;
+
+    /* check the lengths
+     */
+    if (namelen > PROPERTY_MAX_NAME)
+        return -1;
+
+    if (valuelen > PROPERTY_MAX_VALUE)
+        return -2;
+
+    /* check that there are not invalid characters in the
+     * property name
+     */
+    const char*  reject = " =$*?'\"";
+    int          nn;
+
+    for (nn = 0; nn < namelen; nn++) {
+        if (strchr(reject, name[nn]) != NULL)
+            return -3;
+    }
+
+    /* init service if needed */
+    if (!_inited) {
+        boot_property_init_service();
+        _inited = 1;
+    }
+
+    D("Adding boot property: '%.*s' = '%.*s'",
+      namelen, name, valuelen, value);
+
+    /* add to the internal list */
+    prop = boot_property_alloc(name, namelen, value, valuelen);
+
+    *_boot_properties_tail = prop;
+    _boot_properties_tail  = &prop->next;
+
+    return 0;
+}
+
+
+int
+boot_property_add( const char*  name, const char*  value )
+{
+    int  namelen = strlen(name);
+    int  valuelen = strlen(value);
+
+    return boot_property_add2(name, namelen, value, valuelen);
+}
+
+
+
+#define SERVICE_NAME  "boot-properties"
+
+static void
+boot_property_client_recv( void*         opaque,
+                           uint8_t*      msg,
+                           int           msglen,
+                           QemudClient*  client )
+{
+    /* the 'list' command shall send all boot properties
+     * to the client, then close the connection.
+     */
+    if (msglen == 4 && !memcmp(msg, "list", 4)) {
+        BootProperty*  prop;
+        for (prop = _boot_properties; prop != NULL; prop = prop->next) {
+            qemud_client_send(client, (uint8_t*)prop->property, prop->length);
+        }
+        qemud_client_close(client);
+        return;
+    }
+
+    /* unknown command ? */
+    D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg);
+}
+
+static QemudClient*
+boot_property_service_connect( void*          opaque,
+                               QemudService*  serv,
+                               int            channel )
+{
+    QemudClient*  client;
+
+    client = qemud_client_new( serv, channel, NULL,
+                               boot_property_client_recv,
+                               NULL );
+
+    qemud_client_set_framing(client, 1);
+    return client;
+}
+
+
+void
+boot_property_init_service( void )
+{
+    if (!_inited) {
+        QemudService*  serv = qemud_service_register( SERVICE_NAME,
+                                                    1, NULL,
+                                                    boot_property_service_connect );
+        if (serv == NULL) {
+            derror("could not register '%s' service", SERVICE_NAME);
+            return;
+        }
+        D("registered '%s' qemud service", SERVICE_NAME);
+    }
+}
+
+
+
+void
+boot_property_parse_option( const char*  param )
+{
+    char* q = strchr(param,'=');
+    const char* name;
+    const char* value;
+    int   namelen, valuelen, ret;
+
+    if (q == NULL) {
+        dwarning("boot property missing (=) separator: %s", param);
+        return;
+    }
+
+    name    = param;
+    namelen = q - param;
+
+    value    = q+1;
+    valuelen = strlen(name) - (namelen+1);
+
+    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;
+        }
+    }
+}
diff --git a/android/boot-properties.h b/android/boot-properties.h
new file mode 100644
index 0000000..6711c59
--- /dev/null
+++ b/android/boot-properties.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_BOOT_PROPERTIES_H
+#define _ANDROID_BOOT_PROPERTIES_H
+
+/* these values give the maximum length of system property
+ * names and values. They must match the corresponding definitions
+ * in the Android source tree (in system/core/include/cutils/properties.h)
+ */
+#define  PROPERTY_MAX_NAME    32
+#define  PROPERTY_MAX_VALUE   92
+
+/* record a new boot system property, this must be performed before the
+ * VM is started. Returns 0 on success, or < 0 on error.
+ * Possible errors are:
+ * -1 property name too long
+ * -2 property value too long
+ * -3 invalid characters in property name
+ */
+int  boot_property_add( const char*  name, const char*  value );
+
+/* same as boot_property_add, but allows to use non-zero terminated strings.
+ */
+int  boot_property_add2( const char*  name, int  namelen,
+                         const char*  value, int  valuelen );
+
+/* init the boot property QEMUD service. This must be performed before
+ * the VM is started. This is also performed automatically if you call
+ * boot_property_add().
+ */
+void  boot_property_init_service( void );
+
+/* parse the parameter the list of -prop options passed on the command line
+ */
+void  boot_property_parse_option( const char*  param );
+
+#endif /* _ANDROID_BOOT_PROPERTIES_H */
diff --git a/android/cmdline-option.c b/android/cmdline-option.c
index b773d9d..fb5aa23 100644
--- a/android/cmdline-option.c
+++ b/android/cmdline-option.c
@@ -1,6 +1,7 @@
 #include "android/cmdline-option.h"
 #include "android/utils/debug.h"
 #include "android/utils/misc.h"
+#include "android/utils/system.h"
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
@@ -15,11 +16,16 @@
 static void  parse_debug_tags( const char*  tags );
 void  parse_env_debug_tags( void );
 
+enum {
+    OPTION_IS_FLAG = 0,
+    OPTION_IS_PARAM,
+    OPTION_IS_LIST,
+};
 
 typedef struct {
     const char*  name;
     int          var_offset;
-    int          var_is_param;
+    int          var_type;
     int          var_is_config;
 } OptionInfo;
 
@@ -28,10 +34,11 @@
 
 
 static const OptionInfo  option_keys[] = {
-#define  OPT_PARAM(_name,_template,_descr)  OPTION(_name,1,0)
-#define  OPT_FLAG(_name,_descr)             OPTION(_name,0,0)
-#define  CFG_PARAM(_name,_template,_descr)  OPTION(_name,1,1)
-#define  CFG_FLAG(_name,_descr)             OPTION(_name,0,1)
+#define  OPT_FLAG(_name,_descr)             OPTION(_name,OPTION_IS_FLAG,0)
+#define  OPT_PARAM(_name,_template,_descr)  OPTION(_name,OPTION_IS_PARAM,0)
+#define  OPT_LIST(_name,_template,_descr)   OPTION(_name,OPTION_IS_LIST,0)
+#define  CFG_FLAG(_name,_descr)             OPTION(_name,OPTION_IS_FLAG,1)
+#define  CFG_PARAM(_name,_template,_descr)  OPTION(_name,OPTION_IS_PARAM,1)
 #include "android/cmdline-options.h"
     { NULL, 0, 0, 0 }
 };
@@ -145,15 +152,31 @@
                 if ( !strcmp( oo->name, arg2 ) ) {
                     void*  field = (char*)opt + oo->var_offset;
 
-                    if (oo->var_is_param) {
-                        /* parameter option */
+                    if (oo->var_type != OPTION_IS_FLAG) {
+                        /* parameter/list option */
                         if (nargs == 0) {
                             derror( "-%s must be followed by parameter (see -help-%s)",
                                     arg, arg );
                             exit(1);
                         }
                         nargs--;
-                        ((char**)field)[0] = *aread++;
+
+                        if (oo->var_type == OPTION_IS_PARAM)
+                        {
+                            ((char**)field)[0] = *aread++;
+                        }
+                        else if (oo->var_type == OPTION_IS_LIST) 
+                        {
+                            ParamList**  head = (ParamList**)field;
+                            ParamList*   pl;
+                            ANEW0(pl);
+                            /* note: store list items in reverse order here
+                             *       the list is reversed later in this function.
+                             */
+                            pl->param = *aread++;
+                            pl->next  = *head;
+                            *head     = pl;
+                        }
                     } else {
                         /* flag option */
                         ((int*)field)[0] = 1;
@@ -182,6 +205,28 @@
 
     awrite[0] = NULL;
 
+    /* reverse any parameter list before exit.
+     */
+    {
+        const OptionInfo*  oo = option_keys;
+
+        for ( ; oo->name; oo++ ) {
+            if ( oo->var_type == OPTION_IS_LIST ) {
+                ParamList**  head = (ParamList**)((char*)opt + oo->var_offset);
+                ParamList*   prev = NULL;
+                ParamList*   cur  = *head;
+
+                while (cur != NULL) {
+                    ParamList*  next = cur->next;
+                    cur->next = prev;
+                    prev      = cur;
+                    cur       = next;
+                }
+                *head = prev;
+            }
+        }
+    }
+
     return 0;
 }
 
diff --git a/android/cmdline-option.h b/android/cmdline-option.h
index b87144d..90a7e64 100644
--- a/android/cmdline-option.h
+++ b/android/cmdline-option.h
@@ -12,9 +12,17 @@
 #ifndef _ANDROID_OPTION_H
 #define _ANDROID_OPTION_H
 
+/* a structure used to model a linked list of parameters
+ */
+typedef struct ParamList {
+    char*              param;
+    struct ParamList*  next;
+} ParamList;
+
 /* define a structure that will hold all option variables
  */
 typedef struct {
+#define OPT_LIST(n,t,d)    ParamList*  n;
 #define OPT_PARAM(n,t,d)   char*  n;
 #define OPT_FLAG(n,d)      int    n;
 #include "android/cmdline-options.h"
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
index 5291177..0689e83 100644
--- a/android/cmdline-options.h
+++ b/android/cmdline-options.h
@@ -5,6 +5,9 @@
 #ifndef OPT_PARAM
 #error OPT_PARAM is not defined
 #endif
+#ifndef OPT_LIST
+#error OPT_LIST is not defined
+#endif
 #ifndef OPT_FLAG
 #error OPT_FLAG is not defined
 #endif
@@ -128,6 +131,8 @@
 
 OPT_PARAM( bootchart, "<timeout>", "enable bootcharting")
 
+OPT_LIST(  prop, "<name>=<value>", "set system property on boot")
+
 #ifdef CONFIG_NAND_LIMITS
 OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" )
 #endif
@@ -137,3 +142,4 @@
 #undef CFG_PARAM
 #undef OPT_FLAG
 #undef OPT_PARAM
+#undef OPT_LIST
diff --git a/android/help.c b/android/help.c
index 391b63d..d01ccc9 100644
--- a/android/help.c
+++ b/android/help.c
@@ -5,6 +5,7 @@
 #include "android/utils/debug.h"
 #include "android/utils/misc.h"
 #include "android/skin/keyset.h"
+#include "android/boot-properties.h"
 #include "android/android.h"
 #include <stdint.h>
 #include "audio/audio.h"
@@ -1281,6 +1282,24 @@
     );
 }
 
+static void
+help_prop(stralloc_t  *out)
+{
+    PRINTF(
+    "  use '-prop <name>=<value>' to set a boot-time system property.\n"
+    "  <name> must be a property name of at most %d characters, without any\n"
+    "  space in it, and <value> must be a string of at most %d characters.\n\n",
+    PROPERTY_MAX_NAME, PROPERTY_MAX_VALUE );
+
+    PRINTF(
+    "  the corresponding system property will be set at boot time in the\n"
+    "  emulated system. This can be useful for debugging purposes.\n\n"
+
+    "  note that you can use several -prop options to define more than one\n"
+    "  boot property.\n\n"
+    );
+}
+
 #define  help_no_skin   NULL
 #define  help_netspeed  help_shaper
 #define  help_netdelay  help_shaper
@@ -1307,6 +1326,7 @@
 static const OptionHelp    option_help[] = {
 #define  OPT_FLAG(_name,_descr)             { STRINGIFY(_name), NULL, _descr, help_##_name },
 #define  OPT_PARAM(_name,_template,_descr)  { STRINGIFY(_name), _template, _descr, help_##_name },
+#define  OPT_LIST                           OPT_PARAM
 #include "android/cmdline-options.h"
     { NULL, NULL, NULL, NULL }
 };
diff --git a/android/hw-control.c b/android/hw-control.c
index a82a92a..d99f7e3 100644
--- a/android/hw-control.c
+++ b/android/hw-control.c
@@ -51,9 +51,10 @@
 
 /* called when a qemud client sends a command */
 static void
-_hw_control_qemud_client_recv( void*  opaque,
-                               uint8_t*  msg,
-                               int       msglen )
+_hw_control_qemud_client_recv( void*         opaque,
+                               uint8_t*      msg,
+                               int           msglen,
+                               QemudClient*  client )
 {
     hw_control_do_query(opaque, msg, msglen);
 }
diff --git a/android/hw-qemud.c b/android/hw-qemud.c
index ba4ab42..efe6a99 100644
--- a/android/hw-qemud.c
+++ b/android/hw-qemud.c
@@ -545,7 +545,7 @@
     /* no framing, things are simple */
     if (!c->framing) {
         if (c->clie_recv)
-            c->clie_recv( c->clie_opaque, msg, msglen );
+            c->clie_recv( c->clie_opaque, msg, msglen, c );
         return;
     }
 
@@ -566,7 +566,7 @@
             if (c->clie_recv)
                 c->clie_recv( c->clie_opaque, 
                               msg+FRAME_HEADER_SIZE,
-                              msglen-FRAME_HEADER_SIZE);
+                              msglen-FRAME_HEADER_SIZE, c );
             return;
         }
     }
@@ -606,7 +606,7 @@
 
 
         if (c->clie_recv)
-            c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size );
+            c->clie_recv( c->clie_opaque, c->payload->buff, c->payload->size, c );
 
         AFREE(c->payload->buff);
         c->need_header = 1;
@@ -893,9 +893,10 @@
  * (i.e. msg[msglen] is a valid memory read that returns '\0')
  */
 static void
-qemud_multiplexer_control_recv( void*     opaque,
-                                uint8_t*  msg,
-                                int       msglen )
+qemud_multiplexer_control_recv( void*         opaque,
+                                uint8_t*      msg,
+                                int           msglen,
+                                QemudClient*  client )
 {
     QemudMultiplexer*  mult   = opaque;
     uint8_t*           msgend = msg + msglen;
@@ -1226,7 +1227,8 @@
  * this simply sends the message through the charpipe to the user.
  */
 static void
-_qemud_char_client_recv( void*  opaque, uint8_t*  msg, int  msglen )
+_qemud_char_client_recv( void*  opaque, uint8_t*  msg, int  msglen,
+                         QemudClient*  client )
 {
     CharDriverState*  cs = opaque;
     qemu_chr_write(cs, msg, msglen);
diff --git a/android/hw-qemud.h b/android/hw-qemud.h
index 5b45a94..8aae74d 100644
--- a/android/hw-qemud.h
+++ b/android/hw-qemud.h
@@ -62,28 +62,58 @@
 typedef struct QemudService  QemudService;
 
 
+/* A function that will be called when the client running in the emulated
+ * system has closed its connection to qemud.
+ */
 typedef void (*QemudClientClose)( void*  opaque );
-typedef void (*QemudClientRecv) ( void*  opaque, uint8_t*  msg, int  msglen );
 
+/* A function that will be called when the client sends a message to the
+ * service through qemud.
+ */
+typedef void (*QemudClientRecv) ( void*  opaque, uint8_t*  msg, int  msglen, QemudClient*  client );
+
+/* Register a new client for a given service.
+ * 'clie_opaque' will be sent as the first argument to 'clie_recv' and 'clie_close'
+ * 'clie_recv' and 'clie_close' are both optional and may be NULL.
+ *
+ * You should typically use this function within a QemudServiceConnect callback
+ * (see below).
+ */
 extern QemudClient*  qemud_client_new( QemudService*     service,
                                        int               channel_id,
                                        void*             clie_opaque,
                                        QemudClientRecv   clie_recv,
                                        QemudClientClose  clie_close );
 
+/* Enable framing on a given client channel.
+ */
 extern void           qemud_client_set_framing( QemudClient*  client, int  enabled );
 
+/* Send a message to a given qemud client
+ */
 extern void   qemud_client_send ( QemudClient*  client, const uint8_t*  msg, int  msglen );
+
+/* Force-close the connection to a given qemud client.
+ */
 extern void   qemud_client_close( QemudClient*  client );
 
 
+/* A function that will be called each time a new client in the emulated
+ * system tries to connect to a given qemud service. This should typically
+ * call qemud_client_new() to register a new client.
+ */
 typedef QemudClient*  (*QemudServiceConnect)( void*   opaque, QemudService*  service, int  channel );
 
+/* Register a new qemud service.
+ * 'serv_opaque' is the first parameter to 'serv_connect'
+ */
 extern QemudService*  qemud_service_register( const char*          serviceName,
                                               int                  max_clients,
                                               void*                serv_opaque,
                                               QemudServiceConnect  serv_connect );
 
+/* Sends a message to all clients of a given service.
+ */
 extern void           qemud_service_broadcast( QemudService*   sv,
                                                const uint8_t*  msg,
                                                int             msglen );
diff --git a/android/hw-sensors.c b/android/hw-sensors.c
index eb2cc78..37d4af7 100644
--- a/android/hw-sensors.c
+++ b/android/hw-sensors.c
@@ -158,7 +158,8 @@
 /* Qemud service management */
 
 static void
-_hw_sensors_qemud_client_recv( void*  opaque, uint8_t*  msg, int  msglen )
+_hw_sensors_qemud_client_recv( void*  opaque, uint8_t*  msg, int  msglen,
+                               QemudClient*  client )
 {
     hw_sensors_receive(opaque, msg, msglen);
 }
diff --git a/android/main.c b/android/main.c
index ed005d2..cd4162a 100644
--- a/android/main.c
+++ b/android/main.c
@@ -52,6 +52,7 @@
 #include "android/hw-kmsg.h"
 #include "android/hw-control.h"
 #include "android/hw-sensors.h"
+#include "android/boot-properties.h"
 #include "android/user-config.h"
 #include "android/utils/bufprint.h"
 #include "android/utils/dirscanner.h"
@@ -2493,6 +2494,20 @@
         }
     }
 
+    /* start the 'boot-properties service, and parse the -prop
+     * options, if any.
+     */
+    boot_property_init_service();
+
+    if (opts->prop != NULL) {
+        ParamList*  pl = opts->prop;
+        for ( ; pl != NULL; pl = pl->next ) {
+            boot_property_parse_option(pl->param);
+        }
+    }
+
+    /* Setup the kernel init options
+     */
     {
         static char  params[1024];
         char        *p = params, *end = p + sizeof(params);