Started creating external hostname resolution code in order to avoid blocking...

Started creating external hostname resolution code in order to avoid blocking inside glibc's getaddrinfo.
parent 3f39e7f4
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <list>
#include <iostream>
#include <string.h>
#include <arc/Run.h>
#include <arc/ArcLocation.h>
#include "hostname_resolver.h"
#include "HostnameResolver.h"
namespace Arc {
static bool sread(Run& r,void* buf,size_t size) {
while(size) {
int l = r.ReadStdout(-1,(char*)buf,size);
if(l < 0) return false;
size-=l;
buf = (void*)(((char*)buf)+l);
};
return true;
}
static bool swrite(Run& r,const void* buf,size_t size) {
while(size) {
int l = r.WriteStdin(-1,(const char*)buf,size);
if(l < 0) return false;
size-=l;
buf = (void*)(((char*)buf)+l);
};
return true;
}
#define ABORTALL { dispose_executer(hostname_resolver_); hostname_resolver_=NULL; continue; }
#define STARTHEADER(CMD,SIZE) { \
if(!hostname_resolver_) break; \
if(!(hostname_resolver_->Running())) break; \
header_t header; \
header.cmd = CMD; \
header.size = SIZE; \
if(!swrite(*hostname_resolver_,&header,sizeof(header))) ABORTALL; \
}
#define ENDHEADER(CMD,SIZE) { \
header_t header; \
if(!sread(*hostname_resolver_,&header,sizeof(header))) ABORTALL; \
if((header.cmd != CMD) || (header.size != (sizeof(res)+sizeof(errno_)+SIZE))) ABORTALL; \
if(!sread(*hostname_resolver_,&res,sizeof(res))) ABORTALL; \
if(!sread(*hostname_resolver_,&errno_,sizeof(errno_))) ABORTALL; \
}
static void release_executer(Run* hostname_resolver) {
delete hostname_resolver;
}
static void dispose_executer(Run* hostname_resolver) {
delete hostname_resolver;
}
static bool do_tests = false;
static Run* acquire_executer() {
std::list<std::string> argv;
if(!do_tests) {
argv.push_back(Arc::ArcLocation::Get()+G_DIR_SEPARATOR_S+PKGLIBSUBDIR+G_DIR_SEPARATOR_S+"arc-hostname-resolver");
} else {
argv.push_back(std::string("..")+G_DIR_SEPARATOR_S+"arc-hostname-resolver");
}
argv.push_back("0");
argv.push_back("1");
Run* hostname_resolver_ = new Run(argv);
hostname_resolver_->KeepStdin(false);
hostname_resolver_->KeepStdout(false);
hostname_resolver_->KeepStderr(true);
if(!(hostname_resolver_->Start())) {
delete hostname_resolver_;
hostname_resolver_ = NULL;
return NULL;
}
return hostname_resolver_;
}
static bool sread_buf(Run& r,void* buf,unsigned int& bufsize,unsigned int& maxsize) {
char dummy[1024];
unsigned int size;
if(sizeof(size) > maxsize) return false;
if(!sread(r,&size,sizeof(size))) return false;
maxsize -= sizeof(size);
if(size > maxsize) return false;
if(size <= bufsize) {
if(!sread(r,buf,size)) return false;
bufsize = size;
maxsize -= size;
} else {
if(!sread(r,buf,bufsize)) return false;
maxsize -= bufsize;
// skip rest
size -= bufsize;
while(size > sizeof(dummy)) {
if(!sread(r,dummy,sizeof(dummy))) return false;
size -= sizeof(dummy);
maxsize -= sizeof(dummy);
};
if(!sread(r,dummy,size)) return false;
maxsize -= size;
};
return true;
}
static bool swrite_string(Run& r,const std::string& str) {
int l = str.length();
if(!swrite(r,&l,sizeof(l))) return false;
if(!swrite(r,str.c_str(),l)) return false;
return true;
}
#define RETRYLOOP Glib::Mutex::Lock mlock(lock_); for(int n = 2; n && (hostname_resolver_?hostname_resolver_:(hostname_resolver_=acquire_executer())) ;--n)
#define NORETRYLOOP Glib::Mutex::Lock mlock(lock_); for(int n = 1; n && (hostname_resolver_?hostname_resolver_:(hostname_resolver_=acquire_executer())) ;--n)
HostnameResolver::SockAddr::SockAddr():family(0),length(0),addr(NULL) {
}
HostnameResolver::SockAddr::SockAddr(SockAddr const& other):family(0),length(0),addr(NULL) {
operator=(other);
}
HostnameResolver::SockAddr& HostnameResolver::SockAddr::operator=(SockAddr const& other) {
family = other.family;
length = other.length;
::free(addr);
addr = (sockaddr*)::malloc(length);
memcpy(addr,other.addr,length);
}
HostnameResolver::SockAddr::~SockAddr() {
::free(addr);
}
HostnameResolver::HostnameResolver(void):hostname_resolver_(NULL),errno_(0) {
hostname_resolver_ = acquire_executer();
}
HostnameResolver::~HostnameResolver(void) {
release_executer(hostname_resolver_);
hostname_resolver_ = NULL;
}
bool HostnameResolver::ping(void) {
RETRYLOOP {
STARTHEADER(CMD_PING,0);
header_t header;
if(!sread(*hostname_resolver_,&header,sizeof(header))) ABORTALL;
if((header.cmd != CMD_PING) || (header.size != 0)) ABORTALL;
return true;
}
return false;
}
int HostnameResolver::hr_resolve(std::string const& hostname, std::list<SockAddr> addrs) {
NORETRYLOOP {
STARTHEADER(CMD_RESOLVE,hostname.length());
if(!swrite_string(*hostname_resolver_,hostname)) ABORTALL;
int res = 0;
header_t header;
if(!sread(*hostname_resolver_,&header,sizeof(header))) ABORTALL;
if((header.cmd != CMD_RESOLVE) || (header.size < (sizeof(res)+sizeof(errno_)))) ABORTALL; \
if(!sread(*hostname_resolver_,&res,sizeof(res))) ABORTALL;
if(!sread(*hostname_resolver_,&errno_,sizeof(errno_))) ABORTALL;
header.size -= sizeof(res)+sizeof(errno_);
while(header.size > 0) {
SockAddr addr;
if(header.size < sizeof(addr.family)) ABORTALL;
if(!sread(*hostname_resolver_,&addr.family,sizeof(addr.family))) ABORTALL;
if(header.size < sizeof(addr.length)) ABORTALL;
if(!sread(*hostname_resolver_,&addr.length,sizeof(addr.length))) ABORTALL;
if(header.size < addr.length) ABORTALL;
if((addr.addr = (sockaddr*)::malloc(addr.length)) == NULL) ABORTALL;
if(!sread(*hostname_resolver_,addr.addr,addr.length)) ABORTALL;
addrs.push_back(addr);
};
return res;
}
errno_ = -1;
return -1;
}
void HostnameResolver::testtune(void) {
do_tests = true;
}
static HostnameResolverContainer hrs_(0,100);
HostnameResolver* HostnameResolver::Acquire(void) {
return hrs_.Acquire();
}
void HostnameResolver::Release(HostnameResolver* hr) {
hrs_.Release(hr);
}
HostnameResolverContainer::HostnameResolverContainer(unsigned int minval,unsigned int maxval):min_(minval),max_(maxval) {
KeepRange();
}
HostnameResolverContainer::HostnameResolverContainer(void):min_(1),max_(10) {
KeepRange();
}
HostnameResolverContainer::~HostnameResolverContainer(void) {
Glib::Mutex::Lock lock(lock_);
for(std::list<HostnameResolver*>::iterator hr = hrs_.begin();hr != hrs_.end();++hr) {
delete *hr;
}
}
HostnameResolver* HostnameResolverContainer::Acquire(void) {
Glib::Mutex::Lock lock(lock_);
HostnameResolver* r = NULL;
for(std::list<HostnameResolver*>::iterator hr = hrs_.begin();hr != hrs_.end();) {
r = *hr; hr = hrs_.erase(hr);
// Test if it still works
if(r->ping()) break;
// Broken proxy
delete r; r = NULL;
}
// If no proxies - make new
if(!r) r = new HostnameResolver;
KeepRange();
return r;
}
void HostnameResolverContainer::Release(HostnameResolver* hr) {
Glib::Mutex::Lock lock(lock_);
if(!hr) return;
hrs_.push_back(hr);
KeepRange();
return;
}
void HostnameResolverContainer::SetMin(unsigned int val) {
Glib::Mutex::Lock lock(lock_);
min_ = val;
KeepRange();
}
void HostnameResolverContainer::SetMax(unsigned int val) {
Glib::Mutex::Lock lock(lock_);
min_ = val;
KeepRange();
}
void HostnameResolverContainer::KeepRange(void) {
while(hrs_.size() > ((max_>=min_)?max_:min_)) {
HostnameResolver* fa = hrs_.front();
hrs_.pop_front();
delete fa;
}
while(hrs_.size() < ((min_<=max_)?min_:max_)) {
hrs_.push_back(new HostnameResolver);
}
}
}
#ifndef __ARC_HOSTNAMERESOLVER_H__
#define __ARC_HOSTNAMERESOLVER_H__
#include <string>
#include <list>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <glibmm.h>
#ifdef WIN32
#ifndef uid_t
#define uid_t int
#endif
#ifndef gid_t
#define gid_t int
#endif
#endif
namespace Arc {
class Run;
/// Defines interface for accessing filesystems.
/** This class performs host name respolution through a proxy executable.
\ingroup common
\headerfile HostnameResolver.h arc/HostnameResolver.h
*/
class HostnameResolver {
public:
class SockAddr {
friend class HostnameResolver;
public:
SockAddr();
SockAddr(SockAddr const& other);
SockAddr& operator=(SockAddr const& other);
~SockAddr();
int Family() const { return family; }
sockaddr const* Addr() const { return addr; }
socklen_t Length() const { return length; }
private:
int family;
socklen_t length;
sockaddr *addr;
};
/// New HostnameResolver object.
HostnameResolver(void);
/// Shuts down any spawned executable.
~HostnameResolver(void);
/// Constructor which takes already existing object from global cache
static HostnameResolver* Acquire(void);
/// Destructor which returns object into global cache
static void Release(HostnameResolver* fa);
/// Check if communication with proxy works
bool ping(void);
/// Performs resolution of provided host name.
int hr_resolve(std::string const& hostname, std::list<SockAddr> addrs);
/// Get errno of last operation. Every operation resets errno.
int geterrno() { return errno_; };
/// Returns true if this instance is in useful condition
operator bool(void) { return (hostname_resolver_ != NULL); };
/// Returns true if this instance is not in useful condition
bool operator!(void) { return (hostname_resolver_ == NULL); };
/// Special method for using in unit tests.
static void testtune(void);
private:
Glib::Mutex lock_;
Run* hostname_resolver_;
int errno_;
public:
/// Internal struct used for communication between processes.
typedef struct {
unsigned int size;
unsigned int cmd;
} header_t;
};
/// Container for shared HostnameResolver objects.
/** HostnameResolverContainer maintains a pool of executables and can be used to
reduce the overhead in creating and destroying executables when using
HostnameResolver.
\ingroup common
\headerfile HostnameResolver.h arc/HostnameResolver.h */
class HostnameResolverContainer {
public:
/// Creates container with number of stored objects between minval and maxval.
HostnameResolverContainer(unsigned int minval, unsigned int maxval);
/// Creates container with number of stored objects between 1 and 10.
HostnameResolverContainer(void);
/// Destroys container and all stored objects.
~HostnameResolverContainer(void);
/// Get object from container.
/** Object either is taken from stored ones or new one created.
Acquired object looses its connection to container and
can be safely destroyed or returned into other container. */
HostnameResolver* Acquire(void);
/// Returns object into container.
/** It can be any object - taken from another container or created using
new. */
void Release(HostnameResolver* hr);
/// Adjust minimal number of stored objects.
void SetMin(unsigned int val);
/// Adjust maximal number of stored objects.
void SetMax(unsigned int val);
private:
Glib::Mutex lock_;
unsigned int min_;
unsigned int max_;
std::list<HostnameResolver*> hrs_;
void KeepRange(void);
};
} // namespace Arc
#endif // __ARC_HOSTNAMERESOLVER_H__
......@@ -20,7 +20,7 @@ libarccommon_la_HEADERS = ArcVersion.h ArcConfig.h ArcLocation.h \
Base64.h CheckSum.h DBInterface.h DateTime.h \
FileLock.h FileUtils.h FileAccess.h GUID.h IString.h \
Logger.h OptionParser.h StringConv.h Thread.h URL.h \
User.h UserConfig.h Utils.h XMLNode.h \
User.h UserConfig.h Utils.h XMLNode.h HostnameResolver.h \
Counter.h IntraProcessCounter.h IniConfig.h Profile.h \
Run.h Watchdog.h JobPerfLog.h win32.h $(MYSQL_WRAPPER_HEADER)
......@@ -35,7 +35,7 @@ libarccommon_la_SOURCES = ArcVersion.cpp ArcConfig.cpp ArcLocation.cpp \
Base64.cpp CheckSum.cpp DateTime.cpp \
FileLock.cpp FileUtils.cpp FileAccess.cpp GUID.cpp IString.cpp \
Logger.cpp OptionParser.cpp StringConv.cpp Thread.cpp URL.cpp \
User.cpp UserConfig.cpp Utils.cpp XMLNode.cpp \
User.cpp UserConfig.cpp Utils.cpp XMLNode.cpp HostnameResolver.cpp \
Counter.cpp IntraProcessCounter.cpp IniConfig.cpp Profile.cpp JobPerfLog.cpp \
$(PLATFORM_SPECIFIC) $(MYSQL_WRAPPER_CPP)
libarccommon_la_CXXFLAGS = -I$(top_srcdir)/include \
......
#define CMD_PING (0)
// -
#define CMD_RESOLVE (1)
// string hostname
// -
// result
// errno
// [
// int family
// socklen_t length
// void addr[]
// ]*
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment