blob: b57fa81e3bc114bf8802aebb29b46c71a33fddc3 [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
Matt Spinler0dc85ee2020-09-24 14:07:02 -050023#include <unistd.h>
24
Matt Spinler39fcd502020-09-24 14:23:11 -050025#include <xyz/openbmc_project/Logging/Create/server.hpp>
26#include <xyz/openbmc_project/Logging/Entry/server.hpp>
Matt Spinlere8122392020-09-24 13:22:18 -050027
Patrick Williamsfbf47032023-07-17 12:27:34 -050028#include <format>
29
Matt Spinlere8122392020-09-24 13:22:18 -050030namespace phosphor::fan::presence
31{
32
33using json = nlohmann::json;
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(
Patrick Williamscb356d42022-07-22 19:26:53 -050045 sdbusplus::bus_t& bus,
Matt Spinlere8122392020-09-24 13:22:18 -050046 const std::vector<
47 std::tuple<Fan, std::vector<std::unique_ptr<PresenceSensor>>>>& fans) :
Patrick Williamsdfddd642024-08-16 15:21:51 -040048 _bus(bus), _event(sdeventplus::Event::get_default()),
Matt Spinler76e73c22021-04-21 11:03:05 -050049 _powerState(getPowerStateObject())
Matt Spinlere8122392020-09-24 13:22:18 -050050{
Matt Spinler76e73c22021-04-21 11:03:05 -050051 _powerState->addCallback("errorReporter",
52 std::bind(&ErrorReporter::powerStateChanged, this,
53 std::placeholders::_1));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050054
55 for (const auto& fan : fans)
56 {
Matt Spinler9e9f5992020-09-30 08:29:24 -050057 const auto& fanData = std::get<0>(fan);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050058
Matt Spinler9e9f5992020-09-30 08:29:24 -050059 // Only deal with fans that have an error time defined.
60 if (std::get<std::optional<size_t>>(fanData))
61 {
62 auto path = invPrefix + std::get<1>(fanData);
Matt Spinler0dc85ee2020-09-24 14:07:02 -050063
Matt Spinler9e9f5992020-09-30 08:29:24 -050064 // Register for fan presence changes, get their initial states,
65 // and create the fan missing timers.
Matt Spinler0dc85ee2020-09-24 14:07:02 -050066
Matt Spinler9e9f5992020-09-30 08:29:24 -050067 _matches.emplace_back(
68 _bus, rules::propertiesChanged(path, itemIface),
69 std::bind(std::mem_fn(&ErrorReporter::presenceChanged), this,
70 std::placeholders::_1));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050071
Matt Spinler9e9f5992020-09-30 08:29:24 -050072 _fanStates.emplace(path, getPresence(fanData));
Matt Spinler0dc85ee2020-09-24 14:07:02 -050073
Matt Spinler9e9f5992020-09-30 08:29:24 -050074 auto timer = std::make_unique<
75 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
76 _event,
77 std::bind(std::mem_fn(&ErrorReporter::fanMissingTimerExpired),
78 this, path));
79
80 seconds errorTime{std::get<std::optional<size_t>>(fanData).value()};
81
82 _fanMissingTimers.emplace(
83 path, std::make_tuple(std::move(timer), std::move(errorTime)));
84 }
Matt Spinler0dc85ee2020-09-24 14:07:02 -050085 }
86
87 // If power is already on, check for currently missing fans.
88 if (_powerState->isPowerOn())
89 {
90 powerStateChanged(true);
91 }
Matt Spinlere8122392020-09-24 13:22:18 -050092}
93
Patrick Williamscb356d42022-07-22 19:26:53 -050094void ErrorReporter::presenceChanged(sdbusplus::message_t& msg)
Matt Spinler0dc85ee2020-09-24 14:07:02 -050095{
96 bool present;
97 auto fanPath = msg.get_path();
98 std::string interface;
99 std::map<std::string, std::variant<bool>> properties;
100
101 msg.read(interface, properties);
102
103 auto presentProp = properties.find("Present");
104 if (presentProp != properties.end())
105 {
106 present = std::get<bool>(presentProp->second);
107 if (_fanStates[fanPath] != present)
108 {
Patrick Williamsfbf47032023-07-17 12:27:34 -0500109 getLogger().log(std::format("Fan {} presence state change to {}",
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500110 fanPath, present));
111
112 _fanStates[fanPath] = present;
113 checkFan(fanPath);
114 }
115 }
116}
117
118void ErrorReporter::checkFan(const std::string& fanPath)
119{
Matt Spinler9e9f5992020-09-30 08:29:24 -0500120 auto& timer = std::get<0>(_fanMissingTimers[fanPath]);
121
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500122 if (!_fanStates[fanPath])
123 {
124 // Fan is missing. If power is on, start the timer.
125 // If power is off, stop a running timer.
126 if (_powerState->isPowerOn())
127 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500128 timer->restartOnce(std::get<seconds>(_fanMissingTimers[fanPath]));
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500129 }
Matt Spinler9e9f5992020-09-30 08:29:24 -0500130 else if (timer->isEnabled())
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500131 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500132 timer->setEnabled(false);
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500133 }
134 }
135 else
136 {
137 // Fan is present. Stop a running timer.
Matt Spinler9e9f5992020-09-30 08:29:24 -0500138 if (timer->isEnabled())
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500139 {
Matt Spinler9e9f5992020-09-30 08:29:24 -0500140 timer->setEnabled(false);
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500141 }
142 }
143}
144
145void ErrorReporter::fanMissingTimerExpired(const std::string& fanPath)
Matt Spinler39fcd502020-09-24 14:23:11 -0500146{
147 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500148 std::format("Creating event log for missing fan {}", fanPath),
Matt Spinler39fcd502020-09-24 14:23:11 -0500149 Logger::error);
150
151 std::map<std::string, std::string> additionalData;
152 additionalData.emplace("_PID", std::to_string(getpid()));
153 additionalData.emplace("CALLOUT_INVENTORY_PATH", fanPath);
154
155 auto severity =
156 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
157 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
158 Error);
159
Matt Spinlerc6745102020-10-22 10:52:02 -0500160 // Save our logs to a temp file and get the file descriptor
Matt Spinler39fcd502020-09-24 14:23:11 -0500161 // so it can be passed in as FFDC data.
162 auto logFile = getLogger().saveToTempFile();
163 util::FileDescriptor fd{-1};
164 fd.open(logFile, O_RDONLY);
165
166 std::vector<std::tuple<
167 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat,
168 uint8_t, uint8_t, sdbusplus::message::unix_fd>>
169 ffdc;
170
171 ffdc.emplace_back(sdbusplus::xyz::openbmc_project::Logging::server::Create::
Matt Spinlerc6745102020-10-22 10:52:02 -0500172 FFDCFormat::Text,
Matt Spinler39fcd502020-09-24 14:23:11 -0500173 0x01, 0x01, fd());
174
175 try
176 {
177 util::SDBusPlus::lookupAndCallMethod(
178 loggingPath, loggingCreateIface, "CreateWithFFDCFiles",
179 "xyz.openbmc_project.Fan.Error.Missing", severity, additionalData,
180 ffdc);
181 }
182 catch (const util::DBusError& e)
183 {
184 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500185 std::format(
Matt Spinler39fcd502020-09-24 14:23:11 -0500186 "Call to create an error log for missing fan {} failed: {}",
187 fanPath, e.what()),
188 Logger::error);
189 fs::remove(logFile);
190 throw;
191 }
192
193 fs::remove(logFile);
194}
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500195
196void ErrorReporter::powerStateChanged(bool powerState)
197{
198 if (powerState)
199 {
200 // If there are fans already missing, log it.
Patrick Williams5e15c3b2023-10-20 11:18:11 -0500201 auto missing = std::count_if(
202 _fanStates.begin(), _fanStates.end(),
203 [](const auto& fanState) { return fanState.second == false; });
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500204
205 if (missing)
206 {
207 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500208 std::format("At power on, there are {} missing fans", missing));
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500209 }
210 }
211
Patrick Williamsdfddd642024-08-16 15:21:51 -0400212 std::for_each(_fanStates.begin(), _fanStates.end(),
213 [this](const auto& fanState) {
214 this->checkFan(fanState.first);
215 });
Matt Spinler0dc85ee2020-09-24 14:07:02 -0500216}
217
Matt Spinlere8122392020-09-24 13:22:18 -0500218} // namespace phosphor::fan::presence