blob: 6df8e44f167c7b3f4a455f4d78a5a18a990187ff [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 Tanousc3526342023-03-06 13:37:53 -080019#include <boost/asio/io_context.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
JinFuLin7c2810b2022-12-02 13:55:28 +080030#ifdef SEL_LOGGER_MONITOR_HOST_ERROR_EVENTS
31#include <host_error_event_monitor.hpp>
32#endif
Zhikui Ren672bdfc2020-07-14 11:37:01 -070033
Jason M. Billsc4a336f2019-04-23 10:43:10 -070034#include <filesystem>
35#include <fstream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070036#include <iomanip>
37#include <iostream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070038#include <sstream>
Jason M. Bills5e049d32018-10-19 12:59:38 -070039
40struct DBusInternalError final : public sdbusplus::exception_t
41{
Zhikui Ren672bdfc2020-07-14 11:37:01 -070042 const char* name() const noexcept override
Jason M. Bills5e049d32018-10-19 12:59:38 -070043 {
44 return "org.freedesktop.DBus.Error.Failed";
Patrick Williamsa138ebd2021-09-08 15:46:34 -050045 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -070046 const char* description() const noexcept override
Jason M. Bills5e049d32018-10-19 12:59:38 -070047 {
48 return "internal error";
Patrick Williamsa138ebd2021-09-08 15:46:34 -050049 }
Zhikui Ren672bdfc2020-07-14 11:37:01 -070050 const char* what() const noexcept override
Jason M. Bills5e049d32018-10-19 12:59:38 -070051 {
52 return "org.freedesktop.DBus.Error.Failed: "
53 "internal error";
Patrick Williamsa138ebd2021-09-08 15:46:34 -050054 }
55
56 int get_errno() const noexcept override
57 {
58 return EACCES;
59 }
Jason M. Bills5e049d32018-10-19 12:59:38 -070060};
61
Lei YUe526b862020-12-03 15:41:59 +080062#ifndef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
Zhikui Ren672bdfc2020-07-14 11:37:01 -070063static bool getSELLogFiles(std::vector<std::filesystem::path>& selLogFiles)
Jason M. Billsc4a336f2019-04-23 10:43:10 -070064{
65 // Loop through the directory looking for ipmi_sel log files
Zhikui Ren672bdfc2020-07-14 11:37:01 -070066 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Billsc4a336f2019-04-23 10:43:10 -070067 std::filesystem::directory_iterator(selLogDir))
68 {
69 std::string filename = dirEnt.path().filename();
70 if (boost::starts_with(filename, selLogFilename))
71 {
72 // If we find an ipmi_sel log file, save the path
73 selLogFiles.emplace_back(selLogDir / filename);
74 }
75 }
76 // As the log files rotate, they are appended with a ".#" that is higher for
77 // the older logs. Since we don't expect more than 10 log files, we
78 // can just sort the list to get them in order from newest to oldest
79 std::sort(selLogFiles.begin(), selLogFiles.end());
80
81 return !selLogFiles.empty();
82}
83
Jonico Eustaquio0acff272024-04-23 15:12:06 -050084static unsigned int initializeRecordId()
Jason M. Bills5e049d32018-10-19 12:59:38 -070085{
Jason M. Billsc4a336f2019-04-23 10:43:10 -070086 std::vector<std::filesystem::path> selLogFiles;
87 if (!getSELLogFiles(selLogFiles))
Jason M. Bills5e049d32018-10-19 12:59:38 -070088 {
Jonico Eustaquio0acff272024-04-23 15:12:06 -050089 return 0;
Jason M. Bills5e049d32018-10-19 12:59:38 -070090 }
Jason M. Billsc4a336f2019-04-23 10:43:10 -070091 std::ifstream logStream(selLogFiles.front());
92 if (!logStream.is_open())
Jason M. Bills5e049d32018-10-19 12:59:38 -070093 {
Jonico Eustaquio0acff272024-04-23 15:12:06 -050094 return 0;
Jason M. Billsc4a336f2019-04-23 10:43:10 -070095 }
96 std::string line;
97 std::string newestEntry;
98 while (std::getline(logStream, line))
99 {
100 newestEntry = line;
101 }
Jason M. Bills5e049d32018-10-19 12:59:38 -0700102
Jason M. Billsc4a336f2019-04-23 10:43:10 -0700103 std::vector<std::string> newestEntryFields;
104 boost::split(newestEntryFields, newestEntry, boost::is_any_of(" ,"),
105 boost::token_compress_on);
106 if (newestEntryFields.size() < 4)
107 {
Jonico Eustaquio0acff272024-04-23 15:12:06 -0500108 return 0;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700109 }
Jason M. Billsc4a336f2019-04-23 10:43:10 -0700110
111 return std::stoul(newestEntryFields[1]);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700112}
113
Charles Boyer9f476e82021-07-29 16:33:01 -0500114static unsigned int recordId = initializeRecordId();
115
Alexander Hansen8c023192023-09-26 09:15:18 +0200116static void saveClearSelTimestamp()
117{
118 int fd = open("/var/lib/ipmi/sel_erase_time",
119 O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
120 if (fd < 0)
121 {
122 std::cerr << "Failed to open file\n";
123 return;
124 }
125
126 if (futimens(fd, NULL) < 0)
127 {
128 std::cerr << "Failed to update SEL cleared timestamp: "
129 << std::string(strerror(errno));
130 }
131 close(fd);
132}
133
Charles Boyer9f476e82021-07-29 16:33:01 -0500134void clearSelLogFiles()
135{
Alexander Hansen8c023192023-09-26 09:15:18 +0200136 saveClearSelTimestamp();
137
Charles Boyer9f476e82021-07-29 16:33:01 -0500138 // Clear the SEL by deleting the log files
139 std::vector<std::filesystem::path> selLogFiles;
140 if (getSELLogFiles(selLogFiles))
141 {
142 for (const std::filesystem::path& file : selLogFiles)
143 {
144 std::error_code ec;
145 std::filesystem::remove(file, ec);
146 }
147 }
148
Jonico Eustaquio0acff272024-04-23 15:12:06 -0500149 recordId = 0;
Charles Boyer9f476e82021-07-29 16:33:01 -0500150
151 // Reload rsyslog so it knows to start new log files
Ed Tanousc3526342023-03-06 13:37:53 -0800152 boost::asio::io_context io;
Charles Boyer9f476e82021-07-29 16:33:01 -0500153 auto dbus = std::make_shared<sdbusplus::asio::connection>(io);
Patrick Williamsccef2272022-07-22 19:26:54 -0500154 sdbusplus::message_t rsyslogReload = dbus->new_method_call(
Charles Boyer9f476e82021-07-29 16:33:01 -0500155 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
156 "org.freedesktop.systemd1.Manager", "ReloadUnit");
157 rsyslogReload.append("rsyslog.service", "replace");
158 try
159 {
Patrick Williamsccef2272022-07-22 19:26:54 -0500160 sdbusplus::message_t reloadResponse = dbus->call(rsyslogReload);
Charles Boyer9f476e82021-07-29 16:33:01 -0500161 }
Patrick Williams3f4cd972021-10-06 12:42:50 -0500162 catch (const sdbusplus::exception_t& e)
Charles Boyer9f476e82021-07-29 16:33:01 -0500163 {
164 std::cerr << e.what() << "\n";
165 }
166}
Charles Boyer9f476e82021-07-29 16:33:01 -0500167
Jonico Eustaquio0acff272024-04-23 15:12:06 -0500168static unsigned int getNewRecordId()
Jason M. Bills5e049d32018-10-19 12:59:38 -0700169{
Jason M. Bills5e049d32018-10-19 12:59:38 -0700170 if (++recordId >= selInvalidRecID)
171 {
Jonico Eustaquio0acff272024-04-23 15:12:06 -0500172 recordId = selInvalidRecID;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700173 }
174 return recordId;
175}
Lei YUe526b862020-12-03 15:41:59 +0800176#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700177
Zhikui Ren672bdfc2020-07-14 11:37:01 -0700178static void toHexStr(const std::vector<uint8_t>& data, std::string& hexStr)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700179{
180 std::stringstream stream;
181 stream << std::hex << std::uppercase << std::setfill('0');
William A. Kennington III2e437262021-07-30 12:04:19 -0700182 for (int v : data)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700183 {
184 stream << std::setw(2) << v;
185 }
186 hexStr = stream.str();
187}
188
Jason M. Bills97be3532018-11-02 13:09:16 -0700189template <typename... T>
Vincent Chou92721502024-01-24 15:09:24 -0600190static uint16_t selAddSystemRecord(
Konstantin Aladyshev6f5342d2023-04-19 09:23:11 +0000191 [[maybe_unused]] std::shared_ptr<sdbusplus::asio::connection> conn,
192 [[maybe_unused]] const std::string& message, const std::string& path,
193 const std::vector<uint8_t>& selData, const bool& assert,
194 const uint16_t& genId, [[maybe_unused]] T&&... metadata)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700195{
196 // Only 3 bytes of SEL event data are allowed in a system record
197 if (selData.size() > selEvtDataMaxSize)
198 {
199 throw std::invalid_argument("Event data too large");
200 }
201 std::string selDataStr;
202 toHexStr(selData, selDataStr);
203
Lei YUe526b862020-12-03 15:41:59 +0800204#ifdef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
Konstantin Aladyshev6f5342d2023-04-19 09:23:11 +0000205 sdbusplus::message_t AddToLog = conn->new_method_call(
206 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
207 "xyz.openbmc_project.Logging.Create", "Create");
208
209 std::string journalMsg(message + " from " + path + ": " +
210 " RecordType=" + std::to_string(selSystemType) +
211 ", GeneratorID=" + std::to_string(genId) +
212 ", EventDir=" + std::to_string(assert) +
213 ", EventData=" + selDataStr);
214
215 AddToLog.append(journalMsg,
216 "xyz.openbmc_project.Logging.Entry.Level.Informational",
217 std::map<std::string, std::string>(
218 {{"SENSOR_PATH", path},
219 {"GENERATOR_ID", std::to_string(genId)},
220 {"RECORD_TYPE", std::to_string(selSystemType)},
221 {"EVENT_DIR", std::to_string(assert)},
222 {"SENSOR_DATA", selDataStr}}));
223 conn->call(AddToLog);
Vincent Chou92721502024-01-24 15:09:24 -0600224 return 0;
Lei YUe526b862020-12-03 15:41:59 +0800225#else
Jason M. Bills5e049d32018-10-19 12:59:38 -0700226 unsigned int recordId = getNewRecordId();
Jonico Eustaquio0acff272024-04-23 15:12:06 -0500227 if (recordId < selInvalidRecID)
228 {
229 sd_journal_send(
230 "MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
231 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d", recordId,
232 "IPMI_SEL_RECORD_TYPE=%x", selSystemType,
233 "IPMI_SEL_GENERATOR_ID=%x", genId, "IPMI_SEL_SENSOR_PATH=%s",
234 path.c_str(), "IPMI_SEL_EVENT_DIR=%x", assert, "IPMI_SEL_DATA=%s",
235 selDataStr.c_str(), std::forward<T>(metadata)..., NULL);
236 }
Vincent Chou92721502024-01-24 15:09:24 -0600237 return recordId;
Lei YUe526b862020-12-03 15:41:59 +0800238#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700239}
240
Vincent Chou92721502024-01-24 15:09:24 -0600241static uint16_t selAddOemRecord(
Konstantin Aladyshev6f5342d2023-04-19 09:23:11 +0000242 [[maybe_unused]] std::shared_ptr<sdbusplus::asio::connection> conn,
243 [[maybe_unused]] const std::string& message,
244 const std::vector<uint8_t>& selData, const uint8_t& recordType)
Jason M. Bills5e049d32018-10-19 12:59:38 -0700245{
246 // A maximum of 13 bytes of SEL event data are allowed in an OEM record
247 if (selData.size() > selOemDataMaxSize)
248 {
249 throw std::invalid_argument("Event data too large");
250 }
251 std::string selDataStr;
252 toHexStr(selData, selDataStr);
253
Lei YUe526b862020-12-03 15:41:59 +0800254#ifdef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
Konstantin Aladyshev6f5342d2023-04-19 09:23:11 +0000255 sdbusplus::message_t AddToLog = conn->new_method_call(
256 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
257 "xyz.openbmc_project.Logging.Create", "Create");
258
259 std::string journalMsg(
260 message + ": " + " RecordType=" + std::to_string(recordType) +
261 ", GeneratorID=" + std::to_string(0) +
262 ", EventDir=" + std::to_string(0) + ", EventData=" + selDataStr);
263
264 AddToLog.append(journalMsg,
265 "xyz.openbmc_project.Logging.Entry.Level.Informational",
266 std::map<std::string, std::string>(
267 {{"SENSOR_PATH", ""},
268 {"GENERATOR_ID", std::to_string(0)},
269 {"RECORD_TYPE", std::to_string(recordType)},
270 {"EVENT_DIR", std::to_string(0)},
271 {"SENSOR_DATA", selDataStr}}));
272 conn->call(AddToLog);
Vincent Chou92721502024-01-24 15:09:24 -0600273 return 0;
Lei YUe526b862020-12-03 15:41:59 +0800274#else
Jason M. Bills5e049d32018-10-19 12:59:38 -0700275 unsigned int recordId = getNewRecordId();
Jonico Eustaquio0acff272024-04-23 15:12:06 -0500276 if (recordId < selInvalidRecID)
277 {
278 sd_journal_send("MESSAGE=%s", message.c_str(), "PRIORITY=%i",
279 selPriority, "MESSAGE_ID=%s", selMessageId,
280 "IPMI_SEL_RECORD_ID=%d", recordId,
281 "IPMI_SEL_RECORD_TYPE=%x", recordType,
282 "IPMI_SEL_DATA=%s", selDataStr.c_str(), NULL);
283 }
Vincent Chou92721502024-01-24 15:09:24 -0600284 return recordId;
Lei YUe526b862020-12-03 15:41:59 +0800285#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700286}
287
William A. Kennington IIId585c592021-07-30 12:10:25 -0700288int main(int, char*[])
Jason M. Bills5e049d32018-10-19 12:59:38 -0700289{
290 // setup connection to dbus
Ed Tanousc3526342023-03-06 13:37:53 -0800291 boost::asio::io_context io;
Jason M. Bills5e049d32018-10-19 12:59:38 -0700292 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
293
294 // IPMI SEL Object
295 conn->request_name(ipmiSelObject);
296 auto server = sdbusplus::asio::object_server(conn);
297
298 // Add SEL Interface
299 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceAddSel =
300 server.add_interface(ipmiSelPath, ipmiSelAddInterface);
301
302 // Add a new SEL entry
303 ifaceAddSel->register_method(
Konstantin Aladyshev6f5342d2023-04-19 09:23:11 +0000304 "IpmiSelAdd",
305 [conn](const std::string& message, const std::string& path,
306 const std::vector<uint8_t>& selData, const bool& assert,
307 const uint16_t& genId) {
308 return selAddSystemRecord(conn, message, path, selData, assert, genId);
Patrick Williamsc68ea052023-10-20 11:19:00 -0500309 });
Jason M. Bills5e049d32018-10-19 12:59:38 -0700310 // Add a new OEM SEL entry
Patrick Williams69f7fa32023-05-10 07:50:41 -0500311 ifaceAddSel->register_method("IpmiSelAddOem",
Konstantin Aladyshev6f5342d2023-04-19 09:23:11 +0000312 [conn](const std::string& message,
313 const std::vector<uint8_t>& selData,
314 const uint8_t& recordType) {
315 return selAddOemRecord(conn, message, selData, recordType);
Patrick Williams69f7fa32023-05-10 07:50:41 -0500316 });
Charles Boyer9f476e82021-07-29 16:33:01 -0500317
Charles Boyer9f476e82021-07-29 16:33:01 -0500318#ifndef SEL_LOGGER_SEND_TO_LOGGING_SERVICE
319 // Clear SEL entries
320 ifaceAddSel->register_method("Clear", []() { clearSelLogFiles(); });
321#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700322 ifaceAddSel->initialize();
323
324#ifdef SEL_LOGGER_MONITOR_THRESHOLD_EVENTS
Patrick Williamsccef2272022-07-22 19:26:54 -0500325 sdbusplus::bus::match_t thresholdAssertMonitor =
Zhikui Ren25b26e12020-06-26 20:18:19 -0700326 startThresholdAssertMonitor(conn);
Jason M. Bills5e049d32018-10-19 12:59:38 -0700327#endif
328
Nikhil Potadeafbaa092019-03-06 16:18:13 -0800329#ifdef REDFISH_LOG_MONITOR_PULSE_EVENTS
Patrick Williamsccef2272022-07-22 19:26:54 -0500330 sdbusplus::bus::match_t pulseEventMonitor = startPulseEventMonitor(conn);
Nikhil Potadeafbaa092019-03-06 16:18:13 -0800331#endif
332
Charles Hsudbd77b92020-10-29 11:20:34 +0800333#ifdef SEL_LOGGER_MONITOR_WATCHDOG_EVENTS
Patrick Williamsccef2272022-07-22 19:26:54 -0500334 sdbusplus::bus::match_t watchdogEventMonitor =
Charles Hsudbd77b92020-10-29 11:20:34 +0800335 startWatchdogEventMonitor(conn);
336#endif
George Hung486e42e2021-04-14 20:20:42 +0800337
338#ifdef SEL_LOGGER_MONITOR_THRESHOLD_ALARM_EVENTS
339 startThresholdAlarmMonitor(conn);
340#endif
JinFuLin7c2810b2022-12-02 13:55:28 +0800341
342#ifdef SEL_LOGGER_MONITOR_HOST_ERROR_EVENTS
343 startHostErrorEventMonitor(conn);
344#endif
Jason M. Bills5e049d32018-10-19 12:59:38 -0700345 io.run();
346
347 return 0;
348}