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 #pragma once 17 18 #include "rail.hpp" 19 20 #include <nlohmann/json.hpp> 21 22 #include <cstdint> 23 #include <filesystem> 24 #include <memory> 25 #include <stdexcept> 26 #include <string> 27 #include <vector> 28 29 namespace phosphor::power::sequencer::config_file_parser 30 { 31 32 /** 33 * Standard JSON configuration file directory on the BMC. 34 */ 35 extern const std::filesystem::path standardConfigFileDirectory; 36 37 /** 38 * Finds the JSON configuration file for the current system based on the 39 * specified compatible system types. 40 * 41 * This is required when a single BMC firmware image supports multiple system 42 * types and some system types require different configuration files. 43 * 44 * The compatible system types must be ordered from most to least specific. 45 * Example: 46 * - com.acme.Hardware.Chassis.Model.MegaServer4CPU 47 * - com.acme.Hardware.Chassis.Model.MegaServer 48 * - com.acme.Hardware.Chassis.Model.Server 49 * 50 * Throws an exception if an error occurs. 51 * 52 * @param compatibleSystemTypes compatible system types for the current system 53 * ordered from most to least specific 54 * @param configFileDir directory containing configuration files 55 * @return path to the JSON configuration file, or an empty path if none was 56 * found 57 */ 58 std::filesystem::path find( 59 const std::vector<std::string>& compatibleSystemTypes, 60 const std::filesystem::path& configFileDir = standardConfigFileDirectory); 61 62 /** 63 * Parses the specified JSON configuration file. 64 * 65 * Returns the corresponding C++ Rail objects. 66 * 67 * Throws a ConfigFileParserError if an error occurs. 68 * 69 * @param pathName configuration file path name 70 * @return vector of Rail objects 71 */ 72 std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName); 73 74 /* 75 * Internal implementation details for parse() 76 */ 77 namespace internal 78 { 79 80 /** 81 * Returns the specified property of the specified JSON element. 82 * 83 * Throws an invalid_argument exception if the property does not exist. 84 * 85 * @param element JSON element 86 * @param property property name 87 */ 88 #pragma GCC diagnostic push 89 #if __GNUC__ == 13 90 #pragma GCC diagnostic ignored "-Wdangling-reference" 91 #endif 92 inline const nlohmann::json& getRequiredProperty(const nlohmann::json& element, 93 const std::string& property) 94 { 95 auto it = element.find(property); 96 if (it == element.end()) 97 { 98 throw std::invalid_argument{"Required property missing: " + property}; 99 } 100 return *it; 101 } 102 #pragma GCC diagnostic pop 103 104 /** 105 * Parses a JSON element containing a boolean. 106 * 107 * Returns the corresponding C++ boolean value. 108 * 109 * Throws an exception if parsing fails. 110 * 111 * @param element JSON element 112 * @return boolean value 113 */ 114 inline bool parseBoolean(const nlohmann::json& element) 115 { 116 // Verify element contains a boolean 117 if (!element.is_boolean()) 118 { 119 throw std::invalid_argument{"Element is not a boolean"}; 120 } 121 return element.get<bool>(); 122 } 123 124 /** 125 * Parses a JSON element containing a GPIO. 126 * 127 * Returns the corresponding C++ GPIO object. 128 * 129 * Throws an exception if parsing fails. 130 * 131 * @param element JSON element 132 * @return GPIO object 133 */ 134 GPIO parseGPIO(const nlohmann::json& element); 135 136 /** 137 * Parses a JSON element containing a rail. 138 * 139 * Returns the corresponding C++ Rail object. 140 * 141 * Throws an exception if parsing fails. 142 * 143 * @param element JSON element 144 * @return Rail object 145 */ 146 std::unique_ptr<Rail> parseRail(const nlohmann::json& element); 147 148 /** 149 * Parses a JSON element containing an array of rails. 150 * 151 * Returns the corresponding C++ Rail objects. 152 * 153 * Throws an exception if parsing fails. 154 * 155 * @param element JSON element 156 * @return vector of Rail objects 157 */ 158 std::vector<std::unique_ptr<Rail>> 159 parseRailArray(const nlohmann::json& element); 160 161 /** 162 * Parses the JSON root element of the entire configuration file. 163 * 164 * Returns the corresponding C++ Rail objects. 165 * 166 * Throws an exception if parsing fails. 167 * 168 * @param element JSON element 169 * @return vector of Rail objects 170 */ 171 std::vector<std::unique_ptr<Rail>> parseRoot(const nlohmann::json& element); 172 173 /** 174 * Parses a JSON element containing a string. 175 * 176 * Returns the corresponding C++ string. 177 * 178 * Throws an exception if parsing fails. 179 * 180 * @param element JSON element 181 * @param isEmptyValid indicates whether an empty string value is valid 182 * @return string value 183 */ 184 inline std::string parseString(const nlohmann::json& element, 185 bool isEmptyValid = false) 186 { 187 if (!element.is_string()) 188 { 189 throw std::invalid_argument{"Element is not a string"}; 190 } 191 std::string value = element.get<std::string>(); 192 if (value.empty() && !isEmptyValid) 193 { 194 throw std::invalid_argument{"Element contains an empty string"}; 195 } 196 return value; 197 } 198 199 /** 200 * Parses a JSON element containing an 8-bit unsigned integer. 201 * 202 * Returns the corresponding C++ uint8_t value. 203 * 204 * Throws an exception if parsing fails. 205 * 206 * @param element JSON element 207 * @return uint8_t value 208 */ 209 inline uint8_t parseUint8(const nlohmann::json& element) 210 { 211 // Verify element contains an integer 212 if (!element.is_number_integer()) 213 { 214 throw std::invalid_argument{"Element is not an integer"}; 215 } 216 int value = element.get<int>(); 217 if ((value < 0) || (value > UINT8_MAX)) 218 { 219 throw std::invalid_argument{"Element is not an 8-bit unsigned integer"}; 220 } 221 return static_cast<uint8_t>(value); 222 } 223 224 /** 225 * Parses a JSON element containing an unsigned integer. 226 * 227 * Returns the corresponding C++ unsigned int value. 228 * 229 * Throws an exception if parsing fails. 230 * 231 * @param element JSON element 232 * @return unsigned int value 233 */ 234 inline unsigned int parseUnsignedInteger(const nlohmann::json& element) 235 { 236 // Verify element contains an unsigned integer 237 if (!element.is_number_unsigned()) 238 { 239 throw std::invalid_argument{"Element is not an unsigned integer"}; 240 } 241 return element.get<unsigned int>(); 242 } 243 244 /** 245 * Verifies that the specified JSON element is a JSON array. 246 * 247 * Throws an invalid_argument exception if the element is not an array. 248 * 249 * @param element JSON element 250 */ 251 inline void verifyIsArray(const nlohmann::json& element) 252 { 253 if (!element.is_array()) 254 { 255 throw std::invalid_argument{"Element is not an array"}; 256 } 257 } 258 259 /** 260 * Verifies that the specified JSON element is a JSON object. 261 * 262 * Throws an invalid_argument exception if the element is not an object. 263 * 264 * @param element JSON element 265 */ 266 inline void verifyIsObject(const nlohmann::json& element) 267 { 268 if (!element.is_object()) 269 { 270 throw std::invalid_argument{"Element is not an object"}; 271 } 272 } 273 274 /** 275 * Verifies that the specified JSON element contains the expected number of 276 * properties. 277 * 278 * Throws an invalid_argument exception if the element contains a different 279 * number of properties. This indicates the element contains an invalid 280 * property. 281 * 282 * @param element JSON element 283 * @param expectedCount expected number of properties in element 284 */ 285 inline void verifyPropertyCount(const nlohmann::json& element, 286 unsigned int expectedCount) 287 { 288 if (element.size() != expectedCount) 289 { 290 throw std::invalid_argument{"Element contains an invalid property"}; 291 } 292 } 293 294 } // namespace internal 295 296 } // namespace phosphor::power::sequencer::config_file_parser 297