#include #include #include #include #include #include #include namespace crow { namespace openbmc_mapper { void introspectObjects(crow::Response &res, std::string process_name, std::string path, std::shared_ptr transaction) { crow::connections::systemBus->async_method_call( [&res, transaction, processName{std::move(process_name)}, objectPath{std::move(path)}](const boost::system::error_code ec, const std::string &introspect_xml) { if (ec) { BMCWEB_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << processName << " path: " << objectPath << "\n"; } else { transaction->push_back({{"path", objectPath}}); tinyxml2::XMLDocument doc; doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { BMCWEB_LOG_ERROR << "XML document failed to parse " << processName << " " << objectPath << "\n"; } else { tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); while (node != nullptr) { std::string childPath = node->Attribute("name"); std::string newpath; if (objectPath != "/") { newpath += objectPath; } newpath += "/" + childPath; // introspect the subobjects as well introspectObjects(res, processName, newpath, transaction); node = node->NextSiblingElement("node"); } } } // if we're the last outstanding caller, finish the request if (transaction.use_count() == 1) { res.jsonValue = {{"status", "ok"}, {"bus_name", processName}, {"objects", std::move(*transaction)}}; res.end(); } }, process_name, path, "org.freedesktop.DBus.Introspectable", "Introspect"); } // A smattering of common types to unpack. TODO(ed) this should really iterate // the sdbusplus object directly and build the json response using DbusRestVariantType = sdbusplus::message::variant< std::vector>, std::string, int64_t, uint64_t, double, int32_t, uint32_t, int16_t, uint16_t, uint8_t, bool>; using ManagedObjectType = std::vector>>>; void getManagedObjectsForEnumerate(const std::string &object_name, const std::string &connection_name, crow::Response &res, std::shared_ptr transaction) { crow::connections::systemBus->async_method_call( [&res, transaction](const boost::system::error_code ec, const ManagedObjectType &objects) { if (ec) { BMCWEB_LOG_ERROR << ec; } else { nlohmann::json &dataJson = *transaction; for (auto &objectPath : objects) { BMCWEB_LOG_DEBUG << "Reading object " << static_cast(objectPath.first); nlohmann::json &objectJson = dataJson[static_cast( objectPath.first)]; if (objectJson.is_null()) { objectJson = nlohmann::json::object(); } for (const auto &interface : objectPath.second) { for (const auto &property : interface.second) { nlohmann::json &propertyJson = objectJson[property.first]; mapbox::util::apply_visitor( [&propertyJson](auto &&val) { propertyJson = val; }, property.second); } } } } if (transaction.use_count() == 1) { res.jsonValue = {{"message", "200 OK"}, {"status", "ok"}, {"data", std::move(*transaction)}}; res.end(); } }, connection_name, object_name, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); } using GetSubTreeType = std::vector< std::pair>>>>; // Structure for storing data on an in progress action struct InProgressActionData { InProgressActionData(crow::Response &res) : res(res){}; ~InProgressActionData() { if (res.result() == boost::beast::http::status::internal_server_error) { // Reset the json object to clear out any data that made it in // before the error happened todo(ed) handle error condition with // proper code res.jsonValue = nlohmann::json::object(); } res.end(); } void setErrorStatus() { res.result(boost::beast::http::status::internal_server_error); } crow::Response &res; std::string path; std::string methodName; nlohmann::json arguments; }; std::vector dbusArgSplit(const std::string &string) { std::vector ret; if (string.empty()) { return ret; } ret.push_back(""); int containerDepth = 0; for (std::string::const_iterator character = string.begin(); character != string.end(); character++) { ret.back() += *character; switch (*character) { case ('a'): break; case ('('): case ('{'): containerDepth++; break; case ('}'): case (')'): containerDepth--; if (containerDepth == 0) { if (character + 1 != string.end()) { ret.push_back(""); } } break; default: if (containerDepth == 0) { if (character + 1 != string.end()) { ret.push_back(""); } } break; } } } int convertJsonToDbus(sd_bus_message *m, const std::string &arg_type, const nlohmann::json &input_json) { int r = 0; BMCWEB_LOG_DEBUG << "Converting " << input_json.dump() << " to type: " << arg_type; const std::vector argTypes = dbusArgSplit(arg_type); // Assume a single object for now. const nlohmann::json *j = &input_json; nlohmann::json::const_iterator jIt = input_json.begin(); for (const std::string &arg_code : argTypes) { // If we are decoding multiple objects, grab the pointer to the // iterator, and increment it for the next loop if (argTypes.size() > 1) { if (jIt == input_json.end()) { return -2; } j = &*jIt; jIt++; } const int64_t *int_value = j->get_ptr(); const uint64_t *uint_value = j->get_ptr(); const std::string *string_value = j->get_ptr(); const double *double_value = j->get_ptr(); const bool *b = j->get_ptr(); int64_t v = 0; double d = 0.0; // Do some basic type conversions that make sense. uint can be // converted to int. int and uint can be converted to double if (uint_value != nullptr && int_value == nullptr) { v = static_cast(*uint_value); int_value = &v; } if (uint_value != nullptr && double_value == nullptr) { d = static_cast(*uint_value); double_value = &d; } if (int_value != nullptr && double_value == nullptr) { d = static_cast(*int_value); double_value = &d; } if (arg_code == "s") { if (string_value == nullptr) { return -1; } r = sd_bus_message_append_basic(m, arg_code[0], (void *)string_value->c_str()); if (r < 0) { return r; } } else if (arg_code == "i") { if (int_value == nullptr) { return -1; } int32_t i = static_cast(*int_value); r = sd_bus_message_append_basic(m, arg_code[0], &i); if (r < 0) { return r; } } else if (arg_code == "b") { // lots of ways bool could be represented here. Try them all int bool_int = false; if (int_value != nullptr) { bool_int = *int_value > 0 ? 1 : 0; } else if (b != nullptr) { bool_int = b ? 1 : 0; } else if (string_value != nullptr) { bool_int = boost::istarts_with(*string_value, "t") ? 1 : 0; } else { return -1; } r = sd_bus_message_append_basic(m, arg_code[0], &bool_int); if (r < 0) { return r; } } else if (arg_code == "n") { if (int_value == nullptr) { return -1; } int16_t n = static_cast(*int_value); r = sd_bus_message_append_basic(m, arg_code[0], &n); if (r < 0) { return r; } } else if (arg_code == "x") { if (int_value == nullptr) { return -1; } r = sd_bus_message_append_basic(m, arg_code[0], int_value); if (r < 0) { return r; } } else if (arg_code == "y") { if (uint_value == nullptr) { return -1; } uint8_t y = static_cast(*uint_value); r = sd_bus_message_append_basic(m, arg_code[0], &y); } else if (arg_code == "q") { if (uint_value == nullptr) { return -1; } uint16_t q = static_cast(*uint_value); r = sd_bus_message_append_basic(m, arg_code[0], &q); } else if (arg_code == "u") { if (uint_value == nullptr) { return -1; } uint32_t u = static_cast(*uint_value); r = sd_bus_message_append_basic(m, arg_code[0], &u); } else if (arg_code == "t") { if (uint_value == nullptr) { return -1; } r = sd_bus_message_append_basic(m, arg_code[0], uint_value); } else if (arg_code == "d") { sd_bus_message_append_basic(m, arg_code[0], double_value); } else if (boost::starts_with(arg_code, "a")) { std::string contained_type = arg_code.substr(1); r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, contained_type.c_str()); if (r < 0) { return r; } for (nlohmann::json::const_iterator it = j->begin(); it != j->end(); ++it) { r = convertJsonToDbus(m, contained_type, *it); if (r < 0) { return r; } it++; } sd_bus_message_close_container(m); } else if (boost::starts_with(arg_code, "v")) { std::string contained_type = arg_code.substr(1); BMCWEB_LOG_DEBUG << "variant type: " << arg_code << " appending variant of type: " << contained_type; r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, contained_type.c_str()); if (r < 0) { return r; } r = convertJsonToDbus(m, contained_type, input_json); if (r < 0) { return r; } r = sd_bus_message_close_container(m); if (r < 0) { return r; } } else if (boost::starts_with(arg_code, "(") && boost::ends_with(arg_code, ")")) { std::string contained_type = arg_code.substr(1, arg_code.size() - 1); r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, contained_type.c_str()); nlohmann::json::const_iterator it = j->begin(); for (const std::string &arg_code : dbusArgSplit(arg_type)) { if (it == j->end()) { return -1; } r = convertJsonToDbus(m, arg_code, *it); if (r < 0) { return r; } it++; } r = sd_bus_message_close_container(m); } else if (boost::starts_with(arg_code, "{") && boost::ends_with(arg_code, "}")) { std::string contained_type = arg_code.substr(1, arg_code.size() - 1); r = sd_bus_message_open_container(m, SD_BUS_TYPE_DICT_ENTRY, contained_type.c_str()); std::vector codes = dbusArgSplit(contained_type); if (codes.size() != 2) { return -1; } const std::string &key_type = codes[0]; const std::string &value_type = codes[1]; for (auto it : j->items()) { r = convertJsonToDbus(m, key_type, it.key()); if (r < 0) { return r; } r = convertJsonToDbus(m, value_type, it.value()); if (r < 0) { return r; } } r = sd_bus_message_close_container(m); } else { return -2; } if (r < 0) { return r; } if (argTypes.size() > 1) { jIt++; } } } void findActionOnInterface(std::shared_ptr transaction, const std::string &connectionName) { BMCWEB_LOG_DEBUG << "findActionOnInterface for connection " << connectionName; crow::connections::systemBus->async_method_call( [transaction, connectionName{std::string(connectionName)}]( const boost::system::error_code ec, const std::string &introspect_xml) { BMCWEB_LOG_DEBUG << "got xml:\n " << introspect_xml; if (ec) { BMCWEB_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << connectionName << "\n"; } else { tinyxml2::XMLDocument doc; doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { BMCWEB_LOG_ERROR << "XML document failed to parse " << connectionName << "\n"; } else { tinyxml2::XMLElement *interface_node = pRoot->FirstChildElement("interface"); while (interface_node != nullptr) { std::string this_interface_name = interface_node->Attribute("name"); tinyxml2::XMLElement *method_node = interface_node->FirstChildElement("method"); while (method_node != nullptr) { std::string this_methodName = method_node->Attribute("name"); BMCWEB_LOG_DEBUG << "Found method: " << this_methodName; if (this_methodName == transaction->methodName) { sdbusplus::message::message m = crow::connections::systemBus ->new_method_call( connectionName.c_str(), transaction->path.c_str(), this_interface_name.c_str(), transaction->methodName.c_str()); tinyxml2::XMLElement *argument_node = method_node->FirstChildElement("arg"); nlohmann::json::const_iterator arg_it = transaction->arguments.begin(); while (argument_node != nullptr) { std::string arg_direction = argument_node->Attribute("direction"); if (arg_direction == "in") { std::string arg_type = argument_node->Attribute("type"); if (arg_it == transaction->arguments.end()) { transaction->setErrorStatus(); return; } if (convertJsonToDbus(m.get(), arg_type, *arg_it) < 0) { transaction->setErrorStatus(); return; } arg_it++; } argument_node = method_node->NextSiblingElement("arg"); } crow::connections::systemBus->async_send( m, [transaction]( boost::system::error_code ec, sdbusplus::message::message &m) { if (ec) { transaction->setErrorStatus(); return; } transaction->res.jsonValue = { {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; }); break; } method_node = method_node->NextSiblingElement("method"); } interface_node = interface_node->NextSiblingElement("interface"); } } } }, connectionName, transaction->path, "org.freedesktop.DBus.Introspectable", "Introspect"); } void handle_action(const crow::Request &req, crow::Response &res, const std::string &objectPath, const std::string &methodName) { nlohmann::json requestDbusData = nlohmann::json::parse(req.body, nullptr, false); if (requestDbusData.is_discarded()) { res.result(boost::beast::http::status::bad_request); res.end(); return; } if (!requestDbusData.is_array()) { res.result(boost::beast::http::status::bad_request); res.end(); return; } auto transaction = std::make_shared(res); transaction->path = objectPath; transaction->methodName = methodName; transaction->arguments = std::move(requestDbusData); crow::connections::systemBus->async_method_call( [transaction]( const boost::system::error_code ec, const std::vector>> &interface_names) { if (ec || interface_names.size() <= 0) { transaction->setErrorStatus(); return; } BMCWEB_LOG_DEBUG << "GetObject returned objects " << interface_names.size(); for (const std::pair> &object : interface_names) { findActionOnInterface(transaction, object.first); } }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetObject", objectPath, std::array()); } void handle_list(crow::Response &res, const std::string &objectPath) { crow::connections::systemBus->async_method_call( [&res](const boost::system::error_code ec, std::vector &objectPaths) { if (ec) { res.result(boost::beast::http::status::internal_server_error); } else { res.jsonValue = {{"status", "ok"}, {"message", "200 OK"}, {"data", std::move(objectPaths)}}; } res.end(); }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", objectPath, static_cast(99), std::array()); } void handle_enumerate(crow::Response &res, const std::string &objectPath) { crow::connections::systemBus->async_method_call( [&res, objectPath{std::string(objectPath)}]( const boost::system::error_code ec, const GetSubTreeType &object_names) { if (ec) { res.jsonValue = {{"message", "200 OK"}, {"status", "ok"}, {"data", nlohmann::json::object()}}; res.end(); return; } boost::container::flat_set connections; for (const auto &object : object_names) { for (const auto &Connection : object.second) { connections.insert(Connection.first); } } if (connections.size() <= 0) { res.result(boost::beast::http::status::not_found); res.end(); return; } auto transaction = std::make_shared(nlohmann::json::object()); for (const std::string &Connection : connections) { getManagedObjectsForEnumerate(objectPath, Connection, res, transaction); } }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTree", objectPath, (int32_t)0, std::array()); } void handle_get(crow::Response &res, std::string &objectPath, std::string &destProperty) { BMCWEB_LOG_DEBUG << "handle_get: " << objectPath << " prop:" << destProperty; std::shared_ptr property_name = std::make_shared(std::move(destProperty)); std::shared_ptr path = std::make_shared(std::move(objectPath)); using GetObjectType = std::vector>>; crow::connections::systemBus->async_method_call( [&res, path, property_name](const boost::system::error_code ec, const GetObjectType &object_names) { if (ec || object_names.size() <= 0) { res.result(boost::beast::http::status::not_found); res.end(); return; } std::shared_ptr response = std::make_shared(nlohmann::json::object()); // The mapper should never give us an empty interface names list, // but check anyway for (const std::pair> connection : object_names) { const std::vector &interfaceNames = connection.second; if (interfaceNames.size() <= 0) { res.result(boost::beast::http::status::not_found); res.end(); return; } for (const std::string &interface : interfaceNames) { crow::connections::systemBus->async_method_call( [&res, response, property_name]( const boost::system::error_code ec, const std::vector< std::pair> &properties) { if (ec) { BMCWEB_LOG_ERROR << "Bad dbus request error: " << ec; } else { for (const std::pair &property : properties) { // if property name is empty, or matches our // search query, add it to the response json if (property_name->empty()) { mapbox::util::apply_visitor( [&response, &property](auto &&val) { (*response)[property.first] = val; }, property.second); } else if (property.first == *property_name) { mapbox::util::apply_visitor( [&response](auto &&val) { (*response) = val; }, property.second); } } } if (response.use_count() == 1) { res.jsonValue = {{"status", "ok"}, {"message", "200 OK"}, {"data", *response}}; res.end(); } }, connection.first, *path, "org.freedesktop.DBus.Properties", "GetAll", interface); } } }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetObject", *path, std::array()); } struct AsyncPutRequest { AsyncPutRequest(crow::Response &res) : res(res) { res.jsonValue = { {"status", "ok"}, {"message", "200 OK"}, {"data", nullptr}}; } ~AsyncPutRequest() { if (res.result() == boost::beast::http::status::internal_server_error) { // Reset the json object to clear out any data that made it in // before the error happened todo(ed) handle error condition with // proper code res.jsonValue = nlohmann::json::object(); } if (res.jsonValue.empty()) { res.result(boost::beast::http::status::forbidden); res.jsonValue = { {"status", "error"}, {"message", "403 Forbidden"}, {"data", {{"message", "The specified property cannot be created: " + propertyName}}}}; } res.end(); } void setErrorStatus() { res.result(boost::beast::http::status::internal_server_error); } crow::Response &res; std::string objectPath; std::string propertyName; nlohmann::json propertyValue; }; void handlePut(const crow::Request &req, crow::Response &res, const std::string &objectPath, const std::string &destProperty) { nlohmann::json requestDbusData = nlohmann::json::parse(req.body, nullptr, false); if (requestDbusData.is_discarded()) { res.result(boost::beast::http::status::bad_request); res.end(); return; } nlohmann::json::const_iterator propertyIt = requestDbusData.find("data"); if (propertyIt == requestDbusData.end()) { res.result(boost::beast::http::status::bad_request); res.end(); return; } const nlohmann::json &propertySetValue = *propertyIt; auto transaction = std::make_shared(res); transaction->objectPath = objectPath; transaction->propertyName = destProperty; transaction->propertyValue = propertySetValue; using GetObjectType = std::vector>>; crow::connections::systemBus->async_method_call( [transaction](const boost::system::error_code ec, const GetObjectType &object_names) { if (!ec && object_names.size() <= 0) { transaction->res.result(boost::beast::http::status::not_found); return; } for (const std::pair> connection : object_names) { const std::string &connectionName = connection.first; crow::connections::systemBus->async_method_call( [connectionName{std::string(connectionName)}, transaction](const boost::system::error_code ec, const std::string &introspectXml) { if (ec) { BMCWEB_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << connectionName; transaction->setErrorStatus(); return; } tinyxml2::XMLDocument doc; doc.Parse(introspectXml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { BMCWEB_LOG_ERROR << "XML document failed to parse: " << introspectXml; transaction->setErrorStatus(); return; } tinyxml2::XMLElement *ifaceNode = pRoot->FirstChildElement("interface"); while (ifaceNode != nullptr) { const char *interfaceName = ifaceNode->Attribute("name"); BMCWEB_LOG_DEBUG << "found interface " << interfaceName; tinyxml2::XMLElement *propNode = ifaceNode->FirstChildElement("property"); while (propNode != nullptr) { const char *propertyName = propNode->Attribute("name"); BMCWEB_LOG_DEBUG << "Found property " << propertyName; if (propertyName == transaction->propertyName) { const char *argType = propNode->Attribute("type"); if (argType != nullptr) { sdbusplus::message::message m = crow::connections::systemBus ->new_method_call( connectionName.c_str(), transaction->objectPath .c_str(), "org.freedesktop.DBus." "Properties", "Set"); m.append(interfaceName, transaction->propertyName); int r = sd_bus_message_open_container( m.get(), SD_BUS_TYPE_VARIANT, argType); if (r < 0) { transaction->setErrorStatus(); return; } r = convertJsonToDbus( m.get(), argType, transaction->propertyValue); if (r < 0) { transaction->setErrorStatus(); return; } r = sd_bus_message_close_container( m.get()); if (r < 0) { transaction->setErrorStatus(); return; } crow::connections::systemBus ->async_send( m, [transaction]( boost::system::error_code ec, sdbusplus::message::message &m) { BMCWEB_LOG_DEBUG << "sent"; if (ec) { transaction->res .jsonValue ["status"] = "error"; transaction->res .jsonValue ["message"] = ec.message(); } }); } } propNode = propNode->NextSiblingElement("property"); } ifaceNode = ifaceNode->NextSiblingElement("interface"); } }, connectionName, transaction->objectPath, "org.freedesktop.DBus.Introspectable", "Introspect"); } }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetObject", transaction->objectPath, std::array()); } template void requestRoutes(Crow &app) { BMCWEB_ROUTE(app, "/bus/") .methods("GET"_method)( [](const crow::Request &req, crow::Response &res) { res.jsonValue = {{"busses", {{{"name", "system"}}}}, {"status", "ok"}}; }); BMCWEB_ROUTE(app, "/bus/system/") .methods("GET"_method)( [](const crow::Request &req, crow::Response &res) { auto myCallback = [&res](const boost::system::error_code ec, std::vector &names) { if (ec) { BMCWEB_LOG_ERROR << "Dbus call failed with code " << ec; res.result( boost::beast::http::status::internal_server_error); } else { std::sort(names.begin(), names.end()); nlohmann::json j{{"status", "ok"}}; auto &objectsSub = j["objects"]; for (auto &name : names) { objectsSub.push_back({{"name", name}}); } res.jsonValue = std::move(j); } res.end(); }; crow::connections::systemBus->async_method_call( std::move(myCallback), "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames"); }); BMCWEB_ROUTE(app, "/list/") .methods("GET"_method)( [](const crow::Request &req, crow::Response &res) { handle_list(res, "/"); }); BMCWEB_ROUTE(app, "/xyz/") .methods("GET"_method, "PUT"_method, "POST"_method)([](const crow::Request &req, crow::Response &res, const std::string &path) { std::string objectPath = "/xyz/" + path; // Trim any trailing "/" at the end if (boost::ends_with(objectPath, "/")) { objectPath.pop_back(); } // If accessing a single attribute, fill in and update objectPath, // otherwise leave destProperty blank std::string destProperty = ""; const char *attrSeperator = "/attr/"; size_t attrPosition = path.find(attrSeperator); if (attrPosition != path.npos) { objectPath = "/xyz/" + path.substr(0, attrPosition); destProperty = path.substr(attrPosition + strlen(attrSeperator), path.length()); } if (req.method() == "POST"_method) { constexpr const char *action_seperator = "/action/"; size_t action_position = path.find(action_seperator); if (action_position != path.npos) { objectPath = "/xyz/" + path.substr(0, action_position); std::string post_property = path.substr( (action_position + strlen(action_seperator)), path.length()); handle_action(req, res, objectPath, post_property); return; } } else if (req.method() == "GET"_method) { if (boost::ends_with(objectPath, "/enumerate")) { objectPath.erase(objectPath.end() - 10, objectPath.end()); handle_enumerate(res, objectPath); } else if (boost::ends_with(objectPath, "/list")) { objectPath.erase(objectPath.end() - 5, objectPath.end()); handle_list(res, objectPath); } else { handle_get(res, objectPath, destProperty); } return; } else if (req.method() == "PUT"_method) { handlePut(req, res, objectPath, destProperty); return; } res.result(boost::beast::http::status::method_not_allowed); res.end(); }); BMCWEB_ROUTE(app, "/bus/system//") .methods("GET"_method)([](const crow::Request &req, crow::Response &res, const std::string &Connection) { std::shared_ptr transaction; introspectObjects(res, Connection, "/", transaction); }); BMCWEB_ROUTE(app, "/download/dump//") .methods("GET"_method)([](const crow::Request &req, crow::Response &res, const std::string &dumpId) { std::regex validFilename("^[\\w\\- ]+(\\.?[\\w\\- ]+)$"); if (!std::regex_match(dumpId, validFilename)) { res.result(boost::beast::http::status::not_found); res.end(); return; } std::experimental::filesystem::path loc( "/var/lib/phosphor-debug-collector/dumps"); loc += dumpId; if (!std::experimental::filesystem::exists(loc) || !std::experimental::filesystem::is_directory(loc)) { res.result(boost::beast::http::status::not_found); res.end(); return; } std::experimental::filesystem::directory_iterator files(loc); for (auto &file : files) { std::ifstream readFile(file.path()); if (readFile.good()) { continue; } res.addHeader("Content-Type", "application/octet-stream"); res.body() = {std::istreambuf_iterator(readFile), std::istreambuf_iterator()}; res.end(); } res.result(boost::beast::http::status::not_found); res.end(); return; }); BMCWEB_ROUTE(app, "/bus/system//") .methods("GET"_method)([](const crow::Request &req, crow::Response &res, const std::string &processName, const std::string &requestedPath) { std::vector strs; boost::split(strs, requestedPath, boost::is_any_of("/")); std::string objectPath; std::string interfaceName; std::string methodName; auto it = strs.begin(); if (it == strs.end()) { objectPath = "/"; } while (it != strs.end()) { // Check if segment contains ".". If it does, it must be an // interface if (it->find(".") != std::string::npos) { break; // THis check is neccesary as the trailing slash gets parsed // as part of our specifier above, which causes the // normal trailing backslash redirector to fail. } else if (!it->empty()) { objectPath += "/" + *it; } it++; } if (it != strs.end()) { interfaceName = *it; it++; // after interface, we might have a method name if (it != strs.end()) { methodName = *it; it++; } } if (it != strs.end()) { // if there is more levels past the method name, something went // wrong, return not found res.result(boost::beast::http::status::not_found); res.end(); return; } if (interfaceName.empty()) { crow::connections::systemBus->async_method_call( [&, processName, objectPath](const boost::system::error_code ec, const std::string &introspect_xml) { if (ec) { BMCWEB_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << processName << " path: " << objectPath << "\n"; } else { tinyxml2::XMLDocument doc; doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { BMCWEB_LOG_ERROR << "XML document failed to parse " << processName << " " << objectPath << "\n"; res.jsonValue = {{"status", "XML parse error"}}; res.result(boost::beast::http::status:: internal_server_error); } else { nlohmann::json interfacesArray = nlohmann::json::array(); tinyxml2::XMLElement *interface = pRoot->FirstChildElement("interface"); while (interface != nullptr) { std::string ifaceName = interface->Attribute("name"); interfacesArray.push_back( {{"name", ifaceName}}); interface = interface->NextSiblingElement( "interface"); } res.jsonValue = { {"status", "ok"}, {"bus_name", processName}, {"interfaces", interfacesArray}, {"objectPath", objectPath}}; } } res.end(); }, processName, objectPath, "org.freedesktop.DBus.Introspectable", "Introspect"); } else { crow::connections::systemBus->async_method_call( [&, processName, objectPath, interface_name{std::move(interfaceName)}]( const boost::system::error_code ec, const std::string &introspect_xml) { if (ec) { BMCWEB_LOG_ERROR << "Introspect call failed with error: " << ec.message() << " on process: " << processName << " path: " << objectPath << "\n"; } else { tinyxml2::XMLDocument doc; doc.Parse(introspect_xml.c_str()); tinyxml2::XMLNode *pRoot = doc.FirstChildElement("node"); if (pRoot == nullptr) { BMCWEB_LOG_ERROR << "XML document failed to parse " << processName << " " << objectPath << "\n"; res.result(boost::beast::http::status:: internal_server_error); } else { tinyxml2::XMLElement *node = pRoot->FirstChildElement("node"); // if we know we're the only call, build the // json directly nlohmann::json methodsArray = nlohmann::json::array(); nlohmann::json signalsArray = nlohmann::json::array(); tinyxml2::XMLElement *interface = pRoot->FirstChildElement("interface"); while (interface != nullptr) { std::string ifaceName = interface->Attribute("name"); if (ifaceName == interfaceName) { tinyxml2::XMLElement *methods = interface->FirstChildElement( "method"); while (methods != nullptr) { nlohmann::json argsArray = nlohmann::json::array(); tinyxml2::XMLElement *arg = methods->FirstChildElement( "arg"); while (arg != nullptr) { argsArray.push_back( {{"name", arg->Attribute("name")}, {"type", arg->Attribute("type")}, {"direction", arg->Attribute( "direction")}}); arg = arg->NextSiblingElement( "arg"); } methodsArray.push_back( {{"name", methods->Attribute("name")}, {"uri", "/bus/system/" + processName + objectPath + "/" + interfaceName + "/" + methods->Attribute( "name")}, {"args", argsArray}}); methods = methods->NextSiblingElement( "method"); } tinyxml2::XMLElement *signals = interface->FirstChildElement( "signal"); while (signals != nullptr) { nlohmann::json argsArray = nlohmann::json::array(); tinyxml2::XMLElement *arg = signals->FirstChildElement( "arg"); while (arg != nullptr) { std::string name = arg->Attribute("name"); std::string type = arg->Attribute("type"); argsArray.push_back({ {"name", name}, {"type", type}, }); arg = arg->NextSiblingElement( "arg"); } signalsArray.push_back( {{"name", signals->Attribute("name")}, {"args", argsArray}}); signals = signals->NextSiblingElement( "signal"); } res.jsonValue = { {"status", "ok"}, {"bus_name", processName}, {"interface", interfaceName}, {"methods", methodsArray}, {"objectPath", objectPath}, {"properties", nlohmann::json::object()}, {"signals", signalsArray}}; break; } interface = interface->NextSiblingElement( "interface"); } if (interface == nullptr) { // if we got to the end of the list and // never found a match, throw 404 res.result( boost::beast::http::status::not_found); } } } res.end(); }, processName, objectPath, "org.freedesktop.DBus.Introspectable", "Introspect"); } }); } } // namespace openbmc_mapper } // namespace crow