1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2026-10-23 21:49:05 +03:00
Files
mariadb-columnstore-engine/utils/messageqcpp/unixsocket.cpp

452 lines
12 KiB
C++

#include "unixsocket.h"
#include "socketclosed.h"
#include <poll.h>
using namespace std;
namespace messageqcpp
{
UnixSocket::UnixSocket() : parms(AF_UNIX, SOCK_STREAM, 0)
{
memset(&sun, 0, sizeof(sun));
//memset(&fake_sa, 0, sizeof(fake_sa));
sun.sun_family = AF_UNIX;
}
UnixSocket::UnixSocket(const UnixSocket &rhs)
{
parms = rhs.parms;
//memcpy(&fake_sa, &rhs.fake_sa, sizeof(fake_sa));
memcpy(&sun, &rhs.sun, sizeof(sun));
}
UnixSocket::~UnixSocket()
{
}
UnixSocket & UnixSocket::operator=(const UnixSocket &rhs)
{
if (this == &rhs)
{
//cout << "UnixSocket copying itself to itself??" << endl;
return *this;
}
parms = rhs.parms;
//memcpy(&fake_sa, &rhs.fake_sa, sizeof(fake_sa));
memcpy(&sun, &rhs.sun, sizeof(sun));
return *this;
}
void UnixSocket::open()
{
//cout << "open" << endl;
int fd = ::socket(AF_UNIX, SOCK_STREAM, 0);
parms.sd(fd);
}
void UnixSocket::close()
{
if (parms.sd() >= 0)
{
//cout << "closing " << parms.sd() << endl;
::close(parms.sd());
parms.sd(-1);
}
}
void UnixSocket::bind(const sockaddr *serv_addr)
{
if (parms.sd() < 0)
throw runtime_error("UnixSocket::bind(): no socket");
if (serv_addr->sa_family != AF_INET || serv_addr->sa_family != PF_INET)
throw runtime_error("UnixSocket::bind(): got not-a-sockaddr_in");
sockaddr_in *sin = (sockaddr_in *) serv_addr;
uint16_t port = ntohs(sin->sin_port);
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
sprintf(&sun.sun_path[1], "%s%d", &prefix[1], port);
//cout << "binding to " << &sun.sun_path[1] << endl;
int err = ::bind(parms.sd(), (sockaddr *) &sun, sizeof(sun));
if (err < 0)
{
int l_errno = errno;
char buf[80];
throw runtime_error(string("UnixSocket::bind(): got ") + strerror_r(l_errno, buf, 80));
}
}
void UnixSocket::listen(int num)
{
//cout << "listen " << num << endl;
int err = ::listen(parms.sd(), num);
if (err < 0)
{
int l_errno = errno;
char buf[80];
throw runtime_error(string("UnixSocket::listen(): got ") + strerror_r(l_errno, buf, 80));
}
}
void UnixSocket::connect(const sockaddr *serv_addr)
{
if (parms.sd() < 0)
throw runtime_error("UnixSocket::connect(): no socket");
if (serv_addr->sa_family != AF_INET || serv_addr->sa_family != PF_INET)
throw runtime_error("UnixSocket::connect(): got not-a-sockaddr_in");
sockaddr_in *sin = (sockaddr_in *) serv_addr;
uint16_t port = ntohs(sin->sin_port);
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
sprintf(&sun.sun_path[1], "%s%d", &prefix[1], port);
//cout << "connecting to " << &sun.sun_path[1] << endl;
int err = ::connect(parms.sd(), (sockaddr *) &sun, sizeof(sun));
if (err < 0)
throw runtime_error(string("UnixSocket::connect(): failed to connect on ") + &sun.sun_path[1]);
//cout << "US::connect OK: fd = " << parms.sd() << endl;
}
const IOSocket UnixSocket::accept(const struct timespec *timeout)
{
//cout << "accept" << endl;
IOSocket ret(new UnixSocket());
if (timeout)
{
struct pollfd pfd[1];
pfd[0].fd = parms.sd();
pfd[0].events = POLLIN;
int msecs = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
if (poll(pfd, 1, msecs) != 1 || (pfd[0].revents & POLLIN) == 0 ||
pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))
return ret;
}
struct sockaddr remote;
socklen_t sl = sizeof(remote);
int err, clientfd;
do
{
clientfd = ::accept(parms.sd(), &remote, &sl);
err = errno;
} while (clientfd < 0 && (err == EINTR || err == ERESTART ||
err == ECONNABORTED));
if (clientfd < 0)
{
char buf[80];
string msg = string("UnixSocket::accept(): error: ") + strerror_r(err, buf, 80);
throw runtime_error(msg);
}
//cout << "accepted connection" << endl;
SocketParms sp = ret.socketParms();
sp.sd(clientfd);
ret.socketParms(sp);
ret.sa(&remote);
//cout << "US::accept OK: fd = " << clientfd << endl;
return ret;
}
/* This currently assumes the remote is writing complete msgs, not bothering
with message deliminters, or comprehensive timeout checking yet. */
const SBS UnixSocket::read(const timespec *timeout, bool *isTimeout, Stats *stats) const
{
//cout << "read" << endl;
SBS ret;
ssize_t err;
uint64_t msecs;
uint32_t msglen;
int l_errno;
pollfd pfd[1];
pfd[0].fd = parms.sd();
pfd[0].events = POLLIN;
pfd[0].revents = 0;
if (timeout)
{
msecs = timeout->tv_sec * 1000 + timeout->tv_nsec / 1000000;
//cout << "msecs = " << msecs << " fd = " << parms.sd() << endl;
err = ::poll(pfd, 1, msecs);
if (err < 0)
{
l_errno = errno;
ostringstream oss;
char buf[80];
oss << "UnixSocket::read: I/O error1: " << strerror_r(l_errno, buf, 80);
throw runtime_error(oss.str());
}
else if (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))
{
/*
if (pfd[0].revents & POLLNVAL)
cout << "got POLLNVAL" << endl;
else if (pfd[0].revents & POLLERR)
cout << "got POLLERR" << endl;
else cout << "got POLLHUP" << endl;
*/
throw runtime_error("UnixSocket::read: poll revents has an error");
}
if (err == 0) // timeout
{
if (isTimeout)
*isTimeout = true;
return SBS(new ByteStream(0));
}
}
while (err != sizeof(msglen))
{
//cout << "read msglen loop" << endl;
err = ::read(parms.sd(), &msglen, sizeof(msglen));
if (err < 0)
{
if (errno == EINTR)
continue;
else
{
l_errno = errno;
ostringstream oss;
char buf[80];
oss << "UnixSocket::read: I/O error2: " << strerror_r(l_errno, buf, 80);
throw runtime_error(oss.str());
}
}
else if (err == 0)
{
if (timeout)
throw SocketClosed("UnixSocket::read(): Remote is closed");
else
return SBS(new ByteStream(0));
}
else if (err != sizeof(msglen))
throw runtime_error("fixme, unixsocket failed to read 4 bytes at once");
}
//cout << " - read msglen = " << msglen << endl;
if (stats)
stats->dataRecvd(sizeof(msglen));
ret.reset(new ByteStream(msglen));
uint8_t *buf = ret->getInputPtr();
size_t readSoFar = 0;
//uint64_t sum = 0;
while (readSoFar < msglen)
{
//cout << "main read loop, read so far = " << readSoFar << " msglen = " << msglen << endl;
err = ::read(parms.sd(), &buf[readSoFar], msglen - readSoFar);
if (err > 0)
readSoFar += err;
else if (err < 0)
{
if (errno == EINTR)
continue;
else
{
l_errno = errno;
if (stats)
stats->dataRecvd(readSoFar);
ostringstream oss;
char buf[80];
oss << "UnixSocket::read: I/O error3: " << strerror_r(l_errno, buf, 80);
throw runtime_error(oss.str());
}
}
else // err == 0
{
if (stats)
stats->dataRecvd(readSoFar);
if (timeout)
throw SocketClosed("UnixSocket::read: Remote is closed");
else
return SBS(new ByteStream(0));
}
}
//for (int i = 0; i < msglen; i++)
// sum += buf[i];
//cout << "main read loop done sum = " << sum << endl;
if (stats)
stats->dataRecvd(msglen);
ret->advanceInputPtr(msglen);
return ret;
}
void UnixSocket::write_raw(const ByteStream &msg, Stats *stats) const
{
size_t toSend = msg.length(), sent = 0;
const uint8_t *buf = msg.buf();
ssize_t err;
int l_errno;
char errbuf[80];
//uint64_t sum = 0;
//for (int i = 0; i < toSend; i++)
// sum += buf[i];
//cout << " -- sum is " << sum << endl;
while (sent < toSend)
{
err = ::write(parms.sd(), &buf[sent], toSend - sent);
if (err >= 0)
sent += err;
else // err < 0
{
if (errno == EINTR)
continue;
l_errno = errno;
if (stats)
stats->dataSent(sent);
throw runtime_error(string("UnixSocket:write_raw: got ") +
strerror_r(l_errno, errbuf, 80));
}
}
if (stats)
stats->dataSent(sent);
}
void UnixSocket::write(SBS msg, Stats *stats)
{
write(*msg, stats);
}
void UnixSocket::write(const ByteStream &msg, Stats *stats)
{
//cout << "write bytestream len = " << msg.length() << endl;
ssize_t err = 0;
uint32_t size = msg.length();
char buf[80];
while (err != sizeof(size))
{
err = ::write(parms.sd(), &size, sizeof(size));
if (err < 0) // todo, robustify...
{
if (errno == EINTR)
continue;
int l_errno = errno;
throw runtime_error(string("UnixSocket:write: got ") +
strerror_r(l_errno, buf, 80));
}
}
//cout << "wrote len" << endl;
if (stats)
stats->dataSent(sizeof(size));
write_raw(msg, stats);
//cout << "wrote msg" << endl;
}
const bool UnixSocket::isOpen() const
{
return parms.isOpen();
}
const SocketParms UnixSocket::socketParms() const
{
return parms;
}
void UnixSocket::socketParms(const SocketParms &other)
{
parms = other;
}
void UnixSocket::sa(const sockaddr *other)
{
//memcpy(&fake_sa, other, sizeof(fake_sa));
if (other->sa_family == AF_UNIX)
memcpy(&sun, other, sizeof(sun));
else if (other->sa_family == PF_INET || other->sa_family == AF_INET)
{
//cout << "Not an AF_UNIX socket" << endl;
sockaddr_in *sin = (sockaddr_in *) other;
uint16_t port = ntohs(sin->sin_port);
sun.sun_family = AF_UNIX;
sprintf(&sun.sun_path[1], "%s%d", &prefix[1], port);
}
}
Socket * UnixSocket::clone() const
{
return new UnixSocket(*this);
}
void UnixSocket::connectionTimeout(const struct ::timespec *)
{
// TODO: not implemented yet
}
void UnixSocket::syncProto(bool use)
{
// doubt this needs to be implemented for unix sockets
}
const int UnixSocket::getConnectionNum() const
{
return parms.sd();
}
const string UnixSocket::addr2String() const
{
return string("UnixSocket on ") + &sun.sun_path[1];
}
const bool UnixSocket::isSameAddr(const Socket *s) const
{
const UnixSocket *other = dynamic_cast<const UnixSocket *>(s);
if (!other)
{
//cout << "compared to not-a-unixsocket?" << endl;
return false;
}
else
return strncmp(&sun.sun_path[1], &(other->sun.sun_path[1]),
sizeof(sun.sun_path) - 1) == 0;
}
bool UnixSocket::isConnected() const
{
// lifted from InetStreamSocket::isConnected()
int error = 0;
socklen_t len = sizeof(error);
int retval = getsockopt(parms.sd(), SOL_SOCKET, SO_ERROR, &error, &len);
if (error || retval)
return false;
struct pollfd pfd[1];
pfd[0].fd = parms.sd();
pfd[0].events = POLLIN;
pfd[0].revents = 0;
error = ::poll(pfd, 1, 0);
if ((error < 0) || (pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR)))
return false;
return true;
}
bool UnixSocket::hasData() const
{
char buf;
int err = ::recv(parms.sd(), &buf, 1, MSG_PEEK | MSG_DONTWAIT);
return (err == 1);
}
}