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