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