/** * Copyright © 2024 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config_file_parser.hpp" #include "config_file_parser_error.hpp" #include #include #include using json = nlohmann::json; namespace fs = std::filesystem; namespace phosphor::power::sequencer::config_file_parser { const std::filesystem::path standardConfigFileDirectory{ "/usr/share/phosphor-power-sequencer"}; std::filesystem::path find(const std::vector& compatibleSystemTypes, const std::filesystem::path& configFileDir) { fs::path pathName, possiblePath; std::string fileName; for (const std::string& systemType : compatibleSystemTypes) { // Look for file name that is entire system type + ".json" // Example: com.acme.Hardware.Chassis.Model.MegaServer.json fileName = systemType + ".json"; possiblePath = configFileDir / fileName; if (fs::is_regular_file(possiblePath)) { pathName = possiblePath; break; } // Look for file name that is last node of system type + ".json" // Example: MegaServer.json std::string::size_type pos = systemType.rfind('.'); if ((pos != std::string::npos) && ((systemType.size() - pos) > 1)) { fileName = systemType.substr(pos + 1) + ".json"; possiblePath = configFileDir / fileName; if (fs::is_regular_file(possiblePath)) { pathName = possiblePath; break; } } } return pathName; } std::vector> parse(const std::filesystem::path& pathName) { try { // Use standard JSON parser to create tree of JSON elements std::ifstream file{pathName}; json rootElement = json::parse(file); // Parse tree of JSON elements and return corresponding C++ objects return internal::parseRoot(rootElement); } catch (const std::exception& e) { throw ConfigFileParserError{pathName, e.what()}; } } namespace internal { GPIO parseGPIO(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required line property const json& lineElement = getRequiredProperty(element, "line"); unsigned int line = parseUnsignedInteger(lineElement); ++propertyCount; // Optional active_low property bool activeLow{false}; auto activeLowIt = element.find("active_low"); if (activeLowIt != element.end()) { activeLow = parseBoolean(*activeLowIt); ++propertyCount; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return GPIO(line, activeLow); } std::unique_ptr parseRail(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required name property const json& nameElement = getRequiredProperty(element, "name"); std::string name = parseString(nameElement); ++propertyCount; // Optional presence property std::optional presence{}; auto presenceIt = element.find("presence"); if (presenceIt != element.end()) { presence = parseString(*presenceIt); ++propertyCount; } // Optional page property std::optional page{}; auto pageIt = element.find("page"); if (pageIt != element.end()) { page = parseUint8(*pageIt); ++propertyCount; } // Optional is_power_supply_rail property bool isPowerSupplyRail{false}; auto isPowerSupplyRailIt = element.find("is_power_supply_rail"); if (isPowerSupplyRailIt != element.end()) { isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt); ++propertyCount; } // Optional check_status_vout property bool checkStatusVout{false}; auto checkStatusVoutIt = element.find("check_status_vout"); if (checkStatusVoutIt != element.end()) { checkStatusVout = parseBoolean(*checkStatusVoutIt); ++propertyCount; } // Optional compare_voltage_to_limit property bool compareVoltageToLimit{false}; auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit"); if (compareVoltageToLimitIt != element.end()) { compareVoltageToLimit = parseBoolean(*compareVoltageToLimitIt); ++propertyCount; } // Optional gpio property std::optional gpio{}; auto gpioIt = element.find("gpio"); if (gpioIt != element.end()) { gpio = parseGPIO(*gpioIt); ++propertyCount; } // If check_status_vout or compare_voltage_to_limit property is true, the // page property is required; verify page was specified if ((checkStatusVout || compareVoltageToLimit) && !page.has_value()) { throw std::invalid_argument{"Required property missing: page"}; } // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return std::make_unique(name, presence, page, isPowerSupplyRail, checkStatusVout, compareVoltageToLimit, gpio); } std::vector> parseRailArray(const json& element) { verifyIsArray(element); std::vector> rails; for (auto& railElement : element) { rails.emplace_back(parseRail(railElement)); } return rails; } std::vector> parseRoot(const json& element) { verifyIsObject(element); unsigned int propertyCount{0}; // Required rails property const json& railsElement = getRequiredProperty(element, "rails"); std::vector> rails = parseRailArray(railsElement); ++propertyCount; // Verify no invalid properties exist verifyPropertyCount(element, propertyCount); return rails; } } // namespace internal } // namespace phosphor::power::sequencer::config_file_parser