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