1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2019 Intel Corporation 4 #pragma once 5 6 #include "async_resp.hpp" 7 #include "dbus_utility.hpp" 8 #include "error_messages.hpp" 9 10 #include <boost/system/error_code.hpp> 11 #include <sdbusplus/asio/property.hpp> 12 13 #include <array> 14 #include <charconv> 15 #include <ranges> 16 #include <string_view> 17 18 namespace redfish 19 { 20 21 enum NetworkProtocolUnitStructFields 22 { 23 NET_PROTO_UNIT_NAME, 24 NET_PROTO_UNIT_DESC, 25 NET_PROTO_UNIT_LOAD_STATE, 26 NET_PROTO_UNIT_ACTIVE_STATE, 27 NET_PROTO_UNIT_SUB_STATE, 28 NET_PROTO_UNIT_DEVICE, 29 NET_PROTO_UNIT_OBJ_PATH, 30 NET_PROTO_UNIT_ALWAYS_0, 31 NET_PROTO_UNIT_ALWAYS_EMPTY, 32 NET_PROTO_UNIT_ALWAYS_ROOT_PATH 33 }; 34 35 enum NetworkProtocolListenResponseElements 36 { 37 NET_PROTO_LISTEN_TYPE, 38 NET_PROTO_LISTEN_STREAM 39 }; 40 41 /** 42 * @brief D-Bus Unit structure returned in array from ListUnits Method 43 */ 44 using UnitStruct = 45 std::tuple<std::string, std::string, std::string, std::string, std::string, 46 std::string, sdbusplus::message::object_path, uint32_t, 47 std::string, sdbusplus::message::object_path>; 48 49 template <typename CallbackFunc> 50 void getMainChassisId(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 51 CallbackFunc&& callback) 52 { 53 // Find managed chassis 54 constexpr std::array<std::string_view, 2> interfaces = { 55 "xyz.openbmc_project.Inventory.Item.Board", 56 "xyz.openbmc_project.Inventory.Item.Chassis"}; 57 dbus::utility::getSubTree( 58 "/xyz/openbmc_project/inventory", 0, interfaces, 59 [callback = std::forward<CallbackFunc>(callback), 60 asyncResp](const boost::system::error_code& ec, 61 const dbus::utility::MapperGetSubTreeResponse& subtree) { 62 if (ec) 63 { 64 BMCWEB_LOG_ERROR("{}", ec); 65 return; 66 } 67 if (subtree.empty()) 68 { 69 BMCWEB_LOG_DEBUG("Can't find chassis!"); 70 return; 71 } 72 73 std::size_t idPos = subtree[0].first.rfind('/'); 74 if (idPos == std::string::npos || 75 (idPos + 1) >= subtree[0].first.size()) 76 { 77 messages::internalError(asyncResp->res); 78 BMCWEB_LOG_DEBUG("Can't parse chassis ID!"); 79 return; 80 } 81 std::string chassisId = subtree[0].first.substr(idPos + 1); 82 BMCWEB_LOG_DEBUG("chassisId = {}", chassisId); 83 callback(chassisId, asyncResp); 84 }); 85 } 86 87 template <typename CallbackFunc> 88 void getPortStatusAndPath( 89 std::span<const std::pair<std::string_view, std::string_view>> 90 protocolToDBus, 91 CallbackFunc&& callback) 92 { 93 crow::connections::systemBus->async_method_call( 94 [protocolToDBus, callback = std::forward<CallbackFunc>(callback)]( 95 const boost::system::error_code& ec, 96 const std::vector<UnitStruct>& r) { 97 std::vector<std::tuple<std::string, std::string, bool>> socketData; 98 if (ec) 99 { 100 BMCWEB_LOG_ERROR("{}", ec); 101 // return error code 102 callback(ec, socketData); 103 return; 104 } 105 106 // save all service output into vector 107 for (const UnitStruct& unit : r) 108 { 109 // Only traverse through <xyz>.socket units 110 const std::string& unitName = 111 std::get<NET_PROTO_UNIT_NAME>(unit); 112 113 // find "." into unitsName 114 size_t lastCharPos = unitName.rfind('.'); 115 if (lastCharPos == std::string::npos) 116 { 117 continue; 118 } 119 120 // is unitsName end with ".socket" 121 std::string unitNameEnd = unitName.substr(lastCharPos); 122 if (unitNameEnd != ".socket") 123 { 124 continue; 125 } 126 127 // find "@" into unitsName 128 if (size_t atCharPos = unitName.rfind('@'); 129 atCharPos != std::string::npos) 130 { 131 lastCharPos = atCharPos; 132 } 133 134 // unitsName without "@eth(x).socket", only <xyz> 135 // unitsName without ".socket", only <xyz> 136 std::string unitNameStr = unitName.substr(0, lastCharPos); 137 138 for (const auto& kv : protocolToDBus) 139 { 140 // We are interested in services, which starts with 141 // mapped service name 142 if (unitNameStr != kv.second) 143 { 144 continue; 145 } 146 147 const std::string& socketPath = 148 std::get<NET_PROTO_UNIT_OBJ_PATH>(unit); 149 const std::string& unitState = 150 std::get<NET_PROTO_UNIT_SUB_STATE>(unit); 151 152 bool isProtocolEnabled = ((unitState == "running") || 153 (unitState == "listening")); 154 155 // Some protocols may have multiple services associated with 156 // them (for example IPMI). Look to see if we've already 157 // added an entry for the current protocol. 158 auto find = std::ranges::find_if( 159 socketData, 160 [&kv](const std::tuple<std::string, std::string, bool>& 161 i) { return std::get<1>(i) == kv.first; }); 162 if (find != socketData.end()) 163 { 164 // It only takes one enabled systemd service to consider 165 // a protocol enabled so if the current entry already 166 // has it enabled (or the new one is disabled) then just 167 // continue, otherwise remove the current one and add 168 // this new one. 169 if (std::get<2>(*find) || !isProtocolEnabled) 170 { 171 // Already registered as enabled or current one is 172 // not enabled, nothing to do 173 BMCWEB_LOG_DEBUG( 174 "protocolName: {}, already true or current one is false: {}", 175 kv.first, isProtocolEnabled); 176 break; 177 } 178 // Remove existing entry and replace with new one 179 // (below) 180 socketData.erase(find); 181 } 182 183 socketData.emplace_back(socketPath, std::string(kv.first), 184 isProtocolEnabled); 185 // We found service, return from inner loop. 186 break; 187 } 188 } 189 190 callback(ec, socketData); 191 }, 192 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 193 "org.freedesktop.systemd1.Manager", "ListUnits"); 194 } 195 196 template <typename CallbackFunc> 197 void getPortNumber(const std::string& socketPath, CallbackFunc&& callback) 198 { 199 dbus::utility::getProperty< 200 std::vector<std::tuple<std::string, std::string>>>( 201 *crow::connections::systemBus, "org.freedesktop.systemd1", socketPath, 202 "org.freedesktop.systemd1.Socket", "Listen", 203 [callback = std::forward<CallbackFunc>(callback)]( 204 const boost::system::error_code& ec, 205 const std::vector<std::tuple<std::string, std::string>>& resp) { 206 if (ec) 207 { 208 BMCWEB_LOG_ERROR("{}", ec); 209 callback(ec, 0); 210 return; 211 } 212 if (resp.empty()) 213 { 214 // Network Protocol Listen Response Elements is empty 215 boost::system::error_code ec1 = 216 boost::system::errc::make_error_code( 217 boost::system::errc::bad_message); 218 // return error code 219 callback(ec1, 0); 220 BMCWEB_LOG_ERROR("{}", ec1); 221 return; 222 } 223 const std::string& listenStream = 224 std::get<NET_PROTO_LISTEN_STREAM>(resp[0]); 225 const char* pa = &listenStream[listenStream.rfind(':') + 1]; 226 int port{0}; 227 if (auto [p, ec2] = std::from_chars(pa, nullptr, port); 228 ec2 != std::errc()) 229 { 230 // there is only two possibility invalid_argument and 231 // result_out_of_range 232 boost::system::error_code ec3 = 233 boost::system::errc::make_error_code( 234 boost::system::errc::invalid_argument); 235 if (ec2 == std::errc::result_out_of_range) 236 { 237 ec3 = boost::system::errc::make_error_code( 238 boost::system::errc::result_out_of_range); 239 } 240 // return error code 241 callback(ec3, 0); 242 BMCWEB_LOG_ERROR("{}", ec3); 243 } 244 callback(ec, port); 245 }); 246 } 247 248 } // namespace redfish 249