Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d548e30f authored by Tom Cherry's avatar Tom Cherry Committed by Gerrit Code Review
Browse files

Merge "init: Create classes for Action and Command"

parents eb0b1513 fa0c21c9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ endif
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES:= \
    action.cpp \
    init_parser.cpp \
    log.cpp \
    parser.cpp \

init/action.cpp

0 → 100644
+383 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "action.h"

#include <errno.h>

#include <base/strings.h>
#include <base/stringprintf.h>

#include "error.h"
#include "init_parser.h"
#include "log.h"
#include "property_service.h"
#include "util.h"

class Action::Command
{
public:
    Command(int (*f)(int nargs, char** args),
            const std::vector<std::string>& args,
            const std::string& filename,
            int line);

    int InvokeFunc() const;
    std::string BuildCommandString() const;
    std::string BuildSourceString() const;

private:
    int (*func_)(int nargs, char** args);
    const std::vector<std::string> args_;
    const std::string filename_;
    int line_;
};

Action::Command::Command(int (*f)(int nargs, char** args),
                         const std::vector<std::string>& args,
                         const std::string& filename,
                         int line) :
    func_(f), args_(args), filename_(filename), line_(line)
{
}

int Action::Command::InvokeFunc() const
{
    std::vector<std::string> strs;
    strs.resize(args_.size());
    strs[0] = args_[0];
    for (std::size_t i = 1; i < args_.size(); ++i) {
        if (expand_props(args_[i], &strs[i]) == -1) {
            ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
            return -EINVAL;
        }
    }

    std::vector<char*> args;
    for (auto& s : strs) {
        args.push_back(&s[0]);
    }

    return func_(args.size(), &args[0]);
}

std::string Action::Command::BuildCommandString() const
{
    return android::base::Join(args_, ' ');
}

std::string Action::Command::BuildSourceString() const
{
    if (!filename_.empty()) {
        return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
    } else {
        return std::string();
    }
}

Action::Action()
{
}

void Action::AddCommand(int (*f)(int nargs, char** args),
                        const std::vector<std::string>& args,
                        const std::string& filename, int line)
{
    Action::Command* cmd = new Action::Command(f, args, filename, line);
    commands_.push_back(cmd);
}

std::size_t Action::NumCommands() const
{
    return commands_.size();
}

void Action::ExecuteOneCommand(std::size_t command) const
{
    ExecuteCommand(*commands_[command]);
}

void Action::ExecuteAllCommands() const
{
    for (const auto& c : commands_) {
        ExecuteCommand(*c);
    }
}

void Action::ExecuteCommand(const Command& command) const
{
    Timer t;
    int result = command.InvokeFunc();

    if (klog_get_level() >= KLOG_INFO_LEVEL) {
        std::string trigger_name = BuildTriggersString();
        std::string cmd_str = command.BuildCommandString();
        std::string source = command.BuildSourceString();

        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());
    }
}

bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
{
    const static std::string prop_str("property:");
    std::string prop_name(trigger.substr(prop_str.length()));
    size_t equal_pos = prop_name.find('=');
    if (equal_pos == std::string::npos) {
        *err = "property trigger found without matching '='";
        return false;
    }

    std::string prop_value(prop_name.substr(equal_pos + 1));
    prop_name.erase(equal_pos);

    auto res = property_triggers_.emplace(prop_name, prop_value);
    if (res.second == false) {
        *err = "multiple property triggers found for same property";
        return false;
    }
    return true;
}

bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
{
    const static std::string prop_str("property:");
    for (std::size_t i = 0; i < args.size(); ++i) {
        if (i % 2) {
            if (args[i].compare("&&")) {
                *err = "&& is the only symbol allowed to concatenate actions";
                return false;
            } else {
                continue;
            }
        }

        if (!args[i].compare(0, prop_str.length(), prop_str)) {
            if (!ParsePropertyTrigger(args[i], err)) {
                return false;
            }
        } else {
            if (!event_trigger_.empty()) {
                *err = "multiple event triggers are not allowed";
                return false;
            }

            event_trigger_ = args[i];
        }
    }

    return true;
}

bool Action::InitSingleTrigger(const std::string& trigger)
{
    std::vector<std::string> name_vector{trigger};
    std::string err;
    return InitTriggers(name_vector, &err);
}

