Move getPortInfo to Redfish Utility
Plan to use getPortInfo() to get the SSH SerialConsole in the
ComputerSystem.
This commit moves the getPortInfo functionality into the redfish
utility.
Tested: manually tested on Witherspoon system, there is no change in
output. Run Redfish validator, no error found.
Before:
"HTTPS": {
"Certificates": {
"@odata.id": "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/
Certificates"
},
"Port": 443,
"ProtocolEnabled": true
},
"IPMI": {
"Port": 623,
"ProtocolEnabled": true
},
"SSH": {
"Port": 22,
"ProtocolEnabled": true
}
After:
"HTTPS": {
"Certificates": {
"@odata.id": "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/
Certificates"
},
"Port": 443,
"ProtocolEnabled": true
},
"IPMI": {
"Port": 623,
"ProtocolEnabled": true
},
"SSH": {
"Port": 22,
"ProtocolEnabled": true
}
Change-Id: I126827fbbecec59adcf630b88e31bc5ff8151588
Signed-off-by: Abhishek Patel <Abhishek.Patel@ibm.com>
diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp
index 6c28b9e..0fd6eba 100644
--- a/redfish-core/lib/network_protocol.hpp
+++ b/redfish-core/lib/network_protocol.hpp
@@ -17,6 +17,7 @@
#include "error_messages.hpp"
#include "openbmc_dbus_rest.hpp"
+#include "redfish_util.hpp"
#include <app.hpp>
#include <registries/privilege_registry.hpp>
@@ -30,35 +31,7 @@
void getNTPProtocolEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp);
std::string getHostName();
-enum NetworkProtocolUnitStructFields
-{
- NET_PROTO_UNIT_NAME,
- NET_PROTO_UNIT_DESC,
- NET_PROTO_UNIT_LOAD_STATE,
- NET_PROTO_UNIT_ACTIVE_STATE,
- NET_PROTO_UNIT_SUB_STATE,
- NET_PROTO_UNIT_DEVICE,
- NET_PROTO_UNIT_OBJ_PATH,
- NET_PROTO_UNIT_ALWAYS_0,
- NET_PROTO_UNIT_ALWAYS_EMPTY,
- NET_PROTO_UNIT_ALWAYS_ROOT_PATH
-};
-
-enum NetworkProtocolListenResponseElements
-{
- NET_PROTO_LISTEN_TYPE,
- NET_PROTO_LISTEN_STREAM
-};
-
-/**
- * @brief D-Bus Unit structure returned in array from ListUnits Method
- */
-using UnitStruct =
- std::tuple<std::string, std::string, std::string, std::string, std::string,
- std::string, sdbusplus::message::object_path, uint32_t,
- std::string, sdbusplus::message::object_path>;
-
-const static std::array<std::pair<const char*, const char*>, 3> protocolToDBus{
+const static std::array<std::pair<std::string, std::string>, 3> protocolToDBus{
{{"SSH", "dropbear"}, {"HTTPS", "bmcweb"}, {"IPMI", "phosphor-ipmi-net"}}};
inline void
@@ -191,114 +164,48 @@
Privileges effectiveUserPrivileges =
redfish::getUserPrivileges(req.userRole);
- crow::connections::systemBus->async_method_call(
- [asyncResp,
- &effectiveUserPrivileges](const boost::system::error_code e,
- const std::vector<UnitStruct>& r) {
- if (e)
- {
- asyncResp->res.jsonValue = nlohmann::json::object();
- messages::internalError(asyncResp->res);
- return;
- }
- // /redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates is
- // something only ConfigureManager can access then only display when
- // the user has permissions ConfigureManager
- if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
- effectiveUserPrivileges))
- {
- asyncResp->res.jsonValue["HTTPS"]["Certificates"] = {
- {"@odata.id", "/redfish/v1/Managers/bmc/NetworkProtocol/"
- "HTTPS/Certificates"}};
- }
- for (auto& unit : r)
- {
- /* Only traverse through <xyz>.socket units */
- const std::string& unitName =
- std::get<NET_PROTO_UNIT_NAME>(unit);
- if (!boost::ends_with(unitName, ".socket"))
+ // /redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates is
+ // something only ConfigureManager can access then only display when
+ // the user has permissions ConfigureManager
+ if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
+ effectiveUserPrivileges))
+ {
+ asyncResp->res.jsonValue["HTTPS"]["Certificates"] = {
+ {"@odata.id",
+ "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"}};
+ }
+
+ for (const auto& protocol : protocolToDBus)
+ {
+ const std::string& protocolName = protocol.first;
+ const std::string& serviceName = protocol.second;
+ getPortStatusAndPath(
+ serviceName,
+ [asyncResp, protocolName](const boost::system::error_code ec,
+ const std::string& socketPath,
+ bool isProtocolEnabled) {
+ if (ec)
{
- continue;
+ messages::internalError(asyncResp->res);
+ return;
}
-
- for (auto& kv : protocolToDBus)
- {
- // We are interested in services, which starts with
- // mapped service name
- if (!boost::starts_with(unitName, kv.second))
- {
- continue;
- }
- const char* rfServiceKey = kv.first;
- const std::string& socketPath =
- std::get<NET_PROTO_UNIT_OBJ_PATH>(unit);
- const std::string& unitState =
- std::get<NET_PROTO_UNIT_SUB_STATE>(unit);
-
- asyncResp->res.jsonValue[rfServiceKey]["ProtocolEnabled"] =
- (unitState == "running") || (unitState == "listening");
-
- crow::connections::systemBus->async_method_call(
- [asyncResp, rfServiceKey{std::string(rfServiceKey)}](
- const boost::system::error_code ec,
- const std::variant<std::vector<
- std::tuple<std::string, std::string>>>& resp) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- const std::vector<
- std::tuple<std::string, std::string>>*
- responsePtr = std::get_if<std::vector<
- std::tuple<std::string, std::string>>>(
- &resp);
- if (responsePtr == nullptr ||
- responsePtr->size() < 1)
- {
- return;
- }
-
- const std::string& listenStream =
- std::get<NET_PROTO_LISTEN_STREAM>(
- (*responsePtr)[0]);
- std::size_t lastColonPos = listenStream.rfind(':');
- if (lastColonPos == std::string::npos)
- {
- // Not a port
- return;
- }
- std::string portStr =
- listenStream.substr(lastColonPos + 1);
- if (portStr.empty())
- {
- return;
- }
- char* endPtr = nullptr;
- errno = 0;
- // Use strtol instead of stroi to avoid
- // exceptions
- long port =
- std::strtol(portStr.c_str(), &endPtr, 10);
- if ((errno == 0) && (*endPtr == '\0'))
- {
- asyncResp->res.jsonValue[rfServiceKey]["Port"] =
- port;
- }
+ asyncResp->res.jsonValue[protocolName]["ProtocolEnabled"] =
+ isProtocolEnabled;
+ getPortNumber(
+ socketPath,
+ [asyncResp, protocolName](
+ const boost::system::error_code ec, int portNumber) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
return;
- },
- "org.freedesktop.systemd1", socketPath,
- "org.freedesktop.DBus.Properties", "Get",
- "org.freedesktop.systemd1.Socket", "Listen");
-
- // We found service, break the inner loop.
- break;
- }
- }
- },
- "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager", "ListUnits");
-}
+ }
+ asyncResp->res.jsonValue[protocolName]["Port"] =
+ portNumber;
+ });
+ });
+ }
+} // namespace redfish
#ifdef BMCWEB_ALLOW_DEPRECATED_HOSTNAME_PATCH
inline void
diff --git a/redfish-core/lib/redfish_util.hpp b/redfish-core/lib/redfish_util.hpp
index 5494a23..60626a5 100644
--- a/redfish-core/lib/redfish_util.hpp
+++ b/redfish-core/lib/redfish_util.hpp
@@ -19,6 +19,34 @@
namespace redfish
{
+enum NetworkProtocolUnitStructFields
+{
+ NET_PROTO_UNIT_NAME,
+ NET_PROTO_UNIT_DESC,
+ NET_PROTO_UNIT_LOAD_STATE,
+ NET_PROTO_UNIT_ACTIVE_STATE,
+ NET_PROTO_UNIT_SUB_STATE,
+ NET_PROTO_UNIT_DEVICE,
+ NET_PROTO_UNIT_OBJ_PATH,
+ NET_PROTO_UNIT_ALWAYS_0,
+ NET_PROTO_UNIT_ALWAYS_EMPTY,
+ NET_PROTO_UNIT_ALWAYS_ROOT_PATH
+};
+
+enum NetworkProtocolListenResponseElements
+{
+ NET_PROTO_LISTEN_TYPE,
+ NET_PROTO_LISTEN_STREAM
+};
+
+/**
+ * @brief D-Bus Unit structure returned in array from ListUnits Method
+ */
+using UnitStruct =
+ std::tuple<std::string, std::string, std::string, std::string, std::string,
+ std::string, sdbusplus::message::object_path, uint32_t,
+ std::string, sdbusplus::message::object_path>;
+
template <typename CallbackFunc>
void getMainChassisId(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
CallbackFunc&& callback)
@@ -59,5 +87,141 @@
"xyz.openbmc_project.Inventory.Item.Board",
"xyz.openbmc_project.Inventory.Item.Chassis"});
}
+
+template <typename CallbackFunc>
+void getPortStatusAndPath(const std::string& serviceName,
+ CallbackFunc&& callback)
+{
+ crow::connections::systemBus->async_method_call(
+ [serviceName,
+ callback{std::move(callback)}](const boost::system::error_code ec,
+ const std::vector<UnitStruct>& r) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << ec;
+ // return error code
+ callback(ec, "", false);
+ return;
+ }
+
+ for (const UnitStruct& unit : r)
+ {
+ // Only traverse through <xyz>.socket units
+ const std::string& unitName =
+ std::get<NET_PROTO_UNIT_NAME>(unit);
+
+ // find "." into unitsName
+ size_t lastCharPos = unitName.rfind('.');
+ if (lastCharPos == std::string::npos)
+ {
+ continue;
+ }
+
+ // is unitsName end with ".socket"
+ std::string unitNameEnd = unitName.substr(lastCharPos);
+ if (unitNameEnd.compare(".socket") != 0)
+ {
+ continue;
+ }
+
+ // find "@" into unitsName
+ if (size_t atCharPos = unitName.rfind('@');
+ atCharPos != std::string::npos)
+ {
+ lastCharPos = atCharPos;
+ }
+
+ // unitsName without "@eth(x).socket", only <xyz>
+ // unitsName without ".socket", only <xyz>
+ std::string unitNameStr = unitName.substr(0, lastCharPos);
+
+ // We are interested in services, which starts with
+ // mapped service name
+ if (unitNameStr != serviceName)
+ {
+ continue;
+ }
+
+ const std::string& socketPath =
+ std::get<NET_PROTO_UNIT_OBJ_PATH>(unit);
+ const std::string& unitState =
+ std::get<NET_PROTO_UNIT_SUB_STATE>(unit);
+
+ bool isProtocolEnabled =
+ ((unitState == "running") || (unitState == "listening"));
+ // We found service, return from inner loop.
+ callback(ec, socketPath, isProtocolEnabled);
+ return;
+ }
+
+ // no service foudn, throw error
+ boost::system::error_code ec1 =
+ boost::system::errc::make_error_code(
+ boost::system::errc::no_such_process);
+ // return error code
+ callback(ec1, "", false);
+ BMCWEB_LOG_ERROR << ec1;
+ },
+ "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager", "ListUnits");
+}
+
+template <typename CallbackFunc>
+void getPortNumber(const std::string& socketPath, CallbackFunc&& callback)
+{
+ crow::connections::systemBus->async_method_call(
+ [callback{std::move(callback)}](
+ const boost::system::error_code ec,
+ const std::variant<
+ std::vector<std::tuple<std::string, std::string>>>& resp) {
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR << ec;
+ callback(ec, 0);
+ return;
+ }
+ const std::vector<
+ std::tuple<std::string, std::string>>* responsePtr =
+ std::get_if<std::vector<std::tuple<std::string, std::string>>>(
+ &resp);
+ if (responsePtr == nullptr || responsePtr->size() < 1)
+ {
+ // Network Protocol Listen Response Elements is empty
+ boost::system::error_code ec1 =
+ boost::system::errc::make_error_code(
+ boost::system::errc::bad_message);
+ // return error code
+ callback(ec1, 0);
+ BMCWEB_LOG_ERROR << ec1;
+ return;
+ }
+ const std::string& listenStream =
+ std::get<NET_PROTO_LISTEN_STREAM>((*responsePtr)[0]);
+ const char* pa = &listenStream[listenStream.rfind(':') + 1];
+ int port{0};
+ if (auto [p, ec2] = std::from_chars(pa, nullptr, port);
+ ec2 != std::errc())
+ {
+ // there is only two possibility invalid_argument and
+ // result_out_of_range
+ boost::system::error_code ec3 =
+ boost::system::errc::make_error_code(
+ boost::system::errc::invalid_argument);
+ if (ec2 == std::errc::result_out_of_range)
+ {
+ ec3 = boost::system::errc::make_error_code(
+ boost::system::errc::result_out_of_range);
+ }
+ // return error code
+ callback(ec3, 0);
+ BMCWEB_LOG_ERROR << ec3;
+ }
+ callback(ec, port);
+ },
+ "org.freedesktop.systemd1", socketPath,
+ "org.freedesktop.DBus.Properties", "Get",
+ "org.freedesktop.systemd1.Socket", "Listen");
+}
+
} // namespace redfish
#endif