From 80aa44704cd1e4bf6f4255fae9842beeebc2621c Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Mon, 12 Nov 2018 12:08:41 +0900 Subject: [PATCH] Add support for updatable services A service with 'updatable' option can be overriden by the same service definition in APEXes. /system/etc/init/foo.rc: service foo /system/bin/foo updatable /apex/myapex/etc/init.rc: service foo /apex/myapex/bin/foo override Overriding a non-updatable (i.e. without updatable option) service from APEXes is prohibited. When an updatable service is started before APEXes are all activated, the execution is delayed until when the APEXes are all activated. Bug: 117403679 Test: m apex.test; adb push /data/apex; adb reboot adb shell, then lsof -p $(pidof surfaceflinger) shows that the process is executing /apex/com.android.example.apex@1/bin/surfaceflinger instead of /system/bin/surfaceflinger Change-Id: I8a57b8e7f6da81b4d2843e261a9a935dd279067c --- init/README.md | 7 +++++++ init/builtins.cpp | 1 + init/service.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++ init/service.h | 13 ++++++++++++ 4 files changed, 74 insertions(+) diff --git a/init/README.md b/init/README.md index 4b21e32ea..5c07ac1ed 100644 --- a/init/README.md +++ b/init/README.md @@ -332,6 +332,13 @@ runs the service. This is particularly useful for creating a periodic service combined with the restart_period option described above. +`updatable` +> Mark that the service can be overridden (via the 'override' option) later in + the boot sequence by APEXes. When a service with updatable option is started + before APEXes are all activated, the execution is delayed until the activation + is finished. A service that is not marked as updatable cannot be overridden by + APEXes. + `user ` > Change to 'username' before exec'ing this service. Currently defaults to root. (??? probably should default to nobody) diff --git a/init/builtins.cpp b/init/builtins.cpp index 8660d2a99..5676f9206 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -1079,6 +1079,7 @@ static Result do_parse_apex_configs(const BuiltinArguments& args) { } success &= parser.ParseConfigFile(c); } + ServiceList::GetInstance().MarkServicesUpdate(); if (success) { return Success(); } else { diff --git a/init/service.cpp b/init/service.cpp index 1bda7ecfb..5aa376450 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -765,6 +765,11 @@ Result Service::ParseWritepid(std::vector&& args) { return Success(); } +Result Service::ParseUpdatable(std::vector&& args) { + updatable_ = true; + return Success(); +} + class Service::OptionParserMap : public KeywordMap { public: OptionParserMap() {} @@ -817,6 +822,7 @@ const Service::OptionParserMap::Map& Service::OptionParserMap::map() const { {"socket", {3, 6, &Service::ParseSocket}}, {"timeout_period", {1, 1, &Service::ParseTimeoutPeriod}}, + {"updatable", {0, 0, &Service::ParseUpdatable}}, {"user", {1, 1, &Service::ParseUser}}, {"writepid", {1, kMax, &Service::ParseWritepid}}, }; @@ -834,6 +840,13 @@ Result Service::ParseLine(std::vector&& args) { } Result Service::ExecStart() { + if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) { + // Don't delay the service for ExecStart() as the semantic is that + // the caller might depend on the side effect of the execution. + return Error() << "Cannot start an updatable service '" << name_ + << "' before configs from APEXes are all loaded"; + } + flags_ |= SVC_ONESHOT; if (auto result = Start(); !result) { @@ -851,6 +864,13 @@ Result Service::ExecStart() { } Result Service::Start() { + if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) { + ServiceList::GetInstance().DelayService(*this); + return Error() << "Cannot start an updatable service '" << name_ + << "' before configs from APEXes are all loaded. " + << "Queued for execution."; + } + bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET)); // 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. @@ -1280,6 +1300,32 @@ void ServiceList::DumpState() const { } } +void ServiceList::MarkServicesUpdate() { + services_update_finished_ = true; + + // start the delayed services + for (const auto& name : delayed_service_names_) { + Service* service = FindService(name); + if (service == nullptr) { + LOG(ERROR) << "delayed service '" << name << "' could not be found."; + continue; + } + if (auto result = service->Start(); !result) { + LOG(ERROR) << result.error_string(); + } + } + delayed_service_names_.clear(); +} + +void ServiceList::DelayService(const Service& service) { + if (services_update_finished_) { + LOG(ERROR) << "Cannot delay the start of service '" << service.name() + << "' because all services are already updated. Ignoring."; + return; + } + delayed_service_names_.emplace_back(service.name()); +} + Result ServiceParser::ParseSection(std::vector&& args, const std::string& filename, int line) { if (args.size() < 3) { @@ -1291,6 +1337,8 @@ Result ServiceParser::ParseSection(std::vector&& args, return Error() << "invalid service name '" << name << "'"; } + filename_ = filename; + Subcontext* restart_action_subcontext = nullptr; if (subcontexts_) { for (auto& subcontext : *subcontexts_) { @@ -1326,6 +1374,11 @@ Result ServiceParser::EndSection() { << "'"; } + if (StartsWith(filename_, "/apex/") && !old_service->is_updatable()) { + return Error() << "cannot update a non-updatable service '" << service_->name() + << "' with a config in APEX"; + } + service_list_->RemoveService(*old_service); old_service = nullptr; } diff --git a/init/service.h b/init/service.h index 49b09ceca..56e75b0bf 100644 --- a/init/service.h +++ b/init/service.h @@ -123,6 +123,7 @@ class Service { std::chrono::seconds restart_period() const { return restart_period_; } std::optional timeout_period() const { return timeout_period_; } const std::vector& args() const { return args_; } + bool is_updatable() const { return updatable_; } private: using OptionParser = Result (Service::*)(std::vector&& args); @@ -170,6 +171,7 @@ class Service { Result ParseFile(std::vector&& args); Result ParseUser(std::vector&& args); Result ParseWritepid(std::vector&& args); + Result ParseUpdatable(std::vector&& args); template Result AddDescriptor(std::vector&& args); @@ -235,6 +237,8 @@ class Service { std::chrono::seconds restart_period_ = 5s; std::optional timeout_period_; + bool updatable_ = false; + std::vector args_; std::vector> reap_callbacks_; @@ -279,8 +283,15 @@ class ServiceList { const std::vector>& services() const { return services_; } const std::vector services_in_shutdown_order() const; + void MarkServicesUpdate(); + bool IsServicesUpdated() const { return services_update_finished_; } + void DelayService(const Service& service); + private: std::vector> services_; + + bool services_update_finished_ = false; + std::vector delayed_service_names_; }; class ServiceParser : public SectionParser { @@ -291,6 +302,7 @@ class ServiceParser : public SectionParser { int line) override; Result ParseLineSection(std::vector&& args, int line) override; Result EndSection() override; + void EndFile() override { filename_ = ""; } private: bool IsValidName(const std::string& name) const; @@ -298,6 +310,7 @@ class ServiceParser : public SectionParser { ServiceList* service_list_; std::vector* subcontexts_; std::unique_ptr service_; + std::string filename_; }; } // namespace init