1 #pragma once 2 3 #include "bmcweb_config.h" 4 5 #include "app.hpp" 6 #include "async_resp.hpp" 7 #include "http_request.hpp" 8 #include "persistent_data.hpp" 9 #include "query.hpp" 10 #include "registries/privilege_registry.hpp" 11 #include "utils/systemd_utils.hpp" 12 13 #include <tinyxml2.h> 14 15 #include <nlohmann/json.hpp> 16 17 namespace redfish 18 { 19 20 inline std::string 21 getMetadataPieceForFile(const std::filesystem::path& filename) 22 { 23 std::string xml; 24 tinyxml2::XMLDocument doc; 25 std::string pathStr = filename.string(); 26 if (doc.LoadFile(pathStr.c_str()) != tinyxml2::XML_SUCCESS) 27 { 28 BMCWEB_LOG_ERROR("Failed to open XML file {}", pathStr); 29 return ""; 30 } 31 xml += std::format(" <edmx:Reference Uri=\"/redfish/v1/schema/{}\">\n", 32 filename.filename().string()); 33 // std::string edmx = "{http://docs.oasis-open.org/odata/ns/edmx}"; 34 // std::string edm = "{http://docs.oasis-open.org/odata/ns/edm}"; 35 const char* edmx = "edmx:Edmx"; 36 for (tinyxml2::XMLElement* edmxNode = doc.FirstChildElement(edmx); 37 edmxNode != nullptr; edmxNode = edmxNode->NextSiblingElement(edmx)) 38 { 39 const char* dataServices = "edmx:DataServices"; 40 for (tinyxml2::XMLElement* node = 41 edmxNode->FirstChildElement(dataServices); 42 node != nullptr; node = node->NextSiblingElement(dataServices)) 43 { 44 BMCWEB_LOG_DEBUG("Got data service for {}", pathStr); 45 const char* schemaTag = "Schema"; 46 for (tinyxml2::XMLElement* schemaNode = 47 node->FirstChildElement(schemaTag); 48 schemaNode != nullptr; 49 schemaNode = schemaNode->NextSiblingElement(schemaTag)) 50 { 51 std::string ns = schemaNode->Attribute("Namespace"); 52 // BMCWEB_LOG_DEBUG("Found namespace {}", ns); 53 std::string alias; 54 if (std::string_view(ns).starts_with("RedfishExtensions")) 55 { 56 alias = " Alias=\"Redfish\""; 57 } 58 xml += std::format( 59 " <edmx:Include Namespace=\"{}\"{}/>\n", ns, alias); 60 } 61 } 62 } 63 xml += " </edmx:Reference>\n"; 64 return xml; 65 } 66 67 inline void 68 handleMetadataGet(App& /*app*/, const crow::Request& /*req*/, 69 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 70 { 71 std::filesystem::path schema("/usr/share/www/redfish/v1/schema"); 72 std::error_code ec; 73 auto iter = std::filesystem::directory_iterator(schema, ec); 74 if (ec) 75 { 76 BMCWEB_LOG_ERROR("Failed to open XML folder {}", schema.string()); 77 asyncResp->res.result( 78 boost::beast::http::status::internal_server_error); 79 return; 80 } 81 std::string xml; 82 83 xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; 84 xml += 85 "<edmx:Edmx xmlns:edmx=\"http://docs.oasis-open.org/odata/ns/edmx\" Version=\"4.0\">\n"; 86 for (const auto& dirEntry : iter) 87 { 88 std::string path = dirEntry.path().filename(); 89 if (!std::string_view(path).ends_with("_v1.xml")) 90 { 91 continue; 92 } 93 std::string metadataPiece = getMetadataPieceForFile(dirEntry.path()); 94 if (metadataPiece.empty()) 95 { 96 asyncResp->res.result( 97 boost::beast::http::status::internal_server_error); 98 return; 99 } 100 xml += metadataPiece; 101 } 102 xml += " <edmx:DataServices>\n"; 103 xml += 104 " <Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"Service\">\n"; 105 xml += 106 " <EntityContainer Name=\"Service\" Extends=\"ServiceRoot.v1_0_0.ServiceContainer\"/>\n"; 107 xml += " </Schema>\n"; 108 xml += " </edmx:DataServices>\n"; 109 xml += "</edmx:Edmx>\n"; 110 111 asyncResp->res.addHeader(boost::beast::http::field::content_type, 112 "application/xml"); 113 asyncResp->res.write(std::move(xml)); 114 } 115 116 inline void requestRoutesMetadata(App& app) 117 { 118 BMCWEB_ROUTE(app, "/redfish/v1/$metadata") 119 .methods(boost::beast::http::verb::get)( 120 std::bind_front(handleMetadataGet, std::ref(app))); 121 } 122 123 } // namespace redfish 124