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) 54 { 55 FFDCFiles ffdc; 56 auto ad = getAdditionalData(); 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 SDBusPlus::callMethod(loggingService, loggingPath, loggingCreateIface, 75 "CreateWithFFDCFiles", _errorName, _severity, ad, 76 ffdc); 77 } 78 catch (const DBusError& e) 79 { 80 getLogger().log( 81 fmt::format("Call to create a {} error for fan {} failed: {}", 82 _errorName, _fanName, e.what()), 83 Logger::error); 84 } 85 } 86 87 std::map<std::string, std::string> FanError::getAdditionalData() 88 { 89 std::map<std::string, std::string> ad; 90 91 ad.emplace("_PID", std::to_string(getpid())); 92 ad.emplace("CALLOUT_INVENTORY_PATH", _fanName); 93 94 if (!_sensorName.empty()) 95 { 96 ad.emplace("FAN_SENSOR", _sensorName); 97 } 98 99 return ad; 100 } 101 102 std::unique_ptr<FFDCFile> FanError::makeLogFFDCFile() 103 { 104 try 105 { 106 auto logFile = getLogger().saveToTempFile(); 107 return std::make_unique<FFDCFile>(logFile); 108 } 109 catch (const std::exception& e) 110 { 111 log<level::ERR>( 112 fmt::format("Could not save log contents in FFDC. Error msg: {}", 113 e.what()) 114 .c_str()); 115 } 116 return nullptr; 117 } 118 119 std::unique_ptr<FFDCFile> FanError::makeJsonFFDCFile(const json& ffdcData) 120 { 121 char tmpFile[] = "/tmp/fanffdc.XXXXXX"; 122 auto fd = mkstemp(tmpFile); 123 if (fd != -1) 124 { 125 auto jsonString = ffdcData.dump(); 126 127 auto rc = write(fd, jsonString.data(), jsonString.size()); 128 close(fd); 129 if (rc != -1) 130 { 131 fs::path jsonFile{tmpFile}; 132 return std::make_unique<FFDCFile>(jsonFile); 133 } 134 else 135 { 136 getLogger().log("Failed call to write JSON FFDC file"); 137 } 138 } 139 else 140 { 141 auto e = errno; 142 getLogger().log(fmt::format("Failed called to mkstemp, errno = {}", e), 143 Logger::error); 144 } 145 return nullptr; 146 } 147 148 } // namespace phosphor::fan::monitor 149