Commit 0c730e63 authored by Stephen Morris's avatar Stephen Morris
Browse files

Merge branch 'master' into trac641

parents 788d9505 e909ff99
206. [func] shane
Add the ability to list the running BIND 10 processes using the
command channel. To try this, use "Boss show_processes".
(Trac #648, git 451bbb67c2b5d544db2f7deca4315165245d2b3b)
205. [bug] jinmei
b10-auth, src/lib/datasrc: fixed a bug where b10-auth could return
an empty additional section for delegation even if some glue is
crucial when it fails to find some other glue records in its data
source.
(Trac #646, git 6070acd1c5b2f7a61574eda4035b93b40aab3e2b)
204. [bug] jinmei
b10-auth, src/lib/datasrc: class ANY queries were not handled
correctly in the generic data source (mainly for sqlite3). It
could crash b10-auth in the worst case, and could result in
incorrect responses in some other cases.
(Trac #80, git c65637dd41c8d94399bd3e3cee965b694b633339)
203. [bug] zhang likun
Fix resolver cache memory leak: when cache is destructed, rrset
and message entries in it are not destructed properly.
(Trac #643, git aba4c4067da0dc63c97c6356dc3137651755ffce)
202. [func] vorner
It is possible to specify a different directory where we look for
configuration files (by -p) and different configuration file to
use (-c). Also, it is possible to specify the port on which
cmdctl should listen (--cmdctl-port).
(Trac #615, git 5514dd78f2d61a222f3069fc94723ca33fb3200b)
201. [bug] jerry
src/bin/bindctl: bindctl doesn't show traceback on shutdown.
(Trac #588, git 662e99ef050d98e86614c4443326568a0b5be437)
200. [bug] Jelte
Fixed a bug where incoming TCP connections were not closed.
(Trac #589, git 1d88daaa24e8b1ab27f28be876f40a144241e93b)
199. [func] ocean
Cache negative responses (NXDOMAIN/NODATA) from authoritative
server for recursive resolver.
(Trac #493, git f8fb852bc6aef292555063590c361f01cf29e5ca)
198. [bug] jinmei
b10-auth, src/lib/datasrc: fixed a bug where hot spot cache failed
to reuse cached SOA for negative responses. Due to this bug
......@@ -241,7 +285,7 @@ bind10-devel-20110224 released on February 24, 2011
timeout_client for sending an answer back to the client
timeout_lookup for stopping the resolving
(currently 2 and 3 have the same final effect)
(Trac 489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
(Trac #489, git 578ea7f4ba94dc0d8a3d39231dad2be118e125a2)
159. [func] smann
The resolver now has a configurable set of root servers to start
......
......@@ -663,6 +663,7 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/net/tests/Makefile
src/lib/python/isc/notify/Makefile
src/lib/python/isc/notify/tests/Makefile
src/lib/python/isc/testutils/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
......@@ -719,9 +720,8 @@ AC_OUTPUT([doc/version.ent
src/bin/stats/run_b10-stats_stub.sh
src/bin/stats/tests/stats_test
src/bin/bind10/bind10.py
src/bin/bind10/tests/bind10_test
src/bin/bind10/tests/bind10_test.py
src/bin/bind10/run_bind10.sh
src/bin/bind10/tests/bind10_test.py
src/bin/bindctl/run_bindctl.sh
src/bin/bindctl/bindctl_main.py
src/bin/bindctl/tests/bindctl_test
......
......@@ -1199,10 +1199,9 @@ TODO
<title>Incoming Zone Transfers</title>
<para>
The <command>b10-xfrin</command> process is started by
<command>bind10</command>.
It can be manually triggered to request an AXFR zone
transfer. When received, it is stored in the BIND 10
Incoming zones are transferred using the <command>b10-xfrin</command>
process which is started by <command>bind10</command>.
When received, the zone is stored in the BIND 10
data store, and its records can be served by
<command>b10-auth</command>.
In combination with <command>b10-zonemgr</command> (for
......@@ -1213,8 +1212,22 @@ TODO
<note><simpara>
The current development release of BIND 10 only supports
AXFR. (IXFR is not supported.)
<!-- TODO: sqlite3 data source only? -->
</simpara></note>
<!-- TODO:
how to tell bind10 you are a secondary?
when will it first attempt to check for new zone? (using REFRESH?)
what if zonemgr is not running?
what if a NOTIFY is sent?
-->
<para>
To manually trigger a zone transfer to retrieve a remote zone,
you may use the <command>bindctl</command> utility.
......@@ -1223,6 +1236,9 @@ TODO
<screen>&gt; <userinput>Xfrin retransfer zone_name="<option>foo.example.org</option>" master=<option>192.0.2.99</option></userinput></screen>
</para>
<!-- TODO: can that retransfer be used to identify a new zone? -->
<!-- TODO: what if doesn't exist at that master IP? -->
</chapter>
<chapter id="xfrout">
......@@ -1329,28 +1345,34 @@ what is XfroutClient xfr_client??
<!-- TODO: later the above will have some defaults -->
<para>
To enable forwarding, the upstream address and port must be
configured to forward queries to, such as:
<section>
<title>Forwarding</title>
<screen>
<para>
To enable forwarding, the upstream address and port must be
configured to forward queries to, such as:
<screen>
&gt; <userinput>config set Resolver/forward_addresses [{ "address": "<replaceable>192.168.1.1</replaceable>", "port": 53 }]</userinput>
&gt; <userinput>config commit</userinput>
</screen>
(Replace <replaceable>192.168.1.1</replaceable> to point to your
full resolver.)
</para>
(Replace <replaceable>192.168.1.1</replaceable> to point to your
full resolver.)
</para>
<para>
Normal iterative name service can be re-enabled by clearing the
forwarding address(es); for example:
<para>
Normal iterative name service can be re-enabled by clearing the
forwarding address(es); for example:
<screen>
<screen>
&gt; <userinput>config set Resolver/forward_addresses []</userinput>
&gt; <userinput>config commit</userinput>
</screen>
</para>
</para>
</section>
<!-- TODO: later try this
......
......@@ -163,10 +163,6 @@ main(int argc, char* argv[]) {
my_command_handler);
cout << "[b10-auth] Configuration channel established." << endl;
if (uid != NULL) {
changeUser(uid);
}
xfrin_session = new Session(io_service.get_io_service());
cout << "[b10-auth] Xfrin session channel created." << endl;
xfrin_session->establish(NULL);
......@@ -190,6 +186,10 @@ main(int argc, char* argv[]) {
configureAuthServer(*auth_server, config_session->getFullConfig());
auth_server->updateConfig(ElementPtr());
if (uid != NULL) {
changeUser(uid);
}
cout << "[b10-auth] Server started." << endl;
io_service.run();
......
#!@PYTHON@
# Copyright (C) 2010 Internet Systems Consortium.
# Copyright (C) 2010,2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
......@@ -139,7 +139,8 @@ class ProcessInfo:
self.restart_schedule = RestartSchedule()
self.uid = uid
self.username = username
self._spawn()
self.process = None
self.pid = None
def _preexec_work(self):
"""Function used before running a program that needs to run as a
......@@ -186,6 +187,11 @@ class ProcessInfo:
self.pid = self.process.pid
self.restart_schedule.set_run_start_time()
# spawn() and respawn() are the same for now, but in the future they
# may have different functionality
def spawn(self):
self._spawn()
def respawn(self):
self._spawn()
......@@ -194,14 +200,21 @@ class CChannelConnectError(Exception): pass
class BoB:
"""Boss of BIND class."""
def __init__(self, msgq_socket_file=None, nocache=False, verbose=False,
setuid=None, username=None):
def __init__(self, msgq_socket_file=None, data_path=None,
config_filename=None, nocache=False, verbose=False, setuid=None,
username=None, cmdctl_port=None):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
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.
Data path and config filename are passed trough to config manager
(if provided) and specify the config file to be used.
The cmdctl_port is passed to cmdctl and specify on which port it
should listen.
"""
self.cc_session = None
self.ccs = None
......@@ -219,6 +232,9 @@ class BoB:
self.uid = setuid
self.username = username
self.verbose = verbose
self.data_path = data_path
self.config_filename = config_filename
self.cmdctl_port = cmdctl_port
def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
......@@ -270,6 +286,14 @@ class BoB:
answer = isc.config.ccsession.create_answer(0)
return answer
def get_processes(self):
pids = list(self.processes.keys())
pids.sort()
process_list = [ ]
for pid in pids:
process_list.append([pid, self.processes[pid].name])
return process_list
def command_handler(self, command, args):
if self.verbose:
sys.stdout.write("[bind10] Boss got command: " + command + "\n")
......@@ -280,8 +304,13 @@ class BoB:
if command == "shutdown":
self.runnable = False
answer = isc.config.ccsession.create_answer(0)
elif command == "ping":
answer = isc.config.ccsession.create_answer(0, "pong")
elif command == "show_processes":
answer = isc.config.ccsession. \
create_answer(0, self.get_processes())
else:
answer = isc.config.ccsession.create_answer(1,
answer = isc.config.ccsession.create_answer(1,
"Unknown command")
return answer
......@@ -369,6 +398,7 @@ class BoB:
c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
True, not self.verbose, uid=self.uid,
username=self.username)
c_channel.spawn()
self.processes[c_channel.pid] = c_channel
self.log_started(c_channel.pid)
......@@ -390,9 +420,15 @@ class BoB:
Starts the configuration manager process
"""
self.log_starting("b10-cfgmgr")
bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
args = ["b10-cfgmgr"]
if self.data_path is not None:
args.append("--data-path=" + self.data_path)
if self.config_filename is not None:
args.append("--config-filename=" + self.config_filename)
bind_cfgd = ProcessInfo("b10-cfgmgr", args,
c_channel_env, uid=self.uid,
username=self.username)
bind_cfgd.spawn()
self.processes[bind_cfgd.pid] = bind_cfgd
self.log_started(bind_cfgd.pid)
......@@ -427,6 +463,7 @@ class BoB:
"""
self.log_starting(name, port, address)
newproc = ProcessInfo(name, args, c_channel_env)
newproc.spawn()
self.processes[newproc.pid] = newproc
self.log_started(newproc.pid)
......@@ -500,8 +537,13 @@ class BoB:
self.start_simple("b10-stats", c_channel_env)
def start_cmdctl(self, c_channel_env):
# XXX: we hardcode port 8080
self.start_simple("b10-cmdctl", c_channel_env, 8080)
"""
Starts the command control process
"""
args = ["b10-cmdctl"]
if self.cmdctl_port is not None:
args.append("--port=" + str(self.cmdctl_port))
self.start_process("b10-cmdctl", args, c_channel_env, self.cmdctl_port)
def start_all_processes(self):
"""
......@@ -785,6 +827,50 @@ def process_rename(option, opt_str, value, parser):
"""Function that renames the process if it is requested by a option."""
isc.util.process.rename(value)
def parse_args(args=sys.argv[1:], Parser=OptionParser):
"""
Function for parsing command line arguments. Returns the
options object from OptionParser.
"""
parser = Parser(version=VERSION)
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")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
default=False, help="disable hot-spot cache in authoritative DNS server")
parser.add_option("-u", "--user", dest="user", type="string", default=None,
help="Change user after startup (must run as root)")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("--pretty-name", type="string", action="callback",
callback=process_rename,
help="Set the process name (displayed in ps, top, ...)")
parser.add_option("-c", "--config-file", action="store",
dest="config_file", default=None,
help="Configuration database filename")
parser.add_option("-p", "--data-path", dest="data_path",
help="Directory to search for configuration files",
default=None)
parser.add_option("--cmdctl-port", dest="cmdctl_port", type="int",
default=None, help="Port of command control")
parser.add_option("--pid-file", dest="pid_file", type="string",
default=None,
help="file to dump the PID of the BIND 10 process")
(options, args) = parser.parse_args(args)
if options.cmdctl_port is not None:
try:
isc.net.parse.port_parse(options.cmdctl_port)
except ValueError as e:
parser.error(e)
if args:
parser.print_help()
sys.exit(1)
return options
def dump_pid(pid_file):
"""
Dump the PID of the current process to the specified file. If the given
......@@ -814,33 +900,14 @@ def unlink_pid_file(pid_file):
if error.errno is not errno.ENOENT:
raise
def main():
global options
global boss_of_bind
# Enforce line buffering on stdout, even when not a TTY
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
# Parse any command-line options.
parser = OptionParser(version=VERSION)
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")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
default=False, help="disable hot-spot cache in authoritative DNS server")
parser.add_option("-u", "--user", dest="user", type="string", default=None,
help="Change user after startup (must run as root)")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("--pretty-name", type="string", action="callback",
callback=process_rename,
help="Set the process name (displayed in ps, top, ...)")
parser.add_option("--pid-file", dest="pid_file", type="string",
default=None,
help="file to dump the PID of the BIND 10 process")
(options, args) = parser.parse_args()
if args:
parser.print_help()
sys.exit(1)
options = parse_args()
# Check user ID.
setuid = None
......@@ -890,8 +957,9 @@ def main():
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
# Go bob!
boss_of_bind = BoB(options.msgq_socket_file, options.nocache,
options.verbose, setuid, username)
boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
options.config_file, options.nocache, options.verbose,
setuid, username, options.cmdctl_port)
startup_result = boss_of_bind.startup()
if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
......
......@@ -48,6 +48,8 @@
<arg><option>-n</option></arg>
<arg><option>-u <replaceable>user</replaceable></option></arg>
<arg><option>-v</option></arg>
<arg><option>-c<replaceable>config-filename</replaceable></option></arg>
<arg><option>-p<replaceable>data_path</replaceable></option></arg>
<arg><option>--msgq-socket-file <replaceable>file</replaceable></option></arg>
<arg><option>--no-cache</option></arg>
<arg><option>--user <replaceable>user</replaceable></option></arg>
......@@ -80,6 +82,31 @@
<para>The arguments are as follows:</para>
<variablelist>
<varlistentry>
<term>
<option>-c</option><replaceable>config-filename</replaceable>,
<option>--config-file</option> <replaceable>config-filename</replaceable>
</term>
<listitem>
<para>The configuration filename to use. Can be either absolute or
relative to data path. In case it is absolute, value of data path is
not considered.</para>
<para>Defaults to b10-config.db.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>-p</option><replaceable>data-path</replaceable>,
<option>--data-path</option> <replaceable>data-path</replaceable>
</term>
<listitem>
<para>The path where BIND 10 programs look for various data files.
Currently only b10-cfgmgr uses it to locate the configuration file,
but the usage might be extended for other programs and other types
of files.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-m</option> <replaceable>file</replaceable>,
......@@ -144,6 +171,9 @@ The default is the basename of ARG 0.
</variablelist>
</refsect1>
<!--
TODO: configuration section
-->
<!--
<refsect1>
<title>FILES</title>
......
......@@ -21,6 +21,16 @@
"command_name": "shutdown",
"command_description": "Shut down BIND 10",
"command_args": []
},
{
"command_name": "ping",
"command_description": "Ping the boss process",
"command_args": []
},
{
"command_name": "show_processes",
"command_description": "List the running BIND 10 processes",
"command_args": []
}
]
}
......
......@@ -13,5 +13,6 @@ endif
for pytest in $(PYTESTS) ; do \
echo Running test: $$pytest ; \
env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/bin/bind10 \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
BIND10_MSGQ_SOCKET_FILE=$(abs_top_builddir)/msgq_socket \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
from bind10 import ProcessInfo, BoB, dump_pid, unlink_pid_file
# Copyright (C) 2011 Internet Systems Consortium.
#
# Permission to use, copy, modify, and 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 INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM 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.
from bind10 import ProcessInfo, BoB, parse_args, dump_pid, unlink_pid_file
# XXX: environment tests are currently disabled, due to the preprocessor
# setup that we have now complicating the environment
......@@ -9,6 +24,7 @@ import os
import signal
import socket
from isc.net.addr import IPAddr
from isc.testutils.parse_args import TestOptParser, OptsError
class TestProcessInfo(unittest.TestCase):
def setUp(self):
......@@ -30,6 +46,7 @@ class TestProcessInfo(unittest.TestCase):
def test_init(self):
pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
pi.spawn()
os.dup2(self.old_stdout, sys.stdout.fileno())
self.assertEqual(pi.name, 'Test Process')
self.assertEqual(pi.args, [ '/bin/echo', 'foo' ])
......@@ -50,12 +67,14 @@ class TestProcessInfo(unittest.TestCase):
def test_setting_null_stdout(self):
pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ],
dev_null_stdout=True)
pi.spawn()
os.dup2(self.old_stdout, sys.stdout.fileno())
self.assertEqual(pi.dev_null_stdout, True)
self.assertEqual(os.read(self.pipes[0], 100), b"")
def test_respawn(self):
pi = ProcessInfo('Test Process', [ '/bin/echo', 'foo' ])
pi.spawn()
# wait for old process to work...
self.assertEqual(os.read(self.pipes[0], 100), b"foo\n")
# respawn it
......@@ -104,17 +123,19 @@ class TestBoB(unittest.TestCase):
self.assertEqual(bob.cfg_start_auth, True)
self.assertEqual(bob.cfg_start_resolver, False)
# Class for testing the BoB start/stop components routines.
# Class for testing the BoB without actually starting processes.
# This is used for testing the start/stop components routines and
# the BoB commands.
#
# Although testing that external processes start is outside the scope
# Testing that external processes start is outside the scope
# of the unit test, by overriding the process start methods we can check
# that the right processes are started depending on the configuration
# options.
class StartStopCheckBob(BoB):
class MockBob(BoB):
def __init__(self):
BoB.__init__(self)
# Set flags as to which of the overridden methods has been run.
# Set flags as to which of the overridden methods has been run.
self.msgq = False
self.cfgmgr = False
self.ccsession = False
......@@ -126,6 +147,7 @@ class StartStopCheckBob(BoB):
self.stats = False
self.cmdctl = False
self.c_channel_env = {}
self.processes = { }
def read_bind10_config(self):
# Configuration options are set directly
......@@ -133,65 +155,95 @@ class StartStopCheckBob(BoB):
def start_msgq(self, c_channel_env):
self.msgq = True
self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
def start_cfgmgr(self, c_channel_env):
self.cfgmgr = True
self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
def start_ccsession(self, c_channel_env):
self.ccsession = True
self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
def start_auth(self, c_channel_env):
self.auth = True
self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
def start_resolver(self, c_channel_env):
self.resolver = True
self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
def start_xfrout(self, c_channel_env):
self.xfrout = True
self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
def start_xfrin(self, c_channel_env):
self.xfrin = True
self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
def start_zonemgr(self, c_channel_env):
self.zonemgr = True
self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
def start_stats(self, c_channel_env):
self.stats = True
self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
def start_cmdctl(self, c_channel_env):
self.cmdctl = True