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