Refactor Manager Network Protocol to use D-Bus
ManagerNetworkProtocol schema now operates on data fetched from D-Bus
instead of netstat. This also prepares it for PATCH implementation that
will allow services ports & state manipulation.
Does not break other components. ManagerNetworkProtocol however will
display only services available on D-Bus. Tested on platform and x86
machine.
Change-Id: Id693dfbdd870801feb06d08833a261bdbb4285e6
Signed-off-by: Kowalski, Kamil <kamil.kowalski@intel.com>
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp
index 235b2b8..758d2f4 100644
--- a/redfish-core/lib/network_protocol.hpp
+++ b/redfish-core/lib/network_protocol.hpp
@@ -15,10 +15,100 @@
*/
#pragma once
+#include "error_messages.hpp"
#include "node.hpp"
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>;
+
+struct ServiceConfiguration {
+ std::string serviceName;
+ std::string socketPath;
+};
+
+class OnDemandNetworkProtocolProvider {
+ public:
+ template <typename CallbackFunc>
+ static void getServices(CallbackFunc&& callback) {
+ crow::connections::systemBus->async_method_call(
+ [callback{std::move(callback)}](const boost::system::error_code ec,
+ const std::vector<UnitStruct>& resp) {
+ if (ec) {
+ callback(false, resp);
+ } else {
+ callback(true, resp);
+ }
+ },
+ "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager", "ListUnits");
+ }
+
+ template <typename CallbackFunc>
+ static void getSocketListenPort(const std::string& path,
+ CallbackFunc&& callback) {
+ crow::connections::systemBus->async_method_call(
+ [callback{std::move(callback)}](
+ const boost::system::error_code ec,
+ const sdbusplus::message::variant<
+ std::vector<std::tuple<std::string, std::string>>>& resp) {
+ if (ec) {
+ callback(false, false, 0);
+ } else {
+ auto responsePtr = mapbox::getPtr<
+ const std::vector<std::tuple<std::string, std::string>>>(resp);
+
+ std::string listenStream =
+ std::get<NET_PROTO_LISTEN_STREAM>((*responsePtr)[0]);
+ auto lastColonPos = listenStream.rfind(":");
+ if (lastColonPos != std::string::npos) {
+ std::string portStr = listenStream.substr(lastColonPos + 1);
+ char* endPtr;
+ // Use strtol instead of stroi to avoid exceptions
+ long port = std::strtol(portStr.c_str(), &endPtr, 10);
+
+ if (*endPtr != '\0' || portStr.empty()) {
+ // Invalid value
+ callback(true, false, 0);
+ } else {
+ // Everything OK
+ callback(true, true, port);
+ }
+ } else {
+ // Not a port
+ callback(true, false, 0);
+ }
+ }
+ },
+ "org.freedesktop.systemd1", path, "org.freedesktop.DBus.Properties",
+ "Get", "org.freedesktop.systemd1.Socket", "Listen");
+ }
+};
+
class NetworkProtocol : public Node {
public:
NetworkProtocol(CrowApp& app)
@@ -35,6 +125,10 @@
Node::json["Status"]["HealthRollup"] = "OK";
Node::json["Status"]["State"] = "Enabled";
+ for (auto& protocol : protocolToDBus) {
+ Node::json[protocol.first]["ProtocolEnabled"] = false;
+ }
+
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
{boost::beast::http::verb::head, {{"Login"}}},
@@ -47,10 +141,9 @@
private:
void doGet(crow::Response& res, const crow::Request& req,
const std::vector<std::string>& params) override {
- refreshProtocolsState();
- Node::json["HostName"] = getHostName();
- res.jsonValue = Node::json;
- res.end();
+ std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
+
+ getData(asyncResp);
}
std::string getHostName() const {
@@ -63,42 +156,69 @@
return hostName;
}
- void refreshProtocolsState() {
- refreshListeningPorts();
- for (auto& kv : portToProtocolMap) {
- Node::json[kv.second]["Port"] = kv.first;
- if (listeningPorts.find(kv.first) != listeningPorts.end()) {
- Node::json[kv.second]["ProtocolEnabled"] = true;
- } else {
- Node::json[kv.second]["ProtocolEnabled"] = false;
- }
- }
+ void getData(const std::shared_ptr<AsyncResp>& asyncResp) {
+ Node::json["HostName"] = getHostName();
+ asyncResp->res.jsonValue = Node::json;
+
+ OnDemandNetworkProtocolProvider::getServices(
+ [&, asyncResp](const bool success,
+ const std::vector<UnitStruct>& resp) {
+ if (!success) {
+ asyncResp->res.jsonValue = nlohmann::json::object();
+ messages::addMessageToErrorJson(asyncResp->res.jsonValue,
+ messages::internalError());
+ asyncResp->res.result(
+ boost::beast::http::status::internal_server_error);
+ }
+
+ for (auto& unit : resp) {
+ for (auto& kv : protocolToDBus) {
+ if (kv.second.serviceName ==
+ std::get<NET_PROTO_UNIT_NAME>(unit)) {
+ std::string service = kv.first;
+
+ // Process state
+ if (std::get<NET_PROTO_UNIT_SUB_STATE>(unit) == "running") {
+ asyncResp->res.jsonValue[service]["ProtocolEnabled"] = true;
+ } else {
+ asyncResp->res.jsonValue[service]["ProtocolEnabled"] = false;
+ }
+
+ // Process port
+ OnDemandNetworkProtocolProvider::getSocketListenPort(
+ kv.second.socketPath,
+ [&, asyncResp, service{std::move(service)} ](
+ const bool fetchSuccess, const bool portAvailable,
+ const unsigned long port) {
+ if (fetchSuccess) {
+ if (portAvailable) {
+ asyncResp->res.jsonValue[service]["Port"] = port;
+ } else {
+ asyncResp->res.jsonValue[service]["Port"] = nullptr;
+ }
+ } else {
+ messages::addMessageToJson(asyncResp->res.jsonValue,
+ messages::internalError(),
+ "/" + service);
+ }
+ });
+ break;
+ }
+ }
+ }
+ });
}
- void refreshListeningPorts() {
- listeningPorts.clear();
- std::array<char, 128> netstatLine;
- FILE* p = popen("netstat -tuln | awk '{ print $4 }'", "r");
- if (p != nullptr) {
- while (fgets(netstatLine.data(), netstatLine.size(), p) != nullptr) {
- auto s = std::string(netstatLine.data());
-
- // get port num from strings such as: ".*:.*:.*:port"
- s.erase(0, s.find_last_of(":") + strlen(":"));
-
- auto port = atoi(s.c_str());
- if (port != 0 &&
- portToProtocolMap.find(port) != portToProtocolMap.end()) {
- listeningPorts.insert(port);
- }
- }
- }
- }
-
- boost::container::flat_map<int, std::string> portToProtocolMap{
- {22, "SSH"}, {80, "HTTP"}, {443, "HTTPS"}, {623, "IPMI"}, {1900, "SSDP"}};
-
- boost::container::flat_set<int> listeningPorts;
+ boost::container::flat_map<std::string, ServiceConfiguration> protocolToDBus{
+ {"SSH",
+ {"dropbear.service",
+ "/org/freedesktop/systemd1/unit/dropbear_2esocket"}},
+ {"HTTPS",
+ {"phosphor-gevent.service",
+ "/org/freedesktop/systemd1/unit/phosphor_2dgevent_2esocket"}},
+ {"IPMI",
+ {"phosphor-ipmi-net.service",
+ "/org/freedesktop/systemd1/unit/phosphor_2dipmi_2dnet_2esocket"}}};
};
} // namespace redfish