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