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     std::string_view rootPath,
48     std::unique_ptr<FileHandlerInterface> fileHandler) :
49     rootPath(rootPath),
50     fileHandler(std::move(fileHandler)), logServiceId("")
51 {}
52 
53 bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
54 {
55     nlohmann::json jsonDecoded;
56     try
57     {
58         jsonDecoded = nlohmann::json::parse(jsonStr);
59     }
60     catch (nlohmann::json::parse_error& e)
61     {
62         fmt::print(stderr, "JSON parse error: \n{}\n", e.what());
63         return false;
64     }
65 
66     // We need to know the type to determine how to process the decoded JSON
67     // output.
68     if (!jsonDecoded.contains("@odata.type"))
69     {
70         fmt::print(stderr, "@odata.type field doesn't exist in:\n {}\n",
71                    jsonDecoded.dump(4));
72         return false;
73     }
74 
75     auto schemaType = getSchemaType(jsonDecoded);
76     if (schemaType == JsonPdrType::logEntry)
77     {
78         return processLogEntry(jsonDecoded);
79     }
80     if (schemaType == JsonPdrType::logService)
81     {
82         return processLogService(jsonDecoded);
83     }
84     return processOtherTypes(jsonDecoded);
85 }
86 
87 JsonPdrType ExternalStorerFileInterface::getSchemaType(
88     const nlohmann::json& jsonSchema) const
89 {
90     auto logEntryFound =
91         std::string(jsonSchema["@odata.type"]).find("LogEntry");
92     if (logEntryFound != std::string::npos)
93     {
94         return JsonPdrType::logEntry;
95     }
96 
97     auto logServiceFound =
98         std::string(jsonSchema["@odata.type"]).find("LogService");
99     if (logServiceFound != std::string::npos)
100     {
101         return JsonPdrType::logService;
102     }
103 
104     return JsonPdrType::other;
105 }
106 
107 bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry)
108 {
109     // TODO: Add policies for LogEntry retention.
110     // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1.
111     if (logServiceId.empty())
112     {
113         fmt::print(stderr, "First need a LogService PDR with a new UUID.\n");
114         return false;
115     }
116 
117     std::string id = boost::uuids::to_string(randomGen());
118     std::string path = "/redfish/v1/Systems/system/LogServices/" +
119                        logServiceId + "/Entries/" + id;
120 
121     // Populate the "Id" with the UUID we generated.
122     logEntry["Id"] = id;
123     // Remove the @odata.id from the JSON since ExternalStorer will fill it for
124     // a client.
125     logEntry.erase("@odata.id");
126 
127     return createFile(path, logEntry);
128 }
129 
130 bool ExternalStorerFileInterface::processLogService(
131     const nlohmann::json& logService)
132 {
133     if (!logService.contains("@odata.id"))
134     {
135         fmt::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
136                    logService.dump(4));
137         return false;
138     }
139 
140     if (!logService.contains("Id"))
141     {
142         fmt::print(stderr, "Id field doesn't exist in:\n {}\n",
143                    logService.dump(4));
144         return false;
145     }
146 
147     logServiceId = logService["Id"].get<std::string>();
148 
149     if (!createFile(logService["@odata.id"].get<std::string>(), logService))
150     {
151         fmt::print(stderr, "Failed to create LogService index file for:\n{}\n",
152                    logService.dump(4));
153         return false;
154     }
155     // ExternalStorer needs a .../Entries/index.json file with no data.
156     nlohmann::json jEmpty = "{}"_json;
157     return createFile(logService["@odata.id"].get<std::string>() + "/Entries",
158                       jEmpty);
159 }
160 
161 bool ExternalStorerFileInterface::processOtherTypes(
162     const nlohmann::json& jsonPdr) const
163 {
164     if (!jsonPdr.contains("@odata.id"))
165     {
166         fmt::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
167                    jsonPdr.dump(4));
168         return false;
169     }
170     return createFile(jsonPdr["@odata.id"].get<std::string>(), jsonPdr);
171 }
172 
173 bool ExternalStorerFileInterface::createFile(
174     const std::string& subPath, const nlohmann::json& jsonPdr) const
175 {
176     return fileHandler->createFile(rootPath + subPath, jsonPdr);
177 }
178 
179 } // namespace rde
180 } // namespace bios_bmc_smm_error_logger
181