1 #include "fru_parser.hpp"
2 
3 #include <nlohmann/json.hpp>
4 #include <phosphor-logging/lg2.hpp>
5 #include <xyz/openbmc_project/Common/error.hpp>
6 
7 #include <filesystem>
8 #include <fstream>
9 #include <iostream>
10 
11 PHOSPHOR_LOG2_USING;
12 
13 using namespace pldm::responder::dbus;
14 
15 namespace pldm
16 {
17 namespace responder
18 {
19 namespace fru_parser
20 {
21 using Json = nlohmann::json;
22 using InternalFailure =
23     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
24 
25 const Json emptyJson{};
26 const std::vector<Json> emptyJsonList{};
27 const std::vector<std::string> emptyStringVec{};
28 
29 FruParser::FruParser(const std::string& dirPath,
30                      const fs::path& fruMasterJsonPath)
31 {
32     if (fs::exists(fruMasterJsonPath))
33     {
34         setupDefaultDBusLookup(fruMasterJsonPath);
35     }
36     setupDefaultFruRecordMap();
37 
38     fs::path dir(dirPath);
39     if (fs::exists(dir) && !fs::is_empty(dir))
40     {
41         setupFruRecordMap(dirPath);
42     }
43 }
44 
45 void FruParser::setupDefaultDBusLookup(const fs::path& masterJsonPath)
46 {
47     constexpr auto service = "xyz.openbmc_project.Inventory.Manager";
48     constexpr auto rootPath = "/xyz/openbmc_project/inventory";
49     std::ifstream jsonFile(masterJsonPath);
50     auto data = Json::parse(jsonFile, nullptr, false);
51     if (data.is_discarded())
52     {
53         error(
54             "Parsing FRU Dbus Lookup Map config file failed, FILE={JSON_PATH}",
55             "JSON_PATH", masterJsonPath.c_str());
56         std::abort();
57     }
58     std::map<Interface, EntityType> defIntfToEntityType;
59     auto dbusMap = data.value("FruDBusLookupMap", emptyJson);
60     for (const auto& element : dbusMap.items())
61 
62     {
63         try
64         {
65             defIntfToEntityType[static_cast<Interface>(element.key())] =
66                 static_cast<EntityType>(element.value());
67         }
68         catch (const std::exception& e)
69         {
70             error("FRU DBus lookup map format error");
71             throw InternalFailure();
72         }
73     }
74 
75     Interfaces interfaces{};
76     for (auto [intf, entityType] : defIntfToEntityType)
77     {
78         intfToEntityType[intf] = entityType;
79         interfaces.emplace(intf);
80     }
81 
82     lookupInfo.emplace(service, rootPath, std::move(interfaces));
83 }
84 
85 void FruParser::setupDefaultFruRecordMap()
86 {
87     const FruRecordInfo generalRecordInfo = {
88         1, // generalRecordType
89         1, // encodingTypeASCII
90         {
91             // DSP0257 Table 5 General FRU Record Field Type Definitions
92             {"xyz.openbmc_project.Inventory.Decorator.Asset", "Model", "string",
93              2},
94             {"xyz.openbmc_project.Inventory.Decorator.Asset", "PartNumber",
95              "string", 3},
96             {"xyz.openbmc_project.Inventory.Decorator.Asset", "SerialNumber",
97              "string", 4},
98             {"xyz.openbmc_project.Inventory.Decorator.Asset", "Manufacturer",
99              "string", 5},
100             {"xyz.openbmc_project.Inventory.Item", "PrettyName", "string", 8},
101             {"xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag",
102              "string", 11},
103             {"xyz.openbmc_project.Inventory.Decorator.Revision", "Version",
104              "string", 10},
105         }};
106 
107     for (auto [intf, entityType] : intfToEntityType)
108     {
109         recordMap[intf] = {generalRecordInfo};
110     }
111 }
112 
113 void FruParser::setupFruRecordMap(const std::string& dirPath)
114 {
115     for (auto& file : fs::directory_iterator(dirPath))
116     {
117         auto fileName = file.path().filename().string();
118         std::ifstream jsonFile(file.path());
119         auto data = Json::parse(jsonFile, nullptr, false);
120         if (data.is_discarded())
121         {
122             error("Parsing FRU config file failed, FILE={FILE_PATH}",
123                   "FILE_PATH", file.path().c_str());
124             throw InternalFailure();
125         }
126 
127         try
128         {
129             auto record = data.value("record_details", emptyJson);
130             auto recordType =
131                 static_cast<uint8_t>(record.value("fru_record_type", 0));
132             auto encType =
133                 static_cast<uint8_t>(record.value("fru_encoding_type", 0));
134             auto dbusIntfName = record.value("dbus_interface_name", "");
135             auto entries = data.value("fru_fields", emptyJsonList);
136             std::vector<FieldInfo> fieldInfo;
137 
138             for (const auto& entry : entries)
139             {
140                 auto fieldType =
141                     static_cast<uint8_t>(entry.value("fru_field_type", 0));
142                 auto dbus = entry.value("dbus", emptyJson);
143                 auto interface = dbus.value("interface", "");
144                 auto property = dbus.value("property_name", "");
145                 auto propType = dbus.value("property_type", "");
146                 fieldInfo.emplace_back(
147                     std::make_tuple(std::move(interface), std::move(property),
148                                     std::move(propType), std::move(fieldType)));
149             }
150 
151             FruRecordInfo fruInfo;
152             fruInfo = std::make_tuple(recordType, encType,
153                                       std::move(fieldInfo));
154 
155             auto search = recordMap.find(dbusIntfName);
156 
157             // PLDM FRU can have multiple records for the same FRU like General
158             // FRU record and multiple OEM FRU records. If the FRU item
159             // interface name is already in the map, that indicates a record
160             // info is already added for the FRU, so append the new record info
161             // to the same data.
162             if (search != recordMap.end())
163             {
164                 search->second.emplace_back(std::move(fruInfo));
165             }
166             else
167             {
168                 FruRecordInfos recordInfos{fruInfo};
169                 recordMap.emplace(dbusIntfName, recordInfos);
170             }
171         }
172         catch (const std::exception& e)
173         {
174             continue;
175         }
176     }
177 }
178 
179 } // namespace fru_parser
180 
181 } // namespace responder
182 
183 } // namespace pldm
184