blob: 2c801c7c9c87568a2c027bb2cbb86f706e5347f5 [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 Tanousc94ad492019-10-10 15:39:33 -070016#include <app.h>
Ed Tanous911ac312017-08-15 09:37:42 -070017#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -070018
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 Tanous911ac312017-08-15 09:37:42 -070029
Ed Tanous1abe55e2018-09-05 08:30:59 -070030namespace crow
31{
32namespace openbmc_mapper
33{
Ed Tanousba9f9a62017-10-11 16:40:35 -070034
Matt Spinler3ae4ba72018-12-05 14:01:22 -060035using GetSubTreeType = std::vector<
36 std::pair<std::string,
37 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
38
Ed Tanous23a21a12020-07-25 04:45:05 +000039const constexpr char* notFoundMsg = "404 Not Found";
40const constexpr char* badReqMsg = "400 Bad Request";
41const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed";
42const constexpr char* forbiddenMsg = "403 Forbidden";
43const constexpr char* methodFailedMsg = "500 Method Call Failed";
44const constexpr char* methodOutputFailedMsg = "500 Method Output Error";
45const constexpr char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060046 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Ed Tanous23a21a12020-07-25 04:45:05 +000047const constexpr char* propNotFoundDesc =
48 "The specified property cannot be found";
49const constexpr char* noJsonDesc = "No JSON object could be decoded";
50const constexpr char* methodNotFoundDesc =
51 "The specified method cannot be found";
52const constexpr char* methodNotAllowedDesc = "Method not allowed";
53const constexpr char* forbiddenPropDesc =
54 "The specified property cannot be created";
55const constexpr char* forbiddenResDesc =
56 "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060057
Ed Tanous23a21a12020-07-25 04:45:05 +000058inline void setErrorResponse(crow::Response& res,
59 boost::beast::http::status result,
60 const std::string& desc,
61 const std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -060062{
63 res.result(result);
64 res.jsonValue = {{"data", {{"description", desc}}},
65 {"message", msg},
66 {"status", "error"}};
67}
68
Ed Tanous23a21a12020-07-25 04:45:05 +000069inline void introspectObjects(const std::string& processName,
70 const std::string& objectPath,
71 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070072{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070073 if (transaction->res.jsonValue.is_null())
74 {
75 transaction->res.jsonValue = {{"status", "ok"},
76 {"bus_name", processName},
77 {"objects", nlohmann::json::array()}};
78 }
79
Ed Tanous1abe55e2018-09-05 08:30:59 -070080 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070081 [transaction, processName{std::string(processName)},
82 objectPath{std::string(objectPath)}](
83 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -050084 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070085 if (ec)
86 {
87 BMCWEB_LOG_ERROR
88 << "Introspect call failed with error: " << ec.message()
89 << " on process: " << processName << " path: " << objectPath
90 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070091 return;
92 }
93 transaction->res.jsonValue["objects"].push_back(
94 {{"path", objectPath}});
95
96 tinyxml2::XMLDocument doc;
97
98 doc.Parse(introspect_xml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -050099 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700100 if (pRoot == nullptr)
101 {
102 BMCWEB_LOG_ERROR << "XML document failed to parse "
103 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -0700104 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 else
106 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500107 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700108 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500110 const char* childPath = node->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700111 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 std::string newpath;
114 if (objectPath != "/")
115 {
116 newpath += objectPath;
117 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700118 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700120 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700122
123 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 }
125 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700126 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700127 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700129}
Ed Tanous64530012018-02-06 17:08:16 -0800130
Ed Tanous23a21a12020-07-25 04:45:05 +0000131inline void getPropertiesForEnumerate(
132 const std::string& objectPath, const std::string& service,
133 const std::string& interface, std::shared_ptr<bmcweb::AsyncResp> asyncResp)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600134{
135 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
136 << service << " " << interface;
137
138 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139 [asyncResp, objectPath, service, interface](
140 const boost::system::error_code ec,
141 const std::vector<std::pair<
142 std::string, dbus::utility::DbusVariantType>>& propertiesList) {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600143 if (ec)
144 {
145 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
146 << interface << " service " << service
147 << " failed with code " << ec;
148 return;
149 }
150
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500151 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
152 nlohmann::json& objectJson = dataJson[objectPath];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600153 if (objectJson.is_null())
154 {
155 objectJson = nlohmann::json::object();
156 }
157
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500158 for (const auto& [name, value] : propertiesList)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600159 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500160 nlohmann::json& propertyJson = objectJson[name];
161 std::visit([&propertyJson](auto&& val) { propertyJson = val; },
Ed Tanousabf2add2019-01-22 16:40:12 -0800162 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600163 }
164 },
165 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
166 interface);
167}
168
169// Find any results that weren't picked up by ObjectManagers, to be
170// called after all ObjectManagers are searched for and called.
Ed Tanous23a21a12020-07-25 04:45:05 +0000171inline void findRemainingObjectsForEnumerate(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500172 const std::string& objectPath, std::shared_ptr<GetSubTreeType> subtree,
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600173 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
174{
175 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500176 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600177
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500178 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600179 {
180 if (path == objectPath)
181 {
182 // An enumerate does not return the target path's properties
183 continue;
184 }
185 if (dataJson.find(path) == dataJson.end())
186 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500187 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600188 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500189 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600190 {
191 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
192 {
193 getPropertiesForEnumerate(path, service, interface,
194 asyncResp);
195 }
196 }
197 }
198 }
199 }
200}
201
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600202struct InProgressEnumerateData
203{
Ed Tanous23a21a12020-07-25 04:45:05 +0000204 InProgressEnumerateData(const std::string& objectPathIn,
205 std::shared_ptr<bmcweb::AsyncResp> asyncRespIn) :
206 objectPath(objectPathIn),
207 asyncResp(asyncRespIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500208 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600209
210 ~InProgressEnumerateData()
211 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600212 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600213 }
214
215 const std::string objectPath;
216 std::shared_ptr<GetSubTreeType> subtree;
217 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
218};
219
Ed Tanous23a21a12020-07-25 04:45:05 +0000220inline void getManagedObjectsForEnumerate(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500221 const std::string& object_name, const std::string& object_manager_path,
222 const std::string& connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600223 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224{
Ed Tanous049a0512018-11-01 13:58:42 -0700225 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
226 << " object_manager_path " << object_manager_path
227 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600229 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700230 connection_name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500231 const dbus::utility::ManagedObjectType& objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232 if (ec)
233 {
Ed Tanous049a0512018-11-01 13:58:42 -0700234 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600235 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700236 << " failed with code " << ec;
237 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700238 }
Ed Tanous64530012018-02-06 17:08:16 -0800239
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500240 nlohmann::json& dataJson =
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600241 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700242
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500243 for (const auto& objectPath : objects)
Ed Tanous049a0512018-11-01 13:58:42 -0700244 {
245 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 {
Ed Tanous049a0512018-11-01 13:58:42 -0700247 BMCWEB_LOG_DEBUG << "Reading object "
248 << objectPath.first.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500249 nlohmann::json& objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 if (objectJson.is_null())
251 {
252 objectJson = nlohmann::json::object();
253 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500254 for (const auto& interface : objectPath.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500256 for (const auto& property : interface.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700257 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500258 nlohmann::json& propertyJson =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800260 std::visit([&propertyJson](
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500261 auto&& val) { propertyJson = val; },
Ed Tanousabf2add2019-01-22 16:40:12 -0800262 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 }
264 }
265 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500266 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700267 {
268 if (interface.first == "org.freedesktop.DBus.ObjectManager")
269 {
270 getManagedObjectsForEnumerate(
271 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600272 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700273 }
274 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 }
276 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700277 connection_name, object_manager_path,
278 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
279}
280
Ed Tanous23a21a12020-07-25 04:45:05 +0000281inline void findObjectManagerPathForEnumerate(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500282 const std::string& object_name, const std::string& connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600283 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284{
Ed Tanous049a0512018-11-01 13:58:42 -0700285 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
286 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700287 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600288 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 const boost::system::error_code ec,
290 const boost::container::flat_map<
291 std::string, boost::container::flat_map<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500292 std::string, std::vector<std::string>>>&
293 objects) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700294 if (ec)
295 {
Ed Tanous049a0512018-11-01 13:58:42 -0700296 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
297 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700298 return;
299 }
300
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500301 for (const auto& pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700302 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500303 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700304 {
305 if (connectionGroup.first == connection_name)
306 {
307 // Found the object manager path for this resource.
308 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700309 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600310 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700311 return;
312 }
313 }
314 }
315 },
316 "xyz.openbmc_project.ObjectMapper",
317 "/xyz/openbmc_project/object_mapper",
318 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500319 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700320}
Ed Tanous64530012018-02-06 17:08:16 -0800321
Ed Tanous7c091622019-05-23 11:42:36 -0700322// Uses GetObject to add the object info about the target /enumerate path to
323// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600324// target path, and then continues on enumerating the rest of the tree.
Ed Tanous23a21a12020-07-25 04:45:05 +0000325inline void
326 getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600327{
328 using GetObjectType =
329 std::vector<std::pair<std::string, std::vector<std::string>>>;
330
331 crow::connections::systemBus->async_method_call(
332 [transaction](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500333 const GetObjectType& objects) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600334 if (ec)
335 {
336 BMCWEB_LOG_ERROR << "GetObject for path "
337 << transaction->objectPath
338 << " failed with code " << ec;
339 return;
340 }
341
342 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
343 << " has " << objects.size() << " entries";
344 if (!objects.empty())
345 {
346 transaction->subtree->emplace_back(transaction->objectPath,
347 objects);
348 }
349
350 // Map indicating connection name, and the path where the object
351 // manager exists
352 boost::container::flat_map<std::string, std::string> connections;
353
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500354 for (const auto& object : *(transaction->subtree))
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600355 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500356 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600357 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500358 std::string& objectManagerPath =
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600359 connections[connection.first];
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500360 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600361 {
362 BMCWEB_LOG_DEBUG << connection.first
363 << " has interface " << interface;
364 if (interface == "org.freedesktop.DBus.ObjectManager")
365 {
366 BMCWEB_LOG_DEBUG << "found object manager path "
367 << object.first;
368 objectManagerPath = object.first;
369 }
370 }
371 }
372 }
373 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
374
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500375 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600376 {
Ed Tanous7c091622019-05-23 11:42:36 -0700377 // If we already know where the object manager is, we don't
378 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600379 // getManagedObjects
380 if (!connection.second.empty())
381 {
382 getManagedObjectsForEnumerate(
383 transaction->objectPath, connection.second,
384 connection.first, transaction);
385 }
386 else
387 {
Ed Tanous7c091622019-05-23 11:42:36 -0700388 // otherwise we need to find the object manager path
389 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600390 findObjectManagerPathForEnumerate(
391 transaction->objectPath, connection.first, transaction);
392 }
393 }
394 },
395 "xyz.openbmc_project.ObjectMapper",
396 "/xyz/openbmc_project/object_mapper",
397 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500398 transaction->objectPath, std::array<const char*, 0>());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600399}
Ed Tanous64530012018-02-06 17:08:16 -0800400
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700401// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402struct InProgressActionData
403{
Ed Tanous23a21a12020-07-25 04:45:05 +0000404 InProgressActionData(crow::Response& resIn) : res(resIn)
405 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700406 ~InProgressActionData()
407 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600408 // Methods could have been called across different owners
409 // and interfaces, where some calls failed and some passed.
410 //
411 // The rules for this are:
412 // * if no method was called - error
413 // * if a method failed and none passed - error
414 // (converse: if at least one method passed - OK)
415 // * for the method output:
416 // * if output processing didn't fail, return the data
417
418 // Only deal with method returns if nothing failed earlier
419 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600421 if (!methodPassed)
422 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500423 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600424 {
425 setErrorResponse(res, boost::beast::http::status::not_found,
426 methodNotFoundDesc, notFoundMsg);
427 }
428 }
429 else
430 {
431 if (outputFailed)
432 {
433 setErrorResponse(
434 res, boost::beast::http::status::internal_server_error,
435 "Method output failure", methodOutputFailedMsg);
436 }
437 else
438 {
439 res.jsonValue = {{"status", "ok"},
440 {"message", "200 OK"},
441 {"data", methodResponse}};
442 }
443 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600445
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700447 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700448
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500449 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600451 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
452 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700453 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500454 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 std::string path;
456 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600457 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600458 bool methodPassed = false;
459 bool methodFailed = false;
460 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600461 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600462 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700464};
465
Ed Tanous23a21a12020-07-25 04:45:05 +0000466inline std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467{
468 std::vector<std::string> ret;
469 if (string.empty())
470 {
471 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700472 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700473 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700474 int containerDepth = 0;
475
476 for (std::string::const_iterator character = string.begin();
477 character != string.end(); character++)
478 {
479 ret.back() += *character;
480 switch (*character)
481 {
482 case ('a'):
483 break;
484 case ('('):
485 case ('{'):
486 containerDepth++;
487 break;
488 case ('}'):
489 case (')'):
490 containerDepth--;
491 if (containerDepth == 0)
492 {
493 if (character + 1 != string.end())
494 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700495 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700496 }
497 }
498 break;
499 default:
500 if (containerDepth == 0)
501 {
502 if (character + 1 != string.end())
503 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700504 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700505 }
506 }
507 break;
508 }
509 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600510
511 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700512}
513
Ed Tanous23a21a12020-07-25 04:45:05 +0000514inline int convertJsonToDbus(sd_bus_message* m, const std::string& arg_type,
515 const nlohmann::json& input_json)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516{
517 int r = 0;
518 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
519 << " to type: " << arg_type;
520 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700521
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 // Assume a single object for now.
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500523 const nlohmann::json* j = &input_json;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700524 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700525
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500526 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700527 {
528 // If we are decoding multiple objects, grab the pointer to the
529 // iterator, and increment it for the next loop
530 if (argTypes.size() > 1)
531 {
532 if (jIt == input_json.end())
533 {
534 return -2;
535 }
536 j = &*jIt;
537 jIt++;
538 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500539 const int64_t* intValue = j->get_ptr<const int64_t*>();
540 const std::string* stringValue = j->get_ptr<const std::string*>();
541 const double* doubleValue = j->get_ptr<const double*>();
542 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700543 int64_t v = 0;
544 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700545
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 // Do some basic type conversions that make sense. uint can be
547 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700548 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500550 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700551 if (uintValue != nullptr)
552 {
553 v = static_cast<int64_t>(*uintValue);
554 intValue = &v;
555 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 }
Ed Tanous66664f22019-10-11 13:05:49 -0700557 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500559 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700560 if (uintValue != nullptr)
561 {
562 d = static_cast<double>(*uintValue);
563 doubleValue = &d;
564 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700565 }
Ed Tanous66664f22019-10-11 13:05:49 -0700566 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700567 {
Ed Tanous66664f22019-10-11 13:05:49 -0700568 if (intValue != nullptr)
569 {
570 d = static_cast<double>(*intValue);
571 doubleValue = &d;
572 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700573 }
574
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700575 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700577 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700578 {
579 return -1;
580 }
Ed Tanous271584a2019-07-09 16:24:22 -0700581 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500582 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700583 if (r < 0)
584 {
585 return r;
586 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700587 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700590 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591 {
592 return -1;
593 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500594 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
595 (*intValue > std::numeric_limits<int32_t>::max()))
596 {
597 return -ERANGE;
598 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 int32_t i = static_cast<int32_t>(*intValue);
600 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 if (r < 0)
602 {
603 return r;
604 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
608 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700609 int boolInt = false;
610 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700611 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500612 if (*intValue == 1)
613 {
614 boolInt = true;
615 }
616 else if (*intValue == 0)
617 {
618 boolInt = false;
619 }
620 else
621 {
622 return -ERANGE;
623 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 }
625 else if (b != nullptr)
626 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600627 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700629 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700631 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 }
633 else
634 {
635 return -1;
636 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 if (r < 0)
639 {
640 return r;
641 }
642 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700643 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700644 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700645 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700646 {
647 return -1;
648 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500649 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
650 (*intValue > std::numeric_limits<int16_t>::max()))
651 {
652 return -ERANGE;
653 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 int16_t n = static_cast<int16_t>(*intValue);
655 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 if (r < 0)
657 {
658 return r;
659 }
660 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700663 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700664 {
665 return -1;
666 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700667 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700668 if (r < 0)
669 {
670 return r;
671 }
672 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700673 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500675 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700676 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 {
678 return -1;
679 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000680 if (*uintValue > std::numeric_limits<uint8_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500681 {
682 return -ERANGE;
683 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700684 uint8_t y = static_cast<uint8_t>(*uintValue);
685 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700687 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700688 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500689 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700690 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700691 {
692 return -1;
693 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000694 if (*uintValue > std::numeric_limits<uint16_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500695 {
696 return -ERANGE;
697 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700698 uint16_t q = static_cast<uint16_t>(*uintValue);
699 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700700 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700701 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500703 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700704 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 {
706 return -1;
707 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000708 if (*uintValue > std::numeric_limits<uint32_t>::max())
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500709 {
710 return -ERANGE;
711 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700712 uint32_t u = static_cast<uint32_t>(*uintValue);
713 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700714 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700715 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700716 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500717 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700718 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700719 {
720 return -1;
721 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700722 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700724 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700725 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500726 if (doubleValue == nullptr)
727 {
728 return -1;
729 }
730 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
731 (*doubleValue > std::numeric_limits<double>::max()))
732 {
733 return -ERANGE;
734 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700735 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700737 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700738 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700739 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700741 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 if (r < 0)
743 {
744 return r;
745 }
746
Ed Tanous0dfeda62019-10-24 11:21:38 -0700747 for (const auto& it : *j)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 {
Ed Tanous0dfeda62019-10-24 11:21:38 -0700749 r = convertJsonToDbus(m, containedType, it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 if (r < 0)
751 {
752 return r;
753 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 }
755 sd_bus_message_close_container(m);
756 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700757 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 std::string containedType = argCode.substr(1);
760 BMCWEB_LOG_DEBUG << "variant type: " << argCode
761 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700762 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700763 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700764 if (r < 0)
765 {
766 return r;
767 }
768
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700769 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 if (r < 0)
771 {
772 return r;
773 }
774
775 r = sd_bus_message_close_container(m);
776 if (r < 0)
777 {
778 return r;
779 }
780 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700781 else if (boost::starts_with(argCode, "(") &&
782 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700783 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700784 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700786 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800787 if (r < 0)
788 {
789 return r;
790 }
791
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 nlohmann::json::const_iterator it = j->begin();
Ed Tanouscb13a392020-07-25 19:02:03 +0000793 for (const std::string& argCode2 : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700794 {
795 if (it == j->end())
796 {
797 return -1;
798 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000799 r = convertJsonToDbus(m, argCode2, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 if (r < 0)
801 {
802 return r;
803 }
804 it++;
805 }
806 r = sd_bus_message_close_container(m);
807 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700808 else if (boost::starts_with(argCode, "{") &&
809 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700810 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700811 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700813 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800814 if (r < 0)
815 {
816 return r;
817 }
818
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700819 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 if (codes.size() != 2)
821 {
822 return -1;
823 }
Ed Tanous2c70f802020-09-28 14:29:23 -0700824 const std::string& keyType = codes[0];
825 const std::string& valueType = codes[1];
Ed Tanous3174e4d2020-10-07 11:41:22 -0700826 for (const auto& it : j->items())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700828 r = convertJsonToDbus(m, keyType, it.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 if (r < 0)
830 {
831 return r;
832 }
833
Ed Tanous2c70f802020-09-28 14:29:23 -0700834 r = convertJsonToDbus(m, valueType, it.value());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 if (r < 0)
836 {
837 return r;
838 }
839 }
840 r = sd_bus_message_close_container(m);
841 }
842 else
843 {
844 return -2;
845 }
846 if (r < 0)
847 {
848 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700849 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700850
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 if (argTypes.size() > 1)
852 {
853 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700854 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700855 }
Matt Spinler127ea542019-01-14 11:04:28 -0600856
857 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700858}
859
Matt Spinlerd22a7132019-01-14 12:14:30 -0600860template <typename T>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500861int readMessageItem(const std::string& typeCode, sdbusplus::message::message& m,
862 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600863{
864 T value;
865
866 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
867 if (r < 0)
868 {
869 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
870 << " failed!";
871 return r;
872 }
873
874 data = value;
875 return 0;
876}
877
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878int convertDBusToJSON(const std::string& returnType,
879 sdbusplus::message::message& m, nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600880
Ed Tanous23a21a12020-07-25 04:45:05 +0000881inline int readDictEntryFromMessage(const std::string& typeCode,
882 sdbusplus::message::message& m,
883 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600884{
885 std::vector<std::string> types = dbusArgSplit(typeCode);
886 if (types.size() != 2)
887 {
888 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
889 << types.size();
890 return -1;
891 }
892
893 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
894 typeCode.c_str());
895 if (r < 0)
896 {
897 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
898 return r;
899 }
900
901 nlohmann::json key;
902 r = convertDBusToJSON(types[0], m, key);
903 if (r < 0)
904 {
905 return r;
906 }
907
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500908 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -0600909 if (keyPtr == nullptr)
910 {
911 // json doesn't support non-string keys. If we hit this condition,
912 // convert the result to a string so we can proceed
913 key = key.dump();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500914 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -0700915 // in theory this can't fail now, but lets be paranoid about it
916 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600917 if (keyPtr == nullptr)
918 {
919 return -1;
920 }
921 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500922 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -0600923
924 r = convertDBusToJSON(types[1], m, value);
925 if (r < 0)
926 {
927 return r;
928 }
929
930 r = sd_bus_message_exit_container(m.get());
931 if (r < 0)
932 {
933 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
934 return r;
935 }
936
937 return 0;
938}
939
Ed Tanous23a21a12020-07-25 04:45:05 +0000940inline int readArrayFromMessage(const std::string& typeCode,
941 sdbusplus::message::message& m,
942 nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -0600943{
944 if (typeCode.size() < 2)
945 {
946 BMCWEB_LOG_ERROR << "Type code " << typeCode
947 << " too small for an array";
948 return -1;
949 }
950
951 std::string containedType = typeCode.substr(1);
952
953 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
954 containedType.c_str());
955 if (r < 0)
956 {
957 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
958 << r;
959 return r;
960 }
961
962 bool dict = boost::starts_with(containedType, "{") &&
963 boost::ends_with(containedType, "}");
964
965 if (dict)
966 {
967 // Remove the { }
968 containedType = containedType.substr(1, containedType.size() - 2);
969 data = nlohmann::json::object();
970 }
971 else
972 {
973 data = nlohmann::json::array();
974 }
975
976 while (true)
977 {
978 r = sd_bus_message_at_end(m.get(), false);
979 if (r < 0)
980 {
981 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
982 return r;
983 }
984
985 if (r > 0)
986 {
987 break;
988 }
989
990 // Dictionaries are only ever seen in an array
991 if (dict)
992 {
993 r = readDictEntryFromMessage(containedType, m, data);
994 if (r < 0)
995 {
996 return r;
997 }
998 }
999 else
1000 {
1001 data.push_back(nlohmann::json());
1002
1003 r = convertDBusToJSON(containedType, m, data.back());
1004 if (r < 0)
1005 {
1006 return r;
1007 }
1008 }
1009 }
1010
1011 r = sd_bus_message_exit_container(m.get());
1012 if (r < 0)
1013 {
1014 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1015 return r;
1016 }
1017
1018 return 0;
1019}
1020
Ed Tanous23a21a12020-07-25 04:45:05 +00001021inline int readStructFromMessage(const std::string& typeCode,
1022 sdbusplus::message::message& m,
1023 nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001024{
1025 if (typeCode.size() < 3)
1026 {
1027 BMCWEB_LOG_ERROR << "Type code " << typeCode
1028 << " too small for a struct";
1029 return -1;
1030 }
1031
1032 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1033 std::vector<std::string> types = dbusArgSplit(containedTypes);
1034
1035 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1036 containedTypes.c_str());
1037 if (r < 0)
1038 {
1039 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1040 << r;
1041 return r;
1042 }
1043
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001044 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001045 {
1046 data.push_back(nlohmann::json());
1047 r = convertDBusToJSON(type, m, data.back());
1048 if (r < 0)
1049 {
1050 return r;
1051 }
1052 }
1053
1054 r = sd_bus_message_exit_container(m.get());
1055 if (r < 0)
1056 {
1057 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1058 return r;
1059 }
1060 return 0;
1061}
1062
Ed Tanous23a21a12020-07-25 04:45:05 +00001063inline int readVariantFromMessage(sdbusplus::message::message& m,
1064 nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001065{
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001066 const char* containerType;
Ed Tanous99131cd2019-10-24 11:12:47 -07001067 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001068 if (r < 0)
1069 {
1070 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1071 return r;
1072 }
1073
1074 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1075 containerType);
1076 if (r < 0)
1077 {
1078 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1079 << r;
1080 return r;
1081 }
1082
1083 r = convertDBusToJSON(containerType, m, data);
1084 if (r < 0)
1085 {
1086 return r;
1087 }
1088
1089 r = sd_bus_message_exit_container(m.get());
1090 if (r < 0)
1091 {
1092 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1093 return r;
1094 }
1095
1096 return 0;
1097}
1098
Ed Tanous23a21a12020-07-25 04:45:05 +00001099inline int convertDBusToJSON(const std::string& returnType,
1100 sdbusplus::message::message& m,
1101 nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001102{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001103 int r = 0;
1104 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1105
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001106 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001107 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001108 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001109 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 {
1111 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001112 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001113 }
1114
Ed Tanousd4d25792020-09-29 15:15:03 -07001115 if (typeCode == "s" || typeCode == "g" || typeCode == "o")
Matt Spinlerd22a7132019-01-14 12:14:30 -06001116 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001117 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001118 if (r < 0)
1119 {
1120 return r;
1121 }
1122 }
1123 else if (typeCode == "b")
1124 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001125 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001126 if (r < 0)
1127 {
1128 return r;
1129 }
1130
Matt Spinlerf39420c2019-01-30 12:57:18 -06001131 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001132 }
1133 else if (typeCode == "u")
1134 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001135 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001136 if (r < 0)
1137 {
1138 return r;
1139 }
1140 }
1141 else if (typeCode == "i")
1142 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001143 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001144 if (r < 0)
1145 {
1146 return r;
1147 }
1148 }
1149 else if (typeCode == "x")
1150 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001151 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001152 if (r < 0)
1153 {
1154 return r;
1155 }
1156 }
1157 else if (typeCode == "t")
1158 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001159 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001160 if (r < 0)
1161 {
1162 return r;
1163 }
1164 }
1165 else if (typeCode == "n")
1166 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001167 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001168 if (r < 0)
1169 {
1170 return r;
1171 }
1172 }
1173 else if (typeCode == "q")
1174 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001175 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001176 if (r < 0)
1177 {
1178 return r;
1179 }
1180 }
1181 else if (typeCode == "y")
1182 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001183 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001184 if (r < 0)
1185 {
1186 return r;
1187 }
1188 }
1189 else if (typeCode == "d")
1190 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001191 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001192 if (r < 0)
1193 {
1194 return r;
1195 }
1196 }
1197 else if (typeCode == "h")
1198 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001199 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001200 if (r < 0)
1201 {
1202 return r;
1203 }
1204 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001205 else if (boost::starts_with(typeCode, "a"))
1206 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001207 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001208 if (r < 0)
1209 {
1210 return r;
1211 }
1212 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001213 else if (boost::starts_with(typeCode, "(") &&
1214 boost::ends_with(typeCode, ")"))
1215 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001216 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001217 if (r < 0)
1218 {
1219 return r;
1220 }
1221 }
Matt Spinler89c19702019-01-14 13:13:00 -06001222 else if (boost::starts_with(typeCode, "v"))
1223 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001224 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001225 if (r < 0)
1226 {
1227 return r;
1228 }
1229 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001230 else
1231 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001232 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1233 return -2;
1234 }
1235 }
1236
Matt Spinler16caaee2019-01-15 11:40:34 -06001237 return 0;
1238}
1239
Ed Tanous23a21a12020-07-25 04:45:05 +00001240inline void
1241 handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1242 sdbusplus::message::message& m,
1243 const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001244{
Matt Spinler39a4e392019-01-15 11:53:13 -06001245 nlohmann::json data;
1246
1247 int r = convertDBusToJSON(returnType, m, data);
1248 if (r < 0)
1249 {
1250 transaction->outputFailed = true;
1251 return;
1252 }
1253
1254 if (data.is_null())
1255 {
1256 return;
1257 }
1258
1259 if (transaction->methodResponse.is_null())
1260 {
1261 transaction->methodResponse = std::move(data);
1262 return;
1263 }
1264
1265 // If they're both dictionaries or arrays, merge into one.
1266 // Otherwise, make the results an array with every result
1267 // an entry. Could also just fail in that case, but it
1268 // seems better to get the data back somehow.
1269
1270 if (transaction->methodResponse.is_object() && data.is_object())
1271 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001272 for (const auto& obj : data.items())
Matt Spinler39a4e392019-01-15 11:53:13 -06001273 {
1274 // Note: Will overwrite the data for a duplicate key
1275 transaction->methodResponse.emplace(obj.key(),
1276 std::move(obj.value()));
1277 }
1278 return;
1279 }
1280
1281 if (transaction->methodResponse.is_array() && data.is_array())
1282 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001283 for (auto& obj : data)
Matt Spinler39a4e392019-01-15 11:53:13 -06001284 {
1285 transaction->methodResponse.push_back(std::move(obj));
1286 }
1287 return;
1288 }
1289
1290 if (!transaction->convertedToArray)
1291 {
1292 // They are different types. May as well turn them into an array
1293 nlohmann::json j = std::move(transaction->methodResponse);
1294 transaction->methodResponse = nlohmann::json::array();
1295 transaction->methodResponse.push_back(std::move(j));
1296 transaction->methodResponse.push_back(std::move(data));
1297 transaction->convertedToArray = true;
1298 }
1299 else
1300 {
1301 transaction->methodResponse.push_back(std::move(data));
1302 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001303}
1304
Ed Tanous23a21a12020-07-25 04:45:05 +00001305inline void
1306 findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
1307 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001308{
1309 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1310 << connectionName;
1311 crow::connections::systemBus->async_method_call(
1312 [transaction, connectionName{std::string(connectionName)}](
1313 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001314 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001315 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1316 if (ec)
1317 {
1318 BMCWEB_LOG_ERROR
1319 << "Introspect call failed with error: " << ec.message()
1320 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001321 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001322 }
Matt Spinler318bd892019-01-15 09:59:20 -06001323 tinyxml2::XMLDocument doc;
1324
1325 doc.Parse(introspect_xml.data(), introspect_xml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001326 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Matt Spinler318bd892019-01-15 09:59:20 -06001327 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001328 {
Matt Spinler318bd892019-01-15 09:59:20 -06001329 BMCWEB_LOG_ERROR << "XML document failed to parse "
1330 << connectionName << "\n";
1331 return;
1332 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001333 tinyxml2::XMLElement* interfaceNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001334 pRoot->FirstChildElement("interface");
1335 while (interfaceNode != nullptr)
1336 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001337 const char* thisInterfaceName =
Matt Spinler318bd892019-01-15 09:59:20 -06001338 interfaceNode->Attribute("name");
1339 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001340 {
Matt Spinler318bd892019-01-15 09:59:20 -06001341 if (!transaction->interfaceName.empty() &&
1342 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001343 {
Matt Spinler318bd892019-01-15 09:59:20 -06001344 interfaceNode =
1345 interfaceNode->NextSiblingElement("interface");
1346 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001347 }
Matt Spinler318bd892019-01-15 09:59:20 -06001348
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001349 tinyxml2::XMLElement* methodNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001350 interfaceNode->FirstChildElement("method");
1351 while (methodNode != nullptr)
1352 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001353 const char* thisMethodName =
Matt Spinler318bd892019-01-15 09:59:20 -06001354 methodNode->Attribute("name");
1355 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1356 if (thisMethodName != nullptr &&
1357 thisMethodName == transaction->methodName)
1358 {
1359 BMCWEB_LOG_DEBUG
1360 << "Found method named " << thisMethodName
1361 << " on interface " << thisInterfaceName;
1362 sdbusplus::message::message m =
1363 crow::connections::systemBus->new_method_call(
1364 connectionName.c_str(),
1365 transaction->path.c_str(),
1366 thisInterfaceName,
1367 transaction->methodName.c_str());
1368
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001369 tinyxml2::XMLElement* argumentNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001370 methodNode->FirstChildElement("arg");
1371
Matt Spinler16caaee2019-01-15 11:40:34 -06001372 std::string returnType;
1373
1374 // Find the output type
1375 while (argumentNode != nullptr)
1376 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001377 const char* argDirection =
Matt Spinler16caaee2019-01-15 11:40:34 -06001378 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001379 const char* argType =
Matt Spinler16caaee2019-01-15 11:40:34 -06001380 argumentNode->Attribute("type");
1381 if (argDirection != nullptr &&
1382 argType != nullptr &&
1383 std::string(argDirection) == "out")
1384 {
1385 returnType = argType;
1386 break;
1387 }
1388 argumentNode =
1389 argumentNode->NextSiblingElement("arg");
1390 }
1391
Matt Spinler318bd892019-01-15 09:59:20 -06001392 nlohmann::json::const_iterator argIt =
1393 transaction->arguments.begin();
1394
Matt Spinler16caaee2019-01-15 11:40:34 -06001395 argumentNode = methodNode->FirstChildElement("arg");
1396
Matt Spinler318bd892019-01-15 09:59:20 -06001397 while (argumentNode != nullptr)
1398 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001399 const char* argDirection =
Matt Spinler318bd892019-01-15 09:59:20 -06001400 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001401 const char* argType =
Matt Spinler318bd892019-01-15 09:59:20 -06001402 argumentNode->Attribute("type");
1403 if (argDirection != nullptr &&
1404 argType != nullptr &&
1405 std::string(argDirection) == "in")
1406 {
1407 if (argIt == transaction->arguments.end())
1408 {
1409 transaction->setErrorStatus(
1410 "Invalid method args");
1411 return;
1412 }
1413 if (convertJsonToDbus(m.get(),
1414 std::string(argType),
1415 *argIt) < 0)
1416 {
1417 transaction->setErrorStatus(
1418 "Invalid method arg type");
1419 return;
1420 }
1421
1422 argIt++;
1423 }
1424 argumentNode =
1425 argumentNode->NextSiblingElement("arg");
1426 }
1427
1428 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001429 m, [transaction, returnType](
Ed Tanous23a21a12020-07-25 04:45:05 +00001430 boost::system::error_code ec2,
1431 sdbusplus::message::message& m2) {
1432 if (ec2)
Matt Spinler318bd892019-01-15 09:59:20 -06001433 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001434 transaction->methodFailed = true;
Ed Tanous23a21a12020-07-25 04:45:05 +00001435 const sd_bus_error* e = m2.get_error();
Matt Spinler06b1b632019-06-18 16:09:25 -05001436
1437 if (e)
1438 {
1439 setErrorResponse(
1440 transaction->res,
1441 boost::beast::http::status::
1442 bad_request,
1443 e->name, e->message);
1444 }
1445 else
1446 {
1447 setErrorResponse(
1448 transaction->res,
1449 boost::beast::http::status::
1450 bad_request,
1451 "Method call failed",
1452 methodFailedMsg);
1453 }
Matt Spinler318bd892019-01-15 09:59:20 -06001454 return;
1455 }
Ed Tanous3174e4d2020-10-07 11:41:22 -07001456 transaction->methodPassed = true;
Matt Spinler16caaee2019-01-15 11:40:34 -06001457
Ed Tanous23a21a12020-07-25 04:45:05 +00001458 handleMethodResponse(transaction, m2,
Matt Spinler16caaee2019-01-15 11:40:34 -06001459 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001460 });
1461 break;
1462 }
1463 methodNode = methodNode->NextSiblingElement("method");
1464 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001465 }
Matt Spinler318bd892019-01-15 09:59:20 -06001466 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001467 }
1468 },
1469 connectionName, transaction->path,
1470 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001471}
1472
Ed Tanous23a21a12020-07-25 04:45:05 +00001473inline void handleAction(const crow::Request& req, crow::Response& res,
1474 const std::string& objectPath,
1475 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001476{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001477 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1478 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001479 nlohmann::json requestDbusData =
1480 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001481
Ed Tanous1abe55e2018-09-05 08:30:59 -07001482 if (requestDbusData.is_discarded())
1483 {
Matt Spinler6db06242018-12-11 11:21:22 -06001484 setErrorResponse(res, boost::beast::http::status::bad_request,
1485 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001486 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 return;
1488 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001489 nlohmann::json::iterator data = requestDbusData.find("data");
1490 if (data == requestDbusData.end())
1491 {
Matt Spinler6db06242018-12-11 11:21:22 -06001492 setErrorResponse(res, boost::beast::http::status::bad_request,
1493 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001494 res.end();
1495 return;
1496 }
1497
1498 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001499 {
Matt Spinler6db06242018-12-11 11:21:22 -06001500 setErrorResponse(res, boost::beast::http::status::bad_request,
1501 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502 res.end();
1503 return;
1504 }
1505 auto transaction = std::make_shared<InProgressActionData>(res);
1506
1507 transaction->path = objectPath;
1508 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001509 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001510 crow::connections::systemBus->async_method_call(
1511 [transaction](
1512 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001513 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1514 interfaceNames) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001515 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001516 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001517 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001518 setErrorResponse(transaction->res,
1519 boost::beast::http::status::not_found,
1520 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 return;
1522 }
1523
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001524 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1525 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001526
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001527 for (const std::pair<std::string, std::vector<std::string>>&
1528 object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 {
1530 findActionOnInterface(transaction, object.first);
1531 }
1532 },
1533 "xyz.openbmc_project.ObjectMapper",
1534 "/xyz/openbmc_project/object_mapper",
1535 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1536 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001537}
1538
Ed Tanouscb13a392020-07-25 19:02:03 +00001539inline void handleDelete(crow::Response& res, const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001540{
1541 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1542
1543 crow::connections::systemBus->async_method_call(
1544 [&res, objectPath](
1545 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001546 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1547 interfaceNames) {
Matt Spinlerde818812018-12-11 16:39:20 -06001548 if (ec || interfaceNames.size() <= 0)
1549 {
1550 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001551 setErrorResponse(res,
1552 boost::beast::http::status::method_not_allowed,
1553 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001554 res.end();
1555 return;
1556 }
1557
1558 auto transaction = std::make_shared<InProgressActionData>(res);
1559 transaction->path = objectPath;
1560 transaction->methodName = "Delete";
1561 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1562
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001563 for (const std::pair<std::string, std::vector<std::string>>&
1564 object : interfaceNames)
Matt Spinlerde818812018-12-11 16:39:20 -06001565 {
1566 findActionOnInterface(transaction, object.first);
1567 }
1568 },
1569 "xyz.openbmc_project.ObjectMapper",
1570 "/xyz/openbmc_project/object_mapper",
1571 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001572 std::array<const char*, 0>());
Matt Spinlerde818812018-12-11 16:39:20 -06001573}
1574
Ed Tanous23a21a12020-07-25 04:45:05 +00001575inline void handleList(crow::Response& res, const std::string& objectPath,
1576 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001577{
1578 crow::connections::systemBus->async_method_call(
1579 [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001580 std::vector<std::string>& objectPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001581 if (ec)
1582 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001583 setErrorResponse(res, boost::beast::http::status::not_found,
1584 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001585 }
1586 else
1587 {
1588 res.jsonValue = {{"status", "ok"},
1589 {"message", "200 OK"},
1590 {"data", std::move(objectPaths)}};
1591 }
1592 res.end();
1593 },
1594 "xyz.openbmc_project.ObjectMapper",
1595 "/xyz/openbmc_project/object_mapper",
1596 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001597 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001599
Ed Tanous23a21a12020-07-25 04:45:05 +00001600inline void handleEnumerate(crow::Response& res, const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001601{
Ed Tanous049a0512018-11-01 13:58:42 -07001602 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1603 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1604
1605 asyncResp->res.jsonValue = {{"message", "200 OK"},
1606 {"status", "ok"},
1607 {"data", nlohmann::json::object()}};
1608
Ed Tanous1abe55e2018-09-05 08:30:59 -07001609 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001610 [objectPath, asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001611 GetSubTreeType& object_names) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001612 auto transaction = std::make_shared<InProgressEnumerateData>(
1613 objectPath, asyncResp);
1614
1615 transaction->subtree =
1616 std::make_shared<GetSubTreeType>(std::move(object_names));
1617
Ed Tanous1abe55e2018-09-05 08:30:59 -07001618 if (ec)
1619 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001620 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1621 << transaction->objectPath;
1622 setErrorResponse(transaction->asyncResp->res,
1623 boost::beast::http::status::not_found,
1624 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001625 return;
1626 }
Ed Tanous64530012018-02-06 17:08:16 -08001627
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001628 // Add the data for the path passed in to the results
1629 // as if GetSubTree returned it, and continue on enumerating
1630 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001631 },
1632 "xyz.openbmc_project.ObjectMapper",
1633 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -07001634 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001635 std::array<const char*, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001636}
Ed Tanous911ac312017-08-15 09:37:42 -07001637
Ed Tanous23a21a12020-07-25 04:45:05 +00001638inline void handleGet(crow::Response& res, std::string& objectPath,
1639 std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001640{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001641 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1642 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001643 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001644
Ed Tanous1abe55e2018-09-05 08:30:59 -07001645 std::shared_ptr<std::string> path =
1646 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001647
Ed Tanous1abe55e2018-09-05 08:30:59 -07001648 using GetObjectType =
1649 std::vector<std::pair<std::string, std::vector<std::string>>>;
1650 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001651 [&res, path, propertyName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001652 const GetObjectType& object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 if (ec || object_names.size() <= 0)
1654 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001655 setErrorResponse(res, boost::beast::http::status::not_found,
1656 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001657 res.end();
1658 return;
1659 }
1660 std::shared_ptr<nlohmann::json> response =
1661 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001662 // The mapper should never give us an empty interface names
1663 // list, but check anyway
Ed Tanous23a21a12020-07-25 04:45:05 +00001664 for (const std::pair<std::string, std::vector<std::string>>&
Ed Tanous1abe55e2018-09-05 08:30:59 -07001665 connection : object_names)
1666 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001667 const std::vector<std::string>& interfaceNames =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001668 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001669
Ed Tanous1abe55e2018-09-05 08:30:59 -07001670 if (interfaceNames.size() <= 0)
1671 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001672 setErrorResponse(res, boost::beast::http::status::not_found,
1673 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001674 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001675 return;
1676 }
1677
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001678 for (const std::string& interface : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001679 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001680 sdbusplus::message::message m =
1681 crow::connections::systemBus->new_method_call(
1682 connection.first.c_str(), path->c_str(),
1683 "org.freedesktop.DBus.Properties", "GetAll");
1684 m.append(interface);
1685 crow::connections::systemBus->async_send(
1686 m, [&res, response,
Ed Tanous23a21a12020-07-25 04:45:05 +00001687 propertyName](const boost::system::error_code ec2,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001688 sdbusplus::message::message& msg) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001689 if (ec2)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001690 {
1691 BMCWEB_LOG_ERROR << "Bad dbus request error: "
Ed Tanous23a21a12020-07-25 04:45:05 +00001692 << ec2;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001693 }
1694 else
1695 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001696 nlohmann::json properties;
1697 int r =
1698 convertDBusToJSON("a{sv}", msg, properties);
1699 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001700 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001701 BMCWEB_LOG_ERROR
1702 << "convertDBusToJSON failed";
1703 }
1704 else
1705 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001706 for (auto& prop : properties.items())
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001707 {
Ed Tanous7c091622019-05-23 11:42:36 -07001708 // if property name is empty, or
1709 // matches our search query, add it
1710 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001711
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001712 if (propertyName->empty())
1713 {
1714 (*response)[prop.key()] =
1715 std::move(prop.value());
1716 }
1717 else if (prop.key() == *propertyName)
1718 {
1719 *response = std::move(prop.value());
1720 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 }
1722 }
1723 }
1724 if (response.use_count() == 1)
1725 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001726 if (!propertyName->empty() && response->empty())
1727 {
1728 setErrorResponse(
1729 res,
1730 boost::beast::http::status::not_found,
1731 propNotFoundDesc, notFoundMsg);
1732 }
1733 else
1734 {
1735 res.jsonValue = {{"status", "ok"},
1736 {"message", "200 OK"},
1737 {"data", *response}};
1738 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001739 res.end();
1740 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001741 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001742 }
1743 }
1744 },
1745 "xyz.openbmc_project.ObjectMapper",
1746 "/xyz/openbmc_project/object_mapper",
1747 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1748 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001749}
1750
Ed Tanous1abe55e2018-09-05 08:30:59 -07001751struct AsyncPutRequest
1752{
Ed Tanous23a21a12020-07-25 04:45:05 +00001753 AsyncPutRequest(crow::Response& resIn) : res(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001754 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001755 ~AsyncPutRequest()
1756 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001757 if (res.jsonValue.empty())
1758 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001759 setErrorResponse(res, boost::beast::http::status::forbidden,
1760 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001761 }
1762
1763 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001764 }
1765
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001766 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001767 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001768 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1769 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001770 }
1771
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001772 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001773 std::string objectPath;
1774 std::string propertyName;
1775 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001776};
1777
Ed Tanous23a21a12020-07-25 04:45:05 +00001778inline void handlePut(const crow::Request& req, crow::Response& res,
1779 const std::string& objectPath,
1780 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001781{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001782 if (destProperty.empty())
1783 {
1784 setErrorResponse(res, boost::beast::http::status::forbidden,
1785 forbiddenResDesc, forbiddenMsg);
1786 res.end();
1787 return;
1788 }
1789
Ed Tanous1abe55e2018-09-05 08:30:59 -07001790 nlohmann::json requestDbusData =
1791 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001792
Ed Tanous1abe55e2018-09-05 08:30:59 -07001793 if (requestDbusData.is_discarded())
1794 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001795 setErrorResponse(res, boost::beast::http::status::bad_request,
1796 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001797 res.end();
1798 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001799 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001800
Ed Tanous1abe55e2018-09-05 08:30:59 -07001801 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1802 if (propertyIt == requestDbusData.end())
1803 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001804 setErrorResponse(res, boost::beast::http::status::bad_request,
1805 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001806 res.end();
1807 return;
1808 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001809 const nlohmann::json& propertySetValue = *propertyIt;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001810 auto transaction = std::make_shared<AsyncPutRequest>(res);
1811 transaction->objectPath = objectPath;
1812 transaction->propertyName = destProperty;
1813 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001814
Ed Tanous1abe55e2018-09-05 08:30:59 -07001815 using GetObjectType =
1816 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001817
Ed Tanous1abe55e2018-09-05 08:30:59 -07001818 crow::connections::systemBus->async_method_call(
Ed Tanous23a21a12020-07-25 04:45:05 +00001819 [transaction](const boost::system::error_code ec2,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001820 const GetObjectType& object_names) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001821 if (!ec2 && object_names.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001822 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001823 setErrorResponse(transaction->res,
1824 boost::beast::http::status::not_found,
1825 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001826 return;
1827 }
Ed Tanous911ac312017-08-15 09:37:42 -07001828
Ed Tanous23a21a12020-07-25 04:45:05 +00001829 for (const std::pair<std::string, std::vector<std::string>>&
Ed Tanous1abe55e2018-09-05 08:30:59 -07001830 connection : object_names)
1831 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001832 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001833
Ed Tanous1abe55e2018-09-05 08:30:59 -07001834 crow::connections::systemBus->async_method_call(
1835 [connectionName{std::string(connectionName)},
Ed Tanous23a21a12020-07-25 04:45:05 +00001836 transaction](const boost::system::error_code ec3,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001837 const std::string& introspectXml) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001838 if (ec3)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001839 {
1840 BMCWEB_LOG_ERROR
1841 << "Introspect call failed with error: "
Ed Tanous23a21a12020-07-25 04:45:05 +00001842 << ec3.message()
Ed Tanous1abe55e2018-09-05 08:30:59 -07001843 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001844 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001845 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001846 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001847 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001848
Ed Tanous1abe55e2018-09-05 08:30:59 -07001849 doc.Parse(introspectXml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001850 tinyxml2::XMLNode* pRoot =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001851 doc.FirstChildElement("node");
1852 if (pRoot == nullptr)
1853 {
1854 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1855 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001856 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001857 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001858 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001859 tinyxml2::XMLElement* ifaceNode =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001860 pRoot->FirstChildElement("interface");
1861 while (ifaceNode != nullptr)
1862 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001863 const char* interfaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001864 ifaceNode->Attribute("name");
1865 BMCWEB_LOG_DEBUG << "found interface "
1866 << interfaceName;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001867 tinyxml2::XMLElement* propNode =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001868 ifaceNode->FirstChildElement("property");
1869 while (propNode != nullptr)
1870 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001871 const char* propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001872 propNode->Attribute("name");
1873 BMCWEB_LOG_DEBUG << "Found property "
1874 << propertyName;
1875 if (propertyName == transaction->propertyName)
1876 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001877 const char* argType =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001878 propNode->Attribute("type");
1879 if (argType != nullptr)
1880 {
1881 sdbusplus::message::message m =
1882 crow::connections::systemBus
1883 ->new_method_call(
1884 connectionName.c_str(),
1885 transaction->objectPath
1886 .c_str(),
1887 "org.freedesktop.DBus."
1888 "Properties",
1889 "Set");
1890 m.append(interfaceName,
1891 transaction->propertyName);
1892 int r = sd_bus_message_open_container(
1893 m.get(), SD_BUS_TYPE_VARIANT,
1894 argType);
1895 if (r < 0)
1896 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001897 transaction->setErrorStatus(
1898 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001899 return;
1900 }
1901 r = convertJsonToDbus(
1902 m.get(), argType,
1903 transaction->propertyValue);
1904 if (r < 0)
1905 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001906 if (r == -ERANGE)
1907 {
1908 transaction->setErrorStatus(
1909 "Provided property value "
1910 "is out of range for the "
1911 "property type");
1912 }
1913 else
1914 {
1915 transaction->setErrorStatus(
1916 "Invalid arg type");
1917 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001918 return;
1919 }
1920 r = sd_bus_message_close_container(
1921 m.get());
1922 if (r < 0)
1923 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001924 transaction->setErrorStatus(
1925 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001926 return;
1927 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001928 crow::connections::systemBus
1929 ->async_send(
1930 m,
1931 [transaction](
1932 boost::system::error_code
1933 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001934 sdbusplus::message::message&
Ed Tanous23a21a12020-07-25 04:45:05 +00001935 m2) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001936 BMCWEB_LOG_DEBUG << "sent";
1937 if (ec)
1938 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001939 const sd_bus_error* e =
Ed Tanous23a21a12020-07-25 04:45:05 +00001940 m2.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001941 setErrorResponse(
1942 transaction->res,
1943 boost::beast::http::
1944 status::
1945 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001946 (e) ? e->name
1947 : ec.category()
1948 .name(),
1949 (e) ? e->message
1950 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001951 }
1952 else
1953 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001954 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001955 .jsonValue = {
1956 {"status", "ok"},
1957 {"message",
1958 "200 OK"},
1959 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001960 }
1961 });
1962 }
1963 }
1964 propNode =
1965 propNode->NextSiblingElement("property");
1966 }
1967 ifaceNode =
1968 ifaceNode->NextSiblingElement("interface");
1969 }
1970 },
1971 connectionName, transaction->objectPath,
1972 "org.freedesktop.DBus.Introspectable", "Introspect");
1973 }
1974 },
1975 "xyz.openbmc_project.ObjectMapper",
1976 "/xyz/openbmc_project/object_mapper",
1977 "xyz.openbmc_project.ObjectMapper", "GetObject",
1978 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001979}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001980
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001981inline void handleDBusUrl(const crow::Request& req, crow::Response& res,
1982 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07001983{
Ed Tanous049a0512018-11-01 13:58:42 -07001984
1985 // If accessing a single attribute, fill in and update objectPath,
1986 // otherwise leave destProperty blank
1987 std::string destProperty = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001988 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07001989 size_t attrPosition = objectPath.find(attrSeperator);
1990 if (attrPosition != objectPath.npos)
1991 {
1992 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1993 objectPath.length());
1994 objectPath = objectPath.substr(0, attrPosition);
1995 }
1996
Ed Tanousb41187f2019-10-24 16:30:02 -07001997 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07001998 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001999 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002000 size_t actionPosition = objectPath.find(actionSeperator);
2001 if (actionPosition != objectPath.npos)
2002 {
2003 std::string postProperty =
2004 objectPath.substr((actionPosition + strlen(actionSeperator)),
2005 objectPath.length());
2006 objectPath = objectPath.substr(0, actionPosition);
2007 handleAction(req, res, objectPath, postProperty);
2008 return;
2009 }
2010 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002011 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002012 {
2013 if (boost::ends_with(objectPath, "/enumerate"))
2014 {
2015 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2016 objectPath.end());
2017 handleEnumerate(res, objectPath);
2018 }
2019 else if (boost::ends_with(objectPath, "/list"))
2020 {
2021 objectPath.erase(objectPath.end() - sizeof("list"),
2022 objectPath.end());
2023 handleList(res, objectPath);
2024 }
2025 else
2026 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002027 // Trim any trailing "/" at the end
2028 if (boost::ends_with(objectPath, "/"))
2029 {
2030 objectPath.pop_back();
2031 handleList(res, objectPath, 1);
2032 }
2033 else
2034 {
2035 handleGet(res, objectPath, destProperty);
2036 }
Ed Tanous049a0512018-11-01 13:58:42 -07002037 }
2038 return;
2039 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002040 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002041 {
2042 handlePut(req, res, objectPath, destProperty);
2043 return;
2044 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002045 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002046 {
Ed Tanouscb13a392020-07-25 19:02:03 +00002047 handleDelete(res, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002048 return;
2049 }
Ed Tanous049a0512018-11-01 13:58:42 -07002050
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002051 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2052 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002053 res.end();
2054}
2055
Ed Tanous23a21a12020-07-25 04:45:05 +00002056inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002057{
2058 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002059 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002060 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002061 [](const crow::Request&, crow::Response& res) {
Gunnar Millscaa3ce32020-07-08 14:46:53 -05002062 res.jsonValue = {{"buses", {{{"name", "system"}}}},
Ed Tanous1abe55e2018-09-05 08:30:59 -07002063 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002064 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002065 });
2066
2067 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002068 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002069 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002070 [](const crow::Request&, crow::Response& res) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002071 auto myCallback = [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002072 std::vector<std::string>& names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002073 if (ec)
2074 {
2075 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2076 res.result(
2077 boost::beast::http::status::internal_server_error);
2078 }
2079 else
2080 {
2081 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002082 res.jsonValue = {{"status", "ok"}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002083 auto& objectsSub = res.jsonValue["objects"];
2084 for (auto& name : names)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002085 {
2086 objectsSub.push_back({{"name", name}});
2087 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002088 }
2089 res.end();
2090 };
2091 crow::connections::systemBus->async_method_call(
2092 std::move(myCallback), "org.freedesktop.DBus", "/",
2093 "org.freedesktop.DBus", "ListNames");
2094 });
2095
2096 BMCWEB_ROUTE(app, "/list/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002097 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002098 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002099 [](const crow::Request&, crow::Response& res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002100 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002101 });
2102
2103 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002104 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002105 .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2106 crow::Response& res,
2107 const std::string& path) {
Tanousf00032d2018-11-05 01:18:10 -03002108 std::string objectPath = "/xyz/" + path;
2109 handleDBusUrl(req, res, objectPath);
2110 });
2111
2112 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002113 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002114 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2115 boost::beast::http::verb::delete_)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002116 [](const crow::Request& req, crow::Response& res,
2117 const std::string& path) {
Ed Tanous049a0512018-11-01 13:58:42 -07002118 std::string objectPath = "/xyz/" + path;
2119 handleDBusUrl(req, res, objectPath);
2120 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002121
Ed Tanous049a0512018-11-01 13:58:42 -07002122 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002123 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002124 .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2125 crow::Response& res,
2126 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002127 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002128 handleDBusUrl(req, res, objectPath);
2129 });
2130
2131 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002132 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002133 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2134 boost::beast::http::verb::delete_)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002135 [](const crow::Request& req, crow::Response& res,
2136 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002137 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002138 handleDBusUrl(req, res, objectPath);
2139 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002140
Ed Tanous1abe55e2018-09-05 08:30:59 -07002141 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002142 .privileges({"ConfigureManager"})
Ed Tanouscb13a392020-07-25 19:02:03 +00002143 .methods(boost::beast::http::verb::get)([](const crow::Request&,
Ed Tanousb41187f2019-10-24 16:30:02 -07002144 crow::Response& res,
2145 const std::string& dumpId) {
Ed Tanous3174e4d2020-10-07 11:41:22 -07002146 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002147 if (!std::regex_match(dumpId, validFilename))
2148 {
Ed Tanousad18f072018-11-14 14:07:48 -08002149 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002150 res.end();
2151 return;
2152 }
James Feistf6150402019-01-08 10:36:20 -08002153 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002154 "/var/lib/phosphor-debug-collector/dumps");
2155
Ed Tanousad18f072018-11-14 14:07:48 -08002156 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002157
James Feistf6150402019-01-08 10:36:20 -08002158 if (!std::filesystem::exists(loc) ||
2159 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002160 {
Ed Tanousad18f072018-11-14 14:07:48 -08002161 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002162 res.result(boost::beast::http::status::not_found);
2163 res.end();
2164 return;
2165 }
James Feistf6150402019-01-08 10:36:20 -08002166 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002167
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002168 for (auto& file : files)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002169 {
2170 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002171 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002172 {
2173 continue;
2174 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002175
Ed Tanous1abe55e2018-09-05 08:30:59 -07002176 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002177
2178 // Assuming only one dump file will be present in the dump id
2179 // directory
2180 std::string dumpFileName = file.path().filename().string();
2181
2182 // Filename should be in alphanumeric, dot and underscore
2183 // Its based on phosphor-debug-collector application dumpfile
2184 // format
2185 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2186 if (!std::regex_match(dumpFileName, dumpFileRegex))
2187 {
2188 BMCWEB_LOG_ERROR << "Invalid dump filename "
2189 << dumpFileName;
2190 res.result(boost::beast::http::status::not_found);
2191 res.end();
2192 return;
2193 }
2194 std::string contentDispositionParam =
2195 "attachment; filename=\"" + dumpFileName + "\"";
2196
2197 res.addHeader("Content-Disposition", contentDispositionParam);
2198
Ed Tanous1abe55e2018-09-05 08:30:59 -07002199 res.body() = {std::istreambuf_iterator<char>(readFile),
2200 std::istreambuf_iterator<char>()};
2201 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002202 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002203 }
2204 res.result(boost::beast::http::status::not_found);
2205 res.end();
2206 return;
2207 });
2208
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002209 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002210 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002211
2212 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002213 [](const crow::Request&, crow::Response& res,
Ed Tanousb41187f2019-10-24 16:30:02 -07002214 const std::string& Connection) {
2215 introspectObjects(Connection, "/",
2216 std::make_shared<bmcweb::AsyncResp>(res));
2217 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002218
2219 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002220 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002221 .methods(
2222 boost::beast::http::verb::get,
2223 boost::beast::http::verb::post)([](const crow::Request& req,
2224 crow::Response& res,
2225 const std::string& processName,
2226 const std::string&
2227 requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002228 std::vector<std::string> strs;
2229 boost::split(strs, requestedPath, boost::is_any_of("/"));
2230 std::string objectPath;
2231 std::string interfaceName;
2232 std::string methodName;
2233 auto it = strs.begin();
2234 if (it == strs.end())
2235 {
2236 objectPath = "/";
2237 }
2238 while (it != strs.end())
2239 {
2240 // Check if segment contains ".". If it does, it must be an
2241 // interface
2242 if (it->find(".") != std::string::npos)
2243 {
2244 break;
Gunnar Millscaa3ce32020-07-08 14:46:53 -05002245 // This check is necessary as the trailing slash gets
Ed Tanous7c091622019-05-23 11:42:36 -07002246 // parsed as part of our <path> specifier above, which
2247 // causes the normal trailing backslash redirector to
2248 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002249 }
Ed Tanous3174e4d2020-10-07 11:41:22 -07002250 if (!it->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002251 {
2252 objectPath += "/" + *it;
2253 }
2254 it++;
2255 }
2256 if (it != strs.end())
2257 {
2258 interfaceName = *it;
2259 it++;
2260
2261 // after interface, we might have a method name
2262 if (it != strs.end())
2263 {
2264 methodName = *it;
2265 it++;
2266 }
2267 }
2268 if (it != strs.end())
2269 {
Ed Tanous7c091622019-05-23 11:42:36 -07002270 // if there is more levels past the method name, something
2271 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002272 res.result(boost::beast::http::status::not_found);
AppaRao Puli3c27ed32020-03-31 01:21:57 +05302273 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002274 return;
2275 }
2276 if (interfaceName.empty())
2277 {
Ed Tanous7c091622019-05-23 11:42:36 -07002278 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2279 std::make_shared<bmcweb::AsyncResp>(res);
2280
Ed Tanous1abe55e2018-09-05 08:30:59 -07002281 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002282 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002283 objectPath](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002284 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002285 if (ec)
2286 {
2287 BMCWEB_LOG_ERROR
2288 << "Introspect call failed with error: "
2289 << ec.message()
2290 << " on process: " << processName
2291 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002292 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002293 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002294 tinyxml2::XMLDocument doc;
2295
2296 doc.Parse(introspect_xml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002297 tinyxml2::XMLNode* pRoot =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002298 doc.FirstChildElement("node");
2299 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002300 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002301 BMCWEB_LOG_ERROR << "XML document failed to parse "
2302 << processName << " " << objectPath
2303 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002304 asyncResp->res.jsonValue = {
2305 {"status", "XML parse error"}};
2306 asyncResp->res.result(boost::beast::http::status::
2307 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002308 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002309 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002310
2311 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002312 asyncResp->res.jsonValue = {
2313 {"status", "ok"},
2314 {"bus_name", processName},
2315 {"object_path", objectPath}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002316 nlohmann::json& interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002317 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002318 interfacesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002319 tinyxml2::XMLElement* interface =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002320 pRoot->FirstChildElement("interface");
2321
2322 while (interface != nullptr)
2323 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002324 const char* ifaceName =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002325 interface->Attribute("name");
2326 if (ifaceName != nullptr)
2327 {
2328 interfacesArray.push_back(
2329 {{"name", ifaceName}});
2330 }
2331
2332 interface =
2333 interface->NextSiblingElement("interface");
2334 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002335 },
2336 processName, objectPath,
2337 "org.freedesktop.DBus.Introspectable", "Introspect");
2338 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002339 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002340 {
Ed Tanous7c091622019-05-23 11:42:36 -07002341 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2342 std::make_shared<bmcweb::AsyncResp>(res);
2343
Ed Tanous1abe55e2018-09-05 08:30:59 -07002344 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002345 [asyncResp, processName, objectPath,
2346 interfaceName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002347 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002348 if (ec)
2349 {
2350 BMCWEB_LOG_ERROR
2351 << "Introspect call failed with error: "
2352 << ec.message()
2353 << " on process: " << processName
2354 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002355 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002356 }
Ed Tanous7c091622019-05-23 11:42:36 -07002357 tinyxml2::XMLDocument doc;
2358
2359 doc.Parse(introspect_xml.data(), introspect_xml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002360 tinyxml2::XMLNode* pRoot =
Ed Tanous7c091622019-05-23 11:42:36 -07002361 doc.FirstChildElement("node");
2362 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002363 {
Ed Tanous7c091622019-05-23 11:42:36 -07002364 BMCWEB_LOG_ERROR << "XML document failed to parse "
2365 << processName << " " << objectPath
2366 << "\n";
2367 asyncResp->res.result(boost::beast::http::status::
2368 internal_server_error);
2369 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002370 }
Ed Tanous7c091622019-05-23 11:42:36 -07002371 asyncResp->res.jsonValue = {
2372 {"status", "ok"},
2373 {"bus_name", processName},
2374 {"interface", interfaceName},
2375 {"object_path", objectPath}};
2376
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002377 nlohmann::json& methodsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002378 asyncResp->res.jsonValue["methods"];
2379 methodsArray = nlohmann::json::array();
2380
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002381 nlohmann::json& signalsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002382 asyncResp->res.jsonValue["signals"];
2383 signalsArray = nlohmann::json::array();
2384
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002385 nlohmann::json& propertiesObj =
Ed Tanous7c091622019-05-23 11:42:36 -07002386 asyncResp->res.jsonValue["properties"];
2387 propertiesObj = nlohmann::json::object();
2388
2389 // if we know we're the only call, build the
2390 // json directly
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002391 tinyxml2::XMLElement* interface =
Ed Tanous7c091622019-05-23 11:42:36 -07002392 pRoot->FirstChildElement("interface");
2393 while (interface != nullptr)
2394 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002395 const char* ifaceName =
Ed Tanous7c091622019-05-23 11:42:36 -07002396 interface->Attribute("name");
2397
2398 if (ifaceName != nullptr &&
2399 ifaceName == interfaceName)
2400 {
2401 break;
2402 }
2403
2404 interface =
2405 interface->NextSiblingElement("interface");
2406 }
2407 if (interface == nullptr)
2408 {
2409 // if we got to the end of the list and
2410 // never found a match, throw 404
2411 asyncResp->res.result(
2412 boost::beast::http::status::not_found);
2413 return;
2414 }
2415
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002416 tinyxml2::XMLElement* methods =
Ed Tanous7c091622019-05-23 11:42:36 -07002417 interface->FirstChildElement("method");
2418 while (methods != nullptr)
2419 {
2420 nlohmann::json argsArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002421 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002422 methods->FirstChildElement("arg");
2423 while (arg != nullptr)
2424 {
2425 nlohmann::json thisArg;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002426 for (const char* fieldName :
2427 std::array<const char*, 3>{
Ed Tanous7c091622019-05-23 11:42:36 -07002428 "name", "direction", "type"})
2429 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002430 const char* fieldValue =
Ed Tanous7c091622019-05-23 11:42:36 -07002431 arg->Attribute(fieldName);
2432 if (fieldValue != nullptr)
2433 {
2434 thisArg[fieldName] = fieldValue;
2435 }
2436 }
2437 argsArray.push_back(std::move(thisArg));
2438 arg = arg->NextSiblingElement("arg");
2439 }
2440
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002441 const char* name = methods->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002442 if (name != nullptr)
2443 {
2444 methodsArray.push_back(
2445 {{"name", name},
2446 {"uri", "/bus/system/" + processName +
2447 objectPath + "/" +
2448 interfaceName + "/" + name},
2449 {"args", argsArray}});
2450 }
2451 methods = methods->NextSiblingElement("method");
2452 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002453 tinyxml2::XMLElement* signals =
Ed Tanous7c091622019-05-23 11:42:36 -07002454 interface->FirstChildElement("signal");
2455 while (signals != nullptr)
2456 {
2457 nlohmann::json argsArray = nlohmann::json::array();
2458
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002459 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002460 signals->FirstChildElement("arg");
2461 while (arg != nullptr)
2462 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002463 const char* name = arg->Attribute("name");
2464 const char* type = arg->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002465 if (name != nullptr && type != nullptr)
2466 {
2467 argsArray.push_back({
2468 {"name", name},
2469 {"type", type},
2470 });
2471 }
2472 arg = arg->NextSiblingElement("arg");
2473 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002474 const char* name = signals->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002475 if (name != nullptr)
2476 {
2477 signalsArray.push_back(
2478 {{"name", name}, {"args", argsArray}});
2479 }
2480
2481 signals = signals->NextSiblingElement("signal");
2482 }
2483
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002484 tinyxml2::XMLElement* property =
Ed Tanous7c091622019-05-23 11:42:36 -07002485 interface->FirstChildElement("property");
2486 while (property != nullptr)
2487 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002488 const char* name = property->Attribute("name");
2489 const char* type = property->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002490 if (type != nullptr && name != nullptr)
2491 {
2492 sdbusplus::message::message m =
2493 crow::connections::systemBus
2494 ->new_method_call(processName.c_str(),
2495 objectPath.c_str(),
2496 "org.freedesktop."
2497 "DBus."
2498 "Properties",
2499 "Get");
2500 m.append(interfaceName, name);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002501 nlohmann::json& propertyItem =
Ed Tanous7c091622019-05-23 11:42:36 -07002502 propertiesObj[name];
2503 crow::connections::systemBus->async_send(
2504 m, [&propertyItem, asyncResp](
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002505 boost::system::error_code& e,
2506 sdbusplus::message::message& msg) {
Ed Tanous271584a2019-07-09 16:24:22 -07002507 if (e)
Ed Tanous7c091622019-05-23 11:42:36 -07002508 {
2509 return;
2510 }
2511
Ed Tanous271584a2019-07-09 16:24:22 -07002512 convertDBusToJSON("v", msg,
2513 propertyItem);
Ed Tanous7c091622019-05-23 11:42:36 -07002514 });
2515 }
2516 property = property->NextSiblingElement("property");
2517 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002518 },
2519 processName, objectPath,
2520 "org.freedesktop.DBus.Introspectable", "Introspect");
2521 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002522 else
2523 {
Ed Tanousb41187f2019-10-24 16:30:02 -07002524 if (req.method() != boost::beast::http::verb::post)
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002525 {
2526 res.result(boost::beast::http::status::not_found);
2527 res.end();
2528 return;
2529 }
2530
2531 nlohmann::json requestDbusData =
2532 nlohmann::json::parse(req.body, nullptr, false);
2533
2534 if (requestDbusData.is_discarded())
2535 {
2536 res.result(boost::beast::http::status::bad_request);
2537 res.end();
2538 return;
2539 }
2540 if (!requestDbusData.is_array())
2541 {
2542 res.result(boost::beast::http::status::bad_request);
2543 res.end();
2544 return;
2545 }
2546 auto transaction = std::make_shared<InProgressActionData>(res);
2547
2548 transaction->path = objectPath;
2549 transaction->methodName = methodName;
2550 transaction->arguments = std::move(requestDbusData);
2551
2552 findActionOnInterface(transaction, processName);
2553 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002554 });
2555}
2556} // namespace openbmc_mapper
2557} // namespace crow