blob: 72c7dae777aea45960435e57198c12eadd063121 [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 Spinler76e73c22021-04-21 11:03:05 -050018#include "get_power_state.hpp"
Matt Spinler0dc85ee2020-09-24 14:07:02 -050019#include "logging.hpp"
20#include "psensor.hpp"
21#include "utility.hpp"
22
23#include <fmt/format.h>
24#include <unistd.h>
25
Matt Spinlere8122392020-09-24 13:22:18 -050026#include <phosphor-logging/log.hpp>
Matt Spinler39fcd502020-09-24 14:23:11 -050027#include <xyz/openbmc_project/Logging/Create/server.hpp>
28#include <xyz/openbmc_project/Logging/Entry/server.hpp>
Matt Spinlere8122392020-09-24 13:22:18 -050029
30namespace phosphor::fan::presence
31{
32
33using json = nlohmann::json;
34using namespace phosphor::logging;
Matt Spinler0dc85ee2020-09-24 14:07:02 -050035using namespace sdbusplus::bus::match;
36using namespace std::literals::string_literals;
Matt Spinler9e9f5992020-09-30 08:29:24 -050037using namespace std::chrono;
Matt Spinler0dc85ee2020-09-24 14:07:02 -050038namespace fs = std::filesystem;
39
40const auto itemIface = "xyz.openbmc_project.Inventory.Item"s;
41const auto invPrefix = "/xyz/openbmc_project/inventory"s;
Matt Spinler39fcd502020-09-24 14:23:11 -050042const auto loggingPath = "/xyz/openbmc_project/logging";
43const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
Matt Spinlere8122392020-09-24 13:22:18 -050044
45ErrorReporter::ErrorReporter(
Matt Spinler9e9f5992020-09-30 08:29:24 -050046 sdbusplus::bus::bus& bus,
Matt Spinlere8122392020-09-24 13:22:18 -050047 const std::vector<
48 std::tuple<Fan, std::vector<std::unique_ptr<PresenceSensor>>>>& fans) :
Matt Spinler0dc85ee2020-09-24 14:07:02 -050049 _bus(bus),
Matt Spinler76e73c22021-04-21 11:03:05 -050050 _event(sdeventplus::Event::get_default()),
51 _powerState(getPowerStateObject())
Matt Spinlere8122392020-09-24 13:22:18 -050052{
Matt Spinler76e73c22021-04-21 11:03:05 -050053 _powerState->addCallback("errorReporter",
54 std::bind(&ErrorReporter::powerStateChanged, this,
55 std::placeholders::_1));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050056
57 for (const auto& fan : fans)
58 {
Matt Spinler9e9f5992020-09-30 08:29:24 -050059 const auto& fanData = std::get<0>(fan);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050060
Matt Spinler9e9f5992020-09-30 08:29:24 -050061 // Only deal with fans that have an error time defined.
62 if (std::get<std::optional<size_t>>(fanData))
63 {
64 auto path = invPrefix + std::get<1>(fanData);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050065
Matt Spinler9e9f5992020-09-30 08:29:24 -050066 // Register for fan presence changes, get their initial states,
67 // and create the fan missing timers.
Matt Spinler0dc85ee2020-09-24 14:07:02 -050068
Matt Spinler9e9f5992020-09-30 08:29:24 -050069 _matches.emplace_back(
70 _bus, rules::propertiesChanged(path, itemIface),
71 std::bind(std::mem_fn(&ErrorReporter::presenceChanged), this,
72 std::placeholders::_1));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050073
Matt Spinler9e9f5992020-09-30 08:29:24 -050074 _fanStates.emplace(path, getPresence(fanData));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050075
Matt Spinler9e9f5992020-09-30 08:29:24 -050076 auto timer = std::make_unique<
77 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
78 _event,
79 std::bind(std::mem_fn(&ErrorReporter::fanMissingTimerExpired),
80 this, path));
81
82 seconds errorTime{std::get<std::optional<size_t>>(fanData).value()};
83
84 _fanMissingTimers.emplace(
85 path, std::make_tuple(std::move(timer), std::move(errorTime)));
86 }
Matt Spinler0dc85ee2020-09-24 14:07:02 -050087 }
88
89 // If power is already on, check for currently missing fans.
90 if (_powerState->isPowerOn())
91 {
92 powerStateChanged(true);
93 }
Matt Spinlere8122392020-09-24 13:22:18 -050094}
95
Matt Spinler0dc85ee2020-09-24 14:07:02 -050096void ErrorReporter::presenceChanged(sdbusplus::message::message& msg)
97{
98 bool present;
99 auto fanPath = msg.get_path();
100 std::string interface;
101 std::map<std::string, std::variant<bool>> properties;
102
103 msg.read(interface, properties);
104
105 auto presentProp = properties.find("Present");
106 if (presentProp != properties.end())
107 {
108 present = std::get<bool>(presentProp->second);
109 if (_fanStates[fanPath] != present)
110 {
111 getLogger().log(fmt::format("Fan {} presence state change to {}",
112 fanPath, present));
113
114 _fanStates[fanPath] = present;
115 checkFan(fanPath);
116 }
117 }
118}
119
120void ErrorReporter::checkFan(const std::string& fanPath)
121{
Matt Spinler9e9f5992020-09-30 08:29:24 -0500122 auto& timer = std::get<0>(_fanMissingTimers[fanPath]);
123
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500124 if (!_fanStates[fanPath])
125 {
126 // Fan is missing. If power is on, start the timer.
127 // If power is off, stop a running timer.
128 if (_powerState->isPowerOn())
129 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500130 timer->restartOnce(std::get<seconds>(_fanMissingTimers[fanPath]));
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500131 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500132 else if (timer->isEnabled())
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500133 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500134 timer->setEnabled(false);
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500135 }
136 }
137 else
138 {
139 // Fan is present. Stop a running timer.
Matt Spinler9e9f5992020-09-30 08:29:24 -0500140 if (timer->isEnabled())
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500141 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500142 timer->setEnabled(false);
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500143 }
144 }
145}
146
147void ErrorReporter::fanMissingTimerExpired(const std::string& fanPath)
Matt Spinler39fcd502020-09-24 14:23:11 -0500148{
149 getLogger().log(
150 fmt::format("Creating event log for missing fan {}", fanPath),
151 Logger::error);
152
153 std::map<std::string, std::string> additionalData;
154 additionalData.emplace("_PID", std::to_string(getpid()));
155 additionalData.emplace("CALLOUT_INVENTORY_PATH", fanPath);
156
157 auto severity =
158 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
159 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
160 Error);
161
Matt Spinlerc6745102020-10-22 10:52:02 -0500162 // Save our logs to a temp file and get the file descriptor
Matt Spinler39fcd502020-09-24 14:23:11 -0500163 // so it can be passed in as FFDC data.
164 auto logFile = getLogger().saveToTempFile();
165 util::FileDescriptor fd{-1};
166 fd.open(logFile, O_RDONLY);
167
168 std::vector<std::tuple<
169 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
170 uint8_t, uint8_t, sdbusplus::message::unix_fd>>
171 ffdc;
172
173 ffdc.emplace_back(sdbusplus::xyz::openbmc_project::Logging::server::Create::
Matt Spinlerc6745102020-10-22 10:52:02 -0500174 FFDCFormat::Text,
Matt Spinler39fcd502020-09-24 14:23:11 -0500175 0x01, 0x01, fd());
176
177 try
178 {
179 util::SDBusPlus::lookupAndCallMethod(
180 loggingPath, loggingCreateIface, "CreateWithFFDCFiles",
181 "xyz.openbmc_project.Fan.Error.Missing", severity, additionalData,
182 ffdc);
183 }
184 catch (const util::DBusError& e)
185 {
186 getLogger().log(
187 fmt::format(
188 "Call to create an error log for missing fan {} failed: {}",
189 fanPath, e.what()),
190 Logger::error);
191 fs::remove(logFile);
192 throw;
193 }
194
195 fs::remove(logFile);
196}
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500197
198void ErrorReporter::powerStateChanged(bool powerState)
199{
200 if (powerState)
201 {
202 // If there are fans already missing, log it.
203 auto missing = std::count_if(
204 _fanStates.begin(), _fanStates.end(),
205 [](const auto& fanState) { return fanState.second == false; });
206
207 if (missing)
208 {
209 getLogger().log(
210 fmt::format("At power on, there are {} missing fans", missing));
211 }
212 }
213
214 std::for_each(
215 _fanStates.begin(), _fanStates.end(),
216 [this](const auto& fanState) { this->checkFan(fanState.first); });
217}
218
Matt Spinlere8122392020-09-24 13:22:18 -0500219} // namespace phosphor::fan::presence