blob: 9a3a6321bcfe38b17667a43d55870c5625cb4702 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080016#include "app.hpp"
17#include "async_resp.hpp"
Ed Tanous95c63072024-03-26 13:19:52 -070018#include "boost_formatters.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080019#include "dbus_singleton.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +080020#include "dbus_utility.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000021#include "http_request.hpp"
22#include "http_response.hpp"
Ed Tanous95c63072024-03-26 13:19:52 -070023#include "json_formatters.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000024#include "logging.hpp"
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010025#include "parsing.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000026#include "routing.hpp"
Ed Tanous50ebd4a2023-01-19 19:03:17 -080027#include "str_utility.hpp"
Nan Zhoud5c80ad2022-07-11 01:16:31 +000028
29#include <systemd/sd-bus-protocol.h>
30#include <systemd/sd-bus.h>
Ed Tanous911ac312017-08-15 09:37:42 -070031#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070032
Nan Zhoud5c80ad2022-07-11 01:16:31 +000033#include <boost/beast/http/status.hpp>
34#include <boost/beast/http/verb.hpp>
35#include <boost/container/flat_map.hpp>
36#include <boost/container/vector.hpp>
George Liue99073f2022-12-09 11:06:16 +080037#include <boost/system/error_code.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000038#include <nlohmann/json.hpp>
39#include <sdbusplus/asio/connection.hpp>
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +020040#include <sdbusplus/asio/property.hpp>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000041#include <sdbusplus/exception.hpp>
42#include <sdbusplus/message.hpp>
43#include <sdbusplus/message/native_types.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044
Nan Zhoud5c80ad2022-07-11 01:16:31 +000045#include <algorithm>
46#include <array>
47#include <cerrno>
48#include <cstdint>
49#include <cstring>
James Feist4418c7f2019-04-15 11:09:15 -070050#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070051#include <fstream>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000052#include <functional>
53#include <initializer_list>
54#include <iterator>
55#include <limits>
56#include <map>
57#include <memory>
Ed Tanous3544d2a2023-08-06 18:12:20 -070058#include <ranges>
Ramesh Iyyard9207042019-07-05 08:04:42 -050059#include <regex>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000060#include <string>
61#include <string_view>
62#include <type_traits>
Ed Tanousb5a76932020-09-29 16:16:58 -070063#include <utility>
Nan Zhoud5c80ad2022-07-11 01:16:31 +000064#include <variant>
65#include <vector>
66
67// IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp>
68// IWYU pragma: no_include <boost/system/detail/error_code.hpp>
69// IWYU pragma: no_include <boost/system/detail/error_category.hpp>
70// IWYU pragma: no_include <errno.h>
71// IWYU pragma: no_include <string.h>
72// IWYU pragma: no_include <ext/alloc_traits.h>
73// IWYU pragma: no_include <exception>
74// IWYU pragma: no_include <boost/type_index/type_index_facade.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070075
Ed Tanous1abe55e2018-09-05 08:30:59 -070076namespace crow
77{
78namespace openbmc_mapper
79{
Ed Tanous23a21a12020-07-25 04:45:05 +000080const constexpr char* notFoundMsg = "404 Not Found";
81const constexpr char* badReqMsg = "400 Bad Request";
82const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
83const constexpr char* forbiddenMsg = "403 Forbidden";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010084const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
Ed Tanous23a21a12020-07-25 04:45:05 +000085const constexpr char* methodFailedMsg = "500 Method Call Failed";
86const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
87const constexpr char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060088 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous23a21a12020-07-25 04:45:05 +000089const constexpr char* propNotFoundDesc =
90 "The specified property cannot be found";
91const constexpr char* noJsonDesc = "No JSON object could be decoded";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010092const constexpr char* invalidContentType =
93 "Content-type header is missing or invalid";
Ed Tanous23a21a12020-07-25 04:45:05 +000094const constexpr char* methodNotFoundDesc =
95 "The specified method cannot be found";
96const constexpr char* methodNotAllowedDesc = "Method not allowed";
97const constexpr char* forbiddenPropDesc =
98 "The specified property cannot be created";
99const constexpr char* forbiddenResDesc =
100 "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -0600101
Josh Lehan482c45a2022-03-29 17:10:44 -0700102inline bool validateFilename(const std::string& filename)
103{
Ed Tanous4b242742023-05-11 09:51:51 -0700104 static std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
Josh Lehan482c45a2022-03-29 17:10:44 -0700105
106 return std::regex_match(filename, validFilename);
107}
108
Ed Tanous23a21a12020-07-25 04:45:05 +0000109inline void setErrorResponse(crow::Response& res,
110 boost::beast::http::status result,
Ed Tanous26ccae32023-02-16 10:28:44 -0800111 const std::string& desc, std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -0600112{
113 res.result(result);
Ed Tanous14766872022-03-15 10:44:42 -0700114 res.jsonValue["data"]["description"] = desc;
115 res.jsonValue["message"] = msg;
116 res.jsonValue["status"] = "error";
Matt Spinler2ae60092018-12-06 10:35:36 -0600117}
118
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400119inline void introspectObjects(
120 const std::string& processName, const std::string& objectPath,
121 const std::shared_ptr<bmcweb::AsyncResp>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700123 if (transaction->res.jsonValue.is_null())
124 {
Ed Tanous14766872022-03-15 10:44:42 -0700125 transaction->res.jsonValue["status"] = "ok";
126 transaction->res.jsonValue["bus_name"] = processName;
127 transaction->res.jsonValue["objects"] = nlohmann::json::array();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700128 }
129
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700131 [transaction, processName{std::string(processName)},
132 objectPath{std::string(objectPath)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800133 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000134 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400135 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700136 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400137 BMCWEB_LOG_ERROR(
138 "Introspect call failed with error: {} on process: {} path: {}",
139 ec.message(), processName, objectPath);
140 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400142 nlohmann::json::object_t object;
143 object["path"] = objectPath;
144
145 transaction->res.jsonValue["objects"].emplace_back(
146 std::move(object));
147
148 tinyxml2::XMLDocument doc;
149
150 doc.Parse(introspectXml.c_str());
151 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
152 if (pRoot == nullptr)
153 {
154 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
155 processName, objectPath);
156 }
157 else
158 {
159 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
160 while (node != nullptr)
161 {
162 const char* childPath = node->Attribute("name");
163 if (childPath != nullptr)
164 {
165 std::string newpath;
166 if (objectPath != "/")
167 {
168 newpath += objectPath;
169 }
170 newpath += std::string("/") + childPath;
171 // introspect the subobjects as well
172 introspectObjects(processName, newpath, transaction);
173 }
174
175 node = node->NextSiblingElement("node");
176 }
177 }
178 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700179 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700181}
Ed Tanous64530012018-02-06 17:08:16 -0800182
Ed Tanous23a21a12020-07-25 04:45:05 +0000183inline void getPropertiesForEnumerate(
184 const std::string& objectPath, const std::string& service,
Ed Tanousb5a76932020-09-29 16:16:58 -0700185 const std::string& interface,
186 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600187{
Ed Tanous62598e32023-07-17 17:06:25 -0700188 BMCWEB_LOG_DEBUG("getPropertiesForEnumerate {} {} {}", objectPath, service,
189 interface);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600190
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200191 sdbusplus::asio::getAllProperties(
192 *crow::connections::systemBus, service, objectPath, interface,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800193 [asyncResp, objectPath, service,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800194 interface](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800195 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400196 if (ec)
197 {
198 BMCWEB_LOG_ERROR(
199 "GetAll on path {} iface {} service {} failed with code {}",
200 objectPath, interface, service, ec);
201 return;
202 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600203
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400204 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
205 nlohmann::json& objectJson = dataJson[objectPath];
206 if (objectJson.is_null())
207 {
208 objectJson = nlohmann::json::object();
209 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600210
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400211 for (const auto& [name, value] : propertiesList)
212 {
213 nlohmann::json& propertyJson = objectJson[name];
214 std::visit(
215 [&propertyJson](auto&& val) {
216 if constexpr (std::is_same_v<
217 std::decay_t<decltype(val)>,
218 sdbusplus::message::unix_fd>)
219 {
220 propertyJson = val.fd;
221 }
222 else
223 {
224 propertyJson = val;
225 }
226 },
227 value);
228 }
229 });
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600230}
231
232// Find any results that weren't picked up by ObjectManagers, to be
233// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000234inline void findRemainingObjectsForEnumerate(
Ed Tanousb5a76932020-09-29 16:16:58 -0700235 const std::string& objectPath,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800236 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
Ed Tanousb5a76932020-09-29 16:16:58 -0700237 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600238{
Ed Tanous62598e32023-07-17 17:06:25 -0700239 BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate");
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500240 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600241
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500242 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600243 {
244 if (path == objectPath)
245 {
246 // An enumerate does not return the target path's properties
247 continue;
248 }
249 if (dataJson.find(path) == dataJson.end())
250 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500251 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600252 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500253 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600254 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700255 if (!interface.starts_with("org.freedesktop.DBus"))
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600256 {
257 getPropertiesForEnumerate(path, service, interface,
258 asyncResp);
259 }
260 }
261 }
262 }
263 }
264}
265
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600266struct InProgressEnumerateData
267{
zhanghch058d1b46d2021-04-01 11:18:24 +0800268 InProgressEnumerateData(
269 const std::string& objectPathIn,
270 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400271 objectPath(objectPathIn), asyncResp(asyncRespIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500272 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600273
274 ~InProgressEnumerateData()
275 {
Ed Tanous24b2fe82022-01-06 12:45:54 -0800276 try
277 {
278 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
279 }
280 catch (...)
281 {
Ed Tanous62598e32023-07-17 17:06:25 -0700282 BMCWEB_LOG_CRITICAL(
283 "findRemainingObjectsForEnumerate threw exception");
Ed Tanous24b2fe82022-01-06 12:45:54 -0800284 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600285 }
286
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800287 InProgressEnumerateData(const InProgressEnumerateData&) = delete;
288 InProgressEnumerateData(InProgressEnumerateData&&) = delete;
289 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
290 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600291 const std::string objectPath;
Ed Tanousb9d36b42022-02-26 21:42:46 -0800292 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600293 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
294};
295
Ed Tanous23a21a12020-07-25 04:45:05 +0000296inline void getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000297 const std::string& objectName, const std::string& objectManagerPath,
298 const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700299 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300{
Ed Tanous62598e32023-07-17 17:06:25 -0700301 BMCWEB_LOG_DEBUG(
302 "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}",
303 objectName, objectManagerPath, connectionName);
George Liu5eb468d2023-06-20 17:03:24 +0800304 sdbusplus::message::object_path path(objectManagerPath);
305 dbus::utility::getManagedObjects(
306 connectionName, path,
Ed Tanous81ce6092020-12-17 16:54:55 +0000307 [transaction, objectName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800308 connectionName](const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000309 const dbus::utility::ManagedObjectType& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400310 if (ec)
Ed Tanous049a0512018-11-01 13:58:42 -0700311 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400312 BMCWEB_LOG_ERROR(
313 "GetManagedObjects on path {} on connection {} failed with code {}",
314 objectName, connectionName, ec);
315 return;
316 }
317
318 nlohmann::json& dataJson =
319 transaction->asyncResp->res.jsonValue["data"];
320
321 for (const auto& objectPath : objects)
322 {
323 if (objectPath.first.str.starts_with(objectName))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400325 BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str);
326 nlohmann::json& objectJson = dataJson[objectPath.first.str];
327 if (objectJson.is_null())
328 {
329 objectJson = nlohmann::json::object();
330 }
331 for (const auto& interface : objectPath.second)
332 {
333 for (const auto& property : interface.second)
334 {
335 nlohmann::json& propertyJson =
336 objectJson[property.first];
337 std::visit(
338 [&propertyJson](auto&& val) {
339 if constexpr (
340 std::is_same_v<
341 std::decay_t<decltype(val)>,
342 sdbusplus::message::unix_fd>)
343 {
344 propertyJson = val.fd;
345 }
346 else
347 {
348 propertyJson = val;
349 }
350 },
351 property.second);
352 }
353 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700354 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500355 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700356 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400357 if (interface.first == "org.freedesktop.DBus.ObjectManager")
Ed Tanous049a0512018-11-01 13:58:42 -0700358 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400359 getManagedObjectsForEnumerate(
360 objectPath.first.str, objectPath.first.str,
361 connectionName, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700362 }
363 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700364 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400365 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700366}
367
Ed Tanous23a21a12020-07-25 04:45:05 +0000368inline void findObjectManagerPathForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000369 const std::string& objectName, const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700370 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700371{
Ed Tanous62598e32023-07-17 17:06:25 -0700372 BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}",
373 objectName, connectionName);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700374 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000375 [transaction, objectName, connectionName](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800376 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800377 const dbus::utility::MapperGetAncestorsResponse& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400378 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700379 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400380 BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}",
381 objectName, ec);
382 return;
383 }
384
385 for (const auto& pathGroup : objects)
386 {
387 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700388 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400389 if (connectionGroup.first == connectionName)
390 {
391 // Found the object manager path for this resource.
392 getManagedObjectsForEnumerate(
393 objectName, pathGroup.first, connectionName,
394 transaction);
395 return;
396 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700397 }
398 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400399 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700400 "xyz.openbmc_project.ObjectMapper",
401 "/xyz/openbmc_project/object_mapper",
Ed Tanous81ce6092020-12-17 16:54:55 +0000402 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500403 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700404}
Ed Tanous64530012018-02-06 17:08:16 -0800405
Ed Tanous7c091622019-05-23 11:42:36 -0700406// Uses GetObject to add the object info about the target /enumerate path to
407// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600408// target path, and then continues on enumerating the rest of the tree.
Ed Tanousb5a76932020-09-29 16:16:58 -0700409inline void getObjectAndEnumerate(
410 const std::shared_ptr<InProgressEnumerateData>& transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600411{
George Liu2b731192023-01-11 16:27:13 +0800412 dbus::utility::getDbusObject(
413 transaction->objectPath, {},
414 [transaction](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800415 const dbus::utility::MapperGetObject& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400416 if (ec)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600417 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400418 BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}",
419 transaction->objectPath, ec);
420 return;
421 }
422
423 BMCWEB_LOG_DEBUG("GetObject for {} has {} entries",
424 transaction->objectPath, objects.size());
425 if (!objects.empty())
426 {
427 transaction->subtree->emplace_back(transaction->objectPath,
428 objects);
429 }
430
431 // Map indicating connection name, and the path where the object
432 // manager exists
433 boost::container::flat_map<
434 std::string, std::string, std::less<>,
435 std::vector<std::pair<std::string, std::string>>>
436 connections;
437
438 for (const auto& object : *(transaction->subtree))
439 {
440 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600441 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400442 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600443 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400444 BMCWEB_LOG_DEBUG("{} has interface {}",
445 connection.first, interface);
446 if (interface == "org.freedesktop.DBus.ObjectManager")
447 {
448 BMCWEB_LOG_DEBUG("found object manager path {}",
449 object.first);
450 connections[connection.first] = object.first;
451 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600452 }
453 }
454 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400455 BMCWEB_LOG_DEBUG("Got {} connections", connections.size());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600456
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400457 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600458 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400459 // If we already know where the object manager is, we don't
460 // need to search for it, we can call directly in to
461 // getManagedObjects
462 if (!connection.second.empty())
463 {
464 getManagedObjectsForEnumerate(
465 transaction->objectPath, connection.second,
466 connection.first, transaction);
467 }
468 else
469 {
470 // otherwise we need to find the object manager path
471 // before we can continue
472 findObjectManagerPathForEnumerate(
473 transaction->objectPath, connection.first, transaction);
474 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600475 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400476 });
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600477}
Ed Tanous64530012018-02-06 17:08:16 -0800478
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700479// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480struct InProgressActionData
481{
Lei YU28dd5ca2023-03-17 13:17:05 +0800482 explicit InProgressActionData(
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400483 const std::shared_ptr<bmcweb::AsyncResp>& res) : asyncResp(res)
Ed Tanous23a21a12020-07-25 04:45:05 +0000484 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700485 ~InProgressActionData()
486 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600487 // Methods could have been called across different owners
488 // and interfaces, where some calls failed and some passed.
489 //
490 // The rules for this are:
491 // * if no method was called - error
492 // * if a method failed and none passed - error
493 // (converse: if at least one method passed - OK)
494 // * for the method output:
495 // * if output processing didn't fail, return the data
496
497 // Only deal with method returns if nothing failed earlier
Lei YU28dd5ca2023-03-17 13:17:05 +0800498 if (asyncResp->res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600500 if (!methodPassed)
501 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500502 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600503 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800504 setErrorResponse(asyncResp->res,
505 boost::beast::http::status::not_found,
Matt Spinler16caaee2019-01-15 11:40:34 -0600506 methodNotFoundDesc, notFoundMsg);
507 }
508 }
509 else
510 {
511 if (outputFailed)
512 {
513 setErrorResponse(
Lei YU28dd5ca2023-03-17 13:17:05 +0800514 asyncResp->res,
515 boost::beast::http::status::internal_server_error,
Matt Spinler16caaee2019-01-15 11:40:34 -0600516 "Method output failure", methodOutputFailedMsg);
517 }
518 else
519 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800520 asyncResp->res.jsonValue["status"] = "ok";
521 asyncResp->res.jsonValue["message"] = "200 OK";
522 asyncResp->res.jsonValue["data"] = methodResponse;
Matt Spinler16caaee2019-01-15 11:40:34 -0600523 }
524 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700525 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700526 }
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800527 InProgressActionData(const InProgressActionData&) = delete;
528 InProgressActionData(InProgressActionData&&) = delete;
529 InProgressActionData& operator=(const InProgressActionData&) = delete;
530 InProgressActionData& operator=(InProgressActionData&&) = delete;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700531
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500532 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700533 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800534 setErrorResponse(asyncResp->res,
535 boost::beast::http::status::bad_request, desc,
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600536 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700537 }
Lei YU28dd5ca2023-03-17 13:17:05 +0800538 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 std::string path;
540 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600541 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600542 bool methodPassed = false;
543 bool methodFailed = false;
544 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600545 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600546 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700547 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700548};
549
Ed Tanous23a21a12020-07-25 04:45:05 +0000550inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551{
552 std::vector<std::string> ret;
553 if (string.empty())
554 {
555 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700556 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700557 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 int containerDepth = 0;
559
560 for (std::string::const_iterator character = string.begin();
561 character != string.end(); character++)
562 {
563 ret.back() += *character;
564 switch (*character)
565 {
566 case ('a'):
567 break;
568 case ('('):
569 case ('{'):
570 containerDepth++;
571 break;
572 case ('}'):
573 case (')'):
574 containerDepth--;
575 if (containerDepth == 0)
576 {
577 if (character + 1 != string.end())
578 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700579 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700580 }
581 }
582 break;
583 default:
584 if (containerDepth == 0)
585 {
586 if (character + 1 != string.end())
587 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700588 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 }
590 }
591 break;
592 }
593 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600594
595 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700596}
597
Ed Tanous81ce6092020-12-17 16:54:55 +0000598inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
599 const nlohmann::json& inputJson)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600{
601 int r = 0;
Ed Tanous296579b2024-03-11 16:58:24 -0700602 BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType);
Ed Tanous81ce6092020-12-17 16:54:55 +0000603 const std::vector<std::string> argTypes = dbusArgSplit(argType);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700604
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 // Assume a single object for now.
Ed Tanous81ce6092020-12-17 16:54:55 +0000606 const nlohmann::json* j = &inputJson;
607 nlohmann::json::const_iterator jIt = inputJson.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700608
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700610 {
611 // If we are decoding multiple objects, grab the pointer to the
612 // iterator, and increment it for the next loop
613 if (argTypes.size() > 1)
614 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000615 if (jIt == inputJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
617 return -2;
618 }
619 j = &*jIt;
620 jIt++;
621 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500622 const int64_t* intValue = j->get_ptr<const int64_t*>();
623 const std::string* stringValue = j->get_ptr<const std::string*>();
624 const double* doubleValue = j->get_ptr<const double*>();
625 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 int64_t v = 0;
627 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700628
Ed Tanous1abe55e2018-09-05 08:30:59 -0700629 // Do some basic type conversions that make sense. uint can be
630 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700631 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500633 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700634 if (uintValue != nullptr)
635 {
636 v = static_cast<int64_t>(*uintValue);
637 intValue = &v;
638 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 }
Ed Tanous66664f22019-10-11 13:05:49 -0700640 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700641 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500642 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700643 if (uintValue != nullptr)
644 {
645 d = static_cast<double>(*uintValue);
646 doubleValue = &d;
647 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700648 }
Ed Tanous66664f22019-10-11 13:05:49 -0700649 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
Ed Tanous66664f22019-10-11 13:05:49 -0700651 if (intValue != nullptr)
652 {
653 d = static_cast<double>(*intValue);
654 doubleValue = &d;
655 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700656 }
657
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700658 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 {
662 return -1;
663 }
Ed Tanous271584a2019-07-09 16:24:22 -0700664 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500665 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 if (r < 0)
667 {
668 return r;
669 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700670 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
675 return -1;
676 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500677 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
678 (*intValue > std::numeric_limits<int32_t>::max()))
679 {
680 return -ERANGE;
681 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 int32_t i = static_cast<int32_t>(*intValue);
683 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700684 if (r < 0)
685 {
686 return r;
687 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700688 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 {
691 // lots of ways bool could be represented here. Try them all
Ed Tanouse662eae2022-01-25 10:39:19 -0800692 int boolInt = 0;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700693 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500695 if (*intValue == 1)
696 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800697 boolInt = 1;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500698 }
699 else if (*intValue == 0)
700 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800701 boolInt = 0;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500702 }
703 else
704 {
705 return -ERANGE;
706 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700707 }
708 else if (b != nullptr)
709 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600710 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700712 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 {
Ed Tanous18f8f602023-07-18 10:07:23 -0700714 if (!stringValue->empty())
715 {
716 if (stringValue->front() == 't' ||
717 stringValue->front() == 'T')
718 {
719 boolInt = 1;
720 }
721 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 }
723 else
724 {
725 return -1;
726 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700727 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700728 if (r < 0)
729 {
730 return r;
731 }
732 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700733 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700735 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 {
737 return -1;
738 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500739 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
740 (*intValue > std::numeric_limits<int16_t>::max()))
741 {
742 return -ERANGE;
743 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700744 int16_t n = static_cast<int16_t>(*intValue);
745 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 if (r < 0)
747 {
748 return r;
749 }
750 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700751 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700753 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 {
755 return -1;
756 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700757 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 if (r < 0)
759 {
760 return r;
761 }
762 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700763 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500765 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700766 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 {
768 return -1;
769 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000770 if (*uintValue > std::numeric_limits<uint8_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500771 {
772 return -ERANGE;
773 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700774 uint8_t y = static_cast<uint8_t>(*uintValue);
775 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700777 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500779 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700780 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 {
782 return -1;
783 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000784 if (*uintValue > std::numeric_limits<uint16_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500785 {
786 return -ERANGE;
787 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700788 uint16_t q = static_cast<uint16_t>(*uintValue);
789 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700791 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500793 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700794 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 {
796 return -1;
797 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000798 if (*uintValue > std::numeric_limits<uint32_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500799 {
800 return -ERANGE;
801 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700802 uint32_t u = static_cast<uint32_t>(*uintValue);
803 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700805 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500807 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700808 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700809 {
810 return -1;
811 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700812 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700813 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500816 if (doubleValue == nullptr)
817 {
818 return -1;
819 }
820 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
821 (*doubleValue > std::numeric_limits<double>::max()))
822 {
823 return -ERANGE;
824 }
Ed Tanous07900812024-05-06 15:41:30 -0700825 r = sd_bus_message_append_basic(m, argCode[0], doubleValue);
826 if (r < 0)
827 {
828 return r;
829 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700831 else if (argCode.starts_with("a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700833 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700835 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836 if (r < 0)
837 {
838 return r;
839 }
840
Ed Tanous0dfeda62019-10-24 11:21:38 -0700841 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700842 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700843 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844 if (r < 0)
845 {
846 return r;
847 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 }
849 sd_bus_message_close_container(m);
850 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700851 else if (argCode.starts_with("v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700852 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700853 std::string containedType = argCode.substr(1);
Ed Tanous62598e32023-07-17 17:06:25 -0700854 BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}",
855 argCode, containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700856 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700857 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858 if (r < 0)
859 {
860 return r;
861 }
862
Ed Tanous81ce6092020-12-17 16:54:55 +0000863 r = convertJsonToDbus(m, containedType, inputJson);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700864 if (r < 0)
865 {
866 return r;
867 }
868
869 r = sd_bus_message_close_container(m);
870 if (r < 0)
871 {
872 return r;
873 }
874 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700875 else if (argCode.starts_with("(") && argCode.ends_with(")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300877 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700879 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800880 if (r < 0)
881 {
882 return r;
883 }
884
Ed Tanous1abe55e2018-09-05 08:30:59 -0700885 nlohmann::json::const_iterator it = j->begin();
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300886 for (const std::string& argCode2 : dbusArgSplit(containedType))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700887 {
888 if (it == j->end())
889 {
890 return -1;
891 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000892 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893 if (r < 0)
894 {
895 return r;
896 }
897 it++;
898 }
899 r = sd_bus_message_close_container(m);
900 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700901 else if (argCode.starts_with("{") && argCode.ends_with("}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300903 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700905 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800906 if (r < 0)
907 {
908 return r;
909 }
910
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700911 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700912 if (codes.size() != 2)
913 {
914 return -1;
915 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700916 const std::string& keyType = codes[0];
917 const std::string& valueType = codes[1];
Ed Tanous0bdda662023-08-03 17:27:34 -0700918 const nlohmann::json::object_t* arr =
919 j->get_ptr<const nlohmann::json::object_t*>();
920 if (arr == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700921 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700922 return -1;
923 }
924 for (const auto& it : *arr)
925 {
926 r = convertJsonToDbus(m, keyType, it.first);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927 if (r < 0)
928 {
929 return r;
930 }
931
Ed Tanous0bdda662023-08-03 17:27:34 -0700932 r = convertJsonToDbus(m, valueType, it.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700933 if (r < 0)
934 {
935 return r;
936 }
937 }
938 r = sd_bus_message_close_container(m);
939 }
940 else
941 {
942 return -2;
943 }
944 if (r < 0)
945 {
946 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700947 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700948
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 if (argTypes.size() > 1)
950 {
951 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700952 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700953 }
Matt Spinler127ea542019-01-14 11:04:28 -0600954
955 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700956}
957
Matt Spinlerd22a7132019-01-14 12:14:30 -0600958template <typename T>
Patrick Williams59d494e2022-07-22 19:26:55 -0500959int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500960 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600961{
962 T value;
Ed Tanousf79ce6a2024-03-20 12:27:06 -0700963 // When T == char*, this warning fires. Unclear how to resolve
964 // Given that sd-bus takes a void pointer to a char*, and that's
965 // Not something we can fix.
966 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600967 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
968 if (r < 0)
969 {
Ed Tanous62598e32023-07-17 17:06:25 -0700970 BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!",
971 typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -0600972 return r;
973 }
974
975 data = value;
976 return 0;
977}
978
Patrick Williams59d494e2022-07-22 19:26:55 -0500979int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
980 nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600981
Ed Tanous23a21a12020-07-25 04:45:05 +0000982inline int readDictEntryFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -0500983 sdbusplus::message_t& m,
Ed Tanous23a21a12020-07-25 04:45:05 +0000984 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600985{
986 std::vector<std::string> types = dbusArgSplit(typeCode);
987 if (types.size() != 2)
988 {
Ed Tanous62598e32023-07-17 17:06:25 -0700989 BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}",
990 types.size());
Matt Spinler6df8f992019-01-14 12:47:47 -0600991 return -1;
992 }
993
994 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
995 typeCode.c_str());
996 if (r < 0)
997 {
Ed Tanous62598e32023-07-17 17:06:25 -0700998 BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -0600999 return r;
1000 }
1001
1002 nlohmann::json key;
1003 r = convertDBusToJSON(types[0], m, key);
1004 if (r < 0)
1005 {
1006 return r;
1007 }
1008
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001009 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -06001010 if (keyPtr == nullptr)
1011 {
1012 // json doesn't support non-string keys. If we hit this condition,
1013 // convert the result to a string so we can proceed
Ed Tanous71f52d92021-02-19 08:51:17 -08001014 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001015 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -07001016 // in theory this can't fail now, but lets be paranoid about it
1017 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -06001018 if (keyPtr == nullptr)
1019 {
1020 return -1;
1021 }
1022 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001023 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -06001024
1025 r = convertDBusToJSON(types[1], m, value);
1026 if (r < 0)
1027 {
1028 return r;
1029 }
1030
1031 r = sd_bus_message_exit_container(m.get());
1032 if (r < 0)
1033 {
Ed Tanous62598e32023-07-17 17:06:25 -07001034 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001035 return r;
1036 }
1037
1038 return 0;
1039}
1040
Ed Tanous23a21a12020-07-25 04:45:05 +00001041inline int readArrayFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001042 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -06001043{
1044 if (typeCode.size() < 2)
1045 {
Ed Tanous62598e32023-07-17 17:06:25 -07001046 BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode);
Matt Spinler6df8f992019-01-14 12:47:47 -06001047 return -1;
1048 }
1049
1050 std::string containedType = typeCode.substr(1);
1051
1052 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1053 containedType.c_str());
1054 if (r < 0)
1055 {
Ed Tanous62598e32023-07-17 17:06:25 -07001056 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -06001057 return r;
1058 }
1059
Ed Tanous11ba3972022-07-11 09:50:41 -07001060 bool dict = containedType.starts_with("{") && containedType.ends_with("}");
Matt Spinler6df8f992019-01-14 12:47:47 -06001061
1062 if (dict)
1063 {
1064 // Remove the { }
1065 containedType = containedType.substr(1, containedType.size() - 2);
1066 data = nlohmann::json::object();
1067 }
1068 else
1069 {
1070 data = nlohmann::json::array();
1071 }
1072
1073 while (true)
1074 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001075 r = sd_bus_message_at_end(m.get(), 0);
Matt Spinler6df8f992019-01-14 12:47:47 -06001076 if (r < 0)
1077 {
Ed Tanous62598e32023-07-17 17:06:25 -07001078 BMCWEB_LOG_ERROR("sd_bus_message_at_end failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001079 return r;
1080 }
1081
1082 if (r > 0)
1083 {
1084 break;
1085 }
1086
1087 // Dictionaries are only ever seen in an array
1088 if (dict)
1089 {
1090 r = readDictEntryFromMessage(containedType, m, data);
1091 if (r < 0)
1092 {
1093 return r;
1094 }
1095 }
1096 else
1097 {
1098 data.push_back(nlohmann::json());
1099
1100 r = convertDBusToJSON(containedType, m, data.back());
1101 if (r < 0)
1102 {
1103 return r;
1104 }
1105 }
1106 }
1107
1108 r = sd_bus_message_exit_container(m.get());
1109 if (r < 0)
1110 {
Ed Tanous62598e32023-07-17 17:06:25 -07001111 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001112 return r;
1113 }
1114
1115 return 0;
1116}
1117
Ed Tanous23a21a12020-07-25 04:45:05 +00001118inline int readStructFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001119 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001120{
1121 if (typeCode.size() < 3)
1122 {
Ed Tanous62598e32023-07-17 17:06:25 -07001123 BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode);
Matt Spinler75c6c672019-01-14 13:01:46 -06001124 return -1;
1125 }
1126
1127 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1128 std::vector<std::string> types = dbusArgSplit(containedTypes);
1129
1130 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1131 containedTypes.c_str());
1132 if (r < 0)
1133 {
Ed Tanous62598e32023-07-17 17:06:25 -07001134 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler75c6c672019-01-14 13:01:46 -06001135 return r;
1136 }
1137
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001138 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001139 {
1140 data.push_back(nlohmann::json());
1141 r = convertDBusToJSON(type, m, data.back());
1142 if (r < 0)
1143 {
1144 return r;
1145 }
1146 }
1147
1148 r = sd_bus_message_exit_container(m.get());
1149 if (r < 0)
1150 {
Ed Tanous62598e32023-07-17 17:06:25 -07001151 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler75c6c672019-01-14 13:01:46 -06001152 return r;
1153 }
1154 return 0;
1155}
1156
Patrick Williams59d494e2022-07-22 19:26:55 -05001157inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001158{
Ed Tanous543f4402022-01-06 13:12:53 -08001159 const char* containerType = nullptr;
Ed Tanous99131cd2019-10-24 11:12:47 -07001160 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001161 if (r < 0)
1162 {
Ed Tanous62598e32023-07-17 17:06:25 -07001163 BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001164 return r;
1165 }
1166
1167 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1168 containerType);
1169 if (r < 0)
1170 {
Ed Tanous62598e32023-07-17 17:06:25 -07001171 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler89c19702019-01-14 13:13:00 -06001172 return r;
1173 }
1174
1175 r = convertDBusToJSON(containerType, m, data);
1176 if (r < 0)
1177 {
1178 return r;
1179 }
1180
1181 r = sd_bus_message_exit_container(m.get());
1182 if (r < 0)
1183 {
Ed Tanous62598e32023-07-17 17:06:25 -07001184 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001185 return r;
1186 }
1187
1188 return 0;
1189}
1190
Ed Tanous23a21a12020-07-25 04:45:05 +00001191inline int convertDBusToJSON(const std::string& returnType,
Patrick Williams59d494e2022-07-22 19:26:55 -05001192 sdbusplus::message_t& m, nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001193{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001194 int r = 0;
1195 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1196
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001197 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001198 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001199 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001200 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001201 {
1202 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001203 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001204 }
1205
Ed Tanousd4d25792020-09-29 15:15:03 -07001206 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001207 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001208 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001209 if (r < 0)
1210 {
1211 return r;
1212 }
1213 }
1214 else if (typeCode == "b")
1215 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001216 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001217 if (r < 0)
1218 {
1219 return r;
1220 }
1221
Matt Spinlerf39420c2019-01-30 12:57:18 -06001222 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001223 }
1224 else if (typeCode == "u")
1225 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001226 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001227 if (r < 0)
1228 {
1229 return r;
1230 }
1231 }
1232 else if (typeCode == "i")
1233 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001234 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001235 if (r < 0)
1236 {
1237 return r;
1238 }
1239 }
1240 else if (typeCode == "x")
1241 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001242 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001243 if (r < 0)
1244 {
1245 return r;
1246 }
1247 }
1248 else if (typeCode == "t")
1249 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001250 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001251 if (r < 0)
1252 {
1253 return r;
1254 }
1255 }
1256 else if (typeCode == "n")
1257 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001258 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001259 if (r < 0)
1260 {
1261 return r;
1262 }
1263 }
1264 else if (typeCode == "q")
1265 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001266 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001267 if (r < 0)
1268 {
1269 return r;
1270 }
1271 }
1272 else if (typeCode == "y")
1273 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001274 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001275 if (r < 0)
1276 {
1277 return r;
1278 }
1279 }
1280 else if (typeCode == "d")
1281 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001282 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001283 if (r < 0)
1284 {
1285 return r;
1286 }
1287 }
1288 else if (typeCode == "h")
1289 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001290 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001291 if (r < 0)
1292 {
1293 return r;
1294 }
1295 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001296 else if (typeCode.starts_with("a"))
Matt Spinler6df8f992019-01-14 12:47:47 -06001297 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001298 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001299 if (r < 0)
1300 {
1301 return r;
1302 }
1303 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001304 else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
Matt Spinler75c6c672019-01-14 13:01:46 -06001305 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001306 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001307 if (r < 0)
1308 {
1309 return r;
1310 }
1311 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001312 else if (typeCode.starts_with("v"))
Matt Spinler89c19702019-01-14 13:13:00 -06001313 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001314 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001315 if (r < 0)
1316 {
1317 return r;
1318 }
1319 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001320 else
1321 {
Ed Tanous62598e32023-07-17 17:06:25 -07001322 BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001323 return -2;
1324 }
1325 }
1326
Matt Spinler16caaee2019-01-15 11:40:34 -06001327 return 0;
1328}
1329
Ed Tanousb5a76932020-09-29 16:16:58 -07001330inline void handleMethodResponse(
1331 const std::shared_ptr<InProgressActionData>& transaction,
Patrick Williams59d494e2022-07-22 19:26:55 -05001332 sdbusplus::message_t& m, const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001333{
Matt Spinler39a4e392019-01-15 11:53:13 -06001334 nlohmann::json data;
1335
1336 int r = convertDBusToJSON(returnType, m, data);
1337 if (r < 0)
1338 {
1339 transaction->outputFailed = true;
1340 return;
1341 }
1342
1343 if (data.is_null())
1344 {
1345 return;
1346 }
1347
1348 if (transaction->methodResponse.is_null())
1349 {
1350 transaction->methodResponse = std::move(data);
1351 return;
1352 }
1353
1354 // If they're both dictionaries or arrays, merge into one.
1355 // Otherwise, make the results an array with every result
1356 // an entry. Could also just fail in that case, but it
1357 // seems better to get the data back somehow.
Ed Tanous0bdda662023-08-03 17:27:34 -07001358 nlohmann::json::object_t* dataobj =
1359 data.get_ptr<nlohmann::json::object_t*>();
1360 if (transaction->methodResponse.is_object() && dataobj != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001361 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001362 for (auto& obj : *dataobj)
Matt Spinler39a4e392019-01-15 11:53:13 -06001363 {
1364 // Note: Will overwrite the data for a duplicate key
Ed Tanous0bdda662023-08-03 17:27:34 -07001365 transaction->methodResponse.emplace(obj.first,
1366 std::move(obj.second));
Matt Spinler39a4e392019-01-15 11:53:13 -06001367 }
1368 return;
1369 }
1370
Ed Tanous0bdda662023-08-03 17:27:34 -07001371 nlohmann::json::array_t* dataarr = data.get_ptr<nlohmann::json::array_t*>();
1372 if (transaction->methodResponse.is_array() && dataarr != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001373 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001374 for (auto& obj : *dataarr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001375 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001376 transaction->methodResponse.emplace_back(std::move(obj));
Matt Spinler39a4e392019-01-15 11:53:13 -06001377 }
1378 return;
1379 }
1380
1381 if (!transaction->convertedToArray)
1382 {
1383 // They are different types. May as well turn them into an array
1384 nlohmann::json j = std::move(transaction->methodResponse);
1385 transaction->methodResponse = nlohmann::json::array();
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001386 transaction->methodResponse.emplace_back(std::move(j));
1387 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001388 transaction->convertedToArray = true;
1389 }
1390 else
1391 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001392 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001393 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001394}
1395
Ed Tanousb5a76932020-09-29 16:16:58 -07001396inline void findActionOnInterface(
1397 const std::shared_ptr<InProgressActionData>& transaction,
1398 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001399{
Ed Tanous62598e32023-07-17 17:06:25 -07001400 BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001401 crow::connections::systemBus->async_method_call(
1402 [transaction, connectionName{std::string(connectionName)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001403 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001404 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001405 BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml);
1406 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001407 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001408 BMCWEB_LOG_ERROR(
1409 "Introspect call failed with error: {} on process: {}",
1410 ec.message(), connectionName);
1411 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001412 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001413 tinyxml2::XMLDocument doc;
1414
1415 doc.Parse(introspectXml.data(), introspectXml.size());
1416 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1417 if (pRoot == nullptr)
1418 {
1419 BMCWEB_LOG_ERROR("XML document failed to parse {}",
1420 connectionName);
1421 return;
1422 }
1423 tinyxml2::XMLElement* interfaceNode =
1424 pRoot->FirstChildElement("interface");
1425 while (interfaceNode != nullptr)
1426 {
1427 const char* thisInterfaceName =
1428 interfaceNode->Attribute("name");
1429 if (thisInterfaceName != nullptr)
1430 {
1431 if (!transaction->interfaceName.empty() &&
1432 (transaction->interfaceName != thisInterfaceName))
1433 {
1434 interfaceNode =
1435 interfaceNode->NextSiblingElement("interface");
1436 continue;
1437 }
1438
1439 tinyxml2::XMLElement* methodNode =
1440 interfaceNode->FirstChildElement("method");
1441 while (methodNode != nullptr)
1442 {
1443 const char* thisMethodName =
1444 methodNode->Attribute("name");
1445 BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName);
1446 if (thisMethodName != nullptr &&
1447 thisMethodName == transaction->methodName)
1448 {
1449 BMCWEB_LOG_DEBUG(
1450 "Found method named {} on interface {}",
1451 thisMethodName, thisInterfaceName);
1452 sdbusplus::message_t m =
1453 crow::connections::systemBus->new_method_call(
1454 connectionName.c_str(),
1455 transaction->path.c_str(),
1456 thisInterfaceName,
1457 transaction->methodName.c_str());
1458
1459 tinyxml2::XMLElement* argumentNode =
1460 methodNode->FirstChildElement("arg");
1461
1462 std::string returnType;
1463
1464 // Find the output type
1465 while (argumentNode != nullptr)
1466 {
1467 const char* argDirection =
1468 argumentNode->Attribute("direction");
1469 const char* argType =
1470 argumentNode->Attribute("type");
1471 if (argDirection != nullptr &&
1472 argType != nullptr &&
1473 std::string(argDirection) == "out")
1474 {
1475 returnType = argType;
1476 break;
1477 }
1478 argumentNode =
1479 argumentNode->NextSiblingElement("arg");
1480 }
1481
1482 auto argIt = transaction->arguments.begin();
1483
1484 argumentNode = methodNode->FirstChildElement("arg");
1485
1486 while (argumentNode != nullptr)
1487 {
1488 const char* argDirection =
1489 argumentNode->Attribute("direction");
1490 const char* argType =
1491 argumentNode->Attribute("type");
1492 if (argDirection != nullptr &&
1493 argType != nullptr &&
1494 std::string(argDirection) == "in")
1495 {
1496 if (argIt == transaction->arguments.end())
1497 {
1498 transaction->setErrorStatus(
1499 "Invalid method args");
1500 return;
1501 }
1502 if (convertJsonToDbus(m.get(),
1503 std::string(argType),
1504 *argIt) < 0)
1505 {
1506 transaction->setErrorStatus(
1507 "Invalid method arg type");
1508 return;
1509 }
1510
1511 argIt++;
1512 }
1513 argumentNode =
1514 argumentNode->NextSiblingElement("arg");
1515 }
1516
1517 crow::connections::systemBus->async_send(
1518 m, [transaction, returnType](
1519 const boost::system::error_code& ec2,
1520 sdbusplus::message_t& m2) {
1521 if (ec2)
1522 {
1523 transaction->methodFailed = true;
1524 const sd_bus_error* e = m2.get_error();
1525
1526 if (e != nullptr)
1527 {
1528 setErrorResponse(
1529 transaction->asyncResp->res,
1530 boost::beast::http::status::
1531 bad_request,
1532 e->name, e->message);
1533 }
1534 else
1535 {
1536 setErrorResponse(
1537 transaction->asyncResp->res,
1538 boost::beast::http::status::
1539 bad_request,
1540 "Method call failed",
1541 methodFailedMsg);
1542 }
1543 return;
1544 }
1545 transaction->methodPassed = true;
1546
1547 handleMethodResponse(transaction, m2,
1548 returnType);
1549 });
1550 break;
1551 }
1552 methodNode = methodNode->NextSiblingElement("method");
1553 }
1554 }
1555 interfaceNode = interfaceNode->NextSiblingElement("interface");
1556 }
1557 },
Ed Tanous1abe55e2018-09-05 08:30:59 -07001558 connectionName, transaction->path,
1559 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001560}
1561
zhanghch058d1b46d2021-04-01 11:18:24 +08001562inline void handleAction(const crow::Request& req,
1563 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001564 const std::string& objectPath,
1565 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001566{
Ed Tanous62598e32023-07-17 17:06:25 -07001567 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath,
1568 methodName);
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001569 nlohmann::json requestDbusData;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001570
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001571 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1572 if (ret == JsonParseResult::BadContentType)
1573 {
1574 setErrorResponse(asyncResp->res,
1575 boost::beast::http::status::unsupported_media_type,
1576 invalidContentType, unsupportedMediaMsg);
1577 return;
1578 }
1579 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001580 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001581 setErrorResponse(asyncResp->res,
1582 boost::beast::http::status::bad_request, noJsonDesc,
1583 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584 return;
1585 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001586 nlohmann::json::iterator data = requestDbusData.find("data");
1587 if (data == requestDbusData.end())
1588 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001589 setErrorResponse(asyncResp->res,
1590 boost::beast::http::status::bad_request, noJsonDesc,
1591 badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001592 return;
1593 }
1594
1595 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001596 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001597 setErrorResponse(asyncResp->res,
1598 boost::beast::http::status::bad_request, noJsonDesc,
1599 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600 return;
1601 }
Lei YU28dd5ca2023-03-17 13:17:05 +08001602 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001603
1604 transaction->path = objectPath;
1605 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001606 transaction->arguments = std::move(*data);
George Liu2b731192023-01-11 16:27:13 +08001607 dbus::utility::getDbusObject(
1608 objectPath, {},
Ed Tanous1abe55e2018-09-05 08:30:59 -07001609 [transaction](
George Liu2b731192023-01-11 16:27:13 +08001610 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001611 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1612 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001613 if (ec || interfaceNames.empty())
1614 {
1615 BMCWEB_LOG_ERROR("Can't find object");
1616 setErrorResponse(transaction->asyncResp->res,
1617 boost::beast::http::status::not_found,
1618 notFoundDesc, notFoundMsg);
1619 return;
1620 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001621
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001622 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)",
1623 interfaceNames.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001624
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001625 for (const std::pair<std::string, std::vector<std::string>>&
1626 object : interfaceNames)
1627 {
1628 findActionOnInterface(transaction, object.first);
1629 }
1630 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001631}
1632
zhanghch058d1b46d2021-04-01 11:18:24 +08001633inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1634 const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001635{
Ed Tanous62598e32023-07-17 17:06:25 -07001636 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06001637
George Liu2b731192023-01-11 16:27:13 +08001638 dbus::utility::getDbusObject(
1639 objectPath, {},
zhanghch058d1b46d2021-04-01 11:18:24 +08001640 [asyncResp, objectPath](
George Liu2b731192023-01-11 16:27:13 +08001641 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001642 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1643 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001644 if (ec || interfaceNames.empty())
1645 {
1646 BMCWEB_LOG_ERROR("Can't find object");
1647 setErrorResponse(asyncResp->res,
1648 boost::beast::http::status::method_not_allowed,
1649 methodNotAllowedDesc, methodNotAllowedMsg);
1650 return;
1651 }
Matt Spinlerde818812018-12-11 16:39:20 -06001652
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001653 auto transaction =
1654 std::make_shared<InProgressActionData>(asyncResp);
1655 transaction->path = objectPath;
1656 transaction->methodName = "Delete";
1657 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
Matt Spinlerde818812018-12-11 16:39:20 -06001658
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001659 for (const std::pair<std::string, std::vector<std::string>>&
1660 object : interfaceNames)
1661 {
1662 findActionOnInterface(transaction, object.first);
1663 }
1664 });
Matt Spinlerde818812018-12-11 16:39:20 -06001665}
1666
zhanghch058d1b46d2021-04-01 11:18:24 +08001667inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1668 const std::string& objectPath, int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001669{
George Liu7a1dbc42022-12-07 16:03:22 +08001670 dbus::utility::getSubTreePaths(
1671 objectPath, depth, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001672 [asyncResp](
George Liu7a1dbc42022-12-07 16:03:22 +08001673 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001674 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001675 if (ec)
1676 {
1677 setErrorResponse(asyncResp->res,
1678 boost::beast::http::status::not_found,
1679 notFoundDesc, notFoundMsg);
1680 }
1681 else
1682 {
1683 asyncResp->res.jsonValue["status"] = "ok";
1684 asyncResp->res.jsonValue["message"] = "200 OK";
1685 asyncResp->res.jsonValue["data"] = objectPaths;
1686 }
1687 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001688}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001689
zhanghch058d1b46d2021-04-01 11:18:24 +08001690inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1691 const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001692{
Ed Tanous62598e32023-07-17 17:06:25 -07001693 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07001694
Ed Tanous14766872022-03-15 10:44:42 -07001695 asyncResp->res.jsonValue["message"] = "200 OK";
1696 asyncResp->res.jsonValue["status"] = "ok";
1697 asyncResp->res.jsonValue["data"] = nlohmann::json::object();
Ed Tanous049a0512018-11-01 13:58:42 -07001698
George Liue99073f2022-12-09 11:06:16 +08001699 dbus::utility::getSubTree(
1700 objectPath, 0, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001701 [objectPath, asyncResp](
George Liue99073f2022-12-09 11:06:16 +08001702 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001703 const dbus::utility::MapperGetSubTreeResponse& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001704 auto transaction = std::make_shared<InProgressEnumerateData>(
1705 objectPath, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001706
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001707 transaction->subtree =
1708 std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1709 objectNames);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001710
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001711 if (ec)
1712 {
1713 BMCWEB_LOG_ERROR("GetSubTree failed on {}",
1714 transaction->objectPath);
1715 setErrorResponse(transaction->asyncResp->res,
1716 boost::beast::http::status::not_found,
1717 notFoundDesc, notFoundMsg);
1718 return;
1719 }
Ed Tanous64530012018-02-06 17:08:16 -08001720
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001721 // Add the data for the path passed in to the results
1722 // as if GetSubTree returned it, and continue on enumerating
1723 getObjectAndEnumerate(transaction);
1724 });
Ed Tanous64530012018-02-06 17:08:16 -08001725}
Ed Tanous911ac312017-08-15 09:37:42 -07001726
zhanghch058d1b46d2021-04-01 11:18:24 +08001727inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1728 std::string& objectPath, std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001729{
Ed Tanous62598e32023-07-17 17:06:25 -07001730 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001731 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001732 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001733
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 std::shared_ptr<std::string> path =
1735 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001736
George Liu2b731192023-01-11 16:27:13 +08001737 dbus::utility::getDbusObject(
1738 *path, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001739 [asyncResp, path,
George Liu2b731192023-01-11 16:27:13 +08001740 propertyName](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001741 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001742 if (ec || objectNames.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001743 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001744 setErrorResponse(asyncResp->res,
1745 boost::beast::http::status::not_found,
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001746 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001747 return;
1748 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001749 std::shared_ptr<nlohmann::json> response =
1750 std::make_shared<nlohmann::json>(nlohmann::json::object());
1751 // The mapper should never give us an empty interface names
1752 // list, but check anyway
1753 for (const std::pair<std::string, std::vector<std::string>>&
1754 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001755 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001756 const std::vector<std::string>& interfaceNames =
1757 connection.second;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001758
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001759 if (interfaceNames.empty())
1760 {
1761 setErrorResponse(asyncResp->res,
1762 boost::beast::http::status::not_found,
1763 notFoundDesc, notFoundMsg);
1764 return;
1765 }
1766
1767 for (const std::string& interface : interfaceNames)
1768 {
1769 sdbusplus::message_t m =
1770 crow::connections::systemBus->new_method_call(
1771 connection.first.c_str(), path->c_str(),
1772 "org.freedesktop.DBus.Properties", "GetAll");
1773 m.append(interface);
1774 crow::connections::systemBus->async_send(
1775 m, [asyncResp, response,
1776 propertyName](const boost::system::error_code& ec2,
1777 sdbusplus::message_t& msg) {
1778 if (ec2)
1779 {
1780 BMCWEB_LOG_ERROR("Bad dbus request error: {}",
1781 ec2);
1782 }
1783 else
1784 {
1785 nlohmann::json properties;
1786 int r =
1787 convertDBusToJSON("a{sv}", msg, properties);
1788 if (r < 0)
Patrick Williams5a39f772023-10-20 11:20:21 -05001789 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001790 BMCWEB_LOG_ERROR(
1791 "convertDBusToJSON failed");
Patrick Williams5a39f772023-10-20 11:20:21 -05001792 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001793 else
Patrick Williams5a39f772023-10-20 11:20:21 -05001794 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001795 nlohmann::json::object_t* obj =
1796 properties.get_ptr<
1797 nlohmann::json::object_t*>();
1798 if (obj == nullptr)
1799 {
1800 return;
1801 }
1802 for (auto& prop : *obj)
1803 {
1804 // if property name is empty, or
1805 // matches our search query, add it
1806 // to the response json
1807
1808 if (propertyName->empty())
1809 {
1810 (*response)[prop.first] =
1811 std::move(prop.second);
1812 }
1813 else if (prop.first == *propertyName)
1814 {
1815 *response = std::move(prop.second);
1816 }
1817 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001818 }
1819 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001820 if (response.use_count() == 1)
1821 {
1822 if (!propertyName->empty() && response->empty())
1823 {
1824 setErrorResponse(
1825 asyncResp->res,
1826 boost::beast::http::status::not_found,
1827 propNotFoundDesc, notFoundMsg);
1828 }
1829 else
1830 {
1831 asyncResp->res.jsonValue["status"] = "ok";
1832 asyncResp->res.jsonValue["message"] =
1833 "200 OK";
1834 asyncResp->res.jsonValue["data"] =
1835 *response;
1836 }
1837 }
1838 });
1839 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001840 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001841 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001842}
1843
Ed Tanous1abe55e2018-09-05 08:30:59 -07001844struct AsyncPutRequest
1845{
Ed Tanous4e23a442022-06-06 09:57:26 -07001846 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
zhanghch058d1b46d2021-04-01 11:18:24 +08001847 asyncResp(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001848 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001849 ~AsyncPutRequest()
1850 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001851 if (asyncResp->res.jsonValue.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001852 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001853 setErrorResponse(asyncResp->res,
1854 boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001855 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001856 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001857 }
1858
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001859 AsyncPutRequest(const AsyncPutRequest&) = delete;
1860 AsyncPutRequest(AsyncPutRequest&&) = delete;
1861 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1862 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1863
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001864 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001865 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001866 setErrorResponse(asyncResp->res,
1867 boost::beast::http::status::internal_server_error,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001868 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001869 }
1870
zhanghch058d1b46d2021-04-01 11:18:24 +08001871 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872 std::string objectPath;
1873 std::string propertyName;
1874 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001875};
1876
zhanghch058d1b46d2021-04-01 11:18:24 +08001877inline void handlePut(const crow::Request& req,
1878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001879 const std::string& objectPath,
1880 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001881{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001882 if (destProperty.empty())
1883 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001884 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001885 forbiddenResDesc, forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001886 return;
1887 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001888 nlohmann::json requestDbusData;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001889
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001890 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1891 if (ret == JsonParseResult::BadContentType)
1892 {
1893 setErrorResponse(asyncResp->res,
1894 boost::beast::http::status::unsupported_media_type,
1895 invalidContentType, unsupportedMediaMsg);
1896 return;
1897 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001898
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001899 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001900 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001901 setErrorResponse(asyncResp->res,
1902 boost::beast::http::status::bad_request, noJsonDesc,
1903 badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001904 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001905 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001906
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001907 auto propertyIt = requestDbusData.find("data");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001908 if (propertyIt == requestDbusData.end())
1909 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001910 setErrorResponse(asyncResp->res,
1911 boost::beast::http::status::bad_request, noJsonDesc,
1912 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001913 return;
1914 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001915 const nlohmann::json& propertySetValue = *propertyIt;
zhanghch058d1b46d2021-04-01 11:18:24 +08001916 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001917 transaction->objectPath = objectPath;
1918 transaction->propertyName = destProperty;
1919 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001920
George Liu2b731192023-01-11 16:27:13 +08001921 dbus::utility::getDbusObject(
1922 transaction->objectPath, {},
1923 [transaction](const boost::system::error_code& ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001924 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001925 if (!ec2 && objectNames.empty())
1926 {
1927 setErrorResponse(transaction->asyncResp->res,
1928 boost::beast::http::status::not_found,
1929 propNotFoundDesc, notFoundMsg);
1930 return;
1931 }
Ed Tanous911ac312017-08-15 09:37:42 -07001932
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001933 for (const std::pair<std::string, std::vector<std::string>>&
1934 connection : objectNames)
1935 {
1936 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001937
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001938 crow::connections::systemBus->async_method_call(
1939 [connectionName{std::string(connectionName)},
1940 transaction](const boost::system::error_code& ec3,
1941 const std::string& introspectXml) {
1942 if (ec3)
Ed Tanousb0b61522024-08-06 10:20:49 -07001943 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001944 BMCWEB_LOG_ERROR(
1945 "Introspect call failed with error: {} on process: {}",
1946 ec3.message(), connectionName);
1947 transaction->setErrorStatus("Unexpected Error");
1948 return;
Ed Tanousb0b61522024-08-06 10:20:49 -07001949 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001950 tinyxml2::XMLDocument doc;
1951
1952 doc.Parse(introspectXml.c_str());
1953 tinyxml2::XMLNode* pRoot =
1954 doc.FirstChildElement("node");
1955 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001956 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001957 BMCWEB_LOG_ERROR("XML document failed to parse: {}",
1958 introspectXml);
1959 transaction->setErrorStatus("Unexpected Error");
1960 return;
1961 }
1962 tinyxml2::XMLElement* ifaceNode =
1963 pRoot->FirstChildElement("interface");
1964 while (ifaceNode != nullptr)
1965 {
1966 const char* interfaceName =
1967 ifaceNode->Attribute("name");
1968 BMCWEB_LOG_DEBUG("found interface {}",
1969 interfaceName);
1970 tinyxml2::XMLElement* propNode =
1971 ifaceNode->FirstChildElement("property");
1972 while (propNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001973 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001974 const char* propertyName =
1975 propNode->Attribute("name");
1976 if (propertyName == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001977 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001978 BMCWEB_LOG_DEBUG(
1979 "Couldn't find name property");
1980 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001981 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001982 BMCWEB_LOG_DEBUG("Found property {}",
1983 propertyName);
1984 if (propertyName == transaction->propertyName)
Ed Tanous002d39b2022-05-31 08:59:27 -07001985 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001986 const char* argType =
1987 propNode->Attribute("type");
1988 if (argType != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07001989 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001990 sdbusplus::message_t m =
1991 crow::connections::systemBus
1992 ->new_method_call(
1993 connectionName.c_str(),
1994 transaction->objectPath
1995 .c_str(),
1996 "org.freedesktop.DBus."
1997 "Properties",
1998 "Set");
1999 m.append(interfaceName,
2000 transaction->propertyName);
2001 int r = sd_bus_message_open_container(
2002 m.get(), SD_BUS_TYPE_VARIANT,
2003 argType);
2004 if (r < 0)
2005 {
2006 transaction->setErrorStatus(
2007 "Unexpected Error");
2008 return;
2009 }
2010 r = convertJsonToDbus(
2011 m.get(), argType,
2012 transaction->propertyValue);
2013 if (r < 0)
2014 {
2015 if (r == -ERANGE)
2016 {
2017 transaction->setErrorStatus(
2018 "Provided property value "
2019 "is out of range for the "
2020 "property type");
2021 }
2022 else
2023 {
2024 transaction->setErrorStatus(
2025 "Invalid arg type");
2026 }
2027 return;
2028 }
2029 r = sd_bus_message_close_container(
2030 m.get());
2031 if (r < 0)
2032 {
2033 transaction->setErrorStatus(
2034 "Unexpected Error");
2035 return;
2036 }
2037 crow::connections::systemBus
2038 ->async_send(
2039 m,
2040 [transaction](
2041 const boost::system::
2042 error_code& ec,
2043 sdbusplus::message_t& m2) {
2044 BMCWEB_LOG_DEBUG("sent");
2045 if (ec)
2046 {
2047 const sd_bus_error* e =
2048 m2.get_error();
2049 setErrorResponse(
2050 transaction
2051 ->asyncResp
2052 ->res,
2053 boost::beast::http::
2054 status::
2055 forbidden,
2056 (e) != nullptr
2057 ? e->name
2058 : ec.category()
2059 .name(),
2060 (e) != nullptr
2061 ? e->message
2062 : ec.message());
2063 }
2064 else
2065 {
2066 transaction->asyncResp
2067 ->res.jsonValue
2068 ["status"] =
2069 "ok";
2070 transaction->asyncResp
2071 ->res.jsonValue
2072 ["message"] =
2073 "200 OK";
2074 transaction->asyncResp
2075 ->res
2076 .jsonValue["data"] =
2077 nullptr;
2078 }
2079 });
Ed Tanous002d39b2022-05-31 08:59:27 -07002080 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002081 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002082 propNode =
2083 propNode->NextSiblingElement("property");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002084 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002085 ifaceNode =
2086 ifaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002087 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002088 },
2089 connectionName, transaction->objectPath,
2090 "org.freedesktop.DBus.Introspectable", "Introspect");
2091 }
2092 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002093}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002094
zhanghch058d1b46d2021-04-01 11:18:24 +08002095inline void handleDBusUrl(const crow::Request& req,
2096 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002097 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002098{
Ed Tanous049a0512018-11-01 13:58:42 -07002099 // If accessing a single attribute, fill in and update objectPath,
2100 // otherwise leave destProperty blank
Ed Tanouse05aec52022-01-25 10:28:56 -08002101 std::string destProperty;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002102 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002103 size_t attrPosition = objectPath.find(attrSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002104 if (attrPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002105 {
2106 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2107 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002108 objectPath.resize(attrPosition);
Ed Tanous049a0512018-11-01 13:58:42 -07002109 }
2110
Ed Tanousb41187f2019-10-24 16:30:02 -07002111 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002112 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002113 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002114 size_t actionPosition = objectPath.find(actionSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002115 if (actionPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002116 {
2117 std::string postProperty =
2118 objectPath.substr((actionPosition + strlen(actionSeperator)),
2119 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002120 objectPath.resize(actionPosition);
zhanghch058d1b46d2021-04-01 11:18:24 +08002121 handleAction(req, asyncResp, objectPath, postProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002122 return;
2123 }
2124 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002125 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002126 {
Ed Tanous11ba3972022-07-11 09:50:41 -07002127 if (objectPath.ends_with("/enumerate"))
Ed Tanous049a0512018-11-01 13:58:42 -07002128 {
2129 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2130 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002131 handleEnumerate(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002132 }
Ed Tanous11ba3972022-07-11 09:50:41 -07002133 else if (objectPath.ends_with("/list"))
Ed Tanous049a0512018-11-01 13:58:42 -07002134 {
2135 objectPath.erase(objectPath.end() - sizeof("list"),
2136 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002137 handleList(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002138 }
2139 else
2140 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002141 // Trim any trailing "/" at the end
Ed Tanous11ba3972022-07-11 09:50:41 -07002142 if (objectPath.ends_with("/"))
Ed Tanousf839dfe2018-11-12 11:11:15 -08002143 {
2144 objectPath.pop_back();
zhanghch058d1b46d2021-04-01 11:18:24 +08002145 handleList(asyncResp, objectPath, 1);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002146 }
2147 else
2148 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002149 handleGet(asyncResp, objectPath, destProperty);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002150 }
Ed Tanous049a0512018-11-01 13:58:42 -07002151 }
2152 return;
2153 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002154 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002155 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002156 handlePut(req, asyncResp, objectPath, destProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002157 return;
2158 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002159 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002160 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002161 handleDelete(asyncResp, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002162 return;
2163 }
Ed Tanous049a0512018-11-01 13:58:42 -07002164
zhanghch058d1b46d2021-04-01 11:18:24 +08002165 setErrorResponse(asyncResp->res,
2166 boost::beast::http::status::method_not_allowed,
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002167 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002168}
2169
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002170inline void handleBusSystemPost(
2171 const crow::Request& req,
2172 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2173 const std::string& processName, const std::string& requestedPath)
Ed Tanous1656b292022-05-04 11:33:42 -07002174{
2175 std::vector<std::string> strs;
Ed Tanous50ebd4a2023-01-19 19:03:17 -08002176
2177 bmcweb::split(strs, requestedPath, '/');
Ed Tanous1656b292022-05-04 11:33:42 -07002178 std::string objectPath;
2179 std::string interfaceName;
2180 std::string methodName;
2181 auto it = strs.begin();
2182 if (it == strs.end())
2183 {
2184 objectPath = "/";
2185 }
2186 while (it != strs.end())
2187 {
2188 // Check if segment contains ".". If it does, it must be an
2189 // interface
2190 if (it->find(".") != std::string::npos)
2191 {
2192 break;
2193 // This check is necessary as the trailing slash gets
2194 // parsed as part of our <path> specifier above, which
2195 // causes the normal trailing backslash redirector to
2196 // fail.
2197 }
2198 if (!it->empty())
2199 {
2200 objectPath += "/" + *it;
2201 }
2202 it++;
2203 }
2204 if (it != strs.end())
2205 {
2206 interfaceName = *it;
2207 it++;
2208
2209 // after interface, we might have a method name
2210 if (it != strs.end())
2211 {
2212 methodName = *it;
2213 it++;
2214 }
2215 }
2216 if (it != strs.end())
2217 {
2218 // if there is more levels past the method name, something
2219 // went wrong, return not found
2220 asyncResp->res.result(boost::beast::http::status::not_found);
2221 return;
2222 }
2223 if (interfaceName.empty())
2224 {
2225 crow::connections::systemBus->async_method_call(
2226 [asyncResp, processName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002227 objectPath](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002228 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002229 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002230 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002231 BMCWEB_LOG_ERROR(
2232 "Introspect call failed with error: {} on process: {} path: {}",
2233 ec.message(), processName, objectPath);
2234 return;
2235 }
2236 tinyxml2::XMLDocument doc;
2237
2238 doc.Parse(introspectXml.c_str());
2239 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2240 if (pRoot == nullptr)
2241 {
2242 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2243 processName, objectPath);
2244 asyncResp->res.jsonValue["status"] = "XML parse error";
2245 asyncResp->res.result(
2246 boost::beast::http::status::internal_server_error);
2247 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002248 }
2249
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002250 BMCWEB_LOG_DEBUG("{}", introspectXml);
2251 asyncResp->res.jsonValue["status"] = "ok";
2252 asyncResp->res.jsonValue["bus_name"] = processName;
2253 asyncResp->res.jsonValue["object_path"] = objectPath;
2254
2255 nlohmann::json& interfacesArray =
2256 asyncResp->res.jsonValue["interfaces"];
2257 interfacesArray = nlohmann::json::array();
2258 tinyxml2::XMLElement* interface =
2259 pRoot->FirstChildElement("interface");
2260
2261 while (interface != nullptr)
2262 {
2263 const char* ifaceName = interface->Attribute("name");
2264 if (ifaceName != nullptr)
2265 {
2266 nlohmann::json::object_t interfaceObj;
2267 interfaceObj["name"] = ifaceName;
2268 interfacesArray.emplace_back(std::move(interfaceObj));
2269 }
2270
2271 interface = interface->NextSiblingElement("interface");
2272 }
2273 },
Ed Tanous1656b292022-05-04 11:33:42 -07002274 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2275 "Introspect");
2276 }
2277 else if (methodName.empty())
2278 {
2279 crow::connections::systemBus->async_method_call(
2280 [asyncResp, processName, objectPath,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002281 interfaceName](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002282 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002283 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002284 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002285 BMCWEB_LOG_ERROR(
2286 "Introspect call failed with error: {} on process: {} path: {}",
2287 ec.message(), processName, objectPath);
2288 return;
2289 }
2290 tinyxml2::XMLDocument doc;
2291
2292 doc.Parse(introspectXml.data(), introspectXml.size());
2293 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2294 if (pRoot == nullptr)
2295 {
2296 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2297 processName, objectPath);
2298 asyncResp->res.result(
2299 boost::beast::http::status::internal_server_error);
2300 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002301 }
Ed Tanous14766872022-03-15 10:44:42 -07002302
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002303 asyncResp->res.jsonValue["status"] = "ok";
2304 asyncResp->res.jsonValue["bus_name"] = processName;
2305 asyncResp->res.jsonValue["interface"] = interfaceName;
2306 asyncResp->res.jsonValue["object_path"] = objectPath;
Ed Tanous1656b292022-05-04 11:33:42 -07002307
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002308 nlohmann::json& methodsArray =
2309 asyncResp->res.jsonValue["methods"];
2310 methodsArray = nlohmann::json::array();
2311
2312 nlohmann::json& signalsArray =
2313 asyncResp->res.jsonValue["signals"];
2314 signalsArray = nlohmann::json::array();
2315
2316 nlohmann::json& propertiesObj =
2317 asyncResp->res.jsonValue["properties"];
2318 propertiesObj = nlohmann::json::object();
2319
2320 // if we know we're the only call, build the
2321 // json directly
2322 tinyxml2::XMLElement* interface =
2323 pRoot->FirstChildElement("interface");
2324 while (interface != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002325 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002326 const char* ifaceName = interface->Attribute("name");
2327
2328 if (ifaceName != nullptr && ifaceName == interfaceName)
Ed Tanous1656b292022-05-04 11:33:42 -07002329 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002330 break;
Ed Tanous002d39b2022-05-31 08:59:27 -07002331 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002332
2333 interface = interface->NextSiblingElement("interface");
2334 }
2335 if (interface == nullptr)
2336 {
2337 // if we got to the end of the list and
2338 // never found a match, throw 404
2339 asyncResp->res.result(
2340 boost::beast::http::status::not_found);
2341 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002342 }
2343
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002344 tinyxml2::XMLElement* methods =
2345 interface->FirstChildElement("method");
2346 while (methods != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002347 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002348 nlohmann::json argsArray = nlohmann::json::array();
2349 tinyxml2::XMLElement* arg =
2350 methods->FirstChildElement("arg");
2351 while (arg != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002352 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002353 nlohmann::json thisArg;
2354 for (const char* fieldName : std::array<const char*, 3>{
2355 "name", "direction", "type"})
2356 {
2357 const char* fieldValue = arg->Attribute(fieldName);
2358 if (fieldValue != nullptr)
2359 {
2360 thisArg[fieldName] = fieldValue;
2361 }
2362 }
2363 argsArray.emplace_back(std::move(thisArg));
2364 arg = arg->NextSiblingElement("arg");
Ed Tanous002d39b2022-05-31 08:59:27 -07002365 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002366
2367 const char* name = methods->Attribute("name");
2368 if (name != nullptr)
2369 {
2370 std::string uri;
2371 uri.reserve(14 + processName.size() +
2372 objectPath.size() + interfaceName.size() +
2373 strlen(name));
2374 uri += "/bus/system/";
2375 uri += processName;
2376 uri += objectPath;
2377 uri += "/";
2378 uri += interfaceName;
2379 uri += "/";
2380 uri += name;
2381
2382 nlohmann::json::object_t object;
2383 object["name"] = name;
2384 object["uri"] = std::move(uri);
2385 object["args"] = argsArray;
2386
2387 methodsArray.emplace_back(std::move(object));
2388 }
2389 methods = methods->NextSiblingElement("method");
Ed Tanous002d39b2022-05-31 08:59:27 -07002390 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002391 tinyxml2::XMLElement* signals =
2392 interface->FirstChildElement("signal");
2393 while (signals != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002394 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002395 nlohmann::json argsArray = nlohmann::json::array();
2396
2397 tinyxml2::XMLElement* arg =
2398 signals->FirstChildElement("arg");
2399 while (arg != nullptr)
2400 {
2401 const char* name = arg->Attribute("name");
2402 const char* type = arg->Attribute("type");
2403 if (name != nullptr && type != nullptr)
2404 {
2405 nlohmann::json::object_t params;
2406 params["name"] = name;
2407 params["type"] = type;
2408 argsArray.push_back(std::move(params));
2409 }
2410 arg = arg->NextSiblingElement("arg");
2411 }
2412 const char* name = signals->Attribute("name");
2413 if (name != nullptr)
2414 {
2415 nlohmann::json::object_t object;
2416 object["name"] = name;
2417 object["args"] = argsArray;
2418 signalsArray.emplace_back(std::move(object));
2419 }
2420
2421 signals = signals->NextSiblingElement("signal");
Ed Tanous002d39b2022-05-31 08:59:27 -07002422 }
2423
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002424 tinyxml2::XMLElement* property =
2425 interface->FirstChildElement("property");
2426 while (property != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002427 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002428 const char* name = property->Attribute("name");
2429 const char* type = property->Attribute("type");
2430 if (type != nullptr && name != nullptr)
2431 {
2432 sdbusplus::message_t m =
2433 crow::connections::systemBus->new_method_call(
2434 processName.c_str(), objectPath.c_str(),
2435 "org.freedesktop."
2436 "DBus."
2437 "Properties",
2438 "Get");
2439 m.append(interfaceName, name);
2440 nlohmann::json& propertyItem = propertiesObj[name];
2441 crow::connections::systemBus->async_send(
2442 m, [&propertyItem,
2443 asyncResp](const boost::system::error_code& ec2,
2444 sdbusplus::message_t& msg) {
2445 if (ec2)
2446 {
2447 return;
2448 }
Ed Tanous1656b292022-05-04 11:33:42 -07002449
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002450 int r =
2451 convertDBusToJSON("v", msg, propertyItem);
2452 if (r < 0)
2453 {
2454 BMCWEB_LOG_ERROR(
2455 "Couldn't convert vector to json");
2456 }
2457 });
2458 }
2459 property = property->NextSiblingElement("property");
Ed Tanous1656b292022-05-04 11:33:42 -07002460 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002461 },
Ed Tanous1656b292022-05-04 11:33:42 -07002462 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2463 "Introspect");
2464 }
2465 else
2466 {
2467 if (req.method() != boost::beast::http::verb::post)
2468 {
2469 asyncResp->res.result(boost::beast::http::status::not_found);
2470 return;
2471 }
2472
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002473 nlohmann::json requestDbusData;
2474 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2475 if (ret == JsonParseResult::BadContentType)
Ed Tanous1656b292022-05-04 11:33:42 -07002476 {
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002477 setErrorResponse(asyncResp->res,
2478 boost::beast::http::status::unsupported_media_type,
2479 invalidContentType, unsupportedMediaMsg);
Ed Tanous1656b292022-05-04 11:33:42 -07002480 return;
2481 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002482 if (ret != JsonParseResult::Success)
2483 {
2484 setErrorResponse(asyncResp->res,
2485 boost::beast::http::status::bad_request,
2486 noJsonDesc, badReqMsg);
2487 return;
2488 }
2489
Ed Tanous1656b292022-05-04 11:33:42 -07002490 if (!requestDbusData.is_array())
2491 {
2492 asyncResp->res.result(boost::beast::http::status::bad_request);
2493 return;
2494 }
Lei YU28dd5ca2023-03-17 13:17:05 +08002495 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1656b292022-05-04 11:33:42 -07002496
2497 transaction->path = objectPath;
2498 transaction->methodName = methodName;
2499 transaction->arguments = std::move(requestDbusData);
2500
2501 findActionOnInterface(transaction, processName);
2502 }
2503}
2504
Ed Tanous23a21a12020-07-25 04:45:05 +00002505inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002506{
2507 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous432a8902021-06-14 15:28:56 -07002508 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002509 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002510 [](const crow::Request&,
2511 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002512 nlohmann::json::array_t buses;
2513 nlohmann::json& bus = buses.emplace_back();
2514 bus["name"] = "system";
2515 asyncResp->res.jsonValue["busses"] = std::move(buses);
2516 asyncResp->res.jsonValue["status"] = "ok";
2517 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002518
2519 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous432a8902021-06-14 15:28:56 -07002520 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002521 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002522 [](const crow::Request&,
2523 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002524 auto myCallback = [asyncResp](
2525 const boost::system::error_code& ec,
zhanghch058d1b46d2021-04-01 11:18:24 +08002526 std::vector<std::string>& names) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002527 if (ec)
2528 {
2529 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
2530 asyncResp->res.result(
2531 boost::beast::http::status::internal_server_error);
2532 }
2533 else
2534 {
2535 std::ranges::sort(names);
2536 asyncResp->res.jsonValue["status"] = "ok";
2537 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2538 for (const auto& name : names)
2539 {
2540 nlohmann::json::object_t object;
2541 object["name"] = name;
2542 objectsSub.emplace_back(std::move(object));
2543 }
2544 }
2545 };
2546 crow::connections::systemBus->async_method_call(
2547 std::move(myCallback), "org.freedesktop.DBus", "/",
2548 "org.freedesktop.DBus", "ListNames");
2549 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002550
2551 BMCWEB_ROUTE(app, "/list/")
Ed Tanous432a8902021-06-14 15:28:56 -07002552 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002553 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002554 [](const crow::Request&,
2555 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002556 handleList(asyncResp, "/");
2557 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002558
2559 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002560 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002561 .methods(boost::beast::http::verb::get)(
2562 [](const crow::Request& req,
2563 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002564 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002565 std::string objectPath = "/xyz/" + path;
2566 handleDBusUrl(req, asyncResp, objectPath);
2567 });
zhanghch058d1b46d2021-04-01 11:18:24 +08002568
2569 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002570 .privileges({{"ConfigureComponents", "ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002571 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2572 boost::beast::http::verb::delete_)(
2573 [](const crow::Request& req,
2574 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2575 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002576 std::string objectPath = "/xyz/" + path;
2577 handleDBusUrl(req, asyncResp, objectPath);
2578 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002579
Ed Tanous049a0512018-11-01 13:58:42 -07002580 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002581 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002582 .methods(boost::beast::http::verb::get)(
2583 [](const crow::Request& req,
2584 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2585 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002586 std::string objectPath = "/org/" + path;
2587 handleDBusUrl(req, asyncResp, objectPath);
2588 });
Tanousf00032d2018-11-05 01:18:10 -03002589
2590 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002591 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002592 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2593 boost::beast::http::verb::delete_)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002594 [](const crow::Request& req,
2595 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002596 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002597 std::string objectPath = "/org/" + path;
2598 handleDBusUrl(req, asyncResp, objectPath);
2599 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002600
Ed Tanous1abe55e2018-09-05 08:30:59 -07002601 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002602 .privileges({{"ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002603 .methods(boost::beast::http::verb::get)(
2604 [](const crow::Request&,
2605 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2606 const std::string& dumpId) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002607 if (!validateFilename(dumpId))
2608 {
2609 asyncResp->res.result(
2610 boost::beast::http::status::bad_request);
2611 return;
2612 }
2613 std::filesystem::path loc(
2614 "/var/lib/phosphor-debug-collector/dumps");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002615
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002616 loc /= dumpId;
Ramesh Iyyard9207042019-07-05 08:04:42 -05002617
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002618 if (!std::filesystem::exists(loc) ||
2619 !std::filesystem::is_directory(loc))
2620 {
2621 BMCWEB_LOG_ERROR("{}Not found", loc.string());
2622 asyncResp->res.result(
2623 boost::beast::http::status::not_found);
2624 return;
2625 }
2626 std::filesystem::directory_iterator files(loc);
zhanghch058d1b46d2021-04-01 11:18:24 +08002627
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002628 for (const auto& file : files)
2629 {
2630 if (!asyncResp->res.openFile(file))
2631 {
2632 continue;
2633 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002634
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002635 asyncResp->res.addHeader(
2636 boost::beast::http::field::content_type,
2637 "application/octet-stream");
zhanghch058d1b46d2021-04-01 11:18:24 +08002638
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002639 // Assuming only one dump file will be present in the dump
2640 // id directory
2641 std::string dumpFileName = file.path().filename().string();
zhanghch058d1b46d2021-04-01 11:18:24 +08002642
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002643 // Filename should be in alphanumeric, dot and underscore
2644 // Its based on phosphor-debug-collector application
2645 // dumpfile format
2646 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2647 if (!std::regex_match(dumpFileName, dumpFileRegex))
2648 {
2649 BMCWEB_LOG_ERROR("Invalid dump filename {}",
2650 dumpFileName);
2651 asyncResp->res.result(
2652 boost::beast::http::status::not_found);
2653 return;
2654 }
2655 std::string contentDispositionParam =
2656 "attachment; filename=\"" + dumpFileName + "\"";
2657
2658 asyncResp->res.addHeader(
2659 boost::beast::http::field::content_disposition,
2660 contentDispositionParam);
2661
2662 return;
2663 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002664 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanousad18f072018-11-14 14:07:48 -08002665 return;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002666 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002667
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002668 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002669 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002670
2671 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002672 [](const crow::Request&,
2673 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +00002674 const std::string& connection) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002675 introspectObjects(connection, "/", asyncResp);
2676 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002677
2678 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002679 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous1656b292022-05-04 11:33:42 -07002680 .methods(boost::beast::http::verb::get,
2681 boost::beast::http::verb::post)(handleBusSystemPost);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002682}
2683} // namespace openbmc_mapper
2684} // namespace crow