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 for (const auto& it : j->items()) 918 { 919 r = convertJsonToDbus(m, keyType, it.key()); 920 if (r < 0) 921 { 922 return r; 923 } 924 925 r = convertJsonToDbus(m, valueType, it.value()); 926 if (r < 0) 927 { 928 return r; 929 } 930 } 931 r = sd_bus_message_close_container(m); 932 } 933 else 934 { 935 return -2; 936 } 937 if (r < 0) 938 { 939 return r; 940 } 941 942 if (argTypes.size() > 1) 943 { 944 jIt++; 945 } 946 } 947 948 return r; 949 } 950 951 template <typename T> 952 int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m, 953 nlohmann::json& data) 954 { 955 T value; 956 // When T == char*, this warning fires. Unclear how to resolve 957 // Given that sd-bus takes a void pointer to a char*, and that's 958 // Not something we can fix. 959 // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) 960 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value); 961 if (r < 0) 962 { 963 BMCWEB_LOG_ERROR("sd_bus_message_read_basic on type {} failed!", 964 typeCode); 965 return r; 966 } 967 968 data = value; 969 return 0; 970 } 971 972 int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m, 973 nlohmann::json& response); 974 975 inline int readDictEntryFromMessage(const std::string& typeCode, 976 sdbusplus::message_t& m, 977 nlohmann::json& object) 978 { 979 std::vector<std::string> types = dbusArgSplit(typeCode); 980 if (types.size() != 2) 981 { 982 BMCWEB_LOG_ERROR("wrong number contained types in dictionary: {}", 983 types.size()); 984 return -1; 985 } 986 987 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY, 988 typeCode.c_str()); 989 if (r < 0) 990 { 991 BMCWEB_LOG_ERROR("sd_bus_message_enter_container with rc {}", r); 992 return r; 993 } 994 995 nlohmann::json key; 996 r = convertDBusToJSON(types[0], m, key); 997 if (r < 0) 998 { 999 return r; 1000 } 1001 1002 const std::string* keyPtr = key.get_ptr<const std::string*>(); 1003 if (keyPtr == nullptr) 1004 { 1005 // json doesn't support non-string keys. If we hit this condition, 1006 // convert the result to a string so we can proceed 1007 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 1008 keyPtr = key.get_ptr<const std::string*>(); 1009 // in theory this can't fail now, but lets be paranoid about it 1010 // anyway 1011 if (keyPtr == nullptr) 1012 { 1013 return -1; 1014 } 1015 } 1016 nlohmann::json& value = object[*keyPtr]; 1017 1018 r = convertDBusToJSON(types[1], m, value); 1019 if (r < 0) 1020 { 1021 return r; 1022 } 1023 1024 r = sd_bus_message_exit_container(m.get()); 1025 if (r < 0) 1026 { 1027 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed"); 1028 return r; 1029 } 1030 1031 return 0; 1032 } 1033 1034 inline int readArrayFromMessage(const std::string& typeCode, 1035 sdbusplus::message_t& m, nlohmann::json& data) 1036 { 1037 if (typeCode.size() < 2) 1038 { 1039 BMCWEB_LOG_ERROR("Type code {} too small for an array", typeCode); 1040 return -1; 1041 } 1042 1043 std::string containedType = typeCode.substr(1); 1044 1045 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY, 1046 containedType.c_str()); 1047 if (r < 0) 1048 { 1049 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r); 1050 return r; 1051 } 1052 1053 bool dict = containedType.starts_with("{") && containedType.ends_with("}"); 1054 1055 if (dict) 1056 { 1057 // Remove the { } 1058 containedType = containedType.substr(1, containedType.size() - 2); 1059 data = nlohmann::json::object(); 1060 } 1061 else 1062 { 1063 data = nlohmann::json::array(); 1064 } 1065 1066 while (true) 1067 { 1068 r = sd_bus_message_at_end(m.get(), 0); 1069 if (r < 0) 1070 { 1071 BMCWEB_LOG_ERROR("sd_bus_message_at_end failed"); 1072 return r; 1073 } 1074 1075 if (r > 0) 1076 { 1077 break; 1078 } 1079 1080 // Dictionaries are only ever seen in an array 1081 if (dict) 1082 { 1083 r = readDictEntryFromMessage(containedType, m, data); 1084 if (r < 0) 1085 { 1086 return r; 1087 } 1088 } 1089 else 1090 { 1091 data.push_back(nlohmann::json()); 1092 1093 r = convertDBusToJSON(containedType, m, data.back()); 1094 if (r < 0) 1095 { 1096 return r; 1097 } 1098 } 1099 } 1100 1101 r = sd_bus_message_exit_container(m.get()); 1102 if (r < 0) 1103 { 1104 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed"); 1105 return r; 1106 } 1107 1108 return 0; 1109 } 1110 1111 inline int readStructFromMessage(const std::string& typeCode, 1112 sdbusplus::message_t& m, nlohmann::json& data) 1113 { 1114 if (typeCode.size() < 3) 1115 { 1116 BMCWEB_LOG_ERROR("Type code {} too small for a struct", typeCode); 1117 return -1; 1118 } 1119 1120 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2); 1121 std::vector<std::string> types = dbusArgSplit(containedTypes); 1122 1123 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT, 1124 containedTypes.c_str()); 1125 if (r < 0) 1126 { 1127 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r); 1128 return r; 1129 } 1130 1131 for (const std::string& type : types) 1132 { 1133 data.push_back(nlohmann::json()); 1134 r = convertDBusToJSON(type, m, data.back()); 1135 if (r < 0) 1136 { 1137 return r; 1138 } 1139 } 1140 1141 r = sd_bus_message_exit_container(m.get()); 1142 if (r < 0) 1143 { 1144 BMCWEB_LOG_ERROR("sd_bus_message_exit_container failed"); 1145 return r; 1146 } 1147 return 0; 1148 } 1149 1150 inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data) 1151 { 1152 const char* containerType = nullptr; 1153 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType); 1154 if (r < 0) 1155 { 1156 BMCWEB_LOG_ERROR("sd_bus_message_peek_type failed"); 1157 return r; 1158 } 1159 1160 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT, 1161 containerType); 1162 if (r < 0) 1163 { 1164 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed with rc {}", r); 1165 return r; 1166 } 1167 1168 r = convertDBusToJSON(containerType, m, data); 1169 if (r < 0) 1170 { 1171 return r; 1172 } 1173 1174 r = sd_bus_message_exit_container(m.get()); 1175 if (r < 0) 1176 { 1177 BMCWEB_LOG_ERROR("sd_bus_message_enter_container failed"); 1178 return r; 1179 } 1180 1181 return 0; 1182 } 1183 1184 inline int convertDBusToJSON(const std::string& returnType, 1185 sdbusplus::message_t& m, nlohmann::json& response) 1186 { 1187 int r = 0; 1188 const std::vector<std::string> returnTypes = dbusArgSplit(returnType); 1189 1190 for (const std::string& typeCode : returnTypes) 1191 { 1192 nlohmann::json* thisElement = &response; 1193 if (returnTypes.size() > 1) 1194 { 1195 response.push_back(nlohmann::json{}); 1196 thisElement = &response.back(); 1197 } 1198 1199 if (typeCode == "s" || typeCode == "g" || typeCode == "o") 1200 { 1201 r = readMessageItem<char*>(typeCode, m, *thisElement); 1202 if (r < 0) 1203 { 1204 return r; 1205 } 1206 } 1207 else if (typeCode == "b") 1208 { 1209 r = readMessageItem<int>(typeCode, m, *thisElement); 1210 if (r < 0) 1211 { 1212 return r; 1213 } 1214 1215 *thisElement = static_cast<bool>(thisElement->get<int>()); 1216 } 1217 else if (typeCode == "u") 1218 { 1219 r = readMessageItem<uint32_t>(typeCode, m, *thisElement); 1220 if (r < 0) 1221 { 1222 return r; 1223 } 1224 } 1225 else if (typeCode == "i") 1226 { 1227 r = readMessageItem<int32_t>(typeCode, m, *thisElement); 1228 if (r < 0) 1229 { 1230 return r; 1231 } 1232 } 1233 else if (typeCode == "x") 1234 { 1235 r = readMessageItem<int64_t>(typeCode, m, *thisElement); 1236 if (r < 0) 1237 { 1238 return r; 1239 } 1240 } 1241 else if (typeCode == "t") 1242 { 1243 r = readMessageItem<uint64_t>(typeCode, m, *thisElement); 1244 if (r < 0) 1245 { 1246 return r; 1247 } 1248 } 1249 else if (typeCode == "n") 1250 { 1251 r = readMessageItem<int16_t>(typeCode, m, *thisElement); 1252 if (r < 0) 1253 { 1254 return r; 1255 } 1256 } 1257 else if (typeCode == "q") 1258 { 1259 r = readMessageItem<uint16_t>(typeCode, m, *thisElement); 1260 if (r < 0) 1261 { 1262 return r; 1263 } 1264 } 1265 else if (typeCode == "y") 1266 { 1267 r = readMessageItem<uint8_t>(typeCode, m, *thisElement); 1268 if (r < 0) 1269 { 1270 return r; 1271 } 1272 } 1273 else if (typeCode == "d") 1274 { 1275 r = readMessageItem<double>(typeCode, m, *thisElement); 1276 if (r < 0) 1277 { 1278 return r; 1279 } 1280 } 1281 else if (typeCode == "h") 1282 { 1283 r = readMessageItem<int>(typeCode, m, *thisElement); 1284 if (r < 0) 1285 { 1286 return r; 1287 } 1288 } 1289 else if (typeCode.starts_with("a")) 1290 { 1291 r = readArrayFromMessage(typeCode, m, *thisElement); 1292 if (r < 0) 1293 { 1294 return r; 1295 } 1296 } 1297 else if (typeCode.starts_with("(") && typeCode.ends_with(")")) 1298 { 1299 r = readStructFromMessage(typeCode, m, *thisElement); 1300 if (r < 0) 1301 { 1302 return r; 1303 } 1304 } 1305 else if (typeCode.starts_with("v")) 1306 { 1307 r = readVariantFromMessage(m, *thisElement); 1308 if (r < 0) 1309 { 1310 return r; 1311 } 1312 } 1313 else 1314 { 1315 BMCWEB_LOG_ERROR("Invalid D-Bus signature type {}", typeCode); 1316 return -2; 1317 } 1318 } 1319 1320 return 0; 1321 } 1322 1323 inline void handleMethodResponse( 1324 const std::shared_ptr<InProgressActionData>& transaction, 1325 sdbusplus::message_t& m, const std::string& returnType) 1326 { 1327 nlohmann::json data; 1328 1329 int r = convertDBusToJSON(returnType, m, data); 1330 if (r < 0) 1331 { 1332 transaction->outputFailed = true; 1333 return; 1334 } 1335 1336 if (data.is_null()) 1337 { 1338 return; 1339 } 1340 1341 if (transaction->methodResponse.is_null()) 1342 { 1343 transaction->methodResponse = std::move(data); 1344 return; 1345 } 1346 1347 // If they're both dictionaries or arrays, merge into one. 1348 // Otherwise, make the results an array with every result 1349 // an entry. Could also just fail in that case, but it 1350 // seems better to get the data back somehow. 1351 1352 if (transaction->methodResponse.is_object() && data.is_object()) 1353 { 1354 for (const auto& obj : data.items()) 1355 { 1356 // Note: Will overwrite the data for a duplicate key 1357 transaction->methodResponse.emplace(obj.key(), 1358 std::move(obj.value())); 1359 } 1360 return; 1361 } 1362 1363 if (transaction->methodResponse.is_array() && data.is_array()) 1364 { 1365 for (auto& obj : data) 1366 { 1367 transaction->methodResponse.emplace_back(std::move(obj)); 1368 } 1369 return; 1370 } 1371 1372 if (!transaction->convertedToArray) 1373 { 1374 // They are different types. May as well turn them into an array 1375 nlohmann::json j = std::move(transaction->methodResponse); 1376 transaction->methodResponse = nlohmann::json::array(); 1377 transaction->methodResponse.emplace_back(std::move(j)); 1378 transaction->methodResponse.emplace_back(std::move(data)); 1379 transaction->convertedToArray = true; 1380 } 1381 else 1382 { 1383 transaction->methodResponse.emplace_back(std::move(data)); 1384 } 1385 } 1386 1387 inline void findActionOnInterface( 1388 const std::shared_ptr<InProgressActionData>& transaction, 1389 const std::string& connectionName) 1390 { 1391 BMCWEB_LOG_DEBUG("findActionOnInterface for connection {}", connectionName); 1392 crow::connections::systemBus->async_method_call( 1393 [transaction, connectionName{std::string(connectionName)}]( 1394 const boost::system::error_code& ec, 1395 const std::string& introspectXml) { 1396 BMCWEB_LOG_DEBUG("got xml:\n {}", introspectXml); 1397 if (ec) 1398 { 1399 BMCWEB_LOG_ERROR( 1400 "Introspect call failed with error: {} on process: {}", 1401 ec.message(), connectionName); 1402 return; 1403 } 1404 tinyxml2::XMLDocument doc; 1405 1406 doc.Parse(introspectXml.data(), introspectXml.size()); 1407 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1408 if (pRoot == nullptr) 1409 { 1410 BMCWEB_LOG_ERROR("XML document failed to parse {}", connectionName); 1411 return; 1412 } 1413 tinyxml2::XMLElement* interfaceNode = 1414 pRoot->FirstChildElement("interface"); 1415 while (interfaceNode != nullptr) 1416 { 1417 const char* thisInterfaceName = interfaceNode->Attribute("name"); 1418 if (thisInterfaceName != nullptr) 1419 { 1420 if (!transaction->interfaceName.empty() && 1421 (transaction->interfaceName != thisInterfaceName)) 1422 { 1423 interfaceNode = 1424 interfaceNode->NextSiblingElement("interface"); 1425 continue; 1426 } 1427 1428 tinyxml2::XMLElement* methodNode = 1429 interfaceNode->FirstChildElement("method"); 1430 while (methodNode != nullptr) 1431 { 1432 const char* thisMethodName = methodNode->Attribute("name"); 1433 BMCWEB_LOG_DEBUG("Found method: {}", thisMethodName); 1434 if (thisMethodName != nullptr && 1435 thisMethodName == transaction->methodName) 1436 { 1437 BMCWEB_LOG_DEBUG( 1438 "Found method named {} on interface {}", 1439 thisMethodName, thisInterfaceName); 1440 sdbusplus::message_t m = 1441 crow::connections::systemBus->new_method_call( 1442 connectionName.c_str(), 1443 transaction->path.c_str(), thisInterfaceName, 1444 transaction->methodName.c_str()); 1445 1446 tinyxml2::XMLElement* argumentNode = 1447 methodNode->FirstChildElement("arg"); 1448 1449 std::string returnType; 1450 1451 // Find the output type 1452 while (argumentNode != nullptr) 1453 { 1454 const char* argDirection = 1455 argumentNode->Attribute("direction"); 1456 const char* argType = 1457 argumentNode->Attribute("type"); 1458 if (argDirection != nullptr && argType != nullptr && 1459 std::string(argDirection) == "out") 1460 { 1461 returnType = argType; 1462 break; 1463 } 1464 argumentNode = 1465 argumentNode->NextSiblingElement("arg"); 1466 } 1467 1468 auto argIt = transaction->arguments.begin(); 1469 1470 argumentNode = methodNode->FirstChildElement("arg"); 1471 1472 while (argumentNode != nullptr) 1473 { 1474 const char* argDirection = 1475 argumentNode->Attribute("direction"); 1476 const char* argType = 1477 argumentNode->Attribute("type"); 1478 if (argDirection != nullptr && argType != nullptr && 1479 std::string(argDirection) == "in") 1480 { 1481 if (argIt == transaction->arguments.end()) 1482 { 1483 transaction->setErrorStatus( 1484 "Invalid method args"); 1485 return; 1486 } 1487 if (convertJsonToDbus(m.get(), 1488 std::string(argType), 1489 *argIt) < 0) 1490 { 1491 transaction->setErrorStatus( 1492 "Invalid method arg type"); 1493 return; 1494 } 1495 1496 argIt++; 1497 } 1498 argumentNode = 1499 argumentNode->NextSiblingElement("arg"); 1500 } 1501 1502 crow::connections::systemBus->async_send( 1503 m, [transaction, returnType]( 1504 const boost::system::error_code& ec2, 1505 sdbusplus::message_t& m2) { 1506 if (ec2) 1507 { 1508 transaction->methodFailed = true; 1509 const sd_bus_error* e = m2.get_error(); 1510 1511 if (e != nullptr) 1512 { 1513 setErrorResponse( 1514 transaction->asyncResp->res, 1515 boost::beast::http::status::bad_request, 1516 e->name, e->message); 1517 } 1518 else 1519 { 1520 setErrorResponse( 1521 transaction->asyncResp->res, 1522 boost::beast::http::status::bad_request, 1523 "Method call failed", methodFailedMsg); 1524 } 1525 return; 1526 } 1527 transaction->methodPassed = true; 1528 1529 handleMethodResponse(transaction, m2, returnType); 1530 }); 1531 break; 1532 } 1533 methodNode = methodNode->NextSiblingElement("method"); 1534 } 1535 } 1536 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1537 } 1538 }, 1539 connectionName, transaction->path, 1540 "org.freedesktop.DBus.Introspectable", "Introspect"); 1541 } 1542 1543 inline void handleAction(const crow::Request& req, 1544 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1545 const std::string& objectPath, 1546 const std::string& methodName) 1547 { 1548 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath, 1549 methodName); 1550 nlohmann::json requestDbusData; 1551 1552 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1553 if (ret == JsonParseResult::BadContentType) 1554 { 1555 setErrorResponse(asyncResp->res, 1556 boost::beast::http::status::unsupported_media_type, 1557 invalidContentType, unsupportedMediaMsg); 1558 return; 1559 } 1560 if (ret != JsonParseResult::Success) 1561 { 1562 setErrorResponse(asyncResp->res, 1563 boost::beast::http::status::bad_request, noJsonDesc, 1564 badReqMsg); 1565 return; 1566 } 1567 nlohmann::json::iterator data = requestDbusData.find("data"); 1568 if (data == requestDbusData.end()) 1569 { 1570 setErrorResponse(asyncResp->res, 1571 boost::beast::http::status::bad_request, noJsonDesc, 1572 badReqMsg); 1573 return; 1574 } 1575 1576 if (!data->is_array()) 1577 { 1578 setErrorResponse(asyncResp->res, 1579 boost::beast::http::status::bad_request, noJsonDesc, 1580 badReqMsg); 1581 return; 1582 } 1583 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 1584 1585 transaction->path = objectPath; 1586 transaction->methodName = methodName; 1587 transaction->arguments = std::move(*data); 1588 dbus::utility::getDbusObject( 1589 objectPath, {}, 1590 [transaction]( 1591 const boost::system::error_code& ec, 1592 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1593 interfaceNames) { 1594 if (ec || interfaceNames.empty()) 1595 { 1596 BMCWEB_LOG_ERROR("Can't find object"); 1597 setErrorResponse(transaction->asyncResp->res, 1598 boost::beast::http::status::not_found, 1599 notFoundDesc, notFoundMsg); 1600 return; 1601 } 1602 1603 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)", 1604 interfaceNames.size()); 1605 1606 for (const std::pair<std::string, std::vector<std::string>>& object : 1607 interfaceNames) 1608 { 1609 findActionOnInterface(transaction, object.first); 1610 } 1611 }); 1612 } 1613 1614 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1615 const std::string& objectPath) 1616 { 1617 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath); 1618 1619 dbus::utility::getDbusObject( 1620 objectPath, {}, 1621 [asyncResp, objectPath]( 1622 const boost::system::error_code& ec, 1623 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1624 interfaceNames) { 1625 if (ec || interfaceNames.empty()) 1626 { 1627 BMCWEB_LOG_ERROR("Can't find object"); 1628 setErrorResponse(asyncResp->res, 1629 boost::beast::http::status::method_not_allowed, 1630 methodNotAllowedDesc, methodNotAllowedMsg); 1631 return; 1632 } 1633 1634 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 1635 transaction->path = objectPath; 1636 transaction->methodName = "Delete"; 1637 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1638 1639 for (const std::pair<std::string, std::vector<std::string>>& object : 1640 interfaceNames) 1641 { 1642 findActionOnInterface(transaction, object.first); 1643 } 1644 }); 1645 } 1646 1647 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1648 const std::string& objectPath, int32_t depth = 0) 1649 { 1650 dbus::utility::getSubTreePaths( 1651 objectPath, depth, {}, 1652 [asyncResp]( 1653 const boost::system::error_code& ec, 1654 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { 1655 if (ec) 1656 { 1657 setErrorResponse(asyncResp->res, 1658 boost::beast::http::status::not_found, 1659 notFoundDesc, notFoundMsg); 1660 } 1661 else 1662 { 1663 asyncResp->res.jsonValue["status"] = "ok"; 1664 asyncResp->res.jsonValue["message"] = "200 OK"; 1665 asyncResp->res.jsonValue["data"] = objectPaths; 1666 } 1667 }); 1668 } 1669 1670 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1671 const std::string& objectPath) 1672 { 1673 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath); 1674 1675 asyncResp->res.jsonValue["message"] = "200 OK"; 1676 asyncResp->res.jsonValue["status"] = "ok"; 1677 asyncResp->res.jsonValue["data"] = nlohmann::json::object(); 1678 1679 dbus::utility::getSubTree( 1680 objectPath, 0, {}, 1681 [objectPath, asyncResp]( 1682 const boost::system::error_code& ec, 1683 const dbus::utility::MapperGetSubTreeResponse& objectNames) { 1684 auto transaction = std::make_shared<InProgressEnumerateData>(objectPath, 1685 asyncResp); 1686 1687 transaction->subtree = 1688 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( 1689 objectNames); 1690 1691 if (ec) 1692 { 1693 BMCWEB_LOG_ERROR("GetSubTree failed on {}", 1694 transaction->objectPath); 1695 setErrorResponse(transaction->asyncResp->res, 1696 boost::beast::http::status::not_found, 1697 notFoundDesc, notFoundMsg); 1698 return; 1699 } 1700 1701 // Add the data for the path passed in to the results 1702 // as if GetSubTree returned it, and continue on enumerating 1703 getObjectAndEnumerate(transaction); 1704 }); 1705 } 1706 1707 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1708 std::string& objectPath, std::string& destProperty) 1709 { 1710 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty); 1711 std::shared_ptr<std::string> propertyName = 1712 std::make_shared<std::string>(std::move(destProperty)); 1713 1714 std::shared_ptr<std::string> path = 1715 std::make_shared<std::string>(std::move(objectPath)); 1716 1717 dbus::utility::getDbusObject( 1718 *path, {}, 1719 [asyncResp, path, 1720 propertyName](const boost::system::error_code& ec, 1721 const dbus::utility::MapperGetObject& objectNames) { 1722 if (ec || objectNames.empty()) 1723 { 1724 setErrorResponse(asyncResp->res, 1725 boost::beast::http::status::not_found, 1726 notFoundDesc, notFoundMsg); 1727 return; 1728 } 1729 std::shared_ptr<nlohmann::json> response = 1730 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1731 // The mapper should never give us an empty interface names 1732 // list, but check anyway 1733 for (const std::pair<std::string, std::vector<std::string>>& 1734 connection : objectNames) 1735 { 1736 const std::vector<std::string>& interfaceNames = connection.second; 1737 1738 if (interfaceNames.empty()) 1739 { 1740 setErrorResponse(asyncResp->res, 1741 boost::beast::http::status::not_found, 1742 notFoundDesc, notFoundMsg); 1743 return; 1744 } 1745 1746 for (const std::string& interface : interfaceNames) 1747 { 1748 sdbusplus::message_t m = 1749 crow::connections::systemBus->new_method_call( 1750 connection.first.c_str(), path->c_str(), 1751 "org.freedesktop.DBus.Properties", "GetAll"); 1752 m.append(interface); 1753 crow::connections::systemBus->async_send( 1754 m, [asyncResp, response, 1755 propertyName](const boost::system::error_code& ec2, 1756 sdbusplus::message_t& msg) { 1757 if (ec2) 1758 { 1759 BMCWEB_LOG_ERROR("Bad dbus request error: {}", ec2); 1760 } 1761 else 1762 { 1763 nlohmann::json properties; 1764 int r = convertDBusToJSON("a{sv}", msg, properties); 1765 if (r < 0) 1766 { 1767 BMCWEB_LOG_ERROR("convertDBusToJSON failed"); 1768 } 1769 else 1770 { 1771 for (const auto& prop : properties.items()) 1772 { 1773 // if property name is empty, or 1774 // matches our search query, add it 1775 // to the response json 1776 1777 if (propertyName->empty()) 1778 { 1779 (*response)[prop.key()] = 1780 std::move(prop.value()); 1781 } 1782 else if (prop.key() == *propertyName) 1783 { 1784 *response = std::move(prop.value()); 1785 } 1786 } 1787 } 1788 } 1789 if (response.use_count() == 1) 1790 { 1791 if (!propertyName->empty() && response->empty()) 1792 { 1793 setErrorResponse( 1794 asyncResp->res, 1795 boost::beast::http::status::not_found, 1796 propNotFoundDesc, notFoundMsg); 1797 } 1798 else 1799 { 1800 asyncResp->res.jsonValue["status"] = "ok"; 1801 asyncResp->res.jsonValue["message"] = "200 OK"; 1802 asyncResp->res.jsonValue["data"] = *response; 1803 } 1804 } 1805 }); 1806 } 1807 } 1808 }); 1809 } 1810 1811 struct AsyncPutRequest 1812 { 1813 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : 1814 asyncResp(resIn) 1815 {} 1816 ~AsyncPutRequest() 1817 { 1818 if (asyncResp->res.jsonValue.empty()) 1819 { 1820 setErrorResponse(asyncResp->res, 1821 boost::beast::http::status::forbidden, 1822 forbiddenMsg, forbiddenPropDesc); 1823 } 1824 } 1825 1826 AsyncPutRequest(const AsyncPutRequest&) = delete; 1827 AsyncPutRequest(AsyncPutRequest&&) = delete; 1828 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; 1829 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; 1830 1831 void setErrorStatus(const std::string& desc) 1832 { 1833 setErrorResponse(asyncResp->res, 1834 boost::beast::http::status::internal_server_error, 1835 desc, badReqMsg); 1836 } 1837 1838 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1839 std::string objectPath; 1840 std::string propertyName; 1841 nlohmann::json propertyValue; 1842 }; 1843 1844 inline void handlePut(const crow::Request& req, 1845 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1846 const std::string& objectPath, 1847 const std::string& destProperty) 1848 { 1849 if (destProperty.empty()) 1850 { 1851 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, 1852 forbiddenResDesc, forbiddenMsg); 1853 return; 1854 } 1855 nlohmann::json requestDbusData; 1856 1857 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1858 if (ret == JsonParseResult::BadContentType) 1859 { 1860 setErrorResponse(asyncResp->res, 1861 boost::beast::http::status::unsupported_media_type, 1862 invalidContentType, unsupportedMediaMsg); 1863 return; 1864 } 1865 1866 if (ret != JsonParseResult::Success) 1867 { 1868 setErrorResponse(asyncResp->res, 1869 boost::beast::http::status::bad_request, noJsonDesc, 1870 badReqMsg); 1871 return; 1872 } 1873 1874 auto propertyIt = requestDbusData.find("data"); 1875 if (propertyIt == requestDbusData.end()) 1876 { 1877 setErrorResponse(asyncResp->res, 1878 boost::beast::http::status::bad_request, noJsonDesc, 1879 badReqMsg); 1880 return; 1881 } 1882 const nlohmann::json& propertySetValue = *propertyIt; 1883 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); 1884 transaction->objectPath = objectPath; 1885 transaction->propertyName = destProperty; 1886 transaction->propertyValue = propertySetValue; 1887 1888 dbus::utility::getDbusObject( 1889 transaction->objectPath, {}, 1890 [transaction](const boost::system::error_code& ec2, 1891 const dbus::utility::MapperGetObject& objectNames) { 1892 if (!ec2 && objectNames.empty()) 1893 { 1894 setErrorResponse(transaction->asyncResp->res, 1895 boost::beast::http::status::not_found, 1896 propNotFoundDesc, notFoundMsg); 1897 return; 1898 } 1899 1900 for (const std::pair<std::string, std::vector<std::string>>& 1901 connection : objectNames) 1902 { 1903 const std::string& connectionName = connection.first; 1904 1905 crow::connections::systemBus->async_method_call( 1906 [connectionName{std::string(connectionName)}, 1907 transaction](const boost::system::error_code& ec3, 1908 const std::string& introspectXml) { 1909 if (ec3) 1910 { 1911 BMCWEB_LOG_ERROR( 1912 "Introspect call failed with error: {} on process: {}", 1913 ec3.message(), connectionName); 1914 transaction->setErrorStatus("Unexpected Error"); 1915 return; 1916 } 1917 tinyxml2::XMLDocument doc; 1918 1919 doc.Parse(introspectXml.c_str()); 1920 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1921 if (pRoot == nullptr) 1922 { 1923 BMCWEB_LOG_ERROR("XML document failed to parse: {}", 1924 introspectXml); 1925 transaction->setErrorStatus("Unexpected Error"); 1926 return; 1927 } 1928 tinyxml2::XMLElement* ifaceNode = 1929 pRoot->FirstChildElement("interface"); 1930 while (ifaceNode != nullptr) 1931 { 1932 const char* interfaceName = ifaceNode->Attribute("name"); 1933 BMCWEB_LOG_DEBUG("found interface {}", interfaceName); 1934 tinyxml2::XMLElement* propNode = 1935 ifaceNode->FirstChildElement("property"); 1936 while (propNode != nullptr) 1937 { 1938 const char* propertyName = propNode->Attribute("name"); 1939 BMCWEB_LOG_DEBUG("Found property {}", propertyName); 1940 if (propertyName == transaction->propertyName) 1941 { 1942 const char* argType = propNode->Attribute("type"); 1943 if (argType != nullptr) 1944 { 1945 sdbusplus::message_t m = 1946 crow::connections::systemBus 1947 ->new_method_call( 1948 connectionName.c_str(), 1949 transaction->objectPath.c_str(), 1950 "org.freedesktop.DBus." 1951 "Properties", 1952 "Set"); 1953 m.append(interfaceName, 1954 transaction->propertyName); 1955 int r = sd_bus_message_open_container( 1956 m.get(), SD_BUS_TYPE_VARIANT, argType); 1957 if (r < 0) 1958 { 1959 transaction->setErrorStatus( 1960 "Unexpected Error"); 1961 return; 1962 } 1963 r = convertJsonToDbus( 1964 m.get(), argType, 1965 transaction->propertyValue); 1966 if (r < 0) 1967 { 1968 if (r == -ERANGE) 1969 { 1970 transaction->setErrorStatus( 1971 "Provided property value " 1972 "is out of range for the " 1973 "property type"); 1974 } 1975 else 1976 { 1977 transaction->setErrorStatus( 1978 "Invalid arg type"); 1979 } 1980 return; 1981 } 1982 r = sd_bus_message_close_container(m.get()); 1983 if (r < 0) 1984 { 1985 transaction->setErrorStatus( 1986 "Unexpected Error"); 1987 return; 1988 } 1989 crow::connections::systemBus->async_send( 1990 m, [transaction]( 1991 const boost::system::error_code& ec, 1992 sdbusplus::message_t& m2) { 1993 BMCWEB_LOG_DEBUG("sent"); 1994 if (ec) 1995 { 1996 const sd_bus_error* e = m2.get_error(); 1997 setErrorResponse( 1998 transaction->asyncResp->res, 1999 boost::beast::http::status:: 2000 forbidden, 2001 (e) != nullptr 2002 ? e->name 2003 : ec.category().name(), 2004 (e) != nullptr ? e->message 2005 : ec.message()); 2006 } 2007 else 2008 { 2009 transaction->asyncResp->res 2010 .jsonValue["status"] = "ok"; 2011 transaction->asyncResp->res 2012 .jsonValue["message"] = "200 OK"; 2013 transaction->asyncResp->res 2014 .jsonValue["data"] = nullptr; 2015 } 2016 }); 2017 } 2018 } 2019 propNode = propNode->NextSiblingElement("property"); 2020 } 2021 ifaceNode = ifaceNode->NextSiblingElement("interface"); 2022 } 2023 }, 2024 connectionName, transaction->objectPath, 2025 "org.freedesktop.DBus.Introspectable", "Introspect"); 2026 } 2027 }); 2028 } 2029 2030 inline void handleDBusUrl(const crow::Request& req, 2031 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2032 std::string& objectPath) 2033 { 2034 // If accessing a single attribute, fill in and update objectPath, 2035 // otherwise leave destProperty blank 2036 std::string destProperty; 2037 const char* attrSeperator = "/attr/"; 2038 size_t attrPosition = objectPath.find(attrSeperator); 2039 if (attrPosition != std::string::npos) 2040 { 2041 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2042 objectPath.length()); 2043 objectPath.resize(attrPosition); 2044 } 2045 2046 if (req.method() == boost::beast::http::verb::post) 2047 { 2048 constexpr const char* actionSeperator = "/action/"; 2049 size_t actionPosition = objectPath.find(actionSeperator); 2050 if (actionPosition != std::string::npos) 2051 { 2052 std::string postProperty = 2053 objectPath.substr((actionPosition + strlen(actionSeperator)), 2054 objectPath.length()); 2055 objectPath.resize(actionPosition); 2056 handleAction(req, asyncResp, objectPath, postProperty); 2057 return; 2058 } 2059 } 2060 else if (req.method() == boost::beast::http::verb::get) 2061 { 2062 if (objectPath.ends_with("/enumerate")) 2063 { 2064 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2065 objectPath.end()); 2066 handleEnumerate(asyncResp, objectPath); 2067 } 2068 else if (objectPath.ends_with("/list")) 2069 { 2070 objectPath.erase(objectPath.end() - sizeof("list"), 2071 objectPath.end()); 2072 handleList(asyncResp, objectPath); 2073 } 2074 else 2075 { 2076 // Trim any trailing "/" at the end 2077 if (objectPath.ends_with("/")) 2078 { 2079 objectPath.pop_back(); 2080 handleList(asyncResp, objectPath, 1); 2081 } 2082 else 2083 { 2084 handleGet(asyncResp, objectPath, destProperty); 2085 } 2086 } 2087 return; 2088 } 2089 else if (req.method() == boost::beast::http::verb::put) 2090 { 2091 handlePut(req, asyncResp, objectPath, destProperty); 2092 return; 2093 } 2094 else if (req.method() == boost::beast::http::verb::delete_) 2095 { 2096 handleDelete(asyncResp, objectPath); 2097 return; 2098 } 2099 2100 setErrorResponse(asyncResp->res, 2101 boost::beast::http::status::method_not_allowed, 2102 methodNotAllowedDesc, methodNotAllowedMsg); 2103 } 2104 2105 inline void 2106 handleBusSystemPost(const crow::Request& req, 2107 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2108 const std::string& processName, 2109 const std::string& requestedPath) 2110 { 2111 std::vector<std::string> strs; 2112 2113 bmcweb::split(strs, requestedPath, '/'); 2114 std::string objectPath; 2115 std::string interfaceName; 2116 std::string methodName; 2117 auto it = strs.begin(); 2118 if (it == strs.end()) 2119 { 2120 objectPath = "/"; 2121 } 2122 while (it != strs.end()) 2123 { 2124 // Check if segment contains ".". If it does, it must be an 2125 // interface 2126 if (it->find(".") != std::string::npos) 2127 { 2128 break; 2129 // This check is necessary as the trailing slash gets 2130 // parsed as part of our <path> specifier above, which 2131 // causes the normal trailing backslash redirector to 2132 // fail. 2133 } 2134 if (!it->empty()) 2135 { 2136 objectPath += "/" + *it; 2137 } 2138 it++; 2139 } 2140 if (it != strs.end()) 2141 { 2142 interfaceName = *it; 2143 it++; 2144 2145 // after interface, we might have a method name 2146 if (it != strs.end()) 2147 { 2148 methodName = *it; 2149 it++; 2150 } 2151 } 2152 if (it != strs.end()) 2153 { 2154 // if there is more levels past the method name, something 2155 // went wrong, return not found 2156 asyncResp->res.result(boost::beast::http::status::not_found); 2157 return; 2158 } 2159 if (interfaceName.empty()) 2160 { 2161 crow::connections::systemBus->async_method_call( 2162 [asyncResp, processName, 2163 objectPath](const boost::system::error_code& ec, 2164 const std::string& introspectXml) { 2165 if (ec) 2166 { 2167 BMCWEB_LOG_ERROR( 2168 "Introspect call failed with error: {} on process: {} path: {}", 2169 ec.message(), processName, objectPath); 2170 return; 2171 } 2172 tinyxml2::XMLDocument doc; 2173 2174 doc.Parse(introspectXml.c_str()); 2175 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2176 if (pRoot == nullptr) 2177 { 2178 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2179 processName, objectPath); 2180 asyncResp->res.jsonValue["status"] = "XML parse error"; 2181 asyncResp->res.result( 2182 boost::beast::http::status::internal_server_error); 2183 return; 2184 } 2185 2186 BMCWEB_LOG_DEBUG("{}", introspectXml); 2187 asyncResp->res.jsonValue["status"] = "ok"; 2188 asyncResp->res.jsonValue["bus_name"] = processName; 2189 asyncResp->res.jsonValue["object_path"] = objectPath; 2190 2191 nlohmann::json& interfacesArray = 2192 asyncResp->res.jsonValue["interfaces"]; 2193 interfacesArray = nlohmann::json::array(); 2194 tinyxml2::XMLElement* interface = 2195 pRoot->FirstChildElement("interface"); 2196 2197 while (interface != nullptr) 2198 { 2199 const char* ifaceName = interface->Attribute("name"); 2200 if (ifaceName != nullptr) 2201 { 2202 nlohmann::json::object_t interfaceObj; 2203 interfaceObj["name"] = ifaceName; 2204 interfacesArray.emplace_back(std::move(interfaceObj)); 2205 } 2206 2207 interface = interface->NextSiblingElement("interface"); 2208 } 2209 }, 2210 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2211 "Introspect"); 2212 } 2213 else if (methodName.empty()) 2214 { 2215 crow::connections::systemBus->async_method_call( 2216 [asyncResp, processName, objectPath, 2217 interfaceName](const boost::system::error_code& ec, 2218 const std::string& introspectXml) { 2219 if (ec) 2220 { 2221 BMCWEB_LOG_ERROR( 2222 "Introspect call failed with error: {} on process: {} path: {}", 2223 ec.message(), processName, objectPath); 2224 return; 2225 } 2226 tinyxml2::XMLDocument doc; 2227 2228 doc.Parse(introspectXml.data(), introspectXml.size()); 2229 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2230 if (pRoot == nullptr) 2231 { 2232 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2233 processName, objectPath); 2234 asyncResp->res.result( 2235 boost::beast::http::status::internal_server_error); 2236 return; 2237 } 2238 2239 asyncResp->res.jsonValue["status"] = "ok"; 2240 asyncResp->res.jsonValue["bus_name"] = processName; 2241 asyncResp->res.jsonValue["interface"] = interfaceName; 2242 asyncResp->res.jsonValue["object_path"] = objectPath; 2243 2244 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2245 methodsArray = nlohmann::json::array(); 2246 2247 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2248 signalsArray = nlohmann::json::array(); 2249 2250 nlohmann::json& propertiesObj = 2251 asyncResp->res.jsonValue["properties"]; 2252 propertiesObj = nlohmann::json::object(); 2253 2254 // if we know we're the only call, build the 2255 // json directly 2256 tinyxml2::XMLElement* interface = 2257 pRoot->FirstChildElement("interface"); 2258 while (interface != nullptr) 2259 { 2260 const char* ifaceName = interface->Attribute("name"); 2261 2262 if (ifaceName != nullptr && ifaceName == interfaceName) 2263 { 2264 break; 2265 } 2266 2267 interface = interface->NextSiblingElement("interface"); 2268 } 2269 if (interface == nullptr) 2270 { 2271 // if we got to the end of the list and 2272 // never found a match, throw 404 2273 asyncResp->res.result(boost::beast::http::status::not_found); 2274 return; 2275 } 2276 2277 tinyxml2::XMLElement* methods = 2278 interface->FirstChildElement("method"); 2279 while (methods != nullptr) 2280 { 2281 nlohmann::json argsArray = nlohmann::json::array(); 2282 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2283 while (arg != nullptr) 2284 { 2285 nlohmann::json thisArg; 2286 for (const char* fieldName : std::array<const char*, 3>{ 2287 "name", "direction", "type"}) 2288 { 2289 const char* fieldValue = arg->Attribute(fieldName); 2290 if (fieldValue != nullptr) 2291 { 2292 thisArg[fieldName] = fieldValue; 2293 } 2294 } 2295 argsArray.emplace_back(std::move(thisArg)); 2296 arg = arg->NextSiblingElement("arg"); 2297 } 2298 2299 const char* name = methods->Attribute("name"); 2300 if (name != nullptr) 2301 { 2302 std::string uri; 2303 uri.reserve(14 + processName.size() + objectPath.size() + 2304 interfaceName.size() + strlen(name)); 2305 uri += "/bus/system/"; 2306 uri += processName; 2307 uri += objectPath; 2308 uri += "/"; 2309 uri += interfaceName; 2310 uri += "/"; 2311 uri += name; 2312 2313 nlohmann::json::object_t object; 2314 object["name"] = name; 2315 object["uri"] = std::move(uri); 2316 object["args"] = argsArray; 2317 2318 methodsArray.emplace_back(std::move(object)); 2319 } 2320 methods = methods->NextSiblingElement("method"); 2321 } 2322 tinyxml2::XMLElement* signals = 2323 interface->FirstChildElement("signal"); 2324 while (signals != nullptr) 2325 { 2326 nlohmann::json argsArray = nlohmann::json::array(); 2327 2328 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2329 while (arg != nullptr) 2330 { 2331 const char* name = arg->Attribute("name"); 2332 const char* type = arg->Attribute("type"); 2333 if (name != nullptr && type != nullptr) 2334 { 2335 argsArray.push_back({ 2336 {"name", name}, 2337 {"type", type}, 2338 }); 2339 } 2340 arg = arg->NextSiblingElement("arg"); 2341 } 2342 const char* name = signals->Attribute("name"); 2343 if (name != nullptr) 2344 { 2345 nlohmann::json::object_t object; 2346 object["name"] = name; 2347 object["args"] = argsArray; 2348 signalsArray.emplace_back(std::move(object)); 2349 } 2350 2351 signals = signals->NextSiblingElement("signal"); 2352 } 2353 2354 tinyxml2::XMLElement* property = 2355 interface->FirstChildElement("property"); 2356 while (property != nullptr) 2357 { 2358 const char* name = property->Attribute("name"); 2359 const char* type = property->Attribute("type"); 2360 if (type != nullptr && name != nullptr) 2361 { 2362 sdbusplus::message_t m = 2363 crow::connections::systemBus->new_method_call( 2364 processName.c_str(), objectPath.c_str(), 2365 "org.freedesktop." 2366 "DBus." 2367 "Properties", 2368 "Get"); 2369 m.append(interfaceName, name); 2370 nlohmann::json& propertyItem = propertiesObj[name]; 2371 crow::connections::systemBus->async_send( 2372 m, [&propertyItem, 2373 asyncResp](const boost::system::error_code& ec2, 2374 sdbusplus::message_t& msg) { 2375 if (ec2) 2376 { 2377 return; 2378 } 2379 2380 int r = convertDBusToJSON("v", msg, propertyItem); 2381 if (r < 0) 2382 { 2383 BMCWEB_LOG_ERROR("Couldn't convert vector to json"); 2384 } 2385 }); 2386 } 2387 property = property->NextSiblingElement("property"); 2388 } 2389 }, 2390 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2391 "Introspect"); 2392 } 2393 else 2394 { 2395 if (req.method() != boost::beast::http::verb::post) 2396 { 2397 asyncResp->res.result(boost::beast::http::status::not_found); 2398 return; 2399 } 2400 2401 nlohmann::json requestDbusData; 2402 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 2403 if (ret == JsonParseResult::BadContentType) 2404 { 2405 setErrorResponse(asyncResp->res, 2406 boost::beast::http::status::unsupported_media_type, 2407 invalidContentType, unsupportedMediaMsg); 2408 return; 2409 } 2410 if (ret != JsonParseResult::Success) 2411 { 2412 setErrorResponse(asyncResp->res, 2413 boost::beast::http::status::bad_request, 2414 noJsonDesc, badReqMsg); 2415 return; 2416 } 2417 2418 if (!requestDbusData.is_array()) 2419 { 2420 asyncResp->res.result(boost::beast::http::status::bad_request); 2421 return; 2422 } 2423 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 2424 2425 transaction->path = objectPath; 2426 transaction->methodName = methodName; 2427 transaction->arguments = std::move(requestDbusData); 2428 2429 findActionOnInterface(transaction, processName); 2430 } 2431 } 2432 2433 inline void requestRoutes(App& app) 2434 { 2435 BMCWEB_ROUTE(app, "/bus/") 2436 .privileges({{"Login"}}) 2437 .methods(boost::beast::http::verb::get)( 2438 [](const crow::Request&, 2439 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2440 nlohmann::json::array_t buses; 2441 nlohmann::json& bus = buses.emplace_back(); 2442 bus["name"] = "system"; 2443 asyncResp->res.jsonValue["busses"] = std::move(buses); 2444 asyncResp->res.jsonValue["status"] = "ok"; 2445 }); 2446 2447 BMCWEB_ROUTE(app, "/bus/system/") 2448 .privileges({{"Login"}}) 2449 .methods(boost::beast::http::verb::get)( 2450 [](const crow::Request&, 2451 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2452 auto myCallback = [asyncResp](const boost::system::error_code& ec, 2453 std::vector<std::string>& names) { 2454 if (ec) 2455 { 2456 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec); 2457 asyncResp->res.result( 2458 boost::beast::http::status::internal_server_error); 2459 } 2460 else 2461 { 2462 std::ranges::sort(names); 2463 asyncResp->res.jsonValue["status"] = "ok"; 2464 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2465 for (const auto& name : names) 2466 { 2467 nlohmann::json::object_t object; 2468 object["name"] = name; 2469 objectsSub.emplace_back(std::move(object)); 2470 } 2471 } 2472 }; 2473 crow::connections::systemBus->async_method_call( 2474 std::move(myCallback), "org.freedesktop.DBus", "/", 2475 "org.freedesktop.DBus", "ListNames"); 2476 }); 2477 2478 BMCWEB_ROUTE(app, "/list/") 2479 .privileges({{"Login"}}) 2480 .methods(boost::beast::http::verb::get)( 2481 [](const crow::Request&, 2482 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2483 handleList(asyncResp, "/"); 2484 }); 2485 2486 BMCWEB_ROUTE(app, "/xyz/<path>") 2487 .privileges({{"Login"}}) 2488 .methods(boost::beast::http::verb::get)( 2489 [](const crow::Request& req, 2490 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2491 const std::string& path) { 2492 std::string objectPath = "/xyz/" + path; 2493 handleDBusUrl(req, asyncResp, objectPath); 2494 }); 2495 2496 BMCWEB_ROUTE(app, "/xyz/<path>") 2497 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2498 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2499 boost::beast::http::verb::delete_)( 2500 [](const crow::Request& req, 2501 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2502 const std::string& path) { 2503 std::string objectPath = "/xyz/" + path; 2504 handleDBusUrl(req, asyncResp, objectPath); 2505 }); 2506 2507 BMCWEB_ROUTE(app, "/org/<path>") 2508 .privileges({{"Login"}}) 2509 .methods(boost::beast::http::verb::get)( 2510 [](const crow::Request& req, 2511 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2512 const std::string& path) { 2513 std::string objectPath = "/org/" + path; 2514 handleDBusUrl(req, asyncResp, objectPath); 2515 }); 2516 2517 BMCWEB_ROUTE(app, "/org/<path>") 2518 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2519 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2520 boost::beast::http::verb::delete_)( 2521 [](const crow::Request& req, 2522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2523 const std::string& path) { 2524 std::string objectPath = "/org/" + path; 2525 handleDBusUrl(req, asyncResp, objectPath); 2526 }); 2527 2528 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2529 .privileges({{"ConfigureManager"}}) 2530 .methods(boost::beast::http::verb::get)( 2531 [](const crow::Request&, 2532 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2533 const std::string& dumpId) { 2534 if (!validateFilename(dumpId)) 2535 { 2536 asyncResp->res.result(boost::beast::http::status::bad_request); 2537 return; 2538 } 2539 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2540 2541 loc /= dumpId; 2542 2543 if (!std::filesystem::exists(loc) || 2544 !std::filesystem::is_directory(loc)) 2545 { 2546 BMCWEB_LOG_ERROR("{}Not found", loc.string()); 2547 asyncResp->res.result(boost::beast::http::status::not_found); 2548 return; 2549 } 2550 std::filesystem::directory_iterator files(loc); 2551 2552 for (const auto& file : files) 2553 { 2554 if (!asyncResp->res.openFile(file)) 2555 { 2556 continue; 2557 } 2558 2559 asyncResp->res.addHeader(boost::beast::http::field::content_type, 2560 "application/octet-stream"); 2561 2562 // Assuming only one dump file will be present in the dump 2563 // id directory 2564 std::string dumpFileName = file.path().filename().string(); 2565 2566 // Filename should be in alphanumeric, dot and underscore 2567 // Its based on phosphor-debug-collector application 2568 // dumpfile format 2569 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2570 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2571 { 2572 BMCWEB_LOG_ERROR("Invalid dump filename {}", dumpFileName); 2573 asyncResp->res.result(boost::beast::http::status::not_found); 2574 return; 2575 } 2576 std::string contentDispositionParam = "attachment; filename=\"" + 2577 dumpFileName + "\""; 2578 2579 asyncResp->res.addHeader( 2580 boost::beast::http::field::content_disposition, 2581 contentDispositionParam); 2582 2583 return; 2584 } 2585 asyncResp->res.result(boost::beast::http::status::not_found); 2586 return; 2587 }); 2588 2589 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2590 .privileges({{"Login"}}) 2591 2592 .methods(boost::beast::http::verb::get)( 2593 [](const crow::Request&, 2594 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2595 const std::string& connection) { 2596 introspectObjects(connection, "/", asyncResp); 2597 }); 2598 2599 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2600 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2601 .methods(boost::beast::http::verb::get, 2602 boost::beast::http::verb::post)(handleBusSystemPost); 2603 } 2604 } // namespace openbmc_mapper 2605 } // namespace crow 2606