1 #include "vpd_tool_impl.hpp"
2 
3 #include <CLI/CLI.hpp>
4 
5 #include <filesystem>
6 #include <fstream>
7 #include <iostream>
8 
9 using namespace CLI;
10 using namespace std;
11 namespace fs = std::filesystem;
12 using namespace openpower::vpd;
13 using json = nlohmann::json;
14 
main(int argc,char ** argv)15 int main(int argc, char** argv)
16 {
17     int rc = 0;
18     App app{"VPD Command line tool to dump the inventory and to read and "
19             "update the keywords"};
20 
21     string objectPath{};
22     string recordName{};
23     string keyword{};
24     string val{};
25     uint32_t offset = 0;
26 
27     auto object =
28         app.add_option("--object, -O", objectPath, "Enter the Object Path");
29     auto record =
30         app.add_option("--record, -R", recordName, "Enter the Record Name");
31     auto kw = app.add_option("--keyword, -K", keyword, "Enter the Keyword");
32     auto valOption = app.add_option(
33         "--value, -V", val,
34         "Enter the value. The value to be updated should be either in ascii or "
35         "in hex. ascii eg: 01234; hex eg: 0x30313233");
36     app.add_option("--seek, -s", offset,
37                    "User can provide VPD offset using this option. Default "
38                    "offset value is 0. Using --seek is optional and is valid "
39                    "only while using --Hardware/-H option.");
40 
41     auto dumpObjFlag =
42         app.add_flag("--dumpObject, -o",
43                      "Dump the given object from the inventory. { "
44                      "vpd-tool-exe --dumpObject/-o --object/-O object-name }")
45             ->needs(object);
46 
47     auto dumpInvFlag = app.add_flag(
48         "--dumpInventory, -i", "Dump all the inventory objects. { vpd-tool-exe "
49                                "--dumpInventory/-i }");
50 
51     auto readFlag =
52         app.add_flag("--readKeyword, -r",
53                      "Read the data of the given keyword. { "
54                      "vpd-tool-exe --readKeyword/-r --object/-O "
55                      "\"object-name\" --record/-R \"record-name\" --keyword/-K "
56                      "\"keyword-name\" }")
57             ->needs(object)
58             ->needs(record)
59             ->needs(kw);
60 
61     auto writeFlag =
62         app.add_flag(
63                "--writeKeyword, -w, --updateKeyword, -u",
64                "Update the value. { vpd-tool-exe "
65                "--writeKeyword/-w/--updateKeyword/-u "
66                "--object/-O object-name --record/-R record-name --keyword/-K "
67                "keyword-name --value/-V (or) --file }. Value can be given "
68                "directly via console using --value or via file using --file")
69             ->needs(object)
70             ->needs(record)
71             ->needs(kw);
72 
73     auto fileOption = app.add_option(
74         "--file", val,
75         "Enter the file name with its absolute path. This option can be used "
76         "in read and write operations. When used in read, the read value will "
77         "be saved to this file and when used in write, the value to be written "
78         "will be taken from this file.");
79 
80     auto forceResetFlag =
81         app.add_flag("--forceReset, -f, -F",
82                      "Force Collect for Hardware. CAUTION: Developer Only "
83                      "Option. { vpd-tool-exe --forceReset/-f/-F }");
84 
85     auto Hardware = app.add_flag(
86         "--Hardware, -H",
87         "This is a supplementary flag to read/write directly from/to hardware. "
88         "User should provide valid hardware/eeprom path (and not dbus object "
89         "path) in the -O/--object path. CAUTION: Developer Only Option");
90 
91     auto fixSystemVPDFlag = app.add_flag(
92         "--fixSystemVPD", "Use this option to interactively fix critical "
93                           "system VPD keywords {vpd-tool-exe --fixSystemVPD}");
94 
95     auto mfgClean =
96         app.add_flag("--mfgClean", "Flag to clean and reset specific keywords "
97                                    "on system VPD to its default value.");
98 
99     auto confirm =
100         app.add_flag("--yes", "Using this flag with --mfgClean option, assumes "
101                               "yes to proceed without confirmation.");
102 
103     CLI11_PARSE(app, argc, argv);
104 
105     ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
106     auto jsObject = json::parse(inventoryJson);
107 
108     try
109     {
110         if (*object && objectPath.empty())
111         {
112             throw runtime_error("Given path is empty.");
113         }
114 
115         if (*record && (recordName.size() != 4))
116         {
117             throw runtime_error("Record " + recordName + " not supported.");
118         }
119 
120         if ((*kw) && (keyword.size() != 2))
121         {
122             throw runtime_error("Keyword " + keyword + " not supported.");
123         }
124 
125         if (*Hardware)
126         {
127             if (!fs::exists(objectPath)) // if dbus object path is given or
128                                          // invalid eeprom path is given
129             {
130                 string errorMsg = "Invalid EEPROM path : ";
131                 errorMsg += objectPath;
132                 errorMsg +=
133                     ". The given EEPROM path doesn't exist. Provide valid "
134                     "EEPROM path when -H flag is used. Refer help option. ";
135                 throw runtime_error(errorMsg);
136             }
137         }
138 
139         if (*writeFlag)
140         {
141             if (isReadOnlyEEPROM(objectPath, jsObject))
142             {
143                 throw runtime_error("Read only EEPROM. Update not allowed.");
144             }
145 
146             if ((!*fileOption) && (!*valOption))
147             {
148                 throw runtime_error("Please provide the data that needs to be "
149                                     "updated. Use --value/--file to "
150                                     "input data. Refer --help.");
151             }
152 
153             if ((*fileOption) && (!fs::exists(val)))
154             {
155                 throw runtime_error("Please provide a valid file with absolute "
156                                     "path in --file.");
157             }
158         }
159 
160         if (*dumpObjFlag)
161         {
162             VpdTool vpdToolObj(move(objectPath));
163             vpdToolObj.dumpObject(jsObject);
164         }
165 
166         else if (*dumpInvFlag)
167         {
168             VpdTool vpdToolObj;
169             vpdToolObj.dumpInventory(jsObject);
170         }
171 
172         else if (*readFlag && !*Hardware)
173         {
174             VpdTool vpdToolObj(move(objectPath), move(recordName),
175                                move(keyword), move(val));
176             vpdToolObj.readKeyword();
177         }
178 
179         else if (*writeFlag && !*Hardware)
180         {
181             VpdTool vpdToolObj(move(objectPath), move(recordName),
182                                move(keyword), move(val));
183             rc = vpdToolObj.updateKeyword();
184         }
185 
186         else if (*forceResetFlag)
187         {
188             // Force reset the BMC only if the CEC is powered OFF.
189             if (getPowerState() ==
190                 "xyz.openbmc_project.State.Chassis.PowerState.Off")
191             {
192                 VpdTool vpdToolObj;
193                 vpdToolObj.forceReset(jsObject);
194             }
195             else
196             {
197                 std::cerr << "The chassis power state is not Off. Force reset "
198                              "operation is not allowed.";
199                 return -1;
200             }
201         }
202 
203         else if (*writeFlag && *Hardware)
204         {
205             VpdTool vpdToolObj(move(objectPath), move(recordName),
206                                move(keyword), move(val));
207             rc = vpdToolObj.updateHardware(offset);
208         }
209         else if (*readFlag && *Hardware)
210         {
211             VpdTool vpdToolObj(move(objectPath), move(recordName),
212                                move(keyword), move(val));
213             vpdToolObj.readKwFromHw(offset);
214         }
215         else if (*fixSystemVPDFlag)
216         {
217             std::string backupEepromPath;
218             std::string backupInvPath;
219             findBackupVPDPaths(backupEepromPath, backupInvPath, jsObject);
220             VpdTool vpdToolObj;
221 
222             if (backupEepromPath.empty())
223             {
224                 rc = vpdToolObj.fixSystemVPD();
225             }
226             else
227             {
228                 rc = vpdToolObj.fixSystemBackupVPD(backupEepromPath,
229                                                    backupInvPath);
230             }
231         }
232         else if (*mfgClean)
233         {
234             if (!*confirm)
235             {
236                 std::string confirmation{};
237                 std::cout << "\nThis option resets some of the system VPD "
238                              "keywords to their default values. Do you really "
239                              "wish to proceed further?[yes/no]: ";
240                 std::cin >> confirmation;
241 
242                 if (confirmation != "yes")
243                 {
244                     return 0;
245                 }
246             }
247             VpdTool vpdToolObj;
248             rc = vpdToolObj.cleanSystemVPD();
249         }
250         else
251         {
252             throw runtime_error("One of the valid options is required. Refer "
253                                 "--help for list of options.");
254         }
255     }
256 
257     catch (const exception& e)
258     {
259         cerr << e.what();
260         rc = -1;
261     }
262 
263     return rc;
264 }
265