blob: f1dfaa97d92eb41d0922abded71cc8c8f582c2b1 [file] [log] [blame]
Matt Spinlere8122392020-09-24 13:22:18 -05001/**
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 "error_reporter.hpp"
17
Matt Spinler0dc85ee2020-09-24 14:07:02 -050018#include "logging.hpp"
19#include "psensor.hpp"
20#include "utility.hpp"
21
22#include <fmt/format.h>
23#include <unistd.h>
24
Matt Spinlere8122392020-09-24 13:22:18 -050025#include <phosphor-logging/log.hpp>
Matt Spinler39fcd502020-09-24 14:23:11 -050026#include <xyz/openbmc_project/Logging/Create/server.hpp>
27#include <xyz/openbmc_project/Logging/Entry/server.hpp>
Matt Spinlere8122392020-09-24 13:22:18 -050028
29namespace phosphor::fan::presence
30{
31
32using json = nlohmann::json;
33using namespace phosphor::logging;
Matt Spinler0dc85ee2020-09-24 14:07:02 -050034using namespace sdbusplus::bus::match;
35using namespace std::literals::string_literals;
Matt Spinler9e9f5992020-09-30 08:29:24 -050036using namespace std::chrono;
Matt Spinler0dc85ee2020-09-24 14:07:02 -050037namespace fs = std::filesystem;
38
39const auto itemIface = "xyz.openbmc_project.Inventory.Item"s;
40const auto invPrefix = "/xyz/openbmc_project/inventory"s;
Matt Spinler39fcd502020-09-24 14:23:11 -050041const auto loggingPath = "/xyz/openbmc_project/logging";
42const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
Matt Spinlere8122392020-09-24 13:22:18 -050043
44ErrorReporter::ErrorReporter(
Matt Spinler9e9f5992020-09-30 08:29:24 -050045 sdbusplus::bus::bus& bus,
Matt Spinlere8122392020-09-24 13:22:18 -050046 const std::vector<
47 std::tuple<Fan, std::vector<std::unique_ptr<PresenceSensor>>>>& fans) :
Matt Spinler0dc85ee2020-09-24 14:07:02 -050048 _bus(bus),
49 _event(sdeventplus::Event::get_default())
Matt Spinlere8122392020-09-24 13:22:18 -050050{
Matt Spinler0dc85ee2020-09-24 14:07:02 -050051 // If different methods to check the power state are needed across the
52 // various platforms, the method/class to use could be read out of JSON
53 // or set with a compilation flag.
54 _powerState = std::make_unique<PGoodState>(
55 bus, std::bind(std::mem_fn(&ErrorReporter::powerStateChanged), this,
56 std::placeholders::_1));
57
58 for (const auto& fan : fans)
59 {
Matt Spinler9e9f5992020-09-30 08:29:24 -050060 const auto& fanData = std::get<0>(fan);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050061
Matt Spinler9e9f5992020-09-30 08:29:24 -050062 // Only deal with fans that have an error time defined.
63 if (std::get<std::optional<size_t>>(fanData))
64 {
65 auto path = invPrefix + std::get<1>(fanData);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050066
Matt Spinler9e9f5992020-09-30 08:29:24 -050067 // Register for fan presence changes, get their initial states,
68 // and create the fan missing timers.
Matt Spinler0dc85ee2020-09-24 14:07:02 -050069
Matt Spinler9e9f5992020-09-30 08:29:24 -050070 _matches.emplace_back(
71 _bus, rules::propertiesChanged(path, itemIface),
72 std::bind(std::mem_fn(&ErrorReporter::presenceChanged), this,
73 std::placeholders::_1));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050074
Matt Spinler9e9f5992020-09-30 08:29:24 -050075 _fanStates.emplace(path, getPresence(fanData));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050076
Matt Spinler9e9f5992020-09-30 08:29:24 -050077 auto timer = std::make_unique<
78 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
79 _event,
80 std::bind(std::mem_fn(&ErrorReporter::fanMissingTimerExpired),
81 this, path));
82
83 seconds errorTime{std::get<std::optional<size_t>>(fanData).value()};
84
85 _fanMissingTimers.emplace(
86 path, std::make_tuple(std::move(timer), std::move(errorTime)));
87 }
Matt Spinler0dc85ee2020-09-24 14:07:02 -050088 }
89
90 // If power is already on, check for currently missing fans.
91 if (_powerState->isPowerOn())
92 {
93 powerStateChanged(true);
94 }
Matt Spinlere8122392020-09-24 13:22:18 -050095}
96
Matt Spinler0dc85ee2020-09-24 14:07:02 -050097void ErrorReporter::presenceChanged(sdbusplus::message::message& msg)
98{
99 bool present;
100 auto fanPath = msg.get_path();
101 std::string interface;
102 std::map<std::string, std::variant<bool>> properties;
103
104 msg.read(interface, properties);
105
106 auto presentProp = properties.find("Present");
107 if (presentProp != properties.end())
108 {
109 present = std::get<bool>(presentProp->second);
110 if (_fanStates[fanPath] != present)
111 {
112 getLogger().log(fmt::format("Fan {} presence state change to {}",
113 fanPath, present));
114
115 _fanStates[fanPath] = present;
116 checkFan(fanPath);
117 }
118 }
119}
120
121void ErrorReporter::checkFan(const std::string& fanPath)
122{
Matt Spinler9e9f5992020-09-30 08:29:24 -0500123 auto& timer = std::get<0>(_fanMissingTimers[fanPath]);
124
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500125 if (!_fanStates[fanPath])
126 {
127 // Fan is missing. If power is on, start the timer.
128 // If power is off, stop a running timer.
129 if (_powerState->isPowerOn())
130 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500131 timer->restartOnce(std::get<seconds>(_fanMissingTimers[fanPath]));
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500132 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500133 else if (timer->isEnabled())
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500134 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500135 timer->setEnabled(false);
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500136 }
137 }
138 else
139 {
140 // Fan is present. Stop a running timer.
Matt Spinler9e9f5992020-09-30 08:29:24 -0500141 if (timer->isEnabled())
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500142 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500143 timer->setEnabled(false);
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500144 }
145 }
146}
147
148void ErrorReporter::fanMissingTimerExpired(const std::string& fanPath)
Matt Spinler39fcd502020-09-24 14:23:11 -0500149{
150 getLogger().log(
151 fmt::format("Creating event log for missing fan {}", fanPath),
152 Logger::error);
153
154 std::map<std::string, std::string> additionalData;
155 additionalData.emplace("_PID", std::to_string(getpid()));
156 additionalData.emplace("CALLOUT_INVENTORY_PATH", fanPath);
157
158 auto severity =
159 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
160 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
161 Error);
162
Matt Spinlerc6745102020-10-22 10:52:02 -0500163 // Save our logs to a temp file and get the file descriptor
Matt Spinler39fcd502020-09-24 14:23:11 -0500164 // so it can be passed in as FFDC data.
165 auto logFile = getLogger().saveToTempFile();
166 util::FileDescriptor fd{-1};
167 fd.open(logFile, O_RDONLY);
168
169 std::vector<std::tuple<
170 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
171 uint8_t, uint8_t, sdbusplus::message::unix_fd>>
172 ffdc;
173
174 ffdc.emplace_back(sdbusplus::xyz::openbmc_project::Logging::server::Create::
Matt Spinlerc6745102020-10-22 10:52:02 -0500175 FFDCFormat::Text,
Matt Spinler39fcd502020-09-24 14:23:11 -0500176 0x01, 0x01, fd());
177
178 try
179 {
180 util::SDBusPlus::lookupAndCallMethod(
181 loggingPath, loggingCreateIface, "CreateWithFFDCFiles",
182 "xyz.openbmc_project.Fan.Error.Missing", severity, additionalData,
183 ffdc);
184 }
185 catch (const util::DBusError& e)
186 {
187 getLogger().log(
188 fmt::format(
189 "Call to create an error log for missing fan {} failed: {}",
190 fanPath, e.what()),
191 Logger::error);
192 fs::remove(logFile);
193 throw;
194 }
195
196 fs::remove(logFile);
197}
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500198
199void ErrorReporter::powerStateChanged(bool powerState)
200{
201 if (powerState)
202 {
203 // If there are fans already missing, log it.
204 auto missing = std::count_if(
205 _fanStates.begin(), _fanStates.end(),
206 [](const auto& fanState) { return fanState.second == false; });
207
208 if (missing)
209 {
210 getLogger().log(
211 fmt::format("At power on, there are {} missing fans", missing));
212 }
213 }
214
215 std::for_each(
216 _fanStates.begin(), _fanStates.end(),
217 [this](const auto& fanState) { this->checkFan(fanState.first); });
218}
219
Matt Spinlere8122392020-09-24 13:22:18 -0500220} // namespace phosphor::fan::presence