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 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 nlohmann::json::const_iterator argIt = 1429 transaction->arguments.begin(); 1430 1431 argumentNode = methodNode->FirstChildElement("arg"); 1432 1433 while (argumentNode != nullptr) 1434 { 1435 const char* argDirection = 1436 argumentNode->Attribute("direction"); 1437 const char* argType = 1438 argumentNode->Attribute("type"); 1439 if (argDirection != nullptr && argType != nullptr && 1440 std::string(argDirection) == "in") 1441 { 1442 if (argIt == transaction->arguments.end()) 1443 { 1444 transaction->setErrorStatus( 1445 "Invalid method args"); 1446 return; 1447 } 1448 if (convertJsonToDbus(m.get(), 1449 std::string(argType), 1450 *argIt) < 0) 1451 { 1452 transaction->setErrorStatus( 1453 "Invalid method arg type"); 1454 return; 1455 } 1456 1457 argIt++; 1458 } 1459 argumentNode = 1460 argumentNode->NextSiblingElement("arg"); 1461 } 1462 1463 crow::connections::systemBus->async_send( 1464 m, 1465 [transaction, 1466 returnType](boost::system::error_code ec2, 1467 sdbusplus::message::message& m2) { 1468 if (ec2) 1469 { 1470 transaction->methodFailed = true; 1471 const sd_bus_error* e = m2.get_error(); 1472 1473 if (e != nullptr) 1474 { 1475 setErrorResponse( 1476 transaction->res, 1477 boost::beast::http::status::bad_request, 1478 e->name, e->message); 1479 } 1480 else 1481 { 1482 setErrorResponse( 1483 transaction->res, 1484 boost::beast::http::status::bad_request, 1485 "Method call failed", methodFailedMsg); 1486 } 1487 return; 1488 } 1489 transaction->methodPassed = true; 1490 1491 handleMethodResponse(transaction, m2, returnType); 1492 }); 1493 break; 1494 } 1495 methodNode = methodNode->NextSiblingElement("method"); 1496 } 1497 } 1498 interfaceNode = interfaceNode->NextSiblingElement("interface"); 1499 } 1500 }, 1501 connectionName, transaction->path, 1502 "org.freedesktop.DBus.Introspectable", "Introspect"); 1503 } 1504 1505 inline void handleAction(const crow::Request& req, 1506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1507 const std::string& objectPath, 1508 const std::string& methodName) 1509 { 1510 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 1511 << methodName; 1512 nlohmann::json requestDbusData = 1513 nlohmann::json::parse(req.body, nullptr, false); 1514 1515 if (requestDbusData.is_discarded()) 1516 { 1517 setErrorResponse(asyncResp->res, 1518 boost::beast::http::status::bad_request, noJsonDesc, 1519 badReqMsg); 1520 return; 1521 } 1522 nlohmann::json::iterator data = requestDbusData.find("data"); 1523 if (data == requestDbusData.end()) 1524 { 1525 setErrorResponse(asyncResp->res, 1526 boost::beast::http::status::bad_request, noJsonDesc, 1527 badReqMsg); 1528 return; 1529 } 1530 1531 if (!data->is_array()) 1532 { 1533 setErrorResponse(asyncResp->res, 1534 boost::beast::http::status::bad_request, noJsonDesc, 1535 badReqMsg); 1536 return; 1537 } 1538 auto transaction = std::make_shared<InProgressActionData>(asyncResp->res); 1539 1540 transaction->path = objectPath; 1541 transaction->methodName = methodName; 1542 transaction->arguments = std::move(*data); 1543 crow::connections::systemBus->async_method_call( 1544 [transaction]( 1545 const boost::system::error_code ec, 1546 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1547 interfaceNames) { 1548 if (ec || interfaceNames.empty()) 1549 { 1550 BMCWEB_LOG_ERROR << "Can't find object"; 1551 setErrorResponse(transaction->res, 1552 boost::beast::http::status::not_found, 1553 notFoundDesc, notFoundMsg); 1554 return; 1555 } 1556 1557 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 1558 << " object(s)"; 1559 1560 for (const std::pair<std::string, std::vector<std::string>>& object : 1561 interfaceNames) 1562 { 1563 findActionOnInterface(transaction, object.first); 1564 } 1565 }, 1566 "xyz.openbmc_project.ObjectMapper", 1567 "/xyz/openbmc_project/object_mapper", 1568 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1569 std::array<std::string, 0>()); 1570 } 1571 1572 inline void handleDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1573 const std::string& objectPath) 1574 { 1575 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath; 1576 1577 crow::connections::systemBus->async_method_call( 1578 [asyncResp, objectPath]( 1579 const boost::system::error_code ec, 1580 const std::vector<std::pair<std::string, std::vector<std::string>>>& 1581 interfaceNames) { 1582 if (ec || interfaceNames.empty()) 1583 { 1584 BMCWEB_LOG_ERROR << "Can't find object"; 1585 setErrorResponse(asyncResp->res, 1586 boost::beast::http::status::method_not_allowed, 1587 methodNotAllowedDesc, methodNotAllowedMsg); 1588 return; 1589 } 1590 1591 auto transaction = 1592 std::make_shared<InProgressActionData>(asyncResp->res); 1593 transaction->path = objectPath; 1594 transaction->methodName = "Delete"; 1595 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1596 1597 for (const std::pair<std::string, std::vector<std::string>>& object : 1598 interfaceNames) 1599 { 1600 findActionOnInterface(transaction, object.first); 1601 } 1602 }, 1603 "xyz.openbmc_project.ObjectMapper", 1604 "/xyz/openbmc_project/object_mapper", 1605 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1606 std::array<const char*, 0>()); 1607 } 1608 1609 inline void handleList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1610 const std::string& objectPath, int32_t depth = 0) 1611 { 1612 crow::connections::systemBus->async_method_call( 1613 [asyncResp]( 1614 const boost::system::error_code ec, 1615 const dbus::utility::MapperGetSubTreePathsResponse& objectPaths) { 1616 if (ec) 1617 { 1618 setErrorResponse(asyncResp->res, 1619 boost::beast::http::status::not_found, 1620 notFoundDesc, notFoundMsg); 1621 } 1622 else 1623 { 1624 asyncResp->res.jsonValue["status"] = "ok"; 1625 asyncResp->res.jsonValue["message"] = "200 OK"; 1626 asyncResp->res.jsonValue["data"] = objectPaths; 1627 } 1628 }, 1629 "xyz.openbmc_project.ObjectMapper", 1630 "/xyz/openbmc_project/object_mapper", 1631 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 1632 depth, std::array<std::string, 0>()); 1633 } 1634 1635 inline void handleEnumerate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1636 const std::string& objectPath) 1637 { 1638 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 1639 1640 asyncResp->res.jsonValue["message"] = "200 OK"; 1641 asyncResp->res.jsonValue["status"] = "ok"; 1642 asyncResp->res.jsonValue["data"] = nlohmann::json::object(); 1643 1644 crow::connections::systemBus->async_method_call( 1645 [objectPath, asyncResp]( 1646 const boost::system::error_code ec, 1647 const dbus::utility::MapperGetSubTreeResponse& objectNames) { 1648 auto transaction = 1649 std::make_shared<InProgressEnumerateData>(objectPath, asyncResp); 1650 1651 transaction->subtree = 1652 std::make_shared<dbus::utility::MapperGetSubTreeResponse>( 1653 objectNames); 1654 1655 if (ec) 1656 { 1657 BMCWEB_LOG_ERROR << "GetSubTree failed on " 1658 << transaction->objectPath; 1659 setErrorResponse(transaction->asyncResp->res, 1660 boost::beast::http::status::not_found, 1661 notFoundDesc, notFoundMsg); 1662 return; 1663 } 1664 1665 // Add the data for the path passed in to the results 1666 // as if GetSubTree returned it, and continue on enumerating 1667 getObjectAndEnumerate(transaction); 1668 }, 1669 "xyz.openbmc_project.ObjectMapper", 1670 "/xyz/openbmc_project/object_mapper", 1671 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 0, 1672 std::array<const char*, 0>()); 1673 } 1674 1675 inline void handleGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1676 std::string& objectPath, std::string& destProperty) 1677 { 1678 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1679 std::shared_ptr<std::string> propertyName = 1680 std::make_shared<std::string>(std::move(destProperty)); 1681 1682 std::shared_ptr<std::string> path = 1683 std::make_shared<std::string>(std::move(objectPath)); 1684 1685 crow::connections::systemBus->async_method_call( 1686 [asyncResp, path, 1687 propertyName](const boost::system::error_code ec, 1688 const dbus::utility::MapperGetObject& objectNames) { 1689 if (ec || objectNames.empty()) 1690 { 1691 setErrorResponse(asyncResp->res, 1692 boost::beast::http::status::not_found, 1693 notFoundDesc, notFoundMsg); 1694 return; 1695 } 1696 std::shared_ptr<nlohmann::json> response = 1697 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1698 // The mapper should never give us an empty interface names 1699 // list, but check anyway 1700 for (const std::pair<std::string, std::vector<std::string>>& 1701 connection : objectNames) 1702 { 1703 const std::vector<std::string>& interfaceNames = connection.second; 1704 1705 if (interfaceNames.empty()) 1706 { 1707 setErrorResponse(asyncResp->res, 1708 boost::beast::http::status::not_found, 1709 notFoundDesc, notFoundMsg); 1710 return; 1711 } 1712 1713 for (const std::string& interface : interfaceNames) 1714 { 1715 sdbusplus::message::message m = 1716 crow::connections::systemBus->new_method_call( 1717 connection.first.c_str(), path->c_str(), 1718 "org.freedesktop.DBus.Properties", "GetAll"); 1719 m.append(interface); 1720 crow::connections::systemBus->async_send( 1721 m, [asyncResp, response, 1722 propertyName](const boost::system::error_code ec2, 1723 sdbusplus::message::message& msg) { 1724 if (ec2) 1725 { 1726 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1727 << ec2; 1728 } 1729 else 1730 { 1731 nlohmann::json properties; 1732 int r = convertDBusToJSON("a{sv}", msg, properties); 1733 if (r < 0) 1734 { 1735 BMCWEB_LOG_ERROR << "convertDBusToJSON failed"; 1736 } 1737 else 1738 { 1739 for (auto& prop : properties.items()) 1740 { 1741 // if property name is empty, or 1742 // matches our search query, add it 1743 // to the response json 1744 1745 if (propertyName->empty()) 1746 { 1747 (*response)[prop.key()] = 1748 std::move(prop.value()); 1749 } 1750 else if (prop.key() == *propertyName) 1751 { 1752 *response = std::move(prop.value()); 1753 } 1754 } 1755 } 1756 } 1757 if (response.use_count() == 1) 1758 { 1759 if (!propertyName->empty() && response->empty()) 1760 { 1761 setErrorResponse( 1762 asyncResp->res, 1763 boost::beast::http::status::not_found, 1764 propNotFoundDesc, notFoundMsg); 1765 } 1766 else 1767 { 1768 asyncResp->res.jsonValue["status"] = "ok"; 1769 asyncResp->res.jsonValue["message"] = "200 OK"; 1770 asyncResp->res.jsonValue["data"] = *response; 1771 } 1772 } 1773 }); 1774 } 1775 } 1776 }, 1777 "xyz.openbmc_project.ObjectMapper", 1778 "/xyz/openbmc_project/object_mapper", 1779 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 1780 std::array<std::string, 0>()); 1781 } 1782 1783 struct AsyncPutRequest 1784 { 1785 AsyncPutRequest(const std::shared_ptr<bmcweb::AsyncResp>& resIn) : 1786 asyncResp(resIn) 1787 {} 1788 ~AsyncPutRequest() 1789 { 1790 if (asyncResp->res.jsonValue.empty()) 1791 { 1792 setErrorResponse(asyncResp->res, 1793 boost::beast::http::status::forbidden, 1794 forbiddenMsg, forbiddenPropDesc); 1795 } 1796 } 1797 1798 AsyncPutRequest(const AsyncPutRequest&) = delete; 1799 AsyncPutRequest(AsyncPutRequest&&) = delete; 1800 AsyncPutRequest& operator=(const AsyncPutRequest&) = delete; 1801 AsyncPutRequest& operator=(AsyncPutRequest&&) = delete; 1802 1803 void setErrorStatus(const std::string& desc) 1804 { 1805 setErrorResponse(asyncResp->res, 1806 boost::beast::http::status::internal_server_error, 1807 desc, badReqMsg); 1808 } 1809 1810 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1811 std::string objectPath; 1812 std::string propertyName; 1813 nlohmann::json propertyValue; 1814 }; 1815 1816 inline void handlePut(const crow::Request& req, 1817 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1818 const std::string& objectPath, 1819 const std::string& destProperty) 1820 { 1821 if (destProperty.empty()) 1822 { 1823 setErrorResponse(asyncResp->res, boost::beast::http::status::forbidden, 1824 forbiddenResDesc, forbiddenMsg); 1825 return; 1826 } 1827 1828 nlohmann::json requestDbusData = 1829 nlohmann::json::parse(req.body, nullptr, false); 1830 1831 if (requestDbusData.is_discarded()) 1832 { 1833 setErrorResponse(asyncResp->res, 1834 boost::beast::http::status::bad_request, noJsonDesc, 1835 badReqMsg); 1836 return; 1837 } 1838 1839 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); 1840 if (propertyIt == requestDbusData.end()) 1841 { 1842 setErrorResponse(asyncResp->res, 1843 boost::beast::http::status::bad_request, noJsonDesc, 1844 badReqMsg); 1845 return; 1846 } 1847 const nlohmann::json& propertySetValue = *propertyIt; 1848 auto transaction = std::make_shared<AsyncPutRequest>(asyncResp); 1849 transaction->objectPath = objectPath; 1850 transaction->propertyName = destProperty; 1851 transaction->propertyValue = propertySetValue; 1852 1853 crow::connections::systemBus->async_method_call( 1854 [transaction](const boost::system::error_code ec2, 1855 const dbus::utility::MapperGetObject& objectNames) { 1856 if (!ec2 && objectNames.empty()) 1857 { 1858 setErrorResponse(transaction->asyncResp->res, 1859 boost::beast::http::status::not_found, 1860 propNotFoundDesc, notFoundMsg); 1861 return; 1862 } 1863 1864 for (const std::pair<std::string, std::vector<std::string>>& 1865 connection : objectNames) 1866 { 1867 const std::string& connectionName = connection.first; 1868 1869 crow::connections::systemBus->async_method_call( 1870 [connectionName{std::string(connectionName)}, 1871 transaction](const boost::system::error_code ec3, 1872 const std::string& introspectXml) { 1873 if (ec3) 1874 { 1875 BMCWEB_LOG_ERROR << "Introspect call failed with error: " 1876 << ec3.message() 1877 << " on process: " << connectionName; 1878 transaction->setErrorStatus("Unexpected Error"); 1879 return; 1880 } 1881 tinyxml2::XMLDocument doc; 1882 1883 doc.Parse(introspectXml.c_str()); 1884 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 1885 if (pRoot == nullptr) 1886 { 1887 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1888 << introspectXml; 1889 transaction->setErrorStatus("Unexpected Error"); 1890 return; 1891 } 1892 tinyxml2::XMLElement* ifaceNode = 1893 pRoot->FirstChildElement("interface"); 1894 while (ifaceNode != nullptr) 1895 { 1896 const char* interfaceName = ifaceNode->Attribute("name"); 1897 BMCWEB_LOG_DEBUG << "found interface " << interfaceName; 1898 tinyxml2::XMLElement* propNode = 1899 ifaceNode->FirstChildElement("property"); 1900 while (propNode != nullptr) 1901 { 1902 const char* propertyName = propNode->Attribute("name"); 1903 BMCWEB_LOG_DEBUG << "Found property " << propertyName; 1904 if (propertyName == transaction->propertyName) 1905 { 1906 const char* argType = propNode->Attribute("type"); 1907 if (argType != nullptr) 1908 { 1909 sdbusplus::message::message m = 1910 crow::connections::systemBus 1911 ->new_method_call( 1912 connectionName.c_str(), 1913 transaction->objectPath.c_str(), 1914 "org.freedesktop.DBus." 1915 "Properties", 1916 "Set"); 1917 m.append(interfaceName, 1918 transaction->propertyName); 1919 int r = sd_bus_message_open_container( 1920 m.get(), SD_BUS_TYPE_VARIANT, argType); 1921 if (r < 0) 1922 { 1923 transaction->setErrorStatus( 1924 "Unexpected Error"); 1925 return; 1926 } 1927 r = convertJsonToDbus( 1928 m.get(), argType, 1929 transaction->propertyValue); 1930 if (r < 0) 1931 { 1932 if (r == -ERANGE) 1933 { 1934 transaction->setErrorStatus( 1935 "Provided property value " 1936 "is out of range for the " 1937 "property type"); 1938 } 1939 else 1940 { 1941 transaction->setErrorStatus( 1942 "Invalid arg type"); 1943 } 1944 return; 1945 } 1946 r = sd_bus_message_close_container(m.get()); 1947 if (r < 0) 1948 { 1949 transaction->setErrorStatus( 1950 "Unexpected Error"); 1951 return; 1952 } 1953 crow::connections::systemBus->async_send( 1954 m, 1955 [transaction]( 1956 boost::system::error_code ec, 1957 sdbusplus::message::message& m2) { 1958 BMCWEB_LOG_DEBUG << "sent"; 1959 if (ec) 1960 { 1961 const sd_bus_error* e = m2.get_error(); 1962 setErrorResponse( 1963 transaction->asyncResp->res, 1964 boost::beast::http::status:: 1965 forbidden, 1966 (e) != nullptr 1967 ? e->name 1968 : ec.category().name(), 1969 (e) != nullptr ? e->message 1970 : ec.message()); 1971 } 1972 else 1973 { 1974 transaction->asyncResp->res 1975 .jsonValue["status"] = "ok"; 1976 transaction->asyncResp->res 1977 .jsonValue["message"] = "200 OK"; 1978 transaction->asyncResp->res 1979 .jsonValue["data"] = nullptr; 1980 } 1981 }); 1982 } 1983 } 1984 propNode = propNode->NextSiblingElement("property"); 1985 } 1986 ifaceNode = ifaceNode->NextSiblingElement("interface"); 1987 } 1988 }, 1989 connectionName, transaction->objectPath, 1990 "org.freedesktop.DBus.Introspectable", "Introspect"); 1991 } 1992 }, 1993 "xyz.openbmc_project.ObjectMapper", 1994 "/xyz/openbmc_project/object_mapper", 1995 "xyz.openbmc_project.ObjectMapper", "GetObject", 1996 transaction->objectPath, std::array<std::string, 0>()); 1997 } 1998 1999 inline void handleDBusUrl(const crow::Request& req, 2000 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2001 std::string& objectPath) 2002 { 2003 2004 // If accessing a single attribute, fill in and update objectPath, 2005 // otherwise leave destProperty blank 2006 std::string destProperty; 2007 const char* attrSeperator = "/attr/"; 2008 size_t attrPosition = objectPath.find(attrSeperator); 2009 if (attrPosition != std::string::npos) 2010 { 2011 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 2012 objectPath.length()); 2013 objectPath = objectPath.substr(0, attrPosition); 2014 } 2015 2016 if (req.method() == boost::beast::http::verb::post) 2017 { 2018 constexpr const char* actionSeperator = "/action/"; 2019 size_t actionPosition = objectPath.find(actionSeperator); 2020 if (actionPosition != std::string::npos) 2021 { 2022 std::string postProperty = 2023 objectPath.substr((actionPosition + strlen(actionSeperator)), 2024 objectPath.length()); 2025 objectPath = objectPath.substr(0, actionPosition); 2026 handleAction(req, asyncResp, objectPath, postProperty); 2027 return; 2028 } 2029 } 2030 else if (req.method() == boost::beast::http::verb::get) 2031 { 2032 if (boost::ends_with(objectPath, "/enumerate")) 2033 { 2034 objectPath.erase(objectPath.end() - sizeof("enumerate"), 2035 objectPath.end()); 2036 handleEnumerate(asyncResp, objectPath); 2037 } 2038 else if (boost::ends_with(objectPath, "/list")) 2039 { 2040 objectPath.erase(objectPath.end() - sizeof("list"), 2041 objectPath.end()); 2042 handleList(asyncResp, objectPath); 2043 } 2044 else 2045 { 2046 // Trim any trailing "/" at the end 2047 if (boost::ends_with(objectPath, "/")) 2048 { 2049 objectPath.pop_back(); 2050 handleList(asyncResp, objectPath, 1); 2051 } 2052 else 2053 { 2054 handleGet(asyncResp, objectPath, destProperty); 2055 } 2056 } 2057 return; 2058 } 2059 else if (req.method() == boost::beast::http::verb::put) 2060 { 2061 handlePut(req, asyncResp, objectPath, destProperty); 2062 return; 2063 } 2064 else if (req.method() == boost::beast::http::verb::delete_) 2065 { 2066 handleDelete(asyncResp, objectPath); 2067 return; 2068 } 2069 2070 setErrorResponse(asyncResp->res, 2071 boost::beast::http::status::method_not_allowed, 2072 methodNotAllowedDesc, methodNotAllowedMsg); 2073 } 2074 2075 inline void 2076 handleBusSystemPost(const crow::Request& req, 2077 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2078 const std::string& processName, 2079 const std::string& requestedPath) 2080 { 2081 std::vector<std::string> strs; 2082 boost::split(strs, requestedPath, boost::is_any_of("/")); 2083 std::string objectPath; 2084 std::string interfaceName; 2085 std::string methodName; 2086 auto it = strs.begin(); 2087 if (it == strs.end()) 2088 { 2089 objectPath = "/"; 2090 } 2091 while (it != strs.end()) 2092 { 2093 // Check if segment contains ".". If it does, it must be an 2094 // interface 2095 if (it->find(".") != std::string::npos) 2096 { 2097 break; 2098 // This check is necessary as the trailing slash gets 2099 // parsed as part of our <path> specifier above, which 2100 // causes the normal trailing backslash redirector to 2101 // fail. 2102 } 2103 if (!it->empty()) 2104 { 2105 objectPath += "/" + *it; 2106 } 2107 it++; 2108 } 2109 if (it != strs.end()) 2110 { 2111 interfaceName = *it; 2112 it++; 2113 2114 // after interface, we might have a method name 2115 if (it != strs.end()) 2116 { 2117 methodName = *it; 2118 it++; 2119 } 2120 } 2121 if (it != strs.end()) 2122 { 2123 // if there is more levels past the method name, something 2124 // went wrong, return not found 2125 asyncResp->res.result(boost::beast::http::status::not_found); 2126 return; 2127 } 2128 if (interfaceName.empty()) 2129 { 2130 crow::connections::systemBus->async_method_call( 2131 [asyncResp, processName, 2132 objectPath](const boost::system::error_code ec, 2133 const std::string& introspectXml) { 2134 if (ec) 2135 { 2136 BMCWEB_LOG_ERROR 2137 << "Introspect call failed with error: " << ec.message() 2138 << " on process: " << processName << " path: " << objectPath 2139 << "\n"; 2140 return; 2141 } 2142 tinyxml2::XMLDocument doc; 2143 2144 doc.Parse(introspectXml.c_str()); 2145 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2146 if (pRoot == nullptr) 2147 { 2148 BMCWEB_LOG_ERROR << "XML document failed to parse " 2149 << processName << " " << objectPath << "\n"; 2150 asyncResp->res.jsonValue["status"] = "XML parse error"; 2151 asyncResp->res.result( 2152 boost::beast::http::status::internal_server_error); 2153 return; 2154 } 2155 2156 BMCWEB_LOG_DEBUG << introspectXml; 2157 asyncResp->res.jsonValue["status"] = "ok"; 2158 asyncResp->res.jsonValue["bus_name"] = processName; 2159 asyncResp->res.jsonValue["object_path"] = objectPath; 2160 2161 nlohmann::json& interfacesArray = 2162 asyncResp->res.jsonValue["interfaces"]; 2163 interfacesArray = nlohmann::json::array(); 2164 tinyxml2::XMLElement* interface = 2165 pRoot->FirstChildElement("interface"); 2166 2167 while (interface != nullptr) 2168 { 2169 const char* ifaceName = interface->Attribute("name"); 2170 if (ifaceName != nullptr) 2171 { 2172 nlohmann::json::object_t interface; 2173 interface["name"] = ifaceName; 2174 interfacesArray.push_back(std::move(interface)); 2175 } 2176 2177 interface = interface->NextSiblingElement("interface"); 2178 } 2179 }, 2180 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2181 "Introspect"); 2182 } 2183 else if (methodName.empty()) 2184 { 2185 crow::connections::systemBus->async_method_call( 2186 [asyncResp, processName, objectPath, 2187 interfaceName](const boost::system::error_code ec, 2188 const std::string& introspectXml) { 2189 if (ec) 2190 { 2191 BMCWEB_LOG_ERROR 2192 << "Introspect call failed with error: " << ec.message() 2193 << " on process: " << processName << " path: " << objectPath 2194 << "\n"; 2195 return; 2196 } 2197 tinyxml2::XMLDocument doc; 2198 2199 doc.Parse(introspectXml.data(), introspectXml.size()); 2200 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node"); 2201 if (pRoot == nullptr) 2202 { 2203 BMCWEB_LOG_ERROR << "XML document failed to parse " 2204 << processName << " " << objectPath << "\n"; 2205 asyncResp->res.result( 2206 boost::beast::http::status::internal_server_error); 2207 return; 2208 } 2209 2210 asyncResp->res.jsonValue["status"] = "ok"; 2211 asyncResp->res.jsonValue["bus_name"] = processName; 2212 asyncResp->res.jsonValue["interface"] = interfaceName; 2213 asyncResp->res.jsonValue["object_path"] = objectPath; 2214 2215 nlohmann::json& methodsArray = asyncResp->res.jsonValue["methods"]; 2216 methodsArray = nlohmann::json::array(); 2217 2218 nlohmann::json& signalsArray = asyncResp->res.jsonValue["signals"]; 2219 signalsArray = nlohmann::json::array(); 2220 2221 nlohmann::json& propertiesObj = 2222 asyncResp->res.jsonValue["properties"]; 2223 propertiesObj = nlohmann::json::object(); 2224 2225 // if we know we're the only call, build the 2226 // json directly 2227 tinyxml2::XMLElement* interface = 2228 pRoot->FirstChildElement("interface"); 2229 while (interface != nullptr) 2230 { 2231 const char* ifaceName = interface->Attribute("name"); 2232 2233 if (ifaceName != nullptr && ifaceName == interfaceName) 2234 { 2235 break; 2236 } 2237 2238 interface = interface->NextSiblingElement("interface"); 2239 } 2240 if (interface == nullptr) 2241 { 2242 // if we got to the end of the list and 2243 // never found a match, throw 404 2244 asyncResp->res.result(boost::beast::http::status::not_found); 2245 return; 2246 } 2247 2248 tinyxml2::XMLElement* methods = 2249 interface->FirstChildElement("method"); 2250 while (methods != nullptr) 2251 { 2252 nlohmann::json argsArray = nlohmann::json::array(); 2253 tinyxml2::XMLElement* arg = methods->FirstChildElement("arg"); 2254 while (arg != nullptr) 2255 { 2256 nlohmann::json thisArg; 2257 for (const char* fieldName : std::array<const char*, 3>{ 2258 "name", "direction", "type"}) 2259 { 2260 const char* fieldValue = arg->Attribute(fieldName); 2261 if (fieldValue != nullptr) 2262 { 2263 thisArg[fieldName] = fieldValue; 2264 } 2265 } 2266 argsArray.push_back(std::move(thisArg)); 2267 arg = arg->NextSiblingElement("arg"); 2268 } 2269 2270 const char* name = methods->Attribute("name"); 2271 if (name != nullptr) 2272 { 2273 std::string uri; 2274 uri.reserve(14 + processName.size() + objectPath.size() + 2275 interfaceName.size() + strlen(name)); 2276 uri += "/bus/system/"; 2277 uri += processName; 2278 uri += objectPath; 2279 uri += "/"; 2280 uri += interfaceName; 2281 uri += "/"; 2282 uri += name; 2283 2284 nlohmann::json::object_t object; 2285 object["name"] = name; 2286 object["uri"] = std::move(uri); 2287 object["args"] = argsArray; 2288 2289 methodsArray.push_back(std::move(object)); 2290 } 2291 methods = methods->NextSiblingElement("method"); 2292 } 2293 tinyxml2::XMLElement* signals = 2294 interface->FirstChildElement("signal"); 2295 while (signals != nullptr) 2296 { 2297 nlohmann::json argsArray = nlohmann::json::array(); 2298 2299 tinyxml2::XMLElement* arg = signals->FirstChildElement("arg"); 2300 while (arg != nullptr) 2301 { 2302 const char* name = arg->Attribute("name"); 2303 const char* type = arg->Attribute("type"); 2304 if (name != nullptr && type != nullptr) 2305 { 2306 argsArray.push_back({ 2307 {"name", name}, 2308 {"type", type}, 2309 }); 2310 } 2311 arg = arg->NextSiblingElement("arg"); 2312 } 2313 const char* name = signals->Attribute("name"); 2314 if (name != nullptr) 2315 { 2316 nlohmann::json::object_t object; 2317 object["name"] = name; 2318 object["args"] = argsArray; 2319 signalsArray.push_back(std::move(object)); 2320 } 2321 2322 signals = signals->NextSiblingElement("signal"); 2323 } 2324 2325 tinyxml2::XMLElement* property = 2326 interface->FirstChildElement("property"); 2327 while (property != nullptr) 2328 { 2329 const char* name = property->Attribute("name"); 2330 const char* type = property->Attribute("type"); 2331 if (type != nullptr && name != nullptr) 2332 { 2333 sdbusplus::message::message m = 2334 crow::connections::systemBus->new_method_call( 2335 processName.c_str(), objectPath.c_str(), 2336 "org.freedesktop." 2337 "DBus." 2338 "Properties", 2339 "Get"); 2340 m.append(interfaceName, name); 2341 nlohmann::json& propertyItem = propertiesObj[name]; 2342 crow::connections::systemBus->async_send( 2343 m, [&propertyItem, 2344 asyncResp](boost::system::error_code& e, 2345 sdbusplus::message::message& msg) { 2346 if (e) 2347 { 2348 return; 2349 } 2350 2351 convertDBusToJSON("v", msg, propertyItem); 2352 }); 2353 } 2354 property = property->NextSiblingElement("property"); 2355 } 2356 }, 2357 processName, objectPath, "org.freedesktop.DBus.Introspectable", 2358 "Introspect"); 2359 } 2360 else 2361 { 2362 if (req.method() != boost::beast::http::verb::post) 2363 { 2364 asyncResp->res.result(boost::beast::http::status::not_found); 2365 return; 2366 } 2367 2368 nlohmann::json requestDbusData = 2369 nlohmann::json::parse(req.body, nullptr, false); 2370 2371 if (requestDbusData.is_discarded()) 2372 { 2373 asyncResp->res.result(boost::beast::http::status::bad_request); 2374 return; 2375 } 2376 if (!requestDbusData.is_array()) 2377 { 2378 asyncResp->res.result(boost::beast::http::status::bad_request); 2379 return; 2380 } 2381 auto transaction = 2382 std::make_shared<InProgressActionData>(asyncResp->res); 2383 2384 transaction->path = objectPath; 2385 transaction->methodName = methodName; 2386 transaction->arguments = std::move(requestDbusData); 2387 2388 findActionOnInterface(transaction, processName); 2389 } 2390 } 2391 2392 inline void requestRoutes(App& app) 2393 { 2394 BMCWEB_ROUTE(app, "/bus/") 2395 .privileges({{"Login"}}) 2396 .methods(boost::beast::http::verb::get)( 2397 [](const crow::Request&, 2398 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2399 nlohmann::json::array_t buses; 2400 nlohmann::json& bus = buses.emplace_back(); 2401 bus["name"] = "system"; 2402 asyncResp->res.jsonValue["busses"] = std::move(buses); 2403 asyncResp->res.jsonValue["status"] = "ok"; 2404 }); 2405 2406 BMCWEB_ROUTE(app, "/bus/system/") 2407 .privileges({{"Login"}}) 2408 .methods(boost::beast::http::verb::get)( 2409 [](const crow::Request&, 2410 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2411 auto myCallback = [asyncResp](const boost::system::error_code ec, 2412 std::vector<std::string>& names) { 2413 if (ec) 2414 { 2415 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 2416 asyncResp->res.result( 2417 boost::beast::http::status::internal_server_error); 2418 } 2419 else 2420 { 2421 std::sort(names.begin(), names.end()); 2422 asyncResp->res.jsonValue["status"] = "ok"; 2423 auto& objectsSub = asyncResp->res.jsonValue["objects"]; 2424 for (auto& name : names) 2425 { 2426 nlohmann::json::object_t object; 2427 object["name"] = name; 2428 objectsSub.push_back(std::move(object)); 2429 } 2430 } 2431 }; 2432 crow::connections::systemBus->async_method_call( 2433 std::move(myCallback), "org.freedesktop.DBus", "/", 2434 "org.freedesktop.DBus", "ListNames"); 2435 }); 2436 2437 BMCWEB_ROUTE(app, "/list/") 2438 .privileges({{"Login"}}) 2439 .methods(boost::beast::http::verb::get)( 2440 [](const crow::Request&, 2441 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2442 handleList(asyncResp, "/"); 2443 }); 2444 2445 BMCWEB_ROUTE(app, "/xyz/<path>") 2446 .privileges({{"Login"}}) 2447 .methods(boost::beast::http::verb::get)( 2448 [](const crow::Request& req, 2449 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2450 const std::string& path) { 2451 std::string objectPath = "/xyz/" + path; 2452 handleDBusUrl(req, asyncResp, objectPath); 2453 }); 2454 2455 BMCWEB_ROUTE(app, "/xyz/<path>") 2456 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2457 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2458 boost::beast::http::verb::delete_)( 2459 [](const crow::Request& req, 2460 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2461 const std::string& path) { 2462 std::string objectPath = "/xyz/" + path; 2463 handleDBusUrl(req, asyncResp, objectPath); 2464 }); 2465 2466 BMCWEB_ROUTE(app, "/org/<path>") 2467 .privileges({{"Login"}}) 2468 .methods(boost::beast::http::verb::get)( 2469 [](const crow::Request& req, 2470 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2471 const std::string& path) { 2472 std::string objectPath = "/org/" + path; 2473 handleDBusUrl(req, asyncResp, objectPath); 2474 }); 2475 2476 BMCWEB_ROUTE(app, "/org/<path>") 2477 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2478 .methods(boost::beast::http::verb::put, boost::beast::http::verb::post, 2479 boost::beast::http::verb::delete_)( 2480 [](const crow::Request& req, 2481 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2482 const std::string& path) { 2483 std::string objectPath = "/org/" + path; 2484 handleDBusUrl(req, asyncResp, objectPath); 2485 }); 2486 2487 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2488 .privileges({{"ConfigureManager"}}) 2489 .methods(boost::beast::http::verb::get)( 2490 [](const crow::Request&, 2491 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2492 const std::string& dumpId) { 2493 if (!validateFilename(dumpId)) 2494 { 2495 asyncResp->res.result(boost::beast::http::status::bad_request); 2496 return; 2497 } 2498 std::filesystem::path loc("/var/lib/phosphor-debug-collector/dumps"); 2499 2500 loc /= dumpId; 2501 2502 if (!std::filesystem::exists(loc) || 2503 !std::filesystem::is_directory(loc)) 2504 { 2505 BMCWEB_LOG_ERROR << loc.string() << "Not found"; 2506 asyncResp->res.result(boost::beast::http::status::not_found); 2507 return; 2508 } 2509 std::filesystem::directory_iterator files(loc); 2510 2511 for (const auto& file : files) 2512 { 2513 std::ifstream readFile(file.path()); 2514 if (!readFile.good()) 2515 { 2516 continue; 2517 } 2518 2519 asyncResp->res.addHeader("Content-Type", 2520 "application/octet-stream"); 2521 2522 // Assuming only one dump file will be present in the dump 2523 // id directory 2524 std::string dumpFileName = file.path().filename().string(); 2525 2526 // Filename should be in alphanumeric, dot and underscore 2527 // Its based on phosphor-debug-collector application 2528 // dumpfile format 2529 std::regex dumpFileRegex("[a-zA-Z0-9\\._]+"); 2530 if (!std::regex_match(dumpFileName, dumpFileRegex)) 2531 { 2532 BMCWEB_LOG_ERROR << "Invalid dump filename " << dumpFileName; 2533 asyncResp->res.result(boost::beast::http::status::not_found); 2534 return; 2535 } 2536 std::string contentDispositionParam = 2537 "attachment; filename=\"" + dumpFileName + "\""; 2538 2539 asyncResp->res.addHeader("Content-Disposition", 2540 contentDispositionParam); 2541 2542 asyncResp->res.body() = {std::istreambuf_iterator<char>(readFile), 2543 std::istreambuf_iterator<char>()}; 2544 return; 2545 } 2546 asyncResp->res.result(boost::beast::http::status::not_found); 2547 return; 2548 }); 2549 2550 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2551 .privileges({{"Login"}}) 2552 2553 .methods(boost::beast::http::verb::get)( 2554 [](const crow::Request&, 2555 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2556 const std::string& connection) { 2557 introspectObjects(connection, "/", asyncResp); 2558 }); 2559 2560 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2561 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 2562 .methods(boost::beast::http::verb::get, 2563 boost::beast::http::verb::post)(handleBusSystemPost); 2564 } 2565 } // namespace openbmc_mapper 2566 } // namespace crow 2567