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