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