blob: 6ae39d26427ea6e88ea99984c41f057fd0001e1d [file] [log] [blame]
kasunath37bc0df2022-06-07 12:40:26 -07001#include "rde/external_storer_file.hpp"
2
kasunath37bc0df2022-06-07 12:40:26 -07003#include <boost/uuid/uuid.hpp>
4#include <boost/uuid/uuid_io.hpp>
Patrick Williams5de90612024-02-13 21:31:53 -06005#include <stdplus/print.hpp>
kasunath37bc0df2022-06-07 12:40:26 -07006
Patrick Williams5de90612024-02-13 21:31:53 -06007#include <format>
kasunath37bc0df2022-06-07 12:40:26 -07008#include <fstream>
9#include <string_view>
10
11namespace bios_bmc_smm_error_logger
12{
13namespace rde
14{
15
16bool 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 {
Patrick Williams5de90612024-02-13 21:31:53 -060023 stdplus::print(stderr, "Failed to create a folder at {}\n",
24 folderPath);
kasunath37bc0df2022-06-07 12:40:26 -070025 return false;
26 }
27 }
28 return true;
29}
30
31bool ExternalStorerFileWriter::createFile(const std::string& folderPath,
32 const nlohmann::json& jsonPdr) const
33{
34 if (!createFolder(folderPath))
35 {
36 return false;
37 }
38 std::filesystem::path path(folderPath);
39 path /= "index.json";
40 // If the file already exist, overwrite it.
41 std::ofstream output(path);
42 output << jsonPdr;
43 output.close();
44 return true;
45}
46
Brandon Kim41a58d42024-09-06 06:49:42 +000047bool ExternalStorerFileWriter::removeAll(const std::string& filePath) const
48{
49 // Attempt to delete the file
50 std::error_code ec;
51 std::filesystem::remove_all(filePath, ec);
52 if (ec)
53 {
54 return false;
55 }
Brandon Kim41a58d42024-09-06 06:49:42 +000056 return true;
57}
58
kasunath37bc0df2022-06-07 12:40:26 -070059ExternalStorerFileInterface::ExternalStorerFileInterface(
kasunath3d0cd552022-08-25 20:22:58 -070060 const std::shared_ptr<sdbusplus::asio::connection>& conn,
61 std::string_view rootPath,
Brandon Kim41a58d42024-09-06 06:49:42 +000062 std::unique_ptr<FileHandlerInterface> fileHandler,
63 uint32_t numSavedLogEntries, uint32_t numLogEntries) :
Patrick Williams1a643562024-08-16 15:22:05 -040064 rootPath(rootPath), fileHandler(std::move(fileHandler)), logServiceId(""),
Brandon Kim41a58d42024-09-06 06:49:42 +000065 cperNotifier(std::make_unique<CperFileNotifierHandler>(conn)),
66 maxNumSavedLogEntries(numSavedLogEntries), maxNumLogEntries(numLogEntries)
kasunath37bc0df2022-06-07 12:40:26 -070067{}
68
69bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
70{
71 nlohmann::json jsonDecoded;
72 try
73 {
74 jsonDecoded = nlohmann::json::parse(jsonStr);
75 }
76 catch (nlohmann::json::parse_error& e)
77 {
Patrick Williams5de90612024-02-13 21:31:53 -060078 stdplus::print(stderr, "JSON parse error: \n{}\n", e.what());
kasunath37bc0df2022-06-07 12:40:26 -070079 return false;
80 }
81
82 // We need to know the type to determine how to process the decoded JSON
83 // output.
84 if (!jsonDecoded.contains("@odata.type"))
85 {
Patrick Williams5de90612024-02-13 21:31:53 -060086 stdplus::print(stderr, "@odata.type field doesn't exist in:\n {}\n",
87 jsonDecoded.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -070088 return false;
89 }
90
91 auto schemaType = getSchemaType(jsonDecoded);
92 if (schemaType == JsonPdrType::logEntry)
93 {
94 return processLogEntry(jsonDecoded);
95 }
96 if (schemaType == JsonPdrType::logService)
97 {
98 return processLogService(jsonDecoded);
99 }
100 return processOtherTypes(jsonDecoded);
101}
102
103JsonPdrType ExternalStorerFileInterface::getSchemaType(
104 const nlohmann::json& jsonSchema) const
105{
106 auto logEntryFound =
107 std::string(jsonSchema["@odata.type"]).find("LogEntry");
108 if (logEntryFound != std::string::npos)
109 {
110 return JsonPdrType::logEntry;
111 }
112
113 auto logServiceFound =
114 std::string(jsonSchema["@odata.type"]).find("LogService");
115 if (logServiceFound != std::string::npos)
116 {
117 return JsonPdrType::logService;
118 }
119
120 return JsonPdrType::other;
121}
122
123bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry)
124{
125 // TODO: Add policies for LogEntry retention.
126 // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1.
127 if (logServiceId.empty())
128 {
Patrick Williams5de90612024-02-13 21:31:53 -0600129 stdplus::print(stderr,
130 "First need a LogService PDR with a new UUID.\n");
kasunath37bc0df2022-06-07 12:40:26 -0700131 return false;
132 }
133
Brandon Kim41a58d42024-09-06 06:49:42 +0000134 // Check to see if we are hitting the limit of filePathQueue, delete oldest
135 // log entry first before processing another entry
136 if (logEntryQueue.size() == maxNumLogEntries)
137 {
138 std::string oldestFilePath = std::move(logEntryQueue.front());
139 logEntryQueue.pop();
140
141 if (!fileHandler->removeAll(oldestFilePath))
142 {
143 stdplus::print(
144 stderr,
145 "Failed to delete the oldest entry path, not processing the next log,: {}\n",
146 oldestFilePath);
147 return false;
148 }
149 }
150
kasunath37bc0df2022-06-07 12:40:26 -0700151 std::string id = boost::uuids::to_string(randomGen());
kasunatha3b64fb2022-06-15 18:47:18 -0700152 std::string fullPath =
Patrick Williams5de90612024-02-13 21:31:53 -0600153 std::format("{}/redfish/v1/Systems/system/LogServices/{}/Entries/{}",
kasunatha3b64fb2022-06-15 18:47:18 -0700154 rootPath, logServiceId, id);
kasunath37bc0df2022-06-07 12:40:26 -0700155
156 // Populate the "Id" with the UUID we generated.
157 logEntry["Id"] = id;
158 // Remove the @odata.id from the JSON since ExternalStorer will fill it for
159 // a client.
160 logEntry.erase("@odata.id");
161
kasunatha3b64fb2022-06-15 18:47:18 -0700162 if (!fileHandler->createFile(fullPath, logEntry))
163 {
Patrick Williams5de90612024-02-13 21:31:53 -0600164 stdplus::print(stderr,
165 "Failed to create a file for log entry path: {}\n",
166 fullPath);
kasunatha3b64fb2022-06-15 18:47:18 -0700167 return false;
168 }
169
170 cperNotifier->createEntry(fullPath + "/index.json");
Brandon Kim41a58d42024-09-06 06:49:42 +0000171
172 // Attempt to push to logEntrySavedQueue first, before pushing to
173 // logEntryQueue that can be popped
174 if (logEntrySavedQueue.size() < maxNumSavedLogEntries)
175 {
176 logEntrySavedQueue.push(std::move(fullPath));
177 }
178 else
179 {
180 logEntryQueue.push(std::move(fullPath));
181 }
182
kasunatha3b64fb2022-06-15 18:47:18 -0700183 return true;
kasunath37bc0df2022-06-07 12:40:26 -0700184}
185
186bool ExternalStorerFileInterface::processLogService(
187 const nlohmann::json& logService)
188{
189 if (!logService.contains("@odata.id"))
190 {
Patrick Williams5de90612024-02-13 21:31:53 -0600191 stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
192 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700193 return false;
194 }
195
196 if (!logService.contains("Id"))
197 {
Patrick Williams5de90612024-02-13 21:31:53 -0600198 stdplus::print(stderr, "Id field doesn't exist in:\n {}\n",
199 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700200 return false;
201 }
202
203 logServiceId = logService["Id"].get<std::string>();
204
205 if (!createFile(logService["@odata.id"].get<std::string>(), logService))
206 {
Patrick Williams5de90612024-02-13 21:31:53 -0600207 stdplus::print(stderr,
208 "Failed to create LogService index file for:\n{}\n",
209 logService.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700210 return false;
211 }
212 // ExternalStorer needs a .../Entries/index.json file with no data.
213 nlohmann::json jEmpty = "{}"_json;
214 return createFile(logService["@odata.id"].get<std::string>() + "/Entries",
215 jEmpty);
216}
217
218bool ExternalStorerFileInterface::processOtherTypes(
219 const nlohmann::json& jsonPdr) const
220{
221 if (!jsonPdr.contains("@odata.id"))
222 {
Patrick Williams5de90612024-02-13 21:31:53 -0600223 stdplus::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
224 jsonPdr.dump(4));
kasunath37bc0df2022-06-07 12:40:26 -0700225 return false;
226 }
227 return createFile(jsonPdr["@odata.id"].get<std::string>(), jsonPdr);
228}
229
230bool ExternalStorerFileInterface::createFile(
231 const std::string& subPath, const nlohmann::json& jsonPdr) const
232{
233 return fileHandler->createFile(rootPath + subPath, jsonPdr);
234}
235
236} // namespace rde
237} // namespace bios_bmc_smm_error_logger