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 = false; 660 if (intValue != nullptr) 661 { 662 if (*intValue == 1) 663 { 664 boolInt = true; 665 } 666 else if (*intValue == 0) 667 { 668 boolInt = false; 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(), false); 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) 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) ? e->name 2006 : ec.category() 2007 .name(), 2008 (e) ? e->message 2009 : ec.message()); 2010 } 2011 else 2012 { 2013 transaction->asyncResp 2014 ->res.jsonValue = { 2015 {"status", "ok"}, 2016 {"message", 2017 "200 OK"}, 2018 {"data", nullptr}}; 2019 } 2020 }); 2021 } 2022 } 2023 propNode = 2024 propNode->NextSiblingElement("property"); 2025 } 2026 ifaceNode = 2027 ifaceNode->NextSiblingElement("interface"); 2028 } 2029 }, 2030 connectionName, transaction->objectPath, 2031 "org.freedesktop.DBus.Introspectable", "Introspect"); 2032 } 2033 }, 2034 "xyz.openbmc_project.ObjectMapper", 2035 "/xyz/openbmc_project/object_mapper", 2036 "xyz.openbmc_project.ObjectMapper", "GetObject", 2037 transaction->objectPath, std::array<std::string, 0>()); 2038 } 2039 2040 inline void handleDBusUrl(const crow::Request& req, 2041 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2042 std::string& objectPath) 2043 { 2044 2045 // If accessing a single attribute, fill in and update objectPath, 2046 // otherwise leave destProperty blank 2047 std::string destProperty = ""; 2048 const char* attrSeperator = "/attr/"; 2049 size_t attrPosition = objectPath.find(attrSeperator); 2050 if (attrPosition != objectPath.npos) 2051 { 2052 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2053 objectPath.length()); 2054 objectPath = objectPath.substr(0, attrPosition); 2055 } 2056 2057 if (req.method() == boost::beast::http::verb::post) 2058 { 2059 constexpr const char* actionSeperator = "/action/"; 2060 size_t actionPosition = objectPath.find(actionSeperator); 2061 if (actionPosition != objectPath.npos) 2062 { 2063 std::string postProperty = 2064 objectPath.substr((actionPosition + strlen(actionSeperator)), 2065 objectPath.length()); 2066 objectPath = objectPath.substr(0, actionPosition); 2067 handleAction(req, asyncResp, objectPath, postProperty); 2068 return; 2069 } 2070 } 2071 else if (req.method() == boost::beast::http::verb::get) 2072 { 2073 if (boost::ends_with(objectPath, "/enumerate")) 2074 { 2075 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2076 objectPath.end()); 2077 handleEnumerate(asyncResp, objectPath); 2078 } 2079 else if (boost::ends_with(objectPath, "/list")) 2080 { 2081 objectPath.erase(objectPath.end() - sizeof("list"), 2082 objectPath.end()); 2083 handleList(asyncResp, objectPath); 2084 } 2085 else 2086 { 2087 // Trim any trailing "/" at the end 2088 if (boost::ends_with(objectPath, "/")) 2089 { 2090 objectPath.pop_back(); 2091 handleList(asyncResp, objectPath, 1); 2092 } 2093 else 2094 { 2095 handleGet(asyncResp, objectPath, destProperty); 2096 } 2097 } 2098 return; 2099 } 2100 else if (req.method() == boost::beast::http::verb::put) 2101 { 2102 handlePut(req, asyncResp, objectPath, destProperty); 2103 return; 2104 } 2105 else if (req.method() == boost::beast::http::verb::delete_) 2106 { 2107 handleDelete(asyncResp, objectPath); 2108 return; 2109 } 2110 2111 setErrorResponse(asyncResp->res, 2112 boost::beast::http::status::method_not_allowed, 2113 methodNotAllowedDesc, methodNotAllowedMsg); 2114 } 2115 2116 inline void requestRoutes(App& app) 2117 { 2118 BMCWEB_ROUTE(app, "/bus/") 2119 .privileges({{"Login"}}) 2120 .methods(boost::beast::http::verb::get)( 2121 [](const crow::Request&, 2122 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2123 asyncResp->res.jsonValue = {{"buses", {{{"name", "system"}}}}, 2124 {"status", "ok"}}; 2125 }); 2126 2127 BMCWEB_ROUTE(app, "/bus/system/") 2128 .privileges({{"Login"}}) 2129 .methods(boost::beast::http::verb::get)( 2130 [](const crow::Request&, 2131 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2132 auto myCallback = [asyncResp]( 2133 const boost::system::error_code ec, 2134 std::vector<std::string>& names) { 2135 if (ec) 2136 { 2137 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 2138 asyncResp->res.result( 2139 boost::beast::http::status::internal_server_error); 2140 } 2141 else 2142 { 2143 std::sort(names.begin(), names.end()); 2144 asyncResp->res.jsonValue = {{"status", "ok"}}; 2145 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2146 for (auto& name : names) 2147 { 2148 objectsSub.push_back({{"name", name}}); 2149 } 2150 } 2151 }; 2152 crow::connections::systemBus->async_method_call( 2153 std::move(myCallback), "org.freedesktop.DBus", "/", 2154 "org.freedesktop.DBus", "ListNames"); 2155 }); 2156 2157 BMCWEB_ROUTE(app, "/list/") 2158 .privileges({{"Login"}}) 2159 .methods(boost::beast::http::verb::get)( 2160 [](const crow::Request&, 2161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2162 handleList(asyncResp, "/"); 2163 }); 2164 2165 BMCWEB_ROUTE(app, "/xyz/<path>") 2166 .privileges({{"Login"}}) 2167 .methods(boost::beast::http::verb::get)( 2168 [](const crow::Request& req, 2169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2170 const std::string& path) { 2171 std::string objectPath = "/xyz/" + path; 2172 handleDBusUrl(req, asyncResp, objectPath); 2173 }); 2174 2175 BMCWEB_ROUTE(app, "/xyz/<path>") 2176 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2177 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2178 boost::beast::http::verb::delete_)( 2179 [](const crow::Request& req, 2180 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2181 const std::string& path) { 2182 std::string objectPath = "/xyz/" + path; 2183 handleDBusUrl(req, asyncResp, objectPath); 2184 }); 2185 2186 BMCWEB_ROUTE(app, "/org/<path>") 2187 .privileges({{"Login"}}) 2188 .methods(boost::beast::http::verb::get)( 2189 [](const crow::Request& req, 2190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2191 const std::string& path) { 2192 std::string objectPath = "/org/" + path; 2193 handleDBusUrl(req, asyncResp, objectPath); 2194 }); 2195 2196 BMCWEB_ROUTE(app, "/org/<path>") 2197 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2198 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2199 boost::beast::http::verb::delete_)( 2200 [](const crow::Request& req, 2201 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2202 const std::string& path) { 2203 std::string objectPath = "/org/" + path; 2204 handleDBusUrl(req, asyncResp, objectPath); 2205 }); 2206 2207 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2208 .privileges({{"ConfigureManager"}}) 2209 .methods(boost::beast::http::verb::get)( 2210 [](const crow::Request&, 2211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2212 const std::string& dumpId) { 2213 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)"); 2214 if (!std::regex_match(dumpId, validFilename)) 2215 { 2216 asyncResp->res.result( 2217 boost::beast::http::status::bad_request); 2218 return; 2219 } 2220 std::filesystem::path loc( 2221 "/var/lib/phosphor-debug-collector/dumps"); 2222 2223 loc /= dumpId; 2224 2225 if (!std::filesystem::exists(loc) || 2226 !std::filesystem::is_directory(loc)) 2227 { 2228 BMCWEB_LOG_ERROR << loc << "Not found"; 2229 asyncResp->res.result( 2230 boost::beast::http::status::not_found); 2231 return; 2232 } 2233 std::filesystem::directory_iterator files(loc); 2234 2235 for (const auto& file : files) 2236 { 2237 std::ifstream readFile(file.path()); 2238 if (!readFile.good()) 2239 { 2240 continue; 2241 } 2242 2243 asyncResp->res.addHeader("Content-Type", 2244 "application/octet-stream"); 2245 2246 // Assuming only one dump file will be present in the dump 2247 // id directory 2248 std::string dumpFileName = file.path().filename().string(); 2249 2250 // Filename should be in alphanumeric, dot and underscore 2251 // Its based on phosphor-debug-collector application 2252 // dumpfile format 2253 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2254 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2255 { 2256 BMCWEB_LOG_ERROR << "Invalid dump filename " 2257 << dumpFileName; 2258 asyncResp->res.result( 2259 boost::beast::http::status::not_found); 2260 return; 2261 } 2262 std::string contentDispositionParam = 2263 "attachment; filename=\"" + dumpFileName + "\""; 2264 2265 asyncResp->res.addHeader("Content-Disposition", 2266 contentDispositionParam); 2267 2268 asyncResp->res.body() = { 2269 std::istreambuf_iterator<char>(readFile), 2270 std::istreambuf_iterator<char>()}; 2271 return; 2272 } 2273 asyncResp->res.result(boost::beast::http::status::not_found); 2274 return; 2275 }); 2276 2277 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2278 .privileges({{"Login"}}) 2279 2280 .methods(boost::beast::http::verb::get)( 2281 [](const crow::Request&, 2282 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2283 const std::string& connection) { 2284 introspectObjects( 2285 connection, "/", 2286 std::make_shared<bmcweb::AsyncResp>(asyncResp->res)); 2287 }); 2288 2289 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2290 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2291 .methods(boost::beast::http::verb::get, boost::beast::http::verb::post)( 2292 [](const crow::Request& req, 2293 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2294 const std::string& processName, 2295 const std::string& requestedPath) { 2296 std::vector<std::string> strs; 2297 boost::split(strs, requestedPath, boost::is_any_of("/")); 2298 std::string objectPath; 2299 std::string interfaceName; 2300 std::string methodName; 2301 auto it = strs.begin(); 2302 if (it == strs.end()) 2303 { 2304 objectPath = "/"; 2305 } 2306 while (it != strs.end()) 2307 { 2308 // Check if segment contains ".". If it does, it must be an 2309 // interface 2310 if (it->find(".") != std::string::npos) 2311 { 2312 break; 2313 // This check is necessary as the trailing slash gets 2314 // parsed as part of our <path> specifier above, which 2315 // causes the normal trailing backslash redirector to 2316 // fail. 2317 } 2318 if (!it->empty()) 2319 { 2320 objectPath += "/" + *it; 2321 } 2322 it++; 2323 } 2324 if (it != strs.end()) 2325 { 2326 interfaceName = *it; 2327 it++; 2328 2329 // after interface, we might have a method name 2330 if (it != strs.end()) 2331 { 2332 methodName = *it; 2333 it++; 2334 } 2335 } 2336 if (it != strs.end()) 2337 { 2338 // if there is more levels past the method name, something 2339 // went wrong, return not found 2340 asyncResp->res.result( 2341 boost::beast::http::status::not_found); 2342 return; 2343 } 2344 if (interfaceName.empty()) 2345 { 2346 crow::connections::systemBus->async_method_call( 2347 [asyncResp, processName, 2348 objectPath](const boost::system::error_code ec, 2349 const std::string& introspectXml) { 2350 if (ec) 2351 { 2352 BMCWEB_LOG_ERROR 2353 << "Introspect call failed with error: " 2354 << ec.message() 2355 << " on process: " << processName 2356 << " path: " << objectPath << "\n"; 2357 return; 2358 } 2359 tinyxml2::XMLDocument doc; 2360 2361 doc.Parse(introspectXml.c_str()); 2362 tinyxml2::XMLNode* pRoot = 2363 doc.FirstChildElement("node"); 2364 if (pRoot == nullptr) 2365 { 2366 BMCWEB_LOG_ERROR 2367 << "XML document failed to parse " 2368 << processName << " " << objectPath << "\n"; 2369 asyncResp->res.jsonValue = { 2370 {"status", "XML parse error"}}; 2371 asyncResp->res.result( 2372 boost::beast::http::status:: 2373 internal_server_error); 2374 return; 2375 } 2376 2377 BMCWEB_LOG_DEBUG << introspectXml; 2378 asyncResp->res.jsonValue = { 2379 {"status", "ok"}, 2380 {"bus_name", processName}, 2381 {"object_path", objectPath}}; 2382 nlohmann::json& interfacesArray = 2383 asyncResp->res.jsonValue["interfaces"]; 2384 interfacesArray = nlohmann::json::array(); 2385 tinyxml2::XMLElement* interface = 2386 pRoot->FirstChildElement("interface"); 2387 2388 while (interface != nullptr) 2389 { 2390 const char* ifaceName = 2391 interface->Attribute("name"); 2392 if (ifaceName != nullptr) 2393 { 2394 interfacesArray.push_back( 2395 {{"name", ifaceName}}); 2396 } 2397 2398 interface = 2399 interface->NextSiblingElement("interface"); 2400 } 2401 }, 2402 processName, objectPath, 2403 "org.freedesktop.DBus.Introspectable", "Introspect"); 2404 } 2405 else if (methodName.empty()) 2406 { 2407 crow::connections::systemBus->async_method_call( 2408 [asyncResp, processName, objectPath, 2409 interfaceName](const boost::system::error_code ec, 2410 const std::string& introspectXml) { 2411 if (ec) 2412 { 2413 BMCWEB_LOG_ERROR 2414 << "Introspect call failed with error: " 2415 << ec.message() 2416 << " on process: " << processName 2417 << " path: " << objectPath << "\n"; 2418 return; 2419 } 2420 tinyxml2::XMLDocument doc; 2421 2422 doc.Parse(introspectXml.data(), 2423 introspectXml.size()); 2424 tinyxml2::XMLNode* pRoot = 2425 doc.FirstChildElement("node"); 2426 if (pRoot == nullptr) 2427 { 2428 BMCWEB_LOG_ERROR 2429 << "XML document failed to parse " 2430 << processName << " " << objectPath << "\n"; 2431 asyncResp->res.result( 2432 boost::beast::http::status:: 2433 internal_server_error); 2434 return; 2435 } 2436 asyncResp->res.jsonValue = { 2437 {"status", "ok"}, 2438 {"bus_name", processName}, 2439 {"interface", interfaceName}, 2440 {"object_path", objectPath}}; 2441 2442 nlohmann::json& methodsArray = 2443 asyncResp->res.jsonValue["methods"]; 2444 methodsArray = nlohmann::json::array(); 2445 2446 nlohmann::json& signalsArray = 2447 asyncResp->res.jsonValue["signals"]; 2448 signalsArray = nlohmann::json::array(); 2449 2450 nlohmann::json& propertiesObj = 2451 asyncResp->res.jsonValue["properties"]; 2452 propertiesObj = nlohmann::json::object(); 2453 2454 // if we know we're the only call, build the 2455 // json directly 2456 tinyxml2::XMLElement* interface = 2457 pRoot->FirstChildElement("interface"); 2458 while (interface != nullptr) 2459 { 2460 const char* ifaceName = 2461 interface->Attribute("name"); 2462 2463 if (ifaceName != nullptr && 2464 ifaceName == interfaceName) 2465 { 2466 break; 2467 } 2468 2469 interface = 2470 interface->NextSiblingElement("interface"); 2471 } 2472 if (interface == nullptr) 2473 { 2474 // if we got to the end of the list and 2475 // never found a match, throw 404 2476 asyncResp->res.result( 2477 boost::beast::http::status::not_found); 2478 return; 2479 } 2480 2481 tinyxml2::XMLElement* methods = 2482 interface->FirstChildElement("method"); 2483 while (methods != nullptr) 2484 { 2485 nlohmann::json argsArray = 2486 nlohmann::json::array(); 2487 tinyxml2::XMLElement* arg = 2488 methods->FirstChildElement("arg"); 2489 while (arg != nullptr) 2490 { 2491 nlohmann::json thisArg; 2492 for (const char* fieldName : 2493 std::array<const char*, 3>{ 2494 "name", "direction", "type"}) 2495 { 2496 const char* fieldValue = 2497 arg->Attribute(fieldName); 2498 if (fieldValue != nullptr) 2499 { 2500 thisArg[fieldName] = fieldValue; 2501 } 2502 } 2503 argsArray.push_back(std::move(thisArg)); 2504 arg = arg->NextSiblingElement("arg"); 2505 } 2506 2507 const char* name = methods->Attribute("name"); 2508 if (name != nullptr) 2509 { 2510 std::string uri; 2511 uri.reserve(14 + processName.size() + 2512 objectPath.size() + 2513 interfaceName.size() + 2514 strlen(name)); 2515 uri += "/bus/system/"; 2516 uri += processName; 2517 uri += objectPath; 2518 uri += "/"; 2519 uri += interfaceName; 2520 uri += "/"; 2521 uri += name; 2522 methodsArray.push_back( 2523 {{"name", name}, 2524 {"uri", std::move(uri)}, 2525 {"args", argsArray}}); 2526 } 2527 methods = methods->NextSiblingElement("method"); 2528 } 2529 tinyxml2::XMLElement* signals = 2530 interface->FirstChildElement("signal"); 2531 while (signals != nullptr) 2532 { 2533 nlohmann::json argsArray = 2534 nlohmann::json::array(); 2535 2536 tinyxml2::XMLElement* arg = 2537 signals->FirstChildElement("arg"); 2538 while (arg != nullptr) 2539 { 2540 const char* name = arg->Attribute("name"); 2541 const char* type = arg->Attribute("type"); 2542 if (name != nullptr && type != nullptr) 2543 { 2544 argsArray.push_back({ 2545 {"name", name}, 2546 {"type", type}, 2547 }); 2548 } 2549 arg = arg->NextSiblingElement("arg"); 2550 } 2551 const char* name = signals->Attribute("name"); 2552 if (name != nullptr) 2553 { 2554 signalsArray.push_back( 2555 {{"name", name}, {"args", argsArray}}); 2556 } 2557 2558 signals = signals->NextSiblingElement("signal"); 2559 } 2560 2561 tinyxml2::XMLElement* property = 2562 interface->FirstChildElement("property"); 2563 while (property != nullptr) 2564 { 2565 const char* name = property->Attribute("name"); 2566 const char* type = property->Attribute("type"); 2567 if (type != nullptr && name != nullptr) 2568 { 2569 sdbusplus::message::message m = 2570 crow::connections::systemBus 2571 ->new_method_call( 2572 processName.c_str(), 2573 objectPath.c_str(), 2574 "org.freedesktop." 2575 "DBus." 2576 "Properties", 2577 "Get"); 2578 m.append(interfaceName, name); 2579 nlohmann::json& propertyItem = 2580 propertiesObj[name]; 2581 crow::connections::systemBus->async_send( 2582 m, 2583 [&propertyItem, asyncResp]( 2584 boost::system::error_code& e, 2585 sdbusplus::message::message& msg) { 2586 if (e) 2587 { 2588 return; 2589 } 2590 2591 convertDBusToJSON("v", msg, 2592 propertyItem); 2593 }); 2594 } 2595 property = 2596 property->NextSiblingElement("property"); 2597 } 2598 }, 2599 processName, objectPath, 2600 "org.freedesktop.DBus.Introspectable", "Introspect"); 2601 } 2602 else 2603 { 2604 if (req.method() != boost::beast::http::verb::post) 2605 { 2606 asyncResp->res.result( 2607 boost::beast::http::status::not_found); 2608 return; 2609 } 2610 2611 nlohmann::json requestDbusData = 2612 nlohmann::json::parse(req.body, nullptr, false); 2613 2614 if (requestDbusData.is_discarded()) 2615 { 2616 asyncResp->res.result( 2617 boost::beast::http::status::bad_request); 2618 return; 2619 } 2620 if (!requestDbusData.is_array()) 2621 { 2622 asyncResp->res.result( 2623 boost::beast::http::status::bad_request); 2624 return; 2625 } 2626 auto transaction = 2627 std::make_shared<InProgressActionData>(asyncResp->res); 2628 2629 transaction->path = objectPath; 2630 transaction->methodName = methodName; 2631 transaction->arguments = std::move(requestDbusData); 2632 2633 findActionOnInterface(transaction, processName); 2634 } 2635 }); 2636 } 2637 } // namespace openbmc_mapper 2638 } // namespace crow 2639