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