xref: /openbmc/phosphor-logging/extensions/openpower-pels/log_id.cpp (revision 40fb54935ce7367636a7156039396ee91cc4d5e2)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3 
4 #include "log_id.hpp"
5 
6 #include "paths.hpp"
7 
8 #include <phosphor-logging/lg2.hpp>
9 
10 #include <chrono>
11 #include <filesystem>
12 #include <fstream>
13 
14 namespace openpower
15 {
16 namespace pels
17 {
18 
19 namespace fs = std::filesystem;
20 
21 constexpr uint32_t startingLogID = 1;
22 constexpr uint32_t bmcLogIDPrefix = 0x50000000;
23 
24 namespace detail
25 {
26 
addLogIDPrefix(uint32_t id)27 uint32_t addLogIDPrefix(uint32_t id)
28 {
29     // If redundant BMCs are ever a thing, may need a different prefix.
30     return (id & 0x00FFFFFF) | bmcLogIDPrefix;
31 }
32 
getTimeBasedLogID()33 uint32_t getTimeBasedLogID()
34 {
35     using namespace std::chrono;
36 
37     // Use 3 bytes of the nanosecond count since the epoch.
38     uint32_t id =
39         duration_cast<nanoseconds>(system_clock::now().time_since_epoch())
40             .count();
41 
42     return addLogIDPrefix(id);
43 }
44 
45 } // namespace detail
46 
generatePELID()47 uint32_t generatePELID()
48 {
49     // Note: there isn't a need to be thread safe.
50 
51     static std::string idFilename;
52     if (idFilename.empty())
53     {
54         idFilename = getPELIDFile();
55         checkFileForZeroData(idFilename);
56     }
57 
58     uint32_t id = 0;
59 
60     if (!fs::exists(idFilename))
61     {
62         auto path = fs::path(idFilename).parent_path();
63         if (!fs::exists(path))
64         {
65             fs::create_directories(path);
66         }
67 
68         id = startingLogID;
69     }
70     else
71     {
72         std::ifstream idFile{idFilename};
73         idFile >> id;
74         if (idFile.fail())
75         {
76             // Just make up an ID
77             lg2::error("Unable to read PEL ID File!");
78             return detail::getTimeBasedLogID();
79         }
80     }
81 
82     // Wrapping shouldn't be a problem, but check anyway
83     if (id == 0x00FFFFFF)
84     {
85         id = startingLogID;
86     }
87 
88     std::ofstream idFile{idFilename};
89     idFile << (id + 1);
90     if (idFile.fail())
91     {
92         // Just make up an ID so we don't reuse one next time
93         lg2::error("Unable to write PEL ID File!");
94         return detail::getTimeBasedLogID();
95     }
96 
97     return detail::addLogIDPrefix(id);
98 }
99 
checkFileForZeroData(const std::string & filename)100 void checkFileForZeroData(const std::string& filename)
101 {
102     if (fs::exists(filename))
103     {
104         char ch;
105 
106         std::ifstream rf{filename, std::ios::binary};
107         rf.read(&ch, sizeof(ch));
108         while (ch == '\0')
109         {
110             if (rf.eof())
111             {
112                 fs::remove(filename);
113                 lg2::warning("PEL ID file seems corrupted. Deleting it.");
114                 break;
115             }
116             rf.read(&ch, sizeof(ch));
117         }
118     }
119 }
120 } // namespace pels
121 } // namespace openpower
122