From b7349902a945903f9e36a569051f5131beb0bc24 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Wed, 26 Aug 2015 11:43:36 -0700 Subject: [PATCH] init: Use classes for parsing and clean up memory allocations Create a Parser class that uses multiple SectionParser interfaces to handle parsing the different sections of an init rc. Create an ActionParser and ServiceParser that implement SectionParser and parse the sections corresponding to Action and Service classes. Remove the legacy keyword structure and replace it with std::map's that map keyword -> (minimum args, maximum args, function pointer) for Commands and Service Options. Create an ImportParser that implements SectionParser and handles the import 'section'. Clean up the unsafe memory handling of the Action class by using std::unique_ptr. Change-Id: Ic5ea5510cb956dbc3f78745a35096ca7d6da7085 --- init/Android.mk | 1 + init/action.cpp | 308 +++++++++++++------------- init/action.h | 70 ++++-- init/bootchart.cpp | 2 +- init/bootchart.h | 4 + init/builtins.cpp | 208 +++++++++--------- init/builtins.h | 35 +++ init/import_parser.cpp | 55 +++++ init/import_parser.h | 43 ++++ init/init.cpp | 10 +- init/init_parser.cpp | 469 +++++----------------------------------- init/init_parser.h | 35 ++- init/keyword_map.h | 77 +++++++ init/keywords.h | 109 ---------- init/service.cpp | 381 ++++++++++++++++---------------- init/service.h | 48 +++- init/ueventd_parser.cpp | 1 - init/util.cpp | 71 ++++++ init/util.h | 1 + 19 files changed, 944 insertions(+), 984 deletions(-) create mode 100644 init/builtins.h create mode 100644 init/import_parser.cpp create mode 100644 init/import_parser.h create mode 100644 init/keyword_map.h delete mode 100644 init/keywords.h 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