Commit 51803e40 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@2009 e5f2f494-b856-4b98-b285-d166d9295462
parents f28f0da3 1e7b9738
......@@ -141,13 +141,15 @@ AC_SEARCH_LIBS(recvfrom, [socket])
AC_HEADER_STDBOOL
AC_TYPE_SIZE_T
AC_MSG_CHECKING(for sa_len in struct sockaddr)
AC_TRY_COMPILE([
#include <sys/types.h>
#include <sys/socket.h>],
[struct sockaddr sa; sa.sa_len = 0; return (0);],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_SIN_LEN, 1, Define to 1 if sockaddr_in has a sin_len member)],
AC_DEFINE(HAVE_SA_LEN, 1, [Define to 1 if sockaddr has a sa_len member, and corresponding sin_len and sun_len])],
AC_MSG_RESULT(no))
AC_ARG_WITH(lcov,
......@@ -310,6 +312,13 @@ AC_SUBST(GTEST_LDADD)
PKG_CHECK_MODULES(SQLITE, sqlite3 >= 3.3.9, enable_features="$enable_features SQLite3")
# I can't get some of the #include <asio.hpp> right without this
# TODO: find the real cause of asio/boost wanting pthreads
# (this currently only occurs for src/lib/cc/session_unittests)
PTHREAD_LDFLAGS=
AC_CHECK_LIB(pthread, pthread_create,[ PTHREAD_LDFLAGS=-lpthread ], [])
AC_SUBST(PTHREAD_LDFLAGS)
#
# ASIO: we extensively use it as the C++ event management module.
#
......@@ -450,7 +459,9 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/lib/python/isc/config/tests/config_test
src/lib/python/isc/cc/tests/cc_test
src/lib/dns/gen-rdatacode.py
src/lib/python/isc/cc/session.py
src/lib/dns/tests/testdata/gen-wiredata.py
src/lib/cc/session_config.h.pre
], [
chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
chmod +x src/bin/xfrin/run_b10-xfrin.sh
......
......@@ -158,24 +158,23 @@ class ProcessInfo:
class BoB:
"""Boss of BIND class."""
def __init__(self, c_channel_port=9912, auth_port=5300, verbose=False):
def __init__(self, msgq_socket_file=None, auth_port=5300, verbose=False):
"""Initialize the Boss of BIND. This is a singleton (only one
can run).
The c_channel_port specifies the TCP/IP port that the msgq
process listens on. If verbose is True, then the boss reports
what it is doing.
The msgq_socket_file specifies the UNIX domain socket file
that the msgq process listens on.
If verbose is True, then the boss reports what it is doing.
"""
self.verbose = verbose
self.c_channel_port = c_channel_port
self.msgq_socket_file = msgq_socket_file
self.auth_port = auth_port
self.cc_session = None
self.ccs = None
self.processes = {}
self.dead_processes = {}
self.runnable = False
os.environ['ISC_MSGQ_PORT'] = str(self.c_channel_port)
def config_handler(self, new_config):
if self.verbose:
......@@ -220,20 +219,22 @@ class BoB:
"""
# try to connect to the c-channel daemon,
# to see if it is already running
c_channel_env = { "ISC_MSGQ_PORT": str(self.c_channel_port), }
c_channel_env = {}
if self.msgq_socket_file is not None:
c_channel_env["BIND10_MSGQ_SOCKET_FILE"] = self.msgq_socket_file
if self.verbose:
sys.stdout.write("Checking for already running b10-msgq\n")
# try to connect, and if we can't wait a short while
try:
self.cc_session = isc.cc.Session(self.c_channel_port)
return "b10-msgq already running, cannot start"
self.cc_session = isc.cc.Session(self.msgq_socket_file)
return "b10-msgq already running, or socket file not cleaned , cannot start"
except isc.cc.session.SessionError:
pass
# start the c-channel daemon
if self.verbose:
sys.stdout.write("Starting b10-msgq using port %d\n" %
self.c_channel_port)
if self.msgq_socket_file:
sys.stdout.write("Starting b10-msgq\n")
try:
c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
True, not self.verbose)
......@@ -252,7 +253,7 @@ class BoB:
return "Unable to connect to c-channel after 5 seconds"
# try to connect, and if we can't wait a short while
try:
self.cc_session = isc.cc.Session(self.c_channel_port)
self.cc_session = isc.cc.Session(self.msgq_socket_file)
except isc.cc.session.SessionError:
time.sleep(0.1)
#self.cc_session.group_subscribe("Boss", "boss")
......@@ -262,7 +263,7 @@ class BoB:
sys.stdout.write("[bind10] Starting b10-cfgmgr\n")
try:
bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
{ 'ISC_MSGQ_PORT': str(self.c_channel_port)})
c_channel_env)
except Exception as e:
c_channel.process.kill()
return "Unable to start b10-cfgmgr; " + str(e)
......@@ -292,7 +293,7 @@ class BoB:
xfrout_args += ['-v']
try:
xfrout = ProcessInfo("b10-xfrout", xfrout_args,
{ 'ISC_MSGQ_PORT': str(self.c_channel_port)})
c_channel_env )
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
......@@ -310,7 +311,7 @@ class BoB:
authargs += ['-v']
try:
auth = ProcessInfo("b10-auth", authargs,
{ 'ISC_MSGQ_PORT': str(self.c_channel_port)})
c_channel_env)
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
......@@ -327,7 +328,7 @@ class BoB:
xfrin_args += ['-v']
try:
xfrind = ProcessInfo("b10-xfrin", xfrin_args,
{ 'ISC_MSGQ_PORT': str(self.c_channel_port)})
c_channel_env)
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
......@@ -346,7 +347,7 @@ class BoB:
cmdctl_args += ['-v']
try:
cmd_ctrld = ProcessInfo("b10-cmdctl", cmdctl_args,
{ 'ISC_MSGQ_PORT': str(self.c_channel_port)})
c_channel_env)
except Exception as e:
c_channel.process.kill()
bind_cfgd.process.kill()
......@@ -588,9 +589,9 @@ def main():
parser.add_option("-p", "--port", dest="auth_port", type="string",
action="callback", callback=check_port, default="5300",
help="port the b10-auth daemon will use (default 5300)")
parser.add_option("-m", "--msgq-port", dest="msgq_port", type="string",
action="callback", callback=check_port, default="9912",
help="port the b10-msgq daemon will use (default 9912)")
parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
type="string", default=None,
help="UNIX domain socket file the b10-msgq daemon will use")
(options, args) = parser.parse_args()
# Announce startup.
......@@ -613,7 +614,7 @@ def main():
signal.signal(signal.SIGTERM, fatal_signal)
# Go bob!
boss_of_bind = BoB(int(options.msgq_port), int(options.auth_port),
boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
options.verbose)
startup_result = boss_of_bind.startup()
if startup_result:
......
......@@ -29,6 +29,9 @@ export PYTHONPATH
B10_FROM_SOURCE=@abs_top_srcdir@
export B10_FROM_SOURCE
BIND10_MSGQ_SOCKET_FILE=@abs_top_srcdir@/msgq_socket
export BIND10_MSGQ_SOCKET_FILE
cd ${BIND10_PATH}
exec ${PYTHON_EXEC} -O bind10 $*
......@@ -75,16 +75,16 @@ class TestBoB(unittest.TestCase):
def test_init(self):
bob = BoB()
self.assertEqual(bob.verbose, False)
self.assertEqual(bob.c_channel_port, 9912)
self.assertEqual(bob.msgq_socket_file, None)
self.assertEqual(bob.cc_session, None)
self.assertEqual(bob.processes, {})
self.assertEqual(bob.dead_processes, {})
self.assertEqual(bob.runnable, False)
def test_init_alternate_port(self):
bob = BoB(2199)
def test_init_alternate_socket(self):
bob = BoB("alt_socket_file")
self.assertEqual(bob.verbose, False)
self.assertEqual(bob.c_channel_port, 2199)
self.assertEqual(bob.msgq_socket_file, "alt_socket_file")
self.assertEqual(bob.cc_session, None)
self.assertEqual(bob.processes, {})
self.assertEqual(bob.dead_processes, {})
......
......@@ -86,25 +86,33 @@ class SubscriptionManager:
class MsgQ:
"""Message Queue class."""
def __init__(self, port=0, verbose=False):
# did we find a better way to do this?
SOCKET_FILE = os.path.join("@localstatedir@",
"@PACKAGE_NAME@",
"msgq_socket").replace("${prefix}",
"@prefix@")
def __init__(self, socket_file=None, verbose=False):
"""Initialize the MsgQ master.
The port specifies the TCP/IP port that the msgq
process listens on. If verbose is True, then the MsgQ reports
The socket_file specifies the path to the UNIX domain socket
that the msgq process listens on. If it is None, the
environment variable BIND10_MSGQ_SOCKET_FILE is used. If that
is not set, it will default to
@localstatedir@/@PACKAGE_NAME@/msg_socket.
If verbose is True, then the MsgQ reports
what it is doing.
"""
if port == 0:
if 'ISC_MSGQ_PORT' in os.environ:
port = int(os.environ["ISC_MSGQ_PORT"])
else:
port = 9912
print(port)
if socket_file is None:
if "BIND10_MSGQ_SOCKET_FILE" in os.environ:
self.socket_file = os.environ["BIND10_MSGQ_SOCKET_FILE"]
else:
self.socket_file = self.SOCKET_FILE
else:
self.socket_file = socket_file
self.verbose = verbose
self.c_channel_port = port
self.poller = None
self.kqueue = None
self.runnable = False
......@@ -131,10 +139,21 @@ class MsgQ:
def setup_listener(self):
"""Set up the listener socket. Internal function."""
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.listen_socket.bind(("127.0.0.1", self.c_channel_port))
self.listen_socket.listen(1024)
self.listen_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
if os.path.exists(self.socket_file):
os.remove(self.socket_file)
try:
print("[XX] SOCKET FILE: " + self.socket_file)
self.listen_socket.bind(self.socket_file)
print("[XX] LISTENING ON SOCKET FILE: " + self.socket_file)
self.listen_socket.listen(1024)
except Exception as e:
# remove the file again if something goes wrong
# (note this is a catch-all, but we reraise it)
if os.path.exists(self.socket_file):
os.remove(self.socket_file)
raise e
if self.poller:
self.poller.register(self.listen_socket, select.POLLIN)
......@@ -142,7 +161,10 @@ class MsgQ:
self.add_kqueue_socket(self.listen_socket)
def setup(self):
"""Configure listener socket, polling, etc."""
"""Configure listener socket, polling, etc.
Raises a socket.error if the socket_file cannot be
created.
"""
self.setup_poller()
self.setup_listener()
......@@ -366,6 +388,8 @@ class MsgQ:
if self.verbose:
sys.stdout.write("Stopping the server.\n")
self.listen_socket.close()
if os.path.exists(self.socket_file):
os.remove(self.socket_file)
# can signal handling and calling a destructor be done without a
# global variable?
......@@ -389,9 +413,9 @@ if __name__ == "__main__":
parser = OptionParser(version=__version__)
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("-m", "--msgq-port", dest="msgq_port", type="string",
action="callback", callback=check_port, default="0",
help="port the msgq daemon will use")
parser.add_option("-s", "--socket-file", dest="msgq_socket_file",
type="string", default=None,
help="UNIX domain socket file the msgq daemon will use")
(options, args) = parser.parse_args()
signal.signal(signal.SIGTERM, signal_handler)
......@@ -400,7 +424,7 @@ if __name__ == "__main__":
if options.verbose:
sys.stdout.write("MsgQ %s\n" % __version__)
msgq = MsgQ(int(options.msgq_port), options.verbose)
msgq = MsgQ(options.msgq_socket_file, options.verbose)
setup_result = msgq.setup()
if setup_result:
......
from msgq import SubscriptionManager, MsgQ
import unittest
import os
import socket
#
# Currently only the subscription part is implemented... I'd have to mock
......@@ -58,5 +60,46 @@ class TestSubscriptionManager(unittest.TestCase):
self.sm.subscribe('g1', '*', 's2')
self.assertEqual(self.sm.find_sub("g1", "i1"), [ 's1' ])
def test_open_socket_parameter(self):
self.assertFalse(os.path.exists("./my_socket_file"))
msgq = MsgQ("./my_socket_file");
msgq.setup()
self.assertTrue(os.path.exists("./my_socket_file"))
msgq.shutdown();
self.assertFalse(os.path.exists("./my_socket_file"))
def test_open_socket_environment_variable(self):
self.assertFalse(os.path.exists("my_socket_file"))
os.environ["BIND10_MSGQ_SOCKET_FILE"] = "./my_socket_file"
msgq = MsgQ();
msgq.setup()
self.assertTrue(os.path.exists("./my_socket_file"))
msgq.shutdown();
self.assertFalse(os.path.exists("./my_socket_file"))
def test_open_socket_default(self):
env_var = None
if "BIND10_MSGQ_SOCKET_FILE" in os.environ:
env_var = os.environ["BIND10_MSGQ_SOCKET_FILE"]
del os.environ["BIND10_MSGQ_SOCKET_FILE"]
socket_file = MsgQ.SOCKET_FILE
self.assertFalse(os.path.exists(socket_file))
msgq = MsgQ();
try:
msgq.setup()
self.assertTrue(os.path.exists(socket_file))
msgq.shutdown();
self.assertFalse(os.path.exists(socket_file))
except socket.error:
# ok, the install path doesn't exist at all,
# so we can't check any further
pass
if env_var is not None:
os.environ["BIND10_MSGQ_SOCKET_FILE"] = env_var
def test_open_socket_bad(self):
msgq = MsgQ("/does/not/exist")
self.assertRaises(socket.error, msgq.setup)
if __name__ == '__main__':
unittest.main()
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/dns -I$(top_builddir)/src/lib/dns
AM_CPPFLAGS += -I$(top_srcdir)/ext
AM_CXXFLAGS = $(B10_CXXFLAGS)
# ASIO header files used in session.cc will trigger "unused-parameter"
......@@ -12,15 +14,24 @@ libcc_a_SOURCES = data.cc data.h session.cc session.h
CLEANFILES = *.gcno *.gcda
session_config.h: session_config.h.pre
$(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" session_config.h.pre >$@
BUILT_SOURCES = session_config.h
TESTS =
if HAVE_GTEST
TESTS += run_unittests
run_unittests_SOURCES = data_unittests.cc run_unittests.cc
# (TODO: these need to be completed and moved to tests/)
run_unittests_SOURCES = data_unittests.cc session_unittests.cc run_unittests.cc
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
# TODO: remove PTHREAD_LDFLAGS (and from configure too)
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) $(PTHREAD_LDFLAGS)
run_unittests_LDADD = libcc.a $(GTEST_LDADD)
run_unittests_LDADD += $(top_builddir)/src/lib/dns/.libs/libdns.a
run_unittests_LDADD += $(top_builddir)/src/lib/exceptions/.libs/libexceptions.a
endif
noinst_PROGRAMS = $(TESTS)
......@@ -15,6 +15,7 @@
// $Id$
#include <config.h>
#include "session_config.h"
#include <stdint.h>
......@@ -23,6 +24,8 @@
#include <iostream>
#include <sstream>
#include <sys/un.h>
#include <boost/bind.hpp>
#include <boost/function.hpp>
......@@ -40,7 +43,7 @@ using namespace isc::cc;
using namespace isc::data;
// some of the asio names conflict with socket API system calls
// (e.g. write(2)) so we don't import the entire boost::asio namespace.
// (e.g. write(2)) so we don't import the entire asio namespace.
using asio::io_service;
using asio::ip::tcp;
......@@ -55,7 +58,7 @@ class SessionImpl {
public:
SessionImpl() : sequence_(-1) { queue_ = Element::createFromString("[]"); }
virtual ~SessionImpl() {}
virtual void establish() = 0;
virtual void establish(const char& socket_file) = 0;
virtual int getSocket() = 0;
virtual void disconnect() = 0;
virtual void writeData(const void* data, size_t datalen) = 0;
......@@ -73,7 +76,7 @@ public:
ASIOSession(io_service& io_service) :
io_service_(io_service), socket_(io_service_), data_length_(0)
{}
virtual void establish();
virtual void establish(const char& socket_file);
virtual void disconnect();
virtual int getSocket() { return (socket_.native()); }
virtual void writeData(const void* data, size_t datalen);
......@@ -86,18 +89,23 @@ private:
private:
io_service& io_service_;
tcp::socket socket_;
asio::local::stream_protocol::socket socket_;
uint32_t data_length_;
boost::function<void()> user_handler_;
asio::error_code error_;
};
void
ASIOSession::establish() {
socket_.connect(tcp::endpoint(asio::ip::address_v4::loopback(),
9912), error_);
ASIOSession::establish(const char& socket_file) {
try {
socket_.connect(asio::local::stream_protocol::endpoint(&socket_file), error_);
} catch (asio::system_error& se) {
isc_throw(SessionError, se.what());
}
if (error_) {
isc_throw(SessionError, "Unable to connect to message queue");
isc_throw(SessionError, "Unable to connect to message queue.");
}
}
......@@ -175,7 +183,7 @@ public:
SocketSession() : sock_(-1) {}
virtual ~SocketSession() { disconnect(); }
virtual int getSocket() { return (sock_); }
void establish();
void establish(const char& socket_file);
virtual void disconnect()
{
if (sock_ >= 0) {
......@@ -210,29 +218,25 @@ public:
}
void
SocketSession::establish() {
int s;
struct sockaddr_in sin;
SocketSession::establish(const char& socket_file) {
struct sockaddr_un sun;
#ifdef HAVE_SA_LEN
sun.sun_len = sizeof(struct sockaddr_un);
#endif
if (strlen(&socket_file) >= sizeof(sun.sun_path)) {
isc_throw(SessionError, "Unable to connect to message queue; "
"socket file path too long: " << socket_file);
}
sun.sun_family = AF_UNIX;
strncpy(sun.sun_path, &socket_file, sizeof(sun.sun_path) - 1);
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
isc_throw(SessionError, "socket() failed");
}
int port = atoi(getenv("ISC_MSGQ_PORT"));
if (port == 0) {
port = 9912;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = INADDR_ANY;
#ifdef HAVE_SIN_LEN
sin.sin_len = sizeof(struct sockaddr_in);
#endif
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) < 0) {
close(s);
isc_throw(SessionError, "Unable to connect to message queue");
}
......@@ -291,8 +295,15 @@ Session::startRead(boost::function<void()> read_callback) {
}
void
Session::establish() {
impl_->establish();
Session::establish(const char* socket_file) {
if (socket_file == NULL) {
socket_file = getenv("BIND10_MSGQ_SOCKET_FILE");
}
if (socket_file == NULL) {
socket_file = BIND10_MSGQ_SOCKET_FILE;
}
impl_->establish(*socket_file);
// once established, encapsulate the implementation object so that we
// can safely release the internal resource when exception happens
......
......@@ -24,6 +24,7 @@
#include <exceptions/exceptions.h>
#include "data.h"
#include "session_config.h"
namespace asio {
class io_service;
......@@ -57,7 +58,7 @@ namespace isc {
void startRead(boost::function<void()> read_callback);
void establish();
void establish(const char* socket_file = NULL);
void disconnect();
void sendmsg(isc::data::ElementPtr& msg);
void sendmsg(isc::data::ElementPtr& env,
......
#define BIND10_MSGQ_SOCKET_FILE "@@LOCALSTATEDIR@@/@PACKAGE@/msgq_socket"
// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
// $Id: data_unittests.cc 1899 2010-05-21 12:03:59Z jelte $
#include "config.h"
#include <gtest/gtest.h>
#include <session.h>
#include <asio.hpp>
#include <exceptions/exceptions.h>
using namespace isc::cc;
TEST(AsioSession, establish) {
asio::io_service io_service_;
Session sess(io_service_);
EXPECT_THROW(
sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
), isc::cc::SessionError
);
}
TEST(Session, establish) {
Session sess;
EXPECT_THROW(
sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
"/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
), isc::cc::SessionError
);
}
......@@ -157,7 +157,7 @@ Session::startRead(boost::function<void()> read_callback UNUSED_PARAM) {
}
void