diff --git a/init/Android.bp b/init/Android.bp index 9c1ed1522..6b8429960 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -128,6 +128,8 @@ cc_library_static { "selabel.cpp", "selinux.cpp", "service.cpp", + "service_list.cpp", + "service_parser.cpp", "service_utils.cpp", "sigchld_handler.cpp", "subcontext.cpp", @@ -260,6 +262,8 @@ cc_binary { "rlimit_parser.cpp", "tokenizer.cpp", "service.cpp", + "service_list.cpp", + "service_parser.cpp", "service_utils.cpp", "subcontext.cpp", "subcontext.proto", diff --git a/init/builtins.cpp b/init/builtins.cpp index d9b1b8530..f188ef910 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -73,6 +73,7 @@ #include "selabel.h" #include "selinux.h" #include "service.h" +#include "service_list.h" #include "subcontext.h" #include "util.h" diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp index cb861f391..1b4716fd1 100644 --- a/init/host_init_verifier.cpp +++ b/init/host_init_verifier.cpp @@ -36,6 +36,8 @@ #include "parser.h" #include "result.h" #include "service.h" +#include "service_list.h" +#include "service_parser.h" #define EXCLUDE_FS_CONFIG_STRUCTURES #include "generated_android_ids.h" diff --git a/init/init.cpp b/init/init.cpp index 0d3b99f8d..bb12a6e25 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -67,6 +67,8 @@ #include "security.h" #include "selabel.h" #include "selinux.h" +#include "service.h" +#include "service_parser.h" #include "sigchld_handler.h" #include "util.h" diff --git a/init/init.h b/init/init.h index 90ead0edb..198ec2a54 100644 --- a/init/init.h +++ b/init/init.h @@ -26,7 +26,7 @@ #include "action.h" #include "action_manager.h" #include "parser.h" -#include "service.h" +#include "service_list.h" namespace android { namespace init { diff --git a/init/init_test.cpp b/init/init_test.cpp index 18c2b38eb..1bcc5eff2 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -27,6 +27,8 @@ #include "keyword_map.h" #include "parser.h" #include "service.h" +#include "service_list.h" +#include "service_parser.h" #include "test_function_map.h" #include "util.h" diff --git a/init/reboot.cpp b/init/reboot.cpp index eaba3ccff..d9d885ce3 100644 --- a/init/reboot.cpp +++ b/init/reboot.cpp @@ -55,6 +55,7 @@ #include "property_service.h" #include "reboot_utils.h" #include "service.h" +#include "service_list.h" #include "sigchld_handler.h" #define PROC_SYSRQ "/proc/sysrq-trigger" diff --git a/init/service.cpp b/init/service.cpp index 4fe374c45..4ac4571f2 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -40,6 +40,7 @@ #include #include "rlimit_parser.h" +#include "service_list.h" #include "util.h" #if defined(__ANDROID__) @@ -1072,17 +1073,6 @@ void Service::StopOrReset(int how) { } } -ServiceList::ServiceList() {} - -ServiceList& ServiceList::GetInstance() { - static ServiceList instance; - return instance; -} - -void ServiceList::AddService(std::unique_ptr service) { - services_.emplace_back(std::move(service)); -} - std::unique_ptr Service::MakeTemporaryOneshotService(const std::vector& args) { // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... // SECLABEL can be a - to denote default @@ -1147,139 +1137,5 @@ std::unique_ptr Service::MakeTemporaryOneshotService(const std::vector< nullptr, str_args); } -// Shutdown services in the opposite order that they were started. -const std::vector ServiceList::services_in_shutdown_order() const { - std::vector shutdown_services; - for (const auto& service : services_) { - if (service->start_order() > 0) shutdown_services.emplace_back(service.get()); - } - std::sort(shutdown_services.begin(), shutdown_services.end(), - [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); }); - return shutdown_services; -} - -void ServiceList::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(); - }); - if (svc_it == services_.end()) { - return; - } - - services_.erase(svc_it); -} - -void ServiceList::DumpState() const { - for (const auto& s : services_) { - s->DumpState(); - } -} - -void ServiceList::MarkPostData() { - post_data_ = true; -} - -bool ServiceList::IsPostData() { - return post_data_; -} - -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().message(); - } - } - 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) { - return Error() << "services must have a name and a program"; - } - - const std::string& name = args[1]; - if (!IsValidName(name)) { - return Error() << "invalid service name '" << name << "'"; - } - - filename_ = filename; - - Subcontext* restart_action_subcontext = nullptr; - if (subcontexts_) { - for (auto& subcontext : *subcontexts_) { - if (StartsWith(filename, subcontext.path_prefix())) { - restart_action_subcontext = &subcontext; - break; - } - } - } - - std::vector str_args(args.begin() + 2, args.end()); - - if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) { - if (str_args[0] == "/sbin/watchdogd") { - str_args[0] = "/system/bin/watchdogd"; - } - } - - service_ = std::make_unique(name, restart_action_subcontext, str_args); - return {}; -} - -Result ServiceParser::ParseLineSection(std::vector&& args, int line) { - return service_ ? service_->ParseLine(std::move(args)) : Result{}; -} - -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() - << "'"; - } - - 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; - } - - service_list_->AddService(std::move(service_)); - } - - return {}; -} - -bool ServiceParser::IsValidName(const std::string& name) const { - // Property names can be any length, but may only contain certain characters. - // Property values can contain any characters, but may only be a certain length. - // (The latter restriction is needed because `start` and `stop` work by writing - // the service name to the "ctl.start" and "ctl.stop" properties.) - return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX; -} - } // namespace init } // namespace android diff --git a/init/service.h b/init/service.h index b4356c839..0bfc2e6aa 100644 --- a/init/service.h +++ b/init/service.h @@ -238,78 +238,6 @@ class Service { bool running_at_post_data_reset_ = false; }; -class ServiceList { - public: - static ServiceList& GetInstance(); - - // Exposed for testing - ServiceList(); - - void AddService(std::unique_ptr service); - void RemoveService(const Service& svc); - - template - Service* FindService(T value, F function = &Service::name) const { - auto svc = std::find_if(services_.begin(), services_.end(), - [&function, &value](const std::unique_ptr& s) { - return std::invoke(function, s) == value; - }); - if (svc != services_.end()) { - return svc->get(); - } - return nullptr; - } - - Service* FindInterface(const std::string& interface_name) { - for (const auto& svc : services_) { - if (svc->interfaces().count(interface_name) > 0) { - return svc.get(); - } - } - - return nullptr; - } - - void DumpState() const; - - auto begin() const { return services_.begin(); } - auto end() const { return services_.end(); } - const std::vector>& services() const { return services_; } - const std::vector services_in_shutdown_order() const; - - void MarkPostData(); - bool IsPostData(); - void MarkServicesUpdate(); - bool IsServicesUpdated() const { return services_update_finished_; } - void DelayService(const Service& service); - - private: - std::vector> services_; - - bool post_data_ = false; - bool services_update_finished_ = false; - std::vector delayed_service_names_; -}; - -class ServiceParser : public SectionParser { - public: - ServiceParser(ServiceList* service_list, std::vector* subcontexts) - : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {} - Result ParseSection(std::vector&& args, const std::string& filename, - 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; - - ServiceList* service_list_; - std::vector* subcontexts_; - std::unique_ptr service_; - std::string filename_; -}; - } // namespace init } // namespace android diff --git a/init/service_list.cpp b/init/service_list.cpp new file mode 100644 index 000000000..3a4818350 --- /dev/null +++ b/init/service_list.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 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 "service_list.h" + +#include + +namespace android { +namespace init { + +ServiceList::ServiceList() {} + +ServiceList& ServiceList::GetInstance() { + static ServiceList instance; + return instance; +} + +void ServiceList::AddService(std::unique_ptr service) { + services_.emplace_back(std::move(service)); +} + +// Shutdown services in the opposite order that they were started. +const std::vector ServiceList::services_in_shutdown_order() const { + std::vector shutdown_services; + for (const auto& service : services_) { + if (service->start_order() > 0) shutdown_services.emplace_back(service.get()); + } + std::sort(shutdown_services.begin(), shutdown_services.end(), + [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); }); + return shutdown_services; +} + +void ServiceList::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(); }); + if (svc_it == services_.end()) { + return; + } + + services_.erase(svc_it); +} + +void ServiceList::DumpState() const { + for (const auto& s : services_) { + s->DumpState(); + } +} + +void ServiceList::MarkPostData() { + post_data_ = true; +} + +bool ServiceList::IsPostData() { + return post_data_; +} + +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().message(); + } + } + 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()); +} + +} // namespace init +} // namespace android diff --git a/init/service_list.h b/init/service_list.h new file mode 100644 index 000000000..2136a217a --- /dev/null +++ b/init/service_list.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include +#include + +#include "service.h" + +namespace android { +namespace init { + +class ServiceList { + public: + static ServiceList& GetInstance(); + + // Exposed for testing + ServiceList(); + + void AddService(std::unique_ptr service); + void RemoveService(const Service& svc); + + template + Service* FindService(T value, F function = &Service::name) const { + auto svc = std::find_if(services_.begin(), services_.end(), + [&function, &value](const std::unique_ptr& s) { + return std::invoke(function, s) == value; + }); + if (svc != services_.end()) { + return svc->get(); + } + return nullptr; + } + + Service* FindInterface(const std::string& interface_name) { + for (const auto& svc : services_) { + if (svc->interfaces().count(interface_name) > 0) { + return svc.get(); + } + } + + return nullptr; + } + + void DumpState() const; + + auto begin() const { return services_.begin(); } + auto end() const { return services_.end(); } + const std::vector>& services() const { return services_; } + const std::vector services_in_shutdown_order() const; + + void MarkPostData(); + bool IsPostData(); + void MarkServicesUpdate(); + bool IsServicesUpdated() const { return services_update_finished_; } + void DelayService(const Service& service); + + private: + std::vector> services_; + + bool post_data_ = false; + bool services_update_finished_ = false; + std::vector delayed_service_names_; +}; + +} // namespace init +} // namespace android diff --git a/init/service_parser.cpp b/init/service_parser.cpp new file mode 100644 index 000000000..cdfaff3c5 --- /dev/null +++ b/init/service_parser.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019 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 "service_parser.h" + +#include + +#include "util.h" + +#if defined(__ANDROID__) +#include + +#include "selinux.h" +#else +#include "host_init_stubs.h" +#endif + +using android::base::StartsWith; + +namespace android { +namespace init { + +Result ServiceParser::ParseSection(std::vector&& args, + const std::string& filename, int line) { + if (args.size() < 3) { + return Error() << "services must have a name and a program"; + } + + const std::string& name = args[1]; + if (!IsValidName(name)) { + return Error() << "invalid service name '" << name << "'"; + } + + filename_ = filename; + + Subcontext* restart_action_subcontext = nullptr; + if (subcontexts_) { + for (auto& subcontext : *subcontexts_) { + if (StartsWith(filename, subcontext.path_prefix())) { + restart_action_subcontext = &subcontext; + break; + } + } + } + + std::vector str_args(args.begin() + 2, args.end()); + + if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) { + if (str_args[0] == "/sbin/watchdogd") { + str_args[0] = "/system/bin/watchdogd"; + } + } + + service_ = std::make_unique(name, restart_action_subcontext, str_args); + return {}; +} + +Result ServiceParser::ParseLineSection(std::vector&& args, int line) { + return service_ ? service_->ParseLine(std::move(args)) : Result{}; +} + +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() + << "'"; + } + + 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; + } + + service_list_->AddService(std::move(service_)); + } + + return {}; +} + +bool ServiceParser::IsValidName(const std::string& name) const { + // Property names can be any length, but may only contain certain characters. + // Property values can contain any characters, but may only be a certain length. + // (The latter restriction is needed because `start` and `stop` work by writing + // the service name to the "ctl.start" and "ctl.stop" properties.) + return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX; +} + +} // namespace init +} // namespace android diff --git a/init/service_parser.h b/init/service_parser.h new file mode 100644 index 000000000..8efe2ef46 --- /dev/null +++ b/init/service_parser.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include + +#include "parser.h" +#include "service.h" +#include "service_list.h" +#include "subcontext.h" + +namespace android { +namespace init { + +class ServiceParser : public SectionParser { + public: + ServiceParser(ServiceList* service_list, std::vector* subcontexts) + : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {} + Result ParseSection(std::vector&& args, const std::string& filename, + 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; + + ServiceList* service_list_; + std::vector* subcontexts_; + std::unique_ptr service_; + std::string filename_; +}; + +} // namespace init +} // namespace android diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp index 987b2f98c..c9a09cd89 100644 --- a/init/sigchld_handler.cpp +++ b/init/sigchld_handler.cpp @@ -30,6 +30,7 @@ #include "init.h" #include "service.h" +#include "service_list.h" using android::base::StringPrintf; using android::base::boot_clock;