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 #ifndef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS 18 19 #include "async_resp.hpp" 20 #include "dbus_utility.hpp" 21 #include "error_messages.hpp" 22 23 #include <boost/system/error_code.hpp> 24 #include <sdbusplus/asio/property.hpp> 25 26 #include <array> 27 #include <charconv> 28 #include <ranges> 29 #include <string_view> 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(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 64 CallbackFunc&& callback) 65 { 66 // Find managed chassis 67 constexpr std::array<std::string_view, 2> interfaces = { 68 "xyz.openbmc_project.Inventory.Item.Board", 69 "xyz.openbmc_project.Inventory.Item.Chassis"}; 70 dbus::utility::getSubTree( 71 "/xyz/openbmc_project/inventory", 0, interfaces, 72 [callback, 73 asyncResp](const boost::system::error_code& ec, 74 const dbus::utility::MapperGetSubTreeResponse& subtree) { 75 if (ec) 76 { 77 BMCWEB_LOG_ERROR("{}", ec); 78 return; 79 } 80 if (subtree.empty()) 81 { 82 BMCWEB_LOG_DEBUG("Can't find chassis!"); 83 return; 84 } 85 86 std::size_t idPos = subtree[0].first.rfind('/'); 87 if (idPos == std::string::npos || 88 (idPos + 1) >= subtree[0].first.size()) 89 { 90 messages::internalError(asyncResp->res); 91 BMCWEB_LOG_DEBUG("Can't parse chassis ID!"); 92 return; 93 } 94 std::string chassisId = subtree[0].first.substr(idPos + 1); 95 BMCWEB_LOG_DEBUG("chassisId = {}", chassisId); 96 callback(chassisId, asyncResp); 97 }); 98 } 99 100 template <typename CallbackFunc> 101 void getPortStatusAndPath( 102 std::span<const std::pair<std::string_view, std::string_view>> 103 protocolToDBus, 104 CallbackFunc&& callback) 105 { 106 crow::connections::systemBus->async_method_call( 107 [protocolToDBus, callback{std::forward<CallbackFunc>(callback)}]( 108 const boost::system::error_code& ec, 109 const std::vector<UnitStruct>& r) { 110 std::vector<std::tuple<std::string, std::string, bool>> socketData; 111 if (ec) 112 { 113 BMCWEB_LOG_ERROR("{}", ec); 114 // return error code 115 callback(ec, socketData); 116 return; 117 } 118 119 // save all service output into vector 120 for (const UnitStruct& unit : r) 121 { 122 // Only traverse through <xyz>.socket units 123 const std::string& unitName = 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 added 169 // 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>& i) { 173 return std::get<1>(i) == kv.first; 174 }); 175 if (find != socketData.end()) 176 { 177 // It only takes one enabled systemd service to consider a 178 // protocol enabled so if the current entry already has it 179 // enabled (or the new one is disabled) then just continue, 180 // otherwise remove the current one and add this new one. 181 if (std::get<2>(*find) || !isProtocolEnabled) 182 { 183 // Already registered as enabled or current one is not 184 // 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 (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 sdbusplus::asio::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 #endif 261