blob: 18e3bb6c17e16b1c9a5bad6fd6f2b2cc2f7f5712 [file] [log] [blame]
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -05001#include "config.h"
2
3#include "dump_offload.hpp"
4
Ravi Tejaff9c4522020-11-13 05:14:51 -06005#include <sys/socket.h>
6#include <sys/types.h>
7#include <sys/un.h>
8#include <unistd.h>
9
10#include <dump_utils.hpp>
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -050011#include <fstream>
12#include <phosphor-logging/elog-errors.hpp>
13#include <phosphor-logging/elog.hpp>
14#include <xyz/openbmc_project/Common/File/error.hpp>
15#include <xyz/openbmc_project/Common/error.hpp>
16
17namespace phosphor
18{
19namespace dump
20{
21namespace offload
22{
23
24using namespace sdbusplus::xyz::openbmc_project::Common::Error;
25using namespace phosphor::logging;
26
Ravi Tejaff9c4522020-11-13 05:14:51 -060027/** @brief API to write data on unix socket.
28 *
29 * @param[in] socket - unix socket
30 * @param[in] buf - buffer
31 * @param[in] blockSize - size of data
32 *
33 * @return void
34 */
35void writeOnUnixSocket(const int socket, const char* buf,
36 const uint64_t blockSize)
37{
38 int numOfBytesWrote = 0;
39
40 for (uint64_t i = 0; i < blockSize; i = i + numOfBytesWrote)
41 {
42 numOfBytesWrote = 0;
43 fd_set writeFileDescriptor;
44 struct timeval timeVal;
45 timeVal.tv_sec = 5;
46 timeVal.tv_usec = 0;
47
48 FD_ZERO(&writeFileDescriptor);
49 FD_SET(socket, &writeFileDescriptor);
50 int nextFileDescriptor = socket + 1;
51
52 int retVal = select(nextFileDescriptor, NULL, &writeFileDescriptor,
53 NULL, &timeVal);
54 if (retVal <= 0)
55 {
56 log<level::ERR>("writeOnUnixSocket: select() failed",
57 entry("ERRNO=%d", errno));
58 std::string msg = "select() failed " + std::string(strerror(errno));
59 throw std::runtime_error(msg);
60 }
61 if ((retVal > 0) && (FD_ISSET(socket, &writeFileDescriptor)))
62 {
63 numOfBytesWrote = write(socket, buf + i, blockSize - i);
64 if (numOfBytesWrote < 0)
65 {
66 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
67 {
68 numOfBytesWrote = 0;
69 continue;
70 }
71 log<level::ERR>("writeOnUnixSocket: write() failed",
72 entry("ERRNO=%d", errno));
73 std::string msg =
74 "write() on socket failed " + std::string(strerror(errno));
75 throw std::runtime_error(msg);
76 }
77 }
78 }
79 return;
80}
81
82/**@brief API to setup unix socket.
83 *
84 * @param[in] sockPath - unix socket path
85 *
86 * @return returns returns socket fd on success
87 * and on error exception will be thrown
88 */
89int socketInit(const std::string& sockPath)
90{
91 int unixSocket;
92 struct sockaddr_un socketAddr;
93 memset(&socketAddr, 0, sizeof(socketAddr));
94 socketAddr.sun_family = AF_UNIX;
95 if (strnlen(sockPath.c_str(), sizeof(socketAddr.sun_path)) ==
96 sizeof(socketAddr.sun_path))
97 {
98 log<level::ERR>("UNIX socket path too long");
99 std::string msg =
100 "UNIX socket path is too long " + std::string(strerror(errno));
101 throw std::length_error(msg);
102 }
103 strncpy(socketAddr.sun_path, sockPath.c_str(),
104 sizeof(socketAddr.sun_path) - 1);
105 if ((unixSocket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
106 {
107 log<level::ERR>("socketInit: socket() failed",
108 entry("ERRNO=%d", errno));
109 std::string msg = "socket() failed " + std::string(strerror(errno));
110 throw std::runtime_error(msg);
111 }
112 if (bind(unixSocket, (struct sockaddr*)&socketAddr, sizeof(socketAddr)) ==
113 -1)
114 {
115 log<level::ERR>("socketInit: bind() failed", entry("ERRNO=%d", errno));
116 close(unixSocket);
117 std::string msg = "socket bind failed " + std::string(strerror(errno));
118 throw std::runtime_error(msg);
119 }
120 if (listen(unixSocket, 1) == -1)
121 {
122 log<level::ERR>("socketInit: listen() failed",
123 entry("ERRNO=%d", errno));
124 close(unixSocket);
125 std::string msg = "listen() failed " + std::string(strerror(errno));
126 throw std::runtime_error(msg);
127 }
128 return unixSocket;
129}
130
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500131void requestOffload(fs::path file, uint32_t dumpId, std::string writePath)
132{
133 using namespace sdbusplus::xyz::openbmc_project::Common::File::Error;
134 using ErrnoOpen = xyz::openbmc_project::Common::File::Open::ERRNO;
135 using PathOpen = xyz::openbmc_project::Common::File::Open::PATH;
136 using ErrnoWrite = xyz::openbmc_project::Common::File::Write::ERRNO;
137 using PathWrite = xyz::openbmc_project::Common::File::Write::PATH;
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500138 // open a dump file for a transfer.
139 fs::path dumpPath(BMC_DUMP_PATH);
140 dumpPath /= std::to_string(dumpId);
141 dumpPath /= file.filename();
142
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500143 try
144 {
Ravi Tejaff9c4522020-11-13 05:14:51 -0600145
146 CustomFd unixSocket = socketInit(writePath);
147
148 fd_set readFD;
149 struct timeval timeVal;
150 timeVal.tv_sec = 1;
151 timeVal.tv_usec = 0;
152
153 FD_ZERO(&readFD);
154 FD_SET(unixSocket(), &readFD);
155 int numOfFDs = unixSocket() + 1;
156
157 int retVal = select(numOfFDs, &readFD, NULL, NULL, &timeVal);
158 if (retVal <= 0)
159 {
160 log<level::ERR>("select() failed", entry("ERRNO=%d", errno),
161 entry("DUMP ID=%d", dumpId));
162 std::string msg = "select() failed " + std::string(strerror(errno));
163 throw std::runtime_error(msg);
164 }
165 else if ((retVal > 0) && (FD_ISSET(unixSocket(), &readFD)))
166 {
167 CustomFd socketFD = accept(unixSocket(), NULL, NULL);
168 if (socketFD() < 0)
169 {
170 log<level::ERR>("accept() failed", entry("ERRNO=%d", errno),
171 entry("DUMP ID=%d", dumpId));
172 std::string msg =
173 "accept() failed " + std::string(strerror(errno));
174 throw std::runtime_error(msg);
175 }
176
177 std::ifstream infile{dumpPath, std::ios::in | std::ios::binary};
178 if (!infile.good())
179 {
180 // Unable to open the dump file
181 log<level::ERR>("Failed to open the dump from file ",
182 entry("ERR=%d", errno),
183 entry("DUMPFILE=%s", dumpPath.c_str()),
184 entry("DUMP ID=%d", dumpId));
185 elog<Open>(ErrnoOpen(errno), PathOpen(dumpPath.c_str()));
186 }
187
188 infile.exceptions(std::ifstream::failbit | std::ifstream::badbit |
189 std::ifstream::eofbit);
190
191 log<level::INFO>("Opening File for RW ",
192 entry("FILENAME=%s", file.filename().c_str()));
193
194 std::filebuf* pbuf = infile.rdbuf();
195
196 // get file size using buffer's members
197 std::size_t size = pbuf->pubseekoff(0, infile.end, infile.in);
198 pbuf->pubseekpos(0, infile.in);
199
200 // allocate memory to contain file data
201 std::unique_ptr<char[]> buffer(new char[size]);
202 // get file data
203 pbuf->sgetn(buffer.get(), size);
204 infile.close();
205
206 writeOnUnixSocket(socketFD(), buffer.get(), size);
207 }
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500208 }
Ravi Tejaff9c4522020-11-13 05:14:51 -0600209 catch (std::ifstream::failure& oe)
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500210 {
Ravi Tejaff9c4522020-11-13 05:14:51 -0600211 std::remove(writePath.c_str());
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500212 auto err = errno;
Ravi Tejaff9c4522020-11-13 05:14:51 -0600213 log<level::ERR>("Failed to open", entry("ERR=%s", oe.what()),
214 entry("OPENINTERFACE=%s", dumpPath.c_str()),
215 entry("DUMP ID=%d", dumpId));
216 elog<Open>(ErrnoOpen(err), PathOpen(dumpPath.c_str()));
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500217 }
218 catch (const std::exception& e)
219 {
Ravi Tejaff9c4522020-11-13 05:14:51 -0600220 std::remove(writePath.c_str());
221 auto err = errno;
222 log<level::ERR>("Failed to offload dump", entry("ERR=%s", e.what()),
223 entry("DUMPFILE=%s", writePath.c_str()),
224 entry("DUMP ID=%d", dumpId));
225 elog<Write>(ErrnoWrite(err), PathWrite(writePath.c_str()));
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500226 }
Ravi Tejaff9c4522020-11-13 05:14:51 -0600227 std::remove(writePath.c_str());
228 return;
Dhruvaraj Subhashchandran580d91d2020-04-22 12:29:18 -0500229}
230
231} // namespace offload
232} // namespace dump
233} // namespace phosphor