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