xref: /openbmc/phosphor-logging/log_manager.cpp (revision 33ff62a2186f517988b66d1a77aee413904ef2ce)
1 #include <fstream>
2 #include <iostream>
3 #include <cstdio>
4 #include <string>
5 #include <vector>
6 #include <sdbusplus/vtable.hpp>
7 #include <systemd/sd-bus.h>
8 #include <systemd/sd-journal.h>
9 #include "log.hpp"
10 
11 using namespace phosphor;
12 
13 /*
14  * @fn commit()
15  * @brief Create an error/event log based on specified id and metadata variable
16  *        names that includes the journal message and the metadata values.
17  */
18 auto commit(sd_bus_message *msg, void *userdata, sd_bus_error *error)
19 {
20     // TODO Change /tmp path to a permanent location on flash
21     constexpr const auto path = "/tmp/elog";
22     constexpr const auto msgIdStr = "_PID";
23 
24     // Read PID
25     int rc = -1;
26     char *msgId = nullptr;
27     rc = sd_bus_message_read(msg, "s", &msgId);
28     if (rc < 0)
29     {
30         logging::log<logging::level::ERR>("Failed to read msg id",
31                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
32         return sd_bus_reply_method_return(msg, "i", rc);
33     }
34 
35     // Create log file
36     std::string filename{};
37     filename.append(path);
38     // TODO Create error logs in their own separate dir once permanent location
39     // on flash is determined. Ex: ../msgId/1
40     filename.append(msgId);
41     std::ofstream efile;
42     efile.open(filename);
43     efile << "{" << std::endl;
44 
45     // Read metadata variables passed as array of strings and store in vector
46     // TODO Read required metadata fields from header file instead
47     rc = sd_bus_message_enter_container(msg, SD_BUS_TYPE_ARRAY, "s");
48     if (rc < 0)
49     {
50         logging::log<logging::level::ERR>("Failed to read metadata vars",
51                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
52         return sd_bus_reply_method_return(msg, nullptr);
53     }
54     const char* metaVar = nullptr;
55     std::vector<const char*> metaList;
56     while ((rc = sd_bus_message_read_basic(msg, 's', &metaVar)) > 0)
57     {
58         metaList.push_back(metaVar);
59     }
60     sd_bus_message_exit_container(msg);
61 
62     sd_journal *j = nullptr;
63     rc = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
64     if (rc < 0)
65     {
66         logging::log<logging::level::ERR>("Failed to open journal",
67                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
68         return sd_bus_reply_method_return(msg, nullptr);
69     }
70 
71     // Read the journal from the end to get the most recent entry first.
72     // The result from the sd_journal_get_data() is of the form VARIABLE=value.
73     SD_JOURNAL_FOREACH_BACKWARDS(j)
74     {
75         const char *data = nullptr;
76         size_t length = 0;
77 
78         // Search for the msg id
79         rc = sd_journal_get_data(j, msgIdStr, (const void **)&data, &length);
80         if (rc < 0)
81         {
82             // Instance not found, continue to next journal entry
83             continue;
84         }
85         std::string result(data);
86         if (result.find(msgId) == std::string::npos)
87         {
88             // Match not found, continue to next journal entry
89             continue;
90         }
91 
92         // Match found, write to file
93         // TODO This is a draft format based on the redfish event logs written
94         // in json, the final openbmc format is to be determined
95         efile << "\t{" << std::endl;
96         efile << "\t\"@" << data << "\"," << std::endl;
97 
98         // Include the journal message
99         rc = sd_journal_get_data(j, "MESSAGE", (const void **)&data, &length);
100         if (rc < 0)
101         {
102             continue;
103         }
104         efile << "\t\"@" << data << "\"," << std::endl;
105 
106         // Search for the metadata variables in the current journal entry
107         for (auto i : metaList)
108         {
109             rc = sd_journal_get_data(j, i, (const void **)&data, &length);
110             if (rc < 0)
111             {
112                 // Not found, continue to next metadata variable
113                 logging::log<logging::level::INFO>("Failed to find metadata",
114                                     logging::entry("META_FIELD=%s", i));
115                 continue;
116             }
117 
118             // Metatdata variable found, write to file
119             efile << "\t\"@" << data << "\"," << std::endl;
120         }
121         efile << "\t}" << std::endl;
122 
123         // TODO Break only once all metadata fields have been found. Implement
124         // once this function reads the metadata fields from the header file.
125         break;
126     }
127     sd_journal_close(j);
128 
129     efile << "}" << std::endl;
130     efile.close();
131     return sd_bus_reply_method_return(msg, nullptr);
132 }
133 
134 constexpr sdbusplus::vtable::vtable_t log_vtable[] =
135 {
136     sdbusplus::vtable::start(),
137     sdbusplus::vtable::method("Commit", "sas", "", commit),
138     sdbusplus::vtable::end()
139 };
140 
141 int main(int argc, char *argv[])
142 {
143     constexpr const auto dbusLogObj = "/xyz/openbmc_project/Logging";
144     constexpr const auto dbusLogName = "xyz.openbmc_project.Logging";
145     int rc = -1;
146     sd_bus *bus = nullptr;
147 
148     rc = sd_bus_open_system(&bus);
149     if (rc < 0)
150     {
151         logging::log<logging::level::ERR>("Failed to open system bus",
152                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
153         goto cleanup;
154     }
155 
156     rc = sd_bus_add_object_manager(bus, nullptr, dbusLogObj);
157     if (rc < 0)
158     {
159         logging::log<logging::level::ERR>("Failed to add object mgr",
160                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
161         goto cleanup;
162     }
163 
164     rc = sd_bus_add_object_vtable(bus,
165                                   nullptr,
166                                   dbusLogObj,
167                                   dbusLogName,
168                                   log_vtable,
169                                   nullptr);
170     if (rc < 0)
171     {
172         logging::log<logging::level::ERR>("Failed to add vtable",
173                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
174         goto cleanup;
175     }
176 
177     rc = sd_bus_request_name(bus, dbusLogName, 0);
178     if (rc < 0)
179     {
180         logging::log<logging::level::ERR>("Failed to acquire service name",
181                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
182     }
183     else
184     {
185         for(;;)
186         {
187             rc = sd_bus_process(bus, nullptr);
188             if (rc < 0)
189             {
190                 logging::log<logging::level::ERR>("Failed to connect to bus",
191                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
192                 break;
193             }
194             if (rc > 0)
195             {
196                 continue;
197             }
198 
199             rc = sd_bus_wait(bus, (uint64_t) - 1);
200             if (rc < 0)
201             {
202                 logging::log<logging::level::ERR>("Failed to wait on bus",
203                            logging::entry("DESCRIPTION=%s", strerror(-rc)));
204                 break;
205             }
206         }
207     }
208 
209 cleanup:
210     sd_bus_unref(bus);
211 
212     return rc;
213 }
214 
215