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