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