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