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
find(const std::vector<std::string> & compatibleSystemTypes,const std::filesystem::path & configFileDir)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
parse(const std::filesystem::path & pathName)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
parseGPIO(const json & element)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
parseRail(const json & element)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
parseRailArray(const json & element)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
parseRoot(const json & element)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