blob: 3c4b834e034a824046861059042d2e3691c52fe1 [file] [log] [blame]
#include "config.h"
#include "dump_offload.hpp"
#include <fmt/core.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <dump_utils.hpp>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/elog.hpp>
#include <xyz/openbmc_project/Common/File/error.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
#include <fstream>
namespace phosphor
{
namespace dump
{
namespace offload
{
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
using namespace phosphor::logging;
/** @brief API to write data on unix socket.
*
* @param[in] socket - unix socket
* @param[in] buf - buffer
* @param[in] blockSize - size of data
*
* @return void
*/
void writeOnUnixSocket(const int socket, const char* buf,
const uint64_t blockSize)
{
int numOfBytesWrote = 0;
for (uint64_t i = 0; i < blockSize; i = i + numOfBytesWrote)
{
numOfBytesWrote = 0;
fd_set writeFileDescriptor;
struct timeval timeVal;
timeVal.tv_sec = 5;
timeVal.tv_usec = 0;
FD_ZERO(&writeFileDescriptor);
FD_SET(socket, &writeFileDescriptor);
int nextFileDescriptor = socket + 1;
int retVal = select(nextFileDescriptor, NULL, &writeFileDescriptor,
NULL, &timeVal);
if (retVal <= 0)
{
log<level::ERR>(
fmt::format("writeOnUnixSocket: select() failed, errno({})",
errno)
.c_str());
std::string msg = "select() failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
if ((retVal > 0) && (FD_ISSET(socket, &writeFileDescriptor)))
{
numOfBytesWrote = write(socket, buf + i, blockSize - i);
if (numOfBytesWrote < 0)
{
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
{
numOfBytesWrote = 0;
continue;
}
log<level::ERR>(
fmt::format("writeOnUnixSocket: write() failed, errno({})",
errno)
.c_str());
std::string msg =
"write() on socket failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
}
}
return;
}
/**@brief API to setup unix socket.
*
* @param[in] sockPath - unix socket path
*
* @return returns returns socket fd on success
* and on error exception will be thrown
*/
int socketInit(const std::string& sockPath)
{
int unixSocket;
struct sockaddr_un socketAddr;
memset(&socketAddr, 0, sizeof(socketAddr));
socketAddr.sun_family = AF_UNIX;
if (strnlen(sockPath.c_str(), sizeof(socketAddr.sun_path)) ==
sizeof(socketAddr.sun_path))
{
log<level::ERR>("UNIX socket path too long");
std::string msg =
"UNIX socket path is too long " + std::string(strerror(errno));
throw std::length_error(msg);
}
strncpy(socketAddr.sun_path, sockPath.c_str(),
sizeof(socketAddr.sun_path) - 1);
if ((unixSocket = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1)
{
log<level::ERR>(
fmt::format("socketInit: socket() failed, errno({})", errno)
.c_str());
std::string msg = "socket() failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
if (bind(unixSocket, (struct sockaddr*)&socketAddr, sizeof(socketAddr)) ==
-1)
{
log<level::ERR>(
fmt::format("socketInit: bind() failed, errno({})", errno).c_str());
close(unixSocket);
std::string msg = "socket bind failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
if (listen(unixSocket, 1) == -1)
{
log<level::ERR>(
fmt::format("socketInit: listen() failed, errno({})", errno)
.c_str());
close(unixSocket);
std::string msg = "listen() failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
return unixSocket;
}
void requestOffload(std::filesystem::path file, uint32_t dumpId,
std::string writePath)
{
using namespace sdbusplus::xyz::openbmc_project::Common::File::Error;
using ErrnoOpen = xyz::openbmc_project::Common::File::Open::ERRNO;
using PathOpen = xyz::openbmc_project::Common::File::Open::PATH;
using ErrnoWrite = xyz::openbmc_project::Common::File::Write::ERRNO;
using PathWrite = xyz::openbmc_project::Common::File::Write::PATH;
// open a dump file for a transfer.
std::filesystem::path dumpPath(BMC_DUMP_PATH);
dumpPath /= std::to_string(dumpId);
dumpPath /= file.filename();
try
{
CustomFd unixSocket = socketInit(writePath);
fd_set readFD;
struct timeval timeVal;
timeVal.tv_sec = 1;
timeVal.tv_usec = 0;
FD_ZERO(&readFD);
FD_SET(unixSocket(), &readFD);
int numOfFDs = unixSocket() + 1;
int retVal = select(numOfFDs, &readFD, NULL, NULL, &timeVal);
if (retVal <= 0)
{
log<level::ERR>(
fmt::format("select() failed, errno({}), DUMP_ID({})", errno,
dumpId)
.c_str());
std::string msg = "select() failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
else if ((retVal > 0) && (FD_ISSET(unixSocket(), &readFD)))
{
CustomFd socketFD = accept(unixSocket(), NULL, NULL);
if (socketFD() < 0)
{
log<level::ERR>(
fmt::format("accept() failed, errno({}), DUMP_ID({})",
errno, dumpId)
.c_str());
std::string msg =
"accept() failed " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
std::ifstream infile{dumpPath, std::ios::in | std::ios::binary};
if (!infile.good())
{
// Unable to open the dump file
log<level::ERR>(
fmt::format("Failed to open the dump from file, errno({}), "
"DUMPFILE({}), DUMP_ID({})",
errno, dumpPath.c_str(), dumpId)
.c_str());
elog<Open>(ErrnoOpen(errno), PathOpen(dumpPath.c_str()));
}
infile.exceptions(std::ifstream::failbit | std::ifstream::badbit |
std::ifstream::eofbit);
log<level::INFO>(fmt::format("Opening File for RW, FILENAME({})",
file.filename().c_str())
.c_str());
std::filebuf* pbuf = infile.rdbuf();
// get file size using buffer's members
std::size_t size = pbuf->pubseekoff(0, infile.end, infile.in);
pbuf->pubseekpos(0, infile.in);
// allocate memory to contain file data
std::unique_ptr<char[]> buffer(new char[size]);
// get file data
pbuf->sgetn(buffer.get(), size);
infile.close();
writeOnUnixSocket(socketFD(), buffer.get(), size);
}
}
catch (std::ifstream::failure& oe)
{
std::remove(writePath.c_str());
auto err = errno;
log<level::ERR>(
fmt::format(
"Failed to open, errormsg({}), OPENINTERFACE({}), DUMP_ID({})",
oe.what(), dumpPath.c_str(), dumpId)
.c_str());
elog<Open>(ErrnoOpen(err), PathOpen(dumpPath.c_str()));
}
catch (const std::exception& e)
{
std::remove(writePath.c_str());
auto err = errno;
log<level::ERR>(fmt::format("Failed to offload dump, errormsg({}), "
"DUMPFILE({}), DUMP_ID({})",
e.what(), writePath.c_str(), dumpId)
.c_str());
elog<Write>(ErrnoWrite(err), PathWrite(writePath.c_str()));
}
std::remove(writePath.c_str());
return;
}
} // namespace offload
} // namespace dump
} // namespace phosphor