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 <experimental/filesystem> 25 #include <fstream> 26 #include <sdbusplus/message/types.hpp> 27 28 namespace crow 29 { 30 namespace openbmc_mapper 31 { 32 33 using GetSubTreeType = std::vector< 34 std::pair<std::string, 35 std::vector<std::pair<std::string, std::vector<std::string>>>>>; 36 37 const std::string notFoundMsg = "404 Not Found"; 38 const std::string badReqMsg = "400 Bad Request"; 39 const std::string methodNotAllowedMsg = "405 Method Not Allowed"; 40 const std::string forbiddenMsg = "403 Forbidden"; 41 42 const std::string notFoundDesc = 43 "org.freedesktop.DBus.Error.FileNotFound: path or object not found"; 44 const std::string propNotFoundDesc = "The specified property cannot be found"; 45 const std::string noJsonDesc = "No JSON object could be decoded"; 46 const std::string methodNotFoundDesc = "The specified method cannot be found"; 47 const std::string methodNotAllowedDesc = "Method not allowed"; 48 const std::string forbiddenPropDesc = 49 "The specified property cannot be created"; 50 const std::string forbiddenResDesc = "The specified resource cannot be created"; 51 52 void setErrorResponse(crow::Response &res, boost::beast::http::status result, 53 const std::string &desc, const std::string &msg) 54 { 55 res.result(result); 56 res.jsonValue = {{"data", {{"description", desc}}}, 57 {"message", msg}, 58 {"status", "error"}}; 59 } 60 61 void introspectObjects(const std::string &processName, 62 const std::string &objectPath, 63 std::shared_ptr<bmcweb::AsyncResp> transaction) 64 { 65 if (transaction->res.jsonValue.is_null()) 66 { 67 transaction->res.jsonValue = {{"status", "ok"}, 68 {"bus_name", processName}, 69 {"objects", nlohmann::json::array()}}; 70 } 71 72 crow::connections::systemBus->async_method_call( 73 [transaction, processName{std::string(processName)}, 74 objectPath{std::string(objectPath)}]( 75 const boost::system::error_code ec, 76 const std::string &introspect_xml) { 77 if (ec) 78 { 79 BMCWEB_LOG_ERROR 80 << "Introspect call failed with error: " << ec.message() 81 << " on process: " << processName << " path: " << objectPath 82 << "\n"; 83 return; 84 } 85 transaction->res.jsonValue["objects"].push_back( 86 {{"path", objectPath}}); 87 88 tinyxml2::XMLDocument doc; 89 90 doc.Parse(introspect_xml.c_str()); 91 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); 92 if (pRoot == nullptr) 93 { 94 BMCWEB_LOG_ERROR << "XML document failed to parse " 95 << processName << " " << objectPath << "\n"; 96 } 97 else 98 { 99 tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); 100 while (node != nullptr) 101 { 102 const char *childPath = node->Attribute("name"); 103 if (childPath != nullptr) 104 { 105 std::string newpath; 106 if (objectPath != "/") 107 { 108 newpath += objectPath; 109 } 110 newpath += std::string("/") + childPath; 111 // introspect the subobjects as well 112 introspectObjects(processName, newpath, transaction); 113 } 114 115 node = node->NextSiblingElement("node"); 116 } 117 } 118 }, 119 processName, objectPath, "org.freedesktop.DBus.Introspectable", 120 "Introspect"); 121 } 122 123 void getPropertiesForEnumerate(const std::string &objectPath, 124 const std::string &service, 125 const std::string &interface, 126 std::shared_ptr<bmcweb::AsyncResp> asyncResp) 127 { 128 BMCWEB_LOG_DEBUG << "getPropertiesForEnumerate " << objectPath << " " 129 << service << " " << interface; 130 131 crow::connections::systemBus->async_method_call( 132 [asyncResp, objectPath, service, 133 interface](const boost::system::error_code ec, 134 const std::vector< 135 std::pair<std::string, dbus::utility::DbusVariantType>> 136 &propertiesList) { 137 if (ec) 138 { 139 BMCWEB_LOG_ERROR << "GetAll on path " << objectPath << " iface " 140 << interface << " service " << service 141 << " failed with code " << ec; 142 return; 143 } 144 145 nlohmann::json &dataJson = asyncResp->res.jsonValue["data"]; 146 nlohmann::json &objectJson = dataJson[objectPath]; 147 if (objectJson.is_null()) 148 { 149 objectJson = nlohmann::json::object(); 150 } 151 152 for (const auto &[name, value] : propertiesList) 153 { 154 nlohmann::json &propertyJson = objectJson[name]; 155 sdbusplus::message::variant_ns::visit( 156 [&propertyJson](auto &&val) { propertyJson = val; }, value); 157 } 158 }, 159 service, objectPath, "org.freedesktop.DBus.Properties", "GetAll", 160 interface); 161 } 162 163 // Find any results that weren't picked up by ObjectManagers, to be 164 // called after all ObjectManagers are searched for and called. 165 void findRemainingObjectsForEnumerate( 166 const std::string &objectPath, std::shared_ptr<GetSubTreeType> subtree, 167 std::shared_ptr<bmcweb::AsyncResp> asyncResp) 168 { 169 BMCWEB_LOG_DEBUG << "findRemainingObjectsForEnumerate"; 170 const nlohmann::json &dataJson = asyncResp->res.jsonValue["data"]; 171 172 for (const auto &[path, interface_map] : *subtree) 173 { 174 if (path == objectPath) 175 { 176 // An enumerate does not return the target path's properties 177 continue; 178 } 179 if (dataJson.find(path) == dataJson.end()) 180 { 181 for (const auto &[service, interfaces] : interface_map) 182 { 183 for (const auto &interface : interfaces) 184 { 185 if (!boost::starts_with(interface, "org.freedesktop.DBus")) 186 { 187 getPropertiesForEnumerate(path, service, interface, 188 asyncResp); 189 } 190 } 191 } 192 } 193 } 194 } 195 196 struct InProgressEnumerateData 197 { 198 InProgressEnumerateData(const std::string &objectPath, 199 std::shared_ptr<bmcweb::AsyncResp> asyncResp) : 200 objectPath(objectPath), 201 asyncResp(asyncResp) 202 { 203 } 204 205 ~InProgressEnumerateData() 206 { 207 findRemainingObjectsForEnumerate(objectPath, subtree, asyncResp); 208 } 209 210 const std::string objectPath; 211 std::shared_ptr<GetSubTreeType> subtree; 212 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 213 }; 214 215 void getManagedObjectsForEnumerate( 216 const std::string &object_name, const std::string &object_manager_path, 217 const std::string &connection_name, 218 std::shared_ptr<InProgressEnumerateData> transaction) 219 { 220 BMCWEB_LOG_DEBUG << "getManagedObjectsForEnumerate " << object_name 221 << " object_manager_path " << object_manager_path 222 << " connection_name " << connection_name; 223 crow::connections::systemBus->async_method_call( 224 [transaction, object_name, 225 connection_name](const boost::system::error_code ec, 226 const dbus::utility::ManagedObjectType &objects) { 227 if (ec) 228 { 229 BMCWEB_LOG_ERROR << "GetManagedObjects on path " << object_name 230 << " on connection " << connection_name 231 << " failed with code " << ec; 232 return; 233 } 234 235 nlohmann::json &dataJson = 236 transaction->asyncResp->res.jsonValue["data"]; 237 238 for (const auto &objectPath : objects) 239 { 240 if (boost::starts_with(objectPath.first.str, object_name)) 241 { 242 BMCWEB_LOG_DEBUG << "Reading object " 243 << objectPath.first.str; 244 nlohmann::json &objectJson = dataJson[objectPath.first.str]; 245 if (objectJson.is_null()) 246 { 247 objectJson = nlohmann::json::object(); 248 } 249 for (const auto &interface : objectPath.second) 250 { 251 for (const auto &property : interface.second) 252 { 253 nlohmann::json &propertyJson = 254 objectJson[property.first]; 255 sdbusplus::message::variant_ns::visit( 256 [&propertyJson](auto &&val) { 257 propertyJson = val; 258 }, 259 property.second); 260 } 261 } 262 } 263 for (const auto &interface : objectPath.second) 264 { 265 if (interface.first == "org.freedesktop.DBus.ObjectManager") 266 { 267 getManagedObjectsForEnumerate( 268 objectPath.first.str, objectPath.first.str, 269 connection_name, transaction); 270 } 271 } 272 } 273 }, 274 connection_name, object_manager_path, 275 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 276 } 277 278 void findObjectManagerPathForEnumerate( 279 const std::string &object_name, const std::string &connection_name, 280 std::shared_ptr<InProgressEnumerateData> transaction) 281 { 282 BMCWEB_LOG_DEBUG << "Finding objectmanager for path " << object_name 283 << " on connection:" << connection_name; 284 crow::connections::systemBus->async_method_call( 285 [transaction, object_name, connection_name]( 286 const boost::system::error_code ec, 287 const boost::container::flat_map< 288 std::string, boost::container::flat_map< 289 std::string, std::vector<std::string>>> 290 &objects) { 291 if (ec) 292 { 293 BMCWEB_LOG_ERROR << "GetAncestors on path " << object_name 294 << " failed with code " << ec; 295 return; 296 } 297 298 for (const auto &pathGroup : objects) 299 { 300 for (const auto &connectionGroup : pathGroup.second) 301 { 302 if (connectionGroup.first == connection_name) 303 { 304 // Found the object manager path for this resource. 305 getManagedObjectsForEnumerate( 306 object_name, pathGroup.first, connection_name, 307 transaction); 308 return; 309 } 310 } 311 } 312 }, 313 "xyz.openbmc_project.ObjectMapper", 314 "/xyz/openbmc_project/object_mapper", 315 "xyz.openbmc_project.ObjectMapper", "GetAncestors", object_name, 316 std::array<const char *, 1>{"org.freedesktop.DBus.ObjectManager"}); 317 } 318 319 // Uses GetObject to add the object info about the target /enumerate path to the 320 // results of GetSubTree, as GetSubTree will not return info for the 321 // target path, and then continues on enumerating the rest of the tree. 322 void getObjectAndEnumerate(std::shared_ptr<InProgressEnumerateData> transaction) 323 { 324 using GetObjectType = 325 std::vector<std::pair<std::string, std::vector<std::string>>>; 326 327 crow::connections::systemBus->async_method_call( 328 [transaction](const boost::system::error_code ec, 329 const GetObjectType &objects) { 330 if (ec) 331 { 332 BMCWEB_LOG_ERROR << "GetObject for path " 333 << transaction->objectPath 334 << " failed with code " << ec; 335 return; 336 } 337 338 BMCWEB_LOG_DEBUG << "GetObject for " << transaction->objectPath 339 << " has " << objects.size() << " entries"; 340 if (!objects.empty()) 341 { 342 transaction->subtree->emplace_back(transaction->objectPath, 343 objects); 344 } 345 346 // Map indicating connection name, and the path where the object 347 // manager exists 348 boost::container::flat_map<std::string, std::string> connections; 349 350 for (const auto &object : *(transaction->subtree)) 351 { 352 for (const auto &connection : object.second) 353 { 354 std::string &objectManagerPath = 355 connections[connection.first]; 356 for (const auto &interface : connection.second) 357 { 358 BMCWEB_LOG_DEBUG << connection.first 359 << " has interface " << interface; 360 if (interface == "org.freedesktop.DBus.ObjectManager") 361 { 362 BMCWEB_LOG_DEBUG << "found object manager path " 363 << object.first; 364 objectManagerPath = object.first; 365 } 366 } 367 } 368 } 369 BMCWEB_LOG_DEBUG << "Got " << connections.size() << " connections"; 370 371 for (const auto &connection : connections) 372 { 373 // If we already know where the object manager is, we don't need 374 // to search for it, we can call directly in to 375 // getManagedObjects 376 if (!connection.second.empty()) 377 { 378 getManagedObjectsForEnumerate( 379 transaction->objectPath, connection.second, 380 connection.first, transaction); 381 } 382 else 383 { 384 // otherwise we need to find the object manager path before 385 // we can continue 386 findObjectManagerPathForEnumerate( 387 transaction->objectPath, connection.first, transaction); 388 } 389 } 390 }, 391 "xyz.openbmc_project.ObjectMapper", 392 "/xyz/openbmc_project/object_mapper", 393 "xyz.openbmc_project.ObjectMapper", "GetObject", 394 transaction->objectPath, std::array<const char *, 0>()); 395 } 396 397 // Structure for storing data on an in progress action 398 struct InProgressActionData 399 { 400 InProgressActionData(crow::Response &res) : res(res){}; 401 ~InProgressActionData() 402 { 403 // If still no JSON filled in, then we never found the method. 404 if (res.jsonValue.is_null()) 405 { 406 setErrorResponse(res, boost::beast::http::status::not_found, 407 methodNotFoundDesc, notFoundMsg); 408 } 409 res.end(); 410 } 411 412 void setErrorStatus(const std::string &desc) 413 { 414 setErrorResponse(res, boost::beast::http::status::internal_server_error, 415 desc, badReqMsg); 416 } 417 crow::Response &res; 418 std::string path; 419 std::string methodName; 420 nlohmann::json arguments; 421 }; 422 423 std::vector<std::string> dbusArgSplit(const std::string &string) 424 { 425 std::vector<std::string> ret; 426 if (string.empty()) 427 { 428 return ret; 429 } 430 ret.push_back(""); 431 int containerDepth = 0; 432 433 for (std::string::const_iterator character = string.begin(); 434 character != string.end(); character++) 435 { 436 ret.back() += *character; 437 switch (*character) 438 { 439 case ('a'): 440 break; 441 case ('('): 442 case ('{'): 443 containerDepth++; 444 break; 445 case ('}'): 446 case (')'): 447 containerDepth--; 448 if (containerDepth == 0) 449 { 450 if (character + 1 != string.end()) 451 { 452 ret.push_back(""); 453 } 454 } 455 break; 456 default: 457 if (containerDepth == 0) 458 { 459 if (character + 1 != string.end()) 460 { 461 ret.push_back(""); 462 } 463 } 464 break; 465 } 466 } 467 } 468 469 int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type, 470 const nlohmann::json &input_json) 471 { 472 int r = 0; 473 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() 474 << " to type: " << arg_type; 475 const std::vector<std::string> argTypes = dbusArgSplit(arg_type); 476 477 // Assume a single object for now. 478 const nlohmann::json *j = &input_json; 479 nlohmann::json::const_iterator jIt = input_json.begin(); 480 481 for (const std::string &argCode : argTypes) 482 { 483 // If we are decoding multiple objects, grab the pointer to the 484 // iterator, and increment it for the next loop 485 if (argTypes.size() > 1) 486 { 487 if (jIt == input_json.end()) 488 { 489 return -2; 490 } 491 j = &*jIt; 492 jIt++; 493 } 494 const int64_t *intValue = j->get_ptr<const int64_t *>(); 495 const uint64_t *uintValue = j->get_ptr<const uint64_t *>(); 496 const std::string *stringValue = j->get_ptr<const std::string *>(); 497 const double *doubleValue = j->get_ptr<const double *>(); 498 const bool *b = j->get_ptr<const bool *>(); 499 int64_t v = 0; 500 double d = 0.0; 501 502 // Do some basic type conversions that make sense. uint can be 503 // converted to int. int and uint can be converted to double 504 if (uintValue != nullptr && intValue == nullptr) 505 { 506 v = static_cast<int64_t>(*uintValue); 507 intValue = &v; 508 } 509 if (uintValue != nullptr && doubleValue == nullptr) 510 { 511 d = static_cast<double>(*uintValue); 512 doubleValue = &d; 513 } 514 if (intValue != nullptr && doubleValue == nullptr) 515 { 516 d = static_cast<double>(*intValue); 517 doubleValue = &d; 518 } 519 520 if (argCode == "s") 521 { 522 if (stringValue == nullptr) 523 { 524 return -1; 525 } 526 r = sd_bus_message_append_basic(m, argCode[0], 527 (void *)stringValue->c_str()); 528 if (r < 0) 529 { 530 return r; 531 } 532 } 533 else if (argCode == "i") 534 { 535 if (intValue == nullptr) 536 { 537 return -1; 538 } 539 int32_t i = static_cast<int32_t>(*intValue); 540 r = sd_bus_message_append_basic(m, argCode[0], &i); 541 if (r < 0) 542 { 543 return r; 544 } 545 } 546 else if (argCode == "b") 547 { 548 // lots of ways bool could be represented here. Try them all 549 int boolInt = false; 550 if (intValue != nullptr) 551 { 552 boolInt = *intValue > 0 ? 1 : 0; 553 } 554 else if (b != nullptr) 555 { 556 boolInt = b ? 1 : 0; 557 } 558 else if (stringValue != nullptr) 559 { 560 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0; 561 } 562 else 563 { 564 return -1; 565 } 566 r = sd_bus_message_append_basic(m, argCode[0], &boolInt); 567 if (r < 0) 568 { 569 return r; 570 } 571 } 572 else if (argCode == "n") 573 { 574 if (intValue == nullptr) 575 { 576 return -1; 577 } 578 int16_t n = static_cast<int16_t>(*intValue); 579 r = sd_bus_message_append_basic(m, argCode[0], &n); 580 if (r < 0) 581 { 582 return r; 583 } 584 } 585 else if (argCode == "x") 586 { 587 if (intValue == nullptr) 588 { 589 return -1; 590 } 591 r = sd_bus_message_append_basic(m, argCode[0], intValue); 592 if (r < 0) 593 { 594 return r; 595 } 596 } 597 else if (argCode == "y") 598 { 599 if (uintValue == nullptr) 600 { 601 return -1; 602 } 603 uint8_t y = static_cast<uint8_t>(*uintValue); 604 r = sd_bus_message_append_basic(m, argCode[0], &y); 605 } 606 else if (argCode == "q") 607 { 608 if (uintValue == nullptr) 609 { 610 return -1; 611 } 612 uint16_t q = static_cast<uint16_t>(*uintValue); 613 r = sd_bus_message_append_basic(m, argCode[0], &q); 614 } 615 else if (argCode == "u") 616 { 617 if (uintValue == nullptr) 618 { 619 return -1; 620 } 621 uint32_t u = static_cast<uint32_t>(*uintValue); 622 r = sd_bus_message_append_basic(m, argCode[0], &u); 623 } 624 else if (argCode == "t") 625 { 626 if (uintValue == nullptr) 627 { 628 return -1; 629 } 630 r = sd_bus_message_append_basic(m, argCode[0], uintValue); 631 } 632 else if (argCode == "d") 633 { 634 sd_bus_message_append_basic(m, argCode[0], doubleValue); 635 } 636 else if (boost::starts_with(argCode, "a")) 637 { 638 std::string containedType = argCode.substr(1); 639 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, 640 containedType.c_str()); 641 if (r < 0) 642 { 643 return r; 644 } 645 646 for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); 647 ++it) 648 { 649 r = convertJsonToDbus(m, containedType, *it); 650 if (r < 0) 651 { 652 return r; 653 } 654 655 it++; 656 } 657 sd_bus_message_close_container(m); 658 } 659 else if (boost::starts_with(argCode, "v")) 660 { 661 std::string containedType = argCode.substr(1); 662 BMCWEB_LOG_DEBUG << "variant type: " << argCode 663 << " appending variant of type: " << containedType; 664 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, 665 containedType.c_str()); 666 if (r < 0) 667 { 668 return r; 669 } 670 671 r = convertJsonToDbus(m, containedType, input_json); 672 if (r < 0) 673 { 674 return r; 675 } 676 677 r = sd_bus_message_close_container(m); 678 if (r < 0) 679 { 680 return r; 681 } 682 } 683 else if (boost::starts_with(argCode, "(") && 684 boost::ends_with(argCode, ")")) 685 { 686 std::string containedType = argCode.substr(1, argCode.size() - 1); 687 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, 688 containedType.c_str()); 689 nlohmann::json::const_iterator it = j->begin(); 690 for (const std::string &argCode : dbusArgSplit(arg_type)) 691 { 692 if (it == j->end()) 693 { 694 return -1; 695 } 696 r = convertJsonToDbus(m, argCode, *it); 697 if (r < 0) 698 { 699 return r; 700 } 701 it++; 702 } 703 r = sd_bus_message_close_container(m); 704 } 705 else if (boost::starts_with(argCode, "{") && 706 boost::ends_with(argCode, "}")) 707 { 708 std::string containedType = argCode.substr(1, argCode.size() - 1); 709 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, 710 containedType.c_str()); 711 std::vector<std::string> codes = dbusArgSplit(containedType); 712 if (codes.size() != 2) 713 { 714 return -1; 715 } 716 const std::string &key_type = codes[0]; 717 const std::string &value_type = codes[1]; 718 for (auto it : j->items()) 719 { 720 r = convertJsonToDbus(m, key_type, it.key()); 721 if (r < 0) 722 { 723 return r; 724 } 725 726 r = convertJsonToDbus(m, value_type, it.value()); 727 if (r < 0) 728 { 729 return r; 730 } 731 } 732 r = sd_bus_message_close_container(m); 733 } 734 else 735 { 736 return -2; 737 } 738 if (r < 0) 739 { 740 return r; 741 } 742 743 if (argTypes.size() > 1) 744 { 745 jIt++; 746 } 747 } 748 } 749 750 void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction, 751 const std::string &connectionName) 752 { 753 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " 754 << connectionName; 755 crow::connections::systemBus->async_method_call( 756 [transaction, connectionName{std::string(connectionName)}]( 757 const boost::system::error_code ec, 758 const std::string &introspect_xml) { 759 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; 760 if (ec) 761 { 762 BMCWEB_LOG_ERROR 763 << "Introspect call failed with error: " << ec.message() 764 << " on process: " << connectionName << "\n"; 765 } 766 else 767 { 768 tinyxml2::XMLDocument doc; 769 770 doc.Parse(introspect_xml.data(), introspect_xml.size()); 771 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); 772 if (pRoot == nullptr) 773 { 774 BMCWEB_LOG_ERROR << "XML document failed to parse " 775 << connectionName << "\n"; 776 return; 777 } 778 tinyxml2::XMLElement *interfaceNode = 779 pRoot->FirstChildElement("interface"); 780 while (interfaceNode != nullptr) 781 { 782 const char *thisInterfaceName = 783 interfaceNode->Attribute("name"); 784 if (thisInterfaceName != nullptr) 785 { 786 tinyxml2::XMLElement *methodNode = 787 interfaceNode->FirstChildElement("method"); 788 while (methodNode != nullptr) 789 { 790 const char *thisMethodName = 791 methodNode->Attribute("name"); 792 BMCWEB_LOG_DEBUG << "Found method: " 793 << thisMethodName; 794 if (thisMethodName != nullptr && 795 thisMethodName == transaction->methodName) 796 { 797 BMCWEB_LOG_DEBUG 798 << "Found method named " << thisMethodName 799 << " on interface " << thisInterfaceName; 800 sdbusplus::message::message m = 801 crow::connections::systemBus 802 ->new_method_call( 803 connectionName.c_str(), 804 transaction->path.c_str(), 805 thisInterfaceName, 806 transaction->methodName.c_str()); 807 808 tinyxml2::XMLElement *argumentNode = 809 methodNode->FirstChildElement("arg"); 810 811 nlohmann::json::const_iterator argIt = 812 transaction->arguments.begin(); 813 814 while (argumentNode != nullptr) 815 { 816 const char *argDirection = 817 argumentNode->Attribute("direction"); 818 const char *argType = 819 argumentNode->Attribute("type"); 820 if (argDirection != nullptr && 821 argType != nullptr && 822 std::string(argDirection) == "in") 823 { 824 825 if (argIt == 826 transaction->arguments.end()) 827 { 828 transaction->setErrorStatus( 829 "Invalid method args"); 830 return; 831 } 832 if (convertJsonToDbus( 833 m.get(), std::string(argType), 834 *argIt) < 0) 835 { 836 transaction->setErrorStatus( 837 "Invalid method arg type"); 838 return; 839 } 840 841 argIt++; 842 } 843 argumentNode = 844 methodNode->NextSiblingElement("arg"); 845 } 846 847 crow::connections::systemBus->async_send( 848 m, [transaction]( 849 boost::system::error_code ec, 850 sdbusplus::message::message &m) { 851 if (ec) 852 { 853 transaction->setErrorStatus( 854 "Method call failed"); 855 return; 856 } 857 transaction->res.jsonValue = { 858 {"status", "ok"}, 859 {"message", "200 OK"}, 860 {"data", nullptr}}; 861 }); 862 break; 863 } 864 methodNode = 865 methodNode->NextSiblingElement("method"); 866 } 867 } 868 interfaceNode = 869 interfaceNode->NextSiblingElement("interface"); 870 } 871 } 872 }, 873 connectionName, transaction->path, 874 "org.freedesktop.DBus.Introspectable", "Introspect"); 875 } 876 877 void handleAction(const crow::Request &req, crow::Response &res, 878 const std::string &objectPath, const std::string &methodName) 879 { 880 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 881 << methodName; 882 nlohmann::json requestDbusData = 883 nlohmann::json::parse(req.body, nullptr, false); 884 885 if (requestDbusData.is_discarded()) 886 { 887 setErrorResponse(res, boost::beast::http::status::bad_request, 888 noJsonDesc, badReqMsg); 889 res.end(); 890 return; 891 } 892 nlohmann::json::iterator data = requestDbusData.find("data"); 893 if (data == requestDbusData.end()) 894 { 895 setErrorResponse(res, boost::beast::http::status::bad_request, 896 noJsonDesc, badReqMsg); 897 res.end(); 898 return; 899 } 900 901 if (!data->is_array()) 902 { 903 setErrorResponse(res, boost::beast::http::status::bad_request, 904 noJsonDesc, badReqMsg); 905 res.end(); 906 return; 907 } 908 auto transaction = std::make_shared<InProgressActionData>(res); 909 910 transaction->path = objectPath; 911 transaction->methodName = methodName; 912 transaction->arguments = std::move(*data); 913 crow::connections::systemBus->async_method_call( 914 [transaction]( 915 const boost::system::error_code ec, 916 const std::vector<std::pair<std::string, std::vector<std::string>>> 917 &interfaceNames) { 918 if (ec || interfaceNames.size() <= 0) 919 { 920 BMCWEB_LOG_ERROR << "Can't find object"; 921 setErrorResponse(transaction->res, 922 boost::beast::http::status::not_found, 923 notFoundDesc, notFoundMsg); 924 return; 925 } 926 927 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 928 << " object(s)"; 929 930 for (const std::pair<std::string, std::vector<std::string>> 931 &object : interfaceNames) 932 { 933 findActionOnInterface(transaction, object.first); 934 } 935 }, 936 "xyz.openbmc_project.ObjectMapper", 937 "/xyz/openbmc_project/object_mapper", 938 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 939 std::array<std::string, 0>()); 940 } 941 942 void handleList(crow::Response &res, const std::string &objectPath, 943 int32_t depth = 0) 944 { 945 crow::connections::systemBus->async_method_call( 946 [&res](const boost::system::error_code ec, 947 std::vector<std::string> &objectPaths) { 948 if (ec) 949 { 950 setErrorResponse(res, boost::beast::http::status::not_found, 951 notFoundDesc, notFoundMsg); 952 } 953 else 954 { 955 res.jsonValue = {{"status", "ok"}, 956 {"message", "200 OK"}, 957 {"data", std::move(objectPaths)}}; 958 } 959 res.end(); 960 }, 961 "xyz.openbmc_project.ObjectMapper", 962 "/xyz/openbmc_project/object_mapper", 963 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 964 depth, std::array<std::string, 0>()); 965 } 966 967 void handleEnumerate(crow::Response &res, const std::string &objectPath) 968 { 969 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 970 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); 971 972 asyncResp->res.jsonValue = {{"message", "200 OK"}, 973 {"status", "ok"}, 974 {"data", nlohmann::json::object()}}; 975 976 crow::connections::systemBus->async_method_call( 977 [objectPath, asyncResp](const boost::system::error_code ec, 978 GetSubTreeType &object_names) { 979 auto transaction = std::make_shared<InProgressEnumerateData>( 980 objectPath, asyncResp); 981 982 transaction->subtree = 983 std::make_shared<GetSubTreeType>(std::move(object_names)); 984 985 if (ec) 986 { 987 BMCWEB_LOG_ERROR << "GetSubTree failed on " 988 << transaction->objectPath; 989 setErrorResponse(transaction->asyncResp->res, 990 boost::beast::http::status::not_found, 991 notFoundDesc, notFoundMsg); 992 return; 993 } 994 995 // Add the data for the path passed in to the results 996 // as if GetSubTree returned it, and continue on enumerating 997 getObjectAndEnumerate(transaction); 998 }, 999 "xyz.openbmc_project.ObjectMapper", 1000 "/xyz/openbmc_project/object_mapper", 1001 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 1002 static_cast<int32_t>(0), std::array<const char *, 0>()); 1003 } 1004 1005 void handleGet(crow::Response &res, std::string &objectPath, 1006 std::string &destProperty) 1007 { 1008 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1009 std::shared_ptr<std::string> propertyName = 1010 std::make_shared<std::string>(std::move(destProperty)); 1011 1012 std::shared_ptr<std::string> path = 1013 std::make_shared<std::string>(std::move(objectPath)); 1014 1015 using GetObjectType = 1016 std::vector<std::pair<std::string, std::vector<std::string>>>; 1017 crow::connections::systemBus->async_method_call( 1018 [&res, path, propertyName](const boost::system::error_code ec, 1019 const GetObjectType &object_names) { 1020 if (ec || object_names.size() <= 0) 1021 { 1022 setErrorResponse(res, boost::beast::http::status::not_found, 1023 notFoundDesc, notFoundMsg); 1024 res.end(); 1025 return; 1026 } 1027 std::shared_ptr<nlohmann::json> response = 1028 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1029 // The mapper should never give us an empty interface names list, 1030 // but check anyway 1031 for (const std::pair<std::string, std::vector<std::string>> 1032 connection : object_names) 1033 { 1034 const std::vector<std::string> &interfaceNames = 1035 connection.second; 1036 1037 if (interfaceNames.size() <= 0) 1038 { 1039 setErrorResponse(res, boost::beast::http::status::not_found, 1040 notFoundDesc, notFoundMsg); 1041 res.end(); 1042 return; 1043 } 1044 1045 for (const std::string &interface : interfaceNames) 1046 { 1047 crow::connections::systemBus->async_method_call( 1048 [&res, response, propertyName]( 1049 const boost::system::error_code ec, 1050 const std::vector<std::pair< 1051 std::string, dbus::utility::DbusVariantType>> 1052 &properties) { 1053 if (ec) 1054 { 1055 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1056 << ec; 1057 } 1058 else 1059 { 1060 for (const std::pair< 1061 std::string, 1062 dbus::utility::DbusVariantType> 1063 &property : properties) 1064 { 1065 // if property name is empty, or matches our 1066 // search query, add it to the response json 1067 1068 if (propertyName->empty()) 1069 { 1070 sdbusplus::message::variant_ns::visit( 1071 [&response, &property](auto &&val) { 1072 (*response)[property.first] = 1073 val; 1074 }, 1075 property.second); 1076 } 1077 else if (property.first == *propertyName) 1078 { 1079 sdbusplus::message::variant_ns::visit( 1080 [&response](auto &&val) { 1081 (*response) = val; 1082 }, 1083 property.second); 1084 } 1085 } 1086 } 1087 if (response.use_count() == 1) 1088 { 1089 if (!propertyName->empty() && response->empty()) 1090 { 1091 setErrorResponse( 1092 res, 1093 boost::beast::http::status::not_found, 1094 propNotFoundDesc, notFoundMsg); 1095 } 1096 else 1097 { 1098 res.jsonValue = {{"status", "ok"}, 1099 {"message", "200 OK"}, 1100 {"data", *response}}; 1101 } 1102 res.end(); 1103 } 1104 }, 1105 connection.first, *path, 1106 "org.freedesktop.DBus.Properties", "GetAll", interface); 1107 } 1108 } 1109 }, 1110 "xyz.openbmc_project.ObjectMapper", 1111 "/xyz/openbmc_project/object_mapper", 1112 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 1113 std::array<std::string, 0>()); 1114 } 1115 1116 struct AsyncPutRequest 1117 { 1118 AsyncPutRequest(crow::Response &res) : res(res) 1119 { 1120 } 1121 ~AsyncPutRequest() 1122 { 1123 if (res.jsonValue.empty()) 1124 { 1125 setErrorResponse(res, boost::beast::http::status::forbidden, 1126 forbiddenMsg, forbiddenPropDesc); 1127 } 1128 1129 res.end(); 1130 } 1131 1132 void setErrorStatus(const std::string &desc) 1133 { 1134 setErrorResponse(res, boost::beast::http::status::internal_server_error, 1135 desc, badReqMsg); 1136 } 1137 1138 crow::Response &res; 1139 std::string objectPath; 1140 std::string propertyName; 1141 nlohmann::json propertyValue; 1142 }; 1143 1144 void handlePut(const crow::Request &req, crow::Response &res, 1145 const std::string &objectPath, const std::string &destProperty) 1146 { 1147 if (destProperty.empty()) 1148 { 1149 setErrorResponse(res, boost::beast::http::status::forbidden, 1150 forbiddenResDesc, forbiddenMsg); 1151 res.end(); 1152 return; 1153 } 1154 1155 nlohmann::json requestDbusData = 1156 nlohmann::json::parse(req.body, nullptr, false); 1157 1158 if (requestDbusData.is_discarded()) 1159 { 1160 setErrorResponse(res, boost::beast::http::status::bad_request, 1161 noJsonDesc, badReqMsg); 1162 res.end(); 1163 return; 1164 } 1165 1166 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); 1167 if (propertyIt == requestDbusData.end()) 1168 { 1169 setErrorResponse(res, boost::beast::http::status::bad_request, 1170 noJsonDesc, badReqMsg); 1171 res.end(); 1172 return; 1173 } 1174 const nlohmann::json &propertySetValue = *propertyIt; 1175 auto transaction = std::make_shared<AsyncPutRequest>(res); 1176 transaction->objectPath = objectPath; 1177 transaction->propertyName = destProperty; 1178 transaction->propertyValue = propertySetValue; 1179 1180 using GetObjectType = 1181 std::vector<std::pair<std::string, std::vector<std::string>>>; 1182 1183 crow::connections::systemBus->async_method_call( 1184 [transaction](const boost::system::error_code ec, 1185 const GetObjectType &object_names) { 1186 if (!ec && object_names.size() <= 0) 1187 { 1188 setErrorResponse(transaction->res, 1189 boost::beast::http::status::not_found, 1190 propNotFoundDesc, notFoundMsg); 1191 return; 1192 } 1193 1194 for (const std::pair<std::string, std::vector<std::string>> 1195 connection : object_names) 1196 { 1197 const std::string &connectionName = connection.first; 1198 1199 crow::connections::systemBus->async_method_call( 1200 [connectionName{std::string(connectionName)}, 1201 transaction](const boost::system::error_code ec, 1202 const std::string &introspectXml) { 1203 if (ec) 1204 { 1205 BMCWEB_LOG_ERROR 1206 << "Introspect call failed with error: " 1207 << ec.message() 1208 << " on process: " << connectionName; 1209 transaction->setErrorStatus("Unexpected Error"); 1210 return; 1211 } 1212 tinyxml2::XMLDocument doc; 1213 1214 doc.Parse(introspectXml.c_str()); 1215 tinyxml2::XMLNode *pRoot = 1216 doc.FirstChildElement("node"); 1217 if (pRoot == nullptr) 1218 { 1219 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1220 << introspectXml; 1221 transaction->setErrorStatus("Unexpected Error"); 1222 return; 1223 } 1224 tinyxml2::XMLElement *ifaceNode = 1225 pRoot->FirstChildElement("interface"); 1226 while (ifaceNode != nullptr) 1227 { 1228 const char *interfaceName = 1229 ifaceNode->Attribute("name"); 1230 BMCWEB_LOG_DEBUG << "found interface " 1231 << interfaceName; 1232 tinyxml2::XMLElement *propNode = 1233 ifaceNode->FirstChildElement("property"); 1234 while (propNode != nullptr) 1235 { 1236 const char *propertyName = 1237 propNode->Attribute("name"); 1238 BMCWEB_LOG_DEBUG << "Found property " 1239 << propertyName; 1240 if (propertyName == transaction->propertyName) 1241 { 1242 const char *argType = 1243 propNode->Attribute("type"); 1244 if (argType != nullptr) 1245 { 1246 sdbusplus::message::message m = 1247 crow::connections::systemBus 1248 ->new_method_call( 1249 connectionName.c_str(), 1250 transaction->objectPath 1251 .c_str(), 1252 "org.freedesktop.DBus." 1253 "Properties", 1254 "Set"); 1255 m.append(interfaceName, 1256 transaction->propertyName); 1257 int r = sd_bus_message_open_container( 1258 m.get(), SD_BUS_TYPE_VARIANT, 1259 argType); 1260 if (r < 0) 1261 { 1262 transaction->setErrorStatus( 1263 "Unexpected Error"); 1264 return; 1265 } 1266 r = convertJsonToDbus( 1267 m.get(), argType, 1268 transaction->propertyValue); 1269 if (r < 0) 1270 { 1271 transaction->setErrorStatus( 1272 "Invalid arg type"); 1273 return; 1274 } 1275 r = sd_bus_message_close_container( 1276 m.get()); 1277 if (r < 0) 1278 { 1279 transaction->setErrorStatus( 1280 "Unexpected Error"); 1281 return; 1282 } 1283 1284 crow::connections::systemBus 1285 ->async_send( 1286 m, 1287 [transaction]( 1288 boost::system::error_code 1289 ec, 1290 sdbusplus::message::message 1291 &m) { 1292 BMCWEB_LOG_DEBUG << "sent"; 1293 if (ec) 1294 { 1295 setErrorResponse( 1296 transaction->res, 1297 boost::beast::http:: 1298 status:: 1299 forbidden, 1300 forbiddenPropDesc, 1301 ec.message()); 1302 } 1303 else 1304 { 1305 transaction->res 1306 .jsonValue = { 1307 {"status", "ok"}, 1308 {"message", 1309 "200 OK"}, 1310 {"data", nullptr}}; 1311 } 1312 }); 1313 } 1314 } 1315 propNode = 1316 propNode->NextSiblingElement("property"); 1317 } 1318 ifaceNode = 1319 ifaceNode->NextSiblingElement("interface"); 1320 } 1321 }, 1322 connectionName, transaction->objectPath, 1323 "org.freedesktop.DBus.Introspectable", "Introspect"); 1324 } 1325 }, 1326 "xyz.openbmc_project.ObjectMapper", 1327 "/xyz/openbmc_project/object_mapper", 1328 "xyz.openbmc_project.ObjectMapper", "GetObject", 1329 transaction->objectPath, std::array<std::string, 0>()); 1330 } 1331 1332 inline void handleDBusUrl(const crow::Request &req, crow::Response &res, 1333 std::string &objectPath) 1334 { 1335 1336 // If accessing a single attribute, fill in and update objectPath, 1337 // otherwise leave destProperty blank 1338 std::string destProperty = ""; 1339 const char *attrSeperator = "/attr/"; 1340 size_t attrPosition = objectPath.find(attrSeperator); 1341 if (attrPosition != objectPath.npos) 1342 { 1343 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 1344 objectPath.length()); 1345 objectPath = objectPath.substr(0, attrPosition); 1346 } 1347 1348 if (req.method() == "POST"_method) 1349 { 1350 constexpr const char *actionSeperator = "/action/"; 1351 size_t actionPosition = objectPath.find(actionSeperator); 1352 if (actionPosition != objectPath.npos) 1353 { 1354 std::string postProperty = 1355 objectPath.substr((actionPosition + strlen(actionSeperator)), 1356 objectPath.length()); 1357 objectPath = objectPath.substr(0, actionPosition); 1358 handleAction(req, res, objectPath, postProperty); 1359 return; 1360 } 1361 } 1362 else if (req.method() == "GET"_method) 1363 { 1364 if (boost::ends_with(objectPath, "/enumerate")) 1365 { 1366 objectPath.erase(objectPath.end() - sizeof("enumerate"), 1367 objectPath.end()); 1368 handleEnumerate(res, objectPath); 1369 } 1370 else if (boost::ends_with(objectPath, "/list")) 1371 { 1372 objectPath.erase(objectPath.end() - sizeof("list"), 1373 objectPath.end()); 1374 handleList(res, objectPath); 1375 } 1376 else 1377 { 1378 // Trim any trailing "/" at the end 1379 if (boost::ends_with(objectPath, "/")) 1380 { 1381 objectPath.pop_back(); 1382 handleList(res, objectPath, 1); 1383 } 1384 else 1385 { 1386 handleGet(res, objectPath, destProperty); 1387 } 1388 } 1389 return; 1390 } 1391 else if (req.method() == "PUT"_method) 1392 { 1393 handlePut(req, res, objectPath, destProperty); 1394 return; 1395 } 1396 1397 setErrorResponse(res, boost::beast::http::status::method_not_allowed, 1398 methodNotAllowedDesc, methodNotAllowedMsg); 1399 res.end(); 1400 } 1401 1402 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) 1403 { 1404 BMCWEB_ROUTE(app, "/bus/") 1405 .methods("GET"_method)( 1406 [](const crow::Request &req, crow::Response &res) { 1407 res.jsonValue = {{"busses", {{{"name", "system"}}}}, 1408 {"status", "ok"}}; 1409 res.end(); 1410 }); 1411 1412 BMCWEB_ROUTE(app, "/bus/system/") 1413 .methods("GET"_method)( 1414 [](const crow::Request &req, crow::Response &res) { 1415 auto myCallback = [&res](const boost::system::error_code ec, 1416 std::vector<std::string> &names) { 1417 if (ec) 1418 { 1419 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 1420 res.result( 1421 boost::beast::http::status::internal_server_error); 1422 } 1423 else 1424 { 1425 std::sort(names.begin(), names.end()); 1426 res.jsonValue = {{"status", "ok"}}; 1427 auto &objectsSub = res.jsonValue["objects"]; 1428 for (auto &name : names) 1429 { 1430 objectsSub.push_back({{"name", name}}); 1431 } 1432 } 1433 res.end(); 1434 }; 1435 crow::connections::systemBus->async_method_call( 1436 std::move(myCallback), "org.freedesktop.DBus", "/", 1437 "org.freedesktop.DBus", "ListNames"); 1438 }); 1439 1440 BMCWEB_ROUTE(app, "/list/") 1441 .methods("GET"_method)( 1442 [](const crow::Request &req, crow::Response &res) { 1443 handleList(res, "/"); 1444 }); 1445 1446 BMCWEB_ROUTE(app, "/xyz/<path>") 1447 .methods("GET"_method, "PUT"_method, "POST"_method)( 1448 [](const crow::Request &req, crow::Response &res, 1449 const std::string &path) { 1450 std::string objectPath = "/xyz/" + path; 1451 handleDBusUrl(req, res, objectPath); 1452 }); 1453 1454 BMCWEB_ROUTE(app, "/org/<path>") 1455 .methods("GET"_method, "PUT"_method, "POST"_method)( 1456 [](const crow::Request &req, crow::Response &res, 1457 const std::string &path) { 1458 std::string objectPath = "/org/" + path; 1459 handleDBusUrl(req, res, objectPath); 1460 }); 1461 1462 BMCWEB_ROUTE(app, "/download/dump/<str>/") 1463 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 1464 const std::string &dumpId) { 1465 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); 1466 if (!std::regex_match(dumpId, validFilename)) 1467 { 1468 res.result(boost::beast::http::status::not_found); 1469 res.end(); 1470 return; 1471 } 1472 std::experimental::filesystem::path loc( 1473 "/var/lib/phosphor-debug-collector/dumps"); 1474 1475 loc += dumpId; 1476 1477 if (!std::experimental::filesystem::exists(loc) || 1478 !std::experimental::filesystem::is_directory(loc)) 1479 { 1480 res.result(boost::beast::http::status::not_found); 1481 res.end(); 1482 return; 1483 } 1484 std::experimental::filesystem::directory_iterator files(loc); 1485 for (auto &file : files) 1486 { 1487 std::ifstream readFile(file.path()); 1488 if (readFile.good()) 1489 { 1490 continue; 1491 } 1492 res.addHeader("Content-Type", "application/octet-stream"); 1493 res.body() = {std::istreambuf_iterator<char>(readFile), 1494 std::istreambuf_iterator<char>()}; 1495 res.end(); 1496 } 1497 res.result(boost::beast::http::status::not_found); 1498 res.end(); 1499 return; 1500 }); 1501 1502 BMCWEB_ROUTE(app, "/bus/system/<str>/") 1503 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 1504 const std::string &Connection) { 1505 introspectObjects(Connection, "/", 1506 std::make_shared<bmcweb::AsyncResp>(res)); 1507 }); 1508 1509 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 1510 .methods("GET"_method, 1511 "POST"_method)([](const crow::Request &req, 1512 crow::Response &res, 1513 const std::string &processName, 1514 const std::string &requestedPath) { 1515 std::vector<std::string> strs; 1516 boost::split(strs, requestedPath, boost::is_any_of("/")); 1517 std::string objectPath; 1518 std::string interfaceName; 1519 std::string methodName; 1520 auto it = strs.begin(); 1521 if (it == strs.end()) 1522 { 1523 objectPath = "/"; 1524 } 1525 while (it != strs.end()) 1526 { 1527 // Check if segment contains ".". If it does, it must be an 1528 // interface 1529 if (it->find(".") != std::string::npos) 1530 { 1531 break; 1532 // This check is neccesary as the trailing slash gets parsed 1533 // as part of our <path> specifier above, which causes the 1534 // normal trailing backslash redirector to fail. 1535 } 1536 else if (!it->empty()) 1537 { 1538 objectPath += "/" + *it; 1539 } 1540 it++; 1541 } 1542 if (it != strs.end()) 1543 { 1544 interfaceName = *it; 1545 it++; 1546 1547 // after interface, we might have a method name 1548 if (it != strs.end()) 1549 { 1550 methodName = *it; 1551 it++; 1552 } 1553 } 1554 if (it != strs.end()) 1555 { 1556 // if there is more levels past the method name, something went 1557 // wrong, return not found 1558 res.result(boost::beast::http::status::not_found); 1559 res.end(); 1560 return; 1561 } 1562 if (interfaceName.empty()) 1563 { 1564 crow::connections::systemBus->async_method_call( 1565 [&, processName, 1566 objectPath](const boost::system::error_code ec, 1567 const std::string &introspect_xml) { 1568 if (ec) 1569 { 1570 BMCWEB_LOG_ERROR 1571 << "Introspect call failed with error: " 1572 << ec.message() 1573 << " on process: " << processName 1574 << " path: " << objectPath << "\n"; 1575 return; 1576 } 1577 tinyxml2::XMLDocument doc; 1578 1579 doc.Parse(introspect_xml.c_str()); 1580 tinyxml2::XMLNode *pRoot = 1581 doc.FirstChildElement("node"); 1582 if (pRoot == nullptr) 1583 { 1584 BMCWEB_LOG_ERROR << "XML document failed to parse " 1585 << processName << " " << objectPath 1586 << "\n"; 1587 res.jsonValue = {{"status", "XML parse error"}}; 1588 res.result(boost::beast::http::status:: 1589 internal_server_error); 1590 return; 1591 } 1592 1593 BMCWEB_LOG_DEBUG << introspect_xml; 1594 res.jsonValue = {{"status", "ok"}, 1595 {"bus_name", processName}, 1596 {"object_path", objectPath}}; 1597 nlohmann::json &interfacesArray = 1598 res.jsonValue["interfaces"]; 1599 interfacesArray = nlohmann::json::array(); 1600 tinyxml2::XMLElement *interface = 1601 pRoot->FirstChildElement("interface"); 1602 1603 while (interface != nullptr) 1604 { 1605 const char *ifaceName = 1606 interface->Attribute("name"); 1607 if (ifaceName != nullptr) 1608 { 1609 interfacesArray.push_back( 1610 {{"name", ifaceName}}); 1611 } 1612 1613 interface = 1614 interface->NextSiblingElement("interface"); 1615 } 1616 1617 res.end(); 1618 }, 1619 processName, objectPath, 1620 "org.freedesktop.DBus.Introspectable", "Introspect"); 1621 } 1622 else if (methodName.empty()) 1623 { 1624 crow::connections::systemBus->async_method_call( 1625 [&, processName, objectPath, 1626 interfaceName{std::move(interfaceName)}]( 1627 const boost::system::error_code ec, 1628 const std::string &introspect_xml) { 1629 if (ec) 1630 { 1631 BMCWEB_LOG_ERROR 1632 << "Introspect call failed with error: " 1633 << ec.message() 1634 << " on process: " << processName 1635 << " path: " << objectPath << "\n"; 1636 } 1637 else 1638 { 1639 tinyxml2::XMLDocument doc; 1640 1641 doc.Parse(introspect_xml.c_str()); 1642 tinyxml2::XMLNode *pRoot = 1643 doc.FirstChildElement("node"); 1644 if (pRoot == nullptr) 1645 { 1646 BMCWEB_LOG_ERROR 1647 << "XML document failed to parse " 1648 << processName << " " << objectPath << "\n"; 1649 res.result(boost::beast::http::status:: 1650 internal_server_error); 1651 } 1652 else 1653 { 1654 tinyxml2::XMLElement *node = 1655 pRoot->FirstChildElement("node"); 1656 1657 // if we know we're the only call, build the 1658 // json directly 1659 tinyxml2::XMLElement *interface = 1660 pRoot->FirstChildElement("interface"); 1661 1662 res.jsonValue = { 1663 {"status", "ok"}, 1664 {"bus_name", processName}, 1665 {"interface", interfaceName}, 1666 {"object_path", objectPath}, 1667 {"properties", nlohmann::json::object()}}; 1668 1669 nlohmann::json &methodsArray = 1670 res.jsonValue["methods"]; 1671 methodsArray = nlohmann::json::array(); 1672 1673 nlohmann::json &signalsArray = 1674 res.jsonValue["signals"]; 1675 signalsArray = nlohmann::json::array(); 1676 1677 while (interface != nullptr) 1678 { 1679 const char *ifaceName = 1680 interface->Attribute("name"); 1681 1682 if (ifaceName != nullptr && 1683 ifaceName == interfaceName) 1684 { 1685 tinyxml2::XMLElement *methods = 1686 interface->FirstChildElement( 1687 "method"); 1688 while (methods != nullptr) 1689 { 1690 nlohmann::json argsArray = 1691 nlohmann::json::array(); 1692 tinyxml2::XMLElement *arg = 1693 methods->FirstChildElement( 1694 "arg"); 1695 while (arg != nullptr) 1696 { 1697 nlohmann::json thisArg; 1698 for (const char *fieldName : 1699 std::array<const char *, 1700 3>{"name", 1701 "direction", 1702 "type"}) 1703 { 1704 const char *fieldValue = 1705 arg->Attribute( 1706 fieldName); 1707 if (fieldValue != nullptr) 1708 { 1709 thisArg[fieldName] = 1710 fieldValue; 1711 } 1712 } 1713 argsArray.push_back( 1714 std::move(thisArg)); 1715 arg = arg->NextSiblingElement( 1716 "arg"); 1717 } 1718 1719 const char *name = 1720 methods->Attribute("name"); 1721 if (name != nullptr) 1722 { 1723 methodsArray.push_back( 1724 {{"name", name}, 1725 {"uri", "/bus/system/" + 1726 processName + 1727 objectPath + 1728 "/" + 1729 interfaceName + 1730 "/" + name}, 1731 {"args", argsArray}}); 1732 } 1733 methods = 1734 methods->NextSiblingElement( 1735 "method"); 1736 } 1737 tinyxml2::XMLElement *signals = 1738 interface->FirstChildElement( 1739 "signal"); 1740 while (signals != nullptr) 1741 { 1742 nlohmann::json argsArray = 1743 nlohmann::json::array(); 1744 1745 tinyxml2::XMLElement *arg = 1746 signals->FirstChildElement( 1747 "arg"); 1748 while (arg != nullptr) 1749 { 1750 const char *name = 1751 arg->Attribute("name"); 1752 const char *type = 1753 arg->Attribute("type"); 1754 if (name != nullptr && 1755 type != nullptr) 1756 { 1757 argsArray.push_back({ 1758 {"name", name}, 1759 {"type", type}, 1760 }); 1761 } 1762 arg = arg->NextSiblingElement( 1763 "arg"); 1764 } 1765 const char *name = 1766 signals->Attribute("name"); 1767 if (name != nullptr) 1768 { 1769 signalsArray.push_back( 1770 {{"name", name}, 1771 {"args", argsArray}}); 1772 } 1773 1774 signals = 1775 signals->NextSiblingElement( 1776 "signal"); 1777 } 1778 1779 break; 1780 } 1781 1782 interface = interface->NextSiblingElement( 1783 "interface"); 1784 } 1785 if (interface == nullptr) 1786 { 1787 // if we got to the end of the list and 1788 // never found a match, throw 404 1789 res.result( 1790 boost::beast::http::status::not_found); 1791 } 1792 } 1793 } 1794 res.end(); 1795 }, 1796 processName, objectPath, 1797 "org.freedesktop.DBus.Introspectable", "Introspect"); 1798 } 1799 else 1800 { 1801 if (req.method() != "POST"_method) 1802 { 1803 res.result(boost::beast::http::status::not_found); 1804 res.end(); 1805 return; 1806 } 1807 1808 nlohmann::json requestDbusData = 1809 nlohmann::json::parse(req.body, nullptr, false); 1810 1811 if (requestDbusData.is_discarded()) 1812 { 1813 res.result(boost::beast::http::status::bad_request); 1814 res.end(); 1815 return; 1816 } 1817 if (!requestDbusData.is_array()) 1818 { 1819 res.result(boost::beast::http::status::bad_request); 1820 res.end(); 1821 return; 1822 } 1823 auto transaction = std::make_shared<InProgressActionData>(res); 1824 1825 transaction->path = objectPath; 1826 transaction->methodName = methodName; 1827 transaction->arguments = std::move(requestDbusData); 1828 1829 findActionOnInterface(transaction, processName); 1830 } 1831 }); 1832 } 1833 } // namespace openbmc_mapper 1834 } // namespace crow 1835