| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 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 |  | 
| Matt Spinler | f435eb1 | 2021-05-11 14:44:25 -0500 | [diff] [blame] | 53 | void FanError::commit(const json& jsonFFDC, bool isPowerOffError) | 
| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 54 | { | 
|  | 55 | FFDCFiles ffdc; | 
| Matt Spinler | f435eb1 | 2021-05-11 14:44:25 -0500 | [diff] [blame] | 56 | auto ad = getAdditionalData(isPowerOffError); | 
| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 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 | { | 
| Matt Spinler | f435eb1 | 2021-05-11 14:44:25 -0500 | [diff] [blame] | 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 | } | 
| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 82 | SDBusPlus::callMethod(loggingService, loggingPath, loggingCreateIface, | 
| Matt Spinler | f435eb1 | 2021-05-11 14:44:25 -0500 | [diff] [blame] | 83 | "CreateWithFFDCFiles", _errorName, sev, ad, ffdc); | 
| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 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 |  | 
| Matt Spinler | f435eb1 | 2021-05-11 14:44:25 -0500 | [diff] [blame] | 94 | std::map<std::string, std::string> | 
|  | 95 | FanError::getAdditionalData(bool isPowerOffError) | 
| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 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 |  | 
| Matt Spinler | f435eb1 | 2021-05-11 14:44:25 -0500 | [diff] [blame] | 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 |  | 
| Matt Spinler | f13b42e | 2020-10-26 15:29:49 -0500 | [diff] [blame] | 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 |