remotebackend-access.log
test_remotebackend_http
test_remotebackend_pipe
+test_remotebackend_unix
test_remotebackend_json
test_remotebackend_post
#endif
EXTRA_DIST=OBJECTFILES OBJECTLIBS
-EXTRA_PROGRAMS=test_remotebackend_pipe test_remotebackend_http test_remotebackend_post test_remotebackend_json
+EXTRA_PROGRAMS=test_remotebackend_pipe test_remotebackend_unix test_remotebackend_http test_remotebackend_post test_remotebackend_json
EXTRA_LTLIBRARIES=libtestremotebackend.la
lib_LTLIBRARIES = libremotebackend.la
libremotebackend_la_LIBS=$(LIBCURL_LIBS)
TESTS_ENVIRONMENT = env BOOST_TEST_LOG_LEVEL=message REMOTEBACKEND_HTTP=$(REMOTEBACKEND_HTTP) ./testrunner.sh
-TESTS=test_remotebackend_pipe test_remotebackend_http test_remotebackend_post test_remotebackend_json
+TESTS=test_remotebackend_pipe test_remotebackend_unix test_remotebackend_http test_remotebackend_post test_remotebackend_json
BUILT_SOURCES=../../pdns/dnslabeltext.cc
../../pdns/aes/dns_random.cc ../../pdns/packetcache.hh ../../pdns/packetcache.cc \
../../pdns/aes/aescpp.h ../../pdns/dns.hh ../../pdns/dns.cc ../../pdns/json.hh ../../pdns/json.cc \
../../pdns/aes/aescrypt.c ../../pdns/aes/aes.h ../../pdns/aes/aeskey.c ../../pdns/aes/aes_modes.c ../../pdns/aes/aesopt.h \
- ../../pdns/aes/aestab.c ../../pdns/aes/aestab.h ../../pdns/aes/brg_endian.h ../../pdns/aes/brg_types.h ../pipebackend/coprocess.cc \
+ ../../pdns/aes/aestab.c ../../pdns/aes/aestab.h ../../pdns/aes/brg_endian.h ../../pdns/aes/brg_types.h \
remotebackend.hh remotebackend.cc unixconnector.cc httpconnector.cc pipeconnector.cc
libtestremotebackend_la_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
libtestremotebackend_la_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
test_remotebackend_pipe_SOURCES=test-remotebackend.cc test-remotebackend-pipe.cc
+test_remotebackend_unix_SOURCES=test-remotebackend.cc test-remotebackend-unix.cc
test_remotebackend_http_SOURCES=test-remotebackend.cc test-remotebackend-http.cc ../../config.h
test_remotebackend_post_SOURCES=test-remotebackend.cc test-remotebackend-post.cc ../../config.h
test_remotebackend_json_SOURCES=test-remotebackend.cc test-remotebackend-json.cc ../../config.h
test_remotebackend_pipe_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
test_remotebackend_pipe_LDADD=libtestremotebackend.la @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) ../../pdns/ext/polarssl-1.1.2/library/libpolarssl.a $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(BOOST_SERIALIZATION_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) @LIBDL@ $(LIBCURL_LIBS)
+test_remotebackend_unix_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
+test_remotebackend_unix_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
+test_remotebackend_unix_LDADD=libtestremotebackend.la @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) ../../pdns/ext/polarssl-1.1.2/library/libpolarssl.a $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(BOOST_SERIALIZATION_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) @LIBDL@ $(LIBCURL_LIBS)
+
test_remotebackend_http_CFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
test_remotebackend_http_CXXFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ $(LIBCURL_CFLAGS) -g -O0 -I../../pdns
test_remotebackend_http_LDADD=libtestremotebackend.la @DYNLINKFLAGS@ @THREADFLAGS@ $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) ../../pdns/ext/polarssl-1.1.2/library/libpolarssl.a $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(BOOST_SERIALIZATION_LIBS) $(BOOST_PROGRAM_OPTIONS_LIBS) @LIBDL@ $(LIBCURL_LIBS)
#ifdef REMOTEBACKEND_HTTP
HTTPConnector::HTTPConnector(std::map<std::string,std::string> options) {
+ this->d_c = NULL;
this->d_url = options.find("url")->second;
if (options.find("url-suffix") != options.end()) {
this->d_url_suffix = options.find("url-suffix")->second;
curl_easy_setopt(d_c, CURLOPT_POSTFIELDSIZE, nsize);
curl_easy_setopt(d_c, CURLOPT_COPYPOSTFIELDS, postfields);
curl_free(tmpstr);
- delete postfields;
+ delete [] postfields;
} else if (method == "superMasterBackend") {
std::stringstream ss2;
addUrlComponent(parameters, "ip", ss);
+#include <sys/types.h>
+#include <sys/wait.h>
#include "remotebackend.hh"
PipeConnector::PipeConnector(std::map<std::string,std::string> options) {
}
this->command = options.find("command")->second;
this->options = options;
- this->coproc = NULL;
+ d_timeout=2000;
+
+ if (options.find("timeout") != options.end()) {
+ d_timeout = boost::lexical_cast<int>(options.find("timeout")->second);
+ }
+
+ d_pid = -1;
+ d_fp = NULL;
launch();
}
PipeConnector::~PipeConnector(){
- if (this->coproc != NULL)
- delete coproc;
+ int status;
+ // just in case...
+ if (d_pid == -1) return;
+
+ if(!waitpid(d_pid, &status, WNOHANG)) {
+ kill(d_pid, 9);
+ waitpid(d_pid, &status, 0);
+ }
+
+ close(d_fd1[1]);
+ if (d_fp != NULL) fclose(d_fp);
}
void PipeConnector::launch() {
- if (coproc != NULL) return;
+ // no relaunch
+ if (d_pid > 0 && checkStatus()) return;
+
+ std::vector <std::string> v;
+ split(v, command, is_any_of(" "));
+
+ const char *argv[v.size()+1];
+ argv[v.size()]=0;
+
+ for (size_t n = 0; n < v.size(); n++)
+ argv[n]=v[n].c_str();
+
+ signal(SIGPIPE, SIG_IGN);
+
+ if(access(argv[0],X_OK)) // check before fork so we can throw
+ throw AhuException("Command '"+string(argv[0])+"' cannot be executed: "+stringerror());
+
+ if(pipe(d_fd1)<0 || pipe(d_fd2)<0)
+ throw AhuException("Unable to open pipe for coprocess: "+string(strerror(errno)));
+
+ if((d_pid=fork())<0)
+ throw AhuException("Unable to fork for coprocess: "+stringerror());
+ else if(d_pid>0) { // parent speaking
+ close(d_fd1[0]);
+ Utility::setCloseOnExec(d_fd1[1]);
+ close(d_fd2[1]);
+ Utility::setCloseOnExec(d_fd2[0]);
+ if(!(d_fp=fdopen(d_fd2[0],"r")))
+ throw AhuException("Unable to associate a file pointer with pipe: "+stringerror());
+ setbuf(d_fp,0); // no buffering please, confuses select
+ }
+ else if(!d_pid) { // child
+ signal(SIGCHLD, SIG_DFL); // silence a warning from perl
+ close(d_fd1[1]);
+ close(d_fd2[0]);
+
+ if(d_fd1[0]!= 0) {
+ dup2(d_fd1[0], 0);
+ close(d_fd1[0]);
+ }
+
+ if(d_fd2[1]!= 1) {
+ dup2(d_fd2[1], 1);
+ close(d_fd2[1]);
+ }
+
+ // stdin & stdout are now connected, fire up our coprocess!
+
+ if(execv(argv[0], const_cast<char * const *>(argv))<0) // now what
+ exit(123);
+
+ /* not a lot we can do here. We shouldn't return because that will leave a forked process around.
+ no way to log this either - only thing we can do is make sure that our parent catches this soonest! */
+ }
+
rapidjson::Value val;
rapidjson::Document init,res;
- int timeout=2000;
- if (options.find("timeout") != options.end()) {
- timeout = boost::lexical_cast<int>(options.find("timeout")->second);
- }
- coproc = new CoProcess(this->command, timeout);
init.SetObject();
val = "initialize";
+
init.AddMember("method",val, init.GetAllocator());
val.SetObject();
init.AddMember("parameters", val, init.GetAllocator());
int PipeConnector::send_message(const rapidjson::Document &input)
{
- std::string data;
+ std::string line;
+ line = makeStringFromDocument(input);
+ launch();
- data = makeStringFromDocument(input);
+ line.append(1,'\n');
- launch();
- try {
- coproc->send(data);
- return 1;
+ unsigned int sent=0;
+ int bytes;
+
+ // writen routine - socket may not accept al data in one go
+ while(sent<line.size()) {
+ bytes=write(d_fd1[1],line.c_str()+sent,line.length()-sent);
+ if(bytes<0)
+ throw AhuException("Writing to coprocess failed: "+std::string(strerror(errno)));
+
+ sent+=bytes;
}
- catch(AhuException &ae) {
- delete coproc;
- coproc=NULL;
- throw;
- }
+ return sent;
}
int PipeConnector::recv_message(rapidjson::Document &output)
{
+ std::string receive;
rapidjson::GenericReader<rapidjson::UTF8<> , rapidjson::MemoryPoolAllocator<> > r;
std::string tmp;
std::string s_output;
-
launch();
- try {
- while(1) {
- coproc->receive(tmp);
- s_output.append(tmp);
- rapidjson::StringStream ss(s_output.c_str());
- output.ParseStream<0>(ss);
- if (output.HasParseError() == false)
- return s_output.size();
- }
- } catch(AhuException &ae) {
- L<<Logger::Warning<<"[pipeconnector] "<<" unable to receive data from coprocess. "<<ae.reason<<endl;
- delete coproc;
- coproc = NULL;
- throw;
+
+ while(1) {
+ receive.clear();
+ if(d_timeout) {
+ struct timeval tv;
+ tv.tv_sec = d_timeout/1000;
+ tv.tv_usec = (d_timeout % 1000) * 1000;
+ fd_set rds;
+ FD_ZERO(&rds);
+ FD_SET(fileno(d_fp),&rds);
+ int ret=select(fileno(d_fp)+1,&rds,0,0,&tv);
+ if(ret<0)
+ throw AhuException("Error waiting on data from coprocess: "+stringerror());
+ if(!ret)
+ throw AhuException("Timeout waiting for data from coprocess");
+ }
+
+ if(!stringfgets(d_fp, receive))
+ throw AhuException("Child closed pipe");
+
+ s_output.append(receive);
+ rapidjson::StringStream ss(s_output.c_str());
+ output.ParseStream<0>(ss);
+ if (output.HasParseError() == false)
+ return s_output.size();
}
+ return 0;
+}
+
+bool PipeConnector::checkStatus()
+{
+ int status;
+ int ret=waitpid(d_pid, &status, WNOHANG);
+ if(ret<0)
+ throw AhuException("Unable to ascertain status of coprocess "+itoa(d_pid)+" from "+itoa(getpid())+": "+string(strerror(errno)));
+ else if(ret) {
+ if(WIFEXITED(status)) {
+ int ret=WEXITSTATUS(status);
+ throw AhuException("Coprocess exited with code "+itoa(ret));
+ }
+ if(WIFSIGNALED(status)) {
+ int sig=WTERMSIG(status);
+ string reason="CoProcess died on receiving signal "+itoa(sig);
+#ifdef WCOREDUMP
+ if(WCOREDUMP(status))
+ reason+=". Dumped core";
+#endif
+
+ throw AhuException(reason);
+ }
+ }
+ return true;
}
RemoteBackend::RemoteBackend(const std::string &suffix)
{
setArgPrefix("remote"+suffix);
- build(getArg("connection-string"));
+
+ this->d_connstr = getArg("connection-string");
this->d_result = NULL;
this->d_dnssec = mustDo("dnssec");
this->d_index = -1;
this->d_trxid = 0;
+
+ build();
}
RemoteBackend::~RemoteBackend() {
}
}
+bool RemoteBackend::send(rapidjson::Document &value) {
+ try {
+ return connector->send(value);
+ } catch (AhuException &ex) {
+ L<<Logger::Error<<"Exception caught when sending: "<<ex.reason<<std::endl;
+ } catch (...) {
+ L<<Logger::Error<<"Exception caught when sending"<<std::endl;
+ }
+
+ delete this->connector;
+ build();
+ return false;
+}
+
+bool RemoteBackend::recv(rapidjson::Document &value) {
+ try {
+ return connector->recv(value);
+ } catch (AhuException &ex) {
+ L<<Logger::Error<<"Exception caught when receiving: "<<ex.reason<<std::endl;
+ } catch (...) {
+ L<<Logger::Error<<"Exception caught when receiving"<<std::endl;;
+ }
+
+ delete this->connector;
+ build();
+ return false;
+}
+
+
/**
* Builds connector based on options
* Currently supports unix,pipe and http
*/
-int RemoteBackend::build(const std::string &connstr) {
+int RemoteBackend::build() {
std::vector<std::string> parts;
std::string type;
std::string opts;
// connstr is of format "type:options"
size_t pos;
- pos = connstr.find_first_of(":");
+ pos = d_connstr.find_first_of(":");
if (pos == std::string::npos)
throw AhuException("Invalid connection string: malformed");
- type = connstr.substr(0, pos);
- opts = connstr.substr(pos+1);
+ type = d_connstr.substr(0, pos);
+ opts = d_connstr.substr(pos+1);
// tokenize the string on comma
stringtok(parts, opts, ",");
d_result = new rapidjson::Document();
- if (connector->send(query) == false || connector->recv(*d_result) == false) {
+ if (this->send(query) == false || this->recv(*d_result) == false) {
delete d_result;
return;
}
d_result = new rapidjson::Document();
- if (connector->send(query) == false || connector->recv(*d_result) == false) {
+ if (this->send(query) == false || this->recv(*d_result) == false) {
delete d_result;
return false;
}
JSON_ADD_MEMBER(parameters, "qname", qname.c_str(), query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
unhashed = getString(answer["result"]["unhashed"]);
JSON_ADD_MEMBER(parameters, "kind", kind.c_str(), query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false)
+ if (this->send(query) == false)
return false;
meta.clear();
// not mandatory to implement
- if (connector->recv(answer) == false)
+ if (this->recv(answer) == false)
return true;
if (answer["result"].IsArray()) {
parameters.AddMember("value", val, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return getBool(answer["result"]);
JSON_ADD_MEMBER(parameters, "kind", kind, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
keys.clear();
JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
parameters.AddMember("key", jkey, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return getInt(answer["result"]);
JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
JSON_ADD_MEMBER(parameters, "id", id, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
JSON_ADD_MEMBER(parameters, "name", name.c_str(), query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
if (algorithm != NULL)
JSON_ADD_MEMBER(parameters, "name", domain.c_str(), query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
// make sure we got zone & kind
JSON_ADD_MEMBER(parameters, "serial", serial, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false) {
+ if (this->send(query) == false || this->recv(answer) == false) {
L<<Logger::Error<<kBackendId<<"Failed to execute RPC for RemoteBackend::setNotified("<<id<<","<<serial<<")"<<endl;
}
}
*ddb = 0;
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
// we are the backend
JSON_ADD_MEMBER(parameters, "account", account.c_str(), query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
}
parameters.AddMember("rrset", rj_rrset, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true; // XXX FIXME this API should not return 'true' I think -ahu
}
parameters.AddMember("nonterm", nts, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
}
parameters.AddMember("nonterm", nts, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
}
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false) {
+ if (this->send(query) == false || this->recv(answer) == false) {
d_trxid = -1;
return false;
}
query.AddMember("parameters", parameters, query.GetAllocator());
d_trxid = -1;
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
}
query.AddMember("parameters", parameters, query.GetAllocator());
d_trxid = -1;
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
return true;
}
parameters.AddMember("sd", soadata, query.GetAllocator());
query.AddMember("parameters", parameters, query.GetAllocator());
- if (connector->send(query) == false || connector->recv(answer) == false)
+ if (this->send(query) == false || this->recv(answer) == false)
return false;
serial = getInt64(answer["result"]);
#include <boost/lexical_cast.hpp>
#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
-#include "../pipebackend/coprocess.hh"
#include "pdns/json.hh"
#ifdef REMOTEBACKEND_HTTP
private:
void launch();
- CoProcess *coproc;
+ bool checkStatus();
+
std::string command;
std::map<std::string,std::string> options;
+
+ int d_fd1[2], d_fd2[2];
+ int d_pid;
+ int d_timeout;
+ FILE *d_fp;
};
class RemoteBackend : public DNSBackend
static DNSBackend *maker();
private:
- int build(const std::string &connstr);
+ int build();
Connector *connector;
bool d_dnssec;
rapidjson::Document *d_result;
int d_index;
int64_t d_trxid;
+ std::string d_connstr;
bool getBool(rapidjson::Value &value);
int getInt(rapidjson::Value &value);
int64_t getInt64(rapidjson::Value &value);
std::string getString(rapidjson::Value &value);
double getDouble(rapidjson::Value &value);
+
+ bool send(rapidjson::Document &value);
+ bool recv(rapidjson::Document &value);
};
#endif
--- /dev/null
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_MAIN
+#define BOOST_TEST_MODULE unit
+
+#include <boost/test/unit_test.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/tuple/tuple.hpp>
+#include "pdns/namespaces.hh"
+#include <pdns/dns.hh>
+#include <pdns/dnsbackend.hh>
+#include <pdns/dnspacket.hh>
+#include <pdns/ueberbackend.hh>
+#include <pdns/ahuexception.hh>
+#include <pdns/logger.hh>
+#include <pdns/arguments.hh>
+#include "pdns/dnsrecords.hh"
+#include <boost/lexical_cast.hpp>
+#include <rapidjson/rapidjson.h>
+#include <rapidjson/document.h>
+#include "pdns/json.hh"
+#include "pdns/statbag.hh"
+#include "pdns/packetcache.hh"
+
+StatBag S;
+PacketCache PC;
+ArgvMap &arg()
+{
+ static ArgvMap arg;
+ return arg;
+};
+
+class RemoteLoader
+{
+ public:
+ RemoteLoader();
+};
+
+DNSBackend *be;
+
+struct RemotebackendSetup {
+ RemotebackendSetup() {
+ be = 0;
+ try {
+ // setup minimum arguments
+ ::arg().set("module-dir")="";
+ new RemoteLoader();
+ BackendMakers().launch("remote");
+ // then get us a instance of it
+ ::arg().set("remote-connection-string")="unix:path=/tmp/remotebackend.sock";
+ ::arg().set("remote-dnssec")="yes";
+ be = BackendMakers().all()[0];
+ // load few record types to help out
+ SOARecordContent::report();
+ NSRecordContent::report();
+ ARecordContent::report();
+ } catch (AhuException &ex) {
+ BOOST_TEST_MESSAGE("Cannot start remotebackend: " << ex.reason );
+ };
+ }
+ ~RemotebackendSetup() { }
+};
+
+BOOST_GLOBAL_FIXTURE( RemotebackendSetup );
+
#!/bin/bash
webrick_pid=""
+socat_pid=""
+socat=/usr/bin/socat
function start_web() {
if [ x"$REMOTEBACKEND_HTTP" == "xyes" ]; then
fi
}
+function start_unix() {
+ if [ ! -x $socat ]; then
+ echo "Cannot find socat - cannot test (non-fatal)"
+ exit 0
+ fi
+
+ $socat unix-listen:/tmp/remotebackend.sock exec:./unittest_pipe.rb &
+ socat_pid=$!
+ sleep 1
+}
+
+function stop_unix() {
+ if [ ! -z "$socat_pid" ]; then
+ kill -TERM $socat_pid 2>/dev/null
+ if [ $? -ne 0 ]; then
+ # already dead
+ return
+ fi
+ # wait a moment for it to die
+ i=0
+ while [ $i -lt 5 ]; do
+ sleep 1
+ kill -0 $socat_pid 2>/dev/null
+ if [ $? -ne 0 ]; then break; fi
+ let i=i+1
+ done
+ fi
+}
+
mode=`basename "$1"`
case "$mode" in
./test_remotebackend_pipe
rv=$?
;;
+ test_remotebackend_unix)
+ start_unix
+ ./test_remotebackend_unix
+ rv=$?
+ stop_unix
+ ;;
test_remotebackend_http)
start_web "http"
./test_remotebackend_http
this->path = options.find("path")->second;
this->options = options;
this->connected = false;
+ this->fd = -1;
}
UnixsocketConnector::~UnixsocketConnector() {
struct sockaddr_un sock;
rapidjson::Document init,res;
rapidjson::Value val;
+ int rv;
if (connected) return; // no point reconnecting if connected...
connected = true;
sock.sun_family = AF_UNIX;
memset(sock.sun_path, 0, UNIX_PATH_MAX);
path.copy(sock.sun_path, UNIX_PATH_MAX, 0);
- fcntl(fd, F_SETFL, O_NONBLOCK, &fd);
+ if (fcntl(fd, F_SETFL, O_NONBLOCK, &fd)) {
+ connected = false;
+ L<<Logger::Error<<"Cannot manipulate socket: " << strerror(errno) << std::endl;;
+ close(fd);
+ return;
+ }
- while(connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock)==-1 && (errno == EINPROGRESS)) {
- waitForData(fd, 0, 500);
+ if((rv = connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock))==-1 && (errno == EINPROGRESS)) {
+ waitForData(fd, 0, -1);
+ rv = connect(fd, reinterpret_cast<struct sockaddr*>(&sock), sizeof sock);
}
- if (errno != EISCONN && errno != 0) {
+ if (rv != 0 && errno != EISCONN && errno != 0) {
L<<Logger::Error<<"Cannot connect to socket: " << strerror(errno) << std::endl;
close(fd);
connected = false;