blob: 5aa28ae85af99f6356874616c281d8936ee584f0 [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>
Ed Tanous90926992020-09-11 12:59:33 -070019#include <boost/asio/io_service.hpp>
Jason M. Bills5e049d32018-10-19 12:59:38 -070020#include <boost/container/flat_map.hpp>
21#include <boost/container/flat_set.hpp>
Zhikui Ren672bdfc2020-07-14 11:37:01 -070022#include <pulse_event_monitor.hpp>
23#include <sdbusplus/asio/object_server.hpp>
24#include <sel_logger.hpp>
25#include <threshold_event_monitor.hpp>
Charles Hsudbd77b92020-10-29 11:20:34 +080026#include <watchdog_event_monitor.hpp>
George Hung486e42e2021-04-14 20:20:42 +080027#ifdef SEL_LOGGER_MONITOR_THRESHOLD_ALARM_EVENTS
28#include <threshold_alarm_event_monitor.hpp>
29#endif
Zhikui Ren672bdfc2020-07-14 11:37:01 -070030
Jason M. Billsc4a336f2019-04-23 10:43:10 -070031#include <filesystem>
32#include <fstream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070033#include <iomanip>
34#include <iostream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070035#include <sstream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070036
Lei YUe526b862020-12-03 15:41:59 +080037#ifdef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
38#include <phosphor-logging/elog-errors.hpp>
39#include <phosphor-logging/elog.hpp>
40#include <phosphor-logging/log.hpp>
41#include <xyz/openbmc_project/Logging/SEL/error.hpp>
42
43using namespace phosphor::logging;
44using SELCreated =
45 sdbusplus::xyz::openbmc_project::Logging::SEL::Error::Created;
46#endif
47
Jason M. Bills5e049d32018-10-19 12:59:38 -070048struct DBusInternalError final : public sdbusplus::exception_t
49{
Zhikui Ren672bdfc2020-07-14 11:37:01 -070050 const char* name() const noexcept override
Jason M. Bills5e049d32018-10-19 12:59:38 -070051 {
52 return "org.freedesktop.DBus.Error.Failed";
Patrick Williamsa138ebd2021-09-08 15:46:34 -050053 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -070054 const char* description() const noexcept override
Jason M. Bills5e049d32018-10-19 12:59:38 -070055 {
56 return "internal error";
Patrick Williamsa138ebd2021-09-08 15:46:34 -050057 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -070058 const char* what() const noexcept override
Jason M. Bills5e049d32018-10-19 12:59:38 -070059 {
60 return "org.freedesktop.DBus.Error.Failed: "
61 "internal error";
Patrick Williamsa138ebd2021-09-08 15:46:34 -050062 }
63
64 int get_errno() const noexcept override
65 {
66 return EACCES;
67 }
Jason M. Bills5e049d32018-10-19 12:59:38 -070068};
69
Lei YUe526b862020-12-03 15:41:59 +080070#ifndef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
Zhikui Ren672bdfc2020-07-14 11:37:01 -070071static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
Jason M. Billsc4a336f2019-04-23 10:43:10 -070072{
73 // Loop through the directory looking for ipmi_sel log files
Zhikui Ren672bdfc2020-07-14 11:37:01 -070074 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Billsc4a336f2019-04-23 10:43:10 -070075 std::filesystem::directory_iterator(selLogDir))
76 {
77 std::string filename = dirEnt.path().filename();
78 if (boost::starts_with(filename, selLogFilename))
79 {
80 // If we find an ipmi_sel log file, save the path
81 selLogFiles.emplace_back(selLogDir / filename);
82 }
83 }
84 // As the log files rotate, they are appended with a ".#" that is higher for
85 // the older logs. Since we don't expect more than 10 log files, we
86 // can just sort the list to get them in order from newest to oldest
87 std::sort(selLogFiles.begin(), selLogFiles.end());
88
89 return !selLogFiles.empty();
90}
91
Jason M. Bills5e049d32018-10-19 12:59:38 -070092static unsigned int initializeRecordId(void)
93{
Jason M. Billsc4a336f2019-04-23 10:43:10 -070094 std::vector<std::filesystem::path> selLogFiles;
95 if (!getSELLogFiles(selLogFiles))
Jason M. Bills5e049d32018-10-19 12:59:38 -070096 {
Jason M. Billsc4a336f2019-04-23 10:43:10 -070097 return selInvalidRecID;
Jason M. Bills5e049d32018-10-19 12:59:38 -070098 }
Jason M. Billsc4a336f2019-04-23 10:43:10 -070099 std::ifstream logStream(selLogFiles.front());
100 if (!logStream.is_open())
Jason M. Bills5e049d32018-10-19 12:59:38 -0700101 {
Jason M. Billsc4a336f2019-04-23 10:43:10 -0700102 return selInvalidRecID;
103 }
104 std::string line;
105 std::string newestEntry;
106 while (std::getline(logStream, line))
107 {
108 newestEntry = line;
109 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700110
Jason M. Billsc4a336f2019-04-23 10:43:10 -0700111 std::vector<std::string> newestEntryFields;
112 boost::split(newestEntryFields, newestEntry, boost::is_any_of(" ,"),
113 boost::token_compress_on);
114 if (newestEntryFields.size() < 4)
115 {
116 return selInvalidRecID;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700117 }
Jason M. Billsc4a336f2019-04-23 10:43:10 -0700118
119 return std::stoul(newestEntryFields[1]);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700120}
121
Charles Boyer9f476e82021-07-29 16:33:01 -0500122#ifdef SEL_LOGGER_CLEARS_SEL
123static unsigned int recordId = initializeRecordId();
124
125void clearSelLogFiles()
126{
127 // Clear the SEL by deleting the log files
128 std::vector<std::filesystem::path> selLogFiles;
129 if (getSELLogFiles(selLogFiles))
130 {
131 for (const std::filesystem::path& file : selLogFiles)
132 {
133 std::error_code ec;
134 std::filesystem::remove(file, ec);
135 }
136 }
137
138 recordId = selInvalidRecID;
139
140 // Reload rsyslog so it knows to start new log files
141 boost::asio::io_service io;
142 auto dbus = std::make_shared<sdbusplus::asio::connection>(io);
143 sdbusplus::message::message rsyslogReload = dbus->new_method_call(
144 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
145 "org.freedesktop.systemd1.Manager", "ReloadUnit");
146 rsyslogReload.append("rsyslog.service", "replace");
147 try
148 {
149 sdbusplus::message::message reloadResponse = dbus->call(rsyslogReload);
150 }
151 catch (sdbusplus::exception_t& e)
152 {
153 std::cerr << e.what() << "\n";
154 }
155}
156#endif
157
Jason M. Bills5e049d32018-10-19 12:59:38 -0700158static unsigned int getNewRecordId(void)
159{
Charles Boyer9f476e82021-07-29 16:33:01 -0500160#ifndef SEL_LOGGER_CLEARS_SEL
Jason M. Bills5e049d32018-10-19 12:59:38 -0700161 static unsigned int recordId = initializeRecordId();
162
Jason M. Bills6afe9562019-08-29 16:33:55 -0700163 // If the log has been cleared, also clear the current ID
164 std::vector<std::filesystem::path> selLogFiles;
165 if (!getSELLogFiles(selLogFiles))
166 {
167 recordId = selInvalidRecID;
168 }
Charles Boyer9f476e82021-07-29 16:33:01 -0500169#endif
Jason M. Bills6afe9562019-08-29 16:33:55 -0700170
Jason M. Bills5e049d32018-10-19 12:59:38 -0700171 if (++recordId >= selInvalidRecID)
172 {
173 recordId = 1;
174 }
175 return recordId;
176}
Lei YUe526b862020-12-03 15:41:59 +0800177#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700178
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700179static void toHexStr(const std::vector<uint8_t>& data, std::string& hexStr)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700180{
181 std::stringstream stream;
182 stream << std::hex << std::uppercase << std::setfill('0');
William A. Kennington III2e437262021-07-30 12:04:19 -0700183 for (int v : data)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700184 {
185 stream << std::setw(2) << v;
186 }
187 hexStr = stream.str();
188}
189
Jason M. Bills97be3532018-11-02 13:09:16 -0700190template <typename... T>
William A. Kennington IIId585c592021-07-30 12:10:25 -0700191static uint16_t selAddSystemRecord([[maybe_unused]] const std::string& message,
192 const std::string& path,
193 const std::vector<uint8_t>& selData,
194 const bool& assert, const uint16_t& genId,
195 [[maybe_unused]] T&&... metadata)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700196{
197 // Only 3 bytes of SEL event data are allowed in a system record
198 if (selData.size() > selEvtDataMaxSize)
199 {
200 throw std::invalid_argument("Event data too large");
201 }
202 std::string selDataStr;
203 toHexStr(selData, selDataStr);
204
Lei YUe526b862020-12-03 15:41:59 +0800205#ifdef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
206 using namespace xyz::openbmc_project::Logging::SEL;
Lei YU7d8a3002021-01-21 17:16:19 +0800207 auto entryID = report<SELCreated>(
Lei YUe526b862020-12-03 15:41:59 +0800208 Created::RECORD_TYPE(selSystemType), Created::GENERATOR_ID(genId),
209 Created::SENSOR_DATA(selDataStr.c_str()), Created::EVENT_DIR(assert),
210 Created::SENSOR_PATH(path.c_str()));
Lei YU7d8a3002021-01-21 17:16:19 +0800211 return static_cast<uint16_t>(entryID);
Lei YUe526b862020-12-03 15:41:59 +0800212#else
Jason M. Bills5e049d32018-10-19 12:59:38 -0700213 unsigned int recordId = getNewRecordId();
Jason M. Bills97be3532018-11-02 13:09:16 -0700214 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
215 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d",
216 recordId, "IPMI_SEL_RECORD_TYPE=%x", selSystemType,
217 "IPMI_SEL_GENERATOR_ID=%x", genId,
218 "IPMI_SEL_SENSOR_PATH=%s", path.c_str(),
219 "IPMI_SEL_EVENT_DIR=%x", assert, "IPMI_SEL_DATA=%s",
220 selDataStr.c_str(), std::forward<T>(metadata)..., NULL);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700221 return recordId;
Lei YUe526b862020-12-03 15:41:59 +0800222#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700223}
224
William A. Kennington IIId585c592021-07-30 12:10:25 -0700225static uint16_t selAddOemRecord([[maybe_unused]] const std::string& message,
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700226 const std::vector<uint8_t>& selData,
227 const uint8_t& recordType)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700228{
229 // A maximum of 13 bytes of SEL event data are allowed in an OEM record
230 if (selData.size() > selOemDataMaxSize)
231 {
232 throw std::invalid_argument("Event data too large");
233 }
234 std::string selDataStr;
235 toHexStr(selData, selDataStr);
236
Lei YUe526b862020-12-03 15:41:59 +0800237#ifdef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
238 using namespace xyz::openbmc_project::Logging::SEL;
Lei YU7d8a3002021-01-21 17:16:19 +0800239 auto entryID = report<SELCreated>(
240 Created::RECORD_TYPE(recordType), Created::GENERATOR_ID(0),
241 Created::SENSOR_DATA(selDataStr.c_str()), Created::EVENT_DIR(0),
242 Created::SENSOR_PATH(""));
243 return static_cast<uint16_t>(entryID);
Lei YUe526b862020-12-03 15:41:59 +0800244#else
Jason M. Bills5e049d32018-10-19 12:59:38 -0700245 unsigned int recordId = getNewRecordId();
246 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
247 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d",
248 recordId, "IPMI_SEL_RECORD_TYPE=%x", recordType,
249 "IPMI_SEL_DATA=%s", selDataStr.c_str(), NULL);
250 return recordId;
Lei YUe526b862020-12-03 15:41:59 +0800251#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700252}
253
William A. Kennington IIId585c592021-07-30 12:10:25 -0700254int main(int, char*[])
Jason M. Bills5e049d32018-10-19 12:59:38 -0700255{
256 // setup connection to dbus
257 boost::asio::io_service io;
258 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
259
260 // IPMI SEL Object
261 conn->request_name(ipmiSelObject);
262 auto server = sdbusplus::asio::object_server(conn);
263
264 // Add SEL Interface
265 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceAddSel =
266 server.add_interface(ipmiSelPath, ipmiSelAddInterface);
267
268 // Add a new SEL entry
269 ifaceAddSel->register_method(
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700270 "IpmiSelAdd", [](const std::string& message, const std::string& path,
271 const std::vector<uint8_t>& selData,
272 const bool& assert, const uint16_t& genId) {
Jason M. Bills5e049d32018-10-19 12:59:38 -0700273 return selAddSystemRecord(message, path, selData, assert, genId);
274 });
275 // Add a new OEM SEL entry
276 ifaceAddSel->register_method(
277 "IpmiSelAddOem",
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700278 [](const std::string& message, const std::vector<uint8_t>& selData,
279 const uint8_t& recordType) {
Jason M. Bills5e049d32018-10-19 12:59:38 -0700280 return selAddOemRecord(message, selData, recordType);
281 });
Charles Boyer9f476e82021-07-29 16:33:01 -0500282
283#ifdef SEL_LOGGER_CLEARS_SEL
284#ifndef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
285 // Clear SEL entries
286 ifaceAddSel->register_method("Clear", []() { clearSelLogFiles(); });
287#endif
288#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700289 ifaceAddSel->initialize();
290
291#ifdef SEL_LOGGER_MONITOR_THRESHOLD_EVENTS
Zhikui Ren25b26e12020-06-26 20:18:19 -0700292 sdbusplus::bus::match::match thresholdAssertMonitor =
293 startThresholdAssertMonitor(conn);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700294#endif
295
Nikhil Potadeafbaa092019-03-06 16:18:13 -0800296#ifdef REDFISH_LOG_MONITOR_PULSE_EVENTS
297 sdbusplus::bus::match::match pulseEventMonitor =
298 startPulseEventMonitor(conn);
299#endif
300
Charles Hsudbd77b92020-10-29 11:20:34 +0800301#ifdef SEL_LOGGER_MONITOR_WATCHDOG_EVENTS
302 sdbusplus::bus::match::match watchdogEventMonitor =
303 startWatchdogEventMonitor(conn);
304#endif
George Hung486e42e2021-04-14 20:20:42 +0800305
306#ifdef SEL_LOGGER_MONITOR_THRESHOLD_ALARM_EVENTS
307 startThresholdAlarmMonitor(conn);
308#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700309 io.run();
310
311 return 0;
312}