1 #include <fstream>
2 #include <iostream>
3 #include <chrono>
4 #include <cstdio>
5 #include <set>
6 #include <string>
7 #include <vector>
8 #include <sdbusplus/vtable.hpp>
9 #include <systemd/sd-bus.h>
10 #include <systemd/sd-journal.h>
11 #include "config.h"
12 #include "elog_entry.hpp"
13 #include <phosphor-logging/log.hpp>
14 #include "log_manager.hpp"
15 #include "elog_meta.hpp"
16 #include "elog_serialize.hpp"
17 
18 using namespace phosphor::logging;
19 extern const std::map<metadata::Metadata,
20                       std::function<metadata::associations::Type>> meta;
21 
22 namespace phosphor
23 {
24 namespace logging
25 {
26 namespace internal
27 {
28 void Manager::commit(uint64_t transactionId, std::string errMsg)
29 {
30     auto reqLevel = level::ERR; // Default to ERR
31     auto levelmap = g_errLevelMap.find(errMsg);
32 
33     if (levelmap != g_errLevelMap.end())
34     {
35         reqLevel = levelmap->second;
36     }
37 
38     if (static_cast<Entry::Level>(reqLevel) < Entry::sevLowerLimit)
39     {
40         if (realErrors.size() >= ERROR_CAP)
41         {
42             erase(realErrors.front());
43         }
44     }
45     else
46     {
47         if (infoErrors.size() >= ERROR_INFO_CAP)
48         {
49             erase(infoErrors.front());
50         }
51     }
52     constexpr const auto transactionIdVar = "TRANSACTION_ID";
53     // Length of 'TRANSACTION_ID' string.
54     constexpr const auto transactionIdVarSize = strlen(transactionIdVar);
55     // Length of 'TRANSACTION_ID=' string.
56     constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
57 
58     // Flush all the pending log messages into the journal via Synchronize
59     constexpr auto JOURNAL_BUSNAME = "org.freedesktop.journal1";
60     constexpr auto JOURNAL_PATH = "/org/freedesktop/journal1";
61     constexpr auto JOURNAL_INTERFACE = "org.freedesktop.journal1";
62     auto bus = sdbusplus::bus::new_default();
63     auto method = bus.new_method_call(JOURNAL_BUSNAME, JOURNAL_PATH,
64                                       JOURNAL_INTERFACE, "Synchronize");
65     bus.call_noreply(method);
66 
67     sd_journal *j = nullptr;
68     int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
69     if (rc < 0)
70     {
71         logging::log<logging::level::ERR>("Failed to open journal",
72                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
73         return;
74     }
75 
76     std::string transactionIdStr = std::to_string(transactionId);
77     std::set<std::string> metalist;
78     auto metamap = g_errMetaMap.find(errMsg);
79     if (metamap != g_errMetaMap.end())
80     {
81         metalist.insert(metamap->second.begin(), metamap->second.end());
82     }
83 
84     //Add _PID field information in AdditionalData.
85     metalist.insert("_PID");
86 
87     std::vector<std::string> additionalData;
88 
89     // Read the journal from the end to get the most recent entry first.
90     // The result from the sd_journal_get_data() is of the form VARIABLE=value.
91     SD_JOURNAL_FOREACH_BACKWARDS(j)
92     {
93         const char *data = nullptr;
94         size_t length = 0;
95 
96         // Look for the transaction id metadata variable
97         rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data,
98                                 &length);
99         if (rc < 0)
100         {
101             // This journal entry does not have the TRANSACTION_ID
102             // metadata variable.
103             continue;
104         }
105 
106         // journald does not guarantee that sd_journal_get_data() returns NULL
107         // terminated strings, so need to specify the size to use to compare,
108         // use the returned length instead of anything that relies on NULL
109         // terminators like strlen().
110         // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
111         // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
112         // 'data + transactionIdVarOffset' will be in the form of '1234'.
113         // 'length - transactionIdVarOffset' will be the length of '1234'.
114         if ((length <= (transactionIdVarOffset)) ||
115             (transactionIdStr.compare(0,
116                                       transactionIdStr.size(),
117                                       data + transactionIdVarOffset,
118                                       length - transactionIdVarOffset) != 0))
119         {
120             // The value of the TRANSACTION_ID metadata is not the requested
121             // transaction id number.
122             continue;
123         }
124 
125         // Search for all metadata variables in the current journal entry.
126         for (auto i = metalist.cbegin(); i != metalist.cend();)
127         {
128             rc = sd_journal_get_data(j, (*i).c_str(),
129                                     (const void **)&data, &length);
130             if (rc < 0)
131             {
132                 // Metadata variable not found, check next metadata variable.
133                 i++;
134                 continue;
135             }
136 
137             // Metadata variable found, save it and remove it from the set.
138             additionalData.emplace_back(data, length);
139             i = metalist.erase(i);
140         }
141         if (metalist.empty())
142         {
143             // All metadata variables found, break out of journal loop.
144             break;
145         }
146     }
147     if (!metalist.empty())
148     {
149         // Not all the metadata variables were found in the journal.
150         for (auto& metaVarStr : metalist)
151         {
152             logging::log<logging::level::INFO>("Failed to find metadata",
153                     logging::entry("META_FIELD=%s", metaVarStr.c_str()));
154         }
155     }
156 
157     sd_journal_close(j);
158 
159     // Create error Entry dbus object
160     entryId++;
161     if (static_cast<Entry::Level>(reqLevel) >= Entry::sevLowerLimit)
162     {
163         infoErrors.push_back(entryId);
164     }
165     else
166     {
167         realErrors.push_back(entryId);
168     }
169     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
170                 std::chrono::system_clock::now().time_since_epoch()).count();
171     auto objPath =  std::string(OBJ_ENTRY) + '/' +
172             std::to_string(entryId);
173 
174     AssociationList objects {};
175     processMetadata(errMsg, additionalData, objects);
176 
177     auto e = std::make_unique<Entry>(
178                  busLog,
179                  objPath,
180                  entryId,
181                  ms, // Milliseconds since 1970
182                  static_cast<Entry::Level>(reqLevel),
183                  std::move(errMsg),
184                  std::move(additionalData),
185                  std::move(objects),
186                  *this);
187     serialize(*e);
188     entries.insert(std::make_pair(entryId, std::move(e)));
189 }
190 
191 void Manager::processMetadata(const std::string& errorName,
192                               const std::vector<std::string>& additionalData,
193                               AssociationList& objects) const
194 {
195     // additionalData is a list of "metadata=value"
196     constexpr auto separator = '=';
197     for(const auto& entry: additionalData)
198     {
199         auto found = entry.find(separator);
200         if(std::string::npos != found)
201         {
202             auto metadata = entry.substr(0, found);
203             auto iter = meta.find(metadata);
204             if(meta.end() != iter)
205             {
206                 (iter->second)(metadata, additionalData, objects);
207             }
208         }
209     }
210 }
211 
212 void Manager::erase(uint32_t entryId)
213 {
214     auto entry = entries.find(entryId);
215     if(entries.end() != entry)
216     {
217         // Delete the persistent representation of this error.
218         fs::path errorPath(ERRLOG_PERSIST_PATH);
219         errorPath /= std::to_string(entryId);
220         fs::remove(errorPath);
221 
222         auto removeId = [](std::list<uint32_t>& ids , uint32_t id)
223         {
224             auto it = std::find(ids.begin(), ids.end(), id);
225             if (it != ids.end())
226             {
227                 ids.erase(it);
228             }
229         };
230         if (entry->second->severity() >= Entry::sevLowerLimit)
231         {
232             removeId(infoErrors, entryId);
233         }
234         else
235         {
236             removeId(realErrors, entryId);
237         }
238         entries.erase(entry);
239     }
240     else
241     {
242         logging::log<level::ERR>("Invalid entry ID to delete",
243                 logging::entry("ID=%d", entryId));
244     }
245 }
246 
247 void Manager::restore()
248 {
249     auto sanity = [](const auto& id, const auto& restoredId)
250     {
251         return id == restoredId;
252     };
253     std::vector<uint32_t> errorIds;
254 
255     fs::path dir(ERRLOG_PERSIST_PATH);
256     if (!fs::exists(dir) || fs::is_empty(dir))
257     {
258         return;
259     }
260 
261     for(auto& file: fs::directory_iterator(dir))
262     {
263         auto id = file.path().filename().c_str();
264         auto idNum = std::stol(id);
265         auto e = std::make_unique<Entry>(
266                      busLog,
267                      std::string(OBJ_ENTRY) + '/' + id,
268                      idNum,
269                      *this);
270         if (deserialize(file.path(), *e))
271         {
272             //validate the restored error entry id
273             if (sanity(static_cast<uint32_t>(idNum), e->id()))
274             {
275                 e->emit_object_added();
276                 if (e->severity() >= Entry::sevLowerLimit)
277                 {
278                     infoErrors.push_back(idNum);
279                 }
280                 else
281                 {
282                     realErrors.push_back(idNum);
283                 }
284 
285                 entries.insert(std::make_pair(idNum, std::move(e)));
286                 errorIds.push_back(idNum);
287             }
288             else
289             {
290                 logging::log<logging::level::ERR>(
291                     "Failed in sanity check while restoring error entry. "
292                     "Ignoring error entry",
293                     logging::entry("ID_NUM=%d", idNum),
294                     logging::entry("ENTRY_ID=%d", e->id()));
295             }
296         }
297     }
298 
299     if (!errorIds.empty())
300     {
301         entryId = *(std::max_element(errorIds.begin(), errorIds.end()));
302     }
303 }
304 
305 } // namespace internal
306 } // namespace logging
307 } // namepsace phosphor
308