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