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, const FFDCData& ffdcData)
90 {
91     std::map<std::string, std::string> additionalData;
92     auto bus = sdbusplus::bus::new_default();
93 
94     additionalData.emplace("_PID", std::to_string(getpid()));
95     for (auto& data : ffdcData)
96     {
97         additionalData.emplace(data);
98     }
99 
100     try
101     {
102         std::string service =
103             util::getService(bus, loggingObjectPath, loggingInterface);
104         auto method = bus.new_method_call(service.c_str(), loggingObjectPath,
105                                           loggingInterface, "Create");
106         auto level =
107             sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage(
108                 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level::
109                     Error);
110         method.append(event, level, additionalData);
111         auto resp = bus.call(method);
112     }
113     catch (const sdbusplus::exception::exception& e)
114     {
115         log<level::ERR>(fmt::format("sdbusplus D-Bus call exception",
116                                     "OBJPATH={}, INTERFACE={}, EXCEPTION={}",
117                                     loggingObjectPath, loggingInterface,
118                                     e.what())
119                             .c_str());
120         ;
121 
122         throw std::runtime_error(
123             "Error in invoking D-Bus logging create interface");
124     }
125     catch (std::exception& e)
126     {
127         log<level::ERR>(
128             fmt::format("D-bus call exception", "EXCEPTION={}", e.what())
129                 .c_str());
130         throw e;
131     }
132 }
133 
134 FFDCFile::FFDCFile(const json& pHALCalloutData) :
135     calloutData(pHALCalloutData.dump()),
136     calloutFile("/tmp/phalPELCalloutsJson.XXXXXX"), fileFD(-1)
137 {
138     prepareFFDCFile();
139 }
140 
141 FFDCFile::~FFDCFile()
142 {
143     removeCalloutFile();
144 }
145 
146 int FFDCFile::getFileFD() const
147 {
148     return fileFD;
149 }
150 
151 void FFDCFile::prepareFFDCFile()
152 {
153     createCalloutFile();
154     writeCalloutData();
155     setCalloutFileSeekPos();
156 }
157 
158 void FFDCFile::createCalloutFile()
159 {
160     fileFD = mkostemp(const_cast<char*>(calloutFile.c_str()), O_RDWR);
161 
162     if (fileFD == -1)
163     {
164         log<level::ERR>(fmt::format("Failed to create phalPELCallouts "
165                                     "file({}), errorno({}) and errormsg({})",
166                                     calloutFile, errno, strerror(errno))
167                             .c_str());
168         throw std::runtime_error("Failed to create phalPELCallouts file");
169     }
170 }
171 
172 void FFDCFile::writeCalloutData()
173 {
174     ssize_t rc = write(fileFD, calloutData.c_str(), calloutData.size());
175 
176     if (rc == -1)
177     {
178         log<level::ERR>(fmt::format("Failed to write phaPELCallout info "
179                                     "in file({}), errorno({}), errormsg({})",
180                                     calloutFile, errno, strerror(errno))
181                             .c_str());
182         throw std::runtime_error("Failed to write phalPELCallouts info");
183     }
184     else if (rc != static_cast<ssize_t>(calloutData.size()))
185     {
186         log<level::WARNING>(fmt::format("Could not write all phal callout "
187                                         "info in file({}), written byte({}) "
188                                         "and total byte({})",
189                                         calloutFile, rc, calloutData.size())
190                                 .c_str());
191     }
192 }
193 
194 void FFDCFile::setCalloutFileSeekPos()
195 {
196     int rc = lseek(fileFD, 0, SEEK_SET);
197 
198     if (rc == -1)
199     {
200         log<level::ERR>(fmt::format("Failed to set SEEK_SET for "
201                                     "phalPELCallouts in file({}), errorno({}) "
202                                     "and errormsg({})",
203                                     calloutFile, errno, strerror(errno))
204                             .c_str());
205         throw std::runtime_error(
206             "Failed to set SEEK_SET for phalPELCallouts file");
207     }
208 }
209 
210 void FFDCFile::removeCalloutFile()
211 {
212     close(fileFD);
213     std::remove(calloutFile.c_str());
214 }
215 
216 } // namespace pel
217 } // namespace openpower
218