Add multi-host support to handle IPMI restriction mode.

Added multi-host and IPMI based devices to support the IPMI
whitelist restriction mode in ipmid.

The machine specific .yml file has the default restriction
mode dbus path. Ex. Whitelist. These restriction modes dbus
paths added for all the hosts and other IPMI devices Ex.
ocp_debug_card. These information will be displayed in the
setting manager dbus objects.

Ipmid reads hostIdx from the command request. These hostIdx
values are getting from ipmb-channels.json. This ipmb-channels
entires can configure from machine layer.

busctl tree xyz.openbmc_project.Settings | grep restriction
| | |-/xyz/openbmc_project/control/host1/restriction_mode
| | |-/xyz/openbmc_project/control/host2/restriction_mode
| | |-/xyz/openbmc_project/control/host3/restriction_mode
| | |-/xyz/openbmc_project/control/host4/restriction_mode
|-/xyz/openbmc_project/control/ocp_debug_card/restriction_mode

The mapping between IPMI based devices and restriction mode
dbus objects from settings manager is filtered with restriction
mode dbus interface as like above. The mapping is based on the
config file(json, yml) file entries.

Can configure .yml and .json entries based on the order and
IPMI devices and restriction mode dbus object mapping.

TESTED : Built Facebook YosemiteV2 images and loaded on the
target hardware. Verified restriction mode enabled for all
four hosts and ocp_debug_card.

Signed-off-by: Kumar Thangavel <thangavel.k@hcl.com>
Change-Id: I3d338b6cd37a817653ca981c5322a77e60d3f992
diff --git a/whitelist-filter.cpp b/whitelist-filter.cpp
index 114f7ca..c40261f 100644
--- a/whitelist-filter.cpp
+++ b/whitelist-filter.cpp
@@ -35,11 +35,13 @@
 
   private:
     void postInit();
-    void cacheRestrictedMode();
-    void handleRestrictedModeChange(sdbusplus::message_t& m);
+    void cacheRestrictedMode(const std::vector<std::string>& devices);
+    void handleRestrictedModeChange(
+        sdbusplus::message::message& m,
+        const std::map<std::string, size_t>& deviceList);
     ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
 
-    bool restrictedMode = true;
+    std::vector<bool> restrictedMode;
     std::shared_ptr<sdbusplus::asio::connection> bus;
     std::unique_ptr<settings::Objects> objects;
     std::unique_ptr<sdbusplus::bus::match_t> modeChangeMatch;
@@ -53,7 +55,6 @@
     bus = getSdBus();
 
     log<level::INFO>("Loading whitelist filter");
-
     ipmi::registerFilter(ipmi::prioOpenBmcBase,
                          [this](ipmi::message::Request::ptr request) {
                              return filterMessage(request);
@@ -63,51 +64,89 @@
     post_work([this]() { postInit(); });
 }
 
-void WhitelistFilter::cacheRestrictedMode()
+/** @brief Get RestrictionMode of the devices which has RestrictionMode support
+ * enabled
+ *  @param[in] devices - vector of devices object path
+ *  @returns void.
+ */
+
+void WhitelistFilter::cacheRestrictedMode(
+    const std::vector<std::string>& devices)
 {
     using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
     std::string restrictionModeSetting;
     std::string restrictionModeService;
-    try
+
+    for (auto& dev : devices)
     {
-        restrictionModeSetting = objects->map.at(restrictionModeIntf).at(0);
-        restrictionModeService =
-            objects->service(restrictionModeSetting, restrictionModeIntf);
+        try
+        {
+            restrictionModeSetting = dev;
+            restrictionModeService =
+                objects->service(restrictionModeSetting, restrictionModeIntf);
+        }
+        catch (const std::out_of_range& e)
+        {
+            log<level::ERR>(
+                "Could not look up restriction mode interface from cache");
+            return;
+        }
+
+        bus->async_method_call(
+            [this, index = std::distance(&*std::begin(devices), &dev)](
+                boost::system::error_code ec, ipmi::Value v) {
+                if (ec)
+                {
+                    log<level::ERR>("Error in RestrictionMode Get");
+                    // Fail-safe to true.
+                    restrictedMode[index] = true;
+                    return;
+                }
+
+                auto mode = std::get<std::string>(v);
+                auto restrictionMode =
+                    RestrictionMode::convertModesFromString(mode);
+
+                bool restrictMode =
+                    (restrictionMode == RestrictionMode::Modes::Whitelist);
+                restrictedMode.emplace_back(restrictMode);
+
+                log<level::INFO>((restrictMode ? "Set restrictedMode = true"
+                                               : "Set restrictedMode = false"));
+            },
+            restrictionModeService, restrictionModeSetting,
+            "org.freedesktop.DBus.Properties", "Get", restrictionModeIntf,
+            "RestrictionMode");
     }
-    catch (const std::out_of_range& e)
-    {
-        log<level::ERR>(
-            "Could not look up restriction mode interface from cache");
-        return;
-    }
-    bus->async_method_call(
-        [this](boost::system::error_code ec, ipmi::Value v) {
-            if (ec)
-            {
-                log<level::ERR>("Error in RestrictionMode Get");
-                // Fail-safe to true.
-                restrictedMode = true;
-                return;
-            }
-            auto mode = std::get<std::string>(v);
-            auto restrictionMode =
-                RestrictionMode::convertModesFromString(mode);
-            restrictedMode =
-                (restrictionMode == RestrictionMode::Modes::Whitelist);
-            log<level::INFO>((restrictedMode ? "Set restrictedMode = true"
-                                             : "Set restrictedMode = false"));
-        },
-        restrictionModeService, restrictionModeSetting,
-        "org.freedesktop.DBus.Properties", "Get", restrictionModeIntf,
-        "RestrictionMode");
 }
 
-void WhitelistFilter::handleRestrictedModeChange(sdbusplus::message_t& m)
+/** @brief Update RestrictionMode if any changes in RestrictionMode
+ *  @param[in] m - sdbusplus message. Using this to get Updated Mode dbus path
+ *  @param[in] deviceList - map to store devices path and their index
+ *  @returns void.
+ */
+
+void WhitelistFilter::handleRestrictedModeChange(
+    sdbusplus::message_t& m, const std::map<std::string, size_t>& deviceList)
 {
     using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
     std::string intf;
     std::vector<std::pair<std::string, ipmi::Value>> propertyList;
     m.read(intf, propertyList);
+
+    std::string path = m.get_path();
+    size_t hostId = 0;
+    auto it = deviceList.find(path);
+
+    if (it == deviceList.end())
+    {
+        log<level::ERR>("Key not found in deviceList ");
+    }
+    else
+    {
+        hostId = it->second;
+    }
+
     for (const auto& property : propertyList)
     {
         if (property.first == "RestrictionMode")
@@ -115,15 +154,20 @@
             RestrictionMode::Modes restrictionMode =
                 RestrictionMode::convertModesFromString(
                     std::get<std::string>(property.second));
-            restrictedMode =
+            bool restrictMode =
                 (restrictionMode == RestrictionMode::Modes::Whitelist);
-            log<level::INFO>((restrictedMode
-                                  ? "Updated restrictedMode = true"
-                                  : "Updated restrictedMode = false"));
+            restrictedMode[hostId] = restrictMode;
+            log<level::INFO>((restrictMode ? "Updated restrictedMode = true"
+                                           : "Updated restrictedMode = false"));
         }
     }
 }
 
