Checks the interface inheritance hierarchy in init_rc files.

Bug: 118016875
Test: Added 'interface' lines to an init_rc file and observed errors
when misspelled or missing entire inheritance hierarchy.
Change-Id: I681420f15539742d8415808b2a0dcbf0bf6faaf1
This commit is contained in:
Daniel Norman 2019-07-09 11:00:53 -07:00
parent 6da50e319c
commit 3f42a767cd
4 changed files with 77 additions and 29 deletions

View File

@ -243,11 +243,12 @@ cc_binary {
],
whole_static_libs: ["libcap"],
shared_libs: [
"libprotobuf-cpp-lite",
"libhidl-gen-utils",
"libprocessgroup",
"liblog",
"libcutils",
"libhidl-gen-utils",
"libjsoncpp",
"liblog",
"libprocessgroup",
"libprotobuf-cpp-lite",
],
srcs: [
"action.cpp",

View File

@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
@ -29,6 +30,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <json/json.h>
#include "action.h"
#include "action_manager.h"
@ -129,21 +131,33 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
return nullptr;
}
static std::optional<std::set<std::string>> ReadKnownInterfaces(
const std::string& known_interfaces_file) {
if (known_interfaces_file.empty()) {
LOG(WARNING) << "Missing a known interfaces file.";
static std::optional<android::init::InterfaceInheritanceHierarchyMap>
ReadInterfaceInheritanceHierarchy(const std::string& interface_inheritance_hierarchy_file) {
if (interface_inheritance_hierarchy_file.empty()) {
LOG(WARNING) << "Missing an interface inheritance hierarchy file.";
return {};
}
std::string known_interfaces;
if (!ReadFileToString(known_interfaces_file, &known_interfaces)) {
LOG(ERROR) << "Failed to read known interfaces file '" << known_interfaces_file << "'";
Json::Value root;
Json::Reader reader;
std::ifstream stream(interface_inheritance_hierarchy_file);
if (!reader.parse(stream, root)) {
LOG(ERROR) << "Failed to read interface inheritance hierarchy file: "
<< interface_inheritance_hierarchy_file << "\n"
<< reader.getFormattedErrorMessages();
return {};
}
auto interfaces = Split(known_interfaces, " ");
return std::set<std::string>(interfaces.begin(), interfaces.end());
android::init::InterfaceInheritanceHierarchyMap result;
for (const Json::Value& entry : root) {
std::set<std::string> inherited_interfaces;
for (const Json::Value& intf : entry["inheritedInterfaces"]) {
inherited_interfaces.insert(intf.asString());
}
result[entry["interface"].asString()] = inherited_interfaces;
}
return result;
}
namespace android {
@ -169,7 +183,7 @@ int main(int argc, char** argv) {
android::base::InitLogging(argv, &android::base::StdioLogger);
android::base::SetMinimumLogSeverity(android::base::ERROR);
std::string known_interfaces_file;
std::string interface_inheritance_hierarchy_file;
while (true) {
static const struct option long_options[] = {
@ -177,7 +191,7 @@ int main(int argc, char** argv) {
{nullptr, 0, nullptr, 0},
};
int arg = getopt_long(argc, argv, "p:k:", long_options, nullptr);
int arg = getopt_long(argc, argv, "p:i:", long_options, nullptr);
if (arg == -1) {
break;
@ -190,8 +204,8 @@ int main(int argc, char** argv) {
case 'p':
passwd_files.emplace_back(optarg);
break;
case 'k':
known_interfaces_file = optarg;
case 'i':
interface_inheritance_hierarchy_file = optarg;
break;
default:
std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
@ -213,8 +227,10 @@ int main(int argc, char** argv) {
ServiceList& sl = ServiceList::GetInstance();
Parser parser;
parser.AddSectionParser(
"service", std::make_unique<ServiceParser>(&sl, nullptr,
ReadKnownInterfaces(known_interfaces_file)));
"service",
std::make_unique<ServiceParser>(
&sl, nullptr,
ReadInterfaceInheritanceHierarchy(interface_inheritance_hierarchy_file)));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<HostImportParser>());

View File

@ -18,6 +18,9 @@
#include <linux/input.h>
#include <algorithm>
#include <sstream>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@ -152,12 +155,6 @@ Result<void> ServiceParser::ParseInterface(std::vector<std::string>&& args) {
return Error() << "Interface name must not be a value name '" << interface_name << "'";
}
if (known_interfaces_ && known_interfaces_->count(interface_name) == 0) {
return Error() << "Interface is not in the known set of hidl_interfaces: '"
<< interface_name << "'. Please ensure the interface is built "
<< "by a hidl_interface target.";
}
const std::string fullname = interface_name + "/" + instance_name;
for (const auto& svc : *service_list_) {
@ -540,6 +537,37 @@ Result<void> ServiceParser::EndSection() {
return {};
}
if (interface_inheritance_hierarchy_) {
std::set<std::string> interface_names;
for (const std::string& intf : service_->interfaces()) {
interface_names.insert(Split(intf, "/")[0]);
}
std::ostringstream error_stream;
for (const std::string& intf : interface_names) {
if (interface_inheritance_hierarchy_->count(intf) == 0) {
error_stream << "\nInterface is not in the known set of hidl_interfaces: '" << intf
<< "'. Please ensure the interface is spelled correctly and built "
<< "by a hidl_interface target.";
continue;
}
const std::set<std::string>& required_interfaces =
(*interface_inheritance_hierarchy_)[intf];
std::set<std::string> diff;
std::set_difference(required_interfaces.begin(), required_interfaces.end(),
interface_names.begin(), interface_names.end(),
std::inserter(diff, diff.begin()));
if (!diff.empty()) {
error_stream << "\nInterface '" << intf << "' requires its full inheritance "
<< "hierarchy to be listed in this init_rc file. Missing "
<< "interfaces: [" << base::Join(diff, " ") << "]";
}
}
const std::string& errors = error_stream.str();
if (!errors.empty()) {
return Error() << errors;
}
}
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {

View File

@ -26,13 +26,16 @@
namespace android {
namespace init {
using InterfaceInheritanceHierarchyMap = std::map<std::string, std::set<std::string>>;
class ServiceParser : public SectionParser {
public:
ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts,
const std::optional<std::set<std::string>>& known_interfaces)
ServiceParser(
ServiceList* service_list, std::vector<Subcontext>* subcontexts,
const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
: service_list_(service_list),
subcontexts_(subcontexts),
known_interfaces_(known_interfaces),
interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
service_(nullptr) {}
Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
@ -85,7 +88,7 @@ class ServiceParser : public SectionParser {
ServiceList* service_list_;
std::vector<Subcontext>* subcontexts_;
std::optional<std::set<std::string>> known_interfaces_;
std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
std::unique_ptr<Service> service_;
std::string filename_;
};