blob: 1b5bba630729e460bb4bdc4577ee1b37ba7ddbb3 [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());
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001732 for (const std::pair<std::string, std::vector<std::string>>&
1733 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001735 const std::vector<std::string>& interfaceNames =
1736 connection.second;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001737
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001738 if (interfaceNames.empty())
1739 {
Lei YU65622a52025-01-20 09:45:50 +00001740 // mapper allows empty interfaces in case an
1741 // object does not implement any interface.
1742 continue;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001743 }
1744
1745 for (const std::string& interface : interfaceNames)
1746 {
1747 sdbusplus::message_t m =
1748 crow::connections::systemBus->new_method_call(
1749 connection.first.c_str(), path->c_str(),
1750 "org.freedesktop.DBus.Properties", "GetAll");
1751 m.append(interface);
1752 crow::connections::systemBus->async_send(
1753 m, [asyncResp, response,
1754 propertyName](const boost::system::error_code& ec2,
1755 sdbusplus::message_t& msg) {
1756 if (ec2)
1757 {
1758 BMCWEB_LOG_ERROR("Bad dbus request error: {}",
1759 ec2);
1760 }
1761 else
1762 {
1763 nlohmann::json properties;
1764 int r =
1765 convertDBusToJSON("a{sv}", msg, properties);
1766 if (r < 0)
Patrick Williams5a39f772023-10-20 11:20:21 -05001767 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001768 BMCWEB_LOG_ERROR(
1769 "convertDBusToJSON failed");
Patrick Williams5a39f772023-10-20 11:20:21 -05001770 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001771 else
Patrick Williams5a39f772023-10-20 11:20:21 -05001772 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001773 nlohmann::json::object_t* obj =
1774 properties.get_ptr<
1775 nlohmann::json::object_t*>();
1776 if (obj == nullptr)
1777 {
1778 return;
1779 }
1780 for (auto& prop : *obj)
1781 {
1782 // if property name is empty, or
1783 // matches our search query, add it
1784 // to the response json
1785
1786 if (propertyName->empty())
1787 {
1788 (*response)[prop.first] =
1789 std::move(prop.second);
1790 }
1791 else if (prop.first == *propertyName)
1792 {
1793 *response = std::move(prop.second);
1794 }
1795 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001796 }
1797 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001798 if (response.use_count() == 1)
1799 {
1800 if (!propertyName->empty() && response->empty())
1801 {
1802 setErrorResponse(
1803 asyncResp->res,
1804 boost::beast::http::status::not_found,
1805 propNotFoundDesc, notFoundMsg);
1806 }
1807 else
1808 {
1809 asyncResp->res.jsonValue["status"] = "ok";
1810 asyncResp->res.jsonValue["message"] =
1811 "200 OK";
1812 asyncResp->res.jsonValue["data"] =
1813 *response;
1814 }
1815 }
1816 });
1817 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001818 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001819 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001820}
1821
Ed Tanous1abe55e2018-09-05 08:30:59 -07001822struct AsyncPutRequest
1823{
Ed Tanous4e23a442022-06-06 09:57:26 -07001824 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
zhanghch058d1b46d2021-04-01 11:18:24 +08001825 asyncResp(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001826 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001827 ~AsyncPutRequest()
1828 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001829 if (asyncResp->res.jsonValue.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001830 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001831 setErrorResponse(asyncResp->res,
1832 boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001833 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001834 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001835 }
1836
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001837 AsyncPutRequest(const AsyncPutRequest&) = delete;
1838 AsyncPutRequest(AsyncPutRequest&&) = delete;
1839 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1840 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1841
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001842 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001843 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001844 setErrorResponse(asyncResp->res,
1845 boost::beast::http::status::internal_server_error,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001846 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001847 }
1848
zhanghch058d1b46d2021-04-01 11:18:24 +08001849 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001850 std::string objectPath;
1851 std::string propertyName;
1852 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001853};
1854
zhanghch058d1b46d2021-04-01 11:18:24 +08001855inline void handlePut(const crow::Request& req,
1856 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001857 const std::string& objectPath,
1858 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001859{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001860 if (destProperty.empty())
1861 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001862 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001863 forbiddenResDesc, forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001864 return;
1865 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001866 nlohmann::json requestDbusData;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001867
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001868 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1869 if (ret == JsonParseResult::BadContentType)
1870 {
1871 setErrorResponse(asyncResp->res,
1872 boost::beast::http::status::unsupported_media_type,
1873 invalidContentType, unsupportedMediaMsg);
1874 return;
1875 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001876
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001877 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001878 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001879 setErrorResponse(asyncResp->res,
1880 boost::beast::http::status::bad_request, noJsonDesc,
1881 badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001882 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001883 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001884
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001885 auto propertyIt = requestDbusData.find("data");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001886 if (propertyIt == requestDbusData.end())
1887 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001888 setErrorResponse(asyncResp->res,
1889 boost::beast::http::status::bad_request, noJsonDesc,
1890 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001891 return;
1892 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001893 const nlohmann::json& propertySetValue = *propertyIt;
zhanghch058d1b46d2021-04-01 11:18:24 +08001894 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001895 transaction->objectPath = objectPath;
1896 transaction->propertyName = destProperty;
1897 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001898
George Liu2b731192023-01-11 16:27:13 +08001899 dbus::utility::getDbusObject(
1900 transaction->objectPath, {},
1901 [transaction](const boost::system::error_code& ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001902 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001903 if (!ec2 && objectNames.empty())
1904 {
1905 setErrorResponse(transaction->asyncResp->res,
1906 boost::beast::http::status::not_found,
1907 propNotFoundDesc, notFoundMsg);
1908 return;
1909 }
Ed Tanous911ac312017-08-15 09:37:42 -07001910
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001911 for (const std::pair<std::string, std::vector<std::string>>&
1912 connection : objectNames)
1913 {
1914 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001915
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001916 crow::connections::systemBus->async_method_call(
1917 [connectionName{std::string(connectionName)},
1918 transaction](const boost::system::error_code& ec3,
1919 const std::string& introspectXml) {
1920 if (ec3)
Ed Tanousb0b61522024-08-06 10:20:49 -07001921 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001922 BMCWEB_LOG_ERROR(
1923 "Introspect call failed with error: {} on process: {}",
1924 ec3.message(), connectionName);
1925 transaction->setErrorStatus("Unexpected Error");
1926 return;
Ed Tanousb0b61522024-08-06 10:20:49 -07001927 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001928 tinyxml2::XMLDocument doc;
1929
1930 doc.Parse(introspectXml.c_str());
1931 tinyxml2::XMLNode* pRoot =
1932 doc.FirstChildElement("node");
1933 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001934 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001935 BMCWEB_LOG_ERROR("XML document failed to parse: {}",
1936 introspectXml);
1937 transaction->setErrorStatus("Unexpected Error");
1938 return;
1939 }
1940 tinyxml2::XMLElement* ifaceNode =
1941 pRoot->FirstChildElement("interface");
1942 while (ifaceNode != nullptr)
1943 {
1944 const char* interfaceName =
1945 ifaceNode->Attribute("name");
1946 BMCWEB_LOG_DEBUG("found interface {}",
1947 interfaceName);
1948 tinyxml2::XMLElement* propNode =
1949 ifaceNode->FirstChildElement("property");
1950 while (propNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001951 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001952 const char* propertyName =
1953 propNode->Attribute("name");
1954 if (propertyName == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001955 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001956 BMCWEB_LOG_DEBUG(
1957 "Couldn't find name property");
1958 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001959 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001960 BMCWEB_LOG_DEBUG("Found property {}",
1961 propertyName);
1962 if (propertyName == transaction->propertyName)
Ed Tanous002d39b2022-05-31 08:59:27 -07001963 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001964 const char* argType =
1965 propNode->Attribute("type");
1966 if (argType != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07001967 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001968 sdbusplus::message_t m =
1969 crow::connections::systemBus
1970 ->new_method_call(
1971 connectionName.c_str(),
1972 transaction->objectPath
1973 .c_str(),
1974 "org.freedesktop.DBus."
1975 "Properties",
1976 "Set");
1977 m.append(interfaceName,
1978 transaction->propertyName);
1979 int r = sd_bus_message_open_container(
1980 m.get(), SD_BUS_TYPE_VARIANT,
1981 argType);
1982 if (r < 0)
1983 {
1984 transaction->setErrorStatus(
1985 "Unexpected Error");
1986 return;
1987 }
1988 r = convertJsonToDbus(
1989 m.get(), argType,
1990 transaction->propertyValue);
1991 if (r < 0)
1992 {
1993 if (r == -ERANGE)
1994 {
1995 transaction->setErrorStatus(
1996 "Provided property value "
1997 "is out of range for the "
1998 "property type");
1999 }
2000 else
2001 {
2002 transaction->setErrorStatus(
2003 "Invalid arg type");
2004 }
2005 return;
2006 }
2007 r = sd_bus_message_close_container(
2008 m.get());
2009 if (r < 0)
2010 {
2011 transaction->setErrorStatus(
2012 "Unexpected Error");
2013 return;
2014 }
2015 crow::connections::systemBus
2016 ->async_send(
2017 m,
2018 [transaction](
2019 const boost::system::
2020 error_code& ec,
2021 sdbusplus::message_t& m2) {
2022 BMCWEB_LOG_DEBUG("sent");
2023 if (ec)
2024 {
2025 const sd_bus_error* e =
2026 m2.get_error();
2027 setErrorResponse(
2028 transaction
2029 ->asyncResp
2030 ->res,
2031 boost::beast::http::
2032 status::
2033 forbidden,
2034 (e) != nullptr
2035 ? e->name
2036 : ec.category()
2037 .name(),
2038 (e) != nullptr
2039 ? e->message
2040 : ec.message());
2041 }
2042 else
2043 {
2044 transaction->asyncResp
2045 ->res.jsonValue
2046 ["status"] =
2047 "ok";
2048 transaction->asyncResp
2049 ->res.jsonValue
2050 ["message"] =
2051 "200 OK";
2052 transaction->asyncResp
2053 ->res
2054 .jsonValue["data"] =
2055 nullptr;
2056 }
2057 });
Ed Tanous002d39b2022-05-31 08:59:27 -07002058 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002059 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002060 propNode =
2061 propNode->NextSiblingElement("property");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002062 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002063 ifaceNode =
2064 ifaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002065 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002066 },
2067 connectionName, transaction->objectPath,
2068 "org.freedesktop.DBus.Introspectable", "Introspect");
2069 }
2070 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002071}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002072
zhanghch058d1b46d2021-04-01 11:18:24 +08002073inline void handleDBusUrl(const crow::Request& req,
2074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002075 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002076{
Ed Tanous049a0512018-11-01 13:58:42 -07002077 // If accessing a single attribute, fill in and update objectPath,
2078 // otherwise leave destProperty blank
Ed Tanouse05aec52022-01-25 10:28:56 -08002079 std::string destProperty;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002080 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002081 size_t attrPosition = objectPath.find(attrSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002082 if (attrPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002083 {
2084 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2085 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002086 objectPath.resize(attrPosition);
Ed Tanous049a0512018-11-01 13:58:42 -07002087 }
2088
Ed Tanousb41187f2019-10-24 16:30:02 -07002089 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002090 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002091 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002092 size_t actionPosition = objectPath.find(actionSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002093 if (actionPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002094 {
2095 std::string postProperty =
2096 objectPath.substr((actionPosition + strlen(actionSeperator)),
2097 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002098 objectPath.resize(actionPosition);
zhanghch058d1b46d2021-04-01 11:18:24 +08002099 handleAction(req, asyncResp, objectPath, postProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002100 return;
2101 }
2102 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002103 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002104 {
Ed Tanous11ba3972022-07-11 09:50:41 -07002105 if (objectPath.ends_with("/enumerate"))
Ed Tanous049a0512018-11-01 13:58:42 -07002106 {
2107 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2108 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002109 handleEnumerate(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002110 }
Ed Tanous11ba3972022-07-11 09:50:41 -07002111 else if (objectPath.ends_with("/list"))
Ed Tanous049a0512018-11-01 13:58:42 -07002112 {
2113 objectPath.erase(objectPath.end() - sizeof("list"),
2114 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002115 handleList(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002116 }
2117 else
2118 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002119 // Trim any trailing "/" at the end
Ed Tanous11ba3972022-07-11 09:50:41 -07002120 if (objectPath.ends_with("/"))
Ed Tanousf839dfe2018-11-12 11:11:15 -08002121 {
2122 objectPath.pop_back();
zhanghch058d1b46d2021-04-01 11:18:24 +08002123 handleList(asyncResp, objectPath, 1);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002124 }
2125 else
2126 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002127 handleGet(asyncResp, objectPath, destProperty);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002128 }
Ed Tanous049a0512018-11-01 13:58:42 -07002129 }
2130 return;
2131 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002132 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002133 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002134 handlePut(req, asyncResp, objectPath, destProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002135 return;
2136 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002137 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002138 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002139 handleDelete(asyncResp, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002140 return;
2141 }
Ed Tanous049a0512018-11-01 13:58:42 -07002142
zhanghch058d1b46d2021-04-01 11:18:24 +08002143 setErrorResponse(asyncResp->res,
2144 boost::beast::http::status::method_not_allowed,
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002145 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002146}
2147
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002148inline void handleBusSystemPost(
2149 const crow::Request& req,
2150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2151 const std::string& processName, const std::string& requestedPath)
Ed Tanous1656b292022-05-04 11:33:42 -07002152{
2153 std::vector<std::string> strs;
Ed Tanous50ebd4a2023-01-19 19:03:17 -08002154
2155 bmcweb::split(strs, requestedPath, '/');
Ed Tanous1656b292022-05-04 11:33:42 -07002156 std::string objectPath;
2157 std::string interfaceName;
2158 std::string methodName;
2159 auto it = strs.begin();
2160 if (it == strs.end())
2161 {
2162 objectPath = "/";
2163 }
2164 while (it != strs.end())
2165 {
2166 // Check if segment contains ".". If it does, it must be an
2167 // interface
2168 if (it->find(".") != std::string::npos)
2169 {
2170 break;
2171 // This check is necessary as the trailing slash gets
2172 // parsed as part of our <path> specifier above, which
2173 // causes the normal trailing backslash redirector to
2174 // fail.
2175 }
2176 if (!it->empty())
2177 {
2178 objectPath += "/" + *it;
2179 }
2180 it++;
2181 }
2182 if (it != strs.end())
2183 {
2184 interfaceName = *it;
2185 it++;
2186
2187 // after interface, we might have a method name
2188 if (it != strs.end())
2189 {
2190 methodName = *it;
2191 it++;
2192 }
2193 }
2194 if (it != strs.end())
2195 {
2196 // if there is more levels past the method name, something
2197 // went wrong, return not found
2198 asyncResp->res.result(boost::beast::http::status::not_found);
2199 return;
2200 }
2201 if (interfaceName.empty())
2202 {
2203 crow::connections::systemBus->async_method_call(
2204 [asyncResp, processName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002205 objectPath](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002206 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002207 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002208 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002209 BMCWEB_LOG_ERROR(
2210 "Introspect call failed with error: {} on process: {} path: {}",
2211 ec.message(), processName, objectPath);
2212 return;
2213 }
2214 tinyxml2::XMLDocument doc;
2215
2216 doc.Parse(introspectXml.c_str());
2217 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2218 if (pRoot == nullptr)
2219 {
2220 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2221 processName, objectPath);
2222 asyncResp->res.jsonValue["status"] = "XML parse error";
2223 asyncResp->res.result(
2224 boost::beast::http::status::internal_server_error);
2225 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002226 }
2227
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002228 BMCWEB_LOG_DEBUG("{}", introspectXml);
2229 asyncResp->res.jsonValue["status"] = "ok";
2230 asyncResp->res.jsonValue["bus_name"] = processName;
2231 asyncResp->res.jsonValue["object_path"] = objectPath;
2232
2233 nlohmann::json& interfacesArray =
2234 asyncResp->res.jsonValue["interfaces"];
2235 interfacesArray = nlohmann::json::array();
2236 tinyxml2::XMLElement* interface =
2237 pRoot->FirstChildElement("interface");
2238
2239 while (interface != nullptr)
2240 {
2241 const char* ifaceName = interface->Attribute("name");
2242 if (ifaceName != nullptr)
2243 {
2244 nlohmann::json::object_t interfaceObj;
2245 interfaceObj["name"] = ifaceName;
2246 interfacesArray.emplace_back(std::move(interfaceObj));
2247 }
2248
2249 interface = interface->NextSiblingElement("interface");
2250 }
2251 },
Ed Tanous1656b292022-05-04 11:33:42 -07002252 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2253 "Introspect");
2254 }
2255 else if (methodName.empty())
2256 {
2257 crow::connections::systemBus->async_method_call(
2258 [asyncResp, processName, objectPath,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002259 interfaceName](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002260 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002261 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002262 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002263 BMCWEB_LOG_ERROR(
2264 "Introspect call failed with error: {} on process: {} path: {}",
2265 ec.message(), processName, objectPath);
2266 return;
2267 }
2268 tinyxml2::XMLDocument doc;
2269
2270 doc.Parse(introspectXml.data(), introspectXml.size());
2271 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2272 if (pRoot == nullptr)
2273 {
2274 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2275 processName, objectPath);
2276 asyncResp->res.result(
2277 boost::beast::http::status::internal_server_error);
2278 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002279 }
Ed Tanous14766872022-03-15 10:44:42 -07002280
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002281 asyncResp->res.jsonValue["status"] = "ok";
2282 asyncResp->res.jsonValue["bus_name"] = processName;
2283 asyncResp->res.jsonValue["interface"] = interfaceName;
2284 asyncResp->res.jsonValue["object_path"] = objectPath;
Ed Tanous1656b292022-05-04 11:33:42 -07002285
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002286 nlohmann::json& methodsArray =
2287 asyncResp->res.jsonValue["methods"];
2288 methodsArray = nlohmann::json::array();
2289
2290 nlohmann::json& signalsArray =
2291 asyncResp->res.jsonValue["signals"];
2292 signalsArray = nlohmann::json::array();
2293
2294 nlohmann::json& propertiesObj =
2295 asyncResp->res.jsonValue["properties"];
2296 propertiesObj = nlohmann::json::object();
2297
2298 // if we know we're the only call, build the
2299 // json directly
2300 tinyxml2::XMLElement* interface =
2301 pRoot->FirstChildElement("interface");
2302 while (interface != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002303 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002304 const char* ifaceName = interface->Attribute("name");
2305
2306 if (ifaceName != nullptr && ifaceName == interfaceName)
Ed Tanous1656b292022-05-04 11:33:42 -07002307 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002308 break;
Ed Tanous002d39b2022-05-31 08:59:27 -07002309 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002310
2311 interface = interface->NextSiblingElement("interface");
2312 }
2313 if (interface == nullptr)
2314 {
2315 // if we got to the end of the list and
2316 // never found a match, throw 404
2317 asyncResp->res.result(
2318 boost::beast::http::status::not_found);
2319 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002320 }
2321
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002322 tinyxml2::XMLElement* methods =
2323 interface->FirstChildElement("method");
2324 while (methods != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002325 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002326 nlohmann::json argsArray = nlohmann::json::array();
2327 tinyxml2::XMLElement* arg =
2328 methods->FirstChildElement("arg");
2329 while (arg != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002330 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002331 nlohmann::json thisArg;
2332 for (const char* fieldName : std::array<const char*, 3>{
2333 "name", "direction", "type"})
2334 {
2335 const char* fieldValue = arg->Attribute(fieldName);
2336 if (fieldValue != nullptr)
2337 {
2338 thisArg[fieldName] = fieldValue;
2339 }
2340 }
2341 argsArray.emplace_back(std::move(thisArg));
2342 arg = arg->NextSiblingElement("arg");
Ed Tanous002d39b2022-05-31 08:59:27 -07002343 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002344
2345 const char* name = methods->Attribute("name");
2346 if (name != nullptr)
2347 {
2348 std::string uri;
2349 uri.reserve(14 + processName.size() +
2350 objectPath.size() + interfaceName.size() +
2351 strlen(name));
2352 uri += "/bus/system/";
2353 uri += processName;
2354 uri += objectPath;
2355 uri += "/";
2356 uri += interfaceName;
2357 uri += "/";
2358 uri += name;
2359
2360 nlohmann::json::object_t object;
2361 object["name"] = name;
2362 object["uri"] = std::move(uri);
2363 object["args"] = argsArray;
2364
2365 methodsArray.emplace_back(std::move(object));
2366 }
2367 methods = methods->NextSiblingElement("method");
Ed Tanous002d39b2022-05-31 08:59:27 -07002368 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002369 tinyxml2::XMLElement* signals =
2370 interface->FirstChildElement("signal");
2371 while (signals != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002372 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002373 nlohmann::json argsArray = nlohmann::json::array();
2374
2375 tinyxml2::XMLElement* arg =
2376 signals->FirstChildElement("arg");
2377 while (arg != nullptr)
2378 {
2379 const char* name = arg->Attribute("name");
2380 const char* type = arg->Attribute("type");
2381 if (name != nullptr && type != nullptr)
2382 {
2383 nlohmann::json::object_t params;
2384 params["name"] = name;
2385 params["type"] = type;
2386 argsArray.push_back(std::move(params));
2387 }
2388 arg = arg->NextSiblingElement("arg");
2389 }
2390 const char* name = signals->Attribute("name");
2391 if (name != nullptr)
2392 {
2393 nlohmann::json::object_t object;
2394 object["name"] = name;
2395 object["args"] = argsArray;
2396 signalsArray.emplace_back(std::move(object));
2397 }
2398
2399 signals = signals->NextSiblingElement("signal");
Ed Tanous002d39b2022-05-31 08:59:27 -07002400 }
2401
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002402 tinyxml2::XMLElement* property =
2403 interface->FirstChildElement("property");
2404 while (property != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002405 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002406 const char* name = property->Attribute("name");
2407 const char* type = property->Attribute("type");
2408 if (type != nullptr && name != nullptr)
2409 {
2410 sdbusplus::message_t m =
2411 crow::connections::systemBus->new_method_call(
2412 processName.c_str(), objectPath.c_str(),
2413 "org.freedesktop."
2414 "DBus."
2415 "Properties",
2416 "Get");
2417 m.append(interfaceName, name);
2418 nlohmann::json& propertyItem = propertiesObj[name];
2419 crow::connections::systemBus->async_send(
2420 m, [&propertyItem,
2421 asyncResp](const boost::system::error_code& ec2,
2422 sdbusplus::message_t& msg) {
2423 if (ec2)
2424 {
2425 return;
2426 }
Ed Tanous1656b292022-05-04 11:33:42 -07002427
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002428 int r =
2429 convertDBusToJSON("v", msg, propertyItem);
2430 if (r < 0)
2431 {
2432 BMCWEB_LOG_ERROR(
2433 "Couldn't convert vector to json");
2434 }
2435 });
2436 }
2437 property = property->NextSiblingElement("property");
Ed Tanous1656b292022-05-04 11:33:42 -07002438 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002439 },
Ed Tanous1656b292022-05-04 11:33:42 -07002440 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2441 "Introspect");
2442 }
2443 else
2444 {
2445 if (req.method() != boost::beast::http::verb::post)
2446 {
2447 asyncResp->res.result(boost::beast::http::status::not_found);
2448 return;
2449 }
2450
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002451 nlohmann::json requestDbusData;
2452 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2453 if (ret == JsonParseResult::BadContentType)
Ed Tanous1656b292022-05-04 11:33:42 -07002454 {
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002455 setErrorResponse(asyncResp->res,
2456 boost::beast::http::status::unsupported_media_type,
2457 invalidContentType, unsupportedMediaMsg);
Ed Tanous1656b292022-05-04 11:33:42 -07002458 return;
2459 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002460 if (ret != JsonParseResult::Success)
2461 {
2462 setErrorResponse(asyncResp->res,
2463 boost::beast::http::status::bad_request,
2464 noJsonDesc, badReqMsg);
2465 return;
2466 }
2467
Ed Tanous1656b292022-05-04 11:33:42 -07002468 if (!requestDbusData.is_array())
2469 {
2470 asyncResp->res.result(boost::beast::http::status::bad_request);
2471 return;
2472 }
Lei YU28dd5ca2023-03-17 13:17:05 +08002473 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1656b292022-05-04 11:33:42 -07002474
2475 transaction->path = objectPath;
2476 transaction->methodName = methodName;
2477 transaction->arguments = std::move(requestDbusData);
2478
2479 findActionOnInterface(transaction, processName);
2480 }
2481}
2482
Ed Tanous23a21a12020-07-25 04:45:05 +00002483inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002484{
2485 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous432a8902021-06-14 15:28:56 -07002486 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002487 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002488 [](const crow::Request&,
2489 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002490 nlohmann::json::array_t buses;
2491 nlohmann::json& bus = buses.emplace_back();
2492 bus["name"] = "system";
2493 asyncResp->res.jsonValue["busses"] = std::move(buses);
2494 asyncResp->res.jsonValue["status"] = "ok";
2495 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002496
2497 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous432a8902021-06-14 15:28:56 -07002498 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002499 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002500 [](const crow::Request&,
2501 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002502 auto myCallback = [asyncResp](
2503 const boost::system::error_code& ec,
zhanghch058d1b46d2021-04-01 11:18:24 +08002504 std::vector<std::string>& names) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002505 if (ec)
2506 {
2507 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
2508 asyncResp->res.result(
2509 boost::beast::http::status::internal_server_error);
2510 }
2511 else
2512 {
2513 std::ranges::sort(names);
2514 asyncResp->res.jsonValue["status"] = "ok";
2515 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2516 for (const auto& name : names)
2517 {
2518 nlohmann::json::object_t object;
2519 object["name"] = name;
2520 objectsSub.emplace_back(std::move(object));
2521 }
2522 }
2523 };
2524 crow::connections::systemBus->async_method_call(
2525 std::move(myCallback), "org.freedesktop.DBus", "/",
2526 "org.freedesktop.DBus", "ListNames");
2527 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002528
2529 BMCWEB_ROUTE(app, "/list/")
Ed Tanous432a8902021-06-14 15:28:56 -07002530 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002531 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002532 [](const crow::Request&,
2533 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002534 handleList(asyncResp, "/");
2535 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002536
2537 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002538 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002539 .methods(boost::beast::http::verb::get)(
2540 [](const crow::Request& req,
2541 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002542 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002543 std::string objectPath = "/xyz/" + path;
2544 handleDBusUrl(req, asyncResp, objectPath);
2545 });
zhanghch058d1b46d2021-04-01 11:18:24 +08002546
2547 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002548 .privileges({{"ConfigureComponents", "ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002549 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2550 boost::beast::http::verb::delete_)(
2551 [](const crow::Request& req,
2552 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2553 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002554 std::string objectPath = "/xyz/" + path;
2555 handleDBusUrl(req, asyncResp, objectPath);
2556 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002557
Ed Tanous049a0512018-11-01 13:58:42 -07002558 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002559 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002560 .methods(boost::beast::http::verb::get)(
2561 [](const crow::Request& req,
2562 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2563 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002564 std::string objectPath = "/org/" + path;
2565 handleDBusUrl(req, asyncResp, objectPath);
2566 });
Tanousf00032d2018-11-05 01:18:10 -03002567
2568 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002569 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002570 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2571 boost::beast::http::verb::delete_)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002572 [](const crow::Request& req,
2573 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002574 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002575 std::string objectPath = "/org/" + path;
2576 handleDBusUrl(req, asyncResp, objectPath);
2577 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002578
Ed Tanous1abe55e2018-09-05 08:30:59 -07002579 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002580 .privileges({{"ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002581 .methods(boost::beast::http::verb::get)(
2582 [](const crow::Request&,
2583 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2584 const std::string& dumpId) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002585 if (!validateFilename(dumpId))
2586 {
2587 asyncResp->res.result(
2588 boost::beast::http::status::bad_request);
2589 return;
2590 }
2591 std::filesystem::path loc(
2592 "/var/lib/phosphor-debug-collector/dumps");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002593
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002594 loc /= dumpId;
Ramesh Iyyard9207042019-07-05 08:04:42 -05002595
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002596 if (!std::filesystem::exists(loc) ||
2597 !std::filesystem::is_directory(loc))
2598 {
2599 BMCWEB_LOG_ERROR("{}Not found", loc.string());
2600 asyncResp->res.result(
2601 boost::beast::http::status::not_found);
2602 return;
2603 }
2604 std::filesystem::directory_iterator files(loc);
zhanghch058d1b46d2021-04-01 11:18:24 +08002605
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002606 for (const auto& file : files)
2607 {
Myung Baed51c61b2024-09-13 10:35:34 -05002608 if (asyncResp->res.openFile(file) !=
2609 crow::OpenCode::Success)
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002610 {
2611 continue;
2612 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002613
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002614 asyncResp->res.addHeader(
2615 boost::beast::http::field::content_type,
2616 "application/octet-stream");
zhanghch058d1b46d2021-04-01 11:18:24 +08002617
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002618 // Assuming only one dump file will be present in the dump
2619 // id directory
2620 std::string dumpFileName = file.path().filename().string();
zhanghch058d1b46d2021-04-01 11:18:24 +08002621
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002622 // Filename should be in alphanumeric, dot and underscore
2623 // Its based on phosphor-debug-collector application
2624 // dumpfile format
2625 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2626 if (!std::regex_match(dumpFileName, dumpFileRegex))
2627 {
2628 BMCWEB_LOG_ERROR("Invalid dump filename {}",
2629 dumpFileName);
2630 asyncResp->res.result(
2631 boost::beast::http::status::not_found);
2632 return;
2633 }
2634 std::string contentDispositionParam =
2635 "attachment; filename=\"" + dumpFileName + "\"";
2636
2637 asyncResp->res.addHeader(
2638 boost::beast::http::field::content_disposition,
2639 contentDispositionParam);
2640
2641 return;
2642 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002643 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanousad18f072018-11-14 14:07:48 -08002644 return;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002645 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002646
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002647 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002648 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002649
2650 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002651 [](const crow::Request&,
2652 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +00002653 const std::string& connection) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002654 introspectObjects(connection, "/", asyncResp);
2655 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002656
2657 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002658 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous1656b292022-05-04 11:33:42 -07002659 .methods(boost::beast::http::verb::get,
2660 boost::beast::http::verb::post)(handleBusSystemPost);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002661}
2662} // namespace openbmc_mapper
2663} // namespace crow