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