blob: f8d5d4a125137ab5505827f5d4c277003e855aec [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 Tanous1abe55e2018-09-05 08:30:59 -0700826 for (auto it : j->items())
827 {
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
1115 if (typeCode == "s")
1116 {
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 == "g")
1124 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001125 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001126 if (r < 0)
1127 {
1128 return r;
1129 }
1130 }
1131 else if (typeCode == "o")
1132 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001133 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001134 if (r < 0)
1135 {
1136 return r;
1137 }
1138 }
1139 else if (typeCode == "b")
1140 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001141 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001142 if (r < 0)
1143 {
1144 return r;
1145 }
1146
Matt Spinlerf39420c2019-01-30 12:57:18 -06001147 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001148 }
1149 else if (typeCode == "u")
1150 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001151 r = readMessageItem<uint32_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 == "i")
1158 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001159 r = readMessageItem<int32_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 == "x")
1166 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001167 r = readMessageItem<int64_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 == "t")
1174 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001175 r = readMessageItem<uint64_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 == "n")
1182 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001183 r = readMessageItem<int16_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 == "q")
1190 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001191 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001192 if (r < 0)
1193 {
1194 return r;
1195 }
1196 }
1197 else if (typeCode == "y")
1198 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001199 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001200 if (r < 0)
1201 {
1202 return r;
1203 }
1204 }
1205 else if (typeCode == "d")
1206 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001207 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001208 if (r < 0)
1209 {
1210 return r;
1211 }
1212 }
1213 else if (typeCode == "h")
1214 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001215 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001216 if (r < 0)
1217 {
1218 return r;
1219 }
1220 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001221 else if (boost::starts_with(typeCode, "a"))
1222 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001223 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001224 if (r < 0)
1225 {
1226 return r;
1227 }
1228 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001229 else if (boost::starts_with(typeCode, "(") &&
1230 boost::ends_with(typeCode, ")"))
1231 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001232 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001233 if (r < 0)
1234 {
1235 return r;
1236 }
1237 }
Matt Spinler89c19702019-01-14 13:13:00 -06001238 else if (boost::starts_with(typeCode, "v"))
1239 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001240 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001241 if (r < 0)
1242 {
1243 return r;
1244 }
1245 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001246 else
1247 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001248 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1249 return -2;
1250 }
1251 }
1252
Matt Spinler16caaee2019-01-15 11:40:34 -06001253 return 0;
1254}
1255
Ed Tanous23a21a12020-07-25 04:45:05 +00001256inline void
1257 handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1258 sdbusplus::message::message& m,
1259 const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001260{
Matt Spinler39a4e392019-01-15 11:53:13 -06001261 nlohmann::json data;
1262
1263 int r = convertDBusToJSON(returnType, m, data);
1264 if (r < 0)
1265 {
1266 transaction->outputFailed = true;
1267 return;
1268 }
1269
1270 if (data.is_null())
1271 {
1272 return;
1273 }
1274
1275 if (transaction->methodResponse.is_null())
1276 {
1277 transaction->methodResponse = std::move(data);
1278 return;
1279 }
1280
1281 // If they're both dictionaries or arrays, merge into one.
1282 // Otherwise, make the results an array with every result
1283 // an entry. Could also just fail in that case, but it
1284 // seems better to get the data back somehow.
1285
1286 if (transaction->methodResponse.is_object() && data.is_object())
1287 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001288 for (const auto& obj : data.items())
Matt Spinler39a4e392019-01-15 11:53:13 -06001289 {
1290 // Note: Will overwrite the data for a duplicate key
1291 transaction->methodResponse.emplace(obj.key(),
1292 std::move(obj.value()));
1293 }
1294 return;
1295 }
1296
1297 if (transaction->methodResponse.is_array() && data.is_array())
1298 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001299 for (auto& obj : data)
Matt Spinler39a4e392019-01-15 11:53:13 -06001300 {
1301 transaction->methodResponse.push_back(std::move(obj));
1302 }
1303 return;
1304 }
1305
1306 if (!transaction->convertedToArray)
1307 {
1308 // They are different types. May as well turn them into an array
1309 nlohmann::json j = std::move(transaction->methodResponse);
1310 transaction->methodResponse = nlohmann::json::array();
1311 transaction->methodResponse.push_back(std::move(j));
1312 transaction->methodResponse.push_back(std::move(data));
1313 transaction->convertedToArray = true;
1314 }
1315 else
1316 {
1317 transaction->methodResponse.push_back(std::move(data));
1318 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001319}
1320
Ed Tanous23a21a12020-07-25 04:45:05 +00001321inline void
1322 findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
1323 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001324{
1325 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1326 << connectionName;
1327 crow::connections::systemBus->async_method_call(
1328 [transaction, connectionName{std::string(connectionName)}](
1329 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001330 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001331 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1332 if (ec)
1333 {
1334 BMCWEB_LOG_ERROR
1335 << "Introspect call failed with error: " << ec.message()
1336 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001337 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001338 }
Matt Spinler318bd892019-01-15 09:59:20 -06001339 tinyxml2::XMLDocument doc;
1340
1341 doc.Parse(introspect_xml.data(), introspect_xml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001342 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Matt Spinler318bd892019-01-15 09:59:20 -06001343 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001344 {
Matt Spinler318bd892019-01-15 09:59:20 -06001345 BMCWEB_LOG_ERROR << "XML document failed to parse "
1346 << connectionName << "\n";
1347 return;
1348 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001349 tinyxml2::XMLElement* interfaceNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001350 pRoot->FirstChildElement("interface");
1351 while (interfaceNode != nullptr)
1352 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001353 const char* thisInterfaceName =
Matt Spinler318bd892019-01-15 09:59:20 -06001354 interfaceNode->Attribute("name");
1355 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001356 {
Matt Spinler318bd892019-01-15 09:59:20 -06001357 if (!transaction->interfaceName.empty() &&
1358 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001359 {
Matt Spinler318bd892019-01-15 09:59:20 -06001360 interfaceNode =
1361 interfaceNode->NextSiblingElement("interface");
1362 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001363 }
Matt Spinler318bd892019-01-15 09:59:20 -06001364
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001365 tinyxml2::XMLElement* methodNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001366 interfaceNode->FirstChildElement("method");
1367 while (methodNode != nullptr)
1368 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001369 const char* thisMethodName =
Matt Spinler318bd892019-01-15 09:59:20 -06001370 methodNode->Attribute("name");
1371 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1372 if (thisMethodName != nullptr &&
1373 thisMethodName == transaction->methodName)
1374 {
1375 BMCWEB_LOG_DEBUG
1376 << "Found method named " << thisMethodName
1377 << " on interface " << thisInterfaceName;
1378 sdbusplus::message::message m =
1379 crow::connections::systemBus->new_method_call(
1380 connectionName.c_str(),
1381 transaction->path.c_str(),
1382 thisInterfaceName,
1383 transaction->methodName.c_str());
1384
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001385 tinyxml2::XMLElement* argumentNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001386 methodNode->FirstChildElement("arg");
1387
Matt Spinler16caaee2019-01-15 11:40:34 -06001388 std::string returnType;
1389
1390 // Find the output type
1391 while (argumentNode != nullptr)
1392 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001393 const char* argDirection =
Matt Spinler16caaee2019-01-15 11:40:34 -06001394 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001395 const char* argType =
Matt Spinler16caaee2019-01-15 11:40:34 -06001396 argumentNode->Attribute("type");
1397 if (argDirection != nullptr &&
1398 argType != nullptr &&
1399 std::string(argDirection) == "out")
1400 {
1401 returnType = argType;
1402 break;
1403 }
1404 argumentNode =
1405 argumentNode->NextSiblingElement("arg");
1406 }
1407
Matt Spinler318bd892019-01-15 09:59:20 -06001408 nlohmann::json::const_iterator argIt =
1409 transaction->arguments.begin();
1410
Matt Spinler16caaee2019-01-15 11:40:34 -06001411 argumentNode = methodNode->FirstChildElement("arg");
1412
Matt Spinler318bd892019-01-15 09:59:20 -06001413 while (argumentNode != nullptr)
1414 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001415 const char* argDirection =
Matt Spinler318bd892019-01-15 09:59:20 -06001416 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001417 const char* argType =
Matt Spinler318bd892019-01-15 09:59:20 -06001418 argumentNode->Attribute("type");
1419 if (argDirection != nullptr &&
1420 argType != nullptr &&
1421 std::string(argDirection) == "in")
1422 {
1423 if (argIt == transaction->arguments.end())
1424 {
1425 transaction->setErrorStatus(
1426 "Invalid method args");
1427 return;
1428 }
1429 if (convertJsonToDbus(m.get(),
1430 std::string(argType),
1431 *argIt) < 0)
1432 {
1433 transaction->setErrorStatus(
1434 "Invalid method arg type");
1435 return;
1436 }
1437
1438 argIt++;
1439 }
1440 argumentNode =
1441 argumentNode->NextSiblingElement("arg");
1442 }
1443
1444 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001445 m, [transaction, returnType](
Ed Tanous23a21a12020-07-25 04:45:05 +00001446 boost::system::error_code ec2,
1447 sdbusplus::message::message& m2) {
1448 if (ec2)
Matt Spinler318bd892019-01-15 09:59:20 -06001449 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001450 transaction->methodFailed = true;
Ed Tanous23a21a12020-07-25 04:45:05 +00001451 const sd_bus_error* e = m2.get_error();
Matt Spinler06b1b632019-06-18 16:09:25 -05001452
1453 if (e)
1454 {
1455 setErrorResponse(
1456 transaction->res,
1457 boost::beast::http::status::
1458 bad_request,
1459 e->name, e->message);
1460 }
1461 else
1462 {
1463 setErrorResponse(
1464 transaction->res,
1465 boost::beast::http::status::
1466 bad_request,
1467 "Method call failed",
1468 methodFailedMsg);
1469 }
Matt Spinler318bd892019-01-15 09:59:20 -06001470 return;
1471 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001472 else
1473 {
1474 transaction->methodPassed = true;
1475 }
1476
Ed Tanous23a21a12020-07-25 04:45:05 +00001477 handleMethodResponse(transaction, m2,
Matt Spinler16caaee2019-01-15 11:40:34 -06001478 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001479 });
1480 break;
1481 }
1482 methodNode = methodNode->NextSiblingElement("method");
1483 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001484 }
Matt Spinler318bd892019-01-15 09:59:20 -06001485 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001486 }
1487 },
1488 connectionName, transaction->path,
1489 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001490}
1491
Ed Tanous23a21a12020-07-25 04:45:05 +00001492inline void handleAction(const crow::Request& req, crow::Response& res,
1493 const std::string& objectPath,
1494 const std::string& methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001495{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001496 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1497 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001498 nlohmann::json requestDbusData =
1499 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001500
Ed Tanous1abe55e2018-09-05 08:30:59 -07001501 if (requestDbusData.is_discarded())
1502 {
Matt Spinler6db06242018-12-11 11:21:22 -06001503 setErrorResponse(res, boost::beast::http::status::bad_request,
1504 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001505 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001506 return;
1507 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001508 nlohmann::json::iterator data = requestDbusData.find("data");
1509 if (data == requestDbusData.end())
1510 {
Matt Spinler6db06242018-12-11 11:21:22 -06001511 setErrorResponse(res, boost::beast::http::status::bad_request,
1512 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001513 res.end();
1514 return;
1515 }
1516
1517 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001518 {
Matt Spinler6db06242018-12-11 11:21:22 -06001519 setErrorResponse(res, boost::beast::http::status::bad_request,
1520 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 res.end();
1522 return;
1523 }
1524 auto transaction = std::make_shared<InProgressActionData>(res);
1525
1526 transaction->path = objectPath;
1527 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001528 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 crow::connections::systemBus->async_method_call(
1530 [transaction](
1531 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001532 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1533 interfaceNames) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001534 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001535 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001536 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001537 setErrorResponse(transaction->res,
1538 boost::beast::http::status::not_found,
1539 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001540 return;
1541 }
1542
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001543 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1544 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001545
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001546 for (const std::pair<std::string, std::vector<std::string>>&
1547 object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001548 {
1549 findActionOnInterface(transaction, object.first);
1550 }
1551 },
1552 "xyz.openbmc_project.ObjectMapper",
1553 "/xyz/openbmc_project/object_mapper",
1554 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1555 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001556}
1557
Ed Tanouscb13a392020-07-25 19:02:03 +00001558inline void handleDelete(crow::Response& res, const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001559{
1560 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1561
1562 crow::connections::systemBus->async_method_call(
1563 [&res, objectPath](
1564 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001565 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1566 interfaceNames) {
Matt Spinlerde818812018-12-11 16:39:20 -06001567 if (ec || interfaceNames.size() <= 0)
1568 {
1569 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001570 setErrorResponse(res,
1571 boost::beast::http::status::method_not_allowed,
1572 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001573 res.end();
1574 return;
1575 }
1576
1577 auto transaction = std::make_shared<InProgressActionData>(res);
1578 transaction->path = objectPath;
1579 transaction->methodName = "Delete";
1580 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1581
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001582 for (const std::pair<std::string, std::vector<std::string>>&
1583 object : interfaceNames)
Matt Spinlerde818812018-12-11 16:39:20 -06001584 {
1585 findActionOnInterface(transaction, object.first);
1586 }
1587 },
1588 "xyz.openbmc_project.ObjectMapper",
1589 "/xyz/openbmc_project/object_mapper",
1590 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001591 std::array<const char*, 0>());
Matt Spinlerde818812018-12-11 16:39:20 -06001592}
1593
Ed Tanous23a21a12020-07-25 04:45:05 +00001594inline void handleList(crow::Response& res, const std::string& objectPath,
1595 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001596{
1597 crow::connections::systemBus->async_method_call(
1598 [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001599 std::vector<std::string>& objectPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600 if (ec)
1601 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001602 setErrorResponse(res, boost::beast::http::status::not_found,
1603 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001604 }
1605 else
1606 {
1607 res.jsonValue = {{"status", "ok"},
1608 {"message", "200 OK"},
1609 {"data", std::move(objectPaths)}};
1610 }
1611 res.end();
1612 },
1613 "xyz.openbmc_project.ObjectMapper",
1614 "/xyz/openbmc_project/object_mapper",
1615 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001616 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001617}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001618
Ed Tanous23a21a12020-07-25 04:45:05 +00001619inline void handleEnumerate(crow::Response& res, const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001620{
Ed Tanous049a0512018-11-01 13:58:42 -07001621 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1622 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1623
1624 asyncResp->res.jsonValue = {{"message", "200 OK"},
1625 {"status", "ok"},
1626 {"data", nlohmann::json::object()}};
1627
Ed Tanous1abe55e2018-09-05 08:30:59 -07001628 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001629 [objectPath, asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001630 GetSubTreeType& object_names) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001631 auto transaction = std::make_shared<InProgressEnumerateData>(
1632 objectPath, asyncResp);
1633
1634 transaction->subtree =
1635 std::make_shared<GetSubTreeType>(std::move(object_names));
1636
Ed Tanous1abe55e2018-09-05 08:30:59 -07001637 if (ec)
1638 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001639 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1640 << transaction->objectPath;
1641 setErrorResponse(transaction->asyncResp->res,
1642 boost::beast::http::status::not_found,
1643 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001644 return;
1645 }
Ed Tanous64530012018-02-06 17:08:16 -08001646
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001647 // Add the data for the path passed in to the results
1648 // as if GetSubTree returned it, and continue on enumerating
1649 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001650 },
1651 "xyz.openbmc_project.ObjectMapper",
1652 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -07001653 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001654 std::array<const char*, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001655}
Ed Tanous911ac312017-08-15 09:37:42 -07001656
Ed Tanous23a21a12020-07-25 04:45:05 +00001657inline void handleGet(crow::Response& res, std::string& objectPath,
1658 std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001659{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001660 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1661 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001662 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001663
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664 std::shared_ptr<std::string> path =
1665 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001666
Ed Tanous1abe55e2018-09-05 08:30:59 -07001667 using GetObjectType =
1668 std::vector<std::pair<std::string, std::vector<std::string>>>;
1669 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001670 [&res, path, propertyName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001671 const GetObjectType& object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001672 if (ec || object_names.size() <= 0)
1673 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001674 setErrorResponse(res, boost::beast::http::status::not_found,
1675 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001676 res.end();
1677 return;
1678 }
1679 std::shared_ptr<nlohmann::json> response =
1680 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001681 // The mapper should never give us an empty interface names
1682 // list, but check anyway
Ed Tanous23a21a12020-07-25 04:45:05 +00001683 for (const std::pair<std::string, std::vector<std::string>>&
Ed Tanous1abe55e2018-09-05 08:30:59 -07001684 connection : object_names)
1685 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001686 const std::vector<std::string>& interfaceNames =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001688
Ed Tanous1abe55e2018-09-05 08:30:59 -07001689 if (interfaceNames.size() <= 0)
1690 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001691 setErrorResponse(res, boost::beast::http::status::not_found,
1692 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001693 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001694 return;
1695 }
1696
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001697 for (const std::string& interface : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001699 sdbusplus::message::message m =
1700 crow::connections::systemBus->new_method_call(
1701 connection.first.c_str(), path->c_str(),
1702 "org.freedesktop.DBus.Properties", "GetAll");
1703 m.append(interface);
1704 crow::connections::systemBus->async_send(
1705 m, [&res, response,
Ed Tanous23a21a12020-07-25 04:45:05 +00001706 propertyName](const boost::system::error_code ec2,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001707 sdbusplus::message::message& msg) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001708 if (ec2)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709 {
1710 BMCWEB_LOG_ERROR << "Bad dbus request error: "
Ed Tanous23a21a12020-07-25 04:45:05 +00001711 << ec2;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 }
1713 else
1714 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001715 nlohmann::json properties;
1716 int r =
1717 convertDBusToJSON("a{sv}", msg, properties);
1718 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001719 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001720 BMCWEB_LOG_ERROR
1721 << "convertDBusToJSON failed";
1722 }
1723 else
1724 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001725 for (auto& prop : properties.items())
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001726 {
Ed Tanous7c091622019-05-23 11:42:36 -07001727 // if property name is empty, or
1728 // matches our search query, add it
1729 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001730
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001731 if (propertyName->empty())
1732 {
1733 (*response)[prop.key()] =
1734 std::move(prop.value());
1735 }
1736 else if (prop.key() == *propertyName)
1737 {
1738 *response = std::move(prop.value());
1739 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 }
1741 }
1742 }
1743 if (response.use_count() == 1)
1744 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001745 if (!propertyName->empty() && response->empty())
1746 {
1747 setErrorResponse(
1748 res,
1749 boost::beast::http::status::not_found,
1750 propNotFoundDesc, notFoundMsg);
1751 }
1752 else
1753 {
1754 res.jsonValue = {{"status", "ok"},
1755 {"message", "200 OK"},
1756 {"data", *response}};
1757 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001758 res.end();
1759 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001760 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001761 }
1762 }
1763 },
1764 "xyz.openbmc_project.ObjectMapper",
1765 "/xyz/openbmc_project/object_mapper",
1766 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1767 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001768}
1769
Ed Tanous1abe55e2018-09-05 08:30:59 -07001770struct AsyncPutRequest
1771{
Ed Tanous23a21a12020-07-25 04:45:05 +00001772 AsyncPutRequest(crow::Response& resIn) : res(resIn)
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001773 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001774 ~AsyncPutRequest()
1775 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001776 if (res.jsonValue.empty())
1777 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001778 setErrorResponse(res, boost::beast::http::status::forbidden,
1779 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001780 }
1781
1782 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001783 }
1784
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001785 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001786 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001787 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1788 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001789 }
1790
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001791 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001792 std::string objectPath;
1793 std::string propertyName;
1794 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001795};
1796
Ed Tanous23a21a12020-07-25 04:45:05 +00001797inline void handlePut(const crow::Request& req, crow::Response& res,
1798 const std::string& objectPath,
1799 const std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001800{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001801 if (destProperty.empty())
1802 {
1803 setErrorResponse(res, boost::beast::http::status::forbidden,
1804 forbiddenResDesc, forbiddenMsg);
1805 res.end();
1806 return;
1807 }
1808
Ed Tanous1abe55e2018-09-05 08:30:59 -07001809 nlohmann::json requestDbusData =
1810 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001811
Ed Tanous1abe55e2018-09-05 08:30:59 -07001812 if (requestDbusData.is_discarded())
1813 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001814 setErrorResponse(res, boost::beast::http::status::bad_request,
1815 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001816 res.end();
1817 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001818 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001819
Ed Tanous1abe55e2018-09-05 08:30:59 -07001820 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1821 if (propertyIt == requestDbusData.end())
1822 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001823 setErrorResponse(res, boost::beast::http::status::bad_request,
1824 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001825 res.end();
1826 return;
1827 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001828 const nlohmann::json& propertySetValue = *propertyIt;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001829 auto transaction = std::make_shared<AsyncPutRequest>(res);
1830 transaction->objectPath = objectPath;
1831 transaction->propertyName = destProperty;
1832 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001833
Ed Tanous1abe55e2018-09-05 08:30:59 -07001834 using GetObjectType =
1835 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001836
Ed Tanous1abe55e2018-09-05 08:30:59 -07001837 crow::connections::systemBus->async_method_call(
Ed Tanous23a21a12020-07-25 04:45:05 +00001838 [transaction](const boost::system::error_code ec2,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001839 const GetObjectType& object_names) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001840 if (!ec2 && object_names.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001841 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001842 setErrorResponse(transaction->res,
1843 boost::beast::http::status::not_found,
1844 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001845 return;
1846 }
Ed Tanous911ac312017-08-15 09:37:42 -07001847
Ed Tanous23a21a12020-07-25 04:45:05 +00001848 for (const std::pair<std::string, std::vector<std::string>>&
Ed Tanous1abe55e2018-09-05 08:30:59 -07001849 connection : object_names)
1850 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001851 const std::string& connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001852
Ed Tanous1abe55e2018-09-05 08:30:59 -07001853 crow::connections::systemBus->async_method_call(
1854 [connectionName{std::string(connectionName)},
Ed Tanous23a21a12020-07-25 04:45:05 +00001855 transaction](const boost::system::error_code ec3,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001856 const std::string& introspectXml) {
Ed Tanous23a21a12020-07-25 04:45:05 +00001857 if (ec3)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001858 {
1859 BMCWEB_LOG_ERROR
1860 << "Introspect call failed with error: "
Ed Tanous23a21a12020-07-25 04:45:05 +00001861 << ec3.message()
Ed Tanous1abe55e2018-09-05 08:30:59 -07001862 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001863 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001864 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001865 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001866 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001867
Ed Tanous1abe55e2018-09-05 08:30:59 -07001868 doc.Parse(introspectXml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001869 tinyxml2::XMLNode* pRoot =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001870 doc.FirstChildElement("node");
1871 if (pRoot == nullptr)
1872 {
1873 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1874 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001875 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001876 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001877 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001878 tinyxml2::XMLElement* ifaceNode =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001879 pRoot->FirstChildElement("interface");
1880 while (ifaceNode != nullptr)
1881 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001882 const char* interfaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001883 ifaceNode->Attribute("name");
1884 BMCWEB_LOG_DEBUG << "found interface "
1885 << interfaceName;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001886 tinyxml2::XMLElement* propNode =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001887 ifaceNode->FirstChildElement("property");
1888 while (propNode != nullptr)
1889 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001890 const char* propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001891 propNode->Attribute("name");
1892 BMCWEB_LOG_DEBUG << "Found property "
1893 << propertyName;
1894 if (propertyName == transaction->propertyName)
1895 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001896 const char* argType =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001897 propNode->Attribute("type");
1898 if (argType != nullptr)
1899 {
1900 sdbusplus::message::message m =
1901 crow::connections::systemBus
1902 ->new_method_call(
1903 connectionName.c_str(),
1904 transaction->objectPath
1905 .c_str(),
1906 "org.freedesktop.DBus."
1907 "Properties",
1908 "Set");
1909 m.append(interfaceName,
1910 transaction->propertyName);
1911 int r = sd_bus_message_open_container(
1912 m.get(), SD_BUS_TYPE_VARIANT,
1913 argType);
1914 if (r < 0)
1915 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001916 transaction->setErrorStatus(
1917 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001918 return;
1919 }
1920 r = convertJsonToDbus(
1921 m.get(), argType,
1922 transaction->propertyValue);
1923 if (r < 0)
1924 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -05001925 if (r == -ERANGE)
1926 {
1927 transaction->setErrorStatus(
1928 "Provided property value "
1929 "is out of range for the "
1930 "property type");
1931 }
1932 else
1933 {
1934 transaction->setErrorStatus(
1935 "Invalid arg type");
1936 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001937 return;
1938 }
1939 r = sd_bus_message_close_container(
1940 m.get());
1941 if (r < 0)
1942 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001943 transaction->setErrorStatus(
1944 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001945 return;
1946 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001947 crow::connections::systemBus
1948 ->async_send(
1949 m,
1950 [transaction](
1951 boost::system::error_code
1952 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001953 sdbusplus::message::message&
Ed Tanous23a21a12020-07-25 04:45:05 +00001954 m2) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001955 BMCWEB_LOG_DEBUG << "sent";
1956 if (ec)
1957 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001958 const sd_bus_error* e =
Ed Tanous23a21a12020-07-25 04:45:05 +00001959 m2.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001960 setErrorResponse(
1961 transaction->res,
1962 boost::beast::http::
1963 status::
1964 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001965 (e) ? e->name
1966 : ec.category()
1967 .name(),
1968 (e) ? e->message
1969 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001970 }
1971 else
1972 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001973 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001974 .jsonValue = {
1975 {"status", "ok"},
1976 {"message",
1977 "200 OK"},
1978 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001979 }
1980 });
1981 }
1982 }
1983 propNode =
1984 propNode->NextSiblingElement("property");
1985 }
1986 ifaceNode =
1987 ifaceNode->NextSiblingElement("interface");
1988 }
1989 },
1990 connectionName, transaction->objectPath,
1991 "org.freedesktop.DBus.Introspectable", "Introspect");
1992 }
1993 },
1994 "xyz.openbmc_project.ObjectMapper",
1995 "/xyz/openbmc_project/object_mapper",
1996 "xyz.openbmc_project.ObjectMapper", "GetObject",
1997 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001998}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001999
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002000inline void handleDBusUrl(const crow::Request& req, crow::Response& res,
2001 std::string& objectPath)
Ed Tanous049a0512018-11-01 13:58:42 -07002002{
Ed Tanous049a0512018-11-01 13:58:42 -07002003
2004 // If accessing a single attribute, fill in and update objectPath,
2005 // otherwise leave destProperty blank
2006 std::string destProperty = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002007 const char* attrSeperator = "/attr/";
Ed Tanous049a0512018-11-01 13:58:42 -07002008 size_t attrPosition = objectPath.find(attrSeperator);
2009 if (attrPosition != objectPath.npos)
2010 {
2011 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
2012 objectPath.length());
2013 objectPath = objectPath.substr(0, attrPosition);
2014 }
2015
Ed Tanousb41187f2019-10-24 16:30:02 -07002016 if (req.method() == boost::beast::http::verb::post)
Ed Tanous049a0512018-11-01 13:58:42 -07002017 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002018 constexpr const char* actionSeperator = "/action/";
Ed Tanous049a0512018-11-01 13:58:42 -07002019 size_t actionPosition = objectPath.find(actionSeperator);
2020 if (actionPosition != objectPath.npos)
2021 {
2022 std::string postProperty =
2023 objectPath.substr((actionPosition + strlen(actionSeperator)),
2024 objectPath.length());
2025 objectPath = objectPath.substr(0, actionPosition);
2026 handleAction(req, res, objectPath, postProperty);
2027 return;
2028 }
2029 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002030 else if (req.method() == boost::beast::http::verb::get)
Ed Tanous049a0512018-11-01 13:58:42 -07002031 {
2032 if (boost::ends_with(objectPath, "/enumerate"))
2033 {
2034 objectPath.erase(objectPath.end() - sizeof("enumerate"),
2035 objectPath.end());
2036 handleEnumerate(res, objectPath);
2037 }
2038 else if (boost::ends_with(objectPath, "/list"))
2039 {
2040 objectPath.erase(objectPath.end() - sizeof("list"),
2041 objectPath.end());
2042 handleList(res, objectPath);
2043 }
2044 else
2045 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08002046 // Trim any trailing "/" at the end
2047 if (boost::ends_with(objectPath, "/"))
2048 {
2049 objectPath.pop_back();
2050 handleList(res, objectPath, 1);
2051 }
2052 else
2053 {
2054 handleGet(res, objectPath, destProperty);
2055 }
Ed Tanous049a0512018-11-01 13:58:42 -07002056 }
2057 return;
2058 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002059 else if (req.method() == boost::beast::http::verb::put)
Ed Tanous049a0512018-11-01 13:58:42 -07002060 {
2061 handlePut(req, res, objectPath, destProperty);
2062 return;
2063 }
Ed Tanousb41187f2019-10-24 16:30:02 -07002064 else if (req.method() == boost::beast::http::verb::delete_)
Matt Spinlerde818812018-12-11 16:39:20 -06002065 {
Ed Tanouscb13a392020-07-25 19:02:03 +00002066 handleDelete(res, objectPath);
Matt Spinlerde818812018-12-11 16:39:20 -06002067 return;
2068 }
Ed Tanous049a0512018-11-01 13:58:42 -07002069
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06002070 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
2071 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07002072 res.end();
2073}
2074
Ed Tanous23a21a12020-07-25 04:45:05 +00002075inline void requestRoutes(App& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002076{
2077 BMCWEB_ROUTE(app, "/bus/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002078 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002079 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002080 [](const crow::Request&, crow::Response& res) {
Gunnar Millscaa3ce32020-07-08 14:46:53 -05002081 res.jsonValue = {{"buses", {{{"name", "system"}}}},
Ed Tanous1abe55e2018-09-05 08:30:59 -07002082 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002083 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002084 });
2085
2086 BMCWEB_ROUTE(app, "/bus/system/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002087 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002088 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002089 [](const crow::Request&, crow::Response& res) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002090 auto myCallback = [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002091 std::vector<std::string>& names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002092 if (ec)
2093 {
2094 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2095 res.result(
2096 boost::beast::http::status::internal_server_error);
2097 }
2098 else
2099 {
2100 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002101 res.jsonValue = {{"status", "ok"}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002102 auto& objectsSub = res.jsonValue["objects"];
2103 for (auto& name : names)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002104 {
2105 objectsSub.push_back({{"name", name}});
2106 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002107 }
2108 res.end();
2109 };
2110 crow::connections::systemBus->async_method_call(
2111 std::move(myCallback), "org.freedesktop.DBus", "/",
2112 "org.freedesktop.DBus", "ListNames");
2113 });
2114
2115 BMCWEB_ROUTE(app, "/list/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002116 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002117 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002118 [](const crow::Request&, crow::Response& res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002119 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002120 });
2121
2122 BMCWEB_ROUTE(app, "/xyz/<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) {
Tanousf00032d2018-11-05 01:18:10 -03002127 std::string objectPath = "/xyz/" + path;
2128 handleDBusUrl(req, res, objectPath);
2129 });
2130
2131 BMCWEB_ROUTE(app, "/xyz/<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 Tanous049a0512018-11-01 13:58:42 -07002137 std::string objectPath = "/xyz/" + path;
2138 handleDBusUrl(req, res, objectPath);
2139 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002140
Ed Tanous049a0512018-11-01 13:58:42 -07002141 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002142 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002143 .methods(boost::beast::http::verb::get)([](const crow::Request& req,
2144 crow::Response& res,
2145 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002146 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002147 handleDBusUrl(req, res, objectPath);
2148 });
2149
2150 BMCWEB_ROUTE(app, "/org/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002151 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002152 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post,
2153 boost::beast::http::verb::delete_)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002154 [](const crow::Request& req, crow::Response& res,
2155 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002156 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002157 handleDBusUrl(req, res, objectPath);
2158 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002159
Ed Tanous1abe55e2018-09-05 08:30:59 -07002160 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002161 .privileges({"ConfigureManager"})
Ed Tanouscb13a392020-07-25 19:02:03 +00002162 .methods(boost::beast::http::verb::get)([](const crow::Request&,
Ed Tanousb41187f2019-10-24 16:30:02 -07002163 crow::Response& res,
2164 const std::string& dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002165 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002166 if (!std::regex_match(dumpId, validFilename))
2167 {
Ed Tanousad18f072018-11-14 14:07:48 -08002168 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002169 res.end();
2170 return;
2171 }
James Feistf6150402019-01-08 10:36:20 -08002172 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002173 "/var/lib/phosphor-debug-collector/dumps");
2174
Ed Tanousad18f072018-11-14 14:07:48 -08002175 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002176
James Feistf6150402019-01-08 10:36:20 -08002177 if (!std::filesystem::exists(loc) ||
2178 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002179 {
Ed Tanousad18f072018-11-14 14:07:48 -08002180 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002181 res.result(boost::beast::http::status::not_found);
2182 res.end();
2183 return;
2184 }
James Feistf6150402019-01-08 10:36:20 -08002185 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002186
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002187 for (auto& file : files)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002188 {
2189 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002190 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002191 {
2192 continue;
2193 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002194
Ed Tanous1abe55e2018-09-05 08:30:59 -07002195 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002196
2197 // Assuming only one dump file will be present in the dump id
2198 // directory
2199 std::string dumpFileName = file.path().filename().string();
2200
2201 // Filename should be in alphanumeric, dot and underscore
2202 // Its based on phosphor-debug-collector application dumpfile
2203 // format
2204 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2205 if (!std::regex_match(dumpFileName, dumpFileRegex))
2206 {
2207 BMCWEB_LOG_ERROR << "Invalid dump filename "
2208 << dumpFileName;
2209 res.result(boost::beast::http::status::not_found);
2210 res.end();
2211 return;
2212 }
2213 std::string contentDispositionParam =
2214 "attachment; filename=\"" + dumpFileName + "\"";
2215
2216 res.addHeader("Content-Disposition", contentDispositionParam);
2217
Ed Tanous1abe55e2018-09-05 08:30:59 -07002218 res.body() = {std::istreambuf_iterator<char>(readFile),
2219 std::istreambuf_iterator<char>()};
2220 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002221 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002222 }
2223 res.result(boost::beast::http::status::not_found);
2224 res.end();
2225 return;
2226 });
2227
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002228 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous23a21a12020-07-25 04:45:05 +00002229 .privileges({"Login"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002230
2231 .methods(boost::beast::http::verb::get)(
Ed Tanouscb13a392020-07-25 19:02:03 +00002232 [](const crow::Request&, crow::Response& res,
Ed Tanousb41187f2019-10-24 16:30:02 -07002233 const std::string& Connection) {
2234 introspectObjects(Connection, "/",
2235 std::make_shared<bmcweb::AsyncResp>(res));
2236 });
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002237
2238 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous23a21a12020-07-25 04:45:05 +00002239 .privileges({"ConfigureComponents", "ConfigureManager"})
Ed Tanousb41187f2019-10-24 16:30:02 -07002240 .methods(
2241 boost::beast::http::verb::get,
2242 boost::beast::http::verb::post)([](const crow::Request& req,
2243 crow::Response& res,
2244 const std::string& processName,
2245 const std::string&
2246 requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002247 std::vector<std::string> strs;
2248 boost::split(strs, requestedPath, boost::is_any_of("/"));
2249 std::string objectPath;
2250 std::string interfaceName;
2251 std::string methodName;
2252 auto it = strs.begin();
2253 if (it == strs.end())
2254 {
2255 objectPath = "/";
2256 }
2257 while (it != strs.end())
2258 {
2259 // Check if segment contains ".". If it does, it must be an
2260 // interface
2261 if (it->find(".") != std::string::npos)
2262 {
2263 break;
Gunnar Millscaa3ce32020-07-08 14:46:53 -05002264 // This check is necessary as the trailing slash gets
Ed Tanous7c091622019-05-23 11:42:36 -07002265 // parsed as part of our <path> specifier above, which
2266 // causes the normal trailing backslash redirector to
2267 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002268 }
2269 else if (!it->empty())
2270 {
2271 objectPath += "/" + *it;
2272 }
2273 it++;
2274 }
2275 if (it != strs.end())
2276 {
2277 interfaceName = *it;
2278 it++;
2279
2280 // after interface, we might have a method name
2281 if (it != strs.end())
2282 {
2283 methodName = *it;
2284 it++;
2285 }
2286 }
2287 if (it != strs.end())
2288 {
Ed Tanous7c091622019-05-23 11:42:36 -07002289 // if there is more levels past the method name, something
2290 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002291 res.result(boost::beast::http::status::not_found);
AppaRao Puli3c27ed32020-03-31 01:21:57 +05302292 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002293 return;
2294 }
2295 if (interfaceName.empty())
2296 {
Ed Tanous7c091622019-05-23 11:42:36 -07002297 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2298 std::make_shared<bmcweb::AsyncResp>(res);
2299
Ed Tanous1abe55e2018-09-05 08:30:59 -07002300 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002301 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002302 objectPath](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002303 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002304 if (ec)
2305 {
2306 BMCWEB_LOG_ERROR
2307 << "Introspect call failed with error: "
2308 << ec.message()
2309 << " on process: " << processName
2310 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002311 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002312 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002313 tinyxml2::XMLDocument doc;
2314
2315 doc.Parse(introspect_xml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002316 tinyxml2::XMLNode* pRoot =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002317 doc.FirstChildElement("node");
2318 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002319 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002320 BMCWEB_LOG_ERROR << "XML document failed to parse "
2321 << processName << " " << objectPath
2322 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002323 asyncResp->res.jsonValue = {
2324 {"status", "XML parse error"}};
2325 asyncResp->res.result(boost::beast::http::status::
2326 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002327 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002328 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002329
2330 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002331 asyncResp->res.jsonValue = {
2332 {"status", "ok"},
2333 {"bus_name", processName},
2334 {"object_path", objectPath}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002335 nlohmann::json& interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002336 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002337 interfacesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002338 tinyxml2::XMLElement* interface =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002339 pRoot->FirstChildElement("interface");
2340
2341 while (interface != nullptr)
2342 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002343 const char* ifaceName =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002344 interface->Attribute("name");
2345 if (ifaceName != nullptr)
2346 {
2347 interfacesArray.push_back(
2348 {{"name", ifaceName}});
2349 }
2350
2351 interface =
2352 interface->NextSiblingElement("interface");
2353 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002354 },
2355 processName, objectPath,
2356 "org.freedesktop.DBus.Introspectable", "Introspect");
2357 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002358 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002359 {
Ed Tanous7c091622019-05-23 11:42:36 -07002360 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2361 std::make_shared<bmcweb::AsyncResp>(res);
2362
Ed Tanous1abe55e2018-09-05 08:30:59 -07002363 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002364 [asyncResp, processName, objectPath,
2365 interfaceName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002366 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002367 if (ec)
2368 {
2369 BMCWEB_LOG_ERROR
2370 << "Introspect call failed with error: "
2371 << ec.message()
2372 << " on process: " << processName
2373 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002374 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002375 }
Ed Tanous7c091622019-05-23 11:42:36 -07002376 tinyxml2::XMLDocument doc;
2377
2378 doc.Parse(introspect_xml.data(), introspect_xml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002379 tinyxml2::XMLNode* pRoot =
Ed Tanous7c091622019-05-23 11:42:36 -07002380 doc.FirstChildElement("node");
2381 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002382 {
Ed Tanous7c091622019-05-23 11:42:36 -07002383 BMCWEB_LOG_ERROR << "XML document failed to parse "
2384 << processName << " " << objectPath
2385 << "\n";
2386 asyncResp->res.result(boost::beast::http::status::
2387 internal_server_error);
2388 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002389 }
Ed Tanous7c091622019-05-23 11:42:36 -07002390 asyncResp->res.jsonValue = {
2391 {"status", "ok"},
2392 {"bus_name", processName},
2393 {"interface", interfaceName},
2394 {"object_path", objectPath}};
2395
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002396 nlohmann::json& methodsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002397 asyncResp->res.jsonValue["methods"];
2398 methodsArray = nlohmann::json::array();
2399
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002400 nlohmann::json& signalsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002401 asyncResp->res.jsonValue["signals"];
2402 signalsArray = nlohmann::json::array();
2403
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002404 nlohmann::json& propertiesObj =
Ed Tanous7c091622019-05-23 11:42:36 -07002405 asyncResp->res.jsonValue["properties"];
2406 propertiesObj = nlohmann::json::object();
2407
2408 // if we know we're the only call, build the
2409 // json directly
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002410 tinyxml2::XMLElement* interface =
Ed Tanous7c091622019-05-23 11:42:36 -07002411 pRoot->FirstChildElement("interface");
2412 while (interface != nullptr)
2413 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002414 const char* ifaceName =
Ed Tanous7c091622019-05-23 11:42:36 -07002415 interface->Attribute("name");
2416
2417 if (ifaceName != nullptr &&
2418 ifaceName == interfaceName)
2419 {
2420 break;
2421 }
2422
2423 interface =
2424 interface->NextSiblingElement("interface");
2425 }
2426 if (interface == nullptr)
2427 {
2428 // if we got to the end of the list and
2429 // never found a match, throw 404
2430 asyncResp->res.result(
2431 boost::beast::http::status::not_found);
2432 return;
2433 }
2434
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002435 tinyxml2::XMLElement* methods =
Ed Tanous7c091622019-05-23 11:42:36 -07002436 interface->FirstChildElement("method");
2437 while (methods != nullptr)
2438 {
2439 nlohmann::json argsArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002440 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002441 methods->FirstChildElement("arg");
2442 while (arg != nullptr)
2443 {
2444 nlohmann::json thisArg;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002445 for (const char* fieldName :
2446 std::array<const char*, 3>{
Ed Tanous7c091622019-05-23 11:42:36 -07002447 "name", "direction", "type"})
2448 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002449 const char* fieldValue =
Ed Tanous7c091622019-05-23 11:42:36 -07002450 arg->Attribute(fieldName);
2451 if (fieldValue != nullptr)
2452 {
2453 thisArg[fieldName] = fieldValue;
2454 }
2455 }
2456 argsArray.push_back(std::move(thisArg));
2457 arg = arg->NextSiblingElement("arg");
2458 }
2459
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002460 const char* name = methods->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002461 if (name != nullptr)
2462 {
2463 methodsArray.push_back(
2464 {{"name", name},
2465 {"uri", "/bus/system/" + processName +
2466 objectPath + "/" +
2467 interfaceName + "/" + name},
2468 {"args", argsArray}});
2469 }
2470 methods = methods->NextSiblingElement("method");
2471 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002472 tinyxml2::XMLElement* signals =
Ed Tanous7c091622019-05-23 11:42:36 -07002473 interface->FirstChildElement("signal");
2474 while (signals != nullptr)
2475 {
2476 nlohmann::json argsArray = nlohmann::json::array();
2477
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002478 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002479 signals->FirstChildElement("arg");
2480 while (arg != nullptr)
2481 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002482 const char* name = arg->Attribute("name");
2483 const char* type = arg->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002484 if (name != nullptr && type != nullptr)
2485 {
2486 argsArray.push_back({
2487 {"name", name},
2488 {"type", type},
2489 });
2490 }
2491 arg = arg->NextSiblingElement("arg");
2492 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002493 const char* name = signals->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002494 if (name != nullptr)
2495 {
2496 signalsArray.push_back(
2497 {{"name", name}, {"args", argsArray}});
2498 }
2499
2500 signals = signals->NextSiblingElement("signal");
2501 }
2502
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002503 tinyxml2::XMLElement* property =
Ed Tanous7c091622019-05-23 11:42:36 -07002504 interface->FirstChildElement("property");
2505 while (property != nullptr)
2506 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002507 const char* name = property->Attribute("name");
2508 const char* type = property->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002509 if (type != nullptr && name != nullptr)
2510 {
2511 sdbusplus::message::message m =
2512 crow::connections::systemBus
2513 ->new_method_call(processName.c_str(),
2514 objectPath.c_str(),
2515 "org.freedesktop."
2516 "DBus."
2517 "Properties",
2518 "Get");
2519 m.append(interfaceName, name);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002520 nlohmann::json& propertyItem =
Ed Tanous7c091622019-05-23 11:42:36 -07002521 propertiesObj[name];
2522 crow::connections::systemBus->async_send(
2523 m, [&propertyItem, asyncResp](
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002524 boost::system::error_code& e,
2525 sdbusplus::message::message& msg) {
Ed Tanous271584a2019-07-09 16:24:22 -07002526 if (e)
Ed Tanous7c091622019-05-23 11:42:36 -07002527 {
2528 return;
2529 }
2530
Ed Tanous271584a2019-07-09 16:24:22 -07002531 convertDBusToJSON("v", msg,
2532 propertyItem);
Ed Tanous7c091622019-05-23 11:42:36 -07002533 });
2534 }
2535 property = property->NextSiblingElement("property");
2536 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002537 },
2538 processName, objectPath,
2539 "org.freedesktop.DBus.Introspectable", "Introspect");
2540 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002541 else
2542 {
Ed Tanousb41187f2019-10-24 16:30:02 -07002543 if (req.method() != boost::beast::http::verb::post)
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002544 {
2545 res.result(boost::beast::http::status::not_found);
2546 res.end();
2547 return;
2548 }
2549
2550 nlohmann::json requestDbusData =
2551 nlohmann::json::parse(req.body, nullptr, false);
2552
2553 if (requestDbusData.is_discarded())
2554 {
2555 res.result(boost::beast::http::status::bad_request);
2556 res.end();
2557 return;
2558 }
2559 if (!requestDbusData.is_array())
2560 {
2561 res.result(boost::beast::http::status::bad_request);
2562 res.end();
2563 return;
2564 }
2565 auto transaction = std::make_shared<InProgressActionData>(res);
2566
2567 transaction->path = objectPath;
2568 transaction->methodName = methodName;
2569 transaction->arguments = std::move(requestDbusData);
2570
2571 findActionOnInterface(transaction, processName);
2572 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002573 });
2574}
2575} // namespace openbmc_mapper
2576} // namespace crow