1 /** 2 * Copyright © 2024 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "config_file_parser.hpp" 18 19 #include "config_file_parser_error.hpp" 20 21 #include <exception> 22 #include <fstream> 23 #include <optional> 24 25 using json = nlohmann::json; 26 namespace fs = std::filesystem; 27 28 namespace phosphor::power::sequencer::config_file_parser 29 { 30 31 const std::filesystem::path standardConfigFileDirectory{ 32 "/usr/share/phosphor-power-sequencer"}; 33 34 std::filesystem::path 35 find(const std::vector<std::string>& compatibleSystemTypes, 36 const std::filesystem::path& configFileDir) 37 { 38 fs::path pathName, possiblePath; 39 std::string fileName; 40 41 for (const std::string& systemType : compatibleSystemTypes) 42 { 43 // Look for file name that is entire system type + ".json" 44 // Example: com.acme.Hardware.Chassis.Model.MegaServer.json 45 fileName = systemType + ".json"; 46 possiblePath = configFileDir / fileName; 47 if (fs::is_regular_file(possiblePath)) 48 { 49 pathName = possiblePath; 50 break; 51 } 52 53 // Look for file name that is last node of system type + ".json" 54 // Example: MegaServer.json 55 std::string::size_type pos = systemType.rfind('.'); 56 if ((pos != std::string::npos) && ((systemType.size() - pos) > 1)) 57 { 58 fileName = systemType.substr(pos + 1) + ".json"; 59 possiblePath = configFileDir / fileName; 60 if (fs::is_regular_file(possiblePath)) 61 { 62 pathName = possiblePath; 63 break; 64 } 65 } 66 } 67 68 return pathName; 69 } 70 71 std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName) 72 { 73 try 74 { 75 // Use standard JSON parser to create tree of JSON elements 76 std::ifstream file{pathName}; 77 json rootElement = json::parse(file); 78 79 // Parse tree of JSON elements and return corresponding C++ objects 80 return internal::parseRoot(rootElement); 81 } 82 catch (const std::exception& e) 83 { 84 throw ConfigFileParserError{pathName, e.what()}; 85 } 86 } 87 88 namespace internal 89 { 90 91 GPIO parseGPIO(const json& element) 92 { 93 verifyIsObject(element); 94 unsigned int propertyCount{0}; 95 96 // Required line property 97 const json& lineElement = getRequiredProperty(element, "line"); 98 unsigned int line = parseUnsignedInteger(lineElement); 99 ++propertyCount; 100 101 // Optional active_low property 102 bool activeLow{false}; 103 auto activeLowIt = element.find("active_low"); 104 if (activeLowIt != element.end()) 105 { 106 activeLow = parseBoolean(*activeLowIt); 107 ++propertyCount; 108 } 109 110 // Verify no invalid properties exist 111 verifyPropertyCount(element, propertyCount); 112 113 return GPIO(line, activeLow); 114 } 115 116 std::unique_ptr<Rail> parseRail(const json& element) 117 { 118 verifyIsObject(element); 119 unsigned int propertyCount{0}; 120 121 // Required name property 122 const json& nameElement = getRequiredProperty(element, "name"); 123 std::string name = parseString(nameElement); 124 ++propertyCount; 125 126 // Optional presence property 127 std::optional<std::string> presence{}; 128 auto presenceIt = element.find("presence"); 129 if (presenceIt != element.end()) 130 { 131 presence = parseString(*presenceIt); 132 ++propertyCount; 133 } 134 135 // Optional page property 136 std::optional<uint8_t> page{}; 137 auto pageIt = element.find("page"); 138 if (pageIt != element.end()) 139 { 140 page = parseUint8(*pageIt); 141 ++propertyCount; 142 } 143 144 // Optional is_power_supply_rail property 145 bool isPowerSupplyRail{false}; 146 auto isPowerSupplyRailIt = element.find("is_power_supply_rail"); 147 if (isPowerSupplyRailIt != element.end()) 148 { 149 isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt); 150 ++propertyCount; 151 } 152 153 // Optional check_status_vout property 154 bool checkStatusVout{false}; 155 auto checkStatusVoutIt = element.find("check_status_vout"); 156 if (checkStatusVoutIt != element.end()) 157 { 158 checkStatusVout = parseBoolean(*checkStatusVoutIt); 159 ++propertyCount; 160 } 161 162 // Optional compare_voltage_to_limit property 163 bool compareVoltageToLimit{false}; 164 auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit"); 165 if (compareVoltageToLimitIt != element.end()) 166 { 167 compareVoltageToLimit = parseBoolean(*compareVoltageToLimitIt); 168 ++propertyCount; 169 } 170 171 // Optional gpio property 172 std::optional<GPIO> gpio{}; 173 auto gpioIt = element.find("gpio"); 174 if (gpioIt != element.end()) 175 { 176 gpio = parseGPIO(*gpioIt); 177 ++propertyCount; 178 } 179 180 // If check_status_vout or compare_voltage_to_limit property is true, the 181 // page property is required; verify page was specified 182 if ((checkStatusVout || compareVoltageToLimit) && !page.has_value()) 183 { 184 throw std::invalid_argument{"Required property missing: page"}; 185 } 186 187 // Verify no invalid properties exist 188 verifyPropertyCount(element, propertyCount); 189 190 return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail, 191 checkStatusVout, compareVoltageToLimit, gpio); 192 } 193 194 std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element) 195 { 196 verifyIsArray(element); 197 std::vector<std::unique_ptr<Rail>> rails; 198 for (auto& railElement : element) 199 { 200 rails.emplace_back(parseRail(railElement)); 201 } 202 return rails; 203 } 204 205 std::vector<std::unique_ptr<Rail>> parseRoot(const json& element) 206 { 207 verifyIsObject(element); 208 unsigned int propertyCount{0}; 209 210 // Required rails property 211 const json& railsElement = getRequiredProperty(element, "rails"); 212 std::vector<std::unique_ptr<Rail>> rails = parseRailArray(railsElement); 213 ++propertyCount; 214 215 // Verify no invalid properties exist 216 verifyPropertyCount(element, propertyCount); 217 218 return rails; 219 } 220 221 } // namespace internal 222 223 } // namespace phosphor::power::sequencer::config_file_parser 224