1 #pragma once
2 #include "bcd_time.hpp"
3 #include "paths.hpp"
4 #include "pel.hpp"
5 
6 #include <algorithm>
7 #include <bitset>
8 #include <filesystem>
9 #include <map>
10 
11 namespace openpower
12 {
13 namespace pels
14 {
15 
16 /**
17  * @class Repository
18  *
19  * The class handles saving and retrieving PELs on the BMC.
20  */
21 class Repository
22 {
23   public:
24     /**
25      * @brief Structure of commonly used PEL attributes.
26      */
27     struct PELAttributes
28     {
29         std::filesystem::path path;
30         size_t sizeOnDisk;
31         uint8_t creator;
32         uint8_t severity;
33         std::bitset<16> actionFlags;
34         TransmissionState hostState;
35         TransmissionState hmcState;
36 
37         PELAttributes() = delete;
38 
39         PELAttributes(const std::filesystem::path& p, size_t size,
40                       uint8_t creator, uint8_t sev, uint16_t flags,
41                       TransmissionState hostState, TransmissionState hmcState) :
42             path(p),
43             sizeOnDisk(size), creator(creator), severity(sev),
44             actionFlags(flags), hostState(hostState), hmcState(hmcState)
45         {
46         }
47     };
48 
49     /**
50      * @brief A structure that holds both the PEL and corresponding
51      *        OpenBMC IDs.
52      * Used for correlating the IDs with their data files for quick
53      * lookup.  To find a PEL based on just one of the IDs, just use
54      * the constructor that takes that ID.
55      */
56     struct LogID
57     {
58         struct Pel
59         {
60             uint32_t id;
61             explicit Pel(uint32_t i) : id(i)
62             {
63             }
64         };
65         struct Obmc
66         {
67             uint32_t id;
68             explicit Obmc(uint32_t i) : id(i)
69             {
70             }
71         };
72 
73         Pel pelID;
74 
75         Obmc obmcID;
76 
77         LogID(Pel pel, Obmc obmc) : pelID(pel), obmcID(obmc)
78         {
79         }
80 
81         explicit LogID(Pel id) : pelID(id), obmcID(0)
82         {
83         }
84 
85         explicit LogID(Obmc id) : pelID(0), obmcID(id)
86         {
87         }
88 
89         LogID() = delete;
90 
91         /**
92          * @brief A == operator that will match on either ID
93          *        being equal if the other is zero, so that
94          *        one can look up a PEL with just one of the IDs.
95          */
96         bool operator==(const LogID& id) const
97         {
98             if (id.pelID.id != 0)
99             {
100                 return id.pelID.id == pelID.id;
101             }
102             if (id.obmcID.id != 0)
103             {
104                 return id.obmcID.id == obmcID.id;
105             }
106             return false;
107         }
108 
109         bool operator<(const LogID& id) const
110         {
111             return pelID.id < id.pelID.id;
112         }
113     };
114 
115     using AttributesReference =
116         std::reference_wrapper<const std::pair<const LogID, PELAttributes>>;
117 
118     /**
119      * @brief A structure for keeping a breakdown of the sizes of PELs
120      *        of different types in the repository.
121      */
122     struct SizeStats
123     {
124         uint64_t total;
125         uint64_t bmc;
126         uint64_t nonBMC;
127         uint64_t bmcServiceable;
128         uint64_t bmcInfo;
129         uint64_t nonBMCServiceable;
130         uint64_t nonBMCInfo;
131 
132         SizeStats() :
133             total(0), bmc(0), nonBMC(0), bmcServiceable(0), bmcInfo(0),
134             nonBMCServiceable(0), nonBMCInfo(0)
135         {
136         }
137     };
138 
139     Repository() = delete;
140     ~Repository() = default;
141     Repository(const Repository&) = default;
142     Repository& operator=(const Repository&) = default;
143     Repository(Repository&&) = default;
144     Repository& operator=(Repository&&) = default;
145 
146     /**
147      * @brief Constructor
148      *
149      * @param[in] basePath - the base filesystem path for the repository
150      */
151     Repository(const std::filesystem::path& basePath) :
152         Repository(basePath, getPELRepoSize(), getMaxNumPELs())
153     {
154     }
155 
156     /**
157      * @brief Constructor that takes the repository size
158      *
159      * @param[in] basePath - the base filesystem path for the repository
160      * @param[in] repoSize - The maximum amount of space to use for PELs,
161      *                       in bytes
162      * @param[in] maxNumPELs - The maximum number of PELs to allow
163      */
164     Repository(const std::filesystem::path& basePath, size_t repoSize,
165                size_t maxNumPELs);
166 
167     /**
168      * @brief Adds a PEL to the repository
169      *
170      * Throws File.Error.Open or File.Error.Write exceptions on failure
171      *
172      * @param[in] pel - the PEL to add
173      */
174     void add(std::unique_ptr<PEL>& pel);
175 
176     /**
177      * @brief Removes a PEL from the repository
178      *
179      * Note that the returned LogID is the fully filled in LogID, i.e.
180      * it has both the PEL and OpenBMC IDs, unlike the passed in LogID
181      * which can just have one or the other.
182      *
183      * @param[in] id - the ID (either the pel ID, OBMC ID, or both) to remove
184      *
185      * @return std::optional<LogID> - The LogID of the removed PEL
186      */
187     std::optional<LogID> remove(const LogID& id);
188 
189     /**
190      * @brief Generates the filename to use for the PEL ID and BCDTime.
191      *
192      * @param[in] pelID - the PEL ID
193      * @param[in] time - the BCD time
194      *
195      * @return string - A filename string of <BCD_time>_<pelID>
196      */
197     static std::string getPELFilename(uint32_t pelID, const BCDTime& time);
198 
199     /**
200      * @brief Returns true if the PEL with the specified ID is in the repo.
201      *
202      * @param[in] id - the ID (either the pel ID, OBMC ID, or both)
203      * @return bool - true if that PEL is present
204      */
205     inline bool hasPEL(const LogID& id)
206     {
207         return findPEL(id) != _pelAttributes.end();
208     }
209 
210     /**
211      * @brief Returns the PEL data based on its ID.
212      *
213      * If the data can't be found for that ID, then the optional object
214      * will be empty.
215      *
216      * @param[in] id - the LogID to get the PEL for, which can be either a
217      *                 PEL ID or OpenBMC log ID.
218      * @return std::optional<std::vector<uint8_t>> - the PEL data
219      */
220     std::optional<std::vector<uint8_t>> getPELData(const LogID& id);
221 
222     /**
223      * @brief Get a file descriptor to the PEL data
224      *
225      * @param[in] id - The ID to get the FD for
226      *
227      * @return std::optional<sdbusplus::message::unix_fd> -
228      *         The FD, or an empty optional object.
229      */
230     std::optional<sdbusplus::message::unix_fd> getPELFD(const LogID& id);
231 
232     using ForEachFunc = std::function<bool(const PEL&)>;
233 
234     /**
235      * @brief Run a user defined function on every PEL in the repository.
236      *
237      * ForEachFunc takes a const PEL reference, and should return
238      * true to stop iterating and return out of for_each.
239      *
240      * For example, to save up to 100 IDs in the repo into a vector:
241      *
242      *     std::vector<uint32_t> ids;
243      *     ForEachFunc f = [&ids](const PEL& pel) {
244      *         ids.push_back(pel.id());
245      *         return ids.size() == 100 ? true : false;
246      *     };
247      *
248      * @param[in] func - The function to run.
249      */
250     void for_each(ForEachFunc func) const;
251 
252     using AddCallback = std::function<void(const PEL&)>;
253 
254     /**
255      * @brief Subscribe to PELs being added to the repository.
256      *
257      * Every time a PEL is added to the repository, the provided
258      * function will be called with the new PEL as the argument.
259      *
260      * The function must be of type void(const PEL&).
261      *
262      * @param[in] name - The subscription name
263      * @param[in] func - The callback function
264      */
265     void subscribeToAdds(const std::string& name, AddCallback func)
266     {
267         if (_addSubscriptions.find(name) == _addSubscriptions.end())
268         {
269             _addSubscriptions.emplace(name, func);
270         }
271     }
272 
273     /**
274      * @brief Unsubscribe from new PELs.
275      *
276      * @param[in] name - The subscription name
277      */
278     void unsubscribeFromAdds(const std::string& name)
279     {
280         _addSubscriptions.erase(name);
281     }
282 
283     using DeleteCallback = std::function<void(uint32_t)>;
284 
285     /**
286      * @brief Subscribe to PELs being deleted from the repository.
287      *
288      * Every time a PEL is deleted from the repository, the provided
289      * function will be called with the PEL ID as the argument.
290      *
291      * The function must be of type void(const uint32_t).
292      *
293      * @param[in] name - The subscription name
294      * @param[in] func - The callback function
295      */
296     void subscribeToDeletes(const std::string& name, DeleteCallback func)
297     {
298         if (_deleteSubscriptions.find(name) == _deleteSubscriptions.end())
299         {
300             _deleteSubscriptions.emplace(name, func);
301         }
302     }
303 
304     /**
305      * @brief Unsubscribe from deleted PELs.
306      *
307      * @param[in] name - The subscription name
308      */
309     void unsubscribeFromDeletes(const std::string& name)
310     {
311         _deleteSubscriptions.erase(name);
312     }
313 
314     /**
315      * @brief Get the PEL attributes for a PEL
316      *
317      * @param[in] id - The ID to find the attributes for
318      *
319      * @return The attributes or an empty optional if not found
320      */
321     std::optional<std::reference_wrapper<const PELAttributes>>
322         getPELAttributes(const LogID& id) const;
323 
324     /**
325      * @brief Sets the host transmission state on a PEL file
326      *
327      * Writes the host transmission state field in the User Header
328      * section in the PEL data specified by the ID.
329      *
330      * @param[in] pelID - The PEL ID
331      * @param[in] state - The state to write
332      */
333     void setPELHostTransState(uint32_t pelID, TransmissionState state);
334 
335     /**
336      * @brief Sets the HMC transmission state on a PEL file
337      *
338      * Writes the HMC transmission state field in the User Header
339      * section in the PEL data specified by the ID.
340      *
341      * @param[in] pelID - The PEL ID
342      * @param[in] state - The state to write
343      */
344     void setPELHMCTransState(uint32_t pelID, TransmissionState state);
345 
346     /**
347      * @brief Returns the size stats structure
348      *
349      * @return const SizeStats& - The stats structure
350      */
351     const SizeStats& getSizeStats() const
352     {
353         return _sizes;
354     }
355 
356     /**
357      * @brief Says if the PEL is considered serviceable (not just
358      *        informational) as determined by its severity.
359      *
360      * @param[in] pel - The PELAttributes entry for the PEL
361      * @return bool - If serviceable or not
362      */
363     static bool isServiceableSev(const PELAttributes& pel);
364 
365     /**
366      * @brief Returns true if the total amount of disk space occupied
367      *        by the PELs in the repo is over 95% of the maximum
368      *        size, or if there are over the maximum number of
369      *        PELs allowed.
370      *
371      * @return bool - true if repo is > 95% full or too many PELs
372      */
373     bool sizeWarning() const;
374 
375     /**
376      * @brief Deletes PELs to bring the repository size down
377      *        to at most 90% full by placing PELs into 4 different
378      *        catogories and then removing PELs until those catogories
379      *        only take up certain percentages of the allowed space.
380      *
381      * This does not delete the corresponding OpenBMC event logs, which
382      * is why those IDs are returned, so they can be deleted later.
383      *
384      * The categories and their rules are:
385      *  1) Informational BMC PELs cannot take up more than 15% of
386      *     the allocated space.
387      *  2) Non-informational BMC PELs cannot take up more than 30%
388      *     of the allocated space.
389      *  3) Informational non-BMC PELs cannot take up more than 15% of
390      *     the allocated space.
391      *  4) Non-informational non-BMC PELs cannot take up more than 30%
392      *     of the allocated space.
393      *
394      *  While removing PELs in a category, 4 passes will be made, with
395      *  PELs being removed oldest first during each pass.
396      *
397      *   Pass 1: only delete HMC acked PELs
398      *   Pass 2: only delete OS acked PELs
399      *   Pass 3: only delete PHYP sent PELs
400      *   Pass 4: delete all PELs
401      *
402      * @return std::vector<uint32_t> - The OpenBMC event log IDs of
403      *                                 the PELs that were deleted.
404      */
405     std::vector<uint32_t> prune();
406 
407     /**
408      * @brief Returns the path to the directory where the PEL
409      *        files are stored.
410      *
411      * @return std::filesystem::path - The directory path
412      */
413     const std::filesystem::path& repoPath() const
414     {
415         return _logPath;
416     }
417 
418     /**
419      * @brief Returns the ID of the most recently added PEL.
420      *
421      * @return uint32_t - The PEL ID
422      */
423     uint32_t lastPelID() const
424     {
425         return _lastPelID;
426     }
427 
428   private:
429     using PELUpdateFunc = std::function<void(PEL&)>;
430 
431     /**
432      * @brief Lets a function modify a PEL and saves the results
433      *
434      * Runs updateFunc (a void(PEL&) function) on the PEL data
435      * on the file specified, and writes the results back to the file.
436      *
437      * @param[in] path - The file path to use
438      * @param[in] updateFunc - The function to run to update the PEL.
439      */
440     void updatePEL(const std::filesystem::path& path, PELUpdateFunc updateFunc);
441 
442     /**
443      * @brief Finds an entry in the _pelAttributes map.
444      *
445      * @param[in] id - the ID (either the pel ID, OBMC ID, or both)
446      *
447      * @return an iterator to the entry
448      */
449     std::map<LogID, PELAttributes>::const_iterator
450         findPEL(const LogID& id) const
451     {
452         return std::find_if(_pelAttributes.begin(), _pelAttributes.end(),
453                             [&id](const auto& a) { return a.first == id; });
454     }
455 
456     /**
457      * @brief Call any subscribed functions for new PELs
458      *
459      * @param[in] pel - The new PEL
460      */
461     void processAddCallbacks(const PEL& pel) const;
462 
463     /**
464      * @brief Call any subscribed functions for deleted PELs
465      *
466      * @param[in] id - The ID of the deleted PEL
467      */
468     void processDeleteCallbacks(uint32_t id) const;
469 
470     /**
471      * @brief Restores the _pelAttributes map on startup based on the existing
472      *        PEL data files.
473      */
474     void restore();
475 
476     /**
477      * @brief Stores a PEL object in the filesystem.
478      *
479      * @param[in] pel - The PEL to write
480      * @param[in] path - The file to write to
481      *
482      * Throws exceptions on failures.
483      */
484     void write(const PEL& pel, const std::filesystem::path& path);
485 
486     /**
487      * @brief Updates the repository statistics after a PEL is
488      *        added or removed.
489      *
490      * @param[in] pel - The PELAttributes entry for the PEL
491      * @param[in] pelAdded - true if the PEL was added, false if removed
492      */
493     void updateRepoStats(const PELAttributes& pel, bool pelAdded);
494 
495     enum class SortOrder
496     {
497         ascending,
498         descending
499     };
500 
501     /**
502      * @brief Returns a vector of all the _pelAttributes entries sorted
503      *        as specified
504      *
505      * @param[in] order - If the PELs should be returned in ascending
506      *                    (oldest first) or descending order.
507      *
508      * @return std::vector<AttributesReference> - The sorted vector of
509      *         references to the pair<LogID, PELAttributes> entries of
510      *         _pelAttributes.
511      */
512     std::vector<AttributesReference> getAllPELAttributes(SortOrder order) const;
513 
514     using IsOverLimitFunc = std::function<bool()>;
515     using IsPELTypeFunc = std::function<bool(const PELAttributes&)>;
516 
517     /**
518      * @brief Makes 4 passes on the PELs that meet the IsPELTypeFunc
519      *        criteria removing PELs until IsOverLimitFunc returns false.
520      *
521      *   Pass 1: only delete HMC acked PELs
522      *   Pass 2: only delete Os acked PELs
523      *   Pass 3: only delete PHYP sent PELs
524      *   Pass 4: delete all PELs
525      *
526      * @param[in] isOverLimit - The bool(void) function that should
527      *                          return true if PELs still need to be
528      *                           removed.
529      * @param[in] isPELType - The bool(const PELAttributes&) function
530      *                         used to select the PELs to operate on.
531      *
532      * @param[out] removedBMCLogIDs - The OpenBMC event log IDs of the
533      *                                removed PELs.
534      */
535     void removePELs(IsOverLimitFunc& isOverLimit, IsPELTypeFunc& isPELType,
536                     std::vector<uint32_t>& removedBMCLogIDs);
537     /**
538      * @brief The filesystem path to the PEL logs.
539      */
540     const std::filesystem::path _logPath;
541 
542     /**
543      * @brief A map of the PEL/OBMC IDs to PEL attributes.
544      */
545     std::map<LogID, PELAttributes> _pelAttributes;
546 
547     /**
548      * @brief Subcriptions for new PELs.
549      */
550     std::map<std::string, AddCallback> _addSubscriptions;
551 
552     /**
553      * @brief Subscriptions for deleted PELs.
554      */
555     std::map<std::string, DeleteCallback> _deleteSubscriptions;
556 
557     /**
558      * @brief The maximum amount of space that the PELs in the
559      *        repository can occupy.
560      */
561     const uint64_t _maxRepoSize;
562 
563     /**
564      * @brief The maximum number of PELs to allow in the repo
565      *        before pruning.
566      */
567     const size_t _maxNumPELs;
568 
569     /**
570      * @brief Statistics on the sizes of the stored PELs.
571      */
572     SizeStats _sizes;
573 
574     /**
575      * @brief The ID of the most recently added PEL.
576      */
577     uint32_t _lastPelID = 0;
578 };
579 
580 } // namespace pels
581 } // namespace openpower
582