1 #include "rde/external_storer_file.hpp" 2 3 #include <fmt/format.h> 4 5 #include <boost/uuid/uuid.hpp> 6 #include <boost/uuid/uuid_io.hpp> 7 8 #include <fstream> 9 #include <string_view> 10 11 namespace bios_bmc_smm_error_logger 12 { 13 namespace rde 14 { 15 16 bool ExternalStorerFileWriter::createFolder(const std::string& folderPath) const 17 { 18 std::filesystem::path path(folderPath); 19 if (!std::filesystem::is_directory(path)) 20 { 21 if (!std::filesystem::create_directories(path)) 22 { 23 fmt::print(stderr, "Failed to create a folder at {}\n", folderPath); 24 return false; 25 } 26 } 27 return true; 28 } 29 30 bool ExternalStorerFileWriter::createFile(const std::string& folderPath, 31 const nlohmann::json& jsonPdr) const 32 { 33 if (!createFolder(folderPath)) 34 { 35 return false; 36 } 37 std::filesystem::path path(folderPath); 38 path /= "index.json"; 39 // If the file already exist, overwrite it. 40 std::ofstream output(path); 41 output << jsonPdr; 42 output.close(); 43 return true; 44 } 45 46 ExternalStorerFileInterface::ExternalStorerFileInterface( 47 sdbusplus::bus_t& bus, std::string_view rootPath, 48 std::unique_ptr<FileHandlerInterface> fileHandler) : 49 rootPath(rootPath), 50 fileHandler(std::move(fileHandler)), logServiceId(""), 51 cperNotifier(std::make_unique<CperFileNotifierHandler>(bus)) 52 {} 53 54 bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr) 55 { 56 nlohmann::json jsonDecoded; 57 try 58 { 59 jsonDecoded = nlohmann::json::parse(jsonStr); 60 } 61 catch (nlohmann::json::parse_error& e) 62 { 63 fmt::print(stderr, "JSON parse error: \n{}\n", e.what()); 64 return false; 65 } 66 67 // We need to know the type to determine how to process the decoded JSON 68 // output. 69 if (!jsonDecoded.contains("@odata.type")) 70 { 71 fmt::print(stderr, "@odata.type field doesn't exist in:\n {}\n", 72 jsonDecoded.dump(4)); 73 return false; 74 } 75 76 auto schemaType = getSchemaType(jsonDecoded); 77 if (schemaType == JsonPdrType::logEntry) 78 { 79 return processLogEntry(jsonDecoded); 80 } 81 if (schemaType == JsonPdrType::logService) 82 { 83 return processLogService(jsonDecoded); 84 } 85 return processOtherTypes(jsonDecoded); 86 } 87 88 JsonPdrType ExternalStorerFileInterface::getSchemaType( 89 const nlohmann::json& jsonSchema) const 90 { 91 auto logEntryFound = 92 std::string(jsonSchema["@odata.type"]).find("LogEntry"); 93 if (logEntryFound != std::string::npos) 94 { 95 return JsonPdrType::logEntry; 96 } 97 98 auto logServiceFound = 99 std::string(jsonSchema["@odata.type"]).find("LogService"); 100 if (logServiceFound != std::string::npos) 101 { 102 return JsonPdrType::logService; 103 } 104 105 return JsonPdrType::other; 106 } 107 108 bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry) 109 { 110 // TODO: Add policies for LogEntry retention. 111 // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1. 112 if (logServiceId.empty()) 113 { 114 fmt::print(stderr, "First need a LogService PDR with a new UUID.\n"); 115 return false; 116 } 117 118 std::string id = boost::uuids::to_string(randomGen()); 119 std::string fullPath = 120 fmt::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}", 121 rootPath, logServiceId, id); 122 123 // Populate the "Id" with the UUID we generated. 124 logEntry["Id"] = id; 125 // Remove the @odata.id from the JSON since ExternalStorer will fill it for 126 // a client. 127 logEntry.erase("@odata.id"); 128 129 if (!fileHandler->createFile(fullPath, logEntry)) 130 { 131 fmt::print(stderr, "Failed to create a file for log entry path: {}\n", 132 fullPath); 133 return false; 134 } 135 136 cperNotifier->createEntry(fullPath + "/index.json"); 137 return true; 138 } 139 140 bool ExternalStorerFileInterface::processLogService( 141 const nlohmann::json& logService) 142 { 143 if (!logService.contains("@odata.id")) 144 { 145 fmt::print(stderr, "@odata.id field doesn't exist in:\n {}\n", 146 logService.dump(4)); 147 return false; 148 } 149 150 if (!logService.contains("Id")) 151 { 152 fmt::print(stderr, "Id field doesn't exist in:\n {}\n", 153 logService.dump(4)); 154 return false; 155 } 156 157 logServiceId = logService["Id"].get<std::string>(); 158 159 if (!createFile(logService["@odata.id"].get<std::string>(), logService)) 160 { 161 fmt::print(stderr, "Failed to create LogService index file for:\n{}\n", 162 logService.dump(4)); 163 return false; 164 } 165 // ExternalStorer needs a .../Entries/index.json file with no data. 166 nlohmann::json jEmpty = "{}"_json; 167 return createFile(logService["@odata.id"].get<std::string>() + "/Entries", 168 jEmpty); 169 } 170 171 bool ExternalStorerFileInterface::processOtherTypes( 172 const nlohmann::json& jsonPdr) const 173 { 174 if (!jsonPdr.contains("@odata.id")) 175 { 176 fmt::print(stderr, "@odata.id field doesn't exist in:\n {}\n", 177 jsonPdr.dump(4)); 178 return false; 179 } 180 return createFile(jsonPdr["@odata.id"].get<std::string>(), jsonPdr); 181 } 182 183 bool ExternalStorerFileInterface::createFile( 184 const std::string& subPath, const nlohmann::json& jsonPdr) const 185 { 186 return fileHandler->createFile(rootPath + subPath, jsonPdr); 187 } 188 189 } // namespace rde 190 } // namespace bios_bmc_smm_error_logger 191