summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Kosenkov <NKosenkov@IBS.RU>2022-09-16 11:34:26 +0300
committerNikita Kosenkov <NKosenkov@IBS.RU>2022-09-16 11:34:26 +0300
commitf016086520f156980571e67b37a20282bcb536fd (patch)
treedb30895114214de523e12e85113fcfd7010f80f2
parent95af289642bc61acdc6fefa01a0ea8056a32b89d (diff)
downloadopenbmc-f016086520f156980571e67b37a20282bcb536fd.tar.xz
SILABMC-323: Added management port and activity status of the SSH and IPMI protocols
-rw-r--r--meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb3
-rw-r--r--meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb/0010-Support-setting-IPMI-and-SSH-port-via-redfish.patch701
-rw-r--r--meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend1
-rw-r--r--meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager/0001-Added-dependencies-to-controlled-services.patch60
-rw-r--r--meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_%.bbappend5
5 files changed, 769 insertions, 1 deletions
diff --git a/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb b/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb
index 73a541f635..64928f864a 100644
--- a/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb
+++ b/meta-ibs/meta-common/recipes-ibs/packagegroups/packagegroup-ibs-apps.bb
@@ -51,12 +51,13 @@ RDEPENDS:${PN}-system = " \
phosphor-hostlogger \
phosphor-power-regulators \
phosphor-ipmi-flash \
- phosphor-dbus-monitor \
+ phosphor-dbus-monitor \
phosphor-dbus-monitor-config \
${PN}-interface \
${PN}-cli \
peci-pcie \
phosphor-sel-logger \
+ srvcfg-manager \
"
SUMMARY:${PN}-interface = "Interfaces"
diff --git a/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb/0010-Support-setting-IPMI-and-SSH-port-via-redfish.patch b/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb/0010-Support-setting-IPMI-and-SSH-port-via-redfish.patch
new file mode 100644
index 0000000000..015be55b61
--- /dev/null
+++ b/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb/0010-Support-setting-IPMI-and-SSH-port-via-redfish.patch
@@ -0,0 +1,701 @@
+From 6af17d9d37ed4ef06012d22fa91e477633cfb882 Mon Sep 17 00:00:00 2001
+From: Nikita Kosenkov <NKosenkov@IBS.RU>
+Date: Fri, 16 Sep 2022 10:54:15 +0300
+Subject: [PATCH] Support setting IPMI and SSH port via redfish
+
+---
+ meson.build | 1 +
+ redfish-core/include/utils/service_utils.hpp | 270 +++++++++++++++++++
+ redfish-core/lib/network_protocol.hpp | 146 +++-------
+ redfish-core/lib/redfish_util.hpp | 128 ---------
+ redfish-core/ut/service_utils_test.cpp | 33 +++
+ 5 files changed, 342 insertions(+), 236 deletions(-)
+ create mode 100644 redfish-core/include/utils/service_utils.hpp
+ create mode 100644 redfish-core/ut/service_utils_test.cpp
+
+diff --git a/meson.build b/meson.build
+index c1d253fb..7e01c9df 100644
+--- a/meson.build
++++ b/meson.build
+@@ -426,6 +426,7 @@ srcfiles_unittest = [
+ 'redfish-core/ut/lock_test.cpp',
+ 'redfish-core/ut/privileges_test.cpp',
+ 'redfish-core/ut/registries_test.cpp',
++ 'redfish-core/ut/service_utils_test.cpp',
+ 'redfish-core/ut/stl_utils_test.cpp',
+ 'redfish-core/ut/time_utils_test.cpp',
+ 'src/crow_getroutes_test.cpp',
+diff --git a/redfish-core/include/utils/service_utils.hpp b/redfish-core/include/utils/service_utils.hpp
+new file mode 100644
+index 00000000..0ae93732
+--- /dev/null
++++ b/redfish-core/include/utils/service_utils.hpp
+@@ -0,0 +1,270 @@
++#pragma once
++
++#include "app.hpp"
++#include "dbus_utility.hpp"
++#include "error_messages.hpp"
++
++#include <nlohmann/json.hpp>
++#include <sdbusplus/asio/property.hpp>
++#include <sdbusplus/message/types.hpp>
++
++#include <string>
++#include <string_view>
++
++namespace redfish
++{
++namespace service_util
++{
++namespace details
++{
++
++bool matchService(const sdbusplus::message::object_path& objPath,
++ const std::string_view serviceName)
++{
++ // For service named as <unitName>@<instanceName>, only compare the unitName
++ // part. Object path is automatically decoded as it is encoded by sdbusplus.
++ std::string fullUnitName = objPath.filename();
++ size_t pos = fullUnitName.rfind('@');
++ return std::string_view(fullUnitName).substr(0, pos) == serviceName;
++}
++
++enum class FindError
++{
++ Ok,
++ NotFound,
++ DBusError,
++};
++
++template <typename Callback>
++inline void findMatchedServices(const std::string& serviceName,
++ Callback&& callback)
++{
++ crow::connections::systemBus->async_method_call(
++ [serviceName,
++ callback](const boost::system::error_code ec,
++ const dbus::utility::ManagedObjectType& objects) {
++ if (ec)
++ {
++ callback(FindError::DBusError, nullptr);
++ return;
++ }
++
++ bool serviceFound = false;
++ for (const auto& object : objects)
++ {
++ if (!matchService(object.first, serviceName))
++ {
++ continue;
++ }
++
++ serviceFound = true;
++ // The return value indicates whether to break the loop or not,
++ // used for get property
++ if (callback(FindError::Ok, &object))
++ {
++ return;
++ }
++ }
++
++ if (!serviceFound)
++ {
++ callback(FindError::NotFound, nullptr);
++ }
++ },
++ "xyz.openbmc_project.Control.Service.Manager",
++ "/xyz/openbmc_project/control/service",
++ "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
++}
++
++template <typename T>
++inline T
++ getPropertyFromInterface(const dbus::utility::DBusInteracesMap& interfaces,
++ const std::string& interfaceName,
++ const std::string& propertyName)
++{
++ for (const auto& [interface, properties] : interfaces)
++ {
++ if (interface != interfaceName)
++ {
++ continue;
++ }
++
++ for (const auto& [key, val] : properties)
++ {
++ if (key != propertyName)
++ {
++ continue;
++ }
++
++ const auto* value = std::get_if<T>(&val);
++ if (value != nullptr)
++ {
++ return *value;
++ }
++ return T{};
++ }
++ }
++ return T{};
++}
++
++void setEnabledAndRunning(
++ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const dbus::utility::ManagedObjectType::value_type* object, bool enabled)
++{
++ auto errorCallback = [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ };
++ sdbusplus::asio::setProperty(
++ *crow::connections::systemBus,
++ "xyz.openbmc_project.Control.Service.Manager", object->first,
++ "xyz.openbmc_project.Control.Service.Attributes", "Running", enabled,
++ errorCallback);
++ sdbusplus::asio::setProperty(
++ *crow::connections::systemBus,
++ "xyz.openbmc_project.Control.Service.Manager", object->first,
++ "xyz.openbmc_project.Control.Service.Attributes", "Enabled", enabled,
++ errorCallback);
++}
++} // namespace details
++
++void getEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& serviceName,
++ const nlohmann::json::json_pointer& valueJsonPtr)
++{
++ details::findMatchedServices(
++ serviceName,
++ [asyncResp, valueJsonPtr](
++ const details::FindError error,
++ const dbus::utility::ManagedObjectType::value_type* object) -> int {
++ if (error == details::FindError::DBusError)
++ {
++ messages::internalError(asyncResp->res);
++ return 0;
++ }
++ if (error == details::FindError::NotFound)
++ {
++ // Do nothing
++ return 0;
++ }
++
++ bool enabled = details::getPropertyFromInterface<bool>(
++ object->second,
++ "xyz.openbmc_project.Control.Service.Attributes", "Running");
++ asyncResp->res.jsonValue[valueJsonPtr] = enabled;
++
++ // If one of the service instance is running, show it as Enabled
++ // in redfish.
++ if (enabled)
++ {
++ return 1;
++ }
++ return 0;
++ });
++}
++
++void getPortNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& serviceName,
++ const nlohmann::json::json_pointer& valueJsonPtr)
++{
++ details::findMatchedServices(
++ serviceName,
++ [asyncResp, valueJsonPtr](
++ const details::FindError error,
++ const dbus::utility::ManagedObjectType::value_type* object) -> int {
++ if (error == details::FindError::DBusError)
++ {
++ messages::internalError(asyncResp->res);
++ return 0;
++ }
++ if (error == details::FindError::NotFound)
++ {
++ // Do nothing
++ return 0;
++ }
++
++ uint16_t port = details::getPropertyFromInterface<uint16_t>(
++ object->second,
++ "xyz.openbmc_project.Control.Service.SocketAttributes", "Port");
++ asyncResp->res.jsonValue[valueJsonPtr] = port;
++
++ // For service with multiple instances, return the port of first
++ // valid instance found as redfish only support one port value, they
++ // should be same
++ if (port != 0)
++ {
++ return 1;
++ }
++ return 0;
++ });
++}
++
++void setEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& propertyName, const std::string& serviceName,
++ const bool enabled)
++{
++ details::findMatchedServices(
++ serviceName,
++ [asyncResp, propertyName, enabled](
++ const details::FindError error,
++ const dbus::utility::ManagedObjectType::value_type* object) -> int {
++ if (error == details::FindError::DBusError)
++ {
++ messages::internalError(asyncResp->res);
++ return 0;
++ }
++ if (error == details::FindError::NotFound)
++ {
++ // The Redfish property will not be populated in if service is
++ // not found, return PropertyUnknown for PATCH request
++ messages::propertyUnknown(asyncResp->res, propertyName);
++ return 0;
++ }
++
++ details::setEnabledAndRunning(asyncResp, object, enabled);
++ return 0;
++ });
++}
++
++void setPortNumber(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
++ const std::string& propertyName,
++ const std::string& serviceName, const uint16_t portNumber)
++{
++ details::findMatchedServices(
++ serviceName,
++ [asyncResp, propertyName, portNumber](
++ const details::FindError error,
++ const dbus::utility::ManagedObjectType::value_type* object) -> int {
++ if (error == details::FindError::DBusError)
++ {
++ messages::internalError(asyncResp->res);
++ return 0;
++ }
++ if (error == details::FindError::NotFound)
++ {
++ // The Redfish property will not be populated in if service is
++ // not found, return PropertyUnknown for PATCH request
++ messages::propertyUnknown(asyncResp->res, propertyName);
++ return 0;
++ }
++
++ sdbusplus::asio::setProperty(
++ *crow::connections::systemBus,
++ "xyz.openbmc_project.Control.Service.Manager", object->first,
++ "xyz.openbmc_project.Control.Service.SocketAttributes", "Port",
++ portNumber, [asyncResp](const boost::system::error_code ec) {
++ if (ec)
++ {
++ messages::internalError(asyncResp->res);
++ return;
++ }
++ });
++ return 0;
++ });
++}
++
++} // namespace service_util
++} // namespace redfish
+diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp
+index f99cfd36..8b7597ff 100644
+--- a/redfish-core/lib/network_protocol.hpp
++++ b/redfish-core/lib/network_protocol.hpp
+@@ -25,6 +25,7 @@
+ #include <registries/privilege_registry.hpp>
+ #include <sdbusplus/asio/property.hpp>
+ #include <utils/json_utils.hpp>
++#include <utils/service_utils.hpp>
+ #include <utils/stl_utils.hpp>
+
+ #include <optional>
+@@ -44,6 +45,22 @@ static constexpr std::array<std::pair<const char*, const char*>, 3>
+ {"HTTPS", httpsServiceName},
+ {"IPMI", ipmiServiceName}}};
+
++inline void
++ getServiceStatus(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
++{
++ for (const auto& [protocolName, serviceName] : protocolToService)
++ {
++ service_util::getEnabled(
++ asyncResp, serviceName,
++ nlohmann::json::json_pointer(std::string("/") + protocolName +
++ "/ProtocolEnabled"));
++ service_util::getPortNumber(
++ asyncResp, serviceName,
++ nlohmann::json::json_pointer(std::string("/") + protocolName +
++ "/Port"));
++ }
++}
++
+ inline void extractNTPServersAndDomainNamesData(
+ const dbus::utility::ManagedObjectType& dbusData,
+ std::vector<std::string>& ntpData, std::vector<std::string>& dnData)
+@@ -136,6 +153,7 @@ inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ asyncResp->res.jsonValue["HostName"] = hostName;
+
+ getNTPProtocolEnabled(asyncResp);
++ getServiceStatus(asyncResp);
+
+ getEthernetIfaceData(
+ [hostName, asyncResp](const bool& success,
+@@ -172,45 +190,7 @@ inline void getNetworkData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ asyncResp->res.jsonValue["HTTPS"]["Certificates"]["@odata.id"] =
+ "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
+ }
+-
+- for (const auto& protocol : protocolToService)
+- {
+- 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 the service is not installed, that is not an error
+- if (ec == boost::system::errc::no_such_process)
+- {
+- asyncResp->res.jsonValue[protocolName]["Port"] =
+- nlohmann::detail::value_t::null;
+- asyncResp->res.jsonValue[protocolName]["ProtocolEnabled"] =
+- false;
+- return;
+- }
+- if (ec)
+- {
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- asyncResp->res.jsonValue[protocolName]["ProtocolEnabled"] =
+- isProtocolEnabled;
+- getPortNumber(socketPath, [asyncResp, protocolName](
+- const boost::system::error_code ec2,
+- int portNumber) {
+- if (ec2)
+- {
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- asyncResp->res.jsonValue[protocolName]["Port"] = portNumber;
+- });
+- });
+- }
+-} // namespace redfish
++}
+
+ inline void handleNTPProtocolEnabled(
+ const bool& ntpEnabled, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+@@ -363,61 +343,6 @@ inline void
+ "xyz.openbmc_project.Network.EthernetInterface"});
+ }
+
+-inline void
+- handleProtocolEnabled(const bool protocolEnabled,
+- const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+- const std::string& netBasePath)
+-{
+- crow::connections::systemBus->async_method_call(
+- [protocolEnabled, asyncResp,
+- netBasePath](const boost::system::error_code ec,
+- const dbus::utility::MapperGetSubTreeResponse& subtree) {
+- if (ec)
+- {
+- messages::internalError(asyncResp->res);
+- return;
+- }
+-
+- for (const auto& entry : subtree)
+- {
+- if (boost::algorithm::starts_with(entry.first, netBasePath))
+- {
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec2) {
+- if (ec2)
+- {
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- entry.second.begin()->first, entry.first,
+- "org.freedesktop.DBus.Properties", "Set",
+- "xyz.openbmc_project.Control.Service.Attributes", "Running",
+- dbus::utility::DbusVariantType{protocolEnabled});
+-
+- crow::connections::systemBus->async_method_call(
+- [asyncResp](const boost::system::error_code ec2) {
+- if (ec2)
+- {
+- messages::internalError(asyncResp->res);
+- return;
+- }
+- },
+- entry.second.begin()->first, entry.first,
+- "org.freedesktop.DBus.Properties", "Set",
+- "xyz.openbmc_project.Control.Service.Attributes", "Enabled",
+- dbus::utility::DbusVariantType{protocolEnabled});
+- }
+- }
+- },
+- "xyz.openbmc_project.ObjectMapper",
+- "/xyz/openbmc_project/object_mapper",
+- "xyz.openbmc_project.ObjectMapper", "GetSubTree",
+- "/xyz/openbmc_project/control/service", 0,
+- std::array<const char*, 1>{
+- "xyz.openbmc_project.Control.Service.Attributes"});
+-}
+-
+ inline std::string getHostName()
+ {
+ std::string hostName;
+@@ -457,14 +382,6 @@ inline void
+ });
+ }
+
+-inline std::string encodeServiceObjectPath(const std::string& serviceName)
+-{
+- sdbusplus::message::object_path objPath(
+- "/xyz/openbmc_project/control/service");
+- objPath /= serviceName;
+- return objPath.str;
+-}
+-
+ inline void requestRoutesNetworkProtocol(App& app)
+ {
+ BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/NetworkProtocol/")
+@@ -480,7 +397,9 @@ inline void requestRoutesNetworkProtocol(App& app)
+ std::optional<std::vector<nlohmann::json>> ntpServerObjects;
+ std::optional<bool> ntpEnabled;
+ std::optional<bool> ipmiEnabled;
++ std::optional<uint16_t> ipmiPort;
+ std::optional<bool> sshEnabled;
++ std::optional<uint16_t> sshPort;
+
+ // clang-format off
+ if (!json_util::readJsonPatch(
+@@ -489,7 +408,9 @@ inline void requestRoutesNetworkProtocol(App& app)
+ "NTP/NTPServers", ntpServerObjects,
+ "NTP/ProtocolEnabled", ntpEnabled,
+ "IPMI/ProtocolEnabled", ipmiEnabled,
+- "SSH/ProtocolEnabled", sshEnabled))
++ "IPMI/Port", ipmiPort,
++ "SSH/ProtocolEnabled", sshEnabled,
++ "SSH/Port", sshPort))
+ {
+ return;
+ }
+@@ -525,15 +446,24 @@ inline void requestRoutesNetworkProtocol(App& app)
+
+ if (ipmiEnabled)
+ {
+- handleProtocolEnabled(
+- *ipmiEnabled, asyncResp,
+- encodeServiceObjectPath(std::string(ipmiServiceName) + '@'));
++ service_util::setEnabled(asyncResp, "IPMI/ProtocolEnabled",
++ ipmiServiceName, *ipmiEnabled);
++ }
++ if (ipmiPort)
++ {
++ service_util::setPortNumber(asyncResp, "IPMI/Port", ipmiServiceName,
++ *ipmiPort);
+ }
+
+ if (sshEnabled)
+ {
+- handleProtocolEnabled(*sshEnabled, asyncResp,
+- encodeServiceObjectPath(sshServiceName));
++ service_util::setEnabled(asyncResp, "SSH/ProtocolEnabled",
++ sshServiceName, *sshEnabled);
++ }
++ if (sshPort)
++ {
++ service_util::setPortNumber(asyncResp, "SSH/Port", sshServiceName,
++ *sshPort);
+ }
+ });
+
+diff --git a/redfish-core/lib/redfish_util.hpp b/redfish-core/lib/redfish_util.hpp
+index 04d6ceb3..9b604ce9 100644
+--- a/redfish-core/lib/redfish_util.hpp
++++ b/redfish-core/lib/redfish_util.hpp
+@@ -93,133 +93,5 @@ void getMainChassisId(std::shared_ptr<bmcweb::AsyncResp> asyncResp,
+ "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::forward<CallbackFunc>(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 != ".socket")
+- {
+- 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)
+-{
+- sdbusplus::asio::getProperty<
+- std::vector<std::tuple<std::string, std::string>>>(
+- *crow::connections::systemBus, "org.freedesktop.systemd1", socketPath,
+- "org.freedesktop.systemd1.Socket", "Listen",
+- [callback{std::forward<CallbackFunc>(callback)}](
+- const boost::system::error_code ec,
+- const std::vector<std::tuple<std::string, std::string>>& resp) {
+- if (ec)
+- {
+- BMCWEB_LOG_ERROR << ec;
+- callback(ec, 0);
+- return;
+- }
+- if (resp.empty())
+- {
+- // 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>(resp[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);
+- });
+-}
+-
+ } // namespace redfish
+ #endif
+diff --git a/redfish-core/ut/service_utils_test.cpp b/redfish-core/ut/service_utils_test.cpp
+new file mode 100644
+index 00000000..01e5dcc9
+--- /dev/null
++++ b/redfish-core/ut/service_utils_test.cpp
+@@ -0,0 +1,33 @@
++#include "utils/service_utils.hpp"
++
++#include <gmock/gmock.h>
++
++namespace redfish
++{
++namespace service_util
++{
++
++TEST(ServiceUtilsTest, MatchServiceGood)
++{
++ using details::matchService;
++ sdbusplus::message::object_path basePath("/service/base/path");
++
++ EXPECT_TRUE(matchService(basePath / "serviceUnit", "serviceUnit"));
++ EXPECT_TRUE(matchService(basePath / "service-unit", "service-unit"));
++ EXPECT_TRUE(matchService(basePath / "serviceUnit@test", "serviceUnit"));
++ EXPECT_TRUE(matchService(basePath / "service-unit@test", "service-unit"));
++}
++
++TEST(ServiceUtilsTest, MatchServiceBad)
++{
++ using details::matchService;
++ sdbusplus::message::object_path basePath("/service/base/path");
++
++ EXPECT_FALSE(matchService(basePath / "serviceUnit", "service-unit"));
++ EXPECT_FALSE(matchService(basePath / "serviceUnitTest", "serviceUnit"));
++ EXPECT_FALSE(matchService(basePath / "serviceUnit@test", "service-unit"));
++ EXPECT_FALSE(matchService(basePath / "service-unit-test", "service-unit"));
++}
++
++} // namespace service_util
++} // namespace redfish
+--
+2.35.1
+
diff --git a/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend b/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
index b0cb53db12..4d62c06fdc 100644
--- a/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
+++ b/meta-ibs/meta-common/recipes-phosphor/interfaces/bmcweb_%.bbappend
@@ -12,6 +12,7 @@ SRC_URI += "\
file://0007-Add-additional-logs-about-time-sync.patch \
file://0008-Added-additional-log-about-NTP-sync-status.patch \
file://0009-Added-check-user-priv-before-creating-KVM-and-SOL.patch \
+ file://0010-Support-setting-IPMI-and-SSH-port-via-redfish.patch \
"
EXTRA_OEMESON += "\
diff --git a/meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager/0001-Added-dependencies-to-controlled-services.patch b/meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager/0001-Added-dependencies-to-controlled-services.patch
new file mode 100644
index 0000000000..701f942bda
--- /dev/null
+++ b/meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager/0001-Added-dependencies-to-controlled-services.patch
@@ -0,0 +1,60 @@
+From 32c702a97a69acefb3054165f51a4b7b9f102494 Mon Sep 17 00:00:00 2001
+From: Nikita Kosenkov <NKosenkov@IBS.RU>
+Date: Fri, 16 Sep 2022 10:56:47 +0300
+Subject: [PATCH] Added dependencies to controlled services
+
+---
+ src/main.cpp | 10 +++++++---
+ srvcfg-manager.service | 3 +++
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/src/main.cpp b/src/main.cpp
+index f104f22..b62c29e 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -278,14 +278,18 @@ void checkAndInit(sdbusplus::asio::object_server& server,
+ // Check whether systemd completed all the loading before initializing
+ conn->async_method_call(
+ [&server, &conn](boost::system::error_code ec,
+- const std::variant<uint64_t>& value) {
++ const std::variant<double_t>& value) {
+ if (ec)
+ {
+ lg2::error("async_method_call error: ListUnits failed: {EC}",
+ "EC", ec.value());
+ return;
+ }
+- if (std::get<uint64_t>(value))
++
++ auto systemdProgress = std::get<double_t>(value);
++
++ // If systemd units are running at 90% or more
++ if (systemdProgress >= 0.9)
+ {
+ if (!unitQueryStarted)
+ {
+@@ -320,7 +324,7 @@ void checkAndInit(sdbusplus::asio::object_server& server,
+ }
+ },
+ sysdService, sysdObjPath, dBusPropIntf, dBusGetMethod, sysdMgrIntf,
+- "FinishTimestamp");
++ "Progress");
+ }
+
+ int main()
+diff --git a/srvcfg-manager.service b/srvcfg-manager.service
+index b7b22df..1d272bc 100644
+--- a/srvcfg-manager.service
++++ b/srvcfg-manager.service
+@@ -1,5 +1,8 @@
+ [Unit]
+ Description=Daemon to control service configuration
++After=start-ipkvm.service bmcweb.service
++After=phosphor-ipmi-net@.service dropbear@.service
++After=phosphor-ipmi-kcs@.service obmc-console@.service
+
+ [Service]
+ Restart=always
+--
+2.35.1
+
diff --git a/meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_%.bbappend b/meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_%.bbappend
new file mode 100644
index 0000000000..9b7450bb19
--- /dev/null
+++ b/meta-ibs/meta-common/recipes-phosphor/srvcfg-manager/srvcfg-manager_%.bbappend
@@ -0,0 +1,5 @@
+FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
+
+SRC_URI += "\
+ file://0001-Added-dependencies-to-controlled-services.patch \
+ "