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 "http_request.hpp" 17 #include "http_response.hpp" 18 #include "logging.hpp" 19 #include "routing.hpp" 20 21 #include <systemd/sd-bus-protocol.h> 22 #include <systemd/sd-bus.h> 23 #include <tinyxml2.h> 24 25 #include <app.hpp> 26 #include <async_resp.hpp> 27 #include <boost/algorithm/string/classification.hpp> 28 #include <boost/algorithm/string/predicate.hpp> 29 #include <boost/algorithm/string/split.hpp> 30 #include <boost/beast/http/status.hpp> 31 #include <boost/beast/http/verb.hpp> 32 #include <boost/container/flat_map.hpp> 33 #include <boost/container/vector.hpp> 34 #include <boost/iterator/iterator_facade.hpp> 35 #include <dbus_singleton.hpp> 36 #include <dbus_utility.hpp> 37 #include <nlohmann/json.hpp> 38 #include <sdbusplus/asio/connection.hpp> 39 #include <sdbusplus/asio/property.hpp> 40 #include <sdbusplus/exception.hpp> 41 #include <sdbusplus/message.hpp> 42 #include <sdbusplus/message/native_types.hpp> 43 44 #include <algorithm> 45 #include <array> 46 #include <cerrno> 47 #include <cstdint> 48 #include <cstring> 49 #include <filesystem> 50 #include <fstream> 51 #include <functional> 52 #include <initializer_list> 53 #include <iterator> 54 #include <limits> 55 #include <map> 56 #include <memory> 57 #include <regex> 58 #include <string> 59 #include <string_view> 60 #include <type_traits> 61 #include <utility> 62 #include <variant> 63 #include <vector> 64 65 // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp> 66 // IWYU pragma: no_include <boost/system/detail/error_code.hpp> 67 // IWYU pragma: no_include <boost/system/detail/error_category.hpp> 68 // IWYU pragma: no_include <errno.h> 69 // IWYU pragma: no_include <string.h> 70 // IWYU pragma: no_include <ext/alloc_traits.h> 71 // IWYU pragma: no_include <exception> 72 // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp> 73 74 namespace crow 75 { 76 namespace openbmc_mapper 77 { 78 const constexpr char* notFoundMsg = "404 Not Found"; 79 const constexpr char* badReqMsg = "400 Bad Request"; 80 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed"; 81 const constexpr char* forbiddenMsg = "403 Forbidden"; 82 const constexpr char* methodFailedMsg = "500 Method Call Failed"; 83 const constexpr char* methodOutputFailedMsg = "500 Method Output Error"; 84 const constexpr char* notFoundDesc = 85 "org.freedesktop.DBus.Error.FileNotFound: path or object not found"; 86 const constexpr char* propNotFoundDesc = 87 "The specified property cannot be found"; 88 const constexpr char* noJsonDesc = "No JSON object could be decoded"; 89 const constexpr char* methodNotFoundDesc = 90 "The specified method cannot be found"; 91 const constexpr char* methodNotAllowedDesc = "Method not allowed"; 92 const constexpr char* forbiddenPropDesc = 93 "The specified property cannot be created"; 94 const constexpr char* forbiddenResDesc = 95 "The specified resource cannot be created"; 96 97 inline bool validateFilename(const std::string& filename) 98 { 99 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)"); 100 101 return std::regex_match(filename, validFilename); 102 } 103 104 inline void setErrorResponse(crow::Response& res, 105 boost::beast::http::status result, 106 const std::string& desc, 107 const std::string_view msg) 108 { 109 res.result(result); 110 res.jsonValue["data"]["description"] = desc; 111 res.jsonValue["message"] = msg; 112 res.jsonValue["status"] = "error"; 113 } 114 115 inline void 116 introspectObjects(const std::string& processName, 117 const std::string& objectPath, 118 const std::shared_ptr<bmcweb::AsyncResp>& transaction) 119 { 120 if (transaction->res.jsonValue.is_null()) 121 { 122 transaction->res.jsonValue["status"] = "ok"; 123 transaction->res.jsonValue["bus_name"] = processName; 124 transaction->res.jsonValue["objects"] = nlohmann::json::array(); 125 } 126 127 crow::connections::systemBus->async_method_call( 128 [transaction, processName{std::string(processName)}, 129 objectPath{std::string(objectPath)}]( 130 const boost::system::error_code ec, 131 const std::string& introspectXml) { 132 if (ec) 133 { 134 BMCWEB_LOG_ERROR 135 << "Introspect call failed with error: " << ec.message() 136 << " on process: " << processName << " path: " << objectPath 137 << "\n"; 138 return; 139 } 140 nlohmann::json::object_t object; 141 object["path"] = objectPath; 142 143 transaction->res.jsonValue["objects"].push_back(std::move(object)); 144 145 tinyxml2::XMLDocument doc; 146 147 doc.Parse(introspectXml.c_str()); 148 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 149 if (pRoot == nullptr) 150 { 151 BMCWEB_LOG_ERROR << "XML document failed to parse " << processName 152 << " " << objectPath << "\n"; 153 } 154 else 155 { 156 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node"); 157 while (node != nullptr) 158 { 159 const char* childPath = node->Attribute("name"); 160 if (childPath != nullptr) 161 { 162 std::string newpath; 163 if (objectPath != "/") 164 { 165 newpath += objectPath; 166 } 167 newpath += std::string("/") + childPath; 168 // introspect the subobjects as well 169 introspectObjects(processName, newpath, transaction); 170 } 171 172 node = node->NextSiblingElement("node"); 173 } 174 } 175 }, 176 processName, objectPath, "org.freedesktop.DBus.Introspectable", 177 "Introspect"); 178 } 179 180 inline void getPropertiesForEnumerate( 181 const std::string& objectPath, const std::string& service, 182 const std::string& interface, 183 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 184 { 185 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " " 186 << service << " " << interface; 187 188 sdbusplus::asio::getAllProperties( 189 *crow::connections::systemBus, service, objectPath, interface, 190 [asyncResp, objectPath, service, 191 interface](const boost::system::error_code ec, 192 const dbus::utility::DBusPropertiesMap& propertiesList) { 193 if (ec) 194 { 195 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface " 196 << interface << " service " << service 197 << " failed with code " << ec; 198 return; 199 } 200 201 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 202 nlohmann::json& objectJson = dataJson[objectPath]; 203 if (objectJson.is_null()) 204 { 205 objectJson = nlohmann::json::object(); 206 } 207 208 for (const auto& [name, value] : propertiesList) 209 { 210 nlohmann::json& propertyJson = objectJson[name]; 211 std::visit( 212 [&propertyJson](auto&& val) { 213 if constexpr (std::is_same_v<std::decay_t<decltype(val)>, 214 sdbusplus::message::unix_fd>) 215 { 216 propertyJson = val.fd; 217 } 218 else 219 { 220 221 propertyJson = val; 222 } 223 }, 224 value); 225 } 226 }); 227 } 228 229 // Find any results that weren't picked up by ObjectManagers, to be 230 // called after all ObjectManagers are searched for and called. 231 inline void findRemainingObjectsForEnumerate( 232 const std::string& objectPath, 233 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree, 234 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 235 { 236 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate"; 237 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 238 239 for (const auto& [path, interface_map] : *subtree) 240 { 241 if (path == objectPath) 242 { 243 // An enumerate does not return the target path's properties 244 continue; 245 } 246 if (dataJson.find(path) == dataJson.end()) 247 { 248 for (const auto& [service, interfaces] : interface_map) 249 { 250 for (const auto& interface : interfaces) 251 { 252 if (!interface.starts_with("org.freedesktop.DBus")) 253 { 254 getPropertiesForEnumerate(path, service, interface, 255 asyncResp); 256 } 257 } 258 } 259 } 260 } 261 } 262 263 struct InProgressEnumerateData 264 { 265 InProgressEnumerateData( 266 const std::string& objectPathIn, 267 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 268 objectPath(objectPathIn), 269 asyncResp(asyncRespIn) 270 {} 271 272 ~InProgressEnumerateData() 273 { 274 try 275 { 276 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp); 277 } 278 catch (...) 279 { 280 BMCWEB_LOG_CRITICAL 281 << "findRemainingObjectsForEnumerate threw exception"; 282 } 283 } 284 285 InProgressEnumerateData(const InProgressEnumerateData&) = delete; 286 InProgressEnumerateData(InProgressEnumerateData&&) = delete; 287 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete; 288 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete; 289 const std::string objectPath; 290 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree; 291 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 292 }; 293 294 inline void getManagedObjectsForEnumerate( 295 const std::string& objectName, const std::string& objectManagerPath, 296 const std::string& connectionName, 297 const std::shared_ptr<InProgressEnumerateData>& transaction) 298 { 299 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName 300 << " object_manager_path " << objectManagerPath 301 << " connection_name " << connectionName; 302 crow::connections::systemBus->async_method_call( 303 [transaction, objectName, 304 connectionName](const boost::system::error_code ec, 305 const dbus::utility::ManagedObjectType& objects) { 306 if (ec) 307 { 308 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName 309 << " on connection " << connectionName 310 << " failed with code " << ec; 311 return; 312 } 313 314 nlohmann::json& dataJson = 315 transaction->asyncResp->res.jsonValue["data"]; 316 317 for (const auto& objectPath : objects) 318 { 319 if (objectPath.first.str.starts_with(objectName)) 320 { 321 BMCWEB_LOG_DEBUG << "Reading object " << objectPath.first.str; 322 nlohmann::json& objectJson = dataJson[objectPath.first.str]; 323 if (objectJson.is_null()) 324 { 325 objectJson = nlohmann::json::object(); 326 } 327 for (const auto& interface : objectPath.second) 328 { 329 for (const auto& property : interface.second) 330 { 331 nlohmann::json& propertyJson = 332 objectJson[property.first]; 333 std::visit( 334 [&propertyJson](auto&& val) { 335 if constexpr (std::is_same_v< 336 std::decay_t<decltype(val)>, 337 sdbusplus::message::unix_fd>) 338 { 339 propertyJson = val.fd; 340 } 341 else 342 { 343 344 propertyJson = val; 345 } 346 }, 347 property.second); 348 } 349 } 350 } 351 for (const auto& interface : objectPath.second) 352 { 353 if (interface.first == "org.freedesktop.DBus.ObjectManager") 354 { 355 getManagedObjectsForEnumerate(objectPath.first.str, 356 objectPath.first.str, 357 connectionName, transaction); 358 } 359 } 360 } 361 }, 362 connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager", 363 "GetManagedObjects"); 364 } 365 366 inline void findObjectManagerPathForEnumerate( 367 const std::string& objectName, const std::string& connectionName, 368 const std::shared_ptr<InProgressEnumerateData>& transaction) 369 { 370 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName 371 << " on connection:" << connectionName; 372 crow::connections::systemBus->async_method_call( 373 [transaction, objectName, connectionName]( 374 const boost::system::error_code ec, 375 const dbus::utility::MapperGetAncestorsResponse& objects) { 376 if (ec) 377 { 378 BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName 379 << " failed with code " << ec; 380 return; 381 } 382 383 for (const auto& pathGroup : objects) 384 { 385 for (const auto& connectionGroup : pathGroup.second) 386 { 387 if (connectionGroup.first == connectionName) 388 { 389 // Found the object manager path for this resource. 390 getManagedObjectsForEnumerate(objectName, pathGroup.first, 391 connectionName, transaction); 392 return; 393 } 394 } 395 } 396 }, 397 "xyz.openbmc_project.ObjectMapper", 398 "/xyz/openbmc_project/object_mapper", 399 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName, 400 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"}); 401 } 402 403 // Uses GetObject to add the object info about the target /enumerate path to 404 // the results of GetSubTree, as GetSubTree will not return info for the 405 // target path, and then continues on enumerating the rest of the tree. 406 inline void getObjectAndEnumerate( 407 const std::shared_ptr<InProgressEnumerateData>& transaction) 408 { 409 crow::connections::systemBus->async_method_call( 410 [transaction](const boost::system::error_code ec, 411 const dbus::utility::MapperGetObject& objects) { 412 if (ec) 413 { 414 BMCWEB_LOG_ERROR << "GetObject for path " << transaction->objectPath 415 << " failed with code " << ec; 416 return; 417 } 418 419 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath 420 << " has " << objects.size() << " entries"; 421 if (!objects.empty()) 422 { 423 transaction->subtree->emplace_back(transaction->objectPath, 424 objects); 425 } 426 427 // Map indicating connection name, and the path where the object 428 // manager exists 429 boost::container::flat_map<std::string, std::string> connections; 430 431 for (const auto& object : *(transaction->subtree)) 432 { 433 for (const auto& connection : object.second) 434 { 435 std::string& objectManagerPath = connections[connection.first]; 436 for (const auto& interface : connection.second) 437 { 438 BMCWEB_LOG_DEBUG << connection.first << " has interface " 439 << interface; 440 if (interface == "org.freedesktop.DBus.ObjectManager") 441 { 442 BMCWEB_LOG_DEBUG << "found object manager path " 443 << object.first; 444 objectManagerPath = object.first; 445 } 446 } 447 } 448 } 449 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections"; 450 451 for (const auto& connection : connections) 452 { 453 // If we already know where the object manager is, we don't 454 // need to search for it, we can call directly in to 455 // getManagedObjects 456 if (!connection.second.empty()) 457 { 458 getManagedObjectsForEnumerate(transaction->objectPath, 459 connection.second, 460 connection.first, transaction); 461 } 462 else 463 { 464 // otherwise we need to find the object manager path 465 // before we can continue 466 findObjectManagerPathForEnumerate( 467 transaction->objectPath, connection.first, transaction); 468 } 469 } 470 }, 471 "xyz.openbmc_project.ObjectMapper", 472 "/xyz/openbmc_project/object_mapper", 473 "xyz.openbmc_project.ObjectMapper", "GetObject", 474 transaction->objectPath, std::array<const char*, 0>()); 475 } 476 477 // Structure for storing data on an in progress action 478 struct InProgressActionData 479 { 480 explicit InProgressActionData(crow::Response& resIn) : res(resIn) 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 (res.result() == boost::beast::http::status::ok) 496 { 497 if (!methodPassed) 498 { 499 if (!methodFailed) 500 { 501 setErrorResponse(res, boost::beast::http::status::not_found, 502 methodNotFoundDesc, notFoundMsg); 503 } 504 } 505 else 506 { 507 if (outputFailed) 508 { 509 setErrorResponse( 510 res, boost::beast::http::status::internal_server_error, 511 "Method output failure", methodOutputFailedMsg); 512 } 513 else 514 { 515 res.jsonValue["status"] = "ok"; 516 res.jsonValue["message"] = "200 OK"; 517 res.jsonValue["data"] = methodResponse; 518 } 519 } 520 } 521 522 res.end(); 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(res, boost::beast::http::status::bad_request, desc, 532 badReqMsg); 533 } 534 crow::Response& res; 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 << "Converting " 599 << inputJson.dump(2, ' ', true, 600 nlohmann::json::error_handler_t::replace) 601 << " to type: " << 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: " << argCode 843 << " appending variant of type: " << 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 " << typeCode 950 << " failed!"; 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 " << typeCode 1026 << " too small for an array"; 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 " 1037 << r; 1038 return r; 1039 } 1040 1041 bool dict = containedType.starts_with("{") && containedType.ends_with("}"); 1042 1043 if (dict) 1044 { 1045 // Remove the { } 1046 containedType = containedType.substr(1, containedType.size() - 2); 1047 data = nlohmann::json::object(); 1048 } 1049 else 1050 { 1051 data = nlohmann::json::array(); 1052 } 1053 1054 while (true) 1055 { 1056 r = sd_bus_message_at_end(m.get(), 0); 1057 if (r < 0) 1058 { 1059 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed"; 1060 return r; 1061 } 1062 1063 if (r > 0) 1064 { 1065 break; 1066 } 1067 1068 // Dictionaries are only ever seen in an array 1069 if (dict) 1070 { 1071 r = readDictEntryFromMessage(containedType, m, data); 1072 if (r < 0) 1073 { 1074 return r; 1075 } 1076 } 1077 else 1078 { 1079 data.push_back(nlohmann::json()); 1080 1081 r = convertDBusToJSON(containedType, m, data.back()); 1082 if (r < 0) 1083 { 1084 return r; 1085 } 1086 } 1087 } 1088 1089 r = sd_bus_message_exit_container(m.get()); 1090 if (r < 0) 1091 { 1092 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1093 return r; 1094 } 1095 1096 return 0; 1097 } 1098 1099 inline int readStructFromMessage(const std::string& typeCode, 1100 sdbusplus::message_t& m, nlohmann::json& data) 1101 { 1102 if (typeCode.size() < 3) 1103 { 1104 BMCWEB_LOG_ERROR << "Type code " << typeCode 1105 << " too small for a struct"; 1106 return -1; 1107 } 1108 1109 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2); 1110 std::vector<std::string> types = dbusArgSplit(containedTypes); 1111 1112 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT, 1113 containedTypes.c_str()); 1114 if (r < 0) 1115 { 1116 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1117 << r; 1118 return r; 1119 } 1120 1121 for (const std::string& type : types) 1122 { 1123 data.push_back(nlohmann::json()); 1124 r = convertDBusToJSON(type, m, data.back()); 1125 if (r < 0) 1126 { 1127 return r; 1128 } 1129 } 1130 1131 r = sd_bus_message_exit_container(m.get()); 1132 if (r < 0) 1133 { 1134 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1135 return r; 1136 } 1137 return 0; 1138 } 1139 1140 inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data) 1141 { 1142 const char* containerType = nullptr; 1143 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType); 1144 if (r < 0) 1145 { 1146 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed"; 1147 return r; 1148 } 1149 1150 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT, 1151 containerType); 1152 if (r < 0) 1153 { 1154 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1155 << r; 1156 return r; 1157 } 1158 1159 r = convertDBusToJSON(containerType, m, data); 1160 if (r < 0) 1161 { 1162 return r; 1163 } 1164 1165 r = sd_bus_message_exit_container(m.get()); 1166 if (r < 0) 1167 { 1168 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed"; 1169 return r; 1170 } 1171 1172 return 0; 1173 } 1174 1175 inline int convertDBusToJSON(const std::string& returnType, 1176 sdbusplus::message_t& m, nlohmann::json& response) 1177 { 1178 int r = 0; 1179 const std::vector<std::string> returnTypes = dbusArgSplit(returnType); 1180 1181 for (const std::string& typeCode : returnTypes) 1182 { 1183 nlohmann::json* thisElement = &response; 1184 if (returnTypes.size() > 1) 1185 { 1186 response.push_back(nlohmann::json{}); 1187 thisElement = &response.back(); 1188 } 1189 1190 if (typeCode == "s" || typeCode == "g" || typeCode == "o") 1191 { 1192 r = readMessageItem<char*>(typeCode, m, *thisElement); 1193 if (r < 0) 1194 { 1195 return r; 1196 } 1197 } 1198 else if (typeCode == "b") 1199 { 1200 r = readMessageItem<int>(typeCode, m, *thisElement); 1201 if (r < 0) 1202 { 1203 return r; 1204 } 1205 1206 *thisElement = static_cast<bool>(thisElement->get<int>()); 1207 } 1208 else if (typeCode == "u") 1209 { 1210 r = readMessageItem<uint32_t>(typeCode, m, *thisElement); 1211 if (r < 0) 1212 { 1213 return r; 1214 } 1215 } 1216 else if (typeCode == "i") 1217 { 1218 r = readMessageItem<int32_t>(typeCode, m, *thisElement); 1219 if (r < 0) 1220 { 1221 return r; 1222 } 1223 } 1224 else if (typeCode == "x") 1225 { 1226 r = readMessageItem<int64_t>(typeCode, m, *thisElement); 1227 if (r < 0) 1228 { 1229 return r; 1230 } 1231 } 1232 else if (typeCode == "t") 1233 { 1234 r = readMessageItem<uint64_t>(typeCode, m, *thisElement); 1235 if (r < 0) 1236 { 1237 return r; 1238 } 1239 } 1240 else if (typeCode == "n") 1241 { 1242 r = readMessageItem<int16_t>(typeCode, m, *thisElement); 1243 if (r < 0) 1244 { 1245 return r; 1246 } 1247 } 1248 else if (typeCode == "q") 1249 { 1250 r = readMessageItem<uint16_t>(typeCode, m, *thisElement); 1251 if (r < 0) 1252 { 1253 return r; 1254 } 1255 } 1256 else if (typeCode == "y") 1257 { 1258 r = readMessageItem<uint8_t>(typeCode, m, *thisElement); 1259 if (r < 0) 1260 { 1261 return r; 1262 } 1263 } 1264 else if (typeCode == "d") 1265 { 1266 r = readMessageItem<double>(typeCode, m, *thisElement); 1267 if (r < 0) 1268 { 1269 return r; 1270 } 1271 } 1272 else if (typeCode == "h") 1273 { 1274 r = readMessageItem<int>(typeCode, m, *thisElement); 1275 if (r < 0) 1276 { 1277 return r; 1278 } 1279 } 1280 else if (typeCode.starts_with("a")) 1281 { 1282 r = readArrayFromMessage(typeCode, m, *thisElement); 1283 if (r < 0) 1284 { 1285 return r; 1286 } 1287 } 1288 else if (typeCode.starts_with("(") && typeCode.ends_with(")")) 1289 { 1290 r = readStructFromMessage(typeCode, m, *thisElement); 1291 if (r < 0) 1292 { 1293 return r; 1294 } 1295 } 1296 else if (typeCode.starts_with("v")) 1297 { 1298 r = readVariantFromMessage(m, *thisElement); 1299 if (r < 0) 1300 { 1301 return r; 1302 } 1303 } 1304 else 1305 { 1306 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode; 1307 return -2; 1308 } 1309 } 1310 1311 return 0; 1312 } 1313 1314 inline void handleMethodResponse( 1315 const std::shared_ptr<InProgressActionData>& transaction, 1316 sdbusplus::message_t& m, const std::string& returnType) 1317 { 1318 nlohmann::json data; 1319 1320 int r = convertDBusToJSON(returnType, m, data); 1321 if (r < 0) 1322 { 1323 transaction->outputFailed = true; 1324 return; 1325 } 1326 1327 if (data.is_null()) 1328 { 1329 return; 1330 } 1331 1332 if (transaction->methodResponse.is_null()) 1333 { 1334 transaction->methodResponse = std::move(data); 1335 return; 1336 } 1337 1338 // If they're both dictionaries or arrays, merge into one. 1339 // Otherwise, make the results an array with every result 1340 // an entry. Could also just fail in that case, but it 1341 // seems better to get the data back somehow. 1342 1343 if (transaction->methodResponse.is_object() && data.is_object()) 1344 { 1345 for (const auto& obj : data.items()) 1346 { 1347 // Note: Will overwrite the data for a duplicate key 1348 transaction->methodResponse.emplace(obj.key(), 1349 std::move(obj.value())); 1350 } 1351 return; 1352 } 1353 1354 if (transaction->methodResponse.is_array() && data.is_array()) 1355 { 1356 for (auto& obj : data) 1357 { 1358 transaction->methodResponse.push_back(std::move(obj)); 1359 } 1360 return; 1361 } 1362 1363 if (!transaction->convertedToArray) 1364 { 1365 // They are different types. May as well turn them into an array 1366 nlohmann::json j = std::move(transaction->methodResponse); 1367 transaction->methodResponse = nlohmann::json::array(); 1368 transaction->methodResponse.push_back(std::move(j)); 1369 transaction->methodResponse.push_back(std::move(data)); 1370 transaction->convertedToArray = true; 1371 } 1372 else 1373 { 1374 transaction->methodResponse.push_back(std::move(data)); 1375 } 1376 } 1377 1378 inline void findActionOnInterface( 1379 const std::shared_ptr<InProgressActionData>& transaction, 1380 const std::string& connectionName) 1381 { 1382 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " 1383 << connectionName; 1384 crow::connections::systemBus->async_method_call( 1385 [transaction, connectionName{std::string(connectionName)}]( 1386 const boost::system::error_code ec, 1387 const std::string& introspectXml) { 1388 BMCWEB_LOG_DEBUG << "got xml:\n " << introspectXml; 1389 if (ec) 1390 { 1391 BMCWEB_LOG_ERROR 1392 << "Introspect call failed with error: " << ec.message() 1393 << " on process: " << connectionName << "\n"; 1394 return; 1395 } 1396 tinyxml2::XMLDocument doc; 1397 1398 doc.Parse(introspectXml.data(), introspectXml.size()); 1399 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1400 if (pRoot == nullptr) 1401 { 1402 BMCWEB_LOG_ERROR << "XML document failed to parse " 1403 << connectionName << "\n"; 1404 return; 1405 } 1406 tinyxml2::XMLElement* interfaceNode = 1407 pRoot->FirstChildElement("interface"); 1408 while (interfaceNode != nullptr) 1409 { 1410 const char* thisInterfaceName = interfaceNode->Attribute("name"); 1411 if (thisInterfaceName != nullptr) 1412 { 1413 if (!transaction->interfaceName.empty() && 1414 (transaction->interfaceName != thisInterfaceName)) 1415 { 1416 interfaceNode = 1417 interfaceNode->NextSiblingElement("interface"); 1418 continue; 1419 } 1420 1421 tinyxml2::XMLElement* methodNode = 1422 interfaceNode->FirstChildElement("method"); 1423 while (methodNode != nullptr) 1424 { 1425 const char* thisMethodName = methodNode->Attribute("name"); 1426 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName; 1427 if (thisMethodName != nullptr && 1428 thisMethodName == transaction->methodName) 1429 { 1430 BMCWEB_LOG_DEBUG << "Found method named " 1431 << thisMethodName << " on interface " 1432 << thisInterfaceName; 1433 sdbusplus::message_t m = 1434 crow::connections::systemBus->new_method_call( 1435 connectionName.c_str(), 1436 transaction->path.c_str(), thisInterfaceName, 1437 transaction->methodName.c_str()); 1438 1439 tinyxml2::XMLElement* argumentNode = 1440 methodNode->FirstChildElement("arg"); 1441 1442 std::string returnType; 1443 1444 // Find the output type 1445 while (argumentNode != nullptr) 1446 { 1447 const char* argDirection = 1448 argumentNode->Attribute("direction"); 1449 const char* argType = 1450 argumentNode->Attribute("type"); 1451 if (argDirection != nullptr && argType != nullptr && 1452 std::string(argDirection) == "out") 1453 { 1454 returnType = argType; 1455 break; 1456 } 1457 argumentNode = 1458 argumentNode->NextSiblingElement("arg"); 1459 } 1460 1461 auto argIt = transaction->arguments.begin(); 1462 1463 argumentNode = methodNode->FirstChildElement("arg"); 1464 1465 while (argumentNode != nullptr) 1466 { 1467 const char* argDirection = 1468 argumentNode->Attribute("direction"); 1469 const char* argType = 1470 argumentNode->Attribute("type"); 1471 if (argDirection != nullptr && argType != nullptr && 1472 std::string(argDirection) == "in") 1473 { 1474 if (argIt == transaction->arguments.end()) 1475 { 1476 transaction->setErrorStatus( 1477 "Invalid method args"); 1478 return; 1479 } 1480 if (convertJsonToDbus(m.get(), 1481 std::string(argType), 1482 *argIt) < 0) 1483 { 1484 transaction->setErrorStatus( 1485 "Invalid method arg type"); 1486 return; 1487 } 1488 1489 argIt++; 1490 } 1491 argumentNode = 1492 argumentNode->NextSiblingElement("arg"); 1493 } 1494 1495 crow::connections::systemBus->async_send( 1496 m, 1497 [transaction, 1498 returnType](boost::system::error_code ec2, 1499 sdbusplus::message_t& m2) { 1500 if (ec2) 1501 { 1502 transaction->methodFailed = true; 1503 const sd_bus_error* e = m2.get_error(); 1504 1505 if (e != nullptr) 1506 { 1507 setErrorResponse( 1508 transaction->res, 1509 boost::beast::http::status::bad_request, 1510 e->name, e->message); 1511 } 1512 else 1513 { 1514 setErrorResponse( 1515 transaction->res, 1516 boost::beast::http::status::bad_request, 1517 "Method call failed", methodFailedMsg); 1518 } 1519 return; 1520 } 1521 transaction->methodPassed = true; 1522 1523 handleMethodResponse(transaction, m2, returnType); 1524 }); 1525 break; 1526 } 1527 methodNode = methodNode->NextSiblingElement("method"); 1528 } 1529 } 1530 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1531 } 1532 }, 1533 connectionName, transaction->path, 1534 "org.freedesktop.DBus.Introspectable", "Introspect"); 1535 } 1536 1537 inline void handleAction(const crow::Request& req, 1538 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1539 const std::string& objectPath, 1540 const std::string& methodName) 1541 { 1542 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 1543 << methodName; 1544 nlohmann::json requestDbusData = 1545 nlohmann::json::parse(req.body, nullptr, false); 1546 1547 if (requestDbusData.is_discarded()) 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->res); 1571 1572 transaction->path = objectPath; 1573 transaction->methodName = methodName; 1574 transaction->arguments = std::move(*data); 1575 crow::connections::systemBus->async_method_call( 1576 [transaction]( 1577 const boost::system::error_code ec, 1578 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1579 interfaceNames) { 1580 if (ec || interfaceNames.empty()) 1581 { 1582 BMCWEB_LOG_ERROR << "Can't find object"; 1583 setErrorResponse(transaction->res, 1584 boost::beast::http::status::not_found, 1585 notFoundDesc, notFoundMsg); 1586 return; 1587 } 1588 1589 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 1590 << " object(s)"; 1591 1592 for (const std::pair<std::string, std::vector<std::string>>& object : 1593 interfaceNames) 1594 { 1595 findActionOnInterface(transaction, object.first); 1596 } 1597 }, 1598 "xyz.openbmc_project.ObjectMapper", 1599 "/xyz/openbmc_project/object_mapper", 1600 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1601 std::array<std::string, 0>()); 1602 } 1603 1604 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1605 const std::string& objectPath) 1606 { 1607 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath; 1608 1609 crow::connections::systemBus->async_method_call( 1610 [asyncResp, objectPath]( 1611 const boost::system::error_code ec, 1612 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1613 interfaceNames) { 1614 if (ec || interfaceNames.empty()) 1615 { 1616 BMCWEB_LOG_ERROR << "Can't find object"; 1617 setErrorResponse(asyncResp->res, 1618 boost::beast::http::status::method_not_allowed, 1619 methodNotAllowedDesc, methodNotAllowedMsg); 1620 return; 1621 } 1622 1623 auto transaction = 1624 std::make_shared<InProgressActionData>(asyncResp->res); 1625 transaction->path = objectPath; 1626 transaction->methodName = "Delete"; 1627 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1628 1629 for (const std::pair<std::string, std::vector<std::string>>& object : 1630 interfaceNames) 1631 { 1632 findActionOnInterface(transaction, object.first); 1633 } 1634 }, 1635 "xyz.openbmc_project.ObjectMapper", 1636 "/xyz/openbmc_project/object_mapper", 1637 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1638 std::array<const char*, 0>()); 1639 } 1640 1641 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1642 const std::string& objectPath, int32_t depth = 0) 1643 { 1644 crow::connections::systemBus->async_method_call( 1645 [asyncResp]( 1646 const boost::system::error_code ec, 1647 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { 1648 if (ec) 1649 { 1650 setErrorResponse(asyncResp->res, 1651 boost::beast::http::status::not_found, 1652 notFoundDesc, notFoundMsg); 1653 } 1654 else 1655 { 1656 asyncResp->res.jsonValue["status"] = "ok"; 1657 asyncResp->res.jsonValue["message"] = "200 OK"; 1658 asyncResp->res.jsonValue["data"] = objectPaths; 1659 } 1660 }, 1661 "xyz.openbmc_project.ObjectMapper", 1662 "/xyz/openbmc_project/object_mapper", 1663 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 1664 depth, std::array<std::string, 0>()); 1665 } 1666 1667 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1668 const std::string& objectPath) 1669 { 1670 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 1671 1672 asyncResp->res.jsonValue["message"] = "200 OK"; 1673 asyncResp->res.jsonValue["status"] = "ok"; 1674 asyncResp->res.jsonValue["data"] = nlohmann::json::object(); 1675 1676 crow::connections::systemBus->async_method_call( 1677 [objectPath, asyncResp]( 1678 const boost::system::error_code ec, 1679 const dbus::utility::MapperGetSubTreeResponse& objectNames) { 1680 auto transaction = 1681 std::make_shared<InProgressEnumerateData>(objectPath, asyncResp); 1682 1683 transaction->subtree = 1684 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( 1685 objectNames); 1686 1687 if (ec) 1688 { 1689 BMCWEB_LOG_ERROR << "GetSubTree failed on " 1690 << transaction->objectPath; 1691 setErrorResponse(transaction->asyncResp->res, 1692 boost::beast::http::status::not_found, 1693 notFoundDesc, notFoundMsg); 1694 return; 1695 } 1696 1697 // Add the data for the path passed in to the results 1698 // as if GetSubTree returned it, and continue on enumerating 1699 getObjectAndEnumerate(transaction); 1700 }, 1701 "xyz.openbmc_project.ObjectMapper", 1702 "/xyz/openbmc_project/object_mapper", 1703 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0, 1704 std::array<const char*, 0>()); 1705 } 1706 1707 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1708 std::string& objectPath, std::string& destProperty) 1709 { 1710 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1711 std::shared_ptr<std::string> propertyName = 1712 std::make_shared<std::string>(std::move(destProperty)); 1713 1714 std::shared_ptr<std::string> path = 1715 std::make_shared<std::string>(std::move(objectPath)); 1716 1717 crow::connections::systemBus->async_method_call( 1718 [asyncResp, path, 1719 propertyName](const boost::system::error_code ec, 1720 const dbus::utility::MapperGetObject& objectNames) { 1721 if (ec || objectNames.empty()) 1722 { 1723 setErrorResponse(asyncResp->res, 1724 boost::beast::http::status::not_found, 1725 notFoundDesc, notFoundMsg); 1726 return; 1727 } 1728 std::shared_ptr<nlohmann::json> response = 1729 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1730 // The mapper should never give us an empty interface names 1731 // list, but check anyway 1732 for (const std::pair<std::string, std::vector<std::string>>& 1733 connection : objectNames) 1734 { 1735 const std::vector<std::string>& interfaceNames = connection.second; 1736 1737 if (interfaceNames.empty()) 1738 { 1739 setErrorResponse(asyncResp->res, 1740 boost::beast::http::status::not_found, 1741 notFoundDesc, notFoundMsg); 1742 return; 1743 } 1744 1745 for (const std::string& interface : interfaceNames) 1746 { 1747 sdbusplus::message_t m = 1748 crow::connections::systemBus->new_method_call( 1749 connection.first.c_str(), path->c_str(), 1750 "org.freedesktop.DBus.Properties", "GetAll"); 1751 m.append(interface); 1752 crow::connections::systemBus->async_send( 1753 m, [asyncResp, response, 1754 propertyName](const boost::system::error_code ec2, 1755 sdbusplus::message_t& msg) { 1756 if (ec2) 1757 { 1758 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1759 << ec2; 1760 } 1761 else 1762 { 1763 nlohmann::json properties; 1764 int r = convertDBusToJSON("a{sv}", msg, properties); 1765 if (r < 0) 1766 { 1767 BMCWEB_LOG_ERROR << "convertDBusToJSON failed"; 1768 } 1769 else 1770 { 1771 for (const auto& prop : properties.items()) 1772 { 1773 // if property name is empty, or 1774 // matches our search query, add it 1775 // to the response json 1776 1777 if (propertyName->empty()) 1778 { 1779 (*response)[prop.key()] = 1780 std::move(prop.value()); 1781 } 1782 else if (prop.key() == *propertyName) 1783 { 1784 *response = std::move(prop.value()); 1785 } 1786 } 1787 } 1788 } 1789 if (response.use_count() == 1) 1790 { 1791 if (!propertyName->empty() && response->empty()) 1792 { 1793 setErrorResponse( 1794 asyncResp->res, 1795 boost::beast::http::status::not_found, 1796 propNotFoundDesc, notFoundMsg); 1797 } 1798 else 1799 { 1800 asyncResp->res.jsonValue["status"] = "ok"; 1801 asyncResp->res.jsonValue["message"] = "200 OK"; 1802 asyncResp->res.jsonValue["data"] = *response; 1803 } 1804 } 1805 }); 1806 } 1807 } 1808 }, 1809 "xyz.openbmc_project.ObjectMapper", 1810 "/xyz/openbmc_project/object_mapper", 1811 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 1812 std::array<std::string, 0>()); 1813 } 1814 1815 struct AsyncPutRequest 1816 { 1817 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : 1818 asyncResp(resIn) 1819 {} 1820 ~AsyncPutRequest() 1821 { 1822 if (asyncResp->res.jsonValue.empty()) 1823 { 1824 setErrorResponse(asyncResp->res, 1825 boost::beast::http::status::forbidden, 1826 forbiddenMsg, forbiddenPropDesc); 1827 } 1828 } 1829 1830 AsyncPutRequest(const AsyncPutRequest&) = delete; 1831 AsyncPutRequest(AsyncPutRequest&&) = delete; 1832 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; 1833 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; 1834 1835 void setErrorStatus(const std::string& desc) 1836 { 1837 setErrorResponse(asyncResp->res, 1838 boost::beast::http::status::internal_server_error, 1839 desc, badReqMsg); 1840 } 1841 1842 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1843 std::string objectPath; 1844 std::string propertyName; 1845 nlohmann::json propertyValue; 1846 }; 1847 1848 inline void handlePut(const crow::Request& req, 1849 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1850 const std::string& objectPath, 1851 const std::string& destProperty) 1852 { 1853 if (destProperty.empty()) 1854 { 1855 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, 1856 forbiddenResDesc, forbiddenMsg); 1857 return; 1858 } 1859 1860 nlohmann::json requestDbusData = 1861 nlohmann::json::parse(req.body, nullptr, false); 1862 1863 if (requestDbusData.is_discarded()) 1864 { 1865 setErrorResponse(asyncResp->res, 1866 boost::beast::http::status::bad_request, noJsonDesc, 1867 badReqMsg); 1868 return; 1869 } 1870 1871 auto propertyIt = requestDbusData.find("data"); 1872 if (propertyIt == requestDbusData.end()) 1873 { 1874 setErrorResponse(asyncResp->res, 1875 boost::beast::http::status::bad_request, noJsonDesc, 1876 badReqMsg); 1877 return; 1878 } 1879 const nlohmann::json& propertySetValue = *propertyIt; 1880 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); 1881 transaction->objectPath = objectPath; 1882 transaction->propertyName = destProperty; 1883 transaction->propertyValue = propertySetValue; 1884 1885 crow::connections::systemBus->async_method_call( 1886 [transaction](const boost::system::error_code ec2, 1887 const dbus::utility::MapperGetObject& objectNames) { 1888 if (!ec2 && objectNames.empty()) 1889 { 1890 setErrorResponse(transaction->asyncResp->res, 1891 boost::beast::http::status::not_found, 1892 propNotFoundDesc, notFoundMsg); 1893 return; 1894 } 1895 1896 for (const std::pair<std::string, std::vector<std::string>>& 1897 connection : objectNames) 1898 { 1899 const std::string& connectionName = connection.first; 1900 1901 crow::connections::systemBus->async_method_call( 1902 [connectionName{std::string(connectionName)}, 1903 transaction](const boost::system::error_code ec3, 1904 const std::string& introspectXml) { 1905 if (ec3) 1906 { 1907 BMCWEB_LOG_ERROR << "Introspect call failed with error: " 1908 << ec3.message() 1909 << " on process: " << connectionName; 1910 transaction->setErrorStatus("Unexpected Error"); 1911 return; 1912 } 1913 tinyxml2::XMLDocument doc; 1914 1915 doc.Parse(introspectXml.c_str()); 1916 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1917 if (pRoot == nullptr) 1918 { 1919 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1920 << introspectXml; 1921 transaction->setErrorStatus("Unexpected Error"); 1922 return; 1923 } 1924 tinyxml2::XMLElement* ifaceNode = 1925 pRoot->FirstChildElement("interface"); 1926 while (ifaceNode != nullptr) 1927 { 1928 const char* interfaceName = ifaceNode->Attribute("name"); 1929 BMCWEB_LOG_DEBUG << "found interface " << interfaceName; 1930 tinyxml2::XMLElement* propNode = 1931 ifaceNode->FirstChildElement("property"); 1932 while (propNode != nullptr) 1933 { 1934 const char* propertyName = propNode->Attribute("name"); 1935 BMCWEB_LOG_DEBUG << "Found property " << propertyName; 1936 if (propertyName == transaction->propertyName) 1937 { 1938 const char* argType = propNode->Attribute("type"); 1939 if (argType != nullptr) 1940 { 1941 sdbusplus::message_t m = 1942 crow::connections::systemBus 1943 ->new_method_call( 1944 connectionName.c_str(), 1945 transaction->objectPath.c_str(), 1946 "org.freedesktop.DBus." 1947 "Properties", 1948 "Set"); 1949 m.append(interfaceName, 1950 transaction->propertyName); 1951 int r = sd_bus_message_open_container( 1952 m.get(), SD_BUS_TYPE_VARIANT, argType); 1953 if (r < 0) 1954 { 1955 transaction->setErrorStatus( 1956 "Unexpected Error"); 1957 return; 1958 } 1959 r = convertJsonToDbus( 1960 m.get(), argType, 1961 transaction->propertyValue); 1962 if (r < 0) 1963 { 1964 if (r == -ERANGE) 1965 { 1966 transaction->setErrorStatus( 1967 "Provided property value " 1968 "is out of range for the " 1969 "property type"); 1970 } 1971 else 1972 { 1973 transaction->setErrorStatus( 1974 "Invalid arg type"); 1975 } 1976 return; 1977 } 1978 r = sd_bus_message_close_container(m.get()); 1979 if (r < 0) 1980 { 1981 transaction->setErrorStatus( 1982 "Unexpected Error"); 1983 return; 1984 } 1985 crow::connections::systemBus->async_send( 1986 m, 1987 [transaction](boost::system::error_code ec, 1988 sdbusplus::message_t& m2) { 1989 BMCWEB_LOG_DEBUG << "sent"; 1990 if (ec) 1991 { 1992 const sd_bus_error* e = m2.get_error(); 1993 setErrorResponse( 1994 transaction->asyncResp->res, 1995 boost::beast::http::status:: 1996 forbidden, 1997 (e) != nullptr 1998 ? e->name 1999 : ec.category().name(), 2000 (e) != nullptr ? e->message 2001 : ec.message()); 2002 } 2003 else 2004 { 2005 transaction->asyncResp->res 2006 .jsonValue["status"] = "ok"; 2007 transaction->asyncResp->res 2008 .jsonValue["message"] = "200 OK"; 2009 transaction->asyncResp->res 2010 .jsonValue["data"] = nullptr; 2011 } 2012 }); 2013 } 2014 } 2015 propNode = propNode->NextSiblingElement("property"); 2016 } 2017 ifaceNode = ifaceNode->NextSiblingElement("interface"); 2018 } 2019 }, 2020 connectionName, transaction->objectPath, 2021 "org.freedesktop.DBus.Introspectable", "Introspect"); 2022 } 2023 }, 2024 "xyz.openbmc_project.ObjectMapper", 2025 "/xyz/openbmc_project/object_mapper", 2026 "xyz.openbmc_project.ObjectMapper", "GetObject", 2027 transaction->objectPath, std::array<std::string, 0>()); 2028 } 2029 2030 inline void handleDBusUrl(const crow::Request& req, 2031 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2032 std::string& objectPath) 2033 { 2034 2035 // If accessing a single attribute, fill in and update objectPath, 2036 // otherwise leave destProperty blank 2037 std::string destProperty; 2038 const char* attrSeperator = "/attr/"; 2039 size_t attrPosition = objectPath.find(attrSeperator); 2040 if (attrPosition != std::string::npos) 2041 { 2042 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2043 objectPath.length()); 2044 objectPath = objectPath.substr(0, attrPosition); 2045 } 2046 2047 if (req.method() == boost::beast::http::verb::post) 2048 { 2049 constexpr const char* actionSeperator = "/action/"; 2050 size_t actionPosition = objectPath.find(actionSeperator); 2051 if (actionPosition != std::string::npos) 2052 { 2053 std::string postProperty = 2054 objectPath.substr((actionPosition + strlen(actionSeperator)), 2055 objectPath.length()); 2056 objectPath = objectPath.substr(0, actionPosition); 2057 handleAction(req, asyncResp, objectPath, postProperty); 2058 return; 2059 } 2060 } 2061 else if (req.method() == boost::beast::http::verb::get) 2062 { 2063 if (objectPath.ends_with("/enumerate")) 2064 { 2065 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2066 objectPath.end()); 2067 handleEnumerate(asyncResp, objectPath); 2068 } 2069 else if (objectPath.ends_with("/list")) 2070 { 2071 objectPath.erase(objectPath.end() - sizeof("list"), 2072 objectPath.end()); 2073 handleList(asyncResp, objectPath); 2074 } 2075 else 2076 { 2077 // Trim any trailing "/" at the end 2078 if (objectPath.ends_with("/")) 2079 { 2080 objectPath.pop_back(); 2081 handleList(asyncResp, objectPath, 1); 2082 } 2083 else 2084 { 2085 handleGet(asyncResp, objectPath, destProperty); 2086 } 2087 } 2088 return; 2089 } 2090 else if (req.method() == boost::beast::http::verb::put) 2091 { 2092 handlePut(req, asyncResp, objectPath, destProperty); 2093 return; 2094 } 2095 else if (req.method() == boost::beast::http::verb::delete_) 2096 { 2097 handleDelete(asyncResp, objectPath); 2098 return; 2099 } 2100 2101 setErrorResponse(asyncResp->res, 2102 boost::beast::http::status::method_not_allowed, 2103 methodNotAllowedDesc, methodNotAllowedMsg); 2104 } 2105 2106 inline void 2107 handleBusSystemPost(const crow::Request& req, 2108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2109 const std::string& processName, 2110 const std::string& requestedPath) 2111 { 2112 std::vector<std::string> strs; 2113 boost::split(strs, requestedPath, boost::is_any_of("/")); 2114 std::string objectPath; 2115 std::string interfaceName; 2116 std::string methodName; 2117 auto it = strs.begin(); 2118 if (it == strs.end()) 2119 { 2120 objectPath = "/"; 2121 } 2122 while (it != strs.end()) 2123 { 2124 // Check if segment contains ".". If it does, it must be an 2125 // interface 2126 if (it->find(".") != std::string::npos) 2127 { 2128 break; 2129 // This check is necessary as the trailing slash gets 2130 // parsed as part of our <path> specifier above, which 2131 // causes the normal trailing backslash redirector to 2132 // fail. 2133 } 2134 if (!it->empty()) 2135 { 2136 objectPath += "/" + *it; 2137 } 2138 it++; 2139 } 2140 if (it != strs.end()) 2141 { 2142 interfaceName = *it; 2143 it++; 2144 2145 // after interface, we might have a method name 2146 if (it != strs.end()) 2147 { 2148 methodName = *it; 2149 it++; 2150 } 2151 } 2152 if (it != strs.end()) 2153 { 2154 // if there is more levels past the method name, something 2155 // went wrong, return not found 2156 asyncResp->res.result(boost::beast::http::status::not_found); 2157 return; 2158 } 2159 if (interfaceName.empty()) 2160 { 2161 crow::connections::systemBus->async_method_call( 2162 [asyncResp, processName, 2163 objectPath](const boost::system::error_code ec, 2164 const std::string& introspectXml) { 2165 if (ec) 2166 { 2167 BMCWEB_LOG_ERROR 2168 << "Introspect call failed with error: " << ec.message() 2169 << " on process: " << processName << " path: " << objectPath 2170 << "\n"; 2171 return; 2172 } 2173 tinyxml2::XMLDocument doc; 2174 2175 doc.Parse(introspectXml.c_str()); 2176 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2177 if (pRoot == nullptr) 2178 { 2179 BMCWEB_LOG_ERROR << "XML document failed to parse " 2180 << processName << " " << objectPath << "\n"; 2181 asyncResp->res.jsonValue["status"] = "XML parse error"; 2182 asyncResp->res.result( 2183 boost::beast::http::status::internal_server_error); 2184 return; 2185 } 2186 2187 BMCWEB_LOG_DEBUG << introspectXml; 2188 asyncResp->res.jsonValue["status"] = "ok"; 2189 asyncResp->res.jsonValue["bus_name"] = processName; 2190 asyncResp->res.jsonValue["object_path"] = objectPath; 2191 2192 nlohmann::json& interfacesArray = 2193 asyncResp->res.jsonValue["interfaces"]; 2194 interfacesArray = nlohmann::json::array(); 2195 tinyxml2::XMLElement* interface = 2196 pRoot->FirstChildElement("interface"); 2197 2198 while (interface != nullptr) 2199 { 2200 const char* ifaceName = interface->Attribute("name"); 2201 if (ifaceName != nullptr) 2202 { 2203 nlohmann::json::object_t interfaceObj; 2204 interfaceObj["name"] = ifaceName; 2205 interfacesArray.push_back(std::move(interfaceObj)); 2206 } 2207 2208 interface = interface->NextSiblingElement("interface"); 2209 } 2210 }, 2211 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2212 "Introspect"); 2213 } 2214 else if (methodName.empty()) 2215 { 2216 crow::connections::systemBus->async_method_call( 2217 [asyncResp, processName, objectPath, 2218 interfaceName](const boost::system::error_code ec, 2219 const std::string& introspectXml) { 2220 if (ec) 2221 { 2222 BMCWEB_LOG_ERROR 2223 << "Introspect call failed with error: " << ec.message() 2224 << " on process: " << processName << " path: " << objectPath 2225 << "\n"; 2226 return; 2227 } 2228 tinyxml2::XMLDocument doc; 2229 2230 doc.Parse(introspectXml.data(), introspectXml.size()); 2231 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2232 if (pRoot == nullptr) 2233 { 2234 BMCWEB_LOG_ERROR << "XML document failed to parse " 2235 << processName << " " << objectPath << "\n"; 2236 asyncResp->res.result( 2237 boost::beast::http::status::internal_server_error); 2238 return; 2239 } 2240 2241 asyncResp->res.jsonValue["status"] = "ok"; 2242 asyncResp->res.jsonValue["bus_name"] = processName; 2243 asyncResp->res.jsonValue["interface"] = interfaceName; 2244 asyncResp->res.jsonValue["object_path"] = objectPath; 2245 2246 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2247 methodsArray = nlohmann::json::array(); 2248 2249 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2250 signalsArray = nlohmann::json::array(); 2251 2252 nlohmann::json& propertiesObj = 2253 asyncResp->res.jsonValue["properties"]; 2254 propertiesObj = nlohmann::json::object(); 2255 2256 // if we know we're the only call, build the 2257 // json directly 2258 tinyxml2::XMLElement* interface = 2259 pRoot->FirstChildElement("interface"); 2260 while (interface != nullptr) 2261 { 2262 const char* ifaceName = interface->Attribute("name"); 2263 2264 if (ifaceName != nullptr && ifaceName == interfaceName) 2265 { 2266 break; 2267 } 2268 2269 interface = interface->NextSiblingElement("interface"); 2270 } 2271 if (interface == nullptr) 2272 { 2273 // if we got to the end of the list and 2274 // never found a match, throw 404 2275 asyncResp->res.result(boost::beast::http::status::not_found); 2276 return; 2277 } 2278 2279 tinyxml2::XMLElement* methods = 2280 interface->FirstChildElement("method"); 2281 while (methods != nullptr) 2282 { 2283 nlohmann::json argsArray = nlohmann::json::array(); 2284 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2285 while (arg != nullptr) 2286 { 2287 nlohmann::json thisArg; 2288 for (const char* fieldName : std::array<const char*, 3>{ 2289 "name", "direction", "type"}) 2290 { 2291 const char* fieldValue = arg->Attribute(fieldName); 2292 if (fieldValue != nullptr) 2293 { 2294 thisArg[fieldName] = fieldValue; 2295 } 2296 } 2297 argsArray.push_back(std::move(thisArg)); 2298 arg = arg->NextSiblingElement("arg"); 2299 } 2300 2301 const char* name = methods->Attribute("name"); 2302 if (name != nullptr) 2303 { 2304 std::string uri; 2305 uri.reserve(14 + processName.size() + objectPath.size() + 2306 interfaceName.size() + strlen(name)); 2307 uri += "/bus/system/"; 2308 uri += processName; 2309 uri += objectPath; 2310 uri += "/"; 2311 uri += interfaceName; 2312 uri += "/"; 2313 uri += name; 2314 2315 nlohmann::json::object_t object; 2316 object["name"] = name; 2317 object["uri"] = std::move(uri); 2318 object["args"] = argsArray; 2319 2320 methodsArray.push_back(std::move(object)); 2321 } 2322 methods = methods->NextSiblingElement("method"); 2323 } 2324 tinyxml2::XMLElement* signals = 2325 interface->FirstChildElement("signal"); 2326 while (signals != nullptr) 2327 { 2328 nlohmann::json argsArray = nlohmann::json::array(); 2329 2330 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2331 while (arg != nullptr) 2332 { 2333 const char* name = arg->Attribute("name"); 2334 const char* type = arg->Attribute("type"); 2335 if (name != nullptr && type != nullptr) 2336 { 2337 argsArray.push_back({ 2338 {"name", name}, 2339 {"type", type}, 2340 }); 2341 } 2342 arg = arg->NextSiblingElement("arg"); 2343 } 2344 const char* name = signals->Attribute("name"); 2345 if (name != nullptr) 2346 { 2347 nlohmann::json::object_t object; 2348 object["name"] = name; 2349 object["args"] = argsArray; 2350 signalsArray.push_back(std::move(object)); 2351 } 2352 2353 signals = signals->NextSiblingElement("signal"); 2354 } 2355 2356 tinyxml2::XMLElement* property = 2357 interface->FirstChildElement("property"); 2358 while (property != nullptr) 2359 { 2360 const char* name = property->Attribute("name"); 2361 const char* type = property->Attribute("type"); 2362 if (type != nullptr && name != nullptr) 2363 { 2364 sdbusplus::message_t m = 2365 crow::connections::systemBus->new_method_call( 2366 processName.c_str(), objectPath.c_str(), 2367 "org.freedesktop." 2368 "DBus." 2369 "Properties", 2370 "Get"); 2371 m.append(interfaceName, name); 2372 nlohmann::json& propertyItem = propertiesObj[name]; 2373 crow::connections::systemBus->async_send( 2374 m, [&propertyItem, 2375 asyncResp](const boost::system::error_code& e, 2376 sdbusplus::message_t& msg) { 2377 if (e) 2378 { 2379 return; 2380 } 2381 2382 convertDBusToJSON("v", msg, propertyItem); 2383 }); 2384 } 2385 property = property->NextSiblingElement("property"); 2386 } 2387 }, 2388 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2389 "Introspect"); 2390 } 2391 else 2392 { 2393 if (req.method() != boost::beast::http::verb::post) 2394 { 2395 asyncResp->res.result(boost::beast::http::status::not_found); 2396 return; 2397 } 2398 2399 nlohmann::json requestDbusData = 2400 nlohmann::json::parse(req.body, nullptr, false); 2401 2402 if (requestDbusData.is_discarded()) 2403 { 2404 asyncResp->res.result(boost::beast::http::status::bad_request); 2405 return; 2406 } 2407 if (!requestDbusData.is_array()) 2408 { 2409 asyncResp->res.result(boost::beast::http::status::bad_request); 2410 return; 2411 } 2412 auto transaction = 2413 std::make_shared<InProgressActionData>(asyncResp->res); 2414 2415 transaction->path = objectPath; 2416 transaction->methodName = methodName; 2417 transaction->arguments = std::move(requestDbusData); 2418 2419 findActionOnInterface(transaction, processName); 2420 } 2421 } 2422 2423 inline void requestRoutes(App& app) 2424 { 2425 BMCWEB_ROUTE(app, "/bus/") 2426 .privileges({{"Login"}}) 2427 .methods(boost::beast::http::verb::get)( 2428 [](const crow::Request&, 2429 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2430 nlohmann::json::array_t buses; 2431 nlohmann::json& bus = buses.emplace_back(); 2432 bus["name"] = "system"; 2433 asyncResp->res.jsonValue["busses"] = std::move(buses); 2434 asyncResp->res.jsonValue["status"] = "ok"; 2435 }); 2436 2437 BMCWEB_ROUTE(app, "/bus/system/") 2438 .privileges({{"Login"}}) 2439 .methods(boost::beast::http::verb::get)( 2440 [](const crow::Request&, 2441 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2442 auto myCallback = [asyncResp](const boost::system::error_code ec, 2443 std::vector<std::string>& names) { 2444 if (ec) 2445 { 2446 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 2447 asyncResp->res.result( 2448 boost::beast::http::status::internal_server_error); 2449 } 2450 else 2451 { 2452 std::sort(names.begin(), names.end()); 2453 asyncResp->res.jsonValue["status"] = "ok"; 2454 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2455 for (const auto& name : names) 2456 { 2457 nlohmann::json::object_t object; 2458 object["name"] = name; 2459 objectsSub.push_back(std::move(object)); 2460 } 2461 } 2462 }; 2463 crow::connections::systemBus->async_method_call( 2464 std::move(myCallback), "org.freedesktop.DBus", "/", 2465 "org.freedesktop.DBus", "ListNames"); 2466 }); 2467 2468 BMCWEB_ROUTE(app, "/list/") 2469 .privileges({{"Login"}}) 2470 .methods(boost::beast::http::verb::get)( 2471 [](const crow::Request&, 2472 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2473 handleList(asyncResp, "/"); 2474 }); 2475 2476 BMCWEB_ROUTE(app, "/xyz/<path>") 2477 .privileges({{"Login"}}) 2478 .methods(boost::beast::http::verb::get)( 2479 [](const crow::Request& req, 2480 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2481 const std::string& path) { 2482 std::string objectPath = "/xyz/" + path; 2483 handleDBusUrl(req, asyncResp, objectPath); 2484 }); 2485 2486 BMCWEB_ROUTE(app, "/xyz/<path>") 2487 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2488 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2489 boost::beast::http::verb::delete_)( 2490 [](const crow::Request& req, 2491 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2492 const std::string& path) { 2493 std::string objectPath = "/xyz/" + path; 2494 handleDBusUrl(req, asyncResp, objectPath); 2495 }); 2496 2497 BMCWEB_ROUTE(app, "/org/<path>") 2498 .privileges({{"Login"}}) 2499 .methods(boost::beast::http::verb::get)( 2500 [](const crow::Request& req, 2501 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2502 const std::string& path) { 2503 std::string objectPath = "/org/" + path; 2504 handleDBusUrl(req, asyncResp, objectPath); 2505 }); 2506 2507 BMCWEB_ROUTE(app, "/org/<path>") 2508 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2509 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2510 boost::beast::http::verb::delete_)( 2511 [](const crow::Request& req, 2512 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2513 const std::string& path) { 2514 std::string objectPath = "/org/" + path; 2515 handleDBusUrl(req, asyncResp, objectPath); 2516 }); 2517 2518 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2519 .privileges({{"ConfigureManager"}}) 2520 .methods(boost::beast::http::verb::get)( 2521 [](const crow::Request&, 2522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2523 const std::string& dumpId) { 2524 if (!validateFilename(dumpId)) 2525 { 2526 asyncResp->res.result(boost::beast::http::status::bad_request); 2527 return; 2528 } 2529 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2530 2531 loc /= dumpId; 2532 2533 if (!std::filesystem::exists(loc) || 2534 !std::filesystem::is_directory(loc)) 2535 { 2536 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 2537 asyncResp->res.result(boost::beast::http::status::not_found); 2538 return; 2539 } 2540 std::filesystem::directory_iterator files(loc); 2541 2542 for (const auto& file : files) 2543 { 2544 std::ifstream readFile(file.path()); 2545 if (!readFile.good()) 2546 { 2547 continue; 2548 } 2549 2550 asyncResp->res.addHeader(boost::beast::http::field::content_type, 2551 "application/octet-stream"); 2552 2553 // Assuming only one dump file will be present in the dump 2554 // id directory 2555 std::string dumpFileName = file.path().filename().string(); 2556 2557 // Filename should be in alphanumeric, dot and underscore 2558 // Its based on phosphor-debug-collector application 2559 // dumpfile format 2560 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2561 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2562 { 2563 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName; 2564 asyncResp->res.result(boost::beast::http::status::not_found); 2565 return; 2566 } 2567 std::string contentDispositionParam = 2568 "attachment; filename=\"" + dumpFileName + "\""; 2569 2570 asyncResp->res.addHeader( 2571 boost::beast::http::field::content_disposition, 2572 contentDispositionParam); 2573 2574 asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile), 2575 std::istreambuf_iterator<char>()}; 2576 return; 2577 } 2578 asyncResp->res.result(boost::beast::http::status::not_found); 2579 return; 2580 }); 2581 2582 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2583 .privileges({{"Login"}}) 2584 2585 .methods(boost::beast::http::verb::get)( 2586 [](const crow::Request&, 2587 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2588 const std::string& connection) { 2589 introspectObjects(connection, "/", asyncResp); 2590 }); 2591 2592 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2593 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2594 .methods(boost::beast::http::verb::get, 2595 boost::beast::http::verb::post)(handleBusSystemPost); 2596 } 2597 } // namespace openbmc_mapper 2598 } // namespace crow 2599