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 #include "fan_error.hpp" 17 18 #include "logging.hpp" 19 #include "sdbusplus.hpp" 20 21 #include <nlohmann/json.hpp> 22 #include <xyz/openbmc_project/Logging/Create/server.hpp> 23 24 #include <filesystem> 25 26 namespace phosphor::fan::monitor 27 { 28 29 using FFDCFormat = 30 sdbusplus::xyz::openbmc_project::Logging::server::Create::FFDCFormat; 31 using FFDCFiles = std::vector< 32 std::tuple<FFDCFormat, uint8_t, uint8_t, sdbusplus::message::unix_fd>>; 33 using json = nlohmann::json; 34 35 const auto loggingService = "xyz.openbmc_project.Logging"; 36 const auto loggingPath = "/xyz/openbmc_project/logging"; 37 const auto loggingCreateIface = "xyz.openbmc_project.Logging.Create"; 38 39 namespace fs = std::filesystem; 40 using namespace phosphor::fan::util; 41 42 FFDCFile::FFDCFile(const fs::path& name) : 43 _fd(open(name.c_str(), O_RDONLY)), _name(name) 44 { 45 if (_fd() == -1) 46 { 47 auto e = errno; 48 getLogger().log(fmt::format("Could not open FFDC file {}. errno {}", 49 _name.string(), e)); 50 } 51 } 52 53 void FanError::commit(const json& jsonFFDC, bool isPowerOffError) 54 { 55 FFDCFiles ffdc; 56 auto ad = getAdditionalData(isPowerOffError); 57 58 // Add the Logger contents as FFDC 59 auto logFile = makeLogFFDCFile(); 60 if (logFile && (logFile->fd() != -1)) 61 { 62 ffdc.emplace_back(FFDCFormat::Text, 0x01, 0x01, logFile->fd()); 63 } 64 65 // Add the passed in JSON as FFDC 66 auto ffdcFile = makeJsonFFDCFile(jsonFFDC); 67 if (ffdcFile && (ffdcFile->fd() != -1)) 68 { 69 ffdc.emplace_back(FFDCFormat::JSON, 0x01, 0x01, ffdcFile->fd()); 70 } 71 72 try 73 { 74 auto sev = _severity; 75 76 // If this is a power off, change severity to Critical 77 if (isPowerOffError) 78 { 79 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 80 sev = convertForMessage(Entry::Level::Critical); 81 } 82 SDBusPlus::callMethod(loggingService, loggingPath, loggingCreateIface, 83 "CreateWithFFDCFiles", _errorName, sev, ad, ffdc); 84 } 85 catch (const DBusError& e) 86 { 87 getLogger().log( 88 fmt::format("Call to create a {} error for fan {} failed: {}", 89 _errorName, _fanName, e.what()), 90 Logger::error); 91 } 92 } 93 94 std::map<std::string, std::string> 95 FanError::getAdditionalData(bool isPowerOffError) 96 { 97 std::map<std::string, std::string> ad; 98 99 ad.emplace("_PID", std::to_string(getpid())); 100 ad.emplace("CALLOUT_INVENTORY_PATH", _fanName); 101 102 if (!_sensorName.empty()) 103 { 104 ad.emplace("FAN_SENSOR", _sensorName); 105 } 106 107 // If this is a power off, specify that it's a power 108 // fault and a system termination. This is used by some 109 // implementations for service reasons. 110 if (isPowerOffError) 111 { 112 ad.emplace("POWER_THERMAL_CRITICAL_FAULT", "TRUE"); 113 ad.emplace("SEVERITY_DETAIL", "SYSTEM_TERM"); 114 } 115 116 return ad; 117 } 118 119 std::unique_ptr<FFDCFile> FanError::makeLogFFDCFile() 120 { 121 try 122 { 123 auto logFile = getLogger().saveToTempFile(); 124 return std::make_unique<FFDCFile>(logFile); 125 } 126 catch (const std::exception& e) 127 { 128 log<level::ERR>( 129 fmt::format("Could not save log contents in FFDC. Error msg: {}", 130 e.what()) 131 .c_str()); 132 } 133 return nullptr; 134 } 135 136 std::unique_ptr<FFDCFile> FanError::makeJsonFFDCFile(const json& ffdcData) 137 { 138 char tmpFile[] = "/tmp/fanffdc.XXXXXX"; 139 auto fd = mkstemp(tmpFile); 140 if (fd != -1) 141 { 142 auto jsonString = ffdcData.dump(); 143 144 auto rc = write(fd, jsonString.data(), jsonString.size()); 145 close(fd); 146 if (rc != -1) 147 { 148 fs::path jsonFile{tmpFile}; 149 return std::make_unique<FFDCFile>(jsonFile); 150 } 151 else 152 { 153 getLogger().log("Failed call to write JSON FFDC file"); 154 } 155 } 156 else 157 { 158 auto e = errno; 159 getLogger().log(fmt::format("Failed called to mkstemp, errno = {}", e), 160 Logger::error); 161 } 162 return nullptr; 163 } 164 165 } // namespace phosphor::fan::monitor 166