blob: 5886dc405d7cebae855fbb0fd3ccec173a6b8fe0 [file] [log] [blame]
Matt Spinler08a66ef2021-01-21 13:46:45 -06001/**
2 * Copyright © 2021 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#pragma once
17#include "config.h"
18
19#include "types.hpp"
20
21#include <cereal/archives/json.hpp>
22#include <cereal/types/string.hpp>
23#include <cereal/types/tuple.hpp>
24#include <cereal/types/vector.hpp>
Anwaar Hadi32c4fef2025-04-02 16:08:27 +000025#include <phosphor-logging/lg2.hpp>
Matt Spinler08a66ef2021-01-21 13:46:45 -060026#include <sdeventplus/clock.hpp>
27#include <sdeventplus/utility/timer.hpp>
28
29#include <filesystem>
30#include <fstream>
31#include <map>
32#include <tuple>
33
34namespace sensor::monitor
35{
36
37/**
38 * @class AlarmTimestamps
39 *
40 * This class keeps track of the timestamps at which the shutdown
41 * timers are started in case the process or whole BMC restarts
42 * while a timer is running. In the case where the process starts
43 * when a timer was previously running and an alarm is still active,
44 * a new timer can be started with just the remaining time.
45 */
46class AlarmTimestamps
47{
48 public:
49 ~AlarmTimestamps() = default;
50 AlarmTimestamps(const AlarmTimestamps&) = delete;
51 AlarmTimestamps& operator=(const AlarmTimestamps&) = delete;
52 AlarmTimestamps(AlarmTimestamps&&) = delete;
53 AlarmTimestamps& operator=(AlarmTimestamps&&) = delete;
54
55 /**
56 * @brief Constructor
57 *
58 * Loads any saved timestamps
59 */
60 AlarmTimestamps()
61 {
62 load();
63 }
64
65 /**
66 * @brief Adds an entry to the timestamps map and persists it.
67 *
68 * @param[in] key - The AlarmKey value
69 * @param[in] timestamp - The start timestamp to save
70 */
71 void add(const AlarmKey& key, uint64_t timestamp)
72 {
73 // Emplace won't do anything if an entry with that
74 // key was already present, so only save if an actual
75 // entry was added.
76 auto result = timestamps.emplace(key, timestamp);
77 if (result.second)
78 {
79 save();
80 }
81 }
82
83 /**
84 * @brief Erase an entry using the passed in alarm key.
85 *
86 * @param[in] key - The AlarmKey value
87 */
88 void erase(const AlarmKey& key)
89 {
90 size_t removed = timestamps.erase(key);
91 if (removed)
92 {
93 save();
94 }
95 }
96
97 /**
98 * @brief Erase an entry using an iterator.
99 */
100 void erase(std::map<AlarmKey, uint64_t>::const_iterator& entry)
101 {
102 timestamps.erase(entry);
103 save();
104 }
105
106 /**
107 * @brief Clear all entries.
108 */
109 void clear()
110 {
111 if (!timestamps.empty())
112 {
113 timestamps.clear();
114 save();
115 }
116 }
117
118 /**
119 * @brief Remove any entries for which there is not a running timer
120 * for. This is used on startup when an alarm could have cleared
121 * during a restart to get rid of the old entries.
122 *
123 * @param[in] alarms - The current alarms map.
124 */
125 void prune(
126 const std::map<AlarmKey, std::unique_ptr<sdeventplus::utility::Timer<
127 sdeventplus::ClockId::Monotonic>>>& alarms)
128 {
129 auto size = timestamps.size();
130
131 auto isTimerStopped = [&alarms](const AlarmKey& key) {
132 auto alarm = alarms.find(key);
133 if (alarm != alarms.end())
134 {
135 auto& timer = alarm->second;
136 if (timer && timer->isEnabled())
137 {
138 return false;
139 }
140 }
141 return true;
142 };
143
144 auto it = timestamps.begin();
145
146 while (it != timestamps.end())
147 {
148 if (isTimerStopped(it->first))
149 {
150 it = timestamps.erase(it);
151 }
152 else
153 {
154 ++it;
155 }
156 }
157
158 if (size != timestamps.size())
159 {
160 save();
161 }
162 }
163
164 /**
165 * @brief Returns the timestamps map
166 */
167 const std::map<AlarmKey, uint64_t>& get() const
168 {
169 return timestamps;
170 }
171
172 /**
173 * @brief Saves the timestamps map in the filesystem using cereal.
174 *
175 * Since cereal doesn't understand the AlarmType or ShutdownType
176 * enums, they are converted to ints before being written.
177 */
178 void save()
179 {
180 std::filesystem::path path =
181 std::filesystem::path{SENSOR_MONITOR_PERSIST_ROOT_PATH} /
182 timestampsFilename;
183
184 if (!std::filesystem::exists(path.parent_path()))
185 {
186 std::filesystem::create_directory(path.parent_path());
187 }
188
189 std::vector<std::tuple<std::string, int, int, uint64_t>> times;
190
191 for (const auto& [key, time] : timestamps)
192 {
193 times.emplace_back(std::get<std::string>(key),
194 static_cast<int>(std::get<ShutdownType>(key)),
195 static_cast<int>(std::get<AlarmType>(key)),
196 time);
197 }
198
199 std::ofstream stream{path.c_str()};
200 cereal::JSONOutputArchive oarchive{stream};
201
202 oarchive(times);
203 }
204
205 private:
206 static constexpr auto timestampsFilename = "shutdownAlarmStartTimes";
207
208 /**
209 * @brief Loads the saved timestamps from the filesystem.
210 *
211 * As with save(), cereal doesn't understand the ShutdownType or AlarmType
212 * enums so they have to have been saved as ints and converted.
213 */
214 void load()
215 {
Matt Spinler08a66ef2021-01-21 13:46:45 -0600216 std::vector<std::tuple<std::string, int, int, uint64_t>> times;
217
218 std::filesystem::path path =
219 std::filesystem::path{SENSOR_MONITOR_PERSIST_ROOT_PATH} /
220 timestampsFilename;
221
222 if (!std::filesystem::exists(path))
223 {
224 return;
225 }
226
George Liu0a56d452022-01-26 10:06:35 +0800227 try
Matt Spinler08a66ef2021-01-21 13:46:45 -0600228 {
George Liu0a56d452022-01-26 10:06:35 +0800229 std::ifstream stream{path.c_str()};
230 cereal::JSONInputArchive iarchive{stream};
231 iarchive(times);
232
233 for (const auto& [path, shutdownType, alarmType, timestamp] : times)
234 {
235 timestamps.emplace(
236 AlarmKey{path, static_cast<ShutdownType>(shutdownType),
237 static_cast<AlarmType>(alarmType)},
238 timestamp);
239 }
240 }
241 catch (const std::exception& e)
242 {
243 // Include possible exception when removing file, otherwise ec = 0
244 std::error_code ec;
Matt Spinlera899aa02022-05-12 13:18:04 -0500245 std::filesystem::remove(path, ec);
Anwaar Hadi32c4fef2025-04-02 16:08:27 +0000246 lg2::error("Unable to restore persisted times ({ERROR}, ec: {EC})",
247 "ERROR", e, "EC", ec.value());
Matt Spinler08a66ef2021-01-21 13:46:45 -0600248 }
249 }
250
251 /**
252 * @brief The map of AlarmKeys and time start times.
253 */
254 std::map<AlarmKey, uint64_t> timestamps;
255};
256
257} // namespace sensor::monitor