blob: 9bf968a40df795a7a6d4adb3c868ea36428f9fbe [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080016#include "app.hpp"
17#include "async_resp.hpp"
18#include "dbus_singleton.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080019#include "dbus_utility.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000020#include "http_request.hpp"
21#include "http_response.hpp"
22#include "logging.hpp"
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010023#include "parsing.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000024#include "routing.hpp"
Ed Tanous50ebd4a2023-01-19 19:03:17 -080025#include "str_utility.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000026
27#include <systemd/sd-bus-protocol.h>
28#include <systemd/sd-bus.h>
Ed Tanous911ac312017-08-15 09:37:42 -070029#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070030
Ed Tanous11ba3972022-07-11 09:50:41 -070031#include <boost/algorithm/string/classification.hpp>
32#include <boost/algorithm/string/predicate.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000033#include <boost/beast/http/status.hpp>
34#include <boost/beast/http/verb.hpp>
35#include <boost/container/flat_map.hpp>
36#include <boost/container/vector.hpp>
37#include <boost/iterator/iterator_facade.hpp>
George Liue99073f2022-12-09 11:06:16 +080038#include <boost/system/error_code.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000039#include <nlohmann/json.hpp>
40#include <sdbusplus/asio/connection.hpp>
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +020041#include <sdbusplus/asio/property.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000042#include <sdbusplus/exception.hpp>
43#include <sdbusplus/message.hpp>
44#include <sdbusplus/message/native_types.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045
Nan Zhoud5c80ad2022-07-11 01:16:31 +000046#include <algorithm>
47#include <array>
48#include <cerrno>
49#include <cstdint>
50#include <cstring>
James Feist4418c7f2019-04-15 11:09:15 -070051#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070052#include <fstream>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000053#include <functional>
54#include <initializer_list>
55#include <iterator>
56#include <limits>
57#include <map>
58#include <memory>
Ramesh Iyyard9207042019-07-05 08:04:42 -050059#include <regex>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000060#include <string>
61#include <string_view>
62#include <type_traits>
Ed Tanousb5a76932020-09-29 16:16:58 -070063#include <utility>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000064#include <variant>
65#include <vector>
66
67// IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp>
68// IWYU pragma: no_include <boost/system/detail/error_code.hpp>
69// IWYU pragma: no_include <boost/system/detail/error_category.hpp>
70// IWYU pragma: no_include <errno.h>
71// IWYU pragma: no_include <string.h>
72// IWYU pragma: no_include <ext/alloc_traits.h>
73// IWYU pragma: no_include <exception>
74// IWYU pragma: no_include <boost/type_index/type_index_facade.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070075
Ed Tanous1abe55e2018-09-05 08:30:59 -070076namespace crow
77{
78namespace openbmc_mapper
79{
Ed Tanous23a21a12020-07-25 04:45:05 +000080const constexpr char* notFoundMsg = "404 Not Found";
81const constexpr char* badReqMsg = "400 Bad Request";
82const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
83const constexpr char* forbiddenMsg = "403 Forbidden";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010084const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
Ed Tanous23a21a12020-07-25 04:45:05 +000085const constexpr char* methodFailedMsg = "500 Method Call Failed";
86const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
87const constexpr char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060088 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous23a21a12020-07-25 04:45:05 +000089const constexpr char* propNotFoundDesc =
90 "The specified property cannot be found";
91const constexpr char* noJsonDesc = "No JSON object could be decoded";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010092const constexpr char* invalidContentType =
93 "Content-type header is missing or invalid";
Ed Tanous23a21a12020-07-25 04:45:05 +000094const constexpr char* methodNotFoundDesc =
95 "The specified method cannot be found";
96const constexpr char* methodNotAllowedDesc = "Method not allowed";
97const constexpr char* forbiddenPropDesc =
98 "The specified property cannot be created";
99const constexpr char* forbiddenResDesc =
100 "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -0600101
Josh Lehan482c45a2022-03-29 17:10:44 -0700102inline bool validateFilename(const std::string& filename)
103{
104 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
105
106 return std::regex_match(filename, validFilename);
107}
108
Ed Tanous23a21a12020-07-25 04:45:05 +0000109inline void setErrorResponse(crow::Response& res,
110 boost::beast::http::status result,
111 const std::string& desc,
112 const std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -0600113{
114 res.result(result);
Ed Tanous14766872022-03-15 10:44:42 -0700115 res.jsonValue["data"]["description"] = desc;
116 res.jsonValue["message"] = msg;
117 res.jsonValue["status"] = "error";
Matt Spinler2ae60092018-12-06 10:35:36 -0600118}
119
Ed Tanousb5a76932020-09-29 16:16:58 -0700120inline void
121 introspectObjects(const std::string& processName,
122 const std::string& objectPath,
123 const std::shared_ptr<bmcweb::AsyncResp>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700125 if (transaction->res.jsonValue.is_null())
126 {
Ed Tanous14766872022-03-15 10:44:42 -0700127 transaction->res.jsonValue["status"] = "ok";
128 transaction->res.jsonValue["bus_name"] = processName;
129 transaction->res.jsonValue["objects"] = nlohmann::json::array();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700130 }
131
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700133 [transaction, processName{std::string(processName)},
134 objectPath{std::string(objectPath)}](
135 const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000136 const std::string& introspectXml) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700137 if (ec)
138 {
139 BMCWEB_LOG_ERROR
140 << "Introspect call failed with error: " << ec.message()
141 << " on process: " << processName << " path: " << objectPath
142 << "\n";
143 return;
144 }
145 nlohmann::json::object_t object;
146 object["path"] = objectPath;
Ed Tanous14766872022-03-15 10:44:42 -0700147
Ed Tanous002d39b2022-05-31 08:59:27 -0700148 transaction->res.jsonValue["objects"].push_back(std::move(object));
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700149
Ed Tanous002d39b2022-05-31 08:59:27 -0700150 tinyxml2::XMLDocument doc;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700151
Ed Tanous002d39b2022-05-31 08:59:27 -0700152 doc.Parse(introspectXml.c_str());
153 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
154 if (pRoot == nullptr)
155 {
156 BMCWEB_LOG_ERROR << "XML document failed to parse " << processName
157 << " " << objectPath << "\n";
158 }
159 else
160 {
161 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
162 while (node != nullptr)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700163 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700164 const char* childPath = node->Attribute("name");
165 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700167 std::string newpath;
168 if (objectPath != "/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700169 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700170 newpath += objectPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700172 newpath += std::string("/") + childPath;
173 // introspect the subobjects as well
174 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700176
177 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700179 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700181 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700183}
Ed Tanous64530012018-02-06 17:08:16 -0800184
Ed Tanous23a21a12020-07-25 04:45:05 +0000185inline void getPropertiesForEnumerate(
186 const std::string& objectPath, const std::string& service,
Ed Tanousb5a76932020-09-29 16:16:58 -0700187 const std::string& interface,
188 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600189{
190 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
191 << service << " " << interface;
192
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200193 sdbusplus::asio::getAllProperties(
194 *crow::connections::systemBus, service, objectPath, interface,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800195 [asyncResp, objectPath, service,
196 interface](const boost::system::error_code ec,
197 const dbus::utility::DBusPropertiesMap& propertiesList) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700198 if (ec)
199 {
200 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
201 << interface << " service " << service
202 << " failed with code " << ec;
203 return;
204 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600205
Ed Tanous002d39b2022-05-31 08:59:27 -0700206 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
207 nlohmann::json& objectJson = dataJson[objectPath];
208 if (objectJson.is_null())
209 {
210 objectJson = nlohmann::json::object();
211 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600212
Ed Tanous002d39b2022-05-31 08:59:27 -0700213 for (const auto& [name, value] : propertiesList)
214 {
215 nlohmann::json& propertyJson = objectJson[name];
216 std::visit(
217 [&propertyJson](auto&& val) {
218 if constexpr (std::is_same_v<std::decay_t<decltype(val)>,
219 sdbusplus::message::unix_fd>)
220 {
221 propertyJson = val.fd;
222 }
223 else
224 {
Ed Tanousd1a64812021-12-13 12:14:05 -0800225
Ed Tanous002d39b2022-05-31 08:59:27 -0700226 propertyJson = val;
227 }
228 },
229 value);
230 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200231 });
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600232}
233
234// Find any results that weren't picked up by ObjectManagers, to be
235// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000236inline void findRemainingObjectsForEnumerate(
Ed Tanousb5a76932020-09-29 16:16:58 -0700237 const std::string& objectPath,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800238 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
Ed Tanousb5a76932020-09-29 16:16:58 -0700239 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600240{
241 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500242 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600243
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600245 {
246 if (path == objectPath)
247 {
248 // An enumerate does not return the target path's properties
249 continue;
250 }
251 if (dataJson.find(path) == dataJson.end())
252 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500253 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600254 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500255 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600256 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700257 if (!interface.starts_with("org.freedesktop.DBus"))
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600258 {
259 getPropertiesForEnumerate(path, service, interface,
260 asyncResp);
261 }
262 }
263 }
264 }
265 }
266}
267
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600268struct InProgressEnumerateData
269{
zhanghch058d1b46d2021-04-01 11:18:24 +0800270 InProgressEnumerateData(
271 const std::string& objectPathIn,
272 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Ed Tanous23a21a12020-07-25 04:45:05 +0000273 objectPath(objectPathIn),
zhanghch058d1b46d2021-04-01 11:18:24 +0800274 asyncResp(asyncRespIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500275 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600276
277 ~InProgressEnumerateData()
278 {
Ed Tanous24b2fe82022-01-06 12:45:54 -0800279 try
280 {
281 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
282 }
283 catch (...)
284 {
285 BMCWEB_LOG_CRITICAL
286 << "findRemainingObjectsForEnumerate threw exception";
287 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600288 }
289
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800290 InProgressEnumerateData(const InProgressEnumerateData&) = delete;
291 InProgressEnumerateData(InProgressEnumerateData&&) = delete;
292 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
293 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600294 const std::string objectPath;
Ed Tanousb9d36b42022-02-26 21:42:46 -0800295 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600296 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
297};
298
Ed Tanous23a21a12020-07-25 04:45:05 +0000299inline void getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000300 const std::string& objectName, const std::string& objectManagerPath,
301 const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700302 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700303{
Ed Tanous81ce6092020-12-17 16:54:55 +0000304 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName
305 << " object_manager_path " << objectManagerPath
306 << " connection_name " << connectionName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000308 [transaction, objectName,
309 connectionName](const boost::system::error_code ec,
310 const dbus::utility::ManagedObjectType& objects) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700311 if (ec)
312 {
313 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName
314 << " on connection " << connectionName
315 << " failed with code " << ec;
316 return;
317 }
Ed Tanous64530012018-02-06 17:08:16 -0800318
Ed Tanous002d39b2022-05-31 08:59:27 -0700319 nlohmann::json& dataJson =
320 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700321
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 for (const auto& objectPath : objects)
323 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700324 if (objectPath.first.str.starts_with(objectName))
Ed Tanous049a0512018-11-01 13:58:42 -0700325 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700326 BMCWEB_LOG_DEBUG << "Reading object " << objectPath.first.str;
327 nlohmann::json& objectJson = dataJson[objectPath.first.str];
328 if (objectJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 objectJson = nlohmann::json::object();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700331 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500332 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700333 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700334 for (const auto& property : interface.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700335 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700336 nlohmann::json& propertyJson =
337 objectJson[property.first];
338 std::visit(
339 [&propertyJson](auto&& val) {
340 if constexpr (std::is_same_v<
341 std::decay_t<decltype(val)>,
342 sdbusplus::message::unix_fd>)
343 {
344 propertyJson = val.fd;
345 }
346 else
347 {
348
349 propertyJson = val;
350 }
351 },
352 property.second);
Ed Tanous049a0512018-11-01 13:58:42 -0700353 }
354 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700356 for (const auto& interface : objectPath.second)
357 {
358 if (interface.first == "org.freedesktop.DBus.ObjectManager")
359 {
360 getManagedObjectsForEnumerate(objectPath.first.str,
361 objectPath.first.str,
362 connectionName, transaction);
363 }
364 }
365 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700366 },
Ed Tanous81ce6092020-12-17 16:54:55 +0000367 connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager",
368 "GetManagedObjects");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700369}
370
Ed Tanous23a21a12020-07-25 04:45:05 +0000371inline void findObjectManagerPathForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000372 const std::string& objectName, const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700373 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700374{
Ed Tanous81ce6092020-12-17 16:54:55 +0000375 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName
376 << " on connection:" << connectionName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700377 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000378 [transaction, objectName, connectionName](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700379 const boost::system::error_code ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800380 const dbus::utility::MapperGetAncestorsResponse& objects) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700381 if (ec)
382 {
383 BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName
384 << " failed with code " << ec;
385 return;
386 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700387
Ed Tanous002d39b2022-05-31 08:59:27 -0700388 for (const auto& pathGroup : objects)
389 {
390 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700391 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700392 if (connectionGroup.first == connectionName)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700393 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700394 // Found the object manager path for this resource.
395 getManagedObjectsForEnumerate(objectName, pathGroup.first,
396 connectionName, transaction);
397 return;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700398 }
399 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700400 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700401 },
402 "xyz.openbmc_project.ObjectMapper",
403 "/xyz/openbmc_project/object_mapper",
Ed Tanous81ce6092020-12-17 16:54:55 +0000404 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500405 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700406}
Ed Tanous64530012018-02-06 17:08:16 -0800407
Ed Tanous7c091622019-05-23 11:42:36 -0700408// Uses GetObject to add the object info about the target /enumerate path to
409// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600410// target path, and then continues on enumerating the rest of the tree.
Ed Tanousb5a76932020-09-29 16:16:58 -0700411inline void getObjectAndEnumerate(
412 const std::shared_ptr<InProgressEnumerateData>& transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600413{
George Liu2b731192023-01-11 16:27:13 +0800414 dbus::utility::getDbusObject(
415 transaction->objectPath, {},
416 [transaction](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800417 const dbus::utility::MapperGetObject& objects) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700418 if (ec)
419 {
420 BMCWEB_LOG_ERROR << "GetObject for path " << transaction->objectPath
421 << " failed with code " << ec;
422 return;
423 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600424
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
426 << " has " << objects.size() << " entries";
427 if (!objects.empty())
428 {
429 transaction->subtree->emplace_back(transaction->objectPath,
430 objects);
431 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600432
Ed Tanous002d39b2022-05-31 08:59:27 -0700433 // Map indicating connection name, and the path where the object
434 // manager exists
435 boost::container::flat_map<std::string, std::string> connections;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600436
Ed Tanous002d39b2022-05-31 08:59:27 -0700437 for (const auto& object : *(transaction->subtree))
438 {
439 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600440 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700441 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600442 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700443 BMCWEB_LOG_DEBUG << connection.first << " has interface "
444 << interface;
445 if (interface == "org.freedesktop.DBus.ObjectManager")
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600446 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700447 BMCWEB_LOG_DEBUG << "found object manager path "
448 << object.first;
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700449 connections[connection.first] = object.first;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600450 }
451 }
452 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700453 }
454 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600455
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 for (const auto& connection : connections)
457 {
458 // If we already know where the object manager is, we don't
459 // need to search for it, we can call directly in to
460 // getManagedObjects
461 if (!connection.second.empty())
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600462 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700463 getManagedObjectsForEnumerate(transaction->objectPath,
464 connection.second,
465 connection.first, transaction);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600466 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700467 else
468 {
469 // otherwise we need to find the object manager path
470 // before we can continue
471 findObjectManagerPathForEnumerate(
472 transaction->objectPath, connection.first, transaction);
473 }
474 }
George Liu2b731192023-01-11 16:27:13 +0800475 });
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600476}
Ed Tanous64530012018-02-06 17:08:16 -0800477
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700478// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700479struct InProgressActionData
480{
Ed Tanous4e23a442022-06-06 09:57:26 -0700481 explicit InProgressActionData(crow::Response& resIn) : res(resIn)
Ed Tanous23a21a12020-07-25 04:45:05 +0000482 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700483 ~InProgressActionData()
484 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600485 // Methods could have been called across different owners
486 // and interfaces, where some calls failed and some passed.
487 //
488 // The rules for this are:
489 // * if no method was called - error
490 // * if a method failed and none passed - error
491 // (converse: if at least one method passed - OK)
492 // * for the method output:
493 // * if output processing didn't fail, return the data
494
495 // Only deal with method returns if nothing failed earlier
496 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700497 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600498 if (!methodPassed)
499 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500500 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600501 {
502 setErrorResponse(res, boost::beast::http::status::not_found,
503 methodNotFoundDesc, notFoundMsg);
504 }
505 }
506 else
507 {
508 if (outputFailed)
509 {
510 setErrorResponse(
511 res, boost::beast::http::status::internal_server_error,
512 "Method output failure", methodOutputFailedMsg);
513 }
514 else
515 {
Ed Tanous14766872022-03-15 10:44:42 -0700516 res.jsonValue["status"] = "ok";
517 res.jsonValue["message"] = "200 OK";
518 res.jsonValue["data"] = methodResponse;
Matt Spinler16caaee2019-01-15 11:40:34 -0600519 }
520 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700521 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600522
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700524 }
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800525 InProgressActionData(const InProgressActionData&) = delete;
526 InProgressActionData(InProgressActionData&&) = delete;
527 InProgressActionData& operator=(const InProgressActionData&) = delete;
528 InProgressActionData& operator=(InProgressActionData&&) = delete;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700529
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500530 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600532 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
533 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700534 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500535 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 std::string path;
537 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600538 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600539 bool methodPassed = false;
540 bool methodFailed = false;
541 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600542 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600543 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700545};
546
Ed Tanous23a21a12020-07-25 04:45:05 +0000547inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548{
549 std::vector<std::string> ret;
550 if (string.empty())
551 {
552 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700553 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700554 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 int containerDepth = 0;
556
557 for (std::string::const_iterator character = string.begin();
558 character != string.end(); character++)
559 {
560 ret.back() += *character;
561 switch (*character)
562 {
563 case ('a'):
564 break;
565 case ('('):
566 case ('{'):
567 containerDepth++;
568 break;
569 case ('}'):
570 case (')'):
571 containerDepth--;
572 if (containerDepth == 0)
573 {
574 if (character + 1 != string.end())
575 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700576 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 }
578 }
579 break;
580 default:
581 if (containerDepth == 0)
582 {
583 if (character + 1 != string.end())
584 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700585 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 }
587 }
588 break;
589 }
590 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600591
592 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700593}
594
Ed Tanous81ce6092020-12-17 16:54:55 +0000595inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
596 const nlohmann::json& inputJson)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597{
598 int r = 0;
Ed Tanous71f52d92021-02-19 08:51:17 -0800599 BMCWEB_LOG_DEBUG << "Converting "
600 << inputJson.dump(2, ' ', true,
601 nlohmann::json::error_handler_t::replace)
Ed Tanous81ce6092020-12-17 16:54:55 +0000602 << " to type: " << argType;
603 const std::vector<std::string> argTypes = dbusArgSplit(argType);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700604
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 // Assume a single object for now.
Ed Tanous81ce6092020-12-17 16:54:55 +0000606 const nlohmann::json* j = &inputJson;
607 nlohmann::json::const_iterator jIt = inputJson.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700608
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
611 // If we are decoding multiple objects, grab the pointer to the
612 // iterator, and increment it for the next loop
613 if (argTypes.size() > 1)
614 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000615 if (jIt == inputJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
617 return -2;
618 }
619 j = &*jIt;
620 jIt++;
621 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500622 const int64_t* intValue = j->get_ptr<const int64_t*>();
623 const std::string* stringValue = j->get_ptr<const std::string*>();
624 const double* doubleValue = j->get_ptr<const double*>();
625 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 int64_t v = 0;
627 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700628
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 // Do some basic type conversions that make sense. uint can be
630 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700631 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500633 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700634 if (uintValue != nullptr)
635 {
636 v = static_cast<int64_t>(*uintValue);
637 intValue = &v;
638 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 }
Ed Tanous66664f22019-10-11 13:05:49 -0700640 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500642 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700643 if (uintValue != nullptr)
644 {
645 d = static_cast<double>(*uintValue);
646 doubleValue = &d;
647 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 }
Ed Tanous66664f22019-10-11 13:05:49 -0700649 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
Ed Tanous66664f22019-10-11 13:05:49 -0700651 if (intValue != nullptr)
652 {
653 d = static_cast<double>(*intValue);
654 doubleValue = &d;
655 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700656 }
657
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 {
662 return -1;
663 }
Ed Tanous271584a2019-07-09 16:24:22 -0700664 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500665 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 if (r < 0)
667 {
668 return r;
669 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700670 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
675 return -1;
676 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500677 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
678 (*intValue > std::numeric_limits<int32_t>::max()))
679 {
680 return -ERANGE;
681 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 int32_t i = static_cast<int32_t>(*intValue);
683 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 if (r < 0)
685 {
686 return r;
687 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700688 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 {
691 // lots of ways bool could be represented here. Try them all
Ed Tanouse662eae2022-01-25 10:39:19 -0800692 int boolInt = 0;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700693 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500695 if (*intValue == 1)
696 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800697 boolInt = 1;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500698 }
699 else if (*intValue == 0)
700 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800701 boolInt = 0;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500702 }
703 else
704 {
705 return -ERANGE;
706 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 }
708 else if (b != nullptr)
709 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600710 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700712 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700714 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700715 }
716 else
717 {
718 return -1;
719 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700720 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700721 if (r < 0)
722 {
723 return r;
724 }
725 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700726 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700727 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700728 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 {
730 return -1;
731 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500732 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
733 (*intValue > std::numeric_limits<int16_t>::max()))
734 {
735 return -ERANGE;
736 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700737 int16_t n = static_cast<int16_t>(*intValue);
738 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 if (r < 0)
740 {
741 return r;
742 }
743 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700744 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700746 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 {
748 return -1;
749 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700750 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 if (r < 0)
752 {
753 return r;
754 }
755 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700756 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500758 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 {
761 return -1;
762 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000763 if (*uintValue > std::numeric_limits<uint8_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500764 {
765 return -ERANGE;
766 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700767 uint8_t y = static_cast<uint8_t>(*uintValue);
768 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700769 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700770 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500772 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700773 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774 {
775 return -1;
776 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000777 if (*uintValue > std::numeric_limits<uint16_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500778 {
779 return -ERANGE;
780 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700781 uint16_t q = static_cast<uint16_t>(*uintValue);
782 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700784 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500786 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700787 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700788 {
789 return -1;
790 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000791 if (*uintValue > std::numeric_limits<uint32_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500792 {
793 return -ERANGE;
794 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700795 uint32_t u = static_cast<uint32_t>(*uintValue);
796 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700798 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500800 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700801 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 {
803 return -1;
804 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700805 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700807 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700808 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500809 if (doubleValue == nullptr)
810 {
811 return -1;
812 }
813 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
814 (*doubleValue > std::numeric_limits<double>::max()))
815 {
816 return -ERANGE;
817 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700818 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700820 else if (argCode.starts_with("a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700822 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700824 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 if (r < 0)
826 {
827 return r;
828 }
829
Ed Tanous0dfeda62019-10-24 11:21:38 -0700830 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700831 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700832 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 if (r < 0)
834 {
835 return r;
836 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837 }
838 sd_bus_message_close_container(m);
839 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700840 else if (argCode.starts_with("v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700842 std::string containedType = argCode.substr(1);
843 BMCWEB_LOG_DEBUG << "variant type: " << argCode
844 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700846 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 if (r < 0)
848 {
849 return r;
850 }
851
Ed Tanous81ce6092020-12-17 16:54:55 +0000852 r = convertJsonToDbus(m, containedType, inputJson);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 if (r < 0)
854 {
855 return r;
856 }
857
858 r = sd_bus_message_close_container(m);
859 if (r < 0)
860 {
861 return r;
862 }
863 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700864 else if (argCode.starts_with("(") && argCode.ends_with(")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700866 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700868 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800869 if (r < 0)
870 {
871 return r;
872 }
873
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 nlohmann::json::const_iterator it = j->begin();
Ed Tanous81ce6092020-12-17 16:54:55 +0000875 for (const std::string& argCode2 : dbusArgSplit(argType))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 {
877 if (it == j->end())
878 {
879 return -1;
880 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000881 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 if (r < 0)
883 {
884 return r;
885 }
886 it++;
887 }
888 r = sd_bus_message_close_container(m);
889 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700890 else if (argCode.starts_with("{") && argCode.ends_with("}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700892 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700894 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800895 if (r < 0)
896 {
897 return r;
898 }
899
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700900 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700901 if (codes.size() != 2)
902 {
903 return -1;
904 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700905 const std::string& keyType = codes[0];
906 const std::string& valueType = codes[1];
Ed Tanous3174e4d2020-10-07 11:41:22 -0700907 for (const auto& it : j->items())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700909 r = convertJsonToDbus(m, keyType, it.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910 if (r < 0)
911 {
912 return r;
913 }
914
Ed Tanous2c70f802020-09-28 14:29:23 -0700915 r = convertJsonToDbus(m, valueType, it.value());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700916 if (r < 0)
917 {
918 return r;
919 }
920 }
921 r = sd_bus_message_close_container(m);
922 }
923 else
924 {
925 return -2;
926 }
927 if (r < 0)
928 {
929 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700930 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700931
Ed Tanous1abe55e2018-09-05 08:30:59 -0700932 if (argTypes.size() > 1)
933 {
934 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700935 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700936 }
Matt Spinler127ea542019-01-14 11:04:28 -0600937
938 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700939}
940
Matt Spinlerd22a7132019-01-14 12:14:30 -0600941template <typename T>
Patrick Williams59d494e2022-07-22 19:26:55 -0500942int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500943 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600944{
945 T value;
946
947 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
948 if (r < 0)
949 {
950 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
951 << " failed!";
952 return r;
953 }
954
955 data = value;
956 return 0;
957}
958
Patrick Williams59d494e2022-07-22 19:26:55 -0500959int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
960 nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600961
Ed Tanous23a21a12020-07-25 04:45:05 +0000962inline int readDictEntryFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -0500963 sdbusplus::message_t& m,
Ed Tanous23a21a12020-07-25 04:45:05 +0000964 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600965{
966 std::vector<std::string> types = dbusArgSplit(typeCode);
967 if (types.size() != 2)
968 {
969 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
970 << types.size();
971 return -1;
972 }
973
974 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
975 typeCode.c_str());
976 if (r < 0)
977 {
978 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
979 return r;
980 }
981
982 nlohmann::json key;
983 r = convertDBusToJSON(types[0], m, key);
984 if (r < 0)
985 {
986 return r;
987 }
988
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500989 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -0600990 if (keyPtr == nullptr)
991 {
992 // json doesn't support non-string keys. If we hit this condition,
993 // convert the result to a string so we can proceed
Ed Tanous71f52d92021-02-19 08:51:17 -0800994 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500995 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -0700996 // in theory this can't fail now, but lets be paranoid about it
997 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600998 if (keyPtr == nullptr)
999 {
1000 return -1;
1001 }
1002 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001003 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -06001004
1005 r = convertDBusToJSON(types[1], m, value);
1006 if (r < 0)
1007 {
1008 return r;
1009 }
1010
1011 r = sd_bus_message_exit_container(m.get());
1012 if (r < 0)
1013 {
1014 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1015 return r;
1016 }
1017
1018 return 0;
1019}
1020
Ed Tanous23a21a12020-07-25 04:45:05 +00001021inline int readArrayFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001022 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -06001023{
1024 if (typeCode.size() < 2)
1025 {
1026 BMCWEB_LOG_ERROR << "Type code " << typeCode
1027 << " too small for an array";
1028 return -1;
1029 }
1030
1031 std::string containedType = typeCode.substr(1);
1032
1033 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1034 containedType.c_str());
1035 if (r < 0)
1036 {
1037 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1038 << r;
1039 return r;
1040 }
1041
Ed Tanous11ba3972022-07-11 09:50:41 -07001042 bool dict = containedType.starts_with("{") && containedType.ends_with("}");
Matt Spinler6df8f992019-01-14 12:47:47 -06001043
1044 if (dict)
1045 {
1046 // Remove the { }
1047 containedType = containedType.substr(1, containedType.size() - 2);
1048 data = nlohmann::json::object();
1049 }
1050 else
1051 {
1052 data = nlohmann::json::array();
1053 }
1054
1055 while (true)
1056 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001057 r = sd_bus_message_at_end(m.get(), 0);
Matt Spinler6df8f992019-01-14 12:47:47 -06001058 if (r < 0)
1059 {
1060 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
1061 return r;
1062 }
1063
1064 if (r > 0)
1065 {
1066 break;
1067 }
1068
1069 // Dictionaries are only ever seen in an array
1070 if (dict)
1071 {
1072 r = readDictEntryFromMessage(containedType, m, data);
1073 if (r < 0)
1074 {
1075 return r;
1076 }
1077 }
1078 else
1079 {
1080 data.push_back(nlohmann::json());
1081
1082 r = convertDBusToJSON(containedType, m, data.back());
1083 if (r < 0)
1084 {
1085 return r;
1086 }
1087 }
1088 }
1089
1090 r = sd_bus_message_exit_container(m.get());
1091 if (r < 0)
1092 {
1093 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1094 return r;
1095 }
1096
1097 return 0;
1098}
1099
Ed Tanous23a21a12020-07-25 04:45:05 +00001100inline int readStructFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001101 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001102{
1103 if (typeCode.size() < 3)
1104 {
1105 BMCWEB_LOG_ERROR << "Type code " << typeCode
1106 << " too small for a struct";
1107 return -1;
1108 }
1109
1110 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1111 std::vector<std::string> types = dbusArgSplit(containedTypes);
1112
1113 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1114 containedTypes.c_str());
1115 if (r < 0)
1116 {
1117 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1118 << r;
1119 return r;
1120 }
1121
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001122 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001123 {
1124 data.push_back(nlohmann::json());
1125 r = convertDBusToJSON(type, m, data.back());
1126 if (r < 0)
1127 {
1128 return r;
1129 }
1130 }
1131
1132 r = sd_bus_message_exit_container(m.get());
1133 if (r < 0)
1134 {
1135 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1136 return r;
1137 }
1138 return 0;
1139}
1140
Patrick Williams59d494e2022-07-22 19:26:55 -05001141inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001142{
Ed Tanous543f4402022-01-06 13:12:53 -08001143 const char* containerType = nullptr;
Ed Tanous99131cd2019-10-24 11:12:47 -07001144 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001145 if (r < 0)
1146 {
1147 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1148 return r;
1149 }
1150
1151 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1152 containerType);
1153 if (r < 0)
1154 {
1155 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1156 << r;
1157 return r;
1158 }
1159
1160 r = convertDBusToJSON(containerType, m, data);
1161 if (r < 0)
1162 {
1163 return r;
1164 }
1165
1166 r = sd_bus_message_exit_container(m.get());
1167 if (r < 0)
1168 {
1169 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1170 return r;
1171 }
1172
1173 return 0;
1174}
1175
Ed Tanous23a21a12020-07-25 04:45:05 +00001176inline int convertDBusToJSON(const std::string& returnType,
Patrick Williams59d494e2022-07-22 19:26:55 -05001177 sdbusplus::message_t& m, nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001178{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001179 int r = 0;
1180 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1181
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001182 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001183 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001184 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001185 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001186 {
1187 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001188 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001189 }
1190
Ed Tanousd4d25792020-09-29 15:15:03 -07001191 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001192 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001193 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001194 if (r < 0)
1195 {
1196 return r;
1197 }
1198 }
1199 else if (typeCode == "b")
1200 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001201 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001202 if (r < 0)
1203 {
1204 return r;
1205 }
1206
Matt Spinlerf39420c2019-01-30 12:57:18 -06001207 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001208 }
1209 else if (typeCode == "u")
1210 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001211 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001212 if (r < 0)
1213 {
1214 return r;
1215 }
1216 }
1217 else if (typeCode == "i")
1218 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001219 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001220 if (r < 0)
1221 {
1222 return r;
1223 }
1224 }
1225 else if (typeCode == "x")
1226 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001227 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001228 if (r < 0)
1229 {
1230 return r;
1231 }
1232 }
1233 else if (typeCode == "t")
1234 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001235 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001236 if (r < 0)
1237 {
1238 return r;
1239 }
1240 }
1241 else if (typeCode == "n")
1242 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001243 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001244 if (r < 0)
1245 {
1246 return r;
1247 }
1248 }
1249 else if (typeCode == "q")
1250 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001251 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001252 if (r < 0)
1253 {
1254 return r;
1255 }
1256 }
1257 else if (typeCode == "y")
1258 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001259 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001260 if (r < 0)
1261 {
1262 return r;
1263 }
1264 }
1265 else if (typeCode == "d")
1266 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001267 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001268 if (r < 0)
1269 {
1270 return r;
1271 }
1272 }
1273 else if (typeCode == "h")
1274 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001275 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001276 if (r < 0)
1277 {
1278 return r;
1279 }
1280 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001281 else if (typeCode.starts_with("a"))
Matt Spinler6df8f992019-01-14 12:47:47 -06001282 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001283 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001284 if (r < 0)
1285 {
1286 return r;
1287 }
1288 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001289 else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
Matt Spinler75c6c672019-01-14 13:01:46 -06001290 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001291 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001292 if (r < 0)
1293 {
1294 return r;
1295 }
1296 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001297 else if (typeCode.starts_with("v"))
Matt Spinler89c19702019-01-14 13:13:00 -06001298 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001299 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001300 if (r < 0)
1301 {
1302 return r;
1303 }
1304 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001305 else
1306 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001307 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1308 return -2;
1309 }
1310 }
1311
Matt Spinler16caaee2019-01-15 11:40:34 -06001312 return 0;
1313}
1314
Ed Tanousb5a76932020-09-29 16:16:58 -07001315inline void handleMethodResponse(
1316 const std::shared_ptr<InProgressActionData>& transaction,
Patrick Williams59d494e2022-07-22 19:26:55 -05001317 sdbusplus::message_t& m, const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001318{
Matt Spinler39a4e392019-01-15 11:53:13 -06001319 nlohmann::json data;
1320
1321 int r = convertDBusToJSON(returnType, m, data);
1322 if (r < 0)
1323 {
1324 transaction->outputFailed = true;
1325 return;
1326 }
1327
1328 if (data.is_null())
1329 {
1330 return;
1331 }
1332
1333 if (transaction->methodResponse.is_null())
1334 {
1335 transaction->methodResponse = std::move(data);
1336 return;
1337 }
1338
1339 // If they're both dictionaries or arrays, merge into one.
1340 // Otherwise, make the results an array with every result
1341 // an entry. Could also just fail in that case, but it
1342 // seems better to get the data back somehow.
1343
1344 if (transaction->methodResponse.is_object() && data.is_object())
1345 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001346 for (const auto& obj : data.items())
Matt Spinler39a4e392019-01-15 11:53:13 -06001347 {
1348 // Note: Will overwrite the data for a duplicate key
1349 transaction->methodResponse.emplace(obj.key(),
1350 std::move(obj.value()));
1351 }
1352 return;
1353 }
1354
1355 if (transaction->methodResponse.is_array() && data.is_array())
1356 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001357 for (auto& obj : data)
Matt Spinler39a4e392019-01-15 11:53:13 -06001358 {
1359 transaction->methodResponse.push_back(std::move(obj));
1360 }
1361 return;
1362 }
1363
1364 if (!transaction->convertedToArray)
1365 {
1366 // They are different types. May as well turn them into an array
1367 nlohmann::json j = std::move(transaction->methodResponse);
1368 transaction->methodResponse = nlohmann::json::array();
1369 transaction->methodResponse.push_back(std::move(j));
1370 transaction->methodResponse.push_back(std::move(data));
1371 transaction->convertedToArray = true;
1372 }
1373 else
1374 {
1375 transaction->methodResponse.push_back(std::move(data));
1376 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001377}
1378
Ed Tanousb5a76932020-09-29 16:16:58 -07001379inline void findActionOnInterface(
1380 const std::shared_ptr<InProgressActionData>& transaction,
1381 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382{
1383 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1384 << connectionName;
1385 crow::connections::systemBus->async_method_call(
1386 [transaction, connectionName{std::string(connectionName)}](
1387 const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001388 const std::string& introspectXml) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001389 BMCWEB_LOG_DEBUG << "got xml:\n " << introspectXml;
1390 if (ec)
1391 {
1392 BMCWEB_LOG_ERROR
1393 << "Introspect call failed with error: " << ec.message()
1394 << " on process: " << connectionName << "\n";
1395 return;
1396 }
1397 tinyxml2::XMLDocument doc;
Matt Spinler318bd892019-01-15 09:59:20 -06001398
Ed Tanous002d39b2022-05-31 08:59:27 -07001399 doc.Parse(introspectXml.data(), introspectXml.size());
1400 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1401 if (pRoot == nullptr)
1402 {
1403 BMCWEB_LOG_ERROR << "XML document failed to parse "
1404 << connectionName << "\n";
1405 return;
1406 }
1407 tinyxml2::XMLElement* interfaceNode =
1408 pRoot->FirstChildElement("interface");
1409 while (interfaceNode != nullptr)
1410 {
1411 const char* thisInterfaceName = interfaceNode->Attribute("name");
1412 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001413 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001414 if (!transaction->interfaceName.empty() &&
1415 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001417 interfaceNode =
1418 interfaceNode->NextSiblingElement("interface");
1419 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001420 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001421
1422 tinyxml2::XMLElement* methodNode =
1423 interfaceNode->FirstChildElement("method");
1424 while (methodNode != nullptr)
1425 {
1426 const char* thisMethodName = methodNode->Attribute("name");
1427 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1428 if (thisMethodName != nullptr &&
1429 thisMethodName == transaction->methodName)
1430 {
1431 BMCWEB_LOG_DEBUG << "Found method named "
1432 << thisMethodName << " on interface "
1433 << thisInterfaceName;
Patrick Williams59d494e2022-07-22 19:26:55 -05001434 sdbusplus::message_t m =
Ed Tanous002d39b2022-05-31 08:59:27 -07001435 crow::connections::systemBus->new_method_call(
1436 connectionName.c_str(),
1437 transaction->path.c_str(), thisInterfaceName,
1438 transaction->methodName.c_str());
1439
1440 tinyxml2::XMLElement* argumentNode =
1441 methodNode->FirstChildElement("arg");
1442
1443 std::string returnType;
1444
1445 // Find the output type
1446 while (argumentNode != nullptr)
1447 {
1448 const char* argDirection =
1449 argumentNode->Attribute("direction");
1450 const char* argType =
1451 argumentNode->Attribute("type");
1452 if (argDirection != nullptr && argType != nullptr &&
1453 std::string(argDirection) == "out")
1454 {
1455 returnType = argType;
1456 break;
1457 }
1458 argumentNode =
1459 argumentNode->NextSiblingElement("arg");
1460 }
1461
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001462 auto argIt = transaction->arguments.begin();
Ed Tanous002d39b2022-05-31 08:59:27 -07001463
1464 argumentNode = methodNode->FirstChildElement("arg");
1465
1466 while (argumentNode != nullptr)
1467 {
1468 const char* argDirection =
1469 argumentNode->Attribute("direction");
1470 const char* argType =
1471 argumentNode->Attribute("type");
1472 if (argDirection != nullptr && argType != nullptr &&
1473 std::string(argDirection) == "in")
1474 {
1475 if (argIt == transaction->arguments.end())
1476 {
1477 transaction->setErrorStatus(
1478 "Invalid method args");
1479 return;
1480 }
1481 if (convertJsonToDbus(m.get(),
1482 std::string(argType),
1483 *argIt) < 0)
1484 {
1485 transaction->setErrorStatus(
1486 "Invalid method arg type");
1487 return;
1488 }
1489
1490 argIt++;
1491 }
1492 argumentNode =
1493 argumentNode->NextSiblingElement("arg");
1494 }
1495
1496 crow::connections::systemBus->async_send(
1497 m,
1498 [transaction,
1499 returnType](boost::system::error_code ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -05001500 sdbusplus::message_t& m2) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001501 if (ec2)
1502 {
1503 transaction->methodFailed = true;
1504 const sd_bus_error* e = m2.get_error();
1505
1506 if (e != nullptr)
1507 {
1508 setErrorResponse(
1509 transaction->res,
1510 boost::beast::http::status::bad_request,
1511 e->name, e->message);
1512 }
1513 else
1514 {
1515 setErrorResponse(
1516 transaction->res,
1517 boost::beast::http::status::bad_request,
1518 "Method call failed", methodFailedMsg);
1519 }
1520 return;
1521 }
1522 transaction->methodPassed = true;
1523
1524 handleMethodResponse(transaction, m2, returnType);
1525 });
1526 break;
1527 }
1528 methodNode = methodNode->NextSiblingElement("method");
1529 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001530 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001531 interfaceNode = interfaceNode->NextSiblingElement("interface");
1532 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001533 },
1534 connectionName, transaction->path,
1535 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001536}
1537
zhanghch058d1b46d2021-04-01 11:18:24 +08001538inline void handleAction(const crow::Request& req,
1539 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001540 const std::string& objectPath,
1541 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001543 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1544 << methodName;
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001545 nlohmann::json requestDbusData;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001546
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001547 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1548 if (ret == JsonParseResult::BadContentType)
1549 {
1550 setErrorResponse(asyncResp->res,
1551 boost::beast::http::status::unsupported_media_type,
1552 invalidContentType, unsupportedMediaMsg);
1553 return;
1554 }
1555 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001556 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001557 setErrorResponse(asyncResp->res,
1558 boost::beast::http::status::bad_request, noJsonDesc,
1559 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001560 return;
1561 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001562 nlohmann::json::iterator data = requestDbusData.find("data");
1563 if (data == requestDbusData.end())
1564 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001565 setErrorResponse(asyncResp->res,
1566 boost::beast::http::status::bad_request, noJsonDesc,
1567 badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001568 return;
1569 }
1570
1571 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001572 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001573 setErrorResponse(asyncResp->res,
1574 boost::beast::http::status::bad_request, noJsonDesc,
1575 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001576 return;
1577 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001578 auto transaction = std::make_shared<InProgressActionData>(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001579
1580 transaction->path = objectPath;
1581 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001582 transaction->arguments = std::move(*data);
George Liu2b731192023-01-11 16:27:13 +08001583 dbus::utility::getDbusObject(
1584 objectPath, {},
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585 [transaction](
George Liu2b731192023-01-11 16:27:13 +08001586 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001587 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1588 interfaceNames) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001589 if (ec || interfaceNames.empty())
1590 {
1591 BMCWEB_LOG_ERROR << "Can't find object";
1592 setErrorResponse(transaction->res,
1593 boost::beast::http::status::not_found,
1594 notFoundDesc, notFoundMsg);
1595 return;
1596 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001597
Ed Tanous002d39b2022-05-31 08:59:27 -07001598 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1599 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600
Ed Tanous002d39b2022-05-31 08:59:27 -07001601 for (const std::pair<std::string, std::vector<std::string>>& object :
1602 interfaceNames)
1603 {
1604 findActionOnInterface(transaction, object.first);
1605 }
George Liu2b731192023-01-11 16:27:13 +08001606 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001607}
1608
zhanghch058d1b46d2021-04-01 11:18:24 +08001609inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1610 const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001611{
1612 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1613
George Liu2b731192023-01-11 16:27:13 +08001614 dbus::utility::getDbusObject(
1615 objectPath, {},
zhanghch058d1b46d2021-04-01 11:18:24 +08001616 [asyncResp, objectPath](
George Liu2b731192023-01-11 16:27:13 +08001617 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001618 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1619 interfaceNames) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001620 if (ec || interfaceNames.empty())
1621 {
1622 BMCWEB_LOG_ERROR << "Can't find object";
1623 setErrorResponse(asyncResp->res,
1624 boost::beast::http::status::method_not_allowed,
1625 methodNotAllowedDesc, methodNotAllowedMsg);
1626 return;
1627 }
Matt Spinlerde818812018-12-11 16:39:20 -06001628
Ed Tanous002d39b2022-05-31 08:59:27 -07001629 auto transaction =
1630 std::make_shared<InProgressActionData>(asyncResp->res);
1631 transaction->path = objectPath;
1632 transaction->methodName = "Delete";
1633 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
Matt Spinlerde818812018-12-11 16:39:20 -06001634
Ed Tanous002d39b2022-05-31 08:59:27 -07001635 for (const std::pair<std::string, std::vector<std::string>>& object :
1636 interfaceNames)
1637 {
1638 findActionOnInterface(transaction, object.first);
1639 }
George Liu2b731192023-01-11 16:27:13 +08001640 });
Matt Spinlerde818812018-12-11 16:39:20 -06001641}
1642
zhanghch058d1b46d2021-04-01 11:18:24 +08001643inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1644 const std::string& objectPath, int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001645{
George Liu7a1dbc42022-12-07 16:03:22 +08001646 dbus::utility::getSubTreePaths(
1647 objectPath, depth, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001648 [asyncResp](
George Liu7a1dbc42022-12-07 16:03:22 +08001649 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001650 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001651 if (ec)
1652 {
1653 setErrorResponse(asyncResp->res,
1654 boost::beast::http::status::not_found,
1655 notFoundDesc, notFoundMsg);
1656 }
1657 else
1658 {
1659 asyncResp->res.jsonValue["status"] = "ok";
1660 asyncResp->res.jsonValue["message"] = "200 OK";
1661 asyncResp->res.jsonValue["data"] = objectPaths;
1662 }
George Liu7a1dbc42022-12-07 16:03:22 +08001663 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001665
zhanghch058d1b46d2021-04-01 11:18:24 +08001666inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1667 const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001668{
Ed Tanous049a0512018-11-01 13:58:42 -07001669 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
Ed Tanous049a0512018-11-01 13:58:42 -07001670
Ed Tanous14766872022-03-15 10:44:42 -07001671 asyncResp->res.jsonValue["message"] = "200 OK";
1672 asyncResp->res.jsonValue["status"] = "ok";
1673 asyncResp->res.jsonValue["data"] = nlohmann::json::object();
Ed Tanous049a0512018-11-01 13:58:42 -07001674
George Liue99073f2022-12-09 11:06:16 +08001675 dbus::utility::getSubTree(
1676 objectPath, 0, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001677 [objectPath, asyncResp](
George Liue99073f2022-12-09 11:06:16 +08001678 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001679 const dbus::utility::MapperGetSubTreeResponse& objectNames) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001680 auto transaction =
1681 std::make_shared<InProgressEnumerateData>(objectPath, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001682
Ed Tanous002d39b2022-05-31 08:59:27 -07001683 transaction->subtree =
1684 std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1685 objectNames);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001686
Ed Tanous002d39b2022-05-31 08:59:27 -07001687 if (ec)
1688 {
1689 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1690 << transaction->objectPath;
1691 setErrorResponse(transaction->asyncResp->res,
1692 boost::beast::http::status::not_found,
1693 notFoundDesc, notFoundMsg);
1694 return;
1695 }
Ed Tanous64530012018-02-06 17:08:16 -08001696
Ed Tanous002d39b2022-05-31 08:59:27 -07001697 // Add the data for the path passed in to the results
1698 // as if GetSubTree returned it, and continue on enumerating
1699 getObjectAndEnumerate(transaction);
George Liue99073f2022-12-09 11:06:16 +08001700 });
Ed Tanous64530012018-02-06 17:08:16 -08001701}
Ed Tanous911ac312017-08-15 09:37:42 -07001702
zhanghch058d1b46d2021-04-01 11:18:24 +08001703inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1704 std::string& objectPath, std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001706 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1707 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001708 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001709
Ed Tanous1abe55e2018-09-05 08:30:59 -07001710 std::shared_ptr<std::string> path =
1711 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001712
George Liu2b731192023-01-11 16:27:13 +08001713 dbus::utility::getDbusObject(
1714 *path, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001715 [asyncResp, path,
George Liu2b731192023-01-11 16:27:13 +08001716 propertyName](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001717 const dbus::utility::MapperGetObject& objectNames) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001718 if (ec || objectNames.empty())
1719 {
1720 setErrorResponse(asyncResp->res,
1721 boost::beast::http::status::not_found,
1722 notFoundDesc, notFoundMsg);
1723 return;
1724 }
1725 std::shared_ptr<nlohmann::json> response =
1726 std::make_shared<nlohmann::json>(nlohmann::json::object());
1727 // The mapper should never give us an empty interface names
1728 // list, but check anyway
1729 for (const std::pair<std::string, std::vector<std::string>>&
1730 connection : objectNames)
1731 {
1732 const std::vector<std::string>& interfaceNames = connection.second;
1733
1734 if (interfaceNames.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001735 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001736 setErrorResponse(asyncResp->res,
1737 boost::beast::http::status::not_found,
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001738 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001739 return;
1740 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001741
1742 for (const std::string& interface : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001743 {
Patrick Williams59d494e2022-07-22 19:26:55 -05001744 sdbusplus::message_t m =
Ed Tanous002d39b2022-05-31 08:59:27 -07001745 crow::connections::systemBus->new_method_call(
1746 connection.first.c_str(), path->c_str(),
1747 "org.freedesktop.DBus.Properties", "GetAll");
1748 m.append(interface);
1749 crow::connections::systemBus->async_send(
1750 m, [asyncResp, response,
1751 propertyName](const boost::system::error_code ec2,
Patrick Williams59d494e2022-07-22 19:26:55 -05001752 sdbusplus::message_t& msg) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001753 if (ec2)
1754 {
1755 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1756 << ec2;
1757 }
1758 else
1759 {
1760 nlohmann::json properties;
1761 int r = convertDBusToJSON("a{sv}", msg, properties);
1762 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001763 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001764 BMCWEB_LOG_ERROR << "convertDBusToJSON failed";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001765 }
1766 else
1767 {
Patrick Williams62bafc02022-09-08 17:35:35 -05001768 for (const auto& prop : properties.items())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001769 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001770 // if property name is empty, or
1771 // matches our search query, add it
1772 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001773
Ed Tanous002d39b2022-05-31 08:59:27 -07001774 if (propertyName->empty())
1775 {
1776 (*response)[prop.key()] =
1777 std::move(prop.value());
1778 }
1779 else if (prop.key() == *propertyName)
1780 {
1781 *response = std::move(prop.value());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001782 }
1783 }
1784 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001785 }
1786 if (response.use_count() == 1)
1787 {
1788 if (!propertyName->empty() && response->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001789 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001790 setErrorResponse(
1791 asyncResp->res,
1792 boost::beast::http::status::not_found,
1793 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001794 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001795 else
1796 {
1797 asyncResp->res.jsonValue["status"] = "ok";
1798 asyncResp->res.jsonValue["message"] = "200 OK";
1799 asyncResp->res.jsonValue["data"] = *response;
1800 }
1801 }
1802 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001803 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001804 }
George Liu2b731192023-01-11 16:27:13 +08001805 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001806}
1807
Ed Tanous1abe55e2018-09-05 08:30:59 -07001808struct AsyncPutRequest
1809{
Ed Tanous4e23a442022-06-06 09:57:26 -07001810 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
zhanghch058d1b46d2021-04-01 11:18:24 +08001811 asyncResp(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001812 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001813 ~AsyncPutRequest()
1814 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001815 if (asyncResp->res.jsonValue.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001816 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001817 setErrorResponse(asyncResp->res,
1818 boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001819 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001820 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001821 }
1822
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001823 AsyncPutRequest(const AsyncPutRequest&) = delete;
1824 AsyncPutRequest(AsyncPutRequest&&) = delete;
1825 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1826 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1827
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001828 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001829 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001830 setErrorResponse(asyncResp->res,
1831 boost::beast::http::status::internal_server_error,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001832 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001833 }
1834
zhanghch058d1b46d2021-04-01 11:18:24 +08001835 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001836 std::string objectPath;
1837 std::string propertyName;
1838 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001839};
1840
zhanghch058d1b46d2021-04-01 11:18:24 +08001841inline void handlePut(const crow::Request& req,
1842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001843 const std::string& objectPath,
1844 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001845{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001846 if (destProperty.empty())
1847 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001848 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001849 forbiddenResDesc, forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001850 return;
1851 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001852 nlohmann::json requestDbusData;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001853
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001854 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1855 if (ret == JsonParseResult::BadContentType)
1856 {
1857 setErrorResponse(asyncResp->res,
1858 boost::beast::http::status::unsupported_media_type,
1859 invalidContentType, unsupportedMediaMsg);
1860 return;
1861 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001862
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001863 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001864 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001865 setErrorResponse(asyncResp->res,
1866 boost::beast::http::status::bad_request, noJsonDesc,
1867 badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001868 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001869 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001870
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001871 auto propertyIt = requestDbusData.find("data");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872 if (propertyIt == requestDbusData.end())
1873 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001874 setErrorResponse(asyncResp->res,
1875 boost::beast::http::status::bad_request, noJsonDesc,
1876 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001877 return;
1878 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001879 const nlohmann::json& propertySetValue = *propertyIt;
zhanghch058d1b46d2021-04-01 11:18:24 +08001880 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001881 transaction->objectPath = objectPath;
1882 transaction->propertyName = destProperty;
1883 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001884
George Liu2b731192023-01-11 16:27:13 +08001885 dbus::utility::getDbusObject(
1886 transaction->objectPath, {},
1887 [transaction](const boost::system::error_code& ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001888 const dbus::utility::MapperGetObject& objectNames) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001889 if (!ec2 && objectNames.empty())
1890 {
1891 setErrorResponse(transaction->asyncResp->res,
1892 boost::beast::http::status::not_found,
1893 propNotFoundDesc, notFoundMsg);
1894 return;
1895 }
Ed Tanous911ac312017-08-15 09:37:42 -07001896
Ed Tanous002d39b2022-05-31 08:59:27 -07001897 for (const std::pair<std::string, std::vector<std::string>>&
1898 connection : objectNames)
1899 {
1900 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001901
Ed Tanous002d39b2022-05-31 08:59:27 -07001902 crow::connections::systemBus->async_method_call(
1903 [connectionName{std::string(connectionName)},
1904 transaction](const boost::system::error_code ec3,
1905 const std::string& introspectXml) {
1906 if (ec3)
1907 {
1908 BMCWEB_LOG_ERROR << "Introspect call failed with error: "
1909 << ec3.message()
1910 << " on process: " << connectionName;
1911 transaction->setErrorStatus("Unexpected Error");
1912 return;
1913 }
1914 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001915
Ed Tanous002d39b2022-05-31 08:59:27 -07001916 doc.Parse(introspectXml.c_str());
1917 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1918 if (pRoot == nullptr)
1919 {
1920 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1921 << introspectXml;
1922 transaction->setErrorStatus("Unexpected Error");
1923 return;
1924 }
1925 tinyxml2::XMLElement* ifaceNode =
1926 pRoot->FirstChildElement("interface");
1927 while (ifaceNode != nullptr)
1928 {
1929 const char* interfaceName = ifaceNode->Attribute("name");
1930 BMCWEB_LOG_DEBUG << "found interface " << interfaceName;
1931 tinyxml2::XMLElement* propNode =
1932 ifaceNode->FirstChildElement("property");
1933 while (propNode != nullptr)
1934 {
1935 const char* propertyName = propNode->Attribute("name");
1936 BMCWEB_LOG_DEBUG << "Found property " << propertyName;
1937 if (propertyName == transaction->propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001938 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001939 const char* argType = propNode->Attribute("type");
1940 if (argType != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001941 {
Patrick Williams59d494e2022-07-22 19:26:55 -05001942 sdbusplus::message_t m =
Ed Tanous002d39b2022-05-31 08:59:27 -07001943 crow::connections::systemBus
1944 ->new_method_call(
1945 connectionName.c_str(),
1946 transaction->objectPath.c_str(),
1947 "org.freedesktop.DBus."
1948 "Properties",
1949 "Set");
1950 m.append(interfaceName,
1951 transaction->propertyName);
1952 int r = sd_bus_message_open_container(
1953 m.get(), SD_BUS_TYPE_VARIANT, argType);
1954 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001955 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001956 transaction->setErrorStatus(
1957 "Unexpected Error");
1958 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001959 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001960 r = convertJsonToDbus(
1961 m.get(), argType,
1962 transaction->propertyValue);
1963 if (r < 0)
1964 {
1965 if (r == -ERANGE)
1966 {
1967 transaction->setErrorStatus(
1968 "Provided property value "
1969 "is out of range for the "
1970 "property type");
1971 }
1972 else
1973 {
1974 transaction->setErrorStatus(
1975 "Invalid arg type");
1976 }
1977 return;
1978 }
1979 r = sd_bus_message_close_container(m.get());
1980 if (r < 0)
1981 {
1982 transaction->setErrorStatus(
1983 "Unexpected Error");
1984 return;
1985 }
1986 crow::connections::systemBus->async_send(
1987 m,
Patrick Williams59d494e2022-07-22 19:26:55 -05001988 [transaction](boost::system::error_code ec,
1989 sdbusplus::message_t& m2) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001990 BMCWEB_LOG_DEBUG << "sent";
1991 if (ec)
1992 {
1993 const sd_bus_error* e = m2.get_error();
1994 setErrorResponse(
1995 transaction->asyncResp->res,
1996 boost::beast::http::status::
1997 forbidden,
1998 (e) != nullptr
1999 ? e->name
2000 : ec.category().name(),
2001 (e) != nullptr ? e->message
2002 : ec.message());
2003 }
2004 else
2005 {
2006 transaction->asyncResp->res
2007 .jsonValue["status"] = "ok";
2008 transaction->asyncResp->res
2009 .jsonValue["message"] = "200 OK";
2010 transaction->asyncResp->res
2011 .jsonValue["data"] = nullptr;
2012 }
2013 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002014 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002015 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002016 propNode = propNode->NextSiblingElement("property");
2017 }
2018 ifaceNode = ifaceNode->NextSiblingElement("interface");
2019 }
2020 },
2021 connectionName, transaction->objectPath,
2022 "org.freedesktop.DBus.Introspectable", "Introspect");
2023 }
George Liu2b731192023-01-11 16:27:13 +08002024 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002025}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002026
zhanghch058d1b46d2021-04-01 11:18:24 +08002027inline void handleDBusUrl(const crow::Request& req,
2028 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002029 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002030{
Ed Tanous049a0512018-11-01 13:58:42 -07002031
2032 // If accessing a single attribute, fill in and update objectPath,
2033 // otherwise leave destProperty blank
Ed Tanouse05aec52022-01-25 10:28:56 -08002034 std::string destProperty;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002035 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002036 size_t attrPosition = objectPath.find(attrSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002037 if (attrPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002038 {
2039 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2040 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002041 objectPath.resize(attrPosition);
Ed Tanous049a0512018-11-01 13:58:42 -07002042 }
2043
Ed Tanousb41187f2019-10-24 16:30:02 -07002044 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002045 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002046 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002047 size_t actionPosition = objectPath.find(actionSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002048 if (actionPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002049 {
2050 std::string postProperty =
2051 objectPath.substr((actionPosition + strlen(actionSeperator)),
2052 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002053 objectPath.resize(actionPosition);
zhanghch058d1b46d2021-04-01 11:18:24 +08002054 handleAction(req, asyncResp, objectPath, postProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002055 return;
2056 }
2057 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002058 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002059 {
Ed Tanous11ba3972022-07-11 09:50:41 -07002060 if (objectPath.ends_with("/enumerate"))
Ed Tanous049a0512018-11-01 13:58:42 -07002061 {
2062 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2063 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002064 handleEnumerate(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002065 }
Ed Tanous11ba3972022-07-11 09:50:41 -07002066 else if (objectPath.ends_with("/list"))
Ed Tanous049a0512018-11-01 13:58:42 -07002067 {
2068 objectPath.erase(objectPath.end() - sizeof("list"),
2069 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002070 handleList(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002071 }
2072 else
2073 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002074 // Trim any trailing "/" at the end
Ed Tanous11ba3972022-07-11 09:50:41 -07002075 if (objectPath.ends_with("/"))
Ed Tanousf839dfe2018-11-12 11:11:15 -08002076 {
2077 objectPath.pop_back();
zhanghch058d1b46d2021-04-01 11:18:24 +08002078 handleList(asyncResp, objectPath, 1);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002079 }
2080 else
2081 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002082 handleGet(asyncResp, objectPath, destProperty);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002083 }
Ed Tanous049a0512018-11-01 13:58:42 -07002084 }
2085 return;
2086 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002087 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002088 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002089 handlePut(req, asyncResp, objectPath, destProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002090 return;
2091 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002092 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002093 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002094 handleDelete(asyncResp, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002095 return;
2096 }
Ed Tanous049a0512018-11-01 13:58:42 -07002097
zhanghch058d1b46d2021-04-01 11:18:24 +08002098 setErrorResponse(asyncResp->res,
2099 boost::beast::http::status::method_not_allowed,
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002100 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002101}
2102
Ed Tanous1656b292022-05-04 11:33:42 -07002103inline void
2104 handleBusSystemPost(const crow::Request& req,
2105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2106 const std::string& processName,
2107 const std::string& requestedPath)
2108{
2109 std::vector<std::string> strs;
Ed Tanous50ebd4a2023-01-19 19:03:17 -08002110
2111 bmcweb::split(strs, requestedPath, '/');
Ed Tanous1656b292022-05-04 11:33:42 -07002112 std::string objectPath;
2113 std::string interfaceName;
2114 std::string methodName;
2115 auto it = strs.begin();
2116 if (it == strs.end())
2117 {
2118 objectPath = "/";
2119 }
2120 while (it != strs.end())
2121 {
2122 // Check if segment contains ".". If it does, it must be an
2123 // interface
2124 if (it->find(".") != std::string::npos)
2125 {
2126 break;
2127 // This check is necessary as the trailing slash gets
2128 // parsed as part of our <path> specifier above, which
2129 // causes the normal trailing backslash redirector to
2130 // fail.
2131 }
2132 if (!it->empty())
2133 {
2134 objectPath += "/" + *it;
2135 }
2136 it++;
2137 }
2138 if (it != strs.end())
2139 {
2140 interfaceName = *it;
2141 it++;
2142
2143 // after interface, we might have a method name
2144 if (it != strs.end())
2145 {
2146 methodName = *it;
2147 it++;
2148 }
2149 }
2150 if (it != strs.end())
2151 {
2152 // if there is more levels past the method name, something
2153 // went wrong, return not found
2154 asyncResp->res.result(boost::beast::http::status::not_found);
2155 return;
2156 }
2157 if (interfaceName.empty())
2158 {
2159 crow::connections::systemBus->async_method_call(
2160 [asyncResp, processName,
2161 objectPath](const boost::system::error_code ec,
2162 const std::string& introspectXml) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002163 if (ec)
2164 {
2165 BMCWEB_LOG_ERROR
2166 << "Introspect call failed with error: " << ec.message()
2167 << " on process: " << processName << " path: " << objectPath
2168 << "\n";
2169 return;
2170 }
2171 tinyxml2::XMLDocument doc;
Ed Tanous1656b292022-05-04 11:33:42 -07002172
Ed Tanous002d39b2022-05-31 08:59:27 -07002173 doc.Parse(introspectXml.c_str());
2174 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2175 if (pRoot == nullptr)
2176 {
2177 BMCWEB_LOG_ERROR << "XML document failed to parse "
2178 << processName << " " << objectPath << "\n";
2179 asyncResp->res.jsonValue["status"] = "XML parse error";
2180 asyncResp->res.result(
2181 boost::beast::http::status::internal_server_error);
2182 return;
2183 }
2184
2185 BMCWEB_LOG_DEBUG << introspectXml;
2186 asyncResp->res.jsonValue["status"] = "ok";
2187 asyncResp->res.jsonValue["bus_name"] = processName;
2188 asyncResp->res.jsonValue["object_path"] = objectPath;
2189
2190 nlohmann::json& interfacesArray =
2191 asyncResp->res.jsonValue["interfaces"];
2192 interfacesArray = nlohmann::json::array();
2193 tinyxml2::XMLElement* interface =
2194 pRoot->FirstChildElement("interface");
2195
2196 while (interface != nullptr)
2197 {
2198 const char* ifaceName = interface->Attribute("name");
2199 if (ifaceName != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002200 {
Ed Tanous8a592812022-06-04 09:06:59 -07002201 nlohmann::json::object_t interfaceObj;
2202 interfaceObj["name"] = ifaceName;
2203 interfacesArray.push_back(std::move(interfaceObj));
Ed Tanous1656b292022-05-04 11:33:42 -07002204 }
2205
Ed Tanous002d39b2022-05-31 08:59:27 -07002206 interface = interface->NextSiblingElement("interface");
2207 }
Ed Tanous1656b292022-05-04 11:33:42 -07002208 },
2209 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2210 "Introspect");
2211 }
2212 else if (methodName.empty())
2213 {
2214 crow::connections::systemBus->async_method_call(
2215 [asyncResp, processName, objectPath,
2216 interfaceName](const boost::system::error_code ec,
2217 const std::string& introspectXml) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002218 if (ec)
2219 {
2220 BMCWEB_LOG_ERROR
2221 << "Introspect call failed with error: " << ec.message()
2222 << " on process: " << processName << " path: " << objectPath
2223 << "\n";
2224 return;
2225 }
2226 tinyxml2::XMLDocument doc;
Ed Tanous1656b292022-05-04 11:33:42 -07002227
Ed Tanous002d39b2022-05-31 08:59:27 -07002228 doc.Parse(introspectXml.data(), introspectXml.size());
2229 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2230 if (pRoot == nullptr)
2231 {
2232 BMCWEB_LOG_ERROR << "XML document failed to parse "
2233 << processName << " " << objectPath << "\n";
2234 asyncResp->res.result(
2235 boost::beast::http::status::internal_server_error);
2236 return;
2237 }
2238
2239 asyncResp->res.jsonValue["status"] = "ok";
2240 asyncResp->res.jsonValue["bus_name"] = processName;
2241 asyncResp->res.jsonValue["interface"] = interfaceName;
2242 asyncResp->res.jsonValue["object_path"] = objectPath;
2243
2244 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"];
2245 methodsArray = nlohmann::json::array();
2246
2247 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"];
2248 signalsArray = nlohmann::json::array();
2249
2250 nlohmann::json& propertiesObj =
2251 asyncResp->res.jsonValue["properties"];
2252 propertiesObj = nlohmann::json::object();
2253
2254 // if we know we're the only call, build the
2255 // json directly
2256 tinyxml2::XMLElement* interface =
2257 pRoot->FirstChildElement("interface");
2258 while (interface != nullptr)
2259 {
2260 const char* ifaceName = interface->Attribute("name");
2261
2262 if (ifaceName != nullptr && ifaceName == interfaceName)
Ed Tanous1656b292022-05-04 11:33:42 -07002263 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002264 break;
Ed Tanous1656b292022-05-04 11:33:42 -07002265 }
Ed Tanous14766872022-03-15 10:44:42 -07002266
Ed Tanous002d39b2022-05-31 08:59:27 -07002267 interface = interface->NextSiblingElement("interface");
2268 }
2269 if (interface == nullptr)
2270 {
2271 // if we got to the end of the list and
2272 // never found a match, throw 404
2273 asyncResp->res.result(boost::beast::http::status::not_found);
2274 return;
2275 }
Ed Tanous1656b292022-05-04 11:33:42 -07002276
Ed Tanous002d39b2022-05-31 08:59:27 -07002277 tinyxml2::XMLElement* methods =
2278 interface->FirstChildElement("method");
2279 while (methods != nullptr)
2280 {
2281 nlohmann::json argsArray = nlohmann::json::array();
2282 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg");
2283 while (arg != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002284 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002285 nlohmann::json thisArg;
2286 for (const char* fieldName : std::array<const char*, 3>{
2287 "name", "direction", "type"})
Ed Tanous1656b292022-05-04 11:33:42 -07002288 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002289 const char* fieldValue = arg->Attribute(fieldName);
2290 if (fieldValue != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002291 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002292 thisArg[fieldName] = fieldValue;
2293 }
2294 }
2295 argsArray.push_back(std::move(thisArg));
2296 arg = arg->NextSiblingElement("arg");
2297 }
2298
2299 const char* name = methods->Attribute("name");
2300 if (name != nullptr)
2301 {
2302 std::string uri;
2303 uri.reserve(14 + processName.size() + objectPath.size() +
2304 interfaceName.size() + strlen(name));
2305 uri += "/bus/system/";
2306 uri += processName;
2307 uri += objectPath;
2308 uri += "/";
2309 uri += interfaceName;
2310 uri += "/";
2311 uri += name;
2312
2313 nlohmann::json::object_t object;
2314 object["name"] = name;
2315 object["uri"] = std::move(uri);
2316 object["args"] = argsArray;
2317
2318 methodsArray.push_back(std::move(object));
2319 }
2320 methods = methods->NextSiblingElement("method");
2321 }
2322 tinyxml2::XMLElement* signals =
2323 interface->FirstChildElement("signal");
2324 while (signals != nullptr)
2325 {
2326 nlohmann::json argsArray = nlohmann::json::array();
2327
2328 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg");
2329 while (arg != nullptr)
2330 {
2331 const char* name = arg->Attribute("name");
2332 const char* type = arg->Attribute("type");
2333 if (name != nullptr && type != nullptr)
2334 {
2335 argsArray.push_back({
2336 {"name", name},
2337 {"type", type},
2338 });
2339 }
2340 arg = arg->NextSiblingElement("arg");
2341 }
2342 const char* name = signals->Attribute("name");
2343 if (name != nullptr)
2344 {
2345 nlohmann::json::object_t object;
2346 object["name"] = name;
2347 object["args"] = argsArray;
2348 signalsArray.push_back(std::move(object));
2349 }
2350
2351 signals = signals->NextSiblingElement("signal");
2352 }
2353
2354 tinyxml2::XMLElement* property =
2355 interface->FirstChildElement("property");
2356 while (property != nullptr)
2357 {
2358 const char* name = property->Attribute("name");
2359 const char* type = property->Attribute("type");
2360 if (type != nullptr && name != nullptr)
2361 {
Patrick Williams59d494e2022-07-22 19:26:55 -05002362 sdbusplus::message_t m =
Ed Tanous002d39b2022-05-31 08:59:27 -07002363 crow::connections::systemBus->new_method_call(
2364 processName.c_str(), objectPath.c_str(),
2365 "org.freedesktop."
2366 "DBus."
2367 "Properties",
2368 "Get");
2369 m.append(interfaceName, name);
2370 nlohmann::json& propertyItem = propertiesObj[name];
2371 crow::connections::systemBus->async_send(
2372 m, [&propertyItem,
Ed Tanous02cad962022-06-30 16:50:15 -07002373 asyncResp](const boost::system::error_code& e,
Patrick Williams59d494e2022-07-22 19:26:55 -05002374 sdbusplus::message_t& msg) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002375 if (e)
Ed Tanous1656b292022-05-04 11:33:42 -07002376 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002377 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002378 }
Ed Tanous1656b292022-05-04 11:33:42 -07002379
Ed Tanous002d39b2022-05-31 08:59:27 -07002380 convertDBusToJSON("v", msg, propertyItem);
2381 });
Ed Tanous1656b292022-05-04 11:33:42 -07002382 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002383 property = property->NextSiblingElement("property");
2384 }
Ed Tanous1656b292022-05-04 11:33:42 -07002385 },
2386 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2387 "Introspect");
2388 }
2389 else
2390 {
2391 if (req.method() != boost::beast::http::verb::post)
2392 {
2393 asyncResp->res.result(boost::beast::http::status::not_found);
2394 return;
2395 }
2396
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002397 nlohmann::json requestDbusData;
2398 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2399 if (ret == JsonParseResult::BadContentType)
Ed Tanous1656b292022-05-04 11:33:42 -07002400 {
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002401 setErrorResponse(asyncResp->res,
2402 boost::beast::http::status::unsupported_media_type,
2403 invalidContentType, unsupportedMediaMsg);
Ed Tanous1656b292022-05-04 11:33:42 -07002404 return;
2405 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002406 if (ret != JsonParseResult::Success)
2407 {
2408 setErrorResponse(asyncResp->res,
2409 boost::beast::http::status::bad_request,
2410 noJsonDesc, badReqMsg);
2411 return;
2412 }
2413
Ed Tanous1656b292022-05-04 11:33:42 -07002414 if (!requestDbusData.is_array())
2415 {
2416 asyncResp->res.result(boost::beast::http::status::bad_request);
2417 return;
2418 }
2419 auto transaction =
2420 std::make_shared<InProgressActionData>(asyncResp->res);
2421
2422 transaction->path = objectPath;
2423 transaction->methodName = methodName;
2424 transaction->arguments = std::move(requestDbusData);
2425
2426 findActionOnInterface(transaction, processName);
2427 }
2428}
2429
Ed Tanous23a21a12020-07-25 04:45:05 +00002430inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002431{
2432 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous432a8902021-06-14 15:28:56 -07002433 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002434 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002435 [](const crow::Request&,
2436 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002437 nlohmann::json::array_t buses;
2438 nlohmann::json& bus = buses.emplace_back();
2439 bus["name"] = "system";
2440 asyncResp->res.jsonValue["busses"] = std::move(buses);
2441 asyncResp->res.jsonValue["status"] = "ok";
2442 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002443
2444 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous432a8902021-06-14 15:28:56 -07002445 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002446 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002447 [](const crow::Request&,
2448 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002449 auto myCallback = [asyncResp](const boost::system::error_code ec,
zhanghch058d1b46d2021-04-01 11:18:24 +08002450 std::vector<std::string>& names) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002451 if (ec)
2452 {
2453 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2454 asyncResp->res.result(
2455 boost::beast::http::status::internal_server_error);
2456 }
2457 else
2458 {
2459 std::sort(names.begin(), names.end());
2460 asyncResp->res.jsonValue["status"] = "ok";
2461 auto& objectsSub = asyncResp->res.jsonValue["objects"];
Ed Tanous02cad962022-06-30 16:50:15 -07002462 for (const auto& name : names)
Ed Tanous002d39b2022-05-31 08:59:27 -07002463 {
2464 nlohmann::json::object_t object;
2465 object["name"] = name;
2466 objectsSub.push_back(std::move(object));
2467 }
2468 }
2469 };
2470 crow::connections::systemBus->async_method_call(
2471 std::move(myCallback), "org.freedesktop.DBus", "/",
2472 "org.freedesktop.DBus", "ListNames");
2473 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002474
2475 BMCWEB_ROUTE(app, "/list/")
Ed Tanous432a8902021-06-14 15:28:56 -07002476 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002477 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002478 [](const crow::Request&,
2479 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002480 handleList(asyncResp, "/");
2481 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002482
2483 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002484 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002485 .methods(boost::beast::http::verb::get)(
2486 [](const crow::Request& req,
2487 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002488 const std::string& path) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002489 std::string objectPath = "/xyz/" + path;
2490 handleDBusUrl(req, asyncResp, objectPath);
2491 });
zhanghch058d1b46d2021-04-01 11:18:24 +08002492
2493 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002494 .privileges({{"ConfigureComponents", "ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002495 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2496 boost::beast::http::verb::delete_)(
2497 [](const crow::Request& req,
2498 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2499 const std::string& path) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002500 std::string objectPath = "/xyz/" + path;
2501 handleDBusUrl(req, asyncResp, objectPath);
2502 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002503
Ed Tanous049a0512018-11-01 13:58:42 -07002504 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002505 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002506 .methods(boost::beast::http::verb::get)(
2507 [](const crow::Request& req,
2508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2509 const std::string& path) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002510 std::string objectPath = "/org/" + path;
2511 handleDBusUrl(req, asyncResp, objectPath);
2512 });
Tanousf00032d2018-11-05 01:18:10 -03002513
2514 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002515 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002516 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2517 boost::beast::http::verb::delete_)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002518 [](const crow::Request& req,
2519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002520 const std::string& path) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002521 std::string objectPath = "/org/" + path;
2522 handleDBusUrl(req, asyncResp, objectPath);
2523 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002524
Ed Tanous1abe55e2018-09-05 08:30:59 -07002525 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002526 .privileges({{"ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002527 .methods(boost::beast::http::verb::get)(
2528 [](const crow::Request&,
2529 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2530 const std::string& dumpId) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002531 if (!validateFilename(dumpId))
2532 {
2533 asyncResp->res.result(boost::beast::http::status::bad_request);
2534 return;
2535 }
2536 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002537
Ed Tanous002d39b2022-05-31 08:59:27 -07002538 loc /= dumpId;
Ramesh Iyyard9207042019-07-05 08:04:42 -05002539
Ed Tanous002d39b2022-05-31 08:59:27 -07002540 if (!std::filesystem::exists(loc) ||
2541 !std::filesystem::is_directory(loc))
2542 {
2543 BMCWEB_LOG_ERROR << loc.string() << "Not found";
2544 asyncResp->res.result(boost::beast::http::status::not_found);
2545 return;
2546 }
2547 std::filesystem::directory_iterator files(loc);
zhanghch058d1b46d2021-04-01 11:18:24 +08002548
Ed Tanous002d39b2022-05-31 08:59:27 -07002549 for (const auto& file : files)
2550 {
2551 std::ifstream readFile(file.path());
2552 if (!readFile.good())
2553 {
2554 continue;
2555 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002556
Ed Tanousd9f6c622022-03-17 09:12:17 -07002557 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07002558 "application/octet-stream");
zhanghch058d1b46d2021-04-01 11:18:24 +08002559
Ed Tanous002d39b2022-05-31 08:59:27 -07002560 // Assuming only one dump file will be present in the dump
2561 // id directory
2562 std::string dumpFileName = file.path().filename().string();
zhanghch058d1b46d2021-04-01 11:18:24 +08002563
Ed Tanous002d39b2022-05-31 08:59:27 -07002564 // Filename should be in alphanumeric, dot and underscore
2565 // Its based on phosphor-debug-collector application
2566 // dumpfile format
2567 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2568 if (!std::regex_match(dumpFileName, dumpFileRegex))
2569 {
2570 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName;
zhanghch058d1b46d2021-04-01 11:18:24 +08002571 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanousad18f072018-11-14 14:07:48 -08002572 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002573 }
2574 std::string contentDispositionParam =
2575 "attachment; filename=\"" + dumpFileName + "\"";
2576
Ed Tanousd9f6c622022-03-17 09:12:17 -07002577 asyncResp->res.addHeader(
2578 boost::beast::http::field::content_disposition,
2579 contentDispositionParam);
Ed Tanous002d39b2022-05-31 08:59:27 -07002580
2581 asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile),
2582 std::istreambuf_iterator<char>()};
2583 return;
2584 }
2585 asyncResp->res.result(boost::beast::http::status::not_found);
2586 return;
2587 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002588
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002589 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002590 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002591
2592 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002593 [](const crow::Request&,
2594 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +00002595 const std::string& connection) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002596 introspectObjects(connection, "/", asyncResp);
2597 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002598
2599 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002600 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous1656b292022-05-04 11:33:42 -07002601 .methods(boost::beast::http::verb::get,
2602 boost::beast::http::verb::post)(handleBusSystemPost);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002603}
2604} // namespace openbmc_mapper
2605} // namespace crow