Merge "Tell the user to unlock/confirm backup & restore operations"
diff --git a/init/builtins.c b/init/builtins.c
index eccda3f..3781dcd 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -31,6 +31,7 @@
 #include <sys/resource.h>
 #include <linux/loop.h>
 #include <cutils/partition_utils.h>
+#include <sys/system_properties.h>
 
 #include "init.h"
 #include "keywords.h"
@@ -448,22 +449,15 @@
 {
     const char *name = args[1];
     const char *value = args[2];
+    char prop_val[PROP_VALUE_MAX];
+    int ret;
 
-    if (value[0] == '$') {
-        /* Use the value of a system property if value starts with '$' */
-        value++;
-        if (value[0] != '$') {
-            value = property_get(value);
-            if (!value) {
-                ERROR("property %s has no value for assigning to %s\n", value, name);
-                return -EINVAL;
-            }
-        } /* else fall through to support double '$' prefix for setting properties
-           * to string literals that start with '$'
-           */
+    ret = expand_props(prop_val, value, sizeof(prop_val));
+    if (ret) {
+        ERROR("cannot expand '%s' while assigning to '%s'\n", value, name);
+        return -EINVAL;
     }
-
-    property_set(name, value);
+    property_set(name, prop_val);
     return 0;
 }
 
@@ -547,21 +541,15 @@
 {
     const char *path = args[1];
     const char *value = args[2];
-    if (value[0] == '$') {
-        /* Write the value of a system property if value starts with '$' */
-        value++;
-        if (value[0] != '$') {
-            value = property_get(value);
-            if (!value) {
-                ERROR("property %s has no value for writing to %s\n", value, path);
-                return -EINVAL;
-            }
-        } /* else fall through to support double '$' prefix for writing
-           * string literals that start with '$'
-           */
-    }
+    char prop_val[PROP_VALUE_MAX];
+    int ret;
 
-    return write_file(path, value);
+    ret = expand_props(prop_val, value, sizeof(prop_val));
+    if (ret) {
+        ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
+        return -EINVAL;
+    }
+    return write_file(path, prop_val);
 }
 
 int do_copy(int nargs, char **args)
diff --git a/init/init.c b/init/init.c
index d10ca47..c3be93d 100755
--- a/init/init.c
+++ b/init/init.c
@@ -59,11 +59,7 @@
 #endif
 
 static char console[32];
-static char serialno[32];
 static char bootmode[32];
-static char baseband[32];
-static char carrier[32];
-static char bootloader[32];
 static char hardware[32];
 static unsigned revision = 0;
 static char qemu[32];
@@ -420,45 +416,6 @@
     }
 }
 
-static void import_kernel_nv(char *name, int in_qemu)
-{
-    char *value = strchr(name, '=');
-
-    if (value == 0) return;
-    *value++ = 0;
-    if (*name == 0) return;
-
-    if (!in_qemu)
-    {
-        /* on a real device, white-list the kernel options */
-        if (!strcmp(name,"qemu")) {
-            strlcpy(qemu, value, sizeof(qemu));
-        } else if (!strcmp(name,"androidboot.console")) {
-            strlcpy(console, value, sizeof(console));
-        } else if (!strcmp(name,"androidboot.mode")) {
-            strlcpy(bootmode, value, sizeof(bootmode));
-        } else if (!strcmp(name,"androidboot.serialno")) {
-            strlcpy(serialno, value, sizeof(serialno));
-        } else if (!strcmp(name,"androidboot.baseband")) {
-            strlcpy(baseband, value, sizeof(baseband));
-        } else if (!strcmp(name,"androidboot.carrier")) {
-            strlcpy(carrier, value, sizeof(carrier));
-        } else if (!strcmp(name,"androidboot.bootloader")) {
-            strlcpy(bootloader, value, sizeof(bootloader));
-        } else if (!strcmp(name,"androidboot.hardware")) {
-            strlcpy(hardware, value, sizeof(hardware));
-        }
-    } else {
-        /* in the emulator, export any kernel option with the
-         * ro.kernel. prefix */
-        char  buff[32];
-        int   len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
-        if (len < (int)sizeof(buff)) {
-            property_set( buff, value );
-        }
-    }
-}
-
 static struct command *get_first_command(struct action *act)
 {
     struct listnode *node;
@@ -518,17 +475,6 @@
     return ret;
 }
 
-static int property_init_action(int nargs, char **args)
-{
-    bool load_defaults = true;
-
-    INFO("property init\n");
-    if (!strcmp(bootmode, "charger"))
-        load_defaults = false;
-    property_init(load_defaults);
-    return 0;
-}
-
 static int keychord_init_action(int nargs, char **args)
 {
     keychord_init();
@@ -576,30 +522,104 @@
     return 0;
 }
 
