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 <phosphor-logging/elog-errors-HostEvent.hpp>
12 #include "config.h"
13 #include "elog_entry.hpp"
14 #include <phosphor-logging/log.hpp>
15 #include "log_manager.hpp"
16 #include "elog_meta.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 
27 void Manager::commit(uint64_t transactionId, std::string errMsg)
28 {
29     constexpr const auto transactionIdVar = "TRANSACTION_ID";
30     // Length of 'TRANSACTION_ID' string.
31     constexpr const auto transactionIdVarSize = strlen(transactionIdVar);
32     // Length of 'TRANSACTION_ID=' string.
33     constexpr const auto transactionIdVarOffset = transactionIdVarSize + 1;
34 
35     sd_journal *j = nullptr;
36     int rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
37     if (rc < 0)
38     {
39         logging::log<logging::level::ERR>("Failed to open journal",
40                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
41         return;
42     }
43 
44     std::string transactionIdStr = std::to_string(transactionId);
45     std::set<std::string> metalist;
46     auto metamap = g_errMetaMap.find(errMsg);
47     if (metamap != g_errMetaMap.end())
48     {
49         metalist.insert(metamap->second.begin(), metamap->second.end());
50     }
51     const auto& metalistHostEvent = g_errMetaMapHostEvent[errMsg];
52     std::vector<std::string> additionalData;
53 
54     // TODO Remove once host event error header file is auto-generated.
55     // Also make metalist a const variable.
56     // Tracking with issue openbmc/phosphor-logging#4
57     for (auto& metaVarStrHostEvent : metalistHostEvent)
58     {
59         metalist.insert(metaVarStrHostEvent);
60     }
61 
62     // Read the journal from the end to get the most recent entry first.
63     // The result from the sd_journal_get_data() is of the form VARIABLE=value.
64     SD_JOURNAL_FOREACH_BACKWARDS(j)
65     {
66         const char *data = nullptr;
67         size_t length = 0;
68 
69         // Look for the transaction id metadata variable
70         rc = sd_journal_get_data(j, transactionIdVar, (const void **)&data,
71                                 &length);
72         if (rc < 0)
73         {
74             // This journal entry does not have the TRANSACTION_ID
75             // metadata variable.
76             continue;
77         }
78 
79         // journald does not guarantee that sd_journal_get_data() returns NULL
80         // terminated strings, so need to specify the size to use to compare,
81         // use the returned length instead of anything that relies on NULL
82         // terminators like strlen().
83         // The data variable is in the form of 'TRANSACTION_ID=1234'. Remove
84         // the TRANSACTION_ID characters plus the (=) sign to do the comparison.
85         // 'data + transactionIdVarOffset' will be in the form of '1234'.
86         // 'length - transactionIdVarOffset' will be the length of '1234'.
87         if ((length <= (transactionIdVarOffset)) ||
88             (transactionIdStr.compare(0,
89                                       transactionIdStr.size(),
90                                       data + transactionIdVarOffset,
91                                       length - transactionIdVarOffset) != 0))
92         {
93             // The value of the TRANSACTION_ID metadata is not the requested
94             // transaction id number.
95             continue;
96         }
97 
98         // Search for all metadata variables in the current journal entry.
99         for (auto i = metalist.cbegin(); i != metalist.cend();)
100         {
101             rc = sd_journal_get_data(j, (*i).c_str(),
102                                     (const void **)&data, &length);
103             if (rc < 0)
104             {
105                 // Metadata variable not found, check next metadata variable.
106                 i++;
107                 continue;
108             }
109 
110             // Metadata variable found, save it and remove it from the set.
111             additionalData.emplace_back(data, length);
112             i = metalist.erase(i);
113         }
114         if (metalist.empty())
115         {
116             // All metadata variables found, break out of journal loop.
117             break;
118         }
119     }
120     if (!metalist.empty())
121     {
122         // Not all the metadata variables were found in the journal.
123         for (auto& metaVarStr : metalist)
124         {
125             logging::log<logging::level::INFO>("Failed to find metadata",
126                     logging::entry("META_FIELD=%s", metaVarStr.c_str()));
127         }
128     }
129 
130     sd_journal_close(j);
131 
132     // Create error Entry dbus object
133     entryId++;
134     auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
135                 std::chrono::system_clock::now().time_since_epoch()).count();
136     auto objPath =  std::string(OBJ_ENTRY) + '/' +
137             std::to_string(entryId);
138 
139     AssociationList objects {};
140     processMetadata(errMsg, additionalData, objects);
141 
142     level reqLevel = level::INFO; // Default to INFO
143     auto levelmap = g_errLevelMap.find(errMsg);
144     if (levelmap != g_errLevelMap.end())
145     {
146         reqLevel = levelmap->second;
147     }
148     entries.insert(std::make_pair(entryId, std::make_unique<Entry>(
149             busLog,
150             objPath,
151             entryId,
152             ms, // Milliseconds since 1970
153             static_cast<Entry::Level>(reqLevel),
154             std::move(errMsg),
155             std::move(additionalData),
156             std::move(objects))));
157     return;
158 }
159 
160 void Manager::processMetadata(const std::string& errorName,
161                               const std::vector<std::string>& additionalData,
162                               AssociationList& objects) const
163 {
164     // additionalData is a list of "metadata=value"
165     constexpr auto separator = '=';
166     for(const auto& entry: additionalData)
167     {
168         auto found = entry.find(separator);
169         if(std::string::npos != found)
170         {
171             auto metadata = entry.substr(0, found);
172             auto iter = meta.find(metadata);
173             if(meta.end() != iter)
174             {
175                 (iter->second)(metadata, additionalData, objects);
176             }
177         }
178     }
179 }
180 
181 } // namespace logging
182 } // namepsace phosphor
183