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 = 123 std::get<NET_PROTO_UNIT_NAME>(unit); 124 125 // find "." into unitsName 126 size_t lastCharPos = unitName.rfind('.'); 127 if (lastCharPos == std::string::npos) 128 { 129 continue; 130 } 131 132 // is unitsName end with ".socket" 133 std::string unitNameEnd = unitName.substr(lastCharPos); 134 if (unitNameEnd != ".socket") 135 { 136 continue; 137 } 138 139 // find "@" into unitsName 140 if (size_t atCharPos = unitName.rfind('@'); 141 atCharPos != std::string::npos) 142 { 143 lastCharPos = atCharPos; 144 } 145 146 // unitsName without "@eth(x).socket", only <xyz> 147 // unitsName without ".socket", only <xyz> 148 std::string unitNameStr = unitName.substr(0, lastCharPos); 149 150 for (const auto& kv : protocolToDBus) 151 { 152 // We are interested in services, which starts with 153 // mapped service name 154 if (unitNameStr != kv.second) 155 { 156 continue; 157 } 158 159 const std::string& socketPath = 160 std::get<NET_PROTO_UNIT_OBJ_PATH>(unit); 161 const std::string& unitState = 162 std::get<NET_PROTO_UNIT_SUB_STATE>(unit); 163 164 bool isProtocolEnabled = ((unitState == "running") || 165 (unitState == "listening")); 166 167 // Some protocols may have multiple services associated with 168 // them (for example IPMI). Look to see if we've already 169 // added an entry for the current protocol. 170 auto find = std::ranges::find_if( 171 socketData, 172 [&kv](const std::tuple<std::string, std::string, bool>& 173 i) { return std::get<1>(i) == kv.first; }); 174 if (find != socketData.end()) 175 { 176 // It only takes one enabled systemd service to consider 177 // a protocol enabled so if the current entry already 178 // has it enabled (or the new one is disabled) then just 179 // continue, otherwise remove the current one and add 180 // this new one. 181 if (std::get<2>(*find) || !isProtocolEnabled) 182 { 183 // Already registered as enabled or current one is 184 // not enabled, nothing to do 185 BMCWEB_LOG_DEBUG( 186 "protocolName: {}, already true or current one is false: {}", 187 kv.first, isProtocolEnabled); 188 break; 189 } 190 // Remove existing entry and replace with new one 191 // (below) 192 socketData.erase(find); 193 } 194 195 socketData.emplace_back(socketPath, std::string(kv.first), 196 isProtocolEnabled); 197 // We found service, return from inner loop. 198 break; 199 } 200 } 201 202 callback(ec, socketData); 203 }, 204 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 205 "org.freedesktop.systemd1.Manager", "ListUnits"); 206 } 207 208 template <typename CallbackFunc> 209 void getPortNumber(const std::string& socketPath, CallbackFunc&& callback) 210 { 211 sdbusplus::asio::getProperty< 212 std::vector<std::tuple<std::string, std::string>>>( 213 *crow::connections::systemBus, "org.freedesktop.systemd1", socketPath, 214 "org.freedesktop.systemd1.Socket", "Listen", 215 [callback = std::forward<CallbackFunc>(callback)]( 216 const boost::system::error_code& ec, 217 const std::vector<std::tuple<std::string, std::string>>& resp) { 218 if (ec) 219 { 220 BMCWEB_LOG_ERROR("{}", ec); 221 callback(ec, 0); 222 return; 223 } 224 if (resp.empty()) 225 { 226 // Network Protocol Listen Response Elements is empty 227 boost::system::error_code ec1 = 228 boost::system::errc::make_error_code( 229 boost::system::errc::bad_message); 230 // return error code 231 callback(ec1, 0); 232 BMCWEB_LOG_ERROR("{}", ec1); 233 return; 234 } 235 const std::string& listenStream = 236 std::get<NET_PROTO_LISTEN_STREAM>(resp[0]); 237 const char* pa = &listenStream[listenStream.rfind(':') + 1]; 238 int port{0}; 239 if (auto [p, ec2] = std::from_chars(pa, nullptr, port); 240 ec2 != std::errc()) 241 { 242 // there is only two possibility invalid_argument and 243 // result_out_of_range 244 boost::system::error_code ec3 = 245 boost::system::errc::make_error_code( 246 boost::system::errc::invalid_argument); 247 if (ec2 == std::errc::result_out_of_range) 248 { 249 ec3 = boost::system::errc::make_error_code( 250 boost::system::errc::result_out_of_range); 251 } 252 // return error code 253 callback(ec3, 0); 254 BMCWEB_LOG_ERROR("{}", ec3); 255 } 256 callback(ec, port); 257 }); 258 } 259 260 } // namespace redfish 261