53089aa25c
The content parameter of write_file() previously took a char* that was then converted to a std::string in WriteStringToFd(). One unfortunate effect of this, is that it is impossible to write data that contains '\0' within it, as the new string will only contain characters up until the '\0'. This changes write_file() to take an std::string, such that std::string::size() is used to determine the length of the string, allowing it to contain null characters. Also change the path parameter of read_file() and write_file() for consistency. Lastly, add a test for handling strings with '\0' in them. Bug: 36726045 Test: Boot bullhead, run unit tests Change-Id: Idad60e4228ee2de741ab3ab6a4917065b5e63cd8
150 lines
4.5 KiB
C++
150 lines
4.5 KiB
C++
/*
|
|
* 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 <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "action.h"
|
|
#include "init_parser.h"
|
|
#include "log.h"
|
|
#include "parser.h"
|
|
#include "service.h"
|
|
#include "util.h"
|
|
|
|
#include <android-base/stringprintf.h>
|
|
|
|
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::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.filename = filename.c_str();
|
|
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 (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(args, &ret_err)) {
|
|
parse_error(&state, "%s\n", ret_err.c_str());
|
|
section_parser = nullptr;
|
|
}
|
|
} else if (section_parser) {
|
|
std::string ret_err;
|
|
if (!section_parser->ParseLineSection(args, state.filename,
|
|
state.line, &ret_err)) {
|
|
parse_error(&state, "%s\n", ret_err.c_str());
|
|
}
|
|
}
|
|
args.clear();
|
|
break;
|
|
case T_TEXT:
|
|
args.emplace_back(state.text);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Parser::ParseConfigFile(const std::string& path) {
|
|
LOG(INFO) << "Parsing file " << path << "...";
|
|
Timer t;
|
|
std::string data;
|
|
if (!read_file(path, &data)) {
|
|
return false;
|
|
}
|
|
|
|
data.push_back('\n'); // TODO: fix parse_config.
|
|
ParseData(path, data);
|
|
for (const auto& sp : section_parsers_) {
|
|
sp.second->EndFile(path);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void Parser::DumpState() const {
|
|
ServiceManager::GetInstance().DumpState();
|
|
ActionManager::GetInstance().DumpState();
|
|
}
|