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