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