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 
DbusToFileHandler(int,uint8_t mctp_eid,pldm::InstanceIdDb * instanceIdDb,sdbusplus::message::object_path resDumpCurrentObjPath,pldm::requester::Handler<pldm::requester::Request> * handler)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_eid(mctp_eid), instanceIdDb(instanceIdDb),
30     resDumpCurrentObjPath(resDumpCurrentObjPath), handler(handler)
31 {}
32 
sendNewFileAvailableCmd(uint64_t fileSize)33 void DbusToFileHandler::sendNewFileAvailableCmd(uint64_t fileSize)
34 {
35     if (instanceIdDb == NULL)
36     {
37         error(
38             "Failed to send resource dump parameters as instance ID DB is not set");
39         pldm::utils::reportError(
40             "xyz.openbmc_project.bmc.pldm.InternalFailure");
41         return;
42     }
43     auto instanceId = instanceIdDb->next(mctp_eid);
44     std::vector<uint8_t> requestMsg(
45         sizeof(pldm_msg_hdr) + PLDM_NEW_FILE_REQ_BYTES);
46     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
47     // Need to revisit this logic at the time of multiple resource dump support
48     uint32_t fileHandle = 1;
49 
50     auto rc =
51         encode_new_file_req(instanceId, PLDM_FILE_TYPE_RESOURCE_DUMP_PARMS,
52                             fileHandle, fileSize, request);
53     if (rc != PLDM_SUCCESS)
54     {
55         instanceIdDb->free(mctp_eid, instanceId);
56         error("Failed to encode new file request with response code '{RC}'",
57               "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 available response or remote terminus returned error, response code '{RC}' and completion code '{CC}'",
75                 "RC", rc, "CC", completionCode);
76             reportResourceDumpFailure("DecodeNewFileResp");
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(
85             "Failed to send NewFileAvailable Request to Host, response code '{RC}'",
86             "RC", rc);
87         reportResourceDumpFailure("NewFileAvailableRequest");
88     }
89 }
90 
reportResourceDumpFailure(const std::string_view & str)91 void DbusToFileHandler::reportResourceDumpFailure(const std::string_view& str)
92 {
93     std::string s = "xyz.openbmc_project.PLDM.Error.ReportResourceDumpFail.";
94     s += str;
95 
96     pldm::utils::reportError(s.c_str());
97 
98     PropertyValue value{resDumpStatus};
99     DBusMapping dbusMapping{resDumpCurrentObjPath, resDumpProgressIntf,
100                             "Status", "string"};
101     try
102     {
103         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
104     }
105     catch (const std::exception& e)
106     {
107         error("Failed to set resource dump operation status, error - {ERROR}",
108               "ERROR", e);
109     }
110 }
111 
processNewResourceDump(const std::string & vspString,const std::string & resDumpReqPass)112 void DbusToFileHandler::processNewResourceDump(
113     const std::string& vspString, const std::string& resDumpReqPass)
114 {
115     try
116     {
117         std::string objPath = resDumpCurrentObjPath;
118         auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant(
119             objPath.c_str(), "Status", resDumpProgressIntf);
120         const auto& curResDumpStatus = std::get<ResDumpStatus>(propVal);
121 
122         if (curResDumpStatus !=
123             "xyz.openbmc_project.Common.Progress.OperationStatus.InProgress")
124         {
125             return;
126         }
127     }
128     catch (const sdbusplus::exception_t& e)
129     {
130         error(
131             "Error '{ERROR}' found in getting current resource dump status while initiating a new resource dump with object path '{PATH}' and interface {INTERFACE}",
132             "ERROR", e, "PATH", resDumpCurrentObjPath, "INTERFACE",
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("Failed to open resource dump file '{PATH}'", "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 - {ERROR}",
166                 "ERROR", e);
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 
getAcfFileContent()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 
newCsrFileAvailable(const std::string & csr,const std::string fileHandle)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("Failed to open certificate file '{PATH}'", "PATH", certFilePath);
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 
newFileAvailableSendToHost(const uint32_t fileSize,const uint32_t fileHandle,const uint16_t type)245 void DbusToFileHandler::newFileAvailableSendToHost(
246     const uint32_t fileSize, const uint32_t fileHandle, const uint16_t type)
247 {
248     if (instanceIdDb == NULL)
249     {
250         error("Failed to send csr to remote terminus.");
251         pldm::utils::reportError(
252             "xyz.openbmc_project.bmc.pldm.InternalFailure");
253         return;
254     }
255     auto instanceId = instanceIdDb->next(mctp_eid);
256     std::vector<uint8_t> requestMsg(
257         sizeof(pldm_msg_hdr) + PLDM_NEW_FILE_REQ_BYTES);
258     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
259 
260     auto rc =
261         encode_new_file_req(instanceId, type, fileHandle, fileSize, request);
262     if (rc != PLDM_SUCCESS)
263     {
264         instanceIdDb->free(mctp_eid, instanceId);
265         error("Failed to encode new file request with response code '{RC}'",
266               "RC", rc);
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             error(
275                 "Failed to receive response for NewFileAvailable command for vmi");
276             return;
277         }
278         uint8_t completionCode{};
279         auto rc = decode_new_file_resp(response, respMsgLen, &completionCode);
280         if (rc || completionCode)
281         {
282             error(
283                 "Failed to decode new file available response for vmi or remote terminus returned error, response code '{RC}' and completion code '{CC}'",
284                 "RC", rc, "CC", completionCode);
285             pldm::utils::reportError(
286                 "xyz.openbmc_project.PLDM.Error.DecodeNewFileResponseFail");
287         }
288     };
289     rc = handler->registerRequest(
290         mctp_eid, instanceId, PLDM_OEM, PLDM_NEW_FILE_AVAILABLE,
291         std::move(requestMsg), std::move(newFileAvailableRespHandler));
292     if (rc)
293     {
294         error(
295             "Failed to send NewFileAvailable Request to Host for vmi, response code '{RC}'",
296             "RC", rc);
297         pldm::utils::reportError(
298             "xyz.openbmc_project.PLDM.Error.NewFileAvailableRequestFail");
299     }
300 }
301 
302 } // namespace oem_ibm
303 } // namespace requester
304 } // namespace pldm
305