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