-static int set_init_properties_action(int nargs, char **args)
+static void import_kernel_nv(char *name, int for_emulator)
+{
+    char *value = strchr(name, '=');
+    int name_len = strlen(name);
+
+    if (value == 0) return;
+    *value++ = 0;
+    if (name_len == 0) return;
+
+    if (for_emulator) {
+        /* in the emulator, export any kernel option with the
+         * ro.kernel. prefix */
+        char buff[PROP_NAME_MAX];
+        int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
+
+        if (len < (int)sizeof(buff))
+            property_set( buff, value );
+        return;
+    }
+
+    if (!strcmp(name,"qemu")) {
+        strlcpy(qemu, value, sizeof(qemu));
+    } else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
+        const char *boot_prop_name = name + 12;
+        char prop[PROP_NAME_MAX];
+        int cnt;
+
+        cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
+        if (cnt < PROP_NAME_MAX)
+            property_set(prop, value);
+    }
+}
+
+static void export_kernel_boot_props(void)
 {
     char tmp[PROP_VALUE_MAX];
+    const char *pval;
+    unsigned i;
+    struct {
+        const char *src_prop;
+        const char *dest_prop;
+        const char *def_val;
+    } prop_map[] = {
+        { "ro.boot.serialno", "ro.serialno", "", },
+        { "ro.boot.mode", "ro.bootmode", "unknown", },
+        { "ro.boot.baseband", "ro.baseband", "unknown", },
+        { "ro.boot.carrier", "ro.carrier", "unknown", },
+        { "ro.boot.bootloader", "ro.bootloader", "unknown", },
+    };
 
-    if (qemu[0])
-        import_kernel_cmdline(1, import_kernel_nv);
+    for (i = 0; i < ARRAY_SIZE(prop_map); i++) {
+        pval = property_get(prop_map[i].src_prop);
+        property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);
+    }
 
+    pval = property_get("ro.boot.console");
+    if (pval)
+        strlcpy(console, pval, sizeof(console));
+
+    /* save a copy for init's usage during boot */
+    strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));
+
+    /* if this was given on kernel command line, override what we read
+     * before (e.g. from /proc/cpuinfo), if anything */
+    pval = property_get("ro.boot.hardware");
+    if (pval)
+        strlcpy(hardware, pval, sizeof(hardware));
+    property_set("ro.hardware", hardware);
+
+    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
+    property_set("ro.revision", tmp);
+
+    /* TODO: these are obsolete. We should delete them */
     if (!strcmp(bootmode,"factory"))
         property_set("ro.factorytest", "1");
     else if (!strcmp(bootmode,"factory2"))
         property_set("ro.factorytest", "2");
     else
         property_set("ro.factorytest", "0");
+}
 
-    property_set("ro.serialno", serialno[0] ? serialno : "");
-    property_set("ro.bootmode", bootmode[0] ? bootmode : "unknown");
-    property_set("ro.baseband", baseband[0] ? baseband : "unknown");
-    property_set("ro.carrier", carrier[0] ? carrier : "unknown");
-    property_set("ro.bootloader", bootloader[0] ? bootloader : "unknown");
+static void process_kernel_cmdline(void)
+{
+    /* don't expose the raw commandline to nonpriv processes */
+    chmod("/proc/cmdline", 0440);
 
-    property_set("ro.hardware", hardware);
-    snprintf(tmp, PROP_VALUE_MAX, "%d", revision);
-    property_set("ro.revision", tmp);
-    return 0;
+    /* first pass does the common stuff, and finds if we are in qemu.
+     * second pass is only necessary for qemu to export all kernel params
+     * as props.
+     */
+    import_kernel_cmdline(0, import_kernel_nv);
+    if (qemu[0])
+        import_kernel_cmdline(1, import_kernel_nv);
+
+    /* now propogate the info given on command line to internal variables
+     * used by init as well as the current required properties
+     */
+    export_kernel_boot_props();
 }
 
 static int property_service_init_action(int nargs, char **args)
@@ -668,6 +688,7 @@
     int property_set_fd_init = 0;
     int signal_fd_init = 0;
     int keychord_fd_init = 0;
+    bool is_charger = false;
 
     if (!strcmp(basename(argv[0]), "ueventd"))
         return ueventd_main(argc, argv);
@@ -701,31 +722,32 @@
          */
     open_devnull_stdio();
     klog_init();
+    property_init();
+
+    get_hardware_name(hardware, &revision);
+
+    process_kernel_cmdline();
+
+    is_charger = !strcmp(bootmode, "charger");
+
+    INFO("property init\n");
+    if (!is_charger)
+        property_load_boot_defaults();
 
     INFO("reading config file\n");
     init_parse_config_file("/init.rc");
 
