diff --git a/init/Android.bp b/init/Android.bp index 58f4a9eff..6d36945aa 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -53,6 +53,7 @@ init_common_sources = [ "util.cpp", ] init_device_sources = [ + "apex_init_util.cpp", "block_dev_initializer.cpp", "bootchart.cpp", "builtins.cpp", diff --git a/init/action_manager.h b/init/action_manager.h index 2746a7c4a..68912a80d 100644 --- a/init/action_manager.h +++ b/init/action_manager.h @@ -49,6 +49,7 @@ class ActionManager { bool HasMoreCommands() const; void DumpState() const; void ClearQueue(); + auto size() const { return actions_.size(); } private: ActionManager(ActionManager const&) = delete; diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp new file mode 100644 index 000000000..de9f54762 --- /dev/null +++ b/init/apex_init_util.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 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 "apex_init_util.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#include "action_manager.h" +#include "init.h" +#include "parser.h" +#include "service_list.h" +#include "util.h" + +namespace android { +namespace init { + +static Result> CollectApexConfigs(const std::string& apex_name) { + glob_t glob_result; + std::string glob_pattern = apex_name.empty() ? + "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc"; + + const int ret = glob(glob_pattern.c_str(), GLOB_MARK, nullptr, &glob_result); + if (ret != 0 && ret != GLOB_NOMATCH) { + globfree(&glob_result); + return Error() << "Glob pattern '" << glob_pattern << "' failed"; + } + std::vector configs; + for (size_t i = 0; i < glob_result.gl_pathc; i++) { + std::string path = glob_result.gl_pathv[i]; + // Filter-out /apex/@ paths. The paths are bind-mounted to + // /apex/ paths, so unless we filter them out, we will parse the + // same file twice. + std::vector paths = android::base::Split(path, "/"); + if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) { + continue; + } + // Filter directories + if (path.back() == '/') { + continue; + } + configs.push_back(path); + } + globfree(&glob_result); + return configs; +} + +static Result ParseConfigs(const std::vector& configs) { + Parser parser = CreateApexConfigParser(ActionManager::GetInstance(), + ServiceList::GetInstance()); + bool success = true; + for (const auto& c : configs) { + success &= parser.ParseConfigFile(c); + } + + if (success) { + return {}; + } else { + return Error() << "Unable to parse apex configs"; + } +} + +Result ParseApexConfigs(const std::string& apex_name) { + Result> configs = CollectApexConfigs(apex_name); + if (!configs.ok()) { + return configs.error(); + } + + if (configs.value().empty()) { + return {}; + } + + auto filtered_configs = FilterVersionedConfigs(configs.value(), + android::base::GetIntProperty("ro.build.version.sdk", INT_MAX)); + return ParseConfigs(filtered_configs); +} + +} // namespace init +} // namespace android diff --git a/init/apex_init_util.h b/init/apex_init_util.h new file mode 100644 index 000000000..43f8ad5eb --- /dev/null +++ b/init/apex_init_util.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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 "result.h" + +namespace android { +namespace init { + +// Parse all config files for a given apex. +// If apex name is empty(""), config files for all apexes will be parsed. +Result ParseApexConfigs(const std::string& apex_name); + +} // namespace init +} // namespace android diff --git a/init/builtins.cpp b/init/builtins.cpp index 38f6f3939..c8cb25300 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -69,6 +69,7 @@ #include #include "action_manager.h" +#include "apex_init_util.h" #include "bootchart.h" #include "builtin_arguments.h" #include "fscrypt_init_extensions.h" @@ -1279,48 +1280,6 @@ static Result do_update_linker_config(const BuiltinArguments&) { return GenerateLinkerConfiguration(); } -static Result parse_apex_configs() { - glob_t glob_result; - static constexpr char glob_pattern[] = "/apex/*/etc/*rc"; - const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result); - if (ret != 0 && ret != GLOB_NOMATCH) { - globfree(&glob_result); - return Error() << "glob pattern '" << glob_pattern << "' failed"; - } - std::vector configs; - Parser parser = - CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance()); - for (size_t i = 0; i < glob_result.gl_pathc; i++) { - std::string path = glob_result.gl_pathv[i]; - // Filter-out /apex/@ paths. The paths are bind-mounted to - // /apex/ paths, so unless we filter them out, we will parse the - // same file twice. - std::vector paths = android::base::Split(path, "/"); - if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) { - continue; - } - // Filter directories - if (path.back() == '/') { - continue; - } - configs.push_back(path); - } - globfree(&glob_result); - - int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX); - - bool success = true; - for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) { - success &= parser.ParseConfigFile(c); - } - ServiceList::GetInstance().MarkServicesUpdate(); - if (success) { - return {}; - } else { - return Error() << "Could not parse apex configs"; - } -} - /* * Creates a directory under /data/misc/apexdata/ for each APEX. */ @@ -1351,7 +1310,8 @@ static Result do_perform_apex_config(const BuiltinArguments& args) { if (!create_dirs.ok()) { return create_dirs.error(); } - auto parse_configs = parse_apex_configs(); + auto parse_configs = ParseApexConfigs(/*apex_name=*/""); + ServiceList::GetInstance().MarkServicesUpdate(); if (!parse_configs.ok()) { return parse_configs.error(); } diff --git a/init/init.cpp b/init/init.cpp index 91922143b..129cf48fd 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -60,8 +60,10 @@ #include #include +#include "action.h" +#include "action_manager.h" #include "action_parser.h" -#include "builtins.h" +#include "apex_init_util.h" #include "epoll.h" #include "first_stage_init.h" #include "first_stage_mount.h" @@ -464,11 +466,27 @@ int StopServicesFromApex(const std::string& apex_name) { return still_running; } +void RemoveServiceAndActionFromApex(const std::string& apex_name) { + // Remove services and actions that match apex name + ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr& action) -> bool { + if (GetApexNameFromFileName(action->filename()) == apex_name) { + return true; + } + return false; + }); + ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr& s) -> bool { + if (GetApexNameFromFileName(s->filename()) == apex_name) { + return true; + } + return false; + }); +} + static Result DoUnloadApex(const std::string& apex_name) { if (StopServicesFromApex(apex_name) > 0) { return Error() << "Unable to stop all service from " << apex_name; } - // TODO(b/232114573) remove services and actions read from the apex + RemoveServiceAndActionFromApex(apex_name); SetProperty("init.apex." + apex_name, "unloaded"); return {}; } @@ -493,7 +511,10 @@ static Result UpdateApexLinkerConfig(const std::string& apex_name) { } static Result DoLoadApex(const std::string& apex_name) { - // TODO(b/232799709) read .rc files from the apex + if(auto result = ParseApexConfigs(apex_name); !result.ok()) { + return result.error(); + } + if (auto result = UpdateApexLinkerConfig(apex_name); !result.ok()) { return result.error(); } diff --git a/init/init.h b/init/init.h index dd44e95b6..063632a66 100644 --- a/init/init.h +++ b/init/init.h @@ -48,5 +48,7 @@ int SecondStageMain(int argc, char** argv); int StopServicesFromApex(const std::string& apex_name); +void RemoveServiceAndActionFromApex(const std::string& apex_name); + } // namespace init } // namespace android diff --git a/init/init_test.cpp b/init/init_test.cpp index e7218e8fe..529bbdf9d 100644 --- a/init/init_test.cpp +++ b/init/init_test.cpp @@ -229,21 +229,20 @@ void TestStopApexServices(const std::vector& service_names, bool ex ASSERT_NE(nullptr, service); EXPECT_EQ(expect_to_run, service->IsRunning()); } - ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr& s) -> bool { - if (std::find(service_names.begin(), service_names.end(), s->name()) - != service_names.end()) { - return true; - } - return false; - }); +} + +void TestRemoveApexService(const std::vector& service_names, bool exist) { + for (auto const& svc : service_names) { + auto service = ServiceList::GetInstance().FindService(svc); + ASSERT_EQ(exist, service != nullptr); + } } void InitApexService(const std::string_view& init_template) { std::string init_script = StringReplace(init_template, "$selabel", GetSecurityContext(), true); - ActionManager action_manager; - TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, + TestInitText(init_script, BuiltinFunctionMap(), {}, &ActionManager::GetInstance(), &ServiceList::GetInstance()); } @@ -251,8 +250,7 @@ void TestApexServicesInit(const std::vector& apex_services, const std::vector& other_apex_services, const std::vector non_apex_services) { auto num_svc = apex_services.size() + other_apex_services.size() + non_apex_services.size(); - ASSERT_EQ(static_cast(num_svc), std::distance(ServiceList::GetInstance().begin(), - ServiceList::GetInstance().end())); + ASSERT_EQ(num_svc, ServiceList::GetInstance().size()); TestStartApexServices(apex_services, "com.android.apex.test_service"); TestStartApexServices(other_apex_services, "com.android.other_apex.test_service"); @@ -263,8 +261,22 @@ void TestApexServicesInit(const std::vector& apex_services, TestStopApexServices(other_apex_services, /*expect_to_run=*/ true); TestStopApexServices(non_apex_services, /*expect_to_run=*/ true); - ASSERT_EQ(0, std::distance(ServiceList::GetInstance().begin(), - ServiceList::GetInstance().end())); + RemoveServiceAndActionFromApex("com.android.apex.test_service"); + ASSERT_EQ(other_apex_services.size() + non_apex_services.size(), + ServiceList::GetInstance().size()); + + // TODO(b/244232142): Add test to check if actions are removed + TestRemoveApexService(apex_services, /*exist*/ false); + TestRemoveApexService(other_apex_services, /*exist*/ true); + TestRemoveApexService(non_apex_services, /*exist*/ true); + + ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr& s) -> bool { + return true; + }); + + ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr& s) -> bool { + return true; + }); } TEST(init, StopServiceByApexName) { diff --git a/init/parser.cpp b/init/parser.cpp index abc201742..0a388dbf8 100644 --- a/init/parser.cpp +++ b/init/parser.cpp @@ -156,58 +156,6 @@ bool Parser::ParseConfigFile(const std::string& path) { return true; } -std::vector Parser::FilterVersionedConfigs(const std::vector& configs, - int active_sdk) { - std::vector filtered_configs; - - std::map> script_map; - for (const auto& c : configs) { - int sdk = 0; - const std::vector parts = android::base::Split(c, "."); - std::string base; - if (parts.size() < 2) { - continue; - } - - // parts[size()-1], aka the suffix, should be "rc" or "#rc" - // any other pattern gets discarded - - const auto& suffix = parts[parts.size() - 1]; - if (suffix == "rc") { - sdk = 0; - } else { - char trailer[9] = {0}; - int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer); - if (r != 2) { - continue; - } - if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) { - continue; - } - } - - if (sdk < 0 || sdk > active_sdk) { - continue; - } - - base = parts[0]; - for (unsigned int i = 1; i < parts.size() - 1; i++) { - base = base + "." + parts[i]; - } - - // is this preferred over what we already have - auto it = script_map.find(base); - if (it == script_map.end() || it->second.second < sdk) { - script_map[base] = std::make_pair(c, sdk); - } - } - - for (const auto& m : script_map) { - filtered_configs.push_back(m.second.first); - } - return filtered_configs; -} - bool Parser::ParseConfigDir(const std::string& path) { LOG(INFO) << "Parsing directory " << path << "..."; std::unique_ptr config_dir(opendir(path.c_str()), closedir); diff --git a/init/parser.h b/init/parser.h index 2f4108fd0..95b0cd76e 100644 --- a/init/parser.h +++ b/init/parser.h @@ -76,12 +76,6 @@ class Parser { void AddSectionParser(const std::string& name, std::unique_ptr parser); void AddSingleLineParser(const std::string& prefix, LineCallback callback); - // Compare all files */path.#rc and */path.rc with the same path prefix. - // Keep the one with the highest # that doesn't exceed the system's SDK. - // (.rc == .0rc for ranking purposes) - std::vector FilterVersionedConfigs(const std::vector& configs, - int active_sdk); - // Host init verifier check file permissions. bool ParseConfigFileInsecure(const std::string& path); diff --git a/init/service_list.h b/init/service_list.h index 33aaa5f8b..f858bc370 100644 --- a/init/service_list.h +++ b/init/service_list.h @@ -94,6 +94,8 @@ class ServiceList { services_update_finished_ = false; } + auto size() const { return services_.size(); } + private: std::vector> services_; diff --git a/init/util.cpp b/init/util.cpp index bfc3fb6cc..2d401425e 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -748,5 +749,57 @@ std::string GetApexNameFromFileName(const std::string& path) { return ""; } +std::vector FilterVersionedConfigs(const std::vector& configs, + int active_sdk) { + std::vector filtered_configs; + + std::map> script_map; + for (const auto& c : configs) { + int sdk = 0; + const std::vector parts = android::base::Split(c, "."); + std::string base; + if (parts.size() < 2) { + continue; + } + + // parts[size()-1], aka the suffix, should be "rc" or "#rc" + // any other pattern gets discarded + + const auto& suffix = parts[parts.size() - 1]; + if (suffix == "rc") { + sdk = 0; + } else { + char trailer[9] = {0}; + int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer); + if (r != 2) { + continue; + } + if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) { + continue; + } + } + + if (sdk < 0 || sdk > active_sdk) { + continue; + } + + base = parts[0]; + for (unsigned int i = 1; i < parts.size() - 1; i++) { + base = base + "." + parts[i]; + } + + // is this preferred over what we already have + auto it = script_map.find(base); + if (it == script_map.end() || it->second.second < sdk) { + script_map[base] = std::make_pair(c, sdk); + } + } + + for (const auto& m : script_map) { + filtered_configs.push_back(m.second.first); + } + return filtered_configs; +} + } // namespace init } // namespace android diff --git a/init/util.h b/init/util.h index daec470f9..0181bf073 100644 --- a/init/util.h +++ b/init/util.h @@ -109,5 +109,11 @@ bool IsMicrodroid(); bool Has32BitAbi(); std::string GetApexNameFromFileName(const std::string& path); + +// Compare all files */path.#rc and */path.rc with the same path prefix. +// Keep the one with the highest # that doesn't exceed the system's SDK. +// (.rc == .0rc for ranking purposes) +std::vector FilterVersionedConfigs(const std::vector& configs, + int active_sdk); } // namespace init } // namespace android