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