108a66efaSMatt Spinler /** 208a66efaSMatt Spinler * Copyright © 2021 IBM Corporation 308a66efaSMatt Spinler * 408a66efaSMatt Spinler * Licensed under the Apache License, Version 2.0 (the "License"); 508a66efaSMatt Spinler * you may not use this file except in compliance with the License. 608a66efaSMatt Spinler * You may obtain a copy of the License at 708a66efaSMatt Spinler * 808a66efaSMatt Spinler * http://www.apache.org/licenses/LICENSE-2.0 908a66efaSMatt Spinler * 1008a66efaSMatt Spinler * Unless required by applicable law or agreed to in writing, software 1108a66efaSMatt Spinler * distributed under the License is distributed on an "AS IS" BASIS, 1208a66efaSMatt Spinler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1308a66efaSMatt Spinler * See the License for the specific language governing permissions and 1408a66efaSMatt Spinler * limitations under the License. 1508a66efaSMatt Spinler */ 1608a66efaSMatt Spinler #pragma once 1708a66efaSMatt Spinler #include "config.h" 1808a66efaSMatt Spinler 1908a66efaSMatt Spinler #include "types.hpp" 2008a66efaSMatt Spinler 2108a66efaSMatt Spinler #include <cereal/archives/json.hpp> 2208a66efaSMatt Spinler #include <cereal/types/string.hpp> 2308a66efaSMatt Spinler #include <cereal/types/tuple.hpp> 2408a66efaSMatt Spinler #include <cereal/types/vector.hpp> 25*32c4feffSAnwaar Hadi #include <phosphor-logging/lg2.hpp> 2608a66efaSMatt Spinler #include <sdeventplus/clock.hpp> 2708a66efaSMatt Spinler #include <sdeventplus/utility/timer.hpp> 2808a66efaSMatt Spinler 2908a66efaSMatt Spinler #include <filesystem> 3008a66efaSMatt Spinler #include <fstream> 3108a66efaSMatt Spinler #include <map> 3208a66efaSMatt Spinler #include <tuple> 3308a66efaSMatt Spinler 3408a66efaSMatt Spinler namespace sensor::monitor 3508a66efaSMatt Spinler { 3608a66efaSMatt Spinler 3708a66efaSMatt Spinler /** 3808a66efaSMatt Spinler * @class AlarmTimestamps 3908a66efaSMatt Spinler * 4008a66efaSMatt Spinler * This class keeps track of the timestamps at which the shutdown 4108a66efaSMatt Spinler * timers are started in case the process or whole BMC restarts 4208a66efaSMatt Spinler * while a timer is running. In the case where the process starts 4308a66efaSMatt Spinler * when a timer was previously running and an alarm is still active, 4408a66efaSMatt Spinler * a new timer can be started with just the remaining time. 4508a66efaSMatt Spinler */ 4608a66efaSMatt Spinler class AlarmTimestamps 4708a66efaSMatt Spinler { 4808a66efaSMatt Spinler public: 4908a66efaSMatt Spinler ~AlarmTimestamps() = default; 5008a66efaSMatt Spinler AlarmTimestamps(const AlarmTimestamps&) = delete; 5108a66efaSMatt Spinler AlarmTimestamps& operator=(const AlarmTimestamps&) = delete; 5208a66efaSMatt Spinler AlarmTimestamps(AlarmTimestamps&&) = delete; 5308a66efaSMatt Spinler AlarmTimestamps& operator=(AlarmTimestamps&&) = delete; 5408a66efaSMatt Spinler 5508a66efaSMatt Spinler /** 5608a66efaSMatt Spinler * @brief Constructor 5708a66efaSMatt Spinler * 5808a66efaSMatt Spinler * Loads any saved timestamps 5908a66efaSMatt Spinler */ AlarmTimestamps()6008a66efaSMatt Spinler AlarmTimestamps() 6108a66efaSMatt Spinler { 6208a66efaSMatt Spinler load(); 6308a66efaSMatt Spinler } 6408a66efaSMatt Spinler 6508a66efaSMatt Spinler /** 6608a66efaSMatt Spinler * @brief Adds an entry to the timestamps map and persists it. 6708a66efaSMatt Spinler * 6808a66efaSMatt Spinler * @param[in] key - The AlarmKey value 6908a66efaSMatt Spinler * @param[in] timestamp - The start timestamp to save 7008a66efaSMatt Spinler */ add(const AlarmKey & key,uint64_t timestamp)7108a66efaSMatt Spinler void add(const AlarmKey& key, uint64_t timestamp) 7208a66efaSMatt Spinler { 7308a66efaSMatt Spinler // Emplace won't do anything if an entry with that 7408a66efaSMatt Spinler // key was already present, so only save if an actual 7508a66efaSMatt Spinler // entry was added. 7608a66efaSMatt Spinler auto result = timestamps.emplace(key, timestamp); 7708a66efaSMatt Spinler if (result.second) 7808a66efaSMatt Spinler { 7908a66efaSMatt Spinler save(); 8008a66efaSMatt Spinler } 8108a66efaSMatt Spinler } 8208a66efaSMatt Spinler 8308a66efaSMatt Spinler /** 8408a66efaSMatt Spinler * @brief Erase an entry using the passed in alarm key. 8508a66efaSMatt Spinler * 8608a66efaSMatt Spinler * @param[in] key - The AlarmKey value 8708a66efaSMatt Spinler */ erase(const AlarmKey & key)8808a66efaSMatt Spinler void erase(const AlarmKey& key) 8908a66efaSMatt Spinler { 9008a66efaSMatt Spinler size_t removed = timestamps.erase(key); 9108a66efaSMatt Spinler if (removed) 9208a66efaSMatt Spinler { 9308a66efaSMatt Spinler save(); 9408a66efaSMatt Spinler } 9508a66efaSMatt Spinler } 9608a66efaSMatt Spinler 9708a66efaSMatt Spinler /** 9808a66efaSMatt Spinler * @brief Erase an entry using an iterator. 9908a66efaSMatt Spinler */ erase(std::map<AlarmKey,uint64_t>::const_iterator & entry)10008a66efaSMatt Spinler void erase(std::map<AlarmKey, uint64_t>::const_iterator& entry) 10108a66efaSMatt Spinler { 10208a66efaSMatt Spinler timestamps.erase(entry); 10308a66efaSMatt Spinler save(); 10408a66efaSMatt Spinler } 10508a66efaSMatt Spinler 10608a66efaSMatt Spinler /** 10708a66efaSMatt Spinler * @brief Clear all entries. 10808a66efaSMatt Spinler */ clear()10908a66efaSMatt Spinler void clear() 11008a66efaSMatt Spinler { 11108a66efaSMatt Spinler if (!timestamps.empty()) 11208a66efaSMatt Spinler { 11308a66efaSMatt Spinler timestamps.clear(); 11408a66efaSMatt Spinler save(); 11508a66efaSMatt Spinler } 11608a66efaSMatt Spinler } 11708a66efaSMatt Spinler 11808a66efaSMatt Spinler /** 11908a66efaSMatt Spinler * @brief Remove any entries for which there is not a running timer 12008a66efaSMatt Spinler * for. This is used on startup when an alarm could have cleared 12108a66efaSMatt Spinler * during a restart to get rid of the old entries. 12208a66efaSMatt Spinler * 12308a66efaSMatt Spinler * @param[in] alarms - The current alarms map. 12408a66efaSMatt Spinler */ prune(const std::map<AlarmKey,std::unique_ptr<sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>> & alarms)12508a66efaSMatt Spinler void prune( 12608a66efaSMatt Spinler const std::map<AlarmKey, std::unique_ptr<sdeventplus::utility::Timer< 12708a66efaSMatt Spinler sdeventplus::ClockId::Monotonic>>>& alarms) 12808a66efaSMatt Spinler { 12908a66efaSMatt Spinler auto size = timestamps.size(); 13008a66efaSMatt Spinler 13108a66efaSMatt Spinler auto isTimerStopped = [&alarms](const AlarmKey& key) { 13208a66efaSMatt Spinler auto alarm = alarms.find(key); 13308a66efaSMatt Spinler if (alarm != alarms.end()) 13408a66efaSMatt Spinler { 13508a66efaSMatt Spinler auto& timer = alarm->second; 13608a66efaSMatt Spinler if (timer && timer->isEnabled()) 13708a66efaSMatt Spinler { 13808a66efaSMatt Spinler return false; 13908a66efaSMatt Spinler } 14008a66efaSMatt Spinler } 14108a66efaSMatt Spinler return true; 14208a66efaSMatt Spinler }; 14308a66efaSMatt Spinler 14408a66efaSMatt Spinler auto it = timestamps.begin(); 14508a66efaSMatt Spinler 14608a66efaSMatt Spinler while (it != timestamps.end()) 14708a66efaSMatt Spinler { 14808a66efaSMatt Spinler if (isTimerStopped(it->first)) 14908a66efaSMatt Spinler { 15008a66efaSMatt Spinler it = timestamps.erase(it); 15108a66efaSMatt Spinler } 15208a66efaSMatt Spinler else 15308a66efaSMatt Spinler { 15408a66efaSMatt Spinler ++it; 15508a66efaSMatt Spinler } 15608a66efaSMatt Spinler } 15708a66efaSMatt Spinler 15808a66efaSMatt Spinler if (size != timestamps.size()) 15908a66efaSMatt Spinler { 16008a66efaSMatt Spinler save(); 16108a66efaSMatt Spinler } 16208a66efaSMatt Spinler } 16308a66efaSMatt Spinler 16408a66efaSMatt Spinler /** 16508a66efaSMatt Spinler * @brief Returns the timestamps map 16608a66efaSMatt Spinler */ get() const16708a66efaSMatt Spinler const std::map<AlarmKey, uint64_t>& get() const 16808a66efaSMatt Spinler { 16908a66efaSMatt Spinler return timestamps; 17008a66efaSMatt Spinler } 17108a66efaSMatt Spinler 17208a66efaSMatt Spinler /** 17308a66efaSMatt Spinler * @brief Saves the timestamps map in the filesystem using cereal. 17408a66efaSMatt Spinler * 17508a66efaSMatt Spinler * Since cereal doesn't understand the AlarmType or ShutdownType 17608a66efaSMatt Spinler * enums, they are converted to ints before being written. 17708a66efaSMatt Spinler */ save()17808a66efaSMatt Spinler void save() 17908a66efaSMatt Spinler { 18008a66efaSMatt Spinler std::filesystem::path path = 18108a66efaSMatt Spinler std::filesystem::path{SENSOR_MONITOR_PERSIST_ROOT_PATH} / 18208a66efaSMatt Spinler timestampsFilename; 18308a66efaSMatt Spinler 18408a66efaSMatt Spinler if (!std::filesystem::exists(path.parent_path())) 18508a66efaSMatt Spinler { 18608a66efaSMatt Spinler std::filesystem::create_directory(path.parent_path()); 18708a66efaSMatt Spinler } 18808a66efaSMatt Spinler 18908a66efaSMatt Spinler std::vector<std::tuple<std::string, int, int, uint64_t>> times; 19008a66efaSMatt Spinler 19108a66efaSMatt Spinler for (const auto& [key, time] : timestamps) 19208a66efaSMatt Spinler { 19308a66efaSMatt Spinler times.emplace_back(std::get<std::string>(key), 19408a66efaSMatt Spinler static_cast<int>(std::get<ShutdownType>(key)), 19508a66efaSMatt Spinler static_cast<int>(std::get<AlarmType>(key)), 19608a66efaSMatt Spinler time); 19708a66efaSMatt Spinler } 19808a66efaSMatt Spinler 19908a66efaSMatt Spinler std::ofstream stream{path.c_str()}; 20008a66efaSMatt Spinler cereal::JSONOutputArchive oarchive{stream}; 20108a66efaSMatt Spinler 20208a66efaSMatt Spinler oarchive(times); 20308a66efaSMatt Spinler } 20408a66efaSMatt Spinler 20508a66efaSMatt Spinler private: 20608a66efaSMatt Spinler static constexpr auto timestampsFilename = "shutdownAlarmStartTimes"; 20708a66efaSMatt Spinler 20808a66efaSMatt Spinler /** 20908a66efaSMatt Spinler * @brief Loads the saved timestamps from the filesystem. 21008a66efaSMatt Spinler * 21108a66efaSMatt Spinler * As with save(), cereal doesn't understand the ShutdownType or AlarmType 21208a66efaSMatt Spinler * enums so they have to have been saved as ints and converted. 21308a66efaSMatt Spinler */ load()21408a66efaSMatt Spinler void load() 21508a66efaSMatt Spinler { 21608a66efaSMatt Spinler std::vector<std::tuple<std::string, int, int, uint64_t>> times; 21708a66efaSMatt Spinler 21808a66efaSMatt Spinler std::filesystem::path path = 21908a66efaSMatt Spinler std::filesystem::path{SENSOR_MONITOR_PERSIST_ROOT_PATH} / 22008a66efaSMatt Spinler timestampsFilename; 22108a66efaSMatt Spinler 22208a66efaSMatt Spinler if (!std::filesystem::exists(path)) 22308a66efaSMatt Spinler { 22408a66efaSMatt Spinler return; 22508a66efaSMatt Spinler } 22608a66efaSMatt Spinler 2270a56d459SGeorge Liu try 2280a56d459SGeorge Liu { 22908a66efaSMatt Spinler std::ifstream stream{path.c_str()}; 23008a66efaSMatt Spinler cereal::JSONInputArchive iarchive{stream}; 23108a66efaSMatt Spinler iarchive(times); 23208a66efaSMatt Spinler 23308a66efaSMatt Spinler for (const auto& [path, shutdownType, alarmType, timestamp] : times) 23408a66efaSMatt Spinler { 2350a56d459SGeorge Liu timestamps.emplace( 2360a56d459SGeorge Liu AlarmKey{path, static_cast<ShutdownType>(shutdownType), 23708a66efaSMatt Spinler static_cast<AlarmType>(alarmType)}, 23808a66efaSMatt Spinler timestamp); 23908a66efaSMatt Spinler } 24008a66efaSMatt Spinler } 2410a56d459SGeorge Liu catch (const std::exception& e) 2420a56d459SGeorge Liu { 2430a56d459SGeorge Liu // Include possible exception when removing file, otherwise ec = 0 2440a56d459SGeorge Liu std::error_code ec; 245a899aa0cSMatt Spinler std::filesystem::remove(path, ec); 246*32c4feffSAnwaar Hadi lg2::error("Unable to restore persisted times ({ERROR}, ec: {EC})", 247*32c4feffSAnwaar Hadi "ERROR", e, "EC", ec.value()); 2480a56d459SGeorge Liu } 2490a56d459SGeorge Liu } 25008a66efaSMatt Spinler 25108a66efaSMatt Spinler /** 25208a66efaSMatt Spinler * @brief The map of AlarmKeys and time start times. 25308a66efaSMatt Spinler */ 25408a66efaSMatt Spinler std::map<AlarmKey, uint64_t> timestamps; 25508a66efaSMatt Spinler }; 25608a66efaSMatt Spinler 25708a66efaSMatt Spinler } // namespace sensor::monitor 258