diff --git a/init/Android.mk b/init/Android.mk index 58bff5861..76709513c 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -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 \ diff --git a/init/action.cpp b/init/action.cpp index dd366d3ae..c6cbc2ed6 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -21,46 +21,27 @@ #include #include +#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& args), - const std::vector& 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& args); - const std::vector args_; - const std::string filename_; - int line_; -}; - -Action::Command::Command(int (*f)(const std::vector& args), - const std::vector& args, - const std::string& filename, - int line) : - func_(f), args_(args), filename_(filename), line_(line) -{ +Command::Command(BuiltinFunction f, const std::vector& 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 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& args), +const KeywordMap* Action::function_map_ = nullptr; + +bool Action::AddCommand(const std::vector& 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& 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& args, std::string* err) -{ +bool Action::InitTriggers(const std::vector& args, std::string* err) { const static std::string prop_str("property:"); for (std::size_t i = 0; i < args.size(); ++i) { if (i % 2) { @@ -179,21 +178,26 @@ bool Action::InitTriggers(const std::vector& args, std::string* err return true; } -bool Action::InitSingleTrigger(const std::string& trigger) -{ +bool Action::InitSingleTrigger(const std::string& trigger) { std::vector 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) { + auto old_action_it = + std::find_if(actions_.begin(), actions_.end(), + [&action] (std::unique_ptr& 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(trigger)); } void ActionManager::QueuePropertyTrigger(const std::string& name, - const std::string& value) -{ + const std::string& value) { trigger_queue_.push(std::make_unique(name, value)); } -void ActionManager::QueueAllPropertyTriggers() -{ +void ActionManager::QueueAllPropertyTriggers() { QueuePropertyTrigger("", ""); } -void ActionManager::QueueBuiltinAction(int (*func)(const std::vector& args), - const std::string& name) -{ - Action* act = new Action(); +void ActionManager::QueueBuiltinAction(BuiltinFunction func, + const std::string& name) { + auto action = std::make_unique(true); std::vector 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(act)); + trigger_queue_.push(std::make_unique(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& 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& 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& args, + std::string* err) { + std::vector triggers(args.begin() + 1, args.end()); + if (triggers.size() < 1) { + *err = "actions must have a trigger"; + return false; + } + + auto action = std::make_unique(false); + if (!action->InitTriggers(triggers, err)) { + return false; + } + + action_ = std::move(action); + return true; +} + +bool ActionParser::ParseLineSection(const std::vector& 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_)); + } +} diff --git a/init/action.h b/init/action.h index 5088c710f..6dee2d0a5 100644 --- a/init/action.h +++ b/init/action.h @@ -22,13 +22,36 @@ #include #include +#include "builtins.h" +#include "init_parser.h" +#include "keyword_map.h" + +class Command { +public: + Command(BuiltinFunction f, const std::vector& args, + const std::string& filename, int line); + + int InvokeFunc() const; + std::string BuildCommandString() const; + std::string BuildSourceString() const; + +private: + BuiltinFunction func_; + std::vector args_; + std::string filename_; + int line_; +}; + class Action { public: - Action(); + Action(bool oneshot = false); - void AddCommand(int (*f)(const std::vector& args), + bool AddCommand(const std::vector& args, + const std::string& filename, int line, std::string* err); + void AddCommand(BuiltinFunction f, const std::vector& args, const std::string& filename = "", int line = 0); + void CombineAction(const Action& action); bool InitTriggers(const std::vector& 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* 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 property_triggers_; std::string event_trigger_; - std::vector commands_; + std::vector commands_; + bool oneshot_; + static const KeywordMap* 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); 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& args), - const std::string& name); + void QueueBuiltinAction(BuiltinFunction func, const std::string& name); void ExecuteOneCommand(); bool HasMoreCommands() const; - Action* AddNewAction(const std::vector& triggers, - std::string* err); void DumpState() const; private: @@ -80,10 +108,26 @@ private: ActionManager(ActionManager const&) = delete; void operator=(ActionManager const&) = delete; - std::vector actions_; + std::vector> actions_; std::queue> trigger_queue_; - std::vector current_executing_actions_; + std::queue current_executing_actions_; std::size_t current_command_; }; +class ActionParser : public SectionParser { +public: + ActionParser() : action_(nullptr) { + } + bool ParseSection(const std::vector& args, + std::string* err) override; + bool ParseLineSection(const std::vector& 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_; +}; + #endif diff --git a/init/bootchart.cpp b/init/bootchart.cpp index efaee1ca8..a768762b0 100644 --- a/init/bootchart.cpp +++ b/init/bootchart.cpp @@ -15,7 +15,6 @@ */ #include "bootchart.h" -#include "keywords.h" #include "log.h" #include "property_service.h" @@ -32,6 +31,7 @@ #include #include +#include #include diff --git a/init/bootchart.h b/init/bootchart.h index cf61d8350..47eda7abe 100644 --- a/init/bootchart.h +++ b/init/bootchart.h @@ -17,6 +17,10 @@ #ifndef _BOOTCHART_H #define _BOOTCHART_H +#include +#include + +int do_bootchart_init(const std::vector& args); void bootchart_sample(int* timeout); #endif /* _BOOTCHART_H */ diff --git a/init/builtins.cpp b/init/builtins.cpp index 97151c01b..3ffa2e82c 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "builtins.h" + #include #include #include @@ -44,10 +46,10 @@ #include #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& args) -{ +static int do_class_start(const std::vector& 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& args) return 0; } -int do_class_stop(const std::vector& args) -{ +static int do_class_stop(const std::vector& args) { ServiceManager::GetInstance(). ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); }); return 0; } -int do_class_reset(const std::vector& args) -{ +static int do_class_reset(const std::vector& args) { ServiceManager::GetInstance(). ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); }); return 0; } -int do_domainname(const std::vector& args) -{ +static int do_domainname(const std::vector& args) { return write_file("/proc/sys/kernel/domainname", args[1].c_str()); } -int do_enable(const std::vector& args) -{ +static int do_enable(const std::vector& args) { Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]); if (!svc) { return -1; @@ -199,7 +193,7 @@ int do_enable(const std::vector& args) return svc->Enable(); } -int do_exec(const std::vector& args) { +static int do_exec(const std::vector& args) { Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args); if (!svc) { return -1; @@ -211,23 +205,19 @@ int do_exec(const std::vector& args) { return 0; } -int do_export(const std::vector& args) -{ +static int do_export(const std::vector& args) { return add_environment(args[1].c_str(), args[2].c_str()); } -int do_hostname(const std::vector& args) -{ +static int do_hostname(const std::vector& args) { return write_file("/proc/sys/kernel/hostname", args[1].c_str()); } -int do_ifup(const std::vector& args) -{ +static int do_ifup(const std::vector& args) { return __ifupdown(args[1].c_str(), 1); } -int do_insmod(const std::vector& args) -{ +static int do_insmod(const std::vector& args) { std::string options; if (args.size() > 2) { @@ -241,8 +231,7 @@ int do_insmod(const std::vector& args) return insmod(args[1].c_str(), options.c_str()); } -int do_mkdir(const std::vector& args) -{ +static int do_mkdir(const std::vector& args) { mode_t mode = 0755; int ret; @@ -310,8 +299,7 @@ static struct { #define DATA_MNT_POINT "/data" /* mount */ -int do_mount(const std::vector& args) -{ +static int do_mount(const std::vector& 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 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& args) -{ +static int do_mount_all(const std::vector& 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& args) return ret; } -int do_swapon_all(const std::vector& args) -{ +static int do_swapon_all(const std::vector& args) { struct fstab *fstab; int ret; @@ -547,16 +529,14 @@ int do_swapon_all(const std::vector& args) return ret; } -int do_setprop(const std::vector& args) -{ +static int do_setprop(const std::vector& 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& args) -{ +static int do_setrlimit(const std::vector& args) { struct rlimit limit; int resource; resource = std::stoi(args[1]); @@ -565,8 +545,7 @@ int do_setrlimit(const std::vector& args) return setrlimit(resource, &limit); } -int do_start(const std::vector& args) -{ +static int do_start(const std::vector& 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& args) return 0; } -int do_stop(const std::vector& args) -{ +static int do_stop(const std::vector& 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& args) return 0; } -int do_restart(const std::vector& args) -{ +static int do_restart(const std::vector& 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& args) return 0; } -int do_powerctl(const std::vector& args) -{ +static int do_powerctl(const std::vector& 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& args) callback_on_ro_remount); } -int do_trigger(const std::vector& args) -{ +static int do_trigger(const std::vector& args) { ActionManager::GetInstance().QueueEventTrigger(args[1]); return 0; } -int do_symlink(const std::vector& args) -{ +static int do_symlink(const std::vector& args) { return symlink(args[1].c_str(), args[2].c_str()); } -int do_rm(const std::vector& args) -{ +static int do_rm(const std::vector& args) { return unlink(args[1].c_str()); } -int do_rmdir(const std::vector& args) -{ +static int do_rmdir(const std::vector& args) { return rmdir(args[1].c_str()); } -int do_sysclktz(const std::vector& args) -{ +static int do_sysclktz(const std::vector& 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& args) return 0; } -int do_verity_load_state(const std::vector& args) { +static int do_verity_load_state(const std::vector& 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& 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& args) { +static int do_verity_update_state(const std::vector& args) { return fs_mgr_update_verity_state(verity_update_property); } -int do_write(const std::vector& args) -{ +static int do_write(const std::vector& 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& args) -{ +static int do_copy(const std::vector& args) { char *buffer = NULL; int rc = 0; int fd1 = -1, fd2 = -1; @@ -705,9 +672,6 @@ int do_copy(const std::vector& 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& args) { +static int do_chown(const std::vector& 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& args) { +static int do_chmod(const std::vector& 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& args) { return 0; } -int do_restorecon(const std::vector& args) { +static int do_restorecon(const std::vector& 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& args) { return ret; } -int do_restorecon_recursive(const std::vector& args) { +static int do_restorecon_recursive(const std::vector& 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& args) { return ret; } -int do_loglevel(const std::vector& args) { - if (args.size() != 2) { - ERROR("loglevel: missing argument\n"); - return -EINVAL; - } - +static int do_loglevel(const std::vector& 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& args) { } int do_load_persist_props(const std::vector& 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& args) { - if (args.size() == 1) { - load_all_props(); - return 0; - } - return -1; +static int do_load_all_props(const std::vector& args) { + load_all_props(); + return 0; } -int do_wait(const std::vector& args) -{ +static int do_wait(const std::vector& 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& 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& args) -{ - if (args.size() != 2) { - return -1; - } - +static int do_installkey(const std::vector& 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& 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::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; +} diff --git a/init/builtins.h b/init/builtins.h new file mode 100644 index 000000000..53f4a714f --- /dev/null +++ b/init/builtins.h @@ -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 +#include +#include + +#include "keyword_map.h" + +using BuiltinFunction = int (*) (const std::vector& args); +class BuiltinFunctionMap : public KeywordMap { +public: + BuiltinFunctionMap() { + } +private: + Map& map() const override; +}; + +#endif diff --git a/init/import_parser.cpp b/init/import_parser.cpp new file mode 100644 index 000000000..e2a0f8330 --- /dev/null +++ b/init/import_parser.cpp @@ -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 +#include + +#include "log.h" +#include "util.h" + +bool ImportParser::ParseSection(const std::vector& 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)); + } + } +} diff --git a/init/import_parser.h b/init/import_parser.h new file mode 100644 index 000000000..0e91025f4 --- /dev/null +++ b/init/import_parser.h @@ -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 +#include + +class ImportParser : public SectionParser { +public: + ImportParser() { + } + bool ParseSection(const std::vector& args, + std::string* err) override; + bool ParseLineSection(const std::vector& 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 imports_; +}; + +#endif diff --git a/init/init.cpp b/init/init.cpp index c94a6fe64..ee1351d95 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -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()); + parser.AddSectionParser("on", std::make_unique()); + parser.AddSectionParser("import", std::make_unique()); + parser.ParseConfig("/init.rc"); ActionManager& am = ActionManager::GetInstance(); diff --git a/init/init_parser.cpp b/init/init_parser.cpp index 12f44f7cb..02b3985e1 100644 --- a/init/init_parser.cpp +++ b/init/init_parser.cpp @@ -14,387 +14,117 @@ * limitations under the License. */ -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include #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 -#include -#include -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& 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 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 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 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 config_dir(opendir(path), closedir); +bool Parser::ParseConfigDir(const std::string& path) { + INFO("Parsing directory %s...\n", path.c_str()); + std::unique_ptr 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 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(state->context); - int kw = lookup_keyword(args[0]); - std::vector 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 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& 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(state->context); - std::vector 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(); } diff --git a/init/init_parser.h b/init/init_parser.h index 709dca8b4..5ed30ad48 100644 --- a/init/init_parser.h +++ b/init/init_parser.h @@ -17,16 +17,39 @@ #ifndef _INIT_INIT_PARSER_H_ #define _INIT_INIT_PARSER_H_ +#include #include #include -#define INIT_PARSER_MAXARGS 64 +class SectionParser { +public: + virtual ~SectionParser() { + } + virtual bool ParseSection(const std::vector& args, + std::string* err) = 0; + virtual bool ParseLineSection(const std::vector& 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 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& 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> section_parsers_; +}; #endif diff --git a/init/keyword_map.h b/init/keyword_map.h new file mode 100644 index 000000000..dc2357b1b --- /dev/null +++ b/init/keyword_map.h @@ -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 +#include + +#include + +template +class KeywordMap { +public: + using FunctionInfo = std::tuple; + using Map = const std::map; + + 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::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_info); + } + +private: +//Map of keyword -> +//(minimum number of arguments, maximum number of arguments, function pointer) + virtual Map& map() const = 0; +}; + +#endif diff --git a/init/keywords.h b/init/keywords.h deleted file mode 100644 index 922feeeec..000000000 --- a/init/keywords.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef KEYWORD -#include -#include -int do_bootchart_init(const std::vector& args); -int do_class_start(const std::vector& args); -int do_class_stop(const std::vector& args); -int do_class_reset(const std::vector& args); -int do_domainname(const std::vector& args); -int do_enable(const std::vector& args); -int do_exec(const std::vector& args); -int do_export(const std::vector& args); -int do_hostname(const std::vector& args); -int do_ifup(const std::vector& args); -int do_insmod(const std::vector& args); -int do_installkey(const std::vector& args); -int do_mkdir(const std::vector& args); -int do_mount_all(const std::vector& args); -int do_mount(const std::vector& args); -int do_powerctl(const std::vector& args); -int do_restart(const std::vector& args); -int do_restorecon(const std::vector& args); -int do_restorecon_recursive(const std::vector& args); -int do_rm(const std::vector& args); -int do_rmdir(const std::vector& args); -int do_setprop(const std::vector& args); -int do_setrlimit(const std::vector& args); -int do_start(const std::vector& args); -int do_stop(const std::vector& args); -int do_swapon_all(const std::vector& args); -int do_trigger(const std::vector& args); -int do_symlink(const std::vector& args); -int do_sysclktz(const std::vector& args); -int do_write(const std::vector& args); -int do_copy(const std::vector& args); -int do_chown(const std::vector& args); -int do_chmod(const std::vector& args); -int do_loglevel(const std::vector& args); -int do_load_persist_props(const std::vector& args); -int do_load_all_props(const std::vector& args); -int do_verity_load_state(const std::vector& args); -int do_verity_update_state(const std::vector& args); -int do_wait(const std::vector& 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 diff --git a/init/service.cpp b/init/service.cpp index a370d25b9..a3c5ca41c 100644 --- a/init/service.cpp +++ b/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& args, std::string* err) { - std::vector str_args; +bool Service::HandleClass(const std::vector& args, std::string* err) { + classname_ = args[1]; + return true; +} - ioprio_class_ = IoSchedClass_NONE; +bool Service::HandleConsole(const std::vector& 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 \n"; - return false; - } else { - ioprio_pri_ = std::stoul(args[2], 0, 8); +bool Service::HandleCritical(const std::vector& 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& 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 <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& 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& 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 <0-7>"; + return false; + } + + return true; +} + +bool Service::HandleKeycodes(const std::vector& 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& args, std::string* err) { + flags_ |= SVC_ONESHOT; + return true; +} + +bool Service::HandleOnrestart(const std::vector& args, std::string* err) { + std::vector str_args(args.begin() + 1, args.end()); + onrestart_.AddCommand(str_args, "", 0, err); + return true; +} + +bool Service::HandleSeclabel(const std::vector& args, std::string* err) { + seclabel_ = args[1]; + return true; +} + +bool Service::HandleSetenv(const std::vector& 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& 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& args, std::string* err) { + uid_ = decode_uid(args[1].c_str()); + return true; +} + +bool Service::HandleWritepid(const std::vector& args, std::string* err) { + writepid_files_.assign(args.begin() + 1, args.end()); + return true; +} + +class Service::OptionHandlerMap : public KeywordMap { +public: + OptionHandlerMap() { + } +private: + Map& map() const override; +}; + +Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const { + constexpr std::size_t kMax = std::numeric_limits::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& 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& 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& 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& 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& 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* 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 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& args) { @@ -677,8 +668,7 @@ Service* ServiceManager::MakeExecOneshotService(const std::vector& std::vector 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& 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& 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 str_args(args.begin() + 2, args.end()); + service_ = std::make_unique(name, "default", str_args); + return true; +} + +bool ServiceParser::ParseLineSection(const std::vector& 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"); -} diff --git a/init/service.h b/init/service.h index 1ecf78a30..10eb736cb 100644 --- a/init/service.h +++ b/init/service.h @@ -26,6 +26,8 @@ #include #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& supp_gids, const std::string& seclabel, const std::vector& args); - bool HandleLine(int kw, const std::vector& args, std::string* err); + bool HandleLine(const std::vector& args, std::string* err); bool Start(const std::vector& dynamic_args); bool Start(); bool StartIfNotDisabled(); @@ -99,12 +101,31 @@ public: const std::vector& args() const { return args_; } private: + using OptionHandler = bool (Service::*) (const std::vector& 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& args, std::string* err); + bool HandleConsole(const std::vector& args, std::string* err); + bool HandleCritical(const std::vector& args, std::string* err); + bool HandleDisabled(const std::vector& args, std::string* err); + bool HandleGroup(const std::vector& args, std::string* err); + bool HandleIoprio(const std::vector& args, std::string* err); + bool HandleKeycodes(const std::vector& args, std::string* err); + bool HandleOneshot(const std::vector& args, std::string* err); + bool HandleOnrestart(const std::vector& args, std::string* err); + bool HandleSeclabel(const std::vector& args, std::string* err); + bool HandleSetenv(const std::vector& args, std::string* err); + bool HandleSocket(const std::vector& args, std::string* err); + bool HandleUser(const std::vector& args, std::string* err); + bool HandleWritepid(const std::vector& 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& args, - std::string* err); + void AddService(std::unique_ptr service); Service* MakeExecOneshotService(const std::vector& 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> services_; }; +class ServiceParser : public SectionParser { +public: + ServiceParser() : service_(nullptr) { + } + bool ParseSection(const std::vector& args, + std::string* err) override; + bool ParseLineSection(const std::vector& 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_; +}; + #endif diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp index 497c606dd..09f463874 100644 --- a/init/ueventd_parser.cpp +++ b/init/ueventd_parser.cpp @@ -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; } diff --git a/init/util.cpp b/init/util.cpp index f6131e3e3..1eb90e065 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -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; +} diff --git a/init/util.h b/init/util.h index f08cb8d17..a33209ee2 100644 --- a/init/util.h +++ b/init/util.h @@ -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