blob: 0aa23e22cc9c9e7fb9f5727531c01859303de229 [file] [log] [blame]
James Feist5b4aa862018-08-16 14:07:01 -07001// Copyright (c) 2018 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Ed Tanousb9b2e0b2018-09-13 13:47:50 -070014
James Feist5b4aa862018-08-16 14:07:01 -070015#pragma once
Ed Tanous911ac312017-08-15 09:37:42 -070016#include <crow/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>
James Feist4418c7f2019-04-15 11:09:15 -070024#include <filesystem>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070025#include <fstream>
William A. Kennington III0a63b1c2018-10-18 13:37:19 -070026#include <sdbusplus/message/types.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
30namespace openbmc_mapper
31{
Ed Tanousba9f9a62017-10-11 16:40:35 -070032
Matt Spinler3ae4ba72018-12-05 14:01:22 -060033using GetSubTreeType = std::vector<
34 std::pair<std::string,
35 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
36
Matt Spinler2ae60092018-12-06 10:35:36 -060037const std::string notFoundMsg = "404 Not Found";
Matt Spinler6db06242018-12-11 11:21:22 -060038const std::string badReqMsg = "400 Bad Request";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060039const std::string methodNotAllowedMsg = "405 Method Not Allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060040const std::string forbiddenMsg = "403 Forbidden";
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -060041const std::string methodFailedMsg = "500 Method Call Failed";
Matt Spinler16caaee2019-01-15 11:40:34 -060042const std::string methodOutputFailedMsg = "500 Method Output Error";
Matt Spinler6db06242018-12-11 11:21:22 -060043
Matt Spinler2ae60092018-12-06 10:35:36 -060044const std::string notFoundDesc =
45 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
Matt Spinlerdc2f9f12018-12-06 13:53:53 -060046const std::string propNotFoundDesc = "The specified property cannot be found";
Matt Spinler6db06242018-12-11 11:21:22 -060047const std::string noJsonDesc = "No JSON object could be decoded";
48const std::string methodNotFoundDesc = "The specified method cannot be found";
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -060049const std::string methodNotAllowedDesc = "Method not allowed";
Matt Spinlerfbc19ea2018-12-11 14:03:42 -060050const std::string forbiddenPropDesc =
51 "The specified property cannot be created";
52const std::string forbiddenResDesc = "The specified resource cannot be created";
Matt Spinler2ae60092018-12-06 10:35:36 -060053
54void setErrorResponse(crow::Response &res, boost::beast::http::status result,
55 const std::string &desc, const std::string &msg)
56{
57 res.result(result);
58 res.jsonValue = {{"data", {{"description", desc}}},
59 {"message", msg},
60 {"status", "error"}};
61}
62
Ed Tanouse3cb5a32018-08-08 14:16:49 -070063void introspectObjects(const std::string &processName,
64 const std::string &objectPath,
65 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,
78 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());
93 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
94 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 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700101 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
102 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700104 const char *childPath = node->Attribute("name");
105 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
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600125void getPropertiesForEnumerate(const std::string &objectPath,
126 const std::string &service,
127 const std::string &interface,
128 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
129{
130 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
131 << service << " " << interface;
132
133 crow::connections::systemBus->async_method_call(
134 [asyncResp, objectPath, service,
135 interface](const boost::system::error_code ec,
136 const std::vector<
137 std::pair<std::string, dbus::utility::DbusVariantType>>
138 &propertiesList) {
139 if (ec)
140 {
141 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
142 << interface << " service " << service
143 << " failed with code " << ec;
144 return;
145 }
146
147 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
148 nlohmann::json &objectJson = dataJson[objectPath];
149 if (objectJson.is_null())
150 {
151 objectJson = nlohmann::json::object();
152 }
153
154 for (const auto &[name, value] : propertiesList)
155 {
156 nlohmann::json &propertyJson = objectJson[name];
Ed Tanousabf2add2019-01-22 16:40:12 -0800157 std::visit([&propertyJson](auto &&val) { propertyJson = val; },
158 value);
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600159 }
160 },
161 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
162 interface);
163}
164
165// Find any results that weren't picked up by ObjectManagers, to be
166// called after all ObjectManagers are searched for and called.
167void findRemainingObjectsForEnumerate(
168 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
169 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
170{
171 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
172 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
173
174 for (const auto &[path, interface_map] : *subtree)
175 {
176 if (path == objectPath)
177 {
178 // An enumerate does not return the target path's properties
179 continue;
180 }
181 if (dataJson.find(path) == dataJson.end())
182 {
183 for (const auto &[service, interfaces] : interface_map)
184 {
185 for (const auto &interface : interfaces)
186 {
187 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
188 {
189 getPropertiesForEnumerate(path, service, interface,
190 asyncResp);
191 }
192 }
193 }
194 }
195 }
196}
197
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600198struct InProgressEnumerateData
199{
200 InProgressEnumerateData(const std::string &objectPath,
201 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
202 objectPath(objectPath),
203 asyncResp(asyncResp)
204 {
205 }
206
207 ~InProgressEnumerateData()
208 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600209 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600210 }
211
212 const std::string objectPath;
213 std::shared_ptr<GetSubTreeType> subtree;
214 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
215};
216
217void getManagedObjectsForEnumerate(
218 const std::string &object_name, const std::string &object_manager_path,
219 const std::string &connection_name,
220 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700221{
Ed Tanous049a0512018-11-01 13:58:42 -0700222 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
223 << " object_manager_path " << object_manager_path
224 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700225 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600226 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700227 connection_name](const boost::system::error_code ec,
228 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 if (ec)
230 {
Ed Tanous049a0512018-11-01 13:58:42 -0700231 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600232 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700233 << " failed with code " << ec;
234 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700235 }
Ed Tanous64530012018-02-06 17:08:16 -0800236
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600237 nlohmann::json &dataJson =
238 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700239
240 for (const auto &objectPath : objects)
241 {
242 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700243 {
Ed Tanous049a0512018-11-01 13:58:42 -0700244 BMCWEB_LOG_DEBUG << "Reading object "
245 << objectPath.first.str;
246 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247 if (objectJson.is_null())
248 {
249 objectJson = nlohmann::json::object();
250 }
251 for (const auto &interface : objectPath.second)
252 {
253 for (const auto &property : interface.second)
254 {
255 nlohmann::json &propertyJson =
256 objectJson[property.first];
Ed Tanousabf2add2019-01-22 16:40:12 -0800257 std::visit([&propertyJson](
258 auto &&val) { propertyJson = val; },
259 property.second);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 }
261 }
262 }
Ed Tanous049a0512018-11-01 13:58:42 -0700263 for (const auto &interface : objectPath.second)
264 {
265 if (interface.first == "org.freedesktop.DBus.ObjectManager")
266 {
267 getManagedObjectsForEnumerate(
268 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600269 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700270 }
271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
273 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700274 connection_name, object_manager_path,
275 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
276}
277
278void findObjectManagerPathForEnumerate(
279 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600280 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700281{
Ed Tanous049a0512018-11-01 13:58:42 -0700282 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
283 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600285 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700286 const boost::system::error_code ec,
287 const boost::container::flat_map<
288 std::string, boost::container::flat_map<
289 std::string, std::vector<std::string>>>
290 &objects) {
291 if (ec)
292 {
Ed Tanous049a0512018-11-01 13:58:42 -0700293 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
294 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700295 return;
296 }
297
Ed Tanousf254ba72018-10-12 13:40:35 -0700298 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700299 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700300 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700301 {
302 if (connectionGroup.first == connection_name)
303 {
304 // Found the object manager path for this resource.
305 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700306 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600307 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700308 return;
309 }
310 }
311 }
312 },
313 "xyz.openbmc_project.ObjectMapper",
314 "/xyz/openbmc_project/object_mapper",
315 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
316 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700317}
Ed Tanous64530012018-02-06 17:08:16 -0800318
Ed Tanous7c091622019-05-23 11:42:36 -0700319// Uses GetObject to add the object info about the target /enumerate path to
320// the results of GetSubTree, as GetSubTree will not return info for the
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600321// target path, and then continues on enumerating the rest of the tree.
322void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
323{
324 using GetObjectType =
325 std::vector<std::pair<std::string, std::vector<std::string>>>;
326
327 crow::connections::systemBus->async_method_call(
328 [transaction](const boost::system::error_code ec,
329 const GetObjectType &objects) {
330 if (ec)
331 {
332 BMCWEB_LOG_ERROR << "GetObject for path "
333 << transaction->objectPath
334 << " failed with code " << ec;
335 return;
336 }
337
338 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
339 << " has " << objects.size() << " entries";
340 if (!objects.empty())
341 {
342 transaction->subtree->emplace_back(transaction->objectPath,
343 objects);
344 }
345
346 // Map indicating connection name, and the path where the object
347 // manager exists
348 boost::container::flat_map<std::string, std::string> connections;
349
350 for (const auto &object : *(transaction->subtree))
351 {
352 for (const auto &connection : object.second)
353 {
354 std::string &objectManagerPath =
355 connections[connection.first];
356 for (const auto &interface : connection.second)
357 {
358 BMCWEB_LOG_DEBUG << connection.first
359 << " has interface " << interface;
360 if (interface == "org.freedesktop.DBus.ObjectManager")
361 {
362 BMCWEB_LOG_DEBUG << "found object manager path "
363 << object.first;
364 objectManagerPath = object.first;
365 }
366 }
367 }
368 }
369 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
370
371 for (const auto &connection : connections)
372 {
Ed Tanous7c091622019-05-23 11:42:36 -0700373 // If we already know where the object manager is, we don't
374 // need to search for it, we can call directly in to
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600375 // getManagedObjects
376 if (!connection.second.empty())
377 {
378 getManagedObjectsForEnumerate(
379 transaction->objectPath, connection.second,
380 connection.first, transaction);
381 }
382 else
383 {
Ed Tanous7c091622019-05-23 11:42:36 -0700384 // otherwise we need to find the object manager path
385 // before we can continue
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600386 findObjectManagerPathForEnumerate(
387 transaction->objectPath, connection.first, transaction);
388 }
389 }
390 },
391 "xyz.openbmc_project.ObjectMapper",
392 "/xyz/openbmc_project/object_mapper",
393 "xyz.openbmc_project.ObjectMapper", "GetObject",
394 transaction->objectPath, std::array<const char *, 0>());
395}
Ed Tanous64530012018-02-06 17:08:16 -0800396
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700397// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700398struct InProgressActionData
399{
400 InProgressActionData(crow::Response &res) : res(res){};
401 ~InProgressActionData()
402 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600403 // Methods could have been called across different owners
404 // and interfaces, where some calls failed and some passed.
405 //
406 // The rules for this are:
407 // * if no method was called - error
408 // * if a method failed and none passed - error
409 // (converse: if at least one method passed - OK)
410 // * for the method output:
411 // * if output processing didn't fail, return the data
412
413 // Only deal with method returns if nothing failed earlier
414 if (res.result() == boost::beast::http::status::ok)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415 {
Matt Spinler16caaee2019-01-15 11:40:34 -0600416 if (!methodPassed)
417 {
Matt Spinler06b1b632019-06-18 16:09:25 -0500418 if (!methodFailed)
Matt Spinler16caaee2019-01-15 11:40:34 -0600419 {
420 setErrorResponse(res, boost::beast::http::status::not_found,
421 methodNotFoundDesc, notFoundMsg);
422 }
423 }
424 else
425 {
426 if (outputFailed)
427 {
428 setErrorResponse(
429 res, boost::beast::http::status::internal_server_error,
430 "Method output failure", methodOutputFailedMsg);
431 }
432 else
433 {
434 res.jsonValue = {{"status", "ok"},
435 {"message", "200 OK"},
436 {"data", methodResponse}};
437 }
438 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 }
Matt Spinler16caaee2019-01-15 11:40:34 -0600440
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700442 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700443
Matt Spinler6db06242018-12-11 11:21:22 -0600444 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 {
Matt Spinlerc0eb9bd2019-01-09 15:22:30 -0600446 setErrorResponse(res, boost::beast::http::status::bad_request, desc,
447 badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
449 crow::Response &res;
450 std::string path;
451 std::string methodName;
Matt Spinlerde818812018-12-11 16:39:20 -0600452 std::string interfaceName;
Matt Spinler16caaee2019-01-15 11:40:34 -0600453 bool methodPassed = false;
454 bool methodFailed = false;
455 bool outputFailed = false;
Matt Spinler39a4e392019-01-15 11:53:13 -0600456 bool convertedToArray = false;
Matt Spinler16caaee2019-01-15 11:40:34 -0600457 nlohmann::json methodResponse;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700458 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700459};
460
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461std::vector<std::string> dbusArgSplit(const std::string &string)
462{
463 std::vector<std::string> ret;
464 if (string.empty())
465 {
466 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700467 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 ret.push_back("");
469 int containerDepth = 0;
470
471 for (std::string::const_iterator character = string.begin();
472 character != string.end(); character++)
473 {
474 ret.back() += *character;
475 switch (*character)
476 {
477 case ('a'):
478 break;
479 case ('('):
480 case ('{'):
481 containerDepth++;
482 break;
483 case ('}'):
484 case (')'):
485 containerDepth--;
486 if (containerDepth == 0)
487 {
488 if (character + 1 != string.end())
489 {
490 ret.push_back("");
491 }
492 }
493 break;
494 default:
495 if (containerDepth == 0)
496 {
497 if (character + 1 != string.end())
498 {
499 ret.push_back("");
500 }
501 }
502 break;
503 }
504 }
Matt Spinler4ae611d2019-01-11 15:37:06 -0600505
506 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700507}
508
Ed Tanousd76323e2018-08-07 14:35:40 -0700509int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 const nlohmann::json &input_json)
511{
512 int r = 0;
513 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
514 << " to type: " << arg_type;
515 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700516
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 // Assume a single object for now.
518 const nlohmann::json *j = &input_json;
519 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700520
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700521 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700522 {
523 // If we are decoding multiple objects, grab the pointer to the
524 // iterator, and increment it for the next loop
525 if (argTypes.size() > 1)
526 {
527 if (jIt == input_json.end())
528 {
529 return -2;
530 }
531 j = &*jIt;
532 jIt++;
533 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700534 const int64_t *intValue = j->get_ptr<const int64_t *>();
535 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
536 const std::string *stringValue = j->get_ptr<const std::string *>();
537 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700538 const bool *b = j->get_ptr<const bool *>();
539 int64_t v = 0;
540 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700541
Ed Tanous1abe55e2018-09-05 08:30:59 -0700542 // Do some basic type conversions that make sense. uint can be
543 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700544 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700545 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700546 v = static_cast<int64_t>(*uintValue);
547 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700549 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700551 d = static_cast<double>(*uintValue);
552 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700553 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700554 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700555 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700556 d = static_cast<double>(*intValue);
557 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700558 }
559
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700560 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700561 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700562 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700563 {
564 return -1;
565 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000566 r = sd_bus_message_append_basic(m, argCode[0],
567 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700568 if (r < 0)
569 {
570 return r;
571 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700572 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700573 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700574 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700575 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700576 {
577 return -1;
578 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700579 int32_t i = static_cast<int32_t>(*intValue);
580 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 if (r < 0)
582 {
583 return r;
584 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700585 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700586 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
588 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700589 int boolInt = false;
590 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700591 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700593 }
594 else if (b != nullptr)
595 {
Matt Spinlera2f02632019-01-18 10:15:35 -0600596 boolInt = *b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700597 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700598 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700599 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700600 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700601 }
602 else
603 {
604 return -1;
605 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 if (r < 0)
608 {
609 return r;
610 }
611 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700612 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700613 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700614 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 {
616 return -1;
617 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700618 int16_t n = static_cast<int16_t>(*intValue);
619 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 if (r < 0)
621 {
622 return r;
623 }
624 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700627 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 {
629 return -1;
630 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700631 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632 if (r < 0)
633 {
634 return r;
635 }
636 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700637 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700638 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700639 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640 {
641 return -1;
642 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700643 uint8_t y = static_cast<uint8_t>(*uintValue);
644 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700645 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700646 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700648 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 {
650 return -1;
651 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700652 uint16_t q = static_cast<uint16_t>(*uintValue);
653 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700654 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700655 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700656 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700657 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 {
659 return -1;
660 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700661 uint32_t u = static_cast<uint32_t>(*uintValue);
662 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700663 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700664 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700666 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
668 return -1;
669 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700670 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700671 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700673 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700674 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700675 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700676 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700677 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700678 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700679 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700680 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700681 if (r < 0)
682 {
683 return r;
684 }
685
686 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
687 ++it)
688 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700689 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700690 if (r < 0)
691 {
692 return r;
693 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700694 }
695 sd_bus_message_close_container(m);
696 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700697 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700699 std::string containedType = argCode.substr(1);
700 BMCWEB_LOG_DEBUG << "variant type: " << argCode
701 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700702 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700703 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700704 if (r < 0)
705 {
706 return r;
707 }
708
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700709 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700710 if (r < 0)
711 {
712 return r;
713 }
714
715 r = sd_bus_message_close_container(m);
716 if (r < 0)
717 {
718 return r;
719 }
720 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700721 else if (boost::starts_with(argCode, "(") &&
722 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700723 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700724 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700725 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700726 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800727 if (r < 0)
728 {
729 return r;
730 }
731
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 nlohmann::json::const_iterator it = j->begin();
Ed Tanousb01bf292019-03-25 19:25:26 +0000733 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700734 {
735 if (it == j->end())
736 {
737 return -1;
738 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000739 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 if (r < 0)
741 {
742 return r;
743 }
744 it++;
745 }
746 r = sd_bus_message_close_container(m);
747 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700748 else if (boost::starts_with(argCode, "{") &&
749 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700751 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700752 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700753 containedType.c_str());
Ed Tanousf1eebf02019-03-04 15:57:09 -0800754 if (r < 0)
755 {
756 return r;
757 }
758
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 if (codes.size() != 2)
761 {
762 return -1;
763 }
764 const std::string &key_type = codes[0];
765 const std::string &value_type = codes[1];
766 for (auto it : j->items())
767 {
768 r = convertJsonToDbus(m, key_type, it.key());
769 if (r < 0)
770 {
771 return r;
772 }
773
774 r = convertJsonToDbus(m, value_type, it.value());
775 if (r < 0)
776 {
777 return r;
778 }
779 }
780 r = sd_bus_message_close_container(m);
781 }
782 else
783 {
784 return -2;
785 }
786 if (r < 0)
787 {
788 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700789 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700790
Ed Tanous1abe55e2018-09-05 08:30:59 -0700791 if (argTypes.size() > 1)
792 {
793 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700794 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700795 }
Matt Spinler127ea542019-01-14 11:04:28 -0600796
797 return r;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700798}
799
Matt Spinlerd22a7132019-01-14 12:14:30 -0600800template <typename T>
801int readMessageItem(const std::string &typeCode, sdbusplus::message::message &m,
802 nlohmann::json &data)
803{
804 T value;
805
806 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value);
807 if (r < 0)
808 {
809 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode
810 << " failed!";
811 return r;
812 }
813
814 data = value;
815 return 0;
816}
817
Matt Spinler16caaee2019-01-15 11:40:34 -0600818int convertDBusToJSON(const std::string &returnType,
Matt Spinler6df8f992019-01-14 12:47:47 -0600819 sdbusplus::message::message &m, nlohmann::json &response);
820
821int readDictEntryFromMessage(const std::string &typeCode,
822 sdbusplus::message::message &m,
823 nlohmann::json &object)
824{
825 std::vector<std::string> types = dbusArgSplit(typeCode);
826 if (types.size() != 2)
827 {
828 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: "
829 << types.size();
830 return -1;
831 }
832
833 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY,
834 typeCode.c_str());
835 if (r < 0)
836 {
837 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r;
838 return r;
839 }
840
841 nlohmann::json key;
842 r = convertDBusToJSON(types[0], m, key);
843 if (r < 0)
844 {
845 return r;
846 }
847
848 const std::string *keyPtr = key.get_ptr<const std::string *>();
849 if (keyPtr == nullptr)
850 {
851 // json doesn't support non-string keys. If we hit this condition,
852 // convert the result to a string so we can proceed
853 key = key.dump();
854 keyPtr = key.get_ptr<const std::string *>();
Ed Tanous7c091622019-05-23 11:42:36 -0700855 // in theory this can't fail now, but lets be paranoid about it
856 // anyway
Matt Spinler6df8f992019-01-14 12:47:47 -0600857 if (keyPtr == nullptr)
858 {
859 return -1;
860 }
861 }
862 nlohmann::json &value = object[*keyPtr];
863
864 r = convertDBusToJSON(types[1], m, value);
865 if (r < 0)
866 {
867 return r;
868 }
869
870 r = sd_bus_message_exit_container(m.get());
871 if (r < 0)
872 {
873 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
874 return r;
875 }
876
877 return 0;
878}
879
880int readArrayFromMessage(const std::string &typeCode,
881 sdbusplus::message::message &m, nlohmann::json &data)
882{
883 if (typeCode.size() < 2)
884 {
885 BMCWEB_LOG_ERROR << "Type code " << typeCode
886 << " too small for an array";
887 return -1;
888 }
889
890 std::string containedType = typeCode.substr(1);
891
892 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY,
893 containedType.c_str());
894 if (r < 0)
895 {
896 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
897 << r;
898 return r;
899 }
900
901 bool dict = boost::starts_with(containedType, "{") &&
902 boost::ends_with(containedType, "}");
903
904 if (dict)
905 {
906 // Remove the { }
907 containedType = containedType.substr(1, containedType.size() - 2);
908 data = nlohmann::json::object();
909 }
910 else
911 {
912 data = nlohmann::json::array();
913 }
914
915 while (true)
916 {
917 r = sd_bus_message_at_end(m.get(), false);
918 if (r < 0)
919 {
920 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed";
921 return r;
922 }
923
924 if (r > 0)
925 {
926 break;
927 }
928
929 // Dictionaries are only ever seen in an array
930 if (dict)
931 {
932 r = readDictEntryFromMessage(containedType, m, data);
933 if (r < 0)
934 {
935 return r;
936 }
937 }
938 else
939 {
940 data.push_back(nlohmann::json());
941
942 r = convertDBusToJSON(containedType, m, data.back());
943 if (r < 0)
944 {
945 return r;
946 }
947 }
948 }
949
950 r = sd_bus_message_exit_container(m.get());
951 if (r < 0)
952 {
953 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
954 return r;
955 }
956
957 return 0;
958}
959
Matt Spinler75c6c672019-01-14 13:01:46 -0600960int readStructFromMessage(const std::string &typeCode,
961 sdbusplus::message::message &m, nlohmann::json &data)
962{
963 if (typeCode.size() < 3)
964 {
965 BMCWEB_LOG_ERROR << "Type code " << typeCode
966 << " too small for a struct";
967 return -1;
968 }
969
970 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2);
971 std::vector<std::string> types = dbusArgSplit(containedTypes);
972
973 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT,
974 containedTypes.c_str());
975 if (r < 0)
976 {
977 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
978 << r;
979 return r;
980 }
981
982 for (const std::string &type : types)
983 {
984 data.push_back(nlohmann::json());
985 r = convertDBusToJSON(type, m, data.back());
986 if (r < 0)
987 {
988 return r;
989 }
990 }
991
992 r = sd_bus_message_exit_container(m.get());
993 if (r < 0)
994 {
995 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed";
996 return r;
997 }
998 return 0;
999}
1000
Matt Spinler89c19702019-01-14 13:13:00 -06001001int readVariantFromMessage(sdbusplus::message::message &m, nlohmann::json &data)
1002{
1003 const char *containerType;
1004 int r = sd_bus_message_peek_type(m.get(), NULL, &containerType);
1005 if (r < 0)
1006 {
1007 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed";
1008 return r;
1009 }
1010
1011 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT,
1012 containerType);
1013 if (r < 0)
1014 {
1015 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc "
1016 << r;
1017 return r;
1018 }
1019
1020 r = convertDBusToJSON(containerType, m, data);
1021 if (r < 0)
1022 {
1023 return r;
1024 }
1025
1026 r = sd_bus_message_exit_container(m.get());
1027 if (r < 0)
1028 {
1029 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed";
1030 return r;
1031 }
1032
1033 return 0;
1034}
1035
Matt Spinler6df8f992019-01-14 12:47:47 -06001036int convertDBusToJSON(const std::string &returnType,
Matt Spinler16caaee2019-01-15 11:40:34 -06001037 sdbusplus::message::message &m, nlohmann::json &response)
1038{
Matt Spinlerd22a7132019-01-14 12:14:30 -06001039 int r = 0;
1040 const std::vector<std::string> returnTypes = dbusArgSplit(returnType);
1041
Matt Spinlerd22a7132019-01-14 12:14:30 -06001042 for (const std::string &typeCode : returnTypes)
1043 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001044 nlohmann::json *thisElement = &response;
1045 if (returnTypes.size() > 1)
Matt Spinlerd22a7132019-01-14 12:14:30 -06001046 {
1047 response.push_back(nlohmann::json{});
Matt Spinlerf39420c2019-01-30 12:57:18 -06001048 thisElement = &response.back();
Matt Spinlerd22a7132019-01-14 12:14:30 -06001049 }
1050
1051 if (typeCode == "s")
1052 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001053 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001054 if (r < 0)
1055 {
1056 return r;
1057 }
1058 }
1059 else if (typeCode == "g")
1060 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001061 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001062 if (r < 0)
1063 {
1064 return r;
1065 }
1066 }
1067 else if (typeCode == "o")
1068 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001069 r = readMessageItem<char *>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001070 if (r < 0)
1071 {
1072 return r;
1073 }
1074 }
1075 else if (typeCode == "b")
1076 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001077 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001078 if (r < 0)
1079 {
1080 return r;
1081 }
1082
Matt Spinlerf39420c2019-01-30 12:57:18 -06001083 *thisElement = static_cast<bool>(thisElement->get<int>());
Matt Spinlerd22a7132019-01-14 12:14:30 -06001084 }
1085 else if (typeCode == "u")
1086 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001087 r = readMessageItem<uint32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001088 if (r < 0)
1089 {
1090 return r;
1091 }
1092 }
1093 else if (typeCode == "i")
1094 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001095 r = readMessageItem<int32_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001096 if (r < 0)
1097 {
1098 return r;
1099 }
1100 }
1101 else if (typeCode == "x")
1102 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001103 r = readMessageItem<int64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001104 if (r < 0)
1105 {
1106 return r;
1107 }
1108 }
1109 else if (typeCode == "t")
1110 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001111 r = readMessageItem<uint64_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001112 if (r < 0)
1113 {
1114 return r;
1115 }
1116 }
1117 else if (typeCode == "n")
1118 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001119 r = readMessageItem<int16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001120 if (r < 0)
1121 {
1122 return r;
1123 }
1124 }
1125 else if (typeCode == "q")
1126 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001127 r = readMessageItem<uint16_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001128 if (r < 0)
1129 {
1130 return r;
1131 }
1132 }
1133 else if (typeCode == "y")
1134 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001135 r = readMessageItem<uint8_t>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001136 if (r < 0)
1137 {
1138 return r;
1139 }
1140 }
1141 else if (typeCode == "d")
1142 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001143 r = readMessageItem<double>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001144 if (r < 0)
1145 {
1146 return r;
1147 }
1148 }
1149 else if (typeCode == "h")
1150 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001151 r = readMessageItem<int>(typeCode, m, *thisElement);
Matt Spinlerd22a7132019-01-14 12:14:30 -06001152 if (r < 0)
1153 {
1154 return r;
1155 }
1156 }
Matt Spinler6df8f992019-01-14 12:47:47 -06001157 else if (boost::starts_with(typeCode, "a"))
1158 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001159 r = readArrayFromMessage(typeCode, m, *thisElement);
Matt Spinler6df8f992019-01-14 12:47:47 -06001160 if (r < 0)
1161 {
1162 return r;
1163 }
1164 }
Matt Spinler75c6c672019-01-14 13:01:46 -06001165 else if (boost::starts_with(typeCode, "(") &&
1166 boost::ends_with(typeCode, ")"))
1167 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001168 r = readStructFromMessage(typeCode, m, *thisElement);
Matt Spinler75c6c672019-01-14 13:01:46 -06001169 if (r < 0)
1170 {
1171 return r;
1172 }
1173 }
Matt Spinler89c19702019-01-14 13:13:00 -06001174 else if (boost::starts_with(typeCode, "v"))
1175 {
Matt Spinlerf39420c2019-01-30 12:57:18 -06001176 r = readVariantFromMessage(m, *thisElement);
Matt Spinler89c19702019-01-14 13:13:00 -06001177 if (r < 0)
1178 {
1179 return r;
1180 }
1181 }
Matt Spinlerd22a7132019-01-14 12:14:30 -06001182 else
1183 {
Matt Spinlerd22a7132019-01-14 12:14:30 -06001184 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode;
1185 return -2;
1186 }
1187 }
1188
Matt Spinler16caaee2019-01-15 11:40:34 -06001189 return 0;
1190}
1191
1192void handleMethodResponse(std::shared_ptr<InProgressActionData> transaction,
1193 sdbusplus::message::message &m,
1194 const std::string &returnType)
1195{
Matt Spinler39a4e392019-01-15 11:53:13 -06001196 nlohmann::json data;
1197
1198 int r = convertDBusToJSON(returnType, m, data);
1199 if (r < 0)
1200 {
1201 transaction->outputFailed = true;
1202 return;
1203 }
1204
1205 if (data.is_null())
1206 {
1207 return;
1208 }
1209
1210 if (transaction->methodResponse.is_null())
1211 {
1212 transaction->methodResponse = std::move(data);
1213 return;
1214 }
1215
1216 // If they're both dictionaries or arrays, merge into one.
1217 // Otherwise, make the results an array with every result
1218 // an entry. Could also just fail in that case, but it
1219 // seems better to get the data back somehow.
1220
1221 if (transaction->methodResponse.is_object() && data.is_object())
1222 {
1223 for (const auto &obj : data.items())
1224 {
1225 // Note: Will overwrite the data for a duplicate key
1226 transaction->methodResponse.emplace(obj.key(),
1227 std::move(obj.value()));
1228 }
1229 return;
1230 }
1231
1232 if (transaction->methodResponse.is_array() && data.is_array())
1233 {
1234 for (auto &obj : data)
1235 {
1236 transaction->methodResponse.push_back(std::move(obj));
1237 }
1238 return;
1239 }
1240
1241 if (!transaction->convertedToArray)
1242 {
1243 // They are different types. May as well turn them into an array
1244 nlohmann::json j = std::move(transaction->methodResponse);
1245 transaction->methodResponse = nlohmann::json::array();
1246 transaction->methodResponse.push_back(std::move(j));
1247 transaction->methodResponse.push_back(std::move(data));
1248 transaction->convertedToArray = true;
1249 }
1250 else
1251 {
1252 transaction->methodResponse.push_back(std::move(data));
1253 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001254}
1255
Ed Tanousd76323e2018-08-07 14:35:40 -07001256void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001257 const std::string &connectionName)
1258{
1259 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
1260 << connectionName;
1261 crow::connections::systemBus->async_method_call(
1262 [transaction, connectionName{std::string(connectionName)}](
1263 const boost::system::error_code ec,
1264 const std::string &introspect_xml) {
1265 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
1266 if (ec)
1267 {
1268 BMCWEB_LOG_ERROR
1269 << "Introspect call failed with error: " << ec.message()
1270 << " on process: " << connectionName << "\n";
Matt Spinler318bd892019-01-15 09:59:20 -06001271 return;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001272 }
Matt Spinler318bd892019-01-15 09:59:20 -06001273 tinyxml2::XMLDocument doc;
1274
1275 doc.Parse(introspect_xml.data(), introspect_xml.size());
1276 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
1277 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001278 {
Matt Spinler318bd892019-01-15 09:59:20 -06001279 BMCWEB_LOG_ERROR << "XML document failed to parse "
1280 << connectionName << "\n";
1281 return;
1282 }
1283 tinyxml2::XMLElement *interfaceNode =
1284 pRoot->FirstChildElement("interface");
1285 while (interfaceNode != nullptr)
1286 {
1287 const char *thisInterfaceName =
1288 interfaceNode->Attribute("name");
1289 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290 {
Matt Spinler318bd892019-01-15 09:59:20 -06001291 if (!transaction->interfaceName.empty() &&
1292 (transaction->interfaceName != thisInterfaceName))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001293 {
Matt Spinler318bd892019-01-15 09:59:20 -06001294 interfaceNode =
1295 interfaceNode->NextSiblingElement("interface");
1296 continue;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001297 }
Matt Spinler318bd892019-01-15 09:59:20 -06001298
1299 tinyxml2::XMLElement *methodNode =
1300 interfaceNode->FirstChildElement("method");
1301 while (methodNode != nullptr)
1302 {
1303 const char *thisMethodName =
1304 methodNode->Attribute("name");
1305 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName;
1306 if (thisMethodName != nullptr &&
1307 thisMethodName == transaction->methodName)
1308 {
1309 BMCWEB_LOG_DEBUG
1310 << "Found method named " << thisMethodName
1311 << " on interface " << thisInterfaceName;
1312 sdbusplus::message::message m =
1313 crow::connections::systemBus->new_method_call(
1314 connectionName.c_str(),
1315 transaction->path.c_str(),
1316 thisInterfaceName,
1317 transaction->methodName.c_str());
1318
1319 tinyxml2::XMLElement *argumentNode =
1320 methodNode->FirstChildElement("arg");
1321
Matt Spinler16caaee2019-01-15 11:40:34 -06001322 std::string returnType;
1323
1324 // Find the output type
1325 while (argumentNode != nullptr)
1326 {
1327 const char *argDirection =
1328 argumentNode->Attribute("direction");
1329 const char *argType =
1330 argumentNode->Attribute("type");
1331 if (argDirection != nullptr &&
1332 argType != nullptr &&
1333 std::string(argDirection) == "out")
1334 {
1335 returnType = argType;
1336 break;
1337 }
1338 argumentNode =
1339 argumentNode->NextSiblingElement("arg");
1340 }
1341
Matt Spinler318bd892019-01-15 09:59:20 -06001342 nlohmann::json::const_iterator argIt =
1343 transaction->arguments.begin();
1344
Matt Spinler16caaee2019-01-15 11:40:34 -06001345 argumentNode = methodNode->FirstChildElement("arg");
1346
Matt Spinler318bd892019-01-15 09:59:20 -06001347 while (argumentNode != nullptr)
1348 {
1349 const char *argDirection =
1350 argumentNode->Attribute("direction");
1351 const char *argType =
1352 argumentNode->Attribute("type");
1353 if (argDirection != nullptr &&
1354 argType != nullptr &&
1355 std::string(argDirection) == "in")
1356 {
1357 if (argIt == transaction->arguments.end())
1358 {
1359 transaction->setErrorStatus(
1360 "Invalid method args");
1361 return;
1362 }
1363 if (convertJsonToDbus(m.get(),
1364 std::string(argType),
1365 *argIt) < 0)
1366 {
1367 transaction->setErrorStatus(
1368 "Invalid method arg type");
1369 return;
1370 }
1371
1372 argIt++;
1373 }
1374 argumentNode =
1375 argumentNode->NextSiblingElement("arg");
1376 }
1377
1378 crow::connections::systemBus->async_send(
Matt Spinler16caaee2019-01-15 11:40:34 -06001379 m, [transaction, returnType](
1380 boost::system::error_code ec,
1381 sdbusplus::message::message &m) {
Matt Spinler318bd892019-01-15 09:59:20 -06001382 if (ec)
1383 {
Matt Spinler16caaee2019-01-15 11:40:34 -06001384 transaction->methodFailed = true;
Matt Spinler06b1b632019-06-18 16:09:25 -05001385 const sd_bus_error *e = m.get_error();
1386
1387 if (e)
1388 {
1389 setErrorResponse(
1390 transaction->res,
1391 boost::beast::http::status::
1392 bad_request,
1393 e->name, e->message);
1394 }
1395 else
1396 {
1397 setErrorResponse(
1398 transaction->res,
1399 boost::beast::http::status::
1400 bad_request,
1401 "Method call failed",
1402 methodFailedMsg);
1403 }
Matt Spinler318bd892019-01-15 09:59:20 -06001404 return;
1405 }
Matt Spinler16caaee2019-01-15 11:40:34 -06001406 else
1407 {
1408 transaction->methodPassed = true;
1409 }
1410
1411 handleMethodResponse(transaction, m,
1412 returnType);
Matt Spinler318bd892019-01-15 09:59:20 -06001413 });
1414 break;
1415 }
1416 methodNode = methodNode->NextSiblingElement("method");
1417 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001418 }
Matt Spinler318bd892019-01-15 09:59:20 -06001419 interfaceNode = interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001420 }
1421 },
1422 connectionName, transaction->path,
1423 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001424}
1425
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001426void handleAction(const crow::Request &req, crow::Response &res,
1427 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001428{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001429 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
1430 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001431 nlohmann::json requestDbusData =
1432 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001433
Ed Tanous1abe55e2018-09-05 08:30:59 -07001434 if (requestDbusData.is_discarded())
1435 {
Matt Spinler6db06242018-12-11 11:21:22 -06001436 setErrorResponse(res, boost::beast::http::status::bad_request,
1437 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001438 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001439 return;
1440 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001441 nlohmann::json::iterator data = requestDbusData.find("data");
1442 if (data == requestDbusData.end())
1443 {
Matt Spinler6db06242018-12-11 11:21:22 -06001444 setErrorResponse(res, boost::beast::http::status::bad_request,
1445 noJsonDesc, badReqMsg);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001446 res.end();
1447 return;
1448 }
1449
1450 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001451 {
Matt Spinler6db06242018-12-11 11:21:22 -06001452 setErrorResponse(res, boost::beast::http::status::bad_request,
1453 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001454 res.end();
1455 return;
1456 }
1457 auto transaction = std::make_shared<InProgressActionData>(res);
1458
1459 transaction->path = objectPath;
1460 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001461 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001462 crow::connections::systemBus->async_method_call(
1463 [transaction](
1464 const boost::system::error_code ec,
1465 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001466 &interfaceNames) {
1467 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001468 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001469 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler6db06242018-12-11 11:21:22 -06001470 setErrorResponse(transaction->res,
1471 boost::beast::http::status::not_found,
1472 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001473 return;
1474 }
1475
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001476 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
1477 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -07001478
1479 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001480 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001481 {
1482 findActionOnInterface(transaction, object.first);
1483 }
1484 },
1485 "xyz.openbmc_project.ObjectMapper",
1486 "/xyz/openbmc_project/object_mapper",
1487 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1488 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001489}
1490
Matt Spinlerde818812018-12-11 16:39:20 -06001491void handleDelete(const crow::Request &req, crow::Response &res,
1492 const std::string &objectPath)
1493{
1494 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath;
1495
1496 crow::connections::systemBus->async_method_call(
1497 [&res, objectPath](
1498 const boost::system::error_code ec,
1499 const std::vector<std::pair<std::string, std::vector<std::string>>>
1500 &interfaceNames) {
1501 if (ec || interfaceNames.size() <= 0)
1502 {
1503 BMCWEB_LOG_ERROR << "Can't find object";
Matt Spinler62d2e8b2019-01-22 13:45:51 -06001504 setErrorResponse(res,
1505 boost::beast::http::status::method_not_allowed,
1506 methodNotAllowedDesc, methodNotAllowedMsg);
Matt Spinlerde818812018-12-11 16:39:20 -06001507 res.end();
1508 return;
1509 }
1510
1511 auto transaction = std::make_shared<InProgressActionData>(res);
1512 transaction->path = objectPath;
1513 transaction->methodName = "Delete";
1514 transaction->interfaceName = "xyz.openbmc_project.Object.Delete";
1515
1516 for (const std::pair<std::string, std::vector<std::string>>
1517 &object : interfaceNames)
1518 {
1519 findActionOnInterface(transaction, object.first);
1520 }
1521 },
1522 "xyz.openbmc_project.ObjectMapper",
1523 "/xyz/openbmc_project/object_mapper",
1524 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
1525 std::array<const char *, 0>());
1526}
1527
Ed Tanousf839dfe2018-11-12 11:11:15 -08001528void handleList(crow::Response &res, const std::string &objectPath,
1529 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001530{
1531 crow::connections::systemBus->async_method_call(
1532 [&res](const boost::system::error_code ec,
1533 std::vector<std::string> &objectPaths) {
1534 if (ec)
1535 {
Matt Spinlerd6091dd2018-12-06 14:08:27 -06001536 setErrorResponse(res, boost::beast::http::status::not_found,
1537 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001538 }
1539 else
1540 {
1541 res.jsonValue = {{"status", "ok"},
1542 {"message", "200 OK"},
1543 {"data", std::move(objectPaths)}};
1544 }
1545 res.end();
1546 },
1547 "xyz.openbmc_project.ObjectMapper",
1548 "/xyz/openbmc_project/object_mapper",
1549 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -08001550 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001551}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001552
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001553void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001554{
Ed Tanous049a0512018-11-01 13:58:42 -07001555 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
1556 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
1557
1558 asyncResp->res.jsonValue = {{"message", "200 OK"},
1559 {"status", "ok"},
1560 {"data", nlohmann::json::object()}};
1561
Ed Tanous1abe55e2018-09-05 08:30:59 -07001562 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001563 [objectPath, asyncResp](const boost::system::error_code ec,
1564 GetSubTreeType &object_names) {
1565 auto transaction = std::make_shared<InProgressEnumerateData>(
1566 objectPath, asyncResp);
1567
1568 transaction->subtree =
1569 std::make_shared<GetSubTreeType>(std::move(object_names));
1570
Ed Tanous1abe55e2018-09-05 08:30:59 -07001571 if (ec)
1572 {
Matt Spinler2ae60092018-12-06 10:35:36 -06001573 BMCWEB_LOG_ERROR << "GetSubTree failed on "
1574 << transaction->objectPath;
1575 setErrorResponse(transaction->asyncResp->res,
1576 boost::beast::http::status::not_found,
1577 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001578 return;
1579 }
Ed Tanous64530012018-02-06 17:08:16 -08001580
Matt Spinler3ae4ba72018-12-05 14:01:22 -06001581 // Add the data for the path passed in to the results
1582 // as if GetSubTree returned it, and continue on enumerating
1583 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584 },
1585 "xyz.openbmc_project.ObjectMapper",
1586 "/xyz/openbmc_project/object_mapper",
1587 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -07001588 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -08001589}
Ed Tanous911ac312017-08-15 09:37:42 -07001590
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001591void handleGet(crow::Response &res, std::string &objectPath,
1592 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001593{
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001594 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
1595 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001596 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -07001597
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598 std::shared_ptr<std::string> path =
1599 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -07001600
Ed Tanous1abe55e2018-09-05 08:30:59 -07001601 using GetObjectType =
1602 std::vector<std::pair<std::string, std::vector<std::string>>>;
1603 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001604 [&res, path, propertyName](const boost::system::error_code ec,
1605 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001606 if (ec || object_names.size() <= 0)
1607 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001608 setErrorResponse(res, boost::beast::http::status::not_found,
1609 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001610 res.end();
1611 return;
1612 }
1613 std::shared_ptr<nlohmann::json> response =
1614 std::make_shared<nlohmann::json>(nlohmann::json::object());
Ed Tanous7c091622019-05-23 11:42:36 -07001615 // The mapper should never give us an empty interface names
1616 // list, but check anyway
Ed Tanous1abe55e2018-09-05 08:30:59 -07001617 for (const std::pair<std::string, std::vector<std::string>>
1618 connection : object_names)
1619 {
1620 const std::vector<std::string> &interfaceNames =
1621 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001622
Ed Tanous1abe55e2018-09-05 08:30:59 -07001623 if (interfaceNames.size() <= 0)
1624 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001625 setErrorResponse(res, boost::beast::http::status::not_found,
1626 notFoundDesc, notFoundMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001627 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001628 return;
1629 }
1630
1631 for (const std::string &interface : interfaceNames)
1632 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001633 sdbusplus::message::message m =
1634 crow::connections::systemBus->new_method_call(
1635 connection.first.c_str(), path->c_str(),
1636 "org.freedesktop.DBus.Properties", "GetAll");
1637 m.append(interface);
1638 crow::connections::systemBus->async_send(
1639 m, [&res, response,
1640 propertyName](const boost::system::error_code ec,
1641 sdbusplus::message::message &msg) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001642 if (ec)
1643 {
1644 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1645 << ec;
1646 }
1647 else
1648 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001649 nlohmann::json properties;
1650 int r =
1651 convertDBusToJSON("a{sv}", msg, properties);
1652 if (r < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 {
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001654 BMCWEB_LOG_ERROR
1655 << "convertDBusToJSON failed";
1656 }
1657 else
1658 {
1659 for (auto &prop : properties.items())
1660 {
Ed Tanous7c091622019-05-23 11:42:36 -07001661 // if property name is empty, or
1662 // matches our search query, add it
1663 // to the response json
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001665 if (propertyName->empty())
1666 {
1667 (*response)[prop.key()] =
1668 std::move(prop.value());
1669 }
1670 else if (prop.key() == *propertyName)
1671 {
1672 *response = std::move(prop.value());
1673 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001674 }
1675 }
1676 }
1677 if (response.use_count() == 1)
1678 {
Matt Spinlerdc2f9f12018-12-06 13:53:53 -06001679 if (!propertyName->empty() && response->empty())
1680 {
1681 setErrorResponse(
1682 res,
1683 boost::beast::http::status::not_found,
1684 propNotFoundDesc, notFoundMsg);
1685 }
1686 else
1687 {
1688 res.jsonValue = {{"status", "ok"},
1689 {"message", "200 OK"},
1690 {"data", *response}};
1691 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001692 res.end();
1693 }
Matt Spinlerfe7e97d2019-01-30 15:33:21 -06001694 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001695 }
1696 }
1697 },
1698 "xyz.openbmc_project.ObjectMapper",
1699 "/xyz/openbmc_project/object_mapper",
1700 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1701 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001702}
1703
Ed Tanous1abe55e2018-09-05 08:30:59 -07001704struct AsyncPutRequest
1705{
1706 AsyncPutRequest(crow::Response &res) : res(res)
1707 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001708 }
1709 ~AsyncPutRequest()
1710 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001711 if (res.jsonValue.empty())
1712 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001713 setErrorResponse(res, boost::beast::http::status::forbidden,
1714 forbiddenMsg, forbiddenPropDesc);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001715 }
1716
1717 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001718 }
1719
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001720 void setErrorStatus(const std::string &desc)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001722 setErrorResponse(res, boost::beast::http::status::internal_server_error,
1723 desc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001724 }
1725
Ed Tanous1abe55e2018-09-05 08:30:59 -07001726 crow::Response &res;
1727 std::string objectPath;
1728 std::string propertyName;
1729 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001730};
1731
Ed Tanousd76323e2018-08-07 14:35:40 -07001732void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 const std::string &objectPath, const std::string &destProperty)
1734{
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001735 if (destProperty.empty())
1736 {
1737 setErrorResponse(res, boost::beast::http::status::forbidden,
1738 forbiddenResDesc, forbiddenMsg);
1739 res.end();
1740 return;
1741 }
1742
Ed Tanous1abe55e2018-09-05 08:30:59 -07001743 nlohmann::json requestDbusData =
1744 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001745
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 if (requestDbusData.is_discarded())
1747 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001748 setErrorResponse(res, boost::beast::http::status::bad_request,
1749 noJsonDesc, badReqMsg);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001750 res.end();
1751 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001752 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001753
Ed Tanous1abe55e2018-09-05 08:30:59 -07001754 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1755 if (propertyIt == requestDbusData.end())
1756 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001757 setErrorResponse(res, boost::beast::http::status::bad_request,
1758 noJsonDesc, badReqMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001759 res.end();
1760 return;
1761 }
1762 const nlohmann::json &propertySetValue = *propertyIt;
1763 auto transaction = std::make_shared<AsyncPutRequest>(res);
1764 transaction->objectPath = objectPath;
1765 transaction->propertyName = destProperty;
1766 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001767
Ed Tanous1abe55e2018-09-05 08:30:59 -07001768 using GetObjectType =
1769 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001770
Ed Tanous1abe55e2018-09-05 08:30:59 -07001771 crow::connections::systemBus->async_method_call(
1772 [transaction](const boost::system::error_code ec,
1773 const GetObjectType &object_names) {
1774 if (!ec && object_names.size() <= 0)
1775 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001776 setErrorResponse(transaction->res,
1777 boost::beast::http::status::not_found,
1778 propNotFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001779 return;
1780 }
Ed Tanous911ac312017-08-15 09:37:42 -07001781
Ed Tanous1abe55e2018-09-05 08:30:59 -07001782 for (const std::pair<std::string, std::vector<std::string>>
1783 connection : object_names)
1784 {
1785 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001786
Ed Tanous1abe55e2018-09-05 08:30:59 -07001787 crow::connections::systemBus->async_method_call(
1788 [connectionName{std::string(connectionName)},
1789 transaction](const boost::system::error_code ec,
1790 const std::string &introspectXml) {
1791 if (ec)
1792 {
1793 BMCWEB_LOG_ERROR
1794 << "Introspect call failed with error: "
1795 << ec.message()
1796 << " on process: " << connectionName;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001797 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001798 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001799 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001800 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001801
Ed Tanous1abe55e2018-09-05 08:30:59 -07001802 doc.Parse(introspectXml.c_str());
1803 tinyxml2::XMLNode *pRoot =
1804 doc.FirstChildElement("node");
1805 if (pRoot == nullptr)
1806 {
1807 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1808 << introspectXml;
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001809 transaction->setErrorStatus("Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001810 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001811 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001812 tinyxml2::XMLElement *ifaceNode =
1813 pRoot->FirstChildElement("interface");
1814 while (ifaceNode != nullptr)
1815 {
1816 const char *interfaceName =
1817 ifaceNode->Attribute("name");
1818 BMCWEB_LOG_DEBUG << "found interface "
1819 << interfaceName;
1820 tinyxml2::XMLElement *propNode =
1821 ifaceNode->FirstChildElement("property");
1822 while (propNode != nullptr)
1823 {
1824 const char *propertyName =
1825 propNode->Attribute("name");
1826 BMCWEB_LOG_DEBUG << "Found property "
1827 << propertyName;
1828 if (propertyName == transaction->propertyName)
1829 {
1830 const char *argType =
1831 propNode->Attribute("type");
1832 if (argType != nullptr)
1833 {
1834 sdbusplus::message::message m =
1835 crow::connections::systemBus
1836 ->new_method_call(
1837 connectionName.c_str(),
1838 transaction->objectPath
1839 .c_str(),
1840 "org.freedesktop.DBus."
1841 "Properties",
1842 "Set");
1843 m.append(interfaceName,
1844 transaction->propertyName);
1845 int r = sd_bus_message_open_container(
1846 m.get(), SD_BUS_TYPE_VARIANT,
1847 argType);
1848 if (r < 0)
1849 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001850 transaction->setErrorStatus(
1851 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001852 return;
1853 }
1854 r = convertJsonToDbus(
1855 m.get(), argType,
1856 transaction->propertyValue);
1857 if (r < 0)
1858 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001859 transaction->setErrorStatus(
1860 "Invalid arg type");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001861 return;
1862 }
1863 r = sd_bus_message_close_container(
1864 m.get());
1865 if (r < 0)
1866 {
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001867 transaction->setErrorStatus(
1868 "Unexpected Error");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001869 return;
1870 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001871 crow::connections::systemBus
1872 ->async_send(
1873 m,
1874 [transaction](
1875 boost::system::error_code
1876 ec,
1877 sdbusplus::message::message
1878 &m) {
1879 BMCWEB_LOG_DEBUG << "sent";
1880 if (ec)
1881 {
Lei YU97d2a472019-06-11 17:44:27 +08001882 const sd_bus_error *e =
1883 m.get_error();
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001884 setErrorResponse(
1885 transaction->res,
1886 boost::beast::http::
1887 status::
1888 forbidden,
Matt Spinler06b1b632019-06-18 16:09:25 -05001889 (e) ? e->name
1890 : ec.category()
1891 .name(),
1892 (e) ? e->message
1893 : ec.message());
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001894 }
1895 else
1896 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001897 transaction->res
Matt Spinlerfbc19ea2018-12-11 14:03:42 -06001898 .jsonValue = {
1899 {"status", "ok"},
1900 {"message",
1901 "200 OK"},
1902 {"data", nullptr}};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001903 }
1904 });
1905 }
1906 }
1907 propNode =
1908 propNode->NextSiblingElement("property");
1909 }
1910 ifaceNode =
1911 ifaceNode->NextSiblingElement("interface");
1912 }
1913 },
1914 connectionName, transaction->objectPath,
1915 "org.freedesktop.DBus.Introspectable", "Introspect");
1916 }
1917 },
1918 "xyz.openbmc_project.ObjectMapper",
1919 "/xyz/openbmc_project/object_mapper",
1920 "xyz.openbmc_project.ObjectMapper", "GetObject",
1921 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001922}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001923
Ed Tanous049a0512018-11-01 13:58:42 -07001924inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1925 std::string &objectPath)
1926{
Ed Tanous049a0512018-11-01 13:58:42 -07001927
1928 // If accessing a single attribute, fill in and update objectPath,
1929 // otherwise leave destProperty blank
1930 std::string destProperty = "";
1931 const char *attrSeperator = "/attr/";
1932 size_t attrPosition = objectPath.find(attrSeperator);
1933 if (attrPosition != objectPath.npos)
1934 {
1935 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1936 objectPath.length());
1937 objectPath = objectPath.substr(0, attrPosition);
1938 }
1939
1940 if (req.method() == "POST"_method)
1941 {
1942 constexpr const char *actionSeperator = "/action/";
1943 size_t actionPosition = objectPath.find(actionSeperator);
1944 if (actionPosition != objectPath.npos)
1945 {
1946 std::string postProperty =
1947 objectPath.substr((actionPosition + strlen(actionSeperator)),
1948 objectPath.length());
1949 objectPath = objectPath.substr(0, actionPosition);
1950 handleAction(req, res, objectPath, postProperty);
1951 return;
1952 }
1953 }
1954 else if (req.method() == "GET"_method)
1955 {
1956 if (boost::ends_with(objectPath, "/enumerate"))
1957 {
1958 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1959 objectPath.end());
1960 handleEnumerate(res, objectPath);
1961 }
1962 else if (boost::ends_with(objectPath, "/list"))
1963 {
1964 objectPath.erase(objectPath.end() - sizeof("list"),
1965 objectPath.end());
1966 handleList(res, objectPath);
1967 }
1968 else
1969 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001970 // Trim any trailing "/" at the end
1971 if (boost::ends_with(objectPath, "/"))
1972 {
1973 objectPath.pop_back();
1974 handleList(res, objectPath, 1);
1975 }
1976 else
1977 {
1978 handleGet(res, objectPath, destProperty);
1979 }
Ed Tanous049a0512018-11-01 13:58:42 -07001980 }
1981 return;
1982 }
1983 else if (req.method() == "PUT"_method)
1984 {
1985 handlePut(req, res, objectPath, destProperty);
1986 return;
1987 }
Matt Spinlerde818812018-12-11 16:39:20 -06001988 else if (req.method() == "DELETE"_method)
1989 {
1990 handleDelete(req, res, objectPath);
1991 return;
1992 }
Ed Tanous049a0512018-11-01 13:58:42 -07001993
Matt Spinlerc4e8d21d62018-12-11 11:47:17 -06001994 setErrorResponse(res, boost::beast::http::status::method_not_allowed,
1995 methodNotAllowedDesc, methodNotAllowedMsg);
Ed Tanous049a0512018-11-01 13:58:42 -07001996 res.end();
1997}
1998
Ed Tanous1abe55e2018-09-05 08:30:59 -07001999template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
2000{
2001 BMCWEB_ROUTE(app, "/bus/")
Tanousf00032d2018-11-05 01:18:10 -03002002 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002003 .methods("GET"_method)(
2004 [](const crow::Request &req, crow::Response &res) {
2005 res.jsonValue = {{"busses", {{{"name", "system"}}}},
2006 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002007 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07002008 });
2009
2010 BMCWEB_ROUTE(app, "/bus/system/")
Tanousf00032d2018-11-05 01:18:10 -03002011 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002012 .methods("GET"_method)(
2013 [](const crow::Request &req, crow::Response &res) {
2014 auto myCallback = [&res](const boost::system::error_code ec,
2015 std::vector<std::string> &names) {
2016 if (ec)
2017 {
2018 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
2019 res.result(
2020 boost::beast::http::status::internal_server_error);
2021 }
2022 else
2023 {
2024 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002025 res.jsonValue = {{"status", "ok"}};
2026 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07002027 for (auto &name : names)
2028 {
2029 objectsSub.push_back({{"name", name}});
2030 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002031 }
2032 res.end();
2033 };
2034 crow::connections::systemBus->async_method_call(
2035 std::move(myCallback), "org.freedesktop.DBus", "/",
2036 "org.freedesktop.DBus", "ListNames");
2037 });
2038
2039 BMCWEB_ROUTE(app, "/list/")
Tanousf00032d2018-11-05 01:18:10 -03002040 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002041 .methods("GET"_method)(
2042 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002043 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002044 });
2045
2046 BMCWEB_ROUTE(app, "/xyz/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002047 .requires({"Login"})
2048 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2049 const std::string &path) {
2050 std::string objectPath = "/xyz/" + path;
2051 handleDBusUrl(req, res, objectPath);
2052 });
2053
2054 BMCWEB_ROUTE(app, "/xyz/<path>")
2055 .requires({"ConfigureComponents", "ConfigureManager"})
2056 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002057 [](const crow::Request &req, crow::Response &res,
2058 const std::string &path) {
2059 std::string objectPath = "/xyz/" + path;
2060 handleDBusUrl(req, res, objectPath);
2061 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002062
Ed Tanous049a0512018-11-01 13:58:42 -07002063 BMCWEB_ROUTE(app, "/org/<path>")
Tanousf00032d2018-11-05 01:18:10 -03002064 .requires({"Login"})
2065 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2066 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002067 std::string objectPath = "/org/" + path;
Tanousf00032d2018-11-05 01:18:10 -03002068 handleDBusUrl(req, res, objectPath);
2069 });
2070
2071 BMCWEB_ROUTE(app, "/org/<path>")
2072 .requires({"ConfigureComponents", "ConfigureManager"})
2073 .methods("PUT"_method, "POST"_method, "DELETE"_method)(
Ed Tanous049a0512018-11-01 13:58:42 -07002074 [](const crow::Request &req, crow::Response &res,
2075 const std::string &path) {
Ed Tanouse1281402019-04-03 07:07:10 -07002076 std::string objectPath = "/org/" + path;
Ed Tanous049a0512018-11-01 13:58:42 -07002077 handleDBusUrl(req, res, objectPath);
2078 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07002079
Ed Tanous1abe55e2018-09-05 08:30:59 -07002080 BMCWEB_ROUTE(app, "/download/dump/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002081 .requires({"ConfigureManager"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002082 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
2083 const std::string &dumpId) {
Ed Tanousad18f072018-11-14 14:07:48 -08002084 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$");
Ed Tanous1abe55e2018-09-05 08:30:59 -07002085 if (!std::regex_match(dumpId, validFilename))
2086 {
Ed Tanousad18f072018-11-14 14:07:48 -08002087 res.result(boost::beast::http::status::bad_request);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002088 res.end();
2089 return;
2090 }
James Feistf6150402019-01-08 10:36:20 -08002091 std::filesystem::path loc(
Ed Tanous1abe55e2018-09-05 08:30:59 -07002092 "/var/lib/phosphor-debug-collector/dumps");
2093
Ed Tanousad18f072018-11-14 14:07:48 -08002094 loc /= dumpId;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002095
James Feistf6150402019-01-08 10:36:20 -08002096 if (!std::filesystem::exists(loc) ||
2097 !std::filesystem::is_directory(loc))
Ed Tanous1abe55e2018-09-05 08:30:59 -07002098 {
Ed Tanousad18f072018-11-14 14:07:48 -08002099 BMCWEB_LOG_ERROR << loc << "Not found";
Ed Tanous1abe55e2018-09-05 08:30:59 -07002100 res.result(boost::beast::http::status::not_found);
2101 res.end();
2102 return;
2103 }
James Feistf6150402019-01-08 10:36:20 -08002104 std::filesystem::directory_iterator files(loc);
Ed Tanousad18f072018-11-14 14:07:48 -08002105
Ed Tanous1abe55e2018-09-05 08:30:59 -07002106 for (auto &file : files)
2107 {
2108 std::ifstream readFile(file.path());
Ed Tanousad18f072018-11-14 14:07:48 -08002109 if (!readFile.good())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002110 {
2111 continue;
2112 }
2113 res.addHeader("Content-Type", "application/octet-stream");
2114 res.body() = {std::istreambuf_iterator<char>(readFile),
2115 std::istreambuf_iterator<char>()};
2116 res.end();
Ed Tanousad18f072018-11-14 14:07:48 -08002117 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002118 }
2119 res.result(boost::beast::http::status::not_found);
2120 res.end();
2121 return;
2122 });
2123
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002124 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Tanousf00032d2018-11-05 01:18:10 -03002125 .requires({"Login"})
Ed Tanous1abe55e2018-09-05 08:30:59 -07002126 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002127 const std::string &Connection) {
2128 introspectObjects(Connection, "/",
2129 std::make_shared<bmcweb::AsyncResp>(res));
2130 });
2131
2132 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
2133 .methods("GET"_method,
2134 "POST"_method)([](const crow::Request &req,
2135 crow::Response &res,
2136 const std::string &processName,
2137 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002138 std::vector<std::string> strs;
2139 boost::split(strs, requestedPath, boost::is_any_of("/"));
2140 std::string objectPath;
2141 std::string interfaceName;
2142 std::string methodName;
2143 auto it = strs.begin();
2144 if (it == strs.end())
2145 {
2146 objectPath = "/";
2147 }
2148 while (it != strs.end())
2149 {
2150 // Check if segment contains ".". If it does, it must be an
2151 // interface
2152 if (it->find(".") != std::string::npos)
2153 {
2154 break;
Ed Tanous7c091622019-05-23 11:42:36 -07002155 // This check is neccesary as the trailing slash gets
2156 // parsed as part of our <path> specifier above, which
2157 // causes the normal trailing backslash redirector to
2158 // fail.
Ed Tanous1abe55e2018-09-05 08:30:59 -07002159 }
2160 else if (!it->empty())
2161 {
2162 objectPath += "/" + *it;
2163 }
2164 it++;
2165 }
2166 if (it != strs.end())
2167 {
2168 interfaceName = *it;
2169 it++;
2170
2171 // after interface, we might have a method name
2172 if (it != strs.end())
2173 {
2174 methodName = *it;
2175 it++;
2176 }
2177 }
2178 if (it != strs.end())
2179 {
Ed Tanous7c091622019-05-23 11:42:36 -07002180 // if there is more levels past the method name, something
2181 // went wrong, return not found
Ed Tanous1abe55e2018-09-05 08:30:59 -07002182 res.result(boost::beast::http::status::not_found);
Ed Tanous1abe55e2018-09-05 08:30:59 -07002183 return;
2184 }
2185 if (interfaceName.empty())
2186 {
Ed Tanous7c091622019-05-23 11:42:36 -07002187 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2188 std::make_shared<bmcweb::AsyncResp>(res);
2189
Ed Tanous1abe55e2018-09-05 08:30:59 -07002190 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002191 [asyncResp, processName,
Ed Tanous1abe55e2018-09-05 08:30:59 -07002192 objectPath](const boost::system::error_code ec,
2193 const std::string &introspect_xml) {
2194 if (ec)
2195 {
2196 BMCWEB_LOG_ERROR
2197 << "Introspect call failed with error: "
2198 << ec.message()
2199 << " on process: " << processName
2200 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002201 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002202 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002203 tinyxml2::XMLDocument doc;
2204
2205 doc.Parse(introspect_xml.c_str());
2206 tinyxml2::XMLNode *pRoot =
2207 doc.FirstChildElement("node");
2208 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002209 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002210 BMCWEB_LOG_ERROR << "XML document failed to parse "
2211 << processName << " " << objectPath
2212 << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002213 asyncResp->res.jsonValue = {
2214 {"status", "XML parse error"}};
2215 asyncResp->res.result(boost::beast::http::status::
2216 internal_server_error);
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002217 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002218 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002219
2220 BMCWEB_LOG_DEBUG << introspect_xml;
Ed Tanous7c091622019-05-23 11:42:36 -07002221 asyncResp->res.jsonValue = {
2222 {"status", "ok"},
2223 {"bus_name", processName},
2224 {"object_path", objectPath}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002225 nlohmann::json &interfacesArray =
Ed Tanous7c091622019-05-23 11:42:36 -07002226 asyncResp->res.jsonValue["interfaces"];
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002227 interfacesArray = nlohmann::json::array();
2228 tinyxml2::XMLElement *interface =
2229 pRoot->FirstChildElement("interface");
2230
2231 while (interface != nullptr)
2232 {
2233 const char *ifaceName =
2234 interface->Attribute("name");
2235 if (ifaceName != nullptr)
2236 {
2237 interfacesArray.push_back(
2238 {{"name", ifaceName}});
2239 }
2240
2241 interface =
2242 interface->NextSiblingElement("interface");
2243 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002244 },
2245 processName, objectPath,
2246 "org.freedesktop.DBus.Introspectable", "Introspect");
2247 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002248 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07002249 {
Ed Tanous7c091622019-05-23 11:42:36 -07002250 std::shared_ptr<bmcweb::AsyncResp> asyncResp =
2251 std::make_shared<bmcweb::AsyncResp>(res);
2252
Ed Tanous1abe55e2018-09-05 08:30:59 -07002253 crow::connections::systemBus->async_method_call(
Ed Tanous7c091622019-05-23 11:42:36 -07002254 [asyncResp, processName, objectPath,
2255 interfaceName](const boost::system::error_code ec,
2256 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07002257 if (ec)
2258 {
2259 BMCWEB_LOG_ERROR
2260 << "Introspect call failed with error: "
2261 << ec.message()
2262 << " on process: " << processName
2263 << " path: " << objectPath << "\n";
Ed Tanous7c091622019-05-23 11:42:36 -07002264 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002265 }
Ed Tanous7c091622019-05-23 11:42:36 -07002266 tinyxml2::XMLDocument doc;
2267
2268 doc.Parse(introspect_xml.data(), introspect_xml.size());
2269 tinyxml2::XMLNode *pRoot =
2270 doc.FirstChildElement("node");
2271 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07002272 {
Ed Tanous7c091622019-05-23 11:42:36 -07002273 BMCWEB_LOG_ERROR << "XML document failed to parse "
2274 << processName << " " << objectPath
2275 << "\n";
2276 asyncResp->res.result(boost::beast::http::status::
2277 internal_server_error);
2278 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07002279 }
Ed Tanous7c091622019-05-23 11:42:36 -07002280 asyncResp->res.jsonValue = {
2281 {"status", "ok"},
2282 {"bus_name", processName},
2283 {"interface", interfaceName},
2284 {"object_path", objectPath}};
2285
2286 nlohmann::json &methodsArray =
2287 asyncResp->res.jsonValue["methods"];
2288 methodsArray = nlohmann::json::array();
2289
2290 nlohmann::json &signalsArray =
2291 asyncResp->res.jsonValue["signals"];
2292 signalsArray = nlohmann::json::array();
2293
2294 nlohmann::json &propertiesObj =
2295 asyncResp->res.jsonValue["properties"];
2296 propertiesObj = nlohmann::json::object();
2297
2298 // if we know we're the only call, build the
2299 // json directly
2300 tinyxml2::XMLElement *interface =
2301 pRoot->FirstChildElement("interface");
2302 while (interface != nullptr)
2303 {
2304 const char *ifaceName =
2305 interface->Attribute("name");
2306
2307 if (ifaceName != nullptr &&
2308 ifaceName == interfaceName)
2309 {
2310 break;
2311 }
2312
2313 interface =
2314 interface->NextSiblingElement("interface");
2315 }
2316 if (interface == nullptr)
2317 {
2318 // if we got to the end of the list and
2319 // never found a match, throw 404
2320 asyncResp->res.result(
2321 boost::beast::http::status::not_found);
2322 return;
2323 }
2324
2325 tinyxml2::XMLElement *methods =
2326 interface->FirstChildElement("method");
2327 while (methods != nullptr)
2328 {
2329 nlohmann::json argsArray = nlohmann::json::array();
2330 tinyxml2::XMLElement *arg =
2331 methods->FirstChildElement("arg");
2332 while (arg != nullptr)
2333 {
2334 nlohmann::json thisArg;
2335 for (const char *fieldName :
2336 std::array<const char *, 3>{
2337 "name", "direction", "type"})
2338 {
2339 const char *fieldValue =
2340 arg->Attribute(fieldName);
2341 if (fieldValue != nullptr)
2342 {
2343 thisArg[fieldName] = fieldValue;
2344 }
2345 }
2346 argsArray.push_back(std::move(thisArg));
2347 arg = arg->NextSiblingElement("arg");
2348 }
2349
2350 const char *name = methods->Attribute("name");
2351 if (name != nullptr)
2352 {
2353 methodsArray.push_back(
2354 {{"name", name},
2355 {"uri", "/bus/system/" + processName +
2356 objectPath + "/" +
2357 interfaceName + "/" + name},
2358 {"args", argsArray}});
2359 }
2360 methods = methods->NextSiblingElement("method");
2361 }
2362 tinyxml2::XMLElement *signals =
2363 interface->FirstChildElement("signal");
2364 while (signals != nullptr)
2365 {
2366 nlohmann::json argsArray = nlohmann::json::array();
2367
2368 tinyxml2::XMLElement *arg =
2369 signals->FirstChildElement("arg");
2370 while (arg != nullptr)
2371 {
2372 const char *name = arg->Attribute("name");
2373 const char *type = arg->Attribute("type");
2374 if (name != nullptr && type != nullptr)
2375 {
2376 argsArray.push_back({
2377 {"name", name},
2378 {"type", type},
2379 });
2380 }
2381 arg = arg->NextSiblingElement("arg");
2382 }
2383 const char *name = signals->Attribute("name");
2384 if (name != nullptr)
2385 {
2386 signalsArray.push_back(
2387 {{"name", name}, {"args", argsArray}});
2388 }
2389
2390 signals = signals->NextSiblingElement("signal");
2391 }
2392
2393 tinyxml2::XMLElement *property =
2394 interface->FirstChildElement("property");
2395 while (property != nullptr)
2396 {
2397 const char *name = property->Attribute("name");
2398 const char *type = property->Attribute("type");
2399 if (type != nullptr && name != nullptr)
2400 {
2401 sdbusplus::message::message m =
2402 crow::connections::systemBus
2403 ->new_method_call(processName.c_str(),
2404 objectPath.c_str(),
2405 "org.freedesktop."
2406 "DBus."
2407 "Properties",
2408 "Get");
2409 m.append(interfaceName, name);
2410 nlohmann::json &propertyItem =
2411 propertiesObj[name];
2412 crow::connections::systemBus->async_send(
2413 m, [&propertyItem, asyncResp](
2414 boost::system::error_code &ec,
2415 sdbusplus::message::message &m) {
2416 if (ec)
2417 {
2418 return;
2419 }
2420
2421 convertDBusToJSON("v", m, propertyItem);
2422 });
2423 }
2424 property = property->NextSiblingElement("property");
2425 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002426 },
2427 processName, objectPath,
2428 "org.freedesktop.DBus.Introspectable", "Introspect");
2429 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07002430 else
2431 {
2432 if (req.method() != "POST"_method)
2433 {
2434 res.result(boost::beast::http::status::not_found);
2435 res.end();
2436 return;
2437 }
2438
2439 nlohmann::json requestDbusData =
2440 nlohmann::json::parse(req.body, nullptr, false);
2441
2442 if (requestDbusData.is_discarded())
2443 {
2444 res.result(boost::beast::http::status::bad_request);
2445 res.end();
2446 return;
2447 }
2448 if (!requestDbusData.is_array())
2449 {
2450 res.result(boost::beast::http::status::bad_request);
2451 res.end();
2452 return;
2453 }
2454 auto transaction = std::make_shared<InProgressActionData>(res);
2455
2456 transaction->path = objectPath;
2457 transaction->methodName = methodName;
2458 transaction->arguments = std::move(requestDbusData);
2459
2460 findActionOnInterface(transaction, processName);
2461 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07002462 });
2463}
2464} // namespace openbmc_mapper
2465} // namespace crow