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