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     Interfaces interfaces = data.value("interfaces", emptyStringVec);
64     lookupInfo.emplace(std::make_tuple(std::move(service), std::move(rootPath),
65                                        std::move(interfaces)));
66 }
67 
68 void FruParser::setupFruRecordMap(const std::string& dirPath)
69 {
70     for (auto& file : fs::directory_iterator(dirPath))
71     {
72         auto fileName = file.path().filename().string();
73         if (fruMasterJson == fileName)
74         {
75             continue;
76         }
77 
78         std::ifstream jsonFile(file.path());
79         auto data = Json::parse(jsonFile, nullptr, false);
80         if (data.is_discarded())
81         {
82 
83             std::cerr << "Parsing FRU master config file failed, FILE="
84                       << file.path();
85             throw InternalFailure();
86         }
87 
88         auto record = data.value("record_details", emptyJson);
89         auto recordType =
90             static_cast<uint8_t>(record.value("fru_record_type", 0));
91         auto encType =
92             static_cast<uint8_t>(record.value("fru_encoding_type", 0));
93         auto dbusIntfName = record.value("dbus_interface_name", "");
94         auto entries = data.value("fru_fields", emptyJsonList);
95         std::vector<FieldInfo> fieldInfo;
96 
97         for (const auto& entry : entries)
98         {
99             auto fieldType =
100                 static_cast<uint8_t>(entry.value("fru_field_type", 0));
101             auto dbus = entry.value("dbus", emptyJson);
102             auto interface = dbus.value("interface", "");
103             auto property = dbus.value("property_name", "");
104             auto propType = dbus.value("property_type", "");
105             fieldInfo.emplace_back(
106                 std::make_tuple(std::move(interface), std::move(property),
107                                 std::move(propType), std::move(fieldType)));
108         }
109 
110         FruRecordInfo fruInfo;
111         fruInfo = std::make_tuple(recordType, encType, std::move(fieldInfo));
112 
113         auto search = recordMap.find(dbusIntfName);
114 
115         // PLDM FRU can have multiple records for the same FRU like General FRU
116         // record and multiple OEM FRU records. If the FRU item interface name
117         // is already in the map, that indicates a record info is already added
118         // for the FRU, so append the new record info to the same data.
119         if (search != recordMap.end())
120         {
121             search->second.emplace_back(std::move(fruInfo));
122         }
123         else
124         {
125             FruRecordInfos recordInfos{fruInfo};
126             recordMap.emplace(dbusIntfName, recordInfos);
127         }
128     }
129 }
130 
131 } // namespace fru_parser
132 
133 } // namespace responder
134 
135 } // namespace pldm
136