1 // Copyright (c) 2018 Intel Corporation 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #pragma once 16 #include "app.hpp" 17 #include "async_resp.hpp" 18 #include "dbus_singleton.hpp" 19 #include "dbus_utility.hpp" 20 #include "http_request.hpp" 21 #include "http_response.hpp" 22 #include "logging.hpp" 23 #include "parsing.hpp" 24 #include "routing.hpp" 25 #include "str_utility.hpp" 26 27 #include <systemd/sd-bus-protocol.h> 28 #include <systemd/sd-bus.h> 29 #include <tinyxml2.h> 30 31 #include <boost/algorithm/string/classification.hpp> 32 #include <boost/algorithm/string/predicate.hpp> 33 #include <boost/beast/http/status.hpp> 34 #include <boost/beast/http/verb.hpp> 35 #include <boost/container/flat_map.hpp> 36 #include <boost/container/vector.hpp> 37 #include <boost/iterator/iterator_facade.hpp> 38 #include <boost/system/error_code.hpp> 39 #include <nlohmann/json.hpp> 40 #include <sdbusplus/asio/connection.hpp> 41 #include <sdbusplus/asio/property.hpp> 42 #include <sdbusplus/exception.hpp> 43 #include <sdbusplus/message.hpp> 44 #include <sdbusplus/message/native_types.hpp> 45 46 #include <algorithm> 47 #include <array> 48 #include <cerrno> 49 #include <cstdint> 50 #include <cstring> 51 #include <filesystem> 52 #include <fstream> 53 #include <functional> 54 #include <initializer_list> 55 #include <iterator> 56 #include <limits> 57 #include <map> 58 #include <memory> 59 #include <regex> 60 #include <string> 61 #include <string_view> 62 #include <type_traits> 63 #include <utility> 64 #include <variant> 65 #include <vector> 66 67 // IWYU pragma: no_include <boost/algorithm/string/detail/classification.hpp> 68 // IWYU pragma: no_include <boost/system/detail/error_code.hpp> 69 // IWYU pragma: no_include <boost/system/detail/error_category.hpp> 70 // IWYU pragma: no_include <errno.h> 71 // IWYU pragma: no_include <string.h> 72 // IWYU pragma: no_include <ext/alloc_traits.h> 73 // IWYU pragma: no_include <exception> 74 // IWYU pragma: no_include <boost/type_index/type_index_facade.hpp> 75 76 namespace crow 77 { 78 namespace openbmc_mapper 79 { 80 const constexpr char* notFoundMsg = "404 Not Found"; 81 const constexpr char* badReqMsg = "400 Bad Request"; 82 const constexpr char* methodNotAllowedMsg = "405 Method Not Allowed"; 83 const constexpr char* forbiddenMsg = "403 Forbidden"; 84 const constexpr char* unsupportedMediaMsg = "415 Unsupported Media Type"; 85 const constexpr char* methodFailedMsg = "500 Method Call Failed"; 86 const constexpr char* methodOutputFailedMsg = "500 Method Output Error"; 87 const constexpr char* notFoundDesc = 88 "org.freedesktop.DBus.Error.FileNotFound: path or object not found"; 89 const constexpr char* propNotFoundDesc = 90 "The specified property cannot be found"; 91 const constexpr char* noJsonDesc = "No JSON object could be decoded"; 92 const constexpr char* invalidContentType = 93 "Content-type header is missing or invalid"; 94 const constexpr char* methodNotFoundDesc = 95 "The specified method cannot be found"; 96 const constexpr char* methodNotAllowedDesc = "Method not allowed"; 97 const constexpr char* forbiddenPropDesc = 98 "The specified property cannot be created"; 99 const constexpr char* forbiddenResDesc = 100 "The specified resource cannot be created"; 101 102 inline bool validateFilename(const std::string& filename) 103 { 104 std::regex validFilename(R"(^[\w\- ]+(\.?[\w\- ]*)$)"); 105 106 return std::regex_match(filename, validFilename); 107 } 108 109 inline void setErrorResponse(crow::Response& res, 110 boost::beast::http::status result, 111 const std::string& desc, std::string_view msg) 112 { 113 res.result(result); 114 res.jsonValue["data"]["description"] = desc; 115 res.jsonValue["message"] = msg; 116 res.jsonValue["status"] = "error"; 117 } 118 119 inline void 120 introspectObjects(const std::string& processName, 121 const std::string& objectPath, 122 const std::shared_ptr<bmcweb::AsyncResp>& transaction) 123 { 124 if (transaction->res.jsonValue.is_null()) 125 { 126 transaction->res.jsonValue["status"] = "ok"; 127 transaction->res.jsonValue["bus_name"] = processName; 128 transaction->res.jsonValue["objects"] = nlohmann::json::array(); 129 } 130 131 crow::connections::systemBus->async_method_call( 132 [transaction, processName{std::string(processName)}, 133 objectPath{std::string(objectPath)}]( 134 const boost::system::error_code& ec, 135 const std::string& introspectXml) { 136 if (ec) 137 { 138 BMCWEB_LOG_ERROR 139 << "Introspect call failed with error: " << ec.message() 140 << " on process: " << processName << " path: " << objectPath 141 << "\n"; 142 return; 143 } 144 nlohmann::json::object_t object; 145 object["path"] = objectPath; 146 147 transaction->res.jsonValue["objects"].push_back(std::move(object)); 148 149 tinyxml2::XMLDocument doc; 150 151 doc.Parse(introspectXml.c_str()); 152 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 153 if (pRoot == nullptr) 154 { 155 BMCWEB_LOG_ERROR << "XML document failed to parse " << processName 156 << " " << objectPath << "\n"; 157 } 158 else 159 { 160 tinyxml2::XMLElement* node = pRoot->FirstChildElement("node"); 161 while (node != nullptr) 162 { 163 const char* childPath = node->Attribute("name"); 164 if (childPath != nullptr) 165 { 166 std::string newpath; 167 if (objectPath != "/") 168 { 169 newpath += objectPath; 170 } 171 newpath += std::string("/") + childPath; 172 // introspect the subobjects as well 173 introspectObjects(processName, newpath, transaction); 174 } 175 176 node = node->NextSiblingElement("node"); 177 } 178 } 179 }, 180 processName, objectPath, "org.freedesktop.DBus.Introspectable", 181 "Introspect"); 182 } 183 184 inline void getPropertiesForEnumerate( 185 const std::string& objectPath, const std::string& service, 186 const std::string& interface, 187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 188 { 189 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " " 190 << service << " " << interface; 191 192 sdbusplus::asio::getAllProperties( 193 *crow::connections::systemBus, service, objectPath, interface, 194 [asyncResp, objectPath, service, 195 interface](const boost::system::error_code& ec, 196 const dbus::utility::DBusPropertiesMap& propertiesList) { 197 if (ec) 198 { 199 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface " 200 << interface << " service " << service 201 << " failed with code " << ec; 202 return; 203 } 204 205 nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 206 nlohmann::json& objectJson = dataJson[objectPath]; 207 if (objectJson.is_null()) 208 { 209 objectJson = nlohmann::json::object(); 210 } 211 212 for (const auto& [name, value] : propertiesList) 213 { 214 nlohmann::json& propertyJson = objectJson[name]; 215 std::visit( 216 [&propertyJson](auto&& val) { 217 if constexpr (std::is_same_v<std::decay_t<decltype(val)>, 218 sdbusplus::message::unix_fd>) 219 { 220 propertyJson = val.fd; 221 } 222 else 223 { 224 225 propertyJson = val; 226 } 227 }, 228 value); 229 } 230 }); 231 } 232 233 // Find any results that weren't picked up by ObjectManagers, to be 234 // called after all ObjectManagers are searched for and called. 235 inline void findRemainingObjectsForEnumerate( 236 const std::string& objectPath, 237 const std::shared_ptr<dbus::utility::MapperGetSubTreeResponse>& subtree, 238 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 239 { 240 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate"; 241 const nlohmann::json& dataJson = asyncResp->res.jsonValue["data"]; 242 243 for (const auto& [path, interface_map] : *subtree) 244 { 245 if (path == objectPath) 246 { 247 // An enumerate does not return the target path's properties 248 continue; 249 } 250 if (dataJson.find(path) == dataJson.end()) 251 { 252 for (const auto& [service, interfaces] : interface_map) 253 { 254 for (const auto& interface : interfaces) 255 { 256 if (!interface.starts_with("org.freedesktop.DBus")) 257 { 258 getPropertiesForEnumerate(path, service, interface, 259 asyncResp); 260 } 261 } 262 } 263 } 264 } 265 } 266 267 struct InProgressEnumerateData 268 { 269 InProgressEnumerateData( 270 const std::string& objectPathIn, 271 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 272 objectPath(objectPathIn), 273 asyncResp(asyncRespIn) 274 {} 275 276 ~InProgressEnumerateData() 277 { 278 try 279 { 280 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp); 281 } 282 catch (...) 283 { 284 BMCWEB_LOG_CRITICAL 285 << "findRemainingObjectsForEnumerate threw exception"; 286 } 287 } 288 289 InProgressEnumerateData(const InProgressEnumerateData&) = delete; 290 InProgressEnumerateData(InProgressEnumerateData&&) = delete; 291 InProgressEnumerateData& operator=(const InProgressEnumerateData&) = delete; 292 InProgressEnumerateData& operator=(InProgressEnumerateData&&) = delete; 293 const std::string objectPath; 294 std::shared_ptr<dbus::utility::MapperGetSubTreeResponse> subtree; 295 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 296 }; 297 298 inline void getManagedObjectsForEnumerate( 299 const std::string& objectName, const std::string& objectManagerPath, 300 const std::string& connectionName, 301 const std::shared_ptr<InProgressEnumerateData>& transaction) 302 { 303 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << objectName 304 << " object_manager_path " << objectManagerPath 305 << " connection_name " << connectionName; 306 crow::connections::systemBus->async_method_call( 307 [transaction, objectName, 308 connectionName](const boost::system::error_code& ec, 309 const dbus::utility::ManagedObjectType& objects) { 310 if (ec) 311 { 312 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << objectName 313 << " on connection " << connectionName 314 << " failed with code " << ec; 315 return; 316 } 317 318 nlohmann::json& dataJson = 319 transaction->asyncResp->res.jsonValue["data"]; 320 321 for (const auto& objectPath : objects) 322 { 323 if (objectPath.first.str.starts_with(objectName)) 324 { 325 BMCWEB_LOG_DEBUG << "Reading object " << objectPath.first.str; 326 nlohmann::json& objectJson = dataJson[objectPath.first.str]; 327 if (objectJson.is_null()) 328 { 329 objectJson = nlohmann::json::object(); 330 } 331 for (const auto& interface : objectPath.second) 332 { 333 for (const auto& property : interface.second) 334 { 335 nlohmann::json& propertyJson = 336 objectJson[property.first]; 337 std::visit( 338 [&propertyJson](auto&& val) { 339 if constexpr (std::is_same_v< 340 std::decay_t<decltype(val)>, 341 sdbusplus::message::unix_fd>) 342 { 343 propertyJson = val.fd; 344 } 345 else 346 { 347 348 propertyJson = val; 349 } 350 }, 351 property.second); 352 } 353 } 354 } 355 for (const auto& interface : objectPath.second) 356 { 357 if (interface.first == "org.freedesktop.DBus.ObjectManager") 358 { 359 getManagedObjectsForEnumerate(objectPath.first.str, 360 objectPath.first.str, 361 connectionName, transaction); 362 } 363 } 364 } 365 }, 366 connectionName, objectManagerPath, "org.freedesktop.DBus.ObjectManager", 367 "GetManagedObjects"); 368 } 369 370 inline void findObjectManagerPathForEnumerate( 371 const std::string& objectName, const std::string& connectionName, 372 const std::shared_ptr<InProgressEnumerateData>& transaction) 373 { 374 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << objectName 375 << " on connection:" << connectionName; 376 crow::connections::systemBus->async_method_call( 377 [transaction, objectName, connectionName]( 378 const boost::system::error_code& ec, 379 const dbus::utility::MapperGetAncestorsResponse& objects) { 380 if (ec) 381 { 382 BMCWEB_LOG_ERROR << "GetAncestors on path " << objectName 383 << " failed with code " << ec; 384 return; 385 } 386 387 for (const auto& pathGroup : objects) 388 { 389 for (const auto& connectionGroup : pathGroup.second) 390 { 391 if (connectionGroup.first == connectionName) 392 { 393 // Found the object manager path for this resource. 394 getManagedObjectsForEnumerate(objectName, pathGroup.first, 395 connectionName, transaction); 396 return; 397 } 398 } 399 } 400 }, 401 "xyz.openbmc_project.ObjectMapper", 402 "/xyz/openbmc_project/object_mapper", 403 "xyz.openbmc_project.ObjectMapper", "GetAncestors", objectName, 404 std::array<const char*, 1>{"org.freedesktop.DBus.ObjectManager"}); 405 } 406 407 // Uses GetObject to add the object info about the target /enumerate path to 408 // the results of GetSubTree, as GetSubTree will not return info for the 409 // target path, and then continues on enumerating the rest of the tree. 410 inline void getObjectAndEnumerate( 411 const std::shared_ptr<InProgressEnumerateData>& transaction) 412 { 413 dbus::utility::getDbusObject( 414 transaction->objectPath, {}, 415 [transaction](const boost::system::error_code& ec, 416 const dbus::utility::MapperGetObject& objects) { 417 if (ec) 418 { 419 BMCWEB_LOG_ERROR << "GetObject for path " << transaction->objectPath 420 << " failed with code " << ec; 421 return; 422 } 423 424 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath 425 << " has " << objects.size() << " entries"; 426 if (!objects.empty()) 427 { 428 transaction->subtree->emplace_back(transaction->objectPath, 429 objects); 430 } 431 432 // Map indicating connection name, and the path where the object 433 // manager exists 434 boost::container::flat_map<std::string, std::string> connections; 435 436 for (const auto& object : *(transaction->subtree)) 437 { 438 for (const auto& connection : object.second) 439 { 440 for (const auto& interface : connection.second) 441 { 442 BMCWEB_LOG_DEBUG << connection.first << " has interface " 443 << interface; 444 if (interface == "org.freedesktop.DBus.ObjectManager") 445 { 446 BMCWEB_LOG_DEBUG << "found object manager path " 447 << object.first; 448 connections[connection.first] = object.first; 449 } 450 } 451 } 452 } 453 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections"; 454 455 for (const auto& connection : connections) 456 { 457 // If we already know where the object manager is, we don't 458 // need to search for it, we can call directly in to 459 // getManagedObjects 460 if (!connection.second.empty()) 461 { 462 getManagedObjectsForEnumerate(transaction->objectPath, 463 connection.second, 464 connection.first, transaction); 465 } 466 else 467 { 468 // otherwise we need to find the object manager path 469 // before we can continue 470 findObjectManagerPathForEnumerate( 471 transaction->objectPath, connection.first, transaction); 472 } 473 } 474 }); 475 } 476 477 // Structure for storing data on an in progress action 478 struct InProgressActionData 479 { 480 explicit InProgressActionData(crow::Response& resIn) : res(resIn) 481 {} 482 ~InProgressActionData() 483 { 484 // Methods could have been called across different owners 485 // and interfaces, where some calls failed and some passed. 486 // 487 // The rules for this are: 488 // * if no method was called - error 489 // * if a method failed and none passed - error 490 // (converse: if at least one method passed - OK) 491 // * for the method output: 492 // * if output processing didn't fail, return the data 493 494 // Only deal with method returns if nothing failed earlier 495 if (res.result() == boost::beast::http::status::ok) 496 { 497 if (!methodPassed) 498 { 499 if (!methodFailed) 500 { 501 setErrorResponse(res, boost::beast::http::status::not_found, 502 methodNotFoundDesc, notFoundMsg); 503 } 504 } 505 else 506 { 507 if (outputFailed) 508 { 509 setErrorResponse( 510 res, boost::beast::http::status::internal_server_error, 511 "Method output failure", methodOutputFailedMsg); 512 } 513 else 514 { 515 res.jsonValue["status"] = "ok"; 516 res.jsonValue["message"] = "200 OK"; 517 res.jsonValue["data"] = methodResponse; 518 } 519 } 520 } 521 522 res.end(); 523 } 524 InProgressActionData(const InProgressActionData&) = delete; 525 InProgressActionData(InProgressActionData&&) = delete; 526 InProgressActionData& operator=(const InProgressActionData&) = delete; 527 InProgressActionData& operator=(InProgressActionData&&) = delete; 528 529 void setErrorStatus(const std::string& desc) 530 { 531 setErrorResponse(res, boost::beast::http::status::bad_request, desc, 532 badReqMsg); 533 } 534 crow::Response& res; 535 std::string path; 536 std::string methodName; 537 std::string interfaceName; 538 bool methodPassed = false; 539 bool methodFailed = false; 540 bool outputFailed = false; 541 bool convertedToArray = false; 542 nlohmann::json methodResponse; 543 nlohmann::json arguments; 544 }; 545 546 inline std::vector<std::string> dbusArgSplit(const std::string& string) 547 { 548 std::vector<std::string> ret; 549 if (string.empty()) 550 { 551 return ret; 552 } 553 ret.emplace_back(""); 554 int containerDepth = 0; 555 556 for (std::string::const_iterator character = string.begin(); 557 character != string.end(); character++) 558 { 559 ret.back() += *character; 560 switch (*character) 561 { 562 case ('a'): 563 break; 564 case ('('): 565 case ('{'): 566 containerDepth++; 567 break; 568 case ('}'): 569 case (')'): 570 containerDepth--; 571 if (containerDepth == 0) 572 { 573 if (character + 1 != string.end()) 574 { 575 ret.emplace_back(""); 576 } 577 } 578 break; 579 default: 580 if (containerDepth == 0) 581 { 582 if (character + 1 != string.end()) 583 { 584 ret.emplace_back(""); 585 } 586 } 587 break; 588 } 589 } 590 591 return ret; 592 } 593 594 inline int convertJsonToDbus(sd_bus_message* m, const std::string& argType, 595 const nlohmann::json& inputJson) 596 { 597 int r = 0; 598 BMCWEB_LOG_DEBUG << "Converting " 599 << inputJson.dump(2, ' ', true, 600 nlohmann::json::error_handler_t::replace) 601 << " to type: " << argType; 602 const std::vector<std::string> argTypes = dbusArgSplit(argType); 603 604 // Assume a single object for now. 605 const nlohmann::json* j = &inputJson; 606 nlohmann::json::const_iterator jIt = inputJson.begin(); 607 608 for (const std::string& argCode : argTypes) 609 { 610 // If we are decoding multiple objects, grab the pointer to the 611 // iterator, and increment it for the next loop 612 if (argTypes.size() > 1) 613 { 614 if (jIt == inputJson.end()) 615 { 616 return -2; 617 } 618 j = &*jIt; 619 jIt++; 620 } 621 const int64_t* intValue = j->get_ptr<const int64_t*>(); 622 const std::string* stringValue = j->get_ptr<const std::string*>(); 623 const double* doubleValue = j->get_ptr<const double*>(); 624 const bool* b = j->get_ptr<const bool*>(); 625 int64_t v = 0; 626 double d = 0.0; 627 628 // Do some basic type conversions that make sense. uint can be 629 // converted to int. int and uint can be converted to double 630 if (intValue == nullptr) 631 { 632 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 633 if (uintValue != nullptr) 634 { 635 v = static_cast<int64_t>(*uintValue); 636 intValue = &v; 637 } 638 } 639 if (doubleValue == nullptr) 640 { 641 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 642 if (uintValue != nullptr) 643 { 644 d = static_cast<double>(*uintValue); 645 doubleValue = &d; 646 } 647 } 648 if (doubleValue == nullptr) 649 { 650 if (intValue != nullptr) 651 { 652 d = static_cast<double>(*intValue); 653 doubleValue = &d; 654 } 655 } 656 657 if (argCode == "s") 658 { 659 if (stringValue == nullptr) 660 { 661 return -1; 662 } 663 r = sd_bus_message_append_basic( 664 m, argCode[0], static_cast<const void*>(stringValue->data())); 665 if (r < 0) 666 { 667 return r; 668 } 669 } 670 else if (argCode == "i") 671 { 672 if (intValue == nullptr) 673 { 674 return -1; 675 } 676 if ((*intValue < std::numeric_limits<int32_t>::lowest()) || 677 (*intValue > std::numeric_limits<int32_t>::max())) 678 { 679 return -ERANGE; 680 } 681 int32_t i = static_cast<int32_t>(*intValue); 682 r = sd_bus_message_append_basic(m, argCode[0], &i); 683 if (r < 0) 684 { 685 return r; 686 } 687 } 688 else if (argCode == "b") 689 { 690 // lots of ways bool could be represented here. Try them all 691 int boolInt = 0; 692 if (intValue != nullptr) 693 { 694 if (*intValue == 1) 695 { 696 boolInt = 1; 697 } 698 else if (*intValue == 0) 699 { 700 boolInt = 0; 701 } 702 else 703 { 704 return -ERANGE; 705 } 706 } 707 else if (b != nullptr) 708 { 709 boolInt = *b ? 1 : 0; 710 } 711 else if (stringValue != nullptr) 712 { 713 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0; 714 } 715 else 716 { 717 return -1; 718 } 719 r = sd_bus_message_append_basic(m, argCode[0], &boolInt); 720 if (r < 0) 721 { 722 return r; 723 } 724 } 725 else if (argCode == "n") 726 { 727 if (intValue == nullptr) 728 { 729 return -1; 730 } 731 if ((*intValue < std::numeric_limits<int16_t>::lowest()) || 732 (*intValue > std::numeric_limits<int16_t>::max())) 733 { 734 return -ERANGE; 735 } 736 int16_t n = static_cast<int16_t>(*intValue); 737 r = sd_bus_message_append_basic(m, argCode[0], &n); 738 if (r < 0) 739 { 740 return r; 741 } 742 } 743 else if (argCode == "x") 744 { 745 if (intValue == nullptr) 746 { 747 return -1; 748 } 749 r = sd_bus_message_append_basic(m, argCode[0], intValue); 750 if (r < 0) 751 { 752 return r; 753 } 754 } 755 else if (argCode == "y") 756 { 757 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 758 if (uintValue == nullptr) 759 { 760 return -1; 761 } 762 if (*uintValue > std::numeric_limits<uint8_t>::max()) 763 { 764 return -ERANGE; 765 } 766 uint8_t y = static_cast<uint8_t>(*uintValue); 767 r = sd_bus_message_append_basic(m, argCode[0], &y); 768 } 769 else if (argCode == "q") 770 { 771 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 772 if (uintValue == nullptr) 773 { 774 return -1; 775 } 776 if (*uintValue > std::numeric_limits<uint16_t>::max()) 777 { 778 return -ERANGE; 779 } 780 uint16_t q = static_cast<uint16_t>(*uintValue); 781 r = sd_bus_message_append_basic(m, argCode[0], &q); 782 } 783 else if (argCode == "u") 784 { 785 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 786 if (uintValue == nullptr) 787 { 788 return -1; 789 } 790 if (*uintValue > std::numeric_limits<uint32_t>::max()) 791 { 792 return -ERANGE; 793 } 794 uint32_t u = static_cast<uint32_t>(*uintValue); 795 r = sd_bus_message_append_basic(m, argCode[0], &u); 796 } 797 else if (argCode == "t") 798 { 799 const uint64_t* uintValue = j->get_ptr<const uint64_t*>(); 800 if (uintValue == nullptr) 801 { 802 return -1; 803 } 804 r = sd_bus_message_append_basic(m, argCode[0], uintValue); 805 } 806 else if (argCode == "d") 807 { 808 if (doubleValue == nullptr) 809 { 810 return -1; 811 } 812 if ((*doubleValue < std::numeric_limits<double>::lowest()) || 813 (*doubleValue > std::numeric_limits<double>::max())) 814 { 815 return -ERANGE; 816 } 817 sd_bus_message_append_basic(m, argCode[0], doubleValue); 818 } 819 else if (argCode.starts_with("a")) 820 { 821 std::string containedType = argCode.substr(1); 822 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, 823 containedType.c_str()); 824 if (r < 0) 825 { 826 return r; 827 } 828 829 for (const auto& it : *j) 830 { 831 r = convertJsonToDbus(m, containedType, it); 832 if (r < 0) 833 { 834 return r; 835 } 836 } 837 sd_bus_message_close_container(m); 838 } 839 else if (argCode.starts_with("v")) 840 { 841 std::string containedType = argCode.substr(1); 842 BMCWEB_LOG_DEBUG << "variant type: " << argCode 843 << " appending variant of type: " << containedType; 844 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, 845 containedType.c_str()); 846 if (r < 0) 847 { 848 return r; 849 } 850 851 r = convertJsonToDbus(m, containedType, inputJson); 852 if (r < 0) 853 { 854 return r; 855 } 856 857 r = sd_bus_message_close_container(m); 858 if (r < 0) 859 { 860 return r; 861 } 862 } 863 else if (argCode.starts_with("(") && argCode.ends_with(")")) 864 { 865 std::string containedType = argCode.substr(1, argCode.size() - 1); 866 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, 867 containedType.c_str()); 868 if (r < 0) 869 { 870 return r; 871 } 872 873 nlohmann::json::const_iterator it = j->begin(); 874 for (const std::string& argCode2 : dbusArgSplit(argType)) 875 { 876 if (it == j->end()) 877 { 878 return -1; 879 } 880 r = convertJsonToDbus(m, argCode2, *it); 881 if (r < 0) 882 { 883 return r; 884 } 885 it++; 886 } 887 r = sd_bus_message_close_container(m); 888 } 889 else if (argCode.starts_with("{") && argCode.ends_with("}")) 890 { 891 std::string containedType = argCode.substr(1, argCode.size() - 1); 892 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, 893 containedType.c_str()); 894 if (r < 0) 895 { 896 return r; 897 } 898 899 std::vector<std::string> codes = dbusArgSplit(containedType); 900 if (codes.size() != 2) 901 { 902 return -1; 903 } 904 const std::string& keyType = codes[0]; 905 const std::string& valueType = codes[1]; 906 for (const auto& it : j->items()) 907 { 908 r = convertJsonToDbus(m, keyType, it.key()); 909 if (r < 0) 910 { 911 return r; 912 } 913 914 r = convertJsonToDbus(m, valueType, it.value()); 915 if (r < 0) 916 { 917 return r; 918 } 919 } 920 r = sd_bus_message_close_container(m); 921 } 922 else 923 { 924 return -2; 925 } 926 if (r < 0) 927 { 928 return r; 929 } 930 931 if (argTypes.size() > 1) 932 { 933 jIt++; 934 } 935 } 936 937 return r; 938 } 939 940 template <typename T> 941 int readMessageItem(const std::string& typeCode, sdbusplus::message_t& m, 942 nlohmann::json& data) 943 { 944 T value; 945 946 int r = sd_bus_message_read_basic(m.get(), typeCode.front(), &value); 947 if (r < 0) 948 { 949 BMCWEB_LOG_ERROR << "sd_bus_message_read_basic on type " << typeCode 950 << " failed!"; 951 return r; 952 } 953 954 data = value; 955 return 0; 956 } 957 958 int convertDBusToJSON(const std::string& returnType, sdbusplus::message_t& m, 959 nlohmann::json& response); 960 961 inline int readDictEntryFromMessage(const std::string& typeCode, 962 sdbusplus::message_t& m, 963 nlohmann::json& object) 964 { 965 std::vector<std::string> types = dbusArgSplit(typeCode); 966 if (types.size() != 2) 967 { 968 BMCWEB_LOG_ERROR << "wrong number contained types in dictionary: " 969 << types.size(); 970 return -1; 971 } 972 973 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_DICT_ENTRY, 974 typeCode.c_str()); 975 if (r < 0) 976 { 977 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container with rc " << r; 978 return r; 979 } 980 981 nlohmann::json key; 982 r = convertDBusToJSON(types[0], m, key); 983 if (r < 0) 984 { 985 return r; 986 } 987 988 const std::string* keyPtr = key.get_ptr<const std::string*>(); 989 if (keyPtr == nullptr) 990 { 991 // json doesn't support non-string keys. If we hit this condition, 992 // convert the result to a string so we can proceed 993 key = key.dump(2, ' ', true, nlohmann::json::error_handler_t::replace); 994 keyPtr = key.get_ptr<const std::string*>(); 995 // in theory this can't fail now, but lets be paranoid about it 996 // anyway 997 if (keyPtr == nullptr) 998 { 999 return -1; 1000 } 1001 } 1002 nlohmann::json& value = object[*keyPtr]; 1003 1004 r = convertDBusToJSON(types[1], m, value); 1005 if (r < 0) 1006 { 1007 return r; 1008 } 1009 1010 r = sd_bus_message_exit_container(m.get()); 1011 if (r < 0) 1012 { 1013 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1014 return r; 1015 } 1016 1017 return 0; 1018 } 1019 1020 inline int readArrayFromMessage(const std::string& typeCode, 1021 sdbusplus::message_t& m, nlohmann::json& data) 1022 { 1023 if (typeCode.size() < 2) 1024 { 1025 BMCWEB_LOG_ERROR << "Type code " << typeCode 1026 << " too small for an array"; 1027 return -1; 1028 } 1029 1030 std::string containedType = typeCode.substr(1); 1031 1032 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_ARRAY, 1033 containedType.c_str()); 1034 if (r < 0) 1035 { 1036 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1037 << r; 1038 return r; 1039 } 1040 1041 bool dict = containedType.starts_with("{") && containedType.ends_with("}"); 1042 1043 if (dict) 1044 { 1045 // Remove the { } 1046 containedType = containedType.substr(1, containedType.size() - 2); 1047 data = nlohmann::json::object(); 1048 } 1049 else 1050 { 1051 data = nlohmann::json::array(); 1052 } 1053 1054 while (true) 1055 { 1056 r = sd_bus_message_at_end(m.get(), 0); 1057 if (r < 0) 1058 { 1059 BMCWEB_LOG_ERROR << "sd_bus_message_at_end failed"; 1060 return r; 1061 } 1062 1063 if (r > 0) 1064 { 1065 break; 1066 } 1067 1068 // Dictionaries are only ever seen in an array 1069 if (dict) 1070 { 1071 r = readDictEntryFromMessage(containedType, m, data); 1072 if (r < 0) 1073 { 1074 return r; 1075 } 1076 } 1077 else 1078 { 1079 data.push_back(nlohmann::json()); 1080 1081 r = convertDBusToJSON(containedType, m, data.back()); 1082 if (r < 0) 1083 { 1084 return r; 1085 } 1086 } 1087 } 1088 1089 r = sd_bus_message_exit_container(m.get()); 1090 if (r < 0) 1091 { 1092 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1093 return r; 1094 } 1095 1096 return 0; 1097 } 1098 1099 inline int readStructFromMessage(const std::string& typeCode, 1100 sdbusplus::message_t& m, nlohmann::json& data) 1101 { 1102 if (typeCode.size() < 3) 1103 { 1104 BMCWEB_LOG_ERROR << "Type code " << typeCode 1105 << " too small for a struct"; 1106 return -1; 1107 } 1108 1109 std::string containedTypes = typeCode.substr(1, typeCode.size() - 2); 1110 std::vector<std::string> types = dbusArgSplit(containedTypes); 1111 1112 int r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_STRUCT, 1113 containedTypes.c_str()); 1114 if (r < 0) 1115 { 1116 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1117 << r; 1118 return r; 1119 } 1120 1121 for (const std::string& type : types) 1122 { 1123 data.push_back(nlohmann::json()); 1124 r = convertDBusToJSON(type, m, data.back()); 1125 if (r < 0) 1126 { 1127 return r; 1128 } 1129 } 1130 1131 r = sd_bus_message_exit_container(m.get()); 1132 if (r < 0) 1133 { 1134 BMCWEB_LOG_ERROR << "sd_bus_message_exit_container failed"; 1135 return r; 1136 } 1137 return 0; 1138 } 1139 1140 inline int readVariantFromMessage(sdbusplus::message_t& m, nlohmann::json& data) 1141 { 1142 const char* containerType = nullptr; 1143 int r = sd_bus_message_peek_type(m.get(), nullptr, &containerType); 1144 if (r < 0) 1145 { 1146 BMCWEB_LOG_ERROR << "sd_bus_message_peek_type failed"; 1147 return r; 1148 } 1149 1150 r = sd_bus_message_enter_container(m.get(), SD_BUS_TYPE_VARIANT, 1151 containerType); 1152 if (r < 0) 1153 { 1154 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed with rc " 1155 << r; 1156 return r; 1157 } 1158 1159 r = convertDBusToJSON(containerType, m, data); 1160 if (r < 0) 1161 { 1162 return r; 1163 } 1164 1165 r = sd_bus_message_exit_container(m.get()); 1166 if (r < 0) 1167 { 1168 BMCWEB_LOG_ERROR << "sd_bus_message_enter_container failed"; 1169 return r; 1170 } 1171 1172 return 0; 1173 } 1174 1175 inline int convertDBusToJSON(const std::string& returnType, 1176 sdbusplus::message_t& m, nlohmann::json& response) 1177 { 1178 int r = 0; 1179 const std::vector<std::string> returnTypes = dbusArgSplit(returnType); 1180 1181 for (const std::string& typeCode : returnTypes) 1182 { 1183 nlohmann::json* thisElement = &response; 1184 if (returnTypes.size() > 1) 1185 { 1186 response.push_back(nlohmann::json{}); 1187 thisElement = &response.back(); 1188 } 1189 1190 if (typeCode == "s" || typeCode == "g" || typeCode == "o") 1191 { 1192 r = readMessageItem<char*>(typeCode, m, *thisElement); 1193 if (r < 0) 1194 { 1195 return r; 1196 } 1197 } 1198 else if (typeCode == "b") 1199 { 1200 r = readMessageItem<int>(typeCode, m, *thisElement); 1201 if (r < 0) 1202 { 1203 return r; 1204 } 1205 1206 *thisElement = static_cast<bool>(thisElement->get<int>()); 1207 } 1208 else if (typeCode == "u") 1209 { 1210 r = readMessageItem<uint32_t>(typeCode, m, *thisElement); 1211 if (r < 0) 1212 { 1213 return r; 1214 } 1215 } 1216 else if (typeCode == "i") 1217 { 1218 r = readMessageItem<int32_t>(typeCode, m, *thisElement); 1219 if (r < 0) 1220 { 1221 return r; 1222 } 1223 } 1224 else if (typeCode == "x") 1225 { 1226 r = readMessageItem<int64_t>(typeCode, m, *thisElement); 1227 if (r < 0) 1228 { 1229 return r; 1230 } 1231 } 1232 else if (typeCode == "t") 1233 { 1234 r = readMessageItem<uint64_t>(typeCode, m, *thisElement); 1235 if (r < 0) 1236 { 1237 return r; 1238 } 1239 } 1240 else if (typeCode == "n") 1241 { 1242 r = readMessageItem<int16_t>(typeCode, m, *thisElement); 1243 if (r < 0) 1244 { 1245 return r; 1246 } 1247 } 1248 else if (typeCode == "q") 1249 { 1250 r = readMessageItem<uint16_t>(typeCode, m, *thisElement); 1251 if (r < 0) 1252 { 1253 return r; 1254 } 1255 } 1256 else if (typeCode == "y") 1257 { 1258 r = readMessageItem<uint8_t>(typeCode, m, *thisElement); 1259 if (r < 0) 1260 { 1261 return r; 1262 } 1263 } 1264 else if (typeCode == "d") 1265 { 1266 r = readMessageItem<double>(typeCode, m, *thisElement); 1267 if (r < 0) 1268 { 1269 return r; 1270 } 1271 } 1272 else if (typeCode == "h") 1273 { 1274 r = readMessageItem<int>(typeCode, m, *thisElement); 1275 if (r < 0) 1276 { 1277 return r; 1278 } 1279 } 1280 else if (typeCode.starts_with("a")) 1281 { 1282 r = readArrayFromMessage(typeCode, m, *thisElement); 1283 if (r < 0) 1284 { 1285 return r; 1286 } 1287 } 1288 else if (typeCode.starts_with("(") && typeCode.ends_with(")")) 1289 { 1290 r = readStructFromMessage(typeCode, m, *thisElement); 1291 if (r < 0) 1292 { 1293 return r; 1294 } 1295 } 1296 else if (typeCode.starts_with("v")) 1297 { 1298 r = readVariantFromMessage(m, *thisElement); 1299 if (r < 0) 1300 { 1301 return r; 1302 } 1303 } 1304 else 1305 { 1306 BMCWEB_LOG_ERROR << "Invalid D-Bus signature type " << typeCode; 1307 return -2; 1308 } 1309 } 1310 1311 return 0; 1312 } 1313 1314 inline void handleMethodResponse( 1315 const std::shared_ptr<InProgressActionData>& transaction, 1316 sdbusplus::message_t& m, const std::string& returnType) 1317 { 1318 nlohmann::json data; 1319 1320 int r = convertDBusToJSON(returnType, m, data); 1321 if (r < 0) 1322 { 1323 transaction->outputFailed = true; 1324 return; 1325 } 1326 1327 if (data.is_null()) 1328 { 1329 return; 1330 } 1331 1332 if (transaction->methodResponse.is_null()) 1333 { 1334 transaction->methodResponse = std::move(data); 1335 return; 1336 } 1337 1338 // If they're both dictionaries or arrays, merge into one. 1339 // Otherwise, make the results an array with every result 1340 // an entry. Could also just fail in that case, but it 1341 // seems better to get the data back somehow. 1342 1343 if (transaction->methodResponse.is_object() && data.is_object()) 1344 { 1345 for (const auto& obj : data.items()) 1346 { 1347 // Note: Will overwrite the data for a duplicate key 1348 transaction->methodResponse.emplace(obj.key(), 1349 std::move(obj.value())); 1350 } 1351 return; 1352 } 1353 1354 if (transaction->methodResponse.is_array() && data.is_array()) 1355 { 1356 for (auto& obj : data) 1357 { 1358 transaction->methodResponse.push_back(std::move(obj)); 1359 } 1360 return; 1361 } 1362 1363 if (!transaction->convertedToArray) 1364 { 1365 // They are different types. May as well turn them into an array 1366 nlohmann::json j = std::move(transaction->methodResponse); 1367 transaction->methodResponse = nlohmann::json::array(); 1368 transaction->methodResponse.push_back(std::move(j)); 1369 transaction->methodResponse.push_back(std::move(data)); 1370 transaction->convertedToArray = true; 1371 } 1372 else 1373 { 1374 transaction->methodResponse.push_back(std::move(data)); 1375 } 1376 } 1377 1378 inline void findActionOnInterface( 1379 const std::shared_ptr<InProgressActionData>& transaction, 1380 const std::string& connectionName) 1381 { 1382 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " 1383 << connectionName; 1384 crow::connections::systemBus->async_method_call( 1385 [transaction, connectionName{std::string(connectionName)}]( 1386 const boost::system::error_code& ec, 1387 const std::string& introspectXml) { 1388 BMCWEB_LOG_DEBUG << "got xml:\n " << introspectXml; 1389 if (ec) 1390 { 1391 BMCWEB_LOG_ERROR 1392 << "Introspect call failed with error: " << ec.message() 1393 << " on process: " << connectionName << "\n"; 1394 return; 1395 } 1396 tinyxml2::XMLDocument doc; 1397 1398 doc.Parse(introspectXml.data(), introspectXml.size()); 1399 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1400 if (pRoot == nullptr) 1401 { 1402 BMCWEB_LOG_ERROR << "XML document failed to parse " 1403 << connectionName << "\n"; 1404 return; 1405 } 1406 tinyxml2::XMLElement* interfaceNode = 1407 pRoot->FirstChildElement("interface"); 1408 while (interfaceNode != nullptr) 1409 { 1410 const char* thisInterfaceName = interfaceNode->Attribute("name"); 1411 if (thisInterfaceName != nullptr) 1412 { 1413 if (!transaction->interfaceName.empty() && 1414 (transaction->interfaceName != thisInterfaceName)) 1415 { 1416 interfaceNode = 1417 interfaceNode->NextSiblingElement("interface"); 1418 continue; 1419 } 1420 1421 tinyxml2::XMLElement* methodNode = 1422 interfaceNode->FirstChildElement("method"); 1423 while (methodNode != nullptr) 1424 { 1425 const char* thisMethodName = methodNode->Attribute("name"); 1426 BMCWEB_LOG_DEBUG << "Found method: " << thisMethodName; 1427 if (thisMethodName != nullptr && 1428 thisMethodName == transaction->methodName) 1429 { 1430 BMCWEB_LOG_DEBUG << "Found method named " 1431 << thisMethodName << " on interface " 1432 << thisInterfaceName; 1433 sdbusplus::message_t m = 1434 crow::connections::systemBus->new_method_call( 1435 connectionName.c_str(), 1436 transaction->path.c_str(), thisInterfaceName, 1437 transaction->methodName.c_str()); 1438 1439 tinyxml2::XMLElement* argumentNode = 1440 methodNode->FirstChildElement("arg"); 1441 1442 std::string returnType; 1443 1444 // Find the output type 1445 while (argumentNode != nullptr) 1446 { 1447 const char* argDirection = 1448 argumentNode->Attribute("direction"); 1449 const char* argType = 1450 argumentNode->Attribute("type"); 1451 if (argDirection != nullptr && argType != nullptr && 1452 std::string(argDirection) == "out") 1453 { 1454 returnType = argType; 1455 break; 1456 } 1457 argumentNode = 1458 argumentNode->NextSiblingElement("arg"); 1459 } 1460 1461 auto argIt = transaction->arguments.begin(); 1462 1463 argumentNode = methodNode->FirstChildElement("arg"); 1464 1465 while (argumentNode != nullptr) 1466 { 1467 const char* argDirection = 1468 argumentNode->Attribute("direction"); 1469 const char* argType = 1470 argumentNode->Attribute("type"); 1471 if (argDirection != nullptr && argType != nullptr && 1472 std::string(argDirection) == "in") 1473 { 1474 if (argIt == transaction->arguments.end()) 1475 { 1476 transaction->setErrorStatus( 1477 "Invalid method args"); 1478 return; 1479 } 1480 if (convertJsonToDbus(m.get(), 1481 std::string(argType), 1482 *argIt) < 0) 1483 { 1484 transaction->setErrorStatus( 1485 "Invalid method arg type"); 1486 return; 1487 } 1488 1489 argIt++; 1490 } 1491 argumentNode = 1492 argumentNode->NextSiblingElement("arg"); 1493 } 1494 1495 crow::connections::systemBus->async_send( 1496 m, 1497 [transaction, 1498 returnType](const boost::system::error_code& ec2, 1499 sdbusplus::message_t& m2) { 1500 if (ec2) 1501 { 1502 transaction->methodFailed = true; 1503 const sd_bus_error* e = m2.get_error(); 1504 1505 if (e != nullptr) 1506 { 1507 setErrorResponse( 1508 transaction->res, 1509 boost::beast::http::status::bad_request, 1510 e->name, e->message); 1511 } 1512 else 1513 { 1514 setErrorResponse( 1515 transaction->res, 1516 boost::beast::http::status::bad_request, 1517 "Method call failed", methodFailedMsg); 1518 } 1519 return; 1520 } 1521 transaction->methodPassed = true; 1522 1523 handleMethodResponse(transaction, m2, returnType); 1524 }); 1525 break; 1526 } 1527 methodNode = methodNode->NextSiblingElement("method"); 1528 } 1529 } 1530 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1531 } 1532 }, 1533 connectionName, transaction->path, 1534 "org.freedesktop.DBus.Introspectable", "Introspect"); 1535 } 1536 1537 inline void handleAction(const crow::Request& req, 1538 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1539 const std::string& objectPath, 1540 const std::string& methodName) 1541 { 1542 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 1543 << methodName; 1544 nlohmann::json requestDbusData; 1545 1546 JsonParseResult ret = parseRequestAsJson(req, requestDbusData); 1547 if (ret == JsonParseResult::BadContentType) 1548 { 1549 setErrorResponse(asyncResp->res, 1550 boost::beast::http::status::unsupported_media_type, 1551 invalidContentType, unsupportedMediaMsg); 1552 return; 1553 } 1554 if (ret != JsonParseResult::Success) 1555 { 1556 setErrorResponse(asyncResp->res, 1557 boost::beast::http::status::bad_request, noJsonDesc, 1558 badReqMsg); 1559 return; 1560 } 1561 nlohmann::json::iterator data = requestDbusData.find("data"); 1562 if (data == requestDbusData.end()) 1563 { 1564 setErrorResponse(asyncResp->res, 1565 boost::beast::http::status::bad_request, noJsonDesc, 1566 badReqMsg); 1567 return; 1568 } 1569 1570 if (!data->is_array()) 1571 { 1572 setErrorResponse(asyncResp->res, 1573 boost::beast::http::status::bad_request, noJsonDesc, 1574 badReqMsg); 1575 return; 1576 } 1577 auto transaction = std::make_shared<InProgressActionData>(asyncResp->res); 1578 1579 transaction->path = objectPath; 1580 transaction->methodName = methodName; 1581 transaction->arguments = std::move(*data); 1582 dbus::utility::getDbusObject( 1583 objectPath, {}, 1584 [transaction]( 1585 const boost::system::error_code& ec, 1586 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1587 interfaceNames) { 1588 if (ec || interfaceNames.empty()) 1589 { 1590 BMCWEB_LOG_ERROR << "Can't find object"; 1591 setErrorResponse(transaction->res, 1592 boost::beast::http::status::not_found, 1593 notFoundDesc, notFoundMsg); 1594 return; 1595 } 1596 1597 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 1598 << " object(s)"; 1599 1600 for (const std::pair<std::string, std::vector<std::string>>& object : 1601 interfaceNames) 1602 { 1603 findActionOnInterface(transaction, object.first); 1604 } 1605 }); 1606 } 1607 1608 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1609 const std::string& objectPath) 1610 { 1611 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath; 1612 1613 dbus::utility::getDbusObject( 1614 objectPath, {}, 1615 [asyncResp, objectPath]( 1616 const boost::system::error_code& ec, 1617 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1618 interfaceNames) { 1619 if (ec || interfaceNames.empty()) 1620 { 1621 BMCWEB_LOG_ERROR << "Can't find object"; 1622 setErrorResponse(asyncResp->res, 1623 boost::beast::http::status::method_not_allowed, 1624 methodNotAllowedDesc, methodNotAllowedMsg); 1625 return; 1626 } 1627 1628 auto transaction = 1629 std::make_shared<InProgressActionData>(asyncResp->res); 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 = 1680 std::make_shared<InProgressEnumerateData>(objectPath, 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 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