diff --git a/init/Android.mk b/init/Android.mk index fc3f13343..1b7a2a653 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -44,6 +44,7 @@ endif include $(CLEAR_VARS) LOCAL_CPPFLAGS := $(init_cflags) LOCAL_SRC_FILES:= \ + action.cpp \ init_parser.cpp \ log.cpp \ parser.cpp \ diff --git a/init/action.cpp b/init/action.cpp new file mode 100644 index 000000000..bd5fe7574 --- /dev/null +++ b/init/action.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 + +#include +#include + +#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& 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 args_; + const std::string filename_; + int line_; +}; + +Action::Command::Command(int (*f)(int nargs, char** args), + const std::vector& args, + const std::string& filename, + int line) : + func_(f), args_(args), filename_(filename), line_(line) +{ +} + +int Action::Command::InvokeFunc() const +{ + std::vector 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 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& 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& 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 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 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& 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"); +} diff --git a/init/action.h b/init/action.h new file mode 100644 index 000000000..8ee09b021 --- /dev/null +++ b/init/action.h @@ -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 +#include +#include +#include + +class Action { +public: + Action(); + + void AddCommand(int (*f)(int nargs, char** args), + const std::vector& args, + const std::string& filename = "", int line = 0); + bool InitTriggers(const std::vector& 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 property_triggers_; + std::string event_trigger_; + std::vector 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& triggers, + std::string* err); + void DumpState() const; + +private: + ActionManager(); + + ActionManager(ActionManager const&) = delete; + void operator=(ActionManager const&) = delete; + + std::vector action_list_; + std::queue action_queue_; + std::size_t cur_command_; +}; + +#endif diff --git a/init/builtins.cpp b/init/builtins.cpp index 87e56bc4e..beeaa1768 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -43,6 +43,7 @@ #include #include +#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; } diff --git a/init/init.cpp b/init/init.cpp index 4be16ea00..66143bf13 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -53,6 +53,7 @@ #include +#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* expanded_args) { - std::vector& 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 arg_strs; - if (!expand_command_arguments(cur_command->nargs, cur_command->args, &arg_strs)) { - result = -EINVAL; - } - if (result == 0) { - std::vector 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; } diff --git a/init/init.h b/init/init.h index d2b2dfb30..4d231030f 100644 --- a/init/init.h +++ b/init/init.h @@ -18,47 +18,17 @@ #define _INIT_INIT_H #include +#include +#include +#include #include #include #include #include -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* 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* expanded_args); -#endif /* _INIT_INIT_H */ +#endif /* _INIT_INIT_H */ diff --git a/init/init_parser.cpp b/init/init_parser.cpp index 6df79f6cc..fb675cb92 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp @@ -26,6 +26,7 @@ #include #include +#include "action.h" #include "init.h" #include "parser.h" #include "init_parser.h" @@ -38,8 +39,6 @@ #include 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(); } } @@ -218,10 +199,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; } @@ -257,7 +238,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); @@ -270,14 +251,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; } @@ -528,125 +509,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(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 @@ -733,13 +595,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; } @@ -747,8 +606,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 str_args; if (nargs == 0) { return; @@ -841,12 +700,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; @@ -925,48 +780,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 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) { @@ -985,11 +814,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 str_args(args, args + nargs); + act->AddCommand(kw_func(kw), str_args, state->filename, state->line); } diff --git a/init/init_parser.h b/init/init_parser.h index 1ebb1ef21..fe96bdc66 100644 --- a/init/init_parser.h +++ b/init/init_parser.h @@ -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); diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp index 68931632f..5a875bb47 100644 --- a/init/signal_handler.cpp +++ b/init/signal_handler.cpp @@ -28,6 +28,7 @@ #include #include +#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 arg_strs; - if (expand_command_arguments(cmd->nargs, cmd->args, &arg_strs)) { - std::vector 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; }