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