blob: f165c5a3da175abb6b9ad8ade1b6108958535022 [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
Ed Tanousdeae6a72024-11-11 21:58:57 -0800184 dbus::utility::getAllProperties(
185 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];
Ed Tanouse3648032024-10-16 18:06:39 -0700207 std::visit([&propertyJson](auto&& val) { propertyJson = val; },
208 value);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400209 }
210 });
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600211}
212
213// Find any results that weren't picked up by ObjectManagers, to be
214// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000215inline void findRemainingObjectsForEnumerate(
Ed Tanousb5a76932020-09-29 16:16:58 -0700216 const std::string& objectPath,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800217 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
Ed Tanousb5a76932020-09-29 16:16:58 -0700218 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600219{
Ed Tanous62598e32023-07-17 17:06:25 -0700220 BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate");
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500221 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600222
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500223 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600224 {
225 if (path == objectPath)
226 {
227 // An enumerate does not return the target path's properties
228 continue;
229 }
230 if (dataJson.find(path) == dataJson.end())
231 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500232 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600233 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500234 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600235 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700236 if (!interface.starts_with("org.freedesktop.DBus"))
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600237 {
238 getPropertiesForEnumerate(path, service, interface,
239 asyncResp);
240 }
241 }
242 }
243 }
244 }
245}
246
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600247struct InProgressEnumerateData
248{
zhanghch058d1b46d2021-04-01 11:18:24 +0800249 InProgressEnumerateData(
250 const std::string& objectPathIn,
251 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400252 objectPath(objectPathIn), asyncResp(asyncRespIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500253 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600254
255 ~InProgressEnumerateData()
256 {
Ed Tanous24b2fe82022-01-06 12:45:54 -0800257 try
258 {
259 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
260 }
261 catch (...)
262 {
Ed Tanous62598e32023-07-17 17:06:25 -0700263 BMCWEB_LOG_CRITICAL(
264 "findRemainingObjectsForEnumerate threw exception");
Ed Tanous24b2fe82022-01-06 12:45:54 -0800265 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600266 }
267
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800268 InProgressEnumerateData(const InProgressEnumerateData&) = delete;
269 InProgressEnumerateData(InProgressEnumerateData&&) = delete;
270 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
271 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600272 const std::string objectPath;
Ed Tanousb9d36b42022-02-26 21:42:46 -0800273 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600274 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
275};
276
Ed Tanous23a21a12020-07-25 04:45:05 +0000277inline void getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000278 const std::string& objectName, const std::string& objectManagerPath,
279 const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700280 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281{
Ed Tanous62598e32023-07-17 17:06:25 -0700282 BMCWEB_LOG_DEBUG(
283 "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}",
284 objectName, objectManagerPath, connectionName);
George Liu5eb468d2023-06-20 17:03:24 +0800285 sdbusplus::message::object_path path(objectManagerPath);
286 dbus::utility::getManagedObjects(
287 connectionName, path,
Ed Tanous81ce6092020-12-17 16:54:55 +0000288 [transaction, objectName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800289 connectionName](const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000290 const dbus::utility::ManagedObjectType& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400291 if (ec)
Ed Tanous049a0512018-11-01 13:58:42 -0700292 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400293 BMCWEB_LOG_ERROR(
294 "GetManagedObjects on path {} on connection {} failed with code {}",
295 objectName, connectionName, ec);
296 return;
297 }
298
299 nlohmann::json& dataJson =
300 transaction->asyncResp->res.jsonValue["data"];
301
302 for (const auto& objectPath : objects)
303 {
304 if (objectPath.first.str.starts_with(objectName))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400306 BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str);
307 nlohmann::json& objectJson = dataJson[objectPath.first.str];
308 if (objectJson.is_null())
309 {
310 objectJson = nlohmann::json::object();
311 }
312 for (const auto& interface : objectPath.second)
313 {
314 for (const auto& property : interface.second)
315 {
316 nlohmann::json& propertyJson =
317 objectJson[property.first];
318 std::visit(
319 [&propertyJson](auto&& val) {
320 if constexpr (
321 std::is_same_v<
322 std::decay_t<decltype(val)>,
323 sdbusplus::message::unix_fd>)
324 {
325 propertyJson = val.fd;
326 }
327 else
328 {
329 propertyJson = val;
330 }
331 },
332 property.second);
333 }
334 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700335 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500336 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700337 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400338 if (interface.first == "org.freedesktop.DBus.ObjectManager")
Ed Tanous049a0512018-11-01 13:58:42 -0700339 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400340 getManagedObjectsForEnumerate(
341 objectPath.first.str, objectPath.first.str,
342 connectionName, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700343 }
344 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400346 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700347}
348
Ed Tanous23a21a12020-07-25 04:45:05 +0000349inline void findObjectManagerPathForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000350 const std::string& objectName, const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700351 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700352{
Ed Tanous62598e32023-07-17 17:06:25 -0700353 BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}",
354 objectName, connectionName);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700355 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000356 [transaction, objectName, connectionName](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800357 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800358 const dbus::utility::MapperGetAncestorsResponse& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400359 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700360 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400361 BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}",
362 objectName, ec);
363 return;
364 }
365
366 for (const auto& pathGroup : objects)
367 {
368 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700369 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400370 if (connectionGroup.first == connectionName)
371 {
372 // Found the object manager path for this resource.
373 getManagedObjectsForEnumerate(
374 objectName, pathGroup.first, connectionName,
375 transaction);
376 return;
377 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700378 }
379 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400380 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700381 "xyz.openbmc_project.ObjectMapper",
382 "/xyz/openbmc_project/object_mapper",
Ed Tanous81ce6092020-12-17 16:54:55 +0000383 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500384 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700385}
Ed Tanous64530012018-02-06 17:08:16 -0800386
Ed Tanous7c091622019-05-23 11:42:36 -0700387// Uses GetObject to add the object info about the target /enumerate path to
388// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600389// target path, and then continues on enumerating the rest of the tree.
Ed Tanousb5a76932020-09-29 16:16:58 -0700390inline void getObjectAndEnumerate(
391 const std::shared_ptr<InProgressEnumerateData>& transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600392{
George Liu2b731192023-01-11 16:27:13 +0800393 dbus::utility::getDbusObject(
394 transaction->objectPath, {},
395 [transaction](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800396 const dbus::utility::MapperGetObject& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400397 if (ec)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600398 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400399 BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}",
400 transaction->objectPath, ec);
401 return;
402 }
403
404 BMCWEB_LOG_DEBUG("GetObject for {} has {} entries",
405 transaction->objectPath, objects.size());
406 if (!objects.empty())
407 {
408 transaction->subtree->emplace_back(transaction->objectPath,
409 objects);
410 }
411
412 // Map indicating connection name, and the path where the object
413 // manager exists
414 boost::container::flat_map<
415 std::string, std::string, std::less<>,
416 std::vector<std::pair<std::string, std::string>>>
417 connections;
418
419 for (const auto& object : *(transaction->subtree))
420 {
421 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600422 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400423 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600424 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400425 BMCWEB_LOG_DEBUG("{} has interface {}",
426 connection.first, interface);
427 if (interface == "org.freedesktop.DBus.ObjectManager")
428 {
429 BMCWEB_LOG_DEBUG("found object manager path {}",
430 object.first);
431 connections[connection.first] = object.first;
432 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600433 }
434 }
435 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400436 BMCWEB_LOG_DEBUG("Got {} connections", connections.size());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600437
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400438 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600439 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400440 // If we already know where the object manager is, we don't
441 // need to search for it, we can call directly in to
442 // getManagedObjects
443 if (!connection.second.empty())
444 {
445 getManagedObjectsForEnumerate(
446 transaction->objectPath, connection.second,
447 connection.first, transaction);
448 }
449 else
450 {
451 // otherwise we need to find the object manager path
452 // before we can continue
453 findObjectManagerPathForEnumerate(
454 transaction->objectPath, connection.first, transaction);
455 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600456 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400457 });
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600458}
Ed Tanous64530012018-02-06 17:08:16 -0800459
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700460// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461struct InProgressActionData
462{
Lei YU28dd5ca2023-03-17 13:17:05 +0800463 explicit InProgressActionData(
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400464 const std::shared_ptr<bmcweb::AsyncResp>& res) : asyncResp(res)
Ed Tanous23a21a12020-07-25 04:45:05 +0000465 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 ~InProgressActionData()
467 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600468 // Methods could have been called across different owners
469 // and interfaces, where some calls failed and some passed.
470 //
471 // The rules for this are:
472 // * if no method was called - error
473 // * if a method failed and none passed - error
474 // (converse: if at least one method passed - OK)
475 // * for the method output:
476 // * if output processing didn't fail, return the data
477
478 // Only deal with method returns if nothing failed earlier
Lei YU28dd5ca2023-03-17 13:17:05 +0800479 if (asyncResp->res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600481 if (!methodPassed)
482 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500483 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600484 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800485 setErrorResponse(asyncResp->res,
486 boost::beast::http::status::not_found,
Matt Spinler16caaee2019-01-15 11:40:34 -0600487 methodNotFoundDesc, notFoundMsg);
488 }
489 }
490 else
491 {
492 if (outputFailed)
493 {
494 setErrorResponse(
Lei YU28dd5ca2023-03-17 13:17:05 +0800495 asyncResp->res,
496 boost::beast::http::status::internal_server_error,
Matt Spinler16caaee2019-01-15 11:40:34 -0600497 "Method output failure", methodOutputFailedMsg);
498 }
499 else
500 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800501 asyncResp->res.jsonValue["status"] = "ok";
502 asyncResp->res.jsonValue["message"] = "200 OK";
503 asyncResp->res.jsonValue["data"] = methodResponse;
Matt Spinler16caaee2019-01-15 11:40:34 -0600504 }
505 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700507 }
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800508 InProgressActionData(const InProgressActionData&) = delete;
509 InProgressActionData(InProgressActionData&&) = delete;
510 InProgressActionData& operator=(const InProgressActionData&) = delete;
511 InProgressActionData& operator=(InProgressActionData&&) = delete;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700512
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500513 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800515 setErrorResponse(asyncResp->res,
516 boost::beast::http::status::bad_request, desc,
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600517 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700518 }
Lei YU28dd5ca2023-03-17 13:17:05 +0800519 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700520 std::string path;
521 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600522 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600523 bool methodPassed = false;
524 bool methodFailed = false;
525 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600526 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600527 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700529};
530
Ed Tanous23a21a12020-07-25 04:45:05 +0000531inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532{
533 std::vector<std::string> ret;
534 if (string.empty())
535 {
536 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700537 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700538 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 int containerDepth = 0;
540
541 for (std::string::const_iterator character = string.begin();
542 character != string.end(); character++)
543 {
544 ret.back() += *character;
545 switch (*character)
546 {
547 case ('a'):
548 break;
549 case ('('):
550 case ('{'):
551 containerDepth++;
552 break;
553 case ('}'):
554 case (')'):
555 containerDepth--;
556 if (containerDepth == 0)
557 {
558 if (character + 1 != string.end())
559 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700560 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 }
562 }
563 break;
564 default:
565 if (containerDepth == 0)
566 {
567 if (character + 1 != string.end())
568 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700569 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700570 }
571 }
572 break;
573 }
574 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600575
576 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700577}
578
Ed Tanous81ce6092020-12-17 16:54:55 +0000579inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
580 const nlohmann::json& inputJson)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581{
582 int r = 0;
Ed Tanous296579b2024-03-11 16:58:24 -0700583 BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType);
Ed Tanous81ce6092020-12-17 16:54:55 +0000584 const std::vector<std::string> argTypes = dbusArgSplit(argType);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700585
Ed Tanous1abe55e2018-09-05 08:30:59 -0700586 // Assume a single object for now.
Ed Tanous81ce6092020-12-17 16:54:55 +0000587 const nlohmann::json* j = &inputJson;
588 nlohmann::json::const_iterator jIt = inputJson.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700589
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500590 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591 {
592 // If we are decoding multiple objects, grab the pointer to the
593 // iterator, and increment it for the next loop
594 if (argTypes.size() > 1)
595 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000596 if (jIt == inputJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 {
598 return -2;
599 }
600 j = &*jIt;
601 jIt++;
602 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500603 const int64_t* intValue = j->get_ptr<const int64_t*>();
604 const std::string* stringValue = j->get_ptr<const std::string*>();
605 const double* doubleValue = j->get_ptr<const double*>();
606 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 int64_t v = 0;
608 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700609
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 // Do some basic type conversions that make sense. uint can be
611 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700612 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500614 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700615 if (uintValue != nullptr)
616 {
617 v = static_cast<int64_t>(*uintValue);
618 intValue = &v;
619 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 }
Ed Tanous66664f22019-10-11 13:05:49 -0700621 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500623 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700624 if (uintValue != nullptr)
625 {
626 d = static_cast<double>(*uintValue);
627 doubleValue = &d;
628 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 }
Ed Tanous66664f22019-10-11 13:05:49 -0700630 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 {
Ed Tanous66664f22019-10-11 13:05:49 -0700632 if (intValue != nullptr)
633 {
634 d = static_cast<double>(*intValue);
635 doubleValue = &d;
636 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700637 }
638
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700641 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700642 {
643 return -1;
644 }
Ed Tanous271584a2019-07-09 16:24:22 -0700645 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500646 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 if (r < 0)
648 {
649 return r;
650 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700651 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700652 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
656 return -1;
657 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500658 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
659 (*intValue > std::numeric_limits<int32_t>::max()))
660 {
661 return -ERANGE;
662 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700663 int32_t i = static_cast<int32_t>(*intValue);
664 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 if (r < 0)
666 {
667 return r;
668 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700669 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700670 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 {
672 // lots of ways bool could be represented here. Try them all
Ed Tanouse662eae2022-01-25 10:39:19 -0800673 int boolInt = 0;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500676 if (*intValue == 1)
677 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800678 boolInt = 1;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500679 }
680 else if (*intValue == 0)
681 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800682 boolInt = 0;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500683 }
684 else
685 {
686 return -ERANGE;
687 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 }
689 else if (b != nullptr)
690 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600691 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700693 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 {
Ed Tanous18f8f602023-07-18 10:07:23 -0700695 if (!stringValue->empty())
696 {
697 if (stringValue->front() == 't' ||
698 stringValue->front() == 'T')
699 {
700 boolInt = 1;
701 }
702 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 }
704 else
705 {
706 return -1;
707 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700708 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 if (r < 0)
710 {
711 return r;
712 }
713 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700714 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700715 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700716 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700717 {
718 return -1;
719 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500720 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
721 (*intValue > std::numeric_limits<int16_t>::max()))
722 {
723 return -ERANGE;
724 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700725 int16_t n = static_cast<int16_t>(*intValue);
726 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700727 if (r < 0)
728 {
729 return r;
730 }
731 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700732 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700733 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700734 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700735 {
736 return -1;
737 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700738 r = sd_bus_message_append_basic(m, argCode[0], intValue);
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 == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500746 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700747 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 {
749 return -1;
750 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000751 if (*uintValue > std::numeric_limits<uint8_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500752 {
753 return -ERANGE;
754 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700755 uint8_t y = static_cast<uint8_t>(*uintValue);
756 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700758 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700759 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500760 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700761 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700762 {
763 return -1;
764 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000765 if (*uintValue > std::numeric_limits<uint16_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500766 {
767 return -ERANGE;
768 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700769 uint16_t q = static_cast<uint16_t>(*uintValue);
770 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700772 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700773 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500774 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700775 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 {
777 return -1;
778 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000779 if (*uintValue > std::numeric_limits<uint32_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500780 {
781 return -ERANGE;
782 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700783 uint32_t u = static_cast<uint32_t>(*uintValue);
784 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700786 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700787 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500788 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700789 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
791 return -1;
792 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700793 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700795 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700796 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500797 if (doubleValue == nullptr)
798 {
799 return -1;
800 }
801 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
802 (*doubleValue > std::numeric_limits<double>::max()))
803 {
804 return -ERANGE;
805 }
Ed Tanous07900812024-05-06 15:41:30 -0700806 r = sd_bus_message_append_basic(m, argCode[0], doubleValue);
807 if (r < 0)
808 {
809 return r;
810 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700811 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700812 else if (argCode.starts_with("a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700816 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 if (r < 0)
818 {
819 return r;
820 }
821
Ed Tanous0dfeda62019-10-24 11:21:38 -0700822 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700824 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 if (r < 0)
826 {
827 return r;
828 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 }
830 sd_bus_message_close_container(m);
831 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700832 else if (argCode.starts_with("v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700834 std::string containedType = argCode.substr(1);
Ed Tanous62598e32023-07-17 17:06:25 -0700835 BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}",
836 argCode, containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700837 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700838 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 if (r < 0)
840 {
841 return r;
842 }
843
Ed Tanous81ce6092020-12-17 16:54:55 +0000844 r = convertJsonToDbus(m, containedType, inputJson);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700845 if (r < 0)
846 {
847 return r;
848 }
849
850 r = sd_bus_message_close_container(m);
851 if (r < 0)
852 {
853 return r;
854 }
855 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700856 else if (argCode.starts_with("(") && argCode.ends_with(")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300858 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700859 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700860 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800861 if (r < 0)
862 {
863 return r;
864 }
865
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 nlohmann::json::const_iterator it = j->begin();
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300867 for (const std::string& argCode2 : dbusArgSplit(containedType))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700868 {
869 if (it == j->end())
870 {
871 return -1;
872 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000873 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700874 if (r < 0)
875 {
876 return r;
877 }
878 it++;
879 }
880 r = sd_bus_message_close_container(m);
881 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700882 else if (argCode.starts_with("{") && argCode.ends_with("}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700883 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300884 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700886 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800887 if (r < 0)
888 {
889 return r;
890 }
891
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700892 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893 if (codes.size() != 2)
894 {
895 return -1;
896 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700897 const std::string& keyType = codes[0];
898 const std::string& valueType = codes[1];
Ed Tanous0bdda662023-08-03 17:27:34 -0700899 const nlohmann::json::object_t* arr =
900 j->get_ptr<const nlohmann::json::object_t*>();
901 if (arr == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700903 return -1;
904 }
905 for (const auto& it : *arr)
906 {
907 r = convertJsonToDbus(m, keyType, it.first);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 if (r < 0)
909 {
910 return r;
911 }
912
Ed Tanous0bdda662023-08-03 17:27:34 -0700913 r = convertJsonToDbus(m, valueType, it.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 if (r < 0)
915 {
916 return r;
917 }
918 }
919 r = sd_bus_message_close_container(m);
920 }
921 else
922 {
923 return -2;
924 }
925 if (r < 0)
926 {
927 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700928 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700929
Ed Tanous1abe55e2018-09-05 08:30:59 -0700930 if (argTypes.size() > 1)
931 {
932 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700933 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700934 }
Matt Spinler127ea542019-01-14 11:04:28 -0600935
936 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700937}
938
Matt Spinlerd22a7132019-01-14 12:14:30 -0600939template <typename T>
Patrick Williams59d494e2022-07-22 19:26:55 -0500940int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500941 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600942{
943 T value;
Ed Tanousf79ce6a2024-03-20 12:27:06 -0700944 // When T == char*, this warning fires. Unclear how to resolve
945 // Given that sd-bus takes a void pointer to a char*, and that's
946 // Not something we can fix.
947 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600948 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
949 if (r < 0)
950 {
Ed Tanous62598e32023-07-17 17:06:25 -0700951 BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!",
952 typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -0600953 return r;
954 }
955
956 data = value;
957 return 0;
958}
959
Patrick Williams59d494e2022-07-22 19:26:55 -0500960int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
961 nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600962
Ed Tanous23a21a12020-07-25 04:45:05 +0000963inline int readDictEntryFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -0500964 sdbusplus::message_t& m,
Ed Tanous23a21a12020-07-25 04:45:05 +0000965 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600966{
967 std::vector<std::string> types = dbusArgSplit(typeCode);
968 if (types.size() != 2)
969 {
Ed Tanous62598e32023-07-17 17:06:25 -0700970 BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}",
971 types.size());
Matt Spinler6df8f992019-01-14 12:47:47 -0600972 return -1;
973 }
974
975 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
976 typeCode.c_str());
977 if (r < 0)
978 {
Ed Tanous62598e32023-07-17 17:06:25 -0700979 BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -0600980 return r;
981 }
982
983 nlohmann::json key;
984 r = convertDBusToJSON(types[0], m, key);
985 if (r < 0)
986 {
987 return r;
988 }
989
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500990 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -0600991 if (keyPtr == nullptr)
992 {
993 // json doesn't support non-string keys. If we hit this condition,
994 // convert the result to a string so we can proceed
Ed Tanous71f52d92021-02-19 08:51:17 -0800995 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500996 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -0700997 // in theory this can't fail now, but lets be paranoid about it
998 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600999 if (keyPtr == nullptr)
1000 {
1001 return -1;
1002 }
1003 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001004 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -06001005
1006 r = convertDBusToJSON(types[1], m, value);
1007 if (r < 0)
1008 {
1009 return r;
1010 }
1011
1012 r = sd_bus_message_exit_container(m.get());
1013 if (r < 0)
1014 {
Ed Tanous62598e32023-07-17 17:06:25 -07001015 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001016 return r;
1017 }
1018
1019 return 0;
1020}
1021
Ed Tanous23a21a12020-07-25 04:45:05 +00001022inline int readArrayFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001023 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -06001024{
1025 if (typeCode.size() < 2)
1026 {
Ed Tanous62598e32023-07-17 17:06:25 -07001027 BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode);
Matt Spinler6df8f992019-01-14 12:47:47 -06001028 return -1;
1029 }
1030
1031 std::string containedType = typeCode.substr(1);
1032
1033 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1034 containedType.c_str());
1035 if (r < 0)
1036 {
Ed Tanous62598e32023-07-17 17:06:25 -07001037 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -06001038 return r;
1039 }
1040
Ed Tanous11ba3972022-07-11 09:50:41 -07001041 bool dict = containedType.starts_with("{") && containedType.ends_with("}");
Matt Spinler6df8f992019-01-14 12:47:47 -06001042
1043 if (dict)
1044 {
1045 // Remove the { }
1046 containedType = containedType.substr(1, containedType.size() - 2);
1047 data = nlohmann::json::object();
1048 }
1049 else
1050 {
1051 data = nlohmann::json::array();
1052 }
1053
1054 while (true)
1055 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001056 r = sd_bus_message_at_end(m.get(), 0);
Matt Spinler6df8f992019-01-14 12:47:47 -06001057 if (r < 0)
1058 {
Ed Tanous62598e32023-07-17 17:06:25 -07001059 BMCWEB_LOG_ERROR("sd_bus_message_at_end failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001060 return r;
1061 }
1062
1063 if (r > 0)
1064 {
1065 break;
1066 }
1067
1068 // Dictionaries are only ever seen in an array
1069 if (dict)
1070 {
1071 r = readDictEntryFromMessage(containedType, m, data);
1072 if (r < 0)
1073 {
1074 return r;
1075 }
1076 }
1077 else
1078 {
1079 data.push_back(nlohmann::json());
1080
1081 r = convertDBusToJSON(containedType, m, data.back());
1082 if (r < 0)
1083 {
1084 return r;
1085 }
1086 }
1087 }
1088
1089 r = sd_bus_message_exit_container(m.get());
1090 if (r < 0)
1091 {
Ed Tanous62598e32023-07-17 17:06:25 -07001092 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001093 return r;
1094 }
1095
1096 return 0;
1097}
1098
Ed Tanous23a21a12020-07-25 04:45:05 +00001099inline int readStructFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001100 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001101{
1102 if (typeCode.size() < 3)
1103 {
Ed Tanous62598e32023-07-17 17:06:25 -07001104 BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode);
Matt Spinler75c6c672019-01-14 13:01:46 -06001105 return -1;
1106 }
1107
1108 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1109 std::vector<std::string> types = dbusArgSplit(containedTypes);
1110
1111 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1112 containedTypes.c_str());
1113 if (r < 0)
1114 {
Ed Tanous62598e32023-07-17 17:06:25 -07001115 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler75c6c672019-01-14 13:01:46 -06001116 return r;
1117 }
1118
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001119 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001120 {
1121 data.push_back(nlohmann::json());
1122 r = convertDBusToJSON(type, m, data.back());
1123 if (r < 0)
1124 {
1125 return r;
1126 }
1127 }
1128
1129 r = sd_bus_message_exit_container(m.get());
1130 if (r < 0)
1131 {
Ed Tanous62598e32023-07-17 17:06:25 -07001132 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler75c6c672019-01-14 13:01:46 -06001133 return r;
1134 }
1135 return 0;
1136}
1137
Patrick Williams59d494e2022-07-22 19:26:55 -05001138inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001139{
Ed Tanous543f4402022-01-06 13:12:53 -08001140 const char* containerType = nullptr;
Ed Tanous99131cd2019-10-24 11:12:47 -07001141 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001142 if (r < 0)
1143 {
Ed Tanous62598e32023-07-17 17:06:25 -07001144 BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001145 return r;
1146 }
1147
1148 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1149 containerType);
1150 if (r < 0)
1151 {
Ed Tanous62598e32023-07-17 17:06:25 -07001152 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler89c19702019-01-14 13:13:00 -06001153 return r;
1154 }
1155
1156 r = convertDBusToJSON(containerType, m, data);
1157 if (r < 0)
1158 {
1159 return r;
1160 }
1161
1162 r = sd_bus_message_exit_container(m.get());
1163 if (r < 0)
1164 {
Ed Tanous62598e32023-07-17 17:06:25 -07001165 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001166 return r;
1167 }
1168
1169 return 0;
1170}
1171
Ed Tanous23a21a12020-07-25 04:45:05 +00001172inline int convertDBusToJSON(const std::string& returnType,
Patrick Williams59d494e2022-07-22 19:26:55 -05001173 sdbusplus::message_t& m, nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001174{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001175 int r = 0;
1176 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1177
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001178 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001179 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001180 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001181 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001182 {
1183 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001184 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001185 }
1186
Ed Tanousd4d25792020-09-29 15:15:03 -07001187 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001188 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001189 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001190 if (r < 0)
1191 {
1192 return r;
1193 }
1194 }
1195 else if (typeCode == "b")
1196 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001197 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001198 if (r < 0)
1199 {
1200 return r;
1201 }
1202
Matt Spinlerf39420c2019-01-30 12:57:18 -06001203 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001204 }
1205 else if (typeCode == "u")
1206 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001207 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001208 if (r < 0)
1209 {
1210 return r;
1211 }
1212 }
1213 else if (typeCode == "i")
1214 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001215 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001216 if (r < 0)
1217 {
1218 return r;
1219 }
1220 }
1221 else if (typeCode == "x")
1222 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001223 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001224 if (r < 0)
1225 {
1226 return r;
1227 }
1228 }
1229 else if (typeCode == "t")
1230 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001231 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001232 if (r < 0)
1233 {
1234 return r;
1235 }
1236 }
1237 else if (typeCode == "n")
1238 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001239 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001240 if (r < 0)
1241 {
1242 return r;
1243 }
1244 }
1245 else if (typeCode == "q")
1246 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001247 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001248 if (r < 0)
1249 {
1250 return r;
1251 }
1252 }
1253 else if (typeCode == "y")
1254 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001255 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001256 if (r < 0)
1257 {
1258 return r;
1259 }
1260 }
1261 else if (typeCode == "d")
1262 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001263 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001264 if (r < 0)
1265 {
1266 return r;
1267 }
1268 }
1269 else if (typeCode == "h")
1270 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001271 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001272 if (r < 0)
1273 {
1274 return r;
1275 }
1276 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001277 else if (typeCode.starts_with("a"))
Matt Spinler6df8f992019-01-14 12:47:47 -06001278 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001279 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001280 if (r < 0)
1281 {
1282 return r;
1283 }
1284 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001285 else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
Matt Spinler75c6c672019-01-14 13:01:46 -06001286 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001287 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001288 if (r < 0)
1289 {
1290 return r;
1291 }
1292 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001293 else if (typeCode.starts_with("v"))
Matt Spinler89c19702019-01-14 13:13:00 -06001294 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001295 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001296 if (r < 0)
1297 {
1298 return r;
1299 }
1300 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001301 else
1302 {
Ed Tanous62598e32023-07-17 17:06:25 -07001303 BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001304 return -2;
1305 }
1306 }
1307
Matt Spinler16caaee2019-01-15 11:40:34 -06001308 return 0;
1309}
1310
Ed Tanousb5a76932020-09-29 16:16:58 -07001311inline void handleMethodResponse(
1312 const std::shared_ptr<InProgressActionData>& transaction,
Patrick Williams59d494e2022-07-22 19:26:55 -05001313 sdbusplus::message_t& m, const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001314{
Matt Spinler39a4e392019-01-15 11:53:13 -06001315 nlohmann::json data;
1316
1317 int r = convertDBusToJSON(returnType, m, data);
1318 if (r < 0)
1319 {
1320 transaction->outputFailed = true;
1321 return;
1322 }
1323
1324 if (data.is_null())
1325 {
1326 return;
1327 }
1328
1329 if (transaction->methodResponse.is_null())
1330 {
1331 transaction->methodResponse = std::move(data);
1332 return;
1333 }
1334
1335 // If they're both dictionaries or arrays, merge into one.
1336 // Otherwise, make the results an array with every result
1337 // an entry. Could also just fail in that case, but it
1338 // seems better to get the data back somehow.
Ed Tanous0bdda662023-08-03 17:27:34 -07001339 nlohmann::json::object_t* dataobj =
1340 data.get_ptr<nlohmann::json::object_t*>();
1341 if (transaction->methodResponse.is_object() && dataobj != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001342 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001343 for (auto& obj : *dataobj)
Matt Spinler39a4e392019-01-15 11:53:13 -06001344 {
1345 // Note: Will overwrite the data for a duplicate key
Ed Tanous0bdda662023-08-03 17:27:34 -07001346 transaction->methodResponse.emplace(obj.first,
1347 std::move(obj.second));
Matt Spinler39a4e392019-01-15 11:53:13 -06001348 }
1349 return;
1350 }
1351
Ed Tanous0bdda662023-08-03 17:27:34 -07001352 nlohmann::json::array_t* dataarr = data.get_ptr<nlohmann::json::array_t*>();
1353 if (transaction->methodResponse.is_array() && dataarr != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001354 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001355 for (auto& obj : *dataarr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001356 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001357 transaction->methodResponse.emplace_back(std::move(obj));
Matt Spinler39a4e392019-01-15 11:53:13 -06001358 }
1359 return;
1360 }
1361
1362 if (!transaction->convertedToArray)
1363 {
1364 // They are different types. May as well turn them into an array
1365 nlohmann::json j = std::move(transaction->methodResponse);
1366 transaction->methodResponse = nlohmann::json::array();
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001367 transaction->methodResponse.emplace_back(std::move(j));
1368 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001369 transaction->convertedToArray = true;
1370 }
1371 else
1372 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001373 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001374 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001375}
1376
Ed Tanousb5a76932020-09-29 16:16:58 -07001377inline void findActionOnInterface(
1378 const std::shared_ptr<InProgressActionData>& transaction,
1379 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001380{
Ed Tanous62598e32023-07-17 17:06:25 -07001381 BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382 crow::connections::systemBus->async_method_call(
1383 [transaction, connectionName{std::string(connectionName)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001384 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001385 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001386 BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml);
1387 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001388 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001389 BMCWEB_LOG_ERROR(
1390 "Introspect call failed with error: {} on process: {}",
1391 ec.message(), connectionName);
1392 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001393 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001394 tinyxml2::XMLDocument doc;
1395
1396 doc.Parse(introspectXml.data(), introspectXml.size());
1397 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1398 if (pRoot == nullptr)
1399 {
1400 BMCWEB_LOG_ERROR("XML document failed to parse {}",
1401 connectionName);
1402 return;
1403 }
1404 tinyxml2::XMLElement* interfaceNode =
1405 pRoot->FirstChildElement("interface");
1406 while (interfaceNode != nullptr)
1407 {
1408 const char* thisInterfaceName =
1409 interfaceNode->Attribute("name");
1410 if (thisInterfaceName != nullptr)
1411 {
1412 if (!transaction->interfaceName.empty() &&
1413 (transaction->interfaceName != thisInterfaceName))
1414 {
1415 interfaceNode =
1416 interfaceNode->NextSiblingElement("interface");
1417 continue;
1418 }
1419
1420 tinyxml2::XMLElement* methodNode =
1421 interfaceNode->FirstChildElement("method");
1422 while (methodNode != nullptr)
1423 {
1424 const char* thisMethodName =
1425 methodNode->Attribute("name");
1426 BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName);
1427 if (thisMethodName != nullptr &&
1428 thisMethodName == transaction->methodName)
1429 {
1430 BMCWEB_LOG_DEBUG(
1431 "Found method named {} on interface {}",
1432 thisMethodName, thisInterfaceName);
1433 sdbusplus::message_t m =
1434 crow::connections::systemBus->new_method_call(
1435 connectionName.c_str(),
1436 transaction->path.c_str(),
1437 thisInterfaceName,
1438 transaction->methodName.c_str());
1439
1440 tinyxml2::XMLElement* argumentNode =
1441 methodNode->FirstChildElement("arg");
1442
1443 std::string returnType;
1444
1445 // Find the output type
1446 while (argumentNode != nullptr)
1447 {
1448 const char* argDirection =
1449 argumentNode->Attribute("direction");
1450 const char* argType =
1451 argumentNode->Attribute("type");
1452 if (argDirection != nullptr &&
1453 argType != nullptr &&
1454 std::string(argDirection) == "out")
1455 {
1456 returnType = argType;
1457 break;
1458 }
1459 argumentNode =
1460 argumentNode->NextSiblingElement("arg");
1461 }
1462
1463 auto argIt = transaction->arguments.begin();
1464
1465 argumentNode = methodNode->FirstChildElement("arg");
1466
1467 while (argumentNode != nullptr)
1468 {
1469 const char* argDirection =
1470 argumentNode->Attribute("direction");
1471 const char* argType =
1472 argumentNode->Attribute("type");
1473 if (argDirection != nullptr &&
1474 argType != nullptr &&
1475 std::string(argDirection) == "in")
1476 {
1477 if (argIt == transaction->arguments.end())
1478 {
1479 transaction->setErrorStatus(
1480 "Invalid method args");
1481 return;
1482 }
1483 if (convertJsonToDbus(m.get(),
1484 std::string(argType),
1485 *argIt) < 0)
1486 {
1487 transaction->setErrorStatus(
1488 "Invalid method arg type");
1489 return;
1490 }
1491
1492 argIt++;
1493 }
1494 argumentNode =
1495 argumentNode->NextSiblingElement("arg");
1496 }
1497
1498 crow::connections::systemBus->async_send(
1499 m, [transaction, returnType](
1500 const boost::system::error_code& ec2,
1501 sdbusplus::message_t& m2) {
1502 if (ec2)
1503 {
1504 transaction->methodFailed = true;
1505 const sd_bus_error* e = m2.get_error();
1506
1507 if (e != nullptr)
1508 {
1509 setErrorResponse(
1510 transaction->asyncResp->res,
1511 boost::beast::http::status::
1512 bad_request,
1513 e->name, e->message);
1514 }
1515 else
1516 {
1517 setErrorResponse(
1518 transaction->asyncResp->res,
1519 boost::beast::http::status::
1520 bad_request,
1521 "Method call failed",
1522 methodFailedMsg);
1523 }
1524 return;
1525 }
1526 transaction->methodPassed = true;
1527
1528 handleMethodResponse(transaction, m2,
1529 returnType);
1530 });
1531 break;
1532 }
1533 methodNode = methodNode->NextSiblingElement("method");
1534 }
1535 }
1536 interfaceNode = interfaceNode->NextSiblingElement("interface");
1537 }
1538 },
Ed Tanous1abe55e2018-09-05 08:30:59 -07001539 connectionName, transaction->path,
1540 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001541}
1542
zhanghch058d1b46d2021-04-01 11:18:24 +08001543inline void handleAction(const crow::Request& req,
1544 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001545 const std::string& objectPath,
1546 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001547{
Ed Tanous62598e32023-07-17 17:06:25 -07001548 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath,
1549 methodName);
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001550 nlohmann::json requestDbusData;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001551
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001552 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1553 if (ret == JsonParseResult::BadContentType)
1554 {
1555 setErrorResponse(asyncResp->res,
1556 boost::beast::http::status::unsupported_media_type,
1557 invalidContentType, unsupportedMediaMsg);
1558 return;
1559 }
1560 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001561 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001562 setErrorResponse(asyncResp->res,
1563 boost::beast::http::status::bad_request, noJsonDesc,
1564 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001565 return;
1566 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001567 nlohmann::json::iterator data = requestDbusData.find("data");
1568 if (data == requestDbusData.end())
1569 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001570 setErrorResponse(asyncResp->res,
1571 boost::beast::http::status::bad_request, noJsonDesc,
1572 badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001573 return;
1574 }
1575
1576 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001577 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001578 setErrorResponse(asyncResp->res,
1579 boost::beast::http::status::bad_request, noJsonDesc,
1580 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001581 return;
1582 }
Lei YU28dd5ca2023-03-17 13:17:05 +08001583 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584
1585 transaction->path = objectPath;
1586 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001587 transaction->arguments = std::move(*data);
George Liu2b731192023-01-11 16:27:13 +08001588 dbus::utility::getDbusObject(
1589 objectPath, {},
Ed Tanous1abe55e2018-09-05 08:30:59 -07001590 [transaction](
George Liu2b731192023-01-11 16:27:13 +08001591 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001592 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1593 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001594 if (ec || interfaceNames.empty())
1595 {
1596 BMCWEB_LOG_ERROR("Can't find object");
1597 setErrorResponse(transaction->asyncResp->res,
1598 boost::beast::http::status::not_found,
1599 notFoundDesc, notFoundMsg);
1600 return;
1601 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001602
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001603 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)",
1604 interfaceNames.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001605
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001606 for (const std::pair<std::string, std::vector<std::string>>&
1607 object : interfaceNames)
1608 {
1609 findActionOnInterface(transaction, object.first);
1610 }
1611 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001612}
1613
zhanghch058d1b46d2021-04-01 11:18:24 +08001614inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1615 const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001616{
Ed Tanous62598e32023-07-17 17:06:25 -07001617 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06001618
George Liu2b731192023-01-11 16:27:13 +08001619 dbus::utility::getDbusObject(
1620 objectPath, {},
zhanghch058d1b46d2021-04-01 11:18:24 +08001621 [asyncResp, objectPath](
George Liu2b731192023-01-11 16:27:13 +08001622 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001623 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1624 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001625 if (ec || interfaceNames.empty())
1626 {
1627 BMCWEB_LOG_ERROR("Can't find object");
1628 setErrorResponse(asyncResp->res,
1629 boost::beast::http::status::method_not_allowed,
1630 methodNotAllowedDesc, methodNotAllowedMsg);
1631 return;
1632 }
Matt Spinlerde818812018-12-11 16:39:20 -06001633
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001634 auto transaction =
1635 std::make_shared<InProgressActionData>(asyncResp);
1636 transaction->path = objectPath;
1637 transaction->methodName = "Delete";
1638 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
Matt Spinlerde818812018-12-11 16:39:20 -06001639
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001640 for (const std::pair<std::string, std::vector<std::string>>&
1641 object : interfaceNames)
1642 {
1643 findActionOnInterface(transaction, object.first);
1644 }
1645 });
Matt Spinlerde818812018-12-11 16:39:20 -06001646}
1647
zhanghch058d1b46d2021-04-01 11:18:24 +08001648inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1649 const std::string& objectPath, int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001650{
George Liu7a1dbc42022-12-07 16:03:22 +08001651 dbus::utility::getSubTreePaths(
1652 objectPath, depth, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001653 [asyncResp](
George Liu7a1dbc42022-12-07 16:03:22 +08001654 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001655 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001656 if (ec)
1657 {
1658 setErrorResponse(asyncResp->res,
1659 boost::beast::http::status::not_found,
1660 notFoundDesc, notFoundMsg);
1661 }
1662 else
1663 {
1664 asyncResp->res.jsonValue["status"] = "ok";
1665 asyncResp->res.jsonValue["message"] = "200 OK";
1666 asyncResp->res.jsonValue["data"] = objectPaths;
1667 }
1668 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001669}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001670
zhanghch058d1b46d2021-04-01 11:18:24 +08001671inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1672 const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001673{
Ed Tanous62598e32023-07-17 17:06:25 -07001674 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07001675
Ed Tanous14766872022-03-15 10:44:42 -07001676 asyncResp->res.jsonValue["message"] = "200 OK";
1677 asyncResp->res.jsonValue["status"] = "ok";
1678 asyncResp->res.jsonValue["data"] = nlohmann::json::object();
Ed Tanous049a0512018-11-01 13:58:42 -07001679
George Liue99073f2022-12-09 11:06:16 +08001680 dbus::utility::getSubTree(
1681 objectPath, 0, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001682 [objectPath, asyncResp](
George Liue99073f2022-12-09 11:06:16 +08001683 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001684 const dbus::utility::MapperGetSubTreeResponse& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001685 auto transaction = std::make_shared<InProgressEnumerateData>(
1686 objectPath, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001687
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001688 transaction->subtree =
1689 std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1690 objectNames);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001691
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001692 if (ec)
1693 {
1694 BMCWEB_LOG_ERROR("GetSubTree failed on {}",
1695 transaction->objectPath);
1696 setErrorResponse(transaction->asyncResp->res,
1697 boost::beast::http::status::not_found,
1698 notFoundDesc, notFoundMsg);
1699 return;
1700 }
Ed Tanous64530012018-02-06 17:08:16 -08001701
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001702 // Add the data for the path passed in to the results
1703 // as if GetSubTree returned it, and continue on enumerating
1704 getObjectAndEnumerate(transaction);
1705 });
Ed Tanous64530012018-02-06 17:08:16 -08001706}
Ed Tanous911ac312017-08-15 09:37:42 -07001707
zhanghch058d1b46d2021-04-01 11:18:24 +08001708inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1709 std::string& objectPath, std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001710{
Ed Tanous62598e32023-07-17 17:06:25 -07001711 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001712 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001713 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001714
Ed Tanous1abe55e2018-09-05 08:30:59 -07001715 std::shared_ptr<std::string> path =
1716 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001717
George Liu2b731192023-01-11 16:27:13 +08001718 dbus::utility::getDbusObject(
1719 *path, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001720 [asyncResp, path,
George Liu2b731192023-01-11 16:27:13 +08001721 propertyName](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001722 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001723 if (ec || objectNames.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001724 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001725 setErrorResponse(asyncResp->res,
1726 boost::beast::http::status::not_found,
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001727 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001728 return;
1729 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001730 std::shared_ptr<nlohmann::json> response =
1731 std::make_shared<nlohmann::json>(nlohmann::json::object());
1732 // The mapper should never give us an empty interface names
1733 // list, but check anyway
1734 for (const std::pair<std::string, std::vector<std::string>>&
1735 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001736 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001737 const std::vector<std::string>& interfaceNames =
1738 connection.second;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001739
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001740 if (interfaceNames.empty())
1741 {
1742 setErrorResponse(asyncResp->res,
1743 boost::beast::http::status::not_found,
1744 notFoundDesc, notFoundMsg);
1745 return;
1746 }
1747
1748 for (const std::string& interface : interfaceNames)
1749 {
1750 sdbusplus::message_t m =
1751 crow::connections::systemBus->new_method_call(
1752 connection.first.c_str(), path->c_str(),
1753 "org.freedesktop.DBus.Properties", "GetAll");
1754 m.append(interface);
1755 crow::connections::systemBus->async_send(
1756 m, [asyncResp, response,
1757 propertyName](const boost::system::error_code& ec2,
1758 sdbusplus::message_t& msg) {
1759 if (ec2)
1760 {
1761 BMCWEB_LOG_ERROR("Bad dbus request error: {}",
1762 ec2);
1763 }
1764 else
1765 {
1766 nlohmann::json properties;
1767 int r =
1768 convertDBusToJSON("a{sv}", msg, properties);
1769 if (r < 0)
Patrick Williams5a39f772023-10-20 11:20:21 -05001770 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001771 BMCWEB_LOG_ERROR(
1772 "convertDBusToJSON failed");
Patrick Williams5a39f772023-10-20 11:20:21 -05001773 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001774 else
Patrick Williams5a39f772023-10-20 11:20:21 -05001775 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001776 nlohmann::json::object_t* obj =
1777 properties.get_ptr<
1778 nlohmann::json::object_t*>();
1779 if (obj == nullptr)
1780 {
1781 return;
1782 }
1783 for (auto& prop : *obj)
1784 {
1785 // if property name is empty, or
1786 // matches our search query, add it
1787 // to the response json
1788
1789 if (propertyName->empty())
1790 {
1791 (*response)[prop.first] =
1792 std::move(prop.second);
1793 }
1794 else if (prop.first == *propertyName)
1795 {
1796 *response = std::move(prop.second);
1797 }
1798 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001799 }
1800 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001801 if (response.use_count() == 1)
1802 {
1803 if (!propertyName->empty() && response->empty())
1804 {
1805 setErrorResponse(
1806 asyncResp->res,
1807 boost::beast::http::status::not_found,
1808 propNotFoundDesc, notFoundMsg);
1809 }
1810 else
1811 {
1812 asyncResp->res.jsonValue["status"] = "ok";
1813 asyncResp->res.jsonValue["message"] =
1814 "200 OK";
1815 asyncResp->res.jsonValue["data"] =
1816 *response;
1817 }
1818 }
1819 });
1820 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001821 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001822 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001823}
1824
Ed Tanous1abe55e2018-09-05 08:30:59 -07001825struct AsyncPutRequest
1826{
Ed Tanous4e23a442022-06-06 09:57:26 -07001827 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
zhanghch058d1b46d2021-04-01 11:18:24 +08001828 asyncResp(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001829 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001830 ~AsyncPutRequest()
1831 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001832 if (asyncResp->res.jsonValue.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001833 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001834 setErrorResponse(asyncResp->res,
1835 boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001836 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001837 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001838 }
1839
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001840 AsyncPutRequest(const AsyncPutRequest&) = delete;
1841 AsyncPutRequest(AsyncPutRequest&&) = delete;
1842 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1843 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1844
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001845 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001846 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001847 setErrorResponse(asyncResp->res,
1848 boost::beast::http::status::internal_server_error,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001849 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001850 }
1851
zhanghch058d1b46d2021-04-01 11:18:24 +08001852 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001853 std::string objectPath;
1854 std::string propertyName;
1855 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001856};
1857
zhanghch058d1b46d2021-04-01 11:18:24 +08001858inline void handlePut(const crow::Request& req,
1859 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001860 const std::string& objectPath,
1861 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001862{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001863 if (destProperty.empty())
1864 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001865 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001866 forbiddenResDesc, forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001867 return;
1868 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001869 nlohmann::json requestDbusData;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001870
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001871 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1872 if (ret == JsonParseResult::BadContentType)
1873 {
1874 setErrorResponse(asyncResp->res,
1875 boost::beast::http::status::unsupported_media_type,
1876 invalidContentType, unsupportedMediaMsg);
1877 return;
1878 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001879
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001880 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001881 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001882 setErrorResponse(asyncResp->res,
1883 boost::beast::http::status::bad_request, noJsonDesc,
1884 badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001885 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001886 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001887
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001888 auto propertyIt = requestDbusData.find("data");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001889 if (propertyIt == requestDbusData.end())
1890 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001891 setErrorResponse(asyncResp->res,
1892 boost::beast::http::status::bad_request, noJsonDesc,
1893 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001894 return;
1895 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001896 const nlohmann::json& propertySetValue = *propertyIt;
zhanghch058d1b46d2021-04-01 11:18:24 +08001897 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001898 transaction->objectPath = objectPath;
1899 transaction->propertyName = destProperty;
1900 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001901
George Liu2b731192023-01-11 16:27:13 +08001902 dbus::utility::getDbusObject(
1903 transaction->objectPath, {},
1904 [transaction](const boost::system::error_code& ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001905 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001906 if (!ec2 && objectNames.empty())
1907 {
1908 setErrorResponse(transaction->asyncResp->res,
1909 boost::beast::http::status::not_found,
1910 propNotFoundDesc, notFoundMsg);
1911 return;
1912 }
Ed Tanous911ac312017-08-15 09:37:42 -07001913
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001914 for (const std::pair<std::string, std::vector<std::string>>&
1915 connection : objectNames)
1916 {
1917 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001918
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001919 crow::connections::systemBus->async_method_call(
1920 [connectionName{std::string(connectionName)},
1921 transaction](const boost::system::error_code& ec3,
1922 const std::string& introspectXml) {
1923 if (ec3)
Ed Tanousb0b61522024-08-06 10:20:49 -07001924 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001925 BMCWEB_LOG_ERROR(
1926 "Introspect call failed with error: {} on process: {}",
1927 ec3.message(), connectionName);
1928 transaction->setErrorStatus("Unexpected Error");
1929 return;
Ed Tanousb0b61522024-08-06 10:20:49 -07001930 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001931 tinyxml2::XMLDocument doc;
1932
1933 doc.Parse(introspectXml.c_str());
1934 tinyxml2::XMLNode* pRoot =
1935 doc.FirstChildElement("node");
1936 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001937 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001938 BMCWEB_LOG_ERROR("XML document failed to parse: {}",
1939 introspectXml);
1940 transaction->setErrorStatus("Unexpected Error");
1941 return;
1942 }
1943 tinyxml2::XMLElement* ifaceNode =
1944 pRoot->FirstChildElement("interface");
1945 while (ifaceNode != nullptr)
1946 {
1947 const char* interfaceName =
1948 ifaceNode->Attribute("name");
1949 BMCWEB_LOG_DEBUG("found interface {}",
1950 interfaceName);
1951 tinyxml2::XMLElement* propNode =
1952 ifaceNode->FirstChildElement("property");
1953 while (propNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001954 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001955 const char* propertyName =
1956 propNode->Attribute("name");
1957 if (propertyName == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001958 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001959 BMCWEB_LOG_DEBUG(
1960 "Couldn't find name property");
1961 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001962 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001963 BMCWEB_LOG_DEBUG("Found property {}",
1964 propertyName);
1965 if (propertyName == transaction->propertyName)
Ed Tanous002d39b2022-05-31 08:59:27 -07001966 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001967 const char* argType =
1968 propNode->Attribute("type");
1969 if (argType != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07001970 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001971 sdbusplus::message_t m =
1972 crow::connections::systemBus
1973 ->new_method_call(
1974 connectionName.c_str(),
1975 transaction->objectPath
1976 .c_str(),
1977 "org.freedesktop.DBus."
1978 "Properties",
1979 "Set");
1980 m.append(interfaceName,
1981 transaction->propertyName);
1982 int r = sd_bus_message_open_container(
1983 m.get(), SD_BUS_TYPE_VARIANT,
1984 argType);
1985 if (r < 0)
1986 {
1987 transaction->setErrorStatus(
1988 "Unexpected Error");
1989 return;
1990 }
1991 r = convertJsonToDbus(
1992 m.get(), argType,
1993 transaction->propertyValue);
1994 if (r < 0)
1995 {
1996 if (r == -ERANGE)
1997 {
1998 transaction->setErrorStatus(
1999 "Provided property value "
2000 "is out of range for the "
2001 "property type");
2002 }
2003 else
2004 {
2005 transaction->setErrorStatus(
2006 "Invalid arg type");
2007 }
2008 return;
2009 }
2010 r = sd_bus_message_close_container(
2011 m.get());
2012 if (r < 0)
2013 {
2014 transaction->setErrorStatus(
2015 "Unexpected Error");
2016 return;
2017 }
2018 crow::connections::systemBus
2019 ->async_send(
2020 m,
2021 [transaction](
2022 const boost::system::
2023 error_code& ec,
2024 sdbusplus::message_t& m2) {
2025 BMCWEB_LOG_DEBUG("sent");
2026 if (ec)
2027 {
2028 const sd_bus_error* e =
2029 m2.get_error();
2030 setErrorResponse(
2031 transaction
2032 ->asyncResp
2033 ->res,
2034 boost::beast::http::
2035 status::
2036 forbidden,
2037 (e) != nullptr
2038 ? e->name
2039 : ec.category()
2040 .name(),
2041 (e) != nullptr
2042 ? e->message
2043 : ec.message());
2044 }
2045 else
2046 {
2047 transaction->asyncResp
2048 ->res.jsonValue
2049 ["status"] =
2050 "ok";
2051 transaction->asyncResp
2052 ->res.jsonValue
2053 ["message"] =
2054 "200 OK";
2055 transaction->asyncResp
2056 ->res
2057 .jsonValue["data"] =
2058 nullptr;
2059 }
2060 });
Ed Tanous002d39b2022-05-31 08:59:27 -07002061 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002062 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002063 propNode =
2064 propNode->NextSiblingElement("property");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002065 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002066 ifaceNode =
2067 ifaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002068 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002069 },
2070 connectionName, transaction->objectPath,
2071 "org.freedesktop.DBus.Introspectable", "Introspect");
2072 }
2073 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002074}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002075
zhanghch058d1b46d2021-04-01 11:18:24 +08002076inline void handleDBusUrl(const crow::Request& req,
2077 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002078 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002079{
Ed Tanous049a0512018-11-01 13:58:42 -07002080 // If accessing a single attribute, fill in and update objectPath,
2081 // otherwise leave destProperty blank
Ed Tanouse05aec52022-01-25 10:28:56 -08002082 std::string destProperty;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002083 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002084 size_t attrPosition = objectPath.find(attrSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002085 if (attrPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002086 {
2087 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2088 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002089 objectPath.resize(attrPosition);
Ed Tanous049a0512018-11-01 13:58:42 -07002090 }
2091
Ed Tanousb41187f2019-10-24 16:30:02 -07002092 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002093 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002094 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002095 size_t actionPosition = objectPath.find(actionSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002096 if (actionPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002097 {
2098 std::string postProperty =
2099 objectPath.substr((actionPosition + strlen(actionSeperator)),
2100 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002101 objectPath.resize(actionPosition);
zhanghch058d1b46d2021-04-01 11:18:24 +08002102 handleAction(req, asyncResp, objectPath, postProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002103 return;
2104 }
2105 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002106 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002107 {
Ed Tanous11ba3972022-07-11 09:50:41 -07002108 if (objectPath.ends_with("/enumerate"))
Ed Tanous049a0512018-11-01 13:58:42 -07002109 {
2110 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2111 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002112 handleEnumerate(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002113 }
Ed Tanous11ba3972022-07-11 09:50:41 -07002114 else if (objectPath.ends_with("/list"))
Ed Tanous049a0512018-11-01 13:58:42 -07002115 {
2116 objectPath.erase(objectPath.end() - sizeof("list"),
2117 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002118 handleList(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002119 }
2120 else
2121 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002122 // Trim any trailing "/" at the end
Ed Tanous11ba3972022-07-11 09:50:41 -07002123 if (objectPath.ends_with("/"))
Ed Tanousf839dfe2018-11-12 11:11:15 -08002124 {
2125 objectPath.pop_back();
zhanghch058d1b46d2021-04-01 11:18:24 +08002126 handleList(asyncResp, objectPath, 1);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002127 }
2128 else
2129 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002130 handleGet(asyncResp, objectPath, destProperty);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002131 }
Ed Tanous049a0512018-11-01 13:58:42 -07002132 }
2133 return;
2134 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002135 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002136 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002137 handlePut(req, asyncResp, objectPath, destProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002138 return;
2139 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002140 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002141 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002142 handleDelete(asyncResp, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002143 return;
2144 }
Ed Tanous049a0512018-11-01 13:58:42 -07002145
zhanghch058d1b46d2021-04-01 11:18:24 +08002146 setErrorResponse(asyncResp->res,
2147 boost::beast::http::status::method_not_allowed,
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002148 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002149}
2150
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002151inline void handleBusSystemPost(
2152 const crow::Request& req,
2153 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2154 const std::string& processName, const std::string& requestedPath)
Ed Tanous1656b292022-05-04 11:33:42 -07002155{
2156 std::vector<std::string> strs;
Ed Tanous50ebd4a2023-01-19 19:03:17 -08002157
2158 bmcweb::split(strs, requestedPath, '/');
Ed Tanous1656b292022-05-04 11:33:42 -07002159 std::string objectPath;
2160 std::string interfaceName;
2161 std::string methodName;
2162 auto it = strs.begin();
2163 if (it == strs.end())
2164 {
2165 objectPath = "/";
2166 }
2167 while (it != strs.end())
2168 {
2169 // Check if segment contains ".". If it does, it must be an
2170 // interface
2171 if (it->find(".") != std::string::npos)
2172 {
2173 break;
2174 // This check is necessary as the trailing slash gets
2175 // parsed as part of our <path> specifier above, which
2176 // causes the normal trailing backslash redirector to
2177 // fail.
2178 }
2179 if (!it->empty())
2180 {
2181 objectPath += "/" + *it;
2182 }
2183 it++;
2184 }
2185 if (it != strs.end())
2186 {
2187 interfaceName = *it;
2188 it++;
2189
2190 // after interface, we might have a method name
2191 if (it != strs.end())
2192 {
2193 methodName = *it;
2194 it++;
2195 }
2196 }
2197 if (it != strs.end())
2198 {
2199 // if there is more levels past the method name, something
2200 // went wrong, return not found
2201 asyncResp->res.result(boost::beast::http::status::not_found);
2202 return;
2203 }
2204 if (interfaceName.empty())
2205 {
2206 crow::connections::systemBus->async_method_call(
2207 [asyncResp, processName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002208 objectPath](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002209 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002210 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002211 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002212 BMCWEB_LOG_ERROR(
2213 "Introspect call failed with error: {} on process: {} path: {}",
2214 ec.message(), processName, objectPath);
2215 return;
2216 }
2217 tinyxml2::XMLDocument doc;
2218
2219 doc.Parse(introspectXml.c_str());
2220 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2221 if (pRoot == nullptr)
2222 {
2223 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2224 processName, objectPath);
2225 asyncResp->res.jsonValue["status"] = "XML parse error";
2226 asyncResp->res.result(
2227 boost::beast::http::status::internal_server_error);
2228 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002229 }
2230
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002231 BMCWEB_LOG_DEBUG("{}", introspectXml);
2232 asyncResp->res.jsonValue["status"] = "ok";
2233 asyncResp->res.jsonValue["bus_name"] = processName;
2234 asyncResp->res.jsonValue["object_path"] = objectPath;
2235
2236 nlohmann::json& interfacesArray =
2237 asyncResp->res.jsonValue["interfaces"];
2238 interfacesArray = nlohmann::json::array();
2239 tinyxml2::XMLElement* interface =
2240 pRoot->FirstChildElement("interface");
2241
2242 while (interface != nullptr)
2243 {
2244 const char* ifaceName = interface->Attribute("name");
2245 if (ifaceName != nullptr)
2246 {
2247 nlohmann::json::object_t interfaceObj;
2248 interfaceObj["name"] = ifaceName;
2249 interfacesArray.emplace_back(std::move(interfaceObj));
2250 }
2251
2252 interface = interface->NextSiblingElement("interface");
2253 }
2254 },
Ed Tanous1656b292022-05-04 11:33:42 -07002255 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2256 "Introspect");
2257 }
2258 else if (methodName.empty())
2259 {
2260 crow::connections::systemBus->async_method_call(
2261 [asyncResp, processName, objectPath,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002262 interfaceName](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002263 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002264 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002265 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002266 BMCWEB_LOG_ERROR(
2267 "Introspect call failed with error: {} on process: {} path: {}",
2268 ec.message(), processName, objectPath);
2269 return;
2270 }
2271 tinyxml2::XMLDocument doc;
2272
2273 doc.Parse(introspectXml.data(), introspectXml.size());
2274 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2275 if (pRoot == nullptr)
2276 {
2277 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2278 processName, objectPath);
2279 asyncResp->res.result(
2280 boost::beast::http::status::internal_server_error);
2281 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002282 }
Ed Tanous14766872022-03-15 10:44:42 -07002283
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002284 asyncResp->res.jsonValue["status"] = "ok";
2285 asyncResp->res.jsonValue["bus_name"] = processName;
2286 asyncResp->res.jsonValue["interface"] = interfaceName;
2287 asyncResp->res.jsonValue["object_path"] = objectPath;
Ed Tanous1656b292022-05-04 11:33:42 -07002288
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002289 nlohmann::json& methodsArray =
2290 asyncResp->res.jsonValue["methods"];
2291 methodsArray = nlohmann::json::array();
2292
2293 nlohmann::json& signalsArray =
2294 asyncResp->res.jsonValue["signals"];
2295 signalsArray = nlohmann::json::array();
2296
2297 nlohmann::json& propertiesObj =
2298 asyncResp->res.jsonValue["properties"];
2299 propertiesObj = nlohmann::json::object();
2300
2301 // if we know we're the only call, build the
2302 // json directly
2303 tinyxml2::XMLElement* interface =
2304 pRoot->FirstChildElement("interface");
2305 while (interface != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002306 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002307 const char* ifaceName = interface->Attribute("name");
2308
2309 if (ifaceName != nullptr && ifaceName == interfaceName)
Ed Tanous1656b292022-05-04 11:33:42 -07002310 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002311 break;
Ed Tanous002d39b2022-05-31 08:59:27 -07002312 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002313
2314 interface = interface->NextSiblingElement("interface");
2315 }
2316 if (interface == nullptr)
2317 {
2318 // if we got to the end of the list and
2319 // never found a match, throw 404
2320 asyncResp->res.result(
2321 boost::beast::http::status::not_found);
2322 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002323 }
2324
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002325 tinyxml2::XMLElement* methods =
2326 interface->FirstChildElement("method");
2327 while (methods != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002328 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002329 nlohmann::json argsArray = nlohmann::json::array();
2330 tinyxml2::XMLElement* arg =
2331 methods->FirstChildElement("arg");
2332 while (arg != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002333 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002334 nlohmann::json thisArg;
2335 for (const char* fieldName : std::array<const char*, 3>{
2336 "name", "direction", "type"})
2337 {
2338 const char* fieldValue = arg->Attribute(fieldName);
2339 if (fieldValue != nullptr)
2340 {
2341 thisArg[fieldName] = fieldValue;
2342 }
2343 }
2344 argsArray.emplace_back(std::move(thisArg));
2345 arg = arg->NextSiblingElement("arg");
Ed Tanous002d39b2022-05-31 08:59:27 -07002346 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002347
2348 const char* name = methods->Attribute("name");
2349 if (name != nullptr)
2350 {
2351 std::string uri;
2352 uri.reserve(14 + processName.size() +
2353 objectPath.size() + interfaceName.size() +
2354 strlen(name));
2355 uri += "/bus/system/";
2356 uri += processName;
2357 uri += objectPath;
2358 uri += "/";
2359 uri += interfaceName;
2360 uri += "/";
2361 uri += name;
2362
2363 nlohmann::json::object_t object;
2364 object["name"] = name;
2365 object["uri"] = std::move(uri);
2366 object["args"] = argsArray;
2367
2368 methodsArray.emplace_back(std::move(object));
2369 }
2370 methods = methods->NextSiblingElement("method");
Ed Tanous002d39b2022-05-31 08:59:27 -07002371 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002372 tinyxml2::XMLElement* signals =
2373 interface->FirstChildElement("signal");
2374 while (signals != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002375 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002376 nlohmann::json argsArray = nlohmann::json::array();
2377
2378 tinyxml2::XMLElement* arg =
2379 signals->FirstChildElement("arg");
2380 while (arg != nullptr)
2381 {
2382 const char* name = arg->Attribute("name");
2383 const char* type = arg->Attribute("type");
2384 if (name != nullptr && type != nullptr)
2385 {
2386 nlohmann::json::object_t params;
2387 params["name"] = name;
2388 params["type"] = type;
2389 argsArray.push_back(std::move(params));
2390 }
2391 arg = arg->NextSiblingElement("arg");
2392 }
2393 const char* name = signals->Attribute("name");
2394 if (name != nullptr)
2395 {
2396 nlohmann::json::object_t object;
2397 object["name"] = name;
2398 object["args"] = argsArray;
2399 signalsArray.emplace_back(std::move(object));
2400 }
2401
2402 signals = signals->NextSiblingElement("signal");
Ed Tanous002d39b2022-05-31 08:59:27 -07002403 }
2404
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002405 tinyxml2::XMLElement* property =
2406 interface->FirstChildElement("property");
2407 while (property != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002408 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002409 const char* name = property->Attribute("name");
2410 const char* type = property->Attribute("type");
2411 if (type != nullptr && name != nullptr)
2412 {
2413 sdbusplus::message_t m =
2414 crow::connections::systemBus->new_method_call(
2415 processName.c_str(), objectPath.c_str(),
2416 "org.freedesktop."
2417 "DBus."
2418 "Properties",
2419 "Get");
2420 m.append(interfaceName, name);
2421 nlohmann::json& propertyItem = propertiesObj[name];
2422 crow::connections::systemBus->async_send(
2423 m, [&propertyItem,
2424 asyncResp](const boost::system::error_code& ec2,
2425 sdbusplus::message_t& msg) {
2426 if (ec2)
2427 {
2428 return;
2429 }
Ed Tanous1656b292022-05-04 11:33:42 -07002430
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002431 int r =
2432 convertDBusToJSON("v", msg, propertyItem);
2433 if (r < 0)
2434 {
2435 BMCWEB_LOG_ERROR(
2436 "Couldn't convert vector to json");
2437 }
2438 });
2439 }
2440 property = property->NextSiblingElement("property");
Ed Tanous1656b292022-05-04 11:33:42 -07002441 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002442 },
Ed Tanous1656b292022-05-04 11:33:42 -07002443 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2444 "Introspect");
2445 }
2446 else
2447 {
2448 if (req.method() != boost::beast::http::verb::post)
2449 {
2450 asyncResp->res.result(boost::beast::http::status::not_found);
2451 return;
2452 }
2453
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002454 nlohmann::json requestDbusData;
2455 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2456 if (ret == JsonParseResult::BadContentType)
Ed Tanous1656b292022-05-04 11:33:42 -07002457 {
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002458 setErrorResponse(asyncResp->res,
2459 boost::beast::http::status::unsupported_media_type,
2460 invalidContentType, unsupportedMediaMsg);
Ed Tanous1656b292022-05-04 11:33:42 -07002461 return;
2462 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002463 if (ret != JsonParseResult::Success)
2464 {
2465 setErrorResponse(asyncResp->res,
2466 boost::beast::http::status::bad_request,
2467 noJsonDesc, badReqMsg);
2468 return;
2469 }
2470
Ed Tanous1656b292022-05-04 11:33:42 -07002471 if (!requestDbusData.is_array())
2472 {
2473 asyncResp->res.result(boost::beast::http::status::bad_request);
2474 return;
2475 }
Lei YU28dd5ca2023-03-17 13:17:05 +08002476 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1656b292022-05-04 11:33:42 -07002477
2478 transaction->path = objectPath;
2479 transaction->methodName = methodName;
2480 transaction->arguments = std::move(requestDbusData);
2481
2482 findActionOnInterface(transaction, processName);
2483 }
2484}
2485
Ed Tanous23a21a12020-07-25 04:45:05 +00002486inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002487{
2488 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous432a8902021-06-14 15:28:56 -07002489 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002490 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002491 [](const crow::Request&,
2492 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002493 nlohmann::json::array_t buses;
2494 nlohmann::json& bus = buses.emplace_back();
2495 bus["name"] = "system";
2496 asyncResp->res.jsonValue["busses"] = std::move(buses);
2497 asyncResp->res.jsonValue["status"] = "ok";
2498 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002499
2500 BMCWEB_ROUTE(app, "/bus/system/")
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 auto myCallback = [asyncResp](
2506 const boost::system::error_code& ec,
zhanghch058d1b46d2021-04-01 11:18:24 +08002507 std::vector<std::string>& names) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002508 if (ec)
2509 {
2510 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
2511 asyncResp->res.result(
2512 boost::beast::http::status::internal_server_error);
2513 }
2514 else
2515 {
2516 std::ranges::sort(names);
2517 asyncResp->res.jsonValue["status"] = "ok";
2518 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2519 for (const auto& name : names)
2520 {
2521 nlohmann::json::object_t object;
2522 object["name"] = name;
2523 objectsSub.emplace_back(std::move(object));
2524 }
2525 }
2526 };
2527 crow::connections::systemBus->async_method_call(
2528 std::move(myCallback), "org.freedesktop.DBus", "/",
2529 "org.freedesktop.DBus", "ListNames");
2530 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002531
2532 BMCWEB_ROUTE(app, "/list/")
Ed Tanous432a8902021-06-14 15:28:56 -07002533 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002534 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002535 [](const crow::Request&,
2536 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002537 handleList(asyncResp, "/");
2538 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002539
2540 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002541 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002542 .methods(boost::beast::http::verb::get)(
2543 [](const crow::Request& req,
2544 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002545 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002546 std::string objectPath = "/xyz/" + path;
2547 handleDBusUrl(req, asyncResp, objectPath);
2548 });
zhanghch058d1b46d2021-04-01 11:18:24 +08002549
2550 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002551 .privileges({{"ConfigureComponents", "ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002552 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2553 boost::beast::http::verb::delete_)(
2554 [](const crow::Request& req,
2555 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2556 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002557 std::string objectPath = "/xyz/" + path;
2558 handleDBusUrl(req, asyncResp, objectPath);
2559 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002560
Ed Tanous049a0512018-11-01 13:58:42 -07002561 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002562 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002563 .methods(boost::beast::http::verb::get)(
2564 [](const crow::Request& req,
2565 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2566 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002567 std::string objectPath = "/org/" + path;
2568 handleDBusUrl(req, asyncResp, objectPath);
2569 });
Tanousf00032d2018-11-05 01:18:10 -03002570
2571 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002572 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002573 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2574 boost::beast::http::verb::delete_)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002575 [](const crow::Request& req,
2576 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002577 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002578 std::string objectPath = "/org/" + path;
2579 handleDBusUrl(req, asyncResp, objectPath);
2580 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002581
Ed Tanous1abe55e2018-09-05 08:30:59 -07002582 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002583 .privileges({{"ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002584 .methods(boost::beast::http::verb::get)(
2585 [](const crow::Request&,
2586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2587 const std::string& dumpId) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002588 if (!validateFilename(dumpId))
2589 {
2590 asyncResp->res.result(
2591 boost::beast::http::status::bad_request);
2592 return;
2593 }
2594 std::filesystem::path loc(
2595 "/var/lib/phosphor-debug-collector/dumps");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002596
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002597 loc /= dumpId;
Ramesh Iyyard9207042019-07-05 08:04:42 -05002598
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002599 if (!std::filesystem::exists(loc) ||
2600 !std::filesystem::is_directory(loc))
2601 {
2602 BMCWEB_LOG_ERROR("{}Not found", loc.string());
2603 asyncResp->res.result(
2604 boost::beast::http::status::not_found);
2605 return;
2606 }
2607 std::filesystem::directory_iterator files(loc);
zhanghch058d1b46d2021-04-01 11:18:24 +08002608
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002609 for (const auto& file : files)
2610 {
Myung Baed51c61b2024-09-13 10:35:34 -05002611 if (asyncResp->res.openFile(file) !=
2612 crow::OpenCode::Success)
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002613 {
2614 continue;
2615 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002616
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002617 asyncResp->res.addHeader(
2618 boost::beast::http::field::content_type,
2619 "application/octet-stream");
zhanghch058d1b46d2021-04-01 11:18:24 +08002620
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002621 // Assuming only one dump file will be present in the dump
2622 // id directory
2623 std::string dumpFileName = file.path().filename().string();
zhanghch058d1b46d2021-04-01 11:18:24 +08002624
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002625 // Filename should be in alphanumeric, dot and underscore
2626 // Its based on phosphor-debug-collector application
2627 // dumpfile format
2628 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2629 if (!std::regex_match(dumpFileName, dumpFileRegex))
2630 {
2631 BMCWEB_LOG_ERROR("Invalid dump filename {}",
2632 dumpFileName);
2633 asyncResp->res.result(
2634 boost::beast::http::status::not_found);
2635 return;
2636 }
2637 std::string contentDispositionParam =
2638 "attachment; filename=\"" + dumpFileName + "\"";
2639
2640 asyncResp->res.addHeader(
2641 boost::beast::http::field::content_disposition,
2642 contentDispositionParam);
2643
2644 return;
2645 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002646 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanousad18f072018-11-14 14:07:48 -08002647 return;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002648 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002649
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002650 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002651 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002652
2653 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002654 [](const crow::Request&,
2655 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +00002656 const std::string& connection) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002657 introspectObjects(connection, "/", asyncResp);
2658 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002659
2660 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002661 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous1656b292022-05-04 11:33:42 -07002662 .methods(boost::beast::http::verb::get,
2663 boost::beast::http::verb::post)(handleBusSystemPost);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002664}
2665} // namespace openbmc_mapper
2666} // namespace crow