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 std::string interfaceName; 421 nlohmann::json arguments; 422 }; 423 424 std::vector<std::string> dbusArgSplit(const std::string &string) 425 { 426 std::vector<std::string> ret; 427 if (string.empty()) 428 { 429 return ret; 430 } 431 ret.push_back(""); 432 int containerDepth = 0; 433 434 for (std::string::const_iterator character = string.begin(); 435 character != string.end(); character++) 436 { 437 ret.back() += *character; 438 switch (*character) 439 { 440 case ('a'): 441 break; 442 case ('('): 443 case ('{'): 444 containerDepth++; 445 break; 446 case ('}'): 447 case (')'): 448 containerDepth--; 449 if (containerDepth == 0) 450 { 451 if (character + 1 != string.end()) 452 { 453 ret.push_back(""); 454 } 455 } 456 break; 457 default: 458 if (containerDepth == 0) 459 { 460 if (character + 1 != string.end()) 461 { 462 ret.push_back(""); 463 } 464 } 465 break; 466 } 467 } 468 } 469 470 int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type, 471 const nlohmann::json &input_json) 472 { 473 int r = 0; 474 BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() 475 << " to type: " << arg_type; 476 const std::vector<std::string> argTypes = dbusArgSplit(arg_type); 477 478 // Assume a single object for now. 479 const nlohmann::json *j = &input_json; 480 nlohmann::json::const_iterator jIt = input_json.begin(); 481 482 for (const std::string &argCode : argTypes) 483 { 484 // If we are decoding multiple objects, grab the pointer to the 485 // iterator, and increment it for the next loop 486 if (argTypes.size() > 1) 487 { 488 if (jIt == input_json.end()) 489 { 490 return -2; 491 } 492 j = &*jIt; 493 jIt++; 494 } 495 const int64_t *intValue = j->get_ptr<const int64_t *>(); 496 const uint64_t *uintValue = j->get_ptr<const uint64_t *>(); 497 const std::string *stringValue = j->get_ptr<const std::string *>(); 498 const double *doubleValue = j->get_ptr<const double *>(); 499 const bool *b = j->get_ptr<const bool *>(); 500 int64_t v = 0; 501 double d = 0.0; 502 503 // Do some basic type conversions that make sense. uint can be 504 // converted to int. int and uint can be converted to double 505 if (uintValue != nullptr && intValue == nullptr) 506 { 507 v = static_cast<int64_t>(*uintValue); 508 intValue = &v; 509 } 510 if (uintValue != nullptr && doubleValue == nullptr) 511 { 512 d = static_cast<double>(*uintValue); 513 doubleValue = &d; 514 } 515 if (intValue != nullptr && doubleValue == nullptr) 516 { 517 d = static_cast<double>(*intValue); 518 doubleValue = &d; 519 } 520 521 if (argCode == "s") 522 { 523 if (stringValue == nullptr) 524 { 525 return -1; 526 } 527 r = sd_bus_message_append_basic(m, argCode[0], 528 (void *)stringValue->c_str()); 529 if (r < 0) 530 { 531 return r; 532 } 533 } 534 else if (argCode == "i") 535 { 536 if (intValue == nullptr) 537 { 538 return -1; 539 } 540 int32_t i = static_cast<int32_t>(*intValue); 541 r = sd_bus_message_append_basic(m, argCode[0], &i); 542 if (r < 0) 543 { 544 return r; 545 } 546 } 547 else if (argCode == "b") 548 { 549 // lots of ways bool could be represented here. Try them all 550 int boolInt = false; 551 if (intValue != nullptr) 552 { 553 boolInt = *intValue > 0 ? 1 : 0; 554 } 555 else if (b != nullptr) 556 { 557 boolInt = b ? 1 : 0; 558 } 559 else if (stringValue != nullptr) 560 { 561 boolInt = boost::istarts_with(*stringValue, "t") ? 1 : 0; 562 } 563 else 564 { 565 return -1; 566 } 567 r = sd_bus_message_append_basic(m, argCode[0], &boolInt); 568 if (r < 0) 569 { 570 return r; 571 } 572 } 573 else if (argCode == "n") 574 { 575 if (intValue == nullptr) 576 { 577 return -1; 578 } 579 int16_t n = static_cast<int16_t>(*intValue); 580 r = sd_bus_message_append_basic(m, argCode[0], &n); 581 if (r < 0) 582 { 583 return r; 584 } 585 } 586 else if (argCode == "x") 587 { 588 if (intValue == nullptr) 589 { 590 return -1; 591 } 592 r = sd_bus_message_append_basic(m, argCode[0], intValue); 593 if (r < 0) 594 { 595 return r; 596 } 597 } 598 else if (argCode == "y") 599 { 600 if (uintValue == nullptr) 601 { 602 return -1; 603 } 604 uint8_t y = static_cast<uint8_t>(*uintValue); 605 r = sd_bus_message_append_basic(m, argCode[0], &y); 606 } 607 else if (argCode == "q") 608 { 609 if (uintValue == nullptr) 610 { 611 return -1; 612 } 613 uint16_t q = static_cast<uint16_t>(*uintValue); 614 r = sd_bus_message_append_basic(m, argCode[0], &q); 615 } 616 else if (argCode == "u") 617 { 618 if (uintValue == nullptr) 619 { 620 return -1; 621 } 622 uint32_t u = static_cast<uint32_t>(*uintValue); 623 r = sd_bus_message_append_basic(m, argCode[0], &u); 624 } 625 else if (argCode == "t") 626 { 627 if (uintValue == nullptr) 628 { 629 return -1; 630 } 631 r = sd_bus_message_append_basic(m, argCode[0], uintValue); 632 } 633 else if (argCode == "d") 634 { 635 sd_bus_message_append_basic(m, argCode[0], doubleValue); 636 } 637 else if (boost::starts_with(argCode, "a")) 638 { 639 std::string containedType = argCode.substr(1); 640 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, 641 containedType.c_str()); 642 if (r < 0) 643 { 644 return r; 645 } 646 647 for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); 648 ++it) 649 { 650 r = convertJsonToDbus(m, containedType, *it); 651 if (r < 0) 652 { 653 return r; 654 } 655 } 656 sd_bus_message_close_container(m); 657 } 658 else if (boost::starts_with(argCode, "v")) 659 { 660 std::string containedType = argCode.substr(1); 661 BMCWEB_LOG_DEBUG << "variant type: " << argCode 662 << " appending variant of type: " << containedType; 663 r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, 664 containedType.c_str()); 665 if (r < 0) 666 { 667 return r; 668 } 669 670 r = convertJsonToDbus(m, containedType, input_json); 671 if (r < 0) 672 { 673 return r; 674 } 675 676 r = sd_bus_message_close_container(m); 677 if (r < 0) 678 { 679 return r; 680 } 681 } 682 else if (boost::starts_with(argCode, "(") && 683 boost::ends_with(argCode, ")")) 684 { 685 std::string containedType = argCode.substr(1, argCode.size() - 1); 686 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, 687 containedType.c_str()); 688 nlohmann::json::const_iterator it = j->begin(); 689 for (const std::string &argCode : dbusArgSplit(arg_type)) 690 { 691 if (it == j->end()) 692 { 693 return -1; 694 } 695 r = convertJsonToDbus(m, argCode, *it); 696 if (r < 0) 697 { 698 return r; 699 } 700 it++; 701 } 702 r = sd_bus_message_close_container(m); 703 } 704 else if (boost::starts_with(argCode, "{") && 705 boost::ends_with(argCode, "}")) 706 { 707 std::string containedType = argCode.substr(1, argCode.size() - 1); 708 r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, 709 containedType.c_str()); 710 std::vector<std::string> codes = dbusArgSplit(containedType); 711 if (codes.size() != 2) 712 { 713 return -1; 714 } 715 const std::string &key_type = codes[0]; 716 const std::string &value_type = codes[1]; 717 for (auto it : j->items()) 718 { 719 r = convertJsonToDbus(m, key_type, it.key()); 720 if (r < 0) 721 { 722 return r; 723 } 724 725 r = convertJsonToDbus(m, value_type, it.value()); 726 if (r < 0) 727 { 728 return r; 729 } 730 } 731 r = sd_bus_message_close_container(m); 732 } 733 else 734 { 735 return -2; 736 } 737 if (r < 0) 738 { 739 return r; 740 } 741 742 if (argTypes.size() > 1) 743 { 744 jIt++; 745 } 746 } 747 } 748 749 void findActionOnInterface(std::shared_ptr<InProgressActionData> transaction, 750 const std::string &connectionName) 751 { 752 BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " 753 << connectionName; 754 crow::connections::systemBus->async_method_call( 755 [transaction, connectionName{std::string(connectionName)}]( 756 const boost::system::error_code ec, 757 const std::string &introspect_xml) { 758 BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; 759 if (ec) 760 { 761 BMCWEB_LOG_ERROR 762 << "Introspect call failed with error: " << ec.message() 763 << " on process: " << connectionName << "\n"; 764 } 765 else 766 { 767 tinyxml2::XMLDocument doc; 768 769 doc.Parse(introspect_xml.data(), introspect_xml.size()); 770 tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); 771 if (pRoot == nullptr) 772 { 773 BMCWEB_LOG_ERROR << "XML document failed to parse " 774 << connectionName << "\n"; 775 return; 776 } 777 tinyxml2::XMLElement *interfaceNode = 778 pRoot->FirstChildElement("interface"); 779 while (interfaceNode != nullptr) 780 { 781 const char *thisInterfaceName = 782 interfaceNode->Attribute("name"); 783 if (thisInterfaceName != nullptr) 784 { 785 if (!transaction->interfaceName.empty() && 786 (transaction->interfaceName != thisInterfaceName)) 787 { 788 interfaceNode = 789 interfaceNode->NextSiblingElement("interface"); 790 continue; 791 } 792 793 tinyxml2::XMLElement *methodNode = 794 interfaceNode->FirstChildElement("method"); 795 while (methodNode != nullptr) 796 { 797 const char *thisMethodName = 798 methodNode->Attribute("name"); 799 BMCWEB_LOG_DEBUG << "Found method: " 800 << thisMethodName; 801 if (thisMethodName != nullptr && 802 thisMethodName == transaction->methodName) 803 { 804 BMCWEB_LOG_DEBUG 805 << "Found method named " << thisMethodName 806 << " on interface " << thisInterfaceName; 807 sdbusplus::message::message m = 808 crow::connections::systemBus 809 ->new_method_call( 810 connectionName.c_str(), 811 transaction->path.c_str(), 812 thisInterfaceName, 813 transaction->methodName.c_str()); 814 815 tinyxml2::XMLElement *argumentNode = 816 methodNode->FirstChildElement("arg"); 817 818 nlohmann::json::const_iterator argIt = 819 transaction->arguments.begin(); 820 821 while (argumentNode != nullptr) 822 { 823 const char *argDirection = 824 argumentNode->Attribute("direction"); 825 const char *argType = 826 argumentNode->Attribute("type"); 827 if (argDirection != nullptr && 828 argType != nullptr && 829 std::string(argDirection) == "in") 830 { 831 if (argIt == 832 transaction->arguments.end()) 833 { 834 transaction->setErrorStatus( 835 "Invalid method args"); 836 return; 837 } 838 if (convertJsonToDbus( 839 m.get(), std::string(argType), 840 *argIt) < 0) 841 { 842 transaction->setErrorStatus( 843 "Invalid method arg type"); 844 return; 845 } 846 847 argIt++; 848 } 849 argumentNode = 850 argumentNode->NextSiblingElement("arg"); 851 } 852 853 crow::connections::systemBus->async_send( 854 m, [transaction]( 855 boost::system::error_code ec, 856 sdbusplus::message::message &m) { 857 if (ec) 858 { 859 transaction->setErrorStatus( 860 "Method call failed"); 861 return; 862 } 863 transaction->res.jsonValue = { 864 {"status", "ok"}, 865 {"message", "200 OK"}, 866 {"data", nullptr}}; 867 }); 868 break; 869 } 870 methodNode = 871 methodNode->NextSiblingElement("method"); 872 } 873 } 874 interfaceNode = 875 interfaceNode->NextSiblingElement("interface"); 876 } 877 } 878 }, 879 connectionName, transaction->path, 880 "org.freedesktop.DBus.Introspectable", "Introspect"); 881 } 882 883 void handleAction(const crow::Request &req, crow::Response &res, 884 const std::string &objectPath, const std::string &methodName) 885 { 886 BMCWEB_LOG_DEBUG << "handleAction on path: " << objectPath << " and method " 887 << methodName; 888 nlohmann::json requestDbusData = 889 nlohmann::json::parse(req.body, nullptr, false); 890 891 if (requestDbusData.is_discarded()) 892 { 893 setErrorResponse(res, boost::beast::http::status::bad_request, 894 noJsonDesc, badReqMsg); 895 res.end(); 896 return; 897 } 898 nlohmann::json::iterator data = requestDbusData.find("data"); 899 if (data == requestDbusData.end()) 900 { 901 setErrorResponse(res, boost::beast::http::status::bad_request, 902 noJsonDesc, badReqMsg); 903 res.end(); 904 return; 905 } 906 907 if (!data->is_array()) 908 { 909 setErrorResponse(res, boost::beast::http::status::bad_request, 910 noJsonDesc, badReqMsg); 911 res.end(); 912 return; 913 } 914 auto transaction = std::make_shared<InProgressActionData>(res); 915 916 transaction->path = objectPath; 917 transaction->methodName = methodName; 918 transaction->arguments = std::move(*data); 919 crow::connections::systemBus->async_method_call( 920 [transaction]( 921 const boost::system::error_code ec, 922 const std::vector<std::pair<std::string, std::vector<std::string>>> 923 &interfaceNames) { 924 if (ec || interfaceNames.size() <= 0) 925 { 926 BMCWEB_LOG_ERROR << "Can't find object"; 927 setErrorResponse(transaction->res, 928 boost::beast::http::status::not_found, 929 notFoundDesc, notFoundMsg); 930 return; 931 } 932 933 BMCWEB_LOG_DEBUG << "GetObject returned " << interfaceNames.size() 934 << " object(s)"; 935 936 for (const std::pair<std::string, std::vector<std::string>> 937 &object : interfaceNames) 938 { 939 findActionOnInterface(transaction, object.first); 940 } 941 }, 942 "xyz.openbmc_project.ObjectMapper", 943 "/xyz/openbmc_project/object_mapper", 944 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 945 std::array<std::string, 0>()); 946 } 947 948 void handleDelete(const crow::Request &req, crow::Response &res, 949 const std::string &objectPath) 950 { 951 BMCWEB_LOG_DEBUG << "handleDelete on path: " << objectPath; 952 953 crow::connections::systemBus->async_method_call( 954 [&res, objectPath]( 955 const boost::system::error_code ec, 956 const std::vector<std::pair<std::string, std::vector<std::string>>> 957 &interfaceNames) { 958 if (ec || interfaceNames.size() <= 0) 959 { 960 BMCWEB_LOG_ERROR << "Can't find object"; 961 setErrorResponse(res, boost::beast::http::status::not_found, 962 notFoundDesc, notFoundMsg); 963 res.end(); 964 return; 965 } 966 967 auto transaction = std::make_shared<InProgressActionData>(res); 968 transaction->path = objectPath; 969 transaction->methodName = "Delete"; 970 transaction->interfaceName = "xyz.openbmc_project.Object.Delete"; 971 972 for (const std::pair<std::string, std::vector<std::string>> 973 &object : interfaceNames) 974 { 975 findActionOnInterface(transaction, object.first); 976 } 977 }, 978 "xyz.openbmc_project.ObjectMapper", 979 "/xyz/openbmc_project/object_mapper", 980 "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, 981 std::array<const char *, 0>()); 982 } 983 984 void handleList(crow::Response &res, const std::string &objectPath, 985 int32_t depth = 0) 986 { 987 crow::connections::systemBus->async_method_call( 988 [&res](const boost::system::error_code ec, 989 std::vector<std::string> &objectPaths) { 990 if (ec) 991 { 992 setErrorResponse(res, boost::beast::http::status::not_found, 993 notFoundDesc, notFoundMsg); 994 } 995 else 996 { 997 res.jsonValue = {{"status", "ok"}, 998 {"message", "200 OK"}, 999 {"data", std::move(objectPaths)}}; 1000 } 1001 res.end(); 1002 }, 1003 "xyz.openbmc_project.ObjectMapper", 1004 "/xyz/openbmc_project/object_mapper", 1005 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, 1006 depth, std::array<std::string, 0>()); 1007 } 1008 1009 void handleEnumerate(crow::Response &res, const std::string &objectPath) 1010 { 1011 BMCWEB_LOG_DEBUG << "Doing enumerate on " << objectPath; 1012 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(res); 1013 1014 asyncResp->res.jsonValue = {{"message", "200 OK"}, 1015 {"status", "ok"}, 1016 {"data", nlohmann::json::object()}}; 1017 1018 crow::connections::systemBus->async_method_call( 1019 [objectPath, asyncResp](const boost::system::error_code ec, 1020 GetSubTreeType &object_names) { 1021 auto transaction = std::make_shared<InProgressEnumerateData>( 1022 objectPath, asyncResp); 1023 1024 transaction->subtree = 1025 std::make_shared<GetSubTreeType>(std::move(object_names)); 1026 1027 if (ec) 1028 { 1029 BMCWEB_LOG_ERROR << "GetSubTree failed on " 1030 << transaction->objectPath; 1031 setErrorResponse(transaction->asyncResp->res, 1032 boost::beast::http::status::not_found, 1033 notFoundDesc, notFoundMsg); 1034 return; 1035 } 1036 1037 // Add the data for the path passed in to the results 1038 // as if GetSubTree returned it, and continue on enumerating 1039 getObjectAndEnumerate(transaction); 1040 }, 1041 "xyz.openbmc_project.ObjectMapper", 1042 "/xyz/openbmc_project/object_mapper", 1043 "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, 1044 static_cast<int32_t>(0), std::array<const char *, 0>()); 1045 } 1046 1047 void handleGet(crow::Response &res, std::string &objectPath, 1048 std::string &destProperty) 1049 { 1050 BMCWEB_LOG_DEBUG << "handleGet: " << objectPath << " prop:" << destProperty; 1051 std::shared_ptr<std::string> propertyName = 1052 std::make_shared<std::string>(std::move(destProperty)); 1053 1054 std::shared_ptr<std::string> path = 1055 std::make_shared<std::string>(std::move(objectPath)); 1056 1057 using GetObjectType = 1058 std::vector<std::pair<std::string, std::vector<std::string>>>; 1059 crow::connections::systemBus->async_method_call( 1060 [&res, path, propertyName](const boost::system::error_code ec, 1061 const GetObjectType &object_names) { 1062 if (ec || object_names.size() <= 0) 1063 { 1064 setErrorResponse(res, boost::beast::http::status::not_found, 1065 notFoundDesc, notFoundMsg); 1066 res.end(); 1067 return; 1068 } 1069 std::shared_ptr<nlohmann::json> response = 1070 std::make_shared<nlohmann::json>(nlohmann::json::object()); 1071 // The mapper should never give us an empty interface names list, 1072 // but check anyway 1073 for (const std::pair<std::string, std::vector<std::string>> 1074 connection : object_names) 1075 { 1076 const std::vector<std::string> &interfaceNames = 1077 connection.second; 1078 1079 if (interfaceNames.size() <= 0) 1080 { 1081 setErrorResponse(res, boost::beast::http::status::not_found, 1082 notFoundDesc, notFoundMsg); 1083 res.end(); 1084 return; 1085 } 1086 1087 for (const std::string &interface : interfaceNames) 1088 { 1089 crow::connections::systemBus->async_method_call( 1090 [&res, response, propertyName]( 1091 const boost::system::error_code ec, 1092 const std::vector<std::pair< 1093 std::string, dbus::utility::DbusVariantType>> 1094 &properties) { 1095 if (ec) 1096 { 1097 BMCWEB_LOG_ERROR << "Bad dbus request error: " 1098 << ec; 1099 } 1100 else 1101 { 1102 for (const std::pair< 1103 std::string, 1104 dbus::utility::DbusVariantType> 1105 &property : properties) 1106 { 1107 // if property name is empty, or matches our 1108 // search query, add it to the response json 1109 1110 if (propertyName->empty()) 1111 { 1112 sdbusplus::message::variant_ns::visit( 1113 [&response, &property](auto &&val) { 1114 (*response)[property.first] = 1115 val; 1116 }, 1117 property.second); 1118 } 1119 else if (property.first == *propertyName) 1120 { 1121 sdbusplus::message::variant_ns::visit( 1122 [&response](auto &&val) { 1123 (*response) = val; 1124 }, 1125 property.second); 1126 } 1127 } 1128 } 1129 if (response.use_count() == 1) 1130 { 1131 if (!propertyName->empty() && response->empty()) 1132 { 1133 setErrorResponse( 1134 res, 1135 boost::beast::http::status::not_found, 1136 propNotFoundDesc, notFoundMsg); 1137 } 1138 else 1139 { 1140 res.jsonValue = {{"status", "ok"}, 1141 {"message", "200 OK"}, 1142 {"data", *response}}; 1143 } 1144 res.end(); 1145 } 1146 }, 1147 connection.first, *path, 1148 "org.freedesktop.DBus.Properties", "GetAll", interface); 1149 } 1150 } 1151 }, 1152 "xyz.openbmc_project.ObjectMapper", 1153 "/xyz/openbmc_project/object_mapper", 1154 "xyz.openbmc_project.ObjectMapper", "GetObject", *path, 1155 std::array<std::string, 0>()); 1156 } 1157 1158 struct AsyncPutRequest 1159 { 1160 AsyncPutRequest(crow::Response &res) : res(res) 1161 { 1162 } 1163 ~AsyncPutRequest() 1164 { 1165 if (res.jsonValue.empty()) 1166 { 1167 setErrorResponse(res, boost::beast::http::status::forbidden, 1168 forbiddenMsg, forbiddenPropDesc); 1169 } 1170 1171 res.end(); 1172 } 1173 1174 void setErrorStatus(const std::string &desc) 1175 { 1176 setErrorResponse(res, boost::beast::http::status::internal_server_error, 1177 desc, badReqMsg); 1178 } 1179 1180 crow::Response &res; 1181 std::string objectPath; 1182 std::string propertyName; 1183 nlohmann::json propertyValue; 1184 }; 1185 1186 void handlePut(const crow::Request &req, crow::Response &res, 1187 const std::string &objectPath, const std::string &destProperty) 1188 { 1189 if (destProperty.empty()) 1190 { 1191 setErrorResponse(res, boost::beast::http::status::forbidden, 1192 forbiddenResDesc, forbiddenMsg); 1193 res.end(); 1194 return; 1195 } 1196 1197 nlohmann::json requestDbusData = 1198 nlohmann::json::parse(req.body, nullptr, false); 1199 1200 if (requestDbusData.is_discarded()) 1201 { 1202 setErrorResponse(res, boost::beast::http::status::bad_request, 1203 noJsonDesc, badReqMsg); 1204 res.end(); 1205 return; 1206 } 1207 1208 nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); 1209 if (propertyIt == requestDbusData.end()) 1210 { 1211 setErrorResponse(res, boost::beast::http::status::bad_request, 1212 noJsonDesc, badReqMsg); 1213 res.end(); 1214 return; 1215 } 1216 const nlohmann::json &propertySetValue = *propertyIt; 1217 auto transaction = std::make_shared<AsyncPutRequest>(res); 1218 transaction->objectPath = objectPath; 1219 transaction->propertyName = destProperty; 1220 transaction->propertyValue = propertySetValue; 1221 1222 using GetObjectType = 1223 std::vector<std::pair<std::string, std::vector<std::string>>>; 1224 1225 crow::connections::systemBus->async_method_call( 1226 [transaction](const boost::system::error_code ec, 1227 const GetObjectType &object_names) { 1228 if (!ec && object_names.size() <= 0) 1229 { 1230 setErrorResponse(transaction->res, 1231 boost::beast::http::status::not_found, 1232 propNotFoundDesc, notFoundMsg); 1233 return; 1234 } 1235 1236 for (const std::pair<std::string, std::vector<std::string>> 1237 connection : object_names) 1238 { 1239 const std::string &connectionName = connection.first; 1240 1241 crow::connections::systemBus->async_method_call( 1242 [connectionName{std::string(connectionName)}, 1243 transaction](const boost::system::error_code ec, 1244 const std::string &introspectXml) { 1245 if (ec) 1246 { 1247 BMCWEB_LOG_ERROR 1248 << "Introspect call failed with error: " 1249 << ec.message() 1250 << " on process: " << connectionName; 1251 transaction->setErrorStatus("Unexpected Error"); 1252 return; 1253 } 1254 tinyxml2::XMLDocument doc; 1255 1256 doc.Parse(introspectXml.c_str()); 1257 tinyxml2::XMLNode *pRoot = 1258 doc.FirstChildElement("node"); 1259 if (pRoot == nullptr) 1260 { 1261 BMCWEB_LOG_ERROR << "XML document failed to parse: " 1262 << introspectXml; 1263 transaction->setErrorStatus("Unexpected Error"); 1264 return; 1265 } 1266 tinyxml2::XMLElement *ifaceNode = 1267 pRoot->FirstChildElement("interface"); 1268 while (ifaceNode != nullptr) 1269 { 1270 const char *interfaceName = 1271 ifaceNode->Attribute("name"); 1272 BMCWEB_LOG_DEBUG << "found interface " 1273 << interfaceName; 1274 tinyxml2::XMLElement *propNode = 1275 ifaceNode->FirstChildElement("property"); 1276 while (propNode != nullptr) 1277 { 1278 const char *propertyName = 1279 propNode->Attribute("name"); 1280 BMCWEB_LOG_DEBUG << "Found property " 1281 << propertyName; 1282 if (propertyName == transaction->propertyName) 1283 { 1284 const char *argType = 1285 propNode->Attribute("type"); 1286 if (argType != nullptr) 1287 { 1288 sdbusplus::message::message m = 1289 crow::connections::systemBus 1290 ->new_method_call( 1291 connectionName.c_str(), 1292 transaction->objectPath 1293 .c_str(), 1294 "org.freedesktop.DBus." 1295 "Properties", 1296 "Set"); 1297 m.append(interfaceName, 1298 transaction->propertyName); 1299 int r = sd_bus_message_open_container( 1300 m.get(), SD_BUS_TYPE_VARIANT, 1301 argType); 1302 if (r < 0) 1303 { 1304 transaction->setErrorStatus( 1305 "Unexpected Error"); 1306 return; 1307 } 1308 r = convertJsonToDbus( 1309 m.get(), argType, 1310 transaction->propertyValue); 1311 if (r < 0) 1312 { 1313 transaction->setErrorStatus( 1314 "Invalid arg type"); 1315 return; 1316 } 1317 r = sd_bus_message_close_container( 1318 m.get()); 1319 if (r < 0) 1320 { 1321 transaction->setErrorStatus( 1322 "Unexpected Error"); 1323 return; 1324 } 1325 1326 crow::connections::systemBus 1327 ->async_send( 1328 m, 1329 [transaction]( 1330 boost::system::error_code 1331 ec, 1332 sdbusplus::message::message 1333 &m) { 1334 BMCWEB_LOG_DEBUG << "sent"; 1335 if (ec) 1336 { 1337 setErrorResponse( 1338 transaction->res, 1339 boost::beast::http:: 1340 status:: 1341 forbidden, 1342 forbiddenPropDesc, 1343 ec.message()); 1344 } 1345 else 1346 { 1347 transaction->res 1348 .jsonValue = { 1349 {"status", "ok"}, 1350 {"message", 1351 "200 OK"}, 1352 {"data", nullptr}}; 1353 } 1354 }); 1355 } 1356 } 1357 propNode = 1358 propNode->NextSiblingElement("property"); 1359 } 1360 ifaceNode = 1361 ifaceNode->NextSiblingElement("interface"); 1362 } 1363 }, 1364 connectionName, transaction->objectPath, 1365 "org.freedesktop.DBus.Introspectable", "Introspect"); 1366 } 1367 }, 1368 "xyz.openbmc_project.ObjectMapper", 1369 "/xyz/openbmc_project/object_mapper", 1370 "xyz.openbmc_project.ObjectMapper", "GetObject", 1371 transaction->objectPath, std::array<std::string, 0>()); 1372 } 1373 1374 inline void handleDBusUrl(const crow::Request &req, crow::Response &res, 1375 std::string &objectPath) 1376 { 1377 1378 // If accessing a single attribute, fill in and update objectPath, 1379 // otherwise leave destProperty blank 1380 std::string destProperty = ""; 1381 const char *attrSeperator = "/attr/"; 1382 size_t attrPosition = objectPath.find(attrSeperator); 1383 if (attrPosition != objectPath.npos) 1384 { 1385 destProperty = objectPath.substr(attrPosition + strlen(attrSeperator), 1386 objectPath.length()); 1387 objectPath = objectPath.substr(0, attrPosition); 1388 } 1389 1390 if (req.method() == "POST"_method) 1391 { 1392 constexpr const char *actionSeperator = "/action/"; 1393 size_t actionPosition = objectPath.find(actionSeperator); 1394 if (actionPosition != objectPath.npos) 1395 { 1396 std::string postProperty = 1397 objectPath.substr((actionPosition + strlen(actionSeperator)), 1398 objectPath.length()); 1399 objectPath = objectPath.substr(0, actionPosition); 1400 handleAction(req, res, objectPath, postProperty); 1401 return; 1402 } 1403 } 1404 else if (req.method() == "GET"_method) 1405 { 1406 if (boost::ends_with(objectPath, "/enumerate")) 1407 { 1408 objectPath.erase(objectPath.end() - sizeof("enumerate"), 1409 objectPath.end()); 1410 handleEnumerate(res, objectPath); 1411 } 1412 else if (boost::ends_with(objectPath, "/list")) 1413 { 1414 objectPath.erase(objectPath.end() - sizeof("list"), 1415 objectPath.end()); 1416 handleList(res, objectPath); 1417 } 1418 else 1419 { 1420 // Trim any trailing "/" at the end 1421 if (boost::ends_with(objectPath, "/")) 1422 { 1423 objectPath.pop_back(); 1424 handleList(res, objectPath, 1); 1425 } 1426 else 1427 { 1428 handleGet(res, objectPath, destProperty); 1429 } 1430 } 1431 return; 1432 } 1433 else if (req.method() == "PUT"_method) 1434 { 1435 handlePut(req, res, objectPath, destProperty); 1436 return; 1437 } 1438 else if (req.method() == "DELETE"_method) 1439 { 1440 handleDelete(req, res, objectPath); 1441 return; 1442 } 1443 1444 setErrorResponse(res, boost::beast::http::status::method_not_allowed, 1445 methodNotAllowedDesc, methodNotAllowedMsg); 1446 res.end(); 1447 } 1448 1449 template <typename... Middlewares> void requestRoutes(Crow<Middlewares...> &app) 1450 { 1451 BMCWEB_ROUTE(app, "/bus/") 1452 .methods("GET"_method)( 1453 [](const crow::Request &req, crow::Response &res) { 1454 res.jsonValue = {{"busses", {{{"name", "system"}}}}, 1455 {"status", "ok"}}; 1456 res.end(); 1457 }); 1458 1459 BMCWEB_ROUTE(app, "/bus/system/") 1460 .methods("GET"_method)( 1461 [](const crow::Request &req, crow::Response &res) { 1462 auto myCallback = [&res](const boost::system::error_code ec, 1463 std::vector<std::string> &names) { 1464 if (ec) 1465 { 1466 BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; 1467 res.result( 1468 boost::beast::http::status::internal_server_error); 1469 } 1470 else 1471 { 1472 std::sort(names.begin(), names.end()); 1473 res.jsonValue = {{"status", "ok"}}; 1474 auto &objectsSub = res.jsonValue["objects"]; 1475 for (auto &name : names) 1476 { 1477 objectsSub.push_back({{"name", name}}); 1478 } 1479 } 1480 res.end(); 1481 }; 1482 crow::connections::systemBus->async_method_call( 1483 std::move(myCallback), "org.freedesktop.DBus", "/", 1484 "org.freedesktop.DBus", "ListNames"); 1485 }); 1486 1487 BMCWEB_ROUTE(app, "/list/") 1488 .methods("GET"_method)( 1489 [](const crow::Request &req, crow::Response &res) { 1490 handleList(res, "/"); 1491 }); 1492 1493 BMCWEB_ROUTE(app, "/xyz/<path>") 1494 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)( 1495 [](const crow::Request &req, crow::Response &res, 1496 const std::string &path) { 1497 std::string objectPath = "/xyz/" + path; 1498 handleDBusUrl(req, res, objectPath); 1499 }); 1500 1501 BMCWEB_ROUTE(app, "/org/<path>") 1502 .methods("GET"_method, "PUT"_method, "POST"_method, "DELETE"_method)( 1503 [](const crow::Request &req, crow::Response &res, 1504 const std::string &path) { 1505 std::string objectPath = "/org/" + path; 1506 handleDBusUrl(req, res, objectPath); 1507 }); 1508 1509 BMCWEB_ROUTE(app, "/download/dump/<str>/") 1510 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 1511 const std::string &dumpId) { 1512 std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]*)$"); 1513 if (!std::regex_match(dumpId, validFilename)) 1514 { 1515 res.result(boost::beast::http::status::bad_request); 1516 res.end(); 1517 return; 1518 } 1519 std::experimental::filesystem::path loc( 1520 "/var/lib/phosphor-debug-collector/dumps"); 1521 1522 loc /= dumpId; 1523 1524 if (!std::experimental::filesystem::exists(loc) || 1525 !std::experimental::filesystem::is_directory(loc)) 1526 { 1527 BMCWEB_LOG_ERROR << loc << "Not found"; 1528 res.result(boost::beast::http::status::not_found); 1529 res.end(); 1530 return; 1531 } 1532 std::experimental::filesystem::directory_iterator files(loc); 1533 1534 for (auto &file : files) 1535 { 1536 std::ifstream readFile(file.path()); 1537 if (!readFile.good()) 1538 { 1539 continue; 1540 } 1541 res.addHeader("Content-Type", "application/octet-stream"); 1542 res.body() = {std::istreambuf_iterator<char>(readFile), 1543 std::istreambuf_iterator<char>()}; 1544 res.end(); 1545 return; 1546 } 1547 res.result(boost::beast::http::status::not_found); 1548 res.end(); 1549 return; 1550 }); 1551 1552 BMCWEB_ROUTE(app, "/bus/system/<str>/") 1553 .methods("GET"_method)([](const crow::Request &req, crow::Response &res, 1554 const std::string &Connection) { 1555 introspectObjects(Connection, "/", 1556 std::make_shared<bmcweb::AsyncResp>(res)); 1557 }); 1558 1559 BMCWEB_ROUTE(app, "/bus/system/<str>/<path>") 1560 .methods("GET"_method, 1561 "POST"_method)([](const crow::Request &req, 1562 crow::Response &res, 1563 const std::string &processName, 1564 const std::string &requestedPath) { 1565 std::vector<std::string> strs; 1566 boost::split(strs, requestedPath, boost::is_any_of("/")); 1567 std::string objectPath; 1568 std::string interfaceName; 1569 std::string methodName; 1570 auto it = strs.begin(); 1571 if (it == strs.end()) 1572 { 1573 objectPath = "/"; 1574 } 1575 while (it != strs.end()) 1576 { 1577 // Check if segment contains ".". If it does, it must be an 1578 // interface 1579 if (it->find(".") != std::string::npos) 1580 { 1581 break; 1582 // This check is neccesary as the trailing slash gets parsed 1583 // as part of our <path> specifier above, which causes the 1584 // normal trailing backslash redirector to fail. 1585 } 1586 else if (!it->empty()) 1587 { 1588 objectPath += "/" + *it; 1589 } 1590 it++; 1591 } 1592 if (it != strs.end()) 1593 { 1594 interfaceName = *it; 1595 it++; 1596 1597 // after interface, we might have a method name 1598 if (it != strs.end()) 1599 { 1600 methodName = *it; 1601 it++; 1602 } 1603 } 1604 if (it != strs.end()) 1605 { 1606 // if there is more levels past the method name, something went 1607 // wrong, return not found 1608 res.result(boost::beast::http::status::not_found); 1609 res.end(); 1610 return; 1611 } 1612 if (interfaceName.empty()) 1613 { 1614 crow::connections::systemBus->async_method_call( 1615 [&, processName, 1616 objectPath](const boost::system::error_code ec, 1617 const std::string &introspect_xml) { 1618 if (ec) 1619 { 1620 BMCWEB_LOG_ERROR 1621 << "Introspect call failed with error: " 1622 << ec.message() 1623 << " on process: " << processName 1624 << " path: " << objectPath << "\n"; 1625 return; 1626 } 1627 tinyxml2::XMLDocument doc; 1628 1629 doc.Parse(introspect_xml.c_str()); 1630 tinyxml2::XMLNode *pRoot = 1631 doc.FirstChildElement("node"); 1632 if (pRoot == nullptr) 1633 { 1634 BMCWEB_LOG_ERROR << "XML document failed to parse " 1635 << processName << " " << objectPath 1636 << "\n"; 1637 res.jsonValue = {{"status", "XML parse error"}}; 1638 res.result(boost::beast::http::status:: 1639 internal_server_error); 1640 return; 1641 } 1642 1643 BMCWEB_LOG_DEBUG << introspect_xml; 1644 res.jsonValue = {{"status", "ok"}, 1645 {"bus_name", processName}, 1646 {"object_path", objectPath}}; 1647 nlohmann::json &interfacesArray = 1648 res.jsonValue["interfaces"]; 1649 interfacesArray = nlohmann::json::array(); 1650 tinyxml2::XMLElement *interface = 1651 pRoot->FirstChildElement("interface"); 1652 1653 while (interface != nullptr) 1654 { 1655 const char *ifaceName = 1656 interface->Attribute("name"); 1657 if (ifaceName != nullptr) 1658 { 1659 interfacesArray.push_back( 1660 {{"name", ifaceName}}); 1661 } 1662 1663 interface = 1664 interface->NextSiblingElement("interface"); 1665 } 1666 1667 res.end(); 1668 }, 1669 processName, objectPath, 1670 "org.freedesktop.DBus.Introspectable", "Introspect"); 1671 } 1672 else if (methodName.empty()) 1673 { 1674 crow::connections::systemBus->async_method_call( 1675 [&, processName, objectPath, 1676 interfaceName{std::move(interfaceName)}]( 1677 const boost::system::error_code ec, 1678 const std::string &introspect_xml) { 1679 if (ec) 1680 { 1681 BMCWEB_LOG_ERROR 1682 << "Introspect call failed with error: " 1683 << ec.message() 1684 << " on process: " << processName 1685 << " path: " << objectPath << "\n"; 1686 } 1687 else 1688 { 1689 tinyxml2::XMLDocument doc; 1690 1691 doc.Parse(introspect_xml.c_str()); 1692 tinyxml2::XMLNode *pRoot = 1693 doc.FirstChildElement("node"); 1694 if (pRoot == nullptr) 1695 { 1696 BMCWEB_LOG_ERROR 1697 << "XML document failed to parse " 1698 << processName << " " << objectPath << "\n"; 1699 res.result(boost::beast::http::status:: 1700 internal_server_error); 1701 } 1702 else 1703 { 1704 tinyxml2::XMLElement *node = 1705 pRoot->FirstChildElement("node"); 1706 1707 // if we know we're the only call, build the 1708 // json directly 1709 tinyxml2::XMLElement *interface = 1710 pRoot->FirstChildElement("interface"); 1711 1712 res.jsonValue = { 1713 {"status", "ok"}, 1714 {"bus_name", processName}, 1715 {"interface", interfaceName}, 1716 {"object_path", objectPath}, 1717 {"properties", nlohmann::json::object()}}; 1718 1719 nlohmann::json &methodsArray = 1720 res.jsonValue["methods"]; 1721 methodsArray = nlohmann::json::array(); 1722 1723 nlohmann::json &signalsArray = 1724 res.jsonValue["signals"]; 1725 signalsArray = nlohmann::json::array(); 1726 1727 while (interface != nullptr) 1728 { 1729 const char *ifaceName = 1730 interface->Attribute("name"); 1731 1732 if (ifaceName != nullptr && 1733 ifaceName == interfaceName) 1734 { 1735 tinyxml2::XMLElement *methods = 1736 interface->FirstChildElement( 1737 "method"); 1738 while (methods != nullptr) 1739 { 1740 nlohmann::json argsArray = 1741 nlohmann::json::array(); 1742 tinyxml2::XMLElement *arg = 1743 methods->FirstChildElement( 1744 "arg"); 1745 while (arg != nullptr) 1746 { 1747 nlohmann::json thisArg; 1748 for (const char *fieldName : 1749 std::array<const char *, 1750 3>{"name", 1751 "direction", 1752 "type"}) 1753 { 1754 const char *fieldValue = 1755 arg->Attribute( 1756 fieldName); 1757 if (fieldValue != nullptr) 1758 { 1759 thisArg[fieldName] = 1760 fieldValue; 1761 } 1762 } 1763 argsArray.push_back( 1764 std::move(thisArg)); 1765 arg = arg->NextSiblingElement( 1766 "arg"); 1767 } 1768 1769 const char *name = 1770 methods->Attribute("name"); 1771 if (name != nullptr) 1772 { 1773 methodsArray.push_back( 1774 {{"name", name}, 1775 {"uri", "/bus/system/" + 1776 processName + 1777 objectPath + 1778 "/" + 1779 interfaceName + 1780 "/" + name}, 1781 {"args", argsArray}}); 1782 } 1783 methods = 1784 methods->NextSiblingElement( 1785 "method"); 1786 } 1787 tinyxml2::XMLElement *signals = 1788 interface->FirstChildElement( 1789 "signal"); 1790 while (signals != nullptr) 1791 { 1792 nlohmann::json argsArray = 1793 nlohmann::json::array(); 1794 1795 tinyxml2::XMLElement *arg = 1796 signals->FirstChildElement( 1797 "arg"); 1798 while (arg != nullptr) 1799 { 1800 const char *name = 1801 arg->Attribute("name"); 1802 const char *type = 1803 arg->Attribute("type"); 1804 if (name != nullptr && 1805 type != nullptr) 1806 { 1807 argsArray.push_back({ 1808 {"name", name}, 1809 {"type", type}, 1810 }); 1811 } 1812 arg = arg->NextSiblingElement( 1813 "arg"); 1814 } 1815 const char *name = 1816 signals->Attribute("name"); 1817 if (name != nullptr) 1818 { 1819 signalsArray.push_back( 1820 {{"name", name}, 1821 {"args", argsArray}}); 1822 } 1823 1824 signals = 1825 signals->NextSiblingElement( 1826 "signal"); 1827 } 1828 1829 break; 1830 } 1831 1832 interface = interface->NextSiblingElement( 1833 "interface"); 1834 } 1835 if (interface == nullptr) 1836 { 1837 // if we got to the end of the list and 1838 // never found a match, throw 404 1839 res.result( 1840 boost::beast::http::status::not_found); 1841 } 1842 } 1843 } 1844 res.end(); 1845 }, 1846 processName, objectPath, 1847 "org.freedesktop.DBus.Introspectable", "Introspect"); 1848 } 1849 else 1850 { 1851 if (req.method() != "POST"_method) 1852 { 1853 res.result(boost::beast::http::status::not_found); 1854 res.end(); 1855 return; 1856 } 1857 1858 nlohmann::json requestDbusData = 1859 nlohmann::json::parse(req.body, nullptr, false); 1860 1861 if (requestDbusData.is_discarded()) 1862 { 1863 res.result(boost::beast::http::status::bad_request); 1864 res.end(); 1865 return; 1866 } 1867 if (!requestDbusData.is_array()) 1868 { 1869 res.result(boost::beast::http::status::bad_request); 1870 res.end(); 1871 return; 1872 } 1873 auto transaction = std::make_shared<InProgressActionData>(res); 1874 1875 transaction->path = objectPath; 1876 transaction->methodName = methodName; 1877 transaction->arguments = std::move(requestDbusData); 1878 1879 findActionOnInterface(transaction, processName); 1880 } 1881 }); 1882 } 1883 } // namespace openbmc_mapper 1884 } // namespace crow 1885