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