From 194b5d1da9ec8957bbf0987e263e50132776b5e1 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Wed, 9 May 2018 17:38:30 -0700 Subject: [PATCH] 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 --- init/Android.bp | 3 +- init/host_import_parser.cpp | 45 +++++++++++++++++++ init/host_import_parser.h | 40 +++++++++++++++++ init/host_init_stubs.cpp | 8 +++- ...init_parser.cpp => host_init_verifier.cpp} | 36 +++++++++++---- init/import_parser.cpp | 9 ++-- init/import_parser.h | 1 + init/parser.cpp | 36 +++++++-------- init/parser.h | 10 +++-- 9 files changed, 151 insertions(+), 37 deletions(-) create mode 100644 init/host_import_parser.cpp create mode 100644 init/host_import_parser.h rename init/{host_init_parser.cpp => host_init_verifier.cpp} (67%) diff --git a/init/Android.bp b/init/Android.bp index 517847c3f..f579647a8 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -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", diff --git a/init/host_import_parser.cpp b/init/host_import_parser.cpp new file mode 100644 index 000000000..faf6fc1e3 --- /dev/null +++ b/init/host_import_parser.cpp @@ -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 + +using android::base::StartsWith; + +namespace android { +namespace init { + +Result HostImportParser::ParseSection(std::vector&& 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 diff --git a/init/host_import_parser.h b/init/host_import_parser.h new file mode 100644 index 000000000..e2980b2ae --- /dev/null +++ b/init/host_import_parser.h @@ -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 +#include + +#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 ParseSection(std::vector&& args, const std::string& filename, + int line) override; + + private: + std::string out_dir_; +}; + +} // namespace init +} // namespace android diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp index c40a25435..2352fc733 100644 --- a/init/host_init_stubs.cpp +++ b/init/host_init_stubs.cpp @@ -16,6 +16,8 @@ #include "host_init_stubs.h" +#include + // 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; diff --git a/init/host_init_parser.cpp b/init/host_init_verifier.cpp similarity index 67% rename from init/host_init_parser.cpp rename to init/host_init_verifier.cpp index df497eade..c6ec078df 100644 --- a/init/host_init_parser.cpp +++ b/init/host_init_verifier.cpp @@ -16,15 +16,25 @@ #include +#include +#include + #include +#include #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 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] << " "; + android::base::SetMinimumLogSeverity(android::base::ERROR); + if (argc != 3) { + LOG(ERROR) << "Usage: " << argv[0] << " "; 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(&sl, nullptr)); parser.AddSectionParser("on", std::make_unique(&am, nullptr)); + parser.AddSectionParser("import", std::make_unique(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; } diff --git a/init/import_parser.cpp b/init/import_parser.cpp index e335fd111..fb3185e83 100644 --- a/init/import_parser.cpp +++ b/init/import_parser.cpp @@ -41,14 +41,15 @@ Result ImportParser::ParseSection(std::vector&& args, return Success(); } +Result ImportParser::ParseLineSection(std::vector&&, 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); } } diff --git a/init/import_parser.h b/init/import_parser.h index 5a2f89498..7bc72e6e8 100644 --- a/init/import_parser.h +++ b/init/import_parser.h @@ -30,6 +30,7 @@ class ImportParser : public SectionParser { ImportParser(Parser* parser) : parser_(parser) {} Result ParseSection(std::vector&& args, const std::string& filename, int line) override; + Result ParseLineSection(std::vector&&, int) override; void EndFile() override; private: diff --git a/init/parser.cpp b/init/parser.cpp index 4453aaac1..ee6ee06db 100644 --- a/init/parser.cpp +++ b/init/parser.cpp @@ -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 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 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 diff --git a/init/parser.h b/init/parser.h index f6e237f57..3501d8c0b 100644 --- a/init/parser.h +++ b/init/parser.h @@ -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 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> section_parsers_; std::vector> line_callbacks_; + size_t parse_error_count_ = 0; }; } // namespace init