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