diff --git a/init/Android.bp b/init/Android.bp index 6c80ee6cf..1f2ad2e78 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -93,6 +93,8 @@ cc_library_static { defaults: ["init_defaults"], srcs: [ "action.cpp", + "action_manager.cpp", + "action_parser.cpp", "bootchart.cpp", "builtins.cpp", "capabilities.cpp", diff --git a/init/action.cpp b/init/action.cpp index ba03e66fe..11335ca14 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -21,11 +21,9 @@ #include #include -#include "stable_properties.h" #include "util.h" using android::base::Join; -using android::base::StartsWith; namespace android { namespace init { @@ -70,8 +68,15 @@ std::string Command::BuildCommandString() const { return Join(args_, ' '); } -Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line) - : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {} +Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line, + const std::string& event_trigger, + const std::map& property_triggers) + : property_triggers_(property_triggers), + event_trigger_(event_trigger), + oneshot_(oneshot), + subcontext_(subcontext), + filename_(filename), + line_(line) {} const KeywordFunctionMap* Action::function_map_ = nullptr; @@ -135,85 +140,6 @@ void Action::ExecuteCommand(const Command& command) const { } } -static bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) { - static bool enabled = - android::base::GetBoolProperty("ro.actionable_compatible_property.enabled", false); - - if (subcontext == nullptr || !enabled) { - return true; - } - - if (kExportedActionableProperties.count(prop_name) == 1) { - return true; - } - for (const auto& prefix : kPartnerPrefixes) { - if (android::base::StartsWith(prop_name, prefix)) { - return true; - } - } - return false; -} - -Result Action::ParsePropertyTrigger(const std::string& trigger) { - 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) { - return Error() << "property trigger found without matching '='"; - } - - std::string prop_value(prop_name.substr(equal_pos + 1)); - prop_name.erase(equal_pos); - - if (!IsActionableProperty(subcontext_, prop_name)) { - return Error() << "unexported property tigger found: " << prop_name; - } - - if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) { - return Error() << "multiple property triggers found for same property"; - } - return Success(); -} - -Result Action::InitTriggers(const std::vector& args) { - const static std::string prop_str("property:"); - for (std::size_t i = 0; i < args.size(); ++i) { - if (args[i].empty()) { - return Error() << "empty trigger is not valid"; - } - - if (i % 2) { - if (args[i] != "&&") { - return Error() << "&& is the only symbol allowed to concatenate actions"; - } else { - continue; - } - } - - if (!args[i].compare(0, prop_str.length(), prop_str)) { - if (auto result = ParsePropertyTrigger(args[i]); !result) { - return result; - } - } else { - if (!event_trigger_.empty()) { - return Error() << "multiple event triggers are not allowed"; - } - - event_trigger_ = args[i]; - } - } - - return Success(); -} - -Result Action::InitSingleTrigger(const std::string& trigger) { - std::vector name_vector{trigger}; - if (auto result = InitTriggers(name_vector); !result) { - return Error() << "InitTriggers() failed: " << result.error(); - } - return Success(); -} - // This function checks that all property triggers are satisfied, that is // for each (name, value) in property_triggers_, check that the current // value of the property 'name' == value. @@ -281,142 +207,5 @@ void Action::DumpState() const { } } -ActionManager::ActionManager() : current_command_(0) { -} - -ActionManager& ActionManager::GetInstance() { - static ActionManager instance; - return instance; -} - -void ActionManager::AddAction(std::unique_ptr action) { - actions_.emplace_back(std::move(action)); -} - -void ActionManager::QueueEventTrigger(const std::string& trigger) { - event_queue_.emplace(trigger); -} - -void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) { - event_queue_.emplace(std::make_pair(name, value)); -} - -void ActionManager::QueueAllPropertyActions() { - QueuePropertyChange("", ""); -} - -void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) { - auto action = std::make_unique(true, nullptr, "", 0); - std::vector name_vector{name}; - - if (auto result = action->InitSingleTrigger(name); !result) { - LOG(ERROR) << "Cannot queue BuiltinAction for " << name << ": " << result.error(); - return; - } - - action->AddCommand(func, name_vector, 0); - - event_queue_.emplace(action.get()); - actions_.emplace_back(std::move(action)); -} - -void ActionManager::ExecuteOneCommand() { - // Loop through the event queue until we have an action to execute - while (current_executing_actions_.empty() && !event_queue_.empty()) { - for (const auto& action : actions_) { - if (std::visit([&action](const auto& event) { return action->CheckEvent(event); }, - event_queue_.front())) { - current_executing_actions_.emplace(action.get()); - } - } - event_queue_.pop(); - } - - if (current_executing_actions_.empty()) { - return; - } - - auto action = current_executing_actions_.front(); - - if (current_command_ == 0) { - std::string trigger_name = action->BuildTriggersString(); - LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename() - << ":" << action->line() << ")"; - } - - action->ExecuteOneCommand(current_command_); - - // If this was the last command in the current action, then remove - // the action from the executing list. - // If this action was oneshot, then also remove it from actions_. - ++current_command_; - if (current_command_ == action->NumCommands()) { - current_executing_actions_.pop(); - current_command_ = 0; - if (action->oneshot()) { - auto eraser = [&action] (std::unique_ptr& a) { - return a.get() == action; - }; - actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); - } - } -} - -bool ActionManager::HasMoreCommands() const { - return !current_executing_actions_.empty() || !event_queue_.empty(); -} - -void ActionManager::DumpState() const { - for (const auto& a : actions_) { - a->DumpState(); - } -} - -void ActionManager::ClearQueue() { - // We are shutting down so don't claim the oneshot builtin actions back - current_executing_actions_ = {}; - event_queue_ = {}; - current_command_ = 0; -} - -Result ActionParser::ParseSection(std::vector&& args, - const std::string& filename, int line) { - std::vector triggers(args.begin() + 1, args.end()); - if (triggers.size() < 1) { - return Error() << "Actions must have a trigger"; - } - - Subcontext* action_subcontext = nullptr; - if (subcontexts_) { - for (auto& subcontext : *subcontexts_) { - if (StartsWith(filename, subcontext.path_prefix())) { - action_subcontext = &subcontext; - break; - } - } - } - - auto action = std::make_unique(false, action_subcontext, filename, line); - - if (auto result = action->InitTriggers(triggers); !result) { - return Error() << "InitTriggers() failed: " << result.error(); - } - - action_ = std::move(action); - return Success(); -} - -Result ActionParser::ParseLineSection(std::vector&& args, int line) { - return action_ ? action_->AddCommand(std::move(args), line) : Success(); -} - -Result ActionParser::EndSection() { - if (action_ && action_->NumCommands() > 0) { - action_manager_->AddAction(std::move(action_)); - } - - return Success(); -} - } // namespace init } // namespace android diff --git a/init/action.h b/init/action.h index 1bfc6c753..4f063cc54 100644 --- a/init/action.h +++ b/init/action.h @@ -25,7 +25,6 @@ #include "builtins.h" #include "keyword_map.h" -#include "parser.h" #include "result.h" #include "subcontext.h" @@ -58,12 +57,12 @@ using BuiltinAction = class Action*; class Action { public: - Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line); + Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line, + const std::string& event_trigger, + const std::map& property_triggers); Result AddCommand(const std::vector& args, int line); void AddCommand(BuiltinFunction f, const std::vector& args, int line); - Result InitTriggers(const std::vector& args); - Result InitSingleTrigger(const std::string& trigger); std::size_t NumCommands() const; void ExecuteOneCommand(std::size_t command) const; void ExecuteAllCommands() const; @@ -84,7 +83,6 @@ class Action { void ExecuteCommand(const Command& command) const; bool CheckPropertyTriggers(const std::string& name = "", const std::string& value = "") const; - Result ParsePropertyTrigger(const std::string& trigger); std::map property_triggers_; std::string event_trigger_; @@ -96,48 +94,6 @@ class Action { static const KeywordFunctionMap* function_map_; }; -class ActionManager { - public: - static ActionManager& GetInstance(); - - // Exposed for testing - ActionManager(); - - void AddAction(std::unique_ptr action); - void QueueEventTrigger(const std::string& trigger); - void QueuePropertyChange(const std::string& name, const std::string& value); - void QueueAllPropertyActions(); - void QueueBuiltinAction(BuiltinFunction func, const std::string& name); - void ExecuteOneCommand(); - bool HasMoreCommands() const; - void DumpState() const; - void ClearQueue(); - - private: - ActionManager(ActionManager const&) = delete; - void operator=(ActionManager const&) = delete; - - std::vector> actions_; - std::queue> event_queue_; - std::queue current_executing_actions_; - std::size_t current_command_; -}; - -class ActionParser : public SectionParser { - public: - ActionParser(ActionManager* action_manager, std::vector* subcontexts) - : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {} - Result ParseSection(std::vector&& args, const std::string& filename, - int line) override; - Result ParseLineSection(std::vector&& args, int line) override; - Result EndSection() override; - - private: - ActionManager* action_manager_; - std::vector* subcontexts_; - std::unique_ptr action_; -}; - } // namespace init } // namespace android diff --git a/init/action_manager.cpp b/init/action_manager.cpp new file mode 100644 index 000000000..22977bb29 --- /dev/null +++ b/init/action_manager.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2018 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_manager.h" + +#include + +namespace android { +namespace init { + +ActionManager::ActionManager() : current_command_(0) {} + +ActionManager& ActionManager::GetInstance() { + static ActionManager instance; + return instance; +} + +void ActionManager::AddAction(std::unique_ptr action) { + actions_.emplace_back(std::move(action)); +} + +void ActionManager::QueueEventTrigger(const std::string& trigger) { + event_queue_.emplace(trigger); +} + +void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) { + event_queue_.emplace(std::make_pair(name, value)); +} + +void ActionManager::QueueAllPropertyActions() { + QueuePropertyChange("", ""); +} + +void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) { + auto action = std::make_unique(true, nullptr, "", 0, name, + std::map{}); + std::vector name_vector{name}; + + action->AddCommand(func, name_vector, 0); + + event_queue_.emplace(action.get()); + actions_.emplace_back(std::move(action)); +} + +void ActionManager::ExecuteOneCommand() { + // Loop through the event queue until we have an action to execute + while (current_executing_actions_.empty() && !event_queue_.empty()) { + for (const auto& action : actions_) { + if (std::visit([&action](const auto& event) { return action->CheckEvent(event); }, + event_queue_.front())) { + current_executing_actions_.emplace(action.get()); + } + } + event_queue_.pop(); + } + + if (current_executing_actions_.empty()) { + return; + } + + auto action = current_executing_actions_.front(); + + if (current_command_ == 0) { + std::string trigger_name = action->BuildTriggersString(); + LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename() + << ":" << action->line() << ")"; + } + + action->ExecuteOneCommand(current_command_); + + // If this was the last command in the current action, then remove + // the action from the executing list. + // If this action was oneshot, then also remove it from actions_. + ++current_command_; + if (current_command_ == action->NumCommands()) { + current_executing_actions_.pop(); + current_command_ = 0; + if (action->oneshot()) { + auto eraser = [&action](std::unique_ptr& a) { return a.get() == action; }; + actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); + } + } +} + +bool ActionManager::HasMoreCommands() const { + return !current_executing_actions_.empty() || !event_queue_.empty(); +} + +void ActionManager::DumpState() const { + for (const auto& a : actions_) { + a->DumpState(); + } +} + +void ActionManager::ClearQueue() { + // We are shutting down so don't claim the oneshot builtin actions back + current_executing_actions_ = {}; + event_queue_ = {}; + current_command_ = 0; +} + +} // namespace init +} // namespace android diff --git a/init/action_manager.h b/init/action_manager.h new file mode 100644 index 000000000..5f47a6db0 --- /dev/null +++ b/init/action_manager.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 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_MANAGER_H +#define _INIT_ACTION_MANAGER_H + +#include +#include + +#include "action.h" +#include "builtins.h" + +namespace android { +namespace init { + +class ActionManager { + public: + static ActionManager& GetInstance(); + + // Exposed for testing + ActionManager(); + + void AddAction(std::unique_ptr action); + void QueueEventTrigger(const std::string& trigger); + void QueuePropertyChange(const std::string& name, const std::string& value); + void QueueAllPropertyActions(); + void QueueBuiltinAction(BuiltinFunction func, const std::string& name); + void ExecuteOneCommand(); + bool HasMoreCommands() const; + void DumpState() const; + void ClearQueue(); + + private: + ActionManager(ActionManager const&) = delete; + void operator=(ActionManager const&) = delete; + + std::vector> actions_; + std::queue> event_queue_; + std::queue current_executing_actions_; + std::size_t current_command_; +}; + +} // namespace init +} // namespace android + +#endif diff --git a/init/action_parser.cpp b/init/action_parser.cpp new file mode 100644 index 000000000..8a4b518f5 --- /dev/null +++ b/init/action_parser.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2018 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_parser.h" + +#include +#include + +#include "stable_properties.h" + +using android::base::GetBoolProperty; +using android::base::StartsWith; + +namespace android { +namespace init { + +namespace { + +bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) { + static bool enabled = GetBoolProperty("ro.actionable_compatible_property.enabled", false); + + if (subcontext == nullptr || !enabled) { + return true; + } + + if (kExportedActionableProperties.count(prop_name) == 1) { + return true; + } + for (const auto& prefix : kPartnerPrefixes) { + if (android::base::StartsWith(prop_name, prefix)) { + return true; + } + } + return false; +} + +Result ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext, + std::map* property_triggers) { + 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) { + return Error() << "property trigger found without matching '='"; + } + + std::string prop_value(prop_name.substr(equal_pos + 1)); + prop_name.erase(equal_pos); + + if (!IsActionableProperty(subcontext, prop_name)) { + return Error() << "unexported property tigger found: " << prop_name; + } + + if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) { + return Error() << "multiple property triggers found for same property"; + } + return Success(); +} + +Result ParseTriggers(const std::vector& args, Subcontext* subcontext, + std::string* event_trigger, + std::map* property_triggers) { + const static std::string prop_str("property:"); + for (std::size_t i = 0; i < args.size(); ++i) { + if (args[i].empty()) { + return Error() << "empty trigger is not valid"; + } + + if (i % 2) { + if (args[i] != "&&") { + return Error() << "&& is the only symbol allowed to concatenate actions"; + } else { + continue; + } + } + + if (!args[i].compare(0, prop_str.length(), prop_str)) { + if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers); + !result) { + return result; + } + } else { + if (!event_trigger->empty()) { + return Error() << "multiple event triggers are not allowed"; + } + + *event_trigger = args[i]; + } + } + + return Success(); +} + +} // namespace + +Result ActionParser::ParseSection(std::vector&& args, + const std::string& filename, int line) { + std::vector triggers(args.begin() + 1, args.end()); + if (triggers.size() < 1) { + return Error() << "Actions must have a trigger"; + } + + Subcontext* action_subcontext = nullptr; + if (subcontexts_) { + for (auto& subcontext : *subcontexts_) { + if (StartsWith(filename, subcontext.path_prefix())) { + action_subcontext = &subcontext; + break; + } + } + } + + std::string event_trigger; + std::map property_triggers; + + if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers); + !result) { + return Error() << "ParseTriggers() failed: " << result.error(); + } + + auto action = std::make_unique(false, action_subcontext, filename, line, event_trigger, + property_triggers); + + action_ = std::move(action); + return Success(); +} + +Result ActionParser::ParseLineSection(std::vector&& args, int line) { + return action_ ? action_->AddCommand(std::move(args), line) : Success(); +} + +Result ActionParser::EndSection() { + if (action_ && action_->NumCommands() > 0) { + action_manager_->AddAction(std::move(action_)); + } + + return Success(); +} + +} // namespace init +} // namespace android diff --git a/init/action_parser.h b/init/action_parser.h new file mode 100644 index 000000000..b7f70743a --- /dev/null +++ b/init/action_parser.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 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_PARSER_H +#define _INIT_ACTION_PARSER_H + +#include +#include + +#include "action.h" +#include "action_manager.h" +#include "parser.h" +#include "subcontext.h" + +namespace android { +namespace init { + +class ActionParser : public SectionParser { + public: + ActionParser(ActionManager* action_manager, std::vector* subcontexts) + : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {} + Result ParseSection(std::vector&& args, const std::string& filename, + int line) override; + Result ParseLineSection(std::vector&& args, int line) override; + Result EndSection() override; + + private: + ActionManager* action_manager_; + std::vector* subcontexts_; + std::unique_ptr action_; +}; + +} // namespace init +} // namespace android + +#endif diff --git a/init/builtins.cpp b/init/builtins.cpp index be259c2e3..5d924b1ff 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -58,7 +58,7 @@ #include #include -#include "action.h" +#include "action_manager.h" #include "bootchart.h" #include "init.h" #include "parser.h" diff --git a/init/init.cpp b/init/init.cpp index bd09e4bcc..7e4eaa8c2 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -45,6 +45,7 @@ #include #include +#include "action_parser.h" #include "import_parser.h" #include "init_first_stage.h" #include "keychords.h" diff --git a/init/init.h b/init/init.h index ff7bdeb95..ecce5d789 100644 --- a/init/init.h +++ b/init/init.h @@ -21,6 +21,7 @@ #include #include "action.h" +#include "action_manager.h" #include "parser.h" #include "service.h" diff --git a/init/init_test.cpp b/init/init_test.cpp index 268873c12..0f9635f81 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -21,6 +21,8 @@ #include #include "action.h" +#include "action_manager.h" +#include "action_parser.h" #include "builtins.h" #include "import_parser.h" #include "keyword_map.h" diff --git a/init/reboot.cpp b/init/reboot.cpp index a88a42d74..242750a85 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -50,6 +50,7 @@ #include #include +#include "action_manager.h" #include "capabilities.h" #include "init.h" #include "property_service.h" diff --git a/init/service.cpp b/init/service.cpp index a4e33f7ec..7454a34ca 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -189,7 +189,8 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, capabilities_(capabilities), namespace_flags_(namespace_flags), seclabel_(seclabel), - onrestart_(false, subcontext_for_restart_commands, "", 0), + onrestart_(false, subcontext_for_restart_commands, "", 0, + "onrestart", {}), keychord_id_(0), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), @@ -199,9 +200,7 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, soft_limit_in_bytes_(-1), limit_in_bytes_(-1), start_order_(0), - args_(args) { - onrestart_.InitSingleTrigger("onrestart"); -} + args_(args) {} void Service::NotifyStateChange(const std::string& new_state) const { if ((flags_ & SVC_TEMPORARY) != 0) {