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 */ 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 */ 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 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