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