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