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