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