bool Action::CheckPropertyTriggers(const std::string& name,
                                   const std::string& value) const
{
    bool found = !name.compare("");
    if (property_triggers_.empty()) {
        return true;
    }

    for (const auto& t : property_triggers_) {
        if (!t.first.compare(name)) {
            if (t.second.compare("*") &&
                t.second.compare(value)) {
                return false;
            } else {
                found = true;
            }
        } else {
            std::string prop_val = property_get(t.first.c_str());
            if (prop_val.empty() ||
                (t.second.compare("*") &&
                 t.second.compare(prop_val))) {
                return false;
            }
        }
    }
    return found;
}

bool Action::CheckEventTrigger(const std::string& trigger) const
{
    return !event_trigger_.empty() &&
        !trigger.compare(event_trigger_) &&
        CheckPropertyTriggers();
}

bool Action::CheckPropertyTrigger(const std::string& name,
                                  const std::string& value) const
{
    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}

bool Action::TriggersEqual(const class Action& other) const
{
    return property_triggers_.size() == other.property_triggers_.size() &&
        std::equal(property_triggers_.begin(), property_triggers_.end(),
                   other.property_triggers_.begin()) &&
        !event_trigger_.compare(other.event_trigger_);
}

std::string Action::BuildTriggersString() const
{
    std::string result;

    for (const auto& t : property_triggers_) {
        result += t.first;
        result += '=';
        result += t.second;
        result += ' ';
    }
    if (!event_trigger_.empty()) {
        result += event_trigger_;
        result += ' ';
    }
    result.pop_back();
    return result;
}

void Action::DumpState() const
{
    INFO("on ");
    std::string trigger_name = BuildTriggersString();
    INFO("%s", trigger_name.c_str());
    INFO("\n");

    for (const auto& c : commands_) {
        std::string cmd_str = c->BuildCommandString();
        INFO(" %s", cmd_str.c_str());
    }
    INFO("\n");
}

ActionManager::ActionManager() : cur_command_(0)
{
}

ActionManager& ActionManager::GetInstance() {
    static ActionManager instance;
    return instance;
}

void ActionManager::QueueEventTrigger(const std::string& trigger)
{
    for (const auto& a : action_list_) {
        if (a->CheckEventTrigger(trigger)) {
            action_queue_.push(a);
        }
    }
}

void ActionManager::QueuePropertyTrigger(const std::string& name,
                                         const std::string& value)
{
    for (const auto& a : action_list_) {
        if (a->CheckPropertyTrigger(name, value)) {
            action_queue_.push(a);
        }
    }
}

void ActionManager::QueueAllPropertyTriggers()
{
    QueuePropertyTrigger("", "");
}

void ActionManager::QueueBuiltinAction(int (*func)(int nargs, char** args),
                                       const std::string& name)
{
    Action* act = new Action();
    std::vector<std::string> name_vector{name};

    if (!act->InitSingleTrigger(name)) {
        return;
    }

    act->AddCommand(func, name_vector);

    action_queue_.push(act);
}

void ActionManager::ExecuteOneCommand() {
    if (action_queue_.empty()) {
        return;
    }

    Action* action = action_queue_.front();
    if (!action->NumCommands()) {
        action_queue_.pop();
        return;
    }

    if (cur_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        INFO("processing action %p (%s)\n", action, trigger_name.c_str());
    }

    action->ExecuteOneCommand(cur_command_++);
    if (cur_command_ == action->NumCommands()) {
        cur_command_ = 0;
        action_queue_.pop();
    }
}

bool ActionManager::HasMoreCommands() const
{
    return !action_queue_.empty();
}

Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
                                    std::string* err)
{
    if (triggers.size() < 1) {
        *err = "actions must have a trigger\n";
        return nullptr;
    }

    Action* act = new Action();
    if (!act->InitTriggers(triggers, err)) {
        return nullptr;
    }

    auto old_act_it =
        std::find_if(action_list_.begin(), action_list_.end(),
                     [&act] (Action* a) { return act->TriggersEqual(*a); });

    if (old_act_it != action_list_.end()) {
        delete act;
        return *old_act_it;
    }

    action_list_.push_back(act);
    return act;
}

void ActionManager::DumpState() const
{
    for (const auto& a : action_list_) {
        a->DumpState();
    }
    INFO("\n");
}

init/action.h

