Merge changes Iea2d97fb,I08fa3905,Idd5b923e

* changes:
  Move all Action parsing into ActionParser
  Move ActionManager to its own file
  Move ActionParser to its own file
This commit is contained in:
Tom Cherry 2018-02-15 16:32:52 +00:00 committed by Gerrit Code Review
commit e1ae2ff9e4
13 changed files with 400 additions and 272 deletions

View File

@ -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",

View File

@ -21,11 +21,9 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
#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<std::string, std::string>& 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<Success> 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<Success> Action::InitTriggers(const std::vector<std::string>& 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<Success> Action::InitSingleTrigger(const std::string& trigger) {
std::vector<std::string> 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> 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<Action>(true, nullptr, "<Builtin Action>", 0);
std::vector<std::string> 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<Action>& 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<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
std::vector<std::string> 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<Action>(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<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
return action_ ? action_->AddCommand(std::move(args), line) : Success();
}
Result<Success> ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
action_manager_->AddAction(std::move(action_));
}
return Success();
}
} // namespace init
} // namespace android

View File

@ -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<std::string, std::string>& property_triggers);
Result<Success> AddCommand(const std::vector<std::string>& args, int line);
void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
Result<Success> InitTriggers(const std::vector<std::string>& args);
Result<Success> 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<Success> ParsePropertyTrigger(const std::string& trigger);
std::map<std::string, std::string> 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> 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<std::unique_ptr<Action>> actions_;
std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
class ActionParser : public SectionParser {
public:
ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
: action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
Result<Success> EndSection() override;
private:
ActionManager* action_manager_;
std::vector<Subcontext>* subcontexts_;
std::unique_ptr<Action> action_;
};
} // namespace init
} // namespace android

116
init/action_manager.cpp Normal file
View File

@ -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 <android-base/logging.h>
namespace android {
namespace init {
ActionManager::ActionManager() : current_command_(0) {}
ActionManager& ActionManager::GetInstance() {
static ActionManager instance;
return instance;
}
void ActionManager::AddAction(std::unique_ptr<Action> 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<Action>(true, nullptr, "<Builtin Action>", 0, name,
std::map<std::string, std::string>{});
std::vector<std::string> 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<Action>& 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

59
init/action_manager.h Normal file
View File

@ -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 <string>
#include <vector>
#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> 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<std::unique_ptr<Action>> actions_;
std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
} // namespace init
} // namespace android
#endif

153
init/action_parser.cpp Normal file
View File

@ -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 <android-base/properties.h>
#include <android-base/strings.h>
#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<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,
std::map<std::string, std::string>* 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<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,
std::string* event_trigger,
std::map<std::string, std::string>* 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<Success> ActionParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
std::vector<std::string> 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<std::string, std::string> 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<Action>(false, action_subcontext, filename, line, event_trigger,
property_triggers);
action_ = std::move(action);
return Success();
}
Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {
return action_ ? action_->AddCommand(std::move(args), line) : Success();
}
Result<Success> ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
action_manager_->AddAction(std::move(action_));
}
return Success();
}
} // namespace init
} // namespace android

49
init/action_parser.h Normal file
View File

@ -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 <string>
#include <vector>
#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<Subcontext>* subcontexts)
: action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
Result<Success> EndSection() override;
private:
ActionManager* action_manager_;
std::vector<Subcontext>* subcontexts_;
std::unique_ptr<Action> action_;
};
} // namespace init
} // namespace android
#endif

View File

@ -58,7 +58,7 @@
#include <selinux/selinux.h>
#include <system/thread_defs.h>
#include "action.h"
#include "action_manager.h"
#include "bootchart.h"
#include "init.h"
#include "parser.h"

View File

@ -45,6 +45,7 @@
#include <memory>
#include <optional>
#include "action_parser.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "keychords.h"

View File

@ -21,6 +21,7 @@
#include <vector>
#include "action.h"
#include "action_manager.h"
#include "parser.h"
#include "service.h"

View File

@ -21,6 +21,8 @@
#include <gtest/gtest.h>
#include "action.h"
#include "action_manager.h"
#include "action_parser.h"
#include "builtins.h"
#include "import_parser.h"
#include "keyword_map.h"

View File

@ -50,6 +50,7 @@
#include <private/android_filesystem_config.h>
#include <selinux/selinux.h>
#include "action_manager.h"
#include "capabilities.h"
#include "init.h"
#include "property_service.h"

View File

@ -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, "<Service '" + name + "' onrestart>", 0),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 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) {