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