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