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