// -*- 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/xrl_rtrmgr_interface.cc,v 1.54 2007/02/16 22:47:28 pavlin Exp $"
#include "rtrmgr_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/status_codes.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "main_rtrmgr.hh"
#include "master_conf_tree.hh"
#include "module_manager.hh"
#include "randomness.hh"
#include "userdb.hh"
#include "xrl_rtrmgr_interface.hh"
XrlRtrmgrInterface::XrlRtrmgrInterface(XrlRouter& r, UserDB& userdb,
EventLoop& eventloop,
RandomGen& randgen,
Rtrmgr& rtrmgr)
: XrlRtrmgrTargetBase(&r),
_xrl_router(r),
_client_interface(&r),
_finder_notifier_interface(&r),
_userdb(userdb),
_master_config_tree(NULL),
_eventloop(eventloop),
_randgen(randgen),
_rtrmgr(rtrmgr),
_exclusive(false),
_config_locked(false),
_lock_holder_token(""),
_verbose(rtrmgr.verbose())
{
}
XrlRtrmgrInterface::~XrlRtrmgrInterface()
{
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _users.begin(); iter != _users.end(); ++iter) {
delete iter->second;
}
}
XrlCmdError
XrlRtrmgrInterface::common_0_1_get_target_name(// Output values,
string& name)
{
name = XrlRtrmgrTargetBase::name();
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::common_0_1_get_version(// Output values,
string& v)
{
v = string(version());
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::common_0_1_get_status(// Output values,
uint32_t& status,
string& reason)
{
// XXX placeholder only
status = PROC_READY;
reason = "Ready";
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::common_0_1_shutdown()
{
string error_msg;
// The rtrmgr does not accept XRL requests to shutdown via this interface.
error_msg = "Command not supported";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_get_pid(// Output values,
uint32_t& pid)
{
pid = getpid();
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_register_client(
// Input values,
const uint32_t& user_id,
const string& clientname,
// Output values,
string& filename,
uint32_t& pid,
uint32_t& clientid)
{
#ifdef HOST_OS_WINDOWS
pid = GetCurrentProcessId();
#else
pid = getpid();
#endif
const User* user;
UserInstance* newuser;
string error_msg;
//
// Prevent a clientname passing characters that might cause us to
// overwrite files later. Prohibiting "/" makes allowing ".." safe.
//
if (strchr(clientname.c_str(), '/') != NULL) {
error_msg = c_format("Illegal character in clientname: %s",
clientname.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
//
// Note that clientname is user supplied data, so this would be
// dangerous if the user could pass "..".
//
#ifdef HOST_OS_WINDOWS
char tmppath[128];
GetTempPathA(sizeof(tmppath), tmppath);
filename = tmppath + clientname;
#else
filename = "/tmp/rtrmgr-" + clientname;
mode_t oldmode = umask(S_IRWXG|S_IRWXO);
debug_msg("newmode: %o oldmode: %o\n", S_IRWXG|S_IRWXO,
XORP_UINT_CAST(oldmode));
#endif
FILE* file = fopen(filename.c_str(), "w+");
if (file == NULL) {
error_msg = c_format("Failed to create temporary file: %s",
filename.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
#ifndef HOST_OS_WINDOWS
umask(oldmode);
#endif
user = _userdb.find_user_by_user_id(user_id);
if (user == NULL) {
#ifdef HOST_OS_WINDOWS
DeleteFileA(filename.c_str());
#else
unlink(filename.c_str());
#endif
error_msg = c_format("User ID %u not found",
XORP_UINT_CAST(user_id));
return XrlCmdError::COMMAND_FAILED(error_msg);
}
newuser = new UserInstance(user->user_id(), user->username());
newuser->set_clientname(clientname);
newuser->set_authtoken(generate_auth_token(user_id, clientname));
newuser->set_authenticated(false);
clientid = allocate_clientid();
newuser->set_clientid(clientid);
_users.insert(pair<uid_t, UserInstance*>(user_id, newuser));
//
// Be careful here - if for any reason we fail to create, write to,
// or close the temporary auth file, that's a very different error
// from if the xorpsh process can't read it correctly.
//
if (fprintf(file, "%s", newuser->authtoken().c_str())
< (int)(newuser->authtoken().size())) {
#ifdef HOST_OS_WINDOWS
DeleteFileA(filename.c_str());
#else
unlink(filename.c_str());
#endif
error_msg = c_format("Failed to write to temporary file: %s",
filename.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
#ifndef HOST_OS_WINDOWS
if (fchown(fileno(file), user_id, (gid_t)-1) != 0) {
unlink(filename.c_str());
error_msg = c_format("Failed to chown temporary file %s to user_id %u",
filename.c_str(), XORP_UINT_CAST(user_id));
return XrlCmdError::COMMAND_FAILED(error_msg);
}
#endif
if (fclose(file) != 0) {
#ifdef HOST_OS_WINDOWS
DeleteFileA(filename.c_str());
#else
unlink(filename.c_str());
#endif
error_msg = c_format("Failed to close temporary file: %s",
filename.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
#ifdef NO_XORPSH_AUTHENTICATION
//
// XXX: a hack to return back the key itself in case we run
// xorpsh on a remote machine.
//
filename = newuser->authtoken();
#endif
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_unregister_client(const string& token)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _config_users.begin(); iter != _config_users.end(); ++iter) {
if (iter->second->authtoken() == token) {
_config_users.erase(iter);
return XrlCmdError::OKAY();
}
}
for (iter = _users.begin(); iter != _users.end(); ++iter) {
if (iter->second->authtoken() == token) {
deallocate_clientid(iter->second->clientid());
delete iter->second;
_users.erase(iter);
return XrlCmdError::OKAY();
}
}
// This cannot happen, because the token wouldn't have verified
XLOG_UNREACHABLE();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_authenticate_client(
// Input values,
const uint32_t& user_id,
const string& clientname,
const string& token)
{
UserInstance *user;
string error_msg;
user = find_user_instance(user_id, clientname);
if (user == NULL) {
error_msg = "User instance not found (maybe login timed out)";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (token == user->authtoken()) {
user->set_authenticated(true);
initialize_client_state(user_id, user);
return XrlCmdError::OKAY();
} else {
error_msg = "Bad authtoken";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
}
void
XrlRtrmgrInterface::initialize_client_state(uid_t user_id,
UserInstance *user)
{
// We need to register interest via the finder in the XRL target
// associated with this user instance, so that when the user
// instance goes away without deregistering, we can clean up the
// state we associated with them.
XrlFinderEventNotifierV0p1Client::RegisterInstanceEventInterestCB cb;
cb = callback(this, &XrlRtrmgrInterface::finder_register_done,
user->clientname());
_finder_notifier_interface.
send_register_class_event_interest("finder",
_xrl_router.instance_name(),
user->clientname(), cb);
XLOG_TRACE(_verbose,
"registering interest in %s\n", user->clientname().c_str());
//
// We need to send the running config and module state to the
// client, but we first need to return from the current XRL, so we
// schedule this on a zero-second timer.
//
XorpTimer t;
uint32_t delay = 0;
if (!_rtrmgr.ready()) {
delay = 2000;
}
t = _eventloop.new_oneoff_after_ms(delay,
callback(this, &XrlRtrmgrInterface::send_client_state,
user_id, user->clientname()));
_background_tasks.push_front(t);
}
void
XrlRtrmgrInterface::finder_register_done(const XrlError& e, string clientname)
{
if (e != XrlError::OKAY()) {
XLOG_ERROR("Failed to register with finder about XRL %s (err: %s)\n",
clientname.c_str(), e.error_msg());
}
}
void
XrlRtrmgrInterface::send_client_state(uid_t user_id, string client)
{
UserInstance *user = find_user_instance(user_id, client);
if (user == NULL)
return;
debug_msg("send_client_state %s\n", client.c_str());
if (!_rtrmgr.ready()) {
//
// We're still in the process of reconfiguring, so delay this
// until later.
//
initialize_client_state(user_id, user);
return;
}
GENERIC_CALLBACK cb2;
cb2 = callback(this, &XrlRtrmgrInterface::client_updated, user_id, user);
//
// Send the module status
//
debug_msg("Sending mod status changed to %s\n", client.c_str());
list<string> module_names;
ModuleManager &mmgr(_master_config_tree->module_manager());
module_names = mmgr.get_module_names();
list<string>::iterator iter;
for (iter = module_names.begin(); iter != module_names.end(); ++iter) {
const string& module_name = *iter;
debug_msg("module: %s\n", module_name.c_str());
Module::ModuleStatus status = mmgr.module_status(module_name);
if (status != Module::NO_SUCH_MODULE) {
debug_msg("status %d\n", status);
_client_interface.send_module_status(client.c_str(), module_name,
static_cast<uint32_t>(status),
cb2);
}
}
//
// Send the configuration
//
debug_msg("Sending config changed to %s\n", client.c_str());
string config = _master_config_tree->show_tree(/*numbered*/ true);
_client_interface.send_config_changed(client.c_str(), 0, config, "", cb2);
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_enter_config_mode(
// Input values,
const string& token,
const bool& exclusive)
{
string error_msg;
if (! verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission for this operation";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
XLOG_TRACE(_verbose, "user %u entering config mode\n",
XORP_UINT_CAST(user_id));
if (exclusive)
XLOG_TRACE(_verbose, "user requested exclusive config\n");
else
XLOG_TRACE(_verbose, "user requested non-exclusive config\n");
//
// If he's asking for exclusive, and we've already got config users,
// or there's already an exclusive user, deny the request.
//
multimap<uid_t, UserInstance*>::iterator iter;
if (exclusive && !_config_users.empty()) {
string user_names;
for (iter = _config_users.begin();
iter != _config_users.end();
++iter) {
UserInstance* user = iter->second;
if (! user_names.empty())
user_names += ", ";
user_names += user->username();
}
error_msg = c_format("Exclusive config mode requested, but there are "
"already other configuration mode users: %s",
user_names.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_exclusive && !_config_users.empty()) {
error_msg = c_format("User %s is in exclusive configuration mode",
_exclusive_username.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
for (iter = _users.begin(); iter != _users.end(); ++iter) {
if (iter->second->authtoken() == token) {
UserInstance* this_user = iter->second;
_config_users.insert(make_pair(user_id, this_user));
if (exclusive) {
_exclusive = true;
_exclusive_username = this_user->username();
} else {
_exclusive = false;
_exclusive_username = "";
}
return XrlCmdError::OKAY();
}
}
XLOG_UNREACHABLE();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_leave_config_mode(
// Input values,
const string& token)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
XLOG_TRACE(_verbose, "user %u leaving config mode\n",
XORP_UINT_CAST(get_user_id_from_token(token)));
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _config_users.begin(); iter != _config_users.end(); ++iter) {
if (iter->second->authtoken() == token) {
_config_users.erase(iter);
return XrlCmdError::OKAY();
}
}
error_msg = "User was not in configuration mode";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_get_config_users(
// Input values,
const string& token,
// Output values,
XrlAtomList& users)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission to view the configuration "
"mode users";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
multimap<uid_t, UserInstance*>::const_iterator iter;
for (iter = _config_users.begin(); iter != _config_users.end(); ++iter) {
UserInstance* user = iter->second;
if (!user->is_a_zombie()) {
users.append(XrlAtom(static_cast<uint32_t>(user->user_id())));
} else {
XLOG_WARNING("user %u is a zombie\n",
XORP_UINT_CAST(user->user_id()));
}
}
return XrlCmdError::OKAY();
}
/**
* This interface is deprecated as the main way for xorpsh to get the
* running config from the router manager. It is retained for
* debugging purposes. xorpsh now gets its config automatically
* immediatedly after registering which removes a potential timing
* hole where the client could be told about a change to the config
* before it has asked what the current config is.
*/
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_get_running_config(
// Input values,
const string& token,
// Output values,
bool& ready,
string& config)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_rtrmgr.ready()) {
ready = true;
config = _master_config_tree->show_tree(/*numbered*/ true);
} else {
ready = false;
config = "";
}
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_apply_config_change(
// Input values,
const string& token,
const string& target,
const string& deltas,
const string& deletions)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_config_locked == false) {
error_msg = "Cannot commit config changes without locking the config";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission for this operation";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
// XXX: TBD
XLOG_TRACE(_verbose,
"\nXRL got config change: deltas: \n%s\nend deltas\ndeletions:\n%s\nend deletions\n",
deltas.c_str(), deletions.c_str());
ConfigChangeCallBack cb;
cb = callback(this, &XrlRtrmgrInterface::apply_config_change_done,
user_id, string(target));
if (_master_config_tree->apply_config_change(user_id, error_msg, deltas,
deletions, cb) != true) {
return XrlCmdError::COMMAND_FAILED(error_msg);
}
return XrlCmdError::OKAY();
}
void
XrlRtrmgrInterface::apply_config_change_done(bool success,
string error_msg,
string deltas,
string deletions,
uid_t user_id,
string target)
{
XLOG_TRACE(_verbose,
"apply_config_change_done: status: %d response: %s target: %s",
success, error_msg.c_str(), target.c_str());
if (success) {
// Check everything really worked, and finalize the commit
if (_master_config_tree->check_commit_status(error_msg) == false) {
XLOG_TRACE(_verbose,
"check commit status indicates failure: >%s<\n",
error_msg.c_str());
success = false;
}
} else {
XLOG_TRACE(_verbose, "request failed: >%s<\n", error_msg.c_str());
}
GENERIC_CALLBACK cb1;
cb1 = callback(this, &XrlRtrmgrInterface::apply_config_change_done_cb);
if (success) {
// Send the changes to all the clients, including the client
// that originally sent them.
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _users.begin(); iter != _users.end(); ++iter) {
string client = iter->second->clientname();
GENERIC_CALLBACK cb2;
cb2 = callback(this, &XrlRtrmgrInterface::client_updated,
iter->first, iter->second);
debug_msg("Sending config changed to %s\n", client.c_str());
_client_interface.send_config_changed(client.c_str(),
user_id,
deltas, deletions, cb2);
}
debug_msg("Sending config change done to %s\n", target.c_str());
_client_interface.send_config_change_done(target.c_str(),
true, "", cb1);
} else {
// Something went wrong
_master_config_tree->discard_changes();
debug_msg("Sending config change failed to %s\n", target.c_str());
_client_interface.send_config_change_done(target.c_str(),
false, error_msg, cb1);
}
}
void
XrlRtrmgrInterface::config_saved_done_cb(const XrlError& e)
{
if (e != XrlError::OKAY()) {
XLOG_ERROR("Failed to notify client that config change was saved: %s",
e.error_msg());
}
}
void
XrlRtrmgrInterface::apply_config_change_done_cb(const XrlError& e)
{
if (e != XrlError::OKAY()) {
XLOG_ERROR("Failed to notify client that config change was done: %s",
e.error_msg());
}
}
void
XrlRtrmgrInterface::client_updated(const XrlError& e, uid_t user_id,
UserInstance* user)
{
if (e != XrlError::OKAY()) {
XLOG_ERROR("Failed to notify client that config changed: %s\n",
e.error_msg());
if (e != XrlError::COMMAND_FAILED()) {
// We failed to notify the user instance, so we note that
// they are probably dead. We can't guarantee that the
// "user" pointer is still valid, so check before using it.
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _users.lower_bound(user_id);
iter != _users.end();
iter++) {
if (iter->second == user) {
user->set_zombie(true);
return;
}
}
}
}
}
void
XrlRtrmgrInterface::module_status_changed(const string& modname,
GenericModule::ModuleStatus status)
{
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _users.begin(); iter != _users.end(); ++iter) {
string client = iter->second->clientname();
GENERIC_CALLBACK cb2;
cb2 = callback(this, &XrlRtrmgrInterface::client_updated,
iter->first, iter->second);
debug_msg("Sending mod statis changed to %s\n", client.c_str());
_client_interface.send_module_status(client.c_str(),
modname, (uint32_t)status, cb2);
}
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_lock_config(
// Input values,
const string& token,
const uint32_t& timeout /* in milliseconds */,
// Output values,
bool& success,
uint32_t& holder)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission to lock the configuration";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_config_locked) {
// Can't return COMMAND_FAILED and return the lock holder
success = false;
holder = get_user_id_from_token(_lock_holder_token);
return XrlCmdError::OKAY();
} else {
success = true;
_config_locked = true;
_lock_holder_token = token;
_lock_timer = _eventloop.new_oneoff_after_ms(
timeout,
callback(this, &XrlRtrmgrInterface::lock_timeout));
return XrlCmdError::OKAY();
}
}
void
XrlRtrmgrInterface::lock_timeout()
{
_config_locked = false;
_lock_holder_token = "";
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_unlock_config(
// Input values
const string& token)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_config_locked) {
if (token != _lock_holder_token) {
error_msg = c_format("Configuration is not held by process %s",
token.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
_config_locked = false;
_lock_timer.unschedule();
_lock_holder_token = "";
return XrlCmdError::OKAY();
} else {
error_msg = "Configuration is not locked";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_lock_node(
// Input values,
const string& token,
const string& node,
const uint32_t& timeout,
// Output values,
bool& success,
uint32_t& holder)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission to lock the configuration";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (user_id == (uid_t)-1) {
// Shouldn't be possible as we already checked the token
XLOG_UNREACHABLE();
}
success = _master_config_tree->lock_node(node, user_id, timeout, holder);
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_unlock_node(
// Input values,
const string& token,
const string& node)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (user_id == (uid_t)-1) {
// Shouldn't be possible as we already checked the token
XLOG_UNREACHABLE();
}
bool success;
success = _master_config_tree->unlock_node(node, user_id);
if (success) {
return XrlCmdError::OKAY();
} else {
error_msg = "Unlocking the configuration failed";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_save_config(// Input values:
const string& token,
const string& target,
const string& filename)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission to save the configuration";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_config_locked && (token != _lock_holder_token)) {
uid_t uid = get_user_id_from_token(_lock_holder_token);
string holder = _userdb.find_user_by_user_id(uid)->username();
error_msg = c_format("The configuration is currently locked by user %s",
holder.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
ConfigSaveCallBack cb;
cb = callback(this, &XrlRtrmgrInterface::save_config_done,
filename, user_id, target);
if (_master_config_tree->save_config(filename, user_id, error_msg, cb)
!= true) {
return XrlCmdError::COMMAND_FAILED(error_msg);
}
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_load_config(// Input values:
const string& token,
const string& target,
const string& filename)
{
string error_msg;
if (!verify_token(token)) {
error_msg = "AUTH_FAIL";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
uid_t user_id = get_user_id_from_token(token);
if (_userdb.has_capability(user_id, "config") == false) {
error_msg = "You do not have permission to reload the configuration";
return XrlCmdError::COMMAND_FAILED(error_msg);
}
if (_config_locked && (token != _lock_holder_token)) {
uid_t uid = get_user_id_from_token(_lock_holder_token);
string holder = _userdb.find_user_by_user_id(uid)->username();
error_msg = c_format("The configuration is currently locked by user %s",
holder.c_str());
return XrlCmdError::COMMAND_FAILED(error_msg);
}
ConfigLoadCallBack cb;
cb = callback(this, &XrlRtrmgrInterface::load_config_done,
filename, user_id, target);
if (_master_config_tree->load_config(filename, user_id, error_msg, cb)
!= true) {
return XrlCmdError::COMMAND_FAILED(error_msg);
}
return XrlCmdError::OKAY();
}
void
XrlRtrmgrInterface::save_config_done(bool success,
string error_msg,
string filename,
uid_t user_id,
string target)
{
GENERIC_CALLBACK cb1;
cb1 = callback(this, &XrlRtrmgrInterface::config_saved_done_cb);
_client_interface.send_config_saved_done(target.c_str(), success,
error_msg, cb1);
UNUSED(filename);
UNUSED(user_id);
}
void
XrlRtrmgrInterface::load_config_done(bool success,
string error_msg,
string deltas,
string deletions,
string filename,
uid_t user_id,
string target)
{
// Propagate the changes to all clients
apply_config_change_done(success, error_msg, deltas, deletions,
user_id, target);
UNUSED(filename);
}
XrlCmdError
XrlRtrmgrInterface::rtrmgr_0_1_set_config_directory(
// Input values,
const string& config_directory)
{
_master_config_tree->set_config_directory(config_directory);
return XrlCmdError::OKAY();
}
UserInstance *
XrlRtrmgrInterface::find_user_instance(uid_t user_id, const string& clientname)
{
multimap<uid_t, UserInstance*>::iterator iter;
iter = _users.lower_bound(user_id);
while (iter != _users.end() && iter->second->user_id() == user_id) {
if (iter->second->clientname() == clientname)
return iter->second;
++iter;
}
return NULL;
}
bool
XrlRtrmgrInterface::verify_token(const string& token) const
{
uid_t user_id = get_user_id_from_token(token);
if (user_id == (uid_t)-1)
return false;
multimap<uid_t, UserInstance*>::const_iterator iter;
iter = _users.lower_bound(user_id);
while (iter != _users.end() && iter->second->user_id() == user_id) {
if (iter->second->authtoken() == token)
return true;
++iter;
}
return false;
}
uid_t
XrlRtrmgrInterface::get_user_id_from_token(const string& token) const
{
uid_t user_id;
unsigned int u;
size_t tlen = token.size();
if (token.size() < 10)
return (uid_t)-1;
string uidstr = token.substr(0, 10);
string authstr = token.substr(10, tlen - 10);
sscanf(uidstr.c_str(), "%u", &u);
user_id = u;
return user_id;
}
string
XrlRtrmgrInterface::generate_auth_token(uid_t user_id,
const string& clientname)
{
string token;
token = c_format("%10u", XORP_UINT_CAST(user_id));
string shortcname;
size_t clen = clientname.size();
if (clen > CNAMELEN) {
shortcname = clientname.substr(clen - CNAMELEN, CNAMELEN);
} else {
shortcname = clientname;
for (size_t i = 0; i < (CNAMELEN - clen); i++)
shortcname += '*';
}
token += shortcname;
string randomness;
uint8_t buf[16];
_randgen.get_random_bytes(16, buf);
for (size_t i = 0; i < 16; i++)
randomness += c_format("%2x", buf[i]);
token += randomness;
return token;
}
XrlCmdError
XrlRtrmgrInterface::finder_event_observer_0_1_xrl_target_birth(
const string& target_class,
const string& target_instance)
{
XLOG_TRACE(_verbose, "XRL Birth: class %s instance %s\n",
target_class.c_str(), target_instance.c_str());
return XrlCmdError::OKAY();
}
XrlCmdError
XrlRtrmgrInterface::finder_event_observer_0_1_xrl_target_death(
const string& target_class,
const string& target_instance)
{
XLOG_TRACE(_verbose, "XRL Death: class %s instance %s\n",
target_class.c_str(), target_instance.c_str());
multimap<uid_t, UserInstance*>::iterator iter;
for (iter = _config_users.begin(); iter != _config_users.end(); ++iter) {
UserInstance* user = iter->second;
if (user->clientname() == target_class) {
debug_msg("removing user %s from list of config users\n",
target_class.c_str());
_config_users.erase(iter);
break;
}
}
for (iter = _users.begin(); iter != _users.end(); ++iter) {
UserInstance* user = iter->second;
if (user->clientname() == target_class) {
debug_msg("removing user %s from list of regular users\n",
target_class.c_str());
_users.erase(iter);
break;
}
}
return XrlCmdError::OKAY();
}
uint32_t
XrlRtrmgrInterface::allocate_clientid()
{
/* find the first unused client ID number */
uint32_t expected = 1;
set<uint32_t>::iterator i;
for (i = _clientids.begin();; i++) {
if ((i == _clientids.end()) || (*i > expected)) {
_clientids.insert(expected);
return expected;
}
expected++;
}
XLOG_UNREACHABLE();
}
void
XrlRtrmgrInterface::deallocate_clientid(uint32_t clientid)
{
set<uint32_t>::iterator i;
i = _clientids.find(clientid);
XLOG_ASSERT(i != _clientids.end());
_clientids.erase(i);
}
syntax highlighted by Code2HTML, v. 0.9.1