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:
parent
29b9411685
commit
67dee626e0
17 changed files with 542 additions and 582 deletions
|
@ -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",
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include <vector>
|
||||
|
||||
#include "builtins.h"
|
||||
#include "init_parser.h"
|
||||
#include "keyword_map.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 {
|
||||
|
|
247
init/parser.cpp
247
init/parser.cpp
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
124
init/tokenizer.cpp
Normal 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
40
init/tokenizer.h
Normal 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
|
|
@ -21,7 +21,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "devices.h"
|
||||
#include "init_parser.h"
|
||||
#include "parser.h"
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
|
Loading…
Reference in a new issue