Add service and interface whitelist/blacklists

This allows service whitelists and blacklists, and
interface whitelists to be passed into the application.

The whitelists can be prefixes, like xyz.openbmc_project.
The blacklist is the full service name.

A future commit can add support for interface blacklists.

Change-Id: I91f6ef2f7be63e4d13ac03d570bba18ef8277fae
Signed-off-by: Matt Spinler <spinler@us.ibm.com>
diff --git a/src/argument.cpp b/src/argument.cpp
new file mode 100644
index 0000000..80bddef
--- /dev/null
+++ b/src/argument.cpp
@@ -0,0 +1,81 @@
+/**
+ * Copyright © 2018 IBM Corporation
+ *
+ * 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 "argument.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <iterator>
+
+const std::string ArgumentParser::true_string = "true";
+const std::string ArgumentParser::empty_string = "";
+
+const char* ArgumentParser::optionstr = "s:b:i:?h";
+const option ArgumentParser::options[] = {
+    {"service-namespaces", required_argument, nullptr, 's'},
+    {"service-blacklists", required_argument, nullptr, 'b'},
+    {"interface-namespaces", required_argument, nullptr, 'i'},
+    {"help", no_argument, nullptr, 'h'},
+    {0, 0, 0, 0},
+};
+
+ArgumentParser::ArgumentParser(int argc, char** argv)
+{
+    int option = 0;
+    while (-1 !=
+           (option = getopt_long(argc, argv, optionstr, options, nullptr)))
+    {
+        if ((option == '?') || (option == 'h'))
+        {
+            usage(argv);
+            exit(-1);
+        }
+
+        auto i = &options[0];
+        while ((i->val != option) && (i->val != 0))
+            ++i;
+
+        if (i->val)
+            arguments[i->name] = (i->has_arg ? optarg : true_string);
+    }
+}
+
+const std::string& ArgumentParser::operator[](const std::string& opt)
+{
+    auto i = arguments.find(opt);
+    if (i == arguments.end())
+    {
+        return empty_string;
+    }
+    else
+    {
+        return i->second;
+    }
+}
+
+void ArgumentParser::usage(char** argv)
+{
+    std::cerr << "Usage: " << argv[0] << " [options]" << std::endl;
+    std::cerr << "Options:" << std::endl;
+    std::cerr << "    --help               Print this menu" << std::endl;
+    std::cerr << "    --service-namespaces=<services> Space separated list of ";
+    std::cerr << "service namespaces to whitelist\n";
+    std::cerr << "    --service-blacklists=<services> Space separated list of ";
+    std::cerr << "service names to blacklist\n";
+    std::cerr << "    --interface-namespaces=<ifaces> Space separated list of ";
+    std::cerr << "interface namespaces to whitelist\n";
+}
diff --git a/src/argument.hpp b/src/argument.hpp
new file mode 100644
index 0000000..71a2c76
--- /dev/null
+++ b/src/argument.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <getopt.h>
+
+#include <map>
+#include <string>
+
+/** @brief Class - Encapsulates parsing command line options and
+ *                 populating arguments
+ */
+class ArgumentParser
+{
+  public:
+    ArgumentParser() = delete;
+    ~ArgumentParser() = default;
+    ArgumentParser(const ArgumentParser&) = delete;
+    ArgumentParser& operator=(const ArgumentParser&) = delete;
+    ArgumentParser(ArgumentParser&&) = default;
+    ArgumentParser& operator=(ArgumentParser&&) = default;
+
+    /** @brief Constructs Argument object
+     *
+     *  @param argc - the main function's argc passed as is
+     *  @param argv - the main function's argv passed as is
+     *  @return Object constructed
+     */
+    ArgumentParser(int argc, char** argv);
+
+    /** @brief Given a option, returns its argument(optarg) */
+    const std::string& operator[](const std::string& opt);
+
+    /** @brief Displays usage */
+    static void usage(char** argv);
+
+    /** @brief Set to 'true' when an option is passed */
+    static const std::string true_string;
+
+    /** @brief Set to '' when an option is not passed */
+    static const std::string empty_string;
+
+  private:
+    /** @brief Option to argument mapping */
+    std::map<const std::string, std::string> arguments;
+
+    /** @brief Array of struct options as needed by getopt_long */
+    static const option options[];
+
+    /** @brief optstring as needed by getopt_long */
+    static const char* optionstr;
+};
diff --git a/src/main.cpp b/src/main.cpp
index e00e920..c291250 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,3 +1,5 @@
+#include "src/argument.hpp"
+
 #include <tinyxml2.h>
 
 #include <atomic>
@@ -29,6 +31,10 @@
                            std::shared_ptr<sdbusplus::asio::dbus_interface>>
     associationInterfaces;
 
+static boost::container::flat_set<std::string> service_whitelist;
+static boost::container::flat_set<std::string> service_blacklist;
+static boost::container::flat_set<std::string> iface_whitelist;
+
 /** Exception thrown when a path is not found in the object list. */
 struct NotFoundException final : public sdbusplus::exception_t
 {
@@ -302,8 +308,16 @@
                     continue;
                 }
 
-                if (ignored_interfaces.find(std::string(iface_name)) ==
-                    ignored_interfaces.end())
+                std::string iface{iface_name};
+
+                if (((ignored_interfaces.find(iface) ==
+                      ignored_interfaces.end()) &&
+                     (std::find_if(iface_whitelist.begin(),
+                                   iface_whitelist.end(),
+                                   [iface](const auto& prefix) {
+                                       return boost::starts_with(iface, prefix);
+                                   }) != iface_whitelist.end())) ||
+                    (iface == "org.freedesktop.DBus.ObjectManager"))
                 {
                     thisPathMap[transaction->process_name].emplace(iface_name);
                 }
@@ -357,9 +371,17 @@
 
 bool need_to_introspect(const std::string& process_name)
 {
-    return boost::starts_with(process_name, "xyz.openbmc_project.") ||
-           boost::starts_with(process_name, "org.openbmc.") ||
-           boost::starts_with(process_name, "com.intel.");
+    auto inWhitelist =
+        std::find_if(service_whitelist.begin(), service_whitelist.end(),
+                     [&process_name](const auto& prefix) {
+                         return boost::starts_with(process_name, prefix);
+                     }) != service_whitelist.end();
+
+    // This holds full service names, not prefixes
+    auto inBlacklist =
+        service_blacklist.find(process_name) != service_blacklist.end();
+
+    return inWhitelist && !inBlacklist;
 }
 
 void start_new_introspect(
@@ -442,13 +464,36 @@
         "ListNames");
 }
 
+void splitArgs(const std::string& stringArgs,
+               boost::container::flat_set<std::string>& listArgs)
+{
+    std::istringstream args;
+    std::string arg;
+
+    args.str(stringArgs);
+
+    while (!args.eof())
+    {
+        args >> arg;
+        if (!arg.empty())
+        {
+            listArgs.insert(arg);
+        }
+    }
+}
+
 int main(int argc, char** argv)
 {
     std::cerr << "started\n";
+    auto options = ArgumentParser(argc, argv);
     boost::asio::io_service io;
     std::shared_ptr<sdbusplus::asio::connection> system_bus =
         std::make_shared<sdbusplus::asio::connection>(io);
 
+    splitArgs(options["service-namespaces"], service_whitelist);
+    splitArgs(options["interface-namespaces"], iface_whitelist);
+    splitArgs(options["service-blacklists"], service_blacklist);
+
     system_bus->request_name(OBJECT_MAPPER_DBUS_NAME);
     sdbusplus::asio::object_server server(system_bus);