Returns a service parse error on overrides across the treble boundary.
Also includes new --out_<partition> flags for
system,system_ext,product,vendor,odm
to allow host_init_verifier to work with a collection of init rc files.
Test: host_init_verifier --out_system=... --out_vendor=...
where vendor contains an init rc file that overrides a service
present in system. Observe parse failure and non-zero exit.
Bug: 163089173
Change-Id: I520fef613e0036df8a7d47a98d47405eaa969110
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index ef9a451..db127d3 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -25,6 +25,8 @@
#include <fstream>
#include <iostream>
#include <iterator>
+#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -51,6 +53,7 @@
using namespace std::literals;
+using android::base::EndsWith;
using android::base::ParseInt;
using android::base::ReadFileToString;
using android::base::Split;
@@ -61,6 +64,10 @@
static std::vector<std::string> passwd_files;
+// NOTE: Keep this in sync with the order used by init.cpp LoadBootScripts()
+static const std::vector<std::string> partition_search_order =
+ std::vector<std::string>({"system", "system_ext", "odm", "vendor", "product"});
+
static std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {
std::string passwd;
if (!ReadFileToString(passwd_file, &passwd)) {
@@ -148,13 +155,24 @@
#include "generated_stub_builtin_function_map.h"
void PrintUsage() {
- std::cout << "usage: host_init_verifier [options] <init rc file>\n"
- "\n"
- "Tests an init script for correctness\n"
- "\n"
- "-p FILE\tSearch this passwd file for users and groups\n"
- "--property_contexts=FILE\t Use this file for property_contexts\n"
- << std::endl;
+ fprintf(stdout, R"(usage: host_init_verifier [options]
+
+Tests init script(s) for correctness.
+
+Generic options:
+ -p FILE Search this passwd file for users and groups.
+ --property_contexts=FILE Use this file for property_contexts.
+
+Single script mode options:
+ [init rc file] Positional argument; test this init script.
+
+Multiple script mode options:
+ --out_system=DIR Path to the output product directory for the system partition.
+ --out_system_ext=DIR Path to the output product directory for the system_ext partition.
+ --out_odm=DIR Path to the output product directory for the odm partition.
+ --out_vendor=DIR Path to the output product directory for the vendor partition.
+ --out_product=DIR Path to the output product directory for the product partition.
+)");
}
Result<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {
@@ -203,12 +221,18 @@
android::base::SetMinimumLogSeverity(android::base::ERROR);
auto property_infos = std::vector<PropertyInfoEntry>();
+ std::map<std::string, std::string> partition_map;
while (true) {
static const char kPropertyContexts[] = "property-contexts=";
static const struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{kPropertyContexts, required_argument, nullptr, 0},
+ {"out_system", required_argument, nullptr, 0},
+ {"out_system_ext", required_argument, nullptr, 0},
+ {"out_odm", required_argument, nullptr, 0},
+ {"out_vendor", required_argument, nullptr, 0},
+ {"out_product", required_argument, nullptr, 0},
{nullptr, 0, nullptr, 0},
};
@@ -224,6 +248,16 @@
if (long_options[option_index].name == kPropertyContexts) {
HandlePropertyContexts(optarg, &property_infos);
}
+ for (const auto& p : partition_search_order) {
+ if (long_options[option_index].name == "out_" + p) {
+ if (partition_map.find(p) != partition_map.end()) {
+ PrintUsage();
+ return EXIT_FAILURE;
+ }
+ partition_map[p] =
+ EndsWith(optarg, "/") ? optarg : std::string(optarg) + "/";
+ }
+ }
break;
case 'h':
PrintUsage();
@@ -240,7 +274,9 @@
argc -= optind;
argv += optind;
- if (argc != 1) {
+ // If provided, use the partition map to check multiple init rc files.
+ // Otherwise, check a single init rc file.
+ if ((!partition_map.empty() && argc != 0) || (partition_map.empty() && argc != 1)) {
PrintUsage();
return EXIT_FAILURE;
}
@@ -262,24 +298,42 @@
property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());
+ if (!partition_map.empty()) {
+ std::vector<std::string> vendor_prefixes;
+ for (const auto& partition : {"vendor", "odm"}) {
+ if (partition_map.find(partition) != partition_map.end()) {
+ vendor_prefixes.push_back(partition_map.at(partition));
+ }
+ }
+ InitializeHostSubcontext(vendor_prefixes);
+ }
+
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(
- &sl, nullptr, *interface_inheritance_hierarchy_map));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+ parser.AddSectionParser("service",
+ std::make_unique<ServiceParser>(&sl, GetSubcontext(),
+ *interface_inheritance_hierarchy_map));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());
- if (!parser.ParseConfigFileInsecure(*argv)) {
- LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
- return EXIT_FAILURE;
+ if (!partition_map.empty()) {
+ for (const auto& p : partition_search_order) {
+ if (partition_map.find(p) != partition_map.end()) {
+ parser.ParseConfig(partition_map.at(p) + "etc/init");
+ }
+ }
+ } else {
+ if (!parser.ParseConfigFileInsecure(*argv)) {
+ LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
+ return EXIT_FAILURE;
+ }
}
size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
if (failures > 0) {
- LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures
- << " errors";
+ LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
diff --git a/init/service.cpp b/init/service.cpp
index 7b98392..766eb5d 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -154,6 +154,7 @@
.priority = 0},
namespaces_{.flags = namespace_flags},
seclabel_(seclabel),
+ subcontext_(subcontext_for_restart_commands),
onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0,
"onrestart", {}),
oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
diff --git a/init/service.h b/init/service.h
index bc5c90f..aee1e5d 100644
--- a/init/service.h
+++ b/init/service.h
@@ -137,6 +137,7 @@
flags_ &= ~SVC_ONESHOT;
}
}
+ Subcontext* subcontext() const { return subcontext_; }
private:
void NotifyStateChange(const std::string& new_state) const;
@@ -168,6 +169,7 @@
std::vector<FileDescriptor> files_;
std::vector<std::pair<std::string, std::string>> environment_vars_;
+ Subcontext* subcontext_;
Action onrestart_; // Commands to execute on restart.
std::vector<std::string> writepid_files_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 97621da..57c311a 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -657,6 +657,14 @@
<< "' with a config in APEX";
}
+ std::string context = service_->subcontext() ? service_->subcontext()->context() : "";
+ std::string old_context =
+ old_service->subcontext() ? old_service->subcontext()->context() : "";
+ if (context != old_context) {
+ return Error() << "service '" << service_->name() << "' overrides another service "
+ << "across the treble boundary.";
+ }
+
service_list_->RemoveService(*old_service);
old_service = nullptr;
}
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index dc2455e..f1fbffe 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -342,6 +342,9 @@
new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
}
}
+void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes) {
+ subcontext.reset(new Subcontext(vendor_prefixes, kVendorContext, /*host=*/true));
+}
Subcontext* GetSubcontext() {
return subcontext.get();
diff --git a/init/subcontext.h b/init/subcontext.h
index 788d3be..cb4138e 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,9 +36,11 @@
class Subcontext {
public:
- Subcontext(std::vector<std::string> path_prefixes, std::string context)
+ Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
: path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
- Fork();
+ if (!host) {
+ Fork();
+ }
}
Result<void> Execute(const std::vector<std::string>& args);
@@ -61,6 +63,7 @@
int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
void InitializeSubcontext();
+void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes);
Subcontext* GetSubcontext();
bool SubcontextChildReap(pid_t pid);
void SubcontextTerminate();