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, 112 const std::string_view msg) 113 { 114 res.result(result); 115 res.jsonValue["data"]["description"] = desc; 116 res.jsonValue["message"] = msg; 117 res.jsonValue["status"] = "error"; 118 } 119 120 inline void 121 introspectObjects(const std::string& processName, 122 const std::string& objectPath, 123 const std::shared_ptr<bmcweb::AsyncResp>& transaction) 124 { 125 if (transaction->res.jsonValue.is_null()) 126 { 127 transaction->res.jsonValue["status"] = "ok"; 128 transaction->res.jsonValue["bus_name"] = processName; 129 transaction->res.jsonValue["objects"] = nlohmann::json::array(); 130 } 131 132 crow::connections::systemBus->async_method_call( 133 [transaction, processName{std::string(processName)}, 134 objectPath{std::string(objectPath)}]( 135 const boost::system::error_code ec, 136 const std::string& introspectXml) { 137 if (ec) 138 { 139 BMCWEB_LOG_ERROR 140 << "Introspect call failed with error: " << ec.message() 141 << " on process: " << processName << " path: " << objectPath 142 << "\n"; 143 return; 144 } 145 nlohmann::json::object_t object; 146 object["path"] = objectPath; 147 148 transaction->res.jsonValue["objects"].push_back(std::move(object)); 149 150 tinyxml2::XMLDocument doc; 151 152 doc.Parse(introspectXml.c_str()); 153 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 154 if (pRoot == nullptr) 155 { 156 BMCWEB_LOG_ERROR << "XML document failed to parse " << processName 157 << " " << objectPath << "\n"; 158 } 159 else 160 { 161 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node"); 162 while (node != nullptr) 163 { 164 const char* childPath = node->Attribute("name"); 165 if (childPath != nullptr) 166 { 167 std::string newpath; 168 if (objectPath != "/") 169 { 170 newpath += objectPath; 171 } 172 newpath += std::string("/") + childPath; 173 // introspect the subobjects as well 174 introspectObjects(processName, newpath, transaction); 175 } 176 177 node = node->NextSiblingElement("node"); 178 } 179 } 180 }, 181 processName, objectPath, "org.freedesktop.DBus.Introspectable", 182 "Introspect"); 183 } 184 185 inline void getPropertiesForEnumerate( 186 const std::string& objectPath, const std::string& service, 187 const std::string& interface, 188 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 189 { 190 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " " 191 << service << " " << interface; 192 193 sdbusplus::asio::getAllProperties( 194 *crow::connections::systemBus, service, objectPath, interface, 195 [asyncResp, objectPath, service, 196 interface](const boost::system::error_code ec, 197 const dbus::utility::DBusPropertiesMap& propertiesList) { 198 if (ec) 199 { 200 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface " 201 << interface << " service " << service 202 << " failed with code " << ec; 203 return; 204 } 205 206 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 207 nlohmann::json& objectJson = dataJson[objectPath]; 208 if (objectJson.is_null()) 209 { 210 objectJson = nlohmann::json::object(); 211 } 212 213 for (const auto& [name, value] : propertiesList) 214 { 215 nlohmann::json& propertyJson = objectJson[name]; 216 std::visit( 217 [&propertyJson](auto&& val) { 218 if constexpr (std::is_same_v<std::decay_t<decltype(val)>, 219 sdbusplus::message::unix_fd>) 220 { 221 propertyJson = val.fd; 222 } 223 else 224 { 225 226 propertyJson = val; 227 } 228 }, 229 value); 230 } 231 }); 232 } 233 234 // Find any results that weren't picked up by ObjectManagers, to be 235 // called after all ObjectManagers are searched for and called. 236 inline void findRemainingObjectsForEnumerate( 237 const std::string& objectPath, 238 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree, 239 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 240 { 241 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate"; 242 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 243 244 for (const auto& [path, interface_map] : *subtree) 245 { 246 if (path == objectPath) 247 { 248 // An enumerate does not return the target path's properties 249 continue; 250 } 251 if (dataJson.find(path) == dataJson.end()) 252 { 253 for (const auto& [service, interfaces] : interface_map) 254 { 255 for (const auto& interface : interfaces) 256 { 257 if (!interface.starts_with("org.freedesktop.DBus")) 258 { 259 getPropertiesForEnumerate(path, service, interface, 260 asyncResp); 261 } 262 } 263 } 264 } 265 } 266 } 267 268 struct InProgressEnumerateData 269 { 270 InProgressEnumerateData( 271 const std::string& objectPathIn, 272 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 273 objectPath(objectPathIn), 274 asyncResp(asyncRespIn) 275 {} 276 277 ~InProgressEnumerateData() 278 { 279 try 280 { 281 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp); 282 } 283 catch (...) 284 { 285 BMCWEB_LOG_CRITICAL 286 << "findRemainingObjectsForEnumerate threw exception"; 287 } 288 } 289 290 InProgressEnumerateData(const InProgressEnumerateData&) = delete; 291 InProgressEnumerateData(InProgressEnumerateData&&) = delete; 292 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete; 293 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete; 294 const std::string objectPath; 295 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree; 296 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 297 }; 298 299 inline void getManagedObjectsForEnumerate( 300 const std::string& objectName, const std::string& objectManagerPath, 301 const std::string& connectionName, 302 const std::shared_ptr<InProgressEnumerateData>& transaction) 303 { 304 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName 305 << " object_manager_path " << objectManagerPath 306 << " connection_name " << connectionName; 307 crow::connections::systemBus->async_method_call( 308 [transaction, objectName, 309 connectionName](const boost::system::error_code ec, 310 const dbus::utility::ManagedObjectType& objects) { 311 if (ec) 312 { 313 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName 314 << " on connection " << connectionName 315 << " failed with code " << ec; 316 return; 317 } 318 319 nlohmann::json& dataJson = 320 transaction->asyncResp->res.jsonValue["data"]; 321 322 for (const auto& objectPath : objects) 323 { 324 if (objectPath.first.str.starts_with(objectName)) 325 { 326 BMCWEB_LOG_DEBUG << "Reading object " << objectPath.first.str; 327 nlohmann::json& objectJson = dataJson[objectPath.first.str]; 328 if (objectJson.is_null()) 329 { 330 objectJson = nlohmann::json::object(); 331 } 332 for (const auto& interface : objectPath.second) 333 { 334 for (const auto& property : interface.second) 335 { 336 nlohmann::json& propertyJson = 337 objectJson[property.first]; 338 std::visit( 339 [&propertyJson](auto&& val) { 340 if constexpr (std::is_same_v< 341 std::decay_t<decltype(val)>, 342 sdbusplus::message::unix_fd>) 343 { 344 propertyJson = val.fd; 345 } 346 else 347 { 348 349 propertyJson = val; 350 } 351 }, 352 property.second); 353 } 354 } 355 } 356 for (const auto& interface : objectPath.second) 357 { 358 if (interface.first == "org.freedesktop.DBus.ObjectManager") 359 { 360 getManagedObjectsForEnumerate(objectPath.first.str, 361 objectPath.first.str, 362 connectionName, transaction); 363 } 364 } 365 } 366 }, 367 connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager", 368 "GetManagedObjects"); 369 } 370 371 inline void findObjectManagerPathForEnumerate( 372 const std::string& objectName, const std::string& connectionName, 373 const std::shared_ptr<InProgressEnumerateData>& transaction) 374 { 375 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName 376 << " on connection:" << connectionName; 377 crow::connections::systemBus->async_method_call( 378 [transaction, objectName, connectionName]( 379 const boost::system::error_code ec, 380 const dbus::utility::MapperGetAncestorsResponse& objects) { 381 if (ec) 382 { 383 BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName 384 << " failed with code " << ec; 385 return; 386 } 387 388 for (const auto& pathGroup : objects) 389 { 390 for (const auto& connectionGroup : pathGroup.second) 391 { 392 if (connectionGroup.first == connectionName) 393 { 394 // Found the object manager path for this resource. 395 getManagedObjectsForEnumerate(objectName, pathGroup.first, 396 connectionName, transaction); 397 return; 398 } 399 } 400 } 401 }, 402 "xyz.openbmc_project.ObjectMapper", 403 "/xyz/openbmc_project/object_mapper", 404 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName, 405 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"}); 406 } 407 408 // Uses GetObject to add the object info about the target /enumerate path to 409 // the results of GetSubTree, as GetSubTree will not return info for the 410 // target path, and then continues on enumerating the rest of the tree. 411 inline void getObjectAndEnumerate( 412 const std::shared_ptr<InProgressEnumerateData>& transaction) 413 { 414 dbus::utility::getDbusObject( 415 transaction->objectPath, {}, 416 [transaction](const boost::system::error_code& ec, 417 const dbus::utility::MapperGetObject& objects) { 418 if (ec) 419 { 420 BMCWEB_LOG_ERROR << "GetObject for path " << transaction->objectPath 421 << " failed with code " << ec; 422 return; 423 } 424 425 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath 426 << " has " << objects.size() << " entries"; 427 if (!objects.empty()) 428 { 429 transaction->subtree->emplace_back(transaction->objectPath, 430 objects); 431 } 432 433 // Map indicating connection name, and the path where the object 434 // manager exists 435 boost::container::flat_map<std::string, std::string> connections; 436 437 for (const auto& object : *(transaction->subtree)) 438 { 439 for (const auto& connection : object.second) 440 { 441 for (const auto& interface : connection.second) 442 { 443 BMCWEB_LOG_DEBUG << connection.first << " has interface " 444 << interface; 445 if (interface == "org.freedesktop.DBus.ObjectManager") 446 { 447 BMCWEB_LOG_DEBUG << "found object manager path " 448 << object.first; 449 connections[connection.first] = object.first; 450 } 451 } 452 } 453 } 454 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections"; 455 456 for (const auto& connection : connections) 457 { 458 // If we already know where the object manager is, we don't 459 // need to search for it, we can call directly in to 460 // getManagedObjects 461 if (!connection.second.empty()) 462 { 463 getManagedObjectsForEnumerate(transaction->objectPath, 464 connection.second, 465 connection.first, transaction); 466 } 467 else 468 { 469 // otherwise we need to find the object manager path 470 // before we can continue 471 findObjectManagerPathForEnumerate( 472 transaction->objectPath, connection.first, transaction); 473 } 474 } 475 }); 476 } 477 478 // Structure for storing data on an in progress action 479 struct InProgressActionData 480 { 481 explicit InProgressActionData(crow::Response& resIn) : res(resIn) 482 {} 483 ~InProgressActionData() 484 { 485 // Methods could have been called across different owners 486 // and interfaces, where some calls failed and some passed. 487 // 488 // The rules for this are: 489 // * if no method was called - error 490 // * if a method failed and none passed - error 491 // (converse: if at least one method passed - OK) 492 // * for the method output: 493 // * if output processing didn't fail, return the data 494 495 // Only deal with method returns if nothing failed earlier 496 if (res.result() == boost::beast::http::status::ok) 497 { 498 if (!methodPassed) 499 { 500 if (!methodFailed) 501 { 502 setErrorResponse(res, boost::beast::http::status::not_found, 503 methodNotFoundDesc, notFoundMsg); 504 } 505 } 506 else 507 { 508 if (outputFailed) 509 { 510 setErrorResponse( 511 res, boost::beast::http::status::internal_server_error, 512 "Method output failure", methodOutputFailedMsg); 513 } 514 else 515 { 516 res.jsonValue["status"] = "ok"; 517 res.jsonValue["message"] = "200 OK"; 518 res.jsonValue["data"] = methodResponse; 519 } 520 } 521 } 522 523 res.end(); 524 } 525 InProgressActionData(const InProgressActionData&) = delete; 526 InProgressActionData(InProgressActionData&&) = delete; 527 InProgressActionData& operator=(const InProgressActionData&) = delete; 528 InProgressActionData& operator=(InProgressActionData&&) = delete; 529 530 void setErrorStatus(const std::string& desc) 531 { 532 setErrorResponse(res, boost::beast::http::status::bad_request, desc, 533 badReqMsg); 534 } 535 crow::Response& res; 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.push_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.push_back(std::move(j)); 1370 transaction->methodResponse.push_back(std::move(data)); 1371 transaction->convertedToArray = true; 1372 } 1373 else 1374 { 1375 transaction->methodResponse.push_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](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->res, 1510 boost::beast::http::status::bad_request, 1511 e->name, e->message); 1512 } 1513 else 1514 { 1515 setErrorResponse( 1516 transaction->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->res); 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->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 = 1630 std::make_shared<InProgressActionData>(asyncResp->res); 1631 transaction->path = objectPath; 1632 transaction->methodName = "Delete"; 1633 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1634 1635 for (const std::pair<std::string, std::vector<std::string>>& object : 1636 interfaceNames) 1637 { 1638 findActionOnInterface(transaction, object.first); 1639 } 1640 }); 1641 } 1642 1643 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1644 const std::string& objectPath, int32_t depth = 0) 1645 { 1646 dbus::utility::getSubTreePaths( 1647 objectPath, depth, {}, 1648 [asyncResp]( 1649 const boost::system::error_code& ec, 1650 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { 1651 if (ec) 1652 { 1653 setErrorResponse(asyncResp->res, 1654 boost::beast::http::status::not_found, 1655 notFoundDesc, notFoundMsg); 1656 } 1657 else 1658 { 1659 asyncResp->res.jsonValue["status"] = "ok"; 1660 asyncResp->res.jsonValue["message"] = "200 OK"; 1661 asyncResp->res.jsonValue["data"] = objectPaths; 1662 } 1663 }); 1664 } 1665 1666 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1667 const std::string& objectPath) 1668 { 1669 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 1670 1671 asyncResp->res.jsonValue["message"] = "200 OK"; 1672 asyncResp->res.jsonValue["status"] = "ok"; 1673 asyncResp->res.jsonValue["data"] = nlohmann::json::object(); 1674 1675 dbus::utility::getSubTree( 1676 objectPath, 0, {}, 1677 [objectPath, asyncResp]( 1678 const boost::system::error_code& ec, 1679 const dbus::utility::MapperGetSubTreeResponse& objectNames) { 1680 auto transaction = 1681 std::make_shared<InProgressEnumerateData>(objectPath, asyncResp); 1682 1683 transaction->subtree = 1684 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( 1685 objectNames); 1686 1687 if (ec) 1688 { 1689 BMCWEB_LOG_ERROR << "GetSubTree failed on " 1690 << transaction->objectPath; 1691 setErrorResponse(transaction->asyncResp->res, 1692 boost::beast::http::status::not_found, 1693 notFoundDesc, notFoundMsg); 1694 return; 1695 } 1696 1697 // Add the data for the path passed in to the results 1698 // as if GetSubTree returned it, and continue on enumerating 1699 getObjectAndEnumerate(transaction); 1700 }); 1701 } 1702 1703 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1704 std::string& objectPath, std::string& destProperty) 1705 { 1706 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1707 std::shared_ptr<std::string> propertyName = 1708 std::make_shared<std::string>(std::move(destProperty)); 1709 1710 std::shared_ptr<std::string> path = 1711 std::make_shared<std::string>(std::move(objectPath)); 1712 1713 dbus::utility::getDbusObject( 1714 *path, {}, 1715 [asyncResp, path, 1716 propertyName](const boost::system::error_code& ec, 1717 const dbus::utility::MapperGetObject& objectNames) { 1718 if (ec || objectNames.empty()) 1719 { 1720 setErrorResponse(asyncResp->res, 1721 boost::beast::http::status::not_found, 1722 notFoundDesc, notFoundMsg); 1723 return; 1724 } 1725 std::shared_ptr<nlohmann::json> response = 1726 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1727 // The mapper should never give us an empty interface names 1728 // list, but check anyway 1729 for (const std::pair<std::string, std::vector<std::string>>& 1730 connection : objectNames) 1731 { 1732 const std::vector<std::string>& interfaceNames = connection.second; 1733 1734 if (interfaceNames.empty()) 1735 { 1736 setErrorResponse(asyncResp->res, 1737 boost::beast::http::status::not_found, 1738 notFoundDesc, notFoundMsg); 1739 return; 1740 } 1741 1742 for (const std::string& interface : interfaceNames) 1743 { 1744 sdbusplus::message_t m = 1745 crow::connections::systemBus->new_method_call( 1746 connection.first.c_str(), path->c_str(), 1747 "org.freedesktop.DBus.Properties", "GetAll"); 1748 m.append(interface); 1749 crow::connections::systemBus->async_send( 1750 m, [asyncResp, response, 1751 propertyName](const boost::system::error_code ec2, 1752 sdbusplus::message_t& msg) { 1753 if (ec2) 1754 { 1755 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1756 << ec2; 1757 } 1758 else 1759 { 1760 nlohmann::json properties; 1761 int r = convertDBusToJSON("a{sv}", msg, properties); 1762 if (r < 0) 1763 { 1764 BMCWEB_LOG_ERROR << "convertDBusToJSON failed"; 1765 } 1766 else 1767 { 1768 for (const auto& prop : properties.items()) 1769 { 1770 // if property name is empty, or 1771 // matches our search query, add it 1772 // to the response json 1773 1774 if (propertyName->empty()) 1775 { 1776 (*response)[prop.key()] = 1777 std::move(prop.value()); 1778 } 1779 else if (prop.key() == *propertyName) 1780 { 1781 *response = std::move(prop.value()); 1782 } 1783 } 1784 } 1785 } 1786 if (response.use_count() == 1) 1787 { 1788 if (!propertyName->empty() && response->empty()) 1789 { 1790 setErrorResponse( 1791 asyncResp->res, 1792 boost::beast::http::status::not_found, 1793 propNotFoundDesc, notFoundMsg); 1794 } 1795 else 1796 { 1797 asyncResp->res.jsonValue["status"] = "ok"; 1798 asyncResp->res.jsonValue["message"] = "200 OK"; 1799 asyncResp->res.jsonValue["data"] = *response; 1800 } 1801 } 1802 }); 1803 } 1804 } 1805 }); 1806 } 1807 1808 struct AsyncPutRequest 1809 { 1810 explicit AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : 1811 asyncResp(resIn) 1812 {} 1813 ~AsyncPutRequest() 1814 { 1815 if (asyncResp->res.jsonValue.empty()) 1816 { 1817 setErrorResponse(asyncResp->res, 1818 boost::beast::http::status::forbidden, 1819 forbiddenMsg, forbiddenPropDesc); 1820 } 1821 } 1822 1823 AsyncPutRequest(const AsyncPutRequest&) = delete; 1824 AsyncPutRequest(AsyncPutRequest&&) = delete; 1825 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; 1826 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; 1827 1828 void setErrorStatus(const std::string& desc) 1829 { 1830 setErrorResponse(asyncResp->res, 1831 boost::beast::http::status::internal_server_error, 1832 desc, badReqMsg); 1833 } 1834 1835 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1836 std::string objectPath; 1837 std::string propertyName; 1838 nlohmann::json propertyValue; 1839 }; 1840 1841 inline void handlePut(const crow::Request& req, 1842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1843 const std::string& objectPath, 1844 const std::string& destProperty) 1845 { 1846 if (destProperty.empty()) 1847 { 1848 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, 1849 forbiddenResDesc, forbiddenMsg); 1850 return; 1851 } 1852 nlohmann::json requestDbusData; 1853 1854 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1855 if (ret == JsonParseResult::BadContentType) 1856 { 1857 setErrorResponse(asyncResp->res, 1858 boost::beast::http::status::unsupported_media_type, 1859 invalidContentType, unsupportedMediaMsg); 1860 return; 1861 } 1862 1863 if (ret != JsonParseResult::Success) 1864 { 1865 setErrorResponse(asyncResp->res, 1866 boost::beast::http::status::bad_request, noJsonDesc, 1867 badReqMsg); 1868 return; 1869 } 1870 1871 auto propertyIt = requestDbusData.find("data"); 1872 if (propertyIt == requestDbusData.end()) 1873 { 1874 setErrorResponse(asyncResp->res, 1875 boost::beast::http::status::bad_request, noJsonDesc, 1876 badReqMsg); 1877 return; 1878 } 1879 const nlohmann::json& propertySetValue = *propertyIt; 1880 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); 1881 transaction->objectPath = objectPath; 1882 transaction->propertyName = destProperty; 1883 transaction->propertyValue = propertySetValue; 1884 1885 dbus::utility::getDbusObject( 1886 transaction->objectPath, {}, 1887 [transaction](const boost::system::error_code& ec2, 1888 const dbus::utility::MapperGetObject& objectNames) { 1889 if (!ec2 && objectNames.empty()) 1890 { 1891 setErrorResponse(transaction->asyncResp->res, 1892 boost::beast::http::status::not_found, 1893 propNotFoundDesc, notFoundMsg); 1894 return; 1895 } 1896 1897 for (const std::pair<std::string, std::vector<std::string>>& 1898 connection : objectNames) 1899 { 1900 const std::string& connectionName = connection.first; 1901 1902 crow::connections::systemBus->async_method_call( 1903 [connectionName{std::string(connectionName)}, 1904 transaction](const boost::system::error_code ec3, 1905 const std::string& introspectXml) { 1906 if (ec3) 1907 { 1908 BMCWEB_LOG_ERROR << "Introspect call failed with error: " 1909 << ec3.message() 1910 << " on process: " << connectionName; 1911 transaction->setErrorStatus("Unexpected Error"); 1912 return; 1913 } 1914 tinyxml2::XMLDocument doc; 1915 1916 doc.Parse(introspectXml.c_str()); 1917 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1918 if (pRoot == nullptr) 1919 { 1920 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1921 << introspectXml; 1922 transaction->setErrorStatus("Unexpected Error"); 1923 return; 1924 } 1925 tinyxml2::XMLElement* ifaceNode = 1926 pRoot->FirstChildElement("interface"); 1927 while (ifaceNode != nullptr) 1928 { 1929 const char* interfaceName = ifaceNode->Attribute("name"); 1930 BMCWEB_LOG_DEBUG << "found interface " << interfaceName; 1931 tinyxml2::XMLElement* propNode = 1932 ifaceNode->FirstChildElement("property"); 1933 while (propNode != nullptr) 1934 { 1935 const char* propertyName = propNode->Attribute("name"); 1936 BMCWEB_LOG_DEBUG << "Found property " << propertyName; 1937 if (propertyName == transaction->propertyName) 1938 { 1939 const char* argType = propNode->Attribute("type"); 1940 if (argType != nullptr) 1941 { 1942 sdbusplus::message_t m = 1943 crow::connections::systemBus 1944 ->new_method_call( 1945 connectionName.c_str(), 1946 transaction->objectPath.c_str(), 1947 "org.freedesktop.DBus." 1948 "Properties", 1949 "Set"); 1950 m.append(interfaceName, 1951 transaction->propertyName); 1952 int r = sd_bus_message_open_container( 1953 m.get(), SD_BUS_TYPE_VARIANT, argType); 1954 if (r < 0) 1955 { 1956 transaction->setErrorStatus( 1957 "Unexpected Error"); 1958 return; 1959 } 1960 r = convertJsonToDbus( 1961 m.get(), argType, 1962 transaction->propertyValue); 1963 if (r < 0) 1964 { 1965 if (r == -ERANGE) 1966 { 1967 transaction->setErrorStatus( 1968 "Provided property value " 1969 "is out of range for the " 1970 "property type"); 1971 } 1972 else 1973 { 1974 transaction->setErrorStatus( 1975 "Invalid arg type"); 1976 } 1977 return; 1978 } 1979 r = sd_bus_message_close_container(m.get()); 1980 if (r < 0) 1981 { 1982 transaction->setErrorStatus( 1983 "Unexpected Error"); 1984 return; 1985 } 1986 crow::connections::systemBus->async_send( 1987 m, 1988 [transaction](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 2032 // If accessing a single attribute, fill in and update objectPath, 2033 // otherwise leave destProperty blank 2034 std::string destProperty; 2035 const char* attrSeperator = "/attr/"; 2036 size_t attrPosition = objectPath.find(attrSeperator); 2037 if (attrPosition != std::string::npos) 2038 { 2039 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2040 objectPath.length()); 2041 objectPath.resize(attrPosition); 2042 } 2043 2044 if (req.method() == boost::beast::http::verb::post) 2045 { 2046 constexpr const char* actionSeperator = "/action/"; 2047 size_t actionPosition = objectPath.find(actionSeperator); 2048 if (actionPosition != std::string::npos) 2049 { 2050 std::string postProperty = 2051 objectPath.substr((actionPosition + strlen(actionSeperator)), 2052 objectPath.length()); 2053 objectPath.resize(actionPosition); 2054 handleAction(req, asyncResp, objectPath, postProperty); 2055 return; 2056 } 2057 } 2058 else if (req.method() == boost::beast::http::verb::get) 2059 { 2060 if (objectPath.ends_with("/enumerate")) 2061 { 2062 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2063 objectPath.end()); 2064 handleEnumerate(asyncResp, objectPath); 2065 } 2066 else if (objectPath.ends_with("/list")) 2067 { 2068 objectPath.erase(objectPath.end() - sizeof("list"), 2069 objectPath.end()); 2070 handleList(asyncResp, objectPath); 2071 } 2072 else 2073 { 2074 // Trim any trailing "/" at the end 2075 if (objectPath.ends_with("/")) 2076 { 2077 objectPath.pop_back(); 2078 handleList(asyncResp, objectPath, 1); 2079 } 2080 else 2081 { 2082 handleGet(asyncResp, objectPath, destProperty); 2083 } 2084 } 2085 return; 2086 } 2087 else if (req.method() == boost::beast::http::verb::put) 2088 { 2089 handlePut(req, asyncResp, objectPath, destProperty); 2090 return; 2091 } 2092 else if (req.method() == boost::beast::http::verb::delete_) 2093 { 2094 handleDelete(asyncResp, objectPath); 2095 return; 2096 } 2097 2098 setErrorResponse(asyncResp->res, 2099 boost::beast::http::status::method_not_allowed, 2100 methodNotAllowedDesc, methodNotAllowedMsg); 2101 } 2102 2103 inline void 2104 handleBusSystemPost(const crow::Request& req, 2105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2106 const std::string& processName, 2107 const std::string& requestedPath) 2108 { 2109 std::vector<std::string> strs; 2110 2111 bmcweb::split(strs, requestedPath, '/'); 2112 std::string objectPath; 2113 std::string interfaceName; 2114 std::string methodName; 2115 auto it = strs.begin(); 2116 if (it == strs.end()) 2117 { 2118 objectPath = "/"; 2119 } 2120 while (it != strs.end()) 2121 { 2122 // Check if segment contains ".". If it does, it must be an 2123 // interface 2124 if (it->find(".") != std::string::npos) 2125 { 2126 break; 2127 // This check is necessary as the trailing slash gets 2128 // parsed as part of our <path> specifier above, which 2129 // causes the normal trailing backslash redirector to 2130 // fail. 2131 } 2132 if (!it->empty()) 2133 { 2134 objectPath += "/" + *it; 2135 } 2136 it++; 2137 } 2138 if (it != strs.end()) 2139 { 2140 interfaceName = *it; 2141 it++; 2142 2143 // after interface, we might have a method name 2144 if (it != strs.end()) 2145 { 2146 methodName = *it; 2147 it++; 2148 } 2149 } 2150 if (it != strs.end()) 2151 { 2152 // if there is more levels past the method name, something 2153 // went wrong, return not found 2154 asyncResp->res.result(boost::beast::http::status::not_found); 2155 return; 2156 } 2157 if (interfaceName.empty()) 2158 { 2159 crow::connections::systemBus->async_method_call( 2160 [asyncResp, processName, 2161 objectPath](const boost::system::error_code ec, 2162 const std::string& introspectXml) { 2163 if (ec) 2164 { 2165 BMCWEB_LOG_ERROR 2166 << "Introspect call failed with error: " << ec.message() 2167 << " on process: " << processName << " path: " << objectPath 2168 << "\n"; 2169 return; 2170 } 2171 tinyxml2::XMLDocument doc; 2172 2173 doc.Parse(introspectXml.c_str()); 2174 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2175 if (pRoot == nullptr) 2176 { 2177 BMCWEB_LOG_ERROR << "XML document failed to parse " 2178 << processName << " " << objectPath << "\n"; 2179 asyncResp->res.jsonValue["status"] = "XML parse error"; 2180 asyncResp->res.result( 2181 boost::beast::http::status::internal_server_error); 2182 return; 2183 } 2184 2185 BMCWEB_LOG_DEBUG << introspectXml; 2186 asyncResp->res.jsonValue["status"] = "ok"; 2187 asyncResp->res.jsonValue["bus_name"] = processName; 2188 asyncResp->res.jsonValue["object_path"] = objectPath; 2189 2190 nlohmann::json& interfacesArray = 2191 asyncResp->res.jsonValue["interfaces"]; 2192 interfacesArray = nlohmann::json::array(); 2193 tinyxml2::XMLElement* interface = 2194 pRoot->FirstChildElement("interface"); 2195 2196 while (interface != nullptr) 2197 { 2198 const char* ifaceName = interface->Attribute("name"); 2199 if (ifaceName != nullptr) 2200 { 2201 nlohmann::json::object_t interfaceObj; 2202 interfaceObj["name"] = ifaceName; 2203 interfacesArray.push_back(std::move(interfaceObj)); 2204 } 2205 2206 interface = interface->NextSiblingElement("interface"); 2207 } 2208 }, 2209 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2210 "Introspect"); 2211 } 2212 else if (methodName.empty()) 2213 { 2214 crow::connections::systemBus->async_method_call( 2215 [asyncResp, processName, objectPath, 2216 interfaceName](const boost::system::error_code ec, 2217 const std::string& introspectXml) { 2218 if (ec) 2219 { 2220 BMCWEB_LOG_ERROR 2221 << "Introspect call failed with error: " << ec.message() 2222 << " on process: " << processName << " path: " << objectPath 2223 << "\n"; 2224 return; 2225 } 2226 tinyxml2::XMLDocument doc; 2227 2228 doc.Parse(introspectXml.data(), introspectXml.size()); 2229 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2230 if (pRoot == nullptr) 2231 { 2232 BMCWEB_LOG_ERROR << "XML document failed to parse " 2233 << processName << " " << objectPath << "\n"; 2234 asyncResp->res.result( 2235 boost::beast::http::status::internal_server_error); 2236 return; 2237 } 2238 2239 asyncResp->res.jsonValue["status"] = "ok"; 2240 asyncResp->res.jsonValue["bus_name"] = processName; 2241 asyncResp->res.jsonValue["interface"] = interfaceName; 2242 asyncResp->res.jsonValue["object_path"] = objectPath; 2243 2244 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2245 methodsArray = nlohmann::json::array(); 2246 2247 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2248 signalsArray = nlohmann::json::array(); 2249 2250 nlohmann::json& propertiesObj = 2251 asyncResp->res.jsonValue["properties"]; 2252 propertiesObj = nlohmann::json::object(); 2253 2254 // if we know we're the only call, build the 2255 // json directly 2256 tinyxml2::XMLElement* interface = 2257 pRoot->FirstChildElement("interface"); 2258 while (interface != nullptr) 2259 { 2260 const char* ifaceName = interface->Attribute("name"); 2261 2262 if (ifaceName != nullptr && ifaceName == interfaceName) 2263 { 2264 break; 2265 } 2266 2267 interface = interface->NextSiblingElement("interface"); 2268 } 2269 if (interface == nullptr) 2270 { 2271 // if we got to the end of the list and 2272 // never found a match, throw 404 2273 asyncResp->res.result(boost::beast::http::status::not_found); 2274 return; 2275 } 2276 2277 tinyxml2::XMLElement* methods = 2278 interface->FirstChildElement("method"); 2279 while (methods != nullptr) 2280 { 2281 nlohmann::json argsArray = nlohmann::json::array(); 2282 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2283 while (arg != nullptr) 2284 { 2285 nlohmann::json thisArg; 2286 for (const char* fieldName : std::array<const char*, 3>{ 2287 "name", "direction", "type"}) 2288 { 2289 const char* fieldValue = arg->Attribute(fieldName); 2290 if (fieldValue != nullptr) 2291 { 2292 thisArg[fieldName] = fieldValue; 2293 } 2294 } 2295 argsArray.push_back(std::move(thisArg)); 2296 arg = arg->NextSiblingElement("arg"); 2297 } 2298 2299 const char* name = methods->Attribute("name"); 2300 if (name != nullptr) 2301 { 2302 std::string uri; 2303 uri.reserve(14 + processName.size() + objectPath.size() + 2304 interfaceName.size() + strlen(name)); 2305 uri += "/bus/system/"; 2306 uri += processName; 2307 uri += objectPath; 2308 uri += "/"; 2309 uri += interfaceName; 2310 uri += "/"; 2311 uri += name; 2312 2313 nlohmann::json::object_t object; 2314 object["name"] = name; 2315 object["uri"] = std::move(uri); 2316 object["args"] = argsArray; 2317 2318 methodsArray.push_back(std::move(object)); 2319 } 2320 methods = methods->NextSiblingElement("method"); 2321 } 2322 tinyxml2::XMLElement* signals = 2323 interface->FirstChildElement("signal"); 2324 while (signals != nullptr) 2325 { 2326 nlohmann::json argsArray = nlohmann::json::array(); 2327 2328 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2329 while (arg != nullptr) 2330 { 2331 const char* name = arg->Attribute("name"); 2332 const char* type = arg->Attribute("type"); 2333 if (name != nullptr && type != nullptr) 2334 { 2335 argsArray.push_back({ 2336 {"name", name}, 2337 {"type", type}, 2338 }); 2339 } 2340 arg = arg->NextSiblingElement("arg"); 2341 } 2342 const char* name = signals->Attribute("name"); 2343 if (name != nullptr) 2344 { 2345 nlohmann::json::object_t object; 2346 object["name"] = name; 2347 object["args"] = argsArray; 2348 signalsArray.push_back(std::move(object)); 2349 } 2350 2351 signals = signals->NextSiblingElement("signal"); 2352 } 2353 2354 tinyxml2::XMLElement* property = 2355 interface->FirstChildElement("property"); 2356 while (property != nullptr) 2357 { 2358 const char* name = property->Attribute("name"); 2359 const char* type = property->Attribute("type"); 2360 if (type != nullptr && name != nullptr) 2361 { 2362 sdbusplus::message_t m = 2363 crow::connections::systemBus->new_method_call( 2364 processName.c_str(), objectPath.c_str(), 2365 "org.freedesktop." 2366 "DBus." 2367 "Properties", 2368 "Get"); 2369 m.append(interfaceName, name); 2370 nlohmann::json& propertyItem = propertiesObj[name]; 2371 crow::connections::systemBus->async_send( 2372 m, [&propertyItem, 2373 asyncResp](const boost::system::error_code& e, 2374 sdbusplus::message_t& msg) { 2375 if (e) 2376 { 2377 return; 2378 } 2379 2380 convertDBusToJSON("v", msg, propertyItem); 2381 }); 2382 } 2383 property = property->NextSiblingElement("property"); 2384 } 2385 }, 2386 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2387 "Introspect"); 2388 } 2389 else 2390 { 2391 if (req.method() != boost::beast::http::verb::post) 2392 { 2393 asyncResp->res.result(boost::beast::http::status::not_found); 2394 return; 2395 } 2396 2397 nlohmann::json requestDbusData; 2398 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 2399 if (ret == JsonParseResult::BadContentType) 2400 { 2401 setErrorResponse(asyncResp->res, 2402 boost::beast::http::status::unsupported_media_type, 2403 invalidContentType, unsupportedMediaMsg); 2404 return; 2405 } 2406 if (ret != JsonParseResult::Success) 2407 { 2408 setErrorResponse(asyncResp->res, 2409 boost::beast::http::status::bad_request, 2410 noJsonDesc, badReqMsg); 2411 return; 2412 } 2413 2414 if (!requestDbusData.is_array()) 2415 { 2416 asyncResp->res.result(boost::beast::http::status::bad_request); 2417 return; 2418 } 2419 auto transaction = 2420 std::make_shared<InProgressActionData>(asyncResp->res); 2421 2422 transaction->path = objectPath; 2423 transaction->methodName = methodName; 2424 transaction->arguments = std::move(requestDbusData); 2425 2426 findActionOnInterface(transaction, processName); 2427 } 2428 } 2429 2430 inline void requestRoutes(App& app) 2431 { 2432 BMCWEB_ROUTE(app, "/bus/") 2433 .privileges({{"Login"}}) 2434 .methods(boost::beast::http::verb::get)( 2435 [](const crow::Request&, 2436 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2437 nlohmann::json::array_t buses; 2438 nlohmann::json& bus = buses.emplace_back(); 2439 bus["name"] = "system"; 2440 asyncResp->res.jsonValue["busses"] = std::move(buses); 2441 asyncResp->res.jsonValue["status"] = "ok"; 2442 }); 2443 2444 BMCWEB_ROUTE(app, "/bus/system/") 2445 .privileges({{"Login"}}) 2446 .methods(boost::beast::http::verb::get)( 2447 [](const crow::Request&, 2448 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2449 auto myCallback = [asyncResp](const boost::system::error_code ec, 2450 std::vector<std::string>& names) { 2451 if (ec) 2452 { 2453 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 2454 asyncResp->res.result( 2455 boost::beast::http::status::internal_server_error); 2456 } 2457 else 2458 { 2459 std::sort(names.begin(), names.end()); 2460 asyncResp->res.jsonValue["status"] = "ok"; 2461 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2462 for (const auto& name : names) 2463 { 2464 nlohmann::json::object_t object; 2465 object["name"] = name; 2466 objectsSub.push_back(std::move(object)); 2467 } 2468 } 2469 }; 2470 crow::connections::systemBus->async_method_call( 2471 std::move(myCallback), "org.freedesktop.DBus", "/", 2472 "org.freedesktop.DBus", "ListNames"); 2473 }); 2474 2475 BMCWEB_ROUTE(app, "/list/") 2476 .privileges({{"Login"}}) 2477 .methods(boost::beast::http::verb::get)( 2478 [](const crow::Request&, 2479 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2480 handleList(asyncResp, "/"); 2481 }); 2482 2483 BMCWEB_ROUTE(app, "/xyz/<path>") 2484 .privileges({{"Login"}}) 2485 .methods(boost::beast::http::verb::get)( 2486 [](const crow::Request& req, 2487 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2488 const std::string& path) { 2489 std::string objectPath = "/xyz/" + path; 2490 handleDBusUrl(req, asyncResp, objectPath); 2491 }); 2492 2493 BMCWEB_ROUTE(app, "/xyz/<path>") 2494 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2495 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2496 boost::beast::http::verb::delete_)( 2497 [](const crow::Request& req, 2498 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2499 const std::string& path) { 2500 std::string objectPath = "/xyz/" + path; 2501 handleDBusUrl(req, asyncResp, objectPath); 2502 }); 2503 2504 BMCWEB_ROUTE(app, "/org/<path>") 2505 .privileges({{"Login"}}) 2506 .methods(boost::beast::http::verb::get)( 2507 [](const crow::Request& req, 2508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2509 const std::string& path) { 2510 std::string objectPath = "/org/" + path; 2511 handleDBusUrl(req, asyncResp, objectPath); 2512 }); 2513 2514 BMCWEB_ROUTE(app, "/org/<path>") 2515 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2516 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2517 boost::beast::http::verb::delete_)( 2518 [](const crow::Request& req, 2519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2520 const std::string& path) { 2521 std::string objectPath = "/org/" + path; 2522 handleDBusUrl(req, asyncResp, objectPath); 2523 }); 2524 2525 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2526 .privileges({{"ConfigureManager"}}) 2527 .methods(boost::beast::http::verb::get)( 2528 [](const crow::Request&, 2529 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2530 const std::string& dumpId) { 2531 if (!validateFilename(dumpId)) 2532 { 2533 asyncResp->res.result(boost::beast::http::status::bad_request); 2534 return; 2535 } 2536 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2537 2538 loc /= dumpId; 2539 2540 if (!std::filesystem::exists(loc) || 2541 !std::filesystem::is_directory(loc)) 2542 { 2543 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 2544 asyncResp->res.result(boost::beast::http::status::not_found); 2545 return; 2546 } 2547 std::filesystem::directory_iterator files(loc); 2548 2549 for (const auto& file : files) 2550 { 2551 std::ifstream readFile(file.path()); 2552 if (!readFile.good()) 2553 { 2554 continue; 2555 } 2556 2557 asyncResp->res.addHeader(boost::beast::http::field::content_type, 2558 "application/octet-stream"); 2559 2560 // Assuming only one dump file will be present in the dump 2561 // id directory 2562 std::string dumpFileName = file.path().filename().string(); 2563 2564 // Filename should be in alphanumeric, dot and underscore 2565 // Its based on phosphor-debug-collector application 2566 // dumpfile format 2567 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2568 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2569 { 2570 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName; 2571 asyncResp->res.result(boost::beast::http::status::not_found); 2572 return; 2573 } 2574 std::string contentDispositionParam = 2575 "attachment; filename=\"" + dumpFileName + "\""; 2576 2577 asyncResp->res.addHeader( 2578 boost::beast::http::field::content_disposition, 2579 contentDispositionParam); 2580 2581 asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile), 2582 std::istreambuf_iterator<char>()}; 2583 return; 2584 } 2585 asyncResp->res.result(boost::beast::http::status::not_found); 2586 return; 2587 }); 2588 2589 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2590 .privileges({{"Login"}}) 2591 2592 .methods(boost::beast::http::verb::get)( 2593 [](const crow::Request&, 2594 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2595 const std::string& connection) { 2596 introspectObjects(connection, "/", asyncResp); 2597 }); 2598 2599 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2600 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2601 .methods(boost::beast::http::verb::get, 2602 boost::beast::http::verb::post)(handleBusSystemPost); 2603 } 2604 } // namespace openbmc_mapper 2605 } // namespace crow 2606