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