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( 601 "Converting {} to type: {}", 602 inputJson.dump(2, ' ', true, nlohmann::json::error_handler_t::replace), 603 argType); 604 const std::vector<std::string> argTypes = dbusArgSplit(argType); 605 606 // Assume a single object for now. 607 const nlohmann::json* j = &inputJson; 608 nlohmann::json::const_iterator jIt = inputJson.begin(); 609 610 for (const std::string& argCode : argTypes) 611 { 612 // If we are decoding multiple objects, grab the pointer to the 613 // iterator, and increment it for the next loop 614 if (argTypes.size() > 1) 615 { 616 if (jIt == inputJson.end()) 617 { 618 return -2; 619 } 620 j = &*jIt; 621 jIt++; 622 } 623 const int64_t* intValue = j->get_ptr<const int64_t*>(); 624 const std::string* stringValue = j->get_ptr<const std::string*>(); 625 const double* doubleValue = j->get_ptr<const double*>(); 626 const bool* b = j->get_ptr<const bool*>(); 627 int64_t v = 0; 628 double d = 0.0; 629 630 // Do some basic type conversions that make sense. uint can be 631 // converted to int. int and uint can be converted to double 632 if (intValue == nullptr) 633 { 634 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 635 if (uintValue != nullptr) 636 { 637 v = static_cast<int64_t>(*uintValue); 638 intValue = &v; 639 } 640 } 641 if (doubleValue == nullptr) 642 { 643 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 644 if (uintValue != nullptr) 645 { 646 d = static_cast<double>(*uintValue); 647 doubleValue = &d; 648 } 649 } 650 if (doubleValue == nullptr) 651 { 652 if (intValue != nullptr) 653 { 654 d = static_cast<double>(*intValue); 655 doubleValue = &d; 656 } 657 } 658 659 if (argCode == "s") 660 { 661 if (stringValue == nullptr) 662 { 663 return -1; 664 } 665 r = sd_bus_message_append_basic( 666 m, argCode[0], static_cast<const void*>(stringValue->data())); 667 if (r < 0) 668 { 669 return r; 670 } 671 } 672 else if (argCode == "i") 673 { 674 if (intValue == nullptr) 675 { 676 return -1; 677 } 678 if ((*intValue < std::numeric_limits<int32_t>::lowest()) || 679 (*intValue > std::numeric_limits<int32_t>::max())) 680 { 681 return -ERANGE; 682 } 683 int32_t i = static_cast<int32_t>(*intValue); 684 r = sd_bus_message_append_basic(m, argCode[0], &i); 685 if (r < 0) 686 { 687 return r; 688 } 689 } 690 else if (argCode == "b") 691 { 692 // lots of ways bool could be represented here. Try them all 693 int boolInt = 0; 694 if (intValue != nullptr) 695 { 696 if (*intValue == 1) 697 { 698 boolInt = 1; 699 } 700 else if (*intValue == 0) 701 { 702 boolInt = 0; 703 } 704 else 705 { 706 return -ERANGE; 707 } 708 } 709 else if (b != nullptr) 710 { 711 boolInt = *b ? 1 : 0; 712 } 713 else if (stringValue != nullptr) 714 { 715 if (!stringValue->empty()) 716 { 717 if (stringValue->front() == 't' || 718 stringValue->front() == 'T') 719 { 720 boolInt = 1; 721 } 722 } 723 } 724 else 725 { 726 return -1; 727 } 728 r = sd_bus_message_append_basic(m, argCode[0], &boolInt); 729 if (r < 0) 730 { 731 return r; 732 } 733 } 734 else if (argCode == "n") 735 { 736 if (intValue == nullptr) 737 { 738 return -1; 739 } 740 if ((*intValue < std::numeric_limits<int16_t>::lowest()) || 741 (*intValue > std::numeric_limits<int16_t>::max())) 742 { 743 return -ERANGE; 744 } 745 int16_t n = static_cast<int16_t>(*intValue); 746 r = sd_bus_message_append_basic(m, argCode[0], &n); 747 if (r < 0) 748 { 749 return r; 750 } 751 } 752 else if (argCode == "x") 753 { 754 if (intValue == nullptr) 755 { 756 return -1; 757 } 758 r = sd_bus_message_append_basic(m, argCode[0], intValue); 759 if (r < 0) 760 { 761 return r; 762 } 763 } 764 else if (argCode == "y") 765 { 766 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 767 if (uintValue == nullptr) 768 { 769 return -1; 770 } 771 if (*uintValue > std::numeric_limits<uint8_t>::max()) 772 { 773 return -ERANGE; 774 } 775 uint8_t y = static_cast<uint8_t>(*uintValue); 776 r = sd_bus_message_append_basic(m, argCode[0], &y); 777 } 778 else if (argCode == "q") 779 { 780 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 781 if (uintValue == nullptr) 782 { 783 return -1; 784 } 785 if (*uintValue > std::numeric_limits<uint16_t>::max()) 786 { 787 return -ERANGE; 788 } 789 uint16_t q = static_cast<uint16_t>(*uintValue); 790 r = sd_bus_message_append_basic(m, argCode[0], &q); 791 } 792 else if (argCode == "u") 793 { 794 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 795 if (uintValue == nullptr) 796 { 797 return -1; 798 } 799 if (*uintValue > std::numeric_limits<uint32_t>::max()) 800 { 801 return -ERANGE; 802 } 803 uint32_t u = static_cast<uint32_t>(*uintValue); 804 r = sd_bus_message_append_basic(m, argCode[0], &u); 805 } 806 else if (argCode == "t") 807 { 808 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 809 if (uintValue == nullptr) 810 { 811 return -1; 812 } 813 r = sd_bus_message_append_basic(m, argCode[0], uintValue); 814 } 815 else if (argCode == "d") 816 { 817 if (doubleValue == nullptr) 818 { 819 return -1; 820 } 821 if ((*doubleValue < std::numeric_limits<double>::lowest()) || 822 (*doubleValue > std::numeric_limits<double>::max())) 823 { 824 return -ERANGE; 825 } 826 sd_bus_message_append_basic(m, argCode[0], doubleValue); 827 } 828 else if (argCode.starts_with("a")) 829 { 830 std::string containedType = argCode.substr(1); 831 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, 832 containedType.c_str()); 833 if (r < 0) 834 { 835 return r; 836 } 837 838 for (const auto& it : *j) 839 { 840 r = convertJsonToDbus(m, containedType, it); 841 if (r < 0) 842 { 843 return r; 844 } 845 } 846 sd_bus_message_close_container(m); 847 } 848 else if (argCode.starts_with("v")) 849 { 850 std::string containedType = argCode.substr(1); 851 BMCWEB_LOG_DEBUG("variant type: {} appending variant of type: {}", 852 argCode, containedType); 853 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, 854 containedType.c_str()); 855 if (r < 0) 856 { 857 return r; 858 } 859 860 r = convertJsonToDbus(m, containedType, inputJson); 861 if (r < 0) 862 { 863 return r; 864 } 865 866 r = sd_bus_message_close_container(m); 867 if (r < 0) 868 { 869 return r; 870 } 871 } 872 else if (argCode.starts_with("(") && argCode.ends_with(")")) 873 { 874 std::string containedType = argCode.substr(1, argCode.size() - 2); 875 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, 876 containedType.c_str()); 877 if (r < 0) 878 { 879 return r; 880 } 881 882 nlohmann::json::const_iterator it = j->begin(); 883 for (const std::string& argCode2 : dbusArgSplit(containedType)) 884 { 885 if (it == j->end()) 886 { 887 return -1; 888 } 889 r = convertJsonToDbus(m, argCode2, *it); 890 if (r < 0) 891 { 892 return r; 893 } 894 it++; 895 } 896 r = sd_bus_message_close_container(m); 897 } 898 else if (argCode.starts_with("{") && argCode.ends_with("}")) 899 { 900 std::string containedType = argCode.substr(1, argCode.size() - 2); 901 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, 902 containedType.c_str()); 903 if (r < 0) 904 { 905 return r; 906 } 907 908 std::vector<std::string> codes = dbusArgSplit(containedType); 909 if (codes.size() != 2) 910 { 911 return -1; 912 } 913 const std::string& keyType = codes[0]; 914 const std::string& valueType = codes[1]; 915 for (const auto& it : j->items()) 916 { 917 r = convertJsonToDbus(m, keyType, it.key()); 918 if (r < 0) 919 { 920 return r; 921 } 922 923 r = convertJsonToDbus(m, valueType, it.value()); 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 1350 if (transaction->methodResponse.is_object() && data.is_object()) 1351 { 1352 for (const auto& obj : data.items()) 1353 { 1354 // Note: Will overwrite the data for a duplicate key 1355 transaction->methodResponse.emplace(obj.key(), 1356 std::move(obj.value())); 1357 } 1358 return; 1359 } 1360 1361 if (transaction->methodResponse.is_array() && data.is_array()) 1362 { 1363 for (auto& obj : data) 1364 { 1365 transaction->methodResponse.emplace_back(std::move(obj)); 1366 } 1367 return; 1368 } 1369 1370 if (!transaction->convertedToArray) 1371 { 1372 // They are different types. May as well turn them into an array 1373 nlohmann::json j = std::move(transaction->methodResponse); 1374 transaction->methodResponse = nlohmann::json::array(); 1375 transaction->methodResponse.emplace_back(std::move(j)); 1376 transaction->methodResponse.emplace_back(std::move(data)); 1377 transaction->convertedToArray = true; 1378 } 1379 else 1380 { 1381 transaction->methodResponse.emplace_back(std::move(data)); 1382 } 1383 } 1384 1385 inline void findActionOnInterface( 1386 const std::shared_ptr<InProgressActionData>& transaction, 1387 const std::string& connectionName) 1388 { 1389 BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName); 1390 crow::connections::systemBus->async_method_call( 1391 [transaction, connectionName{std::string(connectionName)}]( 1392 const boost::system::error_code& ec, 1393 const std::string& introspectXml) { 1394 BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml); 1395 if (ec) 1396 { 1397 BMCWEB_LOG_ERROR( 1398 "Introspect call failed with error: {} on process: {}", 1399 ec.message(), connectionName); 1400 return; 1401 } 1402 tinyxml2::XMLDocument doc; 1403 1404 doc.Parse(introspectXml.data(), introspectXml.size()); 1405 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1406 if (pRoot == nullptr) 1407 { 1408 BMCWEB_LOG_ERROR("XML document failed to parse {}", connectionName); 1409 return; 1410 } 1411 tinyxml2::XMLElement* interfaceNode = 1412 pRoot->FirstChildElement("interface"); 1413 while (interfaceNode != nullptr) 1414 { 1415 const char* thisInterfaceName = interfaceNode->Attribute("name"); 1416 if (thisInterfaceName != nullptr) 1417 { 1418 if (!transaction->interfaceName.empty() && 1419 (transaction->interfaceName != thisInterfaceName)) 1420 { 1421 interfaceNode = 1422 interfaceNode->NextSiblingElement("interface"); 1423 continue; 1424 } 1425 1426 tinyxml2::XMLElement* methodNode = 1427 interfaceNode->FirstChildElement("method"); 1428 while (methodNode != nullptr) 1429 { 1430 const char* thisMethodName = methodNode->Attribute("name"); 1431 BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName); 1432 if (thisMethodName != nullptr && 1433 thisMethodName == transaction->methodName) 1434 { 1435 BMCWEB_LOG_DEBUG( 1436 "Found method named {} on interface {}", 1437 thisMethodName, thisInterfaceName); 1438 sdbusplus::message_t m = 1439 crow::connections::systemBus->new_method_call( 1440 connectionName.c_str(), 1441 transaction->path.c_str(), thisInterfaceName, 1442 transaction->methodName.c_str()); 1443 1444 tinyxml2::XMLElement* argumentNode = 1445 methodNode->FirstChildElement("arg"); 1446 1447 std::string returnType; 1448 1449 // Find the output type 1450 while (argumentNode != nullptr) 1451 { 1452 const char* argDirection = 1453 argumentNode->Attribute("direction"); 1454 const char* argType = 1455 argumentNode->Attribute("type"); 1456 if (argDirection != nullptr && argType != nullptr && 1457 std::string(argDirection) == "out") 1458 { 1459 returnType = argType; 1460 break; 1461 } 1462 argumentNode = 1463 argumentNode->NextSiblingElement("arg"); 1464 } 1465 1466 auto argIt = transaction->arguments.begin(); 1467 1468 argumentNode = methodNode->FirstChildElement("arg"); 1469 1470 while (argumentNode != nullptr) 1471 { 1472 const char* argDirection = 1473 argumentNode->Attribute("direction"); 1474 const char* argType = 1475 argumentNode->Attribute("type"); 1476 if (argDirection != nullptr && argType != nullptr && 1477 std::string(argDirection) == "in") 1478 { 1479 if (argIt == transaction->arguments.end()) 1480 { 1481 transaction->setErrorStatus( 1482 "Invalid method args"); 1483 return; 1484 } 1485 if (convertJsonToDbus(m.get(), 1486 std::string(argType), 1487 *argIt) < 0) 1488 { 1489 transaction->setErrorStatus( 1490 "Invalid method arg type"); 1491 return; 1492 } 1493 1494 argIt++; 1495 } 1496 argumentNode = 1497 argumentNode->NextSiblingElement("arg"); 1498 } 1499 1500 crow::connections::systemBus->async_send( 1501 m, [transaction, returnType]( 1502 const boost::system::error_code& ec2, 1503 sdbusplus::message_t& m2) { 1504 if (ec2) 1505 { 1506 transaction->methodFailed = true; 1507 const sd_bus_error* e = m2.get_error(); 1508 1509 if (e != nullptr) 1510 { 1511 setErrorResponse( 1512 transaction->asyncResp->res, 1513 boost::beast::http::status::bad_request, 1514 e->name, e->message); 1515 } 1516 else 1517 { 1518 setErrorResponse( 1519 transaction->asyncResp->res, 1520 boost::beast::http::status::bad_request, 1521 "Method call failed", methodFailedMsg); 1522 } 1523 return; 1524 } 1525 transaction->methodPassed = true; 1526 1527 handleMethodResponse(transaction, m2, returnType); 1528 }); 1529 break; 1530 } 1531 methodNode = methodNode->NextSiblingElement("method"); 1532 } 1533 } 1534 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1535 } 1536 }, 1537 connectionName, transaction->path, 1538 "org.freedesktop.DBus.Introspectable", "Introspect"); 1539 } 1540 1541 inline void handleAction(const crow::Request& req, 1542 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1543 const std::string& objectPath, 1544 const std::string& methodName) 1545 { 1546 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath, 1547 methodName); 1548 nlohmann::json requestDbusData; 1549 1550 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1551 if (ret == JsonParseResult::BadContentType) 1552 { 1553 setErrorResponse(asyncResp->res, 1554 boost::beast::http::status::unsupported_media_type, 1555 invalidContentType, unsupportedMediaMsg); 1556 return; 1557 } 1558 if (ret != JsonParseResult::Success) 1559 { 1560 setErrorResponse(asyncResp->res, 1561 boost::beast::http::status::bad_request, noJsonDesc, 1562 badReqMsg); 1563 return; 1564 } 1565 nlohmann::json::iterator data = requestDbusData.find("data"); 1566 if (data == requestDbusData.end()) 1567 { 1568 setErrorResponse(asyncResp->res, 1569 boost::beast::http::status::bad_request, noJsonDesc, 1570 badReqMsg); 1571 return; 1572 } 1573 1574 if (!data->is_array()) 1575 { 1576 setErrorResponse(asyncResp->res, 1577 boost::beast::http::status::bad_request, noJsonDesc, 1578 badReqMsg); 1579 return; 1580 } 1581 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 1582 1583 transaction->path = objectPath; 1584 transaction->methodName = methodName; 1585 transaction->arguments = std::move(*data); 1586 dbus::utility::getDbusObject( 1587 objectPath, {}, 1588 [transaction]( 1589 const boost::system::error_code& ec, 1590 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1591 interfaceNames) { 1592 if (ec || interfaceNames.empty()) 1593 { 1594 BMCWEB_LOG_ERROR("Can't find object"); 1595 setErrorResponse(transaction->asyncResp->res, 1596 boost::beast::http::status::not_found, 1597 notFoundDesc, notFoundMsg); 1598 return; 1599 } 1600 1601 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)", 1602 interfaceNames.size()); 1603 1604 for (const std::pair<std::string, std::vector<std::string>>& object : 1605 interfaceNames) 1606 { 1607 findActionOnInterface(transaction, object.first); 1608 } 1609 }); 1610 } 1611 1612 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1613 const std::string& objectPath) 1614 { 1615 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath); 1616 1617 dbus::utility::getDbusObject( 1618 objectPath, {}, 1619 [asyncResp, objectPath]( 1620 const boost::system::error_code& ec, 1621 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1622 interfaceNames) { 1623 if (ec || interfaceNames.empty()) 1624 { 1625 BMCWEB_LOG_ERROR("Can't find object"); 1626 setErrorResponse(asyncResp->res, 1627 boost::beast::http::status::method_not_allowed, 1628 methodNotAllowedDesc, methodNotAllowedMsg); 1629 return; 1630 } 1631 1632 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 1633 transaction->path = objectPath; 1634 transaction->methodName = "Delete"; 1635 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1636 1637 for (const std::pair<std::string, std::vector<std::string>>& object : 1638 interfaceNames) 1639 { 1640 findActionOnInterface(transaction, object.first); 1641 } 1642 }); 1643 } 1644 1645 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1646 const std::string& objectPath, int32_t depth = 0) 1647 { 1648 dbus::utility::getSubTreePaths( 1649 objectPath, depth, {}, 1650 [asyncResp]( 1651 const boost::system::error_code& ec, 1652 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { 1653 if (ec) 1654 { 1655 setErrorResponse(asyncResp->res, 1656 boost::beast::http::status::not_found, 1657 notFoundDesc, notFoundMsg); 1658 } 1659 else 1660 { 1661 asyncResp->res.jsonValue["status"] = "ok"; 1662 asyncResp->res.jsonValue["message"] = "200 OK"; 1663 asyncResp->res.jsonValue["data"] = objectPaths; 1664 } 1665 }); 1666 } 1667 1668 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1669 const std::string& objectPath) 1670 { 1671 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath); 1672 1673 asyncResp->res.jsonValue["message"] = "200 OK"; 1674 asyncResp->res.jsonValue["status"] = "ok"; 1675 asyncResp->res.jsonValue["data"] = nlohmann::json::object(); 1676 1677 dbus::utility::getSubTree( 1678 objectPath, 0, {}, 1679 [objectPath, asyncResp]( 1680 const boost::system::error_code& ec, 1681 const dbus::utility::MapperGetSubTreeResponse& objectNames) { 1682 auto transaction = std::make_shared<InProgressEnumerateData>(objectPath, 1683 asyncResp); 1684 1685 transaction->subtree = 1686 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( 1687 objectNames); 1688 1689 if (ec) 1690 { 1691 BMCWEB_LOG_ERROR("GetSubTree failed on {}", 1692 transaction->objectPath); 1693 setErrorResponse(transaction->asyncResp->res, 1694 boost::beast::http::status::not_found, 1695 notFoundDesc, notFoundMsg); 1696 return; 1697 } 1698 1699 // Add the data for the path passed in to the results 1700 // as if GetSubTree returned it, and continue on enumerating 1701 getObjectAndEnumerate(transaction); 1702 }); 1703 } 1704 1705 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1706 std::string& objectPath, std::string& destProperty) 1707 { 1708 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty); 1709 std::shared_ptr<std::string> propertyName = 1710 std::make_shared<std::string>(std::move(destProperty)); 1711 1712 std::shared_ptr<std::string> path = 1713 std::make_shared<std::string>(std::move(objectPath)); 1714 1715 dbus::utility::getDbusObject( 1716 *path, {}, 1717 [asyncResp, path, 1718 propertyName](const boost::system::error_code& ec, 1719 const dbus::utility::MapperGetObject& objectNames) { 1720 if (ec || objectNames.empty()) 1721 { 1722 setErrorResponse(asyncResp->res, 1723 boost::beast::http::status::not_found, 1724 notFoundDesc, notFoundMsg); 1725 return; 1726 } 1727 std::shared_ptr<nlohmann::json> response = 1728 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1729 // The mapper should never give us an empty interface names 1730 // list, but check anyway 1731 for (const std::pair<std::string, std::vector<std::string>>& 1732 connection : objectNames) 1733 { 1734 const std::vector<std::string>& interfaceNames = connection.second; 1735 1736 if (interfaceNames.empty()) 1737 { 1738 setErrorResponse(asyncResp->res, 1739 boost::beast::http::status::not_found, 1740 notFoundDesc, notFoundMsg); 1741 return; 1742 } 1743 1744 for (const std::string& interface : interfaceNames) 1745 { 1746 sdbusplus::message_t m = 1747 crow::connections::systemBus->new_method_call( 1748 connection.first.c_str(), path->c_str(), 1749 "org.freedesktop.DBus.Properties", "GetAll"); 1750 m.append(interface); 1751 crow::connections::systemBus->async_send( 1752 m, [asyncResp, response, 1753 propertyName](const boost::system::error_code& ec2, 1754 sdbusplus::message_t& msg) { 1755 if (ec2) 1756 { 1757 BMCWEB_LOG_ERROR("Bad dbus request error: {}", ec2); 1758 } 1759 else 1760 { 1761 nlohmann::json properties; 1762 int r = convertDBusToJSON("a{sv}", msg, properties); 1763 if (r < 0) 1764 { 1765 BMCWEB_LOG_ERROR("convertDBusToJSON failed"); 1766 } 1767 else 1768 { 1769 for (const auto& prop : properties.items()) 1770 { 1771 // if property name is empty, or 1772 // matches our search query, add it 1773 // to the response json 1774 1775 if (propertyName->empty()) 1776 { 1777 (*response)[prop.key()] = 1778 std::move(prop.value()); 1779 } 1780 else if (prop.key() == *propertyName) 1781 { 1782 *response = std::move(prop.value()); 1783 } 1784 } 1785 } 1786 } 1787 if (response.use_count() == 1) 1788 { 1789 if (!propertyName->empty() && response->empty()) 1790 { 1791 setErrorResponse( 1792 asyncResp->res, 1793 boost::beast::http::status::not_found, 1794 propNotFoundDesc, notFoundMsg); 1795 } 1796 else 1797 { 1798 asyncResp->res.jsonValue["status"] = "ok"; 1799 asyncResp->res.jsonValue["message"] = "200 OK"; 1800 asyncResp->res.jsonValue["data"] = *response; 1801 } 1802 } 1803 }); 1804 } 1805 } 1806 }); 1807 } 1808 1809 struct AsyncPutRequest 1810 { 1811 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : 1812 asyncResp(resIn) 1813 {} 1814 ~AsyncPutRequest() 1815 { 1816 if (asyncResp->res.jsonValue.empty()) 1817 { 1818 setErrorResponse(asyncResp->res, 1819 boost::beast::http::status::forbidden, 1820 forbiddenMsg, forbiddenPropDesc); 1821 } 1822 } 1823 1824 AsyncPutRequest(const AsyncPutRequest&) = delete; 1825 AsyncPutRequest(AsyncPutRequest&&) = delete; 1826 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; 1827 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; 1828 1829 void setErrorStatus(const std::string& desc) 1830 { 1831 setErrorResponse(asyncResp->res, 1832 boost::beast::http::status::internal_server_error, 1833 desc, badReqMsg); 1834 } 1835 1836 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1837 std::string objectPath; 1838 std::string propertyName; 1839 nlohmann::json propertyValue; 1840 }; 1841 1842 inline void handlePut(const crow::Request& req, 1843 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1844 const std::string& objectPath, 1845 const std::string& destProperty) 1846 { 1847 if (destProperty.empty()) 1848 { 1849 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, 1850 forbiddenResDesc, forbiddenMsg); 1851 return; 1852 } 1853 nlohmann::json requestDbusData; 1854 1855 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1856 if (ret == JsonParseResult::BadContentType) 1857 { 1858 setErrorResponse(asyncResp->res, 1859 boost::beast::http::status::unsupported_media_type, 1860 invalidContentType, unsupportedMediaMsg); 1861 return; 1862 } 1863 1864 if (ret != JsonParseResult::Success) 1865 { 1866 setErrorResponse(asyncResp->res, 1867 boost::beast::http::status::bad_request, noJsonDesc, 1868 badReqMsg); 1869 return; 1870 } 1871 1872 auto propertyIt = requestDbusData.find("data"); 1873 if (propertyIt == requestDbusData.end()) 1874 { 1875 setErrorResponse(asyncResp->res, 1876 boost::beast::http::status::bad_request, noJsonDesc, 1877 badReqMsg); 1878 return; 1879 } 1880 const nlohmann::json& propertySetValue = *propertyIt; 1881 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); 1882 transaction->objectPath = objectPath; 1883 transaction->propertyName = destProperty; 1884 transaction->propertyValue = propertySetValue; 1885 1886 dbus::utility::getDbusObject( 1887 transaction->objectPath, {}, 1888 [transaction](const boost::system::error_code& ec2, 1889 const dbus::utility::MapperGetObject& objectNames) { 1890 if (!ec2 && objectNames.empty()) 1891 { 1892 setErrorResponse(transaction->asyncResp->res, 1893 boost::beast::http::status::not_found, 1894 propNotFoundDesc, notFoundMsg); 1895 return; 1896 } 1897 1898 for (const std::pair<std::string, std::vector<std::string>>& 1899 connection : objectNames) 1900 { 1901 const std::string& connectionName = connection.first; 1902 1903 crow::connections::systemBus->async_method_call( 1904 [connectionName{std::string(connectionName)}, 1905 transaction](const boost::system::error_code& ec3, 1906 const std::string& introspectXml) { 1907 if (ec3) 1908 { 1909 BMCWEB_LOG_ERROR( 1910 "Introspect call failed with error: {} on process: {}", 1911 ec3.message(), connectionName); 1912 transaction->setErrorStatus("Unexpected Error"); 1913 return; 1914 } 1915 tinyxml2::XMLDocument doc; 1916 1917 doc.Parse(introspectXml.c_str()); 1918 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1919 if (pRoot == nullptr) 1920 { 1921 BMCWEB_LOG_ERROR("XML document failed to parse: {}", 1922 introspectXml); 1923 transaction->setErrorStatus("Unexpected Error"); 1924 return; 1925 } 1926 tinyxml2::XMLElement* ifaceNode = 1927 pRoot->FirstChildElement("interface"); 1928 while (ifaceNode != nullptr) 1929 { 1930 const char* interfaceName = ifaceNode->Attribute("name"); 1931 BMCWEB_LOG_DEBUG("found interface {}", interfaceName); 1932 tinyxml2::XMLElement* propNode = 1933 ifaceNode->FirstChildElement("property"); 1934 while (propNode != nullptr) 1935 { 1936 const char* propertyName = propNode->Attribute("name"); 1937 BMCWEB_LOG_DEBUG("Found property {}", propertyName); 1938 if (propertyName == transaction->propertyName) 1939 { 1940 const char* argType = propNode->Attribute("type"); 1941 if (argType != nullptr) 1942 { 1943 sdbusplus::message_t m = 1944 crow::connections::systemBus 1945 ->new_method_call( 1946 connectionName.c_str(), 1947 transaction->objectPath.c_str(), 1948 "org.freedesktop.DBus." 1949 "Properties", 1950 "Set"); 1951 m.append(interfaceName, 1952 transaction->propertyName); 1953 int r = sd_bus_message_open_container( 1954 m.get(), SD_BUS_TYPE_VARIANT, argType); 1955 if (r < 0) 1956 { 1957 transaction->setErrorStatus( 1958 "Unexpected Error"); 1959 return; 1960 } 1961 r = convertJsonToDbus( 1962 m.get(), argType, 1963 transaction->propertyValue); 1964 if (r < 0) 1965 { 1966 if (r == -ERANGE) 1967 { 1968 transaction->setErrorStatus( 1969 "Provided property value " 1970 "is out of range for the " 1971 "property type"); 1972 } 1973 else 1974 { 1975 transaction->setErrorStatus( 1976 "Invalid arg type"); 1977 } 1978 return; 1979 } 1980 r = sd_bus_message_close_container(m.get()); 1981 if (r < 0) 1982 { 1983 transaction->setErrorStatus( 1984 "Unexpected Error"); 1985 return; 1986 } 1987 crow::connections::systemBus->async_send( 1988 m, [transaction]( 1989 const boost::system::error_code& ec, 1990 sdbusplus::message_t& m2) { 1991 BMCWEB_LOG_DEBUG("sent"); 1992 if (ec) 1993 { 1994 const sd_bus_error* e = m2.get_error(); 1995 setErrorResponse( 1996 transaction->asyncResp->res, 1997 boost::beast::http::status:: 1998 forbidden, 1999 (e) != nullptr 2000 ? e->name 2001 : ec.category().name(), 2002 (e) != nullptr ? e->message 2003 : ec.message()); 2004 } 2005 else 2006 { 2007 transaction->asyncResp->res 2008 .jsonValue["status"] = "ok"; 2009 transaction->asyncResp->res 2010 .jsonValue["message"] = "200 OK"; 2011 transaction->asyncResp->res 2012 .jsonValue["data"] = nullptr; 2013 } 2014 }); 2015 } 2016 } 2017 propNode = propNode->NextSiblingElement("property"); 2018 } 2019 ifaceNode = ifaceNode->NextSiblingElement("interface"); 2020 } 2021 }, 2022 connectionName, transaction->objectPath, 2023 "org.freedesktop.DBus.Introspectable", "Introspect"); 2024 } 2025 }); 2026 } 2027 2028 inline void handleDBusUrl(const crow::Request& req, 2029 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2030 std::string& objectPath) 2031 { 2032 // If accessing a single attribute, fill in and update objectPath, 2033 // otherwise leave destProperty blank 2034 std::string destProperty; 2035 const char* attrSeperator = "/attr/"; 2036 size_t attrPosition = objectPath.find(attrSeperator); 2037 if (attrPosition != std::string::npos) 2038 { 2039 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2040 objectPath.length()); 2041 objectPath.resize(attrPosition); 2042 } 2043 2044 if (req.method() == boost::beast::http::verb::post) 2045 { 2046 constexpr const char* actionSeperator = "/action/"; 2047 size_t actionPosition = objectPath.find(actionSeperator); 2048 if (actionPosition != std::string::npos) 2049 { 2050 std::string postProperty = 2051 objectPath.substr((actionPosition + strlen(actionSeperator)), 2052 objectPath.length()); 2053 objectPath.resize(actionPosition); 2054 handleAction(req, asyncResp, objectPath, postProperty); 2055 return; 2056 } 2057 } 2058 else if (req.method() == boost::beast::http::verb::get) 2059 { 2060 if (objectPath.ends_with("/enumerate")) 2061 { 2062 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2063 objectPath.end()); 2064 handleEnumerate(asyncResp, objectPath); 2065 } 2066 else if (objectPath.ends_with("/list")) 2067 { 2068 objectPath.erase(objectPath.end() - sizeof("list"), 2069 objectPath.end()); 2070 handleList(asyncResp, objectPath); 2071 } 2072 else 2073 { 2074 // Trim any trailing "/" at the end 2075 if (objectPath.ends_with("/")) 2076 { 2077 objectPath.pop_back(); 2078 handleList(asyncResp, objectPath, 1); 2079 } 2080 else 2081 { 2082 handleGet(asyncResp, objectPath, destProperty); 2083 } 2084 } 2085 return; 2086 } 2087 else if (req.method() == boost::beast::http::verb::put) 2088 { 2089 handlePut(req, asyncResp, objectPath, destProperty); 2090 return; 2091 } 2092 else if (req.method() == boost::beast::http::verb::delete_) 2093 { 2094 handleDelete(asyncResp, objectPath); 2095 return; 2096 } 2097 2098 setErrorResponse(asyncResp->res, 2099 boost::beast::http::status::method_not_allowed, 2100 methodNotAllowedDesc, methodNotAllowedMsg); 2101 } 2102 2103 inline void 2104 handleBusSystemPost(const crow::Request& req, 2105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2106 const std::string& processName, 2107 const std::string& requestedPath) 2108 { 2109 std::vector<std::string> strs; 2110 2111 bmcweb::split(strs, requestedPath, '/'); 2112 std::string objectPath; 2113 std::string interfaceName; 2114 std::string methodName; 2115 auto it = strs.begin(); 2116 if (it == strs.end()) 2117 { 2118 objectPath = "/"; 2119 } 2120 while (it != strs.end()) 2121 { 2122 // Check if segment contains ".". If it does, it must be an 2123 // interface 2124 if (it->find(".") != std::string::npos) 2125 { 2126 break; 2127 // This check is necessary as the trailing slash gets 2128 // parsed as part of our <path> specifier above, which 2129 // causes the normal trailing backslash redirector to 2130 // fail. 2131 } 2132 if (!it->empty()) 2133 { 2134 objectPath += "/" + *it; 2135 } 2136 it++; 2137 } 2138 if (it != strs.end()) 2139 { 2140 interfaceName = *it; 2141 it++; 2142 2143 // after interface, we might have a method name 2144 if (it != strs.end()) 2145 { 2146 methodName = *it; 2147 it++; 2148 } 2149 } 2150 if (it != strs.end()) 2151 { 2152 // if there is more levels past the method name, something 2153 // went wrong, return not found 2154 asyncResp->res.result(boost::beast::http::status::not_found); 2155 return; 2156 } 2157 if (interfaceName.empty()) 2158 { 2159 crow::connections::systemBus->async_method_call( 2160 [asyncResp, processName, 2161 objectPath](const boost::system::error_code& ec, 2162 const std::string& introspectXml) { 2163 if (ec) 2164 { 2165 BMCWEB_LOG_ERROR( 2166 "Introspect call failed with error: {} on process: {} path: {}", 2167 ec.message(), processName, objectPath); 2168 return; 2169 } 2170 tinyxml2::XMLDocument doc; 2171 2172 doc.Parse(introspectXml.c_str()); 2173 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2174 if (pRoot == nullptr) 2175 { 2176 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2177 processName, objectPath); 2178 asyncResp->res.jsonValue["status"] = "XML parse error"; 2179 asyncResp->res.result( 2180 boost::beast::http::status::internal_server_error); 2181 return; 2182 } 2183 2184 BMCWEB_LOG_DEBUG("{}", introspectXml); 2185 asyncResp->res.jsonValue["status"] = "ok"; 2186 asyncResp->res.jsonValue["bus_name"] = processName; 2187 asyncResp->res.jsonValue["object_path"] = objectPath; 2188 2189 nlohmann::json& interfacesArray = 2190 asyncResp->res.jsonValue["interfaces"]; 2191 interfacesArray = nlohmann::json::array(); 2192 tinyxml2::XMLElement* interface = 2193 pRoot->FirstChildElement("interface"); 2194 2195 while (interface != nullptr) 2196 { 2197 const char* ifaceName = interface->Attribute("name"); 2198 if (ifaceName != nullptr) 2199 { 2200 nlohmann::json::object_t interfaceObj; 2201 interfaceObj["name"] = ifaceName; 2202 interfacesArray.emplace_back(std::move(interfaceObj)); 2203 } 2204 2205 interface = interface->NextSiblingElement("interface"); 2206 } 2207 }, 2208 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2209 "Introspect"); 2210 } 2211 else if (methodName.empty()) 2212 { 2213 crow::connections::systemBus->async_method_call( 2214 [asyncResp, processName, objectPath, 2215 interfaceName](const boost::system::error_code& ec, 2216 const std::string& introspectXml) { 2217 if (ec) 2218 { 2219 BMCWEB_LOG_ERROR( 2220 "Introspect call failed with error: {} on process: {} path: {}", 2221 ec.message(), processName, objectPath); 2222 return; 2223 } 2224 tinyxml2::XMLDocument doc; 2225 2226 doc.Parse(introspectXml.data(), introspectXml.size()); 2227 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2228 if (pRoot == nullptr) 2229 { 2230 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2231 processName, objectPath); 2232 asyncResp->res.result( 2233 boost::beast::http::status::internal_server_error); 2234 return; 2235 } 2236 2237 asyncResp->res.jsonValue["status"] = "ok"; 2238 asyncResp->res.jsonValue["bus_name"] = processName; 2239 asyncResp->res.jsonValue["interface"] = interfaceName; 2240 asyncResp->res.jsonValue["object_path"] = objectPath; 2241 2242 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2243 methodsArray = nlohmann::json::array(); 2244 2245 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2246 signalsArray = nlohmann::json::array(); 2247 2248 nlohmann::json& propertiesObj = 2249 asyncResp->res.jsonValue["properties"]; 2250 propertiesObj = nlohmann::json::object(); 2251 2252 // if we know we're the only call, build the 2253 // json directly 2254 tinyxml2::XMLElement* interface = 2255 pRoot->FirstChildElement("interface"); 2256 while (interface != nullptr) 2257 { 2258 const char* ifaceName = interface->Attribute("name"); 2259 2260 if (ifaceName != nullptr && ifaceName == interfaceName) 2261 { 2262 break; 2263 } 2264 2265 interface = interface->NextSiblingElement("interface"); 2266 } 2267 if (interface == nullptr) 2268 { 2269 // if we got to the end of the list and 2270 // never found a match, throw 404 2271 asyncResp->res.result(boost::beast::http::status::not_found); 2272 return; 2273 } 2274 2275 tinyxml2::XMLElement* methods = 2276 interface->FirstChildElement("method"); 2277 while (methods != nullptr) 2278 { 2279 nlohmann::json argsArray = nlohmann::json::array(); 2280 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2281 while (arg != nullptr) 2282 { 2283 nlohmann::json thisArg; 2284 for (const char* fieldName : std::array<const char*, 3>{ 2285 "name", "direction", "type"}) 2286 { 2287 const char* fieldValue = arg->Attribute(fieldName); 2288 if (fieldValue != nullptr) 2289 { 2290 thisArg[fieldName] = fieldValue; 2291 } 2292 } 2293 argsArray.emplace_back(std::move(thisArg)); 2294 arg = arg->NextSiblingElement("arg"); 2295 } 2296 2297 const char* name = methods->Attribute("name"); 2298 if (name != nullptr) 2299 { 2300 std::string uri; 2301 uri.reserve(14 + processName.size() + objectPath.size() + 2302 interfaceName.size() + strlen(name)); 2303 uri += "/bus/system/"; 2304 uri += processName; 2305 uri += objectPath; 2306 uri += "/"; 2307 uri += interfaceName; 2308 uri += "/"; 2309 uri += name; 2310 2311 nlohmann::json::object_t object; 2312 object["name"] = name; 2313 object["uri"] = std::move(uri); 2314 object["args"] = argsArray; 2315 2316 methodsArray.emplace_back(std::move(object)); 2317 } 2318 methods = methods->NextSiblingElement("method"); 2319 } 2320 tinyxml2::XMLElement* signals = 2321 interface->FirstChildElement("signal"); 2322 while (signals != nullptr) 2323 { 2324 nlohmann::json argsArray = nlohmann::json::array(); 2325 2326 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2327 while (arg != nullptr) 2328 { 2329 const char* name = arg->Attribute("name"); 2330 const char* type = arg->Attribute("type"); 2331 if (name != nullptr && type != nullptr) 2332 { 2333 argsArray.push_back({ 2334 {"name", name}, 2335 {"type", type}, 2336 }); 2337 } 2338 arg = arg->NextSiblingElement("arg"); 2339 } 2340 const char* name = signals->Attribute("name"); 2341 if (name != nullptr) 2342 { 2343 nlohmann::json::object_t object; 2344 object["name"] = name; 2345 object["args"] = argsArray; 2346 signalsArray.emplace_back(std::move(object)); 2347 } 2348 2349 signals = signals->NextSiblingElement("signal"); 2350 } 2351 2352 tinyxml2::XMLElement* property = 2353 interface->FirstChildElement("property"); 2354 while (property != nullptr) 2355 { 2356 const char* name = property->Attribute("name"); 2357 const char* type = property->Attribute("type"); 2358 if (type != nullptr && name != nullptr) 2359 { 2360 sdbusplus::message_t m = 2361 crow::connections::systemBus->new_method_call( 2362 processName.c_str(), objectPath.c_str(), 2363 "org.freedesktop." 2364 "DBus." 2365 "Properties", 2366 "Get"); 2367 m.append(interfaceName, name); 2368 nlohmann::json& propertyItem = propertiesObj[name]; 2369 crow::connections::systemBus->async_send( 2370 m, [&propertyItem, 2371 asyncResp](const boost::system::error_code& ec2, 2372 sdbusplus::message_t& msg) { 2373 if (ec2) 2374 { 2375 return; 2376 } 2377 2378 convertDBusToJSON("v", msg, propertyItem); 2379 }); 2380 } 2381 property = property->NextSiblingElement("property"); 2382 } 2383 }, 2384 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2385 "Introspect"); 2386 } 2387 else 2388 { 2389 if (req.method() != boost::beast::http::verb::post) 2390 { 2391 asyncResp->res.result(boost::beast::http::status::not_found); 2392 return; 2393 } 2394 2395 nlohmann::json requestDbusData; 2396 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 2397 if (ret == JsonParseResult::BadContentType) 2398 { 2399 setErrorResponse(asyncResp->res, 2400 boost::beast::http::status::unsupported_media_type, 2401 invalidContentType, unsupportedMediaMsg); 2402 return; 2403 } 2404 if (ret != JsonParseResult::Success) 2405 { 2406 setErrorResponse(asyncResp->res, 2407 boost::beast::http::status::bad_request, 2408 noJsonDesc, badReqMsg); 2409 return; 2410 } 2411 2412 if (!requestDbusData.is_array()) 2413 { 2414 asyncResp->res.result(boost::beast::http::status::bad_request); 2415 return; 2416 } 2417 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 2418 2419 transaction->path = objectPath; 2420 transaction->methodName = methodName; 2421 transaction->arguments = std::move(requestDbusData); 2422 2423 findActionOnInterface(transaction, processName); 2424 } 2425 } 2426 2427 inline void requestRoutes(App& app) 2428 { 2429 BMCWEB_ROUTE(app, "/bus/") 2430 .privileges({{"Login"}}) 2431 .methods(boost::beast::http::verb::get)( 2432 [](const crow::Request&, 2433 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2434 nlohmann::json::array_t buses; 2435 nlohmann::json& bus = buses.emplace_back(); 2436 bus["name"] = "system"; 2437 asyncResp->res.jsonValue["busses"] = std::move(buses); 2438 asyncResp->res.jsonValue["status"] = "ok"; 2439 }); 2440 2441 BMCWEB_ROUTE(app, "/bus/system/") 2442 .privileges({{"Login"}}) 2443 .methods(boost::beast::http::verb::get)( 2444 [](const crow::Request&, 2445 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2446 auto myCallback = [asyncResp](const boost::system::error_code& ec, 2447 std::vector<std::string>& names) { 2448 if (ec) 2449 { 2450 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec); 2451 asyncResp->res.result( 2452 boost::beast::http::status::internal_server_error); 2453 } 2454 else 2455 { 2456 std::ranges::sort(names); 2457 asyncResp->res.jsonValue["status"] = "ok"; 2458 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2459 for (const auto& name : names) 2460 { 2461 nlohmann::json::object_t object; 2462 object["name"] = name; 2463 objectsSub.emplace_back(std::move(object)); 2464 } 2465 } 2466 }; 2467 crow::connections::systemBus->async_method_call( 2468 std::move(myCallback), "org.freedesktop.DBus", "/", 2469 "org.freedesktop.DBus", "ListNames"); 2470 }); 2471 2472 BMCWEB_ROUTE(app, "/list/") 2473 .privileges({{"Login"}}) 2474 .methods(boost::beast::http::verb::get)( 2475 [](const crow::Request&, 2476 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2477 handleList(asyncResp, "/"); 2478 }); 2479 2480 BMCWEB_ROUTE(app, "/xyz/<path>") 2481 .privileges({{"Login"}}) 2482 .methods(boost::beast::http::verb::get)( 2483 [](const crow::Request& req, 2484 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2485 const std::string& path) { 2486 std::string objectPath = "/xyz/" + path; 2487 handleDBusUrl(req, asyncResp, objectPath); 2488 }); 2489 2490 BMCWEB_ROUTE(app, "/xyz/<path>") 2491 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2492 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2493 boost::beast::http::verb::delete_)( 2494 [](const crow::Request& req, 2495 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2496 const std::string& path) { 2497 std::string objectPath = "/xyz/" + path; 2498 handleDBusUrl(req, asyncResp, objectPath); 2499 }); 2500 2501 BMCWEB_ROUTE(app, "/org/<path>") 2502 .privileges({{"Login"}}) 2503 .methods(boost::beast::http::verb::get)( 2504 [](const crow::Request& req, 2505 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2506 const std::string& path) { 2507 std::string objectPath = "/org/" + path; 2508 handleDBusUrl(req, asyncResp, objectPath); 2509 }); 2510 2511 BMCWEB_ROUTE(app, "/org/<path>") 2512 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2513 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2514 boost::beast::http::verb::delete_)( 2515 [](const crow::Request& req, 2516 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2517 const std::string& path) { 2518 std::string objectPath = "/org/" + path; 2519 handleDBusUrl(req, asyncResp, objectPath); 2520 }); 2521 2522 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2523 .privileges({{"ConfigureManager"}}) 2524 .methods(boost::beast::http::verb::get)( 2525 [](const crow::Request&, 2526 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2527 const std::string& dumpId) { 2528 if (!validateFilename(dumpId)) 2529 { 2530 asyncResp->res.result(boost::beast::http::status::bad_request); 2531 return; 2532 } 2533 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2534 2535 loc /= dumpId; 2536 2537 if (!std::filesystem::exists(loc) || 2538 !std::filesystem::is_directory(loc)) 2539 { 2540 BMCWEB_LOG_ERROR("{}Not found", loc.string()); 2541 asyncResp->res.result(boost::beast::http::status::not_found); 2542 return; 2543 } 2544 std::filesystem::directory_iterator files(loc); 2545 2546 for (const auto& file : files) 2547 { 2548 if (!asyncResp->res.openFile(file)) 2549 { 2550 continue; 2551 } 2552 2553 asyncResp->res.addHeader(boost::beast::http::field::content_type, 2554 "application/octet-stream"); 2555 2556 // Assuming only one dump file will be present in the dump 2557 // id directory 2558 std::string dumpFileName = file.path().filename().string(); 2559 2560 // Filename should be in alphanumeric, dot and underscore 2561 // Its based on phosphor-debug-collector application 2562 // dumpfile format 2563 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2564 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2565 { 2566 BMCWEB_LOG_ERROR("Invalid dump filename {}", dumpFileName); 2567 asyncResp->res.result(boost::beast::http::status::not_found); 2568 return; 2569 } 2570 std::string contentDispositionParam = "attachment; filename=\"" + 2571 dumpFileName + "\""; 2572 2573 asyncResp->res.addHeader( 2574 boost::beast::http::field::content_disposition, 2575 contentDispositionParam); 2576 2577 return; 2578 } 2579 asyncResp->res.result(boost::beast::http::status::not_found); 2580 return; 2581 }); 2582 2583 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2584 .privileges({{"Login"}}) 2585 2586 .methods(boost::beast::http::verb::get)( 2587 [](const crow::Request&, 2588 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2589 const std::string& connection) { 2590 introspectObjects(connection, "/", asyncResp); 2591 }); 2592 2593 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2594 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2595 .methods(boost::beast::http::verb::get, 2596 boost::beast::http::verb::post)(handleBusSystemPost); 2597 } 2598 } // namespace openbmc_mapper 2599 } // namespace crow 2600