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, [transaction, returnType]( 1491 const boost::system::error_code& ec2, 1492 sdbusplus::message_t& m2) { 1493 if (ec2) 1494 { 1495 transaction->methodFailed = true; 1496 const sd_bus_error* e = m2.get_error(); 1497 1498 if (e != nullptr) 1499 { 1500 setErrorResponse( 1501 transaction->asyncResp->res, 1502 boost::beast::http::status::bad_request, 1503 e->name, e->message); 1504 } 1505 else 1506 { 1507 setErrorResponse( 1508 transaction->asyncResp->res, 1509 boost::beast::http::status::bad_request, 1510 "Method call failed", methodFailedMsg); 1511 } 1512 return; 1513 } 1514 transaction->methodPassed = true; 1515 1516 handleMethodResponse(transaction, m2, returnType); 1517 }); 1518 break; 1519 } 1520 methodNode = methodNode->NextSiblingElement("method"); 1521 } 1522 } 1523 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1524 } 1525 }, 1526 connectionName, transaction->path, 1527 "org.freedesktop.DBus.Introspectable", "Introspect"); 1528 } 1529 1530 inline void handleAction(const crow::Request& req, 1531 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1532 const std::string& objectPath, 1533 const std::string& methodName) 1534 { 1535 BMCWEB_LOG_DEBUG("handleAction on path: {} and method {}", objectPath, 1536 methodName); 1537 nlohmann::json requestDbusData; 1538 1539 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1540 if (ret == JsonParseResult::BadContentType) 1541 { 1542 setErrorResponse(asyncResp->res, 1543 boost::beast::http::status::unsupported_media_type, 1544 invalidContentType, unsupportedMediaMsg); 1545 return; 1546 } 1547 if (ret != JsonParseResult::Success) 1548 { 1549 setErrorResponse(asyncResp->res, 1550 boost::beast::http::status::bad_request, noJsonDesc, 1551 badReqMsg); 1552 return; 1553 } 1554 nlohmann::json::iterator data = requestDbusData.find("data"); 1555 if (data == requestDbusData.end()) 1556 { 1557 setErrorResponse(asyncResp->res, 1558 boost::beast::http::status::bad_request, noJsonDesc, 1559 badReqMsg); 1560 return; 1561 } 1562 1563 if (!data->is_array()) 1564 { 1565 setErrorResponse(asyncResp->res, 1566 boost::beast::http::status::bad_request, noJsonDesc, 1567 badReqMsg); 1568 return; 1569 } 1570 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 1571 1572 transaction->path = objectPath; 1573 transaction->methodName = methodName; 1574 transaction->arguments = std::move(*data); 1575 dbus::utility::getDbusObject( 1576 objectPath, {}, 1577 [transaction]( 1578 const boost::system::error_code& ec, 1579 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1580 interfaceNames) { 1581 if (ec || interfaceNames.empty()) 1582 { 1583 BMCWEB_LOG_ERROR("Can't find object"); 1584 setErrorResponse(transaction->asyncResp->res, 1585 boost::beast::http::status::not_found, 1586 notFoundDesc, notFoundMsg); 1587 return; 1588 } 1589 1590 BMCWEB_LOG_DEBUG("GetObject returned {} object(s)", 1591 interfaceNames.size()); 1592 1593 for (const std::pair<std::string, std::vector<std::string>>& object : 1594 interfaceNames) 1595 { 1596 findActionOnInterface(transaction, object.first); 1597 } 1598 }); 1599 } 1600 1601 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1602 const std::string& objectPath) 1603 { 1604 BMCWEB_LOG_DEBUG("handleDelete on path: {}", objectPath); 1605 1606 dbus::utility::getDbusObject( 1607 objectPath, {}, 1608 [asyncResp, objectPath]( 1609 const boost::system::error_code& ec, 1610 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1611 interfaceNames) { 1612 if (ec || interfaceNames.empty()) 1613 { 1614 BMCWEB_LOG_ERROR("Can't find object"); 1615 setErrorResponse(asyncResp->res, 1616 boost::beast::http::status::method_not_allowed, 1617 methodNotAllowedDesc, methodNotAllowedMsg); 1618 return; 1619 } 1620 1621 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 1622 transaction->path = objectPath; 1623 transaction->methodName = "Delete"; 1624 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1625 1626 for (const std::pair<std::string, std::vector<std::string>>& object : 1627 interfaceNames) 1628 { 1629 findActionOnInterface(transaction, object.first); 1630 } 1631 }); 1632 } 1633 1634 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1635 const std::string& objectPath, int32_t depth = 0) 1636 { 1637 dbus::utility::getSubTreePaths( 1638 objectPath, depth, {}, 1639 [asyncResp]( 1640 const boost::system::error_code& ec, 1641 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { 1642 if (ec) 1643 { 1644 setErrorResponse(asyncResp->res, 1645 boost::beast::http::status::not_found, 1646 notFoundDesc, notFoundMsg); 1647 } 1648 else 1649 { 1650 asyncResp->res.jsonValue["status"] = "ok"; 1651 asyncResp->res.jsonValue["message"] = "200 OK"; 1652 asyncResp->res.jsonValue["data"] = objectPaths; 1653 } 1654 }); 1655 } 1656 1657 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1658 const std::string& objectPath) 1659 { 1660 BMCWEB_LOG_DEBUG("Doing enumerate on {}", objectPath); 1661 1662 asyncResp->res.jsonValue["message"] = "200 OK"; 1663 asyncResp->res.jsonValue["status"] = "ok"; 1664 asyncResp->res.jsonValue["data"] = nlohmann::json::object(); 1665 1666 dbus::utility::getSubTree( 1667 objectPath, 0, {}, 1668 [objectPath, asyncResp]( 1669 const boost::system::error_code& ec, 1670 const dbus::utility::MapperGetSubTreeResponse& objectNames) { 1671 auto transaction = std::make_shared<InProgressEnumerateData>(objectPath, 1672 asyncResp); 1673 1674 transaction->subtree = 1675 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( 1676 objectNames); 1677 1678 if (ec) 1679 { 1680 BMCWEB_LOG_ERROR("GetSubTree failed on {}", 1681 transaction->objectPath); 1682 setErrorResponse(transaction->asyncResp->res, 1683 boost::beast::http::status::not_found, 1684 notFoundDesc, notFoundMsg); 1685 return; 1686 } 1687 1688 // Add the data for the path passed in to the results 1689 // as if GetSubTree returned it, and continue on enumerating 1690 getObjectAndEnumerate(transaction); 1691 }); 1692 } 1693 1694 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1695 std::string& objectPath, std::string& destProperty) 1696 { 1697 BMCWEB_LOG_DEBUG("handleGet: {} prop:{}", objectPath, destProperty); 1698 std::shared_ptr<std::string> propertyName = 1699 std::make_shared<std::string>(std::move(destProperty)); 1700 1701 std::shared_ptr<std::string> path = 1702 std::make_shared<std::string>(std::move(objectPath)); 1703 1704 dbus::utility::getDbusObject( 1705 *path, {}, 1706 [asyncResp, path, 1707 propertyName](const boost::system::error_code& ec, 1708 const dbus::utility::MapperGetObject& objectNames) { 1709 if (ec || objectNames.empty()) 1710 { 1711 setErrorResponse(asyncResp->res, 1712 boost::beast::http::status::not_found, 1713 notFoundDesc, notFoundMsg); 1714 return; 1715 } 1716 std::shared_ptr<nlohmann::json> response = 1717 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1718 // The mapper should never give us an empty interface names 1719 // list, but check anyway 1720 for (const std::pair<std::string, std::vector<std::string>>& 1721 connection : objectNames) 1722 { 1723 const std::vector<std::string>& interfaceNames = connection.second; 1724 1725 if (interfaceNames.empty()) 1726 { 1727 setErrorResponse(asyncResp->res, 1728 boost::beast::http::status::not_found, 1729 notFoundDesc, notFoundMsg); 1730 return; 1731 } 1732 1733 for (const std::string& interface : interfaceNames) 1734 { 1735 sdbusplus::message_t m = 1736 crow::connections::systemBus->new_method_call( 1737 connection.first.c_str(), path->c_str(), 1738 "org.freedesktop.DBus.Properties", "GetAll"); 1739 m.append(interface); 1740 crow::connections::systemBus->async_send( 1741 m, [asyncResp, response, 1742 propertyName](const boost::system::error_code& ec2, 1743 sdbusplus::message_t& msg) { 1744 if (ec2) 1745 { 1746 BMCWEB_LOG_ERROR("Bad dbus request error: {}", ec2); 1747 } 1748 else 1749 { 1750 nlohmann::json properties; 1751 int r = convertDBusToJSON("a{sv}", msg, properties); 1752 if (r < 0) 1753 { 1754 BMCWEB_LOG_ERROR("convertDBusToJSON failed"); 1755 } 1756 else 1757 { 1758 for (const auto& prop : properties.items()) 1759 { 1760 // if property name is empty, or 1761 // matches our search query, add it 1762 // to the response json 1763 1764 if (propertyName->empty()) 1765 { 1766 (*response)[prop.key()] = 1767 std::move(prop.value()); 1768 } 1769 else if (prop.key() == *propertyName) 1770 { 1771 *response = std::move(prop.value()); 1772 } 1773 } 1774 } 1775 } 1776 if (response.use_count() == 1) 1777 { 1778 if (!propertyName->empty() && response->empty()) 1779 { 1780 setErrorResponse( 1781 asyncResp->res, 1782 boost::beast::http::status::not_found, 1783 propNotFoundDesc, notFoundMsg); 1784 } 1785 else 1786 { 1787 asyncResp->res.jsonValue["status"] = "ok"; 1788 asyncResp->res.jsonValue["message"] = "200 OK"; 1789 asyncResp->res.jsonValue["data"] = *response; 1790 } 1791 } 1792 }); 1793 } 1794 } 1795 }); 1796 } 1797 1798 struct AsyncPutRequest 1799 { 1800 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : 1801 asyncResp(resIn) 1802 {} 1803 ~AsyncPutRequest() 1804 { 1805 if (asyncResp->res.jsonValue.empty()) 1806 { 1807 setErrorResponse(asyncResp->res, 1808 boost::beast::http::status::forbidden, 1809 forbiddenMsg, forbiddenPropDesc); 1810 } 1811 } 1812 1813 AsyncPutRequest(const AsyncPutRequest&) = delete; 1814 AsyncPutRequest(AsyncPutRequest&&) = delete; 1815 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; 1816 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; 1817 1818 void setErrorStatus(const std::string& desc) 1819 { 1820 setErrorResponse(asyncResp->res, 1821 boost::beast::http::status::internal_server_error, 1822 desc, badReqMsg); 1823 } 1824 1825 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1826 std::string objectPath; 1827 std::string propertyName; 1828 nlohmann::json propertyValue; 1829 }; 1830 1831 inline void handlePut(const crow::Request& req, 1832 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1833 const std::string& objectPath, 1834 const std::string& destProperty) 1835 { 1836 if (destProperty.empty()) 1837 { 1838 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, 1839 forbiddenResDesc, forbiddenMsg); 1840 return; 1841 } 1842 nlohmann::json requestDbusData; 1843 1844 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1845 if (ret == JsonParseResult::BadContentType) 1846 { 1847 setErrorResponse(asyncResp->res, 1848 boost::beast::http::status::unsupported_media_type, 1849 invalidContentType, unsupportedMediaMsg); 1850 return; 1851 } 1852 1853 if (ret != JsonParseResult::Success) 1854 { 1855 setErrorResponse(asyncResp->res, 1856 boost::beast::http::status::bad_request, noJsonDesc, 1857 badReqMsg); 1858 return; 1859 } 1860 1861 auto propertyIt = requestDbusData.find("data"); 1862 if (propertyIt == requestDbusData.end()) 1863 { 1864 setErrorResponse(asyncResp->res, 1865 boost::beast::http::status::bad_request, noJsonDesc, 1866 badReqMsg); 1867 return; 1868 } 1869 const nlohmann::json& propertySetValue = *propertyIt; 1870 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); 1871 transaction->objectPath = objectPath; 1872 transaction->propertyName = destProperty; 1873 transaction->propertyValue = propertySetValue; 1874 1875 dbus::utility::getDbusObject( 1876 transaction->objectPath, {}, 1877 [transaction](const boost::system::error_code& ec2, 1878 const dbus::utility::MapperGetObject& objectNames) { 1879 if (!ec2 && objectNames.empty()) 1880 { 1881 setErrorResponse(transaction->asyncResp->res, 1882 boost::beast::http::status::not_found, 1883 propNotFoundDesc, notFoundMsg); 1884 return; 1885 } 1886 1887 for (const std::pair<std::string, std::vector<std::string>>& 1888 connection : objectNames) 1889 { 1890 const std::string& connectionName = connection.first; 1891 1892 crow::connections::systemBus->async_method_call( 1893 [connectionName{std::string(connectionName)}, 1894 transaction](const boost::system::error_code& ec3, 1895 const std::string& introspectXml) { 1896 if (ec3) 1897 { 1898 BMCWEB_LOG_ERROR( 1899 "Introspect call failed with error: {} on process: {}", 1900 ec3.message(), connectionName); 1901 transaction->setErrorStatus("Unexpected Error"); 1902 return; 1903 } 1904 tinyxml2::XMLDocument doc; 1905 1906 doc.Parse(introspectXml.c_str()); 1907 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1908 if (pRoot == nullptr) 1909 { 1910 BMCWEB_LOG_ERROR("XML document failed to parse: {}", 1911 introspectXml); 1912 transaction->setErrorStatus("Unexpected Error"); 1913 return; 1914 } 1915 tinyxml2::XMLElement* ifaceNode = 1916 pRoot->FirstChildElement("interface"); 1917 while (ifaceNode != nullptr) 1918 { 1919 const char* interfaceName = ifaceNode->Attribute("name"); 1920 BMCWEB_LOG_DEBUG("found interface {}", interfaceName); 1921 tinyxml2::XMLElement* propNode = 1922 ifaceNode->FirstChildElement("property"); 1923 while (propNode != nullptr) 1924 { 1925 const char* propertyName = propNode->Attribute("name"); 1926 BMCWEB_LOG_DEBUG("Found property {}", propertyName); 1927 if (propertyName == transaction->propertyName) 1928 { 1929 const char* argType = propNode->Attribute("type"); 1930 if (argType != nullptr) 1931 { 1932 sdbusplus::message_t m = 1933 crow::connections::systemBus 1934 ->new_method_call( 1935 connectionName.c_str(), 1936 transaction->objectPath.c_str(), 1937 "org.freedesktop.DBus." 1938 "Properties", 1939 "Set"); 1940 m.append(interfaceName, 1941 transaction->propertyName); 1942 int r = sd_bus_message_open_container( 1943 m.get(), SD_BUS_TYPE_VARIANT, argType); 1944 if (r < 0) 1945 { 1946 transaction->setErrorStatus( 1947 "Unexpected Error"); 1948 return; 1949 } 1950 r = convertJsonToDbus( 1951 m.get(), argType, 1952 transaction->propertyValue); 1953 if (r < 0) 1954 { 1955 if (r == -ERANGE) 1956 { 1957 transaction->setErrorStatus( 1958 "Provided property value " 1959 "is out of range for the " 1960 "property type"); 1961 } 1962 else 1963 { 1964 transaction->setErrorStatus( 1965 "Invalid arg type"); 1966 } 1967 return; 1968 } 1969 r = sd_bus_message_close_container(m.get()); 1970 if (r < 0) 1971 { 1972 transaction->setErrorStatus( 1973 "Unexpected Error"); 1974 return; 1975 } 1976 crow::connections::systemBus->async_send( 1977 m, [transaction]( 1978 const boost::system::error_code& ec, 1979 sdbusplus::message_t& m2) { 1980 BMCWEB_LOG_DEBUG("sent"); 1981 if (ec) 1982 { 1983 const sd_bus_error* e = m2.get_error(); 1984 setErrorResponse( 1985 transaction->asyncResp->res, 1986 boost::beast::http::status:: 1987 forbidden, 1988 (e) != nullptr 1989 ? e->name 1990 : ec.category().name(), 1991 (e) != nullptr ? e->message 1992 : ec.message()); 1993 } 1994 else 1995 { 1996 transaction->asyncResp->res 1997 .jsonValue["status"] = "ok"; 1998 transaction->asyncResp->res 1999 .jsonValue["message"] = "200 OK"; 2000 transaction->asyncResp->res 2001 .jsonValue["data"] = nullptr; 2002 } 2003 }); 2004 } 2005 } 2006 propNode = propNode->NextSiblingElement("property"); 2007 } 2008 ifaceNode = ifaceNode->NextSiblingElement("interface"); 2009 } 2010 }, 2011 connectionName, transaction->objectPath, 2012 "org.freedesktop.DBus.Introspectable", "Introspect"); 2013 } 2014 }); 2015 } 2016 2017 inline void handleDBusUrl(const crow::Request& req, 2018 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2019 std::string& objectPath) 2020 { 2021 // If accessing a single attribute, fill in and update objectPath, 2022 // otherwise leave destProperty blank 2023 std::string destProperty; 2024 const char* attrSeperator = "/attr/"; 2025 size_t attrPosition = objectPath.find(attrSeperator); 2026 if (attrPosition != std::string::npos) 2027 { 2028 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2029 objectPath.length()); 2030 objectPath.resize(attrPosition); 2031 } 2032 2033 if (req.method() == boost::beast::http::verb::post) 2034 { 2035 constexpr const char* actionSeperator = "/action/"; 2036 size_t actionPosition = objectPath.find(actionSeperator); 2037 if (actionPosition != std::string::npos) 2038 { 2039 std::string postProperty = 2040 objectPath.substr((actionPosition + strlen(actionSeperator)), 2041 objectPath.length()); 2042 objectPath.resize(actionPosition); 2043 handleAction(req, asyncResp, objectPath, postProperty); 2044 return; 2045 } 2046 } 2047 else if (req.method() == boost::beast::http::verb::get) 2048 { 2049 if (objectPath.ends_with("/enumerate")) 2050 { 2051 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2052 objectPath.end()); 2053 handleEnumerate(asyncResp, objectPath); 2054 } 2055 else if (objectPath.ends_with("/list")) 2056 { 2057 objectPath.erase(objectPath.end() - sizeof("list"), 2058 objectPath.end()); 2059 handleList(asyncResp, objectPath); 2060 } 2061 else 2062 { 2063 // Trim any trailing "/" at the end 2064 if (objectPath.ends_with("/")) 2065 { 2066 objectPath.pop_back(); 2067 handleList(asyncResp, objectPath, 1); 2068 } 2069 else 2070 { 2071 handleGet(asyncResp, objectPath, destProperty); 2072 } 2073 } 2074 return; 2075 } 2076 else if (req.method() == boost::beast::http::verb::put) 2077 { 2078 handlePut(req, asyncResp, objectPath, destProperty); 2079 return; 2080 } 2081 else if (req.method() == boost::beast::http::verb::delete_) 2082 { 2083 handleDelete(asyncResp, objectPath); 2084 return; 2085 } 2086 2087 setErrorResponse(asyncResp->res, 2088 boost::beast::http::status::method_not_allowed, 2089 methodNotAllowedDesc, methodNotAllowedMsg); 2090 } 2091 2092 inline void 2093 handleBusSystemPost(const crow::Request& req, 2094 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2095 const std::string& processName, 2096 const std::string& requestedPath) 2097 { 2098 std::vector<std::string> strs; 2099 2100 bmcweb::split(strs, requestedPath, '/'); 2101 std::string objectPath; 2102 std::string interfaceName; 2103 std::string methodName; 2104 auto it = strs.begin(); 2105 if (it == strs.end()) 2106 { 2107 objectPath = "/"; 2108 } 2109 while (it != strs.end()) 2110 { 2111 // Check if segment contains ".". If it does, it must be an 2112 // interface 2113 if (it->find(".") != std::string::npos) 2114 { 2115 break; 2116 // This check is necessary as the trailing slash gets 2117 // parsed as part of our <path> specifier above, which 2118 // causes the normal trailing backslash redirector to 2119 // fail. 2120 } 2121 if (!it->empty()) 2122 { 2123 objectPath += "/" + *it; 2124 } 2125 it++; 2126 } 2127 if (it != strs.end()) 2128 { 2129 interfaceName = *it; 2130 it++; 2131 2132 // after interface, we might have a method name 2133 if (it != strs.end()) 2134 { 2135 methodName = *it; 2136 it++; 2137 } 2138 } 2139 if (it != strs.end()) 2140 { 2141 // if there is more levels past the method name, something 2142 // went wrong, return not found 2143 asyncResp->res.result(boost::beast::http::status::not_found); 2144 return; 2145 } 2146 if (interfaceName.empty()) 2147 { 2148 crow::connections::systemBus->async_method_call( 2149 [asyncResp, processName, 2150 objectPath](const boost::system::error_code& ec, 2151 const std::string& introspectXml) { 2152 if (ec) 2153 { 2154 BMCWEB_LOG_ERROR( 2155 "Introspect call failed with error: {} on process: {} path: {}", 2156 ec.message(), processName, objectPath); 2157 return; 2158 } 2159 tinyxml2::XMLDocument doc; 2160 2161 doc.Parse(introspectXml.c_str()); 2162 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2163 if (pRoot == nullptr) 2164 { 2165 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2166 processName, objectPath); 2167 asyncResp->res.jsonValue["status"] = "XML parse error"; 2168 asyncResp->res.result( 2169 boost::beast::http::status::internal_server_error); 2170 return; 2171 } 2172 2173 BMCWEB_LOG_DEBUG("{}", introspectXml); 2174 asyncResp->res.jsonValue["status"] = "ok"; 2175 asyncResp->res.jsonValue["bus_name"] = processName; 2176 asyncResp->res.jsonValue["object_path"] = objectPath; 2177 2178 nlohmann::json& interfacesArray = 2179 asyncResp->res.jsonValue["interfaces"]; 2180 interfacesArray = nlohmann::json::array(); 2181 tinyxml2::XMLElement* interface = 2182 pRoot->FirstChildElement("interface"); 2183 2184 while (interface != nullptr) 2185 { 2186 const char* ifaceName = interface->Attribute("name"); 2187 if (ifaceName != nullptr) 2188 { 2189 nlohmann::json::object_t interfaceObj; 2190 interfaceObj["name"] = ifaceName; 2191 interfacesArray.emplace_back(std::move(interfaceObj)); 2192 } 2193 2194 interface = interface->NextSiblingElement("interface"); 2195 } 2196 }, 2197 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2198 "Introspect"); 2199 } 2200 else if (methodName.empty()) 2201 { 2202 crow::connections::systemBus->async_method_call( 2203 [asyncResp, processName, objectPath, 2204 interfaceName](const boost::system::error_code& ec, 2205 const std::string& introspectXml) { 2206 if (ec) 2207 { 2208 BMCWEB_LOG_ERROR( 2209 "Introspect call failed with error: {} on process: {} path: {}", 2210 ec.message(), processName, objectPath); 2211 return; 2212 } 2213 tinyxml2::XMLDocument doc; 2214 2215 doc.Parse(introspectXml.data(), introspectXml.size()); 2216 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2217 if (pRoot == nullptr) 2218 { 2219 BMCWEB_LOG_ERROR("XML document failed to parse {} {}", 2220 processName, objectPath); 2221 asyncResp->res.result( 2222 boost::beast::http::status::internal_server_error); 2223 return; 2224 } 2225 2226 asyncResp->res.jsonValue["status"] = "ok"; 2227 asyncResp->res.jsonValue["bus_name"] = processName; 2228 asyncResp->res.jsonValue["interface"] = interfaceName; 2229 asyncResp->res.jsonValue["object_path"] = objectPath; 2230 2231 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2232 methodsArray = nlohmann::json::array(); 2233 2234 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2235 signalsArray = nlohmann::json::array(); 2236 2237 nlohmann::json& propertiesObj = 2238 asyncResp->res.jsonValue["properties"]; 2239 propertiesObj = nlohmann::json::object(); 2240 2241 // if we know we're the only call, build the 2242 // json directly 2243 tinyxml2::XMLElement* interface = 2244 pRoot->FirstChildElement("interface"); 2245 while (interface != nullptr) 2246 { 2247 const char* ifaceName = interface->Attribute("name"); 2248 2249 if (ifaceName != nullptr && ifaceName == interfaceName) 2250 { 2251 break; 2252 } 2253 2254 interface = interface->NextSiblingElement("interface"); 2255 } 2256 if (interface == nullptr) 2257 { 2258 // if we got to the end of the list and 2259 // never found a match, throw 404 2260 asyncResp->res.result(boost::beast::http::status::not_found); 2261 return; 2262 } 2263 2264 tinyxml2::XMLElement* methods = 2265 interface->FirstChildElement("method"); 2266 while (methods != nullptr) 2267 { 2268 nlohmann::json argsArray = nlohmann::json::array(); 2269 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2270 while (arg != nullptr) 2271 { 2272 nlohmann::json thisArg; 2273 for (const char* fieldName : std::array<const char*, 3>{ 2274 "name", "direction", "type"}) 2275 { 2276 const char* fieldValue = arg->Attribute(fieldName); 2277 if (fieldValue != nullptr) 2278 { 2279 thisArg[fieldName] = fieldValue; 2280 } 2281 } 2282 argsArray.emplace_back(std::move(thisArg)); 2283 arg = arg->NextSiblingElement("arg"); 2284 } 2285 2286 const char* name = methods->Attribute("name"); 2287 if (name != nullptr) 2288 { 2289 std::string uri; 2290 uri.reserve(14 + processName.size() + objectPath.size() + 2291 interfaceName.size() + strlen(name)); 2292 uri += "/bus/system/"; 2293 uri += processName; 2294 uri += objectPath; 2295 uri += "/"; 2296 uri += interfaceName; 2297 uri += "/"; 2298 uri += name; 2299 2300 nlohmann::json::object_t object; 2301 object["name"] = name; 2302 object["uri"] = std::move(uri); 2303 object["args"] = argsArray; 2304 2305 methodsArray.emplace_back(std::move(object)); 2306 } 2307 methods = methods->NextSiblingElement("method"); 2308 } 2309 tinyxml2::XMLElement* signals = 2310 interface->FirstChildElement("signal"); 2311 while (signals != nullptr) 2312 { 2313 nlohmann::json argsArray = nlohmann::json::array(); 2314 2315 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2316 while (arg != nullptr) 2317 { 2318 const char* name = arg->Attribute("name"); 2319 const char* type = arg->Attribute("type"); 2320 if (name != nullptr && type != nullptr) 2321 { 2322 argsArray.push_back({ 2323 {"name", name}, 2324 {"type", type}, 2325 }); 2326 } 2327 arg = arg->NextSiblingElement("arg"); 2328 } 2329 const char* name = signals->Attribute("name"); 2330 if (name != nullptr) 2331 { 2332 nlohmann::json::object_t object; 2333 object["name"] = name; 2334 object["args"] = argsArray; 2335 signalsArray.emplace_back(std::move(object)); 2336 } 2337 2338 signals = signals->NextSiblingElement("signal"); 2339 } 2340 2341 tinyxml2::XMLElement* property = 2342 interface->FirstChildElement("property"); 2343 while (property != nullptr) 2344 { 2345 const char* name = property->Attribute("name"); 2346 const char* type = property->Attribute("type"); 2347 if (type != nullptr && name != nullptr) 2348 { 2349 sdbusplus::message_t m = 2350 crow::connections::systemBus->new_method_call( 2351 processName.c_str(), objectPath.c_str(), 2352 "org.freedesktop." 2353 "DBus." 2354 "Properties", 2355 "Get"); 2356 m.append(interfaceName, name); 2357 nlohmann::json& propertyItem = propertiesObj[name]; 2358 crow::connections::systemBus->async_send( 2359 m, [&propertyItem, 2360 asyncResp](const boost::system::error_code& ec2, 2361 sdbusplus::message_t& msg) { 2362 if (ec2) 2363 { 2364 return; 2365 } 2366 2367 convertDBusToJSON("v", msg, propertyItem); 2368 }); 2369 } 2370 property = property->NextSiblingElement("property"); 2371 } 2372 }, 2373 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2374 "Introspect"); 2375 } 2376 else 2377 { 2378 if (req.method() != boost::beast::http::verb::post) 2379 { 2380 asyncResp->res.result(boost::beast::http::status::not_found); 2381 return; 2382 } 2383 2384 nlohmann::json requestDbusData; 2385 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 2386 if (ret == JsonParseResult::BadContentType) 2387 { 2388 setErrorResponse(asyncResp->res, 2389 boost::beast::http::status::unsupported_media_type, 2390 invalidContentType, unsupportedMediaMsg); 2391 return; 2392 } 2393 if (ret != JsonParseResult::Success) 2394 { 2395 setErrorResponse(asyncResp->res, 2396 boost::beast::http::status::bad_request, 2397 noJsonDesc, badReqMsg); 2398 return; 2399 } 2400 2401 if (!requestDbusData.is_array()) 2402 { 2403 asyncResp->res.result(boost::beast::http::status::bad_request); 2404 return; 2405 } 2406 auto transaction = std::make_shared<InProgressActionData>(asyncResp); 2407 2408 transaction->path = objectPath; 2409 transaction->methodName = methodName; 2410 transaction->arguments = std::move(requestDbusData); 2411 2412 findActionOnInterface(transaction, processName); 2413 } 2414 } 2415 2416 inline void requestRoutes(App& app) 2417 { 2418 BMCWEB_ROUTE(app, "/bus/") 2419 .privileges({{"Login"}}) 2420 .methods(boost::beast::http::verb::get)( 2421 [](const crow::Request&, 2422 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2423 nlohmann::json::array_t buses; 2424 nlohmann::json& bus = buses.emplace_back(); 2425 bus["name"] = "system"; 2426 asyncResp->res.jsonValue["busses"] = std::move(buses); 2427 asyncResp->res.jsonValue["status"] = "ok"; 2428 }); 2429 2430 BMCWEB_ROUTE(app, "/bus/system/") 2431 .privileges({{"Login"}}) 2432 .methods(boost::beast::http::verb::get)( 2433 [](const crow::Request&, 2434 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2435 auto myCallback = [asyncResp](const boost::system::error_code& ec, 2436 std::vector<std::string>& names) { 2437 if (ec) 2438 { 2439 BMCWEB_LOG_ERROR("Dbus call failed with code {}", ec); 2440 asyncResp->res.result( 2441 boost::beast::http::status::internal_server_error); 2442 } 2443 else 2444 { 2445 std::ranges::sort(names); 2446 asyncResp->res.jsonValue["status"] = "ok"; 2447 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2448 for (const auto& name : names) 2449 { 2450 nlohmann::json::object_t object; 2451 object["name"] = name; 2452 objectsSub.emplace_back(std::move(object)); 2453 } 2454 } 2455 }; 2456 crow::connections::systemBus->async_method_call( 2457 std::move(myCallback), "org.freedesktop.DBus", "/", 2458 "org.freedesktop.DBus", "ListNames"); 2459 }); 2460 2461 BMCWEB_ROUTE(app, "/list/") 2462 .privileges({{"Login"}}) 2463 .methods(boost::beast::http::verb::get)( 2464 [](const crow::Request&, 2465 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2466 handleList(asyncResp, "/"); 2467 }); 2468 2469 BMCWEB_ROUTE(app, "/xyz/<path>") 2470 .privileges({{"Login"}}) 2471 .methods(boost::beast::http::verb::get)( 2472 [](const crow::Request& req, 2473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2474 const std::string& path) { 2475 std::string objectPath = "/xyz/" + path; 2476 handleDBusUrl(req, asyncResp, objectPath); 2477 }); 2478 2479 BMCWEB_ROUTE(app, "/xyz/<path>") 2480 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2481 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2482 boost::beast::http::verb::delete_)( 2483 [](const crow::Request& req, 2484 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2485 const std::string& path) { 2486 std::string objectPath = "/xyz/" + path; 2487 handleDBusUrl(req, asyncResp, objectPath); 2488 }); 2489 2490 BMCWEB_ROUTE(app, "/org/<path>") 2491 .privileges({{"Login"}}) 2492 .methods(boost::beast::http::verb::get)( 2493 [](const crow::Request& req, 2494 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2495 const std::string& path) { 2496 std::string objectPath = "/org/" + path; 2497 handleDBusUrl(req, asyncResp, objectPath); 2498 }); 2499 2500 BMCWEB_ROUTE(app, "/org/<path>") 2501 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2502 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2503 boost::beast::http::verb::delete_)( 2504 [](const crow::Request& req, 2505 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2506 const std::string& path) { 2507 std::string objectPath = "/org/" + path; 2508 handleDBusUrl(req, asyncResp, objectPath); 2509 }); 2510 2511 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2512 .privileges({{"ConfigureManager"}}) 2513 .methods(boost::beast::http::verb::get)( 2514 [](const crow::Request&, 2515 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2516 const std::string& dumpId) { 2517 if (!validateFilename(dumpId)) 2518 { 2519 asyncResp->res.result(boost::beast::http::status::bad_request); 2520 return; 2521 } 2522 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2523 2524 loc /= dumpId; 2525 2526 if (!std::filesystem::exists(loc) || 2527 !std::filesystem::is_directory(loc)) 2528 { 2529 BMCWEB_LOG_ERROR("{}Not found", loc.string()); 2530 asyncResp->res.result(boost::beast::http::status::not_found); 2531 return; 2532 } 2533 std::filesystem::directory_iterator files(loc); 2534 2535 for (const auto& file : files) 2536 { 2537 if (!asyncResp->res.openFile(file)) 2538 { 2539 continue; 2540 } 2541 2542 asyncResp->res.addHeader(boost::beast::http::field::content_type, 2543 "application/octet-stream"); 2544 2545 // Assuming only one dump file will be present in the dump 2546 // id directory 2547 std::string dumpFileName = file.path().filename().string(); 2548 2549 // Filename should be in alphanumeric, dot and underscore 2550 // Its based on phosphor-debug-collector application 2551 // dumpfile format 2552 static std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2553 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2554 { 2555 BMCWEB_LOG_ERROR("Invalid dump filename {}", dumpFileName); 2556 asyncResp->res.result(boost::beast::http::status::not_found); 2557 return; 2558 } 2559 std::string contentDispositionParam = "attachment; filename=\"" + 2560 dumpFileName + "\""; 2561 2562 asyncResp->res.addHeader( 2563 boost::beast::http::field::content_disposition, 2564 contentDispositionParam); 2565 2566 return; 2567 } 2568 asyncResp->res.result(boost::beast::http::status::not_found); 2569 return; 2570 }); 2571 2572 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2573 .privileges({{"Login"}}) 2574 2575 .methods(boost::beast::http::verb::get)( 2576 [](const crow::Request&, 2577 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2578 const std::string& connection) { 2579 introspectObjects(connection, "/", asyncResp); 2580 }); 2581 2582 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2583 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2584 .methods(boost::beast::http::verb::get, 2585 boost::beast::http::verb::post)(handleBusSystemPost); 2586 } 2587 } // namespace openbmc_mapper 2588 } // namespace crow 2589