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