am d548e30f: Merge "init: Create classes for Action and Command"
* commit 'd548e30f04b2eb7aa00deab42f5559d08396c38e': init: Create classes for Action and Command
This commit is contained in:
commit
a21d856205
|
@ -44,6 +44,7 @@ endif
|
|||
include $(CLEAR_VARS)
|
||||
LOCAL_CPPFLAGS := $(init_cflags)
|
||||
LOCAL_SRC_FILES:= \
|
||||
action.cpp \
|
||||
init_parser.cpp \
|
||||
log.cpp \
|
||||
parser.cpp \
|
||||
|
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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
|
|
@ -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;
|
||||
}
|
||||
|
|
142
init/init.cpp
142
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 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;
|
||||
}
|
||||
|
||||
|
|
45
init/init.h
45
init/init.h
|
@ -18,47 +18,17 @@
|
|||
#define _INIT_INIT_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cutils/list.h>
|
||||
#include <cutils/iosched_policy.h>
|
||||
|
||||
struct command
|
||||
{
|
||||
/* list of commands in an action */
|
||||
struct listnode clist;
|
||||
|
||||
int (*func)(int nargs, char **args);
|
||||
|
||||
int line;
|
||||
const char *filename;
|
||||
|
||||
int nargs;
|
||||
char *args[1];
|
||||
};
|
||||
|
||||
struct trigger {
|
||||
struct listnode nlist;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct action {
|
||||
/* node in list of all actions */
|
||||
struct listnode alist;
|
||||
/* node in the queue of pending actions */
|
||||
struct listnode qlist;
|
||||
/* node in list of actions for a trigger */
|
||||
struct listnode tlist;
|
||||
|
||||
unsigned hash;
|
||||
|
||||
/* list of actions which triggers the commands*/
|
||||
struct listnode triggers;
|
||||
struct listnode commands;
|
||||
struct command *current;
|
||||
};
|
||||
class Action;
|
||||
|
||||
struct socketinfo {
|
||||
struct socketinfo *next;
|
||||
|
@ -117,7 +87,7 @@ struct service {
|
|||
struct socketinfo *sockets;
|
||||
struct svcenvinfo *envvars;
|
||||
|
||||
struct action onrestart; /* Actions to execute on restart. */
|
||||
Action* onrestart; /* Commands to execute on restart. */
|
||||
|
||||
std::vector<std::string>* writepid_files_;
|
||||
|
||||
|
@ -138,8 +108,6 @@ extern bool waiting_for_exec;
|
|||
extern struct selabel_handle *sehandle;
|
||||
extern struct selabel_handle *sehandle_prop;
|
||||
|
||||
std::string build_triggers_string(struct action *cur_action);
|
||||
|
||||
void handle_control_message(const char *msg, const char *arg);
|
||||
|
||||
struct service *service_find_by_name(const char *name);
|
||||
|
@ -161,6 +129,5 @@ int selinux_reload_policy(void);
|
|||
void zap_stdio(void);
|
||||
|
||||
void register_epoll_handler(int fd, void (*fn)());
|
||||
bool expand_command_arguments(int nargs, char** args, std::vector<std::string>* expanded_args);
|
||||
|
||||
#endif /* _INIT_INIT_H */
|
||||
#endif /* _INIT_INIT_H */
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "init.h"
|
||||
#include "parser.h"
|
||||
#include "init_parser.h"
|
||||
|
@ -38,8 +39,6 @@
|
|||
#include <cutils/list.h>
|
||||
|
||||
static list_declare(service_list);
|
||||
static list_declare(action_list);
|
||||
static list_declare(action_queue);
|
||||
|
||||
struct import {
|
||||
struct listnode list;
|
||||
|
@ -93,25 +92,7 @@ void dump_parser_state() {
|
|||
INFO(" socket %s %s 0%o\n", si->name, si->type, si->perm);
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each(node, &action_list) {
|
||||
action* act = node_to_item(node, struct action, alist);
|
||||
INFO("on ");
|
||||
std::string trigger_name = build_triggers_string(act);
|
||||
INFO("%s", trigger_name.c_str());
|
||||
INFO("\n");
|
||||
|
||||
struct listnode* node2;
|
||||
list_for_each(node2, &act->commands) {
|
||||
command* cmd = node_to_item(node2, struct command, clist);
|
||||
INFO(" %p", cmd->func);
|
||||
for (int n = 0; n < cmd->nargs; n++) {
|
||||
INFO(" %s", cmd->args[n]);
|
||||
}
|
||||
INFO("\n");
|
||||
}
|
||||
INFO("\n");
|
||||
}
|
||||
ActionManager::GetInstance().DumpState();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,10 +198,10 @@ static int lookup_keyword(const char *s)
|
|||
static void parse_line_no_op(struct parse_state*, int, char**) {
|
||||
}
|
||||
|
||||
int expand_props(const char *src, std::string *dst) {
|
||||
const char *src_ptr = src;
|
||||
int expand_props(const std::string& src, std::string* dst) {
|
||||
const char *src_ptr = src.c_str();
|
||||
|
||||
if (!src || !dst) {
|
||||
if (!dst) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -256,7 +237,7 @@ int expand_props(const char *src, std::string *dst) {
|
|||
const char* end = strchr(c, '}');
|
||||
if (!end) {
|
||||
// failed to find closing brace, abort.
|
||||
ERROR("unexpected end of string in '%s', looking for }\n", src);
|
||||
ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
|
||||
goto err;
|
||||
}
|
||||
prop_name = std::string(c, end);
|
||||
|
@ -269,14 +250,14 @@ int expand_props(const char *src, std::string *dst) {
|
|||
}
|
||||
|
||||
if (prop_name.empty()) {
|
||||
ERROR("invalid zero-length prop name in '%s'\n", src);
|
||||
ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
|
||||
goto err;
|
||||
}
|
||||
|
||||
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_name.c_str(), src);
|
||||
prop_name.c_str(), src.c_str());
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -527,125 +508,6 @@ void service_for_each_flags(unsigned matchflags,
|
|||
}
|
||||
}
|
||||
|
||||
void action_for_each_trigger(const char *trigger,
|
||||
void (*func)(struct action *act))
|
||||
{
|
||||
struct listnode *node, *node2;
|
||||
struct action *act;
|
||||
struct trigger *cur_trigger;
|
||||
|
||||
list_for_each(node, &action_list) {
|
||||
act = node_to_item(node, struct action, alist);
|
||||
list_for_each(node2, &act->triggers) {
|
||||
cur_trigger = node_to_item(node2, struct trigger, nlist);
|
||||
if (!strcmp(cur_trigger->name, trigger)) {
|
||||
func(act);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void queue_property_triggers(const char *name, const char *value)
|
||||
{
|
||||
struct listnode *node, *node2;
|
||||
struct action *act;
|
||||
struct trigger *cur_trigger;
|
||||
bool match;
|
||||
int name_length;
|
||||
|
||||
list_for_each(node, &action_list) {
|
||||
act = node_to_item(node, struct action, alist);
|
||||
match = !name;
|
||||
list_for_each(node2, &act->triggers) {
|
||||
cur_trigger = node_to_item(node2, struct trigger, nlist);
|
||||
if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
|
||||
const char *test = cur_trigger->name + strlen("property:");
|
||||
if (!match) {
|
||||
name_length = strlen(name);
|
||||
if (!strncmp(name, test, name_length) &&
|
||||
test[name_length] == '=' &&
|
||||
(!strcmp(test + name_length + 1, value) ||
|
||||
!strcmp(test + name_length + 1, "*"))) {
|
||||
match = true;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
const char* equals = strchr(test, '=');
|
||||
if (equals) {
|
||||
int length = equals - test;
|
||||
if (length <= PROP_NAME_MAX) {
|
||||
std::string prop_name(test, length);
|
||||
std::string value = property_get(prop_name.c_str());
|
||||
|
||||
/* does the property exist, and match the trigger value? */
|
||||
if (!value.empty() && (!strcmp(equals + 1, value.c_str()) ||
|
||||
!strcmp(equals + 1, "*"))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
if (match) {
|
||||
action_add_queue_tail(act);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void queue_all_property_triggers()
|
||||
{
|
||||
queue_property_triggers(NULL, NULL);
|
||||
}
|
||||
|
||||
void queue_builtin_action(int (*func)(int nargs, char **args), const char *name)
|
||||
{
|
||||
action* act = (action*) calloc(1, sizeof(*act));
|
||||
trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
|
||||
cur_trigger->name = name;
|
||||
list_init(&act->triggers);
|
||||
list_add_tail(&act->triggers, &cur_trigger->nlist);
|
||||
list_init(&act->commands);
|
||||
list_init(&act->qlist);
|
||||
|
||||
command* cmd = (command*) calloc(1, sizeof(*cmd));
|
||||
cmd->func = func;
|
||||
cmd->args[0] = const_cast<char*>(name);
|
||||
cmd->nargs = 1;
|
||||
list_add_tail(&act->commands, &cmd->clist);
|
||||
|
||||
list_add_tail(&action_list, &act->alist);
|
||||
action_add_queue_tail(act);
|
||||
}
|
||||
|
||||
void action_add_queue_tail(struct action *act)
|
||||
{
|
||||
if (list_empty(&act->qlist)) {
|
||||
list_add_tail(&action_queue, &act->qlist);
|
||||
}
|
||||
}
|
||||
|
||||
struct action *action_remove_queue_head(void)
|
||||
{
|
||||
if (list_empty(&action_queue)) {
|
||||
return 0;
|
||||
} else {
|
||||
struct listnode *node = list_head(&action_queue);
|
||||
struct action *act = node_to_item(node, struct action, qlist);
|
||||
list_remove(node);
|
||||
list_init(node);
|
||||
return act;
|
||||
}
|
||||
}
|
||||
|
||||
int action_queue_empty()
|
||||
{
|
||||
return list_empty(&action_queue);
|
||||
}
|
||||
|
||||
service* make_exec_oneshot_service(int nargs, char** args) {
|
||||
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
|
||||
// SECLABEL can be a - to denote default
|
||||
|
@ -732,13 +594,10 @@ static void *parse_service(struct parse_state *state, int nargs, char **args)
|
|||
svc->name = strdup(args[1]);
|
||||
svc->classname = "default";
|
||||
memcpy(svc->args, args + 2, sizeof(char*) * nargs);
|
||||
trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
|
||||
svc->args[nargs] = 0;
|
||||
svc->nargs = nargs;
|
||||
list_init(&svc->onrestart.triggers);
|
||||
cur_trigger->name = "onrestart";
|
||||
list_add_tail(&svc->onrestart.triggers, &cur_trigger->nlist);
|
||||
list_init(&svc->onrestart.commands);
|
||||
svc->onrestart = new Action();
|
||||
svc->onrestart->InitSingleTrigger("onrestart");
|
||||
list_add_tail(&service_list, &svc->slist);
|
||||
return svc;
|
||||
}
|
||||
|
@ -746,8 +605,8 @@ 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)
|
||||
{
|
||||
struct service *svc = (service*) state->context;
|
||||
struct command *cmd;
|
||||
int i, kw, kw_nargs;
|
||||
std::vector<std::string> str_args;
|
||||
|
||||
if (nargs == 0) {
|
||||
return;
|
||||
|
@ -840,12 +699,8 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
|
|||
kw_nargs > 2 ? "arguments" : "argument");
|
||||
break;
|
||||
}
|
||||
|
||||
cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
|
||||
cmd->func = kw_func(kw);
|
||||
cmd->nargs = nargs;
|
||||
memcpy(cmd->args, args, sizeof(char*) * nargs);
|
||||
list_add_tail(&svc->onrestart.commands, &cmd->clist);
|
||||
str_args.assign(args, args + nargs);
|
||||
svc->onrestart->AddCommand(kw_func(kw), str_args);
|
||||
break;
|
||||
case K_critical:
|
||||
svc->flags |= SVC_CRITICAL;
|
||||
|
@ -924,48 +779,22 @@ static void parse_line_service(struct parse_state *state, int nargs, char **args
|
|||
}
|
||||
}
|
||||
|
||||
static void *parse_action(struct parse_state *state, int nargs, char **args)
|
||||
static void *parse_action(struct parse_state* state, int nargs, char **args)
|
||||
{
|
||||
struct trigger *cur_trigger;
|
||||
int i;
|
||||
if (nargs < 2) {
|
||||
parse_error(state, "actions must have a trigger\n");
|
||||
return 0;
|
||||
std::string ret_err;
|
||||
std::vector<std::string> triggers(args + 1, args + nargs);
|
||||
Action* ret = ActionManager::GetInstance().AddNewAction(triggers, &ret_err);
|
||||
|
||||
if (!ret) {
|
||||
parse_error(state, "%s\n", ret_err.c_str());
|
||||
}
|
||||
|
||||
action* act = (action*) calloc(1, sizeof(*act));
|
||||
list_init(&act->triggers);
|
||||
|
||||
for (i = 1; i < nargs; i++) {
|
||||
if (!(i % 2)) {
|
||||
if (strcmp(args[i], "&&")) {
|
||||
struct listnode *node;
|
||||
struct listnode *node2;
|
||||
parse_error(state, "& is the only symbol allowed to concatenate actions\n");
|
||||
list_for_each_safe(node, node2, &act->triggers) {
|
||||
struct trigger *trigger = node_to_item(node, struct trigger, nlist);
|
||||
free(trigger);
|
||||
}
|
||||
free(act);
|
||||
return 0;
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
|
||||
cur_trigger->name = args[i];
|
||||
list_add_tail(&act->triggers, &cur_trigger->nlist);
|
||||
}
|
||||
|
||||
list_init(&act->commands);
|
||||
list_init(&act->qlist);
|
||||
list_add_tail(&action_list, &act->alist);
|
||||
/* XXX add to hash */
|
||||
return act;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void parse_line_action(struct parse_state* state, int nargs, char **args)
|
||||
{
|
||||
struct action *act = (action*) state->context;
|
||||
Action* act = (Action*) state->context;
|
||||
int kw, n;
|
||||
|
||||
if (nargs == 0) {
|
||||
|
@ -984,11 +813,7 @@ static void parse_line_action(struct parse_state* state, int nargs, char **args)
|
|||
n > 2 ? "arguments" : "argument");
|
||||
return;
|
||||
}
|
||||
command* cmd = (command*) malloc(sizeof(*cmd) + sizeof(char*) * nargs);
|
||||
cmd->func = kw_func(kw);
|
||||
cmd->line = state->line;
|
||||
cmd->filename = state->filename;
|
||||
cmd->nargs = nargs;
|
||||
memcpy(cmd->args, args, sizeof(char*) * nargs);
|
||||
list_add_tail(&act->commands, &cmd->clist);
|
||||
|
||||
std::vector<std::string> str_args(args, args + nargs);
|
||||
act->AddCommand(kw_func(kw), str_args, state->filename, state->line);
|
||||
}
|
||||
|
|
|
@ -21,20 +21,10 @@
|
|||
|
||||
#define INIT_PARSER_MAXARGS 64
|
||||
|
||||
struct action;
|
||||
struct service;
|
||||
|
||||
struct action *action_remove_queue_head(void);
|
||||
void action_add_queue_tail(struct action *act);
|
||||
void action_for_each_trigger(const char *trigger,
|
||||
void (*func)(struct action *act));
|
||||
int action_queue_empty(void);
|
||||
void queue_property_triggers(const char *name, const char *value);
|
||||
void queue_all_property_triggers();
|
||||
void queue_builtin_action(int (*func)(int nargs, char **args), const char *name);
|
||||
|
||||
bool init_parse_config(const char* path);
|
||||
int expand_props(const char *src, std::string *dst);
|
||||
int expand_props(const std::string& src, std::string* dst);
|
||||
|
||||
service* make_exec_oneshot_service(int argc, char** argv);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <cutils/list.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "init.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
@ -133,18 +134,8 @@ static bool wait_for_one_process() {
|
|||
svc->flags |= SVC_RESTARTING;
|
||||
|
||||
// Execute all onrestart commands for this service.
|
||||
struct listnode* node;
|
||||
list_for_each(node, &svc->onrestart.commands) {
|
||||
command* cmd = node_to_item(node, struct command, clist);
|
||||
std::vector<std::string> arg_strs;
|
||||
if (expand_command_arguments(cmd->nargs, cmd->args, &arg_strs)) {
|
||||
std::vector<char*> args;
|
||||
for (auto& s : arg_strs) {
|
||||
args.push_back(&s[0]);
|
||||
}
|
||||
cmd->func(args.size(), &args[0]);
|
||||
}
|
||||
}
|
||||
svc->onrestart->ExecuteAllCommands();
|
||||
|
||||
svc->NotifyStateChange("restarting");
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue