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 "error_reporter.hpp" 17 18 #include "get_power_state.hpp" 19 #include "logging.hpp" 20 #include "psensor.hpp" 21 #include "utility.hpp" 22 23 #include <fmt/format.h> 24 #include <unistd.h> 25 26 #include <phosphor-logging/log.hpp> 27 #include <xyz/openbmc_project/Logging/Create/server.hpp> 28 #include <xyz/openbmc_project/Logging/Entry/server.hpp> 29 30 namespace phosphor::fan::presence 31 { 32 33 using json = nlohmann::json; 34 using namespace phosphor::logging; 35 using namespace sdbusplus::bus::match; 36 using namespace std::literals::string_literals; 37 using namespace std::chrono; 38 namespace fs = std::filesystem; 39 40 const auto itemIface = "xyz.openbmc_project.Inventory.Item"s; 41 const auto invPrefix = "/xyz/openbmc_project/inventory"s; 42 const auto loggingPath = "/xyz/openbmc_project/logging"; 43 const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create"; 44 45 ErrorReporter::ErrorReporter( 46 sdbusplus::bus::bus& bus, 47 const std::vector< 48 std::tuple<Fan, std::vector<std::unique_ptr<PresenceSensor>>>>& fans) : 49 _bus(bus), 50 _event(sdeventplus::Event::get_default()), 51 _powerState(getPowerStateObject()) 52 { 53 _powerState->addCallback("errorReporter", 54 std::bind(&ErrorReporter::powerStateChanged, this, 55 std::placeholders::_1)); 56 57 for (const auto& fan : fans) 58 { 59 const auto& fanData = std::get<0>(fan); 60 61 // 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); 65 66 // Register for fan presence changes, get their initial states, 67 // and create the fan missing timers. 68 69 _matches.emplace_back( 70 _bus, rules::propertiesChanged(path, itemIface), 71 std::bind(std::mem_fn(&ErrorReporter::presenceChanged), this, 72 std::placeholders::_1)); 73 74 _fanStates.emplace(path, getPresence(fanData)); 75 76 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 } 87 } 88 89 // If power is already on, check for currently missing fans. 90 if (_powerState->isPowerOn()) 91 { 92 powerStateChanged(true); 93 } 94 } 95 96 void 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 120 void ErrorReporter::checkFan(const std::string& fanPath) 121 { 122 auto& timer = std::get<0>(_fanMissingTimers[fanPath]); 123 124 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 { 130 timer->restartOnce(std::get<seconds>(_fanMissingTimers[fanPath])); 131 } 132 else if (timer->isEnabled()) 133 { 134 timer->setEnabled(false); 135 } 136 } 137 else 138 { 139 // Fan is present. Stop a running timer. 140 if (timer->isEnabled()) 141 { 142 timer->setEnabled(false); 143 } 144 } 145 } 146 147 void ErrorReporter::fanMissingTimerExpired(const std::string& fanPath) 148 { 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 162 // Save our logs to a temp file and get the file descriptor 163 // 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:: 174 FFDCFormat::Text, 175 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 } 197 198 void 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 219 } // namespace phosphor::fan::presence 220