-    /* pull the kernel commandline and ramdisk properties file in */
-    import_kernel_cmdline(0, import_kernel_nv);
-    /* don't expose the raw commandline to nonpriv processes */
-    chmod("/proc/cmdline", 0440);
-    get_hardware_name(hardware, &revision);
-    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
-    init_parse_config_file(tmp);
-
     action_for_each_trigger("early-init", action_add_queue_tail);
 
     queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
-    queue_builtin_action(property_init_action, "property_init");
     queue_builtin_action(keychord_init_action, "keychord_init");
     queue_builtin_action(console_init_action, "console_init");
-    queue_builtin_action(set_init_properties_action, "set_init_properties");
 
     /* execute all the boot actions to get us started */
     action_for_each_trigger("init", action_add_queue_tail);
 
     /* skip mounting filesystems in charger mode */
-    if (strcmp(bootmode, "charger") != 0) {
+    if (!is_charger) {
         action_for_each_trigger("early-fs", action_add_queue_tail);
         action_for_each_trigger("fs", action_add_queue_tail);
         action_for_each_trigger("post-fs", action_add_queue_tail);
@@ -736,7 +758,7 @@
     queue_builtin_action(signal_init_action, "signal_init");
     queue_builtin_action(check_startup_action, "check_startup");
 
-    if (!strcmp(bootmode, "charger")) {
+    if (is_charger) {
         action_for_each_trigger("charger", action_add_queue_tail);
     } else {
         action_for_each_trigger("early-boot", action_add_queue_tail);
diff --git a/init/init_parser.c b/init/init_parser.c
index 13c94eb..d255db9 100644
--- a/init/init_parser.c
+++ b/init/init_parser.c
@@ -40,6 +40,11 @@
 static list_declare(action_list);
 static list_declare(action_queue);
 
+struct import {
+    struct listnode list;
+    const char *filename;
+};
+
 static void *parse_service(struct parse_state *state, int nargs, char **args);
 static void parse_line_service(struct parse_state *state, int nargs, char **args);
 
@@ -159,6 +164,149 @@
 {
 }
 
+static int push_chars(char **dst, int *len, const char *chars, int cnt)
+{
+    if (cnt > *len)
+        return -1;
+
+    memcpy(*dst, chars, cnt);
+    *dst += cnt;
+    *len -= cnt;
+
+    return 0;
+}
+
+int expand_props(char *dst, const char *src, int dst_size)
+{
+    int cnt = 0;
+    char *dst_ptr = dst;
+    const char *src_ptr = src;
+    int src_len;
+    int idx = 0;
+    int ret = 0;
+    int left = dst_size - 1;
+
+    if (!src || !dst || dst_size == 0)
+        return -1;
+
+    src_len = strlen(src);
+
+    /* - variables can either be $x.y or ${x.y}, in case they are only part
+     *   of the string.
+     * - will accept $$ as a literal $.
+     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+     *   bad things will happen
+     */
+    while (*src_ptr && left > 0) {
+        char *c;
+        char prop[PROP_NAME_MAX + 1];
+        const char *prop_val;
+        int prop_len = 0;
+
+        c = strchr(src_ptr, '$');
+        if (!c) {
+            while (left-- > 0 && *src_ptr)
+                *(dst_ptr++) = *(src_ptr++);
+            break;
+        }
+
+        memset(prop, 0, sizeof(prop));
+
+        ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr);
+        if (ret < 0)
+            goto err_nospace;
+        c++;
+
+        if (*c == '$') {
+            *(dst_ptr++) = *(c++);
+            src_ptr = c;
+            left--;
+            continue;
+        } else if (*c == '\0') {
+            break;
+        }
+
+        if (*c == '{') {
+            c++;
+            while (*c && *c != '}' && prop_len < PROP_NAME_MAX)
+                prop[prop_len++] = *(c++);
+            if (*c != '}') {
+                /* failed to find closing brace, abort. */
+                if (prop_len == PROP_NAME_MAX)
+                    ERROR("prop name too long during expansion of '%s'\n",
+                          src);
+                else if (*c == '\0')
+                    ERROR("unexpected end of string in '%s', looking for }\n",
+                          src);
+                goto err;
+            }
+            prop[prop_len] = '\0';
+            c++;
+        } else if (*c) {
+            while (*c && prop_len < PROP_NAME_MAX)
+                prop[prop_len++] = *(c++);
+            if (prop_len == PROP_NAME_MAX && *c != '\0') {
+                ERROR("prop name too long in '%s'\n", src);
+                goto err;
+            }
+            prop[prop_len] = '\0';
+            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
+                  prop);
+        }
+
+        if (prop_len == 0) {
+            ERROR("invalid zero-length prop name in '%s'\n", src);
+            goto err;
+        }
+
+        prop_val = property_get(prop);
+        if (!prop_val) {
+            ERROR("property '%s' doesn't exist while expanding '%s'\n",
+                  prop, src);
+            goto err;
+        }
+
+        ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val));
+        if (ret < 0)
+            goto err_nospace;
+        src_ptr = c;
+        continue;
+    }
+
+    *dst_ptr = '\0';
+    return 0;
+
+err_nospace:
+    ERROR("destination buffer overflow while expanding '%s'\n", src);
+err:
+    return -1;
+}
+
+void parse_import(struct parse_state *state, int nargs, char **args)
+{
+    struct listnode *import_list = state->priv;
+    struct import *import;
+    char conf_file[PATH_MAX];
+    int ret;
+
+    if (nargs != 2) {
+        ERROR("single argument needed for import\n");
+        return;
+    }
+
+    ret = expand_props(conf_file, args[1], sizeof(conf_file));
+    if (ret) {
+        ERROR("error while handling import on line '%d' in '%s'\n",
+              state->line, state->filename);
+        return;
+    }
+
+    import = calloc(1, sizeof(struct import));
+    import->filename = strdup(conf_file);
+    list_add_tail(import_list, &import->list);
+    INFO("found import '%s', adding to import list", import->filename);
+}
+
 void parse_new_section(struct parse_state *state, int kw,
                        int nargs, char **args)
 {
@@ -180,13 +328,8 @@
         }
         break;
     case K_import:
