blob: 581e40b31a7c83bebe099dd61910ef667b0df2b9 [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 Tanous911ac312017-08-15 09:37:42 -070016#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070017
Ed Tanous04e438c2020-10-03 08:06:26 -070018#include <app.hpp>
Ed Tanouse3cb5a32018-08-08 14:16:49 -070019#include <async_resp.hpp>
Ed Tanous1abe55e2018-09-05 08:30:59 -070020#include <boost/algorithm/string.hpp>
21#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070022#include <dbus_singleton.hpp>
James Feist5b4aa862018-08-16 14:07:01 -070023#include <dbus_utility.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024#include <sdbusplus/message/types.hpp>
25
James Feist4418c7f2019-04-15 11:09:15 -070026#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070027#include <fstream>
Ramesh Iyyard9207042019-07-05 08:04:42 -050028#include <regex>
Ed Tanousb5a76932020-09-29 16:16:58 -070029#include <utility>
Ed Tanous911ac312017-08-15 09:37:42 -070030
Ed Tanous1abe55e2018-09-05 08:30:59 -070031namespace crow
32{
33namespace openbmc_mapper
34{
Ed Tanousba9f9a62017-10-11 16:40:35 -070035
Matt Spinler3ae4ba72018-12-05 14:01:22 -060036using GetSubTreeType = std::vector<
37 std::pair<std::string,
38 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
39
Ed Tanous23a21a12020-07-25 04:45:05 +000040const constexpr char* notFoundMsg = "404 Not Found";
41const constexpr char* badReqMsg = "400 Bad Request";
42const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
43const constexpr char* forbiddenMsg = "403 Forbidden";
44const constexpr char* methodFailedMsg = "500 Method Call Failed";
45const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
46const constexpr char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060047 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous23a21a12020-07-25 04:45:05 +000048const constexpr char* propNotFoundDesc =
49 "The specified property cannot be found";
50const constexpr char* noJsonDesc = "No JSON object could be decoded";
51const constexpr char* methodNotFoundDesc =
52 "The specified method cannot be found";
53const constexpr char* methodNotAllowedDesc = "Method not allowed";
54const constexpr char* forbiddenPropDesc =
55 "The specified property cannot be created";
56const constexpr char* forbiddenResDesc =
57 "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060058
Ed Tanous23a21a12020-07-25 04:45:05 +000059inline void setErrorResponse(crow::Response& res,
60 boost::beast::http::status result,
61 const std::string& desc,
62 const std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -060063{
64 res.result(result);
65 res.jsonValue = {{"data", {{"description", desc}}},
66 {"message", msg},
67 {"status", "error"}};
68}
69
Ed Tanousb5a76932020-09-29 16:16:58 -070070inline void
71 introspectObjects(const std::string& processName,
72 const std::string& objectPath,
73 const std::shared_ptr<bmcweb::AsyncResp>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070074{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070075 if (transaction->res.jsonValue.is_null())
76 {
77 transaction->res.jsonValue = {{"status", "ok"},
78 {"bus_name", processName},
79 {"objects", nlohmann::json::array()}};
80 }
81
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070083 [transaction, processName{std::string(processName)},
84 objectPath{std::string(objectPath)}](
85 const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +000086 const std::string& introspectXml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 if (ec)
88 {
89 BMCWEB_LOG_ERROR
90 << "Introspect call failed with error: " << ec.message()
91 << " on process: " << processName << " path: " << objectPath
92 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070093 return;
94 }
95 transaction->res.jsonValue["objects"].push_back(
96 {{"path", objectPath}});
97
98 tinyxml2::XMLDocument doc;
99
Ed Tanous81ce6092020-12-17 16:54:55 +0000100 doc.Parse(introspectXml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500101 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 if (pRoot == nullptr)
103 {
104 BMCWEB_LOG_ERROR << "XML document failed to parse "
105 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -0700106 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 else
108 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700110 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500112 const char* childPath = node->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700113 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 std::string newpath;
116 if (objectPath != "/")
117 {
118 newpath += objectPath;
119 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700120 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700124
125 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700126 }
127 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700129 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700131}
Ed Tanous64530012018-02-06 17:08:16 -0800132
Ed Tanous23a21a12020-07-25 04:45:05 +0000133inline void getPropertiesForEnumerate(
134 const std::string& objectPath, const std::string& service,
Ed Tanousb5a76932020-09-29 16:16:58 -0700135 const std::string& interface,
136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600137{
138 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
139 << service << " " << interface;
140
141 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500142 [asyncResp, objectPath, service, interface](
143 const boost::system::error_code ec,
144 const std::vector<std::pair<
145 std::string, dbus::utility::DbusVariantType>>& propertiesList) {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600146 if (ec)
147 {
148 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
149 << interface << " service " << service
150 << " failed with code " << ec;
151 return;
152 }
153
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
155 nlohmann::json& objectJson = dataJson[objectPath];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600156 if (objectJson.is_null())
157 {
158 objectJson = nlohmann::json::object();
159 }
160
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500161 for (const auto& [name, value] : propertiesList)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600162 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500163 nlohmann::json& propertyJson = objectJson[name];
164 std::visit([&propertyJson](auto&& val) { propertyJson = val; },
Ed Tanousabf2add2019-01-22 16:40:12 -0800165 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600166 }
167 },
168 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
169 interface);
170}
171
172// Find any results that weren't picked up by ObjectManagers, to be
173// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000174inline void findRemainingObjectsForEnumerate(
Ed Tanousb5a76932020-09-29 16:16:58 -0700175 const std::string& objectPath,
176 const std::shared_ptr<GetSubTreeType>& subtree,
177 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600178{
179 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500180 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600181
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600183 {
184 if (path == objectPath)
185 {
186 // An enumerate does not return the target path's properties
187 continue;
188 }
189 if (dataJson.find(path) == dataJson.end())
190 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500191 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600192 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500193 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600194 {
195 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
196 {
197 getPropertiesForEnumerate(path, service, interface,
198 asyncResp);
199 }
200 }
201 }
202 }
203 }
204}
205
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600206struct InProgressEnumerateData
207{
Ed Tanous23a21a12020-07-25 04:45:05 +0000208 InProgressEnumerateData(const std::string& objectPathIn,
209 std::shared_ptr<bmcweb::AsyncResp> asyncRespIn) :
210 objectPath(objectPathIn),
Ed Tanousb5a76932020-09-29 16:16:58 -0700211 asyncResp(std::move(asyncRespIn))
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600213
214 ~InProgressEnumerateData()
215 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600216 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600217 }
218
219 const std::string objectPath;
220 std::shared_ptr<GetSubTreeType> subtree;
221 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
222};
223
Ed Tanous23a21a12020-07-25 04:45:05 +0000224inline void getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000225 const std::string& objectName, const std::string& objectManagerPath,
226 const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700227 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228{
Ed Tanous81ce6092020-12-17 16:54:55 +0000229 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName
230 << " object_manager_path " << objectManagerPath
231 << " connection_name " << connectionName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000233 [transaction, objectName,
234 connectionName](const boost::system::error_code ec,
235 const dbus::utility::ManagedObjectType& objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700236 if (ec)
237 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000238 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName
239 << " on connection " << connectionName
Ed Tanous049a0512018-11-01 13:58:42 -0700240 << " failed with code " << ec;
241 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700242 }
Ed Tanous64530012018-02-06 17:08:16 -0800243
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 nlohmann::json& dataJson =
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600245 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700246
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500247 for (const auto& objectPath : objects)
Ed Tanous049a0512018-11-01 13:58:42 -0700248 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000249 if (boost::starts_with(objectPath.first.str, objectName))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 {
Ed Tanous049a0512018-11-01 13:58:42 -0700251 BMCWEB_LOG_DEBUG << "Reading object "
252 << objectPath.first.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500253 nlohmann::json& objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 if (objectJson.is_null())
255 {
256 objectJson = nlohmann::json::object();
257 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500258 for (const auto& interface : objectPath.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500260 for (const auto& property : interface.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500262 nlohmann::json& propertyJson =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800264 std::visit([&propertyJson](
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500265 auto&& val) { propertyJson = val; },
Ed Tanousabf2add2019-01-22 16:40:12 -0800266 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 }
268 }
269 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500270 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700271 {
272 if (interface.first == "org.freedesktop.DBus.ObjectManager")
273 {
274 getManagedObjectsForEnumerate(
275 objectPath.first.str, objectPath.first.str,
Ed Tanous81ce6092020-12-17 16:54:55 +0000276 connectionName, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700277 }
278 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 }
280 },
Ed Tanous81ce6092020-12-17 16:54:55 +0000281 connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager",
282 "GetManagedObjects");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700283}
284
Ed Tanous23a21a12020-07-25 04:45:05 +0000285inline void findObjectManagerPathForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000286 const std::string& objectName, const std::string& connectionName,
Ed Tanousb5a76932020-09-29 16:16:58 -0700287 const std::shared_ptr<InProgressEnumerateData>& transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700288{
Ed Tanous81ce6092020-12-17 16:54:55 +0000289 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName
290 << " on connection:" << connectionName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700291 crow::connections::systemBus->async_method_call(
Ed Tanous81ce6092020-12-17 16:54:55 +0000292 [transaction, objectName, connectionName](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700293 const boost::system::error_code ec,
294 const boost::container::flat_map<
295 std::string, boost::container::flat_map<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500296 std::string, std::vector<std::string>>>&
297 objects) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700298 if (ec)
299 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000300 BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName
Ed Tanous049a0512018-11-01 13:58:42 -0700301 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 return;
303 }
304
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500305 for (const auto& pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700306 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500307 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700308 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000309 if (connectionGroup.first == connectionName)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700310 {
311 // Found the object manager path for this resource.
312 getManagedObjectsForEnumerate(
Ed Tanous81ce6092020-12-17 16:54:55 +0000313 objectName, pathGroup.first, connectionName,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600314 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700315 return;
316 }
317 }
318 }
319 },
320 "xyz.openbmc_project.ObjectMapper",
321 "/xyz/openbmc_project/object_mapper",
Ed Tanous81ce6092020-12-17 16:54:55 +0000322 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500323 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700324}
Ed Tanous64530012018-02-06 17:08:16 -0800325
Ed Tanous7c091622019-05-23 11:42:36 -0700326// Uses GetObject to add the object info about the target /enumerate path to
327// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600328// target path, and then continues on enumerating the rest of the tree.
Ed Tanousb5a76932020-09-29 16:16:58 -0700329inline void getObjectAndEnumerate(
330 const std::shared_ptr<InProgressEnumerateData>& transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600331{
332 using GetObjectType =
333 std::vector<std::pair<std::string, std::vector<std::string>>>;
334
335 crow::connections::systemBus->async_method_call(
336 [transaction](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500337 const GetObjectType& objects) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600338 if (ec)
339 {
340 BMCWEB_LOG_ERROR << "GetObject for path "
341 << transaction->objectPath
342 << " failed with code " << ec;
343 return;
344 }
345
346 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
347 << " has " << objects.size() << " entries";
348 if (!objects.empty())
349 {
350 transaction->subtree->emplace_back(transaction->objectPath,
351 objects);
352 }
353
354 // Map indicating connection name, and the path where the object
355 // manager exists
356 boost::container::flat_map<std::string, std::string> connections;
357
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500358 for (const auto& object : *(transaction->subtree))
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600359 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500360 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600361 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500362 std::string& objectManagerPath =
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600363 connections[connection.first];
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500364 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600365 {
366 BMCWEB_LOG_DEBUG << connection.first
367 << " has interface " << interface;
368 if (interface == "org.freedesktop.DBus.ObjectManager")
369 {
370 BMCWEB_LOG_DEBUG << "found object manager path "
371 << object.first;
372 objectManagerPath = object.first;
373 }
374 }
375 }
376 }
377 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
378
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500379 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600380 {
Ed Tanous7c091622019-05-23 11:42:36 -0700381 // If we already know where the object manager is, we don't
382 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600383 // getManagedObjects
384 if (!connection.second.empty())
385 {
386 getManagedObjectsForEnumerate(
387 transaction->objectPath, connection.second,
388 connection.first, transaction);
389 }
390 else
391 {
Ed Tanous7c091622019-05-23 11:42:36 -0700392 // otherwise we need to find the object manager path
393 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600394 findObjectManagerPathForEnumerate(
395 transaction->objectPath, connection.first, transaction);
396 }
397 }
398 },
399 "xyz.openbmc_project.ObjectMapper",
400 "/xyz/openbmc_project/object_mapper",
401 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500402 transaction->objectPath, std::array<const char*, 0>());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600403}
Ed Tanous64530012018-02-06 17:08:16 -0800404
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700405// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406struct InProgressActionData
407{
Ed Tanous23a21a12020-07-25 04:45:05 +0000408 InProgressActionData(crow::Response& resIn) : res(resIn)
409 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 ~InProgressActionData()
411 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600412 // Methods could have been called across different owners
413 // and interfaces, where some calls failed and some passed.
414 //
415 // The rules for this are:
416 // * if no method was called - error
417 // * if a method failed and none passed - error
418 // (converse: if at least one method passed - OK)
419 // * for the method output:
420 // * if output processing didn't fail, return the data
421
422 // Only deal with method returns if nothing failed earlier
423 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600425 if (!methodPassed)
426 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500427 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600428 {
429 setErrorResponse(res, boost::beast::http::status::not_found,
430 methodNotFoundDesc, notFoundMsg);
431 }
432 }
433 else
434 {
435 if (outputFailed)
436 {
437 setErrorResponse(
438 res, boost::beast::http::status::internal_server_error,
439 "Method output failure", methodOutputFailedMsg);
440 }
441 else
442 {
443 res.jsonValue = {{"status", "ok"},
444 {"message", "200 OK"},
445 {"data", methodResponse}};
446 }
447 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600449
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700451 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700452
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500453 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700454 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600455 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
456 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500458 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 std::string path;
460 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600461 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600462 bool methodPassed = false;
463 bool methodFailed = false;
464 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600465 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600466 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700468};
469
Ed Tanous23a21a12020-07-25 04:45:05 +0000470inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471{
472 std::vector<std::string> ret;
473 if (string.empty())
474 {
475 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700476 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700477 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 int containerDepth = 0;
479
480 for (std::string::const_iterator character = string.begin();
481 character != string.end(); character++)
482 {
483 ret.back() += *character;
484 switch (*character)
485 {
486 case ('a'):
487 break;
488 case ('('):
489 case ('{'):
490 containerDepth++;
491 break;
492 case ('}'):
493 case (')'):
494 containerDepth--;
495 if (containerDepth == 0)
496 {
497 if (character + 1 != string.end())
498 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700499 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700500 }
501 }
502 break;
503 default:
504 if (containerDepth == 0)
505 {
506 if (character + 1 != string.end())
507 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700508 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509 }
510 }
511 break;
512 }
513 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600514
515 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700516}
517
Ed Tanous81ce6092020-12-17 16:54:55 +0000518inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType,
519 const nlohmann::json& inputJson)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700520{
521 int r = 0;
Ed Tanous71f52d92021-02-19 08:51:17 -0800522 BMCWEB_LOG_DEBUG << "Converting "
523 << inputJson.dump(2, ' ', true,
524 nlohmann::json::error_handler_t::replace)
Ed Tanous81ce6092020-12-17 16:54:55 +0000525 << " to type: " << argType;
526 const std::vector<std::string> argTypes = dbusArgSplit(argType);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700527
Ed Tanous1abe55e2018-09-05 08:30:59 -0700528 // Assume a single object for now.
Ed Tanous81ce6092020-12-17 16:54:55 +0000529 const nlohmann::json* j = &inputJson;
530 nlohmann::json::const_iterator jIt = inputJson.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700531
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500532 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700533 {
534 // If we are decoding multiple objects, grab the pointer to the
535 // iterator, and increment it for the next loop
536 if (argTypes.size() > 1)
537 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000538 if (jIt == inputJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 {
540 return -2;
541 }
542 j = &*jIt;
543 jIt++;
544 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500545 const int64_t* intValue = j->get_ptr<const int64_t*>();
546 const std::string* stringValue = j->get_ptr<const std::string*>();
547 const double* doubleValue = j->get_ptr<const double*>();
548 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 int64_t v = 0;
550 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700551
Ed Tanous1abe55e2018-09-05 08:30:59 -0700552 // Do some basic type conversions that make sense. uint can be
553 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700554 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500556 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700557 if (uintValue != nullptr)
558 {
559 v = static_cast<int64_t>(*uintValue);
560 intValue = &v;
561 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 }
Ed Tanous66664f22019-10-11 13:05:49 -0700563 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500565 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700566 if (uintValue != nullptr)
567 {
568 d = static_cast<double>(*uintValue);
569 doubleValue = &d;
570 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 }
Ed Tanous66664f22019-10-11 13:05:49 -0700572 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700573 {
Ed Tanous66664f22019-10-11 13:05:49 -0700574 if (intValue != nullptr)
575 {
576 d = static_cast<double>(*intValue);
577 doubleValue = &d;
578 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700579 }
580
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700583 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 {
585 return -1;
586 }
Ed Tanous271584a2019-07-09 16:24:22 -0700587 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500588 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 if (r < 0)
590 {
591 return r;
592 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700593 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700594 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700595 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700596 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 {
598 return -1;
599 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500600 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
601 (*intValue > std::numeric_limits<int32_t>::max()))
602 {
603 return -ERANGE;
604 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700605 int32_t i = static_cast<int32_t>(*intValue);
606 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 if (r < 0)
608 {
609 return r;
610 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700611 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700612 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 {
614 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 int boolInt = false;
616 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500618 if (*intValue == 1)
619 {
620 boolInt = true;
621 }
622 else if (*intValue == 0)
623 {
624 boolInt = false;
625 }
626 else
627 {
628 return -ERANGE;
629 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 }
631 else if (b != nullptr)
632 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600633 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700634 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700635 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700636 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 }
639 else
640 {
641 return -1;
642 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700643 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 if (r < 0)
645 {
646 return r;
647 }
648 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700649 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700651 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 {
653 return -1;
654 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500655 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
656 (*intValue > std::numeric_limits<int16_t>::max()))
657 {
658 return -ERANGE;
659 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 int16_t n = static_cast<int16_t>(*intValue);
661 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 if (r < 0)
663 {
664 return r;
665 }
666 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700667 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700669 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700670 {
671 return -1;
672 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 if (r < 0)
675 {
676 return r;
677 }
678 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500681 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700682 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700683 {
684 return -1;
685 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000686 if (*uintValue > std::numeric_limits<uint8_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500687 {
688 return -ERANGE;
689 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700690 uint8_t y = static_cast<uint8_t>(*uintValue);
691 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700692 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700693 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500695 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700696 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700697 {
698 return -1;
699 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000700 if (*uintValue > std::numeric_limits<uint16_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500701 {
702 return -ERANGE;
703 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 uint16_t q = static_cast<uint16_t>(*uintValue);
705 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700706 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700707 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700708 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500709 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700710 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 {
712 return -1;
713 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000714 if (*uintValue > std::numeric_limits<uint32_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500715 {
716 return -ERANGE;
717 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700718 uint32_t u = static_cast<uint32_t>(*uintValue);
719 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700721 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500723 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700724 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700725 {
726 return -1;
727 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700728 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700730 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500732 if (doubleValue == nullptr)
733 {
734 return -1;
735 }
736 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
737 (*doubleValue > std::numeric_limits<double>::max()))
738 {
739 return -ERANGE;
740 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700741 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700743 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700745 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700747 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 if (r < 0)
749 {
750 return r;
751 }
752
Ed Tanous0dfeda62019-10-24 11:21:38 -0700753 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700755 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700756 if (r < 0)
757 {
758 return r;
759 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 }
761 sd_bus_message_close_container(m);
762 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700763 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700765 std::string containedType = argCode.substr(1);
766 BMCWEB_LOG_DEBUG << "variant type: " << argCode
767 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700769 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 if (r < 0)
771 {
772 return r;
773 }
774
Ed Tanous81ce6092020-12-17 16:54:55 +0000775 r = convertJsonToDbus(m, containedType, inputJson);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 if (r < 0)
777 {
778 return r;
779 }
780
781 r = sd_bus_message_close_container(m);
782 if (r < 0)
783 {
784 return r;
785 }
786 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700787 else if (boost::starts_with(argCode, "(") &&
788 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700790 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700791 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700792 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800793 if (r < 0)
794 {
795 return r;
796 }
797
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 nlohmann::json::const_iterator it = j->begin();
Ed Tanous81ce6092020-12-17 16:54:55 +0000799 for (const std::string& argCode2 : dbusArgSplit(argType))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 {
801 if (it == j->end())
802 {
803 return -1;
804 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000805 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806 if (r < 0)
807 {
808 return r;
809 }
810 it++;
811 }
812 r = sd_bus_message_close_container(m);
813 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700814 else if (boost::starts_with(argCode, "{") &&
815 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700817 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700818 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700819 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800820 if (r < 0)
821 {
822 return r;
823 }
824
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700825 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700826 if (codes.size() != 2)
827 {
828 return -1;
829 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700830 const std::string& keyType = codes[0];
831 const std::string& valueType = codes[1];
Ed Tanous3174e4d2020-10-07 11:41:22 -0700832 for (const auto& it : j->items())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700834 r = convertJsonToDbus(m, keyType, it.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 if (r < 0)
836 {
837 return r;
838 }
839
Ed Tanous2c70f802020-09-28 14:29:23 -0700840 r = convertJsonToDbus(m, valueType, it.value());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 if (r < 0)
842 {
843 return r;
844 }
845 }
846 r = sd_bus_message_close_container(m);
847 }
848 else
849 {
850 return -2;
851 }
852 if (r < 0)
853 {
854 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700855 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700856
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 if (argTypes.size() > 1)
858 {
859 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700860 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700861 }
Matt Spinler127ea542019-01-14 11:04:28 -0600862
863 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700864}
865
Matt Spinlerd22a7132019-01-14 12:14:30 -0600866template <typename T>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500867int readMessageItem(const std::string& typeCode, sdbusplus::message::message& m,
868 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600869{
870 T value;
871
872 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
873 if (r < 0)
874 {
875 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
876 << " failed!";
877 return r;
878 }
879
880 data = value;
881 return 0;
882}
883
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500884int convertDBusToJSON(const std::string& returnType,
885 sdbusplus::message::message& m, nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600886
Ed Tanous23a21a12020-07-25 04:45:05 +0000887inline int readDictEntryFromMessage(const std::string& typeCode,
888 sdbusplus::message::message& m,
889 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600890{
891 std::vector<std::string> types = dbusArgSplit(typeCode);
892 if (types.size() != 2)
893 {
894 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
895 << types.size();
896 return -1;
897 }
898
899 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
900 typeCode.c_str());
901 if (r < 0)
902 {
903 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
904 return r;
905 }
906
907 nlohmann::json key;
908 r = convertDBusToJSON(types[0], m, key);
909 if (r < 0)
910 {
911 return r;
912 }
913
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500914 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -0600915 if (keyPtr == nullptr)
916 {
917 // json doesn't support non-string keys. If we hit this condition,
918 // convert the result to a string so we can proceed
Ed Tanous71f52d92021-02-19 08:51:17 -0800919 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500920 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -0700921 // in theory this can't fail now, but lets be paranoid about it
922 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600923 if (keyPtr == nullptr)
924 {
925 return -1;
926 }
927 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500928 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -0600929
930 r = convertDBusToJSON(types[1], m, value);
931 if (r < 0)
932 {
933 return r;
934 }
935
936 r = sd_bus_message_exit_container(m.get());
937 if (r < 0)
938 {
939 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
940 return r;
941 }
942
943 return 0;
944}
945
Ed Tanous23a21a12020-07-25 04:45:05 +0000946inline int readArrayFromMessage(const std::string& typeCode,
947 sdbusplus::message::message& m,
948 nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -0600949{
950 if (typeCode.size() < 2)
951 {
952 BMCWEB_LOG_ERROR << "Type code " << typeCode
953 << " too small for an array";
954 return -1;
955 }
956
957 std::string containedType = typeCode.substr(1);
958
959 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
960 containedType.c_str());
961 if (r < 0)
962 {
963 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
964 << r;
965 return r;
966 }
967
968 bool dict = boost::starts_with(containedType, "{") &&
969 boost::ends_with(containedType, "}");
970
971 if (dict)
972 {
973 // Remove the { }
974 containedType = containedType.substr(1, containedType.size() - 2);
975 data = nlohmann::json::object();
976 }
977 else
978 {
979 data = nlohmann::json::array();
980 }
981
982 while (true)
983 {
984 r = sd_bus_message_at_end(m.get(), false);
985 if (r < 0)
986 {
987 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
988 return r;
989 }
990
991 if (r > 0)
992 {
993 break;
994 }
995
996 // Dictionaries are only ever seen in an array
997 if (dict)
998 {
999 r = readDictEntryFromMessage(containedType, m, data);
1000 if (r < 0)
1001 {
1002 return r;
1003 }
1004 }
1005 else
1006 {
1007 data.push_back(nlohmann::json());
1008
1009 r = convertDBusToJSON(containedType, m, data.back());
1010 if (r < 0)
1011 {
1012 return r;
1013 }
1014 }
1015 }
1016
1017 r = sd_bus_message_exit_container(m.get());
1018 if (r < 0)
1019 {
1020 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1021 return r;
1022 }
1023
1024 return 0;
1025}
1026
Ed Tanous23a21a12020-07-25 04:45:05 +00001027inline int readStructFromMessage(const std::string& typeCode,
1028 sdbusplus::message::message& m,
1029 nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001030{
1031 if (typeCode.size() < 3)
1032 {
1033 BMCWEB_LOG_ERROR << "Type code " << typeCode
1034 << " too small for a struct";
1035 return -1;
1036 }
1037
1038 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1039 std::vector<std::string> types = dbusArgSplit(containedTypes);
1040
1041 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1042 containedTypes.c_str());
1043 if (r < 0)
1044 {
1045 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1046 << r;
1047 return r;
1048 }
1049
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001050 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001051 {
1052 data.push_back(nlohmann::json());
1053 r = convertDBusToJSON(type, m, data.back());
1054 if (r < 0)
1055 {
1056 return r;
1057 }
1058 }
1059
1060 r = sd_bus_message_exit_container(m.get());
1061 if (r < 0)
1062 {
1063 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1064 return r;
1065 }
1066 return 0;
1067}
1068
Ed Tanous23a21a12020-07-25 04:45:05 +00001069inline int readVariantFromMessage(sdbusplus::message::message& m,
1070 nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001071{
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001072 const char* containerType;
Ed Tanous99131cd2019-10-24 11:12:47 -07001073 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001074 if (r < 0)
1075 {
1076 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1077 return r;
1078 }
1079
1080 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1081 containerType);
1082 if (r < 0)
1083 {
1084 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1085 << r;
1086 return r;
1087 }
1088
1089 r = convertDBusToJSON(containerType, m, data);
1090 if (r < 0)
1091 {
1092 return r;
1093 }
1094
1095 r = sd_bus_message_exit_container(m.get());
1096 if (r < 0)
1097 {
1098 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1099 return r;
1100 }
1101
1102 return 0;
1103}
1104
Ed Tanous23a21a12020-07-25 04:45:05 +00001105inline int convertDBusToJSON(const std::string& returnType,
1106 sdbusplus::message::message& m,
1107 nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001108{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001109 int r = 0;
1110 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1111
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001112 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001113 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001114 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001115 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001116 {
1117 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001118 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001119 }
1120
Ed Tanousd4d25792020-09-29 15:15:03 -07001121 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001122 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001123 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001124 if (r < 0)
1125 {
1126 return r;
1127 }
1128 }
1129 else if (typeCode == "b")
1130 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001131 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001132 if (r < 0)
1133 {
1134 return r;
1135 }
1136
Matt Spinlerf39420c2019-01-30 12:57:18 -06001137 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001138 }
1139 else if (typeCode == "u")
1140 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001141 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001142 if (r < 0)
1143 {
1144 return r;
1145 }
1146 }
1147 else if (typeCode == "i")
1148 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001149 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001150 if (r < 0)
1151 {
1152 return r;
1153 }
1154 }
1155 else if (typeCode == "x")
1156 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001157 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001158 if (r < 0)
1159 {
1160 return r;
1161 }
1162 }
1163 else if (typeCode == "t")
1164 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001165 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001166 if (r < 0)
1167 {
1168 return r;
1169 }
1170 }
1171 else if (typeCode == "n")
1172 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001173 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001174 if (r < 0)
1175 {
1176 return r;
1177 }
1178 }
1179 else if (typeCode == "q")
1180 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001181 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001182 if (r < 0)
1183 {
1184 return r;
1185 }
1186 }
1187 else if (typeCode == "y")
1188 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001189 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001190 if (r < 0)
1191 {
1192 return r;
1193 }
1194 }
1195 else if (typeCode == "d")
1196 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001197 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001198 if (r < 0)
1199 {
1200 return r;
1201 }
1202 }
1203 else if (typeCode == "h")
1204 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001205 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001206 if (r < 0)
1207 {
1208 return r;
1209 }
1210 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001211 else if (boost::starts_with(typeCode, "a"))
1212 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001213 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001214 if (r < 0)
1215 {
1216 return r;
1217 }
1218 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001219 else if (boost::starts_with(typeCode, "(") &&
1220 boost::ends_with(typeCode, ")"))
1221 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001222 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001223 if (r < 0)
1224 {
1225 return r;
1226 }
1227 }
Matt Spinler89c19702019-01-14 13:13:00 -06001228 else if (boost::starts_with(typeCode, "v"))
1229 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001230 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001231 if (r < 0)
1232 {
1233 return r;
1234 }
1235 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001236 else
1237 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001238 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1239 return -2;
1240 }
1241 }
1242
Matt Spinler16caaee2019-01-15 11:40:34 -06001243 return 0;
1244}
1245
Ed Tanousb5a76932020-09-29 16:16:58 -07001246inline void handleMethodResponse(
1247 const std::shared_ptr<InProgressActionData>& transaction,
1248 sdbusplus::message::message& m, const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001249{
Matt Spinler39a4e392019-01-15 11:53:13 -06001250 nlohmann::json data;
1251
1252 int r = convertDBusToJSON(returnType, m, data);
1253 if (r < 0)
1254 {
1255 transaction->outputFailed = true;
1256 return;
1257 }
1258
1259 if (data.is_null())
1260 {
1261 return;
1262 }
1263
1264 if (transaction->methodResponse.is_null())
1265 {
1266 transaction->methodResponse = std::move(data);
1267 return;
1268 }
1269
1270 // If they're both dictionaries or arrays, merge into one.
1271 // Otherwise, make the results an array with every result
1272 // an entry. Could also just fail in that case, but it
1273 // seems better to get the data back somehow.
1274
1275 if (transaction->methodResponse.is_object() && data.is_object())
1276 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001277 for (const auto& obj : data.items())
Matt Spinler39a4e392019-01-15 11:53:13 -06001278 {
1279 // Note: Will overwrite the data for a duplicate key
1280 transaction->methodResponse.emplace(obj.key(),
1281 std::move(obj.value()));
1282 }
1283 return;
1284 }
1285
1286 if (transaction->methodResponse.is_array() && data.is_array())
1287 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001288 for (auto& obj : data)
Matt Spinler39a4e392019-01-15 11:53:13 -06001289 {
1290 transaction->methodResponse.push_back(std::move(obj));
1291 }
1292 return;
1293 }
1294
1295 if (!transaction->convertedToArray)
1296 {
1297 // They are different types. May as well turn them into an array
1298 nlohmann::json j = std::move(transaction->methodResponse);
1299 transaction->methodResponse = nlohmann::json::array();
1300 transaction->methodResponse.push_back(std::move(j));
1301 transaction->methodResponse.push_back(std::move(data));
1302 transaction->convertedToArray = true;
1303 }
1304 else
1305 {
1306 transaction->methodResponse.push_back(std::move(data));
1307 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001308}
1309
Ed Tanousb5a76932020-09-29 16:16:58 -07001310inline void findActionOnInterface(
1311 const std::shared_ptr<InProgressActionData>& transaction,
1312 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001313{
1314 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1315 << connectionName;
1316 crow::connections::systemBus->async_method_call(
1317 [transaction, connectionName{std::string(connectionName)}](
1318 const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001319 const std::string& introspectXml) {
1320 BMCWEB_LOG_DEBUG << "got xml:\n " << introspectXml;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001321 if (ec)
1322 {
1323 BMCWEB_LOG_ERROR
1324 << "Introspect call failed with error: " << ec.message()
1325 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001326 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001327 }
Matt Spinler318bd892019-01-15 09:59:20 -06001328 tinyxml2::XMLDocument doc;
1329
Ed Tanous81ce6092020-12-17 16:54:55 +00001330 doc.Parse(introspectXml.data(), introspectXml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001331 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Matt Spinler318bd892019-01-15 09:59:20 -06001332 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001333 {
Matt Spinler318bd892019-01-15 09:59:20 -06001334 BMCWEB_LOG_ERROR << "XML document failed to parse "
1335 << connectionName << "\n";
1336 return;
1337 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001338 tinyxml2::XMLElement* interfaceNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001339 pRoot->FirstChildElement("interface");
1340 while (interfaceNode != nullptr)
1341 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001342 const char* thisInterfaceName =
Matt Spinler318bd892019-01-15 09:59:20 -06001343 interfaceNode->Attribute("name");
1344 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001345 {
Matt Spinler318bd892019-01-15 09:59:20 -06001346 if (!transaction->interfaceName.empty() &&
1347 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348 {
Matt Spinler318bd892019-01-15 09:59:20 -06001349 interfaceNode =
1350 interfaceNode->NextSiblingElement("interface");
1351 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001352 }
Matt Spinler318bd892019-01-15 09:59:20 -06001353
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001354 tinyxml2::XMLElement* methodNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001355 interfaceNode->FirstChildElement("method");
1356 while (methodNode != nullptr)
1357 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001358 const char* thisMethodName =
Matt Spinler318bd892019-01-15 09:59:20 -06001359 methodNode->Attribute("name");
1360 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1361 if (thisMethodName != nullptr &&
1362 thisMethodName == transaction->methodName)
1363 {
1364 BMCWEB_LOG_DEBUG
1365 << "Found method named " << thisMethodName
1366 << " on interface " << thisInterfaceName;
1367 sdbusplus::message::message m =
1368 crow::connections::systemBus->new_method_call(
1369 connectionName.c_str(),
1370 transaction->path.c_str(),
1371 thisInterfaceName,
1372 transaction->methodName.c_str());
1373
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001374 tinyxml2::XMLElement* argumentNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001375 methodNode->FirstChildElement("arg");
1376
Matt Spinler16caaee2019-01-15 11:40:34 -06001377 std::string returnType;
1378
1379 // Find the output type
1380 while (argumentNode != nullptr)
1381 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001382 const char* argDirection =
Matt Spinler16caaee2019-01-15 11:40:34 -06001383 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001384 const char* argType =
Matt Spinler16caaee2019-01-15 11:40:34 -06001385 argumentNode->Attribute("type");
1386 if (argDirection != nullptr &&
1387 argType != nullptr &&
1388 std::string(argDirection) == "out")
1389 {
1390 returnType = argType;
1391 break;
1392 }
1393 argumentNode =
1394 argumentNode->NextSiblingElement("arg");
1395 }
1396
Matt Spinler318bd892019-01-15 09:59:20 -06001397 nlohmann::json::const_iterator argIt =
1398 transaction->arguments.begin();
1399
Matt Spinler16caaee2019-01-15 11:40:34 -06001400 argumentNode = methodNode->FirstChildElement("arg");
1401
Matt Spinler318bd892019-01-15 09:59:20 -06001402 while (argumentNode != nullptr)
1403 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001404 const char* argDirection =
Matt Spinler318bd892019-01-15 09:59:20 -06001405 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001406 const char* argType =
Matt Spinler318bd892019-01-15 09:59:20 -06001407 argumentNode->Attribute("type");
1408 if (argDirection != nullptr &&
1409 argType != nullptr &&
1410 std::string(argDirection) == "in")
1411 {
1412 if (argIt == transaction->arguments.end())
1413 {
1414 transaction->setErrorStatus(
1415 "Invalid method args");
1416 return;
1417 }
1418 if (convertJsonToDbus(m.get(),
1419 std::string(argType),
1420 *argIt) < 0)
1421 {
1422 transaction->setErrorStatus(
1423 "Invalid method arg type");
1424 return;
1425 }
1426
1427 argIt++;
1428 }
1429 argumentNode =
1430 argumentNode->NextSiblingElement("arg");
1431 }
1432
1433 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001434 m, [transaction, returnType](
Ed Tanous23a21a12020-07-25 04:45:05 +00001435 boost::system::error_code ec2,
1436 sdbusplus::message::message& m2) {
1437 if (ec2)
Matt Spinler318bd892019-01-15 09:59:20 -06001438 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001439 transaction->methodFailed = true;
Ed Tanous23a21a12020-07-25 04:45:05 +00001440 const sd_bus_error* e = m2.get_error();
Matt Spinler06b1b632019-06-18 16:09:25 -05001441
1442 if (e)
1443 {
1444 setErrorResponse(
1445 transaction->res,
1446 boost::beast::http::status::
1447 bad_request,
1448 e->name, e->message);
1449 }
1450 else
1451 {
1452 setErrorResponse(
1453 transaction->res,
1454 boost::beast::http::status::
1455 bad_request,
1456 "Method call failed",
1457 methodFailedMsg);
1458 }
Matt Spinler318bd892019-01-15 09:59:20 -06001459 return;
1460 }
Ed Tanous3174e4d2020-10-07 11:41:22 -07001461 transaction->methodPassed = true;
Matt Spinler16caaee2019-01-15 11:40:34 -06001462
Ed Tanous23a21a12020-07-25 04:45:05 +00001463 handleMethodResponse(transaction, m2,
Matt Spinler16caaee2019-01-15 11:40:34 -06001464 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001465 });
1466 break;
1467 }
1468 methodNode = methodNode->NextSiblingElement("method");
1469 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001470 }
Matt Spinler318bd892019-01-15 09:59:20 -06001471 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001472 }
1473 },
1474 connectionName, transaction->path,
1475 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001476}
1477
Ed Tanous23a21a12020-07-25 04:45:05 +00001478inline void handleAction(const crow::Request& req, crow::Response& res,
1479 const std::string& objectPath,
1480 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001481{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001482 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1483 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001484 nlohmann::json requestDbusData =
1485 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001486
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 if (requestDbusData.is_discarded())
1488 {
Matt Spinler6db06242018-12-11 11:21:22 -06001489 setErrorResponse(res, boost::beast::http::status::bad_request,
1490 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001491 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001492 return;
1493 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001494 nlohmann::json::iterator data = requestDbusData.find("data");
1495 if (data == requestDbusData.end())
1496 {
Matt Spinler6db06242018-12-11 11:21:22 -06001497 setErrorResponse(res, boost::beast::http::status::bad_request,
1498 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001499 res.end();
1500 return;
1501 }
1502
1503 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001504 {
Matt Spinler6db06242018-12-11 11:21:22 -06001505 setErrorResponse(res, boost::beast::http::status::bad_request,
1506 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001507 res.end();
1508 return;
1509 }
1510 auto transaction = std::make_shared<InProgressActionData>(res);
1511
1512 transaction->path = objectPath;
1513 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001514 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001515 crow::connections::systemBus->async_method_call(
1516 [transaction](
1517 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001518 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1519 interfaceNames) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001520 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001522 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001523 setErrorResponse(transaction->res,
1524 boost::beast::http::status::not_found,
1525 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001526 return;
1527 }
1528
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001529 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1530 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001531
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001532 for (const std::pair<std::string, std::vector<std::string>>&
1533 object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 {
1535 findActionOnInterface(transaction, object.first);
1536 }
1537 },
1538 "xyz.openbmc_project.ObjectMapper",
1539 "/xyz/openbmc_project/object_mapper",
1540 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1541 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001542}
1543
Ed Tanouscb13a392020-07-25 19:02:03 +00001544inline void handleDelete(crow::Response& res, const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001545{
1546 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1547
1548 crow::connections::systemBus->async_method_call(
1549 [&res, objectPath](
1550 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001551 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1552 interfaceNames) {
Matt Spinlerde818812018-12-11 16:39:20 -06001553 if (ec || interfaceNames.size() <= 0)
1554 {
1555 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001556 setErrorResponse(res,
1557 boost::beast::http::status::method_not_allowed,
1558 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001559 res.end();
1560 return;
1561 }
1562
1563 auto transaction = std::make_shared<InProgressActionData>(res);
1564 transaction->path = objectPath;
1565 transaction->methodName = "Delete";
1566 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1567
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001568 for (const std::pair<std::string, std::vector<std::string>>&
1569 object : interfaceNames)
Matt Spinlerde818812018-12-11 16:39:20 -06001570 {
1571 findActionOnInterface(transaction, object.first);
1572 }
1573 },
1574 "xyz.openbmc_project.ObjectMapper",
1575 "/xyz/openbmc_project/object_mapper",
1576 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001577 std::array<const char*, 0>());
Matt Spinlerde818812018-12-11 16:39:20 -06001578}
1579
Ed Tanous23a21a12020-07-25 04:45:05 +00001580inline void handleList(crow::Response& res, const std::string& objectPath,
1581 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001582{
1583 crow::connections::systemBus->async_method_call(
1584 [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001585 std::vector<std::string>& objectPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586 if (ec)
1587 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001588 setErrorResponse(res, boost::beast::http::status::not_found,
1589 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001590 }
1591 else
1592 {
1593 res.jsonValue = {{"status", "ok"},
1594 {"message", "200 OK"},
1595 {"data", std::move(objectPaths)}};
1596 }
1597 res.end();
1598 },
1599 "xyz.openbmc_project.ObjectMapper",
1600 "/xyz/openbmc_project/object_mapper",
1601 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001602 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001603}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001604
Ed Tanous23a21a12020-07-25 04:45:05 +00001605inline void handleEnumerate(crow::Response& res, const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001606{
Ed Tanous049a0512018-11-01 13:58:42 -07001607 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1608 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1609
1610 asyncResp->res.jsonValue = {{"message", "200 OK"},
1611 {"status", "ok"},
1612 {"data", nlohmann::json::object()}};
1613
Ed Tanous1abe55e2018-09-05 08:30:59 -07001614 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001615 [objectPath, asyncResp](const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001616 GetSubTreeType& objectNames) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001617 auto transaction = std::make_shared<InProgressEnumerateData>(
1618 objectPath, asyncResp);
1619
1620 transaction->subtree =
Ed Tanous81ce6092020-12-17 16:54:55 +00001621 std::make_shared<GetSubTreeType>(std::move(objectNames));
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001622
Ed Tanous1abe55e2018-09-05 08:30:59 -07001623 if (ec)
1624 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001625 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1626 << transaction->objectPath;
1627 setErrorResponse(transaction->asyncResp->res,
1628 boost::beast::http::status::not_found,
1629 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001630 return;
1631 }
Ed Tanous64530012018-02-06 17:08:16 -08001632
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001633 // Add the data for the path passed in to the results
1634 // as if GetSubTree returned it, and continue on enumerating
1635 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001636 },
1637 "xyz.openbmc_project.ObjectMapper",
1638 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -07001639 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001640 std::array<const char*, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001641}
Ed Tanous911ac312017-08-15 09:37:42 -07001642
Ed Tanous23a21a12020-07-25 04:45:05 +00001643inline void handleGet(crow::Response& res, std::string& objectPath,
1644 std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001645{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001646 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1647 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001648 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001649
Ed Tanous1abe55e2018-09-05 08:30:59 -07001650 std::shared_ptr<std::string> path =
1651 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001652
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 using GetObjectType =
1654 std::vector<std::pair<std::string, std::vector<std::string>>>;
1655 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001656 [&res, path, propertyName](const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00001657 const GetObjectType& objectNames) {
1658 if (ec || objectNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001659 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001660 setErrorResponse(res, boost::beast::http::status::not_found,
1661 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001662 res.end();
1663 return;
1664 }
1665 std::shared_ptr<nlohmann::json> response =
1666 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001667 // The mapper should never give us an empty interface names
1668 // list, but check anyway
Ed Tanous23a21a12020-07-25 04:45:05 +00001669 for (const std::pair<std::string, std::vector<std::string>>&
Ed Tanous81ce6092020-12-17 16:54:55 +00001670 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001671 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001672 const std::vector<std::string>& interfaceNames =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001673 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001674
Ed Tanous1abe55e2018-09-05 08:30:59 -07001675 if (interfaceNames.size() <= 0)
1676 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001677 setErrorResponse(res, boost::beast::http::status::not_found,
1678 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001679 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 return;
1681 }
1682
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001683 for (const std::string& interface : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001684 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001685 sdbusplus::message::message m =
1686 crow::connections::systemBus->new_method_call(
1687 connection.first.c_str(), path->c_str(),
1688 "org.freedesktop.DBus.Properties", "GetAll");
1689 m.append(interface);
1690 crow::connections::systemBus->async_send(
1691 m, [&res, response,
Ed Tanous23a21a12020-07-25 04:45:05 +00001692 propertyName](const boost::system::error_code ec2,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001693 sdbusplus::message::message& msg) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001694 if (ec2)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001695 {
1696 BMCWEB_LOG_ERROR << "Bad dbus request error: "
Ed Tanous23a21a12020-07-25 04:45:05 +00001697 << ec2;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 }
1699 else
1700 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001701 nlohmann::json properties;
1702 int r =
1703 convertDBusToJSON("a{sv}", msg, properties);
1704 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001706 BMCWEB_LOG_ERROR
1707 << "convertDBusToJSON failed";
1708 }
1709 else
1710 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001711 for (auto& prop : properties.items())
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001712 {
Ed Tanous7c091622019-05-23 11:42:36 -07001713 // if property name is empty, or
1714 // matches our search query, add it
1715 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001716
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001717 if (propertyName->empty())
1718 {
1719 (*response)[prop.key()] =
1720 std::move(prop.value());
1721 }
1722 else if (prop.key() == *propertyName)
1723 {
1724 *response = std::move(prop.value());
1725 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001726 }
1727 }
1728 }
1729 if (response.use_count() == 1)
1730 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001731 if (!propertyName->empty() && response->empty())
1732 {
1733 setErrorResponse(
1734 res,
1735 boost::beast::http::status::not_found,
1736 propNotFoundDesc, notFoundMsg);
1737 }
1738 else
1739 {
1740 res.jsonValue = {{"status", "ok"},
1741 {"message", "200 OK"},
1742 {"data", *response}};
1743 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001744 res.end();
1745 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001746 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001747 }
1748 }
1749 },
1750 "xyz.openbmc_project.ObjectMapper",
1751 "/xyz/openbmc_project/object_mapper",
1752 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1753 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001754}
1755
Ed Tanous1abe55e2018-09-05 08:30:59 -07001756struct AsyncPutRequest
1757{
Ed Tanous23a21a12020-07-25 04:45:05 +00001758 AsyncPutRequest(crow::Response& resIn) : res(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001759 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001760 ~AsyncPutRequest()
1761 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001762 if (res.jsonValue.empty())
1763 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001764 setErrorResponse(res, boost::beast::http::status::forbidden,
1765 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001766 }
1767
1768 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001769 }
1770
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001771 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001772 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001773 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1774 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001775 }
1776
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001777 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001778 std::string objectPath;
1779 std::string propertyName;
1780 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001781};
1782
Ed Tanous23a21a12020-07-25 04:45:05 +00001783inline void handlePut(const crow::Request& req, crow::Response& res,
1784 const std::string& objectPath,
1785 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001786{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001787 if (destProperty.empty())
1788 {
1789 setErrorResponse(res, boost::beast::http::status::forbidden,
1790 forbiddenResDesc, forbiddenMsg);
1791 res.end();
1792 return;
1793 }
1794
Ed Tanous1abe55e2018-09-05 08:30:59 -07001795 nlohmann::json requestDbusData =
1796 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001797
Ed Tanous1abe55e2018-09-05 08:30:59 -07001798 if (requestDbusData.is_discarded())
1799 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001800 setErrorResponse(res, boost::beast::http::status::bad_request,
1801 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001802 res.end();
1803 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001804 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001805
Ed Tanous1abe55e2018-09-05 08:30:59 -07001806 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1807 if (propertyIt == requestDbusData.end())
1808 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001809 setErrorResponse(res, boost::beast::http::status::bad_request,
1810 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001811 res.end();
1812 return;
1813 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001814 const nlohmann::json& propertySetValue = *propertyIt;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001815 auto transaction = std::make_shared<AsyncPutRequest>(res);
1816 transaction->objectPath = objectPath;
1817 transaction->propertyName = destProperty;
1818 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001819
Ed Tanous1abe55e2018-09-05 08:30:59 -07001820 using GetObjectType =
1821 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001822
Ed Tanous1abe55e2018-09-05 08:30:59 -07001823 crow::connections::systemBus->async_method_call(
Ed Tanous23a21a12020-07-25 04:45:05 +00001824 [transaction](const boost::system::error_code ec2,
Ed Tanous81ce6092020-12-17 16:54:55 +00001825 const GetObjectType& objectNames) {
1826 if (!ec2 && objectNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001827 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001828 setErrorResponse(transaction->res,
1829 boost::beast::http::status::not_found,
1830 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001831 return;
1832 }
Ed Tanous911ac312017-08-15 09:37:42 -07001833
Ed Tanous23a21a12020-07-25 04:45:05 +00001834 for (const std::pair<std::string, std::vector<std::string>>&
Ed Tanous81ce6092020-12-17 16:54:55 +00001835 connection : objectNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001836 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001837 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001838
Ed Tanous1abe55e2018-09-05 08:30:59 -07001839 crow::connections::systemBus->async_method_call(
1840 [connectionName{std::string(connectionName)},
Ed Tanous23a21a12020-07-25 04:45:05 +00001841 transaction](const boost::system::error_code ec3,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001842 const std::string& introspectXml) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001843 if (ec3)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001844 {
1845 BMCWEB_LOG_ERROR
1846 << "Introspect call failed with error: "
Ed Tanous23a21a12020-07-25 04:45:05 +00001847 << ec3.message()
Ed Tanous1abe55e2018-09-05 08:30:59 -07001848 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001849 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001850 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001851 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001852 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001853
Ed Tanous1abe55e2018-09-05 08:30:59 -07001854 doc.Parse(introspectXml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001855 tinyxml2::XMLNode* pRoot =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001856 doc.FirstChildElement("node");
1857 if (pRoot == nullptr)
1858 {
1859 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1860 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001861 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001862 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001863 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001864 tinyxml2::XMLElement* ifaceNode =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001865 pRoot->FirstChildElement("interface");
1866 while (ifaceNode != nullptr)
1867 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001868 const char* interfaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001869 ifaceNode->Attribute("name");
1870 BMCWEB_LOG_DEBUG << "found interface "
1871 << interfaceName;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001872 tinyxml2::XMLElement* propNode =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001873 ifaceNode->FirstChildElement("property");
1874 while (propNode != nullptr)
1875 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001876 const char* propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001877 propNode->Attribute("name");
1878 BMCWEB_LOG_DEBUG << "Found property "
1879 << propertyName;
1880 if (propertyName == transaction->propertyName)
1881 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001882 const char* argType =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001883 propNode->Attribute("type");
1884 if (argType != nullptr)
1885 {
1886 sdbusplus::message::message m =
1887 crow::connections::systemBus
1888 ->new_method_call(
1889 connectionName.c_str(),
1890 transaction->objectPath
1891 .c_str(),
1892 "org.freedesktop.DBus."
1893 "Properties",
1894 "Set");
1895 m.append(interfaceName,
1896 transaction->propertyName);
1897 int r = sd_bus_message_open_container(
1898 m.get(), SD_BUS_TYPE_VARIANT,
1899 argType);
1900 if (r < 0)
1901 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001902 transaction->setErrorStatus(
1903 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001904 return;
1905 }
1906 r = convertJsonToDbus(
1907 m.get(), argType,
1908 transaction->propertyValue);
1909 if (r < 0)
1910 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001911 if (r == -ERANGE)
1912 {
1913 transaction->setErrorStatus(
1914 "Provided property value "
1915 "is out of range for the "
1916 "property type");
1917 }
1918 else
1919 {
1920 transaction->setErrorStatus(
1921 "Invalid arg type");
1922 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001923 return;
1924 }
1925 r = sd_bus_message_close_container(
1926 m.get());
1927 if (r < 0)
1928 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001929 transaction->setErrorStatus(
1930 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001931 return;
1932 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001933 crow::connections::systemBus
1934 ->async_send(
1935 m,
1936 [transaction](
1937 boost::system::error_code
1938 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001939 sdbusplus::message::message&
Ed Tanous23a21a12020-07-25 04:45:05 +00001940 m2) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001941 BMCWEB_LOG_DEBUG << "sent";
1942 if (ec)
1943 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001944 const sd_bus_error* e =
Ed Tanous23a21a12020-07-25 04:45:05 +00001945 m2.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001946 setErrorResponse(
1947 transaction->res,
1948 boost::beast::http::
1949 status::
1950 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001951 (e) ? e->name
1952 : ec.category()
1953 .name(),
1954 (e) ? e->message
1955 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001956 }
1957 else
1958 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001959 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001960 .jsonValue = {
1961 {"status", "ok"},
1962 {"message",
1963 "200 OK"},
1964 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001965 }
1966 });
1967 }
1968 }
1969 propNode =
1970 propNode->NextSiblingElement("property");
1971 }
1972 ifaceNode =
1973 ifaceNode->NextSiblingElement("interface");
1974 }
1975 },
1976 connectionName, transaction->objectPath,
1977 "org.freedesktop.DBus.Introspectable", "Introspect");
1978 }
1979 },
1980 "xyz.openbmc_project.ObjectMapper",
1981 "/xyz/openbmc_project/object_mapper",
1982 "xyz.openbmc_project.ObjectMapper", "GetObject",
1983 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001984}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001985
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001986inline void handleDBusUrl(const crow::Request& req, crow::Response& res,
1987 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07001988{
Ed Tanous049a0512018-11-01 13:58:42 -07001989
1990 // If accessing a single attribute, fill in and update objectPath,
1991 // otherwise leave destProperty blank
1992 std::string destProperty = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001993 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07001994 size_t attrPosition = objectPath.find(attrSeperator);
1995 if (attrPosition != objectPath.npos)
1996 {
1997 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1998 objectPath.length());
1999 objectPath = objectPath.substr(0, attrPosition);
2000 }
2001
Ed Tanousb41187f2019-10-24 16:30:02 -07002002 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002003 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002004 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002005 size_t actionPosition = objectPath.find(actionSeperator);
2006 if (actionPosition != objectPath.npos)
2007 {
2008 std::string postProperty =
2009 objectPath.substr((actionPosition + strlen(actionSeperator)),
2010 objectPath.length());
2011 objectPath = objectPath.substr(0, actionPosition);
2012 handleAction(req, res, objectPath, postProperty);
2013 return;
2014 }
2015 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002016 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002017 {
2018 if (boost::ends_with(objectPath, "/enumerate"))
2019 {
2020 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2021 objectPath.end());
2022 handleEnumerate(res, objectPath);
2023 }
2024 else if (boost::ends_with(objectPath, "/list"))
2025 {
2026 objectPath.erase(objectPath.end() - sizeof("list"),
2027 objectPath.end());
2028 handleList(res, objectPath);
2029 }
2030 else
2031 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002032 // Trim any trailing "/" at the end
2033 if (boost::ends_with(objectPath, "/"))
2034 {
2035 objectPath.pop_back();
2036 handleList(res, objectPath, 1);
2037 }
2038 else
2039 {
2040 handleGet(res, objectPath, destProperty);
2041 }
Ed Tanous049a0512018-11-01 13:58:42 -07002042 }
2043 return;
2044 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002045 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002046 {
2047 handlePut(req, res, objectPath, destProperty);
2048 return;
2049 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002050 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002051 {
Ed Tanouscb13a392020-07-25 19:02:03 +00002052 handleDelete(res, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002053 return;
2054 }
Ed Tanous049a0512018-11-01 13:58:42 -07002055
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002056 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2057 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002058 res.end();
2059}
2060
Ed Tanous23a21a12020-07-25 04:45:05 +00002061inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002062{
2063 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002064 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002065 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002066 [](const crow::Request&, crow::Response& res) {
Gunnar Millscaa3ce32020-07-08 14:46:53 -05002067 res.jsonValue = {{"buses", {{{"name", "system"}}}},
Ed Tanous1abe55e2018-09-05 08:30:59 -07002068 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002069 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002070 });
2071
2072 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002073 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002074 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002075 [](const crow::Request&, crow::Response& res) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002076 auto myCallback = [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002077 std::vector<std::string>& names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002078 if (ec)
2079 {
2080 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2081 res.result(
2082 boost::beast::http::status::internal_server_error);
2083 }
2084 else
2085 {
2086 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002087 res.jsonValue = {{"status", "ok"}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002088 auto& objectsSub = res.jsonValue["objects"];
2089 for (auto& name : names)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002090 {
2091 objectsSub.push_back({{"name", name}});
2092 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002093 }
2094 res.end();
2095 };
2096 crow::connections::systemBus->async_method_call(
2097 std::move(myCallback), "org.freedesktop.DBus", "/",
2098 "org.freedesktop.DBus", "ListNames");
2099 });
2100
2101 BMCWEB_ROUTE(app, "/list/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002102 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002103 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002104 [](const crow::Request&, crow::Response& res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002105 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002106 });
2107
2108 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002109 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002110 .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2111 crow::Response& res,
2112 const std::string& path) {
Tanousf00032d2018-11-05 01:18:10 -03002113 std::string objectPath = "/xyz/" + path;
2114 handleDBusUrl(req, res, objectPath);
2115 });
2116
2117 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002118 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002119 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2120 boost::beast::http::verb::delete_)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002121 [](const crow::Request& req, crow::Response& res,
2122 const std::string& path) {
Ed Tanous049a0512018-11-01 13:58:42 -07002123 std::string objectPath = "/xyz/" + path;
2124 handleDBusUrl(req, res, objectPath);
2125 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002126
Ed Tanous049a0512018-11-01 13:58:42 -07002127 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002128 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002129 .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2130 crow::Response& res,
2131 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002132 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002133 handleDBusUrl(req, res, objectPath);
2134 });
2135
2136 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002137 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002138 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2139 boost::beast::http::verb::delete_)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002140 [](const crow::Request& req, crow::Response& res,
2141 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002142 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002143 handleDBusUrl(req, res, objectPath);
2144 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002145
Ed Tanous1abe55e2018-09-05 08:30:59 -07002146 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002147 .privileges({"ConfigureManager"})
Ed Tanouscb13a392020-07-25 19:02:03 +00002148 .methods(boost::beast::http::verb::get)([](const crow::Request&,
Ed Tanousb41187f2019-10-24 16:30:02 -07002149 crow::Response& res,
2150 const std::string& dumpId) {
Ed Tanous3174e4d2020-10-07 11:41:22 -07002151 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002152 if (!std::regex_match(dumpId, validFilename))
2153 {
Ed Tanousad18f072018-11-14 14:07:48 -08002154 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002155 res.end();
2156 return;
2157 }
James Feistf6150402019-01-08 10:36:20 -08002158 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002159 "/var/lib/phosphor-debug-collector/dumps");
2160
Ed Tanousad18f072018-11-14 14:07:48 -08002161 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002162
James Feistf6150402019-01-08 10:36:20 -08002163 if (!std::filesystem::exists(loc) ||
2164 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002165 {
Ed Tanousad18f072018-11-14 14:07:48 -08002166 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002167 res.result(boost::beast::http::status::not_found);
2168 res.end();
2169 return;
2170 }
James Feistf6150402019-01-08 10:36:20 -08002171 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002172
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002173 for (auto& file : files)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002174 {
2175 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002176 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002177 {
2178 continue;
2179 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002180
Ed Tanous1abe55e2018-09-05 08:30:59 -07002181 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002182
2183 // Assuming only one dump file will be present in the dump id
2184 // directory
2185 std::string dumpFileName = file.path().filename().string();
2186
2187 // Filename should be in alphanumeric, dot and underscore
2188 // Its based on phosphor-debug-collector application dumpfile
2189 // format
2190 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2191 if (!std::regex_match(dumpFileName, dumpFileRegex))
2192 {
2193 BMCWEB_LOG_ERROR << "Invalid dump filename "
2194 << dumpFileName;
2195 res.result(boost::beast::http::status::not_found);
2196 res.end();
2197 return;
2198 }
2199 std::string contentDispositionParam =
2200 "attachment; filename=\"" + dumpFileName + "\"";
2201
2202 res.addHeader("Content-Disposition", contentDispositionParam);
2203
Ed Tanous1abe55e2018-09-05 08:30:59 -07002204 res.body() = {std::istreambuf_iterator<char>(readFile),
2205 std::istreambuf_iterator<char>()};
2206 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002207 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002208 }
2209 res.result(boost::beast::http::status::not_found);
2210 res.end();
2211 return;
2212 });
2213
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002214 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002215 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002216
2217 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002218 [](const crow::Request&, crow::Response& res,
Ed Tanous81ce6092020-12-17 16:54:55 +00002219 const std::string& connection) {
2220 introspectObjects(connection, "/",
Ed Tanousb41187f2019-10-24 16:30:02 -07002221 std::make_shared<bmcweb::AsyncResp>(res));
2222 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002223
2224 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002225 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002226 .methods(
2227 boost::beast::http::verb::get,
2228 boost::beast::http::verb::post)([](const crow::Request& req,
2229 crow::Response& res,
2230 const std::string& processName,
2231 const std::string&
2232 requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002233 std::vector<std::string> strs;
2234 boost::split(strs, requestedPath, boost::is_any_of("/"));
2235 std::string objectPath;
2236 std::string interfaceName;
2237 std::string methodName;
2238 auto it = strs.begin();
2239 if (it == strs.end())
2240 {
2241 objectPath = "/";
2242 }
2243 while (it != strs.end())
2244 {
2245 // Check if segment contains ".". If it does, it must be an
2246 // interface
2247 if (it->find(".") != std::string::npos)
2248 {
2249 break;
Gunnar Millscaa3ce32020-07-08 14:46:53 -05002250 // This check is necessary as the trailing slash gets
Ed Tanous7c091622019-05-23 11:42:36 -07002251 // parsed as part of our <path> specifier above, which
2252 // causes the normal trailing backslash redirector to
2253 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002254 }
Ed Tanous3174e4d2020-10-07 11:41:22 -07002255 if (!it->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002256 {
2257 objectPath += "/" + *it;
2258 }
2259 it++;
2260 }
2261 if (it != strs.end())
2262 {
2263 interfaceName = *it;
2264 it++;
2265
2266 // after interface, we might have a method name
2267 if (it != strs.end())
2268 {
2269 methodName = *it;
2270 it++;
2271 }
2272 }
2273 if (it != strs.end())
2274 {
Ed Tanous7c091622019-05-23 11:42:36 -07002275 // if there is more levels past the method name, something
2276 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002277 res.result(boost::beast::http::status::not_found);
AppaRao Puli3c27ed32020-03-31 01:21:57 +05302278 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002279 return;
2280 }
2281 if (interfaceName.empty())
2282 {
Ed Tanous7c091622019-05-23 11:42:36 -07002283 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2284 std::make_shared<bmcweb::AsyncResp>(res);
2285
Ed Tanous1abe55e2018-09-05 08:30:59 -07002286 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002287 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002288 objectPath](const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00002289 const std::string& introspectXml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002290 if (ec)
2291 {
2292 BMCWEB_LOG_ERROR
2293 << "Introspect call failed with error: "
2294 << ec.message()
2295 << " on process: " << processName
2296 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002297 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002298 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002299 tinyxml2::XMLDocument doc;
2300
Ed Tanous81ce6092020-12-17 16:54:55 +00002301 doc.Parse(introspectXml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002302 tinyxml2::XMLNode* pRoot =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002303 doc.FirstChildElement("node");
2304 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002305 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002306 BMCWEB_LOG_ERROR << "XML document failed to parse "
2307 << processName << " " << objectPath
2308 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002309 asyncResp->res.jsonValue = {
2310 {"status", "XML parse error"}};
2311 asyncResp->res.result(boost::beast::http::status::
2312 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002313 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002314 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002315
Ed Tanous81ce6092020-12-17 16:54:55 +00002316 BMCWEB_LOG_DEBUG << introspectXml;
Ed Tanous7c091622019-05-23 11:42:36 -07002317 asyncResp->res.jsonValue = {
2318 {"status", "ok"},
2319 {"bus_name", processName},
2320 {"object_path", objectPath}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002321 nlohmann::json& interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002322 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002323 interfacesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002324 tinyxml2::XMLElement* interface =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002325 pRoot->FirstChildElement("interface");
2326
2327 while (interface != nullptr)
2328 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002329 const char* ifaceName =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002330 interface->Attribute("name");
2331 if (ifaceName != nullptr)
2332 {
2333 interfacesArray.push_back(
2334 {{"name", ifaceName}});
2335 }
2336
2337 interface =
2338 interface->NextSiblingElement("interface");
2339 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002340 },
2341 processName, objectPath,
2342 "org.freedesktop.DBus.Introspectable", "Introspect");
2343 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002344 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002345 {
Ed Tanous7c091622019-05-23 11:42:36 -07002346 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2347 std::make_shared<bmcweb::AsyncResp>(res);
2348
Ed Tanous1abe55e2018-09-05 08:30:59 -07002349 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002350 [asyncResp, processName, objectPath,
2351 interfaceName](const boost::system::error_code ec,
Ed Tanous81ce6092020-12-17 16:54:55 +00002352 const std::string& introspectXml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002353 if (ec)
2354 {
2355 BMCWEB_LOG_ERROR
2356 << "Introspect call failed with error: "
2357 << ec.message()
2358 << " on process: " << processName
2359 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002360 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002361 }
Ed Tanous7c091622019-05-23 11:42:36 -07002362 tinyxml2::XMLDocument doc;
2363
Ed Tanous81ce6092020-12-17 16:54:55 +00002364 doc.Parse(introspectXml.data(), introspectXml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002365 tinyxml2::XMLNode* pRoot =
Ed Tanous7c091622019-05-23 11:42:36 -07002366 doc.FirstChildElement("node");
2367 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002368 {
Ed Tanous7c091622019-05-23 11:42:36 -07002369 BMCWEB_LOG_ERROR << "XML document failed to parse "
2370 << processName << " " << objectPath
2371 << "\n";
2372 asyncResp->res.result(boost::beast::http::status::
2373 internal_server_error);
2374 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002375 }
Ed Tanous7c091622019-05-23 11:42:36 -07002376 asyncResp->res.jsonValue = {
2377 {"status", "ok"},
2378 {"bus_name", processName},
2379 {"interface", interfaceName},
2380 {"object_path", objectPath}};
2381
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002382 nlohmann::json& methodsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002383 asyncResp->res.jsonValue["methods"];
2384 methodsArray = nlohmann::json::array();
2385
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002386 nlohmann::json& signalsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002387 asyncResp->res.jsonValue["signals"];
2388 signalsArray = nlohmann::json::array();
2389
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002390 nlohmann::json& propertiesObj =
Ed Tanous7c091622019-05-23 11:42:36 -07002391 asyncResp->res.jsonValue["properties"];
2392 propertiesObj = nlohmann::json::object();
2393
2394 // if we know we're the only call, build the
2395 // json directly
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002396 tinyxml2::XMLElement* interface =
Ed Tanous7c091622019-05-23 11:42:36 -07002397 pRoot->FirstChildElement("interface");
2398 while (interface != nullptr)
2399 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002400 const char* ifaceName =
Ed Tanous7c091622019-05-23 11:42:36 -07002401 interface->Attribute("name");
2402
2403 if (ifaceName != nullptr &&
2404 ifaceName == interfaceName)
2405 {
2406 break;
2407 }
2408
2409 interface =
2410 interface->NextSiblingElement("interface");
2411 }
2412 if (interface == nullptr)
2413 {
2414 // if we got to the end of the list and
2415 // never found a match, throw 404
2416 asyncResp->res.result(
2417 boost::beast::http::status::not_found);
2418 return;
2419 }
2420
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002421 tinyxml2::XMLElement* methods =
Ed Tanous7c091622019-05-23 11:42:36 -07002422 interface->FirstChildElement("method");
2423 while (methods != nullptr)
2424 {
2425 nlohmann::json argsArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002426 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002427 methods->FirstChildElement("arg");
2428 while (arg != nullptr)
2429 {
2430 nlohmann::json thisArg;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002431 for (const char* fieldName :
2432 std::array<const char*, 3>{
Ed Tanous7c091622019-05-23 11:42:36 -07002433 "name", "direction", "type"})
2434 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002435 const char* fieldValue =
Ed Tanous7c091622019-05-23 11:42:36 -07002436 arg->Attribute(fieldName);
2437 if (fieldValue != nullptr)
2438 {
2439 thisArg[fieldName] = fieldValue;
2440 }
2441 }
2442 argsArray.push_back(std::move(thisArg));
2443 arg = arg->NextSiblingElement("arg");
2444 }
2445
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002446 const char* name = methods->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002447 if (name != nullptr)
2448 {
Ed Tanousf23b7292020-10-15 09:41:17 -07002449 std::string uri;
2450 uri.reserve(14 + processName.size() +
2451 objectPath.size() +
2452 interfaceName.size() +
2453 strlen(name));
2454 uri += "/bus/system/";
2455 uri += processName;
2456 uri += objectPath;
2457 uri += "/";
2458 uri += interfaceName;
2459 uri += "/";
2460 uri += name;
2461 methodsArray.push_back({{"name", name},
2462 {"uri", std::move(uri)},
2463 {"args", argsArray}});
Ed Tanous7c091622019-05-23 11:42:36 -07002464 }
2465 methods = methods->NextSiblingElement("method");
2466 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002467 tinyxml2::XMLElement* signals =
Ed Tanous7c091622019-05-23 11:42:36 -07002468 interface->FirstChildElement("signal");
2469 while (signals != nullptr)
2470 {
2471 nlohmann::json argsArray = nlohmann::json::array();
2472
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002473 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002474 signals->FirstChildElement("arg");
2475 while (arg != nullptr)
2476 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002477 const char* name = arg->Attribute("name");
2478 const char* type = arg->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002479 if (name != nullptr && type != nullptr)
2480 {
2481 argsArray.push_back({
2482 {"name", name},
2483 {"type", type},
2484 });
2485 }
2486 arg = arg->NextSiblingElement("arg");
2487 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002488 const char* name = signals->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002489 if (name != nullptr)
2490 {
2491 signalsArray.push_back(
2492 {{"name", name}, {"args", argsArray}});
2493 }
2494
2495 signals = signals->NextSiblingElement("signal");
2496 }
2497
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002498 tinyxml2::XMLElement* property =
Ed Tanous7c091622019-05-23 11:42:36 -07002499 interface->FirstChildElement("property");
2500 while (property != nullptr)
2501 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002502 const char* name = property->Attribute("name");
2503 const char* type = property->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002504 if (type != nullptr && name != nullptr)
2505 {
2506 sdbusplus::message::message m =
2507 crow::connections::systemBus
2508 ->new_method_call(processName.c_str(),
2509 objectPath.c_str(),
2510 "org.freedesktop."
2511 "DBus."
2512 "Properties",
2513 "Get");
2514 m.append(interfaceName, name);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002515 nlohmann::json& propertyItem =
Ed Tanous7c091622019-05-23 11:42:36 -07002516 propertiesObj[name];
2517 crow::connections::systemBus->async_send(
2518 m, [&propertyItem, asyncResp](
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002519 boost::system::error_code& e,
2520 sdbusplus::message::message& msg) {
Ed Tanous271584a2019-07-09 16:24:22 -07002521 if (e)
Ed Tanous7c091622019-05-23 11:42:36 -07002522 {
2523 return;
2524 }
2525
Ed Tanous271584a2019-07-09 16:24:22 -07002526 convertDBusToJSON("v", msg,
2527 propertyItem);
Ed Tanous7c091622019-05-23 11:42:36 -07002528 });
2529 }
2530 property = property->NextSiblingElement("property");
2531 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002532 },
2533 processName, objectPath,
2534 "org.freedesktop.DBus.Introspectable", "Introspect");
2535 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002536 else
2537 {
Ed Tanousb41187f2019-10-24 16:30:02 -07002538 if (req.method() != boost::beast::http::verb::post)
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002539 {
2540 res.result(boost::beast::http::status::not_found);
2541 res.end();
2542 return;
2543 }
2544
2545 nlohmann::json requestDbusData =
2546 nlohmann::json::parse(req.body, nullptr, false);
2547
2548 if (requestDbusData.is_discarded())
2549 {
2550 res.result(boost::beast::http::status::bad_request);
2551 res.end();
2552 return;
2553 }
2554 if (!requestDbusData.is_array())
2555 {
2556 res.result(boost::beast::http::status::bad_request);
2557 res.end();
2558 return;
2559 }
2560 auto transaction = std::make_shared<InProgressActionData>(res);
2561
2562 transaction->path = objectPath;
2563 transaction->methodName = methodName;
2564 transaction->arguments = std::move(requestDbusData);
2565
2566 findActionOnInterface(transaction, processName);
2567 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002568 });
2569}
2570} // namespace openbmc_mapper
2571} // namespace crow