blob: 7fe52fb810413af48f1c86bb30b0f5c138588e9c [file] [log] [blame]
Jason M. Bills5e049d32018-10-19 12:59:38 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include <systemd/sd-journal.h>
17
Jason M. Billsc4a336f2019-04-23 10:43:10 -070018#include <boost/algorithm/string.hpp>
Jason M. Bills5e049d32018-10-19 12:59:38 -070019#include <boost/container/flat_map.hpp>
20#include <boost/container/flat_set.hpp>
Jason M. Billsc4a336f2019-04-23 10:43:10 -070021#include <filesystem>
22#include <fstream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070023#include <iomanip>
24#include <iostream>
Nikhil Potadeafbaa092019-03-06 16:18:13 -080025#include <pulse_event_monitor.hpp>
Jason M. Bills5e049d32018-10-19 12:59:38 -070026#include <sdbusplus/asio/object_server.hpp>
27#include <sel_logger.hpp>
28#include <sstream>
29#include <threshold_event_monitor.hpp>
30
31struct DBusInternalError final : public sdbusplus::exception_t
32{
33 const char *name() const noexcept override
34 {
35 return "org.freedesktop.DBus.Error.Failed";
36 };
37 const char *description() const noexcept override
38 {
39 return "internal error";
40 };
41 const char *what() const noexcept override
42 {
43 return "org.freedesktop.DBus.Error.Failed: "
44 "internal error";
45 };
46};
47
Jason M. Billsc4a336f2019-04-23 10:43:10 -070048static bool getSELLogFiles(std::vector<std::filesystem::path> &selLogFiles)
49{
50 // Loop through the directory looking for ipmi_sel log files
51 for (const std::filesystem::directory_entry &dirEnt :
52 std::filesystem::directory_iterator(selLogDir))
53 {
54 std::string filename = dirEnt.path().filename();
55 if (boost::starts_with(filename, selLogFilename))
56 {
57 // If we find an ipmi_sel log file, save the path
58 selLogFiles.emplace_back(selLogDir / filename);
59 }
60 }
61 // As the log files rotate, they are appended with a ".#" that is higher for
62 // the older logs. Since we don't expect more than 10 log files, we
63 // can just sort the list to get them in order from newest to oldest
64 std::sort(selLogFiles.begin(), selLogFiles.end());
65
66 return !selLogFiles.empty();
67}
68
Jason M. Bills5e049d32018-10-19 12:59:38 -070069static unsigned int initializeRecordId(void)
70{
Jason M. Billsc4a336f2019-04-23 10:43:10 -070071 std::vector<std::filesystem::path> selLogFiles;
72 if (!getSELLogFiles(selLogFiles))
Jason M. Bills5e049d32018-10-19 12:59:38 -070073 {
Jason M. Billsc4a336f2019-04-23 10:43:10 -070074 return selInvalidRecID;
Jason M. Bills5e049d32018-10-19 12:59:38 -070075 }
Jason M. Billsc4a336f2019-04-23 10:43:10 -070076 std::ifstream logStream(selLogFiles.front());
77 if (!logStream.is_open())
Jason M. Bills5e049d32018-10-19 12:59:38 -070078 {
Jason M. Billsc4a336f2019-04-23 10:43:10 -070079 return selInvalidRecID;
80 }
81 std::string line;
82 std::string newestEntry;
83 while (std::getline(logStream, line))
84 {
85 newestEntry = line;
86 }
Jason M. Bills5e049d32018-10-19 12:59:38 -070087
Jason M. Billsc4a336f2019-04-23 10:43:10 -070088 std::vector<std::string> newestEntryFields;
89 boost::split(newestEntryFields, newestEntry, boost::is_any_of(" ,"),
90 boost::token_compress_on);
91 if (newestEntryFields.size() < 4)
92 {
93 return selInvalidRecID;
Jason M. Bills5e049d32018-10-19 12:59:38 -070094 }
Jason M. Billsc4a336f2019-04-23 10:43:10 -070095
96 return std::stoul(newestEntryFields[1]);
Jason M. Bills5e049d32018-10-19 12:59:38 -070097}
98
99static unsigned int getNewRecordId(void)
100{
101 static unsigned int recordId = initializeRecordId();
102
Jason M. Bills6afe9562019-08-29 16:33:55 -0700103 // If the log has been cleared, also clear the current ID
104 std::vector<std::filesystem::path> selLogFiles;
105 if (!getSELLogFiles(selLogFiles))
106 {
107 recordId = selInvalidRecID;
108 }
109
Jason M. Bills5e049d32018-10-19 12:59:38 -0700110 if (++recordId >= selInvalidRecID)
111 {
112 recordId = 1;
113 }
114 return recordId;
115}
116
117static void toHexStr(const std::vector<uint8_t> &data, std::string &hexStr)
118{
119 std::stringstream stream;
120 stream << std::hex << std::uppercase << std::setfill('0');
121 for (const int &v : data)
122 {
123 stream << std::setw(2) << v;
124 }
125 hexStr = stream.str();
126}
127
Jason M. Bills97be3532018-11-02 13:09:16 -0700128template <typename... T>
129static uint16_t
130 selAddSystemRecord(const std::string &message, const std::string &path,
131 const std::vector<uint8_t> &selData, const bool &assert,
132 const uint16_t &genId, T &&... metadata)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700133{
134 // Only 3 bytes of SEL event data are allowed in a system record
135 if (selData.size() > selEvtDataMaxSize)
136 {
137 throw std::invalid_argument("Event data too large");
138 }
139 std::string selDataStr;
140 toHexStr(selData, selDataStr);
141
142 unsigned int recordId = getNewRecordId();
Jason M. Bills97be3532018-11-02 13:09:16 -0700143 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
144 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d",
145 recordId, "IPMI_SEL_RECORD_TYPE=%x", selSystemType,
146 "IPMI_SEL_GENERATOR_ID=%x", genId,
147 "IPMI_SEL_SENSOR_PATH=%s", path.c_str(),
148 "IPMI_SEL_EVENT_DIR=%x", assert, "IPMI_SEL_DATA=%s",
149 selDataStr.c_str(), std::forward<T>(metadata)..., NULL);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700150 return recordId;
151}
152
153static uint16_t selAddOemRecord(const std::string &message,
154 const std::vector<uint8_t> &selData,
155 const uint8_t &recordType)
156{
157 // A maximum of 13 bytes of SEL event data are allowed in an OEM record
158 if (selData.size() > selOemDataMaxSize)
159 {
160 throw std::invalid_argument("Event data too large");
161 }
162 std::string selDataStr;
163 toHexStr(selData, selDataStr);
164
165 unsigned int recordId = getNewRecordId();
166 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
167 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d",
168 recordId, "IPMI_SEL_RECORD_TYPE=%x", recordType,
169 "IPMI_SEL_DATA=%s", selDataStr.c_str(), NULL);
170 return recordId;
171}
172
173int main(int argc, char *argv[])
174{
175 // setup connection to dbus
176 boost::asio::io_service io;
177 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
178
179 // IPMI SEL Object
180 conn->request_name(ipmiSelObject);
181 auto server = sdbusplus::asio::object_server(conn);
182
183 // Add SEL Interface
184 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceAddSel =
185 server.add_interface(ipmiSelPath, ipmiSelAddInterface);
186
187 // Add a new SEL entry
188 ifaceAddSel->register_method(
189 "IpmiSelAdd", [](const std::string &message, const std::string &path,
190 const std::vector<uint8_t> &selData,
191 const bool &assert, const uint16_t &genId) {
192 return selAddSystemRecord(message, path, selData, assert, genId);
193 });
194 // Add a new OEM SEL entry
195 ifaceAddSel->register_method(
196 "IpmiSelAddOem",
197 [](const std::string &message, const std::vector<uint8_t> &selData,
198 const uint8_t &recordType) {
199 return selAddOemRecord(message, selData, recordType);
200 });
201 ifaceAddSel->initialize();
202
203#ifdef SEL_LOGGER_MONITOR_THRESHOLD_EVENTS
204 sdbusplus::bus::match::match thresholdEventMonitor =
205 startThresholdEventMonitor(conn);
206#endif
207
Nikhil Potadeafbaa092019-03-06 16:18:13 -0800208#ifdef REDFISH_LOG_MONITOR_PULSE_EVENTS
209 sdbusplus::bus::match::match pulseEventMonitor =
210 startPulseEventMonitor(conn);
211#endif
212
Jason M. Bills5e049d32018-10-19 12:59:38 -0700213 io.run();
214
215 return 0;
216}