xref: /openbmc/phosphor-fan-presence/monitor/fan_error.cpp (revision cceffdd91cf3cc0c651b5c44424fb3377cded964)
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