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