blob: 735db07367b6568c65854f984ff20a7119467586 [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>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -070024#include <experimental/filesystem>
25#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";
38const std::string notFoundDesc =
39 "org.freedesktop.DBus.Error.FileNotFound: path or object not found";
40
41void setErrorResponse(crow::Response &res, boost::beast::http::status result,
42 const std::string &desc, const std::string &msg)
43{
44 res.result(result);
45 res.jsonValue = {{"data", {{"description", desc}}},
46 {"message", msg},
47 {"status", "error"}};
48}
49
Ed Tanouse3cb5a32018-08-08 14:16:49 -070050void introspectObjects(const std::string &processName,
51 const std::string &objectPath,
52 std::shared_ptr<bmcweb::AsyncResp> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -070053{
Ed Tanouse3cb5a32018-08-08 14:16:49 -070054 if (transaction->res.jsonValue.is_null())
55 {
56 transaction->res.jsonValue = {{"status", "ok"},
57 {"bus_name", processName},
58 {"objects", nlohmann::json::array()}};
59 }
60
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -070062 [transaction, processName{std::string(processName)},
63 objectPath{std::string(objectPath)}](
64 const boost::system::error_code ec,
65 const std::string &introspect_xml) {
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 if (ec)
67 {
68 BMCWEB_LOG_ERROR
69 << "Introspect call failed with error: " << ec.message()
70 << " on process: " << processName << " path: " << objectPath
71 << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -070072 return;
73 }
74 transaction->res.jsonValue["objects"].push_back(
75 {{"path", objectPath}});
76
77 tinyxml2::XMLDocument doc;
78
79 doc.Parse(introspect_xml.c_str());
80 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
81 if (pRoot == nullptr)
82 {
83 BMCWEB_LOG_ERROR << "XML document failed to parse "
84 << processName << " " << objectPath << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070085 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070086 else
87 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070088 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node");
89 while (node != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070090 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -070091 const char *childPath = node->Attribute("name");
92 if (childPath != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 {
Ed Tanous1abe55e2018-09-05 08:30:59 -070094 std::string newpath;
95 if (objectPath != "/")
96 {
97 newpath += objectPath;
98 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -070099 newpath += std::string("/") + childPath;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 // introspect the subobjects as well
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700101 introspectObjects(processName, newpath, transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700103
104 node = node->NextSiblingElement("node");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 }
106 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700108 processName, objectPath, "org.freedesktop.DBus.Introspectable",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -0700110}
Ed Tanous64530012018-02-06 17:08:16 -0800111
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600112void getPropertiesForEnumerate(const std::string &objectPath,
113 const std::string &service,
114 const std::string &interface,
115 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
116{
117 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " "
118 << service << " " << interface;
119
120 crow::connections::systemBus->async_method_call(
121 [asyncResp, objectPath, service,
122 interface](const boost::system::error_code ec,
123 const std::vector<
124 std::pair<std::string, dbus::utility::DbusVariantType>>
125 &propertiesList) {
126 if (ec)
127 {
128 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface "
129 << interface << " service " << service
130 << " failed with code " << ec;
131 return;
132 }
133
134 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
135 nlohmann::json &objectJson = dataJson[objectPath];
136 if (objectJson.is_null())
137 {
138 objectJson = nlohmann::json::object();
139 }
140
141 for (const auto &[name, value] : propertiesList)
142 {
143 nlohmann::json &propertyJson = objectJson[name];
144 sdbusplus::message::variant_ns::visit(
145 [&propertyJson](auto &&val) { propertyJson = val; }, value);
146 }
147 },
148 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll",
149 interface);
150}
151
152// Find any results that weren't picked up by ObjectManagers, to be
153// called after all ObjectManagers are searched for and called.
154void findRemainingObjectsForEnumerate(
155 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree,
156 std::shared_ptr<bmcweb::AsyncResp> asyncResp)
157{
158 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate";
159 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"];
160
161 for (const auto &[path, interface_map] : *subtree)
162 {
163 if (path == objectPath)
164 {
165 // An enumerate does not return the target path's properties
166 continue;
167 }
168 if (dataJson.find(path) == dataJson.end())
169 {
170 for (const auto &[service, interfaces] : interface_map)
171 {
172 for (const auto &interface : interfaces)
173 {
174 if (!boost::starts_with(interface, "org.freedesktop.DBus"))
175 {
176 getPropertiesForEnumerate(path, service, interface,
177 asyncResp);
178 }
179 }
180 }
181 }
182 }
183}
184
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600185struct InProgressEnumerateData
186{
187 InProgressEnumerateData(const std::string &objectPath,
188 std::shared_ptr<bmcweb::AsyncResp> asyncResp) :
189 objectPath(objectPath),
190 asyncResp(asyncResp)
191 {
192 }
193
194 ~InProgressEnumerateData()
195 {
Matt Spinler2df1e7d2018-12-05 15:53:16 -0600196 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp);
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600197 }
198
199 const std::string objectPath;
200 std::shared_ptr<GetSubTreeType> subtree;
201 std::shared_ptr<bmcweb::AsyncResp> asyncResp;
202};
203
204void getManagedObjectsForEnumerate(
205 const std::string &object_name, const std::string &object_manager_path,
206 const std::string &connection_name,
207 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700208{
Ed Tanous049a0512018-11-01 13:58:42 -0700209 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name
210 << " object_manager_path " << object_manager_path
211 << " connection_name " << connection_name;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700212 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600213 [transaction, object_name,
Ed Tanous049a0512018-11-01 13:58:42 -0700214 connection_name](const boost::system::error_code ec,
215 const dbus::utility::ManagedObjectType &objects) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700216 if (ec)
217 {
Ed Tanous049a0512018-11-01 13:58:42 -0700218 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600219 << " on connection " << connection_name
Ed Tanous049a0512018-11-01 13:58:42 -0700220 << " failed with code " << ec;
221 return;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700222 }
Ed Tanous64530012018-02-06 17:08:16 -0800223
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600224 nlohmann::json &dataJson =
225 transaction->asyncResp->res.jsonValue["data"];
Ed Tanous049a0512018-11-01 13:58:42 -0700226
227 for (const auto &objectPath : objects)
228 {
229 if (boost::starts_with(objectPath.first.str, object_name))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700230 {
Ed Tanous049a0512018-11-01 13:58:42 -0700231 BMCWEB_LOG_DEBUG << "Reading object "
232 << objectPath.first.str;
233 nlohmann::json &objectJson = dataJson[objectPath.first.str];
Ed Tanous1abe55e2018-09-05 08:30:59 -0700234 if (objectJson.is_null())
235 {
236 objectJson = nlohmann::json::object();
237 }
238 for (const auto &interface : objectPath.second)
239 {
240 for (const auto &property : interface.second)
241 {
242 nlohmann::json &propertyJson =
243 objectJson[property.first];
William A. Kennington III0a63b1c2018-10-18 13:37:19 -0700244 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 [&propertyJson](auto &&val) {
246 propertyJson = val;
247 },
248 property.second);
249 }
250 }
251 }
Ed Tanous049a0512018-11-01 13:58:42 -0700252 for (const auto &interface : objectPath.second)
253 {
254 if (interface.first == "org.freedesktop.DBus.ObjectManager")
255 {
256 getManagedObjectsForEnumerate(
257 objectPath.first.str, objectPath.first.str,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600258 connection_name, transaction);
Ed Tanous049a0512018-11-01 13:58:42 -0700259 }
260 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700261 }
262 },
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700263 connection_name, object_manager_path,
264 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
265}
266
267void findObjectManagerPathForEnumerate(
268 const std::string &object_name, const std::string &connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600269 std::shared_ptr<InProgressEnumerateData> transaction)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700270{
Ed Tanous049a0512018-11-01 13:58:42 -0700271 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name
272 << " on connection:" << connection_name;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700273 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600274 [transaction, object_name, connection_name](
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700275 const boost::system::error_code ec,
276 const boost::container::flat_map<
277 std::string, boost::container::flat_map<
278 std::string, std::vector<std::string>>>
279 &objects) {
280 if (ec)
281 {
Ed Tanous049a0512018-11-01 13:58:42 -0700282 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name
283 << " failed with code " << ec;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700284 return;
285 }
286
Ed Tanousf254ba72018-10-12 13:40:35 -0700287 for (const auto &pathGroup : objects)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700288 {
Ed Tanousf254ba72018-10-12 13:40:35 -0700289 for (const auto &connectionGroup : pathGroup.second)
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700290 {
291 if (connectionGroup.first == connection_name)
292 {
293 // Found the object manager path for this resource.
294 getManagedObjectsForEnumerate(
Ed Tanous049a0512018-11-01 13:58:42 -0700295 object_name, pathGroup.first, connection_name,
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600296 transaction);
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700297 return;
298 }
299 }
300 }
301 },
302 "xyz.openbmc_project.ObjectMapper",
303 "/xyz/openbmc_project/object_mapper",
304 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name,
305 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"});
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700306}
Ed Tanous64530012018-02-06 17:08:16 -0800307
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600308// Uses GetObject to add the object info about the target /enumerate path to the
309// results of GetSubTree, as GetSubTree will not return info for the
310// target path, and then continues on enumerating the rest of the tree.
311void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction)
312{
313 using GetObjectType =
314 std::vector<std::pair<std::string, std::vector<std::string>>>;
315
316 crow::connections::systemBus->async_method_call(
317 [transaction](const boost::system::error_code ec,
318 const GetObjectType &objects) {
319 if (ec)
320 {
321 BMCWEB_LOG_ERROR << "GetObject for path "
322 << transaction->objectPath
323 << " failed with code " << ec;
324 return;
325 }
326
327 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath
328 << " has " << objects.size() << " entries";
329 if (!objects.empty())
330 {
331 transaction->subtree->emplace_back(transaction->objectPath,
332 objects);
333 }
334
335 // Map indicating connection name, and the path where the object
336 // manager exists
337 boost::container::flat_map<std::string, std::string> connections;
338
339 for (const auto &object : *(transaction->subtree))
340 {
341 for (const auto &connection : object.second)
342 {
343 std::string &objectManagerPath =
344 connections[connection.first];
345 for (const auto &interface : connection.second)
346 {
347 BMCWEB_LOG_DEBUG << connection.first
348 << " has interface " << interface;
349 if (interface == "org.freedesktop.DBus.ObjectManager")
350 {
351 BMCWEB_LOG_DEBUG << "found object manager path "
352 << object.first;
353 objectManagerPath = object.first;
354 }
355 }
356 }
357 }
358 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections";
359
360 for (const auto &connection : connections)
361 {
362 // If we already know where the object manager is, we don't need
363 // to search for it, we can call directly in to
364 // getManagedObjects
365 if (!connection.second.empty())
366 {
367 getManagedObjectsForEnumerate(
368 transaction->objectPath, connection.second,
369 connection.first, transaction);
370 }
371 else
372 {
373 // otherwise we need to find the object manager path before
374 // we can continue
375 findObjectManagerPathForEnumerate(
376 transaction->objectPath, connection.first, transaction);
377 }
378 }
379 },
380 "xyz.openbmc_project.ObjectMapper",
381 "/xyz/openbmc_project/object_mapper",
382 "xyz.openbmc_project.ObjectMapper", "GetObject",
383 transaction->objectPath, std::array<const char *, 0>());
384}
Ed Tanous64530012018-02-06 17:08:16 -0800385
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700386// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700387struct InProgressActionData
388{
389 InProgressActionData(crow::Response &res) : res(res){};
390 ~InProgressActionData()
391 {
392 if (res.result() == boost::beast::http::status::internal_server_error)
393 {
394 // Reset the json object to clear out any data that made it in
395 // before the error happened todo(ed) handle error condition with
396 // proper code
397 res.jsonValue = nlohmann::json::object();
398 }
399 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700400 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700401
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 void setErrorStatus()
403 {
404 res.result(boost::beast::http::status::internal_server_error);
405 }
406 crow::Response &res;
407 std::string path;
408 std::string methodName;
409 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700410};
411
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412std::vector<std::string> dbusArgSplit(const std::string &string)
413{
414 std::vector<std::string> ret;
415 if (string.empty())
416 {
417 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700418 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700419 ret.push_back("");
420 int containerDepth = 0;
421
422 for (std::string::const_iterator character = string.begin();
423 character != string.end(); character++)
424 {
425 ret.back() += *character;
426 switch (*character)
427 {
428 case ('a'):
429 break;
430 case ('('):
431 case ('{'):
432 containerDepth++;
433 break;
434 case ('}'):
435 case (')'):
436 containerDepth--;
437 if (containerDepth == 0)
438 {
439 if (character + 1 != string.end())
440 {
441 ret.push_back("");
442 }
443 }
444 break;
445 default:
446 if (containerDepth == 0)
447 {
448 if (character + 1 != string.end())
449 {
450 ret.push_back("");
451 }
452 }
453 break;
454 }
455 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700456}
457
Ed Tanousd76323e2018-08-07 14:35:40 -0700458int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 const nlohmann::json &input_json)
460{
461 int r = 0;
462 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
463 << " to type: " << arg_type;
464 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700465
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 // Assume a single object for now.
467 const nlohmann::json *j = &input_json;
468 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700469
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700470 for (const std::string &argCode : argTypes)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471 {
472 // If we are decoding multiple objects, grab the pointer to the
473 // iterator, and increment it for the next loop
474 if (argTypes.size() > 1)
475 {
476 if (jIt == input_json.end())
477 {
478 return -2;
479 }
480 j = &*jIt;
481 jIt++;
482 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700483 const int64_t *intValue = j->get_ptr<const int64_t *>();
484 const uint64_t *uintValue = j->get_ptr<const uint64_t *>();
485 const std::string *stringValue = j->get_ptr<const std::string *>();
486 const double *doubleValue = j->get_ptr<const double *>();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 const bool *b = j->get_ptr<const bool *>();
488 int64_t v = 0;
489 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700490
Ed Tanous1abe55e2018-09-05 08:30:59 -0700491 // Do some basic type conversions that make sense. uint can be
492 // converted to int. int and uint can be converted to double
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700493 if (uintValue != nullptr && intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700494 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700495 v = static_cast<int64_t>(*uintValue);
496 intValue = &v;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700497 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700498 if (uintValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700500 d = static_cast<double>(*uintValue);
501 doubleValue = &d;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700502 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700503 if (intValue != nullptr && doubleValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700504 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700505 d = static_cast<double>(*intValue);
506 doubleValue = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700507 }
508
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700509 if (argCode == "s")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700510 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700511 if (stringValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700512 {
513 return -1;
514 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700515 r = sd_bus_message_append_basic(m, argCode[0],
516 (void *)stringValue->c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700517 if (r < 0)
518 {
519 return r;
520 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700521 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700522 else if (argCode == "i")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700524 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700525 {
526 return -1;
527 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700528 int32_t i = static_cast<int32_t>(*intValue);
529 r = sd_bus_message_append_basic(m, argCode[0], &i);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 if (r < 0)
531 {
532 return r;
533 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700534 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700535 else if (argCode == "b")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700536 {
537 // lots of ways bool could be represented here. Try them all
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700538 int boolInt = false;
539 if (intValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700541 boolInt = *intValue > 0 ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700542 }
543 else if (b != nullptr)
544 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700545 boolInt = b ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700547 else if (stringValue != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700548 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700549 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700550 }
551 else
552 {
553 return -1;
554 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700555 r = sd_bus_message_append_basic(m, argCode[0], &boolInt);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700556 if (r < 0)
557 {
558 return r;
559 }
560 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700561 else if (argCode == "n")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700562 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700563 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700564 {
565 return -1;
566 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700567 int16_t n = static_cast<int16_t>(*intValue);
568 r = sd_bus_message_append_basic(m, argCode[0], &n);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700569 if (r < 0)
570 {
571 return r;
572 }
573 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700574 else if (argCode == "x")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700575 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700576 if (intValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700577 {
578 return -1;
579 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700580 r = sd_bus_message_append_basic(m, argCode[0], intValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700581 if (r < 0)
582 {
583 return r;
584 }
585 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700586 else if (argCode == "y")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700587 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700588 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 {
590 return -1;
591 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700592 uint8_t y = static_cast<uint8_t>(*uintValue);
593 r = sd_bus_message_append_basic(m, argCode[0], &y);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700594 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700595 else if (argCode == "q")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700596 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700597 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700598 {
599 return -1;
600 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700601 uint16_t q = static_cast<uint16_t>(*uintValue);
602 r = sd_bus_message_append_basic(m, argCode[0], &q);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700603 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700604 else if (argCode == "u")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700605 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700606 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700607 {
608 return -1;
609 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700610 uint32_t u = static_cast<uint32_t>(*uintValue);
611 r = sd_bus_message_append_basic(m, argCode[0], &u);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700613 else if (argCode == "t")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700614 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700615 if (uintValue == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700616 {
617 return -1;
618 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700619 r = sd_bus_message_append_basic(m, argCode[0], uintValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700620 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700621 else if (argCode == "d")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700623 sd_bus_message_append_basic(m, argCode[0], doubleValue);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700625 else if (boost::starts_with(argCode, "a"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700626 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700627 std::string containedType = argCode.substr(1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700629 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700630 if (r < 0)
631 {
632 return r;
633 }
634
635 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
636 ++it)
637 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700638 r = convertJsonToDbus(m, containedType, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700639 if (r < 0)
640 {
641 return r;
642 }
643
644 it++;
645 }
646 sd_bus_message_close_container(m);
647 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700648 else if (boost::starts_with(argCode, "v"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700650 std::string containedType = argCode.substr(1);
651 BMCWEB_LOG_DEBUG << "variant type: " << argCode
652 << " appending variant of type: " << containedType;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700654 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 if (r < 0)
656 {
657 return r;
658 }
659
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700660 r = convertJsonToDbus(m, containedType, input_json);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 if (r < 0)
662 {
663 return r;
664 }
665
666 r = sd_bus_message_close_container(m);
667 if (r < 0)
668 {
669 return r;
670 }
671 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700672 else if (boost::starts_with(argCode, "(") &&
673 boost::ends_with(argCode, ")"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700674 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700675 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700676 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700677 containedType.c_str());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700678 nlohmann::json::const_iterator it = j->begin();
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700679 for (const std::string &argCode : dbusArgSplit(arg_type))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700680 {
681 if (it == j->end())
682 {
683 return -1;
684 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700685 r = convertJsonToDbus(m, argCode, *it);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700686 if (r < 0)
687 {
688 return r;
689 }
690 it++;
691 }
692 r = sd_bus_message_close_container(m);
693 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700694 else if (boost::starts_with(argCode, "{") &&
695 boost::ends_with(argCode, "}"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700697 std::string containedType = argCode.substr(1, argCode.size() - 1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700698 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700699 containedType.c_str());
700 std::vector<std::string> codes = dbusArgSplit(containedType);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700701 if (codes.size() != 2)
702 {
703 return -1;
704 }
705 const std::string &key_type = codes[0];
706 const std::string &value_type = codes[1];
707 for (auto it : j->items())
708 {
709 r = convertJsonToDbus(m, key_type, it.key());
710 if (r < 0)
711 {
712 return r;
713 }
714
715 r = convertJsonToDbus(m, value_type, it.value());
716 if (r < 0)
717 {
718 return r;
719 }
720 }
721 r = sd_bus_message_close_container(m);
722 }
723 else
724 {
725 return -2;
726 }
727 if (r < 0)
728 {
729 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700730 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700731
Ed Tanous1abe55e2018-09-05 08:30:59 -0700732 if (argTypes.size() > 1)
733 {
734 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700735 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700736 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700737}
738
Ed Tanousd76323e2018-08-07 14:35:40 -0700739void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 const std::string &connectionName)
741{
742 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
743 << connectionName;
744 crow::connections::systemBus->async_method_call(
745 [transaction, connectionName{std::string(connectionName)}](
746 const boost::system::error_code ec,
747 const std::string &introspect_xml) {
748 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
749 if (ec)
750 {
751 BMCWEB_LOG_ERROR
752 << "Introspect call failed with error: " << ec.message()
753 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700754 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700755 else
756 {
757 tinyxml2::XMLDocument doc;
758
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700759 doc.Parse(introspect_xml.data(), introspect_xml.size());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700760 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
761 if (pRoot == nullptr)
762 {
763 BMCWEB_LOG_ERROR << "XML document failed to parse "
764 << connectionName << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700765 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700767 tinyxml2::XMLElement *interfaceNode =
768 pRoot->FirstChildElement("interface");
769 while (interfaceNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700770 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700771 const char *thisInterfaceName =
772 interfaceNode->Attribute("name");
773 if (thisInterfaceName != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700775 tinyxml2::XMLElement *methodNode =
776 interfaceNode->FirstChildElement("method");
777 while (methodNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700779 const char *thisMethodName =
780 methodNode->Attribute("name");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700781 BMCWEB_LOG_DEBUG << "Found method: "
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700782 << thisMethodName;
783 if (thisMethodName != nullptr &&
784 thisMethodName == transaction->methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700786 BMCWEB_LOG_DEBUG
787 << "Found method named " << thisMethodName
788 << " on interface " << thisInterfaceName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 sdbusplus::message::message m =
790 crow::connections::systemBus
791 ->new_method_call(
792 connectionName.c_str(),
793 transaction->path.c_str(),
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700794 thisInterfaceName,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 transaction->methodName.c_str());
796
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700797 tinyxml2::XMLElement *argumentNode =
798 methodNode->FirstChildElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700799
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700800 nlohmann::json::const_iterator argIt =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700801 transaction->arguments.begin();
802
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700803 while (argumentNode != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700805 const char *argDirection =
806 argumentNode->Attribute("direction");
807 const char *argType =
808 argumentNode->Attribute("type");
809 if (argDirection != nullptr &&
810 argType != nullptr &&
811 std::string(argDirection) == "in")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700813
814 if (argIt ==
Ed Tanous1abe55e2018-09-05 08:30:59 -0700815 transaction->arguments.end())
816 {
817 transaction->setErrorStatus();
818 return;
819 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700820 if (convertJsonToDbus(
821 m.get(), std::string(argType),
822 *argIt) < 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 {
824 transaction->setErrorStatus();
825 return;
826 }
827
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700828 argIt++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700830 argumentNode =
831 methodNode->NextSiblingElement("arg");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700833
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834 crow::connections::systemBus->async_send(
835 m, [transaction](
836 boost::system::error_code ec,
837 sdbusplus::message::message &m) {
838 if (ec)
839 {
840 transaction->setErrorStatus();
841 return;
842 }
843 transaction->res.jsonValue = {
844 {"status", "ok"},
845 {"message", "200 OK"},
846 {"data", nullptr}};
847 });
848 break;
849 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700850 methodNode =
851 methodNode->NextSiblingElement("method");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700852 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700853 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700854 interfaceNode =
855 interfaceNode->NextSiblingElement("interface");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700856 }
857 }
858 },
859 connectionName, transaction->path,
860 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700861}
862
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700863void handleAction(const crow::Request &req, crow::Response &res,
864 const std::string &objectPath, const std::string &methodName)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700865{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700866 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method "
867 << methodName;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700868 nlohmann::json requestDbusData =
869 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700870
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 if (requestDbusData.is_discarded())
872 {
873 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700874 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875 return;
876 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700877 nlohmann::json::iterator data = requestDbusData.find("data");
878 if (data == requestDbusData.end())
879 {
880 res.result(boost::beast::http::status::bad_request);
881 res.end();
882 return;
883 }
884
885 if (!data->is_array())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700886 {
887 res.result(boost::beast::http::status::bad_request);
888 res.end();
889 return;
890 }
891 auto transaction = std::make_shared<InProgressActionData>(res);
892
893 transaction->path = objectPath;
894 transaction->methodName = methodName;
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700895 transaction->arguments = std::move(*data);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700896 crow::connections::systemBus->async_method_call(
897 [transaction](
898 const boost::system::error_code ec,
899 const std::vector<std::pair<std::string, std::vector<std::string>>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700900 &interfaceNames) {
901 if (ec || interfaceNames.size() <= 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700903 BMCWEB_LOG_ERROR << "Can't find object";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904 transaction->setErrorStatus();
905 return;
906 }
907
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700908 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size()
909 << " object(s)";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910
911 for (const std::pair<std::string, std::vector<std::string>>
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700912 &object : interfaceNames)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700913 {
914 findActionOnInterface(transaction, object.first);
915 }
916 },
917 "xyz.openbmc_project.ObjectMapper",
918 "/xyz/openbmc_project/object_mapper",
919 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
920 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700921}
922
Ed Tanousf839dfe2018-11-12 11:11:15 -0800923void handleList(crow::Response &res, const std::string &objectPath,
924 int32_t depth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925{
926 crow::connections::systemBus->async_method_call(
927 [&res](const boost::system::error_code ec,
928 std::vector<std::string> &objectPaths) {
929 if (ec)
930 {
931 res.result(boost::beast::http::status::internal_server_error);
932 }
933 else
934 {
935 res.jsonValue = {{"status", "ok"},
936 {"message", "200 OK"},
937 {"data", std::move(objectPaths)}};
938 }
939 res.end();
940 },
941 "xyz.openbmc_project.ObjectMapper",
942 "/xyz/openbmc_project/object_mapper",
943 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
Ed Tanousf839dfe2018-11-12 11:11:15 -0800944 depth, std::array<std::string, 0>());
Ed Tanous1abe55e2018-09-05 08:30:59 -0700945}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700946
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700947void handleEnumerate(crow::Response &res, const std::string &objectPath)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700948{
Ed Tanous049a0512018-11-01 13:58:42 -0700949 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath;
950 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res);
951
952 asyncResp->res.jsonValue = {{"message", "200 OK"},
953 {"status", "ok"},
954 {"data", nlohmann::json::object()}};
955
Ed Tanous1abe55e2018-09-05 08:30:59 -0700956 crow::connections::systemBus->async_method_call(
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600957 [objectPath, asyncResp](const boost::system::error_code ec,
958 GetSubTreeType &object_names) {
959 auto transaction = std::make_shared<InProgressEnumerateData>(
960 objectPath, asyncResp);
961
962 transaction->subtree =
963 std::make_shared<GetSubTreeType>(std::move(object_names));
964
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 if (ec)
966 {
Matt Spinler2ae60092018-12-06 10:35:36 -0600967 BMCWEB_LOG_ERROR << "GetSubTree failed on "
968 << transaction->objectPath;
969 setErrorResponse(transaction->asyncResp->res,
970 boost::beast::http::status::not_found,
971 notFoundDesc, notFoundMsg);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700972 return;
973 }
Ed Tanous64530012018-02-06 17:08:16 -0800974
Matt Spinler3ae4ba72018-12-05 14:01:22 -0600975 // Add the data for the path passed in to the results
976 // as if GetSubTree returned it, and continue on enumerating
977 getObjectAndEnumerate(transaction);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700978 },
979 "xyz.openbmc_project.ObjectMapper",
980 "/xyz/openbmc_project/object_mapper",
981 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
Ed Tanous049a0512018-11-01 13:58:42 -0700982 static_cast<int32_t>(0), std::array<const char *, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800983}
Ed Tanous911ac312017-08-15 09:37:42 -0700984
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700985void handleGet(crow::Response &res, std::string &objectPath,
986 std::string &destProperty)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700987{
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700988 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty;
989 std::shared_ptr<std::string> propertyName =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -0700991
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 std::shared_ptr<std::string> path =
993 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -0700994
Ed Tanous1abe55e2018-09-05 08:30:59 -0700995 using GetObjectType =
996 std::vector<std::pair<std::string, std::vector<std::string>>>;
997 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -0700998 [&res, path, propertyName](const boost::system::error_code ec,
999 const GetObjectType &object_names) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001000 if (ec || object_names.size() <= 0)
1001 {
1002 res.result(boost::beast::http::status::not_found);
1003 res.end();
1004 return;
1005 }
1006 std::shared_ptr<nlohmann::json> response =
1007 std::make_shared<nlohmann::json>(nlohmann::json::object());
1008 // The mapper should never give us an empty interface names list,
1009 // but check anyway
1010 for (const std::pair<std::string, std::vector<std::string>>
1011 connection : object_names)
1012 {
1013 const std::vector<std::string> &interfaceNames =
1014 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001015
Ed Tanous1abe55e2018-09-05 08:30:59 -07001016 if (interfaceNames.size() <= 0)
1017 {
1018 res.result(boost::beast::http::status::not_found);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001019 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001020 return;
1021 }
1022
1023 for (const std::string &interface : interfaceNames)
1024 {
1025 crow::connections::systemBus->async_method_call(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001026 [&res, response, propertyName](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001027 const boost::system::error_code ec,
James Feist5b4aa862018-08-16 14:07:01 -07001028 const std::vector<std::pair<
1029 std::string, dbus::utility::DbusVariantType>>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001030 &properties) {
1031 if (ec)
1032 {
1033 BMCWEB_LOG_ERROR << "Bad dbus request error: "
1034 << ec;
1035 }
1036 else
1037 {
James Feist5b4aa862018-08-16 14:07:01 -07001038 for (const std::pair<
1039 std::string,
1040 dbus::utility::DbusVariantType>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001041 &property : properties)
1042 {
1043 // if property name is empty, or matches our
1044 // search query, add it to the response json
1045
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001046 if (propertyName->empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001047 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001048 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001049 [&response, &property](auto &&val) {
1050 (*response)[property.first] =
1051 val;
1052 },
1053 property.second);
1054 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001055 else if (property.first == *propertyName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001056 {
William A. Kennington III0a63b1c2018-10-18 13:37:19 -07001057 sdbusplus::message::variant_ns::visit(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001058 [&response](auto &&val) {
1059 (*response) = val;
1060 },
1061 property.second);
1062 }
1063 }
1064 }
1065 if (response.use_count() == 1)
1066 {
1067 res.jsonValue = {{"status", "ok"},
1068 {"message", "200 OK"},
1069 {"data", *response}};
1070
1071 res.end();
1072 }
1073 },
1074 connection.first, *path,
1075 "org.freedesktop.DBus.Properties", "GetAll", interface);
1076 }
1077 }
1078 },
1079 "xyz.openbmc_project.ObjectMapper",
1080 "/xyz/openbmc_project/object_mapper",
1081 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
1082 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001083}
1084
Ed Tanous1abe55e2018-09-05 08:30:59 -07001085struct AsyncPutRequest
1086{
1087 AsyncPutRequest(crow::Response &res) : res(res)
1088 {
1089 res.jsonValue = {
1090 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
1091 }
1092 ~AsyncPutRequest()
1093 {
1094 if (res.result() == boost::beast::http::status::internal_server_error)
1095 {
1096 // Reset the json object to clear out any data that made it in
1097 // before the error happened todo(ed) handle error condition with
1098 // proper code
1099 res.jsonValue = nlohmann::json::object();
1100 }
1101
1102 if (res.jsonValue.empty())
1103 {
1104 res.result(boost::beast::http::status::forbidden);
1105 res.jsonValue = {
1106 {"status", "error"},
1107 {"message", "403 Forbidden"},
1108 {"data",
1109 {{"message", "The specified property cannot be created: " +
1110 propertyName}}}};
1111 }
1112
1113 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001114 }
1115
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116 void setErrorStatus()
1117 {
1118 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001119 }
1120
Ed Tanous1abe55e2018-09-05 08:30:59 -07001121 crow::Response &res;
1122 std::string objectPath;
1123 std::string propertyName;
1124 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001125};
1126
Ed Tanousd76323e2018-08-07 14:35:40 -07001127void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001128 const std::string &objectPath, const std::string &destProperty)
1129{
1130 nlohmann::json requestDbusData =
1131 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001132
Ed Tanous1abe55e2018-09-05 08:30:59 -07001133 if (requestDbusData.is_discarded())
1134 {
1135 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001136 res.end();
1137 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001138 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001139
Ed Tanous1abe55e2018-09-05 08:30:59 -07001140 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
1141 if (propertyIt == requestDbusData.end())
1142 {
1143 res.result(boost::beast::http::status::bad_request);
1144 res.end();
1145 return;
1146 }
1147 const nlohmann::json &propertySetValue = *propertyIt;
1148 auto transaction = std::make_shared<AsyncPutRequest>(res);
1149 transaction->objectPath = objectPath;
1150 transaction->propertyName = destProperty;
1151 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -07001152
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 using GetObjectType =
1154 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -07001155
Ed Tanous1abe55e2018-09-05 08:30:59 -07001156 crow::connections::systemBus->async_method_call(
1157 [transaction](const boost::system::error_code ec,
1158 const GetObjectType &object_names) {
1159 if (!ec && object_names.size() <= 0)
1160 {
1161 transaction->res.result(boost::beast::http::status::not_found);
1162 return;
1163 }
Ed Tanous911ac312017-08-15 09:37:42 -07001164
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165 for (const std::pair<std::string, std::vector<std::string>>
1166 connection : object_names)
1167 {
1168 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -07001169
Ed Tanous1abe55e2018-09-05 08:30:59 -07001170 crow::connections::systemBus->async_method_call(
1171 [connectionName{std::string(connectionName)},
1172 transaction](const boost::system::error_code ec,
1173 const std::string &introspectXml) {
1174 if (ec)
1175 {
1176 BMCWEB_LOG_ERROR
1177 << "Introspect call failed with error: "
1178 << ec.message()
1179 << " on process: " << connectionName;
1180 transaction->setErrorStatus();
1181 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001182 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001183 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -07001184
Ed Tanous1abe55e2018-09-05 08:30:59 -07001185 doc.Parse(introspectXml.c_str());
1186 tinyxml2::XMLNode *pRoot =
1187 doc.FirstChildElement("node");
1188 if (pRoot == nullptr)
1189 {
1190 BMCWEB_LOG_ERROR << "XML document failed to parse: "
1191 << introspectXml;
1192 transaction->setErrorStatus();
1193 return;
Ed Tanous911ac312017-08-15 09:37:42 -07001194 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001195 tinyxml2::XMLElement *ifaceNode =
1196 pRoot->FirstChildElement("interface");
1197 while (ifaceNode != nullptr)
1198 {
1199 const char *interfaceName =
1200 ifaceNode->Attribute("name");
1201 BMCWEB_LOG_DEBUG << "found interface "
1202 << interfaceName;
1203 tinyxml2::XMLElement *propNode =
1204 ifaceNode->FirstChildElement("property");
1205 while (propNode != nullptr)
1206 {
1207 const char *propertyName =
1208 propNode->Attribute("name");
1209 BMCWEB_LOG_DEBUG << "Found property "
1210 << propertyName;
1211 if (propertyName == transaction->propertyName)
1212 {
1213 const char *argType =
1214 propNode->Attribute("type");
1215 if (argType != nullptr)
1216 {
1217 sdbusplus::message::message m =
1218 crow::connections::systemBus
1219 ->new_method_call(
1220 connectionName.c_str(),
1221 transaction->objectPath
1222 .c_str(),
1223 "org.freedesktop.DBus."
1224 "Properties",
1225 "Set");
1226 m.append(interfaceName,
1227 transaction->propertyName);
1228 int r = sd_bus_message_open_container(
1229 m.get(), SD_BUS_TYPE_VARIANT,
1230 argType);
1231 if (r < 0)
1232 {
1233 transaction->setErrorStatus();
1234 return;
1235 }
1236 r = convertJsonToDbus(
1237 m.get(), argType,
1238 transaction->propertyValue);
1239 if (r < 0)
1240 {
1241 transaction->setErrorStatus();
1242 return;
1243 }
1244 r = sd_bus_message_close_container(
1245 m.get());
1246 if (r < 0)
1247 {
1248 transaction->setErrorStatus();
1249 return;
1250 }
Ed Tanous911ac312017-08-15 09:37:42 -07001251
Ed Tanous1abe55e2018-09-05 08:30:59 -07001252 crow::connections::systemBus
1253 ->async_send(
1254 m,
1255 [transaction](
1256 boost::system::error_code
1257 ec,
1258 sdbusplus::message::message
1259 &m) {
1260 BMCWEB_LOG_DEBUG << "sent";
1261 if (ec)
1262 {
1263 transaction->res
1264 .jsonValue
1265 ["status"] =
1266 "error";
1267 transaction->res
1268 .jsonValue
1269 ["message"] =
1270 ec.message();
1271 }
1272 });
1273 }
1274 }
1275 propNode =
1276 propNode->NextSiblingElement("property");
1277 }
1278 ifaceNode =
1279 ifaceNode->NextSiblingElement("interface");
1280 }
1281 },
1282 connectionName, transaction->objectPath,
1283 "org.freedesktop.DBus.Introspectable", "Introspect");
1284 }
1285 },
1286 "xyz.openbmc_project.ObjectMapper",
1287 "/xyz/openbmc_project/object_mapper",
1288 "xyz.openbmc_project.ObjectMapper", "GetObject",
1289 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001290}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291
Ed Tanous049a0512018-11-01 13:58:42 -07001292inline void handleDBusUrl(const crow::Request &req, crow::Response &res,
1293 std::string &objectPath)
1294{
Ed Tanous049a0512018-11-01 13:58:42 -07001295
1296 // If accessing a single attribute, fill in and update objectPath,
1297 // otherwise leave destProperty blank
1298 std::string destProperty = "";
1299 const char *attrSeperator = "/attr/";
1300 size_t attrPosition = objectPath.find(attrSeperator);
1301 if (attrPosition != objectPath.npos)
1302 {
1303 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator),
1304 objectPath.length());
1305 objectPath = objectPath.substr(0, attrPosition);
1306 }
1307
1308 if (req.method() == "POST"_method)
1309 {
1310 constexpr const char *actionSeperator = "/action/";
1311 size_t actionPosition = objectPath.find(actionSeperator);
1312 if (actionPosition != objectPath.npos)
1313 {
1314 std::string postProperty =
1315 objectPath.substr((actionPosition + strlen(actionSeperator)),
1316 objectPath.length());
1317 objectPath = objectPath.substr(0, actionPosition);
1318 handleAction(req, res, objectPath, postProperty);
1319 return;
1320 }
1321 }
1322 else if (req.method() == "GET"_method)
1323 {
1324 if (boost::ends_with(objectPath, "/enumerate"))
1325 {
1326 objectPath.erase(objectPath.end() - sizeof("enumerate"),
1327 objectPath.end());
1328 handleEnumerate(res, objectPath);
1329 }
1330 else if (boost::ends_with(objectPath, "/list"))
1331 {
1332 objectPath.erase(objectPath.end() - sizeof("list"),
1333 objectPath.end());
1334 handleList(res, objectPath);
1335 }
1336 else
1337 {
Ed Tanousf839dfe2018-11-12 11:11:15 -08001338 // Trim any trailing "/" at the end
1339 if (boost::ends_with(objectPath, "/"))
1340 {
1341 objectPath.pop_back();
1342 handleList(res, objectPath, 1);
1343 }
1344 else
1345 {
1346 handleGet(res, objectPath, destProperty);
1347 }
Ed Tanous049a0512018-11-01 13:58:42 -07001348 }
1349 return;
1350 }
1351 else if (req.method() == "PUT"_method)
1352 {
1353 handlePut(req, res, objectPath, destProperty);
1354 return;
1355 }
1356
1357 res.result(boost::beast::http::status::method_not_allowed);
1358 res.end();
1359}
1360
Ed Tanous1abe55e2018-09-05 08:30:59 -07001361template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1362{
1363 BMCWEB_ROUTE(app, "/bus/")
1364 .methods("GET"_method)(
1365 [](const crow::Request &req, crow::Response &res) {
1366 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1367 {"status", "ok"}};
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001368 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001369 });
1370
1371 BMCWEB_ROUTE(app, "/bus/system/")
1372 .methods("GET"_method)(
1373 [](const crow::Request &req, crow::Response &res) {
1374 auto myCallback = [&res](const boost::system::error_code ec,
1375 std::vector<std::string> &names) {
1376 if (ec)
1377 {
1378 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1379 res.result(
1380 boost::beast::http::status::internal_server_error);
1381 }
1382 else
1383 {
1384 std::sort(names.begin(), names.end());
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001385 res.jsonValue = {{"status", "ok"}};
1386 auto &objectsSub = res.jsonValue["objects"];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001387 for (auto &name : names)
1388 {
1389 objectsSub.push_back({{"name", name}});
1390 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001391 }
1392 res.end();
1393 };
1394 crow::connections::systemBus->async_method_call(
1395 std::move(myCallback), "org.freedesktop.DBus", "/",
1396 "org.freedesktop.DBus", "ListNames");
1397 });
1398
1399 BMCWEB_ROUTE(app, "/list/")
1400 .methods("GET"_method)(
1401 [](const crow::Request &req, crow::Response &res) {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001402 handleList(res, "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001403 });
1404
1405 BMCWEB_ROUTE(app, "/xyz/<path>")
Ed Tanous049a0512018-11-01 13:58:42 -07001406 .methods("GET"_method, "PUT"_method, "POST"_method)(
1407 [](const crow::Request &req, crow::Response &res,
1408 const std::string &path) {
1409 std::string objectPath = "/xyz/" + path;
1410 handleDBusUrl(req, res, objectPath);
1411 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001412
Ed Tanous049a0512018-11-01 13:58:42 -07001413 BMCWEB_ROUTE(app, "/org/<path>")
1414 .methods("GET"_method, "PUT"_method, "POST"_method)(
1415 [](const crow::Request &req, crow::Response &res,
1416 const std::string &path) {
1417 std::string objectPath = "/org/" + path;
1418 handleDBusUrl(req, res, objectPath);
1419 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001420
Ed Tanous1abe55e2018-09-05 08:30:59 -07001421 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1422 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1423 const std::string &dumpId) {
1424 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1425 if (!std::regex_match(dumpId, validFilename))
1426 {
1427 res.result(boost::beast::http::status::not_found);
1428 res.end();
1429 return;
1430 }
1431 std::experimental::filesystem::path loc(
1432 "/var/lib/phosphor-debug-collector/dumps");
1433
1434 loc += dumpId;
1435
1436 if (!std::experimental::filesystem::exists(loc) ||
1437 !std::experimental::filesystem::is_directory(loc))
1438 {
1439 res.result(boost::beast::http::status::not_found);
1440 res.end();
1441 return;
1442 }
1443 std::experimental::filesystem::directory_iterator files(loc);
1444 for (auto &file : files)
1445 {
1446 std::ifstream readFile(file.path());
1447 if (readFile.good())
1448 {
1449 continue;
1450 }
1451 res.addHeader("Content-Type", "application/octet-stream");
1452 res.body() = {std::istreambuf_iterator<char>(readFile),
1453 std::istreambuf_iterator<char>()};
1454 res.end();
1455 }
1456 res.result(boost::beast::http::status::not_found);
1457 res.end();
1458 return;
1459 });
1460
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001461 BMCWEB_ROUTE(app, "/bus/system/<str>/")
Ed Tanous1abe55e2018-09-05 08:30:59 -07001462 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001463 const std::string &Connection) {
1464 introspectObjects(Connection, "/",
1465 std::make_shared<bmcweb::AsyncResp>(res));
1466 });
1467
1468 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1469 .methods("GET"_method,
1470 "POST"_method)([](const crow::Request &req,
1471 crow::Response &res,
1472 const std::string &processName,
1473 const std::string &requestedPath) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001474 std::vector<std::string> strs;
1475 boost::split(strs, requestedPath, boost::is_any_of("/"));
1476 std::string objectPath;
1477 std::string interfaceName;
1478 std::string methodName;
1479 auto it = strs.begin();
1480 if (it == strs.end())
1481 {
1482 objectPath = "/";
1483 }
1484 while (it != strs.end())
1485 {
1486 // Check if segment contains ".". If it does, it must be an
1487 // interface
1488 if (it->find(".") != std::string::npos)
1489 {
1490 break;
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001491 // This check is neccesary as the trailing slash gets parsed
Ed Tanous1abe55e2018-09-05 08:30:59 -07001492 // as part of our <path> specifier above, which causes the
1493 // normal trailing backslash redirector to fail.
1494 }
1495 else if (!it->empty())
1496 {
1497 objectPath += "/" + *it;
1498 }
1499 it++;
1500 }
1501 if (it != strs.end())
1502 {
1503 interfaceName = *it;
1504 it++;
1505
1506 // after interface, we might have a method name
1507 if (it != strs.end())
1508 {
1509 methodName = *it;
1510 it++;
1511 }
1512 }
1513 if (it != strs.end())
1514 {
1515 // if there is more levels past the method name, something went
1516 // wrong, return not found
1517 res.result(boost::beast::http::status::not_found);
1518 res.end();
1519 return;
1520 }
1521 if (interfaceName.empty())
1522 {
1523 crow::connections::systemBus->async_method_call(
1524 [&, processName,
1525 objectPath](const boost::system::error_code ec,
1526 const std::string &introspect_xml) {
1527 if (ec)
1528 {
1529 BMCWEB_LOG_ERROR
1530 << "Introspect call failed with error: "
1531 << ec.message()
1532 << " on process: " << processName
1533 << " path: " << objectPath << "\n";
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001534 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001535 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001536 tinyxml2::XMLDocument doc;
1537
1538 doc.Parse(introspect_xml.c_str());
1539 tinyxml2::XMLNode *pRoot =
1540 doc.FirstChildElement("node");
1541 if (pRoot == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001543 BMCWEB_LOG_ERROR << "XML document failed to parse "
1544 << processName << " " << objectPath
1545 << "\n";
1546 res.jsonValue = {{"status", "XML parse error"}};
1547 res.result(boost::beast::http::status::
1548 internal_server_error);
1549 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001550 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001551
1552 BMCWEB_LOG_DEBUG << introspect_xml;
1553 res.jsonValue = {{"status", "ok"},
1554 {"bus_name", processName},
1555 {"object_path", objectPath}};
1556 nlohmann::json &interfacesArray =
1557 res.jsonValue["interfaces"];
1558 interfacesArray = nlohmann::json::array();
1559 tinyxml2::XMLElement *interface =
1560 pRoot->FirstChildElement("interface");
1561
1562 while (interface != nullptr)
1563 {
1564 const char *ifaceName =
1565 interface->Attribute("name");
1566 if (ifaceName != nullptr)
1567 {
1568 interfacesArray.push_back(
1569 {{"name", ifaceName}});
1570 }
1571
1572 interface =
1573 interface->NextSiblingElement("interface");
1574 }
1575
Ed Tanous1abe55e2018-09-05 08:30:59 -07001576 res.end();
1577 },
1578 processName, objectPath,
1579 "org.freedesktop.DBus.Introspectable", "Introspect");
1580 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001581 else if (methodName.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001582 {
1583 crow::connections::systemBus->async_method_call(
1584 [&, processName, objectPath,
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001585 interfaceName{std::move(interfaceName)}](
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586 const boost::system::error_code ec,
1587 const std::string &introspect_xml) {
1588 if (ec)
1589 {
1590 BMCWEB_LOG_ERROR
1591 << "Introspect call failed with error: "
1592 << ec.message()
1593 << " on process: " << processName
1594 << " path: " << objectPath << "\n";
1595 }
1596 else
1597 {
1598 tinyxml2::XMLDocument doc;
1599
1600 doc.Parse(introspect_xml.c_str());
1601 tinyxml2::XMLNode *pRoot =
1602 doc.FirstChildElement("node");
1603 if (pRoot == nullptr)
1604 {
1605 BMCWEB_LOG_ERROR
1606 << "XML document failed to parse "
1607 << processName << " " << objectPath << "\n";
1608 res.result(boost::beast::http::status::
1609 internal_server_error);
1610 }
1611 else
1612 {
1613 tinyxml2::XMLElement *node =
1614 pRoot->FirstChildElement("node");
1615
1616 // if we know we're the only call, build the
1617 // json directly
Ed Tanous1abe55e2018-09-05 08:30:59 -07001618 tinyxml2::XMLElement *interface =
1619 pRoot->FirstChildElement("interface");
1620
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001621 res.jsonValue = {
1622 {"status", "ok"},
1623 {"bus_name", processName},
1624 {"interface", interfaceName},
1625 {"object_path", objectPath},
1626 {"properties", nlohmann::json::object()}};
1627
1628 nlohmann::json &methodsArray =
1629 res.jsonValue["methods"];
1630 methodsArray = nlohmann::json::array();
1631
1632 nlohmann::json &signalsArray =
1633 res.jsonValue["signals"];
1634 signalsArray = nlohmann::json::array();
1635
Ed Tanous1abe55e2018-09-05 08:30:59 -07001636 while (interface != nullptr)
1637 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001638 const char *ifaceName =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001639 interface->Attribute("name");
1640
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001641 if (ifaceName != nullptr &&
1642 ifaceName == interfaceName)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001643 {
1644 tinyxml2::XMLElement *methods =
1645 interface->FirstChildElement(
1646 "method");
1647 while (methods != nullptr)
1648 {
1649 nlohmann::json argsArray =
1650 nlohmann::json::array();
1651 tinyxml2::XMLElement *arg =
1652 methods->FirstChildElement(
1653 "arg");
1654 while (arg != nullptr)
1655 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001656 nlohmann::json thisArg;
1657 for (const char *fieldName :
1658 std::array<const char *,
1659 3>{"name",
1660 "direction",
1661 "type"})
1662 {
1663 const char *fieldValue =
1664 arg->Attribute(
1665 fieldName);
1666 if (fieldValue != nullptr)
1667 {
1668 thisArg[fieldName] =
1669 fieldValue;
1670 }
1671 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001672 argsArray.push_back(
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001673 std::move(thisArg));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001674 arg = arg->NextSiblingElement(
1675 "arg");
1676 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001677
1678 const char *name =
1679 methods->Attribute("name");
1680 if (name != nullptr)
1681 {
1682 methodsArray.push_back(
1683 {{"name", name},
1684 {"uri", "/bus/system/" +
1685 processName +
1686 objectPath +
1687 "/" +
1688 interfaceName +
1689 "/" + name},
1690 {"args", argsArray}});
1691 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001692 methods =
1693 methods->NextSiblingElement(
1694 "method");
1695 }
1696 tinyxml2::XMLElement *signals =
1697 interface->FirstChildElement(
1698 "signal");
1699 while (signals != nullptr)
1700 {
1701 nlohmann::json argsArray =
1702 nlohmann::json::array();
1703
1704 tinyxml2::XMLElement *arg =
1705 signals->FirstChildElement(
1706 "arg");
1707 while (arg != nullptr)
1708 {
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001709 const char *name =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001710 arg->Attribute("name");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001711 const char *type =
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 arg->Attribute("type");
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001713 if (name != nullptr &&
1714 type != nullptr)
1715 {
1716 argsArray.push_back({
1717 {"name", name},
1718 {"type", type},
1719 });
1720 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 arg = arg->NextSiblingElement(
1722 "arg");
1723 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001724 const char *name =
1725 signals->Attribute("name");
1726 if (name != nullptr)
1727 {
1728 signalsArray.push_back(
1729 {{"name", name},
1730 {"args", argsArray}});
1731 }
1732
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733 signals =
1734 signals->NextSiblingElement(
1735 "signal");
1736 }
1737
Ed Tanous1abe55e2018-09-05 08:30:59 -07001738 break;
1739 }
1740
1741 interface = interface->NextSiblingElement(
1742 "interface");
1743 }
1744 if (interface == nullptr)
1745 {
1746 // if we got to the end of the list and
1747 // never found a match, throw 404
1748 res.result(
1749 boost::beast::http::status::not_found);
1750 }
1751 }
1752 }
1753 res.end();
1754 },
1755 processName, objectPath,
1756 "org.freedesktop.DBus.Introspectable", "Introspect");
1757 }
Ed Tanouse3cb5a32018-08-08 14:16:49 -07001758 else
1759 {
1760 if (req.method() != "POST"_method)
1761 {
1762 res.result(boost::beast::http::status::not_found);
1763 res.end();
1764 return;
1765 }
1766
1767 nlohmann::json requestDbusData =
1768 nlohmann::json::parse(req.body, nullptr, false);
1769
1770 if (requestDbusData.is_discarded())
1771 {
1772 res.result(boost::beast::http::status::bad_request);
1773 res.end();
1774 return;
1775 }
1776 if (!requestDbusData.is_array())
1777 {
1778 res.result(boost::beast::http::status::bad_request);
1779 res.end();
1780 return;
1781 }
1782 auto transaction = std::make_shared<InProgressActionData>(res);
1783
1784 transaction->path = objectPath;
1785 transaction->methodName = methodName;
1786 transaction->arguments = std::move(requestDbusData);
1787
1788 findActionOnInterface(transaction, processName);
1789 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001790 });
1791}
1792} // namespace openbmc_mapper
1793} // namespace crow