1 // 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. 14 15 #pragma once 16 #include <crow/app.h> 17 #include <tinyxml2.h> 18 19 #include <async_resp.hpp> 20 #include <boost/algorithm/string.hpp> 21 #include <boost/container/flat_set.hpp> 22 #include <dbus_singleton.hpp> 23 #include <dbus_utility.hpp> 24 #include <experimental/filesystem> 25 #include <fstream> 26 #include <sdbusplus/message/types.hpp> 27 28 namespace crow 29 { 30 namespace openbmc_mapper 31 { 32 33 using GetSubTreeType = std::vector< 34 std::pair<std::string, 35 std::vector<std::pair<std::string, std::vector<std::string>>>>>; 36 37 void introspectObjects(const std::string &processName, 38 const std::string &objectPath, 39 std::shared_ptr<bmcweb::AsyncResp> transaction) 40 { 41 if (transaction->res.jsonValue.is_null()) 42 { 43 transaction->res.jsonValue = {{"status", "ok"}, 44 {"bus_name", processName}, 45 {"objects", nlohmann::json::array()}}; 46 } 47 48 crow::connections::systemBus->async_method_call( 49 [transaction, processName{std::string(processName)}, 50 objectPath{std::string(objectPath)}]( 51 const boost::system::error_code ec, 52 const std::string &introspect_xml) { 53 if (ec) 54 { 55 BMCWEB_LOG_ERROR 56 << "Introspect call failed with error: " << ec.message() 57 << " on process: " << processName << " path: " << objectPath 58 << "\n"; 59 return; 60 } 61 transaction->res.jsonValue["objects"].push_back( 62 {{"path", objectPath}}); 63 64 tinyxml2::XMLDocument doc; 65 66 doc.Parse(introspect_xml.c_str()); 67 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); 68 if (pRoot == nullptr) 69 { 70 BMCWEB_LOG_ERROR << "XML document failed to parse " 71 << processName << " " << objectPath << "\n"; 72 } 73 else 74 { 75 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); 76 while (node != nullptr) 77 { 78 const char *childPath = node->Attribute("name"); 79 if (childPath != nullptr) 80 { 81 std::string newpath; 82 if (objectPath != "/") 83 { 84 newpath += objectPath; 85 } 86 newpath += std::string("/") + childPath; 87 // introspect the subobjects as well 88 introspectObjects(processName, newpath, transaction); 89 } 90 91 node = node->NextSiblingElement("node"); 92 } 93 } 94 }, 95 processName, objectPath, "org.freedesktop.DBus.Introspectable", 96 "Introspect"); 97 } 98 99 struct InProgressEnumerateData 100 { 101 InProgressEnumerateData(const std::string &objectPath, 102 std::shared_ptr<bmcweb::AsyncResp> asyncResp) : 103 objectPath(objectPath), 104 asyncResp(asyncResp) 105 { 106 } 107 108 ~InProgressEnumerateData() 109 { 110 // TODO 111 } 112 113 const std::string objectPath; 114 std::shared_ptr<GetSubTreeType> subtree; 115 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 116 }; 117 118 void getManagedObjectsForEnumerate( 119 const std::string &object_name, const std::string &object_manager_path, 120 const std::string &connection_name, 121 std::shared_ptr<InProgressEnumerateData> transaction) 122 { 123 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name 124 << " object_manager_path " << object_manager_path 125 << " connection_name " << connection_name; 126 crow::connections::systemBus->async_method_call( 127 [transaction, object_name, 128 connection_name](const boost::system::error_code ec, 129 const dbus::utility::ManagedObjectType &objects) { 130 if (ec) 131 { 132 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name 133 << " on connection " << connection_name 134 << " failed with code " << ec; 135 return; 136 } 137 138 nlohmann::json &dataJson = 139 transaction->asyncResp->res.jsonValue["data"]; 140 141 for (const auto &objectPath : objects) 142 { 143 if (boost::starts_with(objectPath.first.str, object_name)) 144 { 145 BMCWEB_LOG_DEBUG << "Reading object " 146 << objectPath.first.str; 147 nlohmann::json &objectJson = dataJson[objectPath.first.str]; 148 if (objectJson.is_null()) 149 { 150 objectJson = nlohmann::json::object(); 151 } 152 for (const auto &interface : objectPath.second) 153 { 154 for (const auto &property : interface.second) 155 { 156 nlohmann::json &propertyJson = 157 objectJson[property.first]; 158 sdbusplus::message::variant_ns::visit( 159 [&propertyJson](auto &&val) { 160 propertyJson = val; 161 }, 162 property.second); 163 } 164 } 165 } 166 for (const auto &interface : objectPath.second) 167 { 168 if (interface.first == "org.freedesktop.DBus.ObjectManager") 169 { 170 getManagedObjectsForEnumerate( 171 objectPath.first.str, objectPath.first.str, 172 connection_name, transaction); 173 } 174 } 175 } 176 }, 177 connection_name, object_manager_path, 178 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 179 } 180 181 void findObjectManagerPathForEnumerate( 182 const std::string &object_name, const std::string &connection_name, 183 std::shared_ptr<InProgressEnumerateData> transaction) 184 { 185 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name 186 << " on connection:" << connection_name; 187 crow::connections::systemBus->async_method_call( 188 [transaction, object_name, connection_name]( 189 const boost::system::error_code ec, 190 const boost::container::flat_map< 191 std::string, boost::container::flat_map< 192 std::string, std::vector<std::string>>> 193 &objects) { 194 if (ec) 195 { 196 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name 197 << " failed with code " << ec; 198 return; 199 } 200 201 for (const auto &pathGroup : objects) 202 { 203 for (const auto &connectionGroup : pathGroup.second) 204 { 205 if (connectionGroup.first == connection_name) 206 { 207 // Found the object manager path for this resource. 208 getManagedObjectsForEnumerate( 209 object_name, pathGroup.first, connection_name, 210 transaction); 211 return; 212 } 213 } 214 } 215 }, 216 "xyz.openbmc_project.ObjectMapper", 217 "/xyz/openbmc_project/object_mapper", 218 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name, 219 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"}); 220 } 221 222 // Uses GetObject to add the object info about the target /enumerate path to the 223 // results of GetSubTree, as GetSubTree will not return info for the 224 // target path, and then continues on enumerating the rest of the tree. 225 void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction) 226 { 227 using GetObjectType = 228 std::vector<std::pair<std::string, std::vector<std::string>>>; 229 230 crow::connections::systemBus->async_method_call( 231 [transaction](const boost::system::error_code ec, 232 const GetObjectType &objects) { 233 if (ec) 234 { 235 BMCWEB_LOG_ERROR << "GetObject for path " 236 << transaction->objectPath 237 << " failed with code " << ec; 238 return; 239 } 240 241 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath 242 << " has " << objects.size() << " entries"; 243 if (!objects.empty()) 244 { 245 transaction->subtree->emplace_back(transaction->objectPath, 246 objects); 247 } 248 249 // Map indicating connection name, and the path where the object 250 // manager exists 251 boost::container::flat_map<std::string, std::string> connections; 252 253 for (const auto &object : *(transaction->subtree)) 254 { 255 for (const auto &connection : object.second) 256 { 257 std::string &objectManagerPath = 258 connections[connection.first]; 259 for (const auto &interface : connection.second) 260 { 261 BMCWEB_LOG_DEBUG << connection.first 262 << " has interface " << interface; 263 if (interface == "org.freedesktop.DBus.ObjectManager") 264 { 265 BMCWEB_LOG_DEBUG << "found object manager path " 266 << object.first; 267 objectManagerPath = object.first; 268 } 269 } 270 } 271 } 272 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections"; 273 274 for (const auto &connection : connections) 275 { 276 // If we already know where the object manager is, we don't need 277 // to search for it, we can call directly in to 278 // getManagedObjects 279 if (!connection.second.empty()) 280 { 281 getManagedObjectsForEnumerate( 282 transaction->objectPath, connection.second, 283 connection.first, transaction); 284 } 285 else 286 { 287 // otherwise we need to find the object manager path before 288 // we can continue 289 findObjectManagerPathForEnumerate( 290 transaction->objectPath, connection.first, transaction); 291 } 292 } 293 }, 294 "xyz.openbmc_project.ObjectMapper", 295 "/xyz/openbmc_project/object_mapper", 296 "xyz.openbmc_project.ObjectMapper", "GetObject", 297 transaction->objectPath, std::array<const char *, 0>()); 298 } 299 300 // Structure for storing data on an in progress action 301 struct InProgressActionData 302 { 303 InProgressActionData(crow::Response &res) : res(res){}; 304 ~InProgressActionData() 305 { 306 if (res.result() == boost::beast::http::status::internal_server_error) 307 { 308 // Reset the json object to clear out any data that made it in 309 // before the error happened todo(ed) handle error condition with 310 // proper code 311 res.jsonValue = nlohmann::json::object(); 312 } 313 res.end(); 314 } 315 316 void setErrorStatus() 317 { 318 res.result(boost::beast::http::status::internal_server_error); 319 } 320 crow::Response &res; 321 std::string path; 322 std::string methodName; 323 nlohmann::json arguments; 324 }; 325 326 std::vector<std::string> dbusArgSplit(const std::string &string) 327 { 328 std::vector<std::string> ret; 329 if (string.empty()) 330 { 331 return ret; 332 } 333 ret.push_back(""); 334 int containerDepth = 0; 335 336 for (std::string::const_iterator character = string.begin(); 337 character != string.end(); character++) 338 { 339 ret.back() += *character; 340 switch (*character) 341 { 342 case ('a'): 343 break; 344 case ('('): 345 case ('{'): 346 containerDepth++; 347 break; 348 case ('}'): 349 case (')'): 350 containerDepth--; 351 if (containerDepth == 0) 352 { 353 if (character + 1 != string.end()) 354 { 355 ret.push_back(""); 356 } 357 } 358 break; 359 default: 360 if (containerDepth == 0) 361 { 362 if (character + 1 != string.end()) 363 { 364 ret.push_back(""); 365 } 366 } 367 break; 368 } 369 } 370 } 371 372 int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type, 373 const nlohmann::json &input_json) 374 { 375 int r = 0; 376 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() 377 << " to type: " << arg_type; 378 const std::vector<std::string> argTypes = dbusArgSplit(arg_type); 379 380 // Assume a single object for now. 381 const nlohmann::json *j = &input_json; 382 nlohmann::json::const_iterator jIt = input_json.begin(); 383 384 for (const std::string &argCode : argTypes) 385 { 386 // If we are decoding multiple objects, grab the pointer to the 387 // iterator, and increment it for the next loop 388 if (argTypes.size() > 1) 389 { 390 if (jIt == input_json.end()) 391 { 392 return -2; 393 } 394 j = &*jIt; 395 jIt++; 396 } 397 const int64_t *intValue = j->get_ptr<const int64_t *>(); 398 const uint64_t *uintValue = j->get_ptr<const uint64_t *>(); 399 const std::string *stringValue = j->get_ptr<const std::string *>(); 400 const double *doubleValue = j->get_ptr<const double *>(); 401 const bool *b = j->get_ptr<const bool *>(); 402 int64_t v = 0; 403 double d = 0.0; 404 405 // Do some basic type conversions that make sense. uint can be 406 // converted to int. int and uint can be converted to double 407 if (uintValue != nullptr && intValue == nullptr) 408 { 409 v = static_cast<int64_t>(*uintValue); 410 intValue = &v; 411 } 412 if (uintValue != nullptr && doubleValue == nullptr) 413 { 414 d = static_cast<double>(*uintValue); 415 doubleValue = &d; 416 } 417 if (intValue != nullptr && doubleValue == nullptr) 418 { 419 d = static_cast<double>(*intValue); 420 doubleValue = &d; 421 } 422 423 if (argCode == "s") 424 { 425 if (stringValue == nullptr) 426 { 427 return -1; 428 } 429 r = sd_bus_message_append_basic(m, argCode[0], 430 (void *)stringValue->c_str()); 431 if (r < 0) 432 { 433 return r; 434 } 435 } 436 else if (argCode == "i") 437 { 438 if (intValue == nullptr) 439 { 440 return -1; 441 } 442 int32_t i = static_cast<int32_t>(*intValue); 443 r = sd_bus_message_append_basic(m, argCode[0], &i); 444 if (r < 0) 445 { 446 return r; 447 } 448 } 449 else if (argCode == "b") 450 { 451 // lots of ways bool could be represented here. Try them all 452 int boolInt = false; 453 if (intValue != nullptr) 454 { 455 boolInt = *intValue > 0 ? 1 : 0; 456 } 457 else if (b != nullptr) 458 { 459 boolInt = b ? 1 : 0; 460 } 461 else if (stringValue != nullptr) 462 { 463 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0; 464 } 465 else 466 { 467 return -1; 468 } 469 r = sd_bus_message_append_basic(m, argCode[0], &boolInt); 470 if (r < 0) 471 { 472 return r; 473 } 474 } 475 else if (argCode == "n") 476 { 477 if (intValue == nullptr) 478 { 479 return -1; 480 } 481 int16_t n = static_cast<int16_t>(*intValue); 482 r = sd_bus_message_append_basic(m, argCode[0], &n); 483 if (r < 0) 484 { 485 return r; 486 } 487 } 488 else if (argCode == "x") 489 { 490 if (intValue == nullptr) 491 { 492 return -1; 493 } 494 r = sd_bus_message_append_basic(m, argCode[0], intValue); 495 if (r < 0) 496 { 497 return r; 498 } 499 } 500 else if (argCode == "y") 501 { 502 if (uintValue == nullptr) 503 { 504 return -1; 505 } 506 uint8_t y = static_cast<uint8_t>(*uintValue); 507 r = sd_bus_message_append_basic(m, argCode[0], &y); 508 } 509 else if (argCode == "q") 510 { 511 if (uintValue == nullptr) 512 { 513 return -1; 514 } 515 uint16_t q = static_cast<uint16_t>(*uintValue); 516 r = sd_bus_message_append_basic(m, argCode[0], &q); 517 } 518 else if (argCode == "u") 519 { 520 if (uintValue == nullptr) 521 { 522 return -1; 523 } 524 uint32_t u = static_cast<uint32_t>(*uintValue); 525 r = sd_bus_message_append_basic(m, argCode[0], &u); 526 } 527 else if (argCode == "t") 528 { 529 if (uintValue == nullptr) 530 { 531 return -1; 532 } 533 r = sd_bus_message_append_basic(m, argCode[0], uintValue); 534 } 535 else if (argCode == "d") 536 { 537 sd_bus_message_append_basic(m, argCode[0], doubleValue); 538 } 539 else if (boost::starts_with(argCode, "a")) 540 { 541 std::string containedType = argCode.substr(1); 542 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, 543 containedType.c_str()); 544 if (r < 0) 545 { 546 return r; 547 } 548 549 for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); 550 ++it) 551 { 552 r = convertJsonToDbus(m, containedType, *it); 553 if (r < 0) 554 { 555 return r; 556 } 557 558 it++; 559 } 560 sd_bus_message_close_container(m); 561 } 562 else if (boost::starts_with(argCode, "v")) 563 { 564 std::string containedType = argCode.substr(1); 565 BMCWEB_LOG_DEBUG << "variant type: " << argCode 566 << " appending variant of type: " << containedType; 567 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, 568 containedType.c_str()); 569 if (r < 0) 570 { 571 return r; 572 } 573 574 r = convertJsonToDbus(m, containedType, input_json); 575 if (r < 0) 576 { 577 return r; 578 } 579 580 r = sd_bus_message_close_container(m); 581 if (r < 0) 582 { 583 return r; 584 } 585 } 586 else if (boost::starts_with(argCode, "(") && 587 boost::ends_with(argCode, ")")) 588 { 589 std::string containedType = argCode.substr(1, argCode.size() - 1); 590 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, 591 containedType.c_str()); 592 nlohmann::json::const_iterator it = j->begin(); 593 for (const std::string &argCode : dbusArgSplit(arg_type)) 594 { 595 if (it == j->end()) 596 { 597 return -1; 598 } 599 r = convertJsonToDbus(m, argCode, *it); 600 if (r < 0) 601 { 602 return r; 603 } 604 it++; 605 } 606 r = sd_bus_message_close_container(m); 607 } 608 else if (boost::starts_with(argCode, "{") && 609 boost::ends_with(argCode, "}")) 610 { 611 std::string containedType = argCode.substr(1, argCode.size() - 1); 612 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, 613 containedType.c_str()); 614 std::vector<std::string> codes = dbusArgSplit(containedType); 615 if (codes.size() != 2) 616 { 617 return -1; 618 } 619 const std::string &key_type = codes[0]; 620 const std::string &value_type = codes[1]; 621 for (auto it : j->items()) 622 { 623 r = convertJsonToDbus(m, key_type, it.key()); 624 if (r < 0) 625 { 626 return r; 627 } 628 629 r = convertJsonToDbus(m, value_type, it.value()); 630 if (r < 0) 631 { 632 return r; 633 } 634 } 635 r = sd_bus_message_close_container(m); 636 } 637 else 638 { 639 return -2; 640 } 641 if (r < 0) 642 { 643 return r; 644 } 645 646 if (argTypes.size() > 1) 647 { 648 jIt++; 649 } 650 } 651 } 652 653 void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction, 654 const std::string &connectionName) 655 { 656 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " 657 << connectionName; 658 crow::connections::systemBus->async_method_call( 659 [transaction, connectionName{std::string(connectionName)}]( 660 const boost::system::error_code ec, 661 const std::string &introspect_xml) { 662 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; 663 if (ec) 664 { 665 BMCWEB_LOG_ERROR 666 << "Introspect call failed with error: " << ec.message() 667 << " on process: " << connectionName << "\n"; 668 } 669 else 670 { 671 tinyxml2::XMLDocument doc; 672 673 doc.Parse(introspect_xml.data(), introspect_xml.size()); 674 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); 675 if (pRoot == nullptr) 676 { 677 BMCWEB_LOG_ERROR << "XML document failed to parse " 678 << connectionName << "\n"; 679 return; 680 } 681 tinyxml2::XMLElement *interfaceNode = 682 pRoot->FirstChildElement("interface"); 683 while (interfaceNode != nullptr) 684 { 685 const char *thisInterfaceName = 686 interfaceNode->Attribute("name"); 687 if (thisInterfaceName != nullptr) 688 { 689 tinyxml2::XMLElement *methodNode = 690 interfaceNode->FirstChildElement("method"); 691 while (methodNode != nullptr) 692 { 693 const char *thisMethodName = 694 methodNode->Attribute("name"); 695 BMCWEB_LOG_DEBUG << "Found method: " 696 << thisMethodName; 697 if (thisMethodName != nullptr && 698 thisMethodName == transaction->methodName) 699 { 700 BMCWEB_LOG_DEBUG 701 << "Found method named " << thisMethodName 702 << " on interface " << thisInterfaceName; 703 sdbusplus::message::message m = 704 crow::connections::systemBus 705 ->new_method_call( 706 connectionName.c_str(), 707 transaction->path.c_str(), 708 thisInterfaceName, 709 transaction->methodName.c_str()); 710 711 tinyxml2::XMLElement *argumentNode = 712 methodNode->FirstChildElement("arg"); 713 714 nlohmann::json::const_iterator argIt = 715 transaction->arguments.begin(); 716 717 while (argumentNode != nullptr) 718 { 719 const char *argDirection = 720 argumentNode->Attribute("direction"); 721 const char *argType = 722 argumentNode->Attribute("type"); 723 if (argDirection != nullptr && 724 argType != nullptr && 725 std::string(argDirection) == "in") 726 { 727 728 if (argIt == 729 transaction->arguments.end()) 730 { 731 transaction->setErrorStatus(); 732 return; 733 } 734 if (convertJsonToDbus( 735 m.get(), std::string(argType), 736 *argIt) < 0) 737 { 738 transaction->setErrorStatus(); 739 return; 740 } 741 742 argIt++; 743 } 744 argumentNode = 745 methodNode->NextSiblingElement("arg"); 746 } 747 748 crow::connections::systemBus->async_send( 749 m, [transaction]( 750 boost::system::error_code ec, 751 sdbusplus::message::message &m) { 752 if (ec) 753 { 754 transaction->setErrorStatus(); 755 return; 756 } 757 transaction->res.jsonValue = { 758 {"status", "ok"}, 759 {"message", "200 OK"}, 760 {"data", nullptr}}; 761 }); 762 break; 763 } 764 methodNode = 765 methodNode->NextSiblingElement("method"); 766 } 767 } 768 interfaceNode = 769 interfaceNode->NextSiblingElement("interface"); 770 } 771 } 772 }, 773 connectionName, transaction->path, 774 "org.freedesktop.DBus.Introspectable", "Introspect"); 775 } 776 777 void handleAction(const crow::Request &req, crow::Response &res, 778 const std::string &objectPath, const std::string &methodName) 779 { 780 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 781 << methodName; 782 nlohmann::json requestDbusData = 783 nlohmann::json::parse(req.body, nullptr, false); 784 785 if (requestDbusData.is_discarded()) 786 { 787 res.result(boost::beast::http::status::bad_request); 788 res.end(); 789 return; 790 } 791 nlohmann::json::iterator data = requestDbusData.find("data"); 792 if (data == requestDbusData.end()) 793 { 794 res.result(boost::beast::http::status::bad_request); 795 res.end(); 796 return; 797 } 798 799 if (!data->is_array()) 800 { 801 res.result(boost::beast::http::status::bad_request); 802 res.end(); 803 return; 804 } 805 auto transaction = std::make_shared<InProgressActionData>(res); 806 807 transaction->path = objectPath; 808 transaction->methodName = methodName; 809 transaction->arguments = std::move(*data); 810 crow::connections::systemBus->async_method_call( 811 [transaction]( 812 const boost::system::error_code ec, 813 const std::vector<std::pair<std::string, std::vector<std::string>>> 814 &interfaceNames) { 815 if (ec || interfaceNames.size() <= 0) 816 { 817 BMCWEB_LOG_ERROR << "Can't find object"; 818 transaction->setErrorStatus(); 819 return; 820 } 821 822 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 823 << " object(s)"; 824 825 for (const std::pair<std::string, std::vector<std::string>> 826 &object : interfaceNames) 827 { 828 findActionOnInterface(transaction, object.first); 829 } 830 }, 831 "xyz.openbmc_project.ObjectMapper", 832 "/xyz/openbmc_project/object_mapper", 833 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 834 std::array<std::string, 0>()); 835 } 836 837 void handleList(crow::Response &res, const std::string &objectPath, 838 int32_t depth = 0) 839 { 840 crow::connections::systemBus->async_method_call( 841 [&res](const boost::system::error_code ec, 842 std::vector<std::string> &objectPaths) { 843 if (ec) 844 { 845 res.result(boost::beast::http::status::internal_server_error); 846 } 847 else 848 { 849 res.jsonValue = {{"status", "ok"}, 850 {"message", "200 OK"}, 851 {"data", std::move(objectPaths)}}; 852 } 853 res.end(); 854 }, 855 "xyz.openbmc_project.ObjectMapper", 856 "/xyz/openbmc_project/object_mapper", 857 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 858 depth, std::array<std::string, 0>()); 859 } 860 861 void handleEnumerate(crow::Response &res, const std::string &objectPath) 862 { 863 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 864 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); 865 866 asyncResp->res.jsonValue = {{"message", "200 OK"}, 867 {"status", "ok"}, 868 {"data", nlohmann::json::object()}}; 869 870 crow::connections::systemBus->async_method_call( 871 [objectPath, asyncResp](const boost::system::error_code ec, 872 GetSubTreeType &object_names) { 873 auto transaction = std::make_shared<InProgressEnumerateData>( 874 objectPath, asyncResp); 875 876 transaction->subtree = 877 std::make_shared<GetSubTreeType>(std::move(object_names)); 878 879 if (ec) 880 { 881 return; 882 } 883 884 // Add the data for the path passed in to the results 885 // as if GetSubTree returned it, and continue on enumerating 886 getObjectAndEnumerate(transaction); 887 }, 888 "xyz.openbmc_project.ObjectMapper", 889 "/xyz/openbmc_project/object_mapper", 890 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 891 static_cast<int32_t>(0), std::array<const char *, 0>()); 892 } 893 894 void handleGet(crow::Response &res, std::string &objectPath, 895 std::string &destProperty) 896 { 897 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 898 std::shared_ptr<std::string> propertyName = 899 std::make_shared<std::string>(std::move(destProperty)); 900 901 std::shared_ptr<std::string> path = 902 std::make_shared<std::string>(std::move(objectPath)); 903 904 using GetObjectType = 905 std::vector<std::pair<std::string, std::vector<std::string>>>; 906 crow::connections::systemBus->async_method_call( 907 [&res, path, propertyName](const boost::system::error_code ec, 908 const GetObjectType &object_names) { 909 if (ec || object_names.size() <= 0) 910 { 911 res.result(boost::beast::http::status::not_found); 912 res.end(); 913 return; 914 } 915 std::shared_ptr<nlohmann::json> response = 916 std::make_shared<nlohmann::json>(nlohmann::json::object()); 917 // The mapper should never give us an empty interface names list, 918 // but check anyway 919 for (const std::pair<std::string, std::vector<std::string>> 920 connection : object_names) 921 { 922 const std::vector<std::string> &interfaceNames = 923 connection.second; 924 925 if (interfaceNames.size() <= 0) 926 { 927 res.result(boost::beast::http::status::not_found); 928 res.end(); 929 return; 930 } 931 932 for (const std::string &interface : interfaceNames) 933 { 934 crow::connections::systemBus->async_method_call( 935 [&res, response, propertyName]( 936 const boost::system::error_code ec, 937 const std::vector<std::pair< 938 std::string, dbus::utility::DbusVariantType>> 939 &properties) { 940 if (ec) 941 { 942 BMCWEB_LOG_ERROR << "Bad dbus request error: " 943 << ec; 944 } 945 else 946 { 947 for (const std::pair< 948 std::string, 949 dbus::utility::DbusVariantType> 950 &property : properties) 951 { 952 // if property name is empty, or matches our 953 // search query, add it to the response json 954 955 if (propertyName->empty()) 956 { 957 sdbusplus::message::variant_ns::visit( 958 [&response, &property](auto &&val) { 959 (*response)[property.first] = 960 val; 961 }, 962 property.second); 963 } 964 else if (property.first == *propertyName) 965 { 966 sdbusplus::message::variant_ns::visit( 967 [&response](auto &&val) { 968 (*response) = val; 969 }, 970 property.second); 971 } 972 } 973 } 974 if (response.use_count() == 1) 975 { 976 res.jsonValue = {{"status", "ok"}, 977 {"message", "200 OK"}, 978 {"data", *response}}; 979 980 res.end(); 981 } 982 }, 983 connection.first, *path, 984 "org.freedesktop.DBus.Properties", "GetAll", interface); 985 } 986 } 987 }, 988 "xyz.openbmc_project.ObjectMapper", 989 "/xyz/openbmc_project/object_mapper", 990 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 991 std::array<std::string, 0>()); 992 } 993 994 struct AsyncPutRequest 995 { 996 AsyncPutRequest(crow::Response &res) : res(res) 997 { 998 res.jsonValue = { 999 {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; 1000 } 1001 ~AsyncPutRequest() 1002 { 1003 if (res.result() == boost::beast::http::status::internal_server_error) 1004 { 1005 // Reset the json object to clear out any data that made it in 1006 // before the error happened todo(ed) handle error condition with 1007 // proper code 1008 res.jsonValue = nlohmann::json::object(); 1009 } 1010 1011 if (res.jsonValue.empty()) 1012 { 1013 res.result(boost::beast::http::status::forbidden); 1014 res.jsonValue = { 1015 {"status", "error"}, 1016 {"message", "403 Forbidden"}, 1017 {"data", 1018 {{"message", "The specified property cannot be created: " + 1019 propertyName}}}}; 1020 } 1021 1022 res.end(); 1023 } 1024 1025 void setErrorStatus() 1026 { 1027 res.result(boost::beast::http::status::internal_server_error); 1028 } 1029 1030 crow::Response &res; 1031 std::string objectPath; 1032 std::string propertyName; 1033 nlohmann::json propertyValue; 1034 }; 1035 1036 void handlePut(const crow::Request &req, crow::Response &res, 1037 const std::string &objectPath, const std::string &destProperty) 1038 { 1039 nlohmann::json requestDbusData = 1040 nlohmann::json::parse(req.body, nullptr, false); 1041 1042 if (requestDbusData.is_discarded()) 1043 { 1044 res.result(boost::beast::http::status::bad_request); 1045 res.end(); 1046 return; 1047 } 1048 1049 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); 1050 if (propertyIt == requestDbusData.end()) 1051 { 1052 res.result(boost::beast::http::status::bad_request); 1053 res.end(); 1054 return; 1055 } 1056 const nlohmann::json &propertySetValue = *propertyIt; 1057 auto transaction = std::make_shared<AsyncPutRequest>(res); 1058 transaction->objectPath = objectPath; 1059 transaction->propertyName = destProperty; 1060 transaction->propertyValue = propertySetValue; 1061 1062 using GetObjectType = 1063 std::vector<std::pair<std::string, std::vector<std::string>>>; 1064 1065 crow::connections::systemBus->async_method_call( 1066 [transaction](const boost::system::error_code ec, 1067 const GetObjectType &object_names) { 1068 if (!ec && object_names.size() <= 0) 1069 { 1070 transaction->res.result(boost::beast::http::status::not_found); 1071 return; 1072 } 1073 1074 for (const std::pair<std::string, std::vector<std::string>> 1075 connection : object_names) 1076 { 1077 const std::string &connectionName = connection.first; 1078 1079 crow::connections::systemBus->async_method_call( 1080 [connectionName{std::string(connectionName)}, 1081 transaction](const boost::system::error_code ec, 1082 const std::string &introspectXml) { 1083 if (ec) 1084 { 1085 BMCWEB_LOG_ERROR 1086 << "Introspect call failed with error: " 1087 << ec.message() 1088 << " on process: " << connectionName; 1089 transaction->setErrorStatus(); 1090 return; 1091 } 1092 tinyxml2::XMLDocument doc; 1093 1094 doc.Parse(introspectXml.c_str()); 1095 tinyxml2::XMLNode *pRoot = 1096 doc.FirstChildElement("node"); 1097 if (pRoot == nullptr) 1098 { 1099 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1100 << introspectXml; 1101 transaction->setErrorStatus(); 1102 return; 1103 } 1104 tinyxml2::XMLElement *ifaceNode = 1105 pRoot->FirstChildElement("interface"); 1106 while (ifaceNode != nullptr) 1107 { 1108 const char *interfaceName = 1109 ifaceNode->Attribute("name"); 1110 BMCWEB_LOG_DEBUG << "found interface " 1111 << interfaceName; 1112 tinyxml2::XMLElement *propNode = 1113 ifaceNode->FirstChildElement("property"); 1114 while (propNode != nullptr) 1115 { 1116 const char *propertyName = 1117 propNode->Attribute("name"); 1118 BMCWEB_LOG_DEBUG << "Found property " 1119 << propertyName; 1120 if (propertyName == transaction->propertyName) 1121 { 1122 const char *argType = 1123 propNode->Attribute("type"); 1124 if (argType != nullptr) 1125 { 1126 sdbusplus::message::message m = 1127 crow::connections::systemBus 1128 ->new_method_call( 1129 connectionName.c_str(), 1130 transaction->objectPath 1131 .c_str(), 1132 "org.freedesktop.DBus." 1133 "Properties", 1134 "Set"); 1135 m.append(interfaceName, 1136 transaction->propertyName); 1137 int r = sd_bus_message_open_container( 1138 m.get(), SD_BUS_TYPE_VARIANT, 1139 argType); 1140 if (r < 0) 1141 { 1142 transaction->setErrorStatus(); 1143 return; 1144 } 1145 r = convertJsonToDbus( 1146 m.get(), argType, 1147 transaction->propertyValue); 1148 if (r < 0) 1149 { 1150 transaction->setErrorStatus(); 1151 return; 1152 } 1153 r = sd_bus_message_close_container( 1154 m.get()); 1155 if (r < 0) 1156 { 1157 transaction->setErrorStatus(); 1158 return; 1159 } 1160 1161 crow::connections::systemBus 1162 ->async_send( 1163 m, 1164 [transaction]( 1165 boost::system::error_code 1166 ec, 1167 sdbusplus::message::message 1168 &m) { 1169 BMCWEB_LOG_DEBUG << "sent"; 1170 if (ec) 1171 { 1172 transaction->res 1173 .jsonValue 1174 ["status"] = 1175 "error"; 1176 transaction->res 1177 .jsonValue 1178 ["message"] = 1179 ec.message(); 1180 } 1181 }); 1182 } 1183 } 1184 propNode = 1185 propNode->NextSiblingElement("property"); 1186 } 1187 ifaceNode = 1188 ifaceNode->NextSiblingElement("interface"); 1189 } 1190 }, 1191 connectionName, transaction->objectPath, 1192 "org.freedesktop.DBus.Introspectable", "Introspect"); 1193 } 1194 }, 1195 "xyz.openbmc_project.ObjectMapper", 1196 "/xyz/openbmc_project/object_mapper", 1197 "xyz.openbmc_project.ObjectMapper", "GetObject", 1198 transaction->objectPath, std::array<std::string, 0>()); 1199 } 1200 1201 inline void handleDBusUrl(const crow::Request &req, crow::Response &res, 1202 std::string &objectPath) 1203 { 1204 1205 // If accessing a single attribute, fill in and update objectPath, 1206 // otherwise leave destProperty blank 1207 std::string destProperty = ""; 1208 const char *attrSeperator = "/attr/"; 1209 size_t attrPosition = objectPath.find(attrSeperator); 1210 if (attrPosition != objectPath.npos) 1211 { 1212 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 1213 objectPath.length()); 1214 objectPath = objectPath.substr(0, attrPosition); 1215 } 1216 1217 if (req.method() == "POST"_method) 1218 { 1219 constexpr const char *actionSeperator = "/action/"; 1220 size_t actionPosition = objectPath.find(actionSeperator); 1221 if (actionPosition != objectPath.npos) 1222 { 1223 std::string postProperty = 1224 objectPath.substr((actionPosition + strlen(actionSeperator)), 1225 objectPath.length()); 1226 objectPath = objectPath.substr(0, actionPosition); 1227 handleAction(req, res, objectPath, postProperty); 1228 return; 1229 } 1230 } 1231 else if (req.method() == "GET"_method) 1232 { 1233 if (boost::ends_with(objectPath, "/enumerate")) 1234 { 1235 objectPath.erase(objectPath.end() - sizeof("enumerate"), 1236 objectPath.end()); 1237 handleEnumerate(res, objectPath); 1238 } 1239 else if (boost::ends_with(objectPath, "/list")) 1240 { 1241 objectPath.erase(objectPath.end() - sizeof("list"), 1242 objectPath.end()); 1243 handleList(res, objectPath); 1244 } 1245 else 1246 { 1247 // Trim any trailing "/" at the end 1248 if (boost::ends_with(objectPath, "/")) 1249 { 1250 objectPath.pop_back(); 1251 handleList(res, objectPath, 1); 1252 } 1253 else 1254 { 1255 handleGet(res, objectPath, destProperty); 1256 } 1257 } 1258 return; 1259 } 1260 else if (req.method() == "PUT"_method) 1261 { 1262 handlePut(req, res, objectPath, destProperty); 1263 return; 1264 } 1265 1266 res.result(boost::beast::http::status::method_not_allowed); 1267 res.end(); 1268 } 1269 1270 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) 1271 { 1272 BMCWEB_ROUTE(app, "/bus/") 1273 .methods("GET"_method)( 1274 [](const crow::Request &req, crow::Response &res) { 1275 res.jsonValue = {{"busses", {{{"name", "system"}}}}, 1276 {"status", "ok"}}; 1277 res.end(); 1278 }); 1279 1280 BMCWEB_ROUTE(app, "/bus/system/") 1281 .methods("GET"_method)( 1282 [](const crow::Request &req, crow::Response &res) { 1283 auto myCallback = [&res](const boost::system::error_code ec, 1284 std::vector<std::string> &names) { 1285 if (ec) 1286 { 1287 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 1288 res.result( 1289 boost::beast::http::status::internal_server_error); 1290 } 1291 else 1292 { 1293 std::sort(names.begin(), names.end()); 1294 res.jsonValue = {{"status", "ok"}}; 1295 auto &objectsSub = res.jsonValue["objects"]; 1296 for (auto &name : names) 1297 { 1298 objectsSub.push_back({{"name", name}}); 1299 } 1300 } 1301 res.end(); 1302 }; 1303 crow::connections::systemBus->async_method_call( 1304 std::move(myCallback), "org.freedesktop.DBus", "/", 1305 "org.freedesktop.DBus", "ListNames"); 1306 }); 1307 1308 BMCWEB_ROUTE(app, "/list/") 1309 .methods("GET"_method)( 1310 [](const crow::Request &req, crow::Response &res) { 1311 handleList(res, "/"); 1312 }); 1313 1314 BMCWEB_ROUTE(app, "/xyz/<path>") 1315 .methods("GET"_method, "PUT"_method, "POST"_method)( 1316 [](const crow::Request &req, crow::Response &res, 1317 const std::string &path) { 1318 std::string objectPath = "/xyz/" + path; 1319 handleDBusUrl(req, res, objectPath); 1320 }); 1321 1322 BMCWEB_ROUTE(app, "/org/<path>") 1323 .methods("GET"_method, "PUT"_method, "POST"_method)( 1324 [](const crow::Request &req, crow::Response &res, 1325 const std::string &path) { 1326 std::string objectPath = "/org/" + path; 1327 handleDBusUrl(req, res, objectPath); 1328 }); 1329 1330 BMCWEB_ROUTE(app, "/download/dump/<str>/") 1331 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 1332 const std::string &dumpId) { 1333 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); 1334 if (!std::regex_match(dumpId, validFilename)) 1335 { 1336 res.result(boost::beast::http::status::not_found); 1337 res.end(); 1338 return; 1339 } 1340 std::experimental::filesystem::path loc( 1341 "/var/lib/phosphor-debug-collector/dumps"); 1342 1343 loc += dumpId; 1344 1345 if (!std::experimental::filesystem::exists(loc) || 1346 !std::experimental::filesystem::is_directory(loc)) 1347 { 1348 res.result(boost::beast::http::status::not_found); 1349 res.end(); 1350 return; 1351 } 1352 std::experimental::filesystem::directory_iterator files(loc); 1353 for (auto &file : files) 1354 { 1355 std::ifstream readFile(file.path()); 1356 if (readFile.good()) 1357 { 1358 continue; 1359 } 1360 res.addHeader("Content-Type", "application/octet-stream"); 1361 res.body() = {std::istreambuf_iterator<char>(readFile), 1362 std::istreambuf_iterator<char>()}; 1363 res.end(); 1364 } 1365 res.result(boost::beast::http::status::not_found); 1366 res.end(); 1367 return; 1368 }); 1369 1370 BMCWEB_ROUTE(app, "/bus/system/<str>/") 1371 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 1372 const std::string &Connection) { 1373 introspectObjects(Connection, "/", 1374 std::make_shared<bmcweb::AsyncResp>(res)); 1375 }); 1376 1377 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 1378 .methods("GET"_method, 1379 "POST"_method)([](const crow::Request &req, 1380 crow::Response &res, 1381 const std::string &processName, 1382 const std::string &requestedPath) { 1383 std::vector<std::string> strs; 1384 boost::split(strs, requestedPath, boost::is_any_of("/")); 1385 std::string objectPath; 1386 std::string interfaceName; 1387 std::string methodName; 1388 auto it = strs.begin(); 1389 if (it == strs.end()) 1390 { 1391 objectPath = "/"; 1392 } 1393 while (it != strs.end()) 1394 { 1395 // Check if segment contains ".". If it does, it must be an 1396 // interface 1397 if (it->find(".") != std::string::npos) 1398 { 1399 break; 1400 // This check is neccesary as the trailing slash gets parsed 1401 // as part of our <path> specifier above, which causes the 1402 // normal trailing backslash redirector to fail. 1403 } 1404 else if (!it->empty()) 1405 { 1406 objectPath += "/" + *it; 1407 } 1408 it++; 1409 } 1410 if (it != strs.end()) 1411 { 1412 interfaceName = *it; 1413 it++; 1414 1415 // after interface, we might have a method name 1416 if (it != strs.end()) 1417 { 1418 methodName = *it; 1419 it++; 1420 } 1421 } 1422 if (it != strs.end()) 1423 { 1424 // if there is more levels past the method name, something went 1425 // wrong, return not found 1426 res.result(boost::beast::http::status::not_found); 1427 res.end(); 1428 return; 1429 } 1430 if (interfaceName.empty()) 1431 { 1432 crow::connections::systemBus->async_method_call( 1433 [&, processName, 1434 objectPath](const boost::system::error_code ec, 1435 const std::string &introspect_xml) { 1436 if (ec) 1437 { 1438 BMCWEB_LOG_ERROR 1439 << "Introspect call failed with error: " 1440 << ec.message() 1441 << " on process: " << processName 1442 << " path: " << objectPath << "\n"; 1443 return; 1444 } 1445 tinyxml2::XMLDocument doc; 1446 1447 doc.Parse(introspect_xml.c_str()); 1448 tinyxml2::XMLNode *pRoot = 1449 doc.FirstChildElement("node"); 1450 if (pRoot == nullptr) 1451 { 1452 BMCWEB_LOG_ERROR << "XML document failed to parse " 1453 << processName << " " << objectPath 1454 << "\n"; 1455 res.jsonValue = {{"status", "XML parse error"}}; 1456 res.result(boost::beast::http::status:: 1457 internal_server_error); 1458 return; 1459 } 1460 1461 BMCWEB_LOG_DEBUG << introspect_xml; 1462 res.jsonValue = {{"status", "ok"}, 1463 {"bus_name", processName}, 1464 {"object_path", objectPath}}; 1465 nlohmann::json &interfacesArray = 1466 res.jsonValue["interfaces"]; 1467 interfacesArray = nlohmann::json::array(); 1468 tinyxml2::XMLElement *interface = 1469 pRoot->FirstChildElement("interface"); 1470 1471 while (interface != nullptr) 1472 { 1473 const char *ifaceName = 1474 interface->Attribute("name"); 1475 if (ifaceName != nullptr) 1476 { 1477 interfacesArray.push_back( 1478 {{"name", ifaceName}}); 1479 } 1480 1481 interface = 1482 interface->NextSiblingElement("interface"); 1483 } 1484 1485 res.end(); 1486 }, 1487 processName, objectPath, 1488 "org.freedesktop.DBus.Introspectable", "Introspect"); 1489 } 1490 else if (methodName.empty()) 1491 { 1492 crow::connections::systemBus->async_method_call( 1493 [&, processName, objectPath, 1494 interfaceName{std::move(interfaceName)}]( 1495 const boost::system::error_code ec, 1496 const std::string &introspect_xml) { 1497 if (ec) 1498 { 1499 BMCWEB_LOG_ERROR 1500 << "Introspect call failed with error: " 1501 << ec.message() 1502 << " on process: " << processName 1503 << " path: " << objectPath << "\n"; 1504 } 1505 else 1506 { 1507 tinyxml2::XMLDocument doc; 1508 1509 doc.Parse(introspect_xml.c_str()); 1510 tinyxml2::XMLNode *pRoot = 1511 doc.FirstChildElement("node"); 1512 if (pRoot == nullptr) 1513 { 1514 BMCWEB_LOG_ERROR 1515 << "XML document failed to parse " 1516 << processName << " " << objectPath << "\n"; 1517 res.result(boost::beast::http::status:: 1518 internal_server_error); 1519 } 1520 else 1521 { 1522 tinyxml2::XMLElement *node = 1523 pRoot->FirstChildElement("node"); 1524 1525 // if we know we're the only call, build the 1526 // json directly 1527 tinyxml2::XMLElement *interface = 1528 pRoot->FirstChildElement("interface"); 1529 1530 res.jsonValue = { 1531 {"status", "ok"}, 1532 {"bus_name", processName}, 1533 {"interface", interfaceName}, 1534 {"object_path", objectPath}, 1535 {"properties", nlohmann::json::object()}}; 1536 1537 nlohmann::json &methodsArray = 1538 res.jsonValue["methods"]; 1539 methodsArray = nlohmann::json::array(); 1540 1541 nlohmann::json &signalsArray = 1542 res.jsonValue["signals"]; 1543 signalsArray = nlohmann::json::array(); 1544 1545 while (interface != nullptr) 1546 { 1547 const char *ifaceName = 1548 interface->Attribute("name"); 1549 1550 if (ifaceName != nullptr && 1551 ifaceName == interfaceName) 1552 { 1553 tinyxml2::XMLElement *methods = 1554 interface->FirstChildElement( 1555 "method"); 1556 while (methods != nullptr) 1557 { 1558 nlohmann::json argsArray = 1559 nlohmann::json::array(); 1560 tinyxml2::XMLElement *arg = 1561 methods->FirstChildElement( 1562 "arg"); 1563 while (arg != nullptr) 1564 { 1565 nlohmann::json thisArg; 1566 for (const char *fieldName : 1567 std::array<const char *, 1568 3>{"name", 1569 "direction", 1570 "type"}) 1571 { 1572 const char *fieldValue = 1573 arg->Attribute( 1574 fieldName); 1575 if (fieldValue != nullptr) 1576 { 1577 thisArg[fieldName] = 1578 fieldValue; 1579 } 1580 } 1581 argsArray.push_back( 1582 std::move(thisArg)); 1583 arg = arg->NextSiblingElement( 1584 "arg"); 1585 } 1586 1587 const char *name = 1588 methods->Attribute("name"); 1589 if (name != nullptr) 1590 { 1591 methodsArray.push_back( 1592 {{"name", name}, 1593 {"uri", "/bus/system/" + 1594 processName + 1595 objectPath + 1596 "/" + 1597 interfaceName + 1598 "/" + name}, 1599 {"args", argsArray}}); 1600 } 1601 methods = 1602 methods->NextSiblingElement( 1603 "method"); 1604 } 1605 tinyxml2::XMLElement *signals = 1606 interface->FirstChildElement( 1607 "signal"); 1608 while (signals != nullptr) 1609 { 1610 nlohmann::json argsArray = 1611 nlohmann::json::array(); 1612 1613 tinyxml2::XMLElement *arg = 1614 signals->FirstChildElement( 1615 "arg"); 1616 while (arg != nullptr) 1617 { 1618 const char *name = 1619 arg->Attribute("name"); 1620 const char *type = 1621 arg->Attribute("type"); 1622 if (name != nullptr && 1623 type != nullptr) 1624 { 1625 argsArray.push_back({ 1626 {"name", name}, 1627 {"type", type}, 1628 }); 1629 } 1630 arg = arg->NextSiblingElement( 1631 "arg"); 1632 } 1633 const char *name = 1634 signals->Attribute("name"); 1635 if (name != nullptr) 1636 { 1637 signalsArray.push_back( 1638 {{"name", name}, 1639 {"args", argsArray}}); 1640 } 1641 1642 signals = 1643 signals->NextSiblingElement( 1644 "signal"); 1645 } 1646 1647 break; 1648 } 1649 1650 interface = interface->NextSiblingElement( 1651 "interface"); 1652 } 1653 if (interface == nullptr) 1654 { 1655 // if we got to the end of the list and 1656 // never found a match, throw 404 1657 res.result( 1658 boost::beast::http::status::not_found); 1659 } 1660 } 1661 } 1662 res.end(); 1663 }, 1664 processName, objectPath, 1665 "org.freedesktop.DBus.Introspectable", "Introspect"); 1666 } 1667 else 1668 { 1669 if (req.method() != "POST"_method) 1670 { 1671 res.result(boost::beast::http::status::not_found); 1672 res.end(); 1673 return; 1674 } 1675 1676 nlohmann::json requestDbusData = 1677 nlohmann::json::parse(req.body, nullptr, false); 1678 1679 if (requestDbusData.is_discarded()) 1680 { 1681 res.result(boost::beast::http::status::bad_request); 1682 res.end(); 1683 return; 1684 } 1685 if (!requestDbusData.is_array()) 1686 { 1687 res.result(boost::beast::http::status::bad_request); 1688 res.end(); 1689 return; 1690 } 1691 auto transaction = std::make_shared<InProgressActionData>(res); 1692 1693 transaction->path = objectPath; 1694 transaction->methodName = methodName; 1695 transaction->arguments = std::move(requestDbusData); 1696 1697 findActionOnInterface(transaction, processName); 1698 } 1699 }); 1700 } 1701 } // namespace openbmc_mapper 1702 } // namespace crow 1703