blob: 4f6c233ff77cccfae64ed9954b04eec748792f9c [file] [log] [blame]
Ed Tanous911ac312017-08-15 09:37:42 -07001#include <crow/app.h>
Ed Tanous911ac312017-08-15 09:37:42 -07002#include <tinyxml2.h>
Ed Tanous1abe55e2018-09-05 08:30:59 -07003
4#include <boost/algorithm/string.hpp>
5#include <boost/container/flat_set.hpp>
Ed Tanous911ac312017-08-15 09:37:42 -07006#include <dbus_singleton.hpp>
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07007#include <experimental/filesystem>
8#include <fstream>
Ed Tanous911ac312017-08-15 09:37:42 -07009
Ed Tanous1abe55e2018-09-05 08:30:59 -070010namespace crow
11{
12namespace openbmc_mapper
13{
Ed Tanousba9f9a62017-10-11 16:40:35 -070014
Ed Tanous55c7b7a2018-05-22 15:27:24 -070015void introspectObjects(crow::Response &res, std::string process_name,
16 std::string path,
Ed Tanous1abe55e2018-09-05 08:30:59 -070017 std::shared_ptr<nlohmann::json> transaction)
18{
19 crow::connections::systemBus->async_method_call(
20 [&res, transaction, processName{std::move(process_name)},
21 objectPath{std::move(path)}](const boost::system::error_code ec,
22 const std::string &introspect_xml) {
23 if (ec)
24 {
25 BMCWEB_LOG_ERROR
26 << "Introspect call failed with error: " << ec.message()
27 << " on process: " << processName << " path: " << objectPath
28 << "\n";
Ed Tanous911ac312017-08-15 09:37:42 -070029 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070030 else
31 {
32 transaction->push_back({{"path", objectPath}});
33
34 tinyxml2::XMLDocument doc;
35
36 doc.Parse(introspect_xml.c_str());
37 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
38 if (pRoot == nullptr)
39 {
40 BMCWEB_LOG_ERROR << "XML document failed to parse "
41 << processName << " " << objectPath
42 << "\n";
43 }
44 else
45 {
46 tinyxml2::XMLElement *node =
47 pRoot->FirstChildElement("node");
48 while (node != nullptr)
49 {
50 std::string childPath = node->Attribute("name");
51 std::string newpath;
52 if (objectPath != "/")
53 {
54 newpath += objectPath;
55 }
56 newpath += "/" + childPath;
57 // introspect the subobjects as well
58 introspectObjects(res, processName, newpath,
59 transaction);
60
61 node = node->NextSiblingElement("node");
62 }
63 }
64 }
65 // if we're the last outstanding caller, finish the request
66 if (transaction.use_count() == 1)
67 {
68 res.jsonValue = {{"status", "ok"},
69 {"bus_name", processName},
70 {"objects", std::move(*transaction)}};
71 res.end();
72 }
73 },
74 process_name, path, "org.freedesktop.DBus.Introspectable",
75 "Introspect");
Ed Tanous911ac312017-08-15 09:37:42 -070076}
Ed Tanous64530012018-02-06 17:08:16 -080077
Ed Tanousaa2e59c2018-04-12 12:17:20 -070078// A smattering of common types to unpack. TODO(ed) this should really iterate
79// the sdbusplus object directly and build the json response
80using DbusRestVariantType = sdbusplus::message::variant<
81 std::vector<std::tuple<std::string, std::string, std::string>>, std::string,
82 int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t,
83 bool>;
84
85using ManagedObjectType = std::vector<std::pair<
86 sdbusplus::message::object_path,
87 boost::container::flat_map<
88 std::string,
89 boost::container::flat_map<std::string, DbusRestVariantType>>>>;
90
Ed Tanous1abe55e2018-09-05 08:30:59 -070091void getManagedObjectsForEnumerate(const std::string &object_name,
92 const std::string &connection_name,
93 crow::Response &res,
94 std::shared_ptr<nlohmann::json> transaction)
95{
96 crow::connections::systemBus->async_method_call(
97 [&res, transaction](const boost::system::error_code ec,
98 const ManagedObjectType &objects) {
99 if (ec)
100 {
101 BMCWEB_LOG_ERROR << ec;
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700102 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 else
104 {
105 nlohmann::json &dataJson = *transaction;
Ed Tanous64530012018-02-06 17:08:16 -0800106
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 for (auto &objectPath : objects)
108 {
109 BMCWEB_LOG_DEBUG
110 << "Reading object "
111 << static_cast<const std::string &>(objectPath.first);
112 nlohmann::json &objectJson =
113 dataJson[static_cast<const std::string &>(
114 objectPath.first)];
115 if (objectJson.is_null())
116 {
117 objectJson = nlohmann::json::object();
118 }
119 for (const auto &interface : objectPath.second)
120 {
121 for (const auto &property : interface.second)
122 {
123 nlohmann::json &propertyJson =
124 objectJson[property.first];
125 mapbox::util::apply_visitor(
126 [&propertyJson](auto &&val) {
127 propertyJson = val;
128 },
129 property.second);
130 }
131 }
132 }
133 }
134
135 if (transaction.use_count() == 1)
136 {
137 res.jsonValue = {{"message", "200 OK"},
138 {"status", "ok"},
139 {"data", std::move(*transaction)}};
140 res.end();
141 }
142 },
143 connection_name, object_name, "org.freedesktop.DBus.ObjectManager",
144 "GetManagedObjects");
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700145}
Ed Tanous64530012018-02-06 17:08:16 -0800146
147using GetSubTreeType = std::vector<
148 std::pair<std::string,
149 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
150
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700151// Structure for storing data on an in progress action
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152struct InProgressActionData
153{
154 InProgressActionData(crow::Response &res) : res(res){};
155 ~InProgressActionData()
156 {
157 if (res.result() == boost::beast::http::status::internal_server_error)
158 {
159 // Reset the json object to clear out any data that made it in
160 // before the error happened todo(ed) handle error condition with
161 // proper code
162 res.jsonValue = nlohmann::json::object();
163 }
164 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700165 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700166
Ed Tanous1abe55e2018-09-05 08:30:59 -0700167 void setErrorStatus()
168 {
169 res.result(boost::beast::http::status::internal_server_error);
170 }
171 crow::Response &res;
172 std::string path;
173 std::string methodName;
174 nlohmann::json arguments;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700175};
176
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177std::vector<std::string> dbusArgSplit(const std::string &string)
178{
179 std::vector<std::string> ret;
180 if (string.empty())
181 {
182 return ret;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700183 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184 ret.push_back("");
185 int containerDepth = 0;
186
187 for (std::string::const_iterator character = string.begin();
188 character != string.end(); character++)
189 {
190 ret.back() += *character;
191 switch (*character)
192 {
193 case ('a'):
194 break;
195 case ('('):
196 case ('{'):
197 containerDepth++;
198 break;
199 case ('}'):
200 case (')'):
201 containerDepth--;
202 if (containerDepth == 0)
203 {
204 if (character + 1 != string.end())
205 {
206 ret.push_back("");
207 }
208 }
209 break;
210 default:
211 if (containerDepth == 0)
212 {
213 if (character + 1 != string.end())
214 {
215 ret.push_back("");
216 }
217 }
218 break;
219 }
220 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700221}
222
Ed Tanousd76323e2018-08-07 14:35:40 -0700223int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 const nlohmann::json &input_json)
225{
226 int r = 0;
227 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump()
228 << " to type: " << arg_type;
229 const std::vector<std::string> argTypes = dbusArgSplit(arg_type);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700230
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 // Assume a single object for now.
232 const nlohmann::json *j = &input_json;
233 nlohmann::json::const_iterator jIt = input_json.begin();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700234
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 for (const std::string &arg_code : argTypes)
236 {
237 // If we are decoding multiple objects, grab the pointer to the
238 // iterator, and increment it for the next loop
239 if (argTypes.size() > 1)
240 {
241 if (jIt == input_json.end())
242 {
243 return -2;
244 }
245 j = &*jIt;
246 jIt++;
247 }
248 const int64_t *int_value = j->get_ptr<const int64_t *>();
249 const uint64_t *uint_value = j->get_ptr<const uint64_t *>();
250 const std::string *string_value = j->get_ptr<const std::string *>();
251 const double *double_value = j->get_ptr<const double *>();
252 const bool *b = j->get_ptr<const bool *>();
253 int64_t v = 0;
254 double d = 0.0;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700255
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 // Do some basic type conversions that make sense. uint can be
257 // converted to int. int and uint can be converted to double
258 if (uint_value != nullptr && int_value == nullptr)
259 {
260 v = static_cast<int64_t>(*uint_value);
261 int_value = &v;
262 }
263 if (uint_value != nullptr && double_value == nullptr)
264 {
265 d = static_cast<double>(*uint_value);
266 double_value = &d;
267 }
268 if (int_value != nullptr && double_value == nullptr)
269 {
270 d = static_cast<double>(*int_value);
271 double_value = &d;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700272 }
273
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 if (arg_code == "s")
275 {
276 if (string_value == nullptr)
277 {
278 return -1;
279 }
280 r = sd_bus_message_append_basic(m, arg_code[0],
281 (void *)string_value->c_str());
282 if (r < 0)
283 {
284 return r;
285 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700286 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700287 else if (arg_code == "i")
288 {
289 if (int_value == nullptr)
290 {
291 return -1;
292 }
293 int32_t i = static_cast<int32_t>(*int_value);
294 r = sd_bus_message_append_basic(m, arg_code[0], &i);
295 if (r < 0)
296 {
297 return r;
298 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700299 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700300 else if (arg_code == "b")
301 {
302 // lots of ways bool could be represented here. Try them all
303 int bool_int = false;
304 if (int_value != nullptr)
305 {
306 bool_int = *int_value > 0 ? 1 : 0;
307 }
308 else if (b != nullptr)
309 {
310 bool_int = b ? 1 : 0;
311 }
312 else if (string_value != nullptr)
313 {
314 bool_int = boost::istarts_with(*string_value, "t") ? 1 : 0;
315 }
316 else
317 {
318 return -1;
319 }
320 r = sd_bus_message_append_basic(m, arg_code[0], &bool_int);
321 if (r < 0)
322 {
323 return r;
324 }
325 }
326 else if (arg_code == "n")
327 {
328 if (int_value == nullptr)
329 {
330 return -1;
331 }
332 int16_t n = static_cast<int16_t>(*int_value);
333 r = sd_bus_message_append_basic(m, arg_code[0], &n);
334 if (r < 0)
335 {
336 return r;
337 }
338 }
339 else if (arg_code == "x")
340 {
341 if (int_value == nullptr)
342 {
343 return -1;
344 }
345 r = sd_bus_message_append_basic(m, arg_code[0], int_value);
346 if (r < 0)
347 {
348 return r;
349 }
350 }
351 else if (arg_code == "y")
352 {
353 if (uint_value == nullptr)
354 {
355 return -1;
356 }
357 uint8_t y = static_cast<uint8_t>(*uint_value);
358 r = sd_bus_message_append_basic(m, arg_code[0], &y);
359 }
360 else if (arg_code == "q")
361 {
362 if (uint_value == nullptr)
363 {
364 return -1;
365 }
366 uint16_t q = static_cast<uint16_t>(*uint_value);
367 r = sd_bus_message_append_basic(m, arg_code[0], &q);
368 }
369 else if (arg_code == "u")
370 {
371 if (uint_value == nullptr)
372 {
373 return -1;
374 }
375 uint32_t u = static_cast<uint32_t>(*uint_value);
376 r = sd_bus_message_append_basic(m, arg_code[0], &u);
377 }
378 else if (arg_code == "t")
379 {
380 if (uint_value == nullptr)
381 {
382 return -1;
383 }
384 r = sd_bus_message_append_basic(m, arg_code[0], uint_value);
385 }
386 else if (arg_code == "d")
387 {
388 sd_bus_message_append_basic(m, arg_code[0], double_value);
389 }
390 else if (boost::starts_with(arg_code, "a"))
391 {
392 std::string contained_type = arg_code.substr(1);
393 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY,
394 contained_type.c_str());
395 if (r < 0)
396 {
397 return r;
398 }
399
400 for (nlohmann::json::const_iterator it = j->begin(); it != j->end();
401 ++it)
402 {
403 r = convertJsonToDbus(m, contained_type, *it);
404 if (r < 0)
405 {
406 return r;
407 }
408
409 it++;
410 }
411 sd_bus_message_close_container(m);
412 }
413 else if (boost::starts_with(arg_code, "v"))
414 {
415 std::string contained_type = arg_code.substr(1);
416 BMCWEB_LOG_DEBUG
417 << "variant type: " << arg_code
418 << " appending variant of type: " << contained_type;
419 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT,
420 contained_type.c_str());
421 if (r < 0)
422 {
423 return r;
424 }
425
426 r = convertJsonToDbus(m, contained_type, input_json);
427 if (r < 0)
428 {
429 return r;
430 }
431
432 r = sd_bus_message_close_container(m);
433 if (r < 0)
434 {
435 return r;
436 }
437 }
438 else if (boost::starts_with(arg_code, "(") &&
439 boost::ends_with(arg_code, ")"))
440 {
441 std::string contained_type =
442 arg_code.substr(1, arg_code.size() - 1);
443 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT,
444 contained_type.c_str());
445 nlohmann::json::const_iterator it = j->begin();
446 for (const std::string &arg_code : dbusArgSplit(arg_type))
447 {
448 if (it == j->end())
449 {
450 return -1;
451 }
452 r = convertJsonToDbus(m, arg_code, *it);
453 if (r < 0)
454 {
455 return r;
456 }
457 it++;
458 }
459 r = sd_bus_message_close_container(m);
460 }
461 else if (boost::starts_with(arg_code, "{") &&
462 boost::ends_with(arg_code, "}"))
463 {
464 std::string contained_type =
465 arg_code.substr(1, arg_code.size() - 1);
466 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY,
467 contained_type.c_str());
468 std::vector<std::string> codes = dbusArgSplit(contained_type);
469 if (codes.size() != 2)
470 {
471 return -1;
472 }
473 const std::string &key_type = codes[0];
474 const std::string &value_type = codes[1];
475 for (auto it : j->items())
476 {
477 r = convertJsonToDbus(m, key_type, it.key());
478 if (r < 0)
479 {
480 return r;
481 }
482
483 r = convertJsonToDbus(m, value_type, it.value());
484 if (r < 0)
485 {
486 return r;
487 }
488 }
489 r = sd_bus_message_close_container(m);
490 }
491 else
492 {
493 return -2;
494 }
495 if (r < 0)
496 {
497 return r;
Ed Tanous75db20e2018-07-27 13:44:44 -0700498 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700499
Ed Tanous1abe55e2018-09-05 08:30:59 -0700500 if (argTypes.size() > 1)
501 {
502 jIt++;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700503 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700504 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700505}
506
Ed Tanousd76323e2018-08-07 14:35:40 -0700507void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 const std::string &connectionName)
509{
510 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection "
511 << connectionName;
512 crow::connections::systemBus->async_method_call(
513 [transaction, connectionName{std::string(connectionName)}](
514 const boost::system::error_code ec,
515 const std::string &introspect_xml) {
516 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml;
517 if (ec)
518 {
519 BMCWEB_LOG_ERROR
520 << "Introspect call failed with error: " << ec.message()
521 << " on process: " << connectionName << "\n";
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700522 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700523 else
524 {
525 tinyxml2::XMLDocument doc;
526
527 doc.Parse(introspect_xml.c_str());
528 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node");
529 if (pRoot == nullptr)
530 {
531 BMCWEB_LOG_ERROR << "XML document failed to parse "
532 << connectionName << "\n";
533 }
534 else
535 {
536 tinyxml2::XMLElement *interface_node =
537 pRoot->FirstChildElement("interface");
538 while (interface_node != nullptr)
539 {
540 std::string this_interface_name =
541 interface_node->Attribute("name");
542 tinyxml2::XMLElement *method_node =
543 interface_node->FirstChildElement("method");
544 while (method_node != nullptr)
545 {
546 std::string this_methodName =
547 method_node->Attribute("name");
548 BMCWEB_LOG_DEBUG << "Found method: "
549 << this_methodName;
550 if (this_methodName == transaction->methodName)
551 {
552 sdbusplus::message::message m =
553 crow::connections::systemBus
554 ->new_method_call(
555 connectionName.c_str(),
556 transaction->path.c_str(),
557 this_interface_name.c_str(),
558 transaction->methodName.c_str());
559
560 tinyxml2::XMLElement *argument_node =
561 method_node->FirstChildElement("arg");
562
563 nlohmann::json::const_iterator arg_it =
564 transaction->arguments.begin();
565
566 while (argument_node != nullptr)
567 {
568 std::string arg_direction =
569 argument_node->Attribute("direction");
570 if (arg_direction == "in")
571 {
572 std::string arg_type =
573 argument_node->Attribute("type");
574 if (arg_it ==
575 transaction->arguments.end())
576 {
577 transaction->setErrorStatus();
578 return;
579 }
580 if (convertJsonToDbus(m.get(), arg_type,
581 *arg_it) < 0)
582 {
583 transaction->setErrorStatus();
584 return;
585 }
586
587 arg_it++;
588 }
589 argument_node =
590 method_node->NextSiblingElement("arg");
591 }
592 crow::connections::systemBus->async_send(
593 m, [transaction](
594 boost::system::error_code ec,
595 sdbusplus::message::message &m) {
596 if (ec)
597 {
598 transaction->setErrorStatus();
599 return;
600 }
601 transaction->res.jsonValue = {
602 {"status", "ok"},
603 {"message", "200 OK"},
604 {"data", nullptr}};
605 });
606 break;
607 }
608 method_node =
609 method_node->NextSiblingElement("method");
610 }
611 interface_node =
612 interface_node->NextSiblingElement("interface");
613 }
614 }
615 }
616 },
617 connectionName, transaction->path,
618 "org.freedesktop.DBus.Introspectable", "Introspect");
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700619}
620
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700621void handle_action(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700622 const std::string &objectPath, const std::string &methodName)
623{
624 nlohmann::json requestDbusData =
625 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700626
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 if (requestDbusData.is_discarded())
628 {
629 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700630 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631 return;
632 }
633 if (!requestDbusData.is_array())
634 {
635 res.result(boost::beast::http::status::bad_request);
636 res.end();
637 return;
638 }
639 auto transaction = std::make_shared<InProgressActionData>(res);
640
641 transaction->path = objectPath;
642 transaction->methodName = methodName;
643 transaction->arguments = std::move(requestDbusData);
644 crow::connections::systemBus->async_method_call(
645 [transaction](
646 const boost::system::error_code ec,
647 const std::vector<std::pair<std::string, std::vector<std::string>>>
648 &interface_names) {
649 if (ec || interface_names.size() <= 0)
650 {
651 transaction->setErrorStatus();
652 return;
653 }
654
655 BMCWEB_LOG_DEBUG << "GetObject returned objects "
656 << interface_names.size();
657
658 for (const std::pair<std::string, std::vector<std::string>>
659 &object : interface_names)
660 {
661 findActionOnInterface(transaction, object.first);
662 }
663 },
664 "xyz.openbmc_project.ObjectMapper",
665 "/xyz/openbmc_project/object_mapper",
666 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath,
667 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700668}
669
Ed Tanous1abe55e2018-09-05 08:30:59 -0700670void handle_list(crow::Response &res, const std::string &objectPath)
671{
672 crow::connections::systemBus->async_method_call(
673 [&res](const boost::system::error_code ec,
674 std::vector<std::string> &objectPaths) {
675 if (ec)
676 {
677 res.result(boost::beast::http::status::internal_server_error);
678 }
679 else
680 {
681 res.jsonValue = {{"status", "ok"},
682 {"message", "200 OK"},
683 {"data", std::move(objectPaths)}};
684 }
685 res.end();
686 },
687 "xyz.openbmc_project.ObjectMapper",
688 "/xyz/openbmc_project/object_mapper",
689 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath,
690 static_cast<int32_t>(99), std::array<std::string, 0>());
691}
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700692
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693void handle_enumerate(crow::Response &res, const std::string &objectPath)
694{
695 crow::connections::systemBus->async_method_call(
696 [&res, objectPath{std::string(objectPath)}](
697 const boost::system::error_code ec,
698 const GetSubTreeType &object_names) {
699 if (ec)
700 {
701 res.jsonValue = {{"message", "200 OK"},
702 {"status", "ok"},
703 {"data", nlohmann::json::object()}};
Ed Tanous64530012018-02-06 17:08:16 -0800704
Ed Tanous1abe55e2018-09-05 08:30:59 -0700705 res.end();
706 return;
707 }
Ed Tanous64530012018-02-06 17:08:16 -0800708
Ed Tanous1abe55e2018-09-05 08:30:59 -0700709 boost::container::flat_set<std::string> connections;
Ed Tanous64530012018-02-06 17:08:16 -0800710
Ed Tanous1abe55e2018-09-05 08:30:59 -0700711 for (const auto &object : object_names)
712 {
713 for (const auto &Connection : object.second)
714 {
715 connections.insert(Connection.first);
716 }
717 }
718
719 if (connections.size() <= 0)
720 {
721 res.result(boost::beast::http::status::not_found);
722 res.end();
723 return;
724 }
725 auto transaction =
726 std::make_shared<nlohmann::json>(nlohmann::json::object());
727 for (const std::string &Connection : connections)
728 {
729 getManagedObjectsForEnumerate(objectPath, Connection, res,
730 transaction);
731 }
732 },
733 "xyz.openbmc_project.ObjectMapper",
734 "/xyz/openbmc_project/object_mapper",
735 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath,
736 (int32_t)0, std::array<std::string, 0>());
Ed Tanous64530012018-02-06 17:08:16 -0800737}
Ed Tanous911ac312017-08-15 09:37:42 -0700738
Ed Tanousd76323e2018-08-07 14:35:40 -0700739void handle_get(crow::Response &res, std::string &objectPath,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 std::string &destProperty)
741{
742 BMCWEB_LOG_DEBUG << "handle_get: " << objectPath
743 << " prop:" << destProperty;
744 std::shared_ptr<std::string> property_name =
745 std::make_shared<std::string>(std::move(destProperty));
Ed Tanous75db20e2018-07-27 13:44:44 -0700746
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 std::shared_ptr<std::string> path =
748 std::make_shared<std::string>(std::move(objectPath));
Ed Tanous75db20e2018-07-27 13:44:44 -0700749
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 using GetObjectType =
751 std::vector<std::pair<std::string, std::vector<std::string>>>;
752 crow::connections::systemBus->async_method_call(
753 [&res, path, property_name](const boost::system::error_code ec,
754 const GetObjectType &object_names) {
755 if (ec || object_names.size() <= 0)
756 {
757 res.result(boost::beast::http::status::not_found);
758 res.end();
759 return;
760 }
761 std::shared_ptr<nlohmann::json> response =
762 std::make_shared<nlohmann::json>(nlohmann::json::object());
763 // The mapper should never give us an empty interface names list,
764 // but check anyway
765 for (const std::pair<std::string, std::vector<std::string>>
766 connection : object_names)
767 {
768 const std::vector<std::string> &interfaceNames =
769 connection.second;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700770
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 if (interfaceNames.size() <= 0)
772 {
773 res.result(boost::beast::http::status::not_found);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700774 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 return;
776 }
777
778 for (const std::string &interface : interfaceNames)
779 {
780 crow::connections::systemBus->async_method_call(
781 [&res, response, property_name](
782 const boost::system::error_code ec,
783 const std::vector<
784 std::pair<std::string, DbusRestVariantType>>
785 &properties) {
786 if (ec)
787 {
788 BMCWEB_LOG_ERROR << "Bad dbus request error: "
789 << ec;
790 }
791 else
792 {
793 for (const std::pair<std::string,
794 DbusRestVariantType>
795 &property : properties)
796 {
797 // if property name is empty, or matches our
798 // search query, add it to the response json
799
800 if (property_name->empty())
801 {
802 mapbox::util::apply_visitor(
803 [&response, &property](auto &&val) {
804 (*response)[property.first] =
805 val;
806 },
807 property.second);
808 }
809 else if (property.first == *property_name)
810 {
811 mapbox::util::apply_visitor(
812 [&response](auto &&val) {
813 (*response) = val;
814 },
815 property.second);
816 }
817 }
818 }
819 if (response.use_count() == 1)
820 {
821 res.jsonValue = {{"status", "ok"},
822 {"message", "200 OK"},
823 {"data", *response}};
824
825 res.end();
826 }
827 },
828 connection.first, *path,
829 "org.freedesktop.DBus.Properties", "GetAll", interface);
830 }
831 }
832 },
833 "xyz.openbmc_project.ObjectMapper",
834 "/xyz/openbmc_project/object_mapper",
835 "xyz.openbmc_project.ObjectMapper", "GetObject", *path,
836 std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700837}
838
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839struct AsyncPutRequest
840{
841 AsyncPutRequest(crow::Response &res) : res(res)
842 {
843 res.jsonValue = {
844 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}};
845 }
846 ~AsyncPutRequest()
847 {
848 if (res.result() == boost::beast::http::status::internal_server_error)
849 {
850 // Reset the json object to clear out any data that made it in
851 // before the error happened todo(ed) handle error condition with
852 // proper code
853 res.jsonValue = nlohmann::json::object();
854 }
855
856 if (res.jsonValue.empty())
857 {
858 res.result(boost::beast::http::status::forbidden);
859 res.jsonValue = {
860 {"status", "error"},
861 {"message", "403 Forbidden"},
862 {"data",
863 {{"message", "The specified property cannot be created: " +
864 propertyName}}}};
865 }
866
867 res.end();
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700868 }
869
Ed Tanous1abe55e2018-09-05 08:30:59 -0700870 void setErrorStatus()
871 {
872 res.result(boost::beast::http::status::internal_server_error);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700873 }
874
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875 crow::Response &res;
876 std::string objectPath;
877 std::string propertyName;
878 nlohmann::json propertyValue;
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700879};
880
Ed Tanousd76323e2018-08-07 14:35:40 -0700881void handlePut(const crow::Request &req, crow::Response &res,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700882 const std::string &objectPath, const std::string &destProperty)
883{
884 nlohmann::json requestDbusData =
885 nlohmann::json::parse(req.body, nullptr, false);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700886
Ed Tanous1abe55e2018-09-05 08:30:59 -0700887 if (requestDbusData.is_discarded())
888 {
889 res.result(boost::beast::http::status::bad_request);
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700890 res.end();
891 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700892 }
Ed Tanousd4bb9bb2018-05-16 13:36:42 -0700893
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data");
895 if (propertyIt == requestDbusData.end())
896 {
897 res.result(boost::beast::http::status::bad_request);
898 res.end();
899 return;
900 }
901 const nlohmann::json &propertySetValue = *propertyIt;
902 auto transaction = std::make_shared<AsyncPutRequest>(res);
903 transaction->objectPath = objectPath;
904 transaction->propertyName = destProperty;
905 transaction->propertyValue = propertySetValue;
Ed Tanous911ac312017-08-15 09:37:42 -0700906
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 using GetObjectType =
908 std::vector<std::pair<std::string, std::vector<std::string>>>;
Ed Tanous911ac312017-08-15 09:37:42 -0700909
Ed Tanous1abe55e2018-09-05 08:30:59 -0700910 crow::connections::systemBus->async_method_call(
911 [transaction](const boost::system::error_code ec,
912 const GetObjectType &object_names) {
913 if (!ec && object_names.size() <= 0)
914 {
915 transaction->res.result(boost::beast::http::status::not_found);
916 return;
917 }
Ed Tanous911ac312017-08-15 09:37:42 -0700918
Ed Tanous1abe55e2018-09-05 08:30:59 -0700919 for (const std::pair<std::string, std::vector<std::string>>
920 connection : object_names)
921 {
922 const std::string &connectionName = connection.first;
Ed Tanous911ac312017-08-15 09:37:42 -0700923
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 crow::connections::systemBus->async_method_call(
925 [connectionName{std::string(connectionName)},
926 transaction](const boost::system::error_code ec,
927 const std::string &introspectXml) {
928 if (ec)
929 {
930 BMCWEB_LOG_ERROR
931 << "Introspect call failed with error: "
932 << ec.message()
933 << " on process: " << connectionName;
934 transaction->setErrorStatus();
935 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700936 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700937 tinyxml2::XMLDocument doc;
Ed Tanous911ac312017-08-15 09:37:42 -0700938
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 doc.Parse(introspectXml.c_str());
940 tinyxml2::XMLNode *pRoot =
941 doc.FirstChildElement("node");
942 if (pRoot == nullptr)
943 {
944 BMCWEB_LOG_ERROR << "XML document failed to parse: "
945 << introspectXml;
946 transaction->setErrorStatus();
947 return;
Ed Tanous911ac312017-08-15 09:37:42 -0700948 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 tinyxml2::XMLElement *ifaceNode =
950 pRoot->FirstChildElement("interface");
951 while (ifaceNode != nullptr)
952 {
953 const char *interfaceName =
954 ifaceNode->Attribute("name");
955 BMCWEB_LOG_DEBUG << "found interface "
956 << interfaceName;
957 tinyxml2::XMLElement *propNode =
958 ifaceNode->FirstChildElement("property");
959 while (propNode != nullptr)
960 {
961 const char *propertyName =
962 propNode->Attribute("name");
963 BMCWEB_LOG_DEBUG << "Found property "
964 << propertyName;
965 if (propertyName == transaction->propertyName)
966 {
967 const char *argType =
968 propNode->Attribute("type");
969 if (argType != nullptr)
970 {
971 sdbusplus::message::message m =
972 crow::connections::systemBus
973 ->new_method_call(
974 connectionName.c_str(),
975 transaction->objectPath
976 .c_str(),
977 "org.freedesktop.DBus."
978 "Properties",
979 "Set");
980 m.append(interfaceName,
981 transaction->propertyName);
982 int r = sd_bus_message_open_container(
983 m.get(), SD_BUS_TYPE_VARIANT,
984 argType);
985 if (r < 0)
986 {
987 transaction->setErrorStatus();
988 return;
989 }
990 r = convertJsonToDbus(
991 m.get(), argType,
992 transaction->propertyValue);
993 if (r < 0)
994 {
995 transaction->setErrorStatus();
996 return;
997 }
998 r = sd_bus_message_close_container(
999 m.get());
1000 if (r < 0)
1001 {
1002 transaction->setErrorStatus();
1003 return;
1004 }
Ed Tanous911ac312017-08-15 09:37:42 -07001005
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006 crow::connections::systemBus
1007 ->async_send(
1008 m,
1009 [transaction](
1010 boost::system::error_code
1011 ec,
1012 sdbusplus::message::message
1013 &m) {
1014 BMCWEB_LOG_DEBUG << "sent";
1015 if (ec)
1016 {
1017 transaction->res
1018 .jsonValue
1019 ["status"] =
1020 "error";
1021 transaction->res
1022 .jsonValue
1023 ["message"] =
1024 ec.message();
1025 }
1026 });
1027 }
1028 }
1029 propNode =
1030 propNode->NextSiblingElement("property");
1031 }
1032 ifaceNode =
1033 ifaceNode->NextSiblingElement("interface");
1034 }
1035 },
1036 connectionName, transaction->objectPath,
1037 "org.freedesktop.DBus.Introspectable", "Introspect");
1038 }
1039 },
1040 "xyz.openbmc_project.ObjectMapper",
1041 "/xyz/openbmc_project/object_mapper",
1042 "xyz.openbmc_project.ObjectMapper", "GetObject",
1043 transaction->objectPath, std::array<std::string, 0>());
Ed Tanousd4bb9bb2018-05-16 13:36:42 -07001044}
Ed Tanous1abe55e2018-09-05 08:30:59 -07001045
1046template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app)
1047{
1048 BMCWEB_ROUTE(app, "/bus/")
1049 .methods("GET"_method)(
1050 [](const crow::Request &req, crow::Response &res) {
1051 res.jsonValue = {{"busses", {{{"name", "system"}}}},
1052 {"status", "ok"}};
1053 });
1054
1055 BMCWEB_ROUTE(app, "/bus/system/")
1056 .methods("GET"_method)(
1057 [](const crow::Request &req, crow::Response &res) {
1058 auto myCallback = [&res](const boost::system::error_code ec,
1059 std::vector<std::string> &names) {
1060 if (ec)
1061 {
1062 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec;
1063 res.result(
1064 boost::beast::http::status::internal_server_error);
1065 }
1066 else
1067 {
1068 std::sort(names.begin(), names.end());
1069 nlohmann::json j{{"status", "ok"}};
1070 auto &objectsSub = j["objects"];
1071 for (auto &name : names)
1072 {
1073 objectsSub.push_back({{"name", name}});
1074 }
1075 res.jsonValue = std::move(j);
1076 }
1077 res.end();
1078 };
1079 crow::connections::systemBus->async_method_call(
1080 std::move(myCallback), "org.freedesktop.DBus", "/",
1081 "org.freedesktop.DBus", "ListNames");
1082 });
1083
1084 BMCWEB_ROUTE(app, "/list/")
1085 .methods("GET"_method)(
1086 [](const crow::Request &req, crow::Response &res) {
1087 handle_list(res, "/");
1088 });
1089
1090 BMCWEB_ROUTE(app, "/xyz/<path>")
1091 .methods("GET"_method, "PUT"_method,
1092 "POST"_method)([](const crow::Request &req,
1093 crow::Response &res,
1094 const std::string &path) {
1095 std::string objectPath = "/xyz/" + path;
1096
1097 // Trim any trailing "/" at the end
1098 if (boost::ends_with(objectPath, "/"))
1099 {
1100 objectPath.pop_back();
1101 }
1102
1103 // If accessing a single attribute, fill in and update objectPath,
1104 // otherwise leave destProperty blank
1105 std::string destProperty = "";
1106 const char *attrSeperator = "/attr/";
1107 size_t attrPosition = path.find(attrSeperator);
1108 if (attrPosition != path.npos)
1109 {
1110 objectPath = "/xyz/" + path.substr(0, attrPosition);
1111 destProperty = path.substr(attrPosition + strlen(attrSeperator),
1112 path.length());
1113 }
1114
1115 if (req.method() == "POST"_method)
1116 {
1117 constexpr const char *action_seperator = "/action/";
1118 size_t action_position = path.find(action_seperator);
1119 if (action_position != path.npos)
1120 {
1121 objectPath = "/xyz/" + path.substr(0, action_position);
1122 std::string post_property = path.substr(
1123 (action_position + strlen(action_seperator)),
1124 path.length());
1125 handle_action(req, res, objectPath, post_property);
1126 return;
1127 }
1128 }
1129 else if (req.method() == "GET"_method)
1130 {
1131 if (boost::ends_with(objectPath, "/enumerate"))
1132 {
1133 objectPath.erase(objectPath.end() - 10, objectPath.end());
1134 handle_enumerate(res, objectPath);
1135 }
1136 else if (boost::ends_with(objectPath, "/list"))
1137 {
1138 objectPath.erase(objectPath.end() - 5, objectPath.end());
1139 handle_list(res, objectPath);
1140 }
1141 else
1142 {
1143 handle_get(res, objectPath, destProperty);
1144 }
1145 return;
1146 }
1147 else if (req.method() == "PUT"_method)
1148 {
1149 handlePut(req, res, objectPath, destProperty);
1150 return;
1151 }
1152
1153 res.result(boost::beast::http::status::method_not_allowed);
1154 res.end();
1155 });
1156
1157 BMCWEB_ROUTE(app, "/bus/system/<str>/")
1158 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1159 const std::string &Connection) {
1160 std::shared_ptr<nlohmann::json> transaction;
1161 introspectObjects(res, Connection, "/", transaction);
1162 });
1163
1164 BMCWEB_ROUTE(app, "/download/dump/<str>/")
1165 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1166 const std::string &dumpId) {
1167 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$");
1168 if (!std::regex_match(dumpId, validFilename))
1169 {
1170 res.result(boost::beast::http::status::not_found);
1171 res.end();
1172 return;
1173 }
1174 std::experimental::filesystem::path loc(
1175 "/var/lib/phosphor-debug-collector/dumps");
1176
1177 loc += dumpId;
1178
1179 if (!std::experimental::filesystem::exists(loc) ||
1180 !std::experimental::filesystem::is_directory(loc))
1181 {
1182 res.result(boost::beast::http::status::not_found);
1183 res.end();
1184 return;
1185 }
1186 std::experimental::filesystem::directory_iterator files(loc);
1187 for (auto &file : files)
1188 {
1189 std::ifstream readFile(file.path());
1190 if (readFile.good())
1191 {
1192 continue;
1193 }
1194 res.addHeader("Content-Type", "application/octet-stream");
1195 res.body() = {std::istreambuf_iterator<char>(readFile),
1196 std::istreambuf_iterator<char>()};
1197 res.end();
1198 }
1199 res.result(boost::beast::http::status::not_found);
1200 res.end();
1201 return;
1202 });
1203
1204 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>")
1205 .methods("GET"_method)([](const crow::Request &req, crow::Response &res,
1206 const std::string &processName,
1207 const std::string &requestedPath) {
1208 std::vector<std::string> strs;
1209 boost::split(strs, requestedPath, boost::is_any_of("/"));
1210 std::string objectPath;
1211 std::string interfaceName;
1212 std::string methodName;
1213 auto it = strs.begin();
1214 if (it == strs.end())
1215 {
1216 objectPath = "/";
1217 }
1218 while (it != strs.end())
1219 {
1220 // Check if segment contains ".". If it does, it must be an
1221 // interface
1222 if (it->find(".") != std::string::npos)
1223 {
1224 break;
1225 // THis check is neccesary as the trailing slash gets parsed
1226 // as part of our <path> specifier above, which causes the
1227 // normal trailing backslash redirector to fail.
1228 }
1229 else if (!it->empty())
1230 {
1231 objectPath += "/" + *it;
1232 }
1233 it++;
1234 }
1235 if (it != strs.end())
1236 {
1237 interfaceName = *it;
1238 it++;
1239
1240 // after interface, we might have a method name
1241 if (it != strs.end())
1242 {
1243 methodName = *it;
1244 it++;
1245 }
1246 }
1247 if (it != strs.end())
1248 {
1249 // if there is more levels past the method name, something went
1250 // wrong, return not found
1251 res.result(boost::beast::http::status::not_found);
1252 res.end();
1253 return;
1254 }
1255 if (interfaceName.empty())
1256 {
1257 crow::connections::systemBus->async_method_call(
1258 [&, processName,
1259 objectPath](const boost::system::error_code ec,
1260 const std::string &introspect_xml) {
1261 if (ec)
1262 {
1263 BMCWEB_LOG_ERROR
1264 << "Introspect call failed with error: "
1265 << ec.message()
1266 << " on process: " << processName
1267 << " path: " << objectPath << "\n";
1268 }
1269 else
1270 {
1271 tinyxml2::XMLDocument doc;
1272
1273 doc.Parse(introspect_xml.c_str());
1274 tinyxml2::XMLNode *pRoot =
1275 doc.FirstChildElement("node");
1276 if (pRoot == nullptr)
1277 {
1278 BMCWEB_LOG_ERROR
1279 << "XML document failed to parse "
1280 << processName << " " << objectPath << "\n";
1281 res.jsonValue = {{"status", "XML parse error"}};
1282 res.result(boost::beast::http::status::
1283 internal_server_error);
1284 }
1285 else
1286 {
1287 nlohmann::json interfacesArray =
1288 nlohmann::json::array();
1289 tinyxml2::XMLElement *interface =
1290 pRoot->FirstChildElement("interface");
1291
1292 while (interface != nullptr)
1293 {
1294 std::string ifaceName =
1295 interface->Attribute("name");
1296 interfacesArray.push_back(
1297 {{"name", ifaceName}});
1298
1299 interface = interface->NextSiblingElement(
1300 "interface");
1301 }
1302 res.jsonValue = {
1303 {"status", "ok"},
1304 {"bus_name", processName},
1305 {"interfaces", interfacesArray},
1306 {"objectPath", objectPath}};
1307 }
1308 }
1309 res.end();
1310 },
1311 processName, objectPath,
1312 "org.freedesktop.DBus.Introspectable", "Introspect");
1313 }
1314 else
1315 {
1316 crow::connections::systemBus->async_method_call(
1317 [&, processName, objectPath,
1318 interface_name{std::move(interfaceName)}](
1319 const boost::system::error_code ec,
1320 const std::string &introspect_xml) {
1321 if (ec)
1322 {
1323 BMCWEB_LOG_ERROR
1324 << "Introspect call failed with error: "
1325 << ec.message()
1326 << " on process: " << processName
1327 << " path: " << objectPath << "\n";
1328 }
1329 else
1330 {
1331 tinyxml2::XMLDocument doc;
1332
1333 doc.Parse(introspect_xml.c_str());
1334 tinyxml2::XMLNode *pRoot =
1335 doc.FirstChildElement("node");
1336 if (pRoot == nullptr)
1337 {
1338 BMCWEB_LOG_ERROR
1339 << "XML document failed to parse "
1340 << processName << " " << objectPath << "\n";
1341 res.result(boost::beast::http::status::
1342 internal_server_error);
1343 }
1344 else
1345 {
1346 tinyxml2::XMLElement *node =
1347 pRoot->FirstChildElement("node");
1348
1349 // if we know we're the only call, build the
1350 // json directly
1351 nlohmann::json methodsArray =
1352 nlohmann::json::array();
1353 nlohmann::json signalsArray =
1354 nlohmann::json::array();
1355 tinyxml2::XMLElement *interface =
1356 pRoot->FirstChildElement("interface");
1357
1358 while (interface != nullptr)
1359 {
1360 std::string ifaceName =
1361 interface->Attribute("name");
1362
1363 if (ifaceName == interfaceName)
1364 {
1365 tinyxml2::XMLElement *methods =
1366 interface->FirstChildElement(
1367 "method");
1368 while (methods != nullptr)
1369 {
1370 nlohmann::json argsArray =
1371 nlohmann::json::array();
1372 tinyxml2::XMLElement *arg =
1373 methods->FirstChildElement(
1374 "arg");
1375 while (arg != nullptr)
1376 {
1377 argsArray.push_back(
1378 {{"name",
1379 arg->Attribute("name")},
1380 {"type",
1381 arg->Attribute("type")},
1382 {"direction",
1383 arg->Attribute(
1384 "direction")}});
1385 arg = arg->NextSiblingElement(
1386 "arg");
1387 }
1388 methodsArray.push_back(
1389 {{"name",
1390 methods->Attribute("name")},
1391 {"uri",
1392 "/bus/system/" + processName +
1393 objectPath + "/" +
1394 interfaceName + "/" +
1395 methods->Attribute(
1396 "name")},
1397 {"args", argsArray}});
1398 methods =
1399 methods->NextSiblingElement(
1400 "method");
1401 }
1402 tinyxml2::XMLElement *signals =
1403 interface->FirstChildElement(
1404 "signal");
1405 while (signals != nullptr)
1406 {
1407 nlohmann::json argsArray =
1408 nlohmann::json::array();
1409
1410 tinyxml2::XMLElement *arg =
1411 signals->FirstChildElement(
1412 "arg");
1413 while (arg != nullptr)
1414 {
1415 std::string name =
1416 arg->Attribute("name");
1417 std::string type =
1418 arg->Attribute("type");
1419 argsArray.push_back({
1420 {"name", name},
1421 {"type", type},
1422 });
1423 arg = arg->NextSiblingElement(
1424 "arg");
1425 }
1426 signalsArray.push_back(
1427 {{"name",
1428 signals->Attribute("name")},
1429 {"args", argsArray}});
1430 signals =
1431 signals->NextSiblingElement(
1432 "signal");
1433 }
1434
1435 res.jsonValue = {
1436 {"status", "ok"},
1437 {"bus_name", processName},
1438 {"interface", interfaceName},
1439 {"methods", methodsArray},
1440 {"objectPath", objectPath},
1441 {"properties",
1442 nlohmann::json::object()},
1443 {"signals", signalsArray}};
1444
1445 break;
1446 }
1447
1448 interface = interface->NextSiblingElement(
1449 "interface");
1450 }
1451 if (interface == nullptr)
1452 {
1453 // if we got to the end of the list and
1454 // never found a match, throw 404
1455 res.result(
1456 boost::beast::http::status::not_found);
1457 }
1458 }
1459 }
1460 res.end();
1461 },
1462 processName, objectPath,
1463 "org.freedesktop.DBus.Introspectable", "Introspect");
1464 }
1465 });
1466}
1467} // namespace openbmc_mapper
1468} // namespace crow