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 if (propertyName == nullptr) 1954 { 1955 BMCWEB_LOG_DEBUG("Couldn't find name property"); 1956 continue; 1957 } 1958 BMCWEB_LOG_DEBUG("Found property {}", propertyName); 1959 if (propertyName == transaction->propertyName) 1960 { 1961 const char* argType = propNode->Attribute("type"); 1962 if (argType != nullptr) 1963 { 1964 sdbusplus::message_t m = 1965 crow::connections::systemBus 1966 ->new_method_call( 1967 connectionName.c_str(), 1968 transaction->objectPath.c_str(), 1969 "org.freedesktop.DBus." 1970 "Properties", 1971 "Set"); 1972 m.append(interfaceName, 1973 transaction->propertyName); 1974 int r = sd_bus_message_open_container( 1975 m.get(), SD_BUS_TYPE_VARIANT, argType); 1976 if (r < 0) 1977 { 1978 transaction->setErrorStatus( 1979 "Unexpected Error"); 1980 return; 1981 } 1982 r = convertJsonToDbus( 1983 m.get(), argType, 1984 transaction->propertyValue); 1985 if (r < 0) 1986 { 1987 if (r == -ERANGE) 1988 { 1989 transaction->setErrorStatus( 1990 "Provided property value " 1991 "is out of range for the " 1992 "property type"); 1993 } 1994 else 1995 { 1996 transaction->setErrorStatus( 1997 "Invalid arg type"); 1998 } 1999 return; 2000 } 2001 r = sd_bus_message_close_container(m.get()); 2002 if (r < 0) 2003 { 2004 transaction->setErrorStatus( 2005 "Unexpected Error"); 2006 return; 2007 } 2008 crow::connections::systemBus->async_send( 2009 m, [transaction]( 2010 const boost::system::error_code& ec, 2011 sdbusplus::message_t& m2) { 2012 BMCWEB_LOG_DEBUG("sent"); 2013 if (ec) 2014 { 2015 const sd_bus_error* e = m2.get_error(); 2016 setErrorResponse( 2017 transaction->asyncResp->res, 2018 boost::beast::http::status:: 2019 forbidden, 2020 (e) != nullptr 2021 ? e->name 2022 : ec.category().name(), 2023 (e) != nullptr ? e->message 2024 : ec.message()); 2025 } 2026 else 2027 { 2028 transaction->asyncResp->res 2029 .jsonValue["status"] = "ok"; 2030 transaction->asyncResp->res 2031 .jsonValue["message"] = "200 OK"; 2032 transaction->asyncResp->res 2033 .jsonValue["data"] = nullptr; 2034 } 2035 }); 2036 } 2037 } 2038 propNode = propNode->NextSiblingElement("property"); 2039 } 2040 ifaceNode = ifaceNode->NextSiblingElement("interface"); 2041 } 2042 }, 2043 connectionName, transaction->objectPath, 2044 "org.freedesktop.DBus.Introspectable", "Introspect"); 2045 } 2046 }); 2047 } 2048 2049 inline void handleDBusUrl(const crow::Request& req, 2050 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2051 std::string& objectPath) 2052 { 2053 // If accessing a single attribute, fill in and update objectPath, 2054 // otherwise leave destProperty blank 2055 std::string destProperty; 2056 const char* attrSeperator = "/attr/"; 2057 size_t attrPosition = objectPath.find(attrSeperator); 2058 if (attrPosition != std::string::npos) 2059 { 2060 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2061 objectPath.length()); 2062 objectPath.resize(attrPosition); 2063 } 2064 2065 if (req.method() == boost::beast::http::verb::post) 2066 { 2067 constexpr const char* actionSeperator = "/action/"; 2068 size_t actionPosition = objectPath.find(actionSeperator); 2069 if (actionPosition != std::string::npos) 2070 { 2071 std::string postProperty = 2072 objectPath.substr((actionPosition + strlen(actionSeperator)), 2073 objectPath.length()); 2074 objectPath.resize(actionPosition); 2075 handleAction(req, asyncResp, objectPath, postProperty); 2076 return; 2077 } 2078 } 2079 else if (req.method() == boost::beast::http::verb::get) 2080 { 2081 if (objectPath.ends_with("/enumerate")) 2082 { 2083 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2084 objectPath.end()); 2085 handleEnumerate(asyncResp, objectPath); 2086 } 2087 else if (objectPath.ends_with("/list")) 2088 { 2089 objectPath.erase(objectPath.end() - sizeof("list"), 2090 objectPath.end()); 2091 handleList(asyncResp, objectPath); 2092 } 2093 else 2094 { 2095 // Trim any trailing "/" at the end 2096 if (objectPath.ends_with("/")) 2097 { 2098 objectPath.pop_back(); 2099 handleList(asyncResp, objectPath, 1); 2100 } 2101 else 2102 { 2103 handleGet(asyncResp, objectPath, destProperty); 2104 } 2105 } 2106 return; 2107 } 2108 else if (req.method() == boost::beast::http::verb::put) 2109 { 2110 handlePut(req, asyncResp, objectPath, destProperty); 2111 return; 2112 } 2113 else if (req.method() == boost::beast::http::verb::delete_) 2114 { 2115 handleDelete(asyncResp, objectPath); 2116 return; 2117 } 2118 2119 setErrorResponse(asyncResp->res, 2120 boost::beast::http::status::method_not_allowed, 2121 methodNotAllowedDesc, methodNotAllowedMsg); 2122 } 2123 2124 inline void 2125 handleBusSystemPost(const crow::Request& req, 2126 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2127 const std::string& processName, 2128 const std::string& requestedPath) 2129 { 2130 std::vector<std::string> strs; 2131 2132 bmcweb::split(strs, requestedPath, '/'); 2133 std::string objectPath; 2134 std::string interfaceName; 2135 std::string methodName; 2136 auto it = strs.begin(); 2137 if (it == strs.end()) 2138 { 2139 objectPath = "/"; 2140 } 2141 while (it != strs.end()) 2142 { 2143 // Check if segment contains ".". If it does, it must be an 2144 // interface 2145 if (it->find(".") != std::string::npos) 2146 { 2147 break; 2148 // This check is necessary as the trailing slash gets 2149 // parsed as part of our <path> specifier above, which 2150 // causes the normal trailing backslash redirector to 2151 // fail. 2152 } 2153 if (!it->empty()) 2154 { 2155 objectPath += "/" + *it; 2156 } 2157 it++; 2158 } 2159 if (it != strs.end()) 2160 { 2161 interfaceName = *it; 2162 it++; 2163 2164 // after interface, we might have a method name 2165 if (it != strs.end()) 2166 { 2167 methodName = *it; 2168 it++; 2169 } 2170 } 2171 if (it != strs.end()) 2172 { 2173 // if there is more levels past the method name, something 2174 // went wrong, return not found 2175 asyncResp->res.result(boost::beast::http::status::not_found); 2176 return; 2177 } 2178 if (interfaceName.empty()) 2179 { 2180 crow::connections::systemBus->async_method_call( 2181 [asyncResp, processName, 2182 objectPath](const boost::system::error_code& ec, 2183 const std::string& introspectXml) { 2184 if (ec) 2185 { 2186 BMCWEB_LOG_ERROR( 2187 "Introspect call failed with error: {} on process: {} path: {}", 2188 ec.message(), processName, objectPath); 2189 return; 2190 } 2191 tinyxml2::XMLDocument doc; 2192 2193 doc.Parse(introspectXml.c_str()); 2194 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2195 if (pRoot == nullptr) 2196 { 2197 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2198 processName, objectPath); 2199 asyncResp->res.jsonValue["status"] = "XML parse error"; 2200 asyncResp->res.result( 2201 boost::beast::http::status::internal_server_error); 2202 return; 2203 } 2204 2205 BMCWEB_LOG_DEBUG("{}", introspectXml); 2206 asyncResp->res.jsonValue["status"] = "ok"; 2207 asyncResp->res.jsonValue["bus_name"] = processName; 2208 asyncResp->res.jsonValue["object_path"] = objectPath; 2209 2210 nlohmann::json& interfacesArray = 2211 asyncResp->res.jsonValue["interfaces"]; 2212 interfacesArray = nlohmann::json::array(); 2213 tinyxml2::XMLElement* interface = 2214 pRoot->FirstChildElement("interface"); 2215 2216 while (interface != nullptr) 2217 { 2218 const char* ifaceName = interface->Attribute("name"); 2219 if (ifaceName != nullptr) 2220 { 2221 nlohmann::json::object_t interfaceObj; 2222 interfaceObj["name"] = ifaceName; 2223 interfacesArray.emplace_back(std::move(interfaceObj)); 2224 } 2225 2226 interface = interface->NextSiblingElement("interface"); 2227 } 2228 }, 2229 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2230 "Introspect"); 2231 } 2232 else if (methodName.empty()) 2233 { 2234 crow::connections::systemBus->async_method_call( 2235 [asyncResp, processName, objectPath, 2236 interfaceName](const boost::system::error_code& ec, 2237 const std::string& introspectXml) { 2238 if (ec) 2239 { 2240 BMCWEB_LOG_ERROR( 2241 "Introspect call failed with error: {} on process: {} path: {}", 2242 ec.message(), processName, objectPath); 2243 return; 2244 } 2245 tinyxml2::XMLDocument doc; 2246 2247 doc.Parse(introspectXml.data(), introspectXml.size()); 2248 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2249 if (pRoot == nullptr) 2250 { 2251 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2252 processName, objectPath); 2253 asyncResp->res.result( 2254 boost::beast::http::status::internal_server_error); 2255 return; 2256 } 2257 2258 asyncResp->res.jsonValue["status"] = "ok"; 2259 asyncResp->res.jsonValue["bus_name"] = processName; 2260 asyncResp->res.jsonValue["interface"] = interfaceName; 2261 asyncResp->res.jsonValue["object_path"] = objectPath; 2262 2263 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2264 methodsArray = nlohmann::json::array(); 2265 2266 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2267 signalsArray = nlohmann::json::array(); 2268 2269 nlohmann::json& propertiesObj = 2270 asyncResp->res.jsonValue["properties"]; 2271 propertiesObj = nlohmann::json::object(); 2272 2273 // if we know we're the only call, build the 2274 // json directly 2275 tinyxml2::XMLElement* interface = 2276 pRoot->FirstChildElement("interface"); 2277 while (interface != nullptr) 2278 { 2279 const char* ifaceName = interface->Attribute("name"); 2280 2281 if (ifaceName != nullptr && ifaceName == interfaceName) 2282 { 2283 break; 2284 } 2285 2286 interface = interface->NextSiblingElement("interface"); 2287 } 2288 if (interface == nullptr) 2289 { 2290 // if we got to the end of the list and 2291 // never found a match, throw 404 2292 asyncResp->res.result(boost::beast::http::status::not_found); 2293 return; 2294 } 2295 2296 tinyxml2::XMLElement* methods = 2297 interface->FirstChildElement("method"); 2298 while (methods != nullptr) 2299 { 2300 nlohmann::json argsArray = nlohmann::json::array(); 2301 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2302 while (arg != nullptr) 2303 { 2304 nlohmann::json thisArg; 2305 for (const char* fieldName : std::array<const char*, 3>{ 2306 "name", "direction", "type"}) 2307 { 2308 const char* fieldValue = arg->Attribute(fieldName); 2309 if (fieldValue != nullptr) 2310 { 2311 thisArg[fieldName] = fieldValue; 2312 } 2313 } 2314 argsArray.emplace_back(std::move(thisArg)); 2315 arg = arg->NextSiblingElement("arg"); 2316 } 2317 2318 const char* name = methods->Attribute("name"); 2319 if (name != nullptr) 2320 { 2321 std::string uri; 2322 uri.reserve(14 + processName.size() + objectPath.size() + 2323 interfaceName.size() + strlen(name)); 2324 uri += "/bus/system/"; 2325 uri += processName; 2326 uri += objectPath; 2327 uri += "/"; 2328 uri += interfaceName; 2329 uri += "/"; 2330 uri += name; 2331 2332 nlohmann::json::object_t object; 2333 object["name"] = name; 2334 object["uri"] = std::move(uri); 2335 object["args"] = argsArray; 2336 2337 methodsArray.emplace_back(std::move(object)); 2338 } 2339 methods = methods->NextSiblingElement("method"); 2340 } 2341 tinyxml2::XMLElement* signals = 2342 interface->FirstChildElement("signal"); 2343 while (signals != nullptr) 2344 { 2345 nlohmann::json argsArray = nlohmann::json::array(); 2346 2347 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2348 while (arg != nullptr) 2349 { 2350 const char* name = arg->Attribute("name"); 2351 const char* type = arg->Attribute("type"); 2352 if (name != nullptr && type != nullptr) 2353 { 2354 nlohmann::json::object_t params; 2355 params["name"] = name; 2356 params["type"] = type; 2357 argsArray.push_back(std::move(params)); 2358 } 2359 arg = arg->NextSiblingElement("arg"); 2360 } 2361 const char* name = signals->Attribute("name"); 2362 if (name != nullptr) 2363 { 2364 nlohmann::json::object_t object; 2365 object["name"] = name; 2366 object["args"] = argsArray; 2367 signalsArray.emplace_back(std::move(object)); 2368 } 2369 2370 signals = signals->NextSiblingElement("signal"); 2371 } 2372 2373 tinyxml2::XMLElement* property = 2374 interface->FirstChildElement("property"); 2375 while (property != nullptr) 2376 { 2377 const char* name = property->Attribute("name"); 2378 const char* type = property->Attribute("type"); 2379 if (type != nullptr && name != nullptr) 2380 { 2381 sdbusplus::message_t m = 2382 crow::connections::systemBus->new_method_call( 2383 processName.c_str(), objectPath.c_str(), 2384 "org.freedesktop." 2385 "DBus." 2386 "Properties", 2387 "Get"); 2388 m.append(interfaceName, name); 2389 nlohmann::json& propertyItem = propertiesObj[name]; 2390 crow::connections::systemBus->async_send( 2391 m, [&propertyItem, 2392 asyncResp](const boost::system::error_code& ec2, 2393 sdbusplus::message_t& msg) { 2394 if (ec2) 2395 { 2396 return; 2397 } 2398 2399 int r = convertDBusToJSON("v", msg, propertyItem); 2400 if (r < 0) 2401 { 2402 BMCWEB_LOG_ERROR("Couldn't convert vector to json"); 2403 } 2404 }); 2405 } 2406 property = property->NextSiblingElement("property"); 2407 } 2408 }, 2409 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2410 "Introspect"); 2411 } 2412 else 2413 { 2414 if (req.method() != boost::beast::http::verb::post) 2415 { 2416 asyncResp->res.result(boost::beast::http::status::not_found); 2417 return; 2418 } 2419 2420 nlohmann::json requestDbusData; 2421 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 2422 if (ret == JsonParseResult::BadContentType) 2423 { 2424 setErrorResponse(asyncResp->res, 2425 boost::beast::http::status::unsupported_media_type, 2426 invalidContentType, unsupportedMediaMsg); 2427 return; 2428 } 2429 if (ret != JsonParseResult::Success) 2430 { 2431 setErrorResponse(asyncResp->res, 2432 boost::beast::http::status::bad_request, 2433 noJsonDesc, badReqMsg); 2434 return; 2435 } 2436 2437 if (!requestDbusData.is_array()) 2438 { 2439 asyncResp->res.result(boost::beast::http::status::bad_request); 2440 return; 2441 } 2442 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 2443 2444 transaction->path = objectPath; 2445 transaction->methodName = methodName; 2446 transaction->arguments = std::move(requestDbusData); 2447 2448 findActionOnInterface(transaction, processName); 2449 } 2450 } 2451 2452 inline void requestRoutes(App& app) 2453 { 2454 BMCWEB_ROUTE(app, "/bus/") 2455 .privileges({{"Login"}}) 2456 .methods(boost::beast::http::verb::get)( 2457 [](const crow::Request&, 2458 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2459 nlohmann::json::array_t buses; 2460 nlohmann::json& bus = buses.emplace_back(); 2461 bus["name"] = "system"; 2462 asyncResp->res.jsonValue["busses"] = std::move(buses); 2463 asyncResp->res.jsonValue["status"] = "ok"; 2464 }); 2465 2466 BMCWEB_ROUTE(app, "/bus/system/") 2467 .privileges({{"Login"}}) 2468 .methods(boost::beast::http::verb::get)( 2469 [](const crow::Request&, 2470 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2471 auto myCallback = [asyncResp](const boost::system::error_code& ec, 2472 std::vector<std::string>& names) { 2473 if (ec) 2474 { 2475 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec); 2476 asyncResp->res.result( 2477 boost::beast::http::status::internal_server_error); 2478 } 2479 else 2480 { 2481 std::ranges::sort(names); 2482 asyncResp->res.jsonValue["status"] = "ok"; 2483 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2484 for (const auto& name : names) 2485 { 2486 nlohmann::json::object_t object; 2487 object["name"] = name; 2488 objectsSub.emplace_back(std::move(object)); 2489 } 2490 } 2491 }; 2492 crow::connections::systemBus->async_method_call( 2493 std::move(myCallback), "org.freedesktop.DBus", "/", 2494 "org.freedesktop.DBus", "ListNames"); 2495 }); 2496 2497 BMCWEB_ROUTE(app, "/list/") 2498 .privileges({{"Login"}}) 2499 .methods(boost::beast::http::verb::get)( 2500 [](const crow::Request&, 2501 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2502 handleList(asyncResp, "/"); 2503 }); 2504 2505 BMCWEB_ROUTE(app, "/xyz/<path>") 2506 .privileges({{"Login"}}) 2507 .methods(boost::beast::http::verb::get)( 2508 [](const crow::Request& req, 2509 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2510 const std::string& path) { 2511 std::string objectPath = "/xyz/" + path; 2512 handleDBusUrl(req, asyncResp, objectPath); 2513 }); 2514 2515 BMCWEB_ROUTE(app, "/xyz/<path>") 2516 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2517 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2518 boost::beast::http::verb::delete_)( 2519 [](const crow::Request& req, 2520 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2521 const std::string& path) { 2522 std::string objectPath = "/xyz/" + path; 2523 handleDBusUrl(req, asyncResp, objectPath); 2524 }); 2525 2526 BMCWEB_ROUTE(app, "/org/<path>") 2527 .privileges({{"Login"}}) 2528 .methods(boost::beast::http::verb::get)( 2529 [](const crow::Request& req, 2530 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2531 const std::string& path) { 2532 std::string objectPath = "/org/" + path; 2533 handleDBusUrl(req, asyncResp, objectPath); 2534 }); 2535 2536 BMCWEB_ROUTE(app, "/org/<path>") 2537 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2538 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2539 boost::beast::http::verb::delete_)( 2540 [](const crow::Request& req, 2541 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2542 const std::string& path) { 2543 std::string objectPath = "/org/" + path; 2544 handleDBusUrl(req, asyncResp, objectPath); 2545 }); 2546 2547 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2548 .privileges({{"ConfigureManager"}}) 2549 .methods(boost::beast::http::verb::get)( 2550 [](const crow::Request&, 2551 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2552 const std::string& dumpId) { 2553 if (!validateFilename(dumpId)) 2554 { 2555 asyncResp->res.result(boost::beast::http::status::bad_request); 2556 return; 2557 } 2558 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2559 2560 loc /= dumpId; 2561 2562 if (!std::filesystem::exists(loc) || 2563 !std::filesystem::is_directory(loc)) 2564 { 2565 BMCWEB_LOG_ERROR("{}Not found", loc.string()); 2566 asyncResp->res.result(boost::beast::http::status::not_found); 2567 return; 2568 } 2569 std::filesystem::directory_iterator files(loc); 2570 2571 for (const auto& file : files) 2572 { 2573 if (!asyncResp->res.openFile(file)) 2574 { 2575 continue; 2576 } 2577 2578 asyncResp->res.addHeader(boost::beast::http::field::content_type, 2579 "application/octet-stream"); 2580 2581 // Assuming only one dump file will be present in the dump 2582 // id directory 2583 std::string dumpFileName = file.path().filename().string(); 2584 2585 // Filename should be in alphanumeric, dot and underscore 2586 // Its based on phosphor-debug-collector application 2587 // dumpfile format 2588 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2589 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2590 { 2591 BMCWEB_LOG_ERROR("Invalid dump filename {}", dumpFileName); 2592 asyncResp->res.result(boost::beast::http::status::not_found); 2593 return; 2594 } 2595 std::string contentDispositionParam = "attachment; filename=\"" + 2596 dumpFileName + "\""; 2597 2598 asyncResp->res.addHeader( 2599 boost::beast::http::field::content_disposition, 2600 contentDispositionParam); 2601 2602 return; 2603 } 2604 asyncResp->res.result(boost::beast::http::status::not_found); 2605 return; 2606 }); 2607 2608 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2609 .privileges({{"Login"}}) 2610 2611 .methods(boost::beast::http::verb::get)( 2612 [](const crow::Request&, 2613 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2614 const std::string& connection) { 2615 introspectObjects(connection, "/", asyncResp); 2616 }); 2617 2618 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2619 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2620 .methods(boost::beast::http::verb::get, 2621 boost::beast::http::verb::post)(handleBusSystemPost); 2622 } 2623 } // namespace openbmc_mapper 2624 } // namespace crow 2625