Checks each interface in an init_rc file is a known hidl_interface.

Test: Adding a misspelling to an init_rc's interface line and observing
build failure.
Bug: 77646540
Change-Id: I58f66d73f0bd9b4203e8259161843b56ad428d73
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index 9323aa0..8aa3509 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -129,6 +129,23 @@
     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.";
+        return {};
+    }
+
+    std::string known_interfaces;
+    if (!ReadFileToString(known_interfaces_file, &known_interfaces)) {
+        LOG(ERROR) << "Failed to read known interfaces file '" << known_interfaces_file << "'";
+        return {};
+    }
+
+    auto interfaces = Split(known_interfaces, " ");
+    return std::set<std::string>(interfaces.begin(), interfaces.end());
+}
+
 namespace android {
 namespace init {
 
@@ -139,11 +156,12 @@
 #include "generated_stub_builtin_function_map.h"
 
 void PrintUsage() {
-    std::cout << "usage: host_init_verifier [-p FILE] <init rc file>\n"
+    std::cout << "usage: host_init_verifier [-p FILE] -k FILE <init rc file>\n"
                  "\n"
                  "Tests an init script for correctness\n"
                  "\n"
                  "-p FILE\tSearch this passwd file for users and groups\n"
+                 "-k FILE\tUse this file as a space-separated list of known interfaces\n"
               << std::endl;
 }
 
@@ -151,13 +169,15 @@
     android::base::InitLogging(argv, &android::base::StdioLogger);
     android::base::SetMinimumLogSeverity(android::base::ERROR);
 
+    std::string known_interfaces_file;
+
     while (true) {
         static const struct option long_options[] = {
                 {"help", no_argument, nullptr, 'h'},
                 {nullptr, 0, nullptr, 0},
         };
 
-        int arg = getopt_long(argc, argv, "p:", long_options, nullptr);
+        int arg = getopt_long(argc, argv, "p:k:", long_options, nullptr);
 
         if (arg == -1) {
             break;
@@ -170,6 +190,9 @@
             case 'p':
                 passwd_files.emplace_back(optarg);
                 break;
+            case 'k':
+                known_interfaces_file = optarg;
+                break;
             default:
                 std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
                 return EXIT_FAILURE;
@@ -189,7 +212,9 @@
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sl = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&sl, nullptr,
+                                                       ReadKnownInterfaces(known_interfaces_file)));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
diff --git a/init/init.cpp b/init/init.cpp
index b6911e5..675f3e5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -113,7 +113,8 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -124,7 +125,8 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+    parser.AddSectionParser(
+            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
     return parser;
 }
 
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 1bcc5ef..a09db18 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -44,7 +44,8 @@
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 33ed050..ba35104 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -152,6 +152,12 @@
         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_) {
diff --git a/init/service_parser.h b/init/service_parser.h
index 0a5b291..5a16768 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -28,8 +28,12 @@
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
-        : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
+    ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+                  const std::optional<std::set<std::string>>& known_interfaces)
+        : service_list_(service_list),
+          subcontexts_(subcontexts),
+          known_interfaces_(known_interfaces),
+          service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -81,6 +85,7 @@
 
     ServiceList* service_list_;
     std::vector<Subcontext>* subcontexts_;
+    std::optional<std::set<std::string>> known_interfaces_;
     std::unique_ptr<Service> service_;
     std::string filename_;
 };