1 #include "create_pel.hpp"
2 
3 #include "util.hpp"
4 
5 #include <fcntl.h>
6 #include <fmt/format.h>
7 #include <libekb.H>
8 #include <unistd.h>
9 
10 #include <phosphor-logging/elog.hpp>
11 #include <xyz/openbmc_project/Logging/Create/server.hpp>
12 #include <xyz/openbmc_project/Logging/Entry/server.hpp>
13 
14 #include <cerrno>
15 #include <cstdio>
16 #include <cstdlib>
17 #include <cstring>
18 #include <map>
19 #include <stdexcept>
20 #include <string>
21 #include <tuple>
22 #include <vector>
23 
24 namespace openpower
25 {
26 using namespace phosphor::logging;
27 
28 namespace pel
29 {
30 
31 constexpr auto loggingObjectPath = "/xyz/openbmc_project/logging";
32 constexpr auto loggingInterface = "xyz.openbmc_project.Logging.Create";
33 
34 void createBootErrorPEL(const FFDCData& ffdcData, const json& calloutData)
35 {
36     std::map<std::string, std::string> additionalData;
37     auto bus = sdbusplus::bus::new_default();
38     additionalData.emplace("_PID", std::to_string(getpid()));
39     for (auto& data : ffdcData)
40     {
41         additionalData.emplace(data);
42     }
43 
44     try
45     {
46         FFDCFile ffdcFile(calloutData);
47 
48         std::vector<std::tuple<sdbusplus::xyz::openbmc_project::Logging::
49                                    server::Create::FFDCFormat,
50                                uint8_t, uint8_t, sdbusplus::message::unix_fd>>
51             pelCalloutInfo;
52 
53         pelCalloutInfo.push_back(
54             std::make_tuple(sdbusplus::xyz::openbmc_project::Logging::server::
55                                 Create::FFDCFormat::JSON,
56                             static_cast<uint8_t>(0xCA),
57                             static_cast<uint8_t>(0x01), ffdcFile.getFileFD()));
58 
59         static constexpr auto bootErrorMessage =
60             "org.open_power.PHAL.Error.Boot";
61         std::string service =
62             util::getService(bus, loggingObjectPath, loggingInterface);
63         auto method =
64             bus.new_method_call(service.c_str(), loggingObjectPath,
65                                 loggingInterface, "CreateWithFFDCFiles");
66         auto level =
67             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
68                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
69                     Error);
70         method.append(bootErrorMessage, level, additionalData, pelCalloutInfo);
71         auto resp = bus.call(method);
72     }
73     catch (const sdbusplus::exception::exception& e)
74     {
75         log<level::ERR>("D-Bus call exception",
76                         entry("OBJPATH=%s", loggingObjectPath),
77                         entry("INTERFACE=%s", loggingInterface),
78                         entry("EXCEPTION=%s", e.what()));
79 
80         throw std::runtime_error(
81             "Error in invoking D-Bus logging create interface");
82     }
83     catch (std::exception& e)
84     {
85         throw e;
86     }
87 }
88 
89 void createPEL(const std::string& event)
90 {
91     std::map<std::string, std::string> additionalData;
92     auto bus = sdbusplus::bus::new_default();
93     additionalData.emplace("_PID", std::to_string(getpid()));
94 
95     try
96     {
97         std::string service =
98             util::getService(bus, loggingObjectPath, loggingInterface);
99         auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
100                                           loggingInterface, "Create");
101         auto level =
102             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
103                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
104                     Error);
105         method.append(event, level, additionalData);
106         auto resp = bus.call(method);
107     }
108     catch (const sdbusplus::exception::exception& e)
109     {
110         log<level::ERR>(fmt::format("sdbusplus D-Bus call exception",
111                                     "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
112                                     loggingObjectPath, loggingInterface,
113                                     e.what())
114                             .c_str());
115         ;
116 
117         throw std::runtime_error(
118             "Error in invoking D-Bus logging create interface");
119     }
120     catch (std::exception& e)
121     {
122         log<level::ERR>(
123             fmt::format("D-bus call exception", "EXCEPTION={}", e.what())
124                 .c_str());
125         throw e;
126     }
127 }
128 
129 FFDCFile::FFDCFile(const json& pHALCalloutData) :
130     calloutData(pHALCalloutData.dump()),
131     calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
132 {
133     prepareFFDCFile();
134 }
135 
136 FFDCFile::~FFDCFile()
137 {
138     removeCalloutFile();
139 }
140 
141 int FFDCFile::getFileFD() const
142 {
143     return fileFD;
144 }
145 
146 void FFDCFile::prepareFFDCFile()
147 {
148     createCalloutFile();
149     writeCalloutData();
150     setCalloutFileSeekPos();
151 }
152 
153 void FFDCFile::createCalloutFile()
154 {
155     fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
156 
157     if (fileFD == -1)
158     {
159         log<level::ERR>(fmt::format("Failed to create phalPELCallouts "
160                                     "file({}), errorno({}) and errormsg({})",
161                                     calloutFile, errno, strerror(errno))
162                             .c_str());
163         throw std::runtime_error("Failed to create phalPELCallouts file");
164     }
165 }
166 
167 void FFDCFile::writeCalloutData()
168 {
169     ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
170 
171     if (rc == -1)
172     {
173         log<level::ERR>(fmt::format("Failed to write phaPELCallout info "
174                                     "in file({}), errorno({}), errormsg({})",
175                                     calloutFile, errno, strerror(errno))
176                             .c_str());
177         throw std::runtime_error("Failed to write phalPELCallouts info");
178     }
179     else if (rc != static_cast<ssize_t>(calloutData.size()))
180     {
181         log<level::WARNING>(fmt::format("Could not write all phal callout "
182                                         "info in file({}), written byte({}) "
183                                         "and total byte({})",
184                                         calloutFile, rc, calloutData.size())
185                                 .c_str());
186     }
187 }
188 
189 void FFDCFile::setCalloutFileSeekPos()
190 {
191     int rc = lseek(fileFD, 0, SEEK_SET);
192 
193     if (rc == -1)
194     {
195         log<level::ERR>(fmt::format("Failed to set SEEK_SET for "
196                                     "phalPELCallouts in file({}), errorno({}) "
197                                     "and errormsg({})",
198                                     calloutFile, errno, strerror(errno))
199                             .c_str());
200         throw std::runtime_error(
201             "Failed to set SEEK_SET for phalPELCallouts file");
202     }
203 }
204 
205 void FFDCFile::removeCalloutFile()
206 {
207     close(fileFD);
208     std::remove(calloutFile.c_str());
209 }
210 
211 } // namespace pel
212 } // namespace openpower
213