init: delay importing files until after parsing the current file

If we process the import directive inline, then the ordering of the
commands for the "on xxx" sections would be a little unexpected. The
init.rc files do not really have an implied  order as to which section
appears and gets processed first. The init code itself provides that
ordering explicitly. For the user, the expectation is that if both the
current file and the imported file define a section (e.g. "on init"),
then the commands in the current file will be executed first, and then
the ones from the imported file(s).

The current implementation did not do that. It processed the import
directive inline, and thus the imported (i.e. dependent) files would
appear first in the command lists for the sections. This created
unintended side effects and the solution would have been to try and
put the import lines somewhere in the middle of the init file. This
would be difficult to notice and hard to extract the dependencies.

To solve this, we add the imports to a list for each file being parsed
and process the list after finishing parsing the file. This provides
predictable order for imports and provides a logical flow from the
user perspective: the currently parsed file gets to run its commands
before the files being imported.

Change-Id: I06dc35ff286314060e16b18923683cd2787269de
Signed-off-by: Dima Zavin <dima@android.com>
diff --git a/init/init_parser.c b/init/init_parser.c
index 5e07b34..311aaea 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);
 
@@ -277,6 +282,31 @@
     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)
 {
@@ -298,25 +328,7 @@
         }
         break;
     case K_import:
-        {
-            char conf_file[PATH_MAX];
-            int ret;
-
-            if (nargs != 2) {
-                ERROR("single argument needed for import\n");
-                break;
-            }
-
-            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);
-                break;
-            }
-            ret = init_parse_config_file(conf_file);
-            if (ret)
-                ERROR("could not import file '%s'\n", conf_file);
-        }
+        parse_import(state, nargs, args);
         break;
     }
     state->parse_line = parse_line_no_op;
@@ -325,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;
 
@@ -334,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) {
@@ -359,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/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);