|
@@ -15,7 +15,7 @@
|
|
|
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
|
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
-"""\
|
|
|
+"""
|
|
|
This file implements the Boss of Bind (BoB, or bob) program.
|
|
|
|
|
|
Its purpose is to start up the BIND 10 system, and then manage the
|
|
@@ -63,15 +63,19 @@ import pwd
|
|
|
import posix
|
|
|
|
|
|
import isc.cc
|
|
|
-import isc.utils.process
|
|
|
+import isc.util.process
|
|
|
+import isc.net.parse
|
|
|
|
|
|
# Assign this process some longer name
|
|
|
-isc.utils.process.rename(sys.argv[0])
|
|
|
+isc.util.process.rename(sys.argv[0])
|
|
|
|
|
|
# This is the version that gets displayed to the user.
|
|
|
# The VERSION string consists of the module name, the module version
|
|
|
# number, and the overall BIND 10 version number (set in configure.ac).
|
|
|
-VERSION = "bind10 20100916 (BIND 10 @PACKAGE_VERSION@)"
|
|
|
+VERSION = "bind10 20101129 (BIND 10 @PACKAGE_VERSION@)"
|
|
|
+
|
|
|
+# This is for bind10.boottime of stats module
|
|
|
+_BASETIME = time.gmtime()
|
|
|
|
|
|
class RestartSchedule:
|
|
|
"""
|
|
@@ -137,9 +141,14 @@ class ProcessInfo:
|
|
|
self.username = username
|
|
|
self._spawn()
|
|
|
|
|
|
- def _setuid(self):
|
|
|
+ def _preexec_work(self):
|
|
|
"""Function used before running a program that needs to run as a
|
|
|
different user."""
|
|
|
+ # First, put us into a separate process group so we don't get
|
|
|
+ # SIGINT signals on Ctrl-C (the boss will shut everthing down by
|
|
|
+ # other means).
|
|
|
+ os.setpgrp()
|
|
|
+ # Second, set the user ID if one has been specified
|
|
|
if self.uid is not None:
|
|
|
try:
|
|
|
posix.setuid(self.uid)
|
|
@@ -173,169 +182,240 @@ class ProcessInfo:
|
|
|
stderr=spawn_stderr,
|
|
|
close_fds=True,
|
|
|
env=spawn_env,
|
|
|
- preexec_fn=self._setuid)
|
|
|
+ preexec_fn=self._preexec_work)
|
|
|
self.pid = self.process.pid
|
|
|
self.restart_schedule.set_run_start_time()
|
|
|
|
|
|
def respawn(self):
|
|
|
self._spawn()
|
|
|
|
|
|
-class IPAddr:
|
|
|
- """Stores an IPv4 or IPv6 address."""
|
|
|
- family = None
|
|
|
- addr = None
|
|
|
-
|
|
|
- def __init__(self, addr):
|
|
|
- try:
|
|
|
- a = socket.inet_pton(socket.AF_INET, addr)
|
|
|
- self.family = socket.AF_INET
|
|
|
- self.addr = a
|
|
|
- return
|
|
|
- except:
|
|
|
- pass
|
|
|
-
|
|
|
- try:
|
|
|
- a = socket.inet_pton(socket.AF_INET6, addr)
|
|
|
- self.family = socket.AF_INET6
|
|
|
- self.addr = a
|
|
|
- return
|
|
|
- except Exception as e:
|
|
|
- raise e
|
|
|
-
|
|
|
- def __str__(self):
|
|
|
- return socket.inet_ntop(self.family, self.addr)
|
|
|
+class CChannelConnectError(Exception): pass
|
|
|
|
|
|
class BoB:
|
|
|
"""Boss of BIND class."""
|
|
|
|
|
|
- def __init__(self, msgq_socket_file=None, auth_port=5300, address='',
|
|
|
+ def __init__(self, msgq_socket_file=None, auth_port=5300, address=None,
|
|
|
nocache=False, verbose=False, setuid=None, username=None):
|
|
|
- """Initialize the Boss of BIND. This is a singleton (only one
|
|
|
- can run).
|
|
|
+ """
|
|
|
+ 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.
|
|
|
+ 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.msgq_socket_file = msgq_socket_file
|
|
|
+ self.address = address
|
|
|
self.auth_port = auth_port
|
|
|
- self.address = None
|
|
|
- if address:
|
|
|
- self.address = IPAddr(address)
|
|
|
self.cc_session = None
|
|
|
self.ccs = None
|
|
|
- self.processes = {}
|
|
|
+ self.cfg_start_auth = True
|
|
|
+ self.cfg_start_recurse = False
|
|
|
+ self.curproc = None
|
|
|
self.dead_processes = {}
|
|
|
+ self.msgq_socket_file = msgq_socket_file
|
|
|
+ self.nocache = nocache
|
|
|
+ self.processes = {}
|
|
|
self.runnable = False
|
|
|
self.uid = setuid
|
|
|
self.username = username
|
|
|
- self.nocache = nocache
|
|
|
+ self.verbose = verbose
|
|
|
|
|
|
def config_handler(self, new_config):
|
|
|
if self.verbose:
|
|
|
- sys.stdout.write("[bind10] handling new config:\n")
|
|
|
- sys.stdout.write(new_config + "\n")
|
|
|
+ sys.stdout.write("[bind10] Handling new configuration: " +
|
|
|
+ str(new_config) + "\n")
|
|
|
answer = isc.config.ccsession.create_answer(0)
|
|
|
return answer
|
|
|
# TODO
|
|
|
|
|
|
def command_handler(self, command, args):
|
|
|
if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Boss got command:\n")
|
|
|
- sys.stdout.write(command + "\n")
|
|
|
+ sys.stdout.write("[bind10] Boss got command: " + command + "\n")
|
|
|
answer = isc.config.ccsession.create_answer(1, "command not implemented")
|
|
|
if type(command) != str:
|
|
|
answer = isc.config.ccsession.create_answer(1, "bad command")
|
|
|
else:
|
|
|
- cmd = command
|
|
|
- if cmd == "shutdown":
|
|
|
- sys.stdout.write("[bind10] got shutdown command\n")
|
|
|
+ if command == "shutdown":
|
|
|
self.runnable = False
|
|
|
answer = isc.config.ccsession.create_answer(0)
|
|
|
else:
|
|
|
answer = isc.config.ccsession.create_answer(1,
|
|
|
"Unknown command")
|
|
|
return answer
|
|
|
-
|
|
|
- def startup(self):
|
|
|
- """Start the BoB instance.
|
|
|
-
|
|
|
- Returns None if successful, otherwise an string describing the
|
|
|
- problem.
|
|
|
+
|
|
|
+ def kill_started_processes(self):
|
|
|
+ """
|
|
|
+ Called as part of the exception handling when a process fails to
|
|
|
+ start, this runs through the list of started processes, killing
|
|
|
+ each one. It then clears that list.
|
|
|
"""
|
|
|
- # try to connect to the c-channel daemon,
|
|
|
- # to see if it is already running
|
|
|
- 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("[bind10] 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.msgq_socket_file)
|
|
|
- return "b10-msgq already running, or socket file not cleaned , cannot start"
|
|
|
- except isc.cc.session.SessionError:
|
|
|
- # this is the case we want, where the msgq is not running
|
|
|
- pass
|
|
|
+ sys.stdout.write("[bind10] killing started processes:\n")
|
|
|
+
|
|
|
+ for pid in self.processes:
|
|
|
+ if self.verbose:
|
|
|
+ sys.stdout.write("[bind10] - %s\n" % self.processes[pid].name)
|
|
|
+ self.processes[pid].process.kill()
|
|
|
+ self.processes = {}
|
|
|
+
|
|
|
+ def read_bind10_config(self):
|
|
|
+ """
|
|
|
+ Reads the parameters associated with the BoB module itself.
|
|
|
|
|
|
- # start the c-channel daemon
|
|
|
+ At present these are the components to start although arguably this
|
|
|
+ information should be in the configuration for the appropriate
|
|
|
+ module itself. (However, this would cause difficulty in the case of
|
|
|
+ xfrin/xfrout and zone manager as we don't need to start those if we
|
|
|
+ are not running the authoritative server.)
|
|
|
+ """
|
|
|
if self.verbose:
|
|
|
- if self.msgq_socket_file:
|
|
|
- sys.stdout.write("[bind10] Starting b10-msgq\n")
|
|
|
- try:
|
|
|
- c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
|
|
|
- True, not self.verbose, uid=self.uid,
|
|
|
- username=self.username)
|
|
|
- except Exception as e:
|
|
|
- return "Unable to start b10-msgq; " + str(e)
|
|
|
- self.processes[c_channel.pid] = c_channel
|
|
|
+ sys.stdout.write("[bind10] Reading Boss configuration:\n")
|
|
|
+
|
|
|
+ config_data = self.ccs.get_full_config()
|
|
|
+ self.cfg_start_auth = config_data.get("start_auth")
|
|
|
+ self.cfg_start_recurse = config_data.get("start_recurse")
|
|
|
+
|
|
|
+ if self.verbose:
|
|
|
+ sys.stdout.write("[bind10] - start_auth: %s\n" %
|
|
|
+ str(self.cfg_start_auth))
|
|
|
+ sys.stdout.write("[bind10] - start_recurse: %s\n" %
|
|
|
+ str(self.cfg_start_recurse))
|
|
|
+
|
|
|
+ def log_starting(self, process, port = None, address = None):
|
|
|
+ """
|
|
|
+ A convenience function to output a "Starting xxx" message if the
|
|
|
+ verbose option is set. Putting this into a separate method ensures
|
|
|
+ that the output form is consistent across all processes.
|
|
|
+
|
|
|
+ The process name (passed as the first argument) is put into
|
|
|
+ self.curproc, and is used to indicate which process failed to
|
|
|
+ start if there is an error (and is used in the "Started" message
|
|
|
+ on success). The optional port and address information are
|
|
|
+ appended to the message (if present).
|
|
|
+ """
|
|
|
+ self.curproc = process
|
|
|
+ if self.verbose:
|
|
|
+ sys.stdout.write("[bind10] Starting %s" % self.curproc)
|
|
|
+ if port is not None:
|
|
|
+ sys.stdout.write(" on port %d" % port)
|
|
|
+ if address is not None:
|
|
|
+ sys.stdout.write(" (address %s)" % str(address))
|
|
|
+ sys.stdout.write("\n")
|
|
|
+
|
|
|
+ def log_started(self, pid = None):
|
|
|
+ """
|
|
|
+ A convenience function to output a 'Started xxxx (PID yyyy)'
|
|
|
+ message. As with starting_message(), this ensures a consistent
|
|
|
+ format.
|
|
|
+ """
|
|
|
if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-msgq (PID %d)\n" %
|
|
|
- c_channel.pid)
|
|
|
+ sys.stdout.write("[bind10] Started %s" % self.curproc)
|
|
|
+ if pid is not None:
|
|
|
+ sys.stdout.write(" (PID %d)" % pid)
|
|
|
+ sys.stdout.write("\n")
|
|
|
+
|
|
|
+ # The next few methods start the individual processes of BIND-10. They
|
|
|
+ # are called via start_all_process(). If any fail, an exception is raised
|
|
|
+ # which is caught by the caller of start_all_processes(); this kills
|
|
|
+ # processes started up to that point before terminating the program.
|
|
|
|
|
|
- # now connect to the c-channel
|
|
|
+ def start_msgq(self, c_channel_env):
|
|
|
+ """
|
|
|
+ Start the message queue and connect to the command channel.
|
|
|
+ """
|
|
|
+ self.log_starting("b10-msgq")
|
|
|
+ c_channel = ProcessInfo("b10-msgq", ["b10-msgq"], c_channel_env,
|
|
|
+ True, not self.verbose, uid=self.uid,
|
|
|
+ username=self.username)
|
|
|
+ self.processes[c_channel.pid] = c_channel
|
|
|
+ self.log_started(c_channel.pid)
|
|
|
+
|
|
|
+ # Now connect to the c-channel
|
|
|
cc_connect_start = time.time()
|
|
|
while self.cc_session is None:
|
|
|
# if we have been trying for "a while" give up
|
|
|
if (time.time() - cc_connect_start) > 5:
|
|
|
- c_channel.process.kill()
|
|
|
- return "Unable to connect to c-channel after 5 seconds"
|
|
|
+ raise CChannelConnectError("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.msgq_socket_file)
|
|
|
except isc.cc.session.SessionError:
|
|
|
time.sleep(0.1)
|
|
|
|
|
|
- # start the configuration manager
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Starting b10-cfgmgr\n")
|
|
|
- try:
|
|
|
- bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
|
|
|
- c_channel_env, uid=self.uid,
|
|
|
- username=self.username)
|
|
|
- except Exception as e:
|
|
|
- c_channel.process.kill()
|
|
|
- return "Unable to start b10-cfgmgr; " + str(e)
|
|
|
+ def start_cfgmgr(self, c_channel_env):
|
|
|
+ """
|
|
|
+ Starts the configuration manager process
|
|
|
+ """
|
|
|
+ self.log_starting("b10-cfgmgr")
|
|
|
+ bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
|
|
|
+ c_channel_env, uid=self.uid,
|
|
|
+ username=self.username)
|
|
|
self.processes[bind_cfgd.pid] = bind_cfgd
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-cfgmgr (PID %d)\n" %
|
|
|
- bind_cfgd.pid)
|
|
|
+ self.log_started(bind_cfgd.pid)
|
|
|
|
|
|
# sleep until b10-cfgmgr is fully up and running, this is a good place
|
|
|
# to have a (short) timeout on synchronized groupsend/receive
|
|
|
# TODO: replace the sleep by a listen for ConfigManager started
|
|
|
# message
|
|
|
time.sleep(1)
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] starting ccsession\n")
|
|
|
+
|
|
|
+ def start_ccsession(self, c_channel_env):
|
|
|
+ """
|
|
|
+ Start the CC Session
|
|
|
+
|
|
|
+ The argument c_channel_env is unused but is supplied to keep the
|
|
|
+ argument list the same for all start_xxx methods.
|
|
|
+ """
|
|
|
+ self.log_starting("ccsession")
|
|
|
self.ccs = isc.config.ModuleCCSession(SPECFILE_LOCATION,
|
|
|
self.config_handler, self.command_handler)
|
|
|
self.ccs.start()
|
|
|
+ self.log_started()
|
|
|
+
|
|
|
+ # A couple of utility methods for starting processes...
|
|
|
+
|
|
|
+ def start_process(self, name, args, c_channel_env, port=None, address=None):
|
|
|
+ """
|
|
|
+ Given a set of command arguments, start the process and output
|
|
|
+ appropriate log messages. If the start is successful, the process
|
|
|
+ is added to the list of started processes.
|
|
|
+
|
|
|
+ The port and address arguments are for log messages only.
|
|
|
+ """
|
|
|
+ self.log_starting(name, port, address)
|
|
|
+ newproc = ProcessInfo(name, args, c_channel_env)
|
|
|
+ self.processes[newproc.pid] = newproc
|
|
|
+ self.log_started(newproc.pid)
|
|
|
+
|
|
|
+ def start_simple(self, name, c_channel_env, port=None, address=None):
|
|
|
+ """
|
|
|
+ Most of the BIND-10 processes are started with the command:
|
|
|
+
|
|
|
+ <process-name> [-v]
|
|
|
+
|
|
|
+ ... where -v is appended if verbose is enabled. This method
|
|
|
+ generates the arguments from the name and starts the process.
|
|
|
+
|
|
|
+ The port and address arguments are for log messages only.
|
|
|
+ """
|
|
|
+ # Set up the command arguments.
|
|
|
+ args = [name]
|
|
|
if self.verbose:
|
|
|
- sys.stdout.write("[bind10] ccsession started\n")
|
|
|
+ args += ['-v']
|
|
|
+
|
|
|
+ # ... and start the process
|
|
|
+ self.start_process(name, args, c_channel_env, port, address)
|
|
|
|
|
|
- # start b10-auth
|
|
|
+ # The next few methods start up the rest of the BIND-10 processes.
|
|
|
+ # Although many of these methods are little more than a call to
|
|
|
+ # start_simple, they are retained (a) for testing reasons and (b) as a place
|
|
|
+ # where modifications can be made if the process start-up sequence changes
|
|
|
+ # for a given process.
|
|
|
+
|
|
|
+ def start_auth(self, c_channel_env):
|
|
|
+ """
|
|
|
+ Start the Authoritative server
|
|
|
+ """
|
|
|
# XXX: this must be read from the configuration manager in the future
|
|
|
authargs = ['b10-auth', '-p', str(self.auth_port)]
|
|
|
if self.address:
|
|
@@ -346,119 +426,128 @@ class BoB:
|
|
|
authargs += ['-u', str(self.uid)]
|
|
|
if self.verbose:
|
|
|
authargs += ['-v']
|
|
|
- sys.stdout.write("Starting b10-auth using port %d" %
|
|
|
- self.auth_port)
|
|
|
- if self.address:
|
|
|
- sys.stdout.write(" on %s" % str(self.address))
|
|
|
- sys.stdout.write("\n")
|
|
|
- try:
|
|
|
- auth = ProcessInfo("b10-auth", authargs,
|
|
|
- c_channel_env)
|
|
|
- except Exception as e:
|
|
|
- c_channel.process.kill()
|
|
|
- bind_cfgd.process.kill()
|
|
|
- xfrout.process.kill()
|
|
|
- return "Unable to start b10-auth; " + str(e)
|
|
|
- self.processes[auth.pid] = auth
|
|
|
+
|
|
|
+ # ... and start
|
|
|
+ self.start_process("b10-auth", authargs, c_channel_env,
|
|
|
+ self.auth_port, self.address)
|
|
|
+
|
|
|
+ def start_recurse(self, c_channel_env):
|
|
|
+ """
|
|
|
+ Start the Resolver. At present, all these arguments and switches
|
|
|
+ are pure speculation. As with the auth daemon, they should be
|
|
|
+ read from the configuration database.
|
|
|
+ """
|
|
|
+ self.curproc = "b10-recurse"
|
|
|
+ # XXX: this must be read from the configuration manager in the future
|
|
|
+ resargs = ['b10-recurse']
|
|
|
+ if self.uid:
|
|
|
+ resargs += ['-u', str(self.uid)]
|
|
|
if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-auth (PID %d)\n" % auth.pid)
|
|
|
+ resargs += ['-v']
|
|
|
+
|
|
|
+ # ... and start
|
|
|
+ self.start_process("b10-recurse", resargs, c_channel_env)
|
|
|
+
|
|
|
+ def start_xfrout(self, c_channel_env):
|
|
|
+ self.start_simple("b10-xfrout", c_channel_env)
|
|
|
+
|
|
|
+ def start_xfrin(self, c_channel_env):
|
|
|
+ self.start_simple("b10-xfrin", c_channel_env)
|
|
|
|
|
|
- # everything after the authoritative server can run as non-root
|
|
|
+ def start_zonemgr(self, c_channel_env):
|
|
|
+ self.start_simple("b10-zonemgr", c_channel_env)
|
|
|
+
|
|
|
+ def start_stats(self, c_channel_env):
|
|
|
+ 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)
|
|
|
+
|
|
|
+ def start_all_processes(self, c_channel_env):
|
|
|
+ """
|
|
|
+ Starts up all the processes. Any exception generated during the
|
|
|
+ starting of the processes is handled by the caller.
|
|
|
+ """
|
|
|
+ self.start_msgq(c_channel_env)
|
|
|
+ self.start_cfgmgr(c_channel_env)
|
|
|
+ self.start_ccsession(c_channel_env)
|
|
|
+
|
|
|
+ # Extract the parameters associated with Bob. This can only be
|
|
|
+ # done after the CC Session is started.
|
|
|
+ self.read_bind10_config()
|
|
|
+
|
|
|
+ # Continue starting the processes. The authoritative server (if
|
|
|
+ # selected):
|
|
|
+ if self.cfg_start_auth:
|
|
|
+ self.start_auth(c_channel_env)
|
|
|
+
|
|
|
+ # ... and resolver (if selected):
|
|
|
+ if self.cfg_start_recurse:
|
|
|
+ self.start_recurse(c_channel_env)
|
|
|
+
|
|
|
+ # Everything after the main components can run as non-root.
|
|
|
+ # TODO: this is only temporary - once the privileged socket creator is
|
|
|
+ # fully working, nothing else will run as root.
|
|
|
if self.uid is not None:
|
|
|
posix.setuid(self.uid)
|
|
|
|
|
|
- # start the xfrout before auth-server, to make sure every xfr-query can
|
|
|
- # be processed properly.
|
|
|
- xfrout_args = ['b10-xfrout']
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Starting b10-xfrout\n")
|
|
|
- xfrout_args += ['-v']
|
|
|
- try:
|
|
|
- xfrout = ProcessInfo("b10-xfrout", xfrout_args,
|
|
|
- c_channel_env )
|
|
|
- except Exception as e:
|
|
|
- c_channel.process.kill()
|
|
|
- bind_cfgd.process.kill()
|
|
|
- return "Unable to start b10-xfrout; " + str(e)
|
|
|
- self.processes[xfrout.pid] = xfrout
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-xfrout (PID %d)\n" %
|
|
|
- xfrout.pid)
|
|
|
+ # xfrin/xfrout and the zone manager are only meaningful if the
|
|
|
+ # authoritative server has been started.
|
|
|
+ if self.cfg_start_auth:
|
|
|
+ self.start_xfrout(c_channel_env)
|
|
|
+ self.start_xfrin(c_channel_env)
|
|
|
+ self.start_zonemgr(c_channel_env)
|
|
|
|
|
|
- # start b10-xfrin
|
|
|
- xfrin_args = ['b10-xfrin']
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Starting b10-xfrin\n")
|
|
|
- xfrin_args += ['-v']
|
|
|
- try:
|
|
|
- xfrind = ProcessInfo("b10-xfrin", xfrin_args,
|
|
|
- c_channel_env)
|
|
|
- except Exception as e:
|
|
|
- c_channel.process.kill()
|
|
|
- bind_cfgd.process.kill()
|
|
|
- xfrout.process.kill()
|
|
|
- auth.process.kill()
|
|
|
- return "Unable to start b10-xfrin; " + str(e)
|
|
|
- self.processes[xfrind.pid] = xfrind
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-xfrin (PID %d)\n" %
|
|
|
- xfrind.pid)
|
|
|
-
|
|
|
- # start b10-zonemgr
|
|
|
- zonemgr_args = ['b10-zonemgr']
|
|
|
+ # ... and finally start the remaining processes
|
|
|
+ self.start_stats(c_channel_env)
|
|
|
+ self.start_cmdctl(c_channel_env)
|
|
|
+
|
|
|
+ def startup(self):
|
|
|
+ """
|
|
|
+ Start the BoB instance.
|
|
|
+
|
|
|
+ Returns None if successful, otherwise an string describing the
|
|
|
+ problem.
|
|
|
+ """
|
|
|
+ # Try to connect to the c-channel daemon, to see if it is already
|
|
|
+ # running
|
|
|
+ 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("[bind10] Starting b10-zonemgr\n")
|
|
|
- zonemgr_args += ['-v']
|
|
|
+ sys.stdout.write("[bind10] Checking for already running b10-msgq\n")
|
|
|
+ # try to connect, and if we can't wait a short while
|
|
|
try:
|
|
|
- zonemgr = ProcessInfo("b10-zonemgr", zonemgr_args,
|
|
|
- c_channel_env)
|
|
|
- except Exception as e:
|
|
|
- c_channel.process.kill()
|
|
|
- bind_cfgd.process.kill()
|
|
|
- xfrout.process.kill()
|
|
|
- auth.process.kill()
|
|
|
- xfrind.process.kill()
|
|
|
- return "Unable to start b10-zonemgr; " + str(e)
|
|
|
- self.processes[zonemgr.pid] = zonemgr
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-zonemgr(PID %d)\n" %
|
|
|
- zonemgr.pid)
|
|
|
+ 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:
|
|
|
+ # this is the case we want, where the msgq is not running
|
|
|
+ pass
|
|
|
|
|
|
- # start the b10-cmdctl
|
|
|
- # XXX: we hardcode port 8080
|
|
|
- cmdctl_args = ['b10-cmdctl']
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Starting b10-cmdctl on port 8080\n")
|
|
|
- cmdctl_args += ['-v']
|
|
|
+ # Start all processes. If any one fails to start, kill all started
|
|
|
+ # processes and exit with an error indication.
|
|
|
try:
|
|
|
- cmd_ctrld = ProcessInfo("b10-cmdctl", cmdctl_args,
|
|
|
- c_channel_env)
|
|
|
+ self.start_all_processes(c_channel_env)
|
|
|
except Exception as e:
|
|
|
- c_channel.process.kill()
|
|
|
- bind_cfgd.process.kill()
|
|
|
- xfrout.process.kill()
|
|
|
- auth.process.kill()
|
|
|
- xfrind.process.kill()
|
|
|
- zonemgr.process.kill()
|
|
|
- return "Unable to start b10-cmdctl; " + str(e)
|
|
|
- self.processes[cmd_ctrld.pid] = cmd_ctrld
|
|
|
- if self.verbose:
|
|
|
- sys.stdout.write("[bind10] Started b10-cmdctl (PID %d)\n" %
|
|
|
- cmd_ctrld.pid)
|
|
|
+ self.kill_started_processes()
|
|
|
+ return "Unable to start " + self.curproc + ": " + str(e)
|
|
|
|
|
|
+ # Started successfully
|
|
|
self.runnable = True
|
|
|
-
|
|
|
return None
|
|
|
|
|
|
def stop_all_processes(self):
|
|
|
"""Stop all processes."""
|
|
|
cmd = { "command": ['shutdown']}
|
|
|
- self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmdctl')
|
|
|
- self.cc_session.group_sendmsg(cmd, "Boss", "ConfigManager")
|
|
|
- self.cc_session.group_sendmsg(cmd, "Boss", "Auth")
|
|
|
- self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
|
|
|
- self.cc_session.group_sendmsg(cmd, "Boss", "Xfrin")
|
|
|
- self.cc_session.group_sendmsg(cmd, "Boss", "Zonemgr")
|
|
|
+ self.cc_session.group_sendmsg(cmd, 'Cmdctl', 'Cmdctl')
|
|
|
+ self.cc_session.group_sendmsg(cmd, "ConfigManager", "ConfigManager")
|
|
|
+ self.cc_session.group_sendmsg(cmd, "Auth", "Auth")
|
|
|
+ self.cc_session.group_sendmsg(cmd, "Recurse", "Recurse")
|
|
|
+ self.cc_session.group_sendmsg(cmd, "Xfrout", "Xfrout")
|
|
|
+ self.cc_session.group_sendmsg(cmd, "Xfrin", "Xfrin")
|
|
|
+ self.cc_session.group_sendmsg(cmd, "Zonemgr", "Zonemgr")
|
|
|
+ self.cc_session.group_sendmsg(cmd, "Boss", "Stats")
|
|
|
|
|
|
def stop_process(self, process):
|
|
|
"""Stop the given process, friendly-like."""
|
|
@@ -475,7 +564,9 @@ class BoB:
|
|
|
except:
|
|
|
pass
|
|
|
# XXX: some delay probably useful... how much is uncertain
|
|
|
- time.sleep(0.5)
|
|
|
+ # I have changed the delay from 0.5 to 1, but sometime it's
|
|
|
+ # still not enough.
|
|
|
+ time.sleep(1)
|
|
|
self.reap_children()
|
|
|
# next try sending a SIGTERM
|
|
|
processes_to_stop = list(self.processes.values())
|
|
@@ -582,7 +673,7 @@ def reaper(signal_number, stack_frame):
|
|
|
# the Python signal handler has been set up to write
|
|
|
# down a pipe, waking up our select() bit
|
|
|
pass
|
|
|
-
|
|
|
+
|
|
|
def get_signame(signal_number):
|
|
|
"""Return the symbolic name for a signal."""
|
|
|
for sig in dir(signal):
|
|
@@ -604,30 +695,28 @@ def fatal_signal(signal_number, stack_frame):
|
|
|
def check_port(option, opt_str, value, parser):
|
|
|
"""Function to insure that the port we are passed is actually
|
|
|
a valid port number. Used by OptionParser() on startup."""
|
|
|
- if not re.match('^(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$', value):
|
|
|
- raise OptionValueError("%s requires a port number (0-65535)" % opt_str)
|
|
|
- if (opt_str == '-m' or opt_str == '--msgq-port'):
|
|
|
- parser.values.msgq_port = value
|
|
|
- elif (opt_str == '-p' or opt_str == '--port'):
|
|
|
- parser.values.auth_port = value
|
|
|
- else:
|
|
|
- raise OptionValueError("Unknown option " + opt_str)
|
|
|
-
|
|
|
+ try:
|
|
|
+ if opt_str in ['-p', '--port']:
|
|
|
+ parser.values.auth_port = isc.net.parse.port_parse(value)
|
|
|
+ else:
|
|
|
+ raise OptionValueError("Unknown option " + opt_str)
|
|
|
+ except ValueError as e:
|
|
|
+ raise OptionValueError(str(e))
|
|
|
+
|
|
|
def check_addr(option, opt_str, value, parser):
|
|
|
"""Function to insure that the address we are passed is actually
|
|
|
a valid address. Used by OptionParser() on startup."""
|
|
|
try:
|
|
|
- IPAddr(value)
|
|
|
- except:
|
|
|
+ if opt_str in ['-a', '--address']:
|
|
|
+ parser.values.address = isc.net.parse.addr_parse(value)
|
|
|
+ else:
|
|
|
+ raise OptionValueError("Unknown option " + opt_str)
|
|
|
+ except ValueError:
|
|
|
raise OptionValueError("%s requires a valid IPv4 or IPv6 address" % opt_str)
|
|
|
- if (opt_str == '-a' or opt_str == '--address'):
|
|
|
- parser.values.address = value
|
|
|
- else:
|
|
|
- raise OptionValueError("Unknown option " + opt_str)
|
|
|
|
|
|
def process_rename(option, opt_str, value, parser):
|
|
|
"""Function that renames the process if it is requested by a option."""
|
|
|
- isc.utils.process.rename(value)
|
|
|
+ isc.util.process.rename(value)
|
|
|
|
|
|
def main():
|
|
|
global options
|
|
@@ -635,19 +724,18 @@ def main():
|
|
|
# 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("-a", "--address", dest="address", type="string",
|
|
|
- action="callback", callback=check_addr, default='',
|
|
|
+ action="callback", callback=check_addr, default=None,
|
|
|
help="address the b10-auth daemon will use (default: listen on all addresses)")
|
|
|
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 b10-auth")
|
|
|
- parser.add_option("-p", "--port", dest="auth_port", type="string",
|
|
|
- action="callback", callback=check_port, default="5300",
|
|
|
+ parser.add_option("-p", "--port", dest="auth_port", type="int",
|
|
|
+ action="callback", callback=check_port, default=5300,
|
|
|
help="port the b10-auth daemon will use (default 5300)")
|
|
|
parser.add_option("-u", "--user", dest="user",
|
|
|
type="string", default=None,
|
|
@@ -710,8 +798,11 @@ def main():
|
|
|
signal.signal(signal.SIGINT, fatal_signal)
|
|
|
signal.signal(signal.SIGTERM, fatal_signal)
|
|
|
|
|
|
+ # Block SIGPIPE, as we don't want it to end this process
|
|
|
+ signal.signal(signal.SIGPIPE, signal.SIG_IGN)
|
|
|
+
|
|
|
# Go bob!
|
|
|
- boss_of_bind = BoB(options.msgq_socket_file, int(options.auth_port),
|
|
|
+ boss_of_bind = BoB(options.msgq_socket_file, options.auth_port,
|
|
|
options.address, options.nocache, options.verbose,
|
|
|
setuid, username)
|
|
|
startup_result = boss_of_bind.startup()
|
|
@@ -720,6 +811,17 @@ def main():
|
|
|
sys.exit(1)
|
|
|
sys.stdout.write("[bind10] BIND 10 started\n")
|
|
|
|
|
|
+ # send "bind10.boot_time" to b10-stats
|
|
|
+ time.sleep(1) # wait a second
|
|
|
+ if options.verbose:
|
|
|
+ sys.stdout.write("[bind10] send \"bind10.boot_time\" to b10-stats\n")
|
|
|
+ cmd = isc.config.ccsession.create_command('set',
|
|
|
+ { "stats_data": {
|
|
|
+ 'bind10.boot_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', _BASETIME)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ boss_of_bind.cc_session.group_sendmsg(cmd, 'Stats')
|
|
|
+
|
|
|
# In our main loop, we check for dead processes or messages
|
|
|
# on the c-channel.
|
|
|
wakeup_fd = wakeup_pipe[0]
|
|
@@ -760,6 +862,7 @@ def main():
|
|
|
# shutdown
|
|
|
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
|
|
|
boss_of_bind.shutdown()
|
|
|
+ sys.stdout.write("[bind10] BIND 10 exiting\n");
|
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == "__main__":
|