| /** |
| * Copyright © 2020 IBM Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "error_logging.hpp" |
| |
| #include "exception_utils.hpp" |
| |
| #include <errno.h> // for errno |
| #include <string.h> // for strerror() |
| #include <sys/types.h> // for getpid(), lseek(), ssize_t |
| #include <unistd.h> // for getpid(), lseek(), write() |
| |
| #include <sdbusplus/message.hpp> |
| |
| #include <exception> |
| #include <ios> |
| #include <sstream> |
| #include <stdexcept> |
| |
| namespace phosphor::power::regulators |
| { |
| |
| void DBusErrorLogging::logConfigFileError(Entry::Level severity, |
| Journal& journal) |
| { |
| std::string message{ |
| "xyz.openbmc_project.Power.Regulators.Error.ConfigFile"}; |
| if (severity == Entry::Level::Critical) |
| { |
| // Specify a different message property for critical config file errors. |
| // These are logged when a critical operation cannot be performed due to |
| // the lack of a valid config file. These errors may require special |
| // handling, like stopping a power on attempt. |
| message = |
| "xyz.openbmc_project.Power.Regulators.Error.ConfigFile.Critical"; |
| } |
| |
| std::map<std::string, std::string> additionalData{}; |
| logError(message, severity, additionalData, journal); |
| } |
| |
| void DBusErrorLogging::logDBusError(Entry::Level severity, Journal& journal) |
| { |
| std::map<std::string, std::string> additionalData{}; |
| logError("xyz.openbmc_project.Power.Error.DBus", severity, additionalData, |
| journal); |
| } |
| |
| void DBusErrorLogging::logI2CError(Entry::Level severity, Journal& journal, |
| const std::string& bus, uint8_t addr, |
| int errorNumber) |
| { |
| // Convert I2C address to a hex string |
| std::ostringstream ss; |
| ss << "0x" << std::hex << std::uppercase << static_cast<uint16_t>(addr); |
| std::string addrStr = ss.str(); |
| |
| // Convert errno value to an integer string |
| std::string errorNumberStr = std::to_string(errorNumber); |
| |
| std::map<std::string, std::string> additionalData{}; |
| additionalData.emplace("CALLOUT_IIC_BUS", bus); |
| additionalData.emplace("CALLOUT_IIC_ADDR", addrStr); |
| additionalData.emplace("CALLOUT_ERRNO", errorNumberStr); |
| logError("xyz.openbmc_project.Power.Error.I2C", severity, additionalData, |
| journal); |
| } |
| |
| void DBusErrorLogging::logInternalError(Entry::Level severity, Journal& journal) |
| { |
| std::map<std::string, std::string> additionalData{}; |
| logError("xyz.openbmc_project.Power.Error.Internal", severity, |
| additionalData, journal); |
| } |
| |
| void DBusErrorLogging::logPhaseFault( |
| Entry::Level severity, Journal& journal, PhaseFaultType type, |
| const std::string& inventoryPath, |
| std::map<std::string, std::string> additionalData) |
| { |
| std::string message = |
| (type == PhaseFaultType::n) |
| ? "xyz.openbmc_project.Power.Regulators.Error.PhaseFault.N" |
| : "xyz.openbmc_project.Power.Regulators.Error.PhaseFault.NPlus1"; |
| additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); |
| logError(message, severity, additionalData, journal); |
| } |
| |
| void DBusErrorLogging::logPMBusError(Entry::Level severity, Journal& journal, |
| const std::string& inventoryPath) |
| { |
| std::map<std::string, std::string> additionalData{}; |
| additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); |
| logError("xyz.openbmc_project.Power.Error.PMBus", severity, additionalData, |
| journal); |
| } |
| |
| void DBusErrorLogging::logWriteVerificationError( |
| Entry::Level severity, Journal& journal, const std::string& inventoryPath) |
| { |
| std::map<std::string, std::string> additionalData{}; |
| additionalData.emplace("CALLOUT_INVENTORY_PATH", inventoryPath); |
| logError("xyz.openbmc_project.Power.Regulators.Error.WriteVerification", |
| severity, additionalData, journal); |
| } |
| |
| FFDCFile DBusErrorLogging::createFFDCFile(const std::vector<std::string>& lines) |
| { |
| // Create FFDC file of type Text |
| FFDCFile file{FFDCFormat::Text}; |
| int fd = file.getFileDescriptor(); |
| |
| // Write lines to file |
| std::string buffer; |
| for (const std::string& line : lines) |
| { |
| // Copy line to buffer. Add newline if necessary. |
| buffer = line; |
| if (line.empty() || (line.back() != '\n')) |
| { |
| buffer += '\n'; |
| } |
| |
| // Write buffer to file |
| const char* bufPtr = buffer.c_str(); |
| unsigned int count = buffer.size(); |
| while (count > 0) |
| { |
| // Try to write remaining bytes; it might not write all of them |
| ssize_t bytesWritten = write(fd, bufPtr, count); |
| if (bytesWritten == -1) |
| { |
| throw std::runtime_error{ |
| std::string{"Unable to write to FFDC file: "} + |
| strerror(errno)}; |
| } |
| bufPtr += bytesWritten; |
| count -= bytesWritten; |
| } |
| } |
| |
| // Seek to beginning of file so error logging system can read data |
| if (lseek(fd, 0, SEEK_SET) != 0) |
| { |
| throw std::runtime_error{ |
| std::string{"Unable to seek within FFDC file: "} + strerror(errno)}; |
| } |
| |
| return file; |
| } |
| |
| std::vector<FFDCFile> DBusErrorLogging::createFFDCFiles(Journal& journal) |
| { |
| std::vector<FFDCFile> files{}; |
| |
| // Create FFDC files containing journal messages from relevant executables. |
| // Executables in priority order in case error log cannot hold all the FFDC. |
| std::vector<std::string> executables{"phosphor-regulators", "systemd"}; |
| for (const std::string& executable : executables) |
| { |
| try |
| { |
| // Get recent journal messages from the executable |
| std::vector<std::string> messages = |
| journal.getMessages("SYSLOG_IDENTIFIER", executable, 30); |
| |
| // Create FFDC file containing the journal messages |
| if (!messages.empty()) |
| { |
| files.emplace_back(createFFDCFile(messages)); |
| } |
| } |
| catch (const std::exception& e) |
| { |
| journal.logError(exception_utils::getMessages(e)); |
| } |
| } |
| |
| return files; |
| } |
| |
| std::vector<FFDCTuple> |
| DBusErrorLogging::createFFDCTuples(std::vector<FFDCFile>& files) |
| { |
| std::vector<FFDCTuple> ffdcTuples{}; |
| for (FFDCFile& file : files) |
| { |
| ffdcTuples.emplace_back( |
| file.getFormat(), file.getSubType(), file.getVersion(), |
| sdbusplus::message::unix_fd(file.getFileDescriptor())); |
| } |
| return ffdcTuples; |
| } |
| |
| void DBusErrorLogging::logError( |
| const std::string& message, Entry::Level severity, |
| std::map<std::string, std::string>& additionalData, Journal& journal) |
| { |
| try |
| { |
| // Add PID to AdditionalData |
| additionalData.emplace("_PID", std::to_string(getpid())); |
| |
| // Create FFDC files containing debug data to store in error log |
| std::vector<FFDCFile> files{createFFDCFiles(journal)}; |
| |
| // Create FFDC tuples used to pass FFDC files to D-Bus method |
| std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)}; |
| |
| // Call D-Bus method to create an error log with FFDC files |
| const char* service = "xyz.openbmc_project.Logging"; |
| const char* objPath = "/xyz/openbmc_project/logging"; |
| const char* interface = "xyz.openbmc_project.Logging.Create"; |
| const char* method = "CreateWithFFDCFiles"; |
| auto reqMsg = bus.new_method_call(service, objPath, interface, method); |
| reqMsg.append(message, severity, additionalData, ffdcTuples); |
| auto respMsg = bus.call(reqMsg); |
| |
| // Remove FFDC files. If an exception occurs before this, the files |
| // will be deleted by FFDCFile desctructor but errors will be ignored. |
| removeFFDCFiles(files, journal); |
| } |
| catch (const std::exception& e) |
| { |
| journal.logError(exception_utils::getMessages(e)); |
| journal.logError("Unable to log error " + message); |
| } |
| } |
| |
| void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files, |
| Journal& journal) |
| { |
| // Explicitly remove FFDC files rather than relying on FFDCFile destructor. |
| // This allows any resulting errors to be written to the journal. |
| for (FFDCFile& file : files) |
| { |
| try |
| { |
| file.remove(); |
| } |
| catch (const std::exception& e) |
| { |
| journal.logError(exception_utils::getMessages(e)); |
| } |
| } |
| |
| // Clear vector since the FFDCFile objects can no longer be used |
| files.clear(); |
| } |
| |
| } // namespace phosphor::power::regulators |