Add OEM IPMI commands to query node ID and node presence

These OEM IPMI commands are used on multi-node platform to query
info such as node ID, multi-node presence, node role.

Tested:
ipmitool raw 0x30 0x36 // Get multiNodeId
ipmitool raw 0x30 0x63 // Get multiNodePresence
ipmitool raw 0x30 0x33 // Get multiNodeRole

Change-Id: I54a78bb2ed37cf71770c7b9f7e3124f530fa9246
Signed-off-by: Qiang XU <qiang.xu@linux.intel.com>
diff --git a/src/multinodecommands.cpp b/src/multinodecommands.cpp
new file mode 100644
index 0000000..7e79fc0
--- /dev/null
+++ b/src/multinodecommands.cpp
@@ -0,0 +1,135 @@
+/*
+// Copyright (c) 2019 Intel 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 <ipmid/api.hpp>
+#include <ipmid/utils.hpp>
+#include <multinodecommands.hpp>
+#include <oemcommands.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/message/types.hpp>
+#include <string>
+
+namespace ipmi
+{
+void registerMultiNodeFunctions() __attribute__((constructor));
+
+std::optional<uint8_t> getMultiNodeInfo(std::string name)
+{
+    auto pdbus = getSdBus();
+    try
+    {
+        std::string service =
+            getService(*pdbus, multiNodeIntf, multiNodeObjPath);
+        Value dbusValue = getDbusProperty(*pdbus, service, multiNodeObjPath,
+                                          multiNodeIntf, name);
+        return std::get<uint8_t>(dbusValue);
+    }
+    catch (const std::exception& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "getMultiNodeInfo: can't get multi node info from dbus!",
+            phosphor::logging::entry("ERR=%s", e.what()));
+        return std::nullopt;
+    }
+}
+
+std::optional<uint8_t> getMultiNodeRole()
+{
+    auto pdbus = getSdBus();
+    try
+    {
+        std::string service =
+            getService(*pdbus, multiNodeIntf, multiNodeObjPath);
+        Value dbusValue = getDbusProperty(*pdbus, service, multiNodeObjPath,
+                                          multiNodeIntf, "NodeRole");
+        std::string valueStr = std::get<std::string>(dbusValue);
+        uint8_t value;
+        if (valueStr == "single")
+            value = static_cast<uint8_t>(NodeRole::single);
+        else if (valueStr == "master")
+            value = static_cast<uint8_t>(NodeRole::master);
+        else if (valueStr == "slave")
+            value = static_cast<uint8_t>(NodeRole::slave);
+        else if (valueStr == "arbitrating")
+            value = static_cast<uint8_t>(NodeRole::arbitrating);
+        return value;
+    }
+    catch (const std::exception& e)
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "getMultiNodeRole: can't get multi node role from dbus!",
+            phosphor::logging::entry("ERR=%s", e.what()));
+        return std::nullopt;
+    }
+}
+
+ipmi::RspType<uint8_t> ipmiGetMultiNodePresence()
+
+{
+    std::optional<uint8_t> nodeInfo = getMultiNodeInfo("NodePresence");
+    if (!nodeInfo)
+    {
+        return ipmi::responseResponseError();
+    }
+
+    return ipmi::responseSuccess(*nodeInfo);
+}
+
+ipmi::RspType<uint8_t> ipmiGetMultiNodeId()
+{
+    std::optional<uint8_t> nodeInfo = getMultiNodeInfo("NodeId");
+    if (!nodeInfo)
+    {
+        return ipmi::responseResponseError();
+    }
+
+    return ipmi::responseSuccess(*nodeInfo);
+}
+
+ipmi::RspType<uint8_t> ipmiGetMultiNodeRole()
+{
+    std::optional<uint8_t> nodeInfo = getMultiNodeRole();
+    if (!nodeInfo)
+    {
+        return ipmi::responseResponseError();
+    }
+
+    return ipmi::responseSuccess(*nodeInfo);
+}
+
+void registerMultiNodeFunctions(void)
+{
+    phosphor::logging::log<phosphor::logging::level::INFO>(
+        "Registering MultiNode commands");
+
+    ipmi::registerHandler(
+        ipmi::prioOemBase, ipmi::netFnOemOne,
+        static_cast<ipmi::Cmd>(IPMINetfnMultiNodeCmd::cmdGetMultiNodePresence),
+        ipmi::Privilege::User, ipmiGetMultiNodePresence);
+
+    ipmi::registerHandler(
+        ipmi::prioOemBase, ipmi::netFnOemOne,
+        static_cast<ipmi::Cmd>(IPMINetfnMultiNodeCmd::cmdGetMultiNodeId),
+        ipmi::Privilege::User, ipmiGetMultiNodeId);
+
+    ipmi::registerHandler(
+        ipmi::prioOemBase, ipmi::netFnOemOne,
+        static_cast<ipmi::Cmd>(IPMINetfnMultiNodeCmd::cmdGetMultiNodeRole),
+        ipmi::Privilege::User, ipmiGetMultiNodeRole);
+}
+
+} // namespace ipmi