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