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