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 #include <phosphor-logging/lg2.hpp>
9 
10 PHOSPHOR_LOG2_USING;
11 
12 namespace pldm
13 {
14 namespace requester
15 {
16 namespace oem_ibm
17 {
18 using namespace pldm::utils;
19 using namespace sdbusplus::bus::match::rules;
20 
21 static constexpr auto resDumpObjPath =
22     "/xyz/openbmc_project/dump/resource/entry";
23 static constexpr auto resDumpEntry = "com.ibm.Dump.Entry.Resource";
24 static constexpr auto resDumpProgressIntf =
25     "xyz.openbmc_project.Common.Progress";
26 static constexpr auto resDumpStatus =
27     "xyz.openbmc_project.Common.Progress.OperationStatus.Failed";
28 
29 DbusToFileHandler::DbusToFileHandler(
30     int mctp_fd, uint8_t mctp_eid, dbus_api::Requester* requester,
31     sdbusplus::message::object_path resDumpCurrentObjPath,
32     pldm::requester::Handler<pldm::requester::Request>* handler) :
33     mctp_fd(mctp_fd),
34     mctp_eid(mctp_eid), requester(requester),
35     resDumpCurrentObjPath(resDumpCurrentObjPath), handler(handler)
36 {}
37 
38 void DbusToFileHandler::sendNewFileAvailableCmd(uint64_t fileSize)
39 {
40     if (requester == NULL)
41     {
42         error(
43             "Failed to send resource dump parameters as requester is not set");
44         pldm::utils::reportError(
45             "xyz.openbmc_project.bmc.pldm.InternalFailure");
46         return;
47     }
48     auto instanceId = requester->getInstanceId(mctp_eid);
49     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
50                                     PLDM_NEW_FILE_REQ_BYTES);
51     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
52     // Need to revisit this logic at the time of multiple resource dump support
53     uint32_t fileHandle = 1;
54 
55     auto rc =
56         encode_new_file_req(instanceId, PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS,
57                             fileHandle, fileSize, request);
58     if (rc != PLDM_SUCCESS)
59     {
60         requester->markFree(mctp_eid, instanceId);
61         error("Failed to encode_new_file_req, rc = {RC}", "RC", rc);
62         return;
63     }
64 
65     auto newFileAvailableRespHandler = [this](mctp_eid_t /*eid*/,
66                                               const pldm_msg* response,
67                                               size_t respMsgLen) {
68         if (response == nullptr || !respMsgLen)
69         {
70             error("Failed to receive response for NewFileAvailable command");
71             return;
72         }
73         uint8_t completionCode{};
74         auto rc = decode_new_file_resp(response, respMsgLen, &completionCode);
75         if (rc || completionCode)
76         {
77             error(
78                 "Failed to decode_new_file_resp or Host returned error for new_file_available rc={RC}, cc = {CC}",
79                 "RC", rc, "CC", static_cast<unsigned>(completionCode));
80             reportResourceDumpFailure();
81         }
82     };
83     rc = handler->registerRequest(
84         mctp_eid, instanceId, PLDM_OEM, PLDM_NEW_FILE_AVAILABLE,
85         std::move(requestMsg), std::move(newFileAvailableRespHandler));
86     if (rc)
87     {
88         error("Failed to send NewFileAvailable Request to Host");
89         reportResourceDumpFailure();
90     }
91 }
92 
93 void DbusToFileHandler::reportResourceDumpFailure()
94 {
95     pldm::utils::reportError("xyz.openbmc_project.bmc.pldm.InternalFailure");
96 
97     PropertyValue value{resDumpStatus};
98     DBusMapping dbusMapping{resDumpCurrentObjPath, resDumpProgressIntf,
99                             "Status", "string"};
100     try
101     {
102         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
103     }
104     catch (const std::exception& e)
105     {
106         error("failed to set resource dump operation status, ERROR={ERR_EXCEP}",
107               "ERR_EXCEP", e.what());
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         error(
130             "Error {ERR_EXCEP} found in getting current resource dump status while initiating a new resource dump with objPath={DUMP_OBJ_PATH} and intf={DUMP_PROG_INTF}",
131             "ERR_EXCEP", e.what(), "DUMP_OBJ_PATH",
132             resDumpCurrentObjPath.str.c_str(), "DUMP_PROG_INTF",
133             resDumpProgressIntf);
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         error("resource dump file open error:{RES_DUMP_PATH}", "RES_DUMP_PATH",
154               resDumpFilePath);
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             error(
165                 "failed to set resource dump operation status, ERROR={ERR_EXCEP}",
166                 "ERR_EXCEP", e.what());
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         error("cert file open error: {CERT_PATH}", "CERT_PATH",
232               certFilePath.c_str());
233         return;
234     }
235 
236     // Add csr to file
237     certFile << csr << std::endl;
238 
239     certFile.close();
240     uint32_t fileSize = fs::file_size(certFilePath);
241 
242     newFileAvailableSendToHost(fileSize, (uint32_t)stoi(fileHandle),
243                                PLDM_FILE_TYPE_CERT_SIGNING_REQUEST);
244 }
245 
246 void DbusToFileHandler::newFileAvailableSendToHost(const uint32_t fileSize,
247                                                    const uint32_t fileHandle,
248                                                    const uint16_t type)
249 {
250     if (requester == NULL)
251     {
252         error("Failed to send csr to host.");
253         pldm::utils::reportError(
254             "xyz.openbmc_project.bmc.pldm.InternalFailure");
255         return;
256     }
257     auto instanceId = requester->getInstanceId(mctp_eid);
258     std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
259                                     PLDM_NEW_FILE_REQ_BYTES);
260     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
261 
262     auto rc =
263         encode_new_file_req(instanceId, type, fileHandle, fileSize, request);
264     if (rc != PLDM_SUCCESS)
265     {
266         requester->markFree(mctp_eid, instanceId);
267         error("Failed to encode_new_file_req, rc = {RC}", "RC", rc);
268         return;
269     }
270     auto newFileAvailableRespHandler = [](mctp_eid_t /*eid*/,
271                                           const pldm_msg* response,
272                                           size_t respMsgLen) {
273         if (response == nullptr || !respMsgLen)
274         {
275             error(
276                 "Failed to receive response for NewFileAvailable command for vmi");
277             return;
278         }
279         uint8_t completionCode{};
280         auto rc = decode_new_file_resp(response, respMsgLen, &completionCode);
281         if (rc || completionCode)
282         {
283             error(
284                 "Failed to decode_new_file_resp for vmi, or Host returned error for new_file_available rc = {RC}, cc = {CC}",
285                 "RC", rc, "CC", static_cast<unsigned>(completionCode));
286             pldm::utils::reportError(
287                 "xyz.openbmc_project.bmc.pldm.InternalFailure");
288         }
289     };
290     rc = handler->registerRequest(
291         mctp_eid, instanceId, PLDM_OEM, PLDM_NEW_FILE_AVAILABLE,
292         std::move(requestMsg), std::move(newFileAvailableRespHandler));
293     if (rc)
294     {
295         error("Failed to send NewFileAvailable Request to Host for vmi");
296         pldm::utils::reportError(
297             "xyz.openbmc_project.bmc.pldm.InternalFailure");
298     }
299 }
300 
301 } // namespace oem_ibm
302 } // namespace requester
303 } // namespace pldm
304