Merge "init: Use classes for parsing and clean up memory allocations"
This commit is contained in:
commit
54c70ca156
|
@ -45,6 +45,7 @@ include $(CLEAR_VARS)
|
|||
LOCAL_CPPFLAGS := $(init_cflags)
|
||||
LOCAL_SRC_FILES:= \
|
||||
action.cpp \
|
||||
import_parser.cpp \
|
||||
init_parser.cpp \
|
||||
log.cpp \
|
||||
parser.cpp \
|
||||
|
|
308
init/action.cpp
308
init/action.cpp
|
@ -21,46 +21,27 @@
|
|||
#include <base/strings.h>
|
||||
#include <base/stringprintf.h>
|
||||
|
||||
#include "builtins.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)(const std::vector<std::string>& args),
|
||||
const std::vector<std::string>& args,
|
||||
const std::string& filename,
|
||||
int line);
|
||||
using android::base::Join;
|
||||
using android::base::StringPrintf;
|
||||
|
||||
int InvokeFunc() const;
|
||||
std::string BuildCommandString() const;
|
||||
std::string BuildSourceString() const;
|
||||
|
||||
private:
|
||||
int (*func_)(const std::vector<std::string>& args);
|
||||
const std::vector<std::string> args_;
|
||||
const std::string filename_;
|
||||
int line_;
|
||||
};
|
||||
|
||||
Action::Command::Command(int (*f)(const std::vector<std::string>& args),
|
||||
const std::vector<std::string>& args,
|
||||
const std::string& filename,
|
||||
int line) :
|
||||
func_(f), args_(args), filename_(filename), line_(line)
|
||||
{
|
||||
Command::Command(BuiltinFunction f, 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
|
||||
{
|
||||
int Command::InvokeFunc() const {
|
||||
std::vector<std::string> expanded_args;
|
||||
expanded_args.resize(args_.size());
|
||||
expanded_args[0] = args_[0];
|
||||
for (std::size_t i = 1; i < args_.size(); ++i) {
|
||||
if (expand_props(args_[i], &expanded_args[i]) == -1) {
|
||||
if (!expand_props(args_[i], &expanded_args[i])) {
|
||||
ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -69,51 +50,71 @@ int Action::Command::InvokeFunc() const
|
|||
return func_(expanded_args);
|
||||
}
|
||||
|
||||
std::string Action::Command::BuildCommandString() const
|
||||
{
|
||||
return android::base::Join(args_, ' ');
|
||||
std::string Command::BuildCommandString() const {
|
||||
return Join(args_, ' ');
|
||||
}
|
||||
|
||||
std::string Action::Command::BuildSourceString() const
|
||||
{
|
||||
std::string Command::BuildSourceString() const {
|
||||
if (!filename_.empty()) {
|
||||
return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
|
||||
return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
|
||||
} else {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
Action::Action()
|
||||
{
|
||||
Action::Action(bool oneshot) : oneshot_(oneshot) {
|
||||
}
|
||||
|
||||
void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
|
||||
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
|
||||
|
||||
bool Action::AddCommand(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line, std::string* err) {
|
||||
if (!function_map_) {
|
||||
*err = "no function map available";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.empty()) {
|
||||
*err = "command needed, but not provided";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
|
||||
if (!function) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AddCommand(function, args, filename, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Action::AddCommand(BuiltinFunction f,
|
||||
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);
|
||||
const std::string& filename, int line) {
|
||||
commands_.emplace_back(f, args, filename, line);
|
||||
}
|
||||
|
||||
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::CombineAction(const Action& action) {
|
||||
for (const auto& c : action.commands_) {
|
||||
commands_.emplace_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
void Action::ExecuteCommand(const Command& command) const
|
||||
{
|
||||
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();
|
||||
|
||||
|
@ -128,8 +129,7 @@ void Action::ExecuteCommand(const Command& command) const
|
|||
}
|
||||
}
|
||||
|
||||
bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
|
||||
{
|
||||
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('=');
|
||||
|
@ -149,8 +149,7 @@ bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
|
||||
{
|
||||
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) {
|
||||
|
@ -179,21 +178,26 @@ bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Action::InitSingleTrigger(const std::string& trigger)
|
||||
{
|
||||
bool Action::InitSingleTrigger(const std::string& trigger) {
|
||||
std::vector<std::string> name_vector{trigger};
|
||||
std::string err;
|
||||
return InitTriggers(name_vector, &err);
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// It takes an optional (name, value) pair, which if provided must
|
||||
// be present in property_triggers_; it skips the check of the current
|
||||
// property value for this pair.
|
||||
bool Action::CheckPropertyTriggers(const std::string& name,
|
||||
const std::string& value) const
|
||||
{
|
||||
bool found = name.empty();
|
||||
const std::string& value) const {
|
||||
if (property_triggers_.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool found = name.empty();
|
||||
for (const auto& t : property_triggers_) {
|
||||
const auto& trigger_name = t.first;
|
||||
const auto& trigger_value = t.second;
|
||||
|
@ -214,27 +218,23 @@ bool Action::CheckPropertyTriggers(const std::string& name,
|
|||
return found;
|
||||
}
|
||||
|
||||
bool Action::CheckEventTrigger(const std::string& trigger) const
|
||||
{
|
||||
bool Action::CheckEventTrigger(const std::string& trigger) const {
|
||||
return !event_trigger_.empty() &&
|
||||
trigger == event_trigger_ &&
|
||||
CheckPropertyTriggers();
|
||||
}
|
||||
|
||||
bool Action::CheckPropertyTrigger(const std::string& name,
|
||||
const std::string& value) const
|
||||
{
|
||||
const std::string& value) const {
|
||||
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
|
||||
}
|
||||
|
||||
bool Action::TriggersEqual(const class Action& other) const
|
||||
{
|
||||
bool Action::TriggersEqual(const Action& other) const {
|
||||
return property_triggers_ == other.property_triggers_ &&
|
||||
event_trigger_ == other.event_trigger_;
|
||||
}
|
||||
|
||||
std::string Action::BuildTriggersString() const
|
||||
{
|
||||
std::string Action::BuildTriggersString() const {
|
||||
std::string result;
|
||||
|
||||
for (const auto& t : property_triggers_) {
|
||||
|
@ -251,28 +251,26 @@ std::string Action::BuildTriggersString() const
|
|||
return result;
|
||||
}
|
||||
|
||||
void Action::DumpState() const
|
||||
{
|
||||
void Action::DumpState() const {
|
||||
std::string trigger_name = BuildTriggersString();
|
||||
INFO("on %s\n", trigger_name.c_str());
|
||||
|
||||
for (const auto& c : commands_) {
|
||||
std::string cmd_str = c->BuildCommandString();
|
||||
std::string cmd_str = c.BuildCommandString();
|
||||
INFO(" %s\n", cmd_str.c_str());
|
||||
}
|
||||
INFO("\n");
|
||||
}
|
||||
|
||||
|
||||
class EventTrigger : public Trigger {
|
||||
public:
|
||||
EventTrigger(const std::string& trigger) : trigger_(trigger) {
|
||||
}
|
||||
bool CheckTriggers(const Action* action) override {
|
||||
return action->CheckEventTrigger(trigger_);
|
||||
bool CheckTriggers(const Action& action) const override {
|
||||
return action.CheckEventTrigger(trigger_);
|
||||
}
|
||||
private:
|
||||
std::string trigger_;
|
||||
const std::string trigger_;
|
||||
};
|
||||
|
||||
class PropertyTrigger : public Trigger {
|
||||
|
@ -280,27 +278,26 @@ public:
|
|||
PropertyTrigger(const std::string& name, const std::string& value)
|
||||
: name_(name), value_(value) {
|
||||
}
|
||||
bool CheckTriggers(const Action* action) override {
|
||||
return action->CheckPropertyTrigger(name_, value_);
|
||||
bool CheckTriggers(const Action& action) const override {
|
||||
return action.CheckPropertyTrigger(name_, value_);
|
||||
}
|
||||
private:
|
||||
std::string name_;
|
||||
std::string value_;
|
||||
const std::string name_;
|
||||
const std::string value_;
|
||||
};
|
||||
|
||||
class BuiltinTrigger : public Trigger {
|
||||
public:
|
||||
BuiltinTrigger(Action* action) : action_(action) {
|
||||
}
|
||||
bool CheckTriggers(const Action* action) override {
|
||||
return action == action_;
|
||||
bool CheckTriggers(const Action& action) const override {
|
||||
return action_ == &action;
|
||||
}
|
||||
private:
|
||||
Action* action_;
|
||||
const Action* action_;
|
||||
};
|
||||
|
||||
ActionManager::ActionManager() : current_command_(0)
|
||||
{
|
||||
ActionManager::ActionManager() : current_command_(0) {
|
||||
}
|
||||
|
||||
ActionManager& ActionManager::GetInstance() {
|
||||
|
@ -308,45 +305,56 @@ ActionManager& ActionManager::GetInstance() {
|
|||
return instance;
|
||||
}
|
||||
|
||||
void ActionManager::QueueEventTrigger(const std::string& trigger)
|
||||
{
|
||||
void ActionManager::AddAction(std::unique_ptr<Action> action) {
|
||||
auto old_action_it =
|
||||
std::find_if(actions_.begin(), actions_.end(),
|
||||
[&action] (std::unique_ptr<Action>& a) {
|
||||
return action->TriggersEqual(*a);
|
||||
});
|
||||
|
||||
if (old_action_it != actions_.end()) {
|
||||
(*old_action_it)->CombineAction(*action);
|
||||
} else {
|
||||
actions_.emplace_back(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionManager::QueueEventTrigger(const std::string& trigger) {
|
||||
trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
|
||||
}
|
||||
|
||||
void ActionManager::QueuePropertyTrigger(const std::string& name,
|
||||
const std::string& value)
|
||||
{
|
||||
const std::string& value) {
|
||||
trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
|
||||
}
|
||||
|
||||
void ActionManager::QueueAllPropertyTriggers()
|
||||
{
|
||||
void ActionManager::QueueAllPropertyTriggers() {
|
||||
QueuePropertyTrigger("", "");
|
||||
}
|
||||
|
||||
void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
|
||||
const std::string& name)
|
||||
{
|
||||
Action* act = new Action();
|
||||
void ActionManager::QueueBuiltinAction(BuiltinFunction func,
|
||||
const std::string& name) {
|
||||
auto action = std::make_unique<Action>(true);
|
||||
std::vector<std::string> name_vector{name};
|
||||
|
||||
if (!act->InitSingleTrigger(name)) {
|
||||
if (!action->InitSingleTrigger(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
act->AddCommand(func, name_vector);
|
||||
action->AddCommand(func, name_vector);
|
||||
|
||||
actions_.push_back(act);
|
||||
trigger_queue_.push(std::make_unique<BuiltinTrigger>(act));
|
||||
trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
|
||||
actions_.emplace_back(std::move(action));
|
||||
}
|
||||
|
||||
void ActionManager::ExecuteOneCommand() {
|
||||
// Loop through the trigger queue until we have an action to execute
|
||||
while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
|
||||
std::copy_if(actions_.begin(), actions_.end(),
|
||||
std::back_inserter(current_executing_actions_),
|
||||
[this] (Action* act) {
|
||||
return trigger_queue_.front()->CheckTriggers(act);
|
||||
});
|
||||
for (const auto& action : actions_) {
|
||||
if (trigger_queue_.front()->CheckTriggers(*action)) {
|
||||
current_executing_actions_.emplace(action.get());
|
||||
}
|
||||
}
|
||||
trigger_queue_.pop();
|
||||
}
|
||||
|
||||
|
@ -354,59 +362,67 @@ void ActionManager::ExecuteOneCommand() {
|
|||
return;
|
||||
}
|
||||
|
||||
Action* action = current_executing_actions_.back();
|
||||
if (!action->NumCommands()) {
|
||||
current_executing_actions_.pop_back();
|
||||
return;
|
||||
}
|
||||
auto action = current_executing_actions_.front();
|
||||
|
||||
if (current_command_ == 0) {
|
||||
std::string trigger_name = action->BuildTriggersString();
|
||||
INFO("processing action %p (%s)\n", action, trigger_name.c_str());
|
||||
INFO("processing action (%s)\n", trigger_name.c_str());
|
||||
}
|
||||
|
||||
action->ExecuteOneCommand(current_command_++);
|
||||
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;
|
||||
current_executing_actions_.pop_back();
|
||||
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
|
||||
{
|
||||
bool ActionManager::HasMoreCommands() const {
|
||||
return !current_executing_actions_.empty() || !trigger_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(actions_.begin(), actions_.end(),
|
||||
[&act] (Action* a) { return act->TriggersEqual(*a); });
|
||||
|
||||
if (old_act_it != actions_.end()) {
|
||||
delete act;
|
||||
return *old_act_it;
|
||||
}
|
||||
|
||||
actions_.push_back(act);
|
||||
return act;
|
||||
}
|
||||
|
||||
void ActionManager::DumpState() const
|
||||
{
|
||||
void ActionManager::DumpState() const {
|
||||
for (const auto& a : actions_) {
|
||||
a->DumpState();
|
||||
}
|
||||
INFO("\n");
|
||||
}
|
||||
|
||||
bool ActionParser::ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) {
|
||||
std::vector<std::string> triggers(args.begin() + 1, args.end());
|
||||
if (triggers.size() < 1) {
|
||||
*err = "actions must have a trigger";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto action = std::make_unique<Action>(false);
|
||||
if (!action->InitTriggers(triggers, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
action_ = std::move(action);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line,
|
||||
std::string* err) const {
|
||||
return action_ ? action_->AddCommand(args, filename, line, err) : false;
|
||||
}
|
||||
|
||||
void ActionParser::EndSection() {
|
||||
if (action_ && action_->NumCommands() > 0) {
|
||||
ActionManager::GetInstance().AddAction(std::move(action_));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,36 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "builtins.h"
|
||||
#include "init_parser.h"
|
||||
#include "keyword_map.h"
|
||||
|
||||
class Command {
|
||||
public:
|
||||
Command(BuiltinFunction f, const std::vector<std::string>& args,
|
||||
const std::string& filename, int line);
|
||||
|
||||
int InvokeFunc() const;
|
||||
std::string BuildCommandString() const;
|
||||
std::string BuildSourceString() const;
|
||||
|
||||
private:
|
||||
BuiltinFunction func_;
|
||||
std::vector<std::string> args_;
|
||||
std::string filename_;
|
||||
int line_;
|
||||
};
|
||||
|
||||
class Action {
|
||||
public:
|
||||
Action();
|
||||
Action(bool oneshot = false);
|
||||
|
||||
void AddCommand(int (*f)(const std::vector<std::string>& args),
|
||||
bool AddCommand(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line, std::string* err);
|
||||
void AddCommand(BuiltinFunction f,
|
||||
const std::vector<std::string>& args,
|
||||
const std::string& filename = "", int line = 0);
|
||||
void CombineAction(const Action& action);
|
||||
bool InitTriggers(const std::vector<std::string>& args, std::string* err);
|
||||
bool InitSingleTrigger(const std::string& trigger);
|
||||
std::size_t NumCommands() const;
|
||||
|
@ -37,13 +60,17 @@ public:
|
|||
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;
|
||||
bool TriggersEqual(const Action& other) const;
|
||||
std::string BuildTriggersString() const;
|
||||
void DumpState() const;
|
||||
|
||||
private:
|
||||
class Command;
|
||||
bool oneshot() const { return oneshot_; }
|
||||
static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
|
||||
function_map_ = function_map;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
void ExecuteCommand(const Command& command) const;
|
||||
bool CheckPropertyTriggers(const std::string& name = "",
|
||||
const std::string& value = "") const;
|
||||
|
@ -51,27 +78,28 @@ private:
|
|||
|
||||
std::map<std::string, std::string> property_triggers_;
|
||||
std::string event_trigger_;
|
||||
std::vector<Command*> commands_;
|
||||
std::vector<Command> commands_;
|
||||
bool oneshot_;
|
||||
static const KeywordMap<BuiltinFunction>* function_map_;
|
||||
};
|
||||
|
||||
class Trigger {
|
||||
public:
|
||||
virtual ~Trigger() { }
|
||||
virtual bool CheckTriggers(const Action* action) = 0;
|
||||
virtual bool CheckTriggers(const Action& action) const = 0;
|
||||
};
|
||||
|
||||
class ActionManager {
|
||||
public:
|
||||
static ActionManager& GetInstance();
|
||||
|
||||
void AddAction(std::unique_ptr<Action> action);
|
||||
void QueueEventTrigger(const std::string& trigger);
|
||||
void QueuePropertyTrigger(const std::string& name, const std::string& value);
|
||||
void QueueAllPropertyTriggers();
|
||||
void QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
|
||||
const std::string& name);
|
||||
void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
|
||||
void ExecuteOneCommand();
|
||||
bool HasMoreCommands() const;
|
||||
Action* AddNewAction(const std::vector<std::string>& triggers,
|
||||
std::string* err);
|
||||
void DumpState() const;
|
||||
|
||||
private:
|
||||
|
@ -80,10 +108,26 @@ private:
|
|||
ActionManager(ActionManager const&) = delete;
|
||||
void operator=(ActionManager const&) = delete;
|
||||
|
||||
std::vector<Action*> actions_;
|
||||
std::vector<std::unique_ptr<Action>> actions_;
|
||||
std::queue<std::unique_ptr<Trigger>> trigger_queue_;
|
||||
std::vector<Action*> current_executing_actions_;
|
||||
std::queue<const Action*> current_executing_actions_;
|
||||
std::size_t current_command_;
|
||||
};
|
||||
|
||||
class ActionParser : public SectionParser {
|
||||
public:
|
||||
ActionParser() : action_(nullptr) {
|
||||
}
|
||||
bool ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) override;
|
||||
bool ParseLineSection(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line,
|
||||
std::string* err) const override;
|
||||
void EndSection() override;
|
||||
void EndFile(const std::string&) override {
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<Action> action_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
|
||||
#include "bootchart.h"
|
||||
#include "keywords.h"
|
||||
#include "log.h"
|
||||
#include "property_service.h"
|
||||
|
||||
|
@ -32,6 +31,7 @@
|
|||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <base/file.h>
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
#ifndef _BOOTCHART_H
|
||||
#define _BOOTCHART_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
int do_bootchart_init(const std::vector<std::string>& args);
|
||||
void bootchart_sample(int* timeout);
|
||||
|
||||
#endif /* _BOOTCHART_H */
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "builtins.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <mntent.h>
|
||||
|
@ -44,10 +46,10 @@
|
|||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "bootchart.h"
|
||||
#include "devices.h"
|
||||
#include "init.h"
|
||||
#include "init_parser.h"
|
||||
#include "keywords.h"
|
||||
#include "log.h"
|
||||
#include "property_service.h"
|
||||
#include "service.h"
|
||||
|
@ -60,8 +62,7 @@
|
|||
// System call provided by bionic but not in any header file.
|
||||
extern "C" int init_module(void *, unsigned long, const char *);
|
||||
|
||||
static int insmod(const char *filename, const char *options)
|
||||
{
|
||||
static int insmod(const char *filename, const char *options) {
|
||||
std::string module;
|
||||
if (!read_file(filename, &module)) {
|
||||
return -1;
|
||||
|
@ -71,8 +72,7 @@ static int insmod(const char *filename, const char *options)
|
|||
return init_module(&module[0], module.size(), options);
|
||||
}
|
||||
|
||||
static int __ifupdown(const char *interface, int up)
|
||||
{
|
||||
static int __ifupdown(const char *interface, int up) {
|
||||
struct ifreq ifr;
|
||||
int s, ret;
|
||||
|
||||
|
@ -99,8 +99,7 @@ done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void unmount_and_fsck(const struct mntent *entry)
|
||||
{
|
||||
static void unmount_and_fsck(const struct mntent *entry) {
|
||||
if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
|
||||
return;
|
||||
|
||||
|
@ -160,8 +159,7 @@ static void unmount_and_fsck(const struct mntent *entry)
|
|||
}
|
||||
}
|
||||
|
||||
int do_class_start(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_class_start(const std::vector<std::string>& args) {
|
||||
/* Starting a class does not start services
|
||||
* which are explicitly disabled. They must
|
||||
* be started individually.
|
||||
|
@ -171,27 +169,23 @@ int do_class_start(const std::vector<std::string>& args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_class_stop(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_class_stop(const std::vector<std::string>& args) {
|
||||
ServiceManager::GetInstance().
|
||||
ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_class_reset(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_class_reset(const std::vector<std::string>& args) {
|
||||
ServiceManager::GetInstance().
|
||||
ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_domainname(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_domainname(const std::vector<std::string>& args) {
|
||||
return write_file("/proc/sys/kernel/domainname", args[1].c_str());
|
||||
}
|
||||
|
||||
int do_enable(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_enable(const std::vector<std::string>& args) {
|
||||
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
|
||||
if (!svc) {
|
||||
return -1;
|
||||
|
@ -199,7 +193,7 @@ int do_enable(const std::vector<std::string>& args)
|
|||
return svc->Enable();
|
||||
}
|
||||
|
||||
int do_exec(const std::vector<std::string>& args) {
|
||||
static int do_exec(const std::vector<std::string>& args) {
|
||||
Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
|
||||
if (!svc) {
|
||||
return -1;
|
||||
|
@ -211,23 +205,19 @@ int do_exec(const std::vector<std::string>& args) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_export(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_export(const std::vector<std::string>& args) {
|
||||
return add_environment(args[1].c_str(), args[2].c_str());
|
||||
}
|
||||
|
||||
int do_hostname(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_hostname(const std::vector<std::string>& args) {
|
||||
return write_file("/proc/sys/kernel/hostname", args[1].c_str());
|
||||
}
|
||||
|
||||
int do_ifup(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_ifup(const std::vector<std::string>& args) {
|
||||
return __ifupdown(args[1].c_str(), 1);
|
||||
}
|
||||
|
||||
int do_insmod(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_insmod(const std::vector<std::string>& args) {
|
||||
std::string options;
|
||||
|
||||
if (args.size() > 2) {
|
||||
|
@ -241,8 +231,7 @@ int do_insmod(const std::vector<std::string>& args)
|
|||
return insmod(args[1].c_str(), options.c_str());
|
||||
}
|
||||
|
||||
int do_mkdir(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_mkdir(const std::vector<std::string>& args) {
|
||||
mode_t mode = 0755;
|
||||
int ret;
|
||||
|
||||
|
@ -310,8 +299,7 @@ static struct {
|
|||
#define DATA_MNT_POINT "/data"
|
||||
|
||||
/* mount <type> <device> <path> <flags ...> <options> */
|
||||
int do_mount(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_mount(const std::vector<std::string>& args) {
|
||||
char tmp[64];
|
||||
const char *source, *target, *system;
|
||||
const char *options = NULL;
|
||||
|
@ -411,8 +399,7 @@ exit_success:
|
|||
|
||||
}
|
||||
|
||||
static int wipe_data_via_recovery()
|
||||
{
|
||||
static int wipe_data_via_recovery() {
|
||||
mkdir("/cache/recovery", 0700);
|
||||
int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
|
||||
if (fd >= 0) {
|
||||
|
@ -427,16 +414,16 @@ static int wipe_data_via_recovery()
|
|||
while (1) { pause(); } // never reached
|
||||
}
|
||||
|
||||
void import_late()
|
||||
{
|
||||
static void import_late() {
|
||||
static const std::vector<std::string> init_directories = {
|
||||
"/system/etc/init",
|
||||
"/vendor/etc/init",
|
||||
"/odm/etc/init"
|
||||
};
|
||||
|
||||
Parser& parser = Parser::GetInstance();
|
||||
for (const auto& dir : init_directories) {
|
||||
init_parse_config(dir.c_str());
|
||||
parser.ParseConfig(dir.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,17 +431,13 @@ void import_late()
|
|||
* This function might request a reboot, in which case it will
|
||||
* not return.
|
||||
*/
|
||||
int do_mount_all(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_mount_all(const std::vector<std::string>& args) {
|
||||
pid_t pid;
|
||||
int ret = -1;
|
||||
int child_ret = -1;
|
||||
int status;
|
||||
struct fstab *fstab;
|
||||
|
||||
if (args.size() != 2) {
|
||||
return -1;
|
||||
}
|
||||
const char* fstabfile = args[1].c_str();
|
||||
/*
|
||||
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
|
||||
|
@ -535,8 +518,7 @@ int do_mount_all(const std::vector<std::string>& args)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int do_swapon_all(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_swapon_all(const std::vector<std::string>& args) {
|
||||
struct fstab *fstab;
|
||||
int ret;
|
||||
|
||||
|
@ -547,16 +529,14 @@ int do_swapon_all(const std::vector<std::string>& args)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int do_setprop(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_setprop(const std::vector<std::string>& args) {
|
||||
const char* name = args[1].c_str();
|
||||
const char* value = args[2].c_str();
|
||||
property_set(name, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_setrlimit(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_setrlimit(const std::vector<std::string>& args) {
|
||||
struct rlimit limit;
|
||||
int resource;
|
||||
resource = std::stoi(args[1]);
|
||||
|
@ -565,8 +545,7 @@ int do_setrlimit(const std::vector<std::string>& args)
|
|||
return setrlimit(resource, &limit);
|
||||
}
|
||||
|
||||
int do_start(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_start(const std::vector<std::string>& args) {
|
||||
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
|
||||
if (!svc) {
|
||||
ERROR("do_start: Service %s not found\n", args[1].c_str());
|
||||
|
@ -577,8 +556,7 @@ int do_start(const std::vector<std::string>& args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_stop(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_stop(const std::vector<std::string>& args) {
|
||||
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
|
||||
if (!svc) {
|
||||
ERROR("do_stop: Service %s not found\n", args[1].c_str());
|
||||
|
@ -588,8 +566,7 @@ int do_stop(const std::vector<std::string>& args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_restart(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_restart(const std::vector<std::string>& args) {
|
||||
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
|
||||
if (!svc) {
|
||||
ERROR("do_restart: Service %s not found\n", args[1].c_str());
|
||||
|
@ -599,8 +576,7 @@ int do_restart(const std::vector<std::string>& args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_powerctl(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_powerctl(const std::vector<std::string>& args) {
|
||||
const char* command = args[1].c_str();
|
||||
int len = 0;
|
||||
unsigned int cmd = 0;
|
||||
|
@ -636,34 +612,26 @@ int do_powerctl(const std::vector<std::string>& args)
|
|||
callback_on_ro_remount);
|
||||
}
|
||||
|
||||
int do_trigger(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_trigger(const std::vector<std::string>& args) {
|
||||
ActionManager::GetInstance().QueueEventTrigger(args[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_symlink(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_symlink(const std::vector<std::string>& args) {
|
||||
return symlink(args[1].c_str(), args[2].c_str());
|
||||
}
|
||||
|
||||
int do_rm(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_rm(const std::vector<std::string>& args) {
|
||||
return unlink(args[1].c_str());
|
||||
}
|
||||
|
||||
int do_rmdir(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_rmdir(const std::vector<std::string>& args) {
|
||||
return rmdir(args[1].c_str());
|
||||
}
|
||||
|
||||
int do_sysclktz(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_sysclktz(const std::vector<std::string>& args) {
|
||||
struct timezone tz;
|
||||
|
||||
if (args.size() != 2)
|
||||
return -1;
|
||||
|
||||
memset(&tz, 0, sizeof(tz));
|
||||
tz.tz_minuteswest = std::stoi(args[1]);
|
||||
if (settimeofday(NULL, &tz))
|
||||
|
@ -671,7 +639,7 @@ int do_sysclktz(const std::vector<std::string>& args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_verity_load_state(const std::vector<std::string>& args) {
|
||||
static int do_verity_load_state(const std::vector<std::string>& args) {
|
||||
int mode = -1;
|
||||
int rc = fs_mgr_load_verity_state(&mode);
|
||||
if (rc == 0 && mode == VERITY_MODE_LOGGING) {
|
||||
|
@ -680,24 +648,23 @@ int do_verity_load_state(const std::vector<std::string>& args) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
|
||||
static void verity_update_property(fstab_rec *fstab, const char *mount_point,
|
||||
int mode, int status) {
|
||||
property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
|
||||
android::base::StringPrintf("%d", mode).c_str());
|
||||
}
|
||||
|
||||
int do_verity_update_state(const std::vector<std::string>& args) {
|
||||
static int do_verity_update_state(const std::vector<std::string>& args) {
|
||||
return fs_mgr_update_verity_state(verity_update_property);
|
||||
}
|
||||
|
||||
int do_write(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_write(const std::vector<std::string>& args) {
|
||||
const char* path = args[1].c_str();
|
||||
const char* value = args[2].c_str();
|
||||
return write_file(path, value);
|
||||
}
|
||||
|
||||
int do_copy(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_copy(const std::vector<std::string>& args) {
|
||||
char *buffer = NULL;
|
||||
int rc = 0;
|
||||
int fd1 = -1, fd2 = -1;
|
||||
|
@ -705,9 +672,6 @@ int do_copy(const std::vector<std::string>& args)
|
|||
int brtw, brtr;
|
||||
char *p;
|
||||
|
||||
if (args.size() != 3)
|
||||
return -1;
|
||||
|
||||
if (stat(args[1].c_str(), &info) < 0)
|
||||
return -1;
|
||||
|
||||
|
@ -758,7 +722,7 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
int do_chown(const std::vector<std::string>& args) {
|
||||
static int do_chown(const std::vector<std::string>& args) {
|
||||
/* GID is optional. */
|
||||
if (args.size() == 3) {
|
||||
if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
|
||||
|
@ -786,7 +750,7 @@ static mode_t get_mode(const char *s) {
|
|||
return mode;
|
||||
}
|
||||
|
||||
int do_chmod(const std::vector<std::string>& args) {
|
||||
static int do_chmod(const std::vector<std::string>& args) {
|
||||
mode_t mode = get_mode(args[1].c_str());
|
||||
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
|
||||
return -errno;
|
||||
|
@ -794,7 +758,7 @@ int do_chmod(const std::vector<std::string>& args) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_restorecon(const std::vector<std::string>& args) {
|
||||
static int do_restorecon(const std::vector<std::string>& args) {
|
||||
int ret = 0;
|
||||
|
||||
for (auto it = std::next(args.begin()); it != args.end(); ++it) {
|
||||
|
@ -804,7 +768,7 @@ int do_restorecon(const std::vector<std::string>& args) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
int do_restorecon_recursive(const std::vector<std::string>& args) {
|
||||
static int do_restorecon_recursive(const std::vector<std::string>& args) {
|
||||
int ret = 0;
|
||||
|
||||
for (auto it = std::next(args.begin()); it != args.end(); ++it) {
|
||||
|
@ -814,12 +778,7 @@ int do_restorecon_recursive(const std::vector<std::string>& args) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
int do_loglevel(const std::vector<std::string>& args) {
|
||||
if (args.size() != 2) {
|
||||
ERROR("loglevel: missing argument\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int do_loglevel(const std::vector<std::string>& args) {
|
||||
int log_level = std::stoi(args[1]);
|
||||
if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
|
||||
ERROR("loglevel: invalid log level'%d'\n", log_level);
|
||||
|
@ -830,23 +789,16 @@ int do_loglevel(const std::vector<std::string>& args) {
|
|||
}
|
||||
|
||||
int do_load_persist_props(const std::vector<std::string>& args) {
|
||||
if (args.size() == 1) {
|
||||
load_persist_props();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
load_persist_props();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_load_all_props(const std::vector<std::string>& args) {
|
||||
if (args.size() == 1) {
|
||||
load_all_props();
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
static int do_load_all_props(const std::vector<std::string>& args) {
|
||||
load_all_props();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_wait(const std::vector<std::string>& args)
|
||||
{
|
||||
static int do_wait(const std::vector<std::string>& args) {
|
||||
if (args.size() == 2) {
|
||||
return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
|
||||
} else if (args.size() == 3) {
|
||||
|
@ -858,8 +810,7 @@ int do_wait(const std::vector<std::string>& args)
|
|||
/*
|
||||
* Callback to make a directory from the ext4 code
|
||||
*/
|
||||
static int do_installkeys_ensure_dir_exists(const char* dir)
|
||||
{
|
||||
static int do_installkeys_ensure_dir_exists(const char* dir) {
|
||||
if (make_dir(dir, 0700) && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -867,12 +818,7 @@ static int do_installkeys_ensure_dir_exists(const char* dir)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int do_installkey(const std::vector<std::string>& args)
|
||||
{
|
||||
if (args.size() != 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int do_installkey(const std::vector<std::string>& args) {
|
||||
std::string prop_value = property_get("ro.crypto.type");
|
||||
if (prop_value != "file") {
|
||||
return 0;
|
||||
|
@ -881,3 +827,49 @@ int do_installkey(const std::vector<std::string>& args)
|
|||
return e4crypt_create_device_key(args[1].c_str(),
|
||||
do_installkeys_ensure_dir_exists);
|
||||
}
|
||||
|
||||
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
|
||||
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
||||
static const Map builtin_functions = {
|
||||
{"bootchart_init", {0, 0, do_bootchart_init}},
|
||||
{"chmod", {2, 2, do_chmod}},
|
||||
{"chown", {2, 3, do_chown}},
|
||||
{"class_reset", {1, 1, do_class_reset}},
|
||||
{"class_start", {1, 1, do_class_start}},
|
||||
{"class_stop", {1, 1, do_class_stop}},
|
||||
{"copy", {2, 2, do_copy}},
|
||||
{"domainname", {1, 1, do_domainname}},
|
||||
{"enable", {1, 1, do_enable}},
|
||||
{"exec", {1, kMax, do_exec}},
|
||||
{"export", {2, 2, do_export}},
|
||||
{"hostname", {1, 1, do_hostname}},
|
||||
{"ifup", {1, 1, do_ifup}},
|
||||
{"insmod", {1, kMax, do_insmod}},
|
||||
{"installkey", {1, 1, do_installkey}},
|
||||
{"load_all_props", {0, 0, do_load_all_props}},
|
||||
{"load_persist_props", {0, 0, do_load_persist_props}},
|
||||
{"loglevel", {1, 1, do_loglevel}},
|
||||
{"mkdir", {1, 4, do_mkdir}},
|
||||
{"mount_all", {1, 1, do_mount_all}},
|
||||
{"mount", {3, kMax, do_mount}},
|
||||
{"powerctl", {1, 1, do_powerctl}},
|
||||
{"restart", {1, 1, do_restart}},
|
||||
{"restorecon", {1, kMax, do_restorecon}},
|
||||
{"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
|
||||
{"rm", {1, 1, do_rm}},
|
||||
{"rmdir", {1, 1, do_rmdir}},
|
||||
{"setprop", {2, 2, do_setprop}},
|
||||
{"setrlimit", {3, 3, do_setrlimit}},
|
||||
{"start", {1, 1, do_start}},
|
||||
{"stop", {1, 1, do_stop}},
|
||||
{"swapon_all", {1, 1, do_swapon_all}},
|
||||
{"symlink", {2, 2, do_symlink}},
|
||||
{"sysclktz", {1, 1, do_sysclktz}},
|
||||
{"trigger", {1, 1, do_trigger}},
|
||||
{"verity_load_state", {0, 0, do_verity_load_state}},
|
||||
{"verity_update_state", {0, 0, do_verity_update_state}},
|
||||
{"wait", {1, 2, do_wait}},
|
||||
{"write", {2, 2, do_write}},
|
||||
};
|
||||
return builtin_functions;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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_BUILTINS_H
|
||||
#define _INIT_BUILTINS_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "keyword_map.h"
|
||||
|
||||
using BuiltinFunction = int (*) (const std::vector<std::string>& args);
|
||||
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
|
||||
public:
|
||||
BuiltinFunctionMap() {
|
||||
}
|
||||
private:
|
||||
Map& map() const override;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 "import_parser.h"
|
||||
|
||||
#include "errno.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
bool ImportParser::ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) {
|
||||
if (args.size() != 2) {
|
||||
*err = "single argument needed for import\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string conf_file;
|
||||
bool ret = expand_props(args[1], &conf_file);
|
||||
if (!ret) {
|
||||
*err = "error while expanding import";
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO("Added '%s' to import list\n", conf_file.c_str());
|
||||
imports_.emplace_back(std::move(conf_file));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImportParser::EndFile(const std::string& filename) {
|
||||
auto current_imports = std::move(imports_);
|
||||
imports_.clear();
|
||||
for (const auto& s : current_imports) {
|
||||
if (!Parser::GetInstance().ParseConfig(s)) {
|
||||
ERROR("could not import file '%s' from '%s': %s\n",
|
||||
s.c_str(), filename.c_str(), strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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_IMPORT_PARSER_H
|
||||
#define _INIT_IMPORT_PARSER_H
|
||||
|
||||
#include "init_parser.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class ImportParser : public SectionParser {
|
||||
public:
|
||||
ImportParser() {
|
||||
}
|
||||
bool ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) override;
|
||||
bool ParseLineSection(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line,
|
||||
std::string* err) const override {
|
||||
return true;
|
||||
}
|
||||
void EndSection() override {
|
||||
}
|
||||
void EndFile(const std::string& filename) override;
|
||||
private:
|
||||
std::vector<std::string> imports_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -55,6 +55,7 @@
|
|||
#include "action.h"
|
||||
#include "bootchart.h"
|
||||
#include "devices.h"
|
||||
#include "import_parser.h"
|
||||
#include "init.h"
|
||||
#include "init_parser.h"
|
||||
#include "keychords.h"
|
||||
|
@ -604,7 +605,14 @@ int main(int argc, char** argv) {
|
|||
property_load_boot_defaults();
|
||||
start_property_service();
|
||||
|
||||
init_parse_config("/init.rc");
|
||||
const BuiltinFunctionMap function_map;
|
||||
Action::set_function_map(&function_map);
|
||||
|
||||
Parser& parser = Parser::GetInstance();
|
||||
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
|
||||
parser.AddSectionParser("on", std::make_unique<ActionParser>());
|
||||
parser.AddSectionParser("import", std::make_unique<ImportParser>());
|
||||
parser.ParseConfig("/init.rc");
|
||||
|
||||
ActionManager& am = ActionManager::GetInstance();
|
||||
|
||||
|
|
|
@ -14,387 +14,117 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "init.h"
|
||||
#include "init_parser.h"
|
||||
#include "log.h"
|
||||
#include "parser.h"
|
||||
#include "property_service.h"
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
#include <cutils/iosched_policy.h>
|
||||
#include <cutils/list.h>
|
||||
|
||||
static list_declare(service_list);
|
||||
|
||||
struct import {
|
||||
struct listnode list;
|
||||
const char *filename;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
static void *parse_action(struct parse_state *state, int nargs, char **args);
|
||||
static void parse_line_action(struct parse_state *state, int nargs, char **args);
|
||||
|
||||
#define SECTION 0x01
|
||||
#define COMMAND 0x02
|
||||
#define OPTION 0x04
|
||||
|
||||
#include "keywords.h"
|
||||
|
||||
#define KEYWORD(symbol, flags, nargs, func) \
|
||||
[ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int (*func)(const std::vector<std::string>& args);
|
||||
size_t nargs;
|
||||
unsigned char flags;
|
||||
} keyword_info[KEYWORD_COUNT] = {
|
||||
[ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
|
||||
#include "keywords.h"
|
||||
};
|
||||
#undef KEYWORD
|
||||
|
||||
#define kw_is(kw, type) (keyword_info[kw].flags & (type))
|
||||
#define kw_name(kw) (keyword_info[kw].name)
|
||||
#define kw_func(kw) (keyword_info[kw].func)
|
||||
#define kw_nargs(kw) (keyword_info[kw].nargs)
|
||||
|
||||
void dump_parser_state() {
|
||||
ServiceManager::GetInstance().DumpState();
|
||||
ActionManager::GetInstance().DumpState();
|
||||
Parser::Parser() {
|
||||
}
|
||||
|
||||
static int lookup_keyword(const char *s)
|
||||
{
|
||||
switch (*s++) {
|
||||
case 'b':
|
||||
if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
|
||||
break;
|
||||
case 'c':
|
||||
if (!strcmp(s, "opy")) return K_copy;
|
||||
if (!strcmp(s, "lass")) return K_class;
|
||||
if (!strcmp(s, "lass_start")) return K_class_start;
|
||||
if (!strcmp(s, "lass_stop")) return K_class_stop;
|
||||
if (!strcmp(s, "lass_reset")) return K_class_reset;
|
||||
if (!strcmp(s, "onsole")) return K_console;
|
||||
if (!strcmp(s, "hown")) return K_chown;
|
||||
if (!strcmp(s, "hmod")) return K_chmod;
|
||||
if (!strcmp(s, "ritical")) return K_critical;
|
||||
break;
|
||||
case 'd':
|
||||
if (!strcmp(s, "isabled")) return K_disabled;
|
||||
if (!strcmp(s, "omainname")) return K_domainname;
|
||||
break;
|
||||
case 'e':
|
||||
if (!strcmp(s, "nable")) return K_enable;
|
||||
if (!strcmp(s, "xec")) return K_exec;
|
||||
if (!strcmp(s, "xport")) return K_export;
|
||||
break;
|
||||
case 'g':
|
||||
if (!strcmp(s, "roup")) return K_group;
|
||||
break;
|
||||
case 'h':
|
||||
if (!strcmp(s, "ostname")) return K_hostname;
|
||||
break;
|
||||
case 'i':
|
||||
if (!strcmp(s, "oprio")) return K_ioprio;
|
||||
if (!strcmp(s, "fup")) return K_ifup;
|
||||
if (!strcmp(s, "nsmod")) return K_insmod;
|
||||
if (!strcmp(s, "mport")) return K_import;
|
||||
if (!strcmp(s, "nstallkey")) return K_installkey;
|
||||
break;
|
||||
case 'k':
|
||||
if (!strcmp(s, "eycodes")) return K_keycodes;
|
||||
break;
|
||||
case 'l':
|
||||
if (!strcmp(s, "oglevel")) return K_loglevel;
|
||||
if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
|
||||
if (!strcmp(s, "oad_all_props")) return K_load_all_props;
|
||||
break;
|
||||
case 'm':
|
||||
if (!strcmp(s, "kdir")) return K_mkdir;
|
||||
if (!strcmp(s, "ount_all")) return K_mount_all;
|
||||
if (!strcmp(s, "ount")) return K_mount;
|
||||
break;
|
||||
case 'o':
|
||||
if (!strcmp(s, "n")) return K_on;
|
||||
if (!strcmp(s, "neshot")) return K_oneshot;
|
||||
if (!strcmp(s, "nrestart")) return K_onrestart;
|
||||
break;
|
||||
case 'p':
|
||||
if (!strcmp(s, "owerctl")) return K_powerctl;
|
||||
break;
|
||||
case 'r':
|
||||
if (!strcmp(s, "estart")) return K_restart;
|
||||
if (!strcmp(s, "estorecon")) return K_restorecon;
|
||||
if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
|
||||
if (!strcmp(s, "mdir")) return K_rmdir;
|
||||
if (!strcmp(s, "m")) return K_rm;
|
||||
break;
|
||||
case 's':
|
||||
if (!strcmp(s, "eclabel")) return K_seclabel;
|
||||
if (!strcmp(s, "ervice")) return K_service;
|
||||
if (!strcmp(s, "etenv")) return K_setenv;
|
||||
if (!strcmp(s, "etprop")) return K_setprop;
|
||||
if (!strcmp(s, "etrlimit")) return K_setrlimit;
|
||||
if (!strcmp(s, "ocket")) return K_socket;
|
||||
if (!strcmp(s, "tart")) return K_start;
|
||||
if (!strcmp(s, "top")) return K_stop;
|
||||
if (!strcmp(s, "wapon_all")) return K_swapon_all;
|
||||
if (!strcmp(s, "ymlink")) return K_symlink;
|
||||
if (!strcmp(s, "ysclktz")) return K_sysclktz;
|
||||
break;
|
||||
case 't':
|
||||
if (!strcmp(s, "rigger")) return K_trigger;
|
||||
break;
|
||||
case 'u':
|
||||
if (!strcmp(s, "ser")) return K_user;
|
||||
break;
|
||||
case 'v':
|
||||
if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
|
||||
if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
|
||||
break;
|
||||
case 'w':
|
||||
if (!strcmp(s, "rite")) return K_write;
|
||||
if (!strcmp(s, "ritepid")) return K_writepid;
|
||||
if (!strcmp(s, "ait")) return K_wait;
|
||||
break;
|
||||
}
|
||||
return K_UNKNOWN;
|
||||
Parser& Parser::GetInstance() {
|
||||
static Parser instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void parse_line_no_op(struct parse_state*, int, char**) {
|
||||
void Parser::AddSectionParser(const std::string& name,
|
||||
std::unique_ptr<SectionParser> parser) {
|
||||
section_parsers_[name] = std::move(parser);
|
||||
}
|
||||
|
||||
int expand_props(const std::string& src, std::string* dst) {
|
||||
const char *src_ptr = src.c_str();
|
||||
|
||||
if (!dst) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* - variables can either be $x.y or ${x.y}, in case they are only part
|
||||
* of the string.
|
||||
* - will accept $$ as a literal $.
|
||||
* - no nested property expansion, i.e. ${foo.${bar}} is not supported,
|
||||
* bad things will happen
|
||||
*/
|
||||
while (*src_ptr) {
|
||||
const char *c;
|
||||
|
||||
c = strchr(src_ptr, '$');
|
||||
if (!c) {
|
||||
dst->append(src_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
dst->append(src_ptr, c);
|
||||
c++;
|
||||
|
||||
if (*c == '$') {
|
||||
dst->push_back(*(c++));
|
||||
src_ptr = c;
|
||||
continue;
|
||||
} else if (*c == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string prop_name;
|
||||
if (*c == '{') {
|
||||
c++;
|
||||
const char* end = strchr(c, '}');
|
||||
if (!end) {
|
||||
// failed to find closing brace, abort.
|
||||
ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
|
||||
goto err;
|
||||
}
|
||||
prop_name = std::string(c, end);
|
||||
c = end + 1;
|
||||
} else {
|
||||
prop_name = c;
|
||||
ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
|
||||
c);
|
||||
c += prop_name.size();
|
||||
}
|
||||
|
||||
if (prop_name.empty()) {
|
||||
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.c_str());
|
||||
goto err;
|
||||
}
|
||||
|
||||
dst->append(prop_val);
|
||||
src_ptr = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void parse_import(struct parse_state *state, int nargs, char **args)
|
||||
{
|
||||
if (nargs != 2) {
|
||||
ERROR("single argument needed for import\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string conf_file;
|
||||
int ret = expand_props(args[1], &conf_file);
|
||||
if (ret) {
|
||||
ERROR("error while handling import on line '%d' in '%s'\n",
|
||||
state->line, state->filename);
|
||||
return;
|
||||
}
|
||||
|
||||
struct import* import = (struct import*) calloc(1, sizeof(struct import));
|
||||
import->filename = strdup(conf_file.c_str());
|
||||
|
||||
struct listnode *import_list = (listnode*) state->priv;
|
||||
list_add_tail(import_list, &import->list);
|
||||
INFO("Added '%s' to import list\n", import->filename);
|
||||
}
|
||||
|
||||
static void parse_new_section(struct parse_state *state, int kw,
|
||||
int nargs, char **args)
|
||||
{
|
||||
printf("[ %s %s ]\n", args[0],
|
||||
nargs > 1 ? args[1] : "");
|
||||
switch(kw) {
|
||||
case K_service:
|
||||
state->context = parse_service(state, nargs, args);
|
||||
if (state->context) {
|
||||
state->parse_line = parse_line_service;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case K_on:
|
||||
state->context = parse_action(state, nargs, args);
|
||||
if (state->context) {
|
||||
state->parse_line = parse_line_action;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case K_import:
|
||||
parse_import(state, nargs, args);
|
||||
break;
|
||||
}
|
||||
state->parse_line = parse_line_no_op;
|
||||
}
|
||||
|
||||
static void parse_config(const char *fn, const std::string& data)
|
||||
{
|
||||
struct listnode import_list;
|
||||
struct listnode *node;
|
||||
char *args[INIT_PARSER_MAXARGS];
|
||||
|
||||
int nargs = 0;
|
||||
|
||||
void Parser::ParseData(const std::string& filename, const std::string& data) {
|
||||
//TODO: Use a parser with const input and remove this copy
|
||||
std::vector<char> data_copy(data.begin(), data.end());
|
||||
data_copy.push_back('\0');
|
||||
|
||||
parse_state state;
|
||||
state.filename = fn;
|
||||
state.filename = filename.c_str();
|
||||
state.line = 0;
|
||||
state.ptr = &data_copy[0];
|
||||
state.nexttoken = 0;
|
||||
state.parse_line = parse_line_no_op;
|
||||
|
||||
list_init(&import_list);
|
||||
state.priv = &import_list;
|
||||
SectionParser* section_parser = nullptr;
|
||||
std::vector<std::string> args;
|
||||
|
||||
for (;;) {
|
||||
switch (next_token(&state)) {
|
||||
case T_EOF:
|
||||
state.parse_line(&state, 0, 0);
|
||||
goto parser_done;
|
||||
if (section_parser) {
|
||||
section_parser->EndSection();
|
||||
}
|
||||
return;
|
||||
case T_NEWLINE:
|
||||
state.line++;
|
||||
if (nargs) {
|
||||
int kw = lookup_keyword(args[0]);
|
||||
if (kw_is(kw, SECTION)) {
|
||||
state.parse_line(&state, 0, 0);
|
||||
parse_new_section(&state, kw, nargs, args);
|
||||
} else {
|
||||
state.parse_line(&state, nargs, args);
|
||||
}
|
||||
nargs = 0;
|
||||
if (args.empty()) {
|
||||
break;
|
||||
}
|
||||
if (section_parsers_.count(args[0])) {
|
||||
if (section_parser) {
|
||||
section_parser->EndSection();
|
||||
}
|
||||
section_parser = section_parsers_[args[0]].get();
|
||||
std::string ret_err;
|
||||
if (!section_parser->ParseSection(args, &ret_err)) {
|
||||
parse_error(&state, "%s\n", ret_err.c_str());
|
||||
section_parser = nullptr;
|
||||
}
|
||||
} else if (section_parser) {
|
||||
std::string ret_err;
|
||||
if (!section_parser->ParseLineSection(args, state.filename,
|
||||
state.line, &ret_err)) {
|
||||
parse_error(&state, "%s\n", ret_err.c_str());
|
||||
}
|
||||
}
|
||||
args.clear();
|
||||
break;
|
||||
case T_TEXT:
|
||||
if (nargs < INIT_PARSER_MAXARGS) {
|
||||
args[nargs++] = state.text;
|
||||
}
|
||||
args.emplace_back(state.text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parser_done:
|
||||
list_for_each(node, &import_list) {
|
||||
struct import* import = node_to_item(node, struct import, list);
|
||||
if (!init_parse_config(import->filename)) {
|
||||
ERROR("could not import file '%s' from '%s': %s\n",
|
||||
import->filename, fn, strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool init_parse_config_file(const char* path) {
|
||||
INFO("Parsing file %s...\n", path);
|
||||
bool Parser::ParseConfigFile(const std::string& path) {
|
||||
INFO("Parsing file %s...\n", path.c_str());
|
||||
Timer t;
|
||||
std::string data;
|
||||
if (!read_file(path, &data)) {
|
||||
if (!read_file(path.c_str(), &data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data.push_back('\n'); // TODO: fix parse_config.
|
||||
parse_config(path, data);
|
||||
dump_parser_state();
|
||||
ParseData(path, data);
|
||||
for (const auto& sp : section_parsers_) {
|
||||
sp.second->EndFile(path);
|
||||
}
|
||||
DumpState();
|
||||
|
||||
NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
|
||||
NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_parse_config_dir(const char* path) {
|
||||
INFO("Parsing directory %s...\n", path);
|
||||
std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir);
|
||||
bool Parser::ParseConfigDir(const std::string& path) {
|
||||
INFO("Parsing directory %s...\n", path.c_str());
|
||||
std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
|
||||
if (!config_dir) {
|
||||
ERROR("Could not import directory '%s'\n", path);
|
||||
ERROR("Could not import directory '%s'\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
dirent* current_file;
|
||||
while ((current_file = readdir(config_dir.get()))) {
|
||||
std::string current_path =
|
||||
android::base::StringPrintf("%s/%s", path, current_file->d_name);
|
||||
android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
|
||||
// Ignore directories and only process regular files.
|
||||
if (current_file->d_type == DT_REG) {
|
||||
if (!init_parse_config_file(current_path.c_str())) {
|
||||
if (!ParseConfigFile(current_path)) {
|
||||
ERROR("could not import file '%s'\n", current_path.c_str());
|
||||
}
|
||||
}
|
||||
|
@ -402,97 +132,14 @@ static bool init_parse_config_dir(const char* path) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool init_parse_config(const char* path) {
|
||||
if (is_dir(path)) {
|
||||
return init_parse_config_dir(path);
|
||||
bool Parser::ParseConfig(const std::string& path) {
|
||||
if (is_dir(path.c_str())) {
|
||||
return ParseConfigDir(path);
|
||||
}
|
||||
return init_parse_config_file(path);
|
||||
return ParseConfigFile(path);
|
||||
}
|
||||
|
||||
static void *parse_service(struct parse_state *state, int nargs, char **args)
|
||||
{
|
||||
if (nargs < 3) {
|
||||
parse_error(state, "services must have a name and a program\n");
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<std::string> str_args(args + 2, args + nargs);
|
||||
std::string ret_err;
|
||||
Service* svc = ServiceManager::GetInstance().AddNewService(args[1], "default",
|
||||
str_args, &ret_err);
|
||||
|
||||
if (!svc) {
|
||||
parse_error(state, "%s\n", ret_err.c_str());
|
||||
}
|
||||
|
||||
return svc;
|
||||
}
|
||||
|
||||
static void parse_line_service(struct parse_state *state, int nargs, char **args)
|
||||
{
|
||||
if (nargs == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Service* svc = static_cast<Service*>(state->context);
|
||||
int kw = lookup_keyword(args[0]);
|
||||
std::vector<std::string> str_args(args, args + nargs);
|
||||
std::string ret_err;
|
||||
bool ret = svc->HandleLine(kw, str_args, &ret_err);
|
||||
|
||||
if (!ret) {
|
||||
parse_error(state, "%s\n", ret_err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
static void *parse_action(struct parse_state* state, int nargs, char **args)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool add_command_to_action(Action* action, const std::vector<std::string>& args,
|
||||
const std::string& filename, int line, std::string* err)
|
||||
{
|
||||
int kw;
|
||||
size_t n;
|
||||
|
||||
kw = lookup_keyword(args[0].c_str());
|
||||
if (!kw_is(kw, COMMAND)) {
|
||||
*err = android::base::StringPrintf("invalid command '%s'\n", args[0].c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
n = kw_nargs(kw);
|
||||
if (args.size() < n) {
|
||||
*err = android::base::StringPrintf("%s requires %zu %s\n",
|
||||
args[0].c_str(), n - 1,
|
||||
n > 2 ? "arguments" : "argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
action->AddCommand(kw_func(kw), args, filename, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void parse_line_action(struct parse_state* state, int nargs, char **args)
|
||||
{
|
||||
if (nargs == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Action* action = static_cast<Action*>(state->context);
|
||||
std::vector<std::string> str_args(args, args + nargs);
|
||||
std::string ret_err;
|
||||
bool ret = add_command_to_action(action, str_args, state->filename,
|
||||
state->line, &ret_err);
|
||||
if (!ret) {
|
||||
parse_error(state, "%s\n", ret_err.c_str());
|
||||
}
|
||||
void Parser::DumpState() const {
|
||||
ServiceManager::GetInstance().DumpState();
|
||||
ActionManager::GetInstance().DumpState();
|
||||
}
|
||||
|
|
|
@ -17,16 +17,39 @@
|
|||
#ifndef _INIT_INIT_PARSER_H_
|
||||
#define _INIT_INIT_PARSER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define INIT_PARSER_MAXARGS 64
|
||||
class SectionParser {
|
||||
public:
|
||||
virtual ~SectionParser() {
|
||||
}
|
||||
virtual bool ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) = 0;
|
||||
virtual bool ParseLineSection(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line,
|
||||
std::string* err) const = 0;
|
||||
virtual void EndSection() = 0;
|
||||
virtual void EndFile(const std::string& filename) = 0;
|
||||
};
|
||||
|
||||
class Action;
|
||||
class Parser {
|
||||
public:
|
||||
static Parser& GetInstance();
|
||||
void DumpState() const;
|
||||
bool ParseConfig(const std::string& path);
|
||||
void AddSectionParser(const std::string& name,
|
||||
std::unique_ptr<SectionParser> parser);
|
||||
|
||||
bool init_parse_config(const char* path);
|
||||
int expand_props(const std::string& src, std::string* dst);
|
||||
bool add_command_to_action(Action* action, const std::vector<std::string>& args,
|
||||
const std::string& filename, int line, std::string* err);
|
||||
private:
|
||||
Parser();
|
||||
|
||||
void ParseData(const std::string& filename, const std::string& data);
|
||||
bool ParseConfigFile(const std::string& path);
|
||||
bool ParseConfigDir(const std::string& path);
|
||||
|
||||
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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_KEYWORD_MAP_H_
|
||||
#define _INIT_KEYWORD_MAP_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <base/stringprintf.h>
|
||||
|
||||
template <typename Function>
|
||||
class KeywordMap {
|
||||
public:
|
||||
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
|
||||
using Map = const std::map<std::string, FunctionInfo>;
|
||||
|
||||
virtual ~KeywordMap() {
|
||||
}
|
||||
|
||||
const Function FindFunction(const std::string& keyword,
|
||||
size_t num_args,
|
||||
std::string* err) const {
|
||||
using android::base::StringPrintf;
|
||||
|
||||
auto function_info_it = map().find(keyword);
|
||||
if (function_info_it == map().end()) {
|
||||
*err = StringPrintf("invalid keyword '%s'", keyword.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto function_info = function_info_it->second;
|
||||
|
||||
auto min_args = std::get<0>(function_info);
|
||||
auto max_args = std::get<1>(function_info);
|
||||
if (min_args == max_args && num_args != min_args) {
|
||||
*err = StringPrintf("%s requires %zu argument%s",
|
||||
keyword.c_str(), min_args,
|
||||
(min_args > 1 || min_args == 0) ? "s" : "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (num_args < min_args || num_args > max_args) {
|
||||
if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
|
||||
*err = StringPrintf("%s requires at least %zu argument%s",
|
||||
keyword.c_str(), min_args,
|
||||
min_args > 1 ? "s" : "");
|
||||
} else {
|
||||
*err = StringPrintf("%s requires between %zu and %zu arguments",
|
||||
keyword.c_str(), min_args, max_args);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::get<Function>(function_info);
|
||||
}
|
||||
|
||||
private:
|
||||
//Map of keyword ->
|
||||
//(minimum number of arguments, maximum number of arguments, function pointer)
|
||||
virtual Map& map() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
109
init/keywords.h
109
init/keywords.h
|
@ -1,109 +0,0 @@
|
|||
#ifndef KEYWORD
|
||||
#include <string>
|
||||
#include <vector>
|
||||
int do_bootchart_init(const std::vector<std::string>& args);
|
||||
int do_class_start(const std::vector<std::string>& args);
|
||||
int do_class_stop(const std::vector<std::string>& args);
|
||||
int do_class_reset(const std::vector<std::string>& args);
|
||||
int do_domainname(const std::vector<std::string>& args);
|
||||
int do_enable(const std::vector<std::string>& args);
|
||||
int do_exec(const std::vector<std::string>& args);
|
||||
int do_export(const std::vector<std::string>& args);
|
||||
int do_hostname(const std::vector<std::string>& args);
|
||||
int do_ifup(const std::vector<std::string>& args);
|
||||
int do_insmod(const std::vector<std::string>& args);
|
||||
int do_installkey(const std::vector<std::string>& args);
|
||||
int do_mkdir(const std::vector<std::string>& args);
|
||||
int do_mount_all(const std::vector<std::string>& args);
|
||||
int do_mount(const std::vector<std::string>& args);
|
||||
int do_powerctl(const std::vector<std::string>& args);
|
||||
int do_restart(const std::vector<std::string>& args);
|
||||
int do_restorecon(const std::vector<std::string>& args);
|
||||
int do_restorecon_recursive(const std::vector<std::string>& args);
|
||||
int do_rm(const std::vector<std::string>& args);
|
||||
int do_rmdir(const std::vector<std::string>& args);
|
||||
int do_setprop(const std::vector<std::string>& args);
|
||||
int do_setrlimit(const std::vector<std::string>& args);
|
||||
int do_start(const std::vector<std::string>& args);
|
||||
int do_stop(const std::vector<std::string>& args);
|
||||
int do_swapon_all(const std::vector<std::string>& args);
|
||||
int do_trigger(const std::vector<std::string>& args);
|
||||
int do_symlink(const std::vector<std::string>& args);
|
||||
int do_sysclktz(const std::vector<std::string>& args);
|
||||
int do_write(const std::vector<std::string>& args);
|
||||
int do_copy(const std::vector<std::string>& args);
|
||||
int do_chown(const std::vector<std::string>& args);
|
||||
int do_chmod(const std::vector<std::string>& args);
|
||||
int do_loglevel(const std::vector<std::string>& args);
|
||||
int do_load_persist_props(const std::vector<std::string>& args);
|
||||
int do_load_all_props(const std::vector<std::string>& args);
|
||||
int do_verity_load_state(const std::vector<std::string>& args);
|
||||
int do_verity_update_state(const std::vector<std::string>& args);
|
||||
int do_wait(const std::vector<std::string>& args);
|
||||
#define __MAKE_KEYWORD_ENUM__
|
||||
#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
|
||||
enum {
|
||||
K_UNKNOWN,
|
||||
#endif
|
||||
KEYWORD(bootchart_init, COMMAND, 0, do_bootchart_init)
|
||||
KEYWORD(chmod, COMMAND, 2, do_chmod)
|
||||
KEYWORD(chown, COMMAND, 2, do_chown)
|
||||
KEYWORD(class, OPTION, 0, 0)
|
||||
KEYWORD(class_reset, COMMAND, 1, do_class_reset)
|
||||
KEYWORD(class_start, COMMAND, 1, do_class_start)
|
||||
KEYWORD(class_stop, COMMAND, 1, do_class_stop)
|
||||
KEYWORD(console, OPTION, 0, 0)
|
||||
KEYWORD(copy, COMMAND, 2, do_copy)
|
||||
KEYWORD(critical, OPTION, 0, 0)
|
||||
KEYWORD(disabled, OPTION, 0, 0)
|
||||
KEYWORD(domainname, COMMAND, 1, do_domainname)
|
||||
KEYWORD(enable, COMMAND, 1, do_enable)
|
||||
KEYWORD(exec, COMMAND, 1, do_exec)
|
||||
KEYWORD(export, COMMAND, 2, do_export)
|
||||
KEYWORD(group, OPTION, 0, 0)
|
||||
KEYWORD(hostname, COMMAND, 1, do_hostname)
|
||||
KEYWORD(ifup, COMMAND, 1, do_ifup)
|
||||
KEYWORD(import, SECTION, 1, 0)
|
||||
KEYWORD(insmod, COMMAND, 1, do_insmod)
|
||||
KEYWORD(installkey, COMMAND, 1, do_installkey)
|
||||
KEYWORD(ioprio, OPTION, 0, 0)
|
||||
KEYWORD(keycodes, OPTION, 0, 0)
|
||||
KEYWORD(load_all_props, COMMAND, 0, do_load_all_props)
|
||||
KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
|
||||
KEYWORD(loglevel, COMMAND, 1, do_loglevel)
|
||||
KEYWORD(mkdir, COMMAND, 1, do_mkdir)
|
||||
KEYWORD(mount_all, COMMAND, 1, do_mount_all)
|
||||
KEYWORD(mount, COMMAND, 3, do_mount)
|
||||
KEYWORD(oneshot, OPTION, 0, 0)
|
||||
KEYWORD(onrestart, OPTION, 0, 0)
|
||||
KEYWORD(on, SECTION, 0, 0)
|
||||
KEYWORD(powerctl, COMMAND, 1, do_powerctl)
|
||||
KEYWORD(restart, COMMAND, 1, do_restart)
|
||||
KEYWORD(restorecon, COMMAND, 1, do_restorecon)
|
||||
KEYWORD(restorecon_recursive, COMMAND, 1, do_restorecon_recursive)
|
||||
KEYWORD(rm, COMMAND, 1, do_rm)
|
||||
KEYWORD(rmdir, COMMAND, 1, do_rmdir)
|
||||
KEYWORD(seclabel, OPTION, 0, 0)
|
||||
KEYWORD(service, SECTION, 0, 0)
|
||||
KEYWORD(setenv, OPTION, 2, 0)
|
||||
KEYWORD(setprop, COMMAND, 2, do_setprop)
|
||||
KEYWORD(setrlimit, COMMAND, 3, do_setrlimit)
|
||||
KEYWORD(socket, OPTION, 0, 0)
|
||||
KEYWORD(start, COMMAND, 1, do_start)
|
||||
KEYWORD(stop, COMMAND, 1, do_stop)
|
||||
KEYWORD(swapon_all, COMMAND, 1, do_swapon_all)
|
||||
KEYWORD(symlink, COMMAND, 1, do_symlink)
|
||||
KEYWORD(sysclktz, COMMAND, 1, do_sysclktz)
|
||||
KEYWORD(trigger, COMMAND, 1, do_trigger)
|
||||
KEYWORD(user, OPTION, 0, 0)
|
||||
KEYWORD(verity_load_state, COMMAND, 0, do_verity_load_state)
|
||||
KEYWORD(verity_update_state, COMMAND, 0, do_verity_update_state)
|
||||
KEYWORD(wait, COMMAND, 1, do_wait)
|
||||
KEYWORD(write, COMMAND, 2, do_write)
|
||||
KEYWORD(writepid, OPTION, 0, 0)
|
||||
#ifdef __MAKE_KEYWORD_ENUM__
|
||||
KEYWORD_COUNT,
|
||||
};
|
||||
#undef __MAKE_KEYWORD_ENUM__
|
||||
#undef KEYWORD
|
||||
#endif
|
381
init/service.cpp
381
init/service.cpp
|
@ -32,11 +32,13 @@
|
|||
#include "action.h"
|
||||
#include "init.h"
|
||||
#include "init_parser.h"
|
||||
#include "keywords.h"
|
||||
#include "log.h"
|
||||
#include "property_service.h"
|
||||
#include "util.h"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
using android::base::WriteStringToFile;
|
||||
|
||||
#define CRITICAL_CRASH_THRESHOLD 4 // if we crash >4 times ...
|
||||
#define CRITICAL_CRASH_WINDOW (4*60) // ... in 4 minutes, goto recovery
|
||||
|
||||
|
@ -84,7 +86,7 @@ void Service::NotifyStateChange(const std::string& new_state) const {
|
|||
return;
|
||||
}
|
||||
|
||||
std::string prop_name = android::base::StringPrintf("init.svc.%s", name_.c_str());
|
||||
std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
|
||||
if (prop_name.length() >= PROP_NAME_MAX) {
|
||||
// If the property name would be too long, we can't set it.
|
||||
ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
|
||||
|
@ -104,8 +106,7 @@ bool Service::Reap() {
|
|||
|
||||
// Remove any sockets we may have created.
|
||||
for (const auto& si : sockets_) {
|
||||
std::string tmp = android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s",
|
||||
si.name.c_str());
|
||||
std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
|
||||
unlink(tmp.c_str());
|
||||
}
|
||||
|
||||
|
@ -168,148 +169,156 @@ void Service::DumpState() const {
|
|||
}
|
||||
}
|
||||
|
||||
bool Service::HandleLine(int kw, const std::vector<std::string>& args, std::string* err) {
|
||||
std::vector<std::string> str_args;
|
||||
bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
|
||||
classname_ = args[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
ioprio_class_ = IoSchedClass_NONE;
|
||||
bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
|
||||
flags_ |= SVC_CONSOLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (kw) {
|
||||
case K_class:
|
||||
if (args.size() != 2) {
|
||||
*err = "class option requires a classname\n";
|
||||
return false;
|
||||
} else {
|
||||
classname_ = args[1];
|
||||
}
|
||||
break;
|
||||
case K_console:
|
||||
flags_ |= SVC_CONSOLE;
|
||||
break;
|
||||
case K_disabled:
|
||||
flags_ |= SVC_DISABLED;
|
||||
flags_ |= SVC_RC_DISABLED;
|
||||
break;
|
||||
case K_ioprio:
|
||||
if (args.size() != 3) {
|
||||
*err = "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n";
|
||||
return false;
|
||||
} else {
|
||||
ioprio_pri_ = std::stoul(args[2], 0, 8);
|
||||
bool Service::HandleCritical(const std::vector<std::string>& args, std::string* err) {
|
||||
flags_ |= SVC_CRITICAL;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
|
||||
*err = "priority value must be range 0 - 7\n";
|
||||
return false;
|
||||
}
|
||||
bool Service::HandleDisabled(const std::vector<std::string>& args, std::string* err) {
|
||||
flags_ |= SVC_DISABLED;
|
||||
flags_ |= SVC_RC_DISABLED;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[1] == "rt") {
|
||||
ioprio_class_ = IoSchedClass_RT;
|
||||
} else if (args[1] == "be") {
|
||||
ioprio_class_ = IoSchedClass_BE;
|
||||
} else if (args[1] == "idle") {
|
||||
ioprio_class_ = IoSchedClass_IDLE;
|
||||
} else {
|
||||
*err = "ioprio option usage: ioprio <rt|be|idle> <0-7>\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case K_group:
|
||||
if (args.size() < 2) {
|
||||
*err = "group option requires a group id\n";
|
||||
return false;
|
||||
} else if (args.size() > NR_SVC_SUPP_GIDS + 2) {
|
||||
*err = android::base::StringPrintf("group option accepts at most %d supp. groups\n",
|
||||
NR_SVC_SUPP_GIDS);
|
||||
return false;
|
||||
} else {
|
||||
gid_ = decode_uid(args[1].c_str());
|
||||
for (std::size_t n = 2; n < args.size(); n++) {
|
||||
supp_gids_.push_back(decode_uid(args[n].c_str()));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case K_keycodes:
|
||||
if (args.size() < 2) {
|
||||
*err = "keycodes option requires atleast one keycode\n";
|
||||
return false;
|
||||
} else {
|
||||
for (std::size_t i = 1; i < args.size(); i++) {
|
||||
keycodes_.push_back(std::stoi(args[i]));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case K_oneshot:
|
||||
flags_ |= SVC_ONESHOT;
|
||||
break;
|
||||
case K_onrestart:
|
||||
if (args.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
str_args.assign(args.begin() + 1, args.end());
|
||||
add_command_to_action(&onrestart_, str_args, "", 0, err);
|
||||
break;
|
||||
case K_critical:
|
||||
flags_ |= SVC_CRITICAL;
|
||||
break;
|
||||
case K_setenv: { /* name value */
|
||||
if (args.size() < 3) {
|
||||
*err = "setenv option requires name and value arguments\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
envvars_.push_back({args[1], args[2]});
|
||||
break;
|
||||
}
|
||||
case K_socket: {/* name type perm [ uid gid context ] */
|
||||
if (args.size() < 4) {
|
||||
*err = "socket option requires name, type, perm arguments\n";
|
||||
return false;
|
||||
}
|
||||
if (args[2] != "dgram" && args[2] != "stream" &&
|
||||
args[2] != "seqpacket") {
|
||||
*err = "socket type must be 'dgram', 'stream' or 'seqpacket'\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
int perm = std::stoul(args[3], 0, 8);
|
||||
uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
|
||||
gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
|
||||
std::string socketcon = args.size() > 6 ? args[6] : "";
|
||||
|
||||
sockets_.push_back({args[1], args[2], uid, gid, perm, socketcon});
|
||||
break;
|
||||
}
|
||||
case K_user:
|
||||
if (args.size() != 2) {
|
||||
*err = "user option requires a user id\n";
|
||||
return false;
|
||||
} else {
|
||||
uid_ = decode_uid(args[1].c_str());
|
||||
}
|
||||
break;
|
||||
case K_seclabel:
|
||||
if (args.size() != 2) {
|
||||
*err = "seclabel option requires a label string\n";
|
||||
return false;
|
||||
} else {
|
||||
seclabel_ = args[1];
|
||||
}
|
||||
break;
|
||||
case K_writepid:
|
||||
if (args.size() < 2) {
|
||||
*err = "writepid option requires at least one filename\n";
|
||||
return false;
|
||||
}
|
||||
writepid_files_.assign(args.begin() + 1, args.end());
|
||||
break;
|
||||
|
||||
default:
|
||||
*err = android::base::StringPrintf("invalid option '%s'\n", args[0].c_str());
|
||||
return false;
|
||||
bool Service::HandleGroup(const std::vector<std::string>& args, std::string* err) {
|
||||
gid_ = decode_uid(args[1].c_str());
|
||||
for (std::size_t n = 2; n < args.size(); n++) {
|
||||
supp_gids_.emplace_back(decode_uid(args[n].c_str()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
|
||||
ioprio_pri_ = std::stoul(args[2], 0, 8);
|
||||
|
||||
if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
|
||||
*err = "priority value must be range 0 - 7";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args[1] == "rt") {
|
||||
ioprio_class_ = IoSchedClass_RT;
|
||||
} else if (args[1] == "be") {
|
||||
ioprio_class_ = IoSchedClass_BE;
|
||||
} else if (args[1] == "idle") {
|
||||
ioprio_class_ = IoSchedClass_IDLE;
|
||||
} else {
|
||||
*err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleKeycodes(const std::vector<std::string>& args, std::string* err) {
|
||||
for (std::size_t i = 1; i < args.size(); i++) {
|
||||
keycodes_.emplace_back(std::stoi(args[i]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleOneshot(const std::vector<std::string>& args, std::string* err) {
|
||||
flags_ |= SVC_ONESHOT;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleOnrestart(const std::vector<std::string>& args, std::string* err) {
|
||||
std::vector<std::string> str_args(args.begin() + 1, args.end());
|
||||
onrestart_.AddCommand(str_args, "", 0, err);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleSeclabel(const std::vector<std::string>& args, std::string* err) {
|
||||
seclabel_ = args[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleSetenv(const std::vector<std::string>& args, std::string* err) {
|
||||
envvars_.emplace_back(args[1], args[2]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* name type perm [ uid gid context ] */
|
||||
bool Service::HandleSocket(const std::vector<std::string>& args, std::string* err) {
|
||||
if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
|
||||
*err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
|
||||
return false;
|
||||
}
|
||||
|
||||
int perm = std::stoul(args[3], 0, 8);
|
||||
uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
|
||||
gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
|
||||
std::string socketcon = args.size() > 6 ? args[6] : "";
|
||||
|
||||
sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleUser(const std::vector<std::string>& args, std::string* err) {
|
||||
uid_ = decode_uid(args[1].c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Service::HandleWritepid(const std::vector<std::string>& args, std::string* err) {
|
||||
writepid_files_.assign(args.begin() + 1, args.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
|
||||
public:
|
||||
OptionHandlerMap() {
|
||||
}
|
||||
private:
|
||||
Map& map() const override;
|
||||
};
|
||||
|
||||
Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
|
||||
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
||||
static const Map option_handlers = {
|
||||
{"class", {1, 1, &Service::HandleClass}},
|
||||
{"console", {0, 0, &Service::HandleConsole}},
|
||||
{"critical", {0, 0, &Service::HandleCritical}},
|
||||
{"disabled", {0, 0, &Service::HandleDisabled}},
|
||||
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
|
||||
{"ioprio", {2, 2, &Service::HandleIoprio}},
|
||||
{"keycodes", {1, kMax, &Service::HandleKeycodes}},
|
||||
{"oneshot", {0, 0, &Service::HandleOneshot}},
|
||||
{"onrestart", {1, kMax, &Service::HandleOnrestart}},
|
||||
{"seclabel", {1, 1, &Service::HandleSeclabel}},
|
||||
{"setenv", {2, 2, &Service::HandleSetenv}},
|
||||
{"socket", {3, 6, &Service::HandleSocket}},
|
||||
{"user", {1, 1, &Service::HandleUser}},
|
||||
{"writepid", {1, kMax, &Service::HandleWritepid}},
|
||||
};
|
||||
return option_handlers;
|
||||
}
|
||||
|
||||
bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
|
||||
if (args.empty()) {
|
||||
*err = "option needed, but not provided";
|
||||
return false;
|
||||
}
|
||||
|
||||
static const OptionHandlerMap handler_map;
|
||||
auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);
|
||||
|
||||
if (!handler) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this->*handler)(args, err);
|
||||
}
|
||||
|
||||
bool Service::Start(const std::vector<std::string>& dynamic_args) {
|
||||
// Starting a service removes it from the disabled or reset state and
|
||||
// immediately takes it out of the restarting state if it was in there.
|
||||
|
@ -396,7 +405,7 @@ bool Service::Start(const std::vector<std::string>& dynamic_args) {
|
|||
umask(077);
|
||||
if (properties_initialized()) {
|
||||
get_property_workspace(&fd, &sz);
|
||||
std::string tmp = android::base::StringPrintf("%d,%d", dup(fd), sz);
|
||||
std::string tmp = StringPrintf("%d,%d", dup(fd), sz);
|
||||
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
|
||||
}
|
||||
|
||||
|
@ -418,9 +427,9 @@ bool Service::Start(const std::vector<std::string>& dynamic_args) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string pid_str = android::base::StringPrintf("%d", pid);
|
||||
std::string pid_str = StringPrintf("%d", pid);
|
||||
for (const auto& file : writepid_files_) {
|
||||
if (!android::base::WriteStringToFile(pid_str, file)) {
|
||||
if (!WriteStringToFile(pid_str, file)) {
|
||||
ERROR("couldn't write %s to %s: %s\n",
|
||||
pid_str.c_str(), file.c_str(), strerror(errno));
|
||||
}
|
||||
|
@ -609,9 +618,8 @@ void Service::OpenConsole() const {
|
|||
}
|
||||
|
||||
void Service::PublishSocket(const std::string& name, int fd) const {
|
||||
std::string key = android::base::StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s",
|
||||
name.c_str());
|
||||
std::string val = android::base::StringPrintf("%d", fd);
|
||||
std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
|
||||
std::string val = StringPrintf("%d", fd);
|
||||
add_environment(key.c_str(), val.c_str());
|
||||
|
||||
/* make sure we don't close-on-exec */
|
||||
|
@ -628,31 +636,14 @@ ServiceManager& ServiceManager::GetInstance() {
|
|||
return instance;
|
||||
}
|
||||
|
||||
Service* ServiceManager::AddNewService(const std::string& name,
|
||||
const std::string& classname,
|
||||
const std::vector<std::string>& args,
|
||||
std::string* err) {
|
||||
if (!IsValidName(name)) {
|
||||
*err = android::base::StringPrintf("invalid service name '%s'\n", name.c_str());
|
||||
return nullptr;
|
||||
void ServiceManager::AddService(std::unique_ptr<Service> service) {
|
||||
Service* old_service = FindServiceByName(service->name());
|
||||
if (old_service) {
|
||||
ERROR("ignored duplicate definition of service '%s'",
|
||||
service->name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
|
||||
if (svc) {
|
||||
*err = android::base::StringPrintf("ignored duplicate definition of service '%s'\n",
|
||||
name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Service> svc_p(new Service(name, classname, args));
|
||||
if (!svc_p) {
|
||||
ERROR("Couldn't allocate service for service '%s'", name.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
svc = svc_p.get();
|
||||
services_.push_back(std::move(svc_p));
|
||||
|
||||
return svc;
|
||||
services_.emplace_back(std::move(service));
|
||||
}
|
||||
|
||||
Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
|
||||
|
@ -677,8 +668,7 @@ Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>&
|
|||
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
|
||||
|
||||
exec_count_++;
|
||||
std::string name = android::base::StringPrintf("exec %d (%s)", exec_count_,
|
||||
str_args[0].c_str());
|
||||
std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
|
||||
unsigned flags = SVC_EXEC | SVC_ONESHOT;
|
||||
|
||||
std::string seclabel = "";
|
||||
|
@ -770,8 +760,7 @@ void ServiceManager::ForEachServiceWithFlags(unsigned matchflags,
|
|||
}
|
||||
}
|
||||
|
||||
void ServiceManager::RemoveService(const Service& svc)
|
||||
{
|
||||
void ServiceManager::RemoveService(const Service& svc) {
|
||||
auto svc_it = std::find_if(services_.begin(), services_.end(),
|
||||
[&svc] (const std::unique_ptr<Service>& s) {
|
||||
return svc.name() == s->name();
|
||||
|
@ -783,8 +772,44 @@ void ServiceManager::RemoveService(const Service& svc)
|
|||
services_.erase(svc_it);
|
||||
}
|
||||
|
||||
bool ServiceManager::IsValidName(const std::string& name) const
|
||||
{
|
||||
void ServiceManager::DumpState() const {
|
||||
for (const auto& s : services_) {
|
||||
s->DumpState();
|
||||
}
|
||||
INFO("\n");
|
||||
}
|
||||
|
||||
bool ServiceParser::ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) {
|
||||
if (args.size() < 3) {
|
||||
*err = "services must have a name and a program";
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& name = args[1];
|
||||
if (!IsValidName(name)) {
|
||||
*err = StringPrintf("invalid service name '%s'", name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> str_args(args.begin() + 2, args.end());
|
||||
service_ = std::make_unique<Service>(name, "default", str_args);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line,
|
||||
std::string* err) const {
|
||||
return service_ ? service_->HandleLine(args, err) : false;
|
||||
}
|
||||
|
||||
void ServiceParser::EndSection() {
|
||||
if (service_) {
|
||||
ServiceManager::GetInstance().AddService(std::move(service_));
|
||||
}
|
||||
}
|
||||
|
||||
bool ServiceParser::IsValidName(const std::string& name) const {
|
||||
if (name.size() > 16) {
|
||||
return false;
|
||||
}
|
||||
|
@ -795,11 +820,3 @@ bool ServiceManager::IsValidName(const std::string& name) const
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServiceManager::DumpState() const
|
||||
{
|
||||
for (const auto& s : services_) {
|
||||
s->DumpState();
|
||||
}
|
||||
INFO("\n");
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <vector>
|
||||
|
||||
#include "action.h"
|
||||
#include "init_parser.h"
|
||||
#include "keyword_map.h"
|
||||
|
||||
#define SVC_DISABLED 0x001 // do not autostart with class
|
||||
#define SVC_ONESHOT 0x002 // do not restart on exit
|
||||
|
@ -73,7 +75,7 @@ public:
|
|||
unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
|
||||
const std::string& seclabel, const std::vector<std::string>& args);
|
||||
|
||||
bool HandleLine(int kw, const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleLine(const std::vector<std::string>& args, std::string* err);
|
||||
bool Start(const std::vector<std::string>& dynamic_args);
|
||||
bool Start();
|
||||
bool StartIfNotDisabled();
|
||||
|
@ -99,12 +101,31 @@ public:
|
|||
const std::vector<std::string>& args() const { return args_; }
|
||||
|
||||
private:
|
||||
using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
|
||||
std::string* err);
|
||||
class OptionHandlerMap;
|
||||
|
||||
void NotifyStateChange(const std::string& new_state) const;
|
||||
void StopOrReset(int how);
|
||||
void ZapStdio() const;
|
||||
void OpenConsole() const;
|
||||
void PublishSocket(const std::string& name, int fd) const;
|
||||
|
||||
bool HandleClass(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleConsole(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleCritical(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleGroup(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleSocket(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleUser(const std::vector<std::string>& args, std::string* err);
|
||||
bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
|
||||
|
||||
std::string name_;
|
||||
std::string classname_;
|
||||
|
||||
|
@ -141,9 +162,7 @@ class ServiceManager {
|
|||
public:
|
||||
static ServiceManager& GetInstance();
|
||||
|
||||
Service* AddNewService(const std::string& name, const std::string& classname,
|
||||
const std::vector<std::string>& args,
|
||||
std::string* err);
|
||||
void AddService(std::unique_ptr<Service> service);
|
||||
Service* MakeExecOneshotService(const std::vector<std::string>& args);
|
||||
Service* FindServiceByName(const std::string& name) const;
|
||||
Service* FindServiceByPid(pid_t pid) const;
|
||||
|
@ -155,13 +174,30 @@ public:
|
|||
void (*func)(Service* svc)) const;
|
||||
void RemoveService(const Service& svc);
|
||||
void DumpState() const;
|
||||
|
||||
private:
|
||||
ServiceManager();
|
||||
|
||||
bool IsValidName(const std::string& name) const;
|
||||
|
||||
static int exec_count_; // Every service needs a unique name.
|
||||
std::vector<std::unique_ptr<Service>> services_;
|
||||
};
|
||||
|
||||
class ServiceParser : public SectionParser {
|
||||
public:
|
||||
ServiceParser() : service_(nullptr) {
|
||||
}
|
||||
bool ParseSection(const std::vector<std::string>& args,
|
||||
std::string* err) override;
|
||||
bool ParseLineSection(const std::vector<std::string>& args,
|
||||
const std::string& filename, int line,
|
||||
std::string* err) const override;
|
||||
void EndSection() override;
|
||||
void EndFile(const std::string&) override {
|
||||
}
|
||||
private:
|
||||
bool IsValidName(const std::string& name) const;
|
||||
|
||||
std::unique_ptr<Service> service_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -233,7 +233,6 @@ int ueventd_parse_config_file(const char *fn)
|
|||
|
||||
data.push_back('\n'); // TODO: fix parse_config.
|
||||
parse_config(fn, data);
|
||||
dump_parser_state();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
#include "init.h"
|
||||
#include "log.h"
|
||||
#include "property_service.h"
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
|
@ -476,3 +477,73 @@ bool is_dir(const char* pathname) {
|
|||
}
|
||||
return S_ISDIR(info.st_mode);
|
||||
}
|
||||
|
||||
bool expand_props(const std::string& src, std::string* dst) {
|
||||
const char* src_ptr = src.c_str();
|
||||
|
||||
if (!dst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* - variables can either be $x.y or ${x.y}, in case they are only part
|
||||
* of the string.
|
||||
* - will accept $$ as a literal $.
|
||||
* - no nested property expansion, i.e. ${foo.${bar}} is not supported,
|
||||
* bad things will happen
|
||||
*/
|
||||
while (*src_ptr) {
|
||||
const char* c;
|
||||
|
||||
c = strchr(src_ptr, '$');
|
||||
if (!c) {
|
||||
dst->append(src_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
dst->append(src_ptr, c);
|
||||
c++;
|
||||
|
||||
if (*c == '$') {
|
||||
dst->push_back(*(c++));
|
||||
src_ptr = c;
|
||||
continue;
|
||||
} else if (*c == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string prop_name;
|
||||
if (*c == '{') {
|
||||
c++;
|
||||
const char* end = strchr(c, '}');
|
||||
if (!end) {
|
||||
// failed to find closing brace, abort.
|
||||
ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
|
||||
return false;
|
||||
}
|
||||
prop_name = std::string(c, end);
|
||||
c = end + 1;
|
||||
} else {
|
||||
prop_name = c;
|
||||
ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
|
||||
c);
|
||||
c += prop_name.size();
|
||||
}
|
||||
|
||||
if (prop_name.empty()) {
|
||||
ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
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.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
dst->append(prop_val);
|
||||
src_ptr = c;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -65,4 +65,5 @@ int restorecon(const char *pathname);
|
|||
int restorecon_recursive(const char *pathname);
|
||||
std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
|
||||
bool is_dir(const char* pathname);
|
||||
bool expand_props(const std::string& src, std::string* dst);
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue