Loading init/README.md +7 −0 Original line number Diff line number Diff line Loading @@ -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 <username>` > Change to 'username' before exec'ing this service. Currently defaults to root. (??? probably should default to nobody) Loading init/builtins.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -1079,6 +1079,7 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) { } success &= parser.ParseConfigFile(c); } ServiceList::GetInstance().MarkServicesUpdate(); if (success) { return Success(); } else { Loading init/service.cpp +53 −0 Original line number Diff line number Diff line Loading @@ -765,6 +765,11 @@ Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) { return Success(); } Result<Success> Service::ParseUpdatable(std::vector<std::string>&& args) { updatable_ = true; return Success(); } class Service::OptionParserMap : public KeywordMap<OptionParser> { public: OptionParserMap() {} Loading Loading @@ -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}}, }; Loading @@ -834,6 +840,13 @@ Result<Success> Service::ParseLine(std::vector<std::string>&& args) { } Result<Success> 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) { Loading @@ -851,6 +864,13 @@ Result<Success> Service::ExecStart() { } Result<Success> 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. Loading Loading @@ -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<Success> ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename, int line) { if (args.size() < 3) { Loading @@ -1291,6 +1337,8 @@ Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args, return Error() << "invalid service name '" << name << "'"; } filename_ = filename; Subcontext* restart_action_subcontext = nullptr; if (subcontexts_) { for (auto& subcontext : *subcontexts_) { Loading Loading @@ -1326,6 +1374,11 @@ Result<Success> 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; } Loading init/service.h +13 −0 Original line number Diff line number Diff line Loading @@ -123,6 +123,7 @@ class Service { std::chrono::seconds restart_period() const { return restart_period_; } std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; } const std::vector<std::string>& args() const { return args_; } bool is_updatable() const { return updatable_; } private: using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args); Loading Loading @@ -170,6 +171,7 @@ class Service { Result<Success> ParseFile(std::vector<std::string>&& args); Result<Success> ParseUser(std::vector<std::string>&& args); Result<Success> ParseWritepid(std::vector<std::string>&& args); Result<Success> ParseUpdatable(std::vector<std::string>&& args); template <typename T> Result<Success> AddDescriptor(std::vector<std::string>&& args); Loading Loading @@ -235,6 +237,8 @@ class Service { std::chrono::seconds restart_period_ = 5s; std::optional<std::chrono::seconds> timeout_period_; bool updatable_ = false; std::vector<std::string> args_; std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_; Loading Loading @@ -279,8 +283,15 @@ class ServiceList { const std::vector<std::unique_ptr<Service>>& services() const { return services_; } const std::vector<Service*> services_in_shutdown_order() const; void MarkServicesUpdate(); bool IsServicesUpdated() const { return services_update_finished_; } void DelayService(const Service& service); private: std::vector<std::unique_ptr<Service>> services_; bool services_update_finished_ = false; std::vector<std::string> delayed_service_names_; }; class ServiceParser : public SectionParser { Loading @@ -291,6 +302,7 @@ class ServiceParser : public SectionParser { int line) override; Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override; Result<Success> EndSection() override; void EndFile() override { filename_ = ""; } private: bool IsValidName(const std::string& name) const; Loading @@ -298,6 +310,7 @@ class ServiceParser : public SectionParser { ServiceList* service_list_; std::vector<Subcontext>* subcontexts_; std::unique_ptr<Service> service_; std::string filename_; }; } // namespace init Loading Loading
init/README.md +7 −0 Original line number Diff line number Diff line Loading @@ -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 <username>` > Change to 'username' before exec'ing this service. Currently defaults to root. (??? probably should default to nobody) Loading
init/builtins.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -1079,6 +1079,7 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) { } success &= parser.ParseConfigFile(c); } ServiceList::GetInstance().MarkServicesUpdate(); if (success) { return Success(); } else { Loading
init/service.cpp +53 −0 Original line number Diff line number Diff line Loading @@ -765,6 +765,11 @@ Result<Success> Service::ParseWritepid(std::vector<std::string>&& args) { return Success(); } Result<Success> Service::ParseUpdatable(std::vector<std::string>&& args) { updatable_ = true; return Success(); } class Service::OptionParserMap : public KeywordMap<OptionParser> { public: OptionParserMap() {} Loading Loading @@ -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}}, }; Loading @@ -834,6 +840,13 @@ Result<Success> Service::ParseLine(std::vector<std::string>&& args) { } Result<Success> 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) { Loading @@ -851,6 +864,13 @@ Result<Success> Service::ExecStart() { } Result<Success> 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. Loading Loading @@ -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<Success> ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename, int line) { if (args.size() < 3) { Loading @@ -1291,6 +1337,8 @@ Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args, return Error() << "invalid service name '" << name << "'"; } filename_ = filename; Subcontext* restart_action_subcontext = nullptr; if (subcontexts_) { for (auto& subcontext : *subcontexts_) { Loading Loading @@ -1326,6 +1374,11 @@ Result<Success> 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; } Loading
init/service.h +13 −0 Original line number Diff line number Diff line Loading @@ -123,6 +123,7 @@ class Service { std::chrono::seconds restart_period() const { return restart_period_; } std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; } const std::vector<std::string>& args() const { return args_; } bool is_updatable() const { return updatable_; } private: using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args); Loading Loading @@ -170,6 +171,7 @@ class Service { Result<Success> ParseFile(std::vector<std::string>&& args); Result<Success> ParseUser(std::vector<std::string>&& args); Result<Success> ParseWritepid(std::vector<std::string>&& args); Result<Success> ParseUpdatable(std::vector<std::string>&& args); template <typename T> Result<Success> AddDescriptor(std::vector<std::string>&& args); Loading Loading @@ -235,6 +237,8 @@ class Service { std::chrono::seconds restart_period_ = 5s; std::optional<std::chrono::seconds> timeout_period_; bool updatable_ = false; std::vector<std::string> args_; std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_; Loading Loading @@ -279,8 +283,15 @@ class ServiceList { const std::vector<std::unique_ptr<Service>>& services() const { return services_; } const std::vector<Service*> services_in_shutdown_order() const; void MarkServicesUpdate(); bool IsServicesUpdated() const { return services_update_finished_; } void DelayService(const Service& service); private: std::vector<std::unique_ptr<Service>> services_; bool services_update_finished_ = false; std::vector<std::string> delayed_service_names_; }; class ServiceParser : public SectionParser { Loading @@ -291,6 +302,7 @@ class ServiceParser : public SectionParser { int line) override; Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override; Result<Success> EndSection() override; void EndFile() override { filename_ = ""; } private: bool IsValidName(const std::string& name) const; Loading @@ -298,6 +310,7 @@ class ServiceParser : public SectionParser { ServiceList* service_list_; std::vector<Subcontext>* subcontexts_; std::unique_ptr<Service> service_; std::string filename_; }; } // namespace init Loading