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>
getMainChassisId(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,CallbackFunc && callback)62 void getMainChassisId(const 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>
getPortStatusAndPath(std::span<const std::pair<std::string_view,std::string_view>> protocolToDBus,CallbackFunc && callback)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>
getPortNumber(const std::string & socketPath,CallbackFunc && callback)209 void getPortNumber(const std::string& socketPath, CallbackFunc&& callback)
210 {
211     dbus::utility::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