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