blob: 89eee7d88eefcf4727e0857898c61118c6c9c5cd [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
103 if (++recordId >= selInvalidRecID)
104 {
105 recordId = 1;
106 }
107 return recordId;
108}
109
110static void toHexStr(const std::vector<uint8_t> &data, std::string &hexStr)
111{
112 std::stringstream stream;
113 stream << std::hex << std::uppercase << std::setfill('0');
114 for (const int &v : data)
115 {
116 stream << std::setw(2) << v;
117 }
118 hexStr = stream.str();
119}
120
Jason M. Bills97be3532018-11-02 13:09:16 -0700121template <typename... T>
122static uint16_t
123 selAddSystemRecord(const std::string &message, const std::string &path,
124 const std::vector<uint8_t> &selData, const bool &assert,
125 const uint16_t &genId, T &&... metadata)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700126{
127 // Only 3 bytes of SEL event data are allowed in a system record
128 if (selData.size() > selEvtDataMaxSize)
129 {
130 throw std::invalid_argument("Event data too large");
131 }
132 std::string selDataStr;
133 toHexStr(selData, selDataStr);
134
135 unsigned int recordId = getNewRecordId();
Jason M. Bills97be3532018-11-02 13:09:16 -0700136 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
137 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d",
138 recordId, "IPMI_SEL_RECORD_TYPE=%x", selSystemType,
139 "IPMI_SEL_GENERATOR_ID=%x", genId,
140 "IPMI_SEL_SENSOR_PATH=%s", path.c_str(),
141 "IPMI_SEL_EVENT_DIR=%x", assert, "IPMI_SEL_DATA=%s",
142 selDataStr.c_str(), std::forward<T>(metadata)..., NULL);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700143 return recordId;
144}
145
146static uint16_t selAddOemRecord(const std::string &message,
147 const std::vector<uint8_t> &selData,
148 const uint8_t &recordType)
149{
150 // A maximum of 13 bytes of SEL event data are allowed in an OEM record
151 if (selData.size() > selOemDataMaxSize)
152 {
153 throw std::invalid_argument("Event data too large");
154 }
155 std::string selDataStr;
156 toHexStr(selData, selDataStr);
157
158 unsigned int recordId = getNewRecordId();
159 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
160 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d",
161 recordId, "IPMI_SEL_RECORD_TYPE=%x", recordType,
162 "IPMI_SEL_DATA=%s", selDataStr.c_str(), NULL);
163 return recordId;
164}
165
166int main(int argc, char *argv[])
167{
168 // setup connection to dbus
169 boost::asio::io_service io;
170 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
171
172 // IPMI SEL Object
173 conn->request_name(ipmiSelObject);
174 auto server = sdbusplus::asio::object_server(conn);
175
176 // Add SEL Interface
177 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceAddSel =
178 server.add_interface(ipmiSelPath, ipmiSelAddInterface);
179
180 // Add a new SEL entry
181 ifaceAddSel->register_method(
182 "IpmiSelAdd", [](const std::string &message, const std::string &path,
183 const std::vector<uint8_t> &selData,
184 const bool &assert, const uint16_t &genId) {
185 return selAddSystemRecord(message, path, selData, assert, genId);
186 });
187 // Add a new OEM SEL entry
188 ifaceAddSel->register_method(
189 "IpmiSelAddOem",
190 [](const std::string &message, const std::vector<uint8_t> &selData,
191 const uint8_t &recordType) {
192 return selAddOemRecord(message, selData, recordType);
193 });
194 ifaceAddSel->initialize();
195
196#ifdef SEL_LOGGER_MONITOR_THRESHOLD_EVENTS
197 sdbusplus::bus::match::match thresholdEventMonitor =
198 startThresholdEventMonitor(conn);
199#endif
200
Nikhil Potadeafbaa092019-03-06 16:18:13 -0800201#ifdef REDFISH_LOG_MONITOR_PULSE_EVENTS
202 sdbusplus::bus::match::match pulseEventMonitor =
203 startPulseEventMonitor(conn);
204#endif
205
Jason M. Bills5e049d32018-10-19 12:59:38 -0700206 io.run();
207
208 return 0;
209}