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