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 nlohmann::json &thisElement = response; 1041 for (const std::string &typeCode : returnTypes) 1042 { 1043 if (returnType.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, boost::beast::http::status::not_found, 1484 notFoundDesc, notFoundMsg); 1485 res.end(); 1486 return; 1487 } 1488 1489 auto transaction = std::make_shared<InProgressActionData>(res); 1490 transaction->path = objectPath; 1491 transaction->methodName = "Delete"; 1492 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 1493 1494 for (const std::pair<std::string, std::vector<std::string>> 1495 &object : interfaceNames) 1496 { 1497 findActionOnInterface(transaction, object.first); 1498 } 1499 }, 1500 "xyz.openbmc_project.ObjectMapper", 1501 "/xyz/openbmc_project/object_mapper", 1502 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 1503 std::array<const char *, 0>()); 1504 } 1505 1506 void handleList(crow::Response &res, const std::string &objectPath, 1507 int32_t depth = 0) 1508 { 1509 crow::connections::systemBus->async_method_call( 1510 [&res](const boost::system::error_code ec, 1511 std::vector<std::string> &objectPaths) { 1512 if (ec) 1513 { 1514 setErrorResponse(res, boost::beast::http::status::not_found, 1515 notFoundDesc, notFoundMsg); 1516 } 1517 else 1518 { 1519 res.jsonValue = {{"status", "ok"}, 1520 {"message", "200 OK"}, 1521 {"data", std::move(objectPaths)}}; 1522 } 1523 res.end(); 1524 }, 1525 "xyz.openbmc_project.ObjectMapper", 1526 "/xyz/openbmc_project/object_mapper", 1527 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 1528 depth, std::array<std::string, 0>()); 1529 } 1530 1531 void handleEnumerate(crow::Response &res, const std::string &objectPath) 1532 { 1533 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 1534 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); 1535 1536 asyncResp->res.jsonValue = {{"message", "200 OK"}, 1537 {"status", "ok"}, 1538 {"data", nlohmann::json::object()}}; 1539 1540 crow::connections::systemBus->async_method_call( 1541 [objectPath, asyncResp](const boost::system::error_code ec, 1542 GetSubTreeType &object_names) { 1543 auto transaction = std::make_shared<InProgressEnumerateData>( 1544 objectPath, asyncResp); 1545 1546 transaction->subtree = 1547 std::make_shared<GetSubTreeType>(std::move(object_names)); 1548 1549 if (ec) 1550 { 1551 BMCWEB_LOG_ERROR << "GetSubTree failed on " 1552 << transaction->objectPath; 1553 setErrorResponse(transaction->asyncResp->res, 1554 boost::beast::http::status::not_found, 1555 notFoundDesc, notFoundMsg); 1556 return; 1557 } 1558 1559 // Add the data for the path passed in to the results 1560 // as if GetSubTree returned it, and continue on enumerating 1561 getObjectAndEnumerate(transaction); 1562 }, 1563 "xyz.openbmc_project.ObjectMapper", 1564 "/xyz/openbmc_project/object_mapper", 1565 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 1566 static_cast<int32_t>(0), std::array<const char *, 0>()); 1567 } 1568 1569 void handleGet(crow::Response &res, std::string &objectPath, 1570 std::string &destProperty) 1571 { 1572 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1573 std::shared_ptr<std::string> propertyName = 1574 std::make_shared<std::string>(std::move(destProperty)); 1575 1576 std::shared_ptr<std::string> path = 1577 std::make_shared<std::string>(std::move(objectPath)); 1578 1579 using GetObjectType = 1580 std::vector<std::pair<std::string, std::vector<std::string>>>; 1581 crow::connections::systemBus->async_method_call( 1582 [&res, path, propertyName](const boost::system::error_code ec, 1583 const GetObjectType &object_names) { 1584 if (ec || object_names.size() <= 0) 1585 { 1586 setErrorResponse(res, boost::beast::http::status::not_found, 1587 notFoundDesc, notFoundMsg); 1588 res.end(); 1589 return; 1590 } 1591 std::shared_ptr<nlohmann::json> response = 1592 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1593 // The mapper should never give us an empty interface names list, 1594 // but check anyway 1595 for (const std::pair<std::string, std::vector<std::string>> 1596 connection : object_names) 1597 { 1598 const std::vector<std::string> &interfaceNames = 1599 connection.second; 1600 1601 if (interfaceNames.size() <= 0) 1602 { 1603 setErrorResponse(res, boost::beast::http::status::not_found, 1604 notFoundDesc, notFoundMsg); 1605 res.end(); 1606 return; 1607 } 1608 1609 for (const std::string &interface : interfaceNames) 1610 { 1611 crow::connections::systemBus->async_method_call( 1612 [&res, response, propertyName]( 1613 const boost::system::error_code ec, 1614 const std::vector<std::pair< 1615 std::string, dbus::utility::DbusVariantType>> 1616 &properties) { 1617 if (ec) 1618 { 1619 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1620 << ec; 1621 } 1622 else 1623 { 1624 for (const std::pair< 1625 std::string, 1626 dbus::utility::DbusVariantType> 1627 &property : properties) 1628 { 1629 // if property name is empty, or matches our 1630 // search query, add it to the response json 1631 1632 if (propertyName->empty()) 1633 { 1634 sdbusplus::message::variant_ns::visit( 1635 [&response, &property](auto &&val) { 1636 (*response)[property.first] = 1637 val; 1638 }, 1639 property.second); 1640 } 1641 else if (property.first == *propertyName) 1642 { 1643 sdbusplus::message::variant_ns::visit( 1644 [&response](auto &&val) { 1645 (*response) = val; 1646 }, 1647 property.second); 1648 } 1649 } 1650 } 1651 if (response.use_count() == 1) 1652 { 1653 if (!propertyName->empty() && response->empty()) 1654 { 1655 setErrorResponse( 1656 res, 1657 boost::beast::http::status::not_found, 1658 propNotFoundDesc, notFoundMsg); 1659 } 1660 else 1661 { 1662 res.jsonValue = {{"status", "ok"}, 1663 {"message", "200 OK"}, 1664 {"data", *response}}; 1665 } 1666 res.end(); 1667 } 1668 }, 1669 connection.first, *path, 1670 "org.freedesktop.DBus.Properties", "GetAll", interface); 1671 } 1672 } 1673 }, 1674 "xyz.openbmc_project.ObjectMapper", 1675 "/xyz/openbmc_project/object_mapper", 1676 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 1677 std::array<std::string, 0>()); 1678 } 1679 1680 struct AsyncPutRequest 1681 { 1682 AsyncPutRequest(crow::Response &res) : res(res) 1683 { 1684 } 1685 ~AsyncPutRequest() 1686 { 1687 if (res.jsonValue.empty()) 1688 { 1689 setErrorResponse(res, boost::beast::http::status::forbidden, 1690 forbiddenMsg, forbiddenPropDesc); 1691 } 1692 1693 res.end(); 1694 } 1695 1696 void setErrorStatus(const std::string &desc) 1697 { 1698 setErrorResponse(res, boost::beast::http::status::internal_server_error, 1699 desc, badReqMsg); 1700 } 1701 1702 crow::Response &res; 1703 std::string objectPath; 1704 std::string propertyName; 1705 nlohmann::json propertyValue; 1706 }; 1707 1708 void handlePut(const crow::Request &req, crow::Response &res, 1709 const std::string &objectPath, const std::string &destProperty) 1710 { 1711 if (destProperty.empty()) 1712 { 1713 setErrorResponse(res, boost::beast::http::status::forbidden, 1714 forbiddenResDesc, forbiddenMsg); 1715 res.end(); 1716 return; 1717 } 1718 1719 nlohmann::json requestDbusData = 1720 nlohmann::json::parse(req.body, nullptr, false); 1721 1722 if (requestDbusData.is_discarded()) 1723 { 1724 setErrorResponse(res, boost::beast::http::status::bad_request, 1725 noJsonDesc, badReqMsg); 1726 res.end(); 1727 return; 1728 } 1729 1730 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); 1731 if (propertyIt == requestDbusData.end()) 1732 { 1733 setErrorResponse(res, boost::beast::http::status::bad_request, 1734 noJsonDesc, badReqMsg); 1735 res.end(); 1736 return; 1737 } 1738 const nlohmann::json &propertySetValue = *propertyIt; 1739 auto transaction = std::make_shared<AsyncPutRequest>(res); 1740 transaction->objectPath = objectPath; 1741 transaction->propertyName = destProperty; 1742 transaction->propertyValue = propertySetValue; 1743 1744 using GetObjectType = 1745 std::vector<std::pair<std::string, std::vector<std::string>>>; 1746 1747 crow::connections::systemBus->async_method_call( 1748 [transaction](const boost::system::error_code ec, 1749 const GetObjectType &object_names) { 1750 if (!ec && object_names.size() <= 0) 1751 { 1752 setErrorResponse(transaction->res, 1753 boost::beast::http::status::not_found, 1754 propNotFoundDesc, notFoundMsg); 1755 return; 1756 } 1757 1758 for (const std::pair<std::string, std::vector<std::string>> 1759 connection : object_names) 1760 { 1761 const std::string &connectionName = connection.first; 1762 1763 crow::connections::systemBus->async_method_call( 1764 [connectionName{std::string(connectionName)}, 1765 transaction](const boost::system::error_code ec, 1766 const std::string &introspectXml) { 1767 if (ec) 1768 { 1769 BMCWEB_LOG_ERROR 1770 << "Introspect call failed with error: " 1771 << ec.message() 1772 << " on process: " << connectionName; 1773 transaction->setErrorStatus("Unexpected Error"); 1774 return; 1775 } 1776 tinyxml2::XMLDocument doc; 1777 1778 doc.Parse(introspectXml.c_str()); 1779 tinyxml2::XMLNode *pRoot = 1780 doc.FirstChildElement("node"); 1781 if (pRoot == nullptr) 1782 { 1783 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1784 << introspectXml; 1785 transaction->setErrorStatus("Unexpected Error"); 1786 return; 1787 } 1788 tinyxml2::XMLElement *ifaceNode = 1789 pRoot->FirstChildElement("interface"); 1790 while (ifaceNode != nullptr) 1791 { 1792 const char *interfaceName = 1793 ifaceNode->Attribute("name"); 1794 BMCWEB_LOG_DEBUG << "found interface " 1795 << interfaceName; 1796 tinyxml2::XMLElement *propNode = 1797 ifaceNode->FirstChildElement("property"); 1798 while (propNode != nullptr) 1799 { 1800 const char *propertyName = 1801 propNode->Attribute("name"); 1802 BMCWEB_LOG_DEBUG << "Found property " 1803 << propertyName; 1804 if (propertyName == transaction->propertyName) 1805 { 1806 const char *argType = 1807 propNode->Attribute("type"); 1808 if (argType != nullptr) 1809 { 1810 sdbusplus::message::message m = 1811 crow::connections::systemBus 1812 ->new_method_call( 1813 connectionName.c_str(), 1814 transaction->objectPath 1815 .c_str(), 1816 "org.freedesktop.DBus." 1817 "Properties", 1818 "Set"); 1819 m.append(interfaceName, 1820 transaction->propertyName); 1821 int r = sd_bus_message_open_container( 1822 m.get(), SD_BUS_TYPE_VARIANT, 1823 argType); 1824 if (r < 0) 1825 { 1826 transaction->setErrorStatus( 1827 "Unexpected Error"); 1828 return; 1829 } 1830 r = convertJsonToDbus( 1831 m.get(), argType, 1832 transaction->propertyValue); 1833 if (r < 0) 1834 { 1835 transaction->setErrorStatus( 1836 "Invalid arg type"); 1837 return; 1838 } 1839 r = sd_bus_message_close_container( 1840 m.get()); 1841 if (r < 0) 1842 { 1843 transaction->setErrorStatus( 1844 "Unexpected Error"); 1845 return; 1846 } 1847 1848 crow::connections::systemBus 1849 ->async_send( 1850 m, 1851 [transaction]( 1852 boost::system::error_code 1853 ec, 1854 sdbusplus::message::message 1855 &m) { 1856 BMCWEB_LOG_DEBUG << "sent"; 1857 if (ec) 1858 { 1859 setErrorResponse( 1860 transaction->res, 1861 boost::beast::http:: 1862 status:: 1863 forbidden, 1864 forbiddenPropDesc, 1865 ec.message()); 1866 } 1867 else 1868 { 1869 transaction->res 1870 .jsonValue = { 1871 {"status", "ok"}, 1872 {"message", 1873 "200 OK"}, 1874 {"data", nullptr}}; 1875 } 1876 }); 1877 } 1878 } 1879 propNode = 1880 propNode->NextSiblingElement("property"); 1881 } 1882 ifaceNode = 1883 ifaceNode->NextSiblingElement("interface"); 1884 } 1885 }, 1886 connectionName, transaction->objectPath, 1887 "org.freedesktop.DBus.Introspectable", "Introspect"); 1888 } 1889 }, 1890 "xyz.openbmc_project.ObjectMapper", 1891 "/xyz/openbmc_project/object_mapper", 1892 "xyz.openbmc_project.ObjectMapper", "GetObject", 1893 transaction->objectPath, std::array<std::string, 0>()); 1894 } 1895 1896 inline void handleDBusUrl(const crow::Request &req, crow::Response &res, 1897 std::string &objectPath) 1898 { 1899 1900 // If accessing a single attribute, fill in and update objectPath, 1901 // otherwise leave destProperty blank 1902 std::string destProperty = ""; 1903 const char *attrSeperator = "/attr/"; 1904 size_t attrPosition = objectPath.find(attrSeperator); 1905 if (attrPosition != objectPath.npos) 1906 { 1907 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 1908 objectPath.length()); 1909 objectPath = objectPath.substr(0, attrPosition); 1910 } 1911 1912 if (req.method() == "POST"_method) 1913 { 1914 constexpr const char *actionSeperator = "/action/"; 1915 size_t actionPosition = objectPath.find(actionSeperator); 1916 if (actionPosition != objectPath.npos) 1917 { 1918 std::string postProperty = 1919 objectPath.substr((actionPosition + strlen(actionSeperator)), 1920 objectPath.length()); 1921 objectPath = objectPath.substr(0, actionPosition); 1922 handleAction(req, res, objectPath, postProperty); 1923 return; 1924 } 1925 } 1926 else if (req.method() == "GET"_method) 1927 { 1928 if (boost::ends_with(objectPath, "/enumerate")) 1929 { 1930 objectPath.erase(objectPath.end() - sizeof("enumerate"), 1931 objectPath.end()); 1932 handleEnumerate(res, objectPath); 1933 } 1934 else if (boost::ends_with(objectPath, "/list")) 1935 { 1936 objectPath.erase(objectPath.end() - sizeof("list"), 1937 objectPath.end()); 1938 handleList(res, objectPath); 1939 } 1940 else 1941 { 1942 // Trim any trailing "/" at the end 1943 if (boost::ends_with(objectPath, "/")) 1944 { 1945 objectPath.pop_back(); 1946 handleList(res, objectPath, 1); 1947 } 1948 else 1949 { 1950 handleGet(res, objectPath, destProperty); 1951 } 1952 } 1953 return; 1954 } 1955 else if (req.method() == "PUT"_method) 1956 { 1957 handlePut(req, res, objectPath, destProperty); 1958 return; 1959 } 1960 else if (req.method() == "DELETE"_method) 1961 { 1962 handleDelete(req, res, objectPath); 1963 return; 1964 } 1965 1966 setErrorResponse(res, boost::beast::http::status::method_not_allowed, 1967 methodNotAllowedDesc, methodNotAllowedMsg); 1968 res.end(); 1969 } 1970 1971 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) 1972 { 1973 BMCWEB_ROUTE(app, "/bus/") 1974 .methods("GET"_method)( 1975 [](const crow::Request &req, crow::Response &res) { 1976 res.jsonValue = {{"busses", {{{"name", "system"}}}}, 1977 {"status", "ok"}}; 1978 res.end(); 1979 }); 1980 1981 BMCWEB_ROUTE(app, "/bus/system/") 1982 .methods("GET"_method)( 1983 [](const crow::Request &req, crow::Response &res) { 1984 auto myCallback = [&res](const boost::system::error_code ec, 1985 std::vector<std::string> &names) { 1986 if (ec) 1987 { 1988 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 1989 res.result( 1990 boost::beast::http::status::internal_server_error); 1991 } 1992 else 1993 { 1994 std::sort(names.begin(), names.end()); 1995 res.jsonValue = {{"status", "ok"}}; 1996 auto &objectsSub = res.jsonValue["objects"]; 1997 for (auto &name : names) 1998 { 1999 objectsSub.push_back({{"name", name}}); 2000 } 2001 } 2002 res.end(); 2003 }; 2004 crow::connections::systemBus->async_method_call( 2005 std::move(myCallback), "org.freedesktop.DBus", "/", 2006 "org.freedesktop.DBus", "ListNames"); 2007 }); 2008 2009 BMCWEB_ROUTE(app, "/list/") 2010 .methods("GET"_method)( 2011 [](const crow::Request &req, crow::Response &res) { 2012 handleList(res, "/"); 2013 }); 2014 2015 BMCWEB_ROUTE(app, "/xyz/<path>") 2016 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)( 2017 [](const crow::Request &req, crow::Response &res, 2018 const std::string &path) { 2019 std::string objectPath = "/xyz/" + path; 2020 handleDBusUrl(req, res, objectPath); 2021 }); 2022 2023 BMCWEB_ROUTE(app, "/org/<path>") 2024 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)( 2025 [](const crow::Request &req, crow::Response &res, 2026 const std::string &path) { 2027 std::string objectPath = "/org/" + path; 2028 handleDBusUrl(req, res, objectPath); 2029 }); 2030 2031 BMCWEB_ROUTE(app, "/download/dump/<str>/") 2032 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 2033 const std::string &dumpId) { 2034 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$"); 2035 if (!std::regex_match(dumpId, validFilename)) 2036 { 2037 res.result(boost::beast::http::status::bad_request); 2038 res.end(); 2039 return; 2040 } 2041 std::filesystem::path loc( 2042 "/var/lib/phosphor-debug-collector/dumps"); 2043 2044 loc /= dumpId; 2045 2046 if (!std::filesystem::exists(loc) || 2047 !std::filesystem::is_directory(loc)) 2048 { 2049 BMCWEB_LOG_ERROR << loc << "Not found"; 2050 res.result(boost::beast::http::status::not_found); 2051 res.end(); 2052 return; 2053 } 2054 std::filesystem::directory_iterator files(loc); 2055 2056 for (auto &file : files) 2057 { 2058 std::ifstream readFile(file.path()); 2059 if (!readFile.good()) 2060 { 2061 continue; 2062 } 2063 res.addHeader("Content-Type", "application/octet-stream"); 2064 res.body() = {std::istreambuf_iterator<char>(readFile), 2065 std::istreambuf_iterator<char>()}; 2066 res.end(); 2067 return; 2068 } 2069 res.result(boost::beast::http::status::not_found); 2070 res.end(); 2071 return; 2072 }); 2073 2074 BMCWEB_ROUTE(app, "/bus/system/<str>/") 2075 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 2076 const std::string &Connection) { 2077 introspectObjects(Connection, "/", 2078 std::make_shared<bmcweb::AsyncResp>(res)); 2079 }); 2080 2081 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 2082 .methods("GET"_method, 2083 "POST"_method)([](const crow::Request &req, 2084 crow::Response &res, 2085 const std::string &processName, 2086 const std::string &requestedPath) { 2087 std::vector<std::string> strs; 2088 boost::split(strs, requestedPath, boost::is_any_of("/")); 2089 std::string objectPath; 2090 std::string interfaceName; 2091 std::string methodName; 2092 auto it = strs.begin(); 2093 if (it == strs.end()) 2094 { 2095 objectPath = "/"; 2096 } 2097 while (it != strs.end()) 2098 { 2099 // Check if segment contains ".". If it does, it must be an 2100 // interface 2101 if (it->find(".") != std::string::npos) 2102 { 2103 break; 2104 // This check is neccesary as the trailing slash gets parsed 2105 // as part of our <path> specifier above, which causes the 2106 // normal trailing backslash redirector to fail. 2107 } 2108 else if (!it->empty()) 2109 { 2110 objectPath += "/" + *it; 2111 } 2112 it++; 2113 } 2114 if (it != strs.end()) 2115 { 2116 interfaceName = *it; 2117 it++; 2118 2119 // after interface, we might have a method name 2120 if (it != strs.end()) 2121 { 2122 methodName = *it; 2123 it++; 2124 } 2125 } 2126 if (it != strs.end()) 2127 { 2128 // if there is more levels past the method name, something went 2129 // wrong, return not found 2130 res.result(boost::beast::http::status::not_found); 2131 res.end(); 2132 return; 2133 } 2134 if (interfaceName.empty()) 2135 { 2136 crow::connections::systemBus->async_method_call( 2137 [&, processName, 2138 objectPath](const boost::system::error_code ec, 2139 const std::string &introspect_xml) { 2140 if (ec) 2141 { 2142 BMCWEB_LOG_ERROR 2143 << "Introspect call failed with error: " 2144 << ec.message() 2145 << " on process: " << processName 2146 << " path: " << objectPath << "\n"; 2147 return; 2148 } 2149 tinyxml2::XMLDocument doc; 2150 2151 doc.Parse(introspect_xml.c_str()); 2152 tinyxml2::XMLNode *pRoot = 2153 doc.FirstChildElement("node"); 2154 if (pRoot == nullptr) 2155 { 2156 BMCWEB_LOG_ERROR << "XML document failed to parse " 2157 << processName << " " << objectPath 2158 << "\n"; 2159 res.jsonValue = {{"status", "XML parse error"}}; 2160 res.result(boost::beast::http::status:: 2161 internal_server_error); 2162 return; 2163 } 2164 2165 BMCWEB_LOG_DEBUG << introspect_xml; 2166 res.jsonValue = {{"status", "ok"}, 2167 {"bus_name", processName}, 2168 {"object_path", objectPath}}; 2169 nlohmann::json &interfacesArray = 2170 res.jsonValue["interfaces"]; 2171 interfacesArray = nlohmann::json::array(); 2172 tinyxml2::XMLElement *interface = 2173 pRoot->FirstChildElement("interface"); 2174 2175 while (interface != nullptr) 2176 { 2177 const char *ifaceName = 2178 interface->Attribute("name"); 2179 if (ifaceName != nullptr) 2180 { 2181 interfacesArray.push_back( 2182 {{"name", ifaceName}}); 2183 } 2184 2185 interface = 2186 interface->NextSiblingElement("interface"); 2187 } 2188 2189 res.end(); 2190 }, 2191 processName, objectPath, 2192 "org.freedesktop.DBus.Introspectable", "Introspect"); 2193 } 2194 else if (methodName.empty()) 2195 { 2196 crow::connections::systemBus->async_method_call( 2197 [&, processName, objectPath, 2198 interfaceName{std::move(interfaceName)}]( 2199 const boost::system::error_code ec, 2200 const std::string &introspect_xml) { 2201 if (ec) 2202 { 2203 BMCWEB_LOG_ERROR 2204 << "Introspect call failed with error: " 2205 << ec.message() 2206 << " on process: " << processName 2207 << " path: " << objectPath << "\n"; 2208 } 2209 else 2210 { 2211 tinyxml2::XMLDocument doc; 2212 2213 doc.Parse(introspect_xml.c_str()); 2214 tinyxml2::XMLNode *pRoot = 2215 doc.FirstChildElement("node"); 2216 if (pRoot == nullptr) 2217 { 2218 BMCWEB_LOG_ERROR 2219 << "XML document failed to parse " 2220 << processName << " " << objectPath << "\n"; 2221 res.result(boost::beast::http::status:: 2222 internal_server_error); 2223 } 2224 else 2225 { 2226 tinyxml2::XMLElement *node = 2227 pRoot->FirstChildElement("node"); 2228 2229 // if we know we're the only call, build the 2230 // json directly 2231 tinyxml2::XMLElement *interface = 2232 pRoot->FirstChildElement("interface"); 2233 2234 res.jsonValue = { 2235 {"status", "ok"}, 2236 {"bus_name", processName}, 2237 {"interface", interfaceName}, 2238 {"object_path", objectPath}, 2239 {"properties", nlohmann::json::object()}}; 2240 2241 nlohmann::json &methodsArray = 2242 res.jsonValue["methods"]; 2243 methodsArray = nlohmann::json::array(); 2244 2245 nlohmann::json &signalsArray = 2246 res.jsonValue["signals"]; 2247 signalsArray = nlohmann::json::array(); 2248 2249 while (interface != nullptr) 2250 { 2251 const char *ifaceName = 2252 interface->Attribute("name"); 2253 2254 if (ifaceName != nullptr && 2255 ifaceName == interfaceName) 2256 { 2257 tinyxml2::XMLElement *methods = 2258 interface->FirstChildElement( 2259 "method"); 2260 while (methods != nullptr) 2261 { 2262 nlohmann::json argsArray = 2263 nlohmann::json::array(); 2264 tinyxml2::XMLElement *arg = 2265 methods->FirstChildElement( 2266 "arg"); 2267 while (arg != nullptr) 2268 { 2269 nlohmann::json thisArg; 2270 for (const char *fieldName : 2271 std::array<const char *, 2272 3>{"name", 2273 "direction", 2274 "type"}) 2275 { 2276 const char *fieldValue = 2277 arg->Attribute( 2278 fieldName); 2279 if (fieldValue != nullptr) 2280 { 2281 thisArg[fieldName] = 2282 fieldValue; 2283 } 2284 } 2285 argsArray.push_back( 2286 std::move(thisArg)); 2287 arg = arg->NextSiblingElement( 2288 "arg"); 2289 } 2290 2291 const char *name = 2292 methods->Attribute("name"); 2293 if (name != nullptr) 2294 { 2295 methodsArray.push_back( 2296 {{"name", name}, 2297 {"uri", "/bus/system/" + 2298 processName + 2299 objectPath + 2300 "/" + 2301 interfaceName + 2302 "/" + name}, 2303 {"args", argsArray}}); 2304 } 2305 methods = 2306 methods->NextSiblingElement( 2307 "method"); 2308 } 2309 tinyxml2::XMLElement *signals = 2310 interface->FirstChildElement( 2311 "signal"); 2312 while (signals != nullptr) 2313 { 2314 nlohmann::json argsArray = 2315 nlohmann::json::array(); 2316 2317 tinyxml2::XMLElement *arg = 2318 signals->FirstChildElement( 2319 "arg"); 2320 while (arg != nullptr) 2321 { 2322 const char *name = 2323 arg->Attribute("name"); 2324 const char *type = 2325 arg->Attribute("type"); 2326 if (name != nullptr && 2327 type != nullptr) 2328 { 2329 argsArray.push_back({ 2330 {"name", name}, 2331 {"type", type}, 2332 }); 2333 } 2334 arg = arg->NextSiblingElement( 2335 "arg"); 2336 } 2337 const char *name = 2338 signals->Attribute("name"); 2339 if (name != nullptr) 2340 { 2341 signalsArray.push_back( 2342 {{"name", name}, 2343 {"args", argsArray}}); 2344 } 2345 2346 signals = 2347 signals->NextSiblingElement( 2348 "signal"); 2349 } 2350 2351 break; 2352 } 2353 2354 interface = interface->NextSiblingElement( 2355 "interface"); 2356 } 2357 if (interface == nullptr) 2358 { 2359 // if we got to the end of the list and 2360 // never found a match, throw 404 2361 res.result( 2362 boost::beast::http::status::not_found); 2363 } 2364 } 2365 } 2366 res.end(); 2367 }, 2368 processName, objectPath, 2369 "org.freedesktop.DBus.Introspectable", "Introspect"); 2370 } 2371 else 2372 { 2373 if (req.method() != "POST"_method) 2374 { 2375 res.result(boost::beast::http::status::not_found); 2376 res.end(); 2377 return; 2378 } 2379 2380 nlohmann::json requestDbusData = 2381 nlohmann::json::parse(req.body, nullptr, false); 2382 2383 if (requestDbusData.is_discarded()) 2384 { 2385 res.result(boost::beast::http::status::bad_request); 2386 res.end(); 2387 return; 2388 } 2389 if (!requestDbusData.is_array()) 2390 { 2391 res.result(boost::beast::http::status::bad_request); 2392 res.end(); 2393 return; 2394 } 2395 auto transaction = std::make_shared<InProgressActionData>(res); 2396 2397 transaction->path = objectPath; 2398 transaction->methodName = methodName; 2399 transaction->arguments = std::move(requestDbusData); 2400 2401 findActionOnInterface(transaction, processName); 2402 } 2403 }); 2404 } 2405 } // namespace openbmc_mapper 2406 } // namespace crow 2407