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