+/** @brief Get and Update RestrictionModes of supported devices
+ *  @param[in] void
+ *  @returns void.
+ */
+
 void WhitelistFilter::postInit()
 {
     objects = std::make_unique<settings::Objects>(
@@ -135,28 +179,55 @@
         return;
     }
 
-    // Initialize restricted mode
-    cacheRestrictedMode();
-    // Wait for changes on Restricted mode
-    std::string filterStr;
+    std::vector<std::string> devices;
     try
     {
-        filterStr = sdbusplus::bus::match::rules::propertiesChanged(
-            objects->map.at(restrictionModeIntf).at(0), restrictionModeIntf);
+        devices = objects->map.at(restrictionModeIntf);
     }
     catch (const std::out_of_range& e)
     {
-        log<level::ERR>("Failed to determine restriction mode filter string");
+        log<level::ERR>(
+            "Could not look up restriction mode interface from cache");
         return;
     }
+
+    // Initialize restricted mode
+    cacheRestrictedMode(devices);
+    // Wait for changes on Restricted mode
+    std::map<std::string, size_t> deviceList;
+
+    for (size_t index = 0; index < devices.size(); index++)
+    {
+        deviceList.emplace(devices[index], index);
+    }
+
+    std::string filterStr;
+    std::string devicesDbusPath{"/xyz/openbmc_project/control"};
+
+    filterStr = sdbusplus::bus::match::rules::propertiesChangedNamespace(
+        devicesDbusPath, restrictionModeIntf);
+
     modeChangeMatch = std::make_unique<sdbusplus::bus::match_t>(
-        *bus, filterStr,
-        [this](sdbusplus::message_t& m) { handleRestrictedModeChange(m); });
+        *bus, filterStr, [this, deviceList](sdbusplus::message_t& m) {
+            handleRestrictedModeChange(m, deviceList);
+        });
 }
 
+/** @brief Filter IPMI messages with RestrictedMode
+ *  @param[in] request - IPMI messahe request
+ *  @returns IPMI completion code success or error.
+ */
+
 ipmi::Cc WhitelistFilter::filterMessage(ipmi::message::Request::ptr request)
 {
-    if (request->ctx->channel == ipmi::channelSystemIface && restrictedMode)
+    /* Getting hostIdx for all IPMI devices like hosts, debugcard and other
+   devices from ipmi::message::Request and call postInit() to get the
+   restriction mode for all the IPMI commands */
+
+    size_t hostIdx = request->ctx->hostIdx;
+
+    if (request->ctx->channel == ipmi::channelSystemIface &&
+        restrictedMode[hostIdx])
     {
         if (!std::binary_search(
                 whitelist.cbegin(), whitelist.cend(),
@@ -165,6 +236,7 @@
             log<level::ERR>("Net function not whitelisted",
                             entry("NETFN=0x%X", int(request->ctx->netFn)),
                             entry("CMD=0x%X", int(request->ctx->cmd)));
+
             return ipmi::ccInsufficientPrivilege;
         }
     }