11a568650SJolie Ku /**
21a568650SJolie Ku  * Copyright © 2019 IBM Corporation
31a568650SJolie Ku  *
41a568650SJolie Ku  * Licensed under the Apache License, Version 2.0 (the "License");
51a568650SJolie Ku  * you may not use this file except in compliance with the License.
61a568650SJolie Ku  * You may obtain a copy of the License at
71a568650SJolie Ku  *
81a568650SJolie Ku  *     http://www.apache.org/licenses/LICENSE-2.0
91a568650SJolie Ku  *
101a568650SJolie Ku  * Unless required by applicable law or agreed to in writing, software
111a568650SJolie Ku  * distributed under the License is distributed on an "AS IS" BASIS,
121a568650SJolie Ku  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131a568650SJolie Ku  * See the License for the specific language governing permissions and
141a568650SJolie Ku  * limitations under the License.
151a568650SJolie Ku  */
161a568650SJolie Ku #include "json_parser.hpp"
171a568650SJolie Ku 
181a568650SJolie Ku #include "anyof.hpp"
191a568650SJolie Ku #include "fallback.hpp"
201a568650SJolie Ku #include "gpio.hpp"
211a568650SJolie Ku #include "json_config.hpp"
221a568650SJolie Ku #include "sdbusplus.hpp"
231a568650SJolie Ku #include "tach.hpp"
241a568650SJolie Ku 
251a568650SJolie Ku #include <nlohmann/json.hpp>
261a568650SJolie Ku #include <phosphor-logging/log.hpp>
271a568650SJolie Ku #include <sdbusplus/bus.hpp>
28a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Create/server.hpp>
29a35a890bSMike Capps #include <xyz/openbmc_project/Logging/Entry/server.hpp>
301a568650SJolie Ku 
311a568650SJolie Ku #include <filesystem>
321a568650SJolie Ku #include <fstream>
331a568650SJolie Ku #include <string>
341a568650SJolie Ku 
351a568650SJolie Ku namespace phosphor
361a568650SJolie Ku {
371a568650SJolie Ku namespace fan
381a568650SJolie Ku {
391a568650SJolie Ku namespace presence
401a568650SJolie Ku {
411a568650SJolie Ku 
421a568650SJolie Ku using json = nlohmann::json;
431a568650SJolie Ku namespace fs = std::filesystem;
441a568650SJolie Ku using namespace phosphor::logging;
451a568650SJolie Ku 
461a568650SJolie Ku policies JsonConfig::_policies;
471a568650SJolie Ku const std::map<std::string, methodHandler> JsonConfig::_methods = {
481a568650SJolie Ku     {"tach", method::getTach}, {"gpio", method::getGpio}};
491a568650SJolie Ku const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
501a568650SJolie Ku     {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
511a568650SJolie Ku 
52a35a890bSMike Capps const auto loggingPath = "/xyz/openbmc_project/logging";
53a35a890bSMike Capps const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
54a35a890bSMike Capps 
55*cb356d48SPatrick Williams JsonConfig::JsonConfig(sdbusplus::bus_t& bus) : _bus(bus)
560daedd18SMatt Spinler {}
571a568650SJolie Ku 
585b839919SMatthew Barth void JsonConfig::start()
590daedd18SMatt Spinler {
605b839919SMatthew Barth     using config = fan::JsonConfig;
615b839919SMatthew Barth 
62dfc8c4d0SMatt Spinler     if (!_loaded)
63dfc8c4d0SMatt Spinler     {
64808d7fe8SMike Capps         process(config::load(config::getConfFile(confAppName, confFileName)));
65dfc8c4d0SMatt Spinler 
66dfc8c4d0SMatt Spinler         _loaded = true;
670daedd18SMatt Spinler 
680daedd18SMatt Spinler         for (auto& p : _policies)
690daedd18SMatt Spinler         {
700daedd18SMatt Spinler             p->monitor();
710daedd18SMatt Spinler         }
721a568650SJolie Ku     }
73dfc8c4d0SMatt Spinler }
741a568650SJolie Ku 
751a568650SJolie Ku const policies& JsonConfig::get()
761a568650SJolie Ku {
771a568650SJolie Ku     return _policies;
781a568650SJolie Ku }
791a568650SJolie Ku 
80808d7fe8SMike Capps void JsonConfig::sighupHandler(sdeventplus::source::Signal& /*sigSrc*/,
81808d7fe8SMike Capps                                const struct signalfd_siginfo* /*sigInfo*/)
821a568650SJolie Ku {
831a568650SJolie Ku     try
841a568650SJolie Ku     {
851a568650SJolie Ku         using config = fan::JsonConfig;
861a568650SJolie Ku 
879e9f599cSMatt Spinler         _reporter.reset();
889e9f599cSMatt Spinler 
891a568650SJolie Ku         // Load and process the json configuration
90808d7fe8SMike Capps         process(config::load(config::getConfFile(confAppName, confFileName)));
911a568650SJolie Ku 
921a568650SJolie Ku         for (auto& p : _policies)
931a568650SJolie Ku         {
941a568650SJolie Ku             p->monitor();
951a568650SJolie Ku         }
961a568650SJolie Ku         log<level::INFO>("Configuration loaded successfully");
971a568650SJolie Ku     }
98ddb773b2SPatrick Williams     catch (const std::runtime_error& re)
991a568650SJolie Ku     {
1001a568650SJolie Ku         log<level::ERR>("Error loading config, no config changes made",
1011a568650SJolie Ku                         entry("LOAD_ERROR=%s", re.what()));
1021a568650SJolie Ku     }
1031a568650SJolie Ku }
1041a568650SJolie Ku 
1051a568650SJolie Ku void JsonConfig::process(const json& jsonConf)
1061a568650SJolie Ku {
1071a568650SJolie Ku     policies policies;
1081a568650SJolie Ku     std::vector<fanPolicy> fans;
1091a568650SJolie Ku     // Set the expected number of fan entries
1101a568650SJolie Ku     // to be size of the list of fan json config entries
1111a568650SJolie Ku     // (Must be done to eliminate vector reallocation of fan references)
1129e9f599cSMatt Spinler     fans.reserve(jsonConf.size());
1139e9f599cSMatt Spinler     for (auto& member : jsonConf)
1141a568650SJolie Ku     {
1151a568650SJolie Ku         if (!member.contains("name") || !member.contains("path") ||
1161a568650SJolie Ku             !member.contains("methods") || !member.contains("rpolicy"))
1171a568650SJolie Ku         {
1181a568650SJolie Ku             log<level::ERR>("Missing required fan presence properties",
1191a568650SJolie Ku                             entry("REQUIRED_PROPERTIES=%s",
1201a568650SJolie Ku                                   "{name, path, methods, rpolicy}"));
1211a568650SJolie Ku             throw std::runtime_error(
1221a568650SJolie Ku                 "Missing required fan presence properties");
1231a568650SJolie Ku         }
1241a568650SJolie Ku 
1251a568650SJolie Ku         // Loop thru the configured methods of presence detection
1261a568650SJolie Ku         std::vector<std::unique_ptr<PresenceSensor>> sensors;
1271a568650SJolie Ku         for (auto& method : member["methods"].items())
1281a568650SJolie Ku         {
1291a568650SJolie Ku             if (!method.value().contains("type"))
1301a568650SJolie Ku             {
1311a568650SJolie Ku                 log<level::ERR>(
1321a568650SJolie Ku                     "Missing required fan presence method type",
1331a568650SJolie Ku                     entry("FAN_NAME=%s",
1341a568650SJolie Ku                           member["name"].get<std::string>().c_str()));
1351a568650SJolie Ku                 throw std::runtime_error(
1361a568650SJolie Ku                     "Missing required fan presence method type");
1371a568650SJolie Ku             }
1381a568650SJolie Ku             // The method type of fan presence detection
1391a568650SJolie Ku             // (Must have a supported function within the method namespace)
1401a568650SJolie Ku             auto type = method.value()["type"].get<std::string>();
1411a568650SJolie Ku             std::transform(type.begin(), type.end(), type.begin(), tolower);
1421a568650SJolie Ku             auto func = _methods.find(type);
1431a568650SJolie Ku             if (func != _methods.end())
1441a568650SJolie Ku             {
1451a568650SJolie Ku                 // Call function for method type
1461a568650SJolie Ku                 auto sensor = func->second(fans.size(), method.value());
1471a568650SJolie Ku                 if (sensor)
1481a568650SJolie Ku                 {
1491a568650SJolie Ku                     sensors.emplace_back(std::move(sensor));
1501a568650SJolie Ku                 }
1511a568650SJolie Ku             }
1521a568650SJolie Ku             else
1531a568650SJolie Ku             {
1541a568650SJolie Ku                 log<level::ERR>(
1551a568650SJolie Ku                     "Invalid fan presence method type",
1561a568650SJolie Ku                     entry("FAN_NAME=%s",
1571a568650SJolie Ku                           member["name"].get<std::string>().c_str()),
1581a568650SJolie Ku                     entry("METHOD_TYPE=%s", type.c_str()));
1591a568650SJolie Ku                 throw std::runtime_error("Invalid fan presence method type");
1601a568650SJolie Ku             }
1611a568650SJolie Ku         }
1629e9f599cSMatt Spinler 
1639e9f599cSMatt Spinler         // Get the amount of time a fan must be not present before
1649e9f599cSMatt Spinler         // creating an error.
1659e9f599cSMatt Spinler         std::optional<size_t> timeUntilError;
1669e9f599cSMatt Spinler         if (member.contains("fan_missing_error_time"))
1679e9f599cSMatt Spinler         {
1689e9f599cSMatt Spinler             timeUntilError = member["fan_missing_error_time"].get<size_t>();
1699e9f599cSMatt Spinler         }
1709e9f599cSMatt Spinler 
1719e9f599cSMatt Spinler         auto fan =
1729e9f599cSMatt Spinler             std::make_tuple(member["name"], member["path"], timeUntilError);
1731a568650SJolie Ku         // Create a fan object
1741a568650SJolie Ku         fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
1751a568650SJolie Ku 
1761a568650SJolie Ku         // Add fan presence policy
1771a568650SJolie Ku         auto policy = getPolicy(member["rpolicy"], fans.back());
1781a568650SJolie Ku         if (policy)
1791a568650SJolie Ku         {
1801a568650SJolie Ku             policies.emplace_back(std::move(policy));
1811a568650SJolie Ku         }
1821a568650SJolie Ku     }
1831a568650SJolie Ku 
1841a568650SJolie Ku     // Success, refresh fans and policies lists
1851a568650SJolie Ku     _fans.clear();
1861a568650SJolie Ku     _fans.swap(fans);
1871a568650SJolie Ku 
1881a568650SJolie Ku     _policies.clear();
1891a568650SJolie Ku     _policies.swap(policies);
190e8122390SMatt Spinler 
191e8122390SMatt Spinler     // Create the error reporter class if necessary
1929e9f599cSMatt Spinler     if (std::any_of(_fans.begin(), _fans.end(), [](const auto& fan) {
1939e9f599cSMatt Spinler             return std::get<std::optional<size_t>>(std::get<Fan>(fan)) !=
1949e9f599cSMatt Spinler                    std::nullopt;
1959e9f599cSMatt Spinler         }))
196e8122390SMatt Spinler     {
1979e9f599cSMatt Spinler         _reporter = std::make_unique<ErrorReporter>(_bus, _fans);
198e8122390SMatt Spinler     }
1991a568650SJolie Ku }
2001a568650SJolie Ku 
2011a568650SJolie Ku std::unique_ptr<RedundancyPolicy>
2021a568650SJolie Ku     JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
2031a568650SJolie Ku {
2041a568650SJolie Ku     if (!rpolicy.contains("type"))
2051a568650SJolie Ku     {
2061a568650SJolie Ku         log<level::ERR>(
2071a568650SJolie Ku             "Missing required fan presence policy type",
2081a568650SJolie Ku             entry("FAN_NAME=%s",
2091a568650SJolie Ku                   std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
2101a568650SJolie Ku             entry("REQUIRED_PROPERTIES=%s", "{type}"));
2111a568650SJolie Ku         throw std::runtime_error("Missing required fan presence policy type");
2121a568650SJolie Ku     }
2131a568650SJolie Ku 
2141a568650SJolie Ku     // The redundancy policy type for fan presence detection
2151a568650SJolie Ku     // (Must have a supported function within the rpolicy namespace)
2161a568650SJolie Ku     auto type = rpolicy["type"].get<std::string>();
2171a568650SJolie Ku     std::transform(type.begin(), type.end(), type.begin(), tolower);
2181a568650SJolie Ku     auto func = _rpolicies.find(type);
2191a568650SJolie Ku     if (func != _rpolicies.end())
2201a568650SJolie Ku     {
2211a568650SJolie Ku         // Call function for redundancy policy type and return the policy
2221a568650SJolie Ku         return func->second(fpolicy);
2231a568650SJolie Ku     }
2241a568650SJolie Ku     else
2251a568650SJolie Ku     {
2261a568650SJolie Ku         log<level::ERR>(
2271a568650SJolie Ku             "Invalid fan presence policy type",
2281a568650SJolie Ku             entry("FAN_NAME=%s",
2291a568650SJolie Ku                   std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
2301a568650SJolie Ku             entry("RPOLICY_TYPE=%s", type.c_str()));
2311a568650SJolie Ku         throw std::runtime_error("Invalid fan presence methods policy type");
2321a568650SJolie Ku     }
2331a568650SJolie Ku }
2341a568650SJolie Ku 
2351a568650SJolie Ku /**
2361a568650SJolie Ku  * Methods of fan presence detection function definitions
2371a568650SJolie Ku  */
2381a568650SJolie Ku namespace method
2391a568650SJolie Ku {
2401a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by tach
2411a568650SJolie Ku std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
2421a568650SJolie Ku {
2431a568650SJolie Ku     if (!method.contains("sensors") || method["sensors"].size() == 0)
2441a568650SJolie Ku     {
2451a568650SJolie Ku         log<level::ERR>("Missing required tach method properties",
2461a568650SJolie Ku                         entry("FAN_ENTRY=%d", fanIndex),
2471a568650SJolie Ku                         entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
2481a568650SJolie Ku         throw std::runtime_error("Missing required tach method properties");
2491a568650SJolie Ku     }
2501a568650SJolie Ku 
2511a568650SJolie Ku     std::vector<std::string> sensors;
2521a568650SJolie Ku     for (auto& sensor : method["sensors"])
2531a568650SJolie Ku     {
2541a568650SJolie Ku         sensors.emplace_back(sensor.get<std::string>());
2551a568650SJolie Ku     }
2561a568650SJolie Ku 
2571a568650SJolie Ku     return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
2581a568650SJolie Ku                                                             std::move(sensors));
2591a568650SJolie Ku }
2601a568650SJolie Ku 
2611a568650SJolie Ku // Get a constructed presence sensor for fan presence detection by gpio
2621a568650SJolie Ku std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
2631a568650SJolie Ku {
2641a568650SJolie Ku     if (!method.contains("physpath") || !method.contains("devpath") ||
2651a568650SJolie Ku         !method.contains("key"))
2661a568650SJolie Ku     {
2671a568650SJolie Ku         log<level::ERR>(
2681a568650SJolie Ku             "Missing required gpio method properties",
2691a568650SJolie Ku             entry("FAN_ENTRY=%d", fanIndex),
2701a568650SJolie Ku             entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
2711a568650SJolie Ku         throw std::runtime_error("Missing required gpio method properties");
2721a568650SJolie Ku     }
2731a568650SJolie Ku 
2741a568650SJolie Ku     auto physpath = method["physpath"].get<std::string>();
2751a568650SJolie Ku     auto devpath = method["devpath"].get<std::string>();
2761a568650SJolie Ku     auto key = method["key"].get<unsigned int>();
2771a568650SJolie Ku 
278a35a890bSMike Capps     try
279a35a890bSMike Capps     {
280a35a890bSMike Capps         return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(
281a35a890bSMike Capps             fanIndex, physpath, devpath, key);
282a35a890bSMike Capps     }
283a35a890bSMike Capps     catch (const sdbusplus::exception_t& e)
284a35a890bSMike Capps     {
285a35a890bSMike Capps         namespace sdlogging = sdbusplus::xyz::openbmc_project::Logging::server;
286a35a890bSMike Capps 
287a35a890bSMike Capps         log<level::ERR>(
288a35a890bSMike Capps             fmt::format(
289a35a890bSMike Capps                 "Error creating Gpio device bridge, hardware not detected: {}",
290a35a890bSMike Capps                 e.what())
291a35a890bSMike Capps                 .c_str());
292a35a890bSMike Capps 
293a35a890bSMike Capps         auto severity =
294a35a890bSMike Capps             sdlogging::convertForMessage(sdlogging::Entry::Level::Error);
295a35a890bSMike Capps 
296a35a890bSMike Capps         std::map<std::string, std::string> additionalData{
297a35a890bSMike Capps             {"PHYSPATH", physpath},
298a35a890bSMike Capps             {"DEVPATH", devpath},
299a35a890bSMike Capps             {"FANINDEX", std::to_string(fanIndex)}};
300a35a890bSMike Capps 
301a35a890bSMike Capps         try
302a35a890bSMike Capps         {
303a35a890bSMike Capps 
304a35a890bSMike Capps             util::SDBusPlus::lookupAndCallMethod(
305a35a890bSMike Capps                 loggingPath, loggingCreateIface, "Create",
306a35a890bSMike Capps                 "xyz.openbmc_project.Fan.Presence.Error.GPIODeviceUnavailable",
307a35a890bSMike Capps                 severity, additionalData);
308a35a890bSMike Capps         }
309a35a890bSMike Capps         catch (const util::DBusError& e)
310a35a890bSMike Capps         {
311a35a890bSMike Capps             log<level::ERR>(fmt::format("Call to create an error log for "
312a35a890bSMike Capps                                         "presence-sensor failure failed: {}",
313a35a890bSMike Capps                                         e.what())
314a35a890bSMike Capps                                 .c_str());
315a35a890bSMike Capps         }
316a35a890bSMike Capps 
317a35a890bSMike Capps         return std::make_unique<PolicyAccess<NullGpio, JsonConfig>>();
318a35a890bSMike Capps     }
3191a568650SJolie Ku }
3201a568650SJolie Ku 
3211a568650SJolie Ku } // namespace method
3221a568650SJolie Ku 
3231a568650SJolie Ku /**
3241a568650SJolie Ku  * Redundancy policies for fan presence detection function definitions
3251a568650SJolie Ku  */
3261a568650SJolie Ku namespace rpolicy
3271a568650SJolie Ku {
3281a568650SJolie Ku // Get an `Anyof` redundancy policy for the fan
3291a568650SJolie Ku std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
3301a568650SJolie Ku {
3311a568650SJolie Ku     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
3321a568650SJolie Ku     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
3331a568650SJolie Ku     {
3341a568650SJolie Ku         pSensors.emplace_back(*fanSensor);
3351a568650SJolie Ku     }
3361a568650SJolie Ku 
3371a568650SJolie Ku     return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
3381a568650SJolie Ku }
3391a568650SJolie Ku 
3401a568650SJolie Ku // Get a `Fallback` redundancy policy for the fan
3411a568650SJolie Ku std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
3421a568650SJolie Ku {
3431a568650SJolie Ku     std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
3441a568650SJolie Ku     for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
3451a568650SJolie Ku     {
3461a568650SJolie Ku         // Place in the order given to fallback correctly
3471a568650SJolie Ku         pSensors.emplace_back(*fanSensor);
3481a568650SJolie Ku     }
3491a568650SJolie Ku 
3501a568650SJolie Ku     return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
3511a568650SJolie Ku }
3521a568650SJolie Ku 
3531a568650SJolie Ku } // namespace rpolicy
3541a568650SJolie Ku 
3551a568650SJolie Ku } // namespace presence
3561a568650SJolie Ku } // namespace fan
3571a568650SJolie Ku } // namespace phosphor
358