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