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