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