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