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