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