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