-        if (nargs != 2) {
-            ERROR("single argument needed for import\n");
-        } else {
-            int ret = init_parse_config_file(args[1]);
-            if (ret)
-                ERROR("could not import file %s\n", args[1]);
-        }
+        parse_import(state, nargs, args);
+        break;
     }
     state->parse_line = parse_line_no_op;
 }
@@ -194,6 +337,8 @@
 static void parse_config(const char *fn, char *s)
 {
     struct parse_state state;
+    struct listnode import_list;
+    struct listnode *node;
     char *args[INIT_PARSER_MAXARGS];
     int nargs;
 
@@ -203,11 +348,15 @@
     state.ptr = s;
     state.nexttoken = 0;
     state.parse_line = parse_line_no_op;
+
+    list_init(&import_list);
+    state.priv = &import_list;
+
     for (;;) {
         switch (next_token(&state)) {
         case T_EOF:
             state.parse_line(&state, 0, 0);
-            return;
+            goto parser_done;
         case T_NEWLINE:
             state.line++;
             if (nargs) {
@@ -228,6 +377,18 @@
             break;
         }
     }
+
+parser_done:
+    list_for_each(node, &import_list) {
+         struct import *import = node_to_item(node, struct import, list);
+         int ret;
+
+         INFO("importing '%s'", import->filename);
+         ret = init_parse_config_file(import->filename);
+         if (ret)
+             ERROR("could not import file '%s' from '%s'\n",
+                   import->filename, fn);
+    }
 }
 
 int init_parse_config_file(const char *fn)
diff --git a/init/init_parser.h b/init/init_parser.h
index ff13b04..b078cad 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -31,5 +31,6 @@
 void queue_builtin_action(int (*func)(int nargs, char **args), char *name);
 
 int init_parse_config_file(const char *fn);
+int expand_props(char *dst, const char *src, int len);
 
 #endif
diff --git a/init/parser.h b/init/parser.h
index be93758..0a5802a 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -30,6 +30,7 @@
     void *context;
     void (*parse_line)(struct parse_state *state, int nargs, char **args);
     const char *filename;
+    void *priv;
 };
 
 int lookup_keyword(const char *s);
diff --git a/init/property_service.c b/init/property_service.c
index 687de6d..d06df31 100644
--- a/init/property_service.c
+++ b/init/property_service.c
@@ -490,11 +490,14 @@
     persistent_properties_loaded = 1;
 }
 
-void property_init(bool load_defaults)
+void property_init(void)
 {
     init_property_area();
-    if (load_defaults)
-        load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
+}
+
+void property_load_boot_defaults(void)
+{
+    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT);
 }
 
 int properties_inited(void)
diff --git a/init/property_service.h b/init/property_service.h
index 37c2788..b9d1bf6 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -20,7 +20,8 @@
 #include <stdbool.h>
 
 extern void handle_property_set_fd(void);
-extern void property_init(bool load_defaults);
+extern void property_init(void);
+extern void property_load_boot_defaults(void);
 extern void load_persist_props(void);
 extern void start_property_service(void);
 void get_property_workspace(int *fd, int *sz);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 822ae02..8041b93 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1,3 +1,5 @@
+import /init.${ro.hardware}.rc
+
 on early-init
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_adj -16