0 → 100644
+82 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef _INIT_ACTION_H
#define _INIT_ACTION_H

#include <map>
#include <queue>
#include <string>
#include <vector>

class Action {
public:
    Action();

    void AddCommand(int (*f)(int nargs, char** args),
                    const std::vector<std::string>& args,
                    const std::string& filename = "", int line = 0);
    bool InitTriggers(const std::vector<std::string>& args, std::string* err);
    bool InitSingleTrigger(const std::string& trigger);
    std::size_t NumCommands() const;
    void ExecuteOneCommand(std::size_t command) const;
    void ExecuteAllCommands() const;
    bool CheckEventTrigger(const std::string& trigger) const;
    bool CheckPropertyTrigger(const std::string& name,
                              const std::string& value) const;
    bool TriggersEqual(const class Action& other) const;
    std::string BuildTriggersString() const;
    void DumpState() const;

private:
    class Command;

    void ExecuteCommand(const Command& command) const;
    bool CheckPropertyTriggers(const std::string& name = "",
                               const std::string& value = "") const;
    bool ParsePropertyTrigger(const std::string& trigger, std::string* err);

    std::map<std::string, std::string> property_triggers_;
    std::string event_trigger_;
    std::vector<Command*> commands_;
};

class ActionManager {
public:
    static ActionManager& GetInstance();
    void QueueEventTrigger(const std::string& trigger);
    void QueuePropertyTrigger(const std::string& name, const std::string& value);
    void QueueAllPropertyTriggers();
    void QueueBuiltinAction(int (*func)(int nargs, char** args),
                            const std::string& name);
    void ExecuteOneCommand();
    bool HasMoreCommands() const;
    Action* AddNewAction(const std::vector<std::string>& triggers,
                         std::string* err);
    void DumpState() const;

private:
    ActionManager();

    ActionManager(ActionManager const&) = delete;
    void operator=(ActionManager const&) = delete;

    std::vector<Action*> action_list_;
    std::queue<Action*> action_queue_;
    std::size_t cur_command_;
};

#endif
+5 −4
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include <logwrap/logwrap.h>
#include <private/android_filesystem_config.h>

#include "action.h"
#include "init.h"
#include "keywords.h"
#include "property_service.h"
@@ -513,7 +514,7 @@ int do_mount_all(int nargs, char **args)
        /* If fs_mgr determined this is an unencrypted device, then trigger
         * that action.
         */
        action_for_each_trigger("nonencrypted", action_add_queue_tail);
        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
    } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
        /* Setup a wipe via recovery, and reboot into recovery */
        ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
@@ -528,7 +529,7 @@ int do_mount_all(int nargs, char **args)

        // Although encrypted, we have device key, so we do not need to
        // do anything different from the nonencrypted case.
        action_for_each_trigger("nonencrypted", action_add_queue_tail);
        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
    } else if (ret == FS_MGR_MNTALL_DEV_NON_DEFAULT_FILE_ENCRYPTED) {
        if (e4crypt_install_keyring()) {
            return -1;
@@ -639,7 +640,7 @@ int do_powerctl(int nargs, char **args)

int do_trigger(int nargs, char **args)
{
    action_for_each_trigger(args[1], action_add_queue_tail);
    ActionManager::GetInstance().QueueEventTrigger(args[1]);
    return 0;
}

@@ -676,7 +677,7 @@ int do_verity_load_state(int nargs, char **args) {
    int mode = -1;
    int rc = fs_mgr_load_verity_state(&mode);
    if (rc == 0 && mode == VERITY_MODE_LOGGING) {
        action_for_each_trigger("verity-logging", action_add_queue_tail);
        ActionManager::GetInstance().QueueEventTrigger("verity-logging");
    }
    return rc;
}
+17 −125
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@

#include <memory>

#include "action.h"
#include "devices.h"
#include "init.h"
#include "log.h"
@@ -72,9 +73,6 @@ static int property_triggers_enabled = 0;

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 service_restart(struct service *svc)
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 @@ void handle_control_message(const char *msg, const char *arg)
    }
}

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 void process_kernel_cmdline() {

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 @@ int main(int argc, char** argv) {

    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 @@ int main(int argc, char** argv) {
                timeout = 0;
        }

        if (!action_queue_empty() || cur_action) {
        if (am.HasMoreCommands()) {
            timeout = 0;
        }

Loading