1 #include "dbus_to_file_handler.hpp"
2 
3 #include "common/utils.hpp"
4 
5 #include <libpldm/file_io.h>
6 #include <libpldm/pldm.h>
7 
8 namespace pldm
9 {
10 namespace requester
11 {
12 namespace oem_ibm
13 {
14 using namespace pldm::utils;
15 using namespace sdbusplus::bus::match::rules;
16 
17 static constexpr auto resDumpObjPath =
18     "/xyz/openbmc_project/dump/resource/entry";
19 static constexpr auto resDumpEntry = "com.ibm.Dump.Entry.Resource";
20 static constexpr auto resDumpProgressIntf =
21     "xyz.openbmc_project.Common.Progress";
22 static constexpr auto resDumpStatus =
23     "xyz.openbmc_project.Common.Progress.OperationStatus.Failed";
24 
25 DbusToFileHandler::DbusToFileHandler(
26     int mctp_fd, uint8_t mctp_eid, dbus_api::Requester* requester,
27     sdbusplus::message::object_path resDumpCurrentObjPath,
28     pldm::requester::Handler<pldm::requester::Request>* handler) :
29     mctp_fd(mctp_fd),
30     mctp_eid(mctp_eid), requester(requester),
31     resDumpCurrentObjPath(resDumpCurrentObjPath), handler(handler)
32 {}
33 
34 void DbusToFileHandler::sendNewFileAvailableCmd(uint64_t fileSize)
35 {
36     if (requester == NULL)
37     {
38         std::cerr << "Failed to send resource dump parameters as requester is "
39                      "not set";
40         pldm::utils::reportError(
41             "xyz.openbmc_project.bmc.pldm.InternalFailure");
42         return;
43     }
44     auto instanceId = requester->getInstanceId(mctp_eid);
45     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
46                                     PLDM_NEW_FILE_REQ_BYTES);
47     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
48     // Need to revisit this logic at the time of multiple resource dump support
49     uint32_t fileHandle = 1;
50 
51     auto rc =
52         encode_new_file_req(instanceId, PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS,
53                             fileHandle, fileSize, request);
54     if (rc != PLDM_SUCCESS)
55     {
56         requester->markFree(mctp_eid, instanceId);
57         std::cerr << "Failed to encode_new_file_req, rc = " << rc << std::endl;
58         return;
59     }
60 
61     auto newFileAvailableRespHandler = [this](mctp_eid_t /*eid*/,
62                                               const pldm_msg* response,
63                                               size_t respMsgLen) {
64         if (response == nullptr || !respMsgLen)
65         {
66             std::cerr
67                 << "Failed to receive response for NewFileAvailable command \n";
68             return;
69         }
70         uint8_t completionCode{};
71         auto rc = decode_new_file_resp(response, respMsgLen, &completionCode);
72         if (rc || completionCode)
73         {
74             std::cerr << "Failed to decode_new_file_resp or"
75                       << " Host returned error for new_file_available"
76                       << " rc=" << rc
77                       << ", cc=" << static_cast<unsigned>(completionCode)
78                       << "\n";
79             reportResourceDumpFailure();
80         }
81     };
82     rc = handler->registerRequest(
83         mctp_eid, instanceId, PLDM_OEM, PLDM_NEW_FILE_AVAILABLE,
84         std::move(requestMsg), std::move(newFileAvailableRespHandler));
85     if (rc)
86     {
87         std::cerr << "Failed to send NewFileAvailable Request to Host \n";
88         reportResourceDumpFailure();
89     }
90 }
91 
92 void DbusToFileHandler::reportResourceDumpFailure()
93 {
94     pldm::utils::reportError("xyz.openbmc_project.bmc.pldm.InternalFailure");
95 
96     PropertyValue value{resDumpStatus};
97     DBusMapping dbusMapping{resDumpCurrentObjPath, resDumpProgressIntf,
98                             "Status", "string"};
99     try
100     {
101         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
102     }
103     catch (const std::exception& e)
104     {
105         std::cerr << "failed to set resource dump operation status, "
106                      "ERROR="
107                   << e.what() << "\n";
108     }
109 }
110 
111 void DbusToFileHandler::processNewResourceDump(
112     const std::string& vspString, const std::string& resDumpReqPass)
113 {
114     try
115     {
116         std::string objPath = resDumpCurrentObjPath;
117         auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant(
118             objPath.c_str(), "Status", resDumpProgressIntf);
119         const auto& curResDumpStatus = std::get<ResDumpStatus>(propVal);
120 
121         if (curResDumpStatus !=
122             "xyz.openbmc_project.Common.Progress.OperationStatus.InProgress")
123         {
124             return;
125         }
126     }
127     catch (const sdbusplus::exception_t& e)
128     {
129         std::cerr << "Error " << e.what()
130                   << " found in getting current resource dump status while "
131                      "initiating a new resource dump with objPath="
132                   << resDumpCurrentObjPath.str.c_str()
133                   << " and intf=" << resDumpProgressIntf << "\n";
134     }
135 
136     namespace fs = std::filesystem;
137     const fs::path resDumpDirPath = "/var/lib/pldm/resourcedump";
138 
139     if (!fs::exists(resDumpDirPath))
140     {
141         fs::create_directories(resDumpDirPath);
142     }
143 
144     // Need to reconsider this logic to set the value as "1" when we have the
145     // support to handle multiple resource dumps
146     fs::path resDumpFilePath = resDumpDirPath / "1";
147 
148     std::ofstream fileHandle;
149     fileHandle.open(resDumpFilePath, std::ios::out | std::ofstream::binary);
150 
151     if (!fileHandle)
152     {
153         std::cerr << "resource dump file open error: " << resDumpFilePath
154                   << "\n";
155         PropertyValue value{resDumpStatus};
156         DBusMapping dbusMapping{resDumpCurrentObjPath, resDumpProgressIntf,
157                                 "Status", "string"};
158         try
159         {
160             pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
161         }
162         catch (const std::exception& e)
163         {
164             std::cerr << "failed to set resource dump operation status, "
165                          "ERROR="
166                       << e.what() << "\n";
167         }
168         return;
169     }
170 
171     // Fill up the file with resource dump parameters and respective sizes
172     auto fileFunc = [&fileHandle](auto& paramBuf) {
173         uint32_t paramSize = paramBuf.size();
174         fileHandle.write((char*)&paramSize, sizeof(paramSize));
175         fileHandle << paramBuf;
176     };
177     fileFunc(vspString);
178     fileFunc(resDumpReqPass);
179 
180     std::string str;
181     if (!resDumpReqPass.empty())
182     {
183         str = getAcfFileContent();
184     }
185 
186     fileFunc(str);
187 
188     fileHandle.close();
189     size_t fileSize = fs::file_size(resDumpFilePath);
190 
191     sendNewFileAvailableCmd(fileSize);
192 }
193 
194 std::string DbusToFileHandler::getAcfFileContent()
195 {
196     std::string str;
197     static constexpr auto acfDirPath = "/etc/acf/service.acf";
198     if (fs::exists(acfDirPath))
199     {
200         std::ifstream file;
201         file.open(acfDirPath);
202         std::stringstream acfBuf;
203         acfBuf << file.rdbuf();
204         str = acfBuf.str();
205         file.close();
206     }
207     return str;
208 }
209 
210 void DbusToFileHandler::newCsrFileAvailable(const std::string& csr,
211                                             const std::string fileHandle)
212 {
213     namespace fs = std::filesystem;
214     std::string dirPath = "/var/lib/ibm/bmcweb";
215     const fs::path certDirPath = dirPath;
216 
217     if (!fs::exists(certDirPath))
218     {
219         fs::create_directories(certDirPath);
220         fs::permissions(certDirPath,
221                         fs::perms::others_read | fs::perms::owner_write);
222     }
223 
224     fs::path certFilePath = certDirPath / ("CSR_" + fileHandle);
225     std::ofstream certFile;
226 
227     certFile.open(certFilePath, std::ios::out | std::ofstream::binary);
228 
229     if (!certFile)
230     {
231         std::cerr << "cert file open error: " << certFilePath << "\n";
232         return;
233     }
234 
235     // Add csr to file
236     certFile << csr << std::endl;
237 
238     certFile.close();
239     uint32_t fileSize = fs::file_size(certFilePath);
240 
241     newFileAvailableSendToHost(fileSize, (uint32_t)stoi(fileHandle),
242                                PLDM_FILE_TYPE_CERT_SIGNING_REQUEST);
243 }
244 
245 void DbusToFileHandler::newFileAvailableSendToHost(const uint32_t fileSize,
246                                                    const uint32_t fileHandle,
247                                                    const uint16_t type)
248 {
249     if (requester == NULL)
250     {
251         std::cerr << "Failed to send csr to host.";
252         pldm::utils::reportError(
253             "xyz.openbmc_project.bmc.pldm.InternalFailure");
254         return;
255     }
256     auto instanceId = requester->getInstanceId(mctp_eid);
257     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
258                                     PLDM_NEW_FILE_REQ_BYTES);
259     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
260 
261     auto rc =
262         encode_new_file_req(instanceId, type, fileHandle, fileSize, request);
263     if (rc != PLDM_SUCCESS)
264     {
265         requester->markFree(mctp_eid, instanceId);
266         std::cerr << "Failed to encode_new_file_req, rc = " << rc << std::endl;
267         return;
268     }
269     auto newFileAvailableRespHandler = [](mctp_eid_t /*eid*/,
270                                           const pldm_msg* response,
271                                           size_t respMsgLen) {
272         if (response == nullptr || !respMsgLen)
273         {
274             std::cerr << "Failed to receive response for NewFileAvailable "
275                          "command for vmi \n";
276             return;
277         }
278         uint8_t completionCode{};
279         auto rc = decode_new_file_resp(response, respMsgLen, &completionCode);
280         if (rc || completionCode)
281         {
282             std::cerr << "Failed to decode_new_file_resp for vmi, or"
283                       << " Host returned error for new_file_available"
284                       << " rc=" << rc
285                       << ", cc=" << static_cast<unsigned>(completionCode)
286                       << "\n";
287             pldm::utils::reportError(
288                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
289         }
290     };
291     rc = handler->registerRequest(
292         mctp_eid, instanceId, PLDM_OEM, PLDM_NEW_FILE_AVAILABLE,
293         std::move(requestMsg), std::move(newFileAvailableRespHandler));
294     if (rc)
295     {
296         std::cerr
297             << "Failed to send NewFileAvailable Request to Host for vmi \n";
298         pldm::utils::reportError(
299             "xyz.openbmc_project.bmc.pldm.InternalFailure");
300     }
301 }
302 
303 } // namespace oem_ibm
304 } // namespace requester
305 } // namespace pldm
306