1 /**
2  * Copyright © 2020 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 "system.hpp"
17 
18 #include "fan.hpp"
19 #include "fan_defs.hpp"
20 #include "tach_sensor.hpp"
21 #include "trust_manager.hpp"
22 #include "types.hpp"
23 #ifdef MONITOR_USE_JSON
24 #include "json_parser.hpp"
25 #endif
26 
27 #include "config.h"
28 
29 #include <nlohmann/json.hpp>
30 #include <phosphor-logging/log.hpp>
31 #include <sdbusplus/bus.hpp>
32 #include <sdeventplus/event.hpp>
33 #include <sdeventplus/source/signal.hpp>
34 
35 namespace phosphor::fan::monitor
36 {
37 
38 using json = nlohmann::json;
39 using Severity = sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
40 
41 using namespace phosphor::logging;
42 
43 System::System(Mode mode, sdbusplus::bus::bus& bus,
44                const sdeventplus::Event& event) :
45     _mode(mode),
46     _bus(bus), _event(event),
47     _powerState(std::make_unique<PGoodState>(
48         bus, std::bind(std::mem_fn(&System::powerStateChanged), this,
49                        std::placeholders::_1))),
50     _thermalAlert(bus, THERMAL_ALERT_OBJPATH)
51 {}
52 
53 void System::start(
54 #ifdef MONITOR_USE_JSON
55     const std::string& confFile
56 #endif
57 )
58 {
59     _started = true;
60     json jsonObj = json::object();
61 #ifdef MONITOR_USE_JSON
62     jsonObj = fan::JsonConfig::load(confFile);
63 #endif
64     // Retrieve and set trust groups within the trust manager
65     setTrustMgr(getTrustGroups(jsonObj));
66     // Retrieve fan definitions and create fan objects to be monitored
67     setFans(getFanDefinitions(jsonObj));
68     setFaultConfig(jsonObj);
69     log<level::INFO>("Configuration loaded");
70 
71     if (_powerState->isPowerOn())
72     {
73         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
74                       [this](auto& rule) {
75                           rule->check(PowerRuleState::runtime, _fanHealth);
76                       });
77     }
78 }
79 
80 void System::sighupHandler(sdeventplus::source::Signal&,
81                            const struct signalfd_siginfo*)
82 {
83     try
84     {
85         json jsonObj = json::object();
86 #ifdef MONITOR_USE_JSON
87         jsonObj = getJsonObj(_bus);
88 #endif
89         auto trustGrps = getTrustGroups(jsonObj);
90         auto fanDefs = getFanDefinitions(jsonObj);
91         // Set configured trust groups
92         setTrustMgr(trustGrps);
93         // Clear/set configured fan definitions
94         _fans.clear();
95         _fanHealth.clear();
96         setFans(fanDefs);
97         setFaultConfig(jsonObj);
98         log<level::INFO>("Configuration reloaded successfully");
99 
100         if (_powerState->isPowerOn())
101         {
102             std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
103                           [this](auto& rule) {
104                               rule->check(PowerRuleState::runtime, _fanHealth);
105                           });
106         }
107     }
108     catch (std::runtime_error& re)
109     {
110         log<level::ERR>("Error reloading config, no config changes made",
111                         entry("LOAD_ERROR=%s", re.what()));
112     }
113 }
114 
115 const std::vector<CreateGroupFunction>
116     System::getTrustGroups(const json& jsonObj)
117 {
118 #ifdef MONITOR_USE_JSON
119     return getTrustGrps(jsonObj);
120 #else
121     return trustGroups;
122 #endif
123 }
124 
125 void System::setTrustMgr(const std::vector<CreateGroupFunction>& groupFuncs)
126 {
127     _trust = std::make_unique<trust::Manager>(groupFuncs);
128 }
129 
130 const std::vector<FanDefinition> System::getFanDefinitions(const json& jsonObj)
131 {
132 #ifdef MONITOR_USE_JSON
133     return getFanDefs(jsonObj);
134 #else
135     return fanDefinitions;
136 #endif
137 }
138 
139 void System::setFans(const std::vector<FanDefinition>& fanDefs)
140 {
141     for (const auto& fanDef : fanDefs)
142     {
143         // Check if a condition exists on the fan
144         auto condition = std::get<conditionField>(fanDef);
145         if (condition)
146         {
147             // Condition exists, skip adding fan if it fails
148             if (!(*condition)(_bus))
149             {
150                 continue;
151             }
152         }
153         _fans.emplace_back(
154             std::make_unique<Fan>(_mode, _bus, _event, _trust, fanDef, *this));
155 
156         updateFanHealth(*(_fans.back()));
157     }
158 }
159 
160 void System::updateFanHealth(const Fan& fan)
161 {
162     std::vector<bool> sensorStatus;
163     for (const auto& sensor : fan.sensors())
164     {
165         sensorStatus.push_back(sensor->functional());
166     }
167 
168     _fanHealth[fan.getName()] =
169         std::make_tuple(fan.present(), std::move(sensorStatus));
170 }
171 
172 void System::fanStatusChange(const Fan& fan)
173 {
174     updateFanHealth(fan);
175 
176     if (_powerState->isPowerOn())
177     {
178         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
179                       [this](auto& rule) {
180                           rule->check(PowerRuleState::runtime, _fanHealth);
181                       });
182     }
183 }
184 
185 void System::setFaultConfig(const json& jsonObj)
186 {
187 #ifdef MONITOR_USE_JSON
188     std::shared_ptr<PowerInterfaceBase> powerInterface =
189         std::make_shared<PowerInterface>(_thermalAlert);
190 
191     PowerOffAction::PrePowerOffFunc func =
192         std::bind(std::mem_fn(&System::logShutdownError), this);
193 
194     _powerOffRules = getPowerOffRules(jsonObj, powerInterface, func);
195 
196     _numNonfuncSensorsBeforeError = getNumNonfuncRotorsBeforeError(jsonObj);
197 #endif
198 }
199 
200 void System::powerStateChanged(bool powerStateOn)
201 {
202     std::for_each(_fans.begin(), _fans.end(), [powerStateOn](auto& fan) {
203         fan->powerStateChanged(powerStateOn);
204     });
205 
206     if (powerStateOn)
207     {
208         if (!_started)
209         {
210             log<level::ERR>("No conf file found at power on");
211             throw std::runtime_error("No conf file fount at power on");
212         }
213 
214         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
215                       [this](auto& rule) {
216                           rule->check(PowerRuleState::atPgood, _fanHealth);
217                       });
218         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
219                       [this](auto& rule) {
220                           rule->check(PowerRuleState::runtime, _fanHealth);
221                       });
222     }
223     else
224     {
225         _thermalAlert.enabled(false);
226 
227         // Cancel any in-progress power off actions
228         std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
229                       [this](auto& rule) { rule->cancel(); });
230     }
231 }
232 
233 void System::sensorErrorTimerExpired(const Fan& fan, const TachSensor& sensor)
234 {
235     std::string fanPath{util::INVENTORY_PATH + fan.getName()};
236 
237     getLogger().log(
238         fmt::format("Creating event log for faulted fan {} sensor {}", fanPath,
239                     sensor.name()),
240         Logger::error);
241 
242     // In order to know if the event log should have a severity of error or
243     // informational, count the number of existing nonfunctional sensors and
244     // compare it to _numNonfuncSensorsBeforeError.
245     size_t nonfuncSensors = 0;
246     for (const auto& fan : _fans)
247     {
248         for (const auto& s : fan->sensors())
249         {
250             // Don't count nonfunctional sensors that still have their
251             // error timer running as nonfunctional since they haven't
252             // had event logs created for those errors yet.
253             if (!s->functional() && !s->errorTimerRunning())
254             {
255                 nonfuncSensors++;
256             }
257         }
258     }
259 
260     Severity severity = Severity::Error;
261     if (nonfuncSensors < _numNonfuncSensorsBeforeError)
262     {
263         severity = Severity::Informational;
264     }
265 
266     auto error =
267         std::make_unique<FanError>("xyz.openbmc_project.Fan.Error.Fault",
268                                    fanPath, sensor.name(), severity);
269 
270     auto sensorData = captureSensorData();
271     error->commit(sensorData);
272 
273     // Save the error so it can be committed again on a power off.
274     _lastError = std::move(error);
275 }
276 
277 void System::fanMissingErrorTimerExpired(const Fan& fan)
278 {
279     std::string fanPath{util::INVENTORY_PATH + fan.getName()};
280 
281     getLogger().log(
282         fmt::format("Creating event log for missing fan {}", fanPath),
283         Logger::error);
284 
285     auto error = std::make_unique<FanError>(
286         "xyz.openbmc_project.Fan.Error.Missing", fanPath, "", Severity::Error);
287 
288     auto sensorData = captureSensorData();
289     error->commit(sensorData);
290 
291     // Save the error so it can be committed again on a power off.
292     _lastError = std::move(error);
293 }
294 
295 void System::logShutdownError()
296 {
297     if (_lastError)
298     {
299         getLogger().log("Re-committing previous fan error before power off");
300 
301         // Still use the latest sensor data
302         auto sensorData = captureSensorData();
303         _lastError->commit(sensorData);
304     }
305 }
306 
307 json System::captureSensorData()
308 {
309     json data;
310 
311     for (const auto& fan : _fans)
312     {
313         for (const auto& sensor : fan->sensors())
314         {
315             json values;
316             values["present"] = fan->present();
317             values["functional"] = sensor->functional();
318             values["tach"] = sensor->getInput();
319             if (sensor->hasTarget())
320             {
321                 values["target"] = sensor->getTarget();
322             }
323 
324             data["sensors"][sensor->name()] = values;
325         }
326     }
327 
328     return data;
329 }
330 
331 } // namespace phosphor::fan::monitor
332