1 #include "fru_parser.hpp"
2 
3 #include <filesystem>
4 #include <fstream>
5 #include <iostream>
6 #include <nlohmann/json.hpp>
7 #include <xyz/openbmc_project/Common/error.hpp>
8 
9 namespace pldm
10 {
11 
12 namespace responder
13 {
14 
15 namespace fru_parser
16 {
17 
18 using Json = nlohmann::json;
19 using InternalFailure =
20     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
21 
22 const Json emptyJson{};
23 const std::vector<Json> emptyJsonList{};
24 const std::vector<std::string> emptyStringVec{};
25 
26 constexpr auto fruMasterJson = "FRU_Master.json";
27 
28 FruParser::FruParser(const std::string& dirPath)
29 {
30     fs::path dir(dirPath);
31     if (!fs::exists(dir) || fs::is_empty(dir))
32     {
33         std::cerr << "FRU config directory does not exist or empty, DIR="
34                   << dirPath << "\n";
35         return;
36     }
37 
38     fs::path masterFilePath = dir / fruMasterJson;
39     if (!fs::exists(masterFilePath))
40     {
41         std::cerr << "FRU D-Bus lookup JSON does not exist, PATH="
42                   << masterFilePath << "\n";
43         throw InternalFailure();
44     }
45 
46     setupDBusLookup(masterFilePath);
47     setupFruRecordMap(dirPath);
48 }
49 
50 void FruParser::setupDBusLookup(const fs::path& filePath)
51 {
52     std::ifstream jsonFile(filePath);
53 
54     auto data = Json::parse(jsonFile, nullptr, false);
55     if (data.is_discarded())
56     {
57         std::cerr << "Parsing FRU master config file failed, FILE=" << filePath;
58         throw InternalFailure();
59     }
60 
61     Service service = data.value("service", "");
62     RootPath rootPath = data.value("root_path", "");
63     auto entities = data.value("entities", emptyJsonList);
64     Interfaces interfaces{};
65     EntityType entityType{};
66     for (auto& entity : entities)
67     {
68         auto intf = entity.value("interface", "");
69         intfToEntityType[intf] =
70             std::move(entity.value("entity_type", entityType));
71         interfaces.emplace(std::move(intf));
72     }
73     lookupInfo.emplace(std::make_tuple(std::move(service), std::move(rootPath),
74                                        std::move(interfaces)));
75 }
76 
77 void FruParser::setupFruRecordMap(const std::string& dirPath)
78 {
79     for (auto& file : fs::directory_iterator(dirPath))
80     {
81         auto fileName = file.path().filename().string();
82         if (fruMasterJson == fileName)
83         {
84             continue;
85         }
86 
87         std::ifstream jsonFile(file.path());
88         auto data = Json::parse(jsonFile, nullptr, false);
89         if (data.is_discarded())
90         {
91 
92             std::cerr << "Parsing FRU master config file failed, FILE="
93                       << file.path();
94             throw InternalFailure();
95         }
96 
97         auto record = data.value("record_details", emptyJson);
98         auto recordType =
99             static_cast<uint8_t>(record.value("fru_record_type", 0));
100         auto encType =
101             static_cast<uint8_t>(record.value("fru_encoding_type", 0));
102         auto dbusIntfName = record.value("dbus_interface_name", "");
103         auto entries = data.value("fru_fields", emptyJsonList);
104         std::vector<FieldInfo> fieldInfo;
105 
106         for (const auto& entry : entries)
107         {
108             auto fieldType =
109                 static_cast<uint8_t>(entry.value("fru_field_type", 0));
110             auto dbus = entry.value("dbus", emptyJson);
111             auto interface = dbus.value("interface", "");
112             auto property = dbus.value("property_name", "");
113             auto propType = dbus.value("property_type", "");
114             fieldInfo.emplace_back(
115                 std::make_tuple(std::move(interface), std::move(property),
116                                 std::move(propType), std::move(fieldType)));
117         }
118 
119         FruRecordInfo fruInfo;
120         fruInfo = std::make_tuple(recordType, encType, std::move(fieldInfo));
121 
122         auto search = recordMap.find(dbusIntfName);
123 
124         // PLDM FRU can have multiple records for the same FRU like General FRU
125         // record and multiple OEM FRU records. If the FRU item interface name
126         // is already in the map, that indicates a record info is already added
127         // for the FRU, so append the new record info to the same data.
128         if (search != recordMap.end())
129         {
130             search->second.emplace_back(std::move(fruInfo));
131         }
132         else
133         {
134             FruRecordInfos recordInfos{fruInfo};
135             recordMap.emplace(dbusIntfName, recordInfos);
136         }
137     }
138 }
139 
140 } // namespace fru_parser
141 
142 } // namespace responder
143 
144 } // namespace pldm
145