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 */ 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 = "write() on socket failed " + 76 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 */ 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 = "UNIX socket path is too long " + 102 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 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 = "accept() failed " + 172 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