1 /** 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 33 namespace phosphor::power::regulators 34 { 35 36 void 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 44 void 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 51 void 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 71 void 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 78 void 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 90 void 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 102 FFDCFile 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 147 std::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 std::vector<std::string> messages = 160 journal.getMessages("SYSLOG_IDENTIFIER", executable, 30); 161 162 // Create FFDC file containing the journal messages 163 if (!messages.empty()) 164 { 165 files.emplace_back(createFFDCFile(messages)); 166 } 167 } 168 catch (const std::exception& e) 169 { 170 journal.logError(exception_utils::getMessages(e)); 171 } 172 } 173 174 return files; 175 } 176 177 std::vector<FFDCTuple> 178 DBusErrorLogging::createFFDCTuples(std::vector<FFDCFile>& files) 179 { 180 std::vector<FFDCTuple> ffdcTuples{}; 181 for (FFDCFile& file : files) 182 { 183 ffdcTuples.emplace_back( 184 file.getFormat(), file.getSubType(), file.getVersion(), 185 sdbusplus::message::unix_fd(file.getFileDescriptor())); 186 } 187 return ffdcTuples; 188 } 189 190 void DBusErrorLogging::logError( 191 const std::string& message, Entry::Level severity, 192 std::map<std::string, std::string>& additionalData, Journal& journal) 193 { 194 try 195 { 196 // Add PID to AdditionalData 197 additionalData.emplace("_PID", std::to_string(getpid())); 198 199 // Create FFDC files containing debug data to store in error log 200 std::vector<FFDCFile> files{createFFDCFiles(journal)}; 201 202 // Create FFDC tuples used to pass FFDC files to D-Bus method 203 std::vector<FFDCTuple> ffdcTuples{createFFDCTuples(files)}; 204 205 // Call D-Bus method to create an error log with FFDC files 206 const char* service = "xyz.openbmc_project.Logging"; 207 const char* objPath = "/xyz/openbmc_project/logging"; 208 const char* interface = "xyz.openbmc_project.Logging.Create"; 209 const char* method = "CreateWithFFDCFiles"; 210 auto reqMsg = bus.new_method_call(service, objPath, interface, method); 211 reqMsg.append(message, severity, additionalData, ffdcTuples); 212 auto respMsg = bus.call(reqMsg); 213 214 // Remove FFDC files. If an exception occurs before this, the files 215 // will be deleted by FFDCFile desctructor but errors will be ignored. 216 removeFFDCFiles(files, journal); 217 } 218 catch (const std::exception& e) 219 { 220 journal.logError(exception_utils::getMessages(e)); 221 journal.logError("Unable to log error " + message); 222 } 223 } 224 225 void DBusErrorLogging::removeFFDCFiles(std::vector<FFDCFile>& files, 226 Journal& journal) 227 { 228 // Explicitly remove FFDC files rather than relying on FFDCFile destructor. 229 // This allows any resulting errors to be written to the journal. 230 for (FFDCFile& file : files) 231 { 232 try 233 { 234 file.remove(); 235 } 236 catch (const std::exception& e) 237 { 238 journal.logError(exception_utils::getMessages(e)); 239 } 240 } 241 242 // Clear vector since the FFDCFile objects can no longer be used 243 files.clear(); 244 } 245 246 } // namespace phosphor::power::regulators 247