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 101 if (!_fanName.empty()) 102 { 103 ad.emplace("CALLOUT_INVENTORY_PATH", _fanName); 104 } 105 106 if (!_sensorName.empty()) 107 { 108 ad.emplace("FAN_SENSOR", _sensorName); 109 } 110 111 // If this is a power off, specify that it's a power 112 // fault and a system termination. This is used by some 113 // implementations for service reasons. 114 if (isPowerOffError) 115 { 116 ad.emplace("POWER_THERMAL_CRITICAL_FAULT", "TRUE"); 117 ad.emplace("SEVERITY_DETAIL", "SYSTEM_TERM"); 118 } 119 120 return ad; 121 } 122 123 std::unique_ptr<FFDCFile> FanError::makeLogFFDCFile() 124 { 125 try 126 { 127 auto logFile = getLogger().saveToTempFile(); 128 return std::make_unique<FFDCFile>(logFile); 129 } 130 catch (const std::exception& e) 131 { 132 log<level::ERR>( 133 fmt::format("Could not save log contents in FFDC. Error msg: {}", 134 e.what()) 135 .c_str()); 136 } 137 return nullptr; 138 } 139 140 std::unique_ptr<FFDCFile> FanError::makeJsonFFDCFile(const json& ffdcData) 141 { 142 char tmpFile[] = "/tmp/fanffdc.XXXXXX"; 143 auto fd = mkstemp(tmpFile); 144 if (fd != -1) 145 { 146 auto jsonString = ffdcData.dump(); 147 148 auto rc = write(fd, jsonString.data(), jsonString.size()); 149 close(fd); 150 if (rc != -1) 151 { 152 fs::path jsonFile{tmpFile}; 153 return std::make_unique<FFDCFile>(jsonFile); 154 } 155 else 156 { 157 getLogger().log("Failed call to write JSON FFDC file"); 158 } 159 } 160 else 161 { 162 auto e = errno; 163 getLogger().log(fmt::format("Failed called to mkstemp, errno = {}", e), 164 Logger::error); 165 } 166 return nullptr; 167 } 168 169 } // namespace phosphor::fan::monitor 170