init: do expand_props before calling the builtins.

Also switch expand_props to std::string.

Bug: 22654233

Change-Id: I62910d4f74e2b1a5bd2b14aea440767a2a8462b7
diff --git a/init/builtins.cpp b/init/builtins.cpp
index e2c033a..d05f046 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -62,14 +62,8 @@
 
 static int insmod(const char *filename, char *options)
 {
-    char filename_val[PROP_VALUE_MAX];
-    if (expand_props(filename_val, filename, sizeof(filename_val)) == -1) {
-        ERROR("insmod: cannot expand '%s'\n", filename);
-        return -EINVAL;
-    }
-
     std::string module;
-    if (!read_file(filename_val, &module)) {
+    if (!read_file(filename, &module)) {
         return -1;
     }
 
@@ -468,17 +462,11 @@
     int child_ret = -1;
     int status;
     struct fstab *fstab;
-    char fstabfile[PROP_VALUE_MAX];
 
     if (nargs != 2) {
         return -1;
     }
-
-    if (expand_props(fstabfile, args[1], sizeof(fstabfile)) == -1) {
-        ERROR("mount_all: cannot expand '%s' \n", args[1]);
-        return -EINVAL;
-    }
-
+    const char* fstabfile = args[1];
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -572,15 +560,7 @@
 {
     const char *name = args[1];
     const char *value = args[2];
-    char prop_val[PROP_VALUE_MAX];
-    int ret;
-
-    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, prop_val);
+    property_set(name, value);
     return 0;
 }
 
@@ -626,19 +606,12 @@
 
 int do_powerctl(int nargs, char **args)
 {
-    char command[PROP_VALUE_MAX];
-    int res;
+    const char* command = args[1];
     int len = 0;
     int cmd = 0;
     const char *reboot_target;
     void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
-    res = expand_props(command, args[1], sizeof(command));
-    if (res) {
-        ERROR("powerctl: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
-
     if (strncmp(command, "shutdown", 8) == 0) {
         cmd = ANDROID_RB_POWEROFF;
         len = 8;
@@ -666,13 +639,7 @@
 
 int do_trigger(int nargs, char **args)
 {
-    char prop_val[PROP_VALUE_MAX];
-    int res = expand_props(prop_val, args[1], sizeof(prop_val));
-    if (res) {
-        ERROR("trigger: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
-    action_for_each_trigger(prop_val, action_add_queue_tail);
+    action_for_each_trigger(args[1], action_add_queue_tail);
     return 0;
 }
 
@@ -727,13 +694,7 @@
 {
     const char *path = args[1];
     const char *value = args[2];
-
-    char expanded_value[256];
-    if (expand_props(expanded_value, value, sizeof(expanded_value))) {
-        ERROR("cannot expand '%s' while writing to '%s'\n", value, path);
-        return -EINVAL;
-    }
-    return write_file(path, expanded_value);
+    return write_file(path, value);
 }
 
 int do_copy(int nargs, char **args)
@@ -856,18 +817,12 @@
 }
 
 int do_loglevel(int nargs, char **args) {
-    int log_level;
-    char log_level_str[PROP_VALUE_MAX] = "";
     if (nargs != 2) {
         ERROR("loglevel: missing argument\n");
         return -EINVAL;
     }
 
-    if (expand_props(log_level_str, args[1], sizeof(log_level_str))) {
-        ERROR("loglevel: cannot expand '%s'\n", args[1]);
-        return -EINVAL;
-    }
-    log_level = atoi(log_level_str);
+    int log_level = atoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
         return -EINVAL;
diff --git a/init/init.cpp b/init/init.cpp
index cd19647..42eebe1 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -570,25 +570,24 @@
 }
 
 
-void build_triggers_string(char *name_str, int length, struct action *cur_action) {
+std::string build_triggers_string(struct action *cur_action) {
+    std::string result;
     struct listnode *node;
     struct trigger *cur_trigger;
 
     list_for_each(node, &cur_action->triggers) {
         cur_trigger = node_to_item(node, struct trigger, nlist);
         if (node != cur_action->triggers.next) {
-            strlcat(name_str, " " , length);
+            result.push_back(' ');
         }
-        strlcat(name_str, cur_trigger->name , length);
+        result += cur_trigger->name;
     }
+    return result;
 }
 
 void execute_one_command() {
     Timer t;
 
-    char cmd_str[256] = "";
-    char name_str[256] = "";
-
     if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
         cur_action = action_remove_queue_head();
         cur_command = NULL;
@@ -596,9 +595,8 @@
             return;
         }
 
-        build_triggers_string(name_str, sizeof(name_str), cur_action);
-
-        INFO("processing action %p (%s)\n", cur_action, name_str);
+        std::string trigger_name = build_triggers_string(cur_action);
+        INFO("processing action %p (%s)\n", cur_action, trigger_name.c_str());
         cur_command = get_first_command(cur_action);
     } else {
         cur_command = get_next_command(cur_action, cur_command);
@@ -607,23 +605,40 @@
     if (!cur_command) {
         return;
     }
-
-    int result = cur_command->func(cur_command->nargs, cur_command->args);
+    int result = 0;
+    std::vector<std::string> arg_strs(cur_command->nargs);
+    arg_strs[0] = cur_command->args[0];
+    for (int i = 1; i < cur_command->nargs; ++i) {
+        if (expand_props(cur_command->args[i], &arg_strs[i]) == -1) {
+            ERROR("%s: cannot expand '%s'\n", cur_command->args[0], cur_command->args[i]);
+            result = -EINVAL;
+            break;
+        }
+    }
+    if (result == 0) {
+        std::vector<char*> args;
+        for (auto& s : arg_strs) {
+            args.push_back(&s[0]);
+        }
+        result = cur_command->func(args.size(), &args[0]);
+    }
     if (klog_get_level() >= KLOG_INFO_LEVEL) {
-        for (int i = 0; i < cur_command->nargs; i++) {
-            strlcat(cmd_str, cur_command->args[i], sizeof(cmd_str));
-            if (i < cur_command->nargs - 1) {
-                strlcat(cmd_str, " ", sizeof(cmd_str));
+        std::string cmd_str;
+        for (int i = 0; i < cur_command->nargs; ++i) {
+            if (i > 0) {
+                cmd_str.push_back(' ');
             }
+            cmd_str += cur_command->args[i];
         }
-        char source[256];
+        std::string trigger_name = build_triggers_string(cur_action);
+
+        std::string source;
         if (cur_command->filename) {
-            snprintf(source, sizeof(source), " (%s:%d)", cur_command->filename, cur_command->line);
-        } else {
-            *source = '\0';
+            source = android::base::StringPrintf(" (%s:%d)", cur_command->filename, cur_command->line);
         }
+
         INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
-             cmd_str, cur_action ? name_str : "", source, result, t.duration());
+             cmd_str.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration());
     }
 }
 
diff --git a/init/init.h b/init/init.h
index c166969..d812733 100644
--- a/init/init.h
+++ b/init/init.h
@@ -138,7 +138,7 @@
 extern struct selabel_handle *sehandle;
 extern struct selabel_handle *sehandle_prop;
 
-void build_triggers_string(char *name_str, int length, struct action *cur_action);
+std::string build_triggers_string(struct action *cur_action);
 
 void handle_control_message(const char *msg, const char *arg);
 
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 41b89f1..86e9ce6 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -95,9 +95,8 @@
         list_for_each(node, &action_list) {
             action* act = node_to_item(node, struct action, alist);
             INFO("on ");
-            char name_str[256] = "";
-            build_triggers_string(name_str, sizeof(name_str), act);
-            INFO("%s", name_str);
+            std::string trigger_name = build_triggers_string(act);
+            INFO("%s", trigger_name.c_str());
             INFO("\n");
 
             struct listnode* node2;
@@ -216,27 +215,12 @@
 static void parse_line_no_op(struct parse_state*, int, char**) {
 }
 
-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)
-{
-    char *dst_ptr = dst;
+int expand_props(const char *src, std::string *dst) {
     const char *src_ptr = src;
-    int ret = 0;
-    int left = dst_size - 1;
 
-    if (!src || !dst || dst_size == 0)
+    if (!src || !dst) {
         return -1;
+    }
 
     /* - variables can either be $x.y or ${x.y}, in case they are only part
      *   of the string.
@@ -244,102 +228,75 @@
      * - 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];
-        int prop_len = 0;
+    while (*src_ptr) {
+        const char *c;
 
         c = strchr(src_ptr, '$');
         if (!c) {
-            while (left-- > 0 && *src_ptr)
-                *(dst_ptr++) = *(src_ptr++);
+            dst->append(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;
+        dst->append(src_ptr, c);
         c++;
 
         if (*c == '$') {
-            *(dst_ptr++) = *(c++);
+            dst->push_back(*(c++));
             src_ptr = c;
-            left--;
             continue;
         } else if (*c == '\0') {
             break;
         }
 
+        std::string prop_name;
         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);
+            const char* end = strchr(c, '}');
+            if (!end) {
+                // failed to find closing brace, abort.
+                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';
+            prop_name = std::string(c, end);
+            c = end + 1;
+        } else {
+            prop_name = c;
             ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
-                  prop);
+                  c);
+            c += prop_name.size();
         }
 
-        if (prop_len == 0) {
+        if (prop_name.empty()) {
             ERROR("invalid zero-length prop name in '%s'\n", src);
             goto err;
         }
 
-        std::string prop_val = property_get(prop);
+        std::string prop_val = property_get(prop_name.c_str());
         if (prop_val.empty()) {
             ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop, src);
+                  prop_name.c_str(), src);
             goto err;
         }
 
-        ret = push_chars(&dst_ptr, &left, prop_val.c_str(), prop_val.size());
-        if (ret < 0)
-            goto err_nospace;
+        dst->append(prop_val);
         src_ptr = c;
         continue;
     }
 
-    *dst_ptr = '\0';
     return 0;
-
-err_nospace:
-    ERROR("destination buffer overflow while expanding '%s'\n", src);
 err:
     return -1;
 }
 
 static void parse_import(struct parse_state *state, int nargs, char **args)
 {
-    struct listnode *import_list = (listnode*) state->priv;
-    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));
+    std::string conf_file;
+    int ret = expand_props(args[1], &conf_file);
     if (ret) {
         ERROR("error while handling import on line '%d' in '%s'\n",
               state->line, state->filename);
@@ -347,7 +304,9 @@
     }
 
     struct import* import = (struct import*) calloc(1, sizeof(struct import));
-    import->filename = strdup(conf_file);
+    import->filename = strdup(conf_file.c_str());
+
+    struct listnode *import_list = (listnode*) state->priv;
     list_add_tail(import_list, &import->list);
     INFO("Added '%s' to import list\n", import->filename);
 }
diff --git a/init/init_parser.h b/init/init_parser.h
index 90f880f..fa7e67f 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -17,6 +17,8 @@
 #ifndef _INIT_INIT_PARSER_H_
 #define _INIT_INIT_PARSER_H_
 
+#include <string>
+
 #define INIT_PARSER_MAXARGS 64
 
 struct action;
@@ -32,7 +34,7 @@
 void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
 
 bool init_parse_config_file(const char* path);
-int expand_props(char *dst, const char *src, int len);
+int expand_props(const char *src, std::string *dst);
 
 service* make_exec_oneshot_service(int argc, char** argv);