blob: 4671793820ddd43aac9cc45ce48afa5528ac8bcf [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
Ed Tanous1abe55e2018-09-05 08:30:59 -070067namespace crow
68{
69namespace openbmc_mapper
70{
Ed Tanous23a21a12020-07-25 04:45:05 +000071const constexpr char* notFoundMsg = "404 Not Found";
72const constexpr char* badReqMsg = "400 Bad Request";
73const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
74const constexpr char* forbiddenMsg = "403 Forbidden";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010075const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type";
Ed Tanous23a21a12020-07-25 04:45:05 +000076const constexpr char* methodFailedMsg = "500 Method Call Failed";
77const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
78const constexpr char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060079 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous23a21a12020-07-25 04:45:05 +000080const constexpr char* propNotFoundDesc =
81 "The specified property cannot be found";
82const constexpr char* noJsonDesc = "No JSON object could be decoded";
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010083const constexpr char* invalidContentType =
84 "Content-type header is missing or invalid";
Ed Tanous23a21a12020-07-25 04:45:05 +000085const constexpr char* methodNotFoundDesc =
86 "The specified method cannot be found";
87const constexpr char* methodNotAllowedDesc = "Method not allowed";
88const constexpr char* forbiddenPropDesc =
89 "The specified property cannot be created";
90const constexpr char* forbiddenResDesc =
91 "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060092
Josh Lehan482c45a2022-03-29 17:10:44 -070093inline bool validateFilename(const std::string& filename)
94{
Ed Tanous4b242742023-05-11 09:51:51 -070095 static std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
Josh Lehan482c45a2022-03-29 17:10:44 -070096
97 return std::regex_match(filename, validFilename);
98}
99
Ed Tanous23a21a12020-07-25 04:45:05 +0000100inline void setErrorResponse(crow::Response& res,
101 boost::beast::http::status result,
Ed Tanous26ccae32023-02-16 10:28:44 -0800102 const std::string& desc, std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -0600103{
104 res.result(result);
Ed Tanous14766872022-03-15 10:44:42 -0700105 res.jsonValue["data"]["description"] = desc;
106 res.jsonValue["message"] = msg;
107 res.jsonValue["status"] = "error";
Matt Spinler2ae60092018-12-06 10:35:36 -0600108}
109
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400110inline void introspectObjects(
111 const std::string& processName, const std::string& objectPath,
112 const std::shared_ptr<bmcweb::AsyncResp>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700114 if (transaction->res.jsonValue.is_null())
115 {
Ed Tanous14766872022-03-15 10:44:42 -0700116 transaction->res.jsonValue["status"] = "ok";
117 transaction->res.jsonValue["bus_name"] = processName;
118 transaction->res.jsonValue["objects"] = nlohmann::json::array();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700119 }
120
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122 [transaction, processName{std::string(processName)},
123 objectPath{std::string(objectPath)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800124 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000125 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400126 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700127 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400128 BMCWEB_LOG_ERROR(
129 "Introspect call failed with error: {} on process: {} path: {}",
130 ec.message(), processName, objectPath);
131 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400133 nlohmann::json::object_t object;
134 object["path"] = objectPath;
135
136 transaction->res.jsonValue["objects"].emplace_back(
137 std::move(object));
138
139 tinyxml2::XMLDocument doc;
140
141 doc.Parse(introspectXml.c_str());
142 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
143 if (pRoot == nullptr)
144 {
145 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
146 processName, objectPath);
147 }
148 else
149 {
150 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
151 while (node != nullptr)
152 {
153 const char* childPath = node->Attribute("name");
154 if (childPath != nullptr)
155 {
156 std::string newpath;
157 if (objectPath != "/")
158 {
159 newpath += objectPath;
160 }
161 newpath += std::string("/") + childPath;
162 // introspect the subobjects as well
163 introspectObjects(processName, newpath, transaction);
164 }
165
166 node = node->NextSiblingElement("node");
167 }
168 }
169 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700170 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700172}
Ed Tanous64530012018-02-06 17:08:16 -0800173
Ed Tanous23a21a12020-07-25 04:45:05 +0000174inline void getPropertiesForEnumerate(
175 const std::string& objectPath, const std::string& service,
Ed Tanousb5a76932020-09-29 16:16:58 -0700176 const std::string& interface,
177 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600178{
Ed Tanous62598e32023-07-17 17:06:25 -0700179 BMCWEB_LOG_DEBUG("getPropertiesForEnumerate {} {} {}", objectPath, service,
180 interface);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600181
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200182 sdbusplus::asio::getAllProperties(
183 *crow::connections::systemBus, service, objectPath, interface,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800184 [asyncResp, objectPath, service,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800185 interface](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800186 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400187 if (ec)
188 {
189 BMCWEB_LOG_ERROR(
190 "GetAll on path {} iface {} service {} failed with code {}",
191 objectPath, interface, service, ec);
192 return;
193 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600194
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400195 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
196 nlohmann::json& objectJson = dataJson[objectPath];
197 if (objectJson.is_null())
198 {
199 objectJson = nlohmann::json::object();
200 }
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600201
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400202 for (const auto& [name, value] : propertiesList)
203 {
204 nlohmann::json& propertyJson = objectJson[name];
205 std::visit(
206 [&propertyJson](auto&& val) {
207 if constexpr (std::is_same_v<
208 std::decay_t<decltype(val)>,
209 sdbusplus::message::unix_fd>)
210 {
211 propertyJson = val.fd;
212 }
213 else
214 {
215 propertyJson = val;
216 }
217 },
218 value);
219 }
220 });
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600221}
222
223// Find any results that weren't picked up by ObjectManagers, to be
224// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000225inline void findRemainingObjectsForEnumerate(
Ed Tanousb5a76932020-09-29 16:16:58 -0700226 const std::string& objectPath,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800227 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree,
Ed Tanousb5a76932020-09-29 16:16:58 -0700228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600229{
Ed Tanous62598e32023-07-17 17:06:25 -0700230 BMCWEB_LOG_DEBUG("findRemainingObjectsForEnumerate");
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500231 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600232
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500233 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600234 {
235 if (path == objectPath)
236 {
237 // An enumerate does not return the target path's properties
238 continue;
239 }
240 if (dataJson.find(path) == dataJson.end())
241 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500242 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600243 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600245 {
Ed Tanous11ba3972022-07-11 09:50:41 -0700246 if (!interface.starts_with("org.freedesktop.DBus"))
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600247 {
248 getPropertiesForEnumerate(path, service, interface,
249 asyncResp);
250 }
251 }
252 }
253 }
254 }
255}
256
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600257struct InProgressEnumerateData
258{
zhanghch058d1b46d2021-04-01 11:18:24 +0800259 InProgressEnumerateData(
260 const std::string& objectPathIn,
261 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400262 objectPath(objectPathIn), asyncResp(asyncRespIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500263 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600264
265 ~InProgressEnumerateData()
266 {
Ed Tanous24b2fe82022-01-06 12:45:54 -0800267 try
268 {
269 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
270 }
271 catch (...)
272 {
Ed Tanous62598e32023-07-17 17:06:25 -0700273 BMCWEB_LOG_CRITICAL(
274 "findRemainingObjectsForEnumerate threw exception");
Ed Tanous24b2fe82022-01-06 12:45:54 -0800275 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600276 }
277
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800278 InProgressEnumerateData(const InProgressEnumerateData&) = delete;
279 InProgressEnumerateData(InProgressEnumerateData&&) = delete;
280 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete;
281 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600282 const std::string objectPath;
Ed Tanousb9d36b42022-02-26 21:42:46 -0800283 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree;
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600284 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
285};
286
Ed Tanous23a21a12020-07-25 04:45:05 +0000287inline void getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000288 const std::string& objectName, const std::string& objectManagerPath,
289 const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700290 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291{
Ed Tanous62598e32023-07-17 17:06:25 -0700292 BMCWEB_LOG_DEBUG(
293 "getManagedObjectsForEnumerate {} object_manager_path {} connection_name {}",
294 objectName, objectManagerPath, connectionName);
George Liu5eb468d2023-06-20 17:03:24 +0800295 sdbusplus::message::object_path path(objectManagerPath);
296 dbus::utility::getManagedObjects(
297 connectionName, path,
Ed Tanous81ce6092020-12-17 16:54:55 +0000298 [transaction, objectName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800299 connectionName](const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +0000300 const dbus::utility::ManagedObjectType& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400301 if (ec)
Ed Tanous049a0512018-11-01 13:58:42 -0700302 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400303 BMCWEB_LOG_ERROR(
304 "GetManagedObjects on path {} on connection {} failed with code {}",
305 objectName, connectionName, ec);
306 return;
307 }
308
309 nlohmann::json& dataJson =
310 transaction->asyncResp->res.jsonValue["data"];
311
312 for (const auto& objectPath : objects)
313 {
314 if (objectPath.first.str.starts_with(objectName))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700315 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400316 BMCWEB_LOG_DEBUG("Reading object {}", objectPath.first.str);
317 nlohmann::json& objectJson = dataJson[objectPath.first.str];
318 if (objectJson.is_null())
319 {
320 objectJson = nlohmann::json::object();
321 }
322 for (const auto& interface : objectPath.second)
323 {
324 for (const auto& property : interface.second)
325 {
326 nlohmann::json& propertyJson =
327 objectJson[property.first];
328 std::visit(
329 [&propertyJson](auto&& val) {
330 if constexpr (
331 std::is_same_v<
332 std::decay_t<decltype(val)>,
333 sdbusplus::message::unix_fd>)
334 {
335 propertyJson = val.fd;
336 }
337 else
338 {
339 propertyJson = val;
340 }
341 },
342 property.second);
343 }
344 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500346 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700347 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400348 if (interface.first == "org.freedesktop.DBus.ObjectManager")
Ed Tanous049a0512018-11-01 13:58:42 -0700349 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400350 getManagedObjectsForEnumerate(
351 objectPath.first.str, objectPath.first.str,
352 connectionName, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700353 }
354 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400356 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700357}
358
Ed Tanous23a21a12020-07-25 04:45:05 +0000359inline void findObjectManagerPathForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000360 const std::string& objectName, const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700361 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700362{
Ed Tanous62598e32023-07-17 17:06:25 -0700363 BMCWEB_LOG_DEBUG("Finding objectmanager for path {} on connection:{}",
364 objectName, connectionName);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700365 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000366 [transaction, objectName, connectionName](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800367 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800368 const dbus::utility::MapperGetAncestorsResponse& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400369 if (ec)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700370 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400371 BMCWEB_LOG_ERROR("GetAncestors on path {} failed with code {}",
372 objectName, ec);
373 return;
374 }
375
376 for (const auto& pathGroup : objects)
377 {
378 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700379 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400380 if (connectionGroup.first == connectionName)
381 {
382 // Found the object manager path for this resource.
383 getManagedObjectsForEnumerate(
384 objectName, pathGroup.first, connectionName,
385 transaction);
386 return;
387 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700388 }
389 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400390 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700391 "xyz.openbmc_project.ObjectMapper",
392 "/xyz/openbmc_project/object_mapper",
Ed Tanous81ce6092020-12-17 16:54:55 +0000393 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500394 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700395}
Ed Tanous64530012018-02-06 17:08:16 -0800396
Ed Tanous7c091622019-05-23 11:42:36 -0700397// Uses GetObject to add the object info about the target /enumerate path to
398// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600399// target path, and then continues on enumerating the rest of the tree.
Ed Tanousb5a76932020-09-29 16:16:58 -0700400inline void getObjectAndEnumerate(
401 const std::shared_ptr<InProgressEnumerateData>& transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600402{
George Liu2b731192023-01-11 16:27:13 +0800403 dbus::utility::getDbusObject(
404 transaction->objectPath, {},
405 [transaction](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800406 const dbus::utility::MapperGetObject& objects) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400407 if (ec)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600408 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400409 BMCWEB_LOG_ERROR("GetObject for path {} failed with code {}",
410 transaction->objectPath, ec);
411 return;
412 }
413
414 BMCWEB_LOG_DEBUG("GetObject for {} has {} entries",
415 transaction->objectPath, objects.size());
416 if (!objects.empty())
417 {
418 transaction->subtree->emplace_back(transaction->objectPath,
419 objects);
420 }
421
422 // Map indicating connection name, and the path where the object
423 // manager exists
424 boost::container::flat_map<
425 std::string, std::string, std::less<>,
426 std::vector<std::pair<std::string, std::string>>>
427 connections;
428
429 for (const auto& object : *(transaction->subtree))
430 {
431 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600432 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400433 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600434 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400435 BMCWEB_LOG_DEBUG("{} has interface {}",
436 connection.first, interface);
437 if (interface == "org.freedesktop.DBus.ObjectManager")
438 {
439 BMCWEB_LOG_DEBUG("found object manager path {}",
440 object.first);
441 connections[connection.first] = object.first;
442 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600443 }
444 }
445 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400446 BMCWEB_LOG_DEBUG("Got {} connections", connections.size());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600447
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400448 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600449 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400450 // If we already know where the object manager is, we don't
451 // need to search for it, we can call directly in to
452 // getManagedObjects
453 if (!connection.second.empty())
454 {
455 getManagedObjectsForEnumerate(
456 transaction->objectPath, connection.second,
457 connection.first, transaction);
458 }
459 else
460 {
461 // otherwise we need to find the object manager path
462 // before we can continue
463 findObjectManagerPathForEnumerate(
464 transaction->objectPath, connection.first, transaction);
465 }
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600466 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400467 });
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600468}
Ed Tanous64530012018-02-06 17:08:16 -0800469
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700470// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471struct InProgressActionData
472{
Lei YU28dd5ca2023-03-17 13:17:05 +0800473 explicit InProgressActionData(
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400474 const std::shared_ptr<bmcweb::AsyncResp>& res) : asyncResp(res)
Ed Tanous23a21a12020-07-25 04:45:05 +0000475 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 ~InProgressActionData()
477 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600478 // Methods could have been called across different owners
479 // and interfaces, where some calls failed and some passed.
480 //
481 // The rules for this are:
482 // * if no method was called - error
483 // * if a method failed and none passed - error
484 // (converse: if at least one method passed - OK)
485 // * for the method output:
486 // * if output processing didn't fail, return the data
487
488 // Only deal with method returns if nothing failed earlier
Lei YU28dd5ca2023-03-17 13:17:05 +0800489 if (asyncResp->res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700490 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600491 if (!methodPassed)
492 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500493 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600494 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800495 setErrorResponse(asyncResp->res,
496 boost::beast::http::status::not_found,
Matt Spinler16caaee2019-01-15 11:40:34 -0600497 methodNotFoundDesc, notFoundMsg);
498 }
499 }
500 else
501 {
502 if (outputFailed)
503 {
504 setErrorResponse(
Lei YU28dd5ca2023-03-17 13:17:05 +0800505 asyncResp->res,
506 boost::beast::http::status::internal_server_error,
Matt Spinler16caaee2019-01-15 11:40:34 -0600507 "Method output failure", methodOutputFailedMsg);
508 }
509 else
510 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800511 asyncResp->res.jsonValue["status"] = "ok";
512 asyncResp->res.jsonValue["message"] = "200 OK";
513 asyncResp->res.jsonValue["data"] = methodResponse;
Matt Spinler16caaee2019-01-15 11:40:34 -0600514 }
515 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700517 }
Ed Tanousecd6a3a2022-01-07 09:18:40 -0800518 InProgressActionData(const InProgressActionData&) = delete;
519 InProgressActionData(InProgressActionData&&) = delete;
520 InProgressActionData& operator=(const InProgressActionData&) = delete;
521 InProgressActionData& operator=(InProgressActionData&&) = delete;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700522
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500523 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 {
Lei YU28dd5ca2023-03-17 13:17:05 +0800525 setErrorResponse(asyncResp->res,
526 boost::beast::http::status::bad_request, desc,
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600527 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 }
Lei YU28dd5ca2023-03-17 13:17:05 +0800529 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 std::string path;
531 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600532 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600533 bool methodPassed = false;
534 bool methodFailed = false;
535 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600536 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600537 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700539};
540
Ed Tanous23a21a12020-07-25 04:45:05 +0000541inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700542{
543 std::vector<std::string> ret;
544 if (string.empty())
545 {
546 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700547 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700548 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 int containerDepth = 0;
550
551 for (std::string::const_iterator character = string.begin();
552 character != string.end(); character++)
553 {
554 ret.back() += *character;
555 switch (*character)
556 {
557 case ('a'):
558 break;
559 case ('('):
560 case ('{'):
561 containerDepth++;
562 break;
563 case ('}'):
564 case (')'):
565 containerDepth--;
566 if (containerDepth == 0)
567 {
568 if (character + 1 != string.end())
569 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700570 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 }
572 }
573 break;
574 default:
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 }
584 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600585
586 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700587}
588
Ed Tanous81ce6092020-12-17 16:54:55 +0000589inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
590 const nlohmann::json& inputJson)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591{
592 int r = 0;
Ed Tanous296579b2024-03-11 16:58:24 -0700593 BMCWEB_LOG_DEBUG("Converting {} to type: {}", inputJson, argType);
Ed Tanous81ce6092020-12-17 16:54:55 +0000594 const std::vector<std::string> argTypes = dbusArgSplit(argType);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700595
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 // Assume a single object for now.
Ed Tanous81ce6092020-12-17 16:54:55 +0000597 const nlohmann::json* j = &inputJson;
598 nlohmann::json::const_iterator jIt = inputJson.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700599
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500600 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 {
602 // If we are decoding multiple objects, grab the pointer to the
603 // iterator, and increment it for the next loop
604 if (argTypes.size() > 1)
605 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000606 if (jIt == inputJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
608 return -2;
609 }
610 j = &*jIt;
611 jIt++;
612 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500613 const int64_t* intValue = j->get_ptr<const int64_t*>();
614 const std::string* stringValue = j->get_ptr<const std::string*>();
615 const double* doubleValue = j->get_ptr<const double*>();
616 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 int64_t v = 0;
618 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700619
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 // Do some basic type conversions that make sense. uint can be
621 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700622 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500624 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700625 if (uintValue != nullptr)
626 {
627 v = static_cast<int64_t>(*uintValue);
628 intValue = &v;
629 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 }
Ed Tanous66664f22019-10-11 13:05:49 -0700631 if (doubleValue == 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 d = static_cast<double>(*uintValue);
637 doubleValue = &d;
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 {
Ed Tanous66664f22019-10-11 13:05:49 -0700642 if (intValue != nullptr)
643 {
644 d = static_cast<double>(*intValue);
645 doubleValue = &d;
646 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700647 }
648
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700651 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 {
653 return -1;
654 }
Ed Tanous271584a2019-07-09 16:24:22 -0700655 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500656 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 if (r < 0)
658 {
659 return r;
660 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700661 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700662 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
666 return -1;
667 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500668 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
669 (*intValue > std::numeric_limits<int32_t>::max()))
670 {
671 return -ERANGE;
672 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 int32_t i = static_cast<int32_t>(*intValue);
674 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 if (r < 0)
676 {
677 return r;
678 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700679 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 {
682 // lots of ways bool could be represented here. Try them all
Ed Tanouse662eae2022-01-25 10:39:19 -0800683 int boolInt = 0;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500686 if (*intValue == 1)
687 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800688 boolInt = 1;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500689 }
690 else if (*intValue == 0)
691 {
Ed Tanouse662eae2022-01-25 10:39:19 -0800692 boolInt = 0;
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500693 }
694 else
695 {
696 return -ERANGE;
697 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 }
699 else if (b != nullptr)
700 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600701 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700703 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 {
Ed Tanous18f8f602023-07-18 10:07:23 -0700705 if (!stringValue->empty())
706 {
707 if (stringValue->front() == 't' ||
708 stringValue->front() == 'T')
709 {
710 boolInt = 1;
711 }
712 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 }
714 else
715 {
716 return -1;
717 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700718 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 if (r < 0)
720 {
721 return r;
722 }
723 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700724 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700725 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700726 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700727 {
728 return -1;
729 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500730 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
731 (*intValue > std::numeric_limits<int16_t>::max()))
732 {
733 return -ERANGE;
734 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700735 int16_t n = static_cast<int16_t>(*intValue);
736 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700737 if (r < 0)
738 {
739 return r;
740 }
741 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700742 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700744 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 {
746 return -1;
747 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700748 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 if (r < 0)
750 {
751 return r;
752 }
753 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700754 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500756 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700757 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 {
759 return -1;
760 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000761 if (*uintValue > std::numeric_limits<uint8_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500762 {
763 return -ERANGE;
764 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700765 uint8_t y = static_cast<uint8_t>(*uintValue);
766 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700767 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700768 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700769 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500770 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700771 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 {
773 return -1;
774 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000775 if (*uintValue > std::numeric_limits<uint16_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500776 {
777 return -ERANGE;
778 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700779 uint16_t q = static_cast<uint16_t>(*uintValue);
780 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700782 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500784 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700785 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700786 {
787 return -1;
788 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000789 if (*uintValue > std::numeric_limits<uint32_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500790 {
791 return -ERANGE;
792 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700793 uint32_t u = static_cast<uint32_t>(*uintValue);
794 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700796 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500798 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700799 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 {
801 return -1;
802 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700803 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700805 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500807 if (doubleValue == nullptr)
808 {
809 return -1;
810 }
811 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
812 (*doubleValue > std::numeric_limits<double>::max()))
813 {
814 return -ERANGE;
815 }
Ed Tanous07900812024-05-06 15:41:30 -0700816 r = sd_bus_message_append_basic(m, argCode[0], doubleValue);
817 if (r < 0)
818 {
819 return r;
820 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700822 else if (argCode.starts_with("a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700824 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700825 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700826 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 if (r < 0)
828 {
829 return r;
830 }
831
Ed Tanous0dfeda62019-10-24 11:21:38 -0700832 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700834 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 if (r < 0)
836 {
837 return r;
838 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 }
840 sd_bus_message_close_container(m);
841 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700842 else if (argCode.starts_with("v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700844 std::string containedType = argCode.substr(1);
Ed Tanous62598e32023-07-17 17:06:25 -0700845 BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}",
846 argCode, containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700848 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700849 if (r < 0)
850 {
851 return r;
852 }
853
Ed Tanous81ce6092020-12-17 16:54:55 +0000854 r = convertJsonToDbus(m, containedType, inputJson);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 if (r < 0)
856 {
857 return r;
858 }
859
860 r = sd_bus_message_close_container(m);
861 if (r < 0)
862 {
863 return r;
864 }
865 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700866 else if (argCode.starts_with("(") && argCode.ends_with(")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300868 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700869 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700870 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800871 if (r < 0)
872 {
873 return r;
874 }
875
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 nlohmann::json::const_iterator it = j->begin();
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300877 for (const std::string& argCode2 : dbusArgSplit(containedType))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700878 {
879 if (it == j->end())
880 {
881 return -1;
882 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000883 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700884 if (r < 0)
885 {
886 return r;
887 }
888 it++;
889 }
890 r = sd_bus_message_close_container(m);
891 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700892 else if (argCode.starts_with("{") && argCode.ends_with("}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893 {
Mikhail Zhvakinf3477562023-07-25 17:03:32 +0300894 std::string containedType = argCode.substr(1, argCode.size() - 2);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700896 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800897 if (r < 0)
898 {
899 return r;
900 }
901
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700902 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700903 if (codes.size() != 2)
904 {
905 return -1;
906 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700907 const std::string& keyType = codes[0];
908 const std::string& valueType = codes[1];
Ed Tanous0bdda662023-08-03 17:27:34 -0700909 const nlohmann::json::object_t* arr =
910 j->get_ptr<const nlohmann::json::object_t*>();
911 if (arr == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700912 {
Ed Tanous0bdda662023-08-03 17:27:34 -0700913 return -1;
914 }
915 for (const auto& it : *arr)
916 {
917 r = convertJsonToDbus(m, keyType, it.first);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700918 if (r < 0)
919 {
920 return r;
921 }
922
Ed Tanous0bdda662023-08-03 17:27:34 -0700923 r = convertJsonToDbus(m, valueType, it.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 if (r < 0)
925 {
926 return r;
927 }
928 }
929 r = sd_bus_message_close_container(m);
930 }
931 else
932 {
933 return -2;
934 }
935 if (r < 0)
936 {
937 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700938 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700939
Ed Tanous1abe55e2018-09-05 08:30:59 -0700940 if (argTypes.size() > 1)
941 {
942 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700943 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700944 }
Matt Spinler127ea542019-01-14 11:04:28 -0600945
946 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700947}
948
Matt Spinlerd22a7132019-01-14 12:14:30 -0600949template <typename T>
Patrick Williams59d494e2022-07-22 19:26:55 -0500950int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500951 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600952{
953 T value;
Ed Tanousf79ce6a2024-03-20 12:27:06 -0700954 // When T == char*, this warning fires. Unclear how to resolve
955 // Given that sd-bus takes a void pointer to a char*, and that's
956 // Not something we can fix.
957 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600958 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
959 if (r < 0)
960 {
Ed Tanous62598e32023-07-17 17:06:25 -0700961 BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!",
962 typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -0600963 return r;
964 }
965
966 data = value;
967 return 0;
968}
969
Patrick Williams59d494e2022-07-22 19:26:55 -0500970int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m,
971 nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600972
Ed Tanous23a21a12020-07-25 04:45:05 +0000973inline int readDictEntryFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -0500974 sdbusplus::message_t& m,
Ed Tanous23a21a12020-07-25 04:45:05 +0000975 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600976{
977 std::vector<std::string> types = dbusArgSplit(typeCode);
978 if (types.size() != 2)
979 {
Ed Tanous62598e32023-07-17 17:06:25 -0700980 BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}",
981 types.size());
Matt Spinler6df8f992019-01-14 12:47:47 -0600982 return -1;
983 }
984
985 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
986 typeCode.c_str());
987 if (r < 0)
988 {
Ed Tanous62598e32023-07-17 17:06:25 -0700989 BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -0600990 return r;
991 }
992
993 nlohmann::json key;
994 r = convertDBusToJSON(types[0], m, key);
995 if (r < 0)
996 {
997 return r;
998 }
999
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001000 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -06001001 if (keyPtr == nullptr)
1002 {
1003 // json doesn't support non-string keys. If we hit this condition,
1004 // convert the result to a string so we can proceed
Ed Tanous71f52d92021-02-19 08:51:17 -08001005 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001006 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -07001007 // in theory this can't fail now, but lets be paranoid about it
1008 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -06001009 if (keyPtr == nullptr)
1010 {
1011 return -1;
1012 }
1013 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001014 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -06001015
1016 r = convertDBusToJSON(types[1], m, value);
1017 if (r < 0)
1018 {
1019 return r;
1020 }
1021
1022 r = sd_bus_message_exit_container(m.get());
1023 if (r < 0)
1024 {
Ed Tanous62598e32023-07-17 17:06:25 -07001025 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001026 return r;
1027 }
1028
1029 return 0;
1030}
1031
Ed Tanous23a21a12020-07-25 04:45:05 +00001032inline int readArrayFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001033 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -06001034{
1035 if (typeCode.size() < 2)
1036 {
Ed Tanous62598e32023-07-17 17:06:25 -07001037 BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode);
Matt Spinler6df8f992019-01-14 12:47:47 -06001038 return -1;
1039 }
1040
1041 std::string containedType = typeCode.substr(1);
1042
1043 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
1044 containedType.c_str());
1045 if (r < 0)
1046 {
Ed Tanous62598e32023-07-17 17:06:25 -07001047 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler6df8f992019-01-14 12:47:47 -06001048 return r;
1049 }
1050
Ed Tanous11ba3972022-07-11 09:50:41 -07001051 bool dict = containedType.starts_with("{") && containedType.ends_with("}");
Matt Spinler6df8f992019-01-14 12:47:47 -06001052
1053 if (dict)
1054 {
1055 // Remove the { }
1056 containedType = containedType.substr(1, containedType.size() - 2);
1057 data = nlohmann::json::object();
1058 }
1059 else
1060 {
1061 data = nlohmann::json::array();
1062 }
1063
1064 while (true)
1065 {
Ed Tanouse662eae2022-01-25 10:39:19 -08001066 r = sd_bus_message_at_end(m.get(), 0);
Matt Spinler6df8f992019-01-14 12:47:47 -06001067 if (r < 0)
1068 {
Ed Tanous62598e32023-07-17 17:06:25 -07001069 BMCWEB_LOG_ERROR("sd_bus_message_at_end failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001070 return r;
1071 }
1072
1073 if (r > 0)
1074 {
1075 break;
1076 }
1077
1078 // Dictionaries are only ever seen in an array
1079 if (dict)
1080 {
1081 r = readDictEntryFromMessage(containedType, m, data);
1082 if (r < 0)
1083 {
1084 return r;
1085 }
1086 }
1087 else
1088 {
1089 data.push_back(nlohmann::json());
1090
1091 r = convertDBusToJSON(containedType, m, data.back());
1092 if (r < 0)
1093 {
1094 return r;
1095 }
1096 }
1097 }
1098
1099 r = sd_bus_message_exit_container(m.get());
1100 if (r < 0)
1101 {
Ed Tanous62598e32023-07-17 17:06:25 -07001102 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler6df8f992019-01-14 12:47:47 -06001103 return r;
1104 }
1105
1106 return 0;
1107}
1108
Ed Tanous23a21a12020-07-25 04:45:05 +00001109inline int readStructFromMessage(const std::string& typeCode,
Patrick Williams59d494e2022-07-22 19:26:55 -05001110 sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001111{
1112 if (typeCode.size() < 3)
1113 {
Ed Tanous62598e32023-07-17 17:06:25 -07001114 BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode);
Matt Spinler75c6c672019-01-14 13:01:46 -06001115 return -1;
1116 }
1117
1118 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1119 std::vector<std::string> types = dbusArgSplit(containedTypes);
1120
1121 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1122 containedTypes.c_str());
1123 if (r < 0)
1124 {
Ed Tanous62598e32023-07-17 17:06:25 -07001125 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler75c6c672019-01-14 13:01:46 -06001126 return r;
1127 }
1128
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001129 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001130 {
1131 data.push_back(nlohmann::json());
1132 r = convertDBusToJSON(type, m, data.back());
1133 if (r < 0)
1134 {
1135 return r;
1136 }
1137 }
1138
1139 r = sd_bus_message_exit_container(m.get());
1140 if (r < 0)
1141 {
Ed Tanous62598e32023-07-17 17:06:25 -07001142 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed");
Matt Spinler75c6c672019-01-14 13:01:46 -06001143 return r;
1144 }
1145 return 0;
1146}
1147
Patrick Williams59d494e2022-07-22 19:26:55 -05001148inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001149{
Ed Tanous543f4402022-01-06 13:12:53 -08001150 const char* containerType = nullptr;
Ed Tanous99131cd2019-10-24 11:12:47 -07001151 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001152 if (r < 0)
1153 {
Ed Tanous62598e32023-07-17 17:06:25 -07001154 BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001155 return r;
1156 }
1157
1158 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1159 containerType);
1160 if (r < 0)
1161 {
Ed Tanous62598e32023-07-17 17:06:25 -07001162 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r);
Matt Spinler89c19702019-01-14 13:13:00 -06001163 return r;
1164 }
1165
1166 r = convertDBusToJSON(containerType, m, data);
1167 if (r < 0)
1168 {
1169 return r;
1170 }
1171
1172 r = sd_bus_message_exit_container(m.get());
1173 if (r < 0)
1174 {
Ed Tanous62598e32023-07-17 17:06:25 -07001175 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed");
Matt Spinler89c19702019-01-14 13:13:00 -06001176 return r;
1177 }
1178
1179 return 0;
1180}
1181
Ed Tanous23a21a12020-07-25 04:45:05 +00001182inline int convertDBusToJSON(const std::string& returnType,
Patrick Williams59d494e2022-07-22 19:26:55 -05001183 sdbusplus::message_t& m, nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001184{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001185 int r = 0;
1186 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1187
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001188 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001189 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001190 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001191 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001192 {
1193 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001194 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001195 }
1196
Ed Tanousd4d25792020-09-29 15:15:03 -07001197 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001198 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001199 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001200 if (r < 0)
1201 {
1202 return r;
1203 }
1204 }
1205 else if (typeCode == "b")
1206 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001207 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001208 if (r < 0)
1209 {
1210 return r;
1211 }
1212
Matt Spinlerf39420c2019-01-30 12:57:18 -06001213 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001214 }
1215 else if (typeCode == "u")
1216 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001217 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001218 if (r < 0)
1219 {
1220 return r;
1221 }
1222 }
1223 else if (typeCode == "i")
1224 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001225 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001226 if (r < 0)
1227 {
1228 return r;
1229 }
1230 }
1231 else if (typeCode == "x")
1232 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001233 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001234 if (r < 0)
1235 {
1236 return r;
1237 }
1238 }
1239 else if (typeCode == "t")
1240 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001241 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001242 if (r < 0)
1243 {
1244 return r;
1245 }
1246 }
1247 else if (typeCode == "n")
1248 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001249 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001250 if (r < 0)
1251 {
1252 return r;
1253 }
1254 }
1255 else if (typeCode == "q")
1256 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001257 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001258 if (r < 0)
1259 {
1260 return r;
1261 }
1262 }
1263 else if (typeCode == "y")
1264 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001265 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001266 if (r < 0)
1267 {
1268 return r;
1269 }
1270 }
1271 else if (typeCode == "d")
1272 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001273 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001274 if (r < 0)
1275 {
1276 return r;
1277 }
1278 }
1279 else if (typeCode == "h")
1280 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001281 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001282 if (r < 0)
1283 {
1284 return r;
1285 }
1286 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001287 else if (typeCode.starts_with("a"))
Matt Spinler6df8f992019-01-14 12:47:47 -06001288 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001289 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001290 if (r < 0)
1291 {
1292 return r;
1293 }
1294 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001295 else if (typeCode.starts_with("(") && typeCode.ends_with(")"))
Matt Spinler75c6c672019-01-14 13:01:46 -06001296 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001297 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001298 if (r < 0)
1299 {
1300 return r;
1301 }
1302 }
Ed Tanous11ba3972022-07-11 09:50:41 -07001303 else if (typeCode.starts_with("v"))
Matt Spinler89c19702019-01-14 13:13:00 -06001304 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001305 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001306 if (r < 0)
1307 {
1308 return r;
1309 }
1310 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001311 else
1312 {
Ed Tanous62598e32023-07-17 17:06:25 -07001313 BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001314 return -2;
1315 }
1316 }
1317
Matt Spinler16caaee2019-01-15 11:40:34 -06001318 return 0;
1319}
1320
Ed Tanousb5a76932020-09-29 16:16:58 -07001321inline void handleMethodResponse(
1322 const std::shared_ptr<InProgressActionData>& transaction,
Patrick Williams59d494e2022-07-22 19:26:55 -05001323 sdbusplus::message_t& m, const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001324{
Matt Spinler39a4e392019-01-15 11:53:13 -06001325 nlohmann::json data;
1326
1327 int r = convertDBusToJSON(returnType, m, data);
1328 if (r < 0)
1329 {
1330 transaction->outputFailed = true;
1331 return;
1332 }
1333
1334 if (data.is_null())
1335 {
1336 return;
1337 }
1338
1339 if (transaction->methodResponse.is_null())
1340 {
1341 transaction->methodResponse = std::move(data);
1342 return;
1343 }
1344
1345 // If they're both dictionaries or arrays, merge into one.
1346 // Otherwise, make the results an array with every result
1347 // an entry. Could also just fail in that case, but it
1348 // seems better to get the data back somehow.
Ed Tanous0bdda662023-08-03 17:27:34 -07001349 nlohmann::json::object_t* dataobj =
1350 data.get_ptr<nlohmann::json::object_t*>();
1351 if (transaction->methodResponse.is_object() && dataobj != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001352 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001353 for (auto& obj : *dataobj)
Matt Spinler39a4e392019-01-15 11:53:13 -06001354 {
1355 // Note: Will overwrite the data for a duplicate key
Ed Tanous0bdda662023-08-03 17:27:34 -07001356 transaction->methodResponse.emplace(obj.first,
1357 std::move(obj.second));
Matt Spinler39a4e392019-01-15 11:53:13 -06001358 }
1359 return;
1360 }
1361
Ed Tanous0bdda662023-08-03 17:27:34 -07001362 nlohmann::json::array_t* dataarr = data.get_ptr<nlohmann::json::array_t*>();
1363 if (transaction->methodResponse.is_array() && dataarr != nullptr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001364 {
Ed Tanous0bdda662023-08-03 17:27:34 -07001365 for (auto& obj : *dataarr)
Matt Spinler39a4e392019-01-15 11:53:13 -06001366 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001367 transaction->methodResponse.emplace_back(std::move(obj));
Matt Spinler39a4e392019-01-15 11:53:13 -06001368 }
1369 return;
1370 }
1371
1372 if (!transaction->convertedToArray)
1373 {
1374 // They are different types. May as well turn them into an array
1375 nlohmann::json j = std::move(transaction->methodResponse);
1376 transaction->methodResponse = nlohmann::json::array();
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001377 transaction->methodResponse.emplace_back(std::move(j));
1378 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001379 transaction->convertedToArray = true;
1380 }
1381 else
1382 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -05001383 transaction->methodResponse.emplace_back(std::move(data));
Matt Spinler39a4e392019-01-15 11:53:13 -06001384 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001385}
1386
Ed Tanousb5a76932020-09-29 16:16:58 -07001387inline void findActionOnInterface(
1388 const std::shared_ptr<InProgressActionData>& transaction,
1389 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001390{
Ed Tanous62598e32023-07-17 17:06:25 -07001391 BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001392 crow::connections::systemBus->async_method_call(
1393 [transaction, connectionName{std::string(connectionName)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001394 const boost::system::error_code& ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001395 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001396 BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml);
1397 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001398 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001399 BMCWEB_LOG_ERROR(
1400 "Introspect call failed with error: {} on process: {}",
1401 ec.message(), connectionName);
1402 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001403 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001404 tinyxml2::XMLDocument doc;
1405
1406 doc.Parse(introspectXml.data(), introspectXml.size());
1407 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
1408 if (pRoot == nullptr)
1409 {
1410 BMCWEB_LOG_ERROR("XML document failed to parse {}",
1411 connectionName);
1412 return;
1413 }
1414 tinyxml2::XMLElement* interfaceNode =
1415 pRoot->FirstChildElement("interface");
1416 while (interfaceNode != nullptr)
1417 {
1418 const char* thisInterfaceName =
1419 interfaceNode->Attribute("name");
1420 if (thisInterfaceName != nullptr)
1421 {
1422 if (!transaction->interfaceName.empty() &&
1423 (transaction->interfaceName != thisInterfaceName))
1424 {
1425 interfaceNode =
1426 interfaceNode->NextSiblingElement("interface");
1427 continue;
1428 }
1429
1430 tinyxml2::XMLElement* methodNode =
1431 interfaceNode->FirstChildElement("method");
1432 while (methodNode != nullptr)
1433 {
1434 const char* thisMethodName =
1435 methodNode->Attribute("name");
1436 BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName);
1437 if (thisMethodName != nullptr &&
1438 thisMethodName == transaction->methodName)
1439 {
1440 BMCWEB_LOG_DEBUG(
1441 "Found method named {} on interface {}",
1442 thisMethodName, thisInterfaceName);
1443 sdbusplus::message_t m =
1444 crow::connections::systemBus->new_method_call(
1445 connectionName.c_str(),
1446 transaction->path.c_str(),
1447 thisInterfaceName,
1448 transaction->methodName.c_str());
1449
1450 tinyxml2::XMLElement* argumentNode =
1451 methodNode->FirstChildElement("arg");
1452
1453 std::string returnType;
1454
1455 // Find the output type
1456 while (argumentNode != nullptr)
1457 {
1458 const char* argDirection =
1459 argumentNode->Attribute("direction");
1460 const char* argType =
1461 argumentNode->Attribute("type");
1462 if (argDirection != nullptr &&
1463 argType != nullptr &&
1464 std::string(argDirection) == "out")
1465 {
1466 returnType = argType;
1467 break;
1468 }
1469 argumentNode =
1470 argumentNode->NextSiblingElement("arg");
1471 }
1472
1473 auto argIt = transaction->arguments.begin();
1474
1475 argumentNode = methodNode->FirstChildElement("arg");
1476
1477 while (argumentNode != nullptr)
1478 {
1479 const char* argDirection =
1480 argumentNode->Attribute("direction");
1481 const char* argType =
1482 argumentNode->Attribute("type");
1483 if (argDirection != nullptr &&
1484 argType != nullptr &&
1485 std::string(argDirection) == "in")
1486 {
1487 if (argIt == transaction->arguments.end())
1488 {
1489 transaction->setErrorStatus(
1490 "Invalid method args");
1491 return;
1492 }
1493 if (convertJsonToDbus(m.get(),
1494 std::string(argType),
1495 *argIt) < 0)
1496 {
1497 transaction->setErrorStatus(
1498 "Invalid method arg type");
1499 return;
1500 }
1501
1502 argIt++;
1503 }
1504 argumentNode =
1505 argumentNode->NextSiblingElement("arg");
1506 }
1507
1508 crow::connections::systemBus->async_send(
1509 m, [transaction, returnType](
1510 const boost::system::error_code& ec2,
1511 sdbusplus::message_t& m2) {
1512 if (ec2)
1513 {
1514 transaction->methodFailed = true;
1515 const sd_bus_error* e = m2.get_error();
1516
1517 if (e != nullptr)
1518 {
1519 setErrorResponse(
1520 transaction->asyncResp->res,
1521 boost::beast::http::status::
1522 bad_request,
1523 e->name, e->message);
1524 }
1525 else
1526 {
1527 setErrorResponse(
1528 transaction->asyncResp->res,
1529 boost::beast::http::status::
1530 bad_request,
1531 "Method call failed",
1532 methodFailedMsg);
1533 }
1534 return;
1535 }
1536 transaction->methodPassed = true;
1537
1538 handleMethodResponse(transaction, m2,
1539 returnType);
1540 });
1541 break;
1542 }
1543 methodNode = methodNode->NextSiblingElement("method");
1544 }
1545 }
1546 interfaceNode = interfaceNode->NextSiblingElement("interface");
1547 }
1548 },
Ed Tanous1abe55e2018-09-05 08:30:59 -07001549 connectionName, transaction->path,
1550 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001551}
1552
zhanghch058d1b46d2021-04-01 11:18:24 +08001553inline void handleAction(const crow::Request& req,
1554 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001555 const std::string& objectPath,
1556 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001557{
Ed Tanous62598e32023-07-17 17:06:25 -07001558 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath,
1559 methodName);
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001560 nlohmann::json requestDbusData;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001561
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001562 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1563 if (ret == JsonParseResult::BadContentType)
1564 {
1565 setErrorResponse(asyncResp->res,
1566 boost::beast::http::status::unsupported_media_type,
1567 invalidContentType, unsupportedMediaMsg);
1568 return;
1569 }
1570 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001571 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001572 setErrorResponse(asyncResp->res,
1573 boost::beast::http::status::bad_request, noJsonDesc,
1574 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001575 return;
1576 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001577 nlohmann::json::iterator data = requestDbusData.find("data");
1578 if (data == requestDbusData.end())
1579 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001580 setErrorResponse(asyncResp->res,
1581 boost::beast::http::status::bad_request, noJsonDesc,
1582 badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001583 return;
1584 }
1585
1586 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001587 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001588 setErrorResponse(asyncResp->res,
1589 boost::beast::http::status::bad_request, noJsonDesc,
1590 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001591 return;
1592 }
Lei YU28dd5ca2023-03-17 13:17:05 +08001593 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001594
1595 transaction->path = objectPath;
1596 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001597 transaction->arguments = std::move(*data);
George Liu2b731192023-01-11 16:27:13 +08001598 dbus::utility::getDbusObject(
1599 objectPath, {},
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600 [transaction](
George Liu2b731192023-01-11 16:27:13 +08001601 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001602 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1603 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001604 if (ec || interfaceNames.empty())
1605 {
1606 BMCWEB_LOG_ERROR("Can't find object");
1607 setErrorResponse(transaction->asyncResp->res,
1608 boost::beast::http::status::not_found,
1609 notFoundDesc, notFoundMsg);
1610 return;
1611 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001612
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001613 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)",
1614 interfaceNames.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001615
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001616 for (const std::pair<std::string, std::vector<std::string>>&
1617 object : interfaceNames)
1618 {
1619 findActionOnInterface(transaction, object.first);
1620 }
1621 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001622}
1623
zhanghch058d1b46d2021-04-01 11:18:24 +08001624inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1625 const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001626{
Ed Tanous62598e32023-07-17 17:06:25 -07001627 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06001628
George Liu2b731192023-01-11 16:27:13 +08001629 dbus::utility::getDbusObject(
1630 objectPath, {},
zhanghch058d1b46d2021-04-01 11:18:24 +08001631 [asyncResp, objectPath](
George Liu2b731192023-01-11 16:27:13 +08001632 const boost::system::error_code& ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001633 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1634 interfaceNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001635 if (ec || interfaceNames.empty())
1636 {
1637 BMCWEB_LOG_ERROR("Can't find object");
1638 setErrorResponse(asyncResp->res,
1639 boost::beast::http::status::method_not_allowed,
1640 methodNotAllowedDesc, methodNotAllowedMsg);
1641 return;
1642 }
Matt Spinlerde818812018-12-11 16:39:20 -06001643
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001644 auto transaction =
1645 std::make_shared<InProgressActionData>(asyncResp);
1646 transaction->path = objectPath;
1647 transaction->methodName = "Delete";
1648 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
Matt Spinlerde818812018-12-11 16:39:20 -06001649
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001650 for (const std::pair<std::string, std::vector<std::string>>&
1651 object : interfaceNames)
1652 {
1653 findActionOnInterface(transaction, object.first);
1654 }
1655 });
Matt Spinlerde818812018-12-11 16:39:20 -06001656}
1657
zhanghch058d1b46d2021-04-01 11:18:24 +08001658inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1659 const std::string& objectPath, int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001660{
George Liu7a1dbc42022-12-07 16:03:22 +08001661 dbus::utility::getSubTreePaths(
1662 objectPath, depth, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001663 [asyncResp](
George Liu7a1dbc42022-12-07 16:03:22 +08001664 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001665 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001666 if (ec)
1667 {
1668 setErrorResponse(asyncResp->res,
1669 boost::beast::http::status::not_found,
1670 notFoundDesc, notFoundMsg);
1671 }
1672 else
1673 {
1674 asyncResp->res.jsonValue["status"] = "ok";
1675 asyncResp->res.jsonValue["message"] = "200 OK";
1676 asyncResp->res.jsonValue["data"] = objectPaths;
1677 }
1678 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001679}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001680
zhanghch058d1b46d2021-04-01 11:18:24 +08001681inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1682 const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001683{
Ed Tanous62598e32023-07-17 17:06:25 -07001684 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07001685
Ed Tanous14766872022-03-15 10:44:42 -07001686 asyncResp->res.jsonValue["message"] = "200 OK";
1687 asyncResp->res.jsonValue["status"] = "ok";
1688 asyncResp->res.jsonValue["data"] = nlohmann::json::object();
Ed Tanous049a0512018-11-01 13:58:42 -07001689
George Liue99073f2022-12-09 11:06:16 +08001690 dbus::utility::getSubTree(
1691 objectPath, 0, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001692 [objectPath, asyncResp](
George Liue99073f2022-12-09 11:06:16 +08001693 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001694 const dbus::utility::MapperGetSubTreeResponse& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001695 auto transaction = std::make_shared<InProgressEnumerateData>(
1696 objectPath, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001697
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001698 transaction->subtree =
1699 std::make_shared<dbus::utility::MapperGetSubTreeResponse>(
1700 objectNames);
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001701
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001702 if (ec)
1703 {
1704 BMCWEB_LOG_ERROR("GetSubTree failed on {}",
1705 transaction->objectPath);
1706 setErrorResponse(transaction->asyncResp->res,
1707 boost::beast::http::status::not_found,
1708 notFoundDesc, notFoundMsg);
1709 return;
1710 }
Ed Tanous64530012018-02-06 17:08:16 -08001711
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001712 // Add the data for the path passed in to the results
1713 // as if GetSubTree returned it, and continue on enumerating
1714 getObjectAndEnumerate(transaction);
1715 });
Ed Tanous64530012018-02-06 17:08:16 -08001716}
Ed Tanous911ac312017-08-15 09:37:42 -07001717
zhanghch058d1b46d2021-04-01 11:18:24 +08001718inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1719 std::string& objectPath, std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001720{
Ed Tanous62598e32023-07-17 17:06:25 -07001721 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001722 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001723 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001724
Ed Tanous1abe55e2018-09-05 08:30:59 -07001725 std::shared_ptr<std::string> path =
1726 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001727
George Liu2b731192023-01-11 16:27:13 +08001728 dbus::utility::getDbusObject(
1729 *path, {},
Ed Tanousb9d36b42022-02-26 21:42:46 -08001730 [asyncResp, path,
George Liu2b731192023-01-11 16:27:13 +08001731 propertyName](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001732 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001733 if (ec || objectNames.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001735 setErrorResponse(asyncResp->res,
1736 boost::beast::http::status::not_found,
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001737 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001738 return;
1739 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001740 std::shared_ptr<nlohmann::json> response =
1741 std::make_shared<nlohmann::json>(nlohmann::json::object());
1742 // The mapper should never give us an empty interface names
1743 // list, but check anyway
1744 for (const std::pair<std::string, std::vector<std::string>>&
1745 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001747 const std::vector<std::string>& interfaceNames =
1748 connection.second;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001749
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001750 if (interfaceNames.empty())
1751 {
1752 setErrorResponse(asyncResp->res,
1753 boost::beast::http::status::not_found,
1754 notFoundDesc, notFoundMsg);
1755 return;
1756 }
1757
1758 for (const std::string& interface : interfaceNames)
1759 {
1760 sdbusplus::message_t m =
1761 crow::connections::systemBus->new_method_call(
1762 connection.first.c_str(), path->c_str(),
1763 "org.freedesktop.DBus.Properties", "GetAll");
1764 m.append(interface);
1765 crow::connections::systemBus->async_send(
1766 m, [asyncResp, response,
1767 propertyName](const boost::system::error_code& ec2,
1768 sdbusplus::message_t& msg) {
1769 if (ec2)
1770 {
1771 BMCWEB_LOG_ERROR("Bad dbus request error: {}",
1772 ec2);
1773 }
1774 else
1775 {
1776 nlohmann::json properties;
1777 int r =
1778 convertDBusToJSON("a{sv}", msg, properties);
1779 if (r < 0)
Patrick Williams5a39f772023-10-20 11:20:21 -05001780 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001781 BMCWEB_LOG_ERROR(
1782 "convertDBusToJSON failed");
Patrick Williams5a39f772023-10-20 11:20:21 -05001783 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001784 else
Patrick Williams5a39f772023-10-20 11:20:21 -05001785 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001786 nlohmann::json::object_t* obj =
1787 properties.get_ptr<
1788 nlohmann::json::object_t*>();
1789 if (obj == nullptr)
1790 {
1791 return;
1792 }
1793 for (auto& prop : *obj)
1794 {
1795 // if property name is empty, or
1796 // matches our search query, add it
1797 // to the response json
1798
1799 if (propertyName->empty())
1800 {
1801 (*response)[prop.first] =
1802 std::move(prop.second);
1803 }
1804 else if (prop.first == *propertyName)
1805 {
1806 *response = std::move(prop.second);
1807 }
1808 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001809 }
1810 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001811 if (response.use_count() == 1)
1812 {
1813 if (!propertyName->empty() && response->empty())
1814 {
1815 setErrorResponse(
1816 asyncResp->res,
1817 boost::beast::http::status::not_found,
1818 propNotFoundDesc, notFoundMsg);
1819 }
1820 else
1821 {
1822 asyncResp->res.jsonValue["status"] = "ok";
1823 asyncResp->res.jsonValue["message"] =
1824 "200 OK";
1825 asyncResp->res.jsonValue["data"] =
1826 *response;
1827 }
1828 }
1829 });
1830 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001831 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001832 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001833}
1834
Ed Tanous1abe55e2018-09-05 08:30:59 -07001835struct AsyncPutRequest
1836{
Ed Tanous4e23a442022-06-06 09:57:26 -07001837 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) :
zhanghch058d1b46d2021-04-01 11:18:24 +08001838 asyncResp(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001839 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001840 ~AsyncPutRequest()
1841 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001842 if (asyncResp->res.jsonValue.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001843 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001844 setErrorResponse(asyncResp->res,
1845 boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001846 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001847 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001848 }
1849
Ed Tanousecd6a3a2022-01-07 09:18:40 -08001850 AsyncPutRequest(const AsyncPutRequest&) = delete;
1851 AsyncPutRequest(AsyncPutRequest&&) = delete;
1852 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete;
1853 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete;
1854
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001855 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001856 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001857 setErrorResponse(asyncResp->res,
1858 boost::beast::http::status::internal_server_error,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001859 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001860 }
1861
zhanghch058d1b46d2021-04-01 11:18:24 +08001862 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001863 std::string objectPath;
1864 std::string propertyName;
1865 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001866};
1867
zhanghch058d1b46d2021-04-01 11:18:24 +08001868inline void handlePut(const crow::Request& req,
1869 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous23a21a12020-07-25 04:45:05 +00001870 const std::string& objectPath,
1871 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001873 if (destProperty.empty())
1874 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001875 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden,
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001876 forbiddenResDesc, forbiddenMsg);
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001877 return;
1878 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001879 nlohmann::json requestDbusData;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001880
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001881 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
1882 if (ret == JsonParseResult::BadContentType)
1883 {
1884 setErrorResponse(asyncResp->res,
1885 boost::beast::http::status::unsupported_media_type,
1886 invalidContentType, unsupportedMediaMsg);
1887 return;
1888 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001889
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01001890 if (ret != JsonParseResult::Success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001891 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001892 setErrorResponse(asyncResp->res,
1893 boost::beast::http::status::bad_request, noJsonDesc,
1894 badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001895 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001896 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001897
Nan Zhoub2ec0ce2022-06-02 23:57:38 +00001898 auto propertyIt = requestDbusData.find("data");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001899 if (propertyIt == requestDbusData.end())
1900 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001901 setErrorResponse(asyncResp->res,
1902 boost::beast::http::status::bad_request, noJsonDesc,
1903 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001904 return;
1905 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001906 const nlohmann::json& propertySetValue = *propertyIt;
zhanghch058d1b46d2021-04-01 11:18:24 +08001907 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001908 transaction->objectPath = objectPath;
1909 transaction->propertyName = destProperty;
1910 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001911
George Liu2b731192023-01-11 16:27:13 +08001912 dbus::utility::getDbusObject(
1913 transaction->objectPath, {},
1914 [transaction](const boost::system::error_code& ec2,
Ed Tanousb9d36b42022-02-26 21:42:46 -08001915 const dbus::utility::MapperGetObject& objectNames) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001916 if (!ec2 && objectNames.empty())
1917 {
1918 setErrorResponse(transaction->asyncResp->res,
1919 boost::beast::http::status::not_found,
1920 propNotFoundDesc, notFoundMsg);
1921 return;
1922 }
Ed Tanous911ac312017-08-15 09:37:42 -07001923
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001924 for (const std::pair<std::string, std::vector<std::string>>&
1925 connection : objectNames)
1926 {
1927 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001928
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001929 crow::connections::systemBus->async_method_call(
1930 [connectionName{std::string(connectionName)},
1931 transaction](const boost::system::error_code& ec3,
1932 const std::string& introspectXml) {
1933 if (ec3)
Ed Tanousb0b61522024-08-06 10:20:49 -07001934 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001935 BMCWEB_LOG_ERROR(
1936 "Introspect call failed with error: {} on process: {}",
1937 ec3.message(), connectionName);
1938 transaction->setErrorStatus("Unexpected Error");
1939 return;
Ed Tanousb0b61522024-08-06 10:20:49 -07001940 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001941 tinyxml2::XMLDocument doc;
1942
1943 doc.Parse(introspectXml.c_str());
1944 tinyxml2::XMLNode* pRoot =
1945 doc.FirstChildElement("node");
1946 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001947 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001948 BMCWEB_LOG_ERROR("XML document failed to parse: {}",
1949 introspectXml);
1950 transaction->setErrorStatus("Unexpected Error");
1951 return;
1952 }
1953 tinyxml2::XMLElement* ifaceNode =
1954 pRoot->FirstChildElement("interface");
1955 while (ifaceNode != nullptr)
1956 {
1957 const char* interfaceName =
1958 ifaceNode->Attribute("name");
1959 BMCWEB_LOG_DEBUG("found interface {}",
1960 interfaceName);
1961 tinyxml2::XMLElement* propNode =
1962 ifaceNode->FirstChildElement("property");
1963 while (propNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001964 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001965 const char* propertyName =
1966 propNode->Attribute("name");
1967 if (propertyName == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001968 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001969 BMCWEB_LOG_DEBUG(
1970 "Couldn't find name property");
1971 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001972 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001973 BMCWEB_LOG_DEBUG("Found property {}",
1974 propertyName);
1975 if (propertyName == transaction->propertyName)
Ed Tanous002d39b2022-05-31 08:59:27 -07001976 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001977 const char* argType =
1978 propNode->Attribute("type");
1979 if (argType != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07001980 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04001981 sdbusplus::message_t m =
1982 crow::connections::systemBus
1983 ->new_method_call(
1984 connectionName.c_str(),
1985 transaction->objectPath
1986 .c_str(),
1987 "org.freedesktop.DBus."
1988 "Properties",
1989 "Set");
1990 m.append(interfaceName,
1991 transaction->propertyName);
1992 int r = sd_bus_message_open_container(
1993 m.get(), SD_BUS_TYPE_VARIANT,
1994 argType);
1995 if (r < 0)
1996 {
1997 transaction->setErrorStatus(
1998 "Unexpected Error");
1999 return;
2000 }
2001 r = convertJsonToDbus(
2002 m.get(), argType,
2003 transaction->propertyValue);
2004 if (r < 0)
2005 {
2006 if (r == -ERANGE)
2007 {
2008 transaction->setErrorStatus(
2009 "Provided property value "
2010 "is out of range for the "
2011 "property type");
2012 }
2013 else
2014 {
2015 transaction->setErrorStatus(
2016 "Invalid arg type");
2017 }
2018 return;
2019 }
2020 r = sd_bus_message_close_container(
2021 m.get());
2022 if (r < 0)
2023 {
2024 transaction->setErrorStatus(
2025 "Unexpected Error");
2026 return;
2027 }
2028 crow::connections::systemBus
2029 ->async_send(
2030 m,
2031 [transaction](
2032 const boost::system::
2033 error_code& ec,
2034 sdbusplus::message_t& m2) {
2035 BMCWEB_LOG_DEBUG("sent");
2036 if (ec)
2037 {
2038 const sd_bus_error* e =
2039 m2.get_error();
2040 setErrorResponse(
2041 transaction
2042 ->asyncResp
2043 ->res,
2044 boost::beast::http::
2045 status::
2046 forbidden,
2047 (e) != nullptr
2048 ? e->name
2049 : ec.category()
2050 .name(),
2051 (e) != nullptr
2052 ? e->message
2053 : ec.message());
2054 }
2055 else
2056 {
2057 transaction->asyncResp
2058 ->res.jsonValue
2059 ["status"] =
2060 "ok";
2061 transaction->asyncResp
2062 ->res.jsonValue
2063 ["message"] =
2064 "200 OK";
2065 transaction->asyncResp
2066 ->res
2067 .jsonValue["data"] =
2068 nullptr;
2069 }
2070 });
Ed Tanous002d39b2022-05-31 08:59:27 -07002071 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002072 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002073 propNode =
2074 propNode->NextSiblingElement("property");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002075 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002076 ifaceNode =
2077 ifaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002078 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002079 },
2080 connectionName, transaction->objectPath,
2081 "org.freedesktop.DBus.Introspectable", "Introspect");
2082 }
2083 });
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07002084}
Ed Tanous1abe55e2018-09-05 08:30:59 -07002085
zhanghch058d1b46d2021-04-01 11:18:24 +08002086inline void handleDBusUrl(const crow::Request& req,
2087 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002088 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002089{
Ed Tanous049a0512018-11-01 13:58:42 -07002090 // If accessing a single attribute, fill in and update objectPath,
2091 // otherwise leave destProperty blank
Ed Tanouse05aec52022-01-25 10:28:56 -08002092 std::string destProperty;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002093 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002094 size_t attrPosition = objectPath.find(attrSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002095 if (attrPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002096 {
2097 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2098 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002099 objectPath.resize(attrPosition);
Ed Tanous049a0512018-11-01 13:58:42 -07002100 }
2101
Ed Tanousb41187f2019-10-24 16:30:02 -07002102 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002103 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002104 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002105 size_t actionPosition = objectPath.find(actionSeperator);
Ed Tanous71d5d8d2022-01-25 11:04:33 -08002106 if (actionPosition != std::string::npos)
Ed Tanous049a0512018-11-01 13:58:42 -07002107 {
2108 std::string postProperty =
2109 objectPath.substr((actionPosition + strlen(actionSeperator)),
2110 objectPath.length());
Ed Tanous7f57f192022-12-20 09:53:40 -08002111 objectPath.resize(actionPosition);
zhanghch058d1b46d2021-04-01 11:18:24 +08002112 handleAction(req, asyncResp, objectPath, postProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002113 return;
2114 }
2115 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002116 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002117 {
Ed Tanous11ba3972022-07-11 09:50:41 -07002118 if (objectPath.ends_with("/enumerate"))
Ed Tanous049a0512018-11-01 13:58:42 -07002119 {
2120 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2121 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002122 handleEnumerate(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002123 }
Ed Tanous11ba3972022-07-11 09:50:41 -07002124 else if (objectPath.ends_with("/list"))
Ed Tanous049a0512018-11-01 13:58:42 -07002125 {
2126 objectPath.erase(objectPath.end() - sizeof("list"),
2127 objectPath.end());
zhanghch058d1b46d2021-04-01 11:18:24 +08002128 handleList(asyncResp, objectPath);
Ed Tanous049a0512018-11-01 13:58:42 -07002129 }
2130 else
2131 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002132 // Trim any trailing "/" at the end
Ed Tanous11ba3972022-07-11 09:50:41 -07002133 if (objectPath.ends_with("/"))
Ed Tanousf839dfe2018-11-12 11:11:15 -08002134 {
2135 objectPath.pop_back();
zhanghch058d1b46d2021-04-01 11:18:24 +08002136 handleList(asyncResp, objectPath, 1);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002137 }
2138 else
2139 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002140 handleGet(asyncResp, objectPath, destProperty);
Ed Tanousf839dfe2018-11-12 11:11:15 -08002141 }
Ed Tanous049a0512018-11-01 13:58:42 -07002142 }
2143 return;
2144 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002145 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002146 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002147 handlePut(req, asyncResp, objectPath, destProperty);
Ed Tanous049a0512018-11-01 13:58:42 -07002148 return;
2149 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002150 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002151 {
zhanghch058d1b46d2021-04-01 11:18:24 +08002152 handleDelete(asyncResp, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002153 return;
2154 }
Ed Tanous049a0512018-11-01 13:58:42 -07002155
zhanghch058d1b46d2021-04-01 11:18:24 +08002156 setErrorResponse(asyncResp->res,
2157 boost::beast::http::status::method_not_allowed,
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002158 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002159}
2160
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002161inline void handleBusSystemPost(
2162 const crow::Request& req,
2163 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2164 const std::string& processName, const std::string& requestedPath)
Ed Tanous1656b292022-05-04 11:33:42 -07002165{
2166 std::vector<std::string> strs;
Ed Tanous50ebd4a2023-01-19 19:03:17 -08002167
2168 bmcweb::split(strs, requestedPath, '/');
Ed Tanous1656b292022-05-04 11:33:42 -07002169 std::string objectPath;
2170 std::string interfaceName;
2171 std::string methodName;
2172 auto it = strs.begin();
2173 if (it == strs.end())
2174 {
2175 objectPath = "/";
2176 }
2177 while (it != strs.end())
2178 {
2179 // Check if segment contains ".". If it does, it must be an
2180 // interface
2181 if (it->find(".") != std::string::npos)
2182 {
2183 break;
2184 // This check is necessary as the trailing slash gets
2185 // parsed as part of our <path> specifier above, which
2186 // causes the normal trailing backslash redirector to
2187 // fail.
2188 }
2189 if (!it->empty())
2190 {
2191 objectPath += "/" + *it;
2192 }
2193 it++;
2194 }
2195 if (it != strs.end())
2196 {
2197 interfaceName = *it;
2198 it++;
2199
2200 // after interface, we might have a method name
2201 if (it != strs.end())
2202 {
2203 methodName = *it;
2204 it++;
2205 }
2206 }
2207 if (it != strs.end())
2208 {
2209 // if there is more levels past the method name, something
2210 // went wrong, return not found
2211 asyncResp->res.result(boost::beast::http::status::not_found);
2212 return;
2213 }
2214 if (interfaceName.empty())
2215 {
2216 crow::connections::systemBus->async_method_call(
2217 [asyncResp, processName,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002218 objectPath](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002219 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002220 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002221 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002222 BMCWEB_LOG_ERROR(
2223 "Introspect call failed with error: {} on process: {} path: {}",
2224 ec.message(), processName, objectPath);
2225 return;
2226 }
2227 tinyxml2::XMLDocument doc;
2228
2229 doc.Parse(introspectXml.c_str());
2230 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2231 if (pRoot == nullptr)
2232 {
2233 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2234 processName, objectPath);
2235 asyncResp->res.jsonValue["status"] = "XML parse error";
2236 asyncResp->res.result(
2237 boost::beast::http::status::internal_server_error);
2238 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002239 }
2240
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002241 BMCWEB_LOG_DEBUG("{}", introspectXml);
2242 asyncResp->res.jsonValue["status"] = "ok";
2243 asyncResp->res.jsonValue["bus_name"] = processName;
2244 asyncResp->res.jsonValue["object_path"] = objectPath;
2245
2246 nlohmann::json& interfacesArray =
2247 asyncResp->res.jsonValue["interfaces"];
2248 interfacesArray = nlohmann::json::array();
2249 tinyxml2::XMLElement* interface =
2250 pRoot->FirstChildElement("interface");
2251
2252 while (interface != nullptr)
2253 {
2254 const char* ifaceName = interface->Attribute("name");
2255 if (ifaceName != nullptr)
2256 {
2257 nlohmann::json::object_t interfaceObj;
2258 interfaceObj["name"] = ifaceName;
2259 interfacesArray.emplace_back(std::move(interfaceObj));
2260 }
2261
2262 interface = interface->NextSiblingElement("interface");
2263 }
2264 },
Ed Tanous1656b292022-05-04 11:33:42 -07002265 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2266 "Introspect");
2267 }
2268 else if (methodName.empty())
2269 {
2270 crow::connections::systemBus->async_method_call(
2271 [asyncResp, processName, objectPath,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08002272 interfaceName](const boost::system::error_code& ec,
Ed Tanous1656b292022-05-04 11:33:42 -07002273 const std::string& introspectXml) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002274 if (ec)
Ed Tanous1656b292022-05-04 11:33:42 -07002275 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002276 BMCWEB_LOG_ERROR(
2277 "Introspect call failed with error: {} on process: {} path: {}",
2278 ec.message(), processName, objectPath);
2279 return;
2280 }
2281 tinyxml2::XMLDocument doc;
2282
2283 doc.Parse(introspectXml.data(), introspectXml.size());
2284 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
2285 if (pRoot == nullptr)
2286 {
2287 BMCWEB_LOG_ERROR("XML document failed to parse {} {}",
2288 processName, objectPath);
2289 asyncResp->res.result(
2290 boost::beast::http::status::internal_server_error);
2291 return;
Ed Tanous1656b292022-05-04 11:33:42 -07002292 }
Ed Tanous14766872022-03-15 10:44:42 -07002293
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002294 asyncResp->res.jsonValue["status"] = "ok";
2295 asyncResp->res.jsonValue["bus_name"] = processName;
2296 asyncResp->res.jsonValue["interface"] = interfaceName;
2297 asyncResp->res.jsonValue["object_path"] = objectPath;
Ed Tanous1656b292022-05-04 11:33:42 -07002298
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002299 nlohmann::json& methodsArray =
2300 asyncResp->res.jsonValue["methods"];
2301 methodsArray = nlohmann::json::array();
2302
2303 nlohmann::json& signalsArray =
2304 asyncResp->res.jsonValue["signals"];
2305 signalsArray = nlohmann::json::array();
2306
2307 nlohmann::json& propertiesObj =
2308 asyncResp->res.jsonValue["properties"];
2309 propertiesObj = nlohmann::json::object();
2310
2311 // if we know we're the only call, build the
2312 // json directly
2313 tinyxml2::XMLElement* interface =
2314 pRoot->FirstChildElement("interface");
2315 while (interface != nullptr)
Ed Tanous1656b292022-05-04 11:33:42 -07002316 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002317 const char* ifaceName = interface->Attribute("name");
2318
2319 if (ifaceName != nullptr && ifaceName == interfaceName)
Ed Tanous1656b292022-05-04 11:33:42 -07002320 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002321 break;
Ed Tanous002d39b2022-05-31 08:59:27 -07002322 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002323
2324 interface = interface->NextSiblingElement("interface");
2325 }
2326 if (interface == nullptr)
2327 {
2328 // if we got to the end of the list and
2329 // never found a match, throw 404
2330 asyncResp->res.result(
2331 boost::beast::http::status::not_found);
2332 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07002333 }
2334
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002335 tinyxml2::XMLElement* methods =
2336 interface->FirstChildElement("method");
2337 while (methods != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002338 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002339 nlohmann::json argsArray = nlohmann::json::array();
2340 tinyxml2::XMLElement* arg =
2341 methods->FirstChildElement("arg");
2342 while (arg != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002343 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002344 nlohmann::json thisArg;
2345 for (const char* fieldName : std::array<const char*, 3>{
2346 "name", "direction", "type"})
2347 {
2348 const char* fieldValue = arg->Attribute(fieldName);
2349 if (fieldValue != nullptr)
2350 {
2351 thisArg[fieldName] = fieldValue;
2352 }
2353 }
2354 argsArray.emplace_back(std::move(thisArg));
2355 arg = arg->NextSiblingElement("arg");
Ed Tanous002d39b2022-05-31 08:59:27 -07002356 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002357
2358 const char* name = methods->Attribute("name");
2359 if (name != nullptr)
2360 {
2361 std::string uri;
2362 uri.reserve(14 + processName.size() +
2363 objectPath.size() + interfaceName.size() +
2364 strlen(name));
2365 uri += "/bus/system/";
2366 uri += processName;
2367 uri += objectPath;
2368 uri += "/";
2369 uri += interfaceName;
2370 uri += "/";
2371 uri += name;
2372
2373 nlohmann::json::object_t object;
2374 object["name"] = name;
2375 object["uri"] = std::move(uri);
2376 object["args"] = argsArray;
2377
2378 methodsArray.emplace_back(std::move(object));
2379 }
2380 methods = methods->NextSiblingElement("method");
Ed Tanous002d39b2022-05-31 08:59:27 -07002381 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002382 tinyxml2::XMLElement* signals =
2383 interface->FirstChildElement("signal");
2384 while (signals != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002385 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002386 nlohmann::json argsArray = nlohmann::json::array();
2387
2388 tinyxml2::XMLElement* arg =
2389 signals->FirstChildElement("arg");
2390 while (arg != nullptr)
2391 {
2392 const char* name = arg->Attribute("name");
2393 const char* type = arg->Attribute("type");
2394 if (name != nullptr && type != nullptr)
2395 {
2396 nlohmann::json::object_t params;
2397 params["name"] = name;
2398 params["type"] = type;
2399 argsArray.push_back(std::move(params));
2400 }
2401 arg = arg->NextSiblingElement("arg");
2402 }
2403 const char* name = signals->Attribute("name");
2404 if (name != nullptr)
2405 {
2406 nlohmann::json::object_t object;
2407 object["name"] = name;
2408 object["args"] = argsArray;
2409 signalsArray.emplace_back(std::move(object));
2410 }
2411
2412 signals = signals->NextSiblingElement("signal");
Ed Tanous002d39b2022-05-31 08:59:27 -07002413 }
2414
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002415 tinyxml2::XMLElement* property =
2416 interface->FirstChildElement("property");
2417 while (property != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -07002418 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002419 const char* name = property->Attribute("name");
2420 const char* type = property->Attribute("type");
2421 if (type != nullptr && name != nullptr)
2422 {
2423 sdbusplus::message_t m =
2424 crow::connections::systemBus->new_method_call(
2425 processName.c_str(), objectPath.c_str(),
2426 "org.freedesktop."
2427 "DBus."
2428 "Properties",
2429 "Get");
2430 m.append(interfaceName, name);
2431 nlohmann::json& propertyItem = propertiesObj[name];
2432 crow::connections::systemBus->async_send(
2433 m, [&propertyItem,
2434 asyncResp](const boost::system::error_code& ec2,
2435 sdbusplus::message_t& msg) {
2436 if (ec2)
2437 {
2438 return;
2439 }
Ed Tanous1656b292022-05-04 11:33:42 -07002440
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002441 int r =
2442 convertDBusToJSON("v", msg, propertyItem);
2443 if (r < 0)
2444 {
2445 BMCWEB_LOG_ERROR(
2446 "Couldn't convert vector to json");
2447 }
2448 });
2449 }
2450 property = property->NextSiblingElement("property");
Ed Tanous1656b292022-05-04 11:33:42 -07002451 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002452 },
Ed Tanous1656b292022-05-04 11:33:42 -07002453 processName, objectPath, "org.freedesktop.DBus.Introspectable",
2454 "Introspect");
2455 }
2456 else
2457 {
2458 if (req.method() != boost::beast::http::verb::post)
2459 {
2460 asyncResp->res.result(boost::beast::http::status::not_found);
2461 return;
2462 }
2463
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002464 nlohmann::json requestDbusData;
2465 JsonParseResult ret = parseRequestAsJson(req, requestDbusData);
2466 if (ret == JsonParseResult::BadContentType)
Ed Tanous1656b292022-05-04 11:33:42 -07002467 {
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002468 setErrorResponse(asyncResp->res,
2469 boost::beast::http::status::unsupported_media_type,
2470 invalidContentType, unsupportedMediaMsg);
Ed Tanous1656b292022-05-04 11:33:42 -07002471 return;
2472 }
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01002473 if (ret != JsonParseResult::Success)
2474 {
2475 setErrorResponse(asyncResp->res,
2476 boost::beast::http::status::bad_request,
2477 noJsonDesc, badReqMsg);
2478 return;
2479 }
2480
Ed Tanous1656b292022-05-04 11:33:42 -07002481 if (!requestDbusData.is_array())
2482 {
2483 asyncResp->res.result(boost::beast::http::status::bad_request);
2484 return;
2485 }
Lei YU28dd5ca2023-03-17 13:17:05 +08002486 auto transaction = std::make_shared<InProgressActionData>(asyncResp);
Ed Tanous1656b292022-05-04 11:33:42 -07002487
2488 transaction->path = objectPath;
2489 transaction->methodName = methodName;
2490 transaction->arguments = std::move(requestDbusData);
2491
2492 findActionOnInterface(transaction, processName);
2493 }
2494}
2495
Ed Tanous23a21a12020-07-25 04:45:05 +00002496inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002497{
2498 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous432a8902021-06-14 15:28:56 -07002499 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002500 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002501 [](const crow::Request&,
2502 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002503 nlohmann::json::array_t buses;
2504 nlohmann::json& bus = buses.emplace_back();
2505 bus["name"] = "system";
2506 asyncResp->res.jsonValue["busses"] = std::move(buses);
2507 asyncResp->res.jsonValue["status"] = "ok";
2508 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002509
2510 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous432a8902021-06-14 15:28:56 -07002511 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002512 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002513 [](const crow::Request&,
2514 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002515 auto myCallback = [asyncResp](
2516 const boost::system::error_code& ec,
zhanghch058d1b46d2021-04-01 11:18:24 +08002517 std::vector<std::string>& names) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002518 if (ec)
2519 {
2520 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec);
2521 asyncResp->res.result(
2522 boost::beast::http::status::internal_server_error);
2523 }
2524 else
2525 {
2526 std::ranges::sort(names);
2527 asyncResp->res.jsonValue["status"] = "ok";
2528 auto& objectsSub = asyncResp->res.jsonValue["objects"];
2529 for (const auto& name : names)
2530 {
2531 nlohmann::json::object_t object;
2532 object["name"] = name;
2533 objectsSub.emplace_back(std::move(object));
2534 }
2535 }
2536 };
2537 crow::connections::systemBus->async_method_call(
2538 std::move(myCallback), "org.freedesktop.DBus", "/",
2539 "org.freedesktop.DBus", "ListNames");
2540 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002541
2542 BMCWEB_ROUTE(app, "/list/")
Ed Tanous432a8902021-06-14 15:28:56 -07002543 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002544 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002545 [](const crow::Request&,
2546 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002547 handleList(asyncResp, "/");
2548 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002549
2550 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002551 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002552 .methods(boost::beast::http::verb::get)(
2553 [](const crow::Request& req,
2554 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002555 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002556 std::string objectPath = "/xyz/" + path;
2557 handleDBusUrl(req, asyncResp, objectPath);
2558 });
zhanghch058d1b46d2021-04-01 11:18:24 +08002559
2560 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002561 .privileges({{"ConfigureComponents", "ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002562 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2563 boost::beast::http::verb::delete_)(
2564 [](const crow::Request& req,
2565 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2566 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002567 std::string objectPath = "/xyz/" + path;
2568 handleDBusUrl(req, asyncResp, objectPath);
2569 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002570
Ed Tanous049a0512018-11-01 13:58:42 -07002571 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002572 .privileges({{"Login"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002573 .methods(boost::beast::http::verb::get)(
2574 [](const crow::Request& req,
2575 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2576 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002577 std::string objectPath = "/org/" + path;
2578 handleDBusUrl(req, asyncResp, objectPath);
2579 });
Tanousf00032d2018-11-05 01:18:10 -03002580
2581 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002582 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002583 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2584 boost::beast::http::verb::delete_)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002585 [](const crow::Request& req,
2586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002587 const std::string& path) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002588 std::string objectPath = "/org/" + path;
2589 handleDBusUrl(req, asyncResp, objectPath);
2590 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002591
Ed Tanous1abe55e2018-09-05 08:30:59 -07002592 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002593 .privileges({{"ConfigureManager"}})
zhanghch058d1b46d2021-04-01 11:18:24 +08002594 .methods(boost::beast::http::verb::get)(
2595 [](const crow::Request&,
2596 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2597 const std::string& dumpId) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002598 if (!validateFilename(dumpId))
2599 {
2600 asyncResp->res.result(
2601 boost::beast::http::status::bad_request);
2602 return;
2603 }
2604 std::filesystem::path loc(
2605 "/var/lib/phosphor-debug-collector/dumps");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002606
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002607 loc /= dumpId;
Ramesh Iyyard9207042019-07-05 08:04:42 -05002608
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002609 if (!std::filesystem::exists(loc) ||
2610 !std::filesystem::is_directory(loc))
2611 {
2612 BMCWEB_LOG_ERROR("{}Not found", loc.string());
2613 asyncResp->res.result(
2614 boost::beast::http::status::not_found);
2615 return;
2616 }
2617 std::filesystem::directory_iterator files(loc);
zhanghch058d1b46d2021-04-01 11:18:24 +08002618
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002619 for (const auto& file : files)
2620 {
2621 if (!asyncResp->res.openFile(file))
2622 {
2623 continue;
2624 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002625
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002626 asyncResp->res.addHeader(
2627 boost::beast::http::field::content_type,
2628 "application/octet-stream");
zhanghch058d1b46d2021-04-01 11:18:24 +08002629
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002630 // Assuming only one dump file will be present in the dump
2631 // id directory
2632 std::string dumpFileName = file.path().filename().string();
zhanghch058d1b46d2021-04-01 11:18:24 +08002633
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002634 // Filename should be in alphanumeric, dot and underscore
2635 // Its based on phosphor-debug-collector application
2636 // dumpfile format
2637 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2638 if (!std::regex_match(dumpFileName, dumpFileRegex))
2639 {
2640 BMCWEB_LOG_ERROR("Invalid dump filename {}",
2641 dumpFileName);
2642 asyncResp->res.result(
2643 boost::beast::http::status::not_found);
2644 return;
2645 }
2646 std::string contentDispositionParam =
2647 "attachment; filename=\"" + dumpFileName + "\"";
2648
2649 asyncResp->res.addHeader(
2650 boost::beast::http::field::content_disposition,
2651 contentDispositionParam);
2652
2653 return;
2654 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002655 asyncResp->res.result(boost::beast::http::status::not_found);
Ed Tanousad18f072018-11-14 14:07:48 -08002656 return;
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002657 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002658
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002659 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002660 .privileges({{"Login"}})
Ed Tanousb41187f2019-10-24 16:30:02 -07002661
2662 .methods(boost::beast::http::verb::get)(
zhanghch058d1b46d2021-04-01 11:18:24 +08002663 [](const crow::Request&,
2664 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous81ce6092020-12-17 16:54:55 +00002665 const std::string& connection) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -04002666 introspectObjects(connection, "/", asyncResp);
2667 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002668
2669 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous432a8902021-06-14 15:28:56 -07002670 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous1656b292022-05-04 11:33:42 -07002671 .methods(boost::beast::http::verb::get,
2672 boost::beast::http::verb::post)(handleBusSystemPost);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002673}
2674} // namespace openbmc_mapper
2675} // namespace crow