blob: 9ebce1a882218bf4ab02d6b94d8817a51ff1a816 [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
Shawn McCarney5dab5d32021-08-28 11:02:14 -050089void DBusErrorLogging::logPhaseFault(
90 Entry::Level severity, Journal& journal, PhaseFaultType type,
91 const std::string& inventoryPath,
92 std::map<std::string, std::string> additionalData)
93{
94 std::string message =
95 (type == PhaseFaultType::n)
96 ? "xyz.openbmc_project.Power.Regulators.Error.PhaseFault.N"
97 : "xyz.openbmc_project.Power.Regulators.Error.PhaseFault.NPlus1";
98 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
99 logError(message, severity, additionalData, journal);
100}
101
Shawn McCarney1df59542020-09-24 18:43:18 -0500102void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal,
103 const std::string& inventoryPath)
104{
Shawn McCarney1df59542020-09-24 18:43:18 -0500105 std::map<std::string, std::string> additionalData{};
Bob Kinga76898f2020-10-13 15:08:33 +0800106 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
Shawn McCarney1df59542020-09-24 18:43:18 -0500107 logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
108 journal);
109}
110
111void DBusErrorLogging::logWriteVerificationError(
112 Entry::Level severity, Journal& journal, const std::string& inventoryPath)
113{
Shawn McCarney1df59542020-09-24 18:43:18 -0500114 std::map<std::string, std::string> additionalData{};
Bob Kinga76898f2020-10-13 15:08:33 +0800115 additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath);
Shawn McCarney1df59542020-09-24 18:43:18 -0500116 logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
117 severity, additionalData, journal);
118}
119
120FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines)
121{
122 // Create FFDC file of type Text
123 FFDCFile file{FFDCFormat::Text};
124 int fd = file.getFileDescriptor();
125
126 // Write lines to file
127 std::string buffer;
128 for (const std::string& line : lines)
129 {
130 // Copy line to buffer. Add newline if necessary.
131 buffer = line;
132 if (line.empty() || (line.back() != '\n'))
133 {
134 buffer += '\n';
135 }
136
137 // Write buffer to file
138 const char* bufPtr = buffer.c_str();
139 unsigned int count = buffer.size();
140 while (count > 0)
141 {
142 // Try to write remaining bytes; it might not write all of them
143 ssize_t bytesWritten = write(fd, bufPtr, count);
144 if (bytesWritten == -1)
145 {
146 throw std::runtime_error{
147 std::string{"Unable to write to FFDC file: "} +
148 strerror(errno)};
149 }
150 bufPtr += bytesWritten;
151 count -= bytesWritten;
152 }
153 }
154
155 // Seek to beginning of file so error logging system can read data
156 if (lseek(fd, 0, SEEK_SET) != 0)
157 {
158 throw std::runtime_error{
159 std::string{"Unable to seek within FFDC file: "} + strerror(errno)};
160 }
161
162 return file;
163}
164
165std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal)
166{
167 std::vector<FFDCFile> files{};
168
169 // Create FFDC files containing journal messages from relevant executables.
170 // Executables in priority order in case error log cannot hold all the FFDC.
171 std::vector<std::string> executables{"phosphor-regulators", "systemd"};
172 for (const std::string& executable : executables)
173 {
174 try
175 {
176 // Get recent journal messages from the executable
Shawn McCarney76b76432020-10-15 18:46:50 -0500177 std::vector<std::string> messages =
178 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
Shawn McCarney1df59542020-09-24 18:43:18 -0500179
180 // Create FFDC file containing the journal messages
181 if (!messages.empty())
182 {
183 files.emplace_back(createFFDCFile(messages));
184 }
185 }
186 catch (const std::exception& e)
187 {
188 journal.logError(exception_utils::getMessages(e));
189 }
190 }
191
192 return files;
193}
194
195std::vector<FFDCTuple>
196 DBusErrorLogging::createFFDCTuples(std::vector<FFDCFile>& files)
197{
198 std::vector<FFDCTuple> ffdcTuples{};
199 for (FFDCFile& file : files)
200 {
201 ffdcTuples.emplace_back(
202 file.getFormat(), file.getSubType(), file.getVersion(),
203 sdbusplus::message::unix_fd(file.getFileDescriptor()));
204 }
205 return ffdcTuples;
206}
207
208void DBusErrorLogging::logError(
209 const std::string& message, Entry::Level severity,
210 std::map<std::string, std::string>& additionalData, Journal& journal)
211{
212 try
213 {
214 // Add PID to AdditionalData
215 additionalData.emplace("_PID", std::to_string(getpid()));
216
217 // Create FFDC files containing debug data to store in error log
218 std::vector<FFDCFile> files{createFFDCFiles(journal)};
219
220 // Create FFDC tuples used to pass FFDC files to D-Bus method
221 std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)};
222
223 // Call D-Bus method to create an error log with FFDC files
224 const char* service = "xyz.openbmc_project.Logging";
225 const char* objPath = "/xyz/openbmc_project/logging";
226 const char* interface = "xyz.openbmc_project.Logging.Create";
227 const char* method = "CreateWithFFDCFiles";
228 auto reqMsg = bus.new_method_call(service, objPath, interface, method);
229 reqMsg.append(message, severity, additionalData, ffdcTuples);
230 auto respMsg = bus.call(reqMsg);
231
232 // Remove FFDC files. If an exception occurs before this, the files
233 // will be deleted by FFDCFile desctructor but errors will be ignored.
234 removeFFDCFiles(files, journal);
235 }
236 catch (const std::exception& e)
237 {
238 journal.logError(exception_utils::getMessages(e));
239 journal.logError("Unable to log error " + message);
240 }
241}
242
243void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files,
244 Journal& journal)
245{
246 // Explicitly remove FFDC files rather than relying on FFDCFile destructor.
247 // This allows any resulting errors to be written to the journal.
248 for (FFDCFile& file : files)
249 {
250 try
251 {
252 file.remove();
253 }
254 catch (const std::exception& e)
255 {
256 journal.logError(exception_utils::getMessages(e));
257 }
258 }
259
260 // Clear vector since the FFDCFile objects can no longer be used
261 files.clear();
262}
263
264} // namespace phosphor::power::regulators