1 #include "tool_constants.hpp"
2 #include "tool_utils.hpp"
3 #include "vpd_tool.hpp"
4
5 #include <CLI/CLI.hpp>
6
7 #include <filesystem>
8 #include <iostream>
9
10 /**
11 * @brief Resets the VPD on DBus for all the Frus.
12 *
13 * API clears the inventory persisted data and restarts the phosphor inventory
14 * manager(PIM) DBus service and the VPD manager service. VPD manager service
15 * collects the VPD for all the FRU's listed on the system config JSON and calls
16 * PIM to publish VPD on DBus.
17 *
18 * Note: Force reset only happens if chassis is powered off.
19 *
20 * @return On success returns 0, otherwise returns -1.
21 */
forceReset()22 int forceReset()
23 {
24 if (vpd::utils::isChassisPowerOff())
25 {
26 vpd::VpdTool l_vpdToolObj;
27 return l_vpdToolObj.resetVpdOnDbus();
28 }
29
30 std::cerr
31 << "The chassis power state is not Off. Force reset operation is not allowed."
32 << std::endl;
33 return vpd::constants::FAILURE;
34 }
35
36 /**
37 * @brief API to perform manufacturing clean.
38 *
39 * @param[in] i_mfgCleanConfirmFlag - Confirmation flag to perform manufacturing
40 * clean.
41 * @param[in] i_syncBiosAttributesFlag - Flag which specifies whether
42 * BIOS attribute related keywords need to be synced from BIOS Config Manager
43 * instead of being reset to default value.
44 *
45 * @return Status returned by cleanSystemVpd operation, success otherwise.
46 */
doMfgClean(const auto & i_mfgCleanConfirmFlag,const auto & i_syncBiosAttributesFlag)47 int doMfgClean(const auto& i_mfgCleanConfirmFlag,
48 const auto& i_syncBiosAttributesFlag)
49 {
50 if (i_mfgCleanConfirmFlag->empty())
51 {
52 constexpr auto MAX_CONFIRMATION_STR_LENGTH{3};
53 std::string l_confirmation{};
54 std::cout
55 << "This option resets some of the system VPD keywords to their default values. Do you really wish to proceed further?[yes/no]:";
56 std::cin >> std::setw(MAX_CONFIRMATION_STR_LENGTH) >> l_confirmation;
57
58 if (l_confirmation != "yes")
59 {
60 return vpd::constants::SUCCESS;
61 }
62 }
63
64 vpd::VpdTool l_vpdToolObj;
65
66 // delete the vpd dump directory
67 l_vpdToolObj.clearVpdDumpDir();
68
69 return l_vpdToolObj.cleanSystemVpd(!i_syncBiosAttributesFlag->empty());
70 }
71
72 /**
73 * @brief API to write keyword's value.
74 *
75 * @param[in] i_hardwareFlag - Flag to perform write on hardware.
76 * @param[in] i_keywordValueOption - Option to read keyword value from command.
77 * @param[in] i_vpdPath - DBus object path or EEPROM path.
78 * @param[in] i_recordName - Record to be updated.
79 * @param[in] i_keywordName - Keyword to be updated.
80 * @param[in] i_keywordValue - Value to be updated in keyword.
81 *
82 * @return Status of writeKeyword operation, failure otherwise.
83 */
writeKeyword(const auto & i_hardwareFlag,const auto & i_keywordValueOption,const std::string & i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const std::string & i_keywordValue)84 int writeKeyword(const auto& i_hardwareFlag, const auto& i_keywordValueOption,
85 const std::string& i_vpdPath, const std::string& i_recordName,
86 const std::string& i_keywordName,
87 const std::string& i_keywordValue)
88 {
89 std::error_code l_ec;
90
91 if (!i_hardwareFlag->empty() && !std::filesystem::exists(i_vpdPath, l_ec))
92 {
93 std::cerr << "Given EEPROM file path doesn't exist[" + i_vpdPath << "]."
94 << std::endl;
95 if (l_ec)
96 {
97 std::cerr << "Reason: " + l_ec.message() << std::endl;
98 }
99 return vpd::constants::FAILURE;
100 }
101
102 if (!i_keywordValueOption->empty() && i_keywordValue.empty())
103 {
104 std::cerr
105 << "Please provide keyword value.\nUse --value/--file to give "
106 "keyword value. Refer --help."
107 << std::endl;
108 return vpd::constants::FAILURE;
109 }
110
111 if (i_keywordValueOption->empty())
112 {
113 std::cerr
114 << "Please provide keyword value.\nUse --value/--file to give "
115 "keyword value. Refer --help."
116 << std::endl;
117 return vpd::constants::FAILURE;
118 }
119
120 if (i_keywordName == vpd::constants::KwdIM)
121 {
122 if (!(i_keywordValue.substr(0, 2).compare("0x") ==
123 vpd::constants::STR_CMP_SUCCESS))
124 {
125 std::cerr << "Please provide IM value in hex format." << std::endl;
126 return vpd::constants::FAILURE;
127 }
128
129 if (std::find(vpd::constants::validImValues.begin(),
130 vpd::constants::validImValues.end(), i_keywordValue) ==
131 vpd::constants::validImValues.end())
132 {
133 std::cerr << "Given IM value [" << i_keywordValue
134 << "] doesn't match with any of the valid system type."
135 << std::endl;
136 return vpd::constants::FAILURE;
137 }
138 }
139
140 vpd::VpdTool l_vpdToolObj;
141 return l_vpdToolObj.writeKeyword(i_vpdPath, i_recordName, i_keywordName,
142 i_keywordValue, !i_hardwareFlag->empty());
143 }
144
145 /**
146 * @brief API to read keyword's value.
147 *
148 * @param[in] i_hardwareFlag - Flag to perform write on hardware.
149 * @param[in] i_vpdPath - DBus object path or EEPROM path.
150 * @param[in] i_recordName - Record to be updated.
151 * @param[in] i_keywordName - Keyword to be updated.
152 * @param[in] i_filePath - File path to save keyword's read value.
153 *
154 * @return On success return 0, otherwise return -1.
155 */
readKeyword(const auto & i_hardwareFlag,const std::string & i_vpdPath,const std::string & i_recordName,const std::string & i_keywordName,const std::string & i_filePath)156 int readKeyword(const auto& i_hardwareFlag, const std::string& i_vpdPath,
157 const std::string& i_recordName,
158 const std::string& i_keywordName, const std::string& i_filePath)
159 {
160 std::error_code l_ec;
161
162 if (!i_hardwareFlag->empty() && !std::filesystem::exists(i_vpdPath, l_ec))
163 {
164 std::string l_errMessage{
165 "Given EEPROM file path doesn't exist : " + i_vpdPath};
166
167 if (l_ec)
168 {
169 l_errMessage += ". filesystem call exists failed, reason: " +
170 l_ec.message();
171 }
172
173 std::cerr << l_errMessage << std::endl;
174 return vpd::constants::FAILURE;
175 }
176
177 bool l_isHardwareOperation = (!i_hardwareFlag->empty() ? true : false);
178
179 vpd::VpdTool l_vpdToolObj;
180 return l_vpdToolObj.readKeyword(i_vpdPath, i_recordName, i_keywordName,
181 l_isHardwareOperation, i_filePath);
182 }
183
184 /**
185 * @brief API to check option value pair in the tool command.
186 *
187 * In VPD tool command, some of the option(s) mandate values to be passed along
188 * with the option. This API based on option, detects those mandatory value(s).
189 *
190 * @param[in] i_objectOption - Option to pass object path.
191 * @param[in] i_vpdPath - Object path, DBus or EEPROM.
192 * @param[in] i_recordOption - Option to pass record name.
193 * @param[in] i_recordName - Record name.
194 * @param[in] i_keywordOption - Option to pass keyword name.
195 * @param[in] i_keywordName - Keyword name.
196 * @param[in] i_fileOption - Option to pass file path.
197 * @param[in] i_filePath - File path.
198 *
199 * @return Success if corresponding value is found against option, failure
200 * otherwise.
201 */
checkOptionValuePair(const auto & i_objectOption,const auto & i_vpdPath,const auto & i_recordOption,const auto & i_recordName,const auto & i_keywordOption,const auto & i_keywordName,const auto & i_fileOption,const auto & i_filePath)202 int checkOptionValuePair(const auto& i_objectOption, const auto& i_vpdPath,
203 const auto& i_recordOption, const auto& i_recordName,
204 const auto& i_keywordOption, const auto& i_keywordName,
205 const auto& i_fileOption, const auto& i_filePath)
206 {
207 if (!i_objectOption->empty() && i_vpdPath.empty())
208 {
209 std::cout << "Given path is empty." << std::endl;
210 return vpd::constants::FAILURE;
211 }
212
213 if (!i_recordOption->empty() &&
214 (i_recordName.size() != vpd::constants::RECORD_SIZE))
215 {
216 std::cerr << "Record " << i_recordName << " is not supported."
217 << std::endl;
218 return vpd::constants::FAILURE;
219 }
220
221 if (!i_keywordOption->empty() &&
222 (i_keywordName.size() != vpd::constants::KEYWORD_SIZE))
223 {
224 std::cerr << "Keyword " << i_keywordName << " is not supported."
225 << std::endl;
226 return vpd::constants::FAILURE;
227 }
228
229 if (!i_fileOption->empty() && i_filePath.empty())
230 {
231 std::cout << "File path is empty." << std::endl;
232 return vpd::constants::FAILURE;
233 }
234
235 return vpd::constants::SUCCESS;
236 }
237
238 /**
239 * @brief API to create app footer.
240 *
241 * @param[in] i_app - CLI::App object.
242 */
updateFooter(CLI::App & i_app)243 void updateFooter(CLI::App& i_app)
244 {
245 i_app.footer(
246 "Read:\n"
247 " IPZ Format:\n"
248 " From DBus to console: "
249 "vpd-tool -r -O <DBus Object Path> -R <Record Name> -K <Keyword Name>\n"
250 " From DBus to file: "
251 "vpd-tool -r -O <DBus Object Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
252 " From hardware to console: "
253 "vpd-tool -r -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name>\n"
254 " From hardware to file: "
255 "vpd-tool -r -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
256 "Write:\n"
257 " IPZ Format:\n"
258 " On DBus: "
259 "vpd-tool -w/-u -O <DBus Object Path> -R <Record Name> -K <Keyword Name> -V <Keyword Value>\n"
260 " On DBus, take keyword value from file:\n"
261 " vpd-tool -w/-u -O <DBus Object Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
262 " On hardware: "
263 "vpd-tool -w/-u -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> -V <Keyword Value>\n"
264 " On hardware, take keyword value from file:\n"
265 " vpd-tool -w/-u -H -O <EEPROM Path> -R <Record Name> -K <Keyword Name> --file <File Path>\n"
266 "Dump Object:\n"
267 " From DBus to console: "
268 "vpd-tool -o -O <DBus Object Path>\n"
269 "Fix System VPD:\n"
270 " vpd-tool --fixSystemVPD\n"
271 "MfgClean:\n"
272 " Flag to clean and reset specific keywords on system VPD to its default value.\n"
273 " vpd-tool --mfgClean\n"
274 " To sync BIOS attribute related keywords with BIOS Config Manager:\n"
275 " vpd-tool --mfgClean --syncBiosAttributes\n"
276 "Dump Inventory:\n"
277 " From DBus to console in JSON format: "
278 "vpd-tool -i\n"
279 " From DBus to console in Table format: "
280 "vpd-tool -i -t\n"
281 "Force Reset:\n"
282 " vpd-tool --forceReset\n");
283 }
284
main(int argc,char ** argv)285 int main(int argc, char** argv)
286 {
287 CLI::App l_app{"VPD Command Line Tool"};
288
289 std::string l_vpdPath{};
290 std::string l_recordName{};
291 std::string l_keywordName{};
292 std::string l_filePath{};
293 std::string l_keywordValue{};
294
295 updateFooter(l_app);
296
297 auto l_objectOption =
298 l_app.add_option("--object, -O", l_vpdPath, "File path");
299 auto l_recordOption =
300 l_app.add_option("--record, -R", l_recordName, "Record name");
301 auto l_keywordOption =
302 l_app.add_option("--keyword, -K", l_keywordName, "Keyword name");
303
304 auto l_fileOption = l_app.add_option(
305 "--file", l_filePath,
306 "Absolute file path,\nNote: For write operation, file should contain keyword’s value in either ascii or in hex format.");
307
308 auto l_keywordValueOption =
309 l_app.add_option("--value, -V", l_keywordValue,
310 "Keyword value in ascii/hex format."
311 " ascii ex: 01234; hex ex: 0x30313233");
312
313 auto l_hardwareFlag =
314 l_app.add_flag("--Hardware, -H", "CAUTION: Developer only option.");
315
316 auto l_readFlag = l_app.add_flag("--readKeyword, -r", "Read keyword")
317 ->needs(l_objectOption)
318 ->needs(l_recordOption)
319 ->needs(l_keywordOption);
320
321 auto l_writeFlag =
322 l_app
323 .add_flag(
324 "--writeKeyword, -w,--updateKeyword, -u",
325 "Write keyword,\nNote: In case DBus path is provided, both EEPROM and DBus are updated with the given keyword's value.\nIn case EEPROM path is provided, only the given EEPROM is updated with the given keyword's value.")
326 ->needs(l_objectOption)
327 ->needs(l_recordOption)
328 ->needs(l_keywordOption);
329
330 // ToDo: Take offset value from user for hardware path.
331
332 auto l_dumpObjFlag =
333 l_app
334 .add_flag("--dumpObject, -o",
335 "Dump specific properties of an inventory object")
336 ->needs(l_objectOption);
337
338 auto l_fixSystemVpdFlag = l_app.add_flag(
339 "--fixSystemVPD",
340 "Use this option to interactively fix critical system VPD keywords");
341 auto l_dumpInventoryFlag =
342 l_app.add_flag("--dumpInventory, -i", "Dump all the inventory objects");
343
344 auto l_mfgCleanFlag = l_app.add_flag(
345 "--mfgClean", "Manufacturing clean on system VPD keyword");
346
347 auto l_mfgCleanConfirmFlag = l_app.add_flag(
348 "--yes", "Using this flag with --mfgClean option, assumes "
349 "yes to proceed without confirmation.");
350
351 auto l_dumpInventoryTableFlag =
352 l_app.add_flag("--table, -t", "Dump inventory in table format");
353
354 auto l_mfgCleanSyncBiosAttributesFlag = l_app.add_flag(
355 "--syncBiosAttributes, -s",
356 "Using this flag with --mfgClean option, Syncs the BIOS attribute related keywords from BIOS Config Manager service instead resetting keyword's value to default value");
357
358 auto l_forceResetFlag = l_app.add_flag(
359 "--forceReset, -f, -F",
360 "Force collect for hardware. CAUTION: Developer only option.");
361
362 CLI11_PARSE(l_app, argc, argv);
363
364 if (checkOptionValuePair(l_objectOption, l_vpdPath, l_recordOption,
365 l_recordName, l_keywordOption, l_keywordName,
366 l_fileOption, l_filePath) ==
367 vpd::constants::FAILURE)
368 {
369 return vpd::constants::FAILURE;
370 }
371
372 if (!l_readFlag->empty())
373 {
374 return readKeyword(l_hardwareFlag, l_vpdPath, l_recordName,
375 l_keywordName, l_filePath);
376 }
377
378 if (!l_writeFlag->empty())
379 {
380 if ((l_keywordValueOption->empty() && l_fileOption->empty()) ||
381 (!l_keywordValueOption->empty() && !l_fileOption->empty()))
382 {
383 std::cerr
384 << "Please provide keyword value.\nUse --value/--file to give "
385 "keyword value. Refer --help."
386 << std::endl;
387 return vpd::constants::FAILURE;
388 }
389
390 if (!l_fileOption->empty())
391 {
392 l_keywordValue = vpd::utils::readValueFromFile(l_filePath);
393 if (l_keywordValue.empty())
394 {
395 return vpd::constants::FAILURE;
396 }
397
398 return writeKeyword(l_hardwareFlag, l_fileOption, l_vpdPath,
399 l_recordName, l_keywordName, l_keywordValue);
400 }
401
402 return writeKeyword(l_hardwareFlag, l_keywordValueOption, l_vpdPath,
403 l_recordName, l_keywordName, l_keywordValue);
404 }
405
406 if (!l_dumpObjFlag->empty())
407 {
408 vpd::VpdTool l_vpdToolObj;
409 return l_vpdToolObj.dumpObject(l_vpdPath);
410 }
411
412 if (!l_fixSystemVpdFlag->empty())
413 {
414 vpd::VpdTool l_vpdToolObj;
415 return l_vpdToolObj.fixSystemVpd();
416 }
417
418 if (!l_mfgCleanFlag->empty())
419 {
420 return doMfgClean(l_mfgCleanConfirmFlag,
421 l_mfgCleanSyncBiosAttributesFlag);
422 }
423
424 if (!l_dumpInventoryFlag->empty())
425 {
426 vpd::VpdTool l_vpdToolObj;
427 return l_vpdToolObj.dumpInventory(!l_dumpInventoryTableFlag->empty());
428 }
429
430 if (!l_forceResetFlag->empty())
431 {
432 return forceReset();
433 }
434
435 std::cout << l_app.help() << std::endl;
436 return vpd::constants::FAILURE;
437 }
438