diff --git a/init/action.cpp b/init/action.cpp index 2617d009f..5fa6becf8 100644 --- a/init/action.cpp +++ b/init/action.cpp @@ -379,10 +379,12 @@ Result ActionParser::ParseLineSection(std::vector&& args, return action_ ? action_->AddCommand(std::move(args), line) : Success(); } -void ActionParser::EndSection() { +Result ActionParser::EndSection() { if (action_ && action_->NumCommands() > 0) { action_manager_->AddAction(std::move(action_)); } + + return Success(); } } // namespace init diff --git a/init/action.h b/init/action.h index cdfc6a053..1bfc6c753 100644 --- a/init/action.h +++ b/init/action.h @@ -130,7 +130,7 @@ class ActionParser : public SectionParser { Result ParseSection(std::vector&& args, const std::string& filename, int line) override; Result ParseLineSection(std::vector&& args, int line) override; - void EndSection() override; + Result EndSection() override; private: ActionManager* action_manager_; diff --git a/init/init_test.cpp b/init/init_test.cpp index 29a65abb9..268873c12 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -25,6 +25,7 @@ #include "import_parser.h" #include "keyword_map.h" #include "parser.h" +#include "service.h" #include "test_function_map.h" #include "util.h" @@ -34,12 +35,13 @@ namespace init { using ActionManagerCommand = std::function; void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map, - const std::vector& commands) { + const std::vector& commands, ServiceList* service_list) { ActionManager am; Action::set_function_map(&test_function_map); Parser parser; + parser.AddSectionParser("service", std::make_unique(service_list, nullptr)); parser.AddSectionParser("on", std::make_unique(&am, nullptr)); parser.AddSectionParser("import", std::make_unique(&parser)); @@ -55,11 +57,11 @@ void TestInit(const std::string& init_script_file, const TestFunctionMap& test_f } void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map, - const std::vector& commands) { + const std::vector& commands, ServiceList* service_list) { TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd)); - TestInit(tf.path, test_function_map, commands); + TestInit(tf.path, test_function_map, commands, service_list); } TEST(init, SimpleEventTrigger) { @@ -76,7 +78,8 @@ pass_test ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; - TestInitText(init_script, test_function_map, commands); + ServiceList service_list; + TestInitText(init_script, test_function_map, commands, &service_list); EXPECT_TRUE(expect_true); } @@ -104,7 +107,30 @@ execute_third ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; - TestInitText(init_script, test_function_map, commands); + ServiceList service_list; + TestInitText(init_script, test_function_map, commands, &service_list); +} + +TEST(init, OverrideService) { + std::string init_script = R"init( +service A something + class first + +service A something + class second + override + +)init"; + + ServiceList service_list; + TestInitText(init_script, TestFunctionMap(), {}, &service_list); + ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end())); + + auto service = service_list.begin()->get(); + ASSERT_NE(nullptr, service); + EXPECT_EQ(std::set({"second"}), service->classnames()); + EXPECT_EQ("A", service->name()); + EXPECT_TRUE(service->is_override()); } TEST(init, EventTriggerOrderMultipleFiles) { @@ -162,7 +188,9 @@ TEST(init, EventTriggerOrderMultipleFiles) { ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); }; std::vector commands{trigger_boot}; - TestInit(start.path, test_function_map, commands); + ServiceList service_list; + + TestInit(start.path, test_function_map, commands, &service_list); EXPECT_EQ(6, num_executed); } diff --git a/init/parser.cpp b/init/parser.cpp index 8a4e798d0..6ddb09f2b 100644 --- a/init/parser.cpp +++ b/init/parser.cpp @@ -50,12 +50,24 @@ void Parser::ParseData(const std::string& filename, const std::string& data) { state.nexttoken = 0; SectionParser* section_parser = nullptr; + int section_start_line = -1; std::vector args; + auto end_section = [&] { + if (section_parser == nullptr) return; + + if (auto result = section_parser->EndSection(); !result) { + LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error(); + } + + section_parser = nullptr; + section_start_line = -1; + }; + for (;;) { switch (next_token(&state)) { case T_EOF: - if (section_parser) section_parser->EndSection(); + end_section(); return; case T_NEWLINE: state.line++; @@ -65,18 +77,18 @@ void Parser::ParseData(const std::string& filename, const std::string& data) { // uevent. for (const auto& [prefix, callback] : line_callbacks_) { if (android::base::StartsWith(args[0], prefix.c_str())) { - if (section_parser) section_parser->EndSection(); + end_section(); if (auto result = callback(std::move(args)); !result) { LOG(ERROR) << filename << ": " << state.line << ": " << result.error(); } - section_parser = nullptr; break; } } if (section_parsers_.count(args[0])) { - if (section_parser) section_parser->EndSection(); + end_section(); section_parser = section_parsers_[args[0]].get(); + section_start_line = state.line; if (auto result = section_parser->ParseSection(std::move(args), filename, state.line); !result) { diff --git a/init/parser.h b/init/parser.h index 4ab24a4ee..110a4688a 100644 --- a/init/parser.h +++ b/init/parser.h @@ -26,24 +26,22 @@ // SectionParser is an interface that can parse a given 'section' in init. // -// You can implement up to 4 functions below, with ParseSection() being mandatory. -// The first two function return bool with false indicating a failure and has a std::string* err -// parameter into which an error string can be written. It will be reported along with the -// filename and line number of where the error occurred. +// You can implement up to 4 functions below, with ParseSection being mandatory. The first two +// functions return Result indicating if they have an error. It will be reported along +// with the filename and line number of where the error occurred. // -// 1) bool ParseSection(std::vector&& args, const std::string& filename, -// int line, std::string* err) +// 1) ParseSection // This function is called when a section is first encountered. // -// 2) bool ParseLineSection(std::vector&& args, int line, std::string* err) +// 2) ParseLineSection // This function is called on each subsequent line until the next section is encountered. // -// 3) bool EndSection() +// 3) EndSection // This function is called either when a new section is found or at the end of the file. // It indicates that parsing of the current section is complete and any relevant objects should // be committed. // -// 4) bool EndFile() +// 4) EndFile // This function is called at the end of the file. // It indicates that the parsing has completed and any relevant objects should be committed. @@ -56,7 +54,7 @@ class SectionParser { virtual Result ParseSection(std::vector&& args, const std::string& filename, int line) = 0; virtual Result ParseLineSection(std::vector&&, int) { return Success(); }; - virtual void EndSection(){}; + virtual Result EndSection() { return Success(); }; virtual void EndFile(){}; }; diff --git a/init/service.cpp b/init/service.cpp index 12acfc692..481df659c 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -530,6 +530,11 @@ Result Service::ParseOomScoreAdjust(const std::vector& arg return Success(); } +Result Service::ParseOverride(const std::vector& args) { + override_ = true; + return Success(); +} + Result Service::ParseMemcgSwappiness(const std::vector& args) { if (!ParseInt(args[1], &swappiness_, 0)) { return Error() << "swappiness value must be equal or greater than 0"; @@ -671,6 +676,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"keycodes", {1, kMax, &Service::ParseKeycodes}}, {"oneshot", {0, 0, &Service::ParseOneshot}}, {"onrestart", {1, kMax, &Service::ParseOnrestart}}, + {"override", {0, 0, &Service::ParseOverride}}, {"oom_score_adjust", {1, 1, &Service::ParseOomScoreAdjust}}, {"memcg.swappiness", @@ -1111,11 +1117,6 @@ Result ServiceParser::ParseSection(std::vector&& args, return Error() << "invalid service name '" << name << "'"; } - Service* old_service = service_list_->FindService(name); - if (old_service) { - return Error() << "ignored duplicate definition of service '" << name << "'"; - } - Subcontext* restart_action_subcontext = nullptr; if (subcontexts_) { for (auto& subcontext : *subcontexts_) { @@ -1135,10 +1136,23 @@ Result ServiceParser::ParseLineSection(std::vector&& args, return service_ ? service_->ParseLine(std::move(args)) : Success(); } -void ServiceParser::EndSection() { +Result ServiceParser::EndSection() { if (service_) { + Service* old_service = service_list_->FindService(service_->name()); + if (old_service) { + if (!service_->is_override()) { + return Error() << "ignored duplicate definition of service '" << service_->name() + << "'"; + } + + service_list_->RemoveService(*old_service); + old_service = nullptr; + } + service_list_->AddService(std::move(service_)); } + + return Success(); } bool ServiceParser::IsValidName(const std::string& name) const { diff --git a/init/service.h b/init/service.h index 593f78289..d46a41349 100644 --- a/init/service.h +++ b/init/service.h @@ -111,6 +111,7 @@ class Service { const std::set& interfaces() const { return interfaces_; } int priority() const { return priority_; } int oom_score_adjust() const { return oom_score_adjust_; } + bool is_override() const { return override_; } bool process_cgroup_empty() const { return process_cgroup_empty_; } unsigned long start_order() const { return start_order_; } const std::vector& args() const { return args_; } @@ -139,6 +140,7 @@ class Service { Result ParseOneshot(const std::vector& args); Result ParseOnrestart(const std::vector& args); Result ParseOomScoreAdjust(const std::vector& args); + Result ParseOverride(const std::vector& args); Result ParseMemcgLimitInBytes(const std::vector& args); Result ParseMemcgSoftLimitInBytes(const std::vector& args); Result ParseMemcgSwappiness(const std::vector& args); @@ -201,6 +203,8 @@ class Service { bool process_cgroup_empty_ = false; + bool override_ = false; + unsigned long start_order_; std::vector> rlimits_; @@ -248,7 +252,7 @@ class ServiceParser : public SectionParser { Result ParseSection(std::vector&& args, const std::string& filename, int line) override; Result ParseLineSection(std::vector&& args, int line) override; - void EndSection() override; + Result EndSection() override; private: bool IsValidName(const std::string& name) const; diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp index cd7adb433..f74c87849 100644 --- a/init/ueventd_parser.cpp +++ b/init/ueventd_parser.cpp @@ -132,8 +132,10 @@ Result SubsystemParser::ParseLineSection(std::vector&& arg return std::invoke(*parser, this, std::move(args)); } -void SubsystemParser::EndSection() { +Result SubsystemParser::EndSection() { subsystems_->emplace_back(std::move(subsystem_)); + + return Success(); } } // namespace init diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h index 18d1027b4..83684f39e 100644 --- a/init/ueventd_parser.h +++ b/init/ueventd_parser.h @@ -32,7 +32,7 @@ class SubsystemParser : public SectionParser { Result ParseSection(std::vector&& args, const std::string& filename, int line) override; Result ParseLineSection(std::vector&& args, int line) override; - void EndSection() override; + Result EndSection() override; private: Result ParseDevName(std::vector&& args);