init: Create classes for Action and Command

This creates the concept of 'event_trigger' vs 'property_trigger'

Previously these were merged into one, such that 'on property:a=b &&
property:b=c' is triggered when properties a=b and b=c as expected,
however combinations such as 'on early-boot && boot' would trigger
during both early-boot and boot.  Similarly, 'on early-boot &&
property:a=b' would trigger on both early-boot and again when property
a equals b.

The event trigger distinction ensures that the first example fails to
parse and the second example only triggers on early-boot if
property a equals b.

This coalesces Actions with the same triggers into a single Action object

Change-Id: I8f661d96e8a2d40236f252301bfe10979d663ea6
diff --git a/init/init.cpp b/init/init.cpp
index 4be16ea..66143bf 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -53,6 +53,7 @@
 
 #include <memory>
 
+#include "action.h"
 #include "devices.h"
 #include "init.h"
 #include "log.h"
@@ -72,9 +73,6 @@
 
 static char qemu[32];
 
-static struct action *cur_action = NULL;
-static struct command *cur_command = NULL;
-
 static int have_console;
 static std::string console_name = "/dev/console";
 static time_t process_needs_restart;
@@ -453,7 +451,7 @@
 void property_changed(const char *name, const char *value)
 {
     if (property_triggers_enabled)
-        queue_property_triggers(name, value);
+        ActionManager::GetInstance().QueuePropertyTrigger(name, value);
 }
 
 static void restart_service_if_needed(struct service *svc)
@@ -542,114 +540,6 @@
     }
 }
 
-static struct command *get_first_command(struct action *act)
-{
-    struct listnode *node;
-    node = list_head(&act->commands);
-    if (!node || list_empty(&act->commands))
-        return NULL;
-
-    return node_to_item(node, struct command, clist);
-}
-
-static struct command *get_next_command(struct action *act, struct command *cmd)
-{
-    struct listnode *node;
-    node = cmd->clist.next;
-    if (!node)
-        return NULL;
-    if (node == &act->commands)
-        return NULL;
-
-    return node_to_item(node, struct command, clist);
-}
-
-static int is_last_command(struct action *act, struct command *cmd)
-{
-    return (list_tail(&act->commands) == &cmd->clist);
-}
-
-
-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) {
-            result.push_back(' ');
-        }
-        result += cur_trigger->name;
-    }
-    return result;
-}
-
-bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args) {
-    std::vector<std::string>& strs = *expanded_args;
-    strs.resize(nargs);
-    strs[0] = args[0];
-    for (int i = 1; i < nargs; ++i) {
-        if (expand_props(args[i], &strs[i]) == -1) {
-            ERROR("%s: cannot expand '%s'\n", args[0], args[i]);
-            return false;
-        }
-    }
-    return true;
-}
-
-void execute_one_command() {
-    Timer t;
-
-    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
-        cur_action = action_remove_queue_head();
-        cur_command = NULL;
-        if (!cur_action) {
-            return;
-        }
-
-        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);
-    }
-
-    if (!cur_command) {
-        return;
-    }
-    int result = 0;
-    std::vector<std::string> arg_strs;
-    if (!expand_command_arguments(cur_command->nargs, cur_command->args, &arg_strs)) {
-        result = -EINVAL;
-    }
-    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) {
-        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];
-        }
-        std::string trigger_name = build_triggers_string(cur_action);
-
-        std::string source;
-        if (cur_command->filename) {
-            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.c_str(), trigger_name.c_str(), source.c_str(), result, t.duration());
-    }
-}
-
 static int wait_for_coldboot_done_action(int nargs, char **args) {
     Timer t;
 
@@ -865,7 +755,7 @@
 
 static int queue_property_triggers_action(int nargs, char **args)
 {
-    queue_all_property_triggers();
+    ActionManager::GetInstance().QueueAllPropertyTriggers();
     /* enable property triggers */
     property_triggers_enabled = 1;
     return 0;
@@ -1059,36 +949,38 @@
 
     init_parse_config("/init.rc");
 
-    action_for_each_trigger("early-init", action_add_queue_tail);
+    ActionManager& am = ActionManager::GetInstance();
+
+    am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
-    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
+    am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
     // ... so that we can start queuing up actions that require stuff from /dev.
-    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
-    queue_builtin_action(keychord_init_action, "keychord_init");
-    queue_builtin_action(console_init_action, "console_init");
+    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
+    am.QueueBuiltinAction(console_init_action, "console_init");
 
     // Trigger all the boot actions to get us started.
-    action_for_each_trigger("init", action_add_queue_tail);
+    am.QueueEventTrigger("init");
 
     // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     // wasn't ready immediately after wait_for_coldboot_done
-    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
+    am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
 
     // Don't mount filesystems or start core system services in charger mode.
     std::string bootmode = property_get("ro.bootmode");
     if (bootmode == "charger") {
-        action_for_each_trigger("charger", action_add_queue_tail);
+        am.QueueEventTrigger("charger");
     } else {
-        action_for_each_trigger("late-init", action_add_queue_tail);
+        am.QueueEventTrigger("late-init");
     }
 
     // Run all property triggers based on current state of the properties.
-    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
+    am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
 
     while (true) {
         if (!waiting_for_exec) {
-            execute_one_command();
+            am.ExecuteOneCommand();
             restart_processes();
         }
 
@@ -1099,7 +991,7 @@
                 timeout = 0;
         }
 
-        if (!action_queue_empty() || cur_action) {
+        if (am.HasMoreCommands()) {
             timeout = 0;
         }