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