1 /**
2  * Copyright © 2019 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 #include "json_parser.hpp"
17 
18 #include "anyof.hpp"
19 #include "fallback.hpp"
20 #include "gpio.hpp"
21 #include "json_config.hpp"
22 #include "sdbusplus.hpp"
23 #include "tach.hpp"
24 
25 #include <nlohmann/json.hpp>
26 #include <phosphor-logging/log.hpp>
27 #include <sdbusplus/bus.hpp>
28 
29 #include <filesystem>
30 #include <fstream>
31 #include <string>
32 
33 namespace phosphor
34 {
35 namespace fan
36 {
37 namespace presence
38 {
39 
40 using json = nlohmann::json;
41 namespace fs = std::filesystem;
42 using namespace phosphor::logging;
43 
44 policies JsonConfig::_policies;
45 const std::map<std::string, methodHandler> JsonConfig::_methods = {
46     {"tach", method::getTach}, {"gpio", method::getGpio}};
47 const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
48     {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
49 
50 JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus)
51 {}
52 
53 void JsonConfig::start(const std::string& confFile)
54 {
55     process(phosphor::fan::JsonConfig::load(confFile));
56 
57     for (auto& p : _policies)
58     {
59         p->monitor();
60     }
61 }
62 
63 const policies& JsonConfig::get()
64 {
65     return _policies;
66 }
67 
68 void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
69                                const struct signalfd_siginfo* sigInfo)
70 {
71     try
72     {
73         using config = fan::JsonConfig;
74 
75         _reporter.reset();
76 
77         // Load and process the json configuration
78         process(
79             config::load(config::getConfFile(_bus, confAppName, confFileName)));
80 
81         for (auto& p : _policies)
82         {
83             p->monitor();
84         }
85         log<level::INFO>("Configuration loaded successfully");
86     }
87     catch (std::runtime_error& re)
88     {
89         log<level::ERR>("Error loading config, no config changes made",
90                         entry("LOAD_ERROR=%s", re.what()));
91     }
92 }
93 
94 void JsonConfig::process(const json& jsonConf)
95 {
96     policies policies;
97     std::vector<fanPolicy> fans;
98     // Set the expected number of fan entries
99     // to be size of the list of fan json config entries
100     // (Must be done to eliminate vector reallocation of fan references)
101     fans.reserve(jsonConf.size());
102     for (auto& member : jsonConf)
103     {
104         if (!member.contains("name") || !member.contains("path") ||
105             !member.contains("methods") || !member.contains("rpolicy"))
106         {
107             log<level::ERR>("Missing required fan presence properties",
108                             entry("REQUIRED_PROPERTIES=%s",
109                                   "{name, path, methods, rpolicy}"));
110             throw std::runtime_error(
111                 "Missing required fan presence properties");
112         }
113 
114         // Loop thru the configured methods of presence detection
115         std::vector<std::unique_ptr<PresenceSensor>> sensors;
116         for (auto& method : member["methods"].items())
117         {
118             if (!method.value().contains("type"))
119             {
120                 log<level::ERR>(
121                     "Missing required fan presence method type",
122                     entry("FAN_NAME=%s",
123                           member["name"].get<std::string>().c_str()));
124                 throw std::runtime_error(
125                     "Missing required fan presence method type");
126             }
127             // The method type of fan presence detection
128             // (Must have a supported function within the method namespace)
129             auto type = method.value()["type"].get<std::string>();
130             std::transform(type.begin(), type.end(), type.begin(), tolower);
131             auto func = _methods.find(type);
132             if (func != _methods.end())
133             {
134                 // Call function for method type
135                 auto sensor = func->second(fans.size(), method.value());
136                 if (sensor)
137                 {
138                     sensors.emplace_back(std::move(sensor));
139                 }
140             }
141             else
142             {
143                 log<level::ERR>(
144                     "Invalid fan presence method type",
145                     entry("FAN_NAME=%s",
146                           member["name"].get<std::string>().c_str()),
147                     entry("METHOD_TYPE=%s", type.c_str()));
148                 throw std::runtime_error("Invalid fan presence method type");
149             }
150         }
151 
152         // Get the amount of time a fan must be not present before
153         // creating an error.
154         std::optional<size_t> timeUntilError;
155         if (member.contains("fan_missing_error_time"))
156         {
157             timeUntilError = member["fan_missing_error_time"].get<size_t>();
158         }
159 
160         auto fan =
161             std::make_tuple(member["name"], member["path"], timeUntilError);
162         // Create a fan object
163         fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
164 
165         // Add fan presence policy
166         auto policy = getPolicy(member["rpolicy"], fans.back());
167         if (policy)
168         {
169             policies.emplace_back(std::move(policy));
170         }
171     }
172 
173     // Success, refresh fans and policies lists
174     _fans.clear();
175     _fans.swap(fans);
176 
177     _policies.clear();
178     _policies.swap(policies);
179 
180     // Create the error reporter class if necessary
181     if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
182             return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
183                    std::nullopt;
184         }))
185     {
186         _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
187     }
188 }
189 
190 std::unique_ptr<RedundancyPolicy>
191     JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
192 {
193     if (!rpolicy.contains("type"))
194     {
195         log<level::ERR>(
196             "Missing required fan presence policy type",
197             entry("FAN_NAME=%s",
198                   std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
199             entry("REQUIRED_PROPERTIES=%s", "{type}"));
200         throw std::runtime_error("Missing required fan presence policy type");
201     }
202 
203     // The redundancy policy type for fan presence detection
204     // (Must have a supported function within the rpolicy namespace)
205     auto type = rpolicy["type"].get<std::string>();
206     std::transform(type.begin(), type.end(), type.begin(), tolower);
207     auto func = _rpolicies.find(type);
208     if (func != _rpolicies.end())
209     {
210         // Call function for redundancy policy type and return the policy
211         return func->second(fpolicy);
212     }
213     else
214     {
215         log<level::ERR>(
216             "Invalid fan presence policy type",
217             entry("FAN_NAME=%s",
218                   std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
219             entry("RPOLICY_TYPE=%s", type.c_str()));
220         throw std::runtime_error("Invalid fan presence methods policy type");
221     }
222 }
223 
224 /**
225  * Methods of fan presence detection function definitions
226  */
227 namespace method
228 {
229 // Get a constructed presence sensor for fan presence detection by tach
230 std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
231 {
232     if (!method.contains("sensors") || method["sensors"].size() == 0)
233     {
234         log<level::ERR>("Missing required tach method properties",
235                         entry("FAN_ENTRY=%d", fanIndex),
236                         entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
237         throw std::runtime_error("Missing required tach method properties");
238     }
239 
240     std::vector<std::string> sensors;
241     for (auto& sensor : method["sensors"])
242     {
243         sensors.emplace_back(sensor.get<std::string>());
244     }
245 
246     return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
247                                                             std::move(sensors));
248 }
249 
250 // Get a constructed presence sensor for fan presence detection by gpio
251 std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
252 {
253     if (!method.contains("physpath") || !method.contains("devpath") ||
254         !method.contains("key"))
255     {
256         log<level::ERR>(
257             "Missing required gpio method properties",
258             entry("FAN_ENTRY=%d", fanIndex),
259             entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
260         throw std::runtime_error("Missing required gpio method properties");
261     }
262 
263     auto physpath = method["physpath"].get<std::string>();
264     auto devpath = method["devpath"].get<std::string>();
265     auto key = method["key"].get<unsigned int>();
266 
267     return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(fanIndex, physpath,
268                                                             devpath, key);
269 }
270 
271 } // namespace method
272 
273 /**
274  * Redundancy policies for fan presence detection function definitions
275  */
276 namespace rpolicy
277 {
278 // Get an `Anyof` redundancy policy for the fan
279 std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
280 {
281     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
282     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
283     {
284         pSensors.emplace_back(*fanSensor);
285     }
286 
287     return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
288 }
289 
290 // Get a `Fallback` redundancy policy for the fan
291 std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
292 {
293     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
294     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
295     {
296         // Place in the order given to fallback correctly
297         pSensors.emplace_back(*fanSensor);
298     }
299 
300     return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
301 }
302 
303 } // namespace rpolicy
304 
305 } // namespace presence
306 } // namespace fan
307 } // namespace phosphor
308