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