init: remove Parser singleton and related cleanup

* Remove the Parser singleton (Hooray!)
* Rename parser.* to tokenizer.* as this is actually a tokenizer
* Rename init_parser.* to parser.* as this is a generic parser
* Move contents of init_parser_test.cpp to service_test.cpp as this
  actually is a test of the parsing in MakeExecOneshotService() and
  nothing related to (init_)parser.cpp

Test: boot bullhead
Test: bool sailfish
Test: init unit tests
Change-Id: I4fe39e6483f58ebd3ce5ee715a45dbba0acf5d91
This commit is contained in:
Tom Cherry 2017-07-27 12:54:48 -07:00
parent 29b9411685
commit 67dee626e0
17 changed files with 542 additions and 582 deletions

View file

@ -67,10 +67,10 @@ cc_library_static {
"devices.cpp",
"firmware_handler.cpp",
"import_parser.cpp",
"init_parser.cpp",
"log.cpp",
"parser.cpp",
"service.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
"ueventd_parser.cpp",
"util.cpp",
@ -153,7 +153,6 @@ cc_test {
defaults: ["init_defaults"],
srcs: [
"devices_test.cpp",
"init_parser_test.cpp",
"init_test.cpp",
"property_service_test.cpp",
"service_test.cpp",

View file

@ -24,8 +24,8 @@
#include <vector>
#include "builtins.h"
#include "init_parser.h"
#include "keyword_map.h"
#include "parser.h"
namespace android {
namespace init {

View file

@ -57,7 +57,7 @@
#include "action.h"
#include "bootchart.h"
#include "init.h"
#include "init_parser.h"
#include "parser.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
@ -388,21 +388,15 @@ exit_success:
* start_index: index of the first path in the args list
*/
static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
Parser& parser = Parser::GetInstance();
auto& action_manager = ActionManager::GetInstance();
auto& service_manager = ServiceManager::GetInstance();
Parser parser = CreateParser(action_manager, service_manager);
if (end_index <= start_index) {
// Fallbacks for partitions on which early mount isn't enabled.
if (!parser.is_system_etc_init_loaded()) {
parser.ParseConfig("/system/etc/init");
parser.set_is_system_etc_init_loaded(true);
}
if (!parser.is_vendor_etc_init_loaded()) {
parser.ParseConfig("/vendor/etc/init");
parser.set_is_vendor_etc_init_loaded(true);
}
if (!parser.is_odm_etc_init_loaded()) {
parser.ParseConfig("/odm/etc/init");
parser.set_is_odm_etc_init_loaded(true);
for (const auto& path : late_import_paths) {
parser.ParseConfig(path);
}
late_import_paths.clear();
} else {
for (size_t i = start_index; i < end_index; ++i) {
parser.ParseConfig(args[i]);

View file

@ -17,11 +17,11 @@
#ifndef _INIT_IMPORT_PARSER_H
#define _INIT_IMPORT_PARSER_H
#include "init_parser.h"
#include <string>
#include <vector>
#include "parser.h"
namespace android {
namespace init {

View file

@ -55,16 +55,13 @@
#include <memory>
#include <vector>
#include "action.h"
#include "bootchart.h"
#include "import_parser.h"
#include "init_first_stage.h"
#include "init_parser.h"
#include "keychords.h"
#include "log.h"
#include "property_service.h"
#include "reboot.h"
#include "service.h"
#include "signal_handler.h"
#include "ueventd.h"
#include "util.h"
@ -98,11 +95,43 @@ static std::string wait_prop_name;
static std::string wait_prop_value;
static bool shutting_down;
std::vector<std::string> late_import_paths;
void DumpState() {
ServiceManager::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
}
Parser CreateParser(ActionManager& action_manager, ServiceManager& service_manager) {
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_manager));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
static void LoadBootScripts(ActionManager& action_manager, ServiceManager& service_manager) {
Parser parser = CreateParser(action_manager, service_manager);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
@ -1102,25 +1131,8 @@ int main(int argc, char** argv) {
ActionManager& am = ActionManager::GetInstance();
ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.

View file

@ -21,6 +21,10 @@
#include <selinux/label.h>
#include "action.h"
#include "parser.h"
#include "service.h"
namespace android {
namespace init {
@ -32,6 +36,10 @@ extern std::string default_console;
extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;
extern std::vector<std::string> late_import_paths;
Parser CreateParser(ActionManager& action_manager, ServiceManager& service_manager);
void handle_control_message(const std::string& msg, const std::string& arg);
void property_changed(const std::string& name, const std::string& value);

View file

@ -1,168 +0,0 @@
/*
* Copyright (C) 2010 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 "init_parser.h"
#include <dirent.h>
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "parser.h"
#include "util.h"
namespace android {
namespace init {
Parser::Parser() {
}
Parser& Parser::GetInstance() {
static Parser instance;
return instance;
}
void Parser::AddSectionParser(const std::string& name,
std::unique_ptr<SectionParser> parser) {
section_parsers_[name] = std::move(parser);
}
void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
line_callbacks_.emplace_back(prefix, callback);
}
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');
parse_state state;
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
std::vector<std::string> args;
for (;;) {
switch (next_token(&state)) {
case T_EOF:
if (section_parser) {
section_parser->EndSection();
}
return;
case T_NEWLINE:
state.line++;
if (args.empty()) {
break;
}
// If we have a line matching a prefix we recognize, call its callback and unset any
// current section parsers. This is meant for /sys/ and /dev/ line entries for uevent.
for (const auto& [prefix, callback] : line_callbacks_) {
if (android::base::StartsWith(args[0], prefix.c_str())) {
if (section_parser) section_parser->EndSection();
std::string ret_err;
if (!callback(std::move(args), &ret_err)) {
LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
section_parser = nullptr;
break;
}
}
if (section_parsers_.count(args[0])) {
if (section_parser) {
section_parser->EndSection();
}
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
section_parser = nullptr;
}
} else if (section_parser) {
std::string ret_err;
if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
}
args.clear();
break;
case T_TEXT:
args.emplace_back(state.text);
break;
}
}
}
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
std::string data;
std::string err;
if (!ReadFile(path, &data, &err)) {
LOG(ERROR) << err;
return false;
}
data.push_back('\n'); // TODO: fix parse_config.
ParseData(path, data);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
}
bool Parser::ParseConfigDir(const std::string& path) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
PLOG(ERROR) << "Could not import directory '" << path << "'";
return false;
}
dirent* current_file;
std::vector<std::string> files;
while ((current_file = readdir(config_dir.get()))) {
// Ignore directories and only process regular files.
if (current_file->d_type == DT_REG) {
std::string current_path =
android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
files.emplace_back(current_path);
}
}
// 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)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
return true;
}
bool Parser::ParseConfig(const std::string& path) {
if (is_dir(path.c_str())) {
return ParseConfigDir(path);
}
return ParseConfigFile(path);
}
} // namespace init
} // namespace android

View file

@ -1,102 +0,0 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _INIT_INIT_PARSER_H_
#define _INIT_INIT_PARSER_H_
#include <map>
#include <memory>
#include <string>
#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.
namespace android {
namespace init {
class SectionParser {
public:
virtual ~SectionParser() {}
virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) = 0;
virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
virtual void EndSection(){};
virtual void EndFile(){};
};
class Parser {
public:
// LineCallback is the type for callbacks that can parse a line starting with a given prefix.
//
// They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
//
// Similar to ParseSection() and ParseLineSection(), this function returns bool with false
// indicating a failure and has an std::string* err parameter into which an error string can
// be written.
using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
// TODO: init is the only user of this as a singleton; remove it.
static Parser& GetInstance();
Parser();
bool ParseConfig(const std::string& path);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
bool is_system_etc_init_loaded() { return is_system_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_; }
private:
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_;
bool is_system_etc_init_loaded_ = false;
bool is_vendor_etc_init_loaded_ = false;
bool is_odm_etc_init_loaded_ = false;
};
} // namespace init
} // namespace android
#endif

View file

@ -1,150 +0,0 @@
/*
* Copyright (C) 2015 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 "init_parser.h"
#include <string>
#include <vector>
#include <gtest/gtest.h>
#include "init.h"
#include "service.h"
#include "util.h"
namespace android {
namespace init {
TEST(init_parser, make_exec_oneshot_service_invalid_syntax) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
// Nothing.
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
// No arguments to 'exec'.
args.push_back("exec");
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
// No command in "exec --".
args.push_back("--");
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
}
TEST(init_parser, make_exec_oneshot_service_too_many_supplementary_gids) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
args.push_back("exec");
args.push_back("seclabel");
args.push_back("root"); // uid.
args.push_back("root"); // gid.
for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
args.push_back("root"); // Supplementary gid.
}
args.push_back("--");
args.push_back("/system/bin/id");
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
}
static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid,
bool gid, bool supplementary_gids) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
args.push_back("exec");
if (seclabel) {
args.push_back("u:r:su:s0"); // seclabel
if (uid) {
args.push_back("log"); // uid
if (gid) {
args.push_back("shell"); // gid
if (supplementary_gids) {
args.push_back("system"); // supplementary gid 0
args.push_back("adb"); // supplementary gid 1
}
}
}
}
if (dash_dash) {
args.push_back("--");
}
args.push_back("/system/bin/toybox");
args.push_back("id");
Service* svc = sm.MakeExecOneshotService(args);
ASSERT_NE(nullptr, svc);
if (seclabel) {
ASSERT_EQ("u:r:su:s0", svc->seclabel());
} else {
ASSERT_EQ("", svc->seclabel());
}
if (uid) {
uid_t decoded_uid;
std::string err;
ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->uid());
} else {
ASSERT_EQ(0U, svc->uid());
}
if (gid) {
uid_t decoded_uid;
std::string err;
ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->gid());
} else {
ASSERT_EQ(0U, svc->gid());
}
if (supplementary_gids) {
ASSERT_EQ(2U, svc->supp_gids().size());
uid_t decoded_uid;
std::string err;
ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
} else {
ASSERT_EQ(0U, svc->supp_gids().size());
}
ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
ASSERT_EQ("id", svc->args()[1]);
}
TEST(init_parser, make_exec_oneshot_service_with_everything) {
Test_make_exec_oneshot_service(true, true, true, true, true);
}
TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid_gid) {
Test_make_exec_oneshot_service(true, true, true, true, false);
}
TEST(init_parser, make_exec_oneshot_service_with_seclabel_uid) {
Test_make_exec_oneshot_service(true, true, true, false, false);
}
TEST(init_parser, make_exec_oneshot_service_with_seclabel) {
Test_make_exec_oneshot_service(true, true, false, false, false);
}
TEST(init_parser, make_exec_oneshot_service_with_just_command) {
Test_make_exec_oneshot_service(true, false, false, false, false);
}
TEST(init_parser, make_exec_oneshot_service_with_just_command_no_dash) {
Test_make_exec_oneshot_service(false, false, false, false, false);
}
} // namespace init
} // namespace android

View file

@ -23,8 +23,8 @@
#include "action.h"
#include "builtins.h"
#include "import_parser.h"
#include "init_parser.h"
#include "keyword_map.h"
#include "parser.h"
#include "util.h"
namespace android {

View file

@ -1,123 +1,156 @@
/*
* Copyright (C) 2010 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 "parser.h"
#include <dirent.h>
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "tokenizer.h"
#include "util.h"
namespace android {
namespace init {
int next_token(struct parse_state *state)
{
char *x = state->ptr;
char *s;
Parser::Parser() {}
if (state->nexttoken) {
int t = state->nexttoken;
state->nexttoken = 0;
return t;
}
void Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {
section_parsers_[name] = std::move(parser);
}
void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
line_callbacks_.emplace_back(prefix, callback);
}
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');
parse_state state;
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
std::vector<std::string> args;
for (;;) {
switch (*x) {
case 0:
state->ptr = x;
return T_EOF;
case '\n':
x++;
state->ptr = x;
return T_NEWLINE;
case ' ':
case '\t':
case '\r':
x++;
continue;
case '#':
while (*x && (*x != '\n')) x++;
if (*x == '\n') {
state->ptr = x+1;
return T_NEWLINE;
} else {
state->ptr = x;
return T_EOF;
}
default:
goto text;
switch (next_token(&state)) {
case T_EOF:
if (section_parser) section_parser->EndSection();
return;
case T_NEWLINE:
state.line++;
if (args.empty()) break;
// If we have a line matching a prefix we recognize, call its callback and unset any
// current section parsers. This is meant for /sys/ and /dev/ line entries for
// uevent.
for (const auto& [prefix, callback] : line_callbacks_) {
if (android::base::StartsWith(args[0], prefix.c_str())) {
if (section_parser) section_parser->EndSection();
std::string ret_err;
if (!callback(std::move(args), &ret_err)) {
LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
section_parser = nullptr;
break;
}
}
if (section_parsers_.count(args[0])) {
if (section_parser) section_parser->EndSection();
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
if (!section_parser->ParseSection(std::move(args), filename, state.line,
&ret_err)) {
LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
section_parser = nullptr;
}
} else if (section_parser) {
std::string ret_err;
if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
}
args.clear();
break;
case T_TEXT:
args.emplace_back(state.text);
break;
}
}
}
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
std::string data;
std::string err;
if (!ReadFile(path, &data, &err)) {
LOG(ERROR) << err;
return false;
}
textdone:
state->ptr = x;
*s = 0;
return T_TEXT;
text:
state->text = s = x;
textresume:
for (;;) {
switch (*x) {
case 0:
goto textdone;
case ' ':
case '\t':
case '\r':
x++;
goto textdone;
case '\n':
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"':
x++;
for (;;) {
switch (*x) {
case 0:
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"':
x++;
goto textresume;
default:
*s++ = *x++;
}
}
break;
case '\\':
x++;
switch (*x) {
case 0:
goto textdone;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
case '\r':
/* \ <cr> <lf> -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ <lf> -> line continuation */
state->line++;
x++;
/* eat any extra whitespace */
while((*x == ' ') || (*x == '\t')) x++;
continue;
default:
/* unknown escape -- just copy */
*s++ = *x++;
}
continue;
default:
*s++ = *x++;
data.push_back('\n'); // TODO: fix parse_config.
ParseData(path, data);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
}
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 << "'";
return false;
}
dirent* current_file;
std::vector<std::string> files;
while ((current_file = readdir(config_dir.get()))) {
// Ignore directories and only process regular files.
if (current_file->d_type == DT_REG) {
std::string current_path =
android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
files.emplace_back(current_path);
}
}
return T_EOF;
// 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)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
return true;
}
bool Parser::ParseConfig(const std::string& path) {
if (is_dir(path.c_str())) {
return ParseConfigDir(path);
}
return ParseConfigFile(path);
}
} // namespace init

View file

@ -14,27 +14,77 @@
* limitations under the License.
*/
#ifndef PARSER_H_
#define PARSER_H_
#ifndef _INIT_PARSER_H_
#define _INIT_PARSER_H_
#define T_EOF 0
#define T_TEXT 1
#define T_NEWLINE 2
#include <map>
#include <memory>
#include <string>
#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.
namespace android {
namespace init {
struct parse_state
{
char *ptr;
char *text;
int line;
int nexttoken;
class SectionParser {
public:
virtual ~SectionParser() {}
virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) = 0;
virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
virtual void EndSection(){};
virtual void EndFile(){};
};
int next_token(struct parse_state *state);
class Parser {
public:
// LineCallback is the type for callbacks that can parse a line starting with a given prefix.
//
// They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
//
// Similar to ParseSection() and ParseLineSection(), this function returns bool with false
// indicating a failure and has an std::string* err parameter into which an error string can
// be written.
using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
Parser();
bool ParseConfig(const std::string& path);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
private:
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_;
};
} // namespace init
} // namespace android
#endif /* PARSER_H_ */
#endif

View file

@ -30,8 +30,8 @@
#include "action.h"
#include "capabilities.h"
#include "descriptors.h"
#include "init_parser.h"
#include "keyword_map.h"
#include "parser.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit

View file

@ -23,6 +23,8 @@
#include <gtest/gtest.h>
#include "util.h"
namespace android {
namespace init {
@ -71,5 +73,123 @@ TEST(service, pod_initialized) {
EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());
}
TEST(service, make_exec_oneshot_service_invalid_syntax) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
// Nothing.
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
// No arguments to 'exec'.
args.push_back("exec");
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
// No command in "exec --".
args.push_back("--");
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
}
TEST(service, make_exec_oneshot_service_too_many_supplementary_gids) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
args.push_back("exec");
args.push_back("seclabel");
args.push_back("root"); // uid.
args.push_back("root"); // gid.
for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {
args.push_back("root"); // Supplementary gid.
}
args.push_back("--");
args.push_back("/system/bin/id");
ASSERT_EQ(nullptr, sm.MakeExecOneshotService(args));
}
static void Test_make_exec_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,
bool supplementary_gids) {
ServiceManager& sm = ServiceManager::GetInstance();
std::vector<std::string> args;
args.push_back("exec");
if (seclabel) {
args.push_back("u:r:su:s0"); // seclabel
if (uid) {
args.push_back("log"); // uid
if (gid) {
args.push_back("shell"); // gid
if (supplementary_gids) {
args.push_back("system"); // supplementary gid 0
args.push_back("adb"); // supplementary gid 1
}
}
}
}
if (dash_dash) {
args.push_back("--");
}
args.push_back("/system/bin/toybox");
args.push_back("id");
Service* svc = sm.MakeExecOneshotService(args);
ASSERT_NE(nullptr, svc);
if (seclabel) {
ASSERT_EQ("u:r:su:s0", svc->seclabel());
} else {
ASSERT_EQ("", svc->seclabel());
}
if (uid) {
uid_t decoded_uid;
std::string err;
ASSERT_TRUE(DecodeUid("log", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->uid());
} else {
ASSERT_EQ(0U, svc->uid());
}
if (gid) {
uid_t decoded_uid;
std::string err;
ASSERT_TRUE(DecodeUid("shell", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->gid());
} else {
ASSERT_EQ(0U, svc->gid());
}
if (supplementary_gids) {
ASSERT_EQ(2U, svc->supp_gids().size());
uid_t decoded_uid;
std::string err;
ASSERT_TRUE(DecodeUid("system", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->supp_gids()[0]);
ASSERT_TRUE(DecodeUid("adb", &decoded_uid, &err));
ASSERT_EQ(decoded_uid, svc->supp_gids()[1]);
} else {
ASSERT_EQ(0U, svc->supp_gids().size());
}
ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());
ASSERT_EQ("/system/bin/toybox", svc->args()[0]);
ASSERT_EQ("id", svc->args()[1]);
}
TEST(service, make_exec_oneshot_service_with_everything) {
Test_make_exec_oneshot_service(true, true, true, true, true);
}
TEST(service, make_exec_oneshot_service_with_seclabel_uid_gid) {
Test_make_exec_oneshot_service(true, true, true, true, false);
}
TEST(service, make_exec_oneshot_service_with_seclabel_uid) {
Test_make_exec_oneshot_service(true, true, true, false, false);
}
TEST(service, make_exec_oneshot_service_with_seclabel) {
Test_make_exec_oneshot_service(true, true, false, false, false);
}
TEST(service, make_exec_oneshot_service_with_just_command) {
Test_make_exec_oneshot_service(true, false, false, false, false);
}
TEST(service, make_exec_oneshot_service_with_just_command_no_dash) {
Test_make_exec_oneshot_service(false, false, false, false, false);
}
} // namespace init
} // namespace android

124
init/tokenizer.cpp Normal file
View file

@ -0,0 +1,124 @@
#include "tokenizer.h"
namespace android {
namespace init {
int next_token(struct parse_state *state)
{
char *x = state->ptr;
char *s;
if (state->nexttoken) {
int t = state->nexttoken;
state->nexttoken = 0;
return t;
}
for (;;) {
switch (*x) {
case 0:
state->ptr = x;
return T_EOF;
case '\n':
x++;
state->ptr = x;
return T_NEWLINE;
case ' ':
case '\t':
case '\r':
x++;
continue;
case '#':
while (*x && (*x != '\n')) x++;
if (*x == '\n') {
state->ptr = x+1;
return T_NEWLINE;
} else {
state->ptr = x;
return T_EOF;
}
default:
goto text;
}
}
textdone:
state->ptr = x;
*s = 0;
return T_TEXT;
text:
state->text = s = x;
textresume:
for (;;) {
switch (*x) {
case 0:
goto textdone;
case ' ':
case '\t':
case '\r':
x++;
goto textdone;
case '\n':
state->nexttoken = T_NEWLINE;
x++;
goto textdone;
case '"':
x++;
for (;;) {
switch (*x) {
case 0:
/* unterminated quoted thing */
state->ptr = x;
return T_EOF;
case '"':
x++;
goto textresume;
default:
*s++ = *x++;
}
}
break;
case '\\':
x++;
switch (*x) {
case 0:
goto textdone;
case 'n':
*s++ = '\n';
break;
case 'r':
*s++ = '\r';
break;
case 't':
*s++ = '\t';
break;
case '\\':
*s++ = '\\';
break;
case '\r':
/* \ <cr> <lf> -> line continuation */
if (x[1] != '\n') {
x++;
continue;
}
case '\n':
/* \ <lf> -> line continuation */
state->line++;
x++;
/* eat any extra whitespace */
while((*x == ' ') || (*x == '\t')) x++;
continue;
default:
/* unknown escape -- just copy */
*s++ = *x++;
}
continue;
default:
*s++ = *x++;
}
}
return T_EOF;
}
} // namespace init
} // namespace android

40
init/tokenizer.h Normal file
View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2010 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.
*/
#ifndef _INIT_TOKENIZER_H_
#define _INIT_TOKENIZER_H_
#define T_EOF 0
#define T_TEXT 1
#define T_NEWLINE 2
namespace android {
namespace init {
struct parse_state
{
char *ptr;
char *text;
int line;
int nexttoken;
};
int next_token(struct parse_state *state);
} // namespace init
} // namespace android
#endif

View file

@ -21,7 +21,7 @@
#include <vector>
#include "devices.h"
#include "init_parser.h"
#include "parser.h"
namespace android {
namespace init {