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