blob: 456c768ab4810e8148afe298d7a431cc76cc4703 [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
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039const char* notFoundMsg = "404 Not Found";
40const char* badReqMsg = "400 Bad Request";
41const char* methodNotAllowedMsg = "405 Method Not Allowed";
42const char* forbiddenMsg = "403 Forbidden";
43const char* methodFailedMsg = "500 Method Call Failed";
44const char* methodOutputFailedMsg = "500 Method Output Error";
45const char* notFoundDesc =
Matt Spinler2ae60092018-12-06 10:35:36 -060046 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050047const char* propNotFoundDesc = "The specified property cannot be found";
48const char* noJsonDesc = "No JSON object could be decoded";
49const char* methodNotFoundDesc = "The specified method cannot be found";
50const char* methodNotAllowedDesc = "Method not allowed";
51const char* forbiddenPropDesc = "The specified property cannot be created";
52const char* forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060053
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054void setErrorResponse(crow::Response& res, boost::beast::http::status result,
55 const std::string& desc, const std::string_view msg)
Matt Spinler2ae60092018-12-06 10:35:36 -060056{
57 res.result(result);
58 res.jsonValue = {{"data", {{"description", desc}}},
59 {"message", msg},
60 {"status", "error"}};
61}
62
Gunnar Mills1214b7e2020-06-04 10:11:30 -050063void introspectObjects(const std::string& processName,
64 const std::string& objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -070065 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070066{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070067 if (transaction->res.jsonValue.is_null())
68 {
69 transaction->res.jsonValue = {{"status", "ok"},
70 {"bus_name", processName},
71 {"objects", nlohmann::json::array()}};
72 }
73
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070075 [transaction, processName{std::string(processName)},
76 objectPath{std::string(objectPath)}](
77 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -050078 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070079 if (ec)
80 {
81 BMCWEB_LOG_ERROR
82 << "Introspect call failed with error: " << ec.message()
83 << " on process: " << processName << " path: " << objectPath
84 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070085 return;
86 }
87 transaction->res.jsonValue["objects"].push_back(
88 {{"path", objectPath}});
89
90 tinyxml2::XMLDocument doc;
91
92 doc.Parse(introspect_xml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -050093 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Ed Tanouse3cb5a32018-08-08 14:16:49 -070094 if (pRoot == nullptr)
95 {
96 BMCWEB_LOG_ERROR << "XML document failed to parse "
97 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070098 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 else
100 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500101 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700102 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500104 const char* childPath = node->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700105 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 std::string newpath;
108 if (objectPath != "/")
109 {
110 newpath += objectPath;
111 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700112 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700114 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700116
117 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700118 }
119 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700121 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700122 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700123}
Ed Tanous64530012018-02-06 17:08:16 -0800124
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500125void getPropertiesForEnumerate(const std::string& objectPath,
126 const std::string& service,
127 const std::string& interface,
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600128 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
129{
130 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
131 << service << " " << interface;
132
133 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134 [asyncResp, objectPath, service, interface](
135 const boost::system::error_code ec,
136 const std::vector<std::pair<
137 std::string, dbus::utility::DbusVariantType>>& propertiesList) {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600138 if (ec)
139 {
140 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
141 << interface << " service " << service
142 << " failed with code " << ec;
143 return;
144 }
145
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500146 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
147 nlohmann::json& objectJson = dataJson[objectPath];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600148 if (objectJson.is_null())
149 {
150 objectJson = nlohmann::json::object();
151 }
152
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500153 for (const auto& [name, value] : propertiesList)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600154 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500155 nlohmann::json& propertyJson = objectJson[name];
156 std::visit([&propertyJson](auto&& val) { propertyJson = val; },
Ed Tanousabf2add2019-01-22 16:40:12 -0800157 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600158 }
159 },
160 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
161 interface);
162}
163
164// Find any results that weren't picked up by ObjectManagers, to be
165// called after all ObjectManagers are searched for and called.
166void findRemainingObjectsForEnumerate(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500167 const std::string& objectPath, std::shared_ptr<GetSubTreeType> subtree,
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600168 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
169{
170 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500171 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"];
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600172
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500173 for (const auto& [path, interface_map] : *subtree)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600174 {
175 if (path == objectPath)
176 {
177 // An enumerate does not return the target path's properties
178 continue;
179 }
180 if (dataJson.find(path) == dataJson.end())
181 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182 for (const auto& [service, interfaces] : interface_map)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600183 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500184 for (const auto& interface : interfaces)
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600185 {
186 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
187 {
188 getPropertiesForEnumerate(path, service, interface,
189 asyncResp);
190 }
191 }
192 }
193 }
194 }
195}
196
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600197struct InProgressEnumerateData
198{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500199 InProgressEnumerateData(const std::string& objectPath,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600200 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
201 objectPath(objectPath),
202 asyncResp(asyncResp)
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 {}
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600204
205 ~InProgressEnumerateData()
206 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600207 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600208 }
209
210 const std::string objectPath;
211 std::shared_ptr<GetSubTreeType> subtree;
212 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
213};
214
215void getManagedObjectsForEnumerate(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500216 const std::string& object_name, const std::string& object_manager_path,
217 const std::string& connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600218 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219{
Ed Tanous049a0512018-11-01 13:58:42 -0700220 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
221 << " object_manager_path " << object_manager_path
222 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600224 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700225 connection_name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500226 const dbus::utility::ManagedObjectType& objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 if (ec)
228 {
Ed Tanous049a0512018-11-01 13:58:42 -0700229 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600230 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700231 << " failed with code " << ec;
232 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700233 }
Ed Tanous64530012018-02-06 17:08:16 -0800234
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500235 nlohmann::json& dataJson =
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600236 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700237
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500238 for (const auto& objectPath : objects)
Ed Tanous049a0512018-11-01 13:58:42 -0700239 {
240 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700241 {
Ed Tanous049a0512018-11-01 13:58:42 -0700242 BMCWEB_LOG_DEBUG << "Reading object "
243 << objectPath.first.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 nlohmann::json& objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 if (objectJson.is_null())
246 {
247 objectJson = nlohmann::json::object();
248 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500249 for (const auto& interface : objectPath.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500251 for (const auto& property : interface.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500253 nlohmann::json& propertyJson =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800255 std::visit([&propertyJson](
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500256 auto&& val) { propertyJson = val; },
Ed Tanousabf2add2019-01-22 16:40:12 -0800257 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700258 }
259 }
260 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500261 for (const auto& interface : objectPath.second)
Ed Tanous049a0512018-11-01 13:58:42 -0700262 {
263 if (interface.first == "org.freedesktop.DBus.ObjectManager")
264 {
265 getManagedObjectsForEnumerate(
266 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600267 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700268 }
269 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270 }
271 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700272 connection_name, object_manager_path,
273 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
274}
275
276void findObjectManagerPathForEnumerate(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500277 const std::string& object_name, const std::string& connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600278 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700279{
Ed Tanous049a0512018-11-01 13:58:42 -0700280 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
281 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700282 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600283 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284 const boost::system::error_code ec,
285 const boost::container::flat_map<
286 std::string, boost::container::flat_map<
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500287 std::string, std::vector<std::string>>>&
288 objects) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700289 if (ec)
290 {
Ed Tanous049a0512018-11-01 13:58:42 -0700291 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
292 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700293 return;
294 }
295
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500296 for (const auto& pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700297 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500298 for (const auto& connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700299 {
300 if (connectionGroup.first == connection_name)
301 {
302 // Found the object manager path for this resource.
303 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700304 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600305 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700306 return;
307 }
308 }
309 }
310 },
311 "xyz.openbmc_project.ObjectMapper",
312 "/xyz/openbmc_project/object_mapper",
313 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500314 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700315}
Ed Tanous64530012018-02-06 17:08:16 -0800316
Ed Tanous7c091622019-05-23 11:42:36 -0700317// Uses GetObject to add the object info about the target /enumerate path to
318// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600319// target path, and then continues on enumerating the rest of the tree.
320void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
321{
322 using GetObjectType =
323 std::vector<std::pair<std::string, std::vector<std::string>>>;
324
325 crow::connections::systemBus->async_method_call(
326 [transaction](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500327 const GetObjectType& objects) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600328 if (ec)
329 {
330 BMCWEB_LOG_ERROR << "GetObject for path "
331 << transaction->objectPath
332 << " failed with code " << ec;
333 return;
334 }
335
336 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
337 << " has " << objects.size() << " entries";
338 if (!objects.empty())
339 {
340 transaction->subtree->emplace_back(transaction->objectPath,
341 objects);
342 }
343
344 // Map indicating connection name, and the path where the object
345 // manager exists
346 boost::container::flat_map<std::string, std::string> connections;
347
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500348 for (const auto& object : *(transaction->subtree))
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600349 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500350 for (const auto& connection : object.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600351 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500352 std::string& objectManagerPath =
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600353 connections[connection.first];
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500354 for (const auto& interface : connection.second)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600355 {
356 BMCWEB_LOG_DEBUG << connection.first
357 << " has interface " << interface;
358 if (interface == "org.freedesktop.DBus.ObjectManager")
359 {
360 BMCWEB_LOG_DEBUG << "found object manager path "
361 << object.first;
362 objectManagerPath = object.first;
363 }
364 }
365 }
366 }
367 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
368
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500369 for (const auto& connection : connections)
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600370 {
Ed Tanous7c091622019-05-23 11:42:36 -0700371 // If we already know where the object manager is, we don't
372 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600373 // getManagedObjects
374 if (!connection.second.empty())
375 {
376 getManagedObjectsForEnumerate(
377 transaction->objectPath, connection.second,
378 connection.first, transaction);
379 }
380 else
381 {
Ed Tanous7c091622019-05-23 11:42:36 -0700382 // otherwise we need to find the object manager path
383 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600384 findObjectManagerPathForEnumerate(
385 transaction->objectPath, connection.first, transaction);
386 }
387 }
388 },
389 "xyz.openbmc_project.ObjectMapper",
390 "/xyz/openbmc_project/object_mapper",
391 "xyz.openbmc_project.ObjectMapper", "GetObject",
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500392 transaction->objectPath, std::array<const char*, 0>());
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600393}
Ed Tanous64530012018-02-06 17:08:16 -0800394
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700395// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700396struct InProgressActionData
397{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500398 InProgressActionData(crow::Response& res) : res(res){};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 ~InProgressActionData()
400 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600401 // Methods could have been called across different owners
402 // and interfaces, where some calls failed and some passed.
403 //
404 // The rules for this are:
405 // * if no method was called - error
406 // * if a method failed and none passed - error
407 // (converse: if at least one method passed - OK)
408 // * for the method output:
409 // * if output processing didn't fail, return the data
410
411 // Only deal with method returns if nothing failed earlier
412 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700413 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600414 if (!methodPassed)
415 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500416 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600417 {
418 setErrorResponse(res, boost::beast::http::status::not_found,
419 methodNotFoundDesc, notFoundMsg);
420 }
421 }
422 else
423 {
424 if (outputFailed)
425 {
426 setErrorResponse(
427 res, boost::beast::http::status::internal_server_error,
428 "Method output failure", methodOutputFailedMsg);
429 }
430 else
431 {
432 res.jsonValue = {{"status", "ok"},
433 {"message", "200 OK"},
434 {"data", methodResponse}};
435 }
436 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600438
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700440 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700441
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500442 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600444 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
445 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500447 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 std::string path;
449 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600450 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600451 bool methodPassed = false;
452 bool methodFailed = false;
453 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600454 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600455 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700456 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700457};
458
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500459std::vector<std::string> dbusArgSplit(const std::string& string)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700460{
461 std::vector<std::string> ret;
462 if (string.empty())
463 {
464 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700465 }
Ed Tanous0f0353b2019-10-24 11:37:51 -0700466 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 int containerDepth = 0;
468
469 for (std::string::const_iterator character = string.begin();
470 character != string.end(); character++)
471 {
472 ret.back() += *character;
473 switch (*character)
474 {
475 case ('a'):
476 break;
477 case ('('):
478 case ('{'):
479 containerDepth++;
480 break;
481 case ('}'):
482 case (')'):
483 containerDepth--;
484 if (containerDepth == 0)
485 {
486 if (character + 1 != string.end())
487 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700488 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489 }
490 }
491 break;
492 default:
493 if (containerDepth == 0)
494 {
495 if (character + 1 != string.end())
496 {
Ed Tanous0f0353b2019-10-24 11:37:51 -0700497 ret.emplace_back("");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700498 }
499 }
500 break;
501 }
502 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600503
504 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700505}
506
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500507int convertJsonToDbus(sd_bus_message* m, const std::string& arg_type,
508 const nlohmann::json& input_json)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700509{
510 int r = 0;
511 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
512 << " to type: " << arg_type;
513 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700514
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 // Assume a single object for now.
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500516 const nlohmann::json* j = &input_json;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700518
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500519 for (const std::string& argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700520 {
521 // If we are decoding multiple objects, grab the pointer to the
522 // iterator, and increment it for the next loop
523 if (argTypes.size() > 1)
524 {
525 if (jIt == input_json.end())
526 {
527 return -2;
528 }
529 j = &*jIt;
530 jIt++;
531 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500532 const int64_t* intValue = j->get_ptr<const int64_t*>();
533 const std::string* stringValue = j->get_ptr<const std::string*>();
534 const double* doubleValue = j->get_ptr<const double*>();
535 const bool* b = j->get_ptr<const bool*>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 int64_t v = 0;
537 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700538
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 // Do some basic type conversions that make sense. uint can be
540 // converted to int. int and uint can be converted to double
Ed Tanous66664f22019-10-11 13:05:49 -0700541 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700542 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500543 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700544 if (uintValue != nullptr)
545 {
546 v = static_cast<int64_t>(*uintValue);
547 intValue = &v;
548 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700549 }
Ed Tanous66664f22019-10-11 13:05:49 -0700550 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700551 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500552 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanous66664f22019-10-11 13:05:49 -0700553 if (uintValue != nullptr)
554 {
555 d = static_cast<double>(*uintValue);
556 doubleValue = &d;
557 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700558 }
Ed Tanous66664f22019-10-11 13:05:49 -0700559 if (doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700560 {
Ed Tanous66664f22019-10-11 13:05:49 -0700561 if (intValue != nullptr)
562 {
563 d = static_cast<double>(*intValue);
564 doubleValue = &d;
565 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700566 }
567
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700568 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700570 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571 {
572 return -1;
573 }
Ed Tanous271584a2019-07-09 16:24:22 -0700574 r = sd_bus_message_append_basic(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500575 m, argCode[0], static_cast<const void*>(stringValue->data()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 if (r < 0)
577 {
578 return r;
579 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700580 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700581 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700582 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700583 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700584 {
585 return -1;
586 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500587 if ((*intValue < std::numeric_limits<int32_t>::lowest()) ||
588 (*intValue > std::numeric_limits<int32_t>::max()))
589 {
590 return -ERANGE;
591 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 int32_t i = static_cast<int32_t>(*intValue);
593 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 if (r < 0)
595 {
596 return r;
597 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700598 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700599 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700600 {
601 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700602 int boolInt = false;
603 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700604 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500605 if (*intValue == 1)
606 {
607 boolInt = true;
608 }
609 else if (*intValue == 0)
610 {
611 boolInt = false;
612 }
613 else
614 {
615 return -ERANGE;
616 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700617 }
618 else if (b != nullptr)
619 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600620 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700622 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700624 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700625 }
626 else
627 {
628 return -1;
629 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700630 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 if (r < 0)
632 {
633 return r;
634 }
635 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700636 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700637 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700638 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 {
640 return -1;
641 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500642 if ((*intValue < std::numeric_limits<int16_t>::lowest()) ||
643 (*intValue > std::numeric_limits<int16_t>::max()))
644 {
645 return -ERANGE;
646 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700647 int16_t n = static_cast<int16_t>(*intValue);
648 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 if (r < 0)
650 {
651 return r;
652 }
653 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700656 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 {
658 return -1;
659 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500660 if ((*intValue < std::numeric_limits<int64_t>::lowest()) ||
661 (*intValue > std::numeric_limits<int64_t>::max()))
662 {
663 return -ERANGE;
664 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700665 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700666 if (r < 0)
667 {
668 return r;
669 }
670 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700671 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700672 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500673 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 {
676 return -1;
677 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500678 if ((*uintValue < std::numeric_limits<uint8_t>::lowest()) ||
679 (*uintValue > std::numeric_limits<uint8_t>::max()))
680 {
681 return -ERANGE;
682 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700683 uint8_t y = static_cast<uint8_t>(*uintValue);
684 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700685 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700686 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700687 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500688 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 {
691 return -1;
692 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500693 if ((*uintValue < std::numeric_limits<uint16_t>::lowest()) ||
694 (*uintValue > std::numeric_limits<uint16_t>::max()))
695 {
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 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500708 if ((*uintValue < std::numeric_limits<uint32_t>::lowest()) ||
709 (*uintValue > std::numeric_limits<uint32_t>::max()))
710 {
711 return -ERANGE;
712 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700713 uint32_t u = static_cast<uint32_t>(*uintValue);
714 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700715 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700716 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700717 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500718 const uint64_t* uintValue = j->get_ptr<const uint64_t*>();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700719 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700720 {
721 return -1;
722 }
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500723 if ((*uintValue < std::numeric_limits<uint64_t>::lowest()) ||
724 (*uintValue > std::numeric_limits<uint64_t>::max()))
725 {
726 return -ERANGE;
727 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700728 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700729 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700730 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700731 {
Adriana Kobylakc66c8592019-08-06 15:08:05 -0500732 if (doubleValue == nullptr)
733 {
734 return -1;
735 }
736 if ((*doubleValue < std::numeric_limits<double>::lowest()) ||
737 (*doubleValue > std::numeric_limits<double>::max()))
738 {
739 return -ERANGE;
740 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700741 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700743 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700745 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700746 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700747 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 if (r < 0)
749 {
750 return r;
751 }
752
753 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
754 ++it)
755 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700756 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700757 if (r < 0)
758 {
759 return r;
760 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700761 }
762 sd_bus_message_close_container(m);
763 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700764 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700765 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700766 std::string containedType = argCode.substr(1);
767 BMCWEB_LOG_DEBUG << "variant type: " << argCode
768 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700769 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700770 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 if (r < 0)
772 {
773 return r;
774 }
775
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700776 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700777 if (r < 0)
778 {
779 return r;
780 }
781
782 r = sd_bus_message_close_container(m);
783 if (r < 0)
784 {
785 return r;
786 }
787 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700788 else if (boost::starts_with(argCode, "(") &&
789 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700790 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700791 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700792 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700793 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800794 if (r < 0)
795 {
796 return r;
797 }
798
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799 nlohmann::json::const_iterator it = j->begin();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500800 for (const std::string& argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 {
802 if (it == j->end())
803 {
804 return -1;
805 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000806 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 if (r < 0)
808 {
809 return r;
810 }
811 it++;
812 }
813 r = sd_bus_message_close_container(m);
814 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700815 else if (boost::starts_with(argCode, "{") &&
816 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700817 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700818 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700819 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700820 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800821 if (r < 0)
822 {
823 return r;
824 }
825
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700826 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 if (codes.size() != 2)
828 {
829 return -1;
830 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500831 const std::string& key_type = codes[0];
832 const std::string& value_type = codes[1];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700833 for (auto it : j->items())
834 {
835 r = convertJsonToDbus(m, key_type, it.key());
836 if (r < 0)
837 {
838 return r;
839 }
840
841 r = convertJsonToDbus(m, value_type, it.value());
842 if (r < 0)
843 {
844 return r;
845 }
846 }
847 r = sd_bus_message_close_container(m);
848 }
849 else
850 {
851 return -2;
852 }
853 if (r < 0)
854 {
855 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700856 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700857
Ed Tanous1abe55e2018-09-05 08:30:59 -0700858 if (argTypes.size() > 1)
859 {
860 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700861 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700862 }
Matt Spinler127ea542019-01-14 11:04:28 -0600863
864 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700865}
866
Matt Spinlerd22a7132019-01-14 12:14:30 -0600867template <typename T>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500868int readMessageItem(const std::string& typeCode, sdbusplus::message::message& m,
869 nlohmann::json& data)
Matt Spinlerd22a7132019-01-14 12:14:30 -0600870{
871 T value;
872
873 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
874 if (r < 0)
875 {
876 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
877 << " failed!";
878 return r;
879 }
880
881 data = value;
882 return 0;
883}
884
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500885int convertDBusToJSON(const std::string& returnType,
886 sdbusplus::message::message& m, nlohmann::json& response);
Matt Spinler6df8f992019-01-14 12:47:47 -0600887
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500888int readDictEntryFromMessage(const std::string& typeCode,
889 sdbusplus::message::message& m,
890 nlohmann::json& object)
Matt Spinler6df8f992019-01-14 12:47:47 -0600891{
892 std::vector<std::string> types = dbusArgSplit(typeCode);
893 if (types.size() != 2)
894 {
895 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
896 << types.size();
897 return -1;
898 }
899
900 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
901 typeCode.c_str());
902 if (r < 0)
903 {
904 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
905 return r;
906 }
907
908 nlohmann::json key;
909 r = convertDBusToJSON(types[0], m, key);
910 if (r < 0)
911 {
912 return r;
913 }
914
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500915 const std::string* keyPtr = key.get_ptr<const std::string*>();
Matt Spinler6df8f992019-01-14 12:47:47 -0600916 if (keyPtr == nullptr)
917 {
918 // json doesn't support non-string keys. If we hit this condition,
919 // convert the result to a string so we can proceed
920 key = key.dump();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500921 keyPtr = key.get_ptr<const std::string*>();
Ed Tanous7c091622019-05-23 11:42:36 -0700922 // in theory this can't fail now, but lets be paranoid about it
923 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600924 if (keyPtr == nullptr)
925 {
926 return -1;
927 }
928 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500929 nlohmann::json& value = object[*keyPtr];
Matt Spinler6df8f992019-01-14 12:47:47 -0600930
931 r = convertDBusToJSON(types[1], m, value);
932 if (r < 0)
933 {
934 return r;
935 }
936
937 r = sd_bus_message_exit_container(m.get());
938 if (r < 0)
939 {
940 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
941 return r;
942 }
943
944 return 0;
945}
946
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500947int readArrayFromMessage(const std::string& typeCode,
948 sdbusplus::message::message& m, nlohmann::json& data)
Matt Spinler6df8f992019-01-14 12:47:47 -0600949{
950 if (typeCode.size() < 2)
951 {
952 BMCWEB_LOG_ERROR << "Type code " << typeCode
953 << " too small for an array";
954 return -1;
955 }
956
957 std::string containedType = typeCode.substr(1);
958
959 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
960 containedType.c_str());
961 if (r < 0)
962 {
963 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
964 << r;
965 return r;
966 }
967
968 bool dict = boost::starts_with(containedType, "{") &&
969 boost::ends_with(containedType, "}");
970
971 if (dict)
972 {
973 // Remove the { }
974 containedType = containedType.substr(1, containedType.size() - 2);
975 data = nlohmann::json::object();
976 }
977 else
978 {
979 data = nlohmann::json::array();
980 }
981
982 while (true)
983 {
984 r = sd_bus_message_at_end(m.get(), false);
985 if (r < 0)
986 {
987 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
988 return r;
989 }
990
991 if (r > 0)
992 {
993 break;
994 }
995
996 // Dictionaries are only ever seen in an array
997 if (dict)
998 {
999 r = readDictEntryFromMessage(containedType, m, data);
1000 if (r < 0)
1001 {
1002 return r;
1003 }
1004 }
1005 else
1006 {
1007 data.push_back(nlohmann::json());
1008
1009 r = convertDBusToJSON(containedType, m, data.back());
1010 if (r < 0)
1011 {
1012 return r;
1013 }
1014 }
1015 }
1016
1017 r = sd_bus_message_exit_container(m.get());
1018 if (r < 0)
1019 {
1020 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1021 return r;
1022 }
1023
1024 return 0;
1025}
1026
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001027int readStructFromMessage(const std::string& typeCode,
1028 sdbusplus::message::message& m, nlohmann::json& data)
Matt Spinler75c6c672019-01-14 13:01:46 -06001029{
1030 if (typeCode.size() < 3)
1031 {
1032 BMCWEB_LOG_ERROR << "Type code " << typeCode
1033 << " too small for a struct";
1034 return -1;
1035 }
1036
1037 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
1038 std::vector<std::string> types = dbusArgSplit(containedTypes);
1039
1040 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
1041 containedTypes.c_str());
1042 if (r < 0)
1043 {
1044 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1045 << r;
1046 return r;
1047 }
1048
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001049 for (const std::string& type : types)
Matt Spinler75c6c672019-01-14 13:01:46 -06001050 {
1051 data.push_back(nlohmann::json());
1052 r = convertDBusToJSON(type, m, data.back());
1053 if (r < 0)
1054 {
1055 return r;
1056 }
1057 }
1058
1059 r = sd_bus_message_exit_container(m.get());
1060 if (r < 0)
1061 {
1062 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
1063 return r;
1064 }
1065 return 0;
1066}
1067
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001068int readVariantFromMessage(sdbusplus::message::message& m, nlohmann::json& data)
Matt Spinler89c19702019-01-14 13:13:00 -06001069{
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001070 const char* containerType;
Ed Tanous99131cd2019-10-24 11:12:47 -07001071 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType);
Matt Spinler89c19702019-01-14 13:13:00 -06001072 if (r < 0)
1073 {
1074 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1075 return r;
1076 }
1077
1078 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1079 containerType);
1080 if (r < 0)
1081 {
1082 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1083 << r;
1084 return r;
1085 }
1086
1087 r = convertDBusToJSON(containerType, m, data);
1088 if (r < 0)
1089 {
1090 return r;
1091 }
1092
1093 r = sd_bus_message_exit_container(m.get());
1094 if (r < 0)
1095 {
1096 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1097 return r;
1098 }
1099
1100 return 0;
1101}
1102
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001103int convertDBusToJSON(const std::string& returnType,
1104 sdbusplus::message::message& m, nlohmann::json& response)
Matt Spinler16caaee2019-01-15 11:40:34 -06001105{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001106 int r = 0;
1107 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1108
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001109 for (const std::string& typeCode : returnTypes)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001110 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001111 nlohmann::json* thisElement = &response;
Matt Spinlerf39420c2019-01-30 12:57:18 -06001112 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001113 {
1114 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001115 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001116 }
1117
1118 if (typeCode == "s")
1119 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001120 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001121 if (r < 0)
1122 {
1123 return r;
1124 }
1125 }
1126 else if (typeCode == "g")
1127 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001128 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001129 if (r < 0)
1130 {
1131 return r;
1132 }
1133 }
1134 else if (typeCode == "o")
1135 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001136 r = readMessageItem<char*>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001137 if (r < 0)
1138 {
1139 return r;
1140 }
1141 }
1142 else if (typeCode == "b")
1143 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001144 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001145 if (r < 0)
1146 {
1147 return r;
1148 }
1149
Matt Spinlerf39420c2019-01-30 12:57:18 -06001150 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001151 }
1152 else if (typeCode == "u")
1153 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001154 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001155 if (r < 0)
1156 {
1157 return r;
1158 }
1159 }
1160 else if (typeCode == "i")
1161 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001162 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001163 if (r < 0)
1164 {
1165 return r;
1166 }
1167 }
1168 else if (typeCode == "x")
1169 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001170 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001171 if (r < 0)
1172 {
1173 return r;
1174 }
1175 }
1176 else if (typeCode == "t")
1177 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001178 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001179 if (r < 0)
1180 {
1181 return r;
1182 }
1183 }
1184 else if (typeCode == "n")
1185 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001186 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001187 if (r < 0)
1188 {
1189 return r;
1190 }
1191 }
1192 else if (typeCode == "q")
1193 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001194 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001195 if (r < 0)
1196 {
1197 return r;
1198 }
1199 }
1200 else if (typeCode == "y")
1201 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001202 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001203 if (r < 0)
1204 {
1205 return r;
1206 }
1207 }
1208 else if (typeCode == "d")
1209 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001210 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001211 if (r < 0)
1212 {
1213 return r;
1214 }
1215 }
1216 else if (typeCode == "h")
1217 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001218 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001219 if (r < 0)
1220 {
1221 return r;
1222 }
1223 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001224 else if (boost::starts_with(typeCode, "a"))
1225 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001226 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001227 if (r < 0)
1228 {
1229 return r;
1230 }
1231 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001232 else if (boost::starts_with(typeCode, "(") &&
1233 boost::ends_with(typeCode, ")"))
1234 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001235 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001236 if (r < 0)
1237 {
1238 return r;
1239 }
1240 }
Matt Spinler89c19702019-01-14 13:13:00 -06001241 else if (boost::starts_with(typeCode, "v"))
1242 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001243 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001244 if (r < 0)
1245 {
1246 return r;
1247 }
1248 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001249 else
1250 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001251 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1252 return -2;
1253 }
1254 }
1255
Matt Spinler16caaee2019-01-15 11:40:34 -06001256 return 0;
1257}
1258
1259void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001260 sdbusplus::message::message& m,
1261 const std::string& returnType)
Matt Spinler16caaee2019-01-15 11:40:34 -06001262{
Matt Spinler39a4e392019-01-15 11:53:13 -06001263 nlohmann::json data;
1264
1265 int r = convertDBusToJSON(returnType, m, data);
1266 if (r < 0)
1267 {
1268 transaction->outputFailed = true;
1269 return;
1270 }
1271
1272 if (data.is_null())
1273 {
1274 return;
1275 }
1276
1277 if (transaction->methodResponse.is_null())
1278 {
1279 transaction->methodResponse = std::move(data);
1280 return;
1281 }
1282
1283 // If they're both dictionaries or arrays, merge into one.
1284 // Otherwise, make the results an array with every result
1285 // an entry. Could also just fail in that case, but it
1286 // seems better to get the data back somehow.
1287
1288 if (transaction->methodResponse.is_object() && data.is_object())
1289 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001290 for (const auto& obj : data.items())
Matt Spinler39a4e392019-01-15 11:53:13 -06001291 {
1292 // Note: Will overwrite the data for a duplicate key
1293 transaction->methodResponse.emplace(obj.key(),
1294 std::move(obj.value()));
1295 }
1296 return;
1297 }
1298
1299 if (transaction->methodResponse.is_array() && data.is_array())
1300 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001301 for (auto& obj : data)
Matt Spinler39a4e392019-01-15 11:53:13 -06001302 {
1303 transaction->methodResponse.push_back(std::move(obj));
1304 }
1305 return;
1306 }
1307
1308 if (!transaction->convertedToArray)
1309 {
1310 // They are different types. May as well turn them into an array
1311 nlohmann::json j = std::move(transaction->methodResponse);
1312 transaction->methodResponse = nlohmann::json::array();
1313 transaction->methodResponse.push_back(std::move(j));
1314 transaction->methodResponse.push_back(std::move(data));
1315 transaction->convertedToArray = true;
1316 }
1317 else
1318 {
1319 transaction->methodResponse.push_back(std::move(data));
1320 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001321}
1322
Ed Tanousd76323e2018-08-07 14:35:40 -07001323void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001324 const std::string& connectionName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001325{
1326 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1327 << connectionName;
1328 crow::connections::systemBus->async_method_call(
1329 [transaction, connectionName{std::string(connectionName)}](
1330 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001331 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001332 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1333 if (ec)
1334 {
1335 BMCWEB_LOG_ERROR
1336 << "Introspect call failed with error: " << ec.message()
1337 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001338 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001339 }
Matt Spinler318bd892019-01-15 09:59:20 -06001340 tinyxml2::XMLDocument doc;
1341
1342 doc.Parse(introspect_xml.data(), introspect_xml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001343 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
Matt Spinler318bd892019-01-15 09:59:20 -06001344 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001345 {
Matt Spinler318bd892019-01-15 09:59:20 -06001346 BMCWEB_LOG_ERROR << "XML document failed to parse "
1347 << connectionName << "\n";
1348 return;
1349 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001350 tinyxml2::XMLElement* interfaceNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001351 pRoot->FirstChildElement("interface");
1352 while (interfaceNode != nullptr)
1353 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001354 const char* thisInterfaceName =
Matt Spinler318bd892019-01-15 09:59:20 -06001355 interfaceNode->Attribute("name");
1356 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001357 {
Matt Spinler318bd892019-01-15 09:59:20 -06001358 if (!transaction->interfaceName.empty() &&
1359 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001360 {
Matt Spinler318bd892019-01-15 09:59:20 -06001361 interfaceNode =
1362 interfaceNode->NextSiblingElement("interface");
1363 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001364 }
Matt Spinler318bd892019-01-15 09:59:20 -06001365
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001366 tinyxml2::XMLElement* methodNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001367 interfaceNode->FirstChildElement("method");
1368 while (methodNode != nullptr)
1369 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001370 const char* thisMethodName =
Matt Spinler318bd892019-01-15 09:59:20 -06001371 methodNode->Attribute("name");
1372 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1373 if (thisMethodName != nullptr &&
1374 thisMethodName == transaction->methodName)
1375 {
1376 BMCWEB_LOG_DEBUG
1377 << "Found method named " << thisMethodName
1378 << " on interface " << thisInterfaceName;
1379 sdbusplus::message::message m =
1380 crow::connections::systemBus->new_method_call(
1381 connectionName.c_str(),
1382 transaction->path.c_str(),
1383 thisInterfaceName,
1384 transaction->methodName.c_str());
1385
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001386 tinyxml2::XMLElement* argumentNode =
Matt Spinler318bd892019-01-15 09:59:20 -06001387 methodNode->FirstChildElement("arg");
1388
Matt Spinler16caaee2019-01-15 11:40:34 -06001389 std::string returnType;
1390
1391 // Find the output type
1392 while (argumentNode != nullptr)
1393 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001394 const char* argDirection =
Matt Spinler16caaee2019-01-15 11:40:34 -06001395 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001396 const char* argType =
Matt Spinler16caaee2019-01-15 11:40:34 -06001397 argumentNode->Attribute("type");
1398 if (argDirection != nullptr &&
1399 argType != nullptr &&
1400 std::string(argDirection) == "out")
1401 {
1402 returnType = argType;
1403 break;
1404 }
1405 argumentNode =
1406 argumentNode->NextSiblingElement("arg");
1407 }
1408
Matt Spinler318bd892019-01-15 09:59:20 -06001409 nlohmann::json::const_iterator argIt =
1410 transaction->arguments.begin();
1411
Matt Spinler16caaee2019-01-15 11:40:34 -06001412 argumentNode = methodNode->FirstChildElement("arg");
1413
Matt Spinler318bd892019-01-15 09:59:20 -06001414 while (argumentNode != nullptr)
1415 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001416 const char* argDirection =
Matt Spinler318bd892019-01-15 09:59:20 -06001417 argumentNode->Attribute("direction");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001418 const char* argType =
Matt Spinler318bd892019-01-15 09:59:20 -06001419 argumentNode->Attribute("type");
1420 if (argDirection != nullptr &&
1421 argType != nullptr &&
1422 std::string(argDirection) == "in")
1423 {
1424 if (argIt == transaction->arguments.end())
1425 {
1426 transaction->setErrorStatus(
1427 "Invalid method args");
1428 return;
1429 }
1430 if (convertJsonToDbus(m.get(),
1431 std::string(argType),
1432 *argIt) < 0)
1433 {
1434 transaction->setErrorStatus(
1435 "Invalid method arg type");
1436 return;
1437 }
1438
1439 argIt++;
1440 }
1441 argumentNode =
1442 argumentNode->NextSiblingElement("arg");
1443 }
1444
1445 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001446 m, [transaction, returnType](
1447 boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001448 sdbusplus::message::message& m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001449 if (ec)
1450 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001451 transaction->methodFailed = true;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001452 const sd_bus_error* e = m.get_error();
Matt Spinler06b1b632019-06-18 16:09:25 -05001453
1454 if (e)
1455 {
1456 setErrorResponse(
1457 transaction->res,
1458 boost::beast::http::status::
1459 bad_request,
1460 e->name, e->message);
1461 }
1462 else
1463 {
1464 setErrorResponse(
1465 transaction->res,
1466 boost::beast::http::status::
1467 bad_request,
1468 "Method call failed",
1469 methodFailedMsg);
1470 }
Matt Spinler318bd892019-01-15 09:59:20 -06001471 return;
1472 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001473 else
1474 {
1475 transaction->methodPassed = true;
1476 }
1477
1478 handleMethodResponse(transaction, m,
1479 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001480 });
1481 break;
1482 }
1483 methodNode = methodNode->NextSiblingElement("method");
1484 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001485 }
Matt Spinler318bd892019-01-15 09:59:20 -06001486 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001487 }
1488 },
1489 connectionName, transaction->path,
1490 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001491}
1492
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001493void handleAction(const crow::Request& req, crow::Response& res,
1494 const std::string& objectPath, 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
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001558void handleDelete(const crow::Request& req, crow::Response& res,
1559 const std::string& objectPath)
Matt Spinlerde818812018-12-11 16:39:20 -06001560{
1561 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1562
1563 crow::connections::systemBus->async_method_call(
1564 [&res, objectPath](
1565 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001566 const std::vector<std::pair<std::string, std::vector<std::string>>>&
1567 interfaceNames) {
Matt Spinlerde818812018-12-11 16:39:20 -06001568 if (ec || interfaceNames.size() <= 0)
1569 {
1570 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001571 setErrorResponse(res,
1572 boost::beast::http::status::method_not_allowed,
1573 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001574 res.end();
1575 return;
1576 }
1577
1578 auto transaction = std::make_shared<InProgressActionData>(res);
1579 transaction->path = objectPath;
1580 transaction->methodName = "Delete";
1581 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1582
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001583 for (const std::pair<std::string, std::vector<std::string>>&
1584 object : interfaceNames)
Matt Spinlerde818812018-12-11 16:39:20 -06001585 {
1586 findActionOnInterface(transaction, object.first);
1587 }
1588 },
1589 "xyz.openbmc_project.ObjectMapper",
1590 "/xyz/openbmc_project/object_mapper",
1591 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001592 std::array<const char*, 0>());
Matt Spinlerde818812018-12-11 16:39:20 -06001593}
1594
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001595void handleList(crow::Response& res, const std::string& objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001596 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001597{
1598 crow::connections::systemBus->async_method_call(
1599 [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001600 std::vector<std::string>& objectPaths) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001601 if (ec)
1602 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001603 setErrorResponse(res, boost::beast::http::status::not_found,
1604 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001605 }
1606 else
1607 {
1608 res.jsonValue = {{"status", "ok"},
1609 {"message", "200 OK"},
1610 {"data", std::move(objectPaths)}};
1611 }
1612 res.end();
1613 },
1614 "xyz.openbmc_project.ObjectMapper",
1615 "/xyz/openbmc_project/object_mapper",
1616 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001617 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001618}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001619
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001620void handleEnumerate(crow::Response& res, const std::string& objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001621{
Ed Tanous049a0512018-11-01 13:58:42 -07001622 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1623 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1624
1625 asyncResp->res.jsonValue = {{"message", "200 OK"},
1626 {"status", "ok"},
1627 {"data", nlohmann::json::object()}};
1628
Ed Tanous1abe55e2018-09-05 08:30:59 -07001629 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001630 [objectPath, asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001631 GetSubTreeType& object_names) {
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001632 auto transaction = std::make_shared<InProgressEnumerateData>(
1633 objectPath, asyncResp);
1634
1635 transaction->subtree =
1636 std::make_shared<GetSubTreeType>(std::move(object_names));
1637
Ed Tanous1abe55e2018-09-05 08:30:59 -07001638 if (ec)
1639 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001640 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1641 << transaction->objectPath;
1642 setErrorResponse(transaction->asyncResp->res,
1643 boost::beast::http::status::not_found,
1644 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001645 return;
1646 }
Ed Tanous64530012018-02-06 17:08:16 -08001647
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001648 // Add the data for the path passed in to the results
1649 // as if GetSubTree returned it, and continue on enumerating
1650 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001651 },
1652 "xyz.openbmc_project.ObjectMapper",
1653 "/xyz/openbmc_project/object_mapper",
Ed Tanous271584a2019-07-09 16:24:22 -07001654 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001655 std::array<const char*, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001656}
Ed Tanous911ac312017-08-15 09:37:42 -07001657
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001658void handleGet(crow::Response& res, std::string& objectPath,
1659 std::string& destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001660{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001661 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1662 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001663 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001664
Ed Tanous1abe55e2018-09-05 08:30:59 -07001665 std::shared_ptr<std::string> path =
1666 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001667
Ed Tanous1abe55e2018-09-05 08:30:59 -07001668 using GetObjectType =
1669 std::vector<std::pair<std::string, std::vector<std::string>>>;
1670 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001671 [&res, path, propertyName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001672 const GetObjectType& object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001673 if (ec || object_names.size() <= 0)
1674 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001675 setErrorResponse(res, boost::beast::http::status::not_found,
1676 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001677 res.end();
1678 return;
1679 }
1680 std::shared_ptr<nlohmann::json> response =
1681 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001682 // The mapper should never give us an empty interface names
1683 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001684 for (const std::pair<std::string, std::vector<std::string>>
1685 connection : object_names)
1686 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001687 const std::vector<std::string>& interfaceNames =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001688 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001689
Ed Tanous1abe55e2018-09-05 08:30:59 -07001690 if (interfaceNames.size() <= 0)
1691 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001692 setErrorResponse(res, boost::beast::http::status::not_found,
1693 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001694 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001695 return;
1696 }
1697
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001698 for (const std::string& interface : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001699 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001700 sdbusplus::message::message m =
1701 crow::connections::systemBus->new_method_call(
1702 connection.first.c_str(), path->c_str(),
1703 "org.freedesktop.DBus.Properties", "GetAll");
1704 m.append(interface);
1705 crow::connections::systemBus->async_send(
1706 m, [&res, response,
1707 propertyName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001708 sdbusplus::message::message& msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709 if (ec)
1710 {
1711 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1712 << ec;
1713 }
1714 else
1715 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001716 nlohmann::json properties;
1717 int r =
1718 convertDBusToJSON("a{sv}", msg, properties);
1719 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001720 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001721 BMCWEB_LOG_ERROR
1722 << "convertDBusToJSON failed";
1723 }
1724 else
1725 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001726 for (auto& prop : properties.items())
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001727 {
Ed Tanous7c091622019-05-23 11:42:36 -07001728 // if property name is empty, or
1729 // matches our search query, add it
1730 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001731
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001732 if (propertyName->empty())
1733 {
1734 (*response)[prop.key()] =
1735 std::move(prop.value());
1736 }
1737 else if (prop.key() == *propertyName)
1738 {
1739 *response = std::move(prop.value());
1740 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001741 }
1742 }
1743 }
1744 if (response.use_count() == 1)
1745 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001746 if (!propertyName->empty() && response->empty())
1747 {
1748 setErrorResponse(
1749 res,
1750 boost::beast::http::status::not_found,
1751 propNotFoundDesc, notFoundMsg);
1752 }
1753 else
1754 {
1755 res.jsonValue = {{"status", "ok"},
1756 {"message", "200 OK"},
1757 {"data", *response}};
1758 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001759 res.end();
1760 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001761 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001762 }
1763 }
1764 },
1765 "xyz.openbmc_project.ObjectMapper",
1766 "/xyz/openbmc_project/object_mapper",
1767 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1768 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001769}
1770
Ed Tanous1abe55e2018-09-05 08:30:59 -07001771struct AsyncPutRequest
1772{
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001773 AsyncPutRequest(crow::Response& res) : res(res)
1774 {}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001775 ~AsyncPutRequest()
1776 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001777 if (res.jsonValue.empty())
1778 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001779 setErrorResponse(res, boost::beast::http::status::forbidden,
1780 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001781 }
1782
1783 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001784 }
1785
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001786 void setErrorStatus(const std::string& desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001787 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001788 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1789 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001790 }
1791
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001792 crow::Response& res;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001793 std::string objectPath;
1794 std::string propertyName;
1795 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001796};
1797
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001798void handlePut(const crow::Request& req, crow::Response& res,
1799 const std::string& objectPath, 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(
1838 [transaction](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001839 const GetObjectType& object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001840 if (!ec && object_names.size() <= 0)
1841 {
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 Tanous1abe55e2018-09-05 08:30:59 -07001848 for (const std::pair<std::string, std::vector<std::string>>
1849 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)},
1855 transaction](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001856 const std::string& introspectXml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001857 if (ec)
1858 {
1859 BMCWEB_LOG_ERROR
1860 << "Introspect call failed with error: "
1861 << ec.message()
1862 << " 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&
1954 m) {
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 =
Lei YU97d2a472019-06-11 17:44:27 +08001959 m.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
2016 if (req.method() == "POST"_method)
2017 {
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 }
2030 else if (req.method() == "GET"_method)
2031 {
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 }
2059 else if (req.method() == "PUT"_method)
2060 {
2061 handlePut(req, res, objectPath, destProperty);
2062 return;
2063 }
Matt Spinlerde818812018-12-11 16:39:20 -06002064 else if (req.method() == "DELETE"_method)
2065 {
2066 handleDelete(req, res, objectPath);
2067 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
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002075template <typename... Middlewares>
2076void requestRoutes(Crow<Middlewares...>& app)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002077{
2078 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002079 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002080 .methods("GET"_method)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002081 [](const crow::Request& req, crow::Response& res) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002082 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2083 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002084 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002085 });
2086
2087 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002088 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002089 .methods("GET"_method)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002090 [](const crow::Request& req, crow::Response& res) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002091 auto myCallback = [&res](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002092 std::vector<std::string>& names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002093 if (ec)
2094 {
2095 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2096 res.result(
2097 boost::beast::http::status::internal_server_error);
2098 }
2099 else
2100 {
2101 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002102 res.jsonValue = {{"status", "ok"}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002103 auto& objectsSub = res.jsonValue["objects"];
2104 for (auto& name : names)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002105 {
2106 objectsSub.push_back({{"name", name}});
2107 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002108 }
2109 res.end();
2110 };
2111 crow::connections::systemBus->async_method_call(
2112 std::move(myCallback), "org.freedesktop.DBus", "/",
2113 "org.freedesktop.DBus", "ListNames");
2114 });
2115
2116 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002117 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002118 .methods("GET"_method)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002119 [](const crow::Request& req, crow::Response& res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002120 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002121 });
2122
2123 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002124 .requires({"Login"})
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002125 .methods("GET"_method)([](const crow::Request& req, 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>")
2132 .requires({"ConfigureComponents", "ConfigureManager"})
2133 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002134 [](const crow::Request& req, crow::Response& res,
2135 const std::string& path) {
Ed Tanous049a0512018-11-01 13:58:42 -07002136 std::string objectPath = "/xyz/" + path;
2137 handleDBusUrl(req, res, objectPath);
2138 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002139
Ed Tanous049a0512018-11-01 13:58:42 -07002140 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002141 .requires({"Login"})
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002142 .methods("GET"_method)([](const crow::Request& req, crow::Response& res,
2143 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002144 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002145 handleDBusUrl(req, res, objectPath);
2146 });
2147
2148 BMCWEB_ROUTE(app, "/org/<path>")
2149 .requires({"ConfigureComponents", "ConfigureManager"})
2150 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002151 [](const crow::Request& req, crow::Response& res,
2152 const std::string& path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002153 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002154 handleDBusUrl(req, res, objectPath);
2155 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002156
Ed Tanous1abe55e2018-09-05 08:30:59 -07002157 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002158 .requires({"ConfigureManager"})
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002159 .methods("GET"_method)([](const crow::Request& req, crow::Response& res,
2160 const std::string& dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002161 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002162 if (!std::regex_match(dumpId, validFilename))
2163 {
Ed Tanousad18f072018-11-14 14:07:48 -08002164 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002165 res.end();
2166 return;
2167 }
James Feistf6150402019-01-08 10:36:20 -08002168 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002169 "/var/lib/phosphor-debug-collector/dumps");
2170
Ed Tanousad18f072018-11-14 14:07:48 -08002171 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002172
James Feistf6150402019-01-08 10:36:20 -08002173 if (!std::filesystem::exists(loc) ||
2174 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002175 {
Ed Tanousad18f072018-11-14 14:07:48 -08002176 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002177 res.result(boost::beast::http::status::not_found);
2178 res.end();
2179 return;
2180 }
James Feistf6150402019-01-08 10:36:20 -08002181 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002182
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002183 for (auto& file : files)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002184 {
2185 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002186 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002187 {
2188 continue;
2189 }
Ramesh Iyyard9207042019-07-05 08:04:42 -05002190
Ed Tanous1abe55e2018-09-05 08:30:59 -07002191 res.addHeader("Content-Type", "application/octet-stream");
Ramesh Iyyard9207042019-07-05 08:04:42 -05002192
2193 // Assuming only one dump file will be present in the dump id
2194 // directory
2195 std::string dumpFileName = file.path().filename().string();
2196
2197 // Filename should be in alphanumeric, dot and underscore
2198 // Its based on phosphor-debug-collector application dumpfile
2199 // format
2200 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+");
2201 if (!std::regex_match(dumpFileName, dumpFileRegex))
2202 {
2203 BMCWEB_LOG_ERROR << "Invalid dump filename "
2204 << dumpFileName;
2205 res.result(boost::beast::http::status::not_found);
2206 res.end();
2207 return;
2208 }
2209 std::string contentDispositionParam =
2210 "attachment; filename=\"" + dumpFileName + "\"";
2211
2212 res.addHeader("Content-Disposition", contentDispositionParam);
2213
Ed Tanous1abe55e2018-09-05 08:30:59 -07002214 res.body() = {std::istreambuf_iterator<char>(readFile),
2215 std::istreambuf_iterator<char>()};
2216 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002217 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002218 }
2219 res.result(boost::beast::http::status::not_found);
2220 res.end();
2221 return;
2222 });
2223
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002224 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002225 .requires({"Login"})
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002226 .methods("GET"_method)([](const crow::Request& req, crow::Response& res,
2227 const std::string& Connection) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002228 introspectObjects(Connection, "/",
2229 std::make_shared<bmcweb::AsyncResp>(res));
2230 });
2231
2232 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
Ed Tanous8251ffe2019-10-10 14:33:54 -07002233 .requires({"ConfigureComponents", "ConfigureManager"})
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002234 .methods("GET"_method,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002235 "POST"_method)([](const crow::Request& req,
2236 crow::Response& res,
2237 const std::string& processName,
2238 const std::string& requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002239 std::vector<std::string> strs;
2240 boost::split(strs, requestedPath, boost::is_any_of("/"));
2241 std::string objectPath;
2242 std::string interfaceName;
2243 std::string methodName;
2244 auto it = strs.begin();
2245 if (it == strs.end())
2246 {
2247 objectPath = "/";
2248 }
2249 while (it != strs.end())
2250 {
2251 // Check if segment contains ".". If it does, it must be an
2252 // interface
2253 if (it->find(".") != std::string::npos)
2254 {
2255 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002256 // This check is neccesary as the trailing slash gets
2257 // parsed as part of our <path> specifier above, which
2258 // causes the normal trailing backslash redirector to
2259 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002260 }
2261 else if (!it->empty())
2262 {
2263 objectPath += "/" + *it;
2264 }
2265 it++;
2266 }
2267 if (it != strs.end())
2268 {
2269 interfaceName = *it;
2270 it++;
2271
2272 // after interface, we might have a method name
2273 if (it != strs.end())
2274 {
2275 methodName = *it;
2276 it++;
2277 }
2278 }
2279 if (it != strs.end())
2280 {
Ed Tanous7c091622019-05-23 11:42:36 -07002281 // if there is more levels past the method name, something
2282 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002283 res.result(boost::beast::http::status::not_found);
AppaRao Puli3c27ed32020-03-31 01:21:57 +05302284 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002285 return;
2286 }
2287 if (interfaceName.empty())
2288 {
Ed Tanous7c091622019-05-23 11:42:36 -07002289 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2290 std::make_shared<bmcweb::AsyncResp>(res);
2291
Ed Tanous1abe55e2018-09-05 08:30:59 -07002292 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002293 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002294 objectPath](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002295 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002296 if (ec)
2297 {
2298 BMCWEB_LOG_ERROR
2299 << "Introspect call failed with error: "
2300 << ec.message()
2301 << " on process: " << processName
2302 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002303 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002304 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002305 tinyxml2::XMLDocument doc;
2306
2307 doc.Parse(introspect_xml.c_str());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002308 tinyxml2::XMLNode* pRoot =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002309 doc.FirstChildElement("node");
2310 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002311 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002312 BMCWEB_LOG_ERROR << "XML document failed to parse "
2313 << processName << " " << objectPath
2314 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002315 asyncResp->res.jsonValue = {
2316 {"status", "XML parse error"}};
2317 asyncResp->res.result(boost::beast::http::status::
2318 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002319 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002320 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002321
2322 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002323 asyncResp->res.jsonValue = {
2324 {"status", "ok"},
2325 {"bus_name", processName},
2326 {"object_path", objectPath}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002327 nlohmann::json& interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002328 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002329 interfacesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002330 tinyxml2::XMLElement* interface =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002331 pRoot->FirstChildElement("interface");
2332
2333 while (interface != nullptr)
2334 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002335 const char* ifaceName =
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002336 interface->Attribute("name");
2337 if (ifaceName != nullptr)
2338 {
2339 interfacesArray.push_back(
2340 {{"name", ifaceName}});
2341 }
2342
2343 interface =
2344 interface->NextSiblingElement("interface");
2345 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002346 },
2347 processName, objectPath,
2348 "org.freedesktop.DBus.Introspectable", "Introspect");
2349 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002350 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002351 {
Ed Tanous7c091622019-05-23 11:42:36 -07002352 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2353 std::make_shared<bmcweb::AsyncResp>(res);
2354
Ed Tanous1abe55e2018-09-05 08:30:59 -07002355 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002356 [asyncResp, processName, objectPath,
2357 interfaceName](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002358 const std::string& introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002359 if (ec)
2360 {
2361 BMCWEB_LOG_ERROR
2362 << "Introspect call failed with error: "
2363 << ec.message()
2364 << " on process: " << processName
2365 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002366 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002367 }
Ed Tanous7c091622019-05-23 11:42:36 -07002368 tinyxml2::XMLDocument doc;
2369
2370 doc.Parse(introspect_xml.data(), introspect_xml.size());
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002371 tinyxml2::XMLNode* pRoot =
Ed Tanous7c091622019-05-23 11:42:36 -07002372 doc.FirstChildElement("node");
2373 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002374 {
Ed Tanous7c091622019-05-23 11:42:36 -07002375 BMCWEB_LOG_ERROR << "XML document failed to parse "
2376 << processName << " " << objectPath
2377 << "\n";
2378 asyncResp->res.result(boost::beast::http::status::
2379 internal_server_error);
2380 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002381 }
Ed Tanous7c091622019-05-23 11:42:36 -07002382 asyncResp->res.jsonValue = {
2383 {"status", "ok"},
2384 {"bus_name", processName},
2385 {"interface", interfaceName},
2386 {"object_path", objectPath}};
2387
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002388 nlohmann::json& methodsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002389 asyncResp->res.jsonValue["methods"];
2390 methodsArray = nlohmann::json::array();
2391
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002392 nlohmann::json& signalsArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002393 asyncResp->res.jsonValue["signals"];
2394 signalsArray = nlohmann::json::array();
2395
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002396 nlohmann::json& propertiesObj =
Ed Tanous7c091622019-05-23 11:42:36 -07002397 asyncResp->res.jsonValue["properties"];
2398 propertiesObj = nlohmann::json::object();
2399
2400 // if we know we're the only call, build the
2401 // json directly
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002402 tinyxml2::XMLElement* interface =
Ed Tanous7c091622019-05-23 11:42:36 -07002403 pRoot->FirstChildElement("interface");
2404 while (interface != nullptr)
2405 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002406 const char* ifaceName =
Ed Tanous7c091622019-05-23 11:42:36 -07002407 interface->Attribute("name");
2408
2409 if (ifaceName != nullptr &&
2410 ifaceName == interfaceName)
2411 {
2412 break;
2413 }
2414
2415 interface =
2416 interface->NextSiblingElement("interface");
2417 }
2418 if (interface == nullptr)
2419 {
2420 // if we got to the end of the list and
2421 // never found a match, throw 404
2422 asyncResp->res.result(
2423 boost::beast::http::status::not_found);
2424 return;
2425 }
2426
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002427 tinyxml2::XMLElement* methods =
Ed Tanous7c091622019-05-23 11:42:36 -07002428 interface->FirstChildElement("method");
2429 while (methods != nullptr)
2430 {
2431 nlohmann::json argsArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002432 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002433 methods->FirstChildElement("arg");
2434 while (arg != nullptr)
2435 {
2436 nlohmann::json thisArg;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002437 for (const char* fieldName :
2438 std::array<const char*, 3>{
Ed Tanous7c091622019-05-23 11:42:36 -07002439 "name", "direction", "type"})
2440 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002441 const char* fieldValue =
Ed Tanous7c091622019-05-23 11:42:36 -07002442 arg->Attribute(fieldName);
2443 if (fieldValue != nullptr)
2444 {
2445 thisArg[fieldName] = fieldValue;
2446 }
2447 }
2448 argsArray.push_back(std::move(thisArg));
2449 arg = arg->NextSiblingElement("arg");
2450 }
2451
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002452 const char* name = methods->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002453 if (name != nullptr)
2454 {
2455 methodsArray.push_back(
2456 {{"name", name},
2457 {"uri", "/bus/system/" + processName +
2458 objectPath + "/" +
2459 interfaceName + "/" + name},
2460 {"args", argsArray}});
2461 }
2462 methods = methods->NextSiblingElement("method");
2463 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002464 tinyxml2::XMLElement* signals =
Ed Tanous7c091622019-05-23 11:42:36 -07002465 interface->FirstChildElement("signal");
2466 while (signals != nullptr)
2467 {
2468 nlohmann::json argsArray = nlohmann::json::array();
2469
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002470 tinyxml2::XMLElement* arg =
Ed Tanous7c091622019-05-23 11:42:36 -07002471 signals->FirstChildElement("arg");
2472 while (arg != nullptr)
2473 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002474 const char* name = arg->Attribute("name");
2475 const char* type = arg->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002476 if (name != nullptr && type != nullptr)
2477 {
2478 argsArray.push_back({
2479 {"name", name},
2480 {"type", type},
2481 });
2482 }
2483 arg = arg->NextSiblingElement("arg");
2484 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002485 const char* name = signals->Attribute("name");
Ed Tanous7c091622019-05-23 11:42:36 -07002486 if (name != nullptr)
2487 {
2488 signalsArray.push_back(
2489 {{"name", name}, {"args", argsArray}});
2490 }
2491
2492 signals = signals->NextSiblingElement("signal");
2493 }
2494
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002495 tinyxml2::XMLElement* property =
Ed Tanous7c091622019-05-23 11:42:36 -07002496 interface->FirstChildElement("property");
2497 while (property != nullptr)
2498 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002499 const char* name = property->Attribute("name");
2500 const char* type = property->Attribute("type");
Ed Tanous7c091622019-05-23 11:42:36 -07002501 if (type != nullptr && name != nullptr)
2502 {
2503 sdbusplus::message::message m =
2504 crow::connections::systemBus
2505 ->new_method_call(processName.c_str(),
2506 objectPath.c_str(),
2507 "org.freedesktop."
2508 "DBus."
2509 "Properties",
2510 "Get");
2511 m.append(interfaceName, name);
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002512 nlohmann::json& propertyItem =
Ed Tanous7c091622019-05-23 11:42:36 -07002513 propertiesObj[name];
2514 crow::connections::systemBus->async_send(
2515 m, [&propertyItem, asyncResp](
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002516 boost::system::error_code& e,
2517 sdbusplus::message::message& msg) {
Ed Tanous271584a2019-07-09 16:24:22 -07002518 if (e)
Ed Tanous7c091622019-05-23 11:42:36 -07002519 {
2520 return;
2521 }
2522
Ed Tanous271584a2019-07-09 16:24:22 -07002523 convertDBusToJSON("v", msg,
2524 propertyItem);
Ed Tanous7c091622019-05-23 11:42:36 -07002525 });
2526 }
2527 property = property->NextSiblingElement("property");
2528 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002529 },
2530 processName, objectPath,
2531 "org.freedesktop.DBus.Introspectable", "Introspect");
2532 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002533 else
2534 {
2535 if (req.method() != "POST"_method)
2536 {
2537 res.result(boost::beast::http::status::not_found);
2538 res.end();
2539 return;
2540 }
2541
2542 nlohmann::json requestDbusData =
2543 nlohmann::json::parse(req.body, nullptr, false);
2544
2545 if (requestDbusData.is_discarded())
2546 {
2547 res.result(boost::beast::http::status::bad_request);
2548 res.end();
2549 return;
2550 }
2551 if (!requestDbusData.is_array())
2552 {
2553 res.result(boost::beast::http::status::bad_request);
2554 res.end();
2555 return;
2556 }
2557 auto transaction = std::make_shared<InProgressActionData>(res);
2558
2559 transaction->path = objectPath;
2560 transaction->methodName = methodName;
2561 transaction->arguments = std::move(requestDbusData);
2562
2563 findActionOnInterface(transaction, processName);
2564 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002565 });
2566}
2567} // namespace openbmc_mapper
2568} // namespace crow