blob: fc074d6843f5e24f28ecfcdaffe131ea7b308abd [file] [log] [blame]
Ed Tanous6be832e2024-09-10 11:44:48 -07001/*
2Copyright (c) 2018 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070016
James Feist5b4aa862018-08-16 14:07:01 -070017#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
19#include "async_resp.hpp"
Ed Tanous95c63072024-03-26 13:19:52 -070020#include "boost_formatters.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "dbus_singleton.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080022#include "dbus_utility.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000023#include "http_request.hpp"
24#include "http_response.hpp"
Ed Tanous95c63072024-03-26 13:19:52 -070025#include "json_formatters.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000026#include "logging.hpp"
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010027#include "parsing.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000028#include "routing.hpp"
Ed Tanous50ebd4a2023-01-19 19:03:17 -080029#include "str_utility.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000030
31#include <systemd/sd-bus-protocol.h>
32#include <systemd/sd-bus.h>
Ed Tanous911ac312017-08-15 09:37:42 -070033#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070034
Nan Zhoud5c80ad2022-07-11 01:16:31 +000035#include <boost/beast/http/status.hpp>
36#include <boost/beast/http/verb.hpp>
37#include <boost/container/flat_map.hpp>
38#include <boost/container/vector.hpp>
George Liue99073f2022-12-09 11:06:16 +080039#include <boost/system/error_code.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000040#include <nlohmann/json.hpp>
41#include <sdbusplus/asio/connection.hpp>
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +020042#include <sdbusplus/asio/property.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000043#include <sdbusplus/exception.hpp>
44#include <sdbusplus/message.hpp>
45#include <sdbusplus/message/native_types.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046
Nan Zhoud5c80ad2022-07-11 01:16:31 +000047#include <algorithm>
48#include <array>
49#include <cerrno>
50#include <cstdint>
51#include <cstring>
James Feist4418c7f2019-04-15 11:09:15 -070052#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070053#include <fstream>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000054#include <functional>
55#include <initializer_list>
56#include <iterator>
57#include <limits>
58#include <map>
59#include <memory>
Ed Tanous3544d2a2023-08-06 18:12:20 -070060#include <ranges>
Ramesh Iyyard9207042019-07-05 08:04:42 -050061#include <regex>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000062#include <string>
63#include <string_view>
64#include <type_traits>
Ed Tanousb5a76932020-09-29 16:16:58 -070065#include <utility>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000066#include <variant>
67#include <vector>
68
Ed Tanous1abe55e2018-09-05 08:30:59 -070069namespace crow
70{
71namespace openbmc_mapper
72{
Ed Tanous23a21a12020-07-25 04:45:05 +000073const constexpr char* notFoundMsg = "404 Not Found";
74const constexpr char* badReqMsg = "400 Bad Request";
75const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
76const constexpr char* forbiddenMsg = "403 Forbidden";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010077const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
Ed Tanous23a21a12020-07-25 04:45:05 +000078const constexpr char* methodFailedMsg = "500 Method Call Failed";
79const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
80const constexpr char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060081 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous23a21a12020-07-25 04:45:05 +000082const constexpr char* propNotFoundDesc =
83 "The specified property cannot be found";
84const constexpr char* noJsonDesc = "No JSON object could be decoded";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010085const constexpr char* invalidContentType =
86 "Content-type header is missing or invalid";
Ed Tanous23a21a12020-07-25 04:45:05 +000087const constexpr char* methodNotFoundDesc =
88 "The specified method cannot be found";
89const constexpr char* methodNotAllowedDesc = "Method not allowed";
90const constexpr char* forbiddenPropDesc =
91 "The specified property cannot be created";
92const constexpr char* forbiddenResDesc =
93 "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060094
Josh Lehan482c45a2022-03-29 17:10:44 -070095inline bool validateFilename(const std::string& filename)
96{
Ed Tanous4b242742023-05-11 09:51:51 -070097 static std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
Josh Lehan482c45a2022-03-29 17:10:44 -070098
99 return std::regex_match(filename, validFilename);
100}
101
Ed Tanous23a21a12020-07-25 04:45:05 +0000102inline void setErrorResponse(crow::Response& res,
103 boost::beast::http::status result,
Ed Tanous26ccae32023-02-16 10:28:44 -0800104 const std::string& desc, std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -0600105{
106 res.result(result);
Ed Tanous14766872022-03-15 10:44:42 -0700107 res.jsonValue["data"]["description"] = desc;
108 res.jsonValue["message"] = msg;
109 res.jsonValue["status"] = "error";
Matt Spinler2ae60092018-12-06 10:35:36 -0600110}
111
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400112inline void introspectObjects(
113 const std::string& processName, const std::string& objectPath,
114 const std::shared_ptr<bmcweb::AsyncResp>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700116 if (transaction->res.jsonValue.is_null())
117 {
Ed Tanous14766872022-03-15 10:44:42 -0700118 transaction->res.jsonValue["status"] = "ok";
119 transaction->res.jsonValue["bus_name"] = processName;
120 transaction->res.jsonValue["objects"] = nlohmann::json::array();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700121 }
122
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700124 [transaction, processName{std::string(processName)},
125 objectPath{std::string(objectPath)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800126 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000127 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400128 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700129 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400130 BMCWEB_LOG_ERROR(
131 "Introspect call failed with error: {} on process: {} path: {}",
132 ec.message(), processName, objectPath);
133 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400135 nlohmann::json::object_t object;
136 object["path"] = objectPath;
137
138 transaction->res.jsonValue["objects"].emplace_back(
139 std::move(object));
140
141 tinyxml2::XMLDocument doc;
142
143 doc.Parse(introspectXml.c_str());
144 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
145 if (pRoot == nullptr)
146 {
147 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
148 processName, objectPath);
149 }
150 else
151 {
152 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
153 while (node != nullptr)
154 {
155 const char* childPath = node->Attribute("name");
156 if (childPath != nullptr)
157 {
158 std::string newpath;
159 if (objectPath != "/")
160 {
161 newpath += objectPath;
162 }
163 newpath += std::string("/") + childPath;
164 // introspect the subobjects as well
165 introspectObjects(processName, newpath, transaction);
166 }
167
168 node = node->NextSiblingElement("node");
169 }
170 }
171 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700172 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700174}
Ed Tanous64530012018-02-06 17:08:16 -0800175
Ed Tanous23a21a12020-07-25 04:45:05 +0000176inline void getPropertiesForEnumerate(
177 const std::string& objectPath, const std::string& service,
Ed Tanousb5a76932020-09-29 16:16:58 -0700178 const std::string& interface,
179 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600180{
Ed Tanous62598e32023-07-17 17:06:25 -0700181 BMCWEB_LOG_DEBUG("getPropertiesForEnumerate {} {} {}", objectPath, service,
182 interface);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600183
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200184 sdbusplus::asio::getAllProperties(
185 *crow::connections::systemBus, service, objectPath, interface,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800186 [asyncResp, objectPath, service,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800187 interface](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800188 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400189 if (ec)
190 {
191 BMCWEB_LOG_ERROR(
192 "GetAll on path {} iface {} service {} failed with code {}",
193 objectPath, interface, service, ec);
194 return;
195 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600196
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400197 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
198 nlohmann::json& objectJson = dataJson[objectPath];
199 if (objectJson.is_null())
200 {
201 objectJson = nlohmann::json::object();
202 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600203
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400204 for (const auto& [name, value] : propertiesList)
205 {
206 nlohmann::json& propertyJson = objectJson[name];
207 std::visit(
208 [&propertyJson](auto&& val) {
209 if constexpr (std::is_same_v<
210 std::decay_t<decltype(val)>,
211 sdbusplus::message::unix_fd>)
212 {
213 propertyJson = val.fd;
214 }
215 else
216 {
217 propertyJson = val;
218 }
219 },
220 value);
221 }
222 });
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600223}
224
225// Find any results that weren't picked up by ObjectManagers, to be
226// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000227inline void findRemainingObjectsForEnumerate(
Ed Tanousb5a76932020-09-29 16:16:58 -0700228 const std::string& objectPath,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800229 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
Ed Tanousb5a76932020-09-29 16:16:58 -0700230 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600231{
Ed Tanous62598e32023-07-17 17:06:25 -0700232 BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate");
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500233 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600234
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500235 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600236 {
237 if (path == objectPath)
238 {
239 // An enumerate does not return the target path's properties
240 continue;
241 }
242 if (dataJson.find(path) == dataJson.end())
243 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600245 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500246 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600247 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700248 if (!interface.starts_with("org.freedesktop.DBus"))
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600249 {
250 getPropertiesForEnumerate(path, service, interface,
251 asyncResp);
252 }
253 }
254 }
255 }
256 }
257}
258
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600259struct InProgressEnumerateData
260{
zhanghch058d1b46d2021-04-01 11:18:24 +0800261 InProgressEnumerateData(
262 const std::string& objectPathIn,
263 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400264 objectPath(objectPathIn), asyncResp(asyncRespIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500265 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600266
267 ~InProgressEnumerateData()
268 {
Ed Tanous24b2fe82022-01-06 12:45:54 -0800269 try
270 {
271 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
272 }
273 catch (...)
274 {
Ed Tanous62598e32023-07-17 17:06:25 -0700275 BMCWEB_LOG_CRITICAL(
276 "findRemainingObjectsForEnumerate threw exception");
Ed Tanous24b2fe82022-01-06 12:45:54 -0800277 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600278 }
279
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800280 InProgressEnumerateData(const InProgressEnumerateData&) = delete;
281 InProgressEnumerateData(InProgressEnumerateData&&) = delete;
282 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
283 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600284 const std::string objectPath;
Ed Tanousb9d36b42022-02-26 21:42:46 -0800285 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600286 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
287};
288
Ed Tanous23a21a12020-07-25 04:45:05 +0000289inline void getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000290 const std::string& objectName, const std::string& objectManagerPath,
291 const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700292 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700293{
Ed Tanous62598e32023-07-17 17:06:25 -0700294 BMCWEB_LOG_DEBUG(
295 "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}",
296 objectName, objectManagerPath, connectionName);
George Liu5eb468d2023-06-20 17:03:24 +0800297 sdbusplus::message::object_path path(objectManagerPath);
298 dbus::utility::getManagedObjects(
299 connectionName, path,
Ed Tanous81ce6092020-12-17 16:54:55 +0000300 [transaction, objectName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800301 connectionName](const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000302 const dbus::utility::ManagedObjectType& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400303 if (ec)
Ed Tanous049a0512018-11-01 13:58:42 -0700304 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400305 BMCWEB_LOG_ERROR(
306 "GetManagedObjects on path {} on connection {} failed with code {}",
307 objectName, connectionName, ec);
308 return;
309 }
310
311 nlohmann::json& dataJson =
312 transaction->asyncResp->res.jsonValue["data"];
313
314 for (const auto& objectPath : objects)
315 {
316 if (objectPath.first.str.starts_with(objectName))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400318 BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str);
319 nlohmann::json& objectJson = dataJson[objectPath.first.str];
320 if (objectJson.is_null())
321 {
322 objectJson = nlohmann::json::object();
323 }
324 for (const auto& interface : objectPath.second)
325 {
326 for (const auto& property : interface.second)
327 {
328 nlohmann::json& propertyJson =
329 objectJson[property.first];
330 std::visit(
331 [&propertyJson](auto&& val) {
332 if constexpr (
333 std::is_same_v<
334 std::decay_t<decltype(val)>,
335 sdbusplus::message::unix_fd>)
336 {
337 propertyJson = val.fd;
338 }
339 else
340 {
341 propertyJson = val;
342 }
343 },
344 property.second);
345 }
346 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700347 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500348 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700349 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400350 if (interface.first == "org.freedesktop.DBus.ObjectManager")
Ed Tanous049a0512018-11-01 13:58:42 -0700351 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400352 getManagedObjectsForEnumerate(
353 objectPath.first.str, objectPath.first.str,
354 connectionName, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700355 }
356 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700357 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400358 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700359}
360
Ed Tanous23a21a12020-07-25 04:45:05 +0000361inline void findObjectManagerPathForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000362 const std::string& objectName, const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700363 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700364{
Ed Tanous62598e32023-07-17 17:06:25 -0700365 BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}",
366 objectName, connectionName);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700367 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000368 [transaction, objectName, connectionName](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800369 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800370 const dbus::utility::MapperGetAncestorsResponse& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400371 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700372 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400373 BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}",
374 objectName, ec);
375 return;
376 }
377
378 for (const auto& pathGroup : objects)
379 {
380 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700381 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400382 if (connectionGroup.first == connectionName)
383 {
384 // Found the object manager path for this resource.
385 getManagedObjectsForEnumerate(
386 objectName, pathGroup.first, connectionName,
387 transaction);
388 return;
389 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700390 }
391 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400392 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700393 "xyz.openbmc_project.ObjectMapper",
394 "/xyz/openbmc_project/object_mapper",
Ed Tanous81ce6092020-12-17 16:54:55 +0000395 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500396 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700397}
Ed Tanous64530012018-02-06 17:08:16 -0800398
Ed Tanous7c091622019-05-23 11:42:36 -0700399// Uses GetObject to add the object info about the target /enumerate path to
400// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600401// target path, and then continues on enumerating the rest of the tree.
Ed Tanousb5a76932020-09-29 16:16:58 -0700402inline void getObjectAndEnumerate(
403 const std::shared_ptr<InProgressEnumerateData>& transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600404{
George Liu2b731192023-01-11 16:27:13 +0800405 dbus::utility::getDbusObject(
406 transaction->objectPath, {},
407 [transaction](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800408 const dbus::utility::MapperGetObject& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400409 if (ec)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600410 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400411 BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}",
412 transaction->objectPath, ec);
413 return;
414 }
415
416 BMCWEB_LOG_DEBUG("GetObject for {} has {} entries",
417 transaction->objectPath, objects.size());
418 if (!objects.empty())
419 {
420 transaction->subtree->emplace_back(transaction->objectPath,
421 objects);
422 }
423
424 // Map indicating connection name, and the path where the object
425 // manager exists
426 boost::container::flat_map<
427 std::string, std::string, std::less<>,
428 std::vector<std::pair<std::string, std::string>>>
429 connections;
430
431 for (const auto& object : *(transaction->subtree))
432 {
433 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600434 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400435 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600436 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400437 BMCWEB_LOG_DEBUG("{} has interface {}",
438 connection.first, interface);
439 if (interface == "org.freedesktop.DBus.ObjectManager")
440 {
441 BMCWEB_LOG_DEBUG("found object manager path {}",
442 object.first);
443 connections[connection.first] = object.first;
444 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600445 }
446 }
447 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400448 BMCWEB_LOG_DEBUG("Got {} connections", connections.size());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600449
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400450 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600451 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400452 // If we already know where the object manager is, we don't
453 // need to search for it, we can call directly in to
454 // getManagedObjects
455 if (!connection.second.empty())
456 {
457 getManagedObjectsForEnumerate(
458 transaction->objectPath, connection.second,
459 connection.first, transaction);
460 }
461 else
462 {
463 // otherwise we need to find the object manager path
464 // before we can continue
465 findObjectManagerPathForEnumerate(
466 transaction->objectPath, connection.first, transaction);
467 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600468 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400469 });
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600470}
Ed Tanous64530012018-02-06 17:08:16 -0800471
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700472// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700473struct InProgressActionData
474{
Lei YU28dd5ca2023-03-17 13:17:05 +0800475 explicit InProgressActionData(
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400476 const std::shared_ptr<bmcweb::AsyncResp>& res) : asyncResp(res)
Ed Tanous23a21a12020-07-25 04:45:05 +0000477 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 ~InProgressActionData()
479 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600480 // Methods could have been called across different owners
481 // and interfaces, where some calls failed and some passed.
482 //
483 // The rules for this are:
484 // * if no method was called - error
485 // * if a method failed and none passed - error
486 // (converse: if at least one method passed - OK)
487 // * for the method output:
488 // * if output processing didn't fail, return the data
489
490 // Only deal with method returns if nothing failed earlier
Lei YU28dd5ca2023-03-17 13:17:05 +0800491 if (asyncResp->res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700492 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600493 if (!methodPassed)
494 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500495 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600496 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800497 setErrorResponse(asyncResp->res,
498 boost::beast::http::status::not_found,
Matt Spinler16caaee2019-01-15 11:40:34 -0600499 methodNotFoundDesc, notFoundMsg);
500 }
501 }
502 else
503 {
504 if (outputFailed)
505 {
506 setErrorResponse(
Lei YU28dd5ca2023-03-17 13:17:05 +0800507 asyncResp->res,
508 boost::beast::http::status::internal_server_error,
Matt Spinler16caaee2019-01-15 11:40:34 -0600509 "Method output failure", methodOutputFailedMsg);
510 }
511 else
512 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800513 asyncResp->res.jsonValue["status"] = "ok";
514 asyncResp->res.jsonValue["message"] = "200 OK";
515 asyncResp->res.jsonValue["data"] = methodResponse;
Matt Spinler16caaee2019-01-15 11:40:34 -0600516 }
517 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700519 }
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800520 InProgressActionData(const InProgressActionData&) = delete;
521 InProgressActionData(InProgressActionData&&) = delete;
522 InProgressActionData& operator=(const InProgressActionData&) = delete;
523 InProgressActionData& operator=(InProgressActionData&&) = delete;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700524
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500525 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700526 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800527 setErrorResponse(asyncResp->res,
528 boost::beast::http::status::bad_request, desc,
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600529 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 }
Lei YU28dd5ca2023-03-17 13:17:05 +0800531 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532 std::string path;
533 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600534 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600535 bool methodPassed = false;
536 bool methodFailed = false;
537 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600538 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600539 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700541};
542
Ed Tanous23a21a12020-07-25 04:45:05 +0000543inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700544{
545 std::vector<std::string> ret;
546 if (string.empty())
547 {
548 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700549 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700550 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 int containerDepth = 0;
552
553 for (std::string::const_iterator character = string.begin();
554 character != string.end(); character++)
555 {
556 ret.back() += *character;
557 switch (*character)
558 {
559 case ('a'):
560 break;
561 case ('('):
562 case ('{'):
563 containerDepth++;
564 break;
565 case ('}'):
566 case (')'):
567 containerDepth--;
568 if (containerDepth == 0)
569 {
570 if (character + 1 != string.end())
571 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700572 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 }
574 }
575 break;
576 default:
577 if (containerDepth == 0)
578 {
579 if (character + 1 != string.end())
580 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700581 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 }
583 }
584 break;
585 }
586 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600587
588 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700589}
590
Ed Tanous81ce6092020-12-17 16:54:55 +0000591inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
592 const nlohmann::json& inputJson)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593{
594 int r = 0;
Ed Tanous296579b2024-03-11 16:58:24 -0700595 BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType);
Ed Tanous81ce6092020-12-17 16:54:55 +0000596 const std::vector<std::string> argTypes = dbusArgSplit(argType);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700597
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 // Assume a single object for now.
Ed Tanous81ce6092020-12-17 16:54:55 +0000599 const nlohmann::json* j = &inputJson;
600 nlohmann::json::const_iterator jIt = inputJson.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700601
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500602 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 {
604 // If we are decoding multiple objects, grab the pointer to the
605 // iterator, and increment it for the next loop
606 if (argTypes.size() > 1)
607 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000608 if (jIt == inputJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 {
610 return -2;
611 }
612 j = &*jIt;
613 jIt++;
614 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500615 const int64_t* intValue = j->get_ptr<const int64_t*>();
616 const std::string* stringValue = j->get_ptr<const std::string*>();
617 const double* doubleValue = j->get_ptr<const double*>();
618 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619 int64_t v = 0;
620 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700621
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 // Do some basic type conversions that make sense. uint can be
623 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700624 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500626 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700627 if (uintValue != nullptr)
628 {
629 v = static_cast<int64_t>(*uintValue);
630 intValue = &v;
631 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 }
Ed Tanous66664f22019-10-11 13:05:49 -0700633 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500635 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700636 if (uintValue != nullptr)
637 {
638 d = static_cast<double>(*uintValue);
639 doubleValue = &d;
640 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 }
Ed Tanous66664f22019-10-11 13:05:49 -0700642 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700643 {
Ed Tanous66664f22019-10-11 13:05:49 -0700644 if (intValue != nullptr)
645 {
646 d = static_cast<double>(*intValue);
647 doubleValue = &d;
648 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700649 }
650
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700651 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700653 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 {
655 return -1;
656 }
Ed Tanous271584a2019-07-09 16:24:22 -0700657 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500658 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 if (r < 0)
660 {
661 return r;
662 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700663 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700666 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
668 return -1;
669 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500670 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
671 (*intValue > std::numeric_limits<int32_t>::max()))
672 {
673 return -ERANGE;
674 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 int32_t i = static_cast<int32_t>(*intValue);
676 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 if (r < 0)
678 {
679 return r;
680 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700681 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
684 // lots of ways bool could be represented here. Try them all
Ed Tanouse662eae2022-01-25 10:39:19 -0800685 int boolInt = 0;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500688 if (*intValue == 1)
689 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800690 boolInt = 1;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500691 }
692 else if (*intValue == 0)
693 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800694 boolInt = 0;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500695 }
696 else
697 {
698 return -ERANGE;
699 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 }
701 else if (b != nullptr)
702 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600703 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700705 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 {
Ed Tanous18f8f602023-07-18 10:07:23 -0700707 if (!stringValue->empty())
708 {
709 if (stringValue->front() == 't' ||
710 stringValue->front() == 'T')
711 {
712 boolInt = 1;
713 }
714 }
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 Tanous07900812024-05-06 15:41:30 -0700818 r = sd_bus_message_append_basic(m, argCode[0], doubleValue);
819 if (r < 0)
820 {
821 return r;
822 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700824 else if (argCode.starts_with("a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700826 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700828 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 if (r < 0)
830 {
831 return r;
832 }
833
Ed Tanous0dfeda62019-10-24 11:21:38 -0700834 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700836 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837 if (r < 0)
838 {
839 return r;
840 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 }
842 sd_bus_message_close_container(m);
843 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700844 else if (argCode.starts_with("v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700846 std::string containedType = argCode.substr(1);
Ed Tanous62598e32023-07-17 17:06:25 -0700847 BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}",
848 argCode, containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700850 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 if (r < 0)
852 {
853 return r;
854 }
855
Ed Tanous81ce6092020-12-17 16:54:55 +0000856 r = convertJsonToDbus(m, containedType, inputJson);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 if (r < 0)
858 {
859 return r;
860 }
861
862 r = sd_bus_message_close_container(m);
863 if (r < 0)
864 {
865 return r;
866 }
867 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700868 else if (argCode.starts_with("(") && argCode.ends_with(")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300870 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700872 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800873 if (r < 0)
874 {
875 return r;
876 }
877
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 nlohmann::json::const_iterator it = j->begin();
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300879 for (const std::string& argCode2 : dbusArgSplit(containedType))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 {
881 if (it == j->end())
882 {
883 return -1;
884 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000885 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 if (r < 0)
887 {
888 return r;
889 }
890 it++;
891 }
892 r = sd_bus_message_close_container(m);
893 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700894 else if (argCode.starts_with("{") && argCode.ends_with("}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300896 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700898 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800899 if (r < 0)
900 {
901 return r;
902 }
903
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700904 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700905 if (codes.size() != 2)
906 {
907 return -1;
908 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700909 const std::string& keyType = codes[0];
910 const std::string& valueType = codes[1];
Ed Tanous0bdda662023-08-03 17:27:34 -0700911 const nlohmann::json::object_t* arr =
912 j->get_ptr<const nlohmann::json::object_t*>();
913 if (arr == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700915 return -1;
916 }
917 for (const auto& it : *arr)
918 {
919 r = convertJsonToDbus(m, keyType, it.first);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 if (r < 0)
921 {
922 return r;
923 }
924
Ed Tanous0bdda662023-08-03 17:27:34 -0700925 r = convertJsonToDbus(m, valueType, it.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926 if (r < 0)
927 {
928 return r;
929 }
930 }
931 r = sd_bus_message_close_container(m);
932 }
933 else
934 {
935 return -2;
936 }
937 if (r < 0)
938 {
939 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700940 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700941
Ed Tanous1abe55e2018-09-05 08:30:59 -0700942 if (argTypes.size() > 1)
943 {
944 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700945 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700946 }
Matt Spinler127ea542019-01-14 11:04:28 -0600947
948 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700949}
950
Matt Spinlerd22a7132019-01-14 12:14:30 -0600951template <typename T>
Patrick Williams59d494e2022-07-22 19:26:55 -0500952int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500953 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600954{
955 T value;
Ed Tanousf79ce6a2024-03-20 12:27:06 -0700956 // When T == char*, this warning fires. Unclear how to resolve
957 // Given that sd-bus takes a void pointer to a char*, and that's
958 // Not something we can fix.
959 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600960 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
961 if (r < 0)
962 {
Ed Tanous62598e32023-07-17 17:06:25 -0700963 BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!",
964 typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -0600965 return r;
966 }
967
968 data = value;
969 return 0;
970}
971
Patrick Williams59d494e2022-07-22 19:26:55 -0500972int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
973 nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600974
Ed Tanous23a21a12020-07-25 04:45:05 +0000975inline int readDictEntryFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -0500976 sdbusplus::message_t& m,
Ed Tanous23a21a12020-07-25 04:45:05 +0000977 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600978{
979 std::vector<std::string> types = dbusArgSplit(typeCode);
980 if (types.size() != 2)
981 {
Ed Tanous62598e32023-07-17 17:06:25 -0700982 BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}",
983 types.size());
Matt Spinler6df8f992019-01-14 12:47:47 -0600984 return -1;
985 }
986
987 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
988 typeCode.c_str());
989 if (r < 0)
990 {
Ed Tanous62598e32023-07-17 17:06:25 -0700991 BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -0600992 return r;
993 }
994
995 nlohmann::json key;
996 r = convertDBusToJSON(types[0], m, key);
997 if (r < 0)
998 {
999 return r;
1000 }
1001
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001002 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -06001003 if (keyPtr == nullptr)
1004 {
1005 // json doesn't support non-string keys. If we hit this condition,
1006 // convert the result to a string so we can proceed
Ed Tanous71f52d92021-02-19 08:51:17 -08001007 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001008 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -07001009 // in theory this can't fail now, but lets be paranoid about it
1010 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -06001011 if (keyPtr == nullptr)
1012 {
1013 return -1;
1014 }
1015 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001016 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -06001017
1018 r = convertDBusToJSON(types[1], m, value);
1019 if (r < 0)
1020 {
1021 return r;
1022 }
1023
1024 r = sd_bus_message_exit_container(m.get());
1025 if (r < 0)
1026 {
Ed Tanous62598e32023-07-17 17:06:25 -07001027 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001028 return r;
1029 }
1030
1031 return 0;
1032}
1033
Ed Tanous23a21a12020-07-25 04:45:05 +00001034inline int readArrayFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001035 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -06001036{
1037 if (typeCode.size() < 2)
1038 {
Ed Tanous62598e32023-07-17 17:06:25 -07001039 BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode);
Matt Spinler6df8f992019-01-14 12:47:47 -06001040 return -1;
1041 }
1042
1043 std::string containedType = typeCode.substr(1);
1044
1045 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1046 containedType.c_str());
1047 if (r < 0)
1048 {
Ed Tanous62598e32023-07-17 17:06:25 -07001049 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -06001050 return r;
1051 }
1052
Ed Tanous11ba3972022-07-11 09:50:41 -07001053 bool dict = containedType.starts_with("{") && containedType.ends_with("}");
Matt Spinler6df8f992019-01-14 12:47:47 -06001054
1055 if (dict)
1056 {
1057 // Remove the { }
1058 containedType = containedType.substr(1, containedType.size() - 2);
1059 data = nlohmann::json::object();
1060 }
1061 else
1062 {
1063 data = nlohmann::json::array();
1064 }
1065
1066 while (true)
1067 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001068 r = sd_bus_message_at_end(m.get(), 0);
Matt Spinler6df8f992019-01-14 12:47:47 -06001069 if (r < 0)
1070 {
Ed Tanous62598e32023-07-17 17:06:25 -07001071 BMCWEB_LOG_ERROR("sd_bus_message_at_end failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001072 return r;
1073 }
1074
1075 if (r > 0)
1076 {
1077 break;
1078 }
1079
1080 // Dictionaries are only ever seen in an array
1081 if (dict)
1082 {
1083 r = readDictEntryFromMessage(containedType, m, data);
1084 if (r < 0)
1085 {
1086 return r;
1087 }
1088 }
1089 else
1090 {
1091 data.push_back(nlohmann::json());
1092
1093 r = convertDBusToJSON(containedType, m, data.back());
1094 if (r < 0)
1095 {
1096 return r;
1097 }
1098 }
1099 }
1100
1101 r = sd_bus_message_exit_container(m.get());
1102 if (r < 0)
1103 {
Ed Tanous62598e32023-07-17 17:06:25 -07001104 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001105 return r;
1106 }
1107
1108 return 0;
1109}
1110
Ed Tanous23a21a12020-07-25 04:45:05 +00001111inline int readStructFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001112 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001113{
1114 if (typeCode.size() < 3)
1115 {
Ed Tanous62598e32023-07-17 17:06:25 -07001116 BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode);
Matt Spinler75c6c672019-01-14 13:01:46 -06001117 return -1;
1118 }
1119
1120 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1121 std::vector<std::string> types = dbusArgSplit(containedTypes);
1122
1123 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1124 containedTypes.c_str());
1125 if (r < 0)
1126 {
Ed Tanous62598e32023-07-17 17:06:25 -07001127 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler75c6c672019-01-14 13:01:46 -06001128 return r;
1129 }
1130
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001131 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001132 {
1133 data.push_back(nlohmann::json());
1134 r = convertDBusToJSON(type, m, data.back());
1135 if (r < 0)
1136 {
1137 return r;
1138 }
1139 }
1140
1141 r = sd_bus_message_exit_container(m.get());
1142 if (r < 0)
1143 {
Ed Tanous62598e32023-07-17 17:06:25 -07001144 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler75c6c672019-01-14 13:01:46 -06001145 return r;
1146 }
1147 return 0;
1148}
1149
Patrick Williams59d494e2022-07-22 19:26:55 -05001150inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001151{
Ed Tanous543f4402022-01-06 13:12:53 -08001152 const char* containerType = nullptr;
Ed Tanous99131cd2019-10-24 11:12:47 -07001153 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001154 if (r < 0)
1155 {
Ed Tanous62598e32023-07-17 17:06:25 -07001156 BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001157 return r;
1158 }
1159
1160 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1161 containerType);
1162 if (r < 0)
1163 {
Ed Tanous62598e32023-07-17 17:06:25 -07001164 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler89c19702019-01-14 13:13:00 -06001165 return r;
1166 }
1167
1168 r = convertDBusToJSON(containerType, m, data);
1169 if (r < 0)
1170 {
1171 return r;
1172 }
1173
1174 r = sd_bus_message_exit_container(m.get());
1175 if (r < 0)
1176 {
Ed Tanous62598e32023-07-17 17:06:25 -07001177 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001178 return r;
1179 }
1180
1181 return 0;
1182}
1183
Ed Tanous23a21a12020-07-25 04:45:05 +00001184inline int convertDBusToJSON(const std::string& returnType,
Patrick Williams59d494e2022-07-22 19:26:55 -05001185 sdbusplus::message_t& m, nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001186{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001187 int r = 0;
1188 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1189
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001190 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001191 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001192 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001193 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001194 {
1195 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001196 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001197 }
1198
Ed Tanousd4d25792020-09-29 15:15:03 -07001199 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001200 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001201 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001202 if (r < 0)
1203 {
1204 return r;
1205 }
1206 }
1207 else if (typeCode == "b")
1208 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001209 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001210 if (r < 0)
1211 {
1212 return r;
1213 }
1214
Matt Spinlerf39420c2019-01-30 12:57:18 -06001215 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001216 }
1217 else if (typeCode == "u")
1218 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001219 r = readMessageItem<uint32_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 == "i")
1226 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001227 r = readMessageItem<int32_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 == "x")
1234 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001235 r = readMessageItem<int64_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 == "t")
1242 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001243 r = readMessageItem<uint64_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 == "n")
1250 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001251 r = readMessageItem<int16_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 == "q")
1258 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001259 r = readMessageItem<uint16_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 == "y")
1266 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001267 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001268 if (r < 0)
1269 {
1270 return r;
1271 }
1272 }
1273 else if (typeCode == "d")
1274 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001275 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001276 if (r < 0)
1277 {
1278 return r;
1279 }
1280 }
1281 else if (typeCode == "h")
1282 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001283 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001284 if (r < 0)
1285 {
1286 return r;
1287 }
1288 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001289 else if (typeCode.starts_with("a"))
Matt Spinler6df8f992019-01-14 12:47:47 -06001290 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001291 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001292 if (r < 0)
1293 {
1294 return r;
1295 }
1296 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001297 else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
Matt Spinler75c6c672019-01-14 13:01:46 -06001298 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001299 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001300 if (r < 0)
1301 {
1302 return r;
1303 }
1304 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001305 else if (typeCode.starts_with("v"))
Matt Spinler89c19702019-01-14 13:13:00 -06001306 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001307 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001308 if (r < 0)
1309 {
1310 return r;
1311 }
1312 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001313 else
1314 {
Ed Tanous62598e32023-07-17 17:06:25 -07001315 BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001316 return -2;
1317 }
1318 }
1319
Matt Spinler16caaee2019-01-15 11:40:34 -06001320 return 0;
1321}
1322
Ed Tanousb5a76932020-09-29 16:16:58 -07001323inline void handleMethodResponse(
1324 const std::shared_ptr<InProgressActionData>& transaction,
Patrick Williams59d494e2022-07-22 19:26:55 -05001325 sdbusplus::message_t& m, const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001326{
Matt Spinler39a4e392019-01-15 11:53:13 -06001327 nlohmann::json data;
1328
1329 int r = convertDBusToJSON(returnType, m, data);
1330 if (r < 0)
1331 {
1332 transaction->outputFailed = true;
1333 return;
1334 }
1335
1336 if (data.is_null())
1337 {
1338 return;
1339 }
1340
1341 if (transaction->methodResponse.is_null())
1342 {
1343 transaction->methodResponse = std::move(data);
1344 return;
1345 }
1346
1347 // If they're both dictionaries or arrays, merge into one.
1348 // Otherwise, make the results an array with every result
1349 // an entry. Could also just fail in that case, but it
1350 // seems better to get the data back somehow.
Ed Tanous0bdda662023-08-03 17:27:34 -07001351 nlohmann::json::object_t* dataobj =
1352 data.get_ptr<nlohmann::json::object_t*>();
1353 if (transaction->methodResponse.is_object() && dataobj != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001354 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001355 for (auto& obj : *dataobj)
Matt Spinler39a4e392019-01-15 11:53:13 -06001356 {
1357 // Note: Will overwrite the data for a duplicate key
Ed Tanous0bdda662023-08-03 17:27:34 -07001358 transaction->methodResponse.emplace(obj.first,
1359 std::move(obj.second));
Matt Spinler39a4e392019-01-15 11:53:13 -06001360 }
1361 return;
1362 }
1363
Ed Tanous0bdda662023-08-03 17:27:34 -07001364 nlohmann::json::array_t* dataarr = data.get_ptr<nlohmann::json::array_t*>();
1365 if (transaction->methodResponse.is_array() && dataarr != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001366 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001367 for (auto& obj : *dataarr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001368 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001369 transaction->methodResponse.emplace_back(std::move(obj));
Matt Spinler39a4e392019-01-15 11:53:13 -06001370 }
1371 return;
1372 }
1373
1374 if (!transaction->convertedToArray)
1375 {
1376 // They are different types. May as well turn them into an array
1377 nlohmann::json j = std::move(transaction->methodResponse);
1378 transaction->methodResponse = nlohmann::json::array();
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001379 transaction->methodResponse.emplace_back(std::move(j));
1380 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001381 transaction->convertedToArray = true;
1382 }
1383 else
1384 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001385 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001386 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001387}
1388
Ed Tanousb5a76932020-09-29 16:16:58 -07001389inline void findActionOnInterface(
1390 const std::shared_ptr<InProgressActionData>& transaction,
1391 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001392{
Ed Tanous62598e32023-07-17 17:06:25 -07001393 BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001394 crow::connections::systemBus->async_method_call(
1395 [transaction, connectionName{std::string(connectionName)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001396 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001397 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001398 BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml);
1399 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001400 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001401 BMCWEB_LOG_ERROR(
1402 "Introspect call failed with error: {} on process: {}",
1403 ec.message(), connectionName);
1404 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001405 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001406 tinyxml2::XMLDocument doc;
1407
1408 doc.Parse(introspectXml.data(), introspectXml.size());
1409 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1410 if (pRoot == nullptr)
1411 {
1412 BMCWEB_LOG_ERROR("XML document failed to parse {}",
1413 connectionName);
1414 return;
1415 }
1416 tinyxml2::XMLElement* interfaceNode =
1417 pRoot->FirstChildElement("interface");
1418 while (interfaceNode != nullptr)
1419 {
1420 const char* thisInterfaceName =
1421 interfaceNode->Attribute("name");
1422 if (thisInterfaceName != nullptr)
1423 {
1424 if (!transaction->interfaceName.empty() &&
1425 (transaction->interfaceName != thisInterfaceName))
1426 {
1427 interfaceNode =
1428 interfaceNode->NextSiblingElement("interface");
1429 continue;
1430 }
1431
1432 tinyxml2::XMLElement* methodNode =
1433 interfaceNode->FirstChildElement("method");
1434 while (methodNode != nullptr)
1435 {
1436 const char* thisMethodName =
1437 methodNode->Attribute("name");
1438 BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName);
1439 if (thisMethodName != nullptr &&
1440 thisMethodName == transaction->methodName)
1441 {
1442 BMCWEB_LOG_DEBUG(
1443 "Found method named {} on interface {}",
1444 thisMethodName, thisInterfaceName);
1445 sdbusplus::message_t m =
1446 crow::connections::systemBus->new_method_call(
1447 connectionName.c_str(),
1448 transaction->path.c_str(),
1449 thisInterfaceName,
1450 transaction->methodName.c_str());
1451
1452 tinyxml2::XMLElement* argumentNode =
1453 methodNode->FirstChildElement("arg");
1454
1455 std::string returnType;
1456
1457 // Find the output type
1458 while (argumentNode != nullptr)
1459 {
1460 const char* argDirection =
1461 argumentNode->Attribute("direction");
1462 const char* argType =
1463 argumentNode->Attribute("type");
1464 if (argDirection != nullptr &&
1465 argType != nullptr &&
1466 std::string(argDirection) == "out")
1467 {
1468 returnType = argType;
1469 break;
1470 }
1471 argumentNode =
1472 argumentNode->NextSiblingElement("arg");
1473 }
1474
1475 auto argIt = transaction->arguments.begin();
1476
1477 argumentNode = methodNode->FirstChildElement("arg");
1478
1479 while (argumentNode != nullptr)
1480 {
1481 const char* argDirection =
1482 argumentNode->Attribute("direction");
1483 const char* argType =
1484 argumentNode->Attribute("type");
1485 if (argDirection != nullptr &&
1486 argType != nullptr &&
1487 std::string(argDirection) == "in")
1488 {
1489 if (argIt == transaction->arguments.end())
1490 {
1491 transaction->setErrorStatus(
1492 "Invalid method args");
1493 return;
1494 }
1495 if (convertJsonToDbus(m.get(),
1496 std::string(argType),
1497 *argIt) < 0)
1498 {
1499 transaction->setErrorStatus(
1500 "Invalid method arg type");
1501 return;
1502 }
1503
1504 argIt++;
1505 }
1506 argumentNode =
1507 argumentNode->NextSiblingElement("arg");
1508 }
1509
1510 crow::connections::systemBus->async_send(
1511 m, [transaction, returnType](
1512 const boost::system::error_code& ec2,
1513 sdbusplus::message_t& m2) {
1514 if (ec2)
1515 {
1516 transaction->methodFailed = true;
1517 const sd_bus_error* e = m2.get_error();
1518
1519 if (e != nullptr)
1520 {
1521 setErrorResponse(
1522 transaction->asyncResp->res,
1523 boost::beast::http::status::
1524 bad_request,
1525 e->name, e->message);
1526 }
1527 else
1528 {
1529 setErrorResponse(
1530 transaction->asyncResp->res,
1531 boost::beast::http::status::
1532 bad_request,
1533 "Method call failed",
1534 methodFailedMsg);
1535 }
1536 return;
1537 }
1538 transaction->methodPassed = true;
1539
1540 handleMethodResponse(transaction, m2,
1541 returnType);
1542 });
1543 break;
1544 }
1545 methodNode = methodNode->NextSiblingElement("method");
1546 }
1547 }
1548 interfaceNode = interfaceNode->NextSiblingElement("interface");
1549 }
1550 },
Ed Tanous1abe55e2018-09-05 08:30:59 -07001551 connectionName, transaction->path,
1552 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001553}
1554
zhanghch058d1b46d2021-04-01 11:18:24 +08001555inline void handleAction(const crow::Request& req,
1556 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001557 const std::string& objectPath,
1558 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001559{
Ed Tanous62598e32023-07-17 17:06:25 -07001560 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath,
1561 methodName);
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001562 nlohmann::json requestDbusData;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001563
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001564 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1565 if (ret == JsonParseResult::BadContentType)
1566 {
1567 setErrorResponse(asyncResp->res,
1568 boost::beast::http::status::unsupported_media_type,
1569 invalidContentType, unsupportedMediaMsg);
1570 return;
1571 }
1572 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001573 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001574 setErrorResponse(asyncResp->res,
1575 boost::beast::http::status::bad_request, noJsonDesc,
1576 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001577 return;
1578 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001579 nlohmann::json::iterator data = requestDbusData.find("data");
1580 if (data == requestDbusData.end())
1581 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001582 setErrorResponse(asyncResp->res,
1583 boost::beast::http::status::bad_request, noJsonDesc,
1584 badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001585 return;
1586 }
1587
1588 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001589 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001590 setErrorResponse(asyncResp->res,
1591 boost::beast::http::status::bad_request, noJsonDesc,
1592 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001593 return;
1594 }
Lei YU28dd5ca2023-03-17 13:17:05 +08001595 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001596
1597 transaction->path = objectPath;
1598 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001599 transaction->arguments = std::move(*data);
George Liu2b731192023-01-11 16:27:13 +08001600 dbus::utility::getDbusObject(
1601 objectPath, {},
Ed Tanous1abe55e2018-09-05 08:30:59 -07001602 [transaction](
George Liu2b731192023-01-11 16:27:13 +08001603 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001604 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1605 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001606 if (ec || interfaceNames.empty())
1607 {
1608 BMCWEB_LOG_ERROR("Can't find object");
1609 setErrorResponse(transaction->asyncResp->res,
1610 boost::beast::http::status::not_found,
1611 notFoundDesc, notFoundMsg);
1612 return;
1613 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001614
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001615 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)",
1616 interfaceNames.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001617
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001618 for (const std::pair<std::string, std::vector<std::string>>&
1619 object : interfaceNames)
1620 {
1621 findActionOnInterface(transaction, object.first);
1622 }
1623 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001624}
1625
zhanghch058d1b46d2021-04-01 11:18:24 +08001626inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1627 const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001628{
Ed Tanous62598e32023-07-17 17:06:25 -07001629 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06001630
George Liu2b731192023-01-11 16:27:13 +08001631 dbus::utility::getDbusObject(
1632 objectPath, {},
zhanghch058d1b46d2021-04-01 11:18:24 +08001633 [asyncResp, objectPath](
George Liu2b731192023-01-11 16:27:13 +08001634 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001635 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1636 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001637 if (ec || interfaceNames.empty())
1638 {
1639 BMCWEB_LOG_ERROR("Can't find object");
1640 setErrorResponse(asyncResp->res,
1641 boost::beast::http::status::method_not_allowed,
1642 methodNotAllowedDesc, methodNotAllowedMsg);
1643 return;
1644 }
Matt Spinlerde818812018-12-11 16:39:20 -06001645
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001646 auto transaction =
1647 std::make_shared<InProgressActionData>(asyncResp);
1648 transaction->path = objectPath;
1649 transaction->methodName = "Delete";
1650 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
Matt Spinlerde818812018-12-11 16:39:20 -06001651
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001652 for (const std::pair<std::string, std::vector<std::string>>&
1653 object : interfaceNames)
1654 {
1655 findActionOnInterface(transaction, object.first);
1656 }
1657 });
Matt Spinlerde818812018-12-11 16:39:20 -06001658}
1659
zhanghch058d1b46d2021-04-01 11:18:24 +08001660inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1661 const std::string& objectPath, int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001662{
George Liu7a1dbc42022-12-07 16:03:22 +08001663 dbus::utility::getSubTreePaths(
1664 objectPath, depth, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001665 [asyncResp](
George Liu7a1dbc42022-12-07 16:03:22 +08001666 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001667 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001668 if (ec)
1669 {
1670 setErrorResponse(asyncResp->res,
1671 boost::beast::http::status::not_found,
1672 notFoundDesc, notFoundMsg);
1673 }
1674 else
1675 {
1676 asyncResp->res.jsonValue["status"] = "ok";
1677 asyncResp->res.jsonValue["message"] = "200 OK";
1678 asyncResp->res.jsonValue["data"] = objectPaths;
1679 }
1680 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001681}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001682
zhanghch058d1b46d2021-04-01 11:18:24 +08001683inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1684 const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001685{
Ed Tanous62598e32023-07-17 17:06:25 -07001686 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07001687
Ed Tanous14766872022-03-15 10:44:42 -07001688 asyncResp->res.jsonValue["message"] = "200 OK";
1689 asyncResp->res.jsonValue["status"] = "ok";
1690 asyncResp->res.jsonValue["data"] = nlohmann::json::object();
Ed Tanous049a0512018-11-01 13:58:42 -07001691
George Liue99073f2022-12-09 11:06:16 +08001692 dbus::utility::getSubTree(
1693 objectPath, 0, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001694 [objectPath, asyncResp](
George Liue99073f2022-12-09 11:06:16 +08001695 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001696 const dbus::utility::MapperGetSubTreeResponse& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001697 auto transaction = std::make_shared<InProgressEnumerateData>(
1698 objectPath, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001699
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001700 transaction->subtree =
1701 std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1702 objectNames);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001703
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001704 if (ec)
1705 {
1706 BMCWEB_LOG_ERROR("GetSubTree failed on {}",
1707 transaction->objectPath);
1708 setErrorResponse(transaction->asyncResp->res,
1709 boost::beast::http::status::not_found,
1710 notFoundDesc, notFoundMsg);
1711 return;
1712 }
Ed Tanous64530012018-02-06 17:08:16 -08001713
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001714 // Add the data for the path passed in to the results
1715 // as if GetSubTree returned it, and continue on enumerating
1716 getObjectAndEnumerate(transaction);
1717 });
Ed Tanous64530012018-02-06 17:08:16 -08001718}
Ed Tanous911ac312017-08-15 09:37:42 -07001719
zhanghch058d1b46d2021-04-01 11:18:24 +08001720inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1721 std::string& objectPath, std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001722{
Ed Tanous62598e32023-07-17 17:06:25 -07001723 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001724 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001725 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001726
Ed Tanous1abe55e2018-09-05 08:30:59 -07001727 std::shared_ptr<std::string> path =
1728 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001729
George Liu2b731192023-01-11 16:27:13 +08001730 dbus::utility::getDbusObject(
1731 *path, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001732 [asyncResp, path,
George Liu2b731192023-01-11 16:27:13 +08001733 propertyName](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001734 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001735 if (ec || objectNames.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001736 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001737 setErrorResponse(asyncResp->res,
1738 boost::beast::http::status::not_found,
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001739 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 return;
1741 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001742 std::shared_ptr<nlohmann::json> response =
1743 std::make_shared<nlohmann::json>(nlohmann::json::object());
1744 // The mapper should never give us an empty interface names
1745 // list, but check anyway
1746 for (const std::pair<std::string, std::vector<std::string>>&
1747 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001748 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001749 const std::vector<std::string>& interfaceNames =
1750 connection.second;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001751
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001752 if (interfaceNames.empty())
1753 {
1754 setErrorResponse(asyncResp->res,
1755 boost::beast::http::status::not_found,
1756 notFoundDesc, notFoundMsg);
1757 return;
1758 }
1759
1760 for (const std::string& interface : interfaceNames)
1761 {
1762 sdbusplus::message_t m =
1763 crow::connections::systemBus->new_method_call(
1764 connection.first.c_str(), path->c_str(),
1765 "org.freedesktop.DBus.Properties", "GetAll");
1766 m.append(interface);
1767 crow::connections::systemBus->async_send(
1768 m, [asyncResp, response,
1769 propertyName](const boost::system::error_code& ec2,
1770 sdbusplus::message_t& msg) {
1771 if (ec2)
1772 {
1773 BMCWEB_LOG_ERROR("Bad dbus request error: {}",
1774 ec2);
1775 }
1776 else
1777 {
1778 nlohmann::json properties;
1779 int r =
1780 convertDBusToJSON("a{sv}", msg, properties);
1781 if (r < 0)
Patrick Williams5a39f772023-10-20 11:20:21 -05001782 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001783 BMCWEB_LOG_ERROR(
1784 "convertDBusToJSON failed");
Patrick Williams5a39f772023-10-20 11:20:21 -05001785 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001786 else
Patrick Williams5a39f772023-10-20 11:20:21 -05001787 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001788 nlohmann::json::object_t* obj =
1789 properties.get_ptr<
1790 nlohmann::json::object_t*>();
1791 if (obj == nullptr)
1792 {
1793 return;
1794 }
1795 for (auto& prop : *obj)
1796 {
1797 // if property name is empty, or
1798 // matches our search query, add it
1799 // to the response json
1800
1801 if (propertyName->empty())
1802 {
1803 (*response)[prop.first] =
1804 std::move(prop.second);
1805 }
1806 else if (prop.first == *propertyName)
1807 {
1808 *response = std::move(prop.second);
1809 }
1810 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001811 }
1812 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001813 if (response.use_count() == 1)
1814 {
1815 if (!propertyName->empty() && response->empty())
1816 {
1817 setErrorResponse(
1818 asyncResp->res,
1819 boost::beast::http::status::not_found,
1820 propNotFoundDesc, notFoundMsg);
1821 }
1822 else
1823 {
1824 asyncResp->res.jsonValue["status"] = "ok";
1825 asyncResp->res.jsonValue["message"] =
1826 "200 OK";
1827 asyncResp->res.jsonValue["data"] =
1828 *response;
1829 }
1830 }
1831 });
1832 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001833 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001834 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001835}
1836
Ed Tanous1abe55e2018-09-05 08:30:59 -07001837struct AsyncPutRequest
1838{
Ed Tanous4e23a442022-06-06 09:57:26 -07001839 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
zhanghch058d1b46d2021-04-01 11:18:24 +08001840 asyncResp(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001841 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001842 ~AsyncPutRequest()
1843 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001844 if (asyncResp->res.jsonValue.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001845 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001846 setErrorResponse(asyncResp->res,
1847 boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001848 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001849 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001850 }
1851
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001852 AsyncPutRequest(const AsyncPutRequest&) = delete;
1853 AsyncPutRequest(AsyncPutRequest&&) = delete;
1854 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1855 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1856
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001857 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001858 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001859 setErrorResponse(asyncResp->res,
1860 boost::beast::http::status::internal_server_error,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001861 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001862 }
1863
zhanghch058d1b46d2021-04-01 11:18:24 +08001864 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001865 std::string objectPath;
1866 std::string propertyName;
1867 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001868};
1869
zhanghch058d1b46d2021-04-01 11:18:24 +08001870inline void handlePut(const crow::Request& req,
1871 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001872 const std::string& objectPath,
1873 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001874{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001875 if (destProperty.empty())
1876 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001877 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001878 forbiddenResDesc, forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001879 return;
1880 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001881 nlohmann::json requestDbusData;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001882
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001883 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1884 if (ret == JsonParseResult::BadContentType)
1885 {
1886 setErrorResponse(asyncResp->res,
1887 boost::beast::http::status::unsupported_media_type,
1888 invalidContentType, unsupportedMediaMsg);
1889 return;
1890 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001891
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001892 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001893 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001894 setErrorResponse(asyncResp->res,
1895 boost::beast::http::status::bad_request, noJsonDesc,
1896 badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001897 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001898 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001899
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001900 auto propertyIt = requestDbusData.find("data");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001901 if (propertyIt == requestDbusData.end())
1902 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001903 setErrorResponse(asyncResp->res,
1904 boost::beast::http::status::bad_request, noJsonDesc,
1905 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001906 return;
1907 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001908 const nlohmann::json& propertySetValue = *propertyIt;
zhanghch058d1b46d2021-04-01 11:18:24 +08001909 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001910 transaction->objectPath = objectPath;
1911 transaction->propertyName = destProperty;
1912 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001913
George Liu2b731192023-01-11 16:27:13 +08001914 dbus::utility::getDbusObject(
1915 transaction->objectPath, {},
1916 [transaction](const boost::system::error_code& ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001917 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001918 if (!ec2 && objectNames.empty())
1919 {
1920 setErrorResponse(transaction->asyncResp->res,
1921 boost::beast::http::status::not_found,
1922 propNotFoundDesc, notFoundMsg);
1923 return;
1924 }
Ed Tanous911ac312017-08-15 09:37:42 -07001925
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001926 for (const std::pair<std::string, std::vector<std::string>>&
1927 connection : objectNames)
1928 {
1929 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001930
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001931 crow::connections::systemBus->async_method_call(
1932 [connectionName{std::string(connectionName)},
1933 transaction](const boost::system::error_code& ec3,
1934 const std::string& introspectXml) {
1935 if (ec3)
Ed Tanousb0b61522024-08-06 10:20:49 -07001936 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001937 BMCWEB_LOG_ERROR(
1938 "Introspect call failed with error: {} on process: {}",
1939 ec3.message(), connectionName);
1940 transaction->setErrorStatus("Unexpected Error");
1941 return;
Ed Tanousb0b61522024-08-06 10:20:49 -07001942 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001943 tinyxml2::XMLDocument doc;
1944
1945 doc.Parse(introspectXml.c_str());
1946 tinyxml2::XMLNode* pRoot =
1947 doc.FirstChildElement("node");
1948 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001949 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001950 BMCWEB_LOG_ERROR("XML document failed to parse: {}",
1951 introspectXml);
1952 transaction->setErrorStatus("Unexpected Error");
1953 return;
1954 }
1955 tinyxml2::XMLElement* ifaceNode =
1956 pRoot->FirstChildElement("interface");
1957 while (ifaceNode != nullptr)
1958 {
1959 const char* interfaceName =
1960 ifaceNode->Attribute("name");
1961 BMCWEB_LOG_DEBUG("found interface {}",
1962 interfaceName);
1963 tinyxml2::XMLElement* propNode =
1964 ifaceNode->FirstChildElement("property");
1965 while (propNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001966 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001967 const char* propertyName =
1968 propNode->Attribute("name");
1969 if (propertyName == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001970 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001971 BMCWEB_LOG_DEBUG(
1972 "Couldn't find name property");
1973 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001974 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001975 BMCWEB_LOG_DEBUG("Found property {}",
1976 propertyName);
1977 if (propertyName == transaction->propertyName)
Ed Tanous002d39b2022-05-31 08:59:27 -07001978 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001979 const char* argType =
1980 propNode->Attribute("type");
1981 if (argType != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07001982 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001983 sdbusplus::message_t m =
1984 crow::connections::systemBus
1985 ->new_method_call(
1986 connectionName.c_str(),
1987 transaction->objectPath
1988 .c_str(),
1989 "org.freedesktop.DBus."
1990 "Properties",
1991 "Set");
1992 m.append(interfaceName,
1993 transaction->propertyName);
1994 int r = sd_bus_message_open_container(
1995 m.get(), SD_BUS_TYPE_VARIANT,
1996 argType);
1997 if (r < 0)
1998 {
1999 transaction->setErrorStatus(
2000 "Unexpected Error");
2001 return;
2002 }
2003 r = convertJsonToDbus(
2004 m.get(), argType,
2005 transaction->propertyValue);
2006 if (r < 0)
2007 {
2008 if (r == -ERANGE)
2009 {
2010 transaction->setErrorStatus(
2011 "Provided property value "
2012 "is out of range for the "
2013 "property type");
2014 }
2015 else
2016 {
2017 transaction->setErrorStatus(
2018 "Invalid arg type");
2019 }
2020 return;
2021 }
2022 r = sd_bus_message_close_container(
2023 m.get());
2024 if (r < 0)
2025 {
2026 transaction->setErrorStatus(
2027 "Unexpected Error");
2028 return;
2029 }
2030 crow::connections::systemBus
2031 ->async_send(
2032 m,
2033 [transaction](
2034 const boost::system::
2035 error_code& ec,
2036 sdbusplus::message_t& m2) {
2037 BMCWEB_LOG_DEBUG("sent");
2038 if (ec)
2039 {
2040 const sd_bus_error* e =
2041 m2.get_error();
2042 setErrorResponse(
2043 transaction
2044 ->asyncResp
2045 ->res,
2046 boost::beast::http::
2047 status::
2048 forbidden,
2049 (e) != nullptr
2050 ? e->name
2051 : ec.category()
2052 .name(),
2053 (e) != nullptr
2054 ? e->message
2055 : ec.message());
2056 }
2057 else
2058 {
2059 transaction->asyncResp
2060 ->res.jsonValue
2061 ["status"] =
2062 "ok";
2063 transaction->asyncResp
2064 ->res.jsonValue
2065 ["message"] =
2066 "200 OK";
2067 transaction->asyncResp
2068 ->res
2069 .jsonValue["data"] =
2070 nullptr;
2071 }
2072 });
Ed Tanous002d39b2022-05-31 08:59:27 -07002073 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002074 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002075 propNode =
2076 propNode->NextSiblingElement("property");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002077 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002078 ifaceNode =
2079 ifaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002080 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002081 },
2082 connectionName, transaction->objectPath,
2083 "org.freedesktop.DBus.Introspectable", "Introspect");
2084 }
2085 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002086}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002087
zhanghch058d1b46d2021-04-01 11:18:24 +08002088inline void handleDBusUrl(const crow::Request& req,
2089 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002090 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002091{
Ed Tanous049a0512018-11-01 13:58:42 -07002092 // If accessing a single attribute, fill in and update objectPath,
2093 // otherwise leave destProperty blank
Ed Tanouse05aec52022-01-25 10:28:56 -08002094 std::string destProperty;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002095 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002096 size_t attrPosition = objectPath.find(attrSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002097 if (attrPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002098 {
2099 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2100 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002101 objectPath.resize(attrPosition);
Ed Tanous049a0512018-11-01 13:58:42 -07002102 }
2103
Ed Tanousb41187f2019-10-24 16:30:02 -07002104 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002105 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002106 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002107 size_t actionPosition = objectPath.find(actionSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002108 if (actionPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002109 {
2110 std::string postProperty =
2111 objectPath.substr((actionPosition + strlen(actionSeperator)),
2112 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002113 objectPath.resize(actionPosition);
zhanghch058d1b46d2021-04-01 11:18:24 +08002114 handleAction(req, asyncResp, objectPath, postProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002115 return;
2116 }
2117 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002118 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002119 {
Ed Tanous11ba3972022-07-11 09:50:41 -07002120 if (objectPath.ends_with("/enumerate"))
Ed Tanous049a0512018-11-01 13:58:42 -07002121 {
2122 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2123 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002124 handleEnumerate(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002125 }
Ed Tanous11ba3972022-07-11 09:50:41 -07002126 else if (objectPath.ends_with("/list"))
Ed Tanous049a0512018-11-01 13:58:42 -07002127 {
2128 objectPath.erase(objectPath.end() - sizeof("list"),
2129 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002130 handleList(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002131 }
2132 else
2133 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002134 // Trim any trailing "/" at the end
Ed Tanous11ba3972022-07-11 09:50:41 -07002135 if (objectPath.ends_with("/"))
Ed Tanousf839dfe2018-11-12 11:11:15 -08002136 {
2137 objectPath.pop_back();
zhanghch058d1b46d2021-04-01 11:18:24 +08002138 handleList(asyncResp, objectPath, 1);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002139 }
2140 else
2141 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002142 handleGet(asyncResp, objectPath, destProperty);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002143 }
Ed Tanous049a0512018-11-01 13:58:42 -07002144 }
2145 return;
2146 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002147 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002148 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002149 handlePut(req, asyncResp, objectPath, destProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002150 return;
2151 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002152 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002153 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002154 handleDelete(asyncResp, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002155 return;
2156 }
Ed Tanous049a0512018-11-01 13:58:42 -07002157
zhanghch058d1b46d2021-04-01 11:18:24 +08002158 setErrorResponse(asyncResp->res,
2159 boost::beast::http::status::method_not_allowed,
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002160 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002161}
2162
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002163inline void handleBusSystemPost(
2164 const crow::Request& req,
2165 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2166 const std::string& processName, const std::string& requestedPath)
Ed Tanous1656b292022-05-04 11:33:42 -07002167{
2168 std::vector<std::string> strs;
Ed Tanous50ebd4a2023-01-19 19:03:17 -08002169
2170 bmcweb::split(strs, requestedPath, '/');
Ed Tanous1656b292022-05-04 11:33:42 -07002171 std::string objectPath;
2172 std::string interfaceName;
2173 std::string methodName;
2174 auto it = strs.begin();
2175 if (it == strs.end())
2176 {
2177 objectPath = "/";
2178 }
2179 while (it != strs.end())
2180 {
2181 // Check if segment contains ".". If it does, it must be an
2182 // interface
2183 if (it->find(".") != std::string::npos)
2184 {
2185 break;
2186 // This check is necessary as the trailing slash gets
2187 // parsed as part of our <path> specifier above, which
2188 // causes the normal trailing backslash redirector to
2189 // fail.
2190 }
2191 if (!it->empty())
2192 {
2193 objectPath += "/" + *it;
2194 }
2195 it++;
2196 }
2197 if (it != strs.end())
2198 {
2199 interfaceName = *it;
2200 it++;
2201
2202 // after interface, we might have a method name
2203 if (it != strs.end())
2204 {
2205 methodName = *it;
2206 it++;
2207 }
2208 }
2209 if (it != strs.end())
2210 {
2211 // if there is more levels past the method name, something
2212 // went wrong, return not found
2213 asyncResp->res.result(boost::beast::http::status::not_found);
2214 return;
2215 }
2216 if (interfaceName.empty())
2217 {
2218 crow::connections::systemBus->async_method_call(
2219 [asyncResp, processName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002220 objectPath](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002221 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002222 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002223 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002224 BMCWEB_LOG_ERROR(
2225 "Introspect call failed with error: {} on process: {} path: {}",
2226 ec.message(), processName, objectPath);
2227 return;
2228 }
2229 tinyxml2::XMLDocument doc;
2230
2231 doc.Parse(introspectXml.c_str());
2232 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2233 if (pRoot == nullptr)
2234 {
2235 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2236 processName, objectPath);
2237 asyncResp->res.jsonValue["status"] = "XML parse error";
2238 asyncResp->res.result(
2239 boost::beast::http::status::internal_server_error);
2240 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002241 }
2242
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002243 BMCWEB_LOG_DEBUG("{}", introspectXml);
2244 asyncResp->res.jsonValue["status"] = "ok";
2245 asyncResp->res.jsonValue["bus_name"] = processName;
2246 asyncResp->res.jsonValue["object_path"] = objectPath;
2247
2248 nlohmann::json& interfacesArray =
2249 asyncResp->res.jsonValue["interfaces"];
2250 interfacesArray = nlohmann::json::array();
2251 tinyxml2::XMLElement* interface =
2252 pRoot->FirstChildElement("interface");
2253
2254 while (interface != nullptr)
2255 {
2256 const char* ifaceName = interface->Attribute("name");
2257 if (ifaceName != nullptr)
2258 {
2259 nlohmann::json::object_t interfaceObj;
2260 interfaceObj["name"] = ifaceName;
2261 interfacesArray.emplace_back(std::move(interfaceObj));
2262 }
2263
2264 interface = interface->NextSiblingElement("interface");
2265 }
2266 },
Ed Tanous1656b292022-05-04 11:33:42 -07002267 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2268 "Introspect");
2269 }
2270 else if (methodName.empty())
2271 {
2272 crow::connections::systemBus->async_method_call(
2273 [asyncResp, processName, objectPath,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002274 interfaceName](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002275 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002276 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002277 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002278 BMCWEB_LOG_ERROR(
2279 "Introspect call failed with error: {} on process: {} path: {}",
2280 ec.message(), processName, objectPath);
2281 return;
2282 }
2283 tinyxml2::XMLDocument doc;
2284
2285 doc.Parse(introspectXml.data(), introspectXml.size());
2286 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2287 if (pRoot == nullptr)
2288 {
2289 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2290 processName, objectPath);
2291 asyncResp->res.result(
2292 boost::beast::http::status::internal_server_error);
2293 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002294 }
Ed Tanous14766872022-03-15 10:44:42 -07002295
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002296 asyncResp->res.jsonValue["status"] = "ok";
2297 asyncResp->res.jsonValue["bus_name"] = processName;
2298 asyncResp->res.jsonValue["interface"] = interfaceName;
2299 asyncResp->res.jsonValue["object_path"] = objectPath;
Ed Tanous1656b292022-05-04 11:33:42 -07002300
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002301 nlohmann::json& methodsArray =
2302 asyncResp->res.jsonValue["methods"];
2303 methodsArray = nlohmann::json::array();
2304
2305 nlohmann::json& signalsArray =
2306 asyncResp->res.jsonValue["signals"];
2307 signalsArray = nlohmann::json::array();
2308
2309 nlohmann::json& propertiesObj =
2310 asyncResp->res.jsonValue["properties"];
2311 propertiesObj = nlohmann::json::object();
2312
2313 // if we know we're the only call, build the
2314 // json directly
2315 tinyxml2::XMLElement* interface =
2316 pRoot->FirstChildElement("interface");
2317 while (interface != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002318 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002319 const char* ifaceName = interface->Attribute("name");
2320
2321 if (ifaceName != nullptr && ifaceName == interfaceName)
Ed Tanous1656b292022-05-04 11:33:42 -07002322 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002323 break;
Ed Tanous002d39b2022-05-31 08:59:27 -07002324 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002325
2326 interface = interface->NextSiblingElement("interface");
2327 }
2328 if (interface == nullptr)
2329 {
2330 // if we got to the end of the list and
2331 // never found a match, throw 404
2332 asyncResp->res.result(
2333 boost::beast::http::status::not_found);
2334 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002335 }
2336
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002337 tinyxml2::XMLElement* methods =
2338 interface->FirstChildElement("method");
2339 while (methods != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002340 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002341 nlohmann::json argsArray = nlohmann::json::array();
2342 tinyxml2::XMLElement* arg =
2343 methods->FirstChildElement("arg");
2344 while (arg != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002345 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002346 nlohmann::json thisArg;
2347 for (const char* fieldName : std::array<const char*, 3>{
2348 "name", "direction", "type"})
2349 {
2350 const char* fieldValue = arg->Attribute(fieldName);
2351 if (fieldValue != nullptr)
2352 {
2353 thisArg[fieldName] = fieldValue;
2354 }
2355 }
2356 argsArray.emplace_back(std::move(thisArg));
2357 arg = arg->NextSiblingElement("arg");
Ed Tanous002d39b2022-05-31 08:59:27 -07002358 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002359
2360 const char* name = methods->Attribute("name");
2361 if (name != nullptr)
2362 {
2363 std::string uri;
2364 uri.reserve(14 + processName.size() +
2365 objectPath.size() + interfaceName.size() +
2366 strlen(name));
2367 uri += "/bus/system/";
2368 uri += processName;
2369 uri += objectPath;
2370 uri += "/";
2371 uri += interfaceName;
2372 uri += "/";
2373 uri += name;
2374
2375 nlohmann::json::object_t object;
2376 object["name"] = name;
2377 object["uri"] = std::move(uri);
2378 object["args"] = argsArray;
2379
2380 methodsArray.emplace_back(std::move(object));
2381 }
2382 methods = methods->NextSiblingElement("method");
Ed Tanous002d39b2022-05-31 08:59:27 -07002383 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002384 tinyxml2::XMLElement* signals =
2385 interface->FirstChildElement("signal");
2386 while (signals != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002387 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002388 nlohmann::json argsArray = nlohmann::json::array();
2389
2390 tinyxml2::XMLElement* arg =
2391 signals->FirstChildElement("arg");
2392 while (arg != nullptr)
2393 {
2394 const char* name = arg->Attribute("name");
2395 const char* type = arg->Attribute("type");
2396 if (name != nullptr && type != nullptr)
2397 {
2398 nlohmann::json::object_t params;
2399 params["name"] = name;
2400 params["type"] = type;
2401 argsArray.push_back(std::move(params));
2402 }
2403 arg = arg->NextSiblingElement("arg");
2404 }
2405 const char* name = signals->Attribute("name");
2406 if (name != nullptr)
2407 {
2408 nlohmann::json::object_t object;
2409 object["name"] = name;
2410 object["args"] = argsArray;
2411 signalsArray.emplace_back(std::move(object));
2412 }
2413
2414 signals = signals->NextSiblingElement("signal");
Ed Tanous002d39b2022-05-31 08:59:27 -07002415 }
2416
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002417 tinyxml2::XMLElement* property =
2418 interface->FirstChildElement("property");
2419 while (property != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002420 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002421 const char* name = property->Attribute("name");
2422 const char* type = property->Attribute("type");
2423 if (type != nullptr && name != nullptr)
2424 {
2425 sdbusplus::message_t m =
2426 crow::connections::systemBus->new_method_call(
2427 processName.c_str(), objectPath.c_str(),
2428 "org.freedesktop."
2429 "DBus."
2430 "Properties",
2431 "Get");
2432 m.append(interfaceName, name);
2433 nlohmann::json& propertyItem = propertiesObj[name];
2434 crow::connections::systemBus->async_send(
2435 m, [&propertyItem,
2436 asyncResp](const boost::system::error_code& ec2,
2437 sdbusplus::message_t& msg) {
2438 if (ec2)
2439 {
2440 return;
2441 }
Ed Tanous1656b292022-05-04 11:33:42 -07002442
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002443 int r =
2444 convertDBusToJSON("v", msg, propertyItem);
2445 if (r < 0)
2446 {
2447 BMCWEB_LOG_ERROR(
2448 "Couldn't convert vector to json");
2449 }
2450 });
2451 }
2452 property = property->NextSiblingElement("property");
Ed Tanous1656b292022-05-04 11:33:42 -07002453 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002454 },
Ed Tanous1656b292022-05-04 11:33:42 -07002455 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2456 "Introspect");
2457 }
2458 else
2459 {
2460 if (req.method() != boost::beast::http::verb::post)
2461 {
2462 asyncResp->res.result(boost::beast::http::status::not_found);
2463 return;
2464 }
2465
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002466 nlohmann::json requestDbusData;
2467 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2468 if (ret == JsonParseResult::BadContentType)
Ed Tanous1656b292022-05-04 11:33:42 -07002469 {
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002470 setErrorResponse(asyncResp->res,
2471 boost::beast::http::status::unsupported_media_type,
2472 invalidContentType, unsupportedMediaMsg);
Ed Tanous1656b292022-05-04 11:33:42 -07002473 return;
2474 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002475 if (ret != JsonParseResult::Success)
2476 {
2477 setErrorResponse(asyncResp->res,
2478 boost::beast::http::status::bad_request,
2479 noJsonDesc, badReqMsg);
2480 return;
2481 }
2482
Ed Tanous1656b292022-05-04 11:33:42 -07002483 if (!requestDbusData.is_array())
2484 {
2485 asyncResp->res.result(boost::beast::http::status::bad_request);
2486 return;
2487 }
Lei YU28dd5ca2023-03-17 13:17:05 +08002488 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1656b292022-05-04 11:33:42 -07002489
2490 transaction->path = objectPath;
2491 transaction->methodName = methodName;
2492 transaction->arguments = std::move(requestDbusData);
2493
2494 findActionOnInterface(transaction, processName);
2495 }
2496}
2497
Ed Tanous23a21a12020-07-25 04:45:05 +00002498inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002499{
2500 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous432a8902021-06-14 15:28:56 -07002501 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002502 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002503 [](const crow::Request&,
2504 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002505 nlohmann::json::array_t buses;
2506 nlohmann::json& bus = buses.emplace_back();
2507 bus["name"] = "system";
2508 asyncResp->res.jsonValue["busses"] = std::move(buses);
2509 asyncResp->res.jsonValue["status"] = "ok";
2510 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002511
2512 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous432a8902021-06-14 15:28:56 -07002513 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002514 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002515 [](const crow::Request&,
2516 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002517 auto myCallback = [asyncResp](
2518 const boost::system::error_code& ec,
zhanghch058d1b46d2021-04-01 11:18:24 +08002519 std::vector<std::string>& names) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002520 if (ec)
2521 {
2522 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
2523 asyncResp->res.result(
2524 boost::beast::http::status::internal_server_error);
2525 }
2526 else
2527 {
2528 std::ranges::sort(names);
2529 asyncResp->res.jsonValue["status"] = "ok";
2530 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2531 for (const auto& name : names)
2532 {
2533 nlohmann::json::object_t object;
2534 object["name"] = name;
2535 objectsSub.emplace_back(std::move(object));
2536 }
2537 }
2538 };
2539 crow::connections::systemBus->async_method_call(
2540 std::move(myCallback), "org.freedesktop.DBus", "/",
2541 "org.freedesktop.DBus", "ListNames");
2542 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002543
2544 BMCWEB_ROUTE(app, "/list/")
Ed Tanous432a8902021-06-14 15:28:56 -07002545 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002546 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002547 [](const crow::Request&,
2548 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002549 handleList(asyncResp, "/");
2550 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002551
2552 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002553 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002554 .methods(boost::beast::http::verb::get)(
2555 [](const crow::Request& req,
2556 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002557 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002558 std::string objectPath = "/xyz/" + path;
2559 handleDBusUrl(req, asyncResp, objectPath);
2560 });
zhanghch058d1b46d2021-04-01 11:18:24 +08002561
2562 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002563 .privileges({{"ConfigureComponents", "ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002564 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2565 boost::beast::http::verb::delete_)(
2566 [](const crow::Request& req,
2567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2568 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002569 std::string objectPath = "/xyz/" + path;
2570 handleDBusUrl(req, asyncResp, objectPath);
2571 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002572
Ed Tanous049a0512018-11-01 13:58:42 -07002573 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002574 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002575 .methods(boost::beast::http::verb::get)(
2576 [](const crow::Request& req,
2577 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2578 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002579 std::string objectPath = "/org/" + path;
2580 handleDBusUrl(req, asyncResp, objectPath);
2581 });
Tanousf00032d2018-11-05 01:18:10 -03002582
2583 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002584 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002585 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2586 boost::beast::http::verb::delete_)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002587 [](const crow::Request& req,
2588 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002589 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002590 std::string objectPath = "/org/" + path;
2591 handleDBusUrl(req, asyncResp, objectPath);
2592 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002593
Ed Tanous1abe55e2018-09-05 08:30:59 -07002594 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002595 .privileges({{"ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002596 .methods(boost::beast::http::verb::get)(
2597 [](const crow::Request&,
2598 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2599 const std::string& dumpId) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002600 if (!validateFilename(dumpId))
2601 {
2602 asyncResp->res.result(
2603 boost::beast::http::status::bad_request);
2604 return;
2605 }
2606 std::filesystem::path loc(
2607 "/var/lib/phosphor-debug-collector/dumps");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002608
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002609 loc /= dumpId;
Ramesh Iyyard9207042019-07-05 08:04:42 -05002610
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002611 if (!std::filesystem::exists(loc) ||
2612 !std::filesystem::is_directory(loc))
2613 {
2614 BMCWEB_LOG_ERROR("{}Not found", loc.string());
2615 asyncResp->res.result(
2616 boost::beast::http::status::not_found);
2617 return;
2618 }
2619 std::filesystem::directory_iterator files(loc);
zhanghch058d1b46d2021-04-01 11:18:24 +08002620
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002621 for (const auto& file : files)
2622 {
Myung Baed51c61b2024-09-13 10:35:34 -05002623 if (asyncResp->res.openFile(file) !=
2624 crow::OpenCode::Success)
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002625 {
2626 continue;
2627 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002628
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002629 asyncResp->res.addHeader(
2630 boost::beast::http::field::content_type,
2631 "application/octet-stream");
zhanghch058d1b46d2021-04-01 11:18:24 +08002632
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002633 // Assuming only one dump file will be present in the dump
2634 // id directory
2635 std::string dumpFileName = file.path().filename().string();
zhanghch058d1b46d2021-04-01 11:18:24 +08002636
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002637 // Filename should be in alphanumeric, dot and underscore
2638 // Its based on phosphor-debug-collector application
2639 // dumpfile format
2640 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2641 if (!std::regex_match(dumpFileName, dumpFileRegex))
2642 {
2643 BMCWEB_LOG_ERROR("Invalid dump filename {}",
2644 dumpFileName);
2645 asyncResp->res.result(
2646 boost::beast::http::status::not_found);
2647 return;
2648 }
2649 std::string contentDispositionParam =
2650 "attachment; filename=\"" + dumpFileName + "\"";
2651
2652 asyncResp->res.addHeader(
2653 boost::beast::http::field::content_disposition,
2654 contentDispositionParam);
2655
2656 return;
2657 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002658 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanousad18f072018-11-14 14:07:48 -08002659 return;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002660 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002661
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002662 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002663 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002664
2665 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002666 [](const crow::Request&,
2667 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +00002668 const std::string& connection) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002669 introspectObjects(connection, "/", asyncResp);
2670 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002671
2672 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002673 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous1656b292022-05-04 11:33:42 -07002674 .methods(boost::beast::http::verb::get,
2675 boost::beast::http::verb::post)(handleBusSystemPost);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002676}
2677} // namespace openbmc_mapper
2678} // namespace crow