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