Merge changes Ic446c026,I86568a5b am: 51b7cb006f am: 41dbec9791
am: fea35fc7e4
Change-Id: Ia5e7465015618317132c63cdfaddd8cb63c2425a
This commit is contained in:
commit
d0ea8a98cd
15 changed files with 312 additions and 100 deletions
|
|
@ -143,6 +143,7 @@ LOCAL_MODULE := init_tests
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
devices_test.cpp \
|
devices_test.cpp \
|
||||||
init_parser_test.cpp \
|
init_parser_test.cpp \
|
||||||
|
init_test.cpp \
|
||||||
property_service_test.cpp \
|
property_service_test.cpp \
|
||||||
util_test.cpp \
|
util_test.cpp \
|
||||||
|
|
||||||
|
|
@ -154,7 +155,7 @@ LOCAL_SHARED_LIBRARIES += \
|
||||||
LOCAL_STATIC_LIBRARIES := libinit
|
LOCAL_STATIC_LIBRARIES := libinit
|
||||||
LOCAL_SANITIZE := integer
|
LOCAL_SANITIZE := integer
|
||||||
LOCAL_CLANG := true
|
LOCAL_CLANG := true
|
||||||
LOCAL_CPPFLAGS := -Wall -Wextra -Werror
|
LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
|
||||||
include $(BUILD_NATIVE_TEST)
|
include $(BUILD_NATIVE_TEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ void ActionManager::DumpState() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
|
bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||||
int line, std::string* err) {
|
int line, std::string* err) {
|
||||||
std::vector<std::string> triggers(args.begin() + 1, args.end());
|
std::vector<std::string> triggers(args.begin() + 1, args.end());
|
||||||
if (triggers.size() < 1) {
|
if (triggers.size() < 1) {
|
||||||
|
|
@ -380,13 +380,12 @@ bool ActionParser::ParseSection(const std::vector<std::string>& args, const std:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ActionParser::ParseLineSection(const std::vector<std::string>& args, int line,
|
bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
|
||||||
std::string* err) {
|
return action_ ? action_->AddCommand(std::move(args), line, err) : false;
|
||||||
return action_ ? action_->AddCommand(args, line, err) : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ActionParser::EndSection() {
|
void ActionParser::EndSection() {
|
||||||
if (action_ && action_->NumCommands() > 0) {
|
if (action_ && action_->NumCommands() > 0) {
|
||||||
ActionManager::GetInstance().AddAction(std::move(action_));
|
action_manager_->AddAction(std::move(action_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,12 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActionManager {
|
class ActionManager {
|
||||||
public:
|
public:
|
||||||
static ActionManager& GetInstance();
|
static ActionManager& GetInstance();
|
||||||
|
|
||||||
|
// Exposed for testing
|
||||||
|
ActionManager();
|
||||||
|
|
||||||
void AddAction(std::unique_ptr<Action> action);
|
void AddAction(std::unique_ptr<Action> action);
|
||||||
void QueueEventTrigger(const std::string& trigger);
|
void QueueEventTrigger(const std::string& trigger);
|
||||||
void QueuePropertyTrigger(const std::string& name, const std::string& value);
|
void QueuePropertyTrigger(const std::string& name, const std::string& value);
|
||||||
|
|
@ -100,9 +103,7 @@ public:
|
||||||
bool HasMoreCommands() const;
|
bool HasMoreCommands() const;
|
||||||
void DumpState() const;
|
void DumpState() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ActionManager();
|
|
||||||
|
|
||||||
ActionManager(ActionManager const&) = delete;
|
ActionManager(ActionManager const&) = delete;
|
||||||
void operator=(ActionManager const&) = delete;
|
void operator=(ActionManager const&) = delete;
|
||||||
|
|
||||||
|
|
@ -114,16 +115,15 @@ private:
|
||||||
|
|
||||||
class ActionParser : public SectionParser {
|
class ActionParser : public SectionParser {
|
||||||
public:
|
public:
|
||||||
ActionParser() : action_(nullptr) {
|
ActionParser(ActionManager* action_manager)
|
||||||
}
|
: action_manager_(action_manager), action_(nullptr) {}
|
||||||
bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
|
bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
|
||||||
std::string* err) override;
|
std::string* err) override;
|
||||||
bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
|
bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
|
||||||
void EndSection() override;
|
void EndSection() override;
|
||||||
void EndFile(const std::string&) override {
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
ActionManager* action_manager_;
|
||||||
std::unique_ptr<Action> action_;
|
std::unique_ptr<Action> action_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -390,7 +390,7 @@ static void import_late(const std::vector<std::string>& args, size_t start_index
|
||||||
|
|
||||||
// Turning this on and letting the INFO logging be discarded adds 0.2s to
|
// Turning this on and letting the INFO logging be discarded adds 0.2s to
|
||||||
// Nexus 9 boot time, so it's disabled by default.
|
// Nexus 9 boot time, so it's disabled by default.
|
||||||
if (false) parser.DumpState();
|
if (false) DumpState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mount_fstab
|
/* mount_fstab
|
||||||
|
|
@ -838,7 +838,7 @@ static int do_init_user0(const std::vector<std::string>& args) {
|
||||||
return e4crypt_do_init_user0();
|
return e4crypt_do_init_user0();
|
||||||
}
|
}
|
||||||
|
|
||||||
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
|
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
|
||||||
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const Map builtin_functions = {
|
static const Map builtin_functions = {
|
||||||
|
|
|
||||||
|
|
@ -17,19 +17,20 @@
|
||||||
#ifndef _INIT_BUILTINS_H
|
#ifndef _INIT_BUILTINS_H
|
||||||
#define _INIT_BUILTINS_H
|
#define _INIT_BUILTINS_H
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "keyword_map.h"
|
#include "keyword_map.h"
|
||||||
|
|
||||||
using BuiltinFunction = int (*) (const std::vector<std::string>& args);
|
using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
|
||||||
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
|
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
|
||||||
public:
|
public:
|
||||||
BuiltinFunctionMap() {
|
BuiltinFunctionMap() {}
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
Map& map() const override;
|
const Map& map() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
bool ImportParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
|
bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||||
int line, std::string* err) {
|
int line, std::string* err) {
|
||||||
if (args.size() != 2) {
|
if (args.size() != 2) {
|
||||||
*err = "single argument needed for import\n";
|
*err = "single argument needed for import\n";
|
||||||
|
|
@ -35,16 +35,18 @@ bool ImportParser::ParseSection(const std::vector<std::string>& args, const std:
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(INFO) << "Added '" << conf_file << "' to import list";
|
LOG(INFO) << "Added '" << conf_file << "' to import list";
|
||||||
imports_.emplace_back(std::move(conf_file));
|
if (filename_.empty()) filename_ = filename;
|
||||||
|
imports_.emplace_back(std::move(conf_file), line);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportParser::EndFile(const std::string& filename) {
|
void ImportParser::EndFile() {
|
||||||
auto current_imports = std::move(imports_);
|
auto current_imports = std::move(imports_);
|
||||||
imports_.clear();
|
imports_.clear();
|
||||||
for (const auto& s : current_imports) {
|
for (const auto& [import, line_num] : current_imports) {
|
||||||
if (!Parser::GetInstance().ParseConfig(s)) {
|
if (!parser_->ParseConfig(import)) {
|
||||||
PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
|
PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
|
||||||
|
<< "'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,20 +24,17 @@
|
||||||
|
|
||||||
class ImportParser : public SectionParser {
|
class ImportParser : public SectionParser {
|
||||||
public:
|
public:
|
||||||
ImportParser() {
|
ImportParser(Parser* parser) : parser_(parser) {}
|
||||||
}
|
bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
|
||||||
bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
|
|
||||||
std::string* err) override;
|
std::string* err) override;
|
||||||
bool ParseLineSection(const std::vector<std::string>& args, int line,
|
void EndFile() override;
|
||||||
std::string* err) override {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void EndSection() override {
|
|
||||||
}
|
|
||||||
void EndFile(const std::string& filename) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<std::string> imports_;
|
Parser* parser_;
|
||||||
|
// Store filename for later error reporting.
|
||||||
|
std::string filename_;
|
||||||
|
// Vector of imports and their line numbers for later error reporting.
|
||||||
|
std::vector<std::pair<std::string, int>> imports_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,11 @@ static std::unique_ptr<Timer> waiting_for_prop(nullptr);
|
||||||
static std::string wait_prop_name;
|
static std::string wait_prop_name;
|
||||||
static std::string wait_prop_value;
|
static std::string wait_prop_value;
|
||||||
|
|
||||||
|
void DumpState() {
|
||||||
|
ServiceManager::GetInstance().DumpState();
|
||||||
|
ActionManager::GetInstance().DumpState();
|
||||||
|
}
|
||||||
|
|
||||||
void register_epoll_handler(int fd, void (*fn)()) {
|
void register_epoll_handler(int fd, void (*fn)()) {
|
||||||
epoll_event ev;
|
epoll_event ev;
|
||||||
ev.events = EPOLLIN;
|
ev.events = EPOLLIN;
|
||||||
|
|
@ -1429,10 +1434,13 @@ int main(int argc, char** argv) {
|
||||||
const BuiltinFunctionMap function_map;
|
const BuiltinFunctionMap function_map;
|
||||||
Action::set_function_map(&function_map);
|
Action::set_function_map(&function_map);
|
||||||
|
|
||||||
|
ActionManager& am = ActionManager::GetInstance();
|
||||||
|
ServiceManager& sm = ServiceManager::GetInstance();
|
||||||
Parser& parser = Parser::GetInstance();
|
Parser& parser = Parser::GetInstance();
|
||||||
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
|
|
||||||
parser.AddSectionParser("on", std::make_unique<ActionParser>());
|
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
|
||||||
parser.AddSectionParser("import", std::make_unique<ImportParser>());
|
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
|
||||||
|
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
|
||||||
std::string bootscript = GetProperty("ro.boot.init_rc", "");
|
std::string bootscript = GetProperty("ro.boot.init_rc", "");
|
||||||
if (bootscript.empty()) {
|
if (bootscript.empty()) {
|
||||||
parser.ParseConfig("/init.rc");
|
parser.ParseConfig("/init.rc");
|
||||||
|
|
@ -1450,9 +1458,7 @@ int main(int argc, char** argv) {
|
||||||
|
|
||||||
// Turning this on and letting the INFO logging be discarded adds 0.2s to
|
// Turning this on and letting the INFO logging be discarded adds 0.2s to
|
||||||
// Nexus 9 boot time, so it's disabled by default.
|
// Nexus 9 boot time, so it's disabled by default.
|
||||||
if (false) parser.DumpState();
|
if (false) DumpState();
|
||||||
|
|
||||||
ActionManager& am = ActionManager::GetInstance();
|
|
||||||
|
|
||||||
am.QueueEventTrigger("early-init");
|
am.QueueEventTrigger("early-init");
|
||||||
|
|
||||||
|
|
@ -1487,10 +1493,10 @@ int main(int argc, char** argv) {
|
||||||
// By default, sleep until something happens.
|
// By default, sleep until something happens.
|
||||||
int epoll_timeout_ms = -1;
|
int epoll_timeout_ms = -1;
|
||||||
|
|
||||||
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
|
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
|
||||||
am.ExecuteOneCommand();
|
am.ExecuteOneCommand();
|
||||||
}
|
}
|
||||||
if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
|
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
|
||||||
restart_processes();
|
restart_processes();
|
||||||
|
|
||||||
// If there's a process that needs restarting, wake up in time for that.
|
// If there's a process that needs restarting, wake up in time for that.
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,6 @@ int add_environment(const char* key, const char* val);
|
||||||
|
|
||||||
bool start_waiting_for_property(const char *name, const char *value);
|
bool start_waiting_for_property(const char *name, const char *value);
|
||||||
|
|
||||||
|
void DumpState();
|
||||||
|
|
||||||
#endif /* _INIT_INIT_H */
|
#endif /* _INIT_INIT_H */
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,12 @@
|
||||||
#include "init_parser.h"
|
#include "init_parser.h"
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#include <android-base/logging.h>
|
#include <android-base/logging.h>
|
||||||
#include <android-base/stringprintf.h>
|
#include <android-base/stringprintf.h>
|
||||||
|
|
||||||
#include "action.h"
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "service.h"
|
#include "util.h"
|
||||||
|
|
||||||
Parser::Parser() {
|
Parser::Parser() {
|
||||||
}
|
}
|
||||||
|
|
@ -71,13 +69,14 @@ void Parser::ParseData(const std::string& filename, const std::string& data) {
|
||||||
}
|
}
|
||||||
section_parser = section_parsers_[args[0]].get();
|
section_parser = section_parsers_[args[0]].get();
|
||||||
std::string ret_err;
|
std::string ret_err;
|
||||||
if (!section_parser->ParseSection(args, state.filename, state.line, &ret_err)) {
|
if (!section_parser->ParseSection(std::move(args), state.filename, state.line,
|
||||||
|
&ret_err)) {
|
||||||
parse_error(&state, "%s\n", ret_err.c_str());
|
parse_error(&state, "%s\n", ret_err.c_str());
|
||||||
section_parser = nullptr;
|
section_parser = nullptr;
|
||||||
}
|
}
|
||||||
} else if (section_parser) {
|
} else if (section_parser) {
|
||||||
std::string ret_err;
|
std::string ret_err;
|
||||||
if (!section_parser->ParseLineSection(args, state.line, &ret_err)) {
|
if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
|
||||||
parse_error(&state, "%s\n", ret_err.c_str());
|
parse_error(&state, "%s\n", ret_err.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -100,8 +99,8 @@ bool Parser::ParseConfigFile(const std::string& path) {
|
||||||
|
|
||||||
data.push_back('\n'); // TODO: fix parse_config.
|
data.push_back('\n'); // TODO: fix parse_config.
|
||||||
ParseData(path, data);
|
ParseData(path, data);
|
||||||
for (const auto& sp : section_parsers_) {
|
for (const auto& [section_name, section_parser] : section_parsers_) {
|
||||||
sp.second->EndFile(path);
|
section_parser->EndFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
|
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
|
||||||
|
|
@ -141,8 +140,3 @@ bool Parser::ParseConfig(const std::string& path) {
|
||||||
}
|
}
|
||||||
return ParseConfigFile(path);
|
return ParseConfigFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::DumpState() const {
|
|
||||||
ServiceManager::GetInstance().DumpState();
|
|
||||||
ActionManager::GetInstance().DumpState();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -22,25 +22,50 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// SectionParser is an interface that can parse a given 'section' in init.
|
||||||
|
//
|
||||||
|
// You can implement up to 4 functions below, with ParseSection() being mandatory.
|
||||||
|
// The first two function return bool with false indicating a failure and has a std::string* err
|
||||||
|
// parameter into which an error string can be written. It will be reported along with the
|
||||||
|
// filename and line number of where the error occurred.
|
||||||
|
//
|
||||||
|
// 1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||||
|
// int line, std::string* err)
|
||||||
|
// This function is called when a section is first encountered.
|
||||||
|
//
|
||||||
|
// 2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
|
||||||
|
// This function is called on each subsequent line until the next section is encountered.
|
||||||
|
//
|
||||||
|
// 3) bool EndSection()
|
||||||
|
// This function is called either when a new section is found or at the end of the file.
|
||||||
|
// It indicates that parsing of the current section is complete and any relevant objects should
|
||||||
|
// be committed.
|
||||||
|
//
|
||||||
|
// 4) bool EndFile()
|
||||||
|
// This function is called at the end of the file.
|
||||||
|
// It indicates that the parsing has completed and any relevant objects should be committed.
|
||||||
|
|
||||||
class SectionParser {
|
class SectionParser {
|
||||||
public:
|
public:
|
||||||
virtual ~SectionParser() {
|
virtual ~SectionParser() {}
|
||||||
}
|
virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||||
virtual bool ParseSection(const std::vector<std::string>& args, const std::string& filename,
|
|
||||||
int line, std::string* err) = 0;
|
int line, std::string* err) = 0;
|
||||||
virtual bool ParseLineSection(const std::vector<std::string>& args, int line,
|
virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
|
||||||
std::string* err) = 0;
|
virtual void EndSection(){};
|
||||||
virtual void EndSection() = 0;
|
virtual void EndFile(){};
|
||||||
virtual void EndFile(const std::string& filename) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
static Parser& GetInstance();
|
static Parser& GetInstance();
|
||||||
void DumpState() const;
|
|
||||||
|
// Exposed for testing
|
||||||
|
Parser();
|
||||||
|
|
||||||
bool ParseConfig(const std::string& path);
|
bool ParseConfig(const std::string& path);
|
||||||
void AddSectionParser(const std::string& name,
|
void AddSectionParser(const std::string& name,
|
||||||
std::unique_ptr<SectionParser> parser);
|
std::unique_ptr<SectionParser> parser);
|
||||||
|
|
||||||
void set_is_system_etc_init_loaded(bool loaded) {
|
void set_is_system_etc_init_loaded(bool loaded) {
|
||||||
is_system_etc_init_loaded_ = loaded;
|
is_system_etc_init_loaded_ = loaded;
|
||||||
}
|
}
|
||||||
|
|
@ -54,9 +79,7 @@ public:
|
||||||
bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
|
bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
|
||||||
bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
|
bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser();
|
|
||||||
|
|
||||||
void ParseData(const std::string& filename, const std::string& data);
|
void ParseData(const std::string& filename, const std::string& data);
|
||||||
bool ParseConfigFile(const std::string& path);
|
bool ParseConfigFile(const std::string& path);
|
||||||
bool ParseConfigDir(const std::string& path);
|
bool ParseConfigDir(const std::string& path);
|
||||||
|
|
|
||||||
186
init/init_test.cpp
Normal file
186
init/init_test.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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 <functional>
|
||||||
|
|
||||||
|
#include <android-base/file.h>
|
||||||
|
#include <android-base/test_utils.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "action.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include "import_parser.h"
|
||||||
|
#include "init_parser.h"
|
||||||
|
#include "keyword_map.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
class TestFunctionMap : public KeywordMap<BuiltinFunction> {
|
||||||
|
public:
|
||||||
|
// Helper for argument-less functions
|
||||||
|
using BuiltinFunctionNoArgs = std::function<void(void)>;
|
||||||
|
void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
|
||||||
|
Add(name, 0, 0, [function](const std::vector<std::string>&) {
|
||||||
|
function();
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
|
||||||
|
const BuiltinFunction function) {
|
||||||
|
builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Map builtin_functions_ = {};
|
||||||
|
|
||||||
|
const Map& map() const override { return builtin_functions_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
using ActionManagerCommand = std::function<void(ActionManager&)>;
|
||||||
|
|
||||||
|
void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
|
||||||
|
const std::vector<ActionManagerCommand>& commands) {
|
||||||
|
ActionManager am;
|
||||||
|
|
||||||
|
Action::set_function_map(&test_function_map);
|
||||||
|
|
||||||
|
Parser parser;
|
||||||
|
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
|
||||||
|
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
|
||||||
|
|
||||||
|
ASSERT_TRUE(parser.ParseConfig(init_script_file));
|
||||||
|
|
||||||
|
for (const auto& command : commands) {
|
||||||
|
command(am);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (am.HasMoreCommands()) {
|
||||||
|
am.ExecuteOneCommand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
|
||||||
|
const std::vector<ActionManagerCommand>& commands) {
|
||||||
|
TemporaryFile tf;
|
||||||
|
ASSERT_TRUE(tf.fd != -1);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
|
||||||
|
TestInit(tf.path, test_function_map, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(init, SimpleEventTrigger) {
|
||||||
|
bool expect_true = false;
|
||||||
|
std::string init_script =
|
||||||
|
R"init(
|
||||||
|
on boot
|
||||||
|
pass_test
|
||||||
|
)init";
|
||||||
|
|
||||||
|
TestFunctionMap test_function_map;
|
||||||
|
test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
|
||||||
|
|
||||||
|
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
|
||||||
|
std::vector<ActionManagerCommand> commands{trigger_boot};
|
||||||
|
|
||||||
|
TestInitText(init_script, test_function_map, commands);
|
||||||
|
|
||||||
|
EXPECT_TRUE(expect_true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(init, EventTriggerOrder) {
|
||||||
|
std::string init_script =
|
||||||
|
R"init(
|
||||||
|
on boot
|
||||||
|
execute_first
|
||||||
|
|
||||||
|
on boot && property:ro.hardware=*
|
||||||
|
execute_second
|
||||||
|
|
||||||
|
on boot
|
||||||
|
execute_third
|
||||||
|
|
||||||
|
)init";
|
||||||
|
|
||||||
|
int num_executed = 0;
|
||||||
|
TestFunctionMap test_function_map;
|
||||||
|
test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
|
||||||
|
test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
|
||||||
|
test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
|
||||||
|
|
||||||
|
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
|
||||||
|
std::vector<ActionManagerCommand> commands{trigger_boot};
|
||||||
|
|
||||||
|
TestInitText(init_script, test_function_map, commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(init, EventTriggerOrderMultipleFiles) {
|
||||||
|
// 6 total files, which should have their triggers executed in the following order:
|
||||||
|
// 1: start - original script parsed
|
||||||
|
// 2: first_import - immediately imported by first_script
|
||||||
|
// 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
|
||||||
|
// 4: a_import - file imported by dir_a
|
||||||
|
// 5: dir_b - file named 'b.rc' in dir
|
||||||
|
// 6: last_import - imported after dir is imported
|
||||||
|
|
||||||
|
TemporaryFile first_import;
|
||||||
|
ASSERT_TRUE(first_import.fd != -1);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
|
||||||
|
|
||||||
|
TemporaryFile dir_a_import;
|
||||||
|
ASSERT_TRUE(dir_a_import.fd != -1);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
|
||||||
|
|
||||||
|
TemporaryFile last_import;
|
||||||
|
ASSERT_TRUE(last_import.fd != -1);
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
|
||||||
|
|
||||||
|
TemporaryDir dir;
|
||||||
|
// clang-format off
|
||||||
|
std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
|
||||||
|
"on boot\n"
|
||||||
|
"execute 3";
|
||||||
|
// clang-format on
|
||||||
|
// write_file() ensures the right mode is set
|
||||||
|
ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
|
||||||
|
|
||||||
|
ASSERT_TRUE(write_file(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
std::string start_script = "import " + std::string(first_import.path) + "\n"
|
||||||
|
"import " + std::string(dir.path) + "\n"
|
||||||
|
"import " + std::string(last_import.path) + "\n"
|
||||||
|
"on boot\n"
|
||||||
|
"execute 1";
|
||||||
|
// clang-format on
|
||||||
|
TemporaryFile start;
|
||||||
|
ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
|
||||||
|
|
||||||
|
int num_executed = 0;
|
||||||
|
auto execute_command = [&num_executed](const std::vector<std::string>& args) {
|
||||||
|
EXPECT_EQ(2U, args.size());
|
||||||
|
EXPECT_EQ(++num_executed, std::stoi(args[1]));
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestFunctionMap test_function_map;
|
||||||
|
test_function_map.Add("execute", 1, 1, execute_command);
|
||||||
|
|
||||||
|
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
|
||||||
|
std::vector<ActionManagerCommand> commands{trigger_boot};
|
||||||
|
|
||||||
|
TestInit(start.path, test_function_map, commands);
|
||||||
|
|
||||||
|
EXPECT_EQ(6, num_executed);
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,9 @@
|
||||||
|
|
||||||
template <typename Function>
|
template <typename Function>
|
||||||
class KeywordMap {
|
class KeywordMap {
|
||||||
public:
|
public:
|
||||||
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
|
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
|
||||||
using Map = const std::map<std::string, FunctionInfo>;
|
using Map = std::map<std::string, FunctionInfo>;
|
||||||
|
|
||||||
virtual ~KeywordMap() {
|
virtual ~KeywordMap() {
|
||||||
}
|
}
|
||||||
|
|
@ -68,10 +68,10 @@ public:
|
||||||
return std::get<Function>(function_info);
|
return std::get<Function>(function_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//Map of keyword ->
|
// Map of keyword ->
|
||||||
//(minimum number of arguments, maximum number of arguments, function pointer)
|
// (minimum number of arguments, maximum number of arguments, function pointer)
|
||||||
virtual Map& map() const = 0;
|
virtual const Map& map() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -534,14 +534,14 @@ bool Service::ParseWritepid(const std::vector<std::string>& args, std::string* e
|
||||||
}
|
}
|
||||||
|
|
||||||
class Service::OptionParserMap : public KeywordMap<OptionParser> {
|
class Service::OptionParserMap : public KeywordMap<OptionParser> {
|
||||||
public:
|
public:
|
||||||
OptionParserMap() {
|
OptionParserMap() {}
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
Map& map() const override;
|
const Map& map() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
|
const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
|
||||||
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const Map option_parsers = {
|
static const Map option_parsers = {
|
||||||
|
|
@ -877,11 +877,6 @@ ServiceManager& ServiceManager::GetInstance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceManager::AddService(std::unique_ptr<Service> service) {
|
void ServiceManager::AddService(std::unique_ptr<Service> service) {
|
||||||
Service* old_service = FindServiceByName(service->name());
|
|
||||||
if (old_service) {
|
|
||||||
LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
services_.emplace_back(std::move(service));
|
services_.emplace_back(std::move(service));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1095,7 +1090,7 @@ void ServiceManager::ReapAnyOutstandingChildren() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename,
|
bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
|
||||||
int line, std::string* err) {
|
int line, std::string* err) {
|
||||||
if (args.size() < 3) {
|
if (args.size() < 3) {
|
||||||
*err = "services must have a name and a program";
|
*err = "services must have a name and a program";
|
||||||
|
|
@ -1108,19 +1103,24 @@ bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Service* old_service = service_manager_->FindServiceByName(name);
|
||||||
|
if (old_service) {
|
||||||
|
*err = "ignored duplicate definition of service '" + name + "'";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> str_args(args.begin() + 2, args.end());
|
std::vector<std::string> str_args(args.begin() + 2, args.end());
|
||||||
service_ = std::make_unique<Service>(name, str_args);
|
service_ = std::make_unique<Service>(name, str_args);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServiceParser::ParseLineSection(const std::vector<std::string>& args, int line,
|
bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
|
||||||
std::string* err) {
|
return service_ ? service_->ParseLine(std::move(args), err) : false;
|
||||||
return service_ ? service_->ParseLine(args, err) : false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServiceParser::EndSection() {
|
void ServiceParser::EndSection() {
|
||||||
if (service_) {
|
if (service_) {
|
||||||
ServiceManager::GetInstance().AddService(std::move(service_));
|
service_manager_->AddService(std::move(service_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,16 +213,17 @@ private:
|
||||||
|
|
||||||
class ServiceParser : public SectionParser {
|
class ServiceParser : public SectionParser {
|
||||||
public:
|
public:
|
||||||
ServiceParser() : service_(nullptr) {}
|
ServiceParser(ServiceManager* service_manager)
|
||||||
bool ParseSection(const std::vector<std::string>& args, const std::string& filename, int line,
|
: service_manager_(service_manager), service_(nullptr) {}
|
||||||
|
bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
|
||||||
std::string* err) override;
|
std::string* err) override;
|
||||||
bool ParseLineSection(const std::vector<std::string>& args, int line, std::string* err) override;
|
bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
|
||||||
void EndSection() override;
|
void EndSection() override;
|
||||||
void EndFile(const std::string&) override {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool IsValidName(const std::string& name) const;
|
bool IsValidName(const std::string& name) const;
|
||||||
|
|
||||||
|
ServiceManager* service_manager_;
|
||||||
std::unique_ptr<Service> service_;
|
std::unique_ptr<Service> service_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue