1 /* 2 // Copyright (c) 2019 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "async_resp.hpp" 19 #include "dbus_utility.hpp" 20 #include "error_messages.hpp" 21 22 #include <boost/system/error_code.hpp> 23 #include <sdbusplus/asio/property.hpp> 24 25 #include <array> 26 #include <charconv> 27 #include <ranges> 28 #include <string_view> 29 30 namespace redfish 31 { 32 33 enum NetworkProtocolUnitStructFields 34 { 35 NET_PROTO_UNIT_NAME, 36 NET_PROTO_UNIT_DESC, 37 NET_PROTO_UNIT_LOAD_STATE, 38 NET_PROTO_UNIT_ACTIVE_STATE, 39 NET_PROTO_UNIT_SUB_STATE, 40 NET_PROTO_UNIT_DEVICE, 41 NET_PROTO_UNIT_OBJ_PATH, 42 NET_PROTO_UNIT_ALWAYS_0, 43 NET_PROTO_UNIT_ALWAYS_EMPTY, 44 NET_PROTO_UNIT_ALWAYS_ROOT_PATH 45 }; 46 47 enum NetworkProtocolListenResponseElements 48 { 49 NET_PROTO_LISTEN_TYPE, 50 NET_PROTO_LISTEN_STREAM 51 }; 52 53 /** 54 * @brief D-Bus Unit structure returned in array from ListUnits Method 55 */ 56 using UnitStruct = 57 std::tuple<std::string, std::string, std::string, std::string, std::string, 58 std::string, sdbusplus::message::object_path, uint32_t, 59 std::string, sdbusplus::message::object_path>; 60 61 template <typename CallbackFunc> 62 void getMainChassisId(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 63 CallbackFunc&& callback) 64 { 65 // Find managed chassis 66 constexpr std::array<std::string_view, 2> interfaces = { 67 "xyz.openbmc_project.Inventory.Item.Board", 68 "xyz.openbmc_project.Inventory.Item.Chassis"}; 69 dbus::utility::getSubTree( 70 "/xyz/openbmc_project/inventory", 0, interfaces, 71 [callback = std::forward<CallbackFunc>(callback), 72 asyncResp](const boost::system::error_code& ec, 73 const dbus::utility::MapperGetSubTreeResponse& subtree) { 74 if (ec) 75 { 76 BMCWEB_LOG_ERROR("{}", ec); 77 return; 78 } 79 if (subtree.empty()) 80 { 81 BMCWEB_LOG_DEBUG("Can't find chassis!"); 82 return; 83 } 84 85 std::size_t idPos = subtree[0].first.rfind('/'); 86 if (idPos == std::string::npos || 87 (idPos + 1) >= subtree[0].first.size()) 88 { 89 messages::internalError(asyncResp->res); 90 BMCWEB_LOG_DEBUG("Can't parse chassis ID!"); 91 return; 92 } 93 std::string chassisId = subtree[0].first.substr(idPos + 1); 94 BMCWEB_LOG_DEBUG("chassisId = {}", chassisId); 95 callback(chassisId, asyncResp); 96 }); 97 } 98 99 template <typename CallbackFunc> 100 void getPortStatusAndPath( 101 std::span<const std::pair<std::string_view, std::string_view>> 102 protocolToDBus, 103 CallbackFunc&& callback) 104 { 105 crow::connections::systemBus->async_method_call( 106 [protocolToDBus, callback = std::forward<CallbackFunc>(callback)]( 107 const boost::system::error_code& ec, 108 const std::vector<UnitStruct>& r) { 109 std::vector<std::tuple<std::string, std::string, bool>> socketData; 110 if (ec) 111 { 112 BMCWEB_LOG_ERROR("{}", ec); 113 // return error code 114 callback(ec, socketData); 115 return; 116 } 117 118 // save all service output into vector 119 for (const UnitStruct& unit : r) 120 { 121 // Only traverse through <xyz>.socket units 122 const std::string& unitName = 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 added 168 // 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>& i) { 172 return std::get<1>(i) == kv.first; 173 }); 174 if (find != socketData.end()) 175 { 176 // It only takes one enabled systemd service to consider a 177 // protocol enabled so if the current entry already has it 178 // enabled (or the new one is disabled) then just continue, 179 // otherwise remove the current one and add this new one. 180 if (std::get<2>(*find) || !isProtocolEnabled) 181 { 182 // Already registered as enabled or current one is not 183 // 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 (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 sdbusplus::asio::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