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 <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 <sdbusplus/message/types.hpp> 25 26 #include <filesystem> 27 #include <fstream> 28 #include <regex> 29 30 namespace crow 31 { 32 namespace openbmc_mapper 33 { 34 35 using GetSubTreeType = std::vector< 36 std::pair<std::string, 37 std::vector<std::pair<std::string, std::vector<std::string>>>>>; 38 39 const constexpr char* notFoundMsg = "404 Not Found"; 40 const constexpr char* badReqMsg = "400 Bad Request"; 41 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed"; 42 const constexpr char* forbiddenMsg = "403 Forbidden"; 43 const constexpr char* methodFailedMsg = "500 Method Call Failed"; 44 const constexpr char* methodOutputFailedMsg = "500 Method Output Error"; 45 const constexpr char* notFoundDesc = 46 "org.freedesktop.DBus.Error.FileNotFound: path or object not found"; 47 const constexpr char* propNotFoundDesc = 48 "The specified property cannot be found"; 49 const constexpr char* noJsonDesc = "No JSON object could be decoded"; 50 const constexpr char* methodNotFoundDesc = 51 "The specified method cannot be found"; 52 const constexpr char* methodNotAllowedDesc = "Method not allowed"; 53 const constexpr char* forbiddenPropDesc = 54 "The specified property cannot be created"; 55 const constexpr char* forbiddenResDesc = 56 "The specified resource cannot be created"; 57 58 inline void setErrorResponse(crow::Response& res, 59 boost::beast::http::status result, 60 const std::string& desc, 61 const std::string_view msg) 62 { 63 res.result(result); 64 res.jsonValue = {{"data", {{"description", desc}}}, 65 {"message", msg}, 66 {"status", "error"}}; 67 } 68 69 inline void introspectObjects(const std::string& processName, 70 const std::string& objectPath, 71 std::shared_ptr<bmcweb::AsyncResp> transaction) 72 { 73 if (transaction->res.jsonValue.is_null()) 74 { 75 transaction->res.jsonValue = {{"status", "ok"}, 76 {"bus_name", processName}, 77 {"objects", nlohmann::json::array()}}; 78 } 79 80 crow::connections::systemBus->async_method_call( 81 [transaction, processName{std::string(processName)}, 82 objectPath{std::string(objectPath)}]( 83 const boost::system::error_code ec, 84 const std::string& introspect_xml) { 85 if (ec) 86 { 87 BMCWEB_LOG_ERROR 88 << "Introspect call failed with error: " << ec.message() 89 << " on process: " << processName << " path: " << objectPath 90 << "\n"; 91 return; 92 } 93 transaction->res.jsonValue["objects"].push_back( 94 {{"path", objectPath}}); 95 96 tinyxml2::XMLDocument doc; 97 98 doc.Parse(introspect_xml.c_str()); 99 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 100 if (pRoot == nullptr) 101 { 102 BMCWEB_LOG_ERROR << "XML document failed to parse " 103 << processName << " " << objectPath << "\n"; 104 } 105 else 106 { 107 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node"); 108 while (node != nullptr) 109 { 110 const char* childPath = node->Attribute("name"); 111 if (childPath != nullptr) 112 { 113 std::string newpath; 114 if (objectPath != "/") 115 { 116 newpath += objectPath; 117 } 118 newpath += std::string("/") + childPath; 119 // introspect the subobjects as well 120 introspectObjects(processName, newpath, transaction); 121 } 122 123 node = node->NextSiblingElement("node"); 124 } 125 } 126 }, 127 processName, objectPath, "org.freedesktop.DBus.Introspectable", 128 "Introspect"); 129 } 130 131 inline void getPropertiesForEnumerate( 132 const std::string& objectPath, const std::string& service, 133 const std::string& interface, std::shared_ptr<bmcweb::AsyncResp> asyncResp) 134 { 135 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " " 136 << service << " " << interface; 137 138 crow::connections::systemBus->async_method_call( 139 [asyncResp, objectPath, service, interface]( 140 const boost::system::error_code ec, 141 const std::vector<std::pair< 142 std::string, dbus::utility::DbusVariantType>>& propertiesList) { 143 if (ec) 144 { 145 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface " 146 << interface << " service " << service 147 << " failed with code " << ec; 148 return; 149 } 150 151 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 152 nlohmann::json& objectJson = dataJson[objectPath]; 153 if (objectJson.is_null()) 154 { 155 objectJson = nlohmann::json::object(); 156 } 157 158 for (const auto& [name, value] : propertiesList) 159 { 160 nlohmann::json& propertyJson = objectJson[name]; 161 std::visit([&propertyJson](auto&& val) { propertyJson = val; }, 162 value); 163 } 164 }, 165 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll", 166 interface); 167 } 168 169 // Find any results that weren't picked up by ObjectManagers, to be 170 // called after all ObjectManagers are searched for and called. 171 inline void findRemainingObjectsForEnumerate( 172 const std::string& objectPath, std::shared_ptr<GetSubTreeType> subtree, 173 std::shared_ptr<bmcweb::AsyncResp> asyncResp) 174 { 175 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate"; 176 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 177 178 for (const auto& [path, interface_map] : *subtree) 179 { 180 if (path == objectPath) 181 { 182 // An enumerate does not return the target path's properties 183 continue; 184 } 185 if (dataJson.find(path) == dataJson.end()) 186 { 187 for (const auto& [service, interfaces] : interface_map) 188 { 189 for (const auto& interface : interfaces) 190 { 191 if (!boost::starts_with(interface, "org.freedesktop.DBus")) 192 { 193 getPropertiesForEnumerate(path, service, interface, 194 asyncResp); 195 } 196 } 197 } 198 } 199 } 200 } 201 202 struct InProgressEnumerateData 203 { 204 InProgressEnumerateData(const std::string& objectPathIn, 205 std::shared_ptr<bmcweb::AsyncResp> asyncRespIn) : 206 objectPath(objectPathIn), 207 asyncResp(asyncRespIn) 208 {} 209 210 ~InProgressEnumerateData() 211 { 212 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp); 213 } 214 215 const std::string objectPath; 216 std::shared_ptr<GetSubTreeType> subtree; 217 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 218 }; 219 220 inline void getManagedObjectsForEnumerate( 221 const std::string& object_name, const std::string& object_manager_path, 222 const std::string& connection_name, 223 std::shared_ptr<InProgressEnumerateData> transaction) 224 { 225 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name 226 << " object_manager_path " << object_manager_path 227 << " connection_name " << connection_name; 228 crow::connections::systemBus->async_method_call( 229 [transaction, object_name, 230 connection_name](const boost::system::error_code ec, 231 const dbus::utility::ManagedObjectType& objects) { 232 if (ec) 233 { 234 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name 235 << " on connection " << connection_name 236 << " failed with code " << ec; 237 return; 238 } 239 240 nlohmann::json& dataJson = 241 transaction->asyncResp->res.jsonValue["data"]; 242 243 for (const auto& objectPath : objects) 244 { 245 if (boost::starts_with(objectPath.first.str, object_name)) 246 { 247 BMCWEB_LOG_DEBUG << "Reading object " 248 << objectPath.first.str; 249 nlohmann::json& objectJson = dataJson[objectPath.first.str]; 250 if (objectJson.is_null()) 251 { 252 objectJson = nlohmann::json::object(); 253 } 254 for (const auto& interface : objectPath.second) 255 { 256 for (const auto& property : interface.second) 257 { 258 nlohmann::json& propertyJson = 259 objectJson[property.first]; 260 std::visit([&propertyJson]( 261 auto&& val) { propertyJson = val; }, 262 property.second); 263 } 264 } 265 } 266 for (const auto& interface : objectPath.second) 267 { 268 if (interface.first == "org.freedesktop.DBus.ObjectManager") 269 { 270 getManagedObjectsForEnumerate( 271 objectPath.first.str, objectPath.first.str, 272 connection_name, transaction); 273 } 274 } 275 } 276 }, 277 connection_name, object_manager_path, 278 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 279 } 280 281 inline void findObjectManagerPathForEnumerate( 282 const std::string& object_name, const std::string& connection_name, 283 std::shared_ptr<InProgressEnumerateData> transaction) 284 { 285 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name 286 << " on connection:" << connection_name; 287 crow::connections::systemBus->async_method_call( 288 [transaction, object_name, connection_name]( 289 const boost::system::error_code ec, 290 const boost::container::flat_map< 291 std::string, boost::container::flat_map< 292 std::string, std::vector<std::string>>>& 293 objects) { 294 if (ec) 295 { 296 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name 297 << " failed with code " << ec; 298 return; 299 } 300 301 for (const auto& pathGroup : objects) 302 { 303 for (const auto& connectionGroup : pathGroup.second) 304 { 305 if (connectionGroup.first == connection_name) 306 { 307 // Found the object manager path for this resource. 308 getManagedObjectsForEnumerate( 309 object_name, pathGroup.first, connection_name, 310 transaction); 311 return; 312 } 313 } 314 } 315 }, 316 "xyz.openbmc_project.ObjectMapper", 317 "/xyz/openbmc_project/object_mapper", 318 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name, 319 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"}); 320 } 321 322 // Uses GetObject to add the object info about the target /enumerate path to 323 // the results of GetSubTree, as GetSubTree will not return info for the 324 // target path, and then continues on enumerating the rest of the tree. 325 inline void 326 getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction) 327 { 328 using GetObjectType = 329 std::vector<std::pair<std::string, std::vector<std::string>>>; 330 331 crow::connections::systemBus->async_method_call( 332 [transaction](const boost::system::error_code ec, 333 const GetObjectType& objects) { 334 if (ec) 335 { 336 BMCWEB_LOG_ERROR << "GetObject for path " 337 << transaction->objectPath 338 << " failed with code " << ec; 339 return; 340 } 341 342 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath 343 << " has " << objects.size() << " entries"; 344 if (!objects.empty()) 345 { 346 transaction->subtree->emplace_back(transaction->objectPath, 347 objects); 348 } 349 350 // Map indicating connection name, and the path where the object 351 // manager exists 352 boost::container::flat_map<std::string, std::string> connections; 353 354 for (const auto& object : *(transaction->subtree)) 355 { 356 for (const auto& connection : object.second) 357 { 358 std::string& objectManagerPath = 359 connections[connection.first]; 360 for (const auto& interface : connection.second) 361 { 362 BMCWEB_LOG_DEBUG << connection.first 363 << " has interface " << interface; 364 if (interface == "org.freedesktop.DBus.ObjectManager") 365 { 366 BMCWEB_LOG_DEBUG << "found object manager path " 367 << object.first; 368 objectManagerPath = object.first; 369 } 370 } 371 } 372 } 373 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections"; 374 375 for (const auto& connection : connections) 376 { 377 // If we already know where the object manager is, we don't 378 // need to search for it, we can call directly in to 379 // getManagedObjects 380 if (!connection.second.empty()) 381 { 382 getManagedObjectsForEnumerate( 383 transaction->objectPath, connection.second, 384 connection.first, transaction); 385 } 386 else 387 { 388 // otherwise we need to find the object manager path 389 // before we can continue 390 findObjectManagerPathForEnumerate( 391 transaction->objectPath, connection.first, transaction); 392 } 393 } 394 }, 395 "xyz.openbmc_project.ObjectMapper", 396 "/xyz/openbmc_project/object_mapper", 397 "xyz.openbmc_project.ObjectMapper", "GetObject", 398 transaction->objectPath, std::array<const char*, 0>()); 399 } 400 401 // Structure for storing data on an in progress action 402 struct InProgressActionData 403 { 404 InProgressActionData(crow::Response& resIn) : res(resIn) 405 {} 406 ~InProgressActionData() 407 { 408 // Methods could have been called across different owners 409 // and interfaces, where some calls failed and some passed. 410 // 411 // The rules for this are: 412 // * if no method was called - error 413 // * if a method failed and none passed - error 414 // (converse: if at least one method passed - OK) 415 // * for the method output: 416 // * if output processing didn't fail, return the data 417 418 // Only deal with method returns if nothing failed earlier 419 if (res.result() == boost::beast::http::status::ok) 420 { 421 if (!methodPassed) 422 { 423 if (!methodFailed) 424 { 425 setErrorResponse(res, boost::beast::http::status::not_found, 426 methodNotFoundDesc, notFoundMsg); 427 } 428 } 429 else 430 { 431 if (outputFailed) 432 { 433 setErrorResponse( 434 res, boost::beast::http::status::internal_server_error, 435 "Method output failure", methodOutputFailedMsg); 436 } 437 else 438 { 439 res.jsonValue = {{"status", "ok"}, 440 {"message", "200 OK"}, 441 {"data", methodResponse}}; 442 } 443 } 444 } 445 446 res.end(); 447 } 448 449 void setErrorStatus(const std::string& desc) 450 { 451 setErrorResponse(res, boost::beast::http::status::bad_request, desc, 452 badReqMsg); 453 } 454 crow::Response& res; 455 std::string path; 456 std::string methodName; 457 std::string interfaceName; 458 bool methodPassed = false; 459 bool methodFailed = false; 460 bool outputFailed = false; 461 bool convertedToArray = false; 462 nlohmann::json methodResponse; 463 nlohmann::json arguments; 464 }; 465 466 inline std::vector<std::string> dbusArgSplit(const std::string& string) 467 { 468 std::vector<std::string> ret; 469 if (string.empty()) 470 { 471 return ret; 472 } 473 ret.emplace_back(""); 474 int containerDepth = 0; 475 476 for (std::string::const_iterator character = string.begin(); 477 character != string.end(); character++) 478 { 479 ret.back() += *character; 480 switch (*character) 481 { 482 case ('a'): 483 break; 484 case ('('): 485 case ('{'): 486 containerDepth++; 487 break; 488 case ('}'): 489 case (')'): 490 containerDepth--; 491 if (containerDepth == 0) 492 { 493 if (character + 1 != string.end()) 494 { 495 ret.emplace_back(""); 496 } 497 } 498 break; 499 default: 500 if (containerDepth == 0) 501 { 502 if (character + 1 != string.end()) 503 { 504 ret.emplace_back(""); 505 } 506 } 507 break; 508 } 509 } 510 511 return ret; 512 } 513 514 inline int convertJsonToDbus(sd_bus_message* m, const std::string& arg_type, 515 const nlohmann::json& input_json) 516 { 517 int r = 0; 518 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() 519 << " to type: " << arg_type; 520 const std::vector<std::string> argTypes = dbusArgSplit(arg_type); 521 522 // Assume a single object for now. 523 const nlohmann::json* j = &input_json; 524 nlohmann::json::const_iterator jIt = input_json.begin(); 525 526 for (const std::string& argCode : argTypes) 527 { 528 // If we are decoding multiple objects, grab the pointer to the 529 // iterator, and increment it for the next loop 530 if (argTypes.size() > 1) 531 { 532 if (jIt == input_json.end()) 533 { 534 return -2; 535 } 536 j = &*jIt; 537 jIt++; 538 } 539 const int64_t* intValue = j->get_ptr<const int64_t*>(); 540 const std::string* stringValue = j->get_ptr<const std::string*>(); 541 const double* doubleValue = j->get_ptr<const double*>(); 542 const bool* b = j->get_ptr<const bool*>(); 543 int64_t v = 0; 544 double d = 0.0; 545 546 // Do some basic type conversions that make sense. uint can be 547 // converted to int. int and uint can be converted to double 548 if (intValue == nullptr) 549 { 550 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 551 if (uintValue != nullptr) 552 { 553 v = static_cast<int64_t>(*uintValue); 554 intValue = &v; 555 } 556 } 557 if (doubleValue == nullptr) 558 { 559 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 560 if (uintValue != nullptr) 561 { 562 d = static_cast<double>(*uintValue); 563 doubleValue = &d; 564 } 565 } 566 if (doubleValue == nullptr) 567 { 568 if (intValue != nullptr) 569 { 570 d = static_cast<double>(*intValue); 571 doubleValue = &d; 572 } 573 } 574 575 if (argCode == "s") 576 { 577 if (stringValue == nullptr) 578 { 579 return -1; 580 } 581 r = sd_bus_message_append_basic( 582 m, argCode[0], static_cast<const void*>(stringValue->data())); 583 if (r < 0) 584 { 585 return r; 586 } 587 } 588 else if (argCode == "i") 589 { 590 if (intValue == nullptr) 591 { 592 return -1; 593 } 594 if ((*intValue < std::numeric_limits<int32_t>::lowest()) || 595 (*intValue > std::numeric_limits<int32_t>::max())) 596 { 597 return -ERANGE; 598 } 599 int32_t i = static_cast<int32_t>(*intValue); 600 r = sd_bus_message_append_basic(m, argCode[0], &i); 601 if (r < 0) 602 { 603 return r; 604 } 605 } 606 else if (argCode == "b") 607 { 608 // lots of ways bool could be represented here. Try them all 609 int boolInt = false; 610 if (intValue != nullptr) 611 { 612 if (*intValue == 1) 613 { 614 boolInt = true; 615 } 616 else if (*intValue == 0) 617 { 618 boolInt = false; 619 } 620 else 621 { 622 return -ERANGE; 623 } 624 } 625 else if (b != nullptr) 626 { 627 boolInt = *b ? 1 : 0; 628 } 629 else if (stringValue != nullptr) 630 { 631 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0; 632 } 633 else 634 { 635 return -1; 636 } 637 r = sd_bus_message_append_basic(m, argCode[0], &boolInt); 638 if (r < 0) 639 { 640 return r; 641 } 642 } 643 else if (argCode == "n") 644 { 645 if (intValue == nullptr) 646 { 647 return -1; 648 } 649 if ((*intValue < std::numeric_limits<int16_t>::lowest()) || 650 (*intValue > std::numeric_limits<int16_t>::max())) 651 { 652 return -ERANGE; 653 } 654 int16_t n = static_cast<int16_t>(*intValue); 655 r = sd_bus_message_append_basic(m, argCode[0], &n); 656 if (r < 0) 657 { 658 return r; 659 } 660 } 661 else if (argCode == "x") 662 { 663 if (intValue == nullptr) 664 { 665 return -1; 666 } 667 r = sd_bus_message_append_basic(m, argCode[0], intValue); 668 if (r < 0) 669 { 670 return r; 671 } 672 } 673 else if (argCode == "y") 674 { 675 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 676 if (uintValue == nullptr) 677 { 678 return -1; 679 } 680 if (*uintValue > std::numeric_limits<uint8_t>::max()) 681 { 682 return -ERANGE; 683 } 684 uint8_t y = static_cast<uint8_t>(*uintValue); 685 r = sd_bus_message_append_basic(m, argCode[0], &y); 686 } 687 else if (argCode == "q") 688 { 689 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 690 if (uintValue == nullptr) 691 { 692 return -1; 693 } 694 if (*uintValue > std::numeric_limits<uint16_t>::max()) 695 { 696 return -ERANGE; 697 } 698 uint16_t q = static_cast<uint16_t>(*uintValue); 699 r = sd_bus_message_append_basic(m, argCode[0], &q); 700 } 701 else if (argCode == "u") 702 { 703 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 704 if (uintValue == nullptr) 705 { 706 return -1; 707 } 708 if (*uintValue > std::numeric_limits<uint32_t>::max()) 709 { 710 return -ERANGE; 711 } 712 uint32_t u = static_cast<uint32_t>(*uintValue); 713 r = sd_bus_message_append_basic(m, argCode[0], &u); 714 } 715 else if (argCode == "t") 716 { 717 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 718 if (uintValue == nullptr) 719 { 720 return -1; 721 } 722 r = sd_bus_message_append_basic(m, argCode[0], uintValue); 723 } 724 else if (argCode == "d") 725 { 726 if (doubleValue == nullptr) 727 { 728 return -1; 729 } 730 if ((*doubleValue < std::numeric_limits<double>::lowest()) || 731 (*doubleValue > std::numeric_limits<double>::max())) 732 { 733 return -ERANGE; 734 } 735 sd_bus_message_append_basic(m, argCode[0], doubleValue); 736 } 737 else if (boost::starts_with(argCode, "a")) 738 { 739 std::string containedType = argCode.substr(1); 740 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, 741 containedType.c_str()); 742 if (r < 0) 743 { 744 return r; 745 } 746 747 for (const auto& it : *j) 748 { 749 r = convertJsonToDbus(m, containedType, it); 750 if (r < 0) 751 { 752 return r; 753 } 754 } 755 sd_bus_message_close_container(m); 756 } 757 else if (boost::starts_with(argCode, "v")) 758 { 759 std::string containedType = argCode.substr(1); 760 BMCWEB_LOG_DEBUG << "variant type: " << argCode 761 << " appending variant of type: " << containedType; 762 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, 763 containedType.c_str()); 764 if (r < 0) 765 { 766 return r; 767 } 768 769 r = convertJsonToDbus(m, containedType, input_json); 770 if (r < 0) 771 { 772 return r; 773 } 774 775 r = sd_bus_message_close_container(m); 776 if (r < 0) 777 { 778 return r; 779 } 780 } 781 else if (boost::starts_with(argCode, "(") && 782 boost::ends_with(argCode, ")")) 783 { 784 std::string containedType = argCode.substr(1, argCode.size() - 1); 785 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, 786 containedType.c_str()); 787 if (r < 0) 788 { 789 return r; 790 } 791 792 nlohmann::json::const_iterator it = j->begin(); 793 for (const std::string& argCode2 : dbusArgSplit(arg_type)) 794 { 795 if (it == j->end()) 796 { 797 return -1; 798 } 799 r = convertJsonToDbus(m, argCode2, *it); 800 if (r < 0) 801 { 802 return r; 803 } 804 it++; 805 } 806 r = sd_bus_message_close_container(m); 807 } 808 else if (boost::starts_with(argCode, "{") && 809 boost::ends_with(argCode, "}")) 810 { 811 std::string containedType = argCode.substr(1, argCode.size() - 1); 812 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, 813 containedType.c_str()); 814 if (r < 0) 815 { 816 return r; 817 } 818 819 std::vector<std::string> codes = dbusArgSplit(containedType); 820 if (codes.size() != 2) 821 { 822 return -1; 823 } 824 const std::string& keyType = codes[0]; 825 const std::string& valueType = codes[1]; 826 for (auto it : j->items()) 827 { 828 r = convertJsonToDbus(m, keyType, it.key()); 829 if (r < 0) 830 { 831 return r; 832 } 833 834 r = convertJsonToDbus(m, valueType, it.value()); 835 if (r < 0) 836 { 837 return r; 838 } 839 } 840 r = sd_bus_message_close_container(m); 841 } 842 else 843 { 844 return -2; 845 } 846 if (r < 0) 847 { 848 return r; 849 } 850 851 if (argTypes.size() > 1) 852 { 853 jIt++; 854 } 855 } 856 857 return r; 858 } 859 860 template <typename T> 861 int readMessageItem(const std::string& typeCode, sdbusplus::message::message& m, 862 nlohmann::json& data) 863 { 864 T value; 865 866 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value); 867 if (r < 0) 868 { 869 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode 870 << " failed!"; 871 return r; 872 } 873 874 data = value; 875 return 0; 876 } 877 878 int convertDBusToJSON(const std::string& returnType, 879 sdbusplus::message::message& m, nlohmann::json& response); 880 881 inline int readDictEntryFromMessage(const std::string& typeCode, 882 sdbusplus::message::message& m, 883 nlohmann::json& object) 884 { 885 std::vector<std::string> types = dbusArgSplit(typeCode); 886 if (types.size() != 2) 887 { 888 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: " 889 << types.size(); 890 return -1; 891 } 892 893 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY, 894 typeCode.c_str()); 895 if (r < 0) 896 { 897 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r; 898 return r; 899 } 900 901 nlohmann::json key; 902 r = convertDBusToJSON(types[0], m, key); 903 if (r < 0) 904 { 905 return r; 906 } 907 908 const std::string* keyPtr = key.get_ptr<const std::string*>(); 909 if (keyPtr == nullptr) 910 { 911 // json doesn't support non-string keys. If we hit this condition, 912 // convert the result to a string so we can proceed 913 key = key.dump(); 914 keyPtr = key.get_ptr<const std::string*>(); 915 // in theory this can't fail now, but lets be paranoid about it 916 // anyway 917 if (keyPtr == nullptr) 918 { 919 return -1; 920 } 921 } 922 nlohmann::json& value = object[*keyPtr]; 923 924 r = convertDBusToJSON(types[1], m, value); 925 if (r < 0) 926 { 927 return r; 928 } 929 930 r = sd_bus_message_exit_container(m.get()); 931 if (r < 0) 932 { 933 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 934 return r; 935 } 936 937 return 0; 938 } 939 940 inline int readArrayFromMessage(const std::string& typeCode, 941 sdbusplus::message::message& m, 942 nlohmann::json& data) 943 { 944 if (typeCode.size() < 2) 945 { 946 BMCWEB_LOG_ERROR << "Type code " << typeCode 947 << " too small for an array"; 948 return -1; 949 } 950 951 std::string containedType = typeCode.substr(1); 952 953 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY, 954 containedType.c_str()); 955 if (r < 0) 956 { 957 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 958 << r; 959 return r; 960 } 961 962 bool dict = boost::starts_with(containedType, "{") && 963 boost::ends_with(containedType, "}"); 964 965 if (dict) 966 { 967 // Remove the { } 968 containedType = containedType.substr(1, containedType.size() - 2); 969 data = nlohmann::json::object(); 970 } 971 else 972 { 973 data = nlohmann::json::array(); 974 } 975 976 while (true) 977 { 978 r = sd_bus_message_at_end(m.get(), false); 979 if (r < 0) 980 { 981 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed"; 982 return r; 983 } 984 985 if (r > 0) 986 { 987 break; 988 } 989 990 // Dictionaries are only ever seen in an array 991 if (dict) 992 { 993 r = readDictEntryFromMessage(containedType, m, data); 994 if (r < 0) 995 { 996 return r; 997 } 998 } 999 else 1000 { 1001 data.push_back(nlohmann::json()); 1002 1003 r = convertDBusToJSON(containedType, m, data.back()); 1004 if (r < 0) 1005 { 1006 return r; 1007 } 1008 } 1009 } 1010 1011 r = sd_bus_message_exit_container(m.get()); 1012 if (r < 0) 1013 { 1014 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1015 return r; 1016 } 1017 1018 return 0; 1019 } 1020 1021 inline int readStructFromMessage(const std::string& typeCode, 1022 sdbusplus::message::message& m, 1023 nlohmann::json& data) 1024 { 1025 if (typeCode.size() < 3) 1026 { 1027 BMCWEB_LOG_ERROR << "Type code " << typeCode 1028 << " too small for a struct"; 1029 return -1; 1030 } 1031 1032 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2); 1033 std::vector<std::string> types = dbusArgSplit(containedTypes); 1034 1035 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT, 1036 containedTypes.c_str()); 1037 if (r < 0) 1038 { 1039 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1040 << r; 1041 return r; 1042 } 1043 1044 for (const std::string& type : types) 1045 { 1046 data.push_back(nlohmann::json()); 1047 r = convertDBusToJSON(type, m, data.back()); 1048 if (r < 0) 1049 { 1050 return r; 1051 } 1052 } 1053 1054 r = sd_bus_message_exit_container(m.get()); 1055 if (r < 0) 1056 { 1057 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1058 return r; 1059 } 1060 return 0; 1061 } 1062 1063 inline int readVariantFromMessage(sdbusplus::message::message& m, 1064 nlohmann::json& data) 1065 { 1066 const char* containerType; 1067 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType); 1068 if (r < 0) 1069 { 1070 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed"; 1071 return r; 1072 } 1073 1074 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT, 1075 containerType); 1076 if (r < 0) 1077 { 1078 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1079 << r; 1080 return r; 1081 } 1082 1083 r = convertDBusToJSON(containerType, m, data); 1084 if (r < 0) 1085 { 1086 return r; 1087 } 1088 1089 r = sd_bus_message_exit_container(m.get()); 1090 if (r < 0) 1091 { 1092 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed"; 1093 return r; 1094 } 1095 1096 return 0; 1097 } 1098 1099 inline int convertDBusToJSON(const std::string& returnType, 1100 sdbusplus::message::message& m, 1101 nlohmann::json& response) 1102 { 1103 int r = 0; 1104 const std::vector<std::string> returnTypes = dbusArgSplit(returnType); 1105 1106 for (const std::string& typeCode : returnTypes) 1107 { 1108 nlohmann::json* thisElement = &response; 1109 if (returnTypes.size() > 1) 1110 { 1111 response.push_back(nlohmann::json{}); 1112 thisElement = &response.back(); 1113 } 1114 1115 if (typeCode == "s") 1116 { 1117 r = readMessageItem<char*>(typeCode, m, *thisElement); 1118 if (r < 0) 1119 { 1120 return r; 1121 } 1122 } 1123 else if (typeCode == "g") 1124 { 1125 r = readMessageItem<char*>(typeCode, m, *thisElement); 1126 if (r < 0) 1127 { 1128 return r; 1129 } 1130 } 1131 else if (typeCode == "o") 1132 { 1133 r = readMessageItem<char*>(typeCode, m, *thisElement); 1134 if (r < 0) 1135 { 1136 return r; 1137 } 1138 } 1139 else if (typeCode == "b") 1140 { 1141 r = readMessageItem<int>(typeCode, m, *thisElement); 1142 if (r < 0) 1143 { 1144 return r; 1145 } 1146 1147 *thisElement = static_cast<bool>(thisElement->get<int>()); 1148 } 1149 else if (typeCode == "u") 1150 { 1151 r = readMessageItem<uint32_t>(typeCode, m, *thisElement); 1152 if (r < 0) 1153 { 1154 return r; 1155 } 1156 } 1157 else if (typeCode == "i") 1158 { 1159 r = readMessageItem<int32_t>(typeCode, m, *thisElement); 1160 if (r < 0) 1161 { 1162 return r; 1163 } 1164 } 1165 else if (typeCode == "x") 1166 { 1167 r = readMessageItem<int64_t>(typeCode, m, *thisElement); 1168 if (r < 0) 1169 { 1170 return r; 1171 } 1172 } 1173 else if (typeCode == "t") 1174 { 1175 r = readMessageItem<uint64_t>(typeCode, m, *thisElement); 1176 if (r < 0) 1177 { 1178 return r; 1179 } 1180 } 1181 else if (typeCode == "n") 1182 { 1183 r = readMessageItem<int16_t>(typeCode, m, *thisElement); 1184 if (r < 0) 1185 { 1186 return r; 1187 } 1188 } 1189 else if (typeCode == "q") 1190 { 1191 r = readMessageItem<uint16_t>(typeCode, m, *thisElement); 1192 if (r < 0) 1193 { 1194 return r; 1195 } 1196 } 1197 else if (typeCode == "y") 1198 { 1199 r = readMessageItem<uint8_t>(typeCode, m, *thisElement); 1200 if (r < 0) 1201 { 1202 return r; 1203 } 1204 } 1205 else if (typeCode == "d") 1206 { 1207 r = readMessageItem<double>(typeCode, m, *thisElement); 1208 if (r < 0) 1209 { 1210 return r; 1211 } 1212 } 1213 else if (typeCode == "h") 1214 { 1215 r = readMessageItem<int>(typeCode, m, *thisElement); 1216 if (r < 0) 1217 { 1218 return r; 1219 } 1220 } 1221 else if (boost::starts_with(typeCode, "a")) 1222 { 1223 r = readArrayFromMessage(typeCode, m, *thisElement); 1224 if (r < 0) 1225 { 1226 return r; 1227 } 1228 } 1229 else if (boost::starts_with(typeCode, "(") && 1230 boost::ends_with(typeCode, ")")) 1231 { 1232 r = readStructFromMessage(typeCode, m, *thisElement); 1233 if (r < 0) 1234 { 1235 return r; 1236 } 1237 } 1238 else if (boost::starts_with(typeCode, "v")) 1239 { 1240 r = readVariantFromMessage(m, *thisElement); 1241 if (r < 0) 1242 { 1243 return r; 1244 } 1245 } 1246 else 1247 { 1248 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode; 1249 return -2; 1250 } 1251 } 1252 1253 return 0; 1254 } 1255 1256 inline void 1257 handleMethodResponse(std::shared_ptr<InProgressActionData> transaction, 1258 sdbusplus::message::message& m, 1259 const std::string& returnType) 1260 { 1261 nlohmann::json data; 1262 1263 int r = convertDBusToJSON(returnType, m, data); 1264 if (r < 0) 1265 { 1266 transaction->outputFailed = true; 1267 return; 1268 } 1269 1270 if (data.is_null()) 1271 { 1272 return; 1273 } 1274 1275 if (transaction->methodResponse.is_null()) 1276 { 1277 transaction->methodResponse = std::move(data); 1278 return; 1279 } 1280 1281 // If they're both dictionaries or arrays, merge into one. 1282 // Otherwise, make the results an array with every result 1283 // an entry. Could also just fail in that case, but it 1284 // seems better to get the data back somehow. 1285 1286 if (transaction->methodResponse.is_object() && data.is_object()) 1287 { 1288 for (const auto& obj : data.items()) 1289 { 1290 // Note: Will overwrite the data for a duplicate key 1291 transaction->methodResponse.emplace(obj.key(), 1292 std::move(obj.value())); 1293 } 1294 return; 1295 } 1296 1297 if (transaction->methodResponse.is_array() && data.is_array()) 1298 { 1299 for (auto& obj : data) 1300 { 1301 transaction->methodResponse.push_back(std::move(obj)); 1302 } 1303 return; 1304 } 1305 1306 if (!transaction->convertedToArray) 1307 { 1308 // They are different types. May as well turn them into an array 1309 nlohmann::json j = std::move(transaction->methodResponse); 1310 transaction->methodResponse = nlohmann::json::array(); 1311 transaction->methodResponse.push_back(std::move(j)); 1312 transaction->methodResponse.push_back(std::move(data)); 1313 transaction->convertedToArray = true; 1314 } 1315 else 1316 { 1317 transaction->methodResponse.push_back(std::move(data)); 1318 } 1319 } 1320 1321 inline void 1322 findActionOnInterface(std::shared_ptr<InProgressActionData> transaction, 1323 const std::string& connectionName) 1324 { 1325 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " 1326 << connectionName; 1327 crow::connections::systemBus->async_method_call( 1328 [transaction, connectionName{std::string(connectionName)}]( 1329 const boost::system::error_code ec, 1330 const std::string& introspect_xml) { 1331 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; 1332 if (ec) 1333 { 1334 BMCWEB_LOG_ERROR 1335 << "Introspect call failed with error: " << ec.message() 1336 << " on process: " << connectionName << "\n"; 1337 return; 1338 } 1339 tinyxml2::XMLDocument doc; 1340 1341 doc.Parse(introspect_xml.data(), introspect_xml.size()); 1342 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1343 if (pRoot == nullptr) 1344 { 1345 BMCWEB_LOG_ERROR << "XML document failed to parse " 1346 << connectionName << "\n"; 1347 return; 1348 } 1349 tinyxml2::XMLElement* interfaceNode = 1350 pRoot->FirstChildElement("interface"); 1351 while (interfaceNode != nullptr) 1352 { 1353 const char* thisInterfaceName = 1354 interfaceNode->Attribute("name"); 1355 if (thisInterfaceName != nullptr) 1356 { 1357 if (!transaction->interfaceName.empty() && 1358 (transaction->interfaceName != thisInterfaceName)) 1359 { 1360 interfaceNode = 1361 interfaceNode->NextSiblingElement("interface"); 1362 continue; 1363 } 1364 1365 tinyxml2::XMLElement* methodNode = 1366 interfaceNode->FirstChildElement("method"); 1367 while (methodNode != nullptr) 1368 { 1369 const char* thisMethodName = 1370 methodNode->Attribute("name"); 1371 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName; 1372 if (thisMethodName != nullptr && 1373 thisMethodName == transaction->methodName) 1374 { 1375 BMCWEB_LOG_DEBUG 1376 << "Found method named " << thisMethodName 1377 << " on interface " << thisInterfaceName; 1378 sdbusplus::message::message m = 1379 crow::connections::systemBus->new_method_call( 1380 connectionName.c_str(), 1381 transaction->path.c_str(), 1382 thisInterfaceName, 1383 transaction->methodName.c_str()); 1384 1385 tinyxml2::XMLElement* argumentNode = 1386 methodNode->FirstChildElement("arg"); 1387 1388 std::string returnType; 1389 1390 // Find the output type 1391 while (argumentNode != nullptr) 1392 { 1393 const char* argDirection = 1394 argumentNode->Attribute("direction"); 1395 const char* argType = 1396 argumentNode->Attribute("type"); 1397 if (argDirection != nullptr && 1398 argType != nullptr && 1399 std::string(argDirection) == "out") 1400 { 1401 returnType = argType; 1402 break; 1403 } 1404 argumentNode = 1405 argumentNode->NextSiblingElement("arg"); 1406 } 1407 1408 nlohmann::json::const_iterator argIt = 1409 transaction->arguments.begin(); 1410 1411 argumentNode = methodNode->FirstChildElement("arg"); 1412 1413 while (argumentNode != nullptr) 1414 { 1415 const char* argDirection = 1416 argumentNode->Attribute("direction"); 1417 const char* argType = 1418 argumentNode->Attribute("type"); 1419 if (argDirection != nullptr && 1420 argType != nullptr && 1421 std::string(argDirection) == "in") 1422 { 1423 if (argIt == transaction->arguments.end()) 1424 { 1425 transaction->setErrorStatus( 1426 "Invalid method args"); 1427 return; 1428 } 1429 if (convertJsonToDbus(m.get(), 1430 std::string(argType), 1431 *argIt) < 0) 1432 { 1433 transaction->setErrorStatus( 1434 "Invalid method arg type"); 1435 return; 1436 } 1437 1438 argIt++; 1439 } 1440 argumentNode = 1441 argumentNode->NextSiblingElement("arg"); 1442 } 1443 1444 crow::connections::systemBus->async_send( 1445 m, [transaction, returnType]( 1446 boost::system::error_code ec2, 1447 sdbusplus::message::message& m2) { 1448 if (ec2) 1449 { 1450 transaction->methodFailed = true; 1451 const sd_bus_error* e = m2.get_error(); 1452 1453 if (e) 1454 { 1455 setErrorResponse( 1456 transaction->res, 1457 boost::beast::http::status:: 1458 bad_request, 1459 e->name, e->message); 1460 } 1461 else 1462 { 1463 setErrorResponse( 1464 transaction->res, 1465 boost::beast::http::status:: 1466 bad_request, 1467 "Method call failed", 1468 methodFailedMsg); 1469 } 1470 return; 1471 } 1472 else 1473 { 1474 transaction->methodPassed = true; 1475 } 1476 1477 handleMethodResponse(transaction, m2, 1478 returnType); 1479 }); 1480 break; 1481 } 1482 methodNode = methodNode->NextSiblingElement("method"); 1483 } 1484 } 1485 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1486 } 1487 }, 1488 connectionName, transaction->path, 1489 "org.freedesktop.DBus.Introspectable", "Introspect"); 1490 } 1491 1492 inline void handleAction(const crow::Request& req, crow::Response& res, 1493 const std::string& objectPath, 1494 const std::string& methodName) 1495 { 1496 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 1497 << methodName; 1498 nlohmann::json requestDbusData = 1499 nlohmann::json::parse(req.body, nullptr, false); 1500 1501 if (requestDbusData.is_discarded()) 1502 { 1503 setErrorResponse(res, boost::beast::http::status::bad_request, 1504 noJsonDesc, badReqMsg); 1505 res.end(); 1506 return; 1507 } 1508 nlohmann::json::iterator data = requestDbusData.find("data"); 1509 if (data == requestDbusData.end()) 1510 { 1511 setErrorResponse(res, boost::beast::http::status::bad_request, 1512 noJsonDesc, badReqMsg); 1513 res.end(); 1514 return; 1515 } 1516 1517 if (!data->is_array()) 1518 { 1519 setErrorResponse(res, boost::beast::http::status::bad_request, 1520 noJsonDesc, badReqMsg); 1521 res.end(); 1522 return; 1523 } 1524 auto transaction = std::make_shared<InProgressActionData>(res); 1525 1526 transaction->path = objectPath; 1527 transaction->methodName = methodName; 1528 transaction->arguments = std::move(*data); 1529 crow::connections::systemBus->async_method_call( 1530 [transaction]( 1531 const boost::system::error_code ec, 1532 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1533 interfaceNames) { 1534 if (ec || interfaceNames.size() <= 0) 1535 { 1536 BMCWEB_LOG_ERROR << "Can't find object"; 1537 setErrorResponse(transaction->res, 1538 boost::beast::http::status::not_found, 1539 notFoundDesc, notFoundMsg); 1540 return; 1541 } 1542 1543 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 1544 << " object(s)"; 1545 1546 for (const std::pair<std::string, std::vector<std::string>>& 1547 object : interfaceNames) 1548 { 1549 findActionOnInterface(transaction, object.first); 1550 } 1551 }, 1552 "xyz.openbmc_project.ObjectMapper", 1553 "/xyz/openbmc_project/object_mapper", 1554 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1555 std::array<std::string, 0>()); 1556 } 1557 1558 inline void handleDelete(crow::Response& res, const std::string& objectPath) 1559 { 1560 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath; 1561 1562 crow::connections::systemBus->async_method_call( 1563 [&res, objectPath]( 1564 const boost::system::error_code ec, 1565 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1566 interfaceNames) { 1567 if (ec || interfaceNames.size() <= 0) 1568 { 1569 BMCWEB_LOG_ERROR << "Can't find object"; 1570 setErrorResponse(res, 1571 boost::beast::http::status::method_not_allowed, 1572 methodNotAllowedDesc, methodNotAllowedMsg); 1573 res.end(); 1574 return; 1575 } 1576 1577 auto transaction = std::make_shared<InProgressActionData>(res); 1578 transaction->path = objectPath; 1579 transaction->methodName = "Delete"; 1580 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1581 1582 for (const std::pair<std::string, std::vector<std::string>>& 1583 object : interfaceNames) 1584 { 1585 findActionOnInterface(transaction, object.first); 1586 } 1587 }, 1588 "xyz.openbmc_project.ObjectMapper", 1589 "/xyz/openbmc_project/object_mapper", 1590 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1591 std::array<const char*, 0>()); 1592 } 1593 1594 inline void handleList(crow::Response& res, const std::string& objectPath, 1595 int32_t depth = 0) 1596 { 1597 crow::connections::systemBus->async_method_call( 1598 [&res](const boost::system::error_code ec, 1599 std::vector<std::string>& objectPaths) { 1600 if (ec) 1601 { 1602 setErrorResponse(res, boost::beast::http::status::not_found, 1603 notFoundDesc, notFoundMsg); 1604 } 1605 else 1606 { 1607 res.jsonValue = {{"status", "ok"}, 1608 {"message", "200 OK"}, 1609 {"data", std::move(objectPaths)}}; 1610 } 1611 res.end(); 1612 }, 1613 "xyz.openbmc_project.ObjectMapper", 1614 "/xyz/openbmc_project/object_mapper", 1615 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 1616 depth, std::array<std::string, 0>()); 1617 } 1618 1619 inline void handleEnumerate(crow::Response& res, const std::string& objectPath) 1620 { 1621 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 1622 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); 1623 1624 asyncResp->res.jsonValue = {{"message", "200 OK"}, 1625 {"status", "ok"}, 1626 {"data", nlohmann::json::object()}}; 1627 1628 crow::connections::systemBus->async_method_call( 1629 [objectPath, asyncResp](const boost::system::error_code ec, 1630 GetSubTreeType& object_names) { 1631 auto transaction = std::make_shared<InProgressEnumerateData>( 1632 objectPath, asyncResp); 1633 1634 transaction->subtree = 1635 std::make_shared<GetSubTreeType>(std::move(object_names)); 1636 1637 if (ec) 1638 { 1639 BMCWEB_LOG_ERROR << "GetSubTree failed on " 1640 << transaction->objectPath; 1641 setErrorResponse(transaction->asyncResp->res, 1642 boost::beast::http::status::not_found, 1643 notFoundDesc, notFoundMsg); 1644 return; 1645 } 1646 1647 // Add the data for the path passed in to the results 1648 // as if GetSubTree returned it, and continue on enumerating 1649 getObjectAndEnumerate(transaction); 1650 }, 1651 "xyz.openbmc_project.ObjectMapper", 1652 "/xyz/openbmc_project/object_mapper", 1653 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0, 1654 std::array<const char*, 0>()); 1655 } 1656 1657 inline void handleGet(crow::Response& res, std::string& objectPath, 1658 std::string& destProperty) 1659 { 1660 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1661 std::shared_ptr<std::string> propertyName = 1662 std::make_shared<std::string>(std::move(destProperty)); 1663 1664 std::shared_ptr<std::string> path = 1665 std::make_shared<std::string>(std::move(objectPath)); 1666 1667 using GetObjectType = 1668 std::vector<std::pair<std::string, std::vector<std::string>>>; 1669 crow::connections::systemBus->async_method_call( 1670 [&res, path, propertyName](const boost::system::error_code ec, 1671 const GetObjectType& object_names) { 1672 if (ec || object_names.size() <= 0) 1673 { 1674 setErrorResponse(res, boost::beast::http::status::not_found, 1675 notFoundDesc, notFoundMsg); 1676 res.end(); 1677 return; 1678 } 1679 std::shared_ptr<nlohmann::json> response = 1680 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1681 // The mapper should never give us an empty interface names 1682 // list, but check anyway 1683 for (const std::pair<std::string, std::vector<std::string>>& 1684 connection : object_names) 1685 { 1686 const std::vector<std::string>& interfaceNames = 1687 connection.second; 1688 1689 if (interfaceNames.size() <= 0) 1690 { 1691 setErrorResponse(res, boost::beast::http::status::not_found, 1692 notFoundDesc, notFoundMsg); 1693 res.end(); 1694 return; 1695 } 1696 1697 for (const std::string& interface : interfaceNames) 1698 { 1699 sdbusplus::message::message m = 1700 crow::connections::systemBus->new_method_call( 1701 connection.first.c_str(), path->c_str(), 1702 "org.freedesktop.DBus.Properties", "GetAll"); 1703 m.append(interface); 1704 crow::connections::systemBus->async_send( 1705 m, [&res, response, 1706 propertyName](const boost::system::error_code ec2, 1707 sdbusplus::message::message& msg) { 1708 if (ec2) 1709 { 1710 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1711 << ec2; 1712 } 1713 else 1714 { 1715 nlohmann::json properties; 1716 int r = 1717 convertDBusToJSON("a{sv}", msg, properties); 1718 if (r < 0) 1719 { 1720 BMCWEB_LOG_ERROR 1721 << "convertDBusToJSON failed"; 1722 } 1723 else 1724 { 1725 for (auto& prop : properties.items()) 1726 { 1727 // if property name is empty, or 1728 // matches our search query, add it 1729 // to the response json 1730 1731 if (propertyName->empty()) 1732 { 1733 (*response)[prop.key()] = 1734 std::move(prop.value()); 1735 } 1736 else if (prop.key() == *propertyName) 1737 { 1738 *response = std::move(prop.value()); 1739 } 1740 } 1741 } 1742 } 1743 if (response.use_count() == 1) 1744 { 1745 if (!propertyName->empty() && response->empty()) 1746 { 1747 setErrorResponse( 1748 res, 1749 boost::beast::http::status::not_found, 1750 propNotFoundDesc, notFoundMsg); 1751 } 1752 else 1753 { 1754 res.jsonValue = {{"status", "ok"}, 1755 {"message", "200 OK"}, 1756 {"data", *response}}; 1757 } 1758 res.end(); 1759 } 1760 }); 1761 } 1762 } 1763 }, 1764 "xyz.openbmc_project.ObjectMapper", 1765 "/xyz/openbmc_project/object_mapper", 1766 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 1767 std::array<std::string, 0>()); 1768 } 1769 1770 struct AsyncPutRequest 1771 { 1772 AsyncPutRequest(crow::Response& resIn) : res(resIn) 1773 {} 1774 ~AsyncPutRequest() 1775 { 1776 if (res.jsonValue.empty()) 1777 { 1778 setErrorResponse(res, boost::beast::http::status::forbidden, 1779 forbiddenMsg, forbiddenPropDesc); 1780 } 1781 1782 res.end(); 1783 } 1784 1785 void setErrorStatus(const std::string& desc) 1786 { 1787 setErrorResponse(res, boost::beast::http::status::internal_server_error, 1788 desc, badReqMsg); 1789 } 1790 1791 crow::Response& res; 1792 std::string objectPath; 1793 std::string propertyName; 1794 nlohmann::json propertyValue; 1795 }; 1796 1797 inline void handlePut(const crow::Request& req, crow::Response& res, 1798 const std::string& objectPath, 1799 const std::string& destProperty) 1800 { 1801 if (destProperty.empty()) 1802 { 1803 setErrorResponse(res, boost::beast::http::status::forbidden, 1804 forbiddenResDesc, forbiddenMsg); 1805 res.end(); 1806 return; 1807 } 1808 1809 nlohmann::json requestDbusData = 1810 nlohmann::json::parse(req.body, nullptr, false); 1811 1812 if (requestDbusData.is_discarded()) 1813 { 1814 setErrorResponse(res, boost::beast::http::status::bad_request, 1815 noJsonDesc, badReqMsg); 1816 res.end(); 1817 return; 1818 } 1819 1820 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); 1821 if (propertyIt == requestDbusData.end()) 1822 { 1823 setErrorResponse(res, boost::beast::http::status::bad_request, 1824 noJsonDesc, badReqMsg); 1825 res.end(); 1826 return; 1827 } 1828 const nlohmann::json& propertySetValue = *propertyIt; 1829 auto transaction = std::make_shared<AsyncPutRequest>(res); 1830 transaction->objectPath = objectPath; 1831 transaction->propertyName = destProperty; 1832 transaction->propertyValue = propertySetValue; 1833 1834 using GetObjectType = 1835 std::vector<std::pair<std::string, std::vector<std::string>>>; 1836 1837 crow::connections::systemBus->async_method_call( 1838 [transaction](const boost::system::error_code ec2, 1839 const GetObjectType& object_names) { 1840 if (!ec2 && object_names.size() <= 0) 1841 { 1842 setErrorResponse(transaction->res, 1843 boost::beast::http::status::not_found, 1844 propNotFoundDesc, notFoundMsg); 1845 return; 1846 } 1847 1848 for (const std::pair<std::string, std::vector<std::string>>& 1849 connection : object_names) 1850 { 1851 const std::string& connectionName = connection.first; 1852 1853 crow::connections::systemBus->async_method_call( 1854 [connectionName{std::string(connectionName)}, 1855 transaction](const boost::system::error_code ec3, 1856 const std::string& introspectXml) { 1857 if (ec3) 1858 { 1859 BMCWEB_LOG_ERROR 1860 << "Introspect call failed with error: " 1861 << ec3.message() 1862 << " on process: " << connectionName; 1863 transaction->setErrorStatus("Unexpected Error"); 1864 return; 1865 } 1866 tinyxml2::XMLDocument doc; 1867 1868 doc.Parse(introspectXml.c_str()); 1869 tinyxml2::XMLNode* pRoot = 1870 doc.FirstChildElement("node"); 1871 if (pRoot == nullptr) 1872 { 1873 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1874 << introspectXml; 1875 transaction->setErrorStatus("Unexpected Error"); 1876 return; 1877 } 1878 tinyxml2::XMLElement* ifaceNode = 1879 pRoot->FirstChildElement("interface"); 1880 while (ifaceNode != nullptr) 1881 { 1882 const char* interfaceName = 1883 ifaceNode->Attribute("name"); 1884 BMCWEB_LOG_DEBUG << "found interface " 1885 << interfaceName; 1886 tinyxml2::XMLElement* propNode = 1887 ifaceNode->FirstChildElement("property"); 1888 while (propNode != nullptr) 1889 { 1890 const char* propertyName = 1891 propNode->Attribute("name"); 1892 BMCWEB_LOG_DEBUG << "Found property " 1893 << propertyName; 1894 if (propertyName == transaction->propertyName) 1895 { 1896 const char* argType = 1897 propNode->Attribute("type"); 1898 if (argType != nullptr) 1899 { 1900 sdbusplus::message::message m = 1901 crow::connections::systemBus 1902 ->new_method_call( 1903 connectionName.c_str(), 1904 transaction->objectPath 1905 .c_str(), 1906 "org.freedesktop.DBus." 1907 "Properties", 1908 "Set"); 1909 m.append(interfaceName, 1910 transaction->propertyName); 1911 int r = sd_bus_message_open_container( 1912 m.get(), SD_BUS_TYPE_VARIANT, 1913 argType); 1914 if (r < 0) 1915 { 1916 transaction->setErrorStatus( 1917 "Unexpected Error"); 1918 return; 1919 } 1920 r = convertJsonToDbus( 1921 m.get(), argType, 1922 transaction->propertyValue); 1923 if (r < 0) 1924 { 1925 if (r == -ERANGE) 1926 { 1927 transaction->setErrorStatus( 1928 "Provided property value " 1929 "is out of range for the " 1930 "property type"); 1931 } 1932 else 1933 { 1934 transaction->setErrorStatus( 1935 "Invalid arg type"); 1936 } 1937 return; 1938 } 1939 r = sd_bus_message_close_container( 1940 m.get()); 1941 if (r < 0) 1942 { 1943 transaction->setErrorStatus( 1944 "Unexpected Error"); 1945 return; 1946 } 1947 crow::connections::systemBus 1948 ->async_send( 1949 m, 1950 [transaction]( 1951 boost::system::error_code 1952 ec, 1953 sdbusplus::message::message& 1954 m2) { 1955 BMCWEB_LOG_DEBUG << "sent"; 1956 if (ec) 1957 { 1958 const sd_bus_error* e = 1959 m2.get_error(); 1960 setErrorResponse( 1961 transaction->res, 1962 boost::beast::http:: 1963 status:: 1964 forbidden, 1965 (e) ? e->name 1966 : ec.category() 1967 .name(), 1968 (e) ? e->message 1969 : ec.message()); 1970 } 1971 else 1972 { 1973 transaction->res 1974 .jsonValue = { 1975 {"status", "ok"}, 1976 {"message", 1977 "200 OK"}, 1978 {"data", nullptr}}; 1979 } 1980 }); 1981 } 1982 } 1983 propNode = 1984 propNode->NextSiblingElement("property"); 1985 } 1986 ifaceNode = 1987 ifaceNode->NextSiblingElement("interface"); 1988 } 1989 }, 1990 connectionName, transaction->objectPath, 1991 "org.freedesktop.DBus.Introspectable", "Introspect"); 1992 } 1993 }, 1994 "xyz.openbmc_project.ObjectMapper", 1995 "/xyz/openbmc_project/object_mapper", 1996 "xyz.openbmc_project.ObjectMapper", "GetObject", 1997 transaction->objectPath, std::array<std::string, 0>()); 1998 } 1999 2000 inline void handleDBusUrl(const crow::Request& req, crow::Response& res, 2001 std::string& objectPath) 2002 { 2003 2004 // If accessing a single attribute, fill in and update objectPath, 2005 // otherwise leave destProperty blank 2006 std::string destProperty = ""; 2007 const char* attrSeperator = "/attr/"; 2008 size_t attrPosition = objectPath.find(attrSeperator); 2009 if (attrPosition != objectPath.npos) 2010 { 2011 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2012 objectPath.length()); 2013 objectPath = objectPath.substr(0, attrPosition); 2014 } 2015 2016 if (req.method() == boost::beast::http::verb::post) 2017 { 2018 constexpr const char* actionSeperator = "/action/"; 2019 size_t actionPosition = objectPath.find(actionSeperator); 2020 if (actionPosition != objectPath.npos) 2021 { 2022 std::string postProperty = 2023 objectPath.substr((actionPosition + strlen(actionSeperator)), 2024 objectPath.length()); 2025 objectPath = objectPath.substr(0, actionPosition); 2026 handleAction(req, res, objectPath, postProperty); 2027 return; 2028 } 2029 } 2030 else if (req.method() == boost::beast::http::verb::get) 2031 { 2032 if (boost::ends_with(objectPath, "/enumerate")) 2033 { 2034 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2035 objectPath.end()); 2036 handleEnumerate(res, objectPath); 2037 } 2038 else if (boost::ends_with(objectPath, "/list")) 2039 { 2040 objectPath.erase(objectPath.end() - sizeof("list"), 2041 objectPath.end()); 2042 handleList(res, objectPath); 2043 } 2044 else 2045 { 2046 // Trim any trailing "/" at the end 2047 if (boost::ends_with(objectPath, "/")) 2048 { 2049 objectPath.pop_back(); 2050 handleList(res, objectPath, 1); 2051 } 2052 else 2053 { 2054 handleGet(res, objectPath, destProperty); 2055 } 2056 } 2057 return; 2058 } 2059 else if (req.method() == boost::beast::http::verb::put) 2060 { 2061 handlePut(req, res, objectPath, destProperty); 2062 return; 2063 } 2064 else if (req.method() == boost::beast::http::verb::delete_) 2065 { 2066 handleDelete(res, objectPath); 2067 return; 2068 } 2069 2070 setErrorResponse(res, boost::beast::http::status::method_not_allowed, 2071 methodNotAllowedDesc, methodNotAllowedMsg); 2072 res.end(); 2073 } 2074 2075 inline void requestRoutes(App& app) 2076 { 2077 BMCWEB_ROUTE(app, "/bus/") 2078 .privileges({"Login"}) 2079 .methods(boost::beast::http::verb::get)( 2080 [](const crow::Request&, crow::Response& res) { 2081 res.jsonValue = {{"buses", {{{"name", "system"}}}}, 2082 {"status", "ok"}}; 2083 res.end(); 2084 }); 2085 2086 BMCWEB_ROUTE(app, "/bus/system/") 2087 .privileges({"Login"}) 2088 .methods(boost::beast::http::verb::get)( 2089 [](const crow::Request&, crow::Response& res) { 2090 auto myCallback = [&res](const boost::system::error_code ec, 2091 std::vector<std::string>& names) { 2092 if (ec) 2093 { 2094 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 2095 res.result( 2096 boost::beast::http::status::internal_server_error); 2097 } 2098 else 2099 { 2100 std::sort(names.begin(), names.end()); 2101 res.jsonValue = {{"status", "ok"}}; 2102 auto& objectsSub = res.jsonValue["objects"]; 2103 for (auto& name : names) 2104 { 2105 objectsSub.push_back({{"name", name}}); 2106 } 2107 } 2108 res.end(); 2109 }; 2110 crow::connections::systemBus->async_method_call( 2111 std::move(myCallback), "org.freedesktop.DBus", "/", 2112 "org.freedesktop.DBus", "ListNames"); 2113 }); 2114 2115 BMCWEB_ROUTE(app, "/list/") 2116 .privileges({"Login"}) 2117 .methods(boost::beast::http::verb::get)( 2118 [](const crow::Request&, crow::Response& res) { 2119 handleList(res, "/"); 2120 }); 2121 2122 BMCWEB_ROUTE(app, "/xyz/<path>") 2123 .privileges({"Login"}) 2124 .methods(boost::beast::http::verb::get)([](const crow::Request& req, 2125 crow::Response& res, 2126 const std::string& path) { 2127 std::string objectPath = "/xyz/" + path; 2128 handleDBusUrl(req, res, objectPath); 2129 }); 2130 2131 BMCWEB_ROUTE(app, "/xyz/<path>") 2132 .privileges({"ConfigureComponents", "ConfigureManager"}) 2133 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2134 boost::beast::http::verb::delete_)( 2135 [](const crow::Request& req, crow::Response& res, 2136 const std::string& path) { 2137 std::string objectPath = "/xyz/" + path; 2138 handleDBusUrl(req, res, objectPath); 2139 }); 2140 2141 BMCWEB_ROUTE(app, "/org/<path>") 2142 .privileges({"Login"}) 2143 .methods(boost::beast::http::verb::get)([](const crow::Request& req, 2144 crow::Response& res, 2145 const std::string& path) { 2146 std::string objectPath = "/org/" + path; 2147 handleDBusUrl(req, res, objectPath); 2148 }); 2149 2150 BMCWEB_ROUTE(app, "/org/<path>") 2151 .privileges({"ConfigureComponents", "ConfigureManager"}) 2152 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2153 boost::beast::http::verb::delete_)( 2154 [](const crow::Request& req, crow::Response& res, 2155 const std::string& path) { 2156 std::string objectPath = "/org/" + path; 2157 handleDBusUrl(req, res, objectPath); 2158 }); 2159 2160 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2161 .privileges({"ConfigureManager"}) 2162 .methods(boost::beast::http::verb::get)([](const crow::Request&, 2163 crow::Response& res, 2164 const std::string& dumpId) { 2165 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$"); 2166 if (!std::regex_match(dumpId, validFilename)) 2167 { 2168 res.result(boost::beast::http::status::bad_request); 2169 res.end(); 2170 return; 2171 } 2172 std::filesystem::path loc( 2173 "/var/lib/phosphor-debug-collector/dumps"); 2174 2175 loc /= dumpId; 2176 2177 if (!std::filesystem::exists(loc) || 2178 !std::filesystem::is_directory(loc)) 2179 { 2180 BMCWEB_LOG_ERROR << loc << "Not found"; 2181 res.result(boost::beast::http::status::not_found); 2182 res.end(); 2183 return; 2184 } 2185 std::filesystem::directory_iterator files(loc); 2186 2187 for (auto& file : files) 2188 { 2189 std::ifstream readFile(file.path()); 2190 if (!readFile.good()) 2191 { 2192 continue; 2193 } 2194 2195 res.addHeader("Content-Type", "application/octet-stream"); 2196 2197 // Assuming only one dump file will be present in the dump id 2198 // directory 2199 std::string dumpFileName = file.path().filename().string(); 2200 2201 // Filename should be in alphanumeric, dot and underscore 2202 // Its based on phosphor-debug-collector application dumpfile 2203 // format 2204 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2205 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2206 { 2207 BMCWEB_LOG_ERROR << "Invalid dump filename " 2208 << dumpFileName; 2209 res.result(boost::beast::http::status::not_found); 2210 res.end(); 2211 return; 2212 } 2213 std::string contentDispositionParam = 2214 "attachment; filename=\"" + dumpFileName + "\""; 2215 2216 res.addHeader("Content-Disposition", contentDispositionParam); 2217 2218 res.body() = {std::istreambuf_iterator<char>(readFile), 2219 std::istreambuf_iterator<char>()}; 2220 res.end(); 2221 return; 2222 } 2223 res.result(boost::beast::http::status::not_found); 2224 res.end(); 2225 return; 2226 }); 2227 2228 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2229 .privileges({"Login"}) 2230 2231 .methods(boost::beast::http::verb::get)( 2232 [](const crow::Request&, crow::Response& res, 2233 const std::string& Connection) { 2234 introspectObjects(Connection, "/", 2235 std::make_shared<bmcweb::AsyncResp>(res)); 2236 }); 2237 2238 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2239 .privileges({"ConfigureComponents", "ConfigureManager"}) 2240 .methods( 2241 boost::beast::http::verb::get, 2242 boost::beast::http::verb::post)([](const crow::Request& req, 2243 crow::Response& res, 2244 const std::string& processName, 2245 const std::string& 2246 requestedPath) { 2247 std::vector<std::string> strs; 2248 boost::split(strs, requestedPath, boost::is_any_of("/")); 2249 std::string objectPath; 2250 std::string interfaceName; 2251 std::string methodName; 2252 auto it = strs.begin(); 2253 if (it == strs.end()) 2254 { 2255 objectPath = "/"; 2256 } 2257 while (it != strs.end()) 2258 { 2259 // Check if segment contains ".". If it does, it must be an 2260 // interface 2261 if (it->find(".") != std::string::npos) 2262 { 2263 break; 2264 // This check is necessary as the trailing slash gets 2265 // parsed as part of our <path> specifier above, which 2266 // causes the normal trailing backslash redirector to 2267 // fail. 2268 } 2269 else if (!it->empty()) 2270 { 2271 objectPath += "/" + *it; 2272 } 2273 it++; 2274 } 2275 if (it != strs.end()) 2276 { 2277 interfaceName = *it; 2278 it++; 2279 2280 // after interface, we might have a method name 2281 if (it != strs.end()) 2282 { 2283 methodName = *it; 2284 it++; 2285 } 2286 } 2287 if (it != strs.end()) 2288 { 2289 // if there is more levels past the method name, something 2290 // went wrong, return not found 2291 res.result(boost::beast::http::status::not_found); 2292 res.end(); 2293 return; 2294 } 2295 if (interfaceName.empty()) 2296 { 2297 std::shared_ptr<bmcweb::AsyncResp> asyncResp = 2298 std::make_shared<bmcweb::AsyncResp>(res); 2299 2300 crow::connections::systemBus->async_method_call( 2301 [asyncResp, processName, 2302 objectPath](const boost::system::error_code ec, 2303 const std::string& introspect_xml) { 2304 if (ec) 2305 { 2306 BMCWEB_LOG_ERROR 2307 << "Introspect call failed with error: " 2308 << ec.message() 2309 << " on process: " << processName 2310 << " path: " << objectPath << "\n"; 2311 return; 2312 } 2313 tinyxml2::XMLDocument doc; 2314 2315 doc.Parse(introspect_xml.c_str()); 2316 tinyxml2::XMLNode* pRoot = 2317 doc.FirstChildElement("node"); 2318 if (pRoot == nullptr) 2319 { 2320 BMCWEB_LOG_ERROR << "XML document failed to parse " 2321 << processName << " " << objectPath 2322 << "\n"; 2323 asyncResp->res.jsonValue = { 2324 {"status", "XML parse error"}}; 2325 asyncResp->res.result(boost::beast::http::status:: 2326 internal_server_error); 2327 return; 2328 } 2329 2330 BMCWEB_LOG_DEBUG << introspect_xml; 2331 asyncResp->res.jsonValue = { 2332 {"status", "ok"}, 2333 {"bus_name", processName}, 2334 {"object_path", objectPath}}; 2335 nlohmann::json& interfacesArray = 2336 asyncResp->res.jsonValue["interfaces"]; 2337 interfacesArray = nlohmann::json::array(); 2338 tinyxml2::XMLElement* interface = 2339 pRoot->FirstChildElement("interface"); 2340 2341 while (interface != nullptr) 2342 { 2343 const char* ifaceName = 2344 interface->Attribute("name"); 2345 if (ifaceName != nullptr) 2346 { 2347 interfacesArray.push_back( 2348 {{"name", ifaceName}}); 2349 } 2350 2351 interface = 2352 interface->NextSiblingElement("interface"); 2353 } 2354 }, 2355 processName, objectPath, 2356 "org.freedesktop.DBus.Introspectable", "Introspect"); 2357 } 2358 else if (methodName.empty()) 2359 { 2360 std::shared_ptr<bmcweb::AsyncResp> asyncResp = 2361 std::make_shared<bmcweb::AsyncResp>(res); 2362 2363 crow::connections::systemBus->async_method_call( 2364 [asyncResp, processName, objectPath, 2365 interfaceName](const boost::system::error_code ec, 2366 const std::string& introspect_xml) { 2367 if (ec) 2368 { 2369 BMCWEB_LOG_ERROR 2370 << "Introspect call failed with error: " 2371 << ec.message() 2372 << " on process: " << processName 2373 << " path: " << objectPath << "\n"; 2374 return; 2375 } 2376 tinyxml2::XMLDocument doc; 2377 2378 doc.Parse(introspect_xml.data(), introspect_xml.size()); 2379 tinyxml2::XMLNode* pRoot = 2380 doc.FirstChildElement("node"); 2381 if (pRoot == nullptr) 2382 { 2383 BMCWEB_LOG_ERROR << "XML document failed to parse " 2384 << processName << " " << objectPath 2385 << "\n"; 2386 asyncResp->res.result(boost::beast::http::status:: 2387 internal_server_error); 2388 return; 2389 } 2390 asyncResp->res.jsonValue = { 2391 {"status", "ok"}, 2392 {"bus_name", processName}, 2393 {"interface", interfaceName}, 2394 {"object_path", objectPath}}; 2395 2396 nlohmann::json& methodsArray = 2397 asyncResp->res.jsonValue["methods"]; 2398 methodsArray = nlohmann::json::array(); 2399 2400 nlohmann::json& signalsArray = 2401 asyncResp->res.jsonValue["signals"]; 2402 signalsArray = nlohmann::json::array(); 2403 2404 nlohmann::json& propertiesObj = 2405 asyncResp->res.jsonValue["properties"]; 2406 propertiesObj = nlohmann::json::object(); 2407 2408 // if we know we're the only call, build the 2409 // json directly 2410 tinyxml2::XMLElement* interface = 2411 pRoot->FirstChildElement("interface"); 2412 while (interface != nullptr) 2413 { 2414 const char* ifaceName = 2415 interface->Attribute("name"); 2416 2417 if (ifaceName != nullptr && 2418 ifaceName == interfaceName) 2419 { 2420 break; 2421 } 2422 2423 interface = 2424 interface->NextSiblingElement("interface"); 2425 } 2426 if (interface == nullptr) 2427 { 2428 // if we got to the end of the list and 2429 // never found a match, throw 404 2430 asyncResp->res.result( 2431 boost::beast::http::status::not_found); 2432 return; 2433 } 2434 2435 tinyxml2::XMLElement* methods = 2436 interface->FirstChildElement("method"); 2437 while (methods != nullptr) 2438 { 2439 nlohmann::json argsArray = nlohmann::json::array(); 2440 tinyxml2::XMLElement* arg = 2441 methods->FirstChildElement("arg"); 2442 while (arg != nullptr) 2443 { 2444 nlohmann::json thisArg; 2445 for (const char* fieldName : 2446 std::array<const char*, 3>{ 2447 "name", "direction", "type"}) 2448 { 2449 const char* fieldValue = 2450 arg->Attribute(fieldName); 2451 if (fieldValue != nullptr) 2452 { 2453 thisArg[fieldName] = fieldValue; 2454 } 2455 } 2456 argsArray.push_back(std::move(thisArg)); 2457 arg = arg->NextSiblingElement("arg"); 2458 } 2459 2460 const char* name = methods->Attribute("name"); 2461 if (name != nullptr) 2462 { 2463 methodsArray.push_back( 2464 {{"name", name}, 2465 {"uri", "/bus/system/" + processName + 2466 objectPath + "/" + 2467 interfaceName + "/" + name}, 2468 {"args", argsArray}}); 2469 } 2470 methods = methods->NextSiblingElement("method"); 2471 } 2472 tinyxml2::XMLElement* signals = 2473 interface->FirstChildElement("signal"); 2474 while (signals != nullptr) 2475 { 2476 nlohmann::json argsArray = nlohmann::json::array(); 2477 2478 tinyxml2::XMLElement* arg = 2479 signals->FirstChildElement("arg"); 2480 while (arg != nullptr) 2481 { 2482 const char* name = arg->Attribute("name"); 2483 const char* type = arg->Attribute("type"); 2484 if (name != nullptr && type != nullptr) 2485 { 2486 argsArray.push_back({ 2487 {"name", name}, 2488 {"type", type}, 2489 }); 2490 } 2491 arg = arg->NextSiblingElement("arg"); 2492 } 2493 const char* name = signals->Attribute("name"); 2494 if (name != nullptr) 2495 { 2496 signalsArray.push_back( 2497 {{"name", name}, {"args", argsArray}}); 2498 } 2499 2500 signals = signals->NextSiblingElement("signal"); 2501 } 2502 2503 tinyxml2::XMLElement* property = 2504 interface->FirstChildElement("property"); 2505 while (property != nullptr) 2506 { 2507 const char* name = property->Attribute("name"); 2508 const char* type = property->Attribute("type"); 2509 if (type != nullptr && name != nullptr) 2510 { 2511 sdbusplus::message::message m = 2512 crow::connections::systemBus 2513 ->new_method_call(processName.c_str(), 2514 objectPath.c_str(), 2515 "org.freedesktop." 2516 "DBus." 2517 "Properties", 2518 "Get"); 2519 m.append(interfaceName, name); 2520 nlohmann::json& propertyItem = 2521 propertiesObj[name]; 2522 crow::connections::systemBus->async_send( 2523 m, [&propertyItem, asyncResp]( 2524 boost::system::error_code& e, 2525 sdbusplus::message::message& msg) { 2526 if (e) 2527 { 2528 return; 2529 } 2530 2531 convertDBusToJSON("v", msg, 2532 propertyItem); 2533 }); 2534 } 2535 property = property->NextSiblingElement("property"); 2536 } 2537 }, 2538 processName, objectPath, 2539 "org.freedesktop.DBus.Introspectable", "Introspect"); 2540 } 2541 else 2542 { 2543 if (req.method() != boost::beast::http::verb::post) 2544 { 2545 res.result(boost::beast::http::status::not_found); 2546 res.end(); 2547 return; 2548 } 2549 2550 nlohmann::json requestDbusData = 2551 nlohmann::json::parse(req.body, nullptr, false); 2552 2553 if (requestDbusData.is_discarded()) 2554 { 2555 res.result(boost::beast::http::status::bad_request); 2556 res.end(); 2557 return; 2558 } 2559 if (!requestDbusData.is_array()) 2560 { 2561 res.result(boost::beast::http::status::bad_request); 2562 res.end(); 2563 return; 2564 } 2565 auto transaction = std::make_shared<InProgressActionData>(res); 2566 2567 transaction->path = objectPath; 2568 transaction->methodName = methodName; 2569 transaction->arguments = std::move(requestDbusData); 2570 2571 findActionOnInterface(transaction, processName); 2572 } 2573 }); 2574 } 2575 } // namespace openbmc_mapper 2576 } // namespace crow 2577