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