From a527c56234a1d85b189f180efd0391bd27bf62a1 Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Tue, 22 Jul 2014 22:59:10 +0300 Subject: [PATCH] Use zeromq library C API, based on work by @wtfuzz and @hexwave. Fixes #1760. (cherry picked from commit 49e4360a664e68a62eda2101e9e6af9b18d6005f) Conflicts: .travis.yml --- .travis.yml | 4 +- m4/pdns_enable_remotebackend_zeromq.m4 | 12 ++++ modules/remotebackend/Gemfile.lock | 14 ++-- .../remotebackend/regression-tests/Gemfile | 9 +-- .../regression-tests/Gemfile.lock | 16 +++-- .../regression-tests/zeromq-backend.rb | 2 +- modules/remotebackend/remotebackend.hh | 12 +++- .../test-remotebackend-zeromq.cc | 2 +- modules/remotebackend/unittest_zeromq.rb | 6 +- modules/remotebackend/zmqconnector.cc | 64 ++++++++++++------- regression-tests/backends/remote-master | 1 + 11 files changed, 93 insertions(+), 49 deletions(-) diff --git a/.travis.yml b/.travis.yml index dfd83156e..b5070eac7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,10 @@ before_script: - sudo /sbin/ip addr add 1.2.3.4/32 dev lo - sudo rm /etc/apt/sources.list.d/travis_ci_zeromq3-source.list - sudo apt-get update - - sudo apt-get install --no-install-recommends libboost-all-dev libtolua-dev bc libcdb-dev libnet-dns-perl unbound-host ldnsutils dnsutils bind9utils libtool libcdb-dev xmlto links asciidoc ruby-json ruby-sqlite3 rubygems libcurl4-openssl-dev ruby1.9.1 socat time libzmq1 libzmq-dev pkg-config daemontools authbind liblua5.1-posix1 libopendbx1-dev libopendbx1-sqlite3 python-virtualenv libldap2-dev softhsm libp11-kit-dev p11-kit moreutils libgeoip-dev geoip-database + - sudo apt-get install --no-install-recommends libboost-all-dev libtolua-dev bc libcdb-dev libnet-dns-perl unbound-host ldnsutils dnsutils bind9utils libtool libcdb-dev xmlto links asciidoc ruby-json ruby-sqlite3 rubygems libcurl4-openssl-dev ruby1.9.1 socat time pkg-config daemontools authbind liblua5.1-posix1 libopendbx1-dev libopendbx1-sqlite3 python-virtualenv libldap2-dev softhsm libp11-kit-dev p11-kit moreutils libgeoip-dev geoip-database - sudo sh -c 'sed s/precise/trusty/g /etc/apt/sources.list > /etc/apt/sources.list.d/trusty.list' - sudo apt-get update - - sudo apt-get install liblmdb0 liblmdb-dev lmdb-utils libyaml-cpp-dev + - sudo apt-get install liblmdb0 liblmdb-dev lmdb-utils libyaml-cpp-dev libzmq3-dev - sudo update-alternatives --set ruby /usr/bin/ruby1.9.1 - sudo touch /etc/authbind/byport/53 - sudo chmod 755 /etc/authbind/byport/53 diff --git a/m4/pdns_enable_remotebackend_zeromq.m4 b/m4/pdns_enable_remotebackend_zeromq.m4 index 10f41606a..9fba38fdf 100644 --- a/m4/pdns_enable_remotebackend_zeromq.m4 +++ b/m4/pdns_enable_remotebackend_zeromq.m4 @@ -18,9 +18,21 @@ AC_DEFUN([PDNS_ENABLE_REMOTEBACKEND_ZEROMQ],[ AC_DEFINE([HAVE_LIBZMQ], [1], [Define to 1 if you have libzmq]) AC_DEFINE([REMOTEBACKEND_ZEROMQ], [1], [Define to 1 if you have the ZeroMQ connector]) REMOTEBACKEND_ZEROMQ=yes + ], [AC_MSG_ERROR([Could not find libzmq])] )] + + old_CXXFLAGS="$CXXFLAGS" + old_LDFLAGS="$LDFLAGS" + CXXFLAGS="$CFLAGS $LIBZMQ_CFLAGS" + LDFLAGS="$LDFLAGS $LIBZMQ_LIBS" + AC_CHECK_LIB([zmq], [zmq_msg_send], + [ + AC_DEFINE([HAVE_ZMQ_MSG_SEND], [1], [Define to 1 if the ZeroMQ 3.x or greater API is available]) + ]) + CXXFLAGS="$old_CXXFLAGS" + LDFLAGS="$old_LDFLAGS" ) ]) diff --git a/modules/remotebackend/Gemfile.lock b/modules/remotebackend/Gemfile.lock index 6750b1184..276a96c9c 100644 --- a/modules/remotebackend/Gemfile.lock +++ b/modules/remotebackend/Gemfile.lock @@ -1,14 +1,16 @@ GEM remote: https://rubygems.org/ specs: - ffi (1.9.3) - ffi-rzmq (1.0.3) - ffi + ffi (1.9.6) + ffi-rzmq (2.0.1) + ffi-rzmq-core (>= 1.0.1) + ffi-rzmq-core (1.0.3) + ffi (~> 1.9) json (1.8.1) - sqlite3 (1.3.8) + sqlite3 (1.3.9) webrick (1.3.1) - zeromqrb (0.1.1) - ffi-rzmq (~> 1.0) + zeromqrb (0.1.3) + ffi-rzmq PLATFORMS ruby diff --git a/modules/remotebackend/regression-tests/Gemfile b/modules/remotebackend/regression-tests/Gemfile index 019fc2a22..2762b5430 100644 --- a/modules/remotebackend/regression-tests/Gemfile +++ b/modules/remotebackend/regression-tests/Gemfile @@ -1,5 +1,6 @@ -source 'https://rubygems.org' +source "https://rubygems.org" -gem 'webrick' -gem 'sqlite3' -gem 'zeromqrb' +gem "json" +gem "webrick" +gem "zeromqrb" +gem "sqlite3" diff --git a/modules/remotebackend/regression-tests/Gemfile.lock b/modules/remotebackend/regression-tests/Gemfile.lock index 44b2bee99..276a96c9c 100644 --- a/modules/remotebackend/regression-tests/Gemfile.lock +++ b/modules/remotebackend/regression-tests/Gemfile.lock @@ -1,18 +1,22 @@ GEM remote: https://rubygems.org/ specs: - ffi (1.9.3) - ffi-rzmq (1.0.3) - ffi - sqlite3 (1.3.8) + ffi (1.9.6) + ffi-rzmq (2.0.1) + ffi-rzmq-core (>= 1.0.1) + ffi-rzmq-core (1.0.3) + ffi (~> 1.9) + json (1.8.1) + sqlite3 (1.3.9) webrick (1.3.1) - zeromqrb (0.1.1) - ffi-rzmq (~> 1.0) + zeromqrb (0.1.3) + ffi-rzmq PLATFORMS ruby DEPENDENCIES + json sqlite3 webrick zeromqrb diff --git a/modules/remotebackend/regression-tests/zeromq-backend.rb b/modules/remotebackend/regression-tests/zeromq-backend.rb index c8f725821..2af3bd9f9 100755 --- a/modules/remotebackend/regression-tests/zeromq-backend.rb +++ b/modules/remotebackend/regression-tests/zeromq-backend.rb @@ -14,7 +14,7 @@ f.sync = true begin context = ZeroMQ::Context.new socket = context.socket ZMQ::REP - socket.bind("ipc:///tmp/pdns.0") + socket.bind("ipc:///tmp/pdns.0") or raise "Cannot bind to IPC socket" while(true) do line = "" diff --git a/modules/remotebackend/remotebackend.hh b/modules/remotebackend/remotebackend.hh index 38118efbc..b730c2564 100644 --- a/modules/remotebackend/remotebackend.hh +++ b/modules/remotebackend/remotebackend.hh @@ -18,7 +18,13 @@ #include "sstuff.hh" #ifdef REMOTEBACKEND_ZEROMQ -#include +#include + +// If the available ZeroMQ library version is < 2.x, create macros for the zmq_msg_send/recv functions +#ifndef HAVE_ZMQ_MSG_SEND +#define zmq_msg_send(msg, socket, flags) zmq_send(socket, msg, flags) +#define zmq_msg_recv(msg, socket, flags) zmq_recv(socket, msg, flags) +#endif #endif #define JSON_GET(obj,val,def) (obj.HasMember(val)?obj["" val ""]:def) #define JSON_ADD_MEMBER(obj, name, val, alloc) { rapidjson::Value __xval; __xval = val; obj.AddMember(name, __xval, alloc); } @@ -90,8 +96,8 @@ class ZeroMQConnector: public Connector { int d_timeout; int d_timespent; std::map d_options; - zmq::context_t d_ctx; - zmq::socket_t d_sock; + void *d_ctx; + void *d_sock; }; #endif diff --git a/modules/remotebackend/test-remotebackend-zeromq.cc b/modules/remotebackend/test-remotebackend-zeromq.cc index 266ea1916..b096f8d85 100644 --- a/modules/remotebackend/test-remotebackend-zeromq.cc +++ b/modules/remotebackend/test-remotebackend-zeromq.cc @@ -49,7 +49,7 @@ struct RemotebackendSetup { new RemoteLoader(); BackendMakers().launch("remote"); // then get us a instance of it - ::arg().set("remote-connection-string")="zeromq:endpoint=tcp://127.0.0.1:43622"; + ::arg().set("remote-connection-string")="zeromq:endpoint=ipc:///tmp/remotebackend.0"; ::arg().set("remote-dnssec")="yes"; be = BackendMakers().all()[0]; // load few record types to help out diff --git a/modules/remotebackend/unittest_zeromq.rb b/modules/remotebackend/unittest_zeromq.rb index a61a97942..7f1b82af7 100755 --- a/modules/remotebackend/unittest_zeromq.rb +++ b/modules/remotebackend/unittest_zeromq.rb @@ -18,7 +18,7 @@ trap('TERM') { runcond = false } begin context = ZeroMQ::Context.new socket = context.socket ZMQ::REP - socket.bind("tcp://127.0.0.1:43622") + socket.bind("ipc:///tmp/remotebackend.0") print "[#{Time.now.to_s}] ZeroMQ unit test responder running\n" @@ -43,10 +43,10 @@ begin else res, log = h.send(method) end - socket.send_string ({:result => res, :log => log}).to_json, 0 + socket.send_string ({:result => res, :log => log}).to_json + "\n" , 0 f.puts "#{Time.now.to_f} [zmq]: #{({:result => res, :log => log}).to_json}" rescue JSON::ParserError - socket.send_string ({:result => false, :log => "Cannot parse input #{line}"}).to_json + socket.send_string ({:result => false, :log => "Cannot parse input #{line}"}).to_json + "\n"; f.puts "#{Time.now.to_f} [zmq]: #{({:result => false, :log => "Cannot parse input #{line}"}).to_json}" next end diff --git a/modules/remotebackend/zmqconnector.cc b/modules/remotebackend/zmqconnector.cc index 5f863d2e8..8e427c49f 100644 --- a/modules/remotebackend/zmqconnector.cc +++ b/modules/remotebackend/zmqconnector.cc @@ -9,9 +9,10 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" -ZeroMQConnector::ZeroMQConnector(std::map options) : d_ctx(1), d_sock(d_ctx, ZMQ_REQ) { +ZeroMQConnector::ZeroMQConnector(std::map options) { rapidjson::Value val; rapidjson::Document init,res; + int opt=0; // lookup timeout, target and stuff if (options.count("endpoint") == 0) { @@ -26,7 +27,15 @@ ZeroMQConnector::ZeroMQConnector(std::map options) : d_ this->d_timeout = boost::lexical_cast(options.find("timeout")->second); } - d_sock.connect(d_endpoint.c_str()); + d_ctx = zmq_init(2); + d_sock = zmq_socket(this->d_ctx, ZMQ_REQ); + zmq_setsockopt(d_sock, ZMQ_LINGER, &opt, sizeof(opt)); + + if(zmq_connect(this->d_sock, this->d_endpoint.c_str()) < 0) + { + L< options) : d_ this->send(init); if (this->recv(res)==false) { L<d_sock); + zmq_term(this->d_ctx); }; int ZeroMQConnector::send_message(const rapidjson::Document &input) { std::string line; line = makeStringFromDocument(input); - zmq::message_t message(line.size()+1); - line.copy(reinterpret_cast(message.data()), line.size()); - reinterpret_cast(message.data())[line.size()]=0; + zmq_msg_t message; + + zmq_msg_init_size(&message, line.size()+1); + line.copy(reinterpret_cast(zmq_msg_data(&message)), line.size()); + ((char *)zmq_msg_data(&message))[line.size()] = '\0'; try { zmq_pollitem_t item; @@ -63,17 +77,16 @@ int ZeroMQConnector::send_message(const rapidjson::Document &input) { // poll until it's sent or timeout is spent. try to leave // leave few cycles for read. just in case. for(d_timespent = 0; d_timespent < d_timeout-5; d_timespent++) { - if (zmq::poll(&item, 1, 1000)>0) { - if (d_sock.send(message, 0) == false) { + if (zmq_poll(&item, 1, 1)>0) { + if(zmq_msg_send(&message, this->d_sock, 0) == -1) { // message was not sent - L<d_endpoint << ": " << errno; - return 0; - } - return line.size(); + L<d_endpoint << ": " << zmq_strerror(errno)<d_endpoint << ": " << ex.what(); + L<d_endpoint << ": " << ex.what()< , rapidjson::MemoryPoolAllocator<> > r; - zmq::message_t message; + zmq_msg_t message; item.socket = d_sock; item.events = ZMQ_POLLIN; @@ -95,23 +108,28 @@ int ZeroMQConnector::recv_message(rapidjson::Document &output) { // d_timespent should always be initialized by send_message, recv should never // be called without send first. for(; d_timespent < d_timeout; d_timespent++) { - if (zmq::poll(&item, 1, 1000)>0) { + if (zmq_poll(&item, 1, 1)>0) { // we have an event if ((item.revents & ZMQ_POLLIN) == ZMQ_POLLIN) { char *data; + size_t msg_size; + zmq_msg_init(&message); // read something - if (d_sock.recv(&message, 0) && message.size() > 0) { - data = new char[message.size()+1]; - // convert it into json - memcpy(data, message.data(), message.size()); - data[message.size()]=0; + if(zmq_msg_recv(&message, this->d_sock, ZMQ_NOBLOCK)>0) { + msg_size = zmq_msg_size(&message); + data = new char[msg_size+1]; + memcpy(data, zmq_msg_data(&message), msg_size); + data[msg_size] = '\0'; + zmq_msg_close(&message); + rapidjson::StringStream ss(data); output.ParseStream<0>(ss); - delete [] data; + delete[] data; + if (output.HasParseError() == false) - rv = message.size(); + rv = msg_size; else - L<d_endpoint; + L<d_endpoint<d_endpoint << ": " << ex.what(); + L<d_endpoint << ": " << ex.what()< pdns-remotebackend.pid + sleep 20 # just a test ;; unix) connstr="unix:path=/tmp/remote.socket" -- 2.40.0