xref: /openbmc/phosphor-fan-presence/sensor-monitor/shutdown_alarm_monitor.cpp (revision 32c4feff9b1cd1fd4d75bb530761f70f0e5922e0)
19c0715b6SMatt Spinler /**
29c0715b6SMatt Spinler  * Copyright © 2021 IBM Corporation
39c0715b6SMatt Spinler  *
49c0715b6SMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
59c0715b6SMatt Spinler  * you may not use this file except in compliance with the License.
69c0715b6SMatt Spinler  * You may obtain a copy of the License at
79c0715b6SMatt Spinler  *
89c0715b6SMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
99c0715b6SMatt Spinler  *
109c0715b6SMatt Spinler  * Unless required by applicable law or agreed to in writing, software
119c0715b6SMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
129c0715b6SMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139c0715b6SMatt Spinler  * See the License for the specific language governing permissions and
149c0715b6SMatt Spinler  * limitations under the License.
159c0715b6SMatt Spinler  */
169c0715b6SMatt Spinler #include "config.h"
179c0715b6SMatt Spinler 
189c0715b6SMatt Spinler #include "shutdown_alarm_monitor.hpp"
199c0715b6SMatt Spinler 
203efec61cSMatt Spinler #include <unistd.h>
219c0715b6SMatt Spinler 
22*32c4feffSAnwaar Hadi #include <phosphor-logging/lg2.hpp>
23ffd54674SMatt Spinler #include <xyz/openbmc_project/Logging/Entry/server.hpp>
249c0715b6SMatt Spinler 
259c0715b6SMatt Spinler namespace sensor::monitor
269c0715b6SMatt Spinler {
279c0715b6SMatt Spinler using namespace phosphor::fan::util;
289c0715b6SMatt Spinler using namespace phosphor::fan;
299c0715b6SMatt Spinler namespace fs = std::filesystem;
309c0715b6SMatt Spinler 
319c0715b6SMatt Spinler const std::map<ShutdownType, std::string> shutdownInterfaces{
329c0715b6SMatt Spinler     {ShutdownType::hard, "xyz.openbmc_project.Sensor.Threshold.HardShutdown"},
339c0715b6SMatt Spinler     {ShutdownType::soft, "xyz.openbmc_project.Sensor.Threshold.SoftShutdown"}};
349c0715b6SMatt Spinler 
359c0715b6SMatt Spinler const std::map<ShutdownType, std::map<AlarmType, std::string>> alarmProperties{
369c0715b6SMatt Spinler     {ShutdownType::hard,
379c0715b6SMatt Spinler      {{AlarmType::low, "HardShutdownAlarmLow"},
389c0715b6SMatt Spinler       {AlarmType::high, "HardShutdownAlarmHigh"}}},
399c0715b6SMatt Spinler     {ShutdownType::soft,
409c0715b6SMatt Spinler      {{AlarmType::low, "SoftShutdownAlarmLow"},
419c0715b6SMatt Spinler       {AlarmType::high, "SoftShutdownAlarmHigh"}}}};
429c0715b6SMatt Spinler 
430b839516SMatt Spinler const std::map<ShutdownType, std::chrono::milliseconds> shutdownDelays{
440b839516SMatt Spinler     {ShutdownType::hard,
450b839516SMatt Spinler      std::chrono::milliseconds{SHUTDOWN_ALARM_HARD_SHUTDOWN_DELAY_MS}},
460b839516SMatt Spinler     {ShutdownType::soft,
470b839516SMatt Spinler      std::chrono::milliseconds{SHUTDOWN_ALARM_SOFT_SHUTDOWN_DELAY_MS}}};
480b839516SMatt Spinler 
49ffd54674SMatt Spinler const std::map<ShutdownType, std::map<AlarmType, std::string>> alarmEventLogs{
50ffd54674SMatt Spinler     {ShutdownType::hard,
51ffd54674SMatt Spinler      {{AlarmType::high,
52ffd54674SMatt Spinler        "xyz.openbmc_project.Sensor.Threshold.Error.HardShutdownAlarmHigh"},
53ffd54674SMatt Spinler       {AlarmType::low, "xyz.openbmc_project.Sensor.Threshold.Error."
54ffd54674SMatt Spinler                        "HardShutdownAlarmLow"}}},
55ffd54674SMatt Spinler     {ShutdownType::soft,
56ffd54674SMatt Spinler      {{AlarmType::high,
57ffd54674SMatt Spinler        "xyz.openbmc_project.Sensor.Threshold.Error.SoftShutdownAlarmHigh"},
58ffd54674SMatt Spinler       {AlarmType::low, "xyz.openbmc_project.Sensor.Threshold.Error."
59ffd54674SMatt Spinler                        "SoftShutdownAlarmLow"}}}};
60ffd54674SMatt Spinler 
61ffd54674SMatt Spinler const std::map<ShutdownType, std::map<AlarmType, std::string>>
62ffd54674SMatt Spinler     alarmClearEventLogs{
63ffd54674SMatt Spinler         {ShutdownType::hard,
64ffd54674SMatt Spinler          {{AlarmType::high, "xyz.openbmc_project.Sensor.Threshold.Error."
65ffd54674SMatt Spinler                             "HardShutdownAlarmHighClear"},
66ffd54674SMatt Spinler           {AlarmType::low, "xyz.openbmc_project.Sensor.Threshold.Error."
67ffd54674SMatt Spinler                            "HardShutdownAlarmLowClear"}}},
68ffd54674SMatt Spinler         {ShutdownType::soft,
69ffd54674SMatt Spinler          {{AlarmType::high, "xyz.openbmc_project.Sensor.Threshold.Error."
70ffd54674SMatt Spinler                             "SoftShutdownAlarmHighClear"},
71ffd54674SMatt Spinler           {AlarmType::low, "xyz.openbmc_project.Sensor.Threshold.Error."
72ffd54674SMatt Spinler                            "SoftShutdownAlarmLowClear"}}}};
73ffd54674SMatt Spinler 
740b839516SMatt Spinler constexpr auto systemdService = "org.freedesktop.systemd1";
750b839516SMatt Spinler constexpr auto systemdPath = "/org/freedesktop/systemd1";
760b839516SMatt Spinler constexpr auto systemdMgrIface = "org.freedesktop.systemd1.Manager";
770b839516SMatt Spinler constexpr auto valueInterface = "xyz.openbmc_project.Sensor.Value";
780b839516SMatt Spinler constexpr auto valueProperty = "Value";
79ffd54674SMatt Spinler const auto loggingService = "xyz.openbmc_project.Logging";
80ffd54674SMatt Spinler const auto loggingPath = "/xyz/openbmc_project/logging";
81ffd54674SMatt Spinler const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create";
820b839516SMatt Spinler 
839c0715b6SMatt Spinler using namespace sdbusplus::bus::match;
849c0715b6SMatt Spinler 
ShutdownAlarmMonitor(sdbusplus::bus_t & bus,sdeventplus::Event & event,std::shared_ptr<PowerState> powerState)857f6946b2SMatt Spinler ShutdownAlarmMonitor::ShutdownAlarmMonitor(
86cb356d48SPatrick Williams     sdbusplus::bus_t& bus, sdeventplus::Event& event,
877f6946b2SMatt Spinler     std::shared_ptr<PowerState> powerState) :
88dfddd648SPatrick Williams     bus(bus), event(event), _powerState(std::move(powerState)),
899c0715b6SMatt Spinler     hardShutdownMatch(bus,
909c0715b6SMatt Spinler                       "type='signal',member='PropertiesChanged',"
919c0715b6SMatt Spinler                       "path_namespace='/xyz/openbmc_project/sensors',"
929c0715b6SMatt Spinler                       "arg0='" +
93d914ebf4SMatthew Barth                           shutdownInterfaces.at(ShutdownType::hard) + "'",
949c0715b6SMatt Spinler                       std::bind(&ShutdownAlarmMonitor::propertiesChanged, this,
959c0715b6SMatt Spinler                                 std::placeholders::_1)),
969c0715b6SMatt Spinler     softShutdownMatch(bus,
979c0715b6SMatt Spinler                       "type='signal',member='PropertiesChanged',"
989c0715b6SMatt Spinler                       "path_namespace='/xyz/openbmc_project/sensors',"
999c0715b6SMatt Spinler                       "arg0='" +
100d914ebf4SMatthew Barth                           shutdownInterfaces.at(ShutdownType::soft) + "'",
1019c0715b6SMatt Spinler                       std::bind(&ShutdownAlarmMonitor::propertiesChanged, this,
1027f6946b2SMatt Spinler                                 std::placeholders::_1))
1039c0715b6SMatt Spinler {
1047f6946b2SMatt Spinler     _powerState->addCallback("shutdownMon",
1057f6946b2SMatt Spinler                              std::bind(&ShutdownAlarmMonitor::powerStateChanged,
1067f6946b2SMatt Spinler                                        this, std::placeholders::_1));
1079c0715b6SMatt Spinler     findAlarms();
1089c0715b6SMatt Spinler 
1099c0715b6SMatt Spinler     if (_powerState->isPowerOn())
1109c0715b6SMatt Spinler     {
1119c0715b6SMatt Spinler         checkAlarms();
11208a66efaSMatt Spinler 
11308a66efaSMatt Spinler         // Get rid of any previous saved timestamps that don't
11408a66efaSMatt Spinler         // apply anymore.
11508a66efaSMatt Spinler         timestamps.prune(alarms);
11608a66efaSMatt Spinler     }
11708a66efaSMatt Spinler     else
11808a66efaSMatt Spinler     {
11908a66efaSMatt Spinler         timestamps.clear();
1209c0715b6SMatt Spinler     }
1219c0715b6SMatt Spinler }
1229c0715b6SMatt Spinler 
findAlarms()1239c0715b6SMatt Spinler void ShutdownAlarmMonitor::findAlarms()
1249c0715b6SMatt Spinler {
1259c0715b6SMatt Spinler     // Find all shutdown threshold ifaces currently on D-Bus.
1269c0715b6SMatt Spinler     for (const auto& [shutdownType, interface] : shutdownInterfaces)
1279c0715b6SMatt Spinler     {
1289c0715b6SMatt Spinler         auto paths = SDBusPlus::getSubTreePathsRaw(bus, "/", interface, 0);
1299c0715b6SMatt Spinler 
130808d7fe8SMike Capps         auto shutdownType2 = shutdownType;
131808d7fe8SMike Capps 
132dfddd648SPatrick Williams         std::for_each(
133dfddd648SPatrick Williams             paths.begin(), paths.end(),
134808d7fe8SMike Capps             [this, shutdownType2](const auto& path) {
135808d7fe8SMike Capps                 alarms.emplace(AlarmKey{path, shutdownType2, AlarmType::high},
1369c0715b6SMatt Spinler                                nullptr);
137808d7fe8SMike Capps                 alarms.emplace(AlarmKey{path, shutdownType2, AlarmType::low},
1389c0715b6SMatt Spinler                                nullptr);
1399c0715b6SMatt Spinler             });
1409c0715b6SMatt Spinler     }
1419c0715b6SMatt Spinler }
1429c0715b6SMatt Spinler 
checkAlarms()1439c0715b6SMatt Spinler void ShutdownAlarmMonitor::checkAlarms()
1449c0715b6SMatt Spinler {
1459c0715b6SMatt Spinler     for (auto& [alarmKey, timer] : alarms)
1469c0715b6SMatt Spinler     {
1479c0715b6SMatt Spinler         const auto& [sensorPath, shutdownType, alarmType] = alarmKey;
1489c0715b6SMatt Spinler         const auto& interface = shutdownInterfaces.at(shutdownType);
1499c0715b6SMatt Spinler         auto propertyName = alarmProperties.at(shutdownType).at(alarmType);
1509c0715b6SMatt Spinler         bool value;
1519c0715b6SMatt Spinler 
1529c0715b6SMatt Spinler         try
1539c0715b6SMatt Spinler         {
1549c0715b6SMatt Spinler             value = SDBusPlus::getProperty<bool>(bus, sensorPath, interface,
1559c0715b6SMatt Spinler                                                  propertyName);
1569c0715b6SMatt Spinler         }
1579c0715b6SMatt Spinler         catch (const DBusServiceError& e)
1589c0715b6SMatt Spinler         {
1599c0715b6SMatt Spinler             // The sensor isn't on D-Bus anymore
160*32c4feffSAnwaar Hadi             lg2::info("No {INTERFACE} interface on {SENSOR_PATH} anymore.",
161*32c4feffSAnwaar Hadi                       "INTERFACE", interface, "SENSOR_PATH", sensorPath);
1629c0715b6SMatt Spinler             continue;
1639c0715b6SMatt Spinler         }
1649c0715b6SMatt Spinler 
1659c0715b6SMatt Spinler         checkAlarm(value, alarmKey);
1669c0715b6SMatt Spinler     }
1679c0715b6SMatt Spinler }
1689c0715b6SMatt Spinler 
propertiesChanged(sdbusplus::message_t & message)169cb356d48SPatrick Williams void ShutdownAlarmMonitor::propertiesChanged(sdbusplus::message_t& message)
1709c0715b6SMatt Spinler {
17116c7dc1eSMatt Spinler     std::map<std::string, std::variant<bool>> properties;
17216c7dc1eSMatt Spinler     std::string interface;
17316c7dc1eSMatt Spinler 
17416c7dc1eSMatt Spinler     if (!_powerState->isPowerOn())
17516c7dc1eSMatt Spinler     {
17616c7dc1eSMatt Spinler         return;
17716c7dc1eSMatt Spinler     }
17816c7dc1eSMatt Spinler 
17916c7dc1eSMatt Spinler     message.read(interface, properties);
18016c7dc1eSMatt Spinler 
18116c7dc1eSMatt Spinler     auto type = getShutdownType(interface);
18216c7dc1eSMatt Spinler     if (!type)
18316c7dc1eSMatt Spinler     {
18416c7dc1eSMatt Spinler         return;
18516c7dc1eSMatt Spinler     }
18616c7dc1eSMatt Spinler 
18716c7dc1eSMatt Spinler     std::string sensorPath = message.get_path();
18816c7dc1eSMatt Spinler 
18916c7dc1eSMatt Spinler     const auto& lowAlarmName = alarmProperties.at(*type).at(AlarmType::low);
19016c7dc1eSMatt Spinler     if (properties.count(lowAlarmName) > 0)
19116c7dc1eSMatt Spinler     {
19216c7dc1eSMatt Spinler         AlarmKey alarmKey{sensorPath, *type, AlarmType::low};
19316c7dc1eSMatt Spinler         auto alarm = alarms.find(alarmKey);
19416c7dc1eSMatt Spinler         if (alarm == alarms.end())
19516c7dc1eSMatt Spinler         {
19616c7dc1eSMatt Spinler             alarms.emplace(alarmKey, nullptr);
19716c7dc1eSMatt Spinler         }
19816c7dc1eSMatt Spinler         checkAlarm(std::get<bool>(properties.at(lowAlarmName)), alarmKey);
19916c7dc1eSMatt Spinler     }
20016c7dc1eSMatt Spinler 
20116c7dc1eSMatt Spinler     const auto& highAlarmName = alarmProperties.at(*type).at(AlarmType::high);
20216c7dc1eSMatt Spinler     if (properties.count(highAlarmName) > 0)
20316c7dc1eSMatt Spinler     {
20416c7dc1eSMatt Spinler         AlarmKey alarmKey{sensorPath, *type, AlarmType::high};
20516c7dc1eSMatt Spinler         auto alarm = alarms.find(alarmKey);
20616c7dc1eSMatt Spinler         if (alarm == alarms.end())
20716c7dc1eSMatt Spinler         {
20816c7dc1eSMatt Spinler             alarms.emplace(alarmKey, nullptr);
20916c7dc1eSMatt Spinler         }
21016c7dc1eSMatt Spinler         checkAlarm(std::get<bool>(properties.at(highAlarmName)), alarmKey);
21116c7dc1eSMatt Spinler     }
2129c0715b6SMatt Spinler }
2139c0715b6SMatt Spinler 
checkAlarm(bool value,const AlarmKey & alarmKey)2149c0715b6SMatt Spinler void ShutdownAlarmMonitor::checkAlarm(bool value, const AlarmKey& alarmKey)
2159c0715b6SMatt Spinler {
2160b839516SMatt Spinler     auto alarm = alarms.find(alarmKey);
2170b839516SMatt Spinler     if (alarm == alarms.end())
2180b839516SMatt Spinler     {
2190b839516SMatt Spinler         return;
2200b839516SMatt Spinler     }
2210b839516SMatt Spinler 
2220b839516SMatt Spinler     // Start or stop the timer if necessary.
2230b839516SMatt Spinler     auto& timer = alarm->second;
2240b839516SMatt Spinler     if (value)
2250b839516SMatt Spinler     {
2260b839516SMatt Spinler         if (!timer)
2270b839516SMatt Spinler         {
2280b839516SMatt Spinler             startTimer(alarmKey);
2290b839516SMatt Spinler         }
2300b839516SMatt Spinler     }
2310b839516SMatt Spinler     else
2320b839516SMatt Spinler     {
2330b839516SMatt Spinler         if (timer)
2340b839516SMatt Spinler         {
2350b839516SMatt Spinler             stopTimer(alarmKey);
2360b839516SMatt Spinler         }
2370b839516SMatt Spinler     }
2380b839516SMatt Spinler }
2390b839516SMatt Spinler 
startTimer(const AlarmKey & alarmKey)2400b839516SMatt Spinler void ShutdownAlarmMonitor::startTimer(const AlarmKey& alarmKey)
2410b839516SMatt Spinler {
2420b839516SMatt Spinler     const auto& [sensorPath, shutdownType, alarmType] = alarmKey;
2430b839516SMatt Spinler     const auto& propertyName = alarmProperties.at(shutdownType).at(alarmType);
2440b839516SMatt Spinler     std::chrono::milliseconds shutdownDelay{shutdownDelays.at(shutdownType)};
2450b839516SMatt Spinler     std::optional<double> value;
2460b839516SMatt Spinler 
2470b839516SMatt Spinler     auto alarm = alarms.find(alarmKey);
2480b839516SMatt Spinler     if (alarm == alarms.end())
2490b839516SMatt Spinler     {
2500b839516SMatt Spinler         throw std::runtime_error("Couldn't find alarm inside startTimer");
2510b839516SMatt Spinler     }
2520b839516SMatt Spinler 
2530b839516SMatt Spinler     try
2540b839516SMatt Spinler     {
2550b839516SMatt Spinler         value = SDBusPlus::getProperty<double>(bus, sensorPath, valueInterface,
2560b839516SMatt Spinler                                                valueProperty);
2570b839516SMatt Spinler     }
2580b839516SMatt Spinler     catch (const DBusServiceError& e)
2590b839516SMatt Spinler     {
2600b839516SMatt Spinler         // If the sensor was just added, the Value interface for it may
2610b839516SMatt Spinler         // not be in the mapper yet.  This could only happen if the sensor
2620b839516SMatt Spinler         // application was started with power up and the value exceeded the
2630b839516SMatt Spinler         // threshold immediately.
2640b839516SMatt Spinler     }
2650b839516SMatt Spinler 
266ffd54674SMatt Spinler     createEventLog(alarmKey, true, value);
267ffd54674SMatt Spinler 
26808a66efaSMatt Spinler     uint64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
26908a66efaSMatt Spinler                        std::chrono::system_clock::now().time_since_epoch())
27008a66efaSMatt Spinler                        .count();
27108a66efaSMatt Spinler 
27208a66efaSMatt Spinler     // If there is a saved timestamp for this timer, then we were restarted
27308a66efaSMatt Spinler     // while the timer was running.  Calculate the remaining time to use
27408a66efaSMatt Spinler     // for the timer.
27508a66efaSMatt Spinler     auto previousStartTime = timestamps.get().find(alarmKey);
27608a66efaSMatt Spinler     if (previousStartTime != timestamps.get().end())
27708a66efaSMatt Spinler     {
27808a66efaSMatt Spinler         const uint64_t& original = previousStartTime->second;
27908a66efaSMatt Spinler 
280*32c4feffSAnwaar Hadi         lg2::info("Found previously running {PROPERTY_NAME} timer "
281*32c4feffSAnwaar Hadi                   "for {SENSOR_PATH} with start time {START_TIME}",
282*32c4feffSAnwaar Hadi                   "PROPERTY_NAME", propertyName, "SENSOR_PATH", sensorPath,
283*32c4feffSAnwaar Hadi                   "START_TIME", original);
28408a66efaSMatt Spinler         // Sanity check it isn't total garbage.
28508a66efaSMatt Spinler         if (now > original)
28608a66efaSMatt Spinler         {
28708a66efaSMatt Spinler             uint64_t remainingTime = 0;
28808a66efaSMatt Spinler             auto elapsedTime = now - original;
28908a66efaSMatt Spinler 
29008a66efaSMatt Spinler             if (elapsedTime < static_cast<uint64_t>(shutdownDelay.count()))
29108a66efaSMatt Spinler             {
29261b73296SPatrick Williams                 remainingTime = static_cast<uint64_t>(shutdownDelay.count()) -
29361b73296SPatrick Williams                                 elapsedTime;
29408a66efaSMatt Spinler             }
29508a66efaSMatt Spinler 
29608a66efaSMatt Spinler             shutdownDelay = std::chrono::milliseconds{remainingTime};
29708a66efaSMatt Spinler         }
29808a66efaSMatt Spinler         else
29908a66efaSMatt Spinler         {
300*32c4feffSAnwaar Hadi             lg2::warning(
301*32c4feffSAnwaar Hadi                 "Restarting {PROPERTY_NAME} shutdown timer for {SENSOR_PATH} for full "
302*32c4feffSAnwaar Hadi                 "time because saved time {SAVED_TIME} is after current time {CURRENT_TIME}",
303*32c4feffSAnwaar Hadi                 "PROPERTY_NAME", propertyName, "SENSOR_PATH", sensorPath,
304*32c4feffSAnwaar Hadi                 "SAVED_TIME", original, "CURRENT_TIME", now);
30508a66efaSMatt Spinler         }
30608a66efaSMatt Spinler     }
30708a66efaSMatt Spinler 
308*32c4feffSAnwaar Hadi     lg2::info(
309*32c4feffSAnwaar Hadi         "Starting {TIME}ms {PROPERTY_NAME} shutdown timer due to sensor {SENSOR_PATH} value {VALUE}",
310*32c4feffSAnwaar Hadi         "TIME", shutdownDelay.count(), "PROPERTY_NAME", propertyName,
311*32c4feffSAnwaar Hadi         "SENSOR_PATH", sensorPath, "VALUE", *value);
3120b839516SMatt Spinler 
3130b839516SMatt Spinler     auto& timer = alarm->second;
3140b839516SMatt Spinler 
3150b839516SMatt Spinler     timer = std::make_unique<
3160b839516SMatt Spinler         sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
3170b839516SMatt Spinler         event, std::bind(&ShutdownAlarmMonitor::timerExpired, this, alarmKey));
3180b839516SMatt Spinler 
3190b839516SMatt Spinler     timer->restartOnce(shutdownDelay);
32008a66efaSMatt Spinler 
32108a66efaSMatt Spinler     // Note that if this key is already in the timestamps map because
32208a66efaSMatt Spinler     // the timer was already running the timestamp wil not be updated.
32308a66efaSMatt Spinler     timestamps.add(alarmKey, now);
3240b839516SMatt Spinler }
3250b839516SMatt Spinler 
stopTimer(const AlarmKey & alarmKey)3260b839516SMatt Spinler void ShutdownAlarmMonitor::stopTimer(const AlarmKey& alarmKey)
3270b839516SMatt Spinler {
3280b839516SMatt Spinler     const auto& [sensorPath, shutdownType, alarmType] = alarmKey;
3290b839516SMatt Spinler     const auto& propertyName = alarmProperties.at(shutdownType).at(alarmType);
3300b839516SMatt Spinler 
3310b839516SMatt Spinler     auto value = SDBusPlus::getProperty<double>(bus, sensorPath, valueInterface,
3320b839516SMatt Spinler                                                 valueProperty);
3330b839516SMatt Spinler 
3340b839516SMatt Spinler     auto alarm = alarms.find(alarmKey);
3350b839516SMatt Spinler     if (alarm == alarms.end())
3360b839516SMatt Spinler     {
3370b839516SMatt Spinler         throw std::runtime_error("Couldn't find alarm inside stopTimer");
3380b839516SMatt Spinler     }
3390b839516SMatt Spinler 
340ffd54674SMatt Spinler     createEventLog(alarmKey, false, value);
341ffd54674SMatt Spinler 
342*32c4feffSAnwaar Hadi     lg2::info(
343*32c4feffSAnwaar Hadi         "Stopping {PROPERTY_NAME} shutdown timer due to sensor {SENSOR_PATH} value {VALUE}",
344*32c4feffSAnwaar Hadi         "PROPERTY_NAME", propertyName, "SENSOR_PATH", sensorPath, "VALUE",
345*32c4feffSAnwaar Hadi         value);
3460b839516SMatt Spinler     auto& timer = alarm->second;
3470b839516SMatt Spinler     timer->setEnabled(false);
3480b839516SMatt Spinler     timer.reset();
34908a66efaSMatt Spinler 
35008a66efaSMatt Spinler     timestamps.erase(alarmKey);
3510b839516SMatt Spinler }
3520b839516SMatt Spinler 
createBmcDump() const353683a96c6SMike Capps void ShutdownAlarmMonitor::createBmcDump() const
354683a96c6SMike Capps {
355683a96c6SMike Capps     try
356683a96c6SMike Capps     {
357683a96c6SMike Capps         util::SDBusPlus::callMethod(
358683a96c6SMike Capps             "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc",
359683a96c6SMike Capps             "xyz.openbmc_project.Dump.Create", "CreateDump",
360683a96c6SMike Capps             std::vector<
361683a96c6SMike Capps                 std::pair<std::string, std::variant<std::string, uint64_t>>>());
362683a96c6SMike Capps     }
363477b13bdSMike Capps     catch (const std::exception& e)
364477b13bdSMike Capps     {
365*32c4feffSAnwaar Hadi         lg2::error("Caught exception while creating BMC dump: {ERROR}", "ERROR",
366*32c4feffSAnwaar Hadi                    e);
367477b13bdSMike Capps     }
368683a96c6SMike Capps }
369683a96c6SMike Capps 
timerExpired(const AlarmKey & alarmKey)3700b839516SMatt Spinler void ShutdownAlarmMonitor::timerExpired(const AlarmKey& alarmKey)
3710b839516SMatt Spinler {
3720b839516SMatt Spinler     const auto& [sensorPath, shutdownType, alarmType] = alarmKey;
3730b839516SMatt Spinler     const auto& propertyName = alarmProperties.at(shutdownType).at(alarmType);
3740b839516SMatt Spinler 
375ffd54674SMatt Spinler     auto value = SDBusPlus::getProperty<double>(bus, sensorPath, valueInterface,
376ffd54674SMatt Spinler                                                 valueProperty);
377ffd54674SMatt Spinler 
378*32c4feffSAnwaar Hadi     lg2::error(
379*32c4feffSAnwaar Hadi         "The {PROPERTY_NAME} shutdown timer expired for sensor {SENSOR_PATH}, shutting down",
380*32c4feffSAnwaar Hadi         "PROPERTY_NAME", propertyName, "SENSOR_PATH", sensorPath);
3810b839516SMatt Spinler 
382ffd54674SMatt Spinler     // Re-send the event log.  If someone didn't want this it could be
383ffd54674SMatt Spinler     // wrapped by a compile option.
3842a6b39c8SMatt Spinler     createEventLog(alarmKey, true, value, true);
385ffd54674SMatt Spinler 
3860b839516SMatt Spinler     SDBusPlus::callMethod(systemdService, systemdPath, systemdMgrIface,
3870b839516SMatt Spinler                           "StartUnit", "obmc-chassis-hard-poweroff@0.target",
3880b839516SMatt Spinler                           "replace");
38908a66efaSMatt Spinler 
39008a66efaSMatt Spinler     timestamps.erase(alarmKey);
391683a96c6SMike Capps     createBmcDump();
3929c0715b6SMatt Spinler }
3939c0715b6SMatt Spinler 
powerStateChanged(bool powerStateOn)3949c0715b6SMatt Spinler void ShutdownAlarmMonitor::powerStateChanged(bool powerStateOn)
3959c0715b6SMatt Spinler {
3969c0715b6SMatt Spinler     if (powerStateOn)
3979c0715b6SMatt Spinler     {
3989c0715b6SMatt Spinler         checkAlarms();
3999c0715b6SMatt Spinler     }
4009c0715b6SMatt Spinler     else
4019c0715b6SMatt Spinler     {
40208a66efaSMatt Spinler         timestamps.clear();
40308a66efaSMatt Spinler 
4049c0715b6SMatt Spinler         // Cancel and delete all timers
4050b839516SMatt Spinler         std::for_each(alarms.begin(), alarms.end(), [](auto& alarm) {
4060b839516SMatt Spinler             auto& timer = alarm.second;
4070b839516SMatt Spinler             if (timer)
4080b839516SMatt Spinler             {
4090b839516SMatt Spinler                 timer->setEnabled(false);
4100b839516SMatt Spinler                 timer.reset();
4110b839516SMatt Spinler             }
4120b839516SMatt Spinler         });
4139c0715b6SMatt Spinler     }
4149c0715b6SMatt Spinler }
4159c0715b6SMatt Spinler 
createEventLog(const AlarmKey & alarmKey,bool alarmValue,const std::optional<double> & sensorValue,bool isPowerOffError)416ffd54674SMatt Spinler void ShutdownAlarmMonitor::createEventLog(
417ffd54674SMatt Spinler     const AlarmKey& alarmKey, bool alarmValue,
4182a6b39c8SMatt Spinler     const std::optional<double>& sensorValue, bool isPowerOffError)
419ffd54674SMatt Spinler {
420ffd54674SMatt Spinler     using namespace sdbusplus::xyz::openbmc_project::Logging::server;
421ffd54674SMatt Spinler     const auto& [sensorPath, shutdownType, alarmType] = alarmKey;
4223efec61cSMatt Spinler     std::map<std::string, std::string> ad{{"SENSOR_NAME", sensorPath},
4233efec61cSMatt Spinler                                           {"_PID", std::to_string(getpid())}};
424ffd54674SMatt Spinler 
425ffd54674SMatt Spinler     std::string errorName =
426ffd54674SMatt Spinler         (alarmValue) ? alarmEventLogs.at(shutdownType).at(alarmType)
427ffd54674SMatt Spinler                      : alarmClearEventLogs.at(shutdownType).at(alarmType);
428ffd54674SMatt Spinler 
4292a6b39c8SMatt Spinler     // severity = Critical if a power off
4302a6b39c8SMatt Spinler     // severity = Error if alarm was asserted
4312a6b39c8SMatt Spinler     // severity = Informational if alarm was deasserted
4322a6b39c8SMatt Spinler     Entry::Level severity = Entry::Level::Error;
4332a6b39c8SMatt Spinler     if (isPowerOffError)
4342a6b39c8SMatt Spinler     {
4352a6b39c8SMatt Spinler         severity = Entry::Level::Critical;
4362a6b39c8SMatt Spinler     }
4372a6b39c8SMatt Spinler     else if (!alarmValue)
4382a6b39c8SMatt Spinler     {
4392a6b39c8SMatt Spinler         severity = Entry::Level::Informational;
4402a6b39c8SMatt Spinler     }
441ffd54674SMatt Spinler 
442ffd54674SMatt Spinler     if (sensorValue)
443ffd54674SMatt Spinler     {
444ffd54674SMatt Spinler         ad.emplace("SENSOR_VALUE", std::to_string(*sensorValue));
445ffd54674SMatt Spinler     }
446ffd54674SMatt Spinler 
4472a6b39c8SMatt Spinler     // If this is a power off, specify that it's a power
4482a6b39c8SMatt Spinler     // fault and a system termination.  This is used by some
4492a6b39c8SMatt Spinler     // implementations for service reasons.
4502a6b39c8SMatt Spinler     if (isPowerOffError)
4512a6b39c8SMatt Spinler     {
4522a6b39c8SMatt Spinler         ad.emplace("SEVERITY_DETAIL", "SYSTEM_TERM");
4532a6b39c8SMatt Spinler     }
4542a6b39c8SMatt Spinler 
455ffd54674SMatt Spinler     SDBusPlus::callMethod(loggingService, loggingPath, loggingCreateIface,
456ffd54674SMatt Spinler                           "Create", errorName, convertForMessage(severity), ad);
457ffd54674SMatt Spinler }
458ffd54674SMatt Spinler 
getShutdownType(const std::string & interface) const4594fa67aa1SPatrick Williams std::optional<ShutdownType> ShutdownAlarmMonitor::getShutdownType(
4604fa67aa1SPatrick Williams     const std::string& interface) const
46116c7dc1eSMatt Spinler {
4625e15c3baSPatrick Williams     auto it = std::find_if(
4635e15c3baSPatrick Williams         shutdownInterfaces.begin(), shutdownInterfaces.end(),
4645e15c3baSPatrick Williams         [interface](const auto& a) { return a.second == interface; });
46516c7dc1eSMatt Spinler 
46616c7dc1eSMatt Spinler     if (it == shutdownInterfaces.end())
46716c7dc1eSMatt Spinler     {
46816c7dc1eSMatt Spinler         return std::nullopt;
46916c7dc1eSMatt Spinler     }
47016c7dc1eSMatt Spinler 
47116c7dc1eSMatt Spinler     return it->first;
47216c7dc1eSMatt Spinler }
47316c7dc1eSMatt Spinler 
4749c0715b6SMatt Spinler } // namespace sensor::monitor
475