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