1 #include "parser.hpp"
2
3 #include "constants.hpp"
4
5 #include <utility/dbus_utility.hpp>
6 #include <utility/event_logger_utility.hpp>
7 #include <utility/json_utility.hpp>
8 #include <utility/vpd_specific_utility.hpp>
9
10 #include <fstream>
11
12 namespace vpd
13 {
Parser(const std::string & vpdFilePath,nlohmann::json parsedJson)14 Parser::Parser(const std::string& vpdFilePath, nlohmann::json parsedJson) :
15 m_vpdFilePath(vpdFilePath), m_parsedJson(parsedJson)
16 {
17 std::error_code l_errCode;
18
19 // ToDo: Add minimum file size check in all the concert praser classes,
20 // depends on their VPD type.
21 if (!std::filesystem::exists(m_vpdFilePath, l_errCode))
22 {
23 std::string l_message{"Parser object creation failed, file [" +
24 m_vpdFilePath + "] doesn't exists."};
25
26 if (l_errCode)
27 {
28 l_message += " Error message: " + l_errCode.message();
29 }
30
31 throw std::runtime_error(l_message);
32 }
33
34 // Read VPD offset if applicable.
35 if (!m_parsedJson.empty())
36 {
37 uint16_t l_errorCode = 0;
38
39 m_vpdStartOffset =
40 jsonUtility::getVPDOffset(m_parsedJson, vpdFilePath, l_errorCode);
41
42 if (l_errorCode)
43 {
44 logging::logMessage(
45 "Failed to get vpd offset for path [" + m_vpdFilePath +
46 "], error: " + commonUtility::getErrCodeMsg(l_errorCode));
47 }
48 }
49 }
50
getVpdParserInstance()51 std::shared_ptr<vpd::ParserInterface> Parser::getVpdParserInstance()
52 {
53 // Read the VPD data into a vector.
54 uint16_t l_errCode = 0;
55 vpdSpecificUtility::getVpdDataInVector(m_vpdFilePath, m_vpdVector,
56 m_vpdStartOffset, l_errCode);
57
58 if (l_errCode)
59 {
60 logging::logMessage("Failed to get VPD in vector, error : " +
61 commonUtility::getErrCodeMsg(l_errCode));
62 }
63
64 // This will detect the type of parser required.
65 std::shared_ptr<vpd::ParserInterface> l_parser =
66 ParserFactory::getParser(m_vpdVector, m_vpdFilePath, m_vpdStartOffset);
67
68 return l_parser;
69 }
70
parse()71 types::VPDMapVariant Parser::parse()
72 {
73 std::shared_ptr<vpd::ParserInterface> l_parser = getVpdParserInstance();
74 return l_parser->parse();
75 }
76
updateVpdKeyword(const types::WriteVpdParams & i_paramsToWriteData,types::DbusVariantType & o_updatedValue)77 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData,
78 types::DbusVariantType& o_updatedValue)
79 {
80 int l_bytesUpdatedOnHardware = constants::FAILURE;
81
82 // A lambda to extract Record : Keyword string from i_paramsToWriteData
83 auto l_keyWordIdentifier =
84 [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
85 std::string l_keywordString{};
86 if (const types::IpzData* l_ipzData =
87 std::get_if<types::IpzData>(&i_paramsToWriteData))
88 {
89 l_keywordString =
90 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
91 }
92 else if (const types::KwData* l_kwData =
93 std::get_if<types::KwData>(&i_paramsToWriteData))
94 {
95 l_keywordString = std::get<0>(*l_kwData);
96 }
97 return l_keywordString;
98 };
99
100 try
101 {
102 // Enable Reboot Guard
103 if (constants::FAILURE == dbusUtility::EnableRebootGuard())
104 {
105 EventLogger::createAsyncPel(
106 types::ErrorType::DbusFailure,
107 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
108 std::string(
109 "Failed to enable BMC Reboot Guard while updating " +
110 l_keyWordIdentifier(i_paramsToWriteData)),
111 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
112
113 return constants::FAILURE;
114 }
115
116 // Update keyword's value on hardware
117 try
118 {
119 std::shared_ptr<ParserInterface> l_vpdParserInstance =
120 getVpdParserInstance();
121 l_bytesUpdatedOnHardware =
122 l_vpdParserInstance->writeKeywordOnHardware(
123 i_paramsToWriteData);
124 }
125 catch (const std::exception& l_exception)
126 {
127 std::string l_errMsg(
128 "Error while updating keyword's value on hardware path " +
129 m_vpdFilePath + ", error: " + std::string(l_exception.what()));
130
131 // TODO : Log PEL
132
133 throw std::runtime_error(l_errMsg);
134 }
135
136 uint16_t l_errCode = 0;
137
138 auto [l_fruPath, l_inventoryObjPath, l_redundantFruPath] =
139 jsonUtility::getAllPathsToUpdateKeyword(m_parsedJson, m_vpdFilePath,
140 l_errCode);
141
142 if (l_errCode == error_code::INVALID_INPUT_PARAMETER ||
143 l_errCode == error_code::INVALID_JSON)
144 {
145 throw std::runtime_error(
146 "Failed to get paths to update keyword. Error : " +
147 commonUtility::getErrCodeMsg(l_errCode));
148 }
149
150 // If inventory D-bus object path is present, update keyword's value on
151 // DBus
152 if (!l_inventoryObjPath.empty())
153 {
154 types::Record l_recordName;
155 std::string l_interfaceName;
156 std::string l_propertyName;
157 types::DbusVariantType l_keywordValue;
158
159 if (const types::IpzData* l_ipzData =
160 std::get_if<types::IpzData>(&i_paramsToWriteData))
161 {
162 l_recordName = std::get<0>(*l_ipzData);
163 l_interfaceName = constants::ipzVpdInf + l_recordName;
164 l_propertyName = std::get<1>(*l_ipzData);
165
166 try
167 {
168 // Read keyword's value from hardware to write the same on
169 // D-bus.
170 std::shared_ptr<ParserInterface> l_vpdParserInstance =
171 getVpdParserInstance();
172
173 l_keywordValue =
174 l_vpdParserInstance->readKeywordFromHardware(
175 types::ReadVpdParams(
176 std::make_tuple(l_recordName, l_propertyName)));
177
178 // return the actual value updated on hardware
179 o_updatedValue = l_keywordValue;
180 }
181 catch (const std::exception& l_exception)
182 {
183 // Unable to read keyword's value from hardware.
184 std::string l_errMsg(
185 "Error while reading keyword's value from hadware path " +
186 m_vpdFilePath +
187 ", error: " + std::string(l_exception.what()));
188
189 // TODO: Log PEL
190
191 throw std::runtime_error(l_errMsg);
192 }
193 }
194 else
195 {
196 // Input parameter type provided isn't compatible to perform
197 // update.
198 std::string l_errMsg(
199 "Input parameter type isn't compatible to update keyword's value on DBus for object path: " +
200 l_inventoryObjPath);
201 throw std::runtime_error(l_errMsg);
202 }
203
204 // Get D-bus name for the given keyword
205 l_propertyName = vpdSpecificUtility::getDbusPropNameForGivenKw(
206 l_propertyName, l_errCode);
207
208 if (l_errCode)
209 {
210 logging::logMessage(
211 "Failed to get Dbus property name for given keyword, error : " +
212 commonUtility::getErrCodeMsg(l_errCode));
213 }
214
215 // Create D-bus object map
216 types::ObjectMap l_dbusObjMap = {std::make_pair(
217 l_inventoryObjPath,
218 types::InterfaceMap{std::make_pair(
219 l_interfaceName, types::PropertyMap{std::make_pair(
220 l_propertyName, l_keywordValue)})})};
221
222 // Call method to update on dbus
223 if (!dbusUtility::publishVpdOnDBus(std::move(l_dbusObjMap)))
224 {
225 // Update to dbus failed.
226 std::string l_errMsg(
227 "publishVpdOnDBus is failed for object path: " +
228 l_inventoryObjPath);
229 throw std::runtime_error(l_errMsg);
230 }
231 }
232
233 if (l_errCode == error_code::ERROR_GETTING_REDUNDANT_PATH)
234 {
235 logging::logMessage(commonUtility::getErrCodeMsg(l_errCode));
236 }
237
238 // Update keyword's value on redundant hardware if present
239 if (!l_redundantFruPath.empty())
240 {
241 if (updateVpdKeywordOnRedundantPath(l_redundantFruPath,
242 i_paramsToWriteData) < 0)
243 {
244 std::string l_errMsg(
245 "Error while updating keyword's value on redundant path " +
246 l_redundantFruPath);
247 throw std::runtime_error(l_errMsg);
248 }
249 }
250
251 // TODO: Check if revert is required when any of the writes fails.
252 // TODO: Handle error logging
253 }
254 catch (const std::exception& l_ex)
255 {
256 logging::logMessage("Update VPD Keyword failed for : " +
257 l_keyWordIdentifier(i_paramsToWriteData) +
258 " failed due to error: " + l_ex.what());
259
260 // update failed, set return value to failure
261 l_bytesUpdatedOnHardware = constants::FAILURE;
262 }
263
264 // Disable Reboot Guard
265 if (constants::FAILURE == dbusUtility::DisableRebootGuard())
266 {
267 EventLogger::createAsyncPel(
268 types::ErrorType::DbusFailure, types::SeverityType::Critical,
269 __FILE__, __FUNCTION__, 0,
270 std::string("Failed to disable BMC Reboot Guard while updating " +
271 l_keyWordIdentifier(i_paramsToWriteData)),
272 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
273 }
274
275 return l_bytesUpdatedOnHardware;
276 }
277
updateVpdKeyword(const types::WriteVpdParams & i_paramsToWriteData)278 int Parser::updateVpdKeyword(const types::WriteVpdParams& i_paramsToWriteData)
279 {
280 types::DbusVariantType o_updatedValue;
281 return updateVpdKeyword(i_paramsToWriteData, o_updatedValue);
282 }
283
updateVpdKeywordOnRedundantPath(const std::string & i_fruPath,const types::WriteVpdParams & i_paramsToWriteData)284 int Parser::updateVpdKeywordOnRedundantPath(
285 const std::string& i_fruPath,
286 const types::WriteVpdParams& i_paramsToWriteData)
287 {
288 try
289 {
290 std::shared_ptr<Parser> l_parserObj =
291 std::make_shared<Parser>(i_fruPath, m_parsedJson);
292
293 std::shared_ptr<ParserInterface> l_vpdParserInstance =
294 l_parserObj->getVpdParserInstance();
295
296 return l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
297 }
298 catch (const std::exception& l_exception)
299 {
300 EventLogger::createSyncPel(
301 types::ErrorType::InvalidVpdMessage,
302 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
303 "Error while updating keyword's value on redundant path " +
304 i_fruPath + ", error: " + std::string(l_exception.what()),
305 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
306 return -1;
307 }
308 }
309
updateVpdKeywordOnHardware(const types::WriteVpdParams & i_paramsToWriteData)310 int Parser::updateVpdKeywordOnHardware(
311 const types::WriteVpdParams& i_paramsToWriteData)
312 {
313 int l_bytesUpdatedOnHardware = constants::FAILURE;
314
315 // A lambda to extract Record : Keyword string from i_paramsToWriteData
316 auto l_keyWordIdentifier =
317 [](const types::WriteVpdParams& i_paramsToWriteData) -> std::string {
318 std::string l_keywordString{};
319 if (const types::IpzData* l_ipzData =
320 std::get_if<types::IpzData>(&i_paramsToWriteData))
321 {
322 l_keywordString =
323 std::get<0>(*l_ipzData) + ":" + std::get<1>(*l_ipzData);
324 }
325 else if (const types::KwData* l_kwData =
326 std::get_if<types::KwData>(&i_paramsToWriteData))
327 {
328 l_keywordString = std::get<0>(*l_kwData);
329 }
330 return l_keywordString;
331 };
332
333 try
334 {
335 // Enable Reboot Guard
336 if (constants::FAILURE == dbusUtility::EnableRebootGuard())
337 {
338 EventLogger::createAsyncPel(
339 types::ErrorType::DbusFailure,
340 types::SeverityType::Informational, __FILE__, __FUNCTION__, 0,
341 std::string(
342 "Failed to enable BMC Reboot Guard while updating " +
343 l_keyWordIdentifier(i_paramsToWriteData)),
344 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
345
346 return constants::FAILURE;
347 }
348
349 std::shared_ptr<ParserInterface> l_vpdParserInstance =
350 getVpdParserInstance();
351 l_bytesUpdatedOnHardware =
352 l_vpdParserInstance->writeKeywordOnHardware(i_paramsToWriteData);
353 }
354 catch (const std::exception& l_exception)
355 {
356 types::ErrorType l_errorType;
357
358 if (typeid(l_exception) == typeid(EccException))
359 {
360 l_errorType = types::ErrorType::EccCheckFailed;
361 }
362 else
363 {
364 l_errorType = types::ErrorType::InvalidVpdMessage;
365 }
366
367 EventLogger::createAsyncPel(
368 l_errorType, types::SeverityType::Informational, __FILE__,
369 __FUNCTION__, 0,
370 "Error while updating keyword's value on hardware path [" +
371 m_vpdFilePath + "], error: " + std::string(l_exception.what()),
372 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
373 }
374
375 // Disable Reboot Guard
376 if (constants::FAILURE == dbusUtility::DisableRebootGuard())
377 {
378 EventLogger::createAsyncPel(
379 types::ErrorType::DbusFailure, types::SeverityType::Critical,
380 __FILE__, __FUNCTION__, 0,
381 std::string("Failed to disable BMC Reboot Guard while updating " +
382 l_keyWordIdentifier(i_paramsToWriteData)),
383 std::nullopt, std::nullopt, std::nullopt, std::nullopt);
384 }
385
386 return l_bytesUpdatedOnHardware;
387 }
388
389 } // namespace vpd
390