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
This commit is contained in:
parent
7e62aa568c
commit
f597fa5d1d
6 changed files with 89 additions and 18 deletions
|
@ -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 @@ using android::properties::PropertyInfoEntry;
|
|||
|
||||
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 @@ static Result<void> check_stub(const BuiltinArguments& args) {
|
|||
#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 @@ int main(int argc, char** argv) {
|
|||
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 @@ int main(int argc, char** argv) {
|
|||
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 @@ int main(int argc, char** argv) {
|
|||
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 @@ int main(int argc, char** argv) {
|
|||
|
||||
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;
|
||||
|
|
|
@ -154,6 +154,7 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
|
|||
.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),
|
||||
|
|
|
@ -137,6 +137,7 @@ class Service {
|
|||
flags_ &= ~SVC_ONESHOT;
|
||||
}
|
||||
}
|
||||
Subcontext* subcontext() const { return subcontext_; }
|
||||
|
||||
private:
|
||||
void NotifyStateChange(const std::string& new_state) const;
|
||||
|
@ -168,6 +169,7 @@ class Service {
|
|||
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_;
|
||||
|
|
|
@ -657,6 +657,14 @@ Result<void> ServiceParser::EndSection() {
|
|||
<< "' 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;
|
||||
}
|
||||
|
|
|
@ -342,6 +342,9 @@ void InitializeSubcontext() {
|
|||
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();
|
||||
|
|
|
@ -36,9 +36,11 @@ static constexpr const char kTestContext[] = "test-test-test";
|
|||
|
||||
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 @@ class Subcontext {
|
|||
|
||||
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();
|
||||
|
|
Loading…
Reference in a new issue