blob: 37f00a95bafa03f024592ca9e0c34334bdfb0654 [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{
39 std::map<std::string, std::string> additionalData{};
40 logError("xyz.openbmc_project.Power.Regulators.Error.ConfigFile", severity,
41 additionalData, journal);
42}
43
44void DBusErrorLogging::logDBusError(Entry::Level severity, Journal& journal)
45{
46 std::map<std::string, std::string> additionalData{};
47 logError("xyz.openbmc_project.Power.Error.DBus", severity, additionalData,
48 journal);
49}
50
51void DBusErrorLogging::logI2CError(Entry::Level severity, Journal& journal,
52 const std::string& bus, uint8_t addr,
53 int errorNumber)
54{
55 // Convert I2C address to a hex string
56 std::ostringstream ss;
57 ss << "0x" << std::hex << std::uppercase << static_cast<uint16_t>(addr);
58 std::string addrStr = ss.str();
59
60 // Convert errno value to an integer string
61 std::string errorNumberStr = std::to_string(errorNumber);
62
63 std::map<std::string, std::string> additionalData{};
64 additionalData.emplace("CALLOUT_IIC_BUS", bus);
65 additionalData.emplace("CALLOUT_IIC_ADDR", addrStr);
66 additionalData.emplace("CALLOUT_ERRNO", errorNumberStr);
67 logError("xyz.openbmc_project.Power.Error.I2C", severity, additionalData,
68 journal);
69}
70
71void DBusErrorLogging::logInternalError(Entry::Level severity, Journal& journal)
72{
73 std::map<std::string, std::string> additionalData{};
74 logError("xyz.openbmc_project.Power.Error.Internal", severity,
75 additionalData, journal);
76}
77
78void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal,
79 const std::string& inventoryPath)
80{
81 // Convert relative inventory path to an absolute path
82 std::string absInventoryPath = getAbsoluteInventoryPath(inventoryPath);
83
84 std::map<std::string, std::string> additionalData{};
85 additionalData.emplace("CALLOUT_INVENTORY_PATH", absInventoryPath);
86 logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData,
87 journal);
88}
89
90void DBusErrorLogging::logWriteVerificationError(
91 Entry::Level severity, Journal& journal, const std::string& inventoryPath)
92{
93 // Convert relative inventory path to an absolute path
94 std::string absInventoryPath = getAbsoluteInventoryPath(inventoryPath);
95
96 std::map<std::string, std::string> additionalData{};
97 additionalData.emplace("CALLOUT_INVENTORY_PATH", absInventoryPath);
98 logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification",
99 severity, additionalData, journal);
100}
101
102FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines)
103{
104 // Create FFDC file of type Text
105 FFDCFile file{FFDCFormat::Text};
106 int fd = file.getFileDescriptor();
107
108 // Write lines to file
109 std::string buffer;
110 for (const std::string& line : lines)
111 {
112 // Copy line to buffer. Add newline if necessary.
113 buffer = line;
114 if (line.empty() || (line.back() != '\n'))
115 {
116 buffer += '\n';
117 }
118
119 // Write buffer to file
120 const char* bufPtr = buffer.c_str();
121 unsigned int count = buffer.size();
122 while (count > 0)
123 {
124 // Try to write remaining bytes; it might not write all of them
125 ssize_t bytesWritten = write(fd, bufPtr, count);
126 if (bytesWritten == -1)
127 {
128 throw std::runtime_error{
129 std::string{"Unable to write to FFDC file: "} +
130 strerror(errno)};
131 }
132 bufPtr += bytesWritten;
133 count -= bytesWritten;
134 }
135 }
136
137 // Seek to beginning of file so error logging system can read data
138 if (lseek(fd, 0, SEEK_SET) != 0)
139 {
140 throw std::runtime_error{
141 std::string{"Unable to seek within FFDC file: "} + strerror(errno)};
142 }
143
144 return file;
145}
146
147std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal)
148{
149 std::vector<FFDCFile> files{};
150
151 // Create FFDC files containing journal messages from relevant executables.
152 // Executables in priority order in case error log cannot hold all the FFDC.
153 std::vector<std::string> executables{"phosphor-regulators", "systemd"};
154 for (const std::string& executable : executables)
155 {
156 try
157 {
158 // Get recent journal messages from the executable
159 // TODO: Uncomment the following line and remove the temporary code
160 // when Journal::getMessages() is implemented
161 // std::vector<std::string> messages =
162 // journal.getMessages("SYSLOG_IDENTIFIER", executable, 30);
163 std::vector<std::string> messages{
164 executable + ": journal message 1",
165 executable + ": journal message 2"};
166
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