Remove and add service and action during apex load and unload
Bug: 232114573
Test: atest CtsInitTestCases ApexTestCases
Change-Id: I1f91ada1d44ac2380e2faf8fc25e0a521cc1a826
diff --git a/init/Android.bp b/init/Android.bp
index 58f4a9e..6d36945 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -53,6 +53,7 @@
"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 2746a7c..68912a8 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -49,6 +49,7 @@
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 0000000..de9f547
--- /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 <glob.h>
+
+#include <map>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "action_manager.h"
+#include "init.h"
+#include "parser.h"
+#include "service_list.h"
+#include "util.h"
+
+namespace android {
+namespace init {
+
+static Result<std::vector<std::string>> 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<std::string> configs;
+ for (size_t i = 0; i < glob_result.gl_pathc; i++) {
+ std::string path = glob_result.gl_pathv[i];
+ // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
+ // /apex/<name> paths, so unless we filter them out, we will parse the
+ // same file twice.
+ std::vector<std::string> 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<void> ParseConfigs(const std::vector<std::string>& 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<void> ParseApexConfigs(const std::string& apex_name) {
+ Result<std::vector<std::string>> 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 0000000..43f8ad5
--- /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 <string>
+#include <vector>
+
+#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<void> ParseApexConfigs(const std::string& apex_name);
+
+} // namespace init
+} // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 38f6f39..c8cb253 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -69,6 +69,7 @@
#include <system/thread_defs.h>
#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 @@
return GenerateLinkerConfiguration();
}
-static Result<void> 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<std::string> 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/<name>@<ver> paths. The paths are bind-mounted to
- // /apex/<name> paths, so unless we filter them out, we will parse the
- // same file twice.
- std::vector<std::string> 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 @@
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 9192214..129cf48 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -60,8 +60,10 @@
#include <selinux/android.h>
#include <unwindstack/AndroidUnwinder.h>
+#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 @@
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>& action) -> bool {
+ if (GetApexNameFromFileName(action->filename()) == apex_name) {
+ return true;
+ }
+ return false;
+ });
+ ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr<Service>& s) -> bool {
+ if (GetApexNameFromFileName(s->filename()) == apex_name) {
+ return true;
+ }
+ return false;
+ });
+}
+
static Result<void> 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<void> 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 dd44e95..063632a 100644
--- a/init/init.h
+++ b/init/init.h
@@ -48,5 +48,7 @@
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 e7218e8..529bbdf 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -229,21 +229,20 @@
ASSERT_NE(nullptr, service);
EXPECT_EQ(expect_to_run, service->IsRunning());
}
- ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr<Service>& 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<std::string>& 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 @@
const std::vector<std::string>& other_apex_services,
const std::vector<std::string> non_apex_services) {
auto num_svc = apex_services.size() + other_apex_services.size() + non_apex_services.size();
- ASSERT_EQ(static_cast<long>(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 @@
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<Service>& s) -> bool {
+ return true;
+ });
+
+ ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& s) -> bool {
+ return true;
+ });
}
TEST(init, StopServiceByApexName) {
diff --git a/init/parser.cpp b/init/parser.cpp
index abc2017..0a388db 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -156,58 +156,6 @@
return true;
}
-std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
- int active_sdk) {
- std::vector<std::string> filtered_configs;
-
- std::map<std::string, std::pair<std::string, int>> script_map;
- for (const auto& c : configs) {
- int sdk = 0;
- const std::vector<std::string> 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<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
diff --git a/init/parser.h b/init/parser.h
index 2f4108f..95b0cd7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -76,12 +76,6 @@
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> 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<std::string> FilterVersionedConfigs(const std::vector<std::string>& 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 33aaa5f..f858bc3 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -94,6 +94,8 @@
services_update_finished_ = false;
}
+ auto size() const { return services_.size(); }
+
private:
std::vector<std::unique_ptr<Service>> services_;
diff --git a/init/util.cpp b/init/util.cpp
index bfc3fb6..2d40142 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,6 +30,7 @@
#include <time.h>
#include <unistd.h>
+#include <map>
#include <thread>
#include <android-base/file.h>
@@ -748,5 +749,57 @@
return "";
}
+std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk) {
+ std::vector<std::string> filtered_configs;
+
+ std::map<std::string, std::pair<std::string, int>> script_map;
+ for (const auto& c : configs) {
+ int sdk = 0;
+ const std::vector<std::string> 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 daec470..0181bf0 100644
--- a/init/util.h
+++ b/init/util.h
@@ -109,5 +109,11 @@
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<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+ int active_sdk);
} // namespace init
} // namespace android