// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.
#ident "$XORP: xorp/rtrmgr/main_rtrmgr.cc,v 1.71 2007/02/16 22:47:22 pavlin Exp $"
#include "rtrmgr_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/eventloop.hh"
#include "libxorp/utils.hh"
#include <signal.h>
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include "libxipc/sockutil.hh"
#include "libxipc/finder_server.hh"
#include "libxipc/finder_constants.hh"
#include "libxipc/permits.hh"
#include "libxipc/xrl_std_router.hh"
#include "main_rtrmgr.hh"
#include "master_conf_tree.hh"
#include "module_manager.hh"
#include "randomness.hh"
#include "rtrmgr_error.hh"
#include "task.hh"
#include "template_commands.hh"
#include "master_template_tree.hh"
#include "master_template_tree_node.hh"
#include "userdb.hh"
#include "util.hh"
#include "xrl_rtrmgr_interface.hh"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
//
// Default values
//
static bool default_do_exec = true;
static bool default_do_restart = false;
static bool default_verbose = false;
//
// Local state
//
static volatile bool running = false;
static string template_dir;
static string xrl_targets_dir;
static string boot_file;
static bool do_exec = default_do_exec;
static bool do_restart = default_do_restart;
static bool verbose = default_verbose;
list<IPv4> bind_addrs;
uint16_t bind_port = FinderConstants::FINDER_DEFAULT_PORT();
int32_t quit_time = -1;
static void cleanup_and_exit(int errcode);
static void
signalhandler(int)
{
running = false;
}
static void
usage(const char* argv0)
{
fprintf(stderr, "Usage: %s [options]\n", xorp_basename(argv0));
fprintf(stderr, "Options:\n");
fprintf(stderr, " -a <allowed host> Host allowed by the finder\n");
fprintf(stderr, " -n <allowed net> Subnet allowed by the finder\n");
fprintf(stderr, " -h Display this information\n");
fprintf(stderr, " -v Print verbose information\n");
fprintf(stderr, " -b <file> Specify boot file\n");
fprintf(stderr, " -N Do not execute XRLs and do not start processes\n");
fprintf(stderr, " -r Restart failed processes (not implemented yet)\n");
fprintf(stderr, " -i <addr> Set or add an interface run Finder on\n");
fprintf(stderr, " -p <port> Set port to run Finder on\n");
fprintf(stderr, " -q <secs> Set forced quit period\n");
fprintf(stderr, " -t <dir> Specify templates directory\n");
fprintf(stderr, " -x <dir> Specify Xrl targets directory\n");
}
static void
display_defaults()
{
fprintf(stderr, "Defaults:\n");
fprintf(stderr, " Boot file := %s\n",
xorp_boot_file().c_str());
fprintf(stderr, " Templates directory := %s\n",
xorp_template_dir().c_str());
fprintf(stderr, " Xrl targets directory := %s\n",
xorp_xrl_targets_dir().c_str());
fprintf(stderr, " Execute Xrls := %s\n",
default_do_exec ? "true" : "false");
fprintf(stderr, " Restart failed processes := %s\n",
default_do_restart ? "true" : "false");
fprintf(stderr, " Print verbose information := %s\n",
default_verbose ? "true" : "false");
}
// the following two functions are an ugly hack to cause the C code in
// the parser to call methods on the right version of the TemplateTree
void
add_cmd_adaptor(char *cmd, TemplateTree* tt) throw (ParseError)
{
((MasterTemplateTree*)tt)->add_cmd(cmd);
}
void
add_cmd_action_adaptor(const string& cmd, const list<string>& action,
TemplateTree* tt) throw (ParseError)
{
((MasterTemplateTree*)tt)->add_cmd_action(cmd, action);
}
Rtrmgr::Rtrmgr(const string& template_dir,
const string& xrl_targets_dir,
const string& boot_file,
const list<IPv4>& bind_addrs,
uint16_t bind_port,
bool do_exec,
bool do_restart,
bool verbose,
int32_t quit_time)
: _template_dir(template_dir),
_xrl_targets_dir(xrl_targets_dir),
_boot_file(boot_file),
_bind_addrs(bind_addrs),
_bind_port(bind_port),
_do_exec(do_exec),
_do_restart(do_restart),
_verbose(verbose),
_quit_time(quit_time),
_ready(false),
_mct(NULL)
{
}
int
Rtrmgr::run()
{
int errcode = 0;
string errmsg;
running = true;
//
// Install signal handlers so we can clean up when we're killed
//
signal(SIGTERM, signalhandler);
signal(SIGINT, signalhandler);
// XXX signal(SIGBUS, signalhandler);
// XXX signal(SIGSEGV, signalhandler);
//
// Initialize the event loop
//
EventLoop eventloop;
//
// Print various information
//
XLOG_TRACE(_verbose, "Boot file := %s\n",
boot_file.c_str());
XLOG_TRACE(_verbose, "Templates directory := %s\n",
template_dir.c_str());
XLOG_TRACE(_verbose, "Xrl targets directory := %s\n",
xrl_targets_dir.c_str());
XLOG_TRACE(_verbose, "Execute Xrls := %s\n",
do_exec ? "true" : "false");
XLOG_TRACE(_verbose, "Restart failed processes := %s\n",
do_restart ? "true" : "false");
XLOG_TRACE(_verbose, "Print verbose information := %s\n",
_verbose ? "true" : "false");
XRLdb* xrldb = NULL;
try {
xrldb = new XRLdb(_xrl_targets_dir, _verbose);
} catch (const InitError& e) {
XLOG_ERROR("Shutting down due to an init error: %s", e.why().c_str());
return (1);
}
//
// Read the router config template files
//
MasterTemplateTree* tt = new MasterTemplateTree(xorp_config_root_dir(),
*xrldb, _verbose);
if (!tt->load_template_tree(_template_dir, errmsg)) {
XLOG_ERROR("Shutting down due to an init error: %s", errmsg.c_str());
return (1);
}
debug_msg("%s", tt->tree_str().c_str());
//
// Start the finder and the rest of the rtrmgr components.
// These are dynamically created so we have control over the
// deletion order.
//
FinderServer* fs = NULL;
try {
fs = new FinderServer(eventloop,
FinderConstants::FINDER_DEFAULT_HOST(),
_bind_port);
while (_bind_addrs.empty() == false) {
if (fs->add_binding(_bind_addrs.front(), _bind_port) == false) {
XLOG_WARNING("Finder failed to bind interface %s port %d",
_bind_addrs.front().str().c_str(), _bind_port);
}
_bind_addrs.pop_front();
}
} catch (const InvalidPort& i) {
XLOG_ERROR("%s: a finder may already be running.", i.why().c_str());
delete tt;
return (1);
} catch (...) {
xorp_catch_standard_exceptions();
delete tt;
return (1);
}
//
// Initialize the IPC mechanism
//
XrlStdRouter xrl_router(eventloop, "rtrmgr", fs->addr(), fs->port());
XorpClient xclient(eventloop, xrl_router);
//
// Start the module manager
//
ModuleManager mmgr(eventloop, *this, _do_restart, _verbose,
xorp_binary_root_dir());
try {
//
// Read the router startup configuration file,
// start the processes required, and initialize them.
//
RandomGen randgen;
UserDB userdb(_verbose);
userdb.load_password_file();
_xrt = new XrlRtrmgrInterface(xrl_router, userdb, eventloop,
randgen, *this);
wait_until_xrl_router_is_ready(eventloop, xrl_router);
#if 0
// Let the module manager know how to send XRLs to xorpsh
mmgr.set_xrl_interface(_xrt);
#endif
_mct = new MasterConfigTree(boot_file, tt, mmgr, xclient, _do_exec,
_verbose);
//
// XXX: note that theoretically we may receive an XRL before
// we call XrlRtrmgrInterface::set_master_config_tree()
// or ModuleManager::set_master_config_tree() below.
// For now we ignore that possibility...
//
_xrt->set_master_config_tree(_mct);
mmgr.set_master_config_tree(_mct);
// For testing purposes, rtrmgr can terminate itself after some time.
XorpTimer quit_timer;
if (_quit_time > 0) {
quit_timer =
eventloop.new_oneoff_after_ms(_quit_time * 1000,
callback(signalhandler, 0));
}
_ready = true;
//
// Loop while handling configuration events and signals
//
while (running) {
fflush(stdout);
eventloop.run();
if (_mct->config_failed())
running = false;
}
fflush(stdout);
_ready = false;
//
// Shutdown everything
//
// Delete the configuration
_mct->delete_entire_config();
// Wait until changes due to deleting config have finished
// being applied.
while (eventloop.events_pending() && (_mct->commit_in_progress())) {
eventloop.run();
}
delete _mct;
} catch (const InitError& e) {
XLOG_ERROR("rtrmgr shutting down due to an init error: %s",
e.why().c_str());
errcode = 1;
}
// Shut down child processes that haven't already been shutdown
mmgr.shutdown();
// Wait until child processes have terminated
while ((mmgr.is_shutdown_completed() != true)
&& eventloop.events_pending()) {
eventloop.run();
}
// Delete the template tree
delete tt;
// Delete the XRLdb
delete xrldb;
// Shutdown the finder
delete fs;
return (errcode);
}
bool
Rtrmgr::ready() const {
if (!_ready)
return false;
if (_mct->commit_in_progress())
return false;
return true;
}
void
Rtrmgr::module_status_changed(const string& module_name,
GenericModule::ModuleStatus status)
{
_xrt->module_status_changed(module_name, status);
}
int
main(int argc, char* const argv[])
{
int errcode = 0;
//
// Initialize and start xlog
//
xlog_init(argv[0], NULL);
xlog_set_verbose(XLOG_VERBOSE_LOW); // Least verbose messages
// XXX: verbosity of the error messages temporary increased
xlog_level_set_verbose(XLOG_LEVEL_ERROR, XLOG_VERBOSE_HIGH);
xlog_level_set_verbose(XLOG_LEVEL_WARNING, XLOG_VERBOSE_HIGH);
xlog_level_set_verbose(XLOG_LEVEL_INFO, XLOG_VERBOSE_HIGH);
xlog_add_default_output();
xlog_start();
//
// Install the handler for unexpected exceptions
//
XorpUnexpectedHandler ex(xorp_unexpected_handler);
//
// Expand the default variables to include the XORP root path
//
xorp_path_init(argv[0]);
template_dir = xorp_template_dir();
xrl_targets_dir = xorp_xrl_targets_dir();
boot_file = xorp_boot_file();
int c;
while ((c = getopt(argc, argv, "a:n:t:b:x:i:p:q:Nrvh")) != EOF) {
switch(c) {
case 'a':
//
// User is specifying an IPv4 address to accept finder
// connections from.
//
try {
add_permitted_host(IPv4(optarg));
} catch (const InvalidString&) {
fprintf(stderr, "%s is not a valid IPv4 address.\n", optarg);
usage(argv[0]);
cleanup_and_exit(1);
}
break;
case 'n':
//
// User is specifying a network address to accept finder
// connections from.
//
try {
add_permitted_net(IPv4Net(optarg));
} catch (const InvalidString&) {
fprintf(stderr, "%s is not a valid IPv4 network.\n", optarg);
usage(argv[0]);
cleanup_and_exit(1);
}
break;
case 't':
template_dir = optarg;
break;
case 'b':
boot_file = optarg;
break;
case 'x':
xrl_targets_dir = optarg;
break;
case 'q':
quit_time = atoi(optarg);
break;
case 'N':
do_exec = false;
break;
case 'r':
//
// Enable failed processes to be restarted by the rtrmgr.
// Note: this option is not recommended, because it does
// not work properly. E.g., if a process fails during
// reconfiguration via xorpsh, then the rtrmgr itself may
// coredump.
//
do_restart = true;
break;
case 'v':
verbose = true;
break;
case 'p':
bind_port = static_cast<uint16_t>(atoi(optarg));
if (bind_port == 0) {
fprintf(stderr, "0 is not a valid port.\n");
cleanup_and_exit(1);
}
break;
case 'i':
//
// User is specifying which interface to bind finder to
//
try {
IPv4 bind_addr = IPv4(optarg);
in_addr ina;
bind_addr.copy_out(ina);
if (is_ip_configured(ina) == false) {
fprintf(stderr,
"%s is not the address of an active interface.\n",
optarg);
cleanup_and_exit(1);
}
bind_addrs.push_back(bind_addr);
} catch (const InvalidString&) {
fprintf(stderr, "%s is not a valid interface address.\n",
optarg);
cleanup_and_exit(1);
}
break;
case 'h':
case '?':
default:
usage(argv[0]);
display_defaults();
cleanup_and_exit(1);
}
}
//
// The main procedure
//
Rtrmgr rtrmgr(template_dir, xrl_targets_dir, boot_file, bind_addrs,
bind_port, do_exec, do_restart, verbose, quit_time);
errcode = rtrmgr.run();
cleanup_and_exit(errcode);
}
void
cleanup_and_exit(int errcode)
{
//
// Gracefully stop and exit xlog
//
xlog_stop();
xlog_exit();
exit(errcode);
}
syntax highlighted by Code2HTML, v. 0.9.1