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