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