blob: 59032440b25da8266d2e595dd3e2774b23b03c55 [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
18#include <boost/container/flat_map.hpp>
19#include <boost/container/flat_set.hpp>
20#include <experimental/string_view>
21#include <iomanip>
22#include <iostream>
23#include <sdbusplus/asio/object_server.hpp>
24#include <sel_logger.hpp>
25#include <sstream>
26#include <threshold_event_monitor.hpp>
27
28struct DBusInternalError final : public sdbusplus::exception_t
29{
30 const char *name() const noexcept override
31 {
32 return "org.freedesktop.DBus.Error.Failed";
33 };
34 const char *description() const noexcept override
35 {
36 return "internal error";
37 };
38 const char *what() const noexcept override
39 {
40 return "org.freedesktop.DBus.Error.Failed: "
41 "internal error";
42 };
43};
44
45static unsigned int initializeRecordId(void)
46{
47 int journalError = -1;
48 sd_journal *journal;
49 journalError = sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY);
50 if (journalError < 0)
51 {
52 std::cerr << "Failed to open journal: " << strerror(-journalError)
53 << "\n";
54 throw DBusInternalError();
55 }
56 unsigned int recordId = selInvalidRecID;
57 char match[256] = {};
58 snprintf(match, sizeof(match), "MESSAGE_ID=%s", selMessageId);
59 sd_journal_add_match(journal, match, 0);
60 SD_JOURNAL_FOREACH_BACKWARDS(journal)
61 {
62 const char *data = nullptr;
63 size_t length;
64
65 journalError = sd_journal_get_data(journal, "IPMI_SEL_RECORD_ID",
66 (const void **)&data, &length);
67 if (journalError < 0)
68 {
69 std::cerr << "Failed to read IPMI_SEL_RECORD_ID field: "
70 << strerror(-journalError) << "\n";
71 continue;
72 }
73 if (journalError =
74 sscanf(data, "IPMI_SEL_RECORD_ID=%u", &recordId) != 1)
75 {
76 std::cerr << "Failed to parse record ID: " << journalError << "\n";
77 throw DBusInternalError();
78 }
79 break;
80 }
81 sd_journal_close(journal);
82 return recordId;
83}
84
85static unsigned int getNewRecordId(void)
86{
87 static unsigned int recordId = initializeRecordId();
88
89 if (++recordId >= selInvalidRecID)
90 {
91 recordId = 1;
92 }
93 return recordId;
94}
95
96static void toHexStr(const std::vector<uint8_t> &data, std::string &hexStr)
97{
98 std::stringstream stream;
99 stream << std::hex << std::uppercase << std::setfill('0');
100 for (const int &v : data)
101 {
102 stream << std::setw(2) << v;
103 }
104 hexStr = stream.str();
105}
106
107static uint16_t selAddSystemRecord(const std::string &message,
108 const std::string &path,
109 const std::vector<uint8_t> &selData,
110 const bool &assert, const uint16_t &genId)
111{
112 // Only 3 bytes of SEL event data are allowed in a system record
113 if (selData.size() > selEvtDataMaxSize)
114 {
115 throw std::invalid_argument("Event data too large");
116 }
117 std::string selDataStr;
118 toHexStr(selData, selDataStr);
119
120 unsigned int recordId = getNewRecordId();
121 sd_journal_send(
122 "MESSAGE=%s", message.c_str(), "PRIORITY=%i", selPriority,
123 "MESSAGE_ID=%s", selMessageId, "IPMI_SEL_RECORD_ID=%d", recordId,
124 "IPMI_SEL_RECORD_TYPE=%x", selSystemType, "IPMI_SEL_GENERATOR_ID=%x",
125 genId, "IPMI_SEL_SENSOR_PATH=%s", path.c_str(), "IPMI_SEL_EVENT_DIR=%x",
126 assert, "IPMI_SEL_DATA=%s", selDataStr.c_str(), NULL);
127 return recordId;
128}
129
130static uint16_t selAddOemRecord(const std::string &message,
131 const std::vector<uint8_t> &selData,
132 const uint8_t &recordType)
133{
134 // A maximum of 13 bytes of SEL event data are allowed in an OEM record
135 if (selData.size() > selOemDataMaxSize)
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();
143 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", recordType,
146 "IPMI_SEL_DATA=%s", selDataStr.c_str(), NULL);
147 return recordId;
148}
149
150int main(int argc, char *argv[])
151{
152 // setup connection to dbus
153 boost::asio::io_service io;
154 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
155
156 // IPMI SEL Object
157 conn->request_name(ipmiSelObject);
158 auto server = sdbusplus::asio::object_server(conn);
159
160 // Add SEL Interface
161 std::shared_ptr<sdbusplus::asio::dbus_interface> ifaceAddSel =
162 server.add_interface(ipmiSelPath, ipmiSelAddInterface);
163
164 // Add a new SEL entry
165 ifaceAddSel->register_method(
166 "IpmiSelAdd", [](const std::string &message, const std::string &path,
167 const std::vector<uint8_t> &selData,
168 const bool &assert, const uint16_t &genId) {
169 return selAddSystemRecord(message, path, selData, assert, genId);
170 });
171 // Add a new OEM SEL entry
172 ifaceAddSel->register_method(
173 "IpmiSelAddOem",
174 [](const std::string &message, const std::vector<uint8_t> &selData,
175 const uint8_t &recordType) {
176 return selAddOemRecord(message, selData, recordType);
177 });
178 ifaceAddSel->initialize();
179
180#ifdef SEL_LOGGER_MONITOR_THRESHOLD_EVENTS
181 sdbusplus::bus::match::match thresholdEventMonitor =
182 startThresholdEventMonitor(conn);
183#endif
184
185 io.run();
186
187 return 0;
188}