blob: bc3d38892d159a147cc2f546991b99b8fe9f55a2 [file] [log] [blame]
Shawn McCarney1df59542020-09-24 18:43:18 -05001/**
2 * Copyright © 2020 IBM 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
17#include "error_logging.hpp"
18
19#include "exception_utils.hpp"
20
21#include <errno.h> // for errno
22#include <string.h> // for strerror()
23#include <sys/types.h> // for getpid(), lseek(), ssize_t
24#include <unistd.h> // for getpid(), lseek(), write()
25
26#include <sdbusplus/message.hpp>
27
28#include <exception>
29#include <ios>
30#include <sstream>
31#include <stdexcept>
32
33namespace phosphor::power::regulators
34{
35
36void DBusErrorLogging::logConfigFileError(Entry::Level severity,
37 Journal& journal)
38{
Shawn McCarney415094c2021-02-15 11:08:19 -060039 std::string message{
40 "xyz.openbmc_project.Power.Regulators.Error.ConfigFile"};
41 if (severity == Entry::Level::Critical)
42 {
43 // Specify a different message property for critical config file errors.
44 // These are logged when a critical operation cannot be performed due to
45 // the lack of a valid config file. These errors may require special
46 // handling, like stopping a power on attempt.
47 message =
48 "xyz.openbmc_project.Power.Regulators.Error.ConfigFile.Critical";
49 }
50
Shawn McCarney1df59542020-09-24 18:43:18 -050051 std::map<std::string, std::string> additionalData{};
Shawn McCarney415094c2021-02-15 11:08:19 -060052 logError(message, severity, additionalData, journal);
Shawn McCarney1df59542020-09-24 18:43:18 -050053}
54
55void DBusErrorLogging::logDBusError(Entry::Level severity, Journal& journal)
56{
57 std::map<std::string, std::string> additionalData{};
58 logError("xyz.openbmc_project.Power.Error.DBus", severity, additionalData,
59 journal);
60}
61
62void DBusErrorLogging::logI2CError(Entry::Level severity, Journal& journal,
63 const std::string& bus, uint8_t addr,
64 int errorNumber)
65{
66 // Convert I2C address to a hex string
67 std::ostringstream ss;
68 ss << "0x" << std::hex << std::uppercase << static_cast<uint16_t>(addr);
69 std::string addrStr = ss.str();
70
71 // Convert errno value to an integer string
72 std::string errorNumberStr = std::to_string(errorNumber);
73
74 std::map<std::string, std::string> additionalData{};
75 additionalData.emplace("CALLOUT_IIC_BUS", bus);
76 additionalData.emplace("CALLOUT_IIC_ADDR", addrStr);
77 additionalData.emplace("CALLOUT_ERRNO", errorNumberStr);
78 logError("xyz.openbmc_project.Power.Error.I2C", severity, additionalData,
79 journal);
80}
81
82void DBusErrorLogging::logInternalError(Entry::Level severity, Journal& journal)
83{
84 std::map<std::string, std::string> additionalData{};
85 logError("xyz.openbmc_project.Power.Error.Internal", severity,
86 additionalData, journal);
87}
88
89void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal,
90 const std::string& inventoryPath)
91{
Shawn McCarney1df59542020-09-24 18:43:18 -050092 std::map<std::string, std::string> additionalData{};
Bob Kinga76898f2020-10-13 15:08:33 +080093 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
Shawn McCarney1df59542020-09-24 18:43:18 -050094 logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
95 journal);
96}
97
98void DBusErrorLogging::logWriteVerificationError(
99 Entry::Level severity, Journal& journal, const std::string& inventoryPath)
100{
Shawn McCarney1df59542020-09-24 18:43:18 -0500101 std::map<std::string, std::string> additionalData{};
Bob Kinga76898f2020-10-13 15:08:33 +0800102 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
Shawn McCarney1df59542020-09-24 18:43:18 -0500103 logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
104 severity, additionalData, journal);
105}
106
107FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines)
108{
109 // Create FFDC file of type Text
110 FFDCFile file{FFDCFormat::Text};
111 int fd = file.getFileDescriptor();
112
113 // Write lines to file
114 std::string buffer;
115 for (const std::string& line : lines)
116 {
117 // Copy line to buffer. Add newline if necessary.
118 buffer = line;
119 if (line.empty() || (line.back() != '\n'))
120 {
121 buffer += '\n';
122 }
123
124 // Write buffer to file
125 const char* bufPtr = buffer.c_str();
126 unsigned int count = buffer.size();
127 while (count > 0)
128 {
129 // Try to write remaining bytes; it might not write all of them
130 ssize_t bytesWritten = write(fd, bufPtr, count);
131 if (bytesWritten == -1)
132 {
133 throw std::runtime_error{
134 std::string{"Unable to write to FFDC file: "} +
135 strerror(errno)};
136 }
137 bufPtr += bytesWritten;
138 count -= bytesWritten;
139 }
140 }
141
142 // Seek to beginning of file so error logging system can read data
143 if (lseek(fd, 0, SEEK_SET) != 0)
144 {
145 throw std::runtime_error{
146 std::string{"Unable to seek within FFDC file: "} + strerror(errno)};
147 }
148
149 return file;
150}
151
152std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal)
153{
154 std::vector<FFDCFile> files{};
155
156 // Create FFDC files containing journal messages from relevant executables.
157 // Executables in priority order in case error log cannot hold all the FFDC.
158 std::vector<std::string> executables{"phosphor-regulators", "systemd"};
159 for (const std::string& executable : executables)
160 {
161 try
162 {
163 // Get recent journal messages from the executable
Shawn McCarney76b76432020-10-15 18:46:50 -0500164 std::vector<std::string> messages =
165 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
Shawn McCarney1df59542020-09-24 18:43:18 -0500166
167 // Create FFDC file containing the journal messages
168 if (!messages.empty())
169 {
170 files.emplace_back(createFFDCFile(messages));
171 }
172 }
173 catch (const std::exception& e)
174 {
175 journal.logError(exception_utils::getMessages(e));
176 }
177 }
178
179 return files;
180}
181
182std::vector<FFDCTuple>
183 DBusErrorLogging::createFFDCTuples(std::vector<FFDCFile>& files)
184{
185 std::vector<FFDCTuple> ffdcTuples{};
186 for (FFDCFile& file : files)
187 {
188 ffdcTuples.emplace_back(
189 file.getFormat(), file.getSubType(), file.getVersion(),
190 sdbusplus::message::unix_fd(file.getFileDescriptor()));
191 }
192 return ffdcTuples;
193}
194
195void DBusErrorLogging::logError(
196 const std::string& message, Entry::Level severity,
197 std::map<std::string, std::string>& additionalData, Journal& journal)
198{
199 try
200 {
201 // Add PID to AdditionalData
202 additionalData.emplace("_PID", std::to_string(getpid()));
203
204 // Create FFDC files containing debug data to store in error log
205 std::vector<FFDCFile> files{createFFDCFiles(journal)};
206
207 // Create FFDC tuples used to pass FFDC files to D-Bus method
208 std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)};
209
210 // Call D-Bus method to create an error log with FFDC files
211 const char* service = "xyz.openbmc_project.Logging";
212 const char* objPath = "/xyz/openbmc_project/logging";
213 const char* interface = "xyz.openbmc_project.Logging.Create";
214 const char* method = "CreateWithFFDCFiles";
215 auto reqMsg = bus.new_method_call(service, objPath, interface, method);
216 reqMsg.append(message, severity, additionalData, ffdcTuples);
217 auto respMsg = bus.call(reqMsg);
218
219 // Remove FFDC files. If an exception occurs before this, the files
220 // will be deleted by FFDCFile desctructor but errors will be ignored.
221 removeFFDCFiles(files, journal);
222 }
223 catch (const std::exception& e)
224 {
225 journal.logError(exception_utils::getMessages(e));
226 journal.logError("Unable to log error " + message);
227 }
228}
229
230void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files,
231 Journal& journal)
232{
233 // Explicitly remove FFDC files rather than relying on FFDCFile destructor.
234 // This allows any resulting errors to be written to the journal.
235 for (FFDCFile& file : files)
236 {
237 try
238 {
239 file.remove();
240 }
241 catch (const std::exception& e)
242 {
243 journal.logError(exception_utils::getMessages(e));
244 }
245 }
246
247 // Clear vector since the FFDCFile objects can no longer be used
248 files.clear();
249}
250
251} // namespace phosphor::power::regulators