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