xref: /openbmc/phosphor-fan-presence/monitor/system.cpp (revision 4fa67aa12bd73f7d528225f0c3c93c3242bbcc23)
1c95c527aSMatthew Barth /**
27b34ee0fSMike Capps  * Copyright © 2022 IBM Corporation
3c95c527aSMatthew Barth  *
4c95c527aSMatthew Barth  * Licensed under the Apache License, Version 2.0 (the "License");
5c95c527aSMatthew Barth  * you may not use this file except in compliance with the License.
6c95c527aSMatthew Barth  * You may obtain a copy of the License at
7c95c527aSMatthew Barth  *
8c95c527aSMatthew Barth  *     http://www.apache.org/licenses/LICENSE-2.0
9c95c527aSMatthew Barth  *
10c95c527aSMatthew Barth  * Unless required by applicable law or agreed to in writing, software
11c95c527aSMatthew Barth  * distributed under the License is distributed on an "AS IS" BASIS,
12c95c527aSMatthew Barth  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c95c527aSMatthew Barth  * See the License for the specific language governing permissions and
14c95c527aSMatthew Barth  * limitations under the License.
15c95c527aSMatthew Barth  */
16c95c527aSMatthew Barth #include "system.hpp"
17c95c527aSMatthew Barth 
18bf8e56f6SMike Capps #include "dbus_paths.hpp"
19c95c527aSMatthew Barth #include "fan.hpp"
20c95c527aSMatthew Barth #include "fan_defs.hpp"
21c95c527aSMatthew Barth #include "tach_sensor.hpp"
22c95c527aSMatthew Barth #include "trust_manager.hpp"
23c95c527aSMatthew Barth #include "types.hpp"
24fdcd5db3SMike Capps #include "utility.hpp"
25c95c527aSMatthew Barth #ifdef MONITOR_USE_JSON
26b4379a1eSMike Capps #include "json_config.hpp"
27c95c527aSMatthew Barth #include "json_parser.hpp"
28c95c527aSMatthew Barth #endif
29c95c527aSMatthew Barth 
30c8d3c51fSMatt Spinler #include "config.h"
31c8d3c51fSMatt Spinler 
32bb449c1cSMatt Spinler #include "hwmon_ffdc.hpp"
33bb449c1cSMatt Spinler 
34c95c527aSMatthew Barth #include <nlohmann/json.hpp>
35d06905c9SMatthew Barth #include <phosphor-logging/log.hpp>
36c95c527aSMatthew Barth #include <sdbusplus/bus.hpp>
37cb356d48SPatrick Williams #include <sdbusplus/bus/match.hpp>
38c95c527aSMatthew Barth #include <sdeventplus/event.hpp>
39d06905c9SMatthew Barth #include <sdeventplus/source/signal.hpp>
40c95c527aSMatthew Barth 
41c95c527aSMatthew Barth namespace phosphor::fan::monitor
42c95c527aSMatthew Barth {
43c95c527aSMatthew Barth 
44c95c527aSMatthew Barth using json = nlohmann::json;
45f13b42e2SMatt Spinler using Severity = sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
46f13b42e2SMatt Spinler 
47d06905c9SMatthew Barth using namespace phosphor::logging;
48c95c527aSMatthew Barth 
494f472a86SMatt Spinler const std::string System::dumpFile = "/tmp/fan_monitor_dump.json";
504f472a86SMatt Spinler 
System(Mode mode,sdbusplus::bus_t & bus,const sdeventplus::Event & event)51cb356d48SPatrick Williams System::System(Mode mode, sdbusplus::bus_t& bus,
52c95c527aSMatthew Barth                const sdeventplus::Event& event) :
53dfddd648SPatrick Williams     _mode(mode), _bus(bus), _event(event),
54fce14908SChau Ly #ifdef MONITOR_USE_HOST_STATE
55fce14908SChau Ly     _powerState(std::make_unique<HostPowerState>(
56fce14908SChau Ly #else
57c8d3c51fSMatt Spinler     _powerState(std::make_unique<PGoodState>(
58fce14908SChau Ly #endif
59e892e39aSMatt Spinler         bus, std::bind(std::mem_fn(&System::powerStateChanged), this,
60c8d3c51fSMatt Spinler                        std::placeholders::_1))),
61c8d3c51fSMatt Spinler     _thermalAlert(bus, THERMAL_ALERT_OBJPATH)
627d135641SMatt Spinler {}
63e892e39aSMatt Spinler 
start()64823bc49eSMatthew Barth void System::start()
657d135641SMatt Spinler {
66b4379a1eSMike Capps     namespace match = sdbusplus::bus::match;
67b4379a1eSMike Capps 
68b4379a1eSMike Capps     // must be done before service detection
69cb356d48SPatrick Williams     _inventoryMatch = std::make_unique<sdbusplus::bus::match_t>(
70b4379a1eSMike Capps         _bus, match::rules::nameOwnerChanged(util::INVENTORY_SVC),
71b4379a1eSMike Capps         std::bind(&System::inventoryOnlineCb, this, std::placeholders::_1));
72b4379a1eSMike Capps 
73b4379a1eSMike Capps     bool invServiceRunning = util::SDBusPlus::callMethodAndRead<bool>(
74b4379a1eSMike Capps         _bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
75b4379a1eSMike Capps         "org.freedesktop.DBus", "NameHasOwner", util::INVENTORY_SVC);
76b4379a1eSMike Capps 
77b4379a1eSMike Capps     if (invServiceRunning)
78b4379a1eSMike Capps     {
79b4379a1eSMike Capps         _inventoryMatch.reset();
80b4379a1eSMike Capps 
81b4379a1eSMike Capps         if (!_loaded)
82b4379a1eSMike Capps         {
83b4379a1eSMike Capps             load();
84b4379a1eSMike Capps         }
85b4379a1eSMike Capps     }
86b4379a1eSMike Capps }
87b4379a1eSMike Capps 
load()88b4379a1eSMike Capps void System::load()
89b4379a1eSMike Capps {
90c95c527aSMatthew Barth     json jsonObj = json::object();
91c95c527aSMatthew Barth #ifdef MONITOR_USE_JSON
92b4379a1eSMike Capps     try
93b4379a1eSMike Capps     {
94808d7fe8SMike Capps         jsonObj = getJsonObj();
95c95c527aSMatthew Barth #endif
96b4379a1eSMike Capps         auto trustGrps = getTrustGroups(jsonObj);
97b4379a1eSMike Capps         auto fanDefs = getFanDefinitions(jsonObj);
98c95c527aSMatthew Barth         // Retrieve and set trust groups within the trust manager
99d06905c9SMatthew Barth         setTrustMgr(getTrustGroups(jsonObj));
100b4379a1eSMike Capps         // Clear/set configured fan definitions
101b4379a1eSMike Capps         _fans.clear();
102b4379a1eSMike Capps         _fanHealth.clear();
103c95c527aSMatthew Barth         // Retrieve fan definitions and create fan objects to be monitored
104b4379a1eSMike Capps         setFans(fanDefs);
105e892e39aSMatt Spinler         setFaultConfig(jsonObj);
106d06905c9SMatthew Barth         log<level::INFO>("Configuration loaded");
107e892e39aSMatt Spinler 
108b4379a1eSMike Capps         _loaded = true;
109b4379a1eSMike Capps #ifdef MONITOR_USE_JSON
110b4379a1eSMike Capps     }
111b4379a1eSMike Capps     catch (const phosphor::fan::NoConfigFound&)
112b4379a1eSMike Capps     {}
113b4379a1eSMike Capps #endif
114b4379a1eSMike Capps 
115e892e39aSMatt Spinler     if (_powerState->isPowerOn())
116e892e39aSMatt Spinler     {
117752f24e4SMatt Spinler         // Fans could be missing on startup, so check the power off rules.
118752f24e4SMatt Spinler         // Tach sensors default to functional, so they wouldn't cause a power
119752f24e4SMatt Spinler         // off here.
120e892e39aSMatt Spinler         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
121e892e39aSMatt Spinler                       [this](auto& rule) {
122e892e39aSMatt Spinler                           rule->check(PowerRuleState::runtime, _fanHealth);
123e892e39aSMatt Spinler                       });
124e892e39aSMatt Spinler     }
125fdcd5db3SMike Capps 
12625f0327eSMike Capps     subscribeSensorsToServices();
12725f0327eSMike Capps }
128fdcd5db3SMike Capps 
subscribeSensorsToServices()12925f0327eSMike Capps void System::subscribeSensorsToServices()
13025f0327eSMike Capps {
131fdcd5db3SMike Capps     namespace match = sdbusplus::bus::match;
132fdcd5db3SMike Capps 
133b4379a1eSMike Capps     _sensorMatch.clear();
134b4379a1eSMike Capps 
135fdcd5db3SMike Capps     SensorMapType sensorMap;
136fdcd5db3SMike Capps 
137fdcd5db3SMike Capps     // build a list of all interfaces, always including the value interface
138fdcd5db3SMike Capps     // using set automatically guards against duplicates
139fdcd5db3SMike Capps     std::set<std::string> unique_interfaces{util::FAN_SENSOR_VALUE_INTF};
140fdcd5db3SMike Capps 
141fdcd5db3SMike Capps     for (const auto& fan : _fans)
142fdcd5db3SMike Capps     {
143fdcd5db3SMike Capps         for (const auto& sensor : fan->sensors())
144fdcd5db3SMike Capps         {
145fdcd5db3SMike Capps             unique_interfaces.insert(sensor->getInterface());
146fdcd5db3SMike Capps         }
147fdcd5db3SMike Capps     }
148fdcd5db3SMike Capps     // convert them to vector to pass into getSubTreeRaw
149fdcd5db3SMike Capps     std::vector<std::string> interfaces(unique_interfaces.begin(),
150fdcd5db3SMike Capps                                         unique_interfaces.end());
151fdcd5db3SMike Capps 
15225f0327eSMike Capps     try
15325f0327eSMike Capps     {
154fdcd5db3SMike Capps         // get service information for all service names that are
155fdcd5db3SMike Capps         // hosting these interfaces
15625f0327eSMike Capps         auto serviceObjects = util::SDBusPlus::getSubTreeRaw(
15725f0327eSMike Capps             _bus, FAN_SENSOR_PATH, interfaces, 0);
158fdcd5db3SMike Capps 
159fdcd5db3SMike Capps         for (const auto& fan : _fans)
160fdcd5db3SMike Capps         {
161fdcd5db3SMike Capps             // For every sensor in each fan
162fdcd5db3SMike Capps             for (const auto& sensor : fan->sensors())
163fdcd5db3SMike Capps             {
164fdcd5db3SMike Capps                 const auto itServ = serviceObjects.find(sensor->name());
165fdcd5db3SMike Capps 
166fdcd5db3SMike Capps                 if (serviceObjects.end() == itServ || itServ->second.empty())
167fdcd5db3SMike Capps                 {
168fdcd5db3SMike Capps                     getLogger().log(
169fbf4703fSPatrick Williams                         std::format("Fan sensor entry {} not found in D-Bus",
170fdcd5db3SMike Capps                                     sensor->name()),
171fdcd5db3SMike Capps                         Logger::error);
172fdcd5db3SMike Capps                     continue;
173fdcd5db3SMike Capps                 }
174fdcd5db3SMike Capps 
175fdcd5db3SMike Capps                 for (const auto& [serviceName, unused] : itServ->second)
176fdcd5db3SMike Capps                 {
17725f0327eSMike Capps                     // associate service name with sensor
178fdcd5db3SMike Capps                     sensorMap[serviceName].insert(sensor);
179fdcd5db3SMike Capps                 }
180fdcd5db3SMike Capps             }
181fdcd5db3SMike Capps         }
182fdcd5db3SMike Capps 
18325f0327eSMike Capps         // only create 1 match per service
18425f0327eSMike Capps         for (const auto& [serviceName, unused] : sensorMap)
18525f0327eSMike Capps         {
18625f0327eSMike Capps             // map its service name to the sensor
187cb356d48SPatrick Williams             _sensorMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
18825f0327eSMike Capps                 _bus, match::rules::nameOwnerChanged(serviceName),
18925f0327eSMike Capps                 std::bind(&System::tachSignalOffline, this,
19025f0327eSMike Capps                           std::placeholders::_1, sensorMap)));
19125f0327eSMike Capps         }
19225f0327eSMike Capps     }
19325f0327eSMike Capps     catch (const util::DBusError&)
19425f0327eSMike Capps     {
19525f0327eSMike Capps         // catch exception from getSubTreeRaw() when fan sensor paths don't
19625f0327eSMike Capps         // exist yet
19725f0327eSMike Capps     }
198c95c527aSMatthew Barth }
199d06905c9SMatthew Barth 
inventoryOnlineCb(sdbusplus::message_t & msg)200cb356d48SPatrick Williams void System::inventoryOnlineCb(sdbusplus::message_t& msg)
201b4379a1eSMike Capps {
202b4379a1eSMike Capps     namespace match = sdbusplus::bus::match;
203b4379a1eSMike Capps 
204b4379a1eSMike Capps     std::string iface;
205b4379a1eSMike Capps     msg.read(iface);
206b4379a1eSMike Capps 
207b4379a1eSMike Capps     if (util::INVENTORY_INTF != iface)
208b4379a1eSMike Capps     {
209b4379a1eSMike Capps         return;
210b4379a1eSMike Capps     }
211b4379a1eSMike Capps 
212b4379a1eSMike Capps     std::string oldName;
213b4379a1eSMike Capps     msg.read(oldName);
214b4379a1eSMike Capps 
215b4379a1eSMike Capps     std::string newName;
216b4379a1eSMike Capps     msg.read(newName);
217b4379a1eSMike Capps 
218b4379a1eSMike Capps     // newName should never be empty since match was reset on the first
219b4379a1eSMike Capps     // nameOwnerChanged signal received from the service.
220b4379a1eSMike Capps     if (!_loaded && !newName.empty())
221b4379a1eSMike Capps     {
222b4379a1eSMike Capps         load();
223b4379a1eSMike Capps     }
224b4379a1eSMike Capps 
225b4379a1eSMike Capps     // cancel any further notifications about the service state
226b4379a1eSMike Capps     _inventoryMatch.reset();
227b4379a1eSMike Capps }
228b4379a1eSMike Capps 
sighupHandler(sdeventplus::source::Signal &,const struct signalfd_siginfo *)229d06905c9SMatthew Barth void System::sighupHandler(sdeventplus::source::Signal&,
230d06905c9SMatthew Barth                            const struct signalfd_siginfo*)
231d06905c9SMatthew Barth {
232d06905c9SMatthew Barth     try
233d06905c9SMatthew Barth     {
234b4379a1eSMike Capps         load();
235e892e39aSMatt Spinler     }
236b4379a1eSMike Capps     catch (std::runtime_error& re)
237d06905c9SMatthew Barth     {
238d06905c9SMatthew Barth         log<level::ERR>("Error reloading config, no config changes made",
239d06905c9SMatthew Barth                         entry("LOAD_ERROR=%s", re.what()));
240c95c527aSMatthew Barth     }
241c95c527aSMatthew Barth }
242c95c527aSMatthew Barth 
getTrustGroups(const json & jsonObj)243*4fa67aa1SPatrick Williams const std::vector<CreateGroupFunction> System::getTrustGroups(
244*4fa67aa1SPatrick Williams     [[maybe_unused]] const json& jsonObj)
245c95c527aSMatthew Barth {
246c95c527aSMatthew Barth #ifdef MONITOR_USE_JSON
247c95c527aSMatthew Barth     return getTrustGrps(jsonObj);
248c95c527aSMatthew Barth #else
249c95c527aSMatthew Barth     return trustGroups;
250c95c527aSMatthew Barth #endif
251c95c527aSMatthew Barth }
252c95c527aSMatthew Barth 
setTrustMgr(const std::vector<CreateGroupFunction> & groupFuncs)253d06905c9SMatthew Barth void System::setTrustMgr(const std::vector<CreateGroupFunction>& groupFuncs)
254d06905c9SMatthew Barth {
255d06905c9SMatthew Barth     _trust = std::make_unique<trust::Manager>(groupFuncs);
256d06905c9SMatthew Barth }
257d06905c9SMatthew Barth 
getFanDefinitions(const json & jsonObj)258*4fa67aa1SPatrick Williams const std::vector<FanDefinition> System::getFanDefinitions(
259*4fa67aa1SPatrick Williams     [[maybe_unused]] const json& jsonObj)
260c95c527aSMatthew Barth {
261c95c527aSMatthew Barth #ifdef MONITOR_USE_JSON
262c95c527aSMatthew Barth     return getFanDefs(jsonObj);
263c95c527aSMatthew Barth #else
264c95c527aSMatthew Barth     return fanDefinitions;
265c95c527aSMatthew Barth #endif
266c95c527aSMatthew Barth }
267c95c527aSMatthew Barth 
setFans(const std::vector<FanDefinition> & fanDefs)268d06905c9SMatthew Barth void System::setFans(const std::vector<FanDefinition>& fanDefs)
269d06905c9SMatthew Barth {
270d06905c9SMatthew Barth     for (const auto& fanDef : fanDefs)
271d06905c9SMatthew Barth     {
272d06905c9SMatthew Barth         // Check if a condition exists on the fan
27318fb12b8SMatt Spinler         auto condition = fanDef.condition;
274d06905c9SMatthew Barth         if (condition)
275d06905c9SMatthew Barth         {
276d06905c9SMatthew Barth             // Condition exists, skip adding fan if it fails
277d06905c9SMatthew Barth             if (!(*condition)(_bus))
278d06905c9SMatthew Barth             {
279d06905c9SMatthew Barth                 continue;
280d06905c9SMatthew Barth             }
281d06905c9SMatthew Barth         }
282d06905c9SMatthew Barth         _fans.emplace_back(
283b0412d07SMatt Spinler             std::make_unique<Fan>(_mode, _bus, _event, _trust, fanDef, *this));
284b63aa09eSMatt Spinler 
285b63aa09eSMatt Spinler         updateFanHealth(*(_fans.back()));
286d06905c9SMatthew Barth     }
287d06905c9SMatthew Barth }
288d06905c9SMatthew Barth 
289fdcd5db3SMike Capps // callback indicating a service went [on|off]line.
290fdcd5db3SMike Capps // Determine on/offline status, set all sensors for that service
291fdcd5db3SMike Capps // to new state
292fdcd5db3SMike Capps //
tachSignalOffline(sdbusplus::message_t & msg,const SensorMapType & sensorMap)293cb356d48SPatrick Williams void System::tachSignalOffline(sdbusplus::message_t& msg,
29461b73296SPatrick Williams                                const SensorMapType& sensorMap)
295fdcd5db3SMike Capps {
296fdcd5db3SMike Capps     std::string serviceName, oldOwner, newOwner;
297fdcd5db3SMike Capps 
298fdcd5db3SMike Capps     msg.read(serviceName);
299fdcd5db3SMike Capps     msg.read(oldOwner);
300fdcd5db3SMike Capps     msg.read(newOwner);
301fdcd5db3SMike Capps 
302fdcd5db3SMike Capps     // true if sensor server came back online, false -> went offline
303fdcd5db3SMike Capps     bool hasOwner = !newOwner.empty() && oldOwner.empty();
304fdcd5db3SMike Capps 
305fdcd5db3SMike Capps     std::string stateStr(hasOwner ? "online" : "offline");
306fbf4703fSPatrick Williams     getLogger().log(std::format("Changing sensors for service {} to {}",
307fdcd5db3SMike Capps                                 serviceName, stateStr),
308fdcd5db3SMike Capps                     Logger::info);
309fdcd5db3SMike Capps 
310fdcd5db3SMike Capps     auto sensorItr(sensorMap.find(serviceName));
311fdcd5db3SMike Capps 
312fdcd5db3SMike Capps     if (sensorItr != sensorMap.end())
313fdcd5db3SMike Capps     {
314fdcd5db3SMike Capps         // set all sensors' owner state to not-owned
315fdcd5db3SMike Capps         for (auto& sensor : sensorItr->second)
316fdcd5db3SMike Capps         {
317fdcd5db3SMike Capps             sensor->setOwner(hasOwner);
318fdcd5db3SMike Capps             sensor->getFan().process(*sensor);
319fdcd5db3SMike Capps         }
320fdcd5db3SMike Capps     }
321fdcd5db3SMike Capps }
322fdcd5db3SMike Capps 
updateFanHealth(const Fan & fan)323b63aa09eSMatt Spinler void System::updateFanHealth(const Fan& fan)
324b63aa09eSMatt Spinler {
325b63aa09eSMatt Spinler     std::vector<bool> sensorStatus;
326b63aa09eSMatt Spinler     for (const auto& sensor : fan.sensors())
327b63aa09eSMatt Spinler     {
328b63aa09eSMatt Spinler         sensorStatus.push_back(sensor->functional());
329b63aa09eSMatt Spinler     }
330b63aa09eSMatt Spinler 
331dfddd648SPatrick Williams     _fanHealth[fan.getName()] =
332dfddd648SPatrick Williams         std::make_tuple(fan.present(), std::move(sensorStatus));
333b63aa09eSMatt Spinler }
334b63aa09eSMatt Spinler 
fanStatusChange(const Fan & fan,bool skipRulesCheck)3354283c5d5SMatt Spinler void System::fanStatusChange(const Fan& fan, bool skipRulesCheck)
336b63aa09eSMatt Spinler {
337b63aa09eSMatt Spinler     updateFanHealth(fan);
338e892e39aSMatt Spinler 
3394283c5d5SMatt Spinler     if (_powerState->isPowerOn() && !skipRulesCheck)
340e892e39aSMatt Spinler     {
341e892e39aSMatt Spinler         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
342e892e39aSMatt Spinler                       [this](auto& rule) {
343e892e39aSMatt Spinler                           rule->check(PowerRuleState::runtime, _fanHealth);
344e892e39aSMatt Spinler                       });
345e892e39aSMatt Spinler     }
346e892e39aSMatt Spinler }
347e892e39aSMatt Spinler 
setFaultConfig(const json & jsonObj)348808d7fe8SMike Capps void System::setFaultConfig([[maybe_unused]] const json& jsonObj)
349e892e39aSMatt Spinler {
350e892e39aSMatt Spinler #ifdef MONITOR_USE_JSON
351e892e39aSMatt Spinler     std::shared_ptr<PowerInterfaceBase> powerInterface =
352ba3ee9aeSMatt Spinler         std::make_shared<PowerInterface>(_thermalAlert);
353e892e39aSMatt Spinler 
354ac1efc11SMatt Spinler     PowerOffAction::PrePowerOffFunc func =
355ac1efc11SMatt Spinler         std::bind(std::mem_fn(&System::logShutdownError), this);
356ac1efc11SMatt Spinler 
357ac1efc11SMatt Spinler     _powerOffRules = getPowerOffRules(jsonObj, powerInterface, func);
358f13b42e2SMatt Spinler 
359f13b42e2SMatt Spinler     _numNonfuncSensorsBeforeError = getNumNonfuncRotorsBeforeError(jsonObj);
360e892e39aSMatt Spinler #endif
361e892e39aSMatt Spinler }
362e892e39aSMatt Spinler 
powerStateChanged(bool powerStateOn)363e892e39aSMatt Spinler void System::powerStateChanged(bool powerStateOn)
364e892e39aSMatt Spinler {
3657d135641SMatt Spinler     std::for_each(_fans.begin(), _fans.end(), [powerStateOn](auto& fan) {
3667d135641SMatt Spinler         fan->powerStateChanged(powerStateOn);
3677d135641SMatt Spinler     });
3687d135641SMatt Spinler 
369e892e39aSMatt Spinler     if (powerStateOn)
370e892e39aSMatt Spinler     {
371b4379a1eSMike Capps         if (!_loaded)
3727d135641SMatt Spinler         {
3737d135641SMatt Spinler             log<level::ERR>("No conf file found at power on");
374ba53d3e4SMatthew Barth             throw std::runtime_error("No conf file found at power on");
3757d135641SMatt Spinler         }
3767d135641SMatt Spinler 
377bb449c1cSMatt Spinler         // If no fan has its sensors on D-Bus, then there is a problem
378bb449c1cSMatt Spinler         // with the fan controller.  Log an error and shut down.
379bb449c1cSMatt Spinler         if (std::all_of(_fans.begin(), _fans.end(), [](const auto& fan) {
380bb449c1cSMatt Spinler                 return fan->numSensorsOnDBusAtPowerOn() == 0;
381bb449c1cSMatt Spinler             }))
382bb449c1cSMatt Spinler         {
383751c8bebSChau Ly #if DELAY_HOST_CONTROL > 0
384751c8bebSChau Ly             sleep(DELAY_HOST_CONTROL);
385751c8bebSChau Ly             std::for_each(_fans.begin(), _fans.end(),
386751c8bebSChau Ly                           [powerStateOn](auto& fan) {
387751c8bebSChau Ly                               fan->powerStateChanged(powerStateOn);
388751c8bebSChau Ly                           });
389751c8bebSChau Ly             if (std::all_of(_fans.begin(), _fans.end(), [](const auto& fan) {
390751c8bebSChau Ly                     return fan->numSensorsOnDBusAtPowerOn() == 0;
391751c8bebSChau Ly                 }))
392751c8bebSChau Ly             {
393bb449c1cSMatt Spinler                 handleOfflineFanController();
394bb449c1cSMatt Spinler                 return;
395bb449c1cSMatt Spinler             }
396751c8bebSChau Ly #else
397751c8bebSChau Ly             handleOfflineFanController();
398751c8bebSChau Ly             return;
399751c8bebSChau Ly #endif
400751c8bebSChau Ly         }
401bb449c1cSMatt Spinler 
40225f0327eSMike Capps         if (_sensorMatch.empty())
40325f0327eSMike Capps         {
40425f0327eSMike Capps             subscribeSensorsToServices();
40525f0327eSMike Capps         }
40625f0327eSMike Capps 
407e892e39aSMatt Spinler         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
408e892e39aSMatt Spinler                       [this](auto& rule) {
409e892e39aSMatt Spinler                           rule->check(PowerRuleState::atPgood, _fanHealth);
410e892e39aSMatt Spinler                       });
411e892e39aSMatt Spinler         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
412e892e39aSMatt Spinler                       [this](auto& rule) {
413e892e39aSMatt Spinler                           rule->check(PowerRuleState::runtime, _fanHealth);
414e892e39aSMatt Spinler                       });
415e892e39aSMatt Spinler     }
416e892e39aSMatt Spinler     else
417e892e39aSMatt Spinler     {
418c8d3c51fSMatt Spinler         _thermalAlert.enabled(false);
419c8d3c51fSMatt Spinler 
420e892e39aSMatt Spinler         // Cancel any in-progress power off actions
421e892e39aSMatt Spinler         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
422e892e39aSMatt Spinler                       [this](auto& rule) { rule->cancel(); });
423e892e39aSMatt Spinler     }
424b63aa09eSMatt Spinler }
425b63aa09eSMatt Spinler 
sensorErrorTimerExpired(const Fan & fan,const TachSensor & sensor)426f13b42e2SMatt Spinler void System::sensorErrorTimerExpired(const Fan& fan, const TachSensor& sensor)
427f13b42e2SMatt Spinler {
428f13b42e2SMatt Spinler     std::string fanPath{util::INVENTORY_PATH + fan.getName()};
429f13b42e2SMatt Spinler 
430f13b42e2SMatt Spinler     getLogger().log(
431fbf4703fSPatrick Williams         std::format("Creating event log for faulted fan {} sensor {}", fanPath,
432f13b42e2SMatt Spinler                     sensor.name()),
433f13b42e2SMatt Spinler         Logger::error);
434f13b42e2SMatt Spinler 
435f13b42e2SMatt Spinler     // In order to know if the event log should have a severity of error or
436f13b42e2SMatt Spinler     // informational, count the number of existing nonfunctional sensors and
437f13b42e2SMatt Spinler     // compare it to _numNonfuncSensorsBeforeError.
438f13b42e2SMatt Spinler     size_t nonfuncSensors = 0;
439f13b42e2SMatt Spinler     for (const auto& fan : _fans)
440f13b42e2SMatt Spinler     {
441f13b42e2SMatt Spinler         for (const auto& s : fan->sensors())
442f13b42e2SMatt Spinler         {
443f13b42e2SMatt Spinler             // Don't count nonfunctional sensors that still have their
444f13b42e2SMatt Spinler             // error timer running as nonfunctional since they haven't
445f13b42e2SMatt Spinler             // had event logs created for those errors yet.
446f13b42e2SMatt Spinler             if (!s->functional() && !s->errorTimerRunning())
447f13b42e2SMatt Spinler             {
448f13b42e2SMatt Spinler                 nonfuncSensors++;
449f13b42e2SMatt Spinler             }
450f13b42e2SMatt Spinler         }
451f13b42e2SMatt Spinler     }
452f13b42e2SMatt Spinler 
453f13b42e2SMatt Spinler     Severity severity = Severity::Error;
454f13b42e2SMatt Spinler     if (nonfuncSensors < _numNonfuncSensorsBeforeError)
455f13b42e2SMatt Spinler     {
456f13b42e2SMatt Spinler         severity = Severity::Informational;
457f13b42e2SMatt Spinler     }
458f13b42e2SMatt Spinler 
459f13b42e2SMatt Spinler     auto error =
460f13b42e2SMatt Spinler         std::make_unique<FanError>("xyz.openbmc_project.Fan.Error.Fault",
461f13b42e2SMatt Spinler                                    fanPath, sensor.name(), severity);
462f13b42e2SMatt Spinler 
463f13b42e2SMatt Spinler     auto sensorData = captureSensorData();
464f13b42e2SMatt Spinler     error->commit(sensorData);
465f13b42e2SMatt Spinler 
466ac1efc11SMatt Spinler     // Save the error so it can be committed again on a power off.
467ac1efc11SMatt Spinler     _lastError = std::move(error);
468f13b42e2SMatt Spinler }
469f13b42e2SMatt Spinler 
fanMissingErrorTimerExpired(const Fan & fan)47027f6b686SMatt Spinler void System::fanMissingErrorTimerExpired(const Fan& fan)
47127f6b686SMatt Spinler {
47227f6b686SMatt Spinler     std::string fanPath{util::INVENTORY_PATH + fan.getName()};
47327f6b686SMatt Spinler 
47427f6b686SMatt Spinler     getLogger().log(
475fbf4703fSPatrick Williams         std::format("Creating event log for missing fan {}", fanPath),
47627f6b686SMatt Spinler         Logger::error);
47727f6b686SMatt Spinler 
47827f6b686SMatt Spinler     auto error = std::make_unique<FanError>(
47927f6b686SMatt Spinler         "xyz.openbmc_project.Fan.Error.Missing", fanPath, "", Severity::Error);
48027f6b686SMatt Spinler 
48127f6b686SMatt Spinler     auto sensorData = captureSensorData();
48227f6b686SMatt Spinler     error->commit(sensorData);
48327f6b686SMatt Spinler 
484ac1efc11SMatt Spinler     // Save the error so it can be committed again on a power off.
485ac1efc11SMatt Spinler     _lastError = std::move(error);
486ac1efc11SMatt Spinler }
487ac1efc11SMatt Spinler 
logShutdownError()488ac1efc11SMatt Spinler void System::logShutdownError()
489ac1efc11SMatt Spinler {
490ac1efc11SMatt Spinler     if (_lastError)
491ac1efc11SMatt Spinler     {
492ac1efc11SMatt Spinler         getLogger().log("Re-committing previous fan error before power off");
493ac1efc11SMatt Spinler 
494ac1efc11SMatt Spinler         // Still use the latest sensor data
495ac1efc11SMatt Spinler         auto sensorData = captureSensorData();
496f435eb1aSMatt Spinler         _lastError->commit(sensorData, true);
497ac1efc11SMatt Spinler     }
49827f6b686SMatt Spinler }
49927f6b686SMatt Spinler 
captureSensorData()500f13b42e2SMatt Spinler json System::captureSensorData()
501f13b42e2SMatt Spinler {
502f13b42e2SMatt Spinler     json data;
503f13b42e2SMatt Spinler 
504f13b42e2SMatt Spinler     for (const auto& fan : _fans)
505f13b42e2SMatt Spinler     {
506f13b42e2SMatt Spinler         for (const auto& sensor : fan->sensors())
507f13b42e2SMatt Spinler         {
508f13b42e2SMatt Spinler             json values;
509f13b42e2SMatt Spinler             values["present"] = fan->present();
510f13b42e2SMatt Spinler             values["functional"] = sensor->functional();
511d16d464aSMatt Spinler             values["in_range"] = !fan->outOfRange(*sensor);
512f13b42e2SMatt Spinler             values["tach"] = sensor->getInput();
5137b34ee0fSMike Capps 
514f13b42e2SMatt Spinler             if (sensor->hasTarget())
515f13b42e2SMatt Spinler             {
516f13b42e2SMatt Spinler                 values["target"] = sensor->getTarget();
517f13b42e2SMatt Spinler             }
518f13b42e2SMatt Spinler 
5197b34ee0fSMike Capps             // convert between string/json to remove newlines
5207b34ee0fSMike Capps             values["prev_tachs"] = json(sensor->getPrevTach()).dump();
5217b34ee0fSMike Capps 
5227b34ee0fSMike Capps             if (sensor->hasTarget())
5237b34ee0fSMike Capps             {
5247b34ee0fSMike Capps                 values["prev_targets"] = json(sensor->getPrevTarget()).dump();
5257b34ee0fSMike Capps             }
5267b34ee0fSMike Capps 
52787f9adc4SMatt Spinler             if (sensor->getMethod() == MethodMode::count)
52887f9adc4SMatt Spinler             {
52987f9adc4SMatt Spinler                 values["ticks"] = sensor->getCounter();
53087f9adc4SMatt Spinler             }
531f13b42e2SMatt Spinler             data["sensors"][sensor->name()] = values;
532f13b42e2SMatt Spinler         }
533f13b42e2SMatt Spinler     }
534f13b42e2SMatt Spinler 
535f13b42e2SMatt Spinler     return data;
536f13b42e2SMatt Spinler }
537f13b42e2SMatt Spinler 
handleOfflineFanController()538bb449c1cSMatt Spinler void System::handleOfflineFanController()
539bb449c1cSMatt Spinler {
540bb449c1cSMatt Spinler     getLogger().log("The fan controller appears to be offline.  Shutting down.",
541bb449c1cSMatt Spinler                     Logger::error);
542bb449c1cSMatt Spinler 
543bb449c1cSMatt Spinler     auto ffdc = collectHwmonFFDC();
544bb449c1cSMatt Spinler 
545bb449c1cSMatt Spinler     FanError error{"xyz.openbmc_project.Fan.Error.FanControllerOffline",
546bb449c1cSMatt Spinler                    Severity::Critical};
547bb449c1cSMatt Spinler     error.commit(ffdc, true);
548bb449c1cSMatt Spinler 
549bb449c1cSMatt Spinler     PowerInterface::executeHardPowerOff();
550683a96c6SMike Capps 
551683a96c6SMike Capps     createBmcDump();
552683a96c6SMike Capps }
553683a96c6SMike Capps 
554683a96c6SMike Capps /**
555683a96c6SMike Capps  * @brief Create a BMC Dump
556683a96c6SMike Capps  */
createBmcDump() const557683a96c6SMike Capps void System::createBmcDump() const
558683a96c6SMike Capps {
559683a96c6SMike Capps     try
560683a96c6SMike Capps     {
561683a96c6SMike Capps         util::SDBusPlus::callMethod(
562683a96c6SMike Capps             "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc",
563683a96c6SMike Capps             "xyz.openbmc_project.Dump.Create", "CreateDump",
564683a96c6SMike Capps             std::vector<
565683a96c6SMike Capps                 std::pair<std::string, std::variant<std::string, uint64_t>>>());
566683a96c6SMike Capps     }
567477b13bdSMike Capps     catch (const std::exception& e)
568477b13bdSMike Capps     {
569477b13bdSMike Capps         getLogger().log(
570fbf4703fSPatrick Williams             std::format("Caught exception while creating BMC dump: {}",
571477b13bdSMike Capps                         e.what()),
572477b13bdSMike Capps             Logger::error);
573477b13bdSMike Capps     }
574bb449c1cSMatt Spinler }
575bb449c1cSMatt Spinler 
dumpDebugData(sdeventplus::source::Signal &,const struct signalfd_siginfo *)5764f472a86SMatt Spinler void System::dumpDebugData(sdeventplus::source::Signal&,
5774f472a86SMatt Spinler                            const struct signalfd_siginfo*)
5784f472a86SMatt Spinler {
5794f472a86SMatt Spinler     json output;
5804f472a86SMatt Spinler 
5814f472a86SMatt Spinler     if (_loaded)
5824f472a86SMatt Spinler     {
5834f472a86SMatt Spinler         output["logs"] = getLogger().getLogs();
5844f472a86SMatt Spinler         output["sensors"] = captureSensorData();
5854f472a86SMatt Spinler     }
5864f472a86SMatt Spinler     else
5874f472a86SMatt Spinler     {
5884f472a86SMatt Spinler         output["error"] = "Fan monitor not loaded yet.  Try again later.";
5894f472a86SMatt Spinler     }
5904f472a86SMatt Spinler 
5914f472a86SMatt Spinler     std::ofstream file{System::dumpFile};
5924f472a86SMatt Spinler     if (!file)
5934f472a86SMatt Spinler     {
5944f472a86SMatt Spinler         log<level::ERR>("Could not open file for fan monitor dump");
5954f472a86SMatt Spinler     }
5964f472a86SMatt Spinler     else
5974f472a86SMatt Spinler     {
5984f472a86SMatt Spinler         file << std::setw(4) << output;
5994f472a86SMatt Spinler     }
6004f472a86SMatt Spinler }
6014f472a86SMatt Spinler 
602c95c527aSMatthew Barth } // namespace phosphor::fan::monitor
603