init: handle properties and imports for host init verifier

Allow specifying properties on the command line when running host init
verifier.  This is needed particularly for importing files that have a
property expansion in their path.

Handle the import statement on host, basing paths off of the out
directory of Android builds.

Bug: 36970783
Test: verify that bullhead imports the correct files and checks them
Change-Id: I4fe263016b3764a372708b559bc0c739b1b7e5e3
This commit is contained in:
Tom Cherry 2018-05-09 17:38:30 -07:00
parent fe86a1473f
commit 194b5d1da9
9 changed files with 151 additions and 37 deletions

View file

@ -236,7 +236,8 @@ cc_binary {
"epoll.cpp",
"keychords.cpp",
"import_parser.cpp",
"host_init_parser.cpp",
"host_import_parser.cpp",
"host_init_verifier.cpp",
"host_init_stubs.cpp",
"parser.cpp",
"rlimit_parser.cpp",

View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 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 "host_import_parser.h"
#include <android-base/strings.h>
using android::base::StartsWith;
namespace android {
namespace init {
Result<Success> HostImportParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
if (args.size() != 2) {
return Error() << "single argument needed for import\n";
}
auto import_path = args[1];
if (StartsWith(import_path, "/system") || StartsWith(import_path, "/product") ||
StartsWith(import_path, "/odm") || StartsWith(import_path, "/vendor")) {
import_path = out_dir_ + "/" + import_path;
} else {
import_path = out_dir_ + "/root/" + import_path;
}
return ImportParser::ParseSection({"import", import_path}, filename, line);
}
} // namespace init
} // namespace android

40
init/host_import_parser.h Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2018 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 "import_parser.h"
#include "parser.h"
namespace android {
namespace init {
class HostImportParser : public ImportParser {
public:
HostImportParser(const std::string& out_dir, Parser* parser)
: ImportParser(parser), out_dir_(out_dir) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
private:
std::string out_dir_;
};
} // namespace init
} // namespace android

View file

@ -16,6 +16,8 @@
#include "host_init_stubs.h"
#include <android-base/properties.h>
// unistd.h
int setgroups(size_t __size, const gid_t* __list) {
return 0;
@ -28,7 +30,11 @@ namespace init {
std::string default_console = "/dev/console";
// property_service.h
uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
uint32_t SetProperty(const std::string& key, const std::string& value) {
android::base::SetProperty(key, value);
return 0;
}
uint32_t (*property_set)(const std::string& name, const std::string& value) = SetProperty;
uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
std::string*) {
return 0;

View file

@ -16,15 +16,25 @@
#include <pwd.h>
#include <iostream>
#include <string>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include "action.h"
#include "action_manager.h"
#include "action_parser.h"
#include "host_import_parser.h"
#include "host_init_stubs.h"
#include "parser.h"
#include "result.h"
#include "service.h"
using namespace std::literals;
using android::base::Split;
// The host passwd file won't have the Android entries, so we fake success here.
passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
char dummy_buf[] = "dummy";
@ -49,10 +59,21 @@ static Result<Success> do_stub(const BuiltinArguments& args) {
int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
if (argc != 2) {
LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
android::base::SetMinimumLogSeverity(android::base::ERROR);
if (argc != 3) {
LOG(ERROR) << "Usage: " << argv[0] << " <out directory> <properties>";
return -1;
}
auto properties = Split(argv[2], ",");
for (const auto& property : properties) {
auto split_property = Split(property, "=");
if (split_property.size() != 2) {
continue;
}
property_set(split_property[0], split_property[1]);
}
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
ActionManager& am = ActionManager::GetInstance();
@ -60,17 +81,16 @@ int main(int argc, char** argv) {
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>(argv[1], &parser));
size_t num_errors = 0;
if (!parser.ParseConfig(argv[1], &num_errors)) {
LOG(ERROR) << "Failed to find script";
if (!parser.ParseConfig(argv[1] + "/root/init.rc"s)) {
LOG(ERROR) << "Failed to find root init.rc script";
return -1;
}
if (num_errors > 0) {
LOG(ERROR) << "Parse failed with " << num_errors << " errors";
if (parser.parse_error_count() > 0) {
LOG(ERROR) << "Init script parsing failed with " << parser.parse_error_count() << " errors";
return -1;
}
LOG(INFO) << "Parse success!";
return 0;
}

View file

@ -41,14 +41,15 @@ Result<Success> ImportParser::ParseSection(std::vector<std::string>&& args,
return Success();
}
Result<Success> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {
return Error() << "Unexpected line found after import statement";
}
void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
for (const auto& [import, line_num] : current_imports) {
if (!parser_->ParseConfig(import)) {
PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
<< "'";
}
parser_->ParseConfig(import);
}
}

View file

@ -30,6 +30,7 @@ class ImportParser : public SectionParser {
ImportParser(Parser* parser) : parser_(parser) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&&, int) override;
void EndFile() override;
private:

View file

@ -39,7 +39,7 @@ void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callbac
line_callbacks_.emplace_back(prefix, callback);
}
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
void Parser::ParseData(const std::string& filename, const std::string& data) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
@ -57,7 +57,7 @@ void Parser::ParseData(const std::string& filename, const std::string& data, siz
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
(*parse_errors)++;
parse_error_count_++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@ -81,7 +81,7 @@ void Parser::ParseData(const std::string& filename, const std::string& data, siz
end_section();
if (auto result = callback(std::move(args)); !result) {
(*parse_errors)++;
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@ -94,16 +94,20 @@ void Parser::ParseData(const std::string& filename, const std::string& data, siz
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
(*parse_errors)++;
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
(*parse_errors)++;
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
} else {
parse_error_count_++;
LOG(ERROR) << filename << ": " << state.line
<< ": Invalid section keyword found";
}
args.clear();
break;
@ -114,17 +118,17 @@ void Parser::ParseData(const std::string& filename, const std::string& data, siz
}
}
bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
if (!config_contents) {
LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
config_contents->push_back('\n'); // TODO: fix parse_config.
ParseData(path, *config_contents, parse_errors);
ParseData(path, *config_contents);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@ -133,11 +137,11 @@ bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
return true;
}
bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
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);
if (!config_dir) {
PLOG(ERROR) << "Could not import directory '" << path << "'";
PLOG(INFO) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
@ -153,7 +157,7 @@ bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
if (!ParseConfigFile(file, parse_errors)) {
if (!ParseConfigFile(file)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@ -161,16 +165,10 @@ bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
}
bool Parser::ParseConfig(const std::string& path) {
size_t parse_errors;
return ParseConfig(path, &parse_errors);
}
bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
*parse_errors = 0;
if (is_dir(path.c_str())) {
return ParseConfigDir(path, parse_errors);
return ParseConfigDir(path);
}
return ParseConfigFile(path, parse_errors);
return ParseConfigFile(path);
}
} // namespace init

View file

@ -72,17 +72,19 @@ class Parser {
Parser();
bool ParseConfig(const std::string& path);
bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
size_t parse_error_count() const { return parse_error_count_; }
private:
void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
bool ParseConfigFile(const std::string& path, size_t* parse_errors);
bool ParseConfigDir(const std::string& path, size_t* parse_errors);
void ParseData(const std::string& filename, const std::string& data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
size_t parse_error_count_ = 0;
};
} // namespace init