// -*- 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/template_base_command.cc,v 1.23 2007/02/16 22:47:25 pavlin Exp $" #include "rtrmgr_module.h" #include "libxorp/xorp.h" #include "libxorp/xlog.h" #include "libxorp/debug.h" #include #include "libxipc/xrl_router.hh" #include "conf_tree_node.hh" #include "task.hh" #include "template_commands.hh" #include "template_tree.hh" #include "template_tree_node.hh" #include "util.hh" #include "config_operators.hh" BaseCommand::BaseCommand(TemplateTreeNode& template_tree_node, const string& cmd_name) : _template_tree_node(template_tree_node) { debug_msg("BaseCommand constructor: %s\n", cmd_name.c_str()); _cmd_name = cmd_name; } BaseCommand::~BaseCommand() { } string BaseCommand::str() const { string tmp = _cmd_name; return tmp; } // ---------------------------------------------------------------------------- // AllowCommand implementation AllowCommand::AllowCommand(TemplateTreeNode& template_tree_node, const string& cmd_name) : BaseCommand(template_tree_node, cmd_name) { } // ---------------------------------------------------------------------------- // AllowOptionsCommand implementation AllowOptionsCommand::AllowOptionsCommand(TemplateTreeNode& ttn, const string& cmd_name) : AllowCommand(ttn, cmd_name) { } bool AllowOptionsCommand::expand_actions(string& error_msg) { map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; TemplateTreeNode* ttn; ttn = template_tree_node().find_varname_node(varname); if (ttn == NULL) { // Error: invalid variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } const Filter& filter = iter->second; Filter::const_iterator iter2; for (iter2 = filter.begin(); iter2 != filter.end(); ++iter2) { const string& value = iter2->first; const string& help = iter2->second; ttn->add_allowed_value(value, help); } } return (true); } bool AllowOptionsCommand::check_referred_variables(string& error_msg) const { map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; const TemplateTreeNode* ttn; ttn = template_tree_node().find_const_varname_node(varname); if (ttn == NULL) { // Error: invalid variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } } return (true); } void AllowOptionsCommand::add_action(const list& action) throw (ParseError) { string error_msg; string new_varname, new_value, new_help_keyword, new_help_str; size_t expected_parameters_n = 4; list unparsed_action = action; // // Check the number of parameters // if (action.size() != expected_parameters_n) { error_msg = c_format("%%allow command with invalid number of " "parameters: %u (expected %u)", XORP_UINT_CAST(action.size()), XORP_UINT_CAST(expected_parameters_n)); xorp_throw(ParseError, error_msg); } // // Extract each parameter // new_varname = unparsed_action.front(); unparsed_action.pop_front(); new_value = unquote(unparsed_action.front()); unparsed_action.pop_front(); new_help_keyword = unparsed_action.front(); unparsed_action.pop_front(); new_help_str = unquote(unparsed_action.front()); unparsed_action.pop_front(); // // Verify all parameters // if (new_help_keyword != "%help") { error_msg = c_format("Invalid %%allow argument: %s " "(expected \"%%help:\")", new_help_keyword.c_str()); xorp_throw(ParseError, error_msg); } // // Insert the new entry // map::iterator iter; iter = _filters.find(new_varname); if (iter == _filters.end()) { // Insert a new map Filter new_filter; _filters.insert(make_pair(new_varname, new_filter)); iter = _filters.find(new_varname); } XLOG_ASSERT(iter != _filters.end()); Filter& filter = iter->second; // XXX: insert the new pair even if we overwrite an existing one filter.insert(make_pair(new_value, new_help_str)); } bool AllowOptionsCommand::verify_variables(const ConfigTreeNode& ctn, string& error_msg) const { string value; map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; if (ctn.expand_variable(varname, value, false) != true) { // Error: cannot expand the variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } Filter filter = iter->second; // XXX: create a copy we can manipulate if (filter.find(value) != filter.end()) continue; // This variable is OK // Error: variable value is not allowed string full_varname; if (ctn.expand_variable_to_full_varname(varname, full_varname) != true) { // Error: cannot expand the variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } error_msg = c_format("Value \"%s\" is not a valid value for " "variable \"%s\". ", value.c_str(), full_varname.c_str()); if (filter.size() == 1) { error_msg += c_format("The only value allowed is %s.", filter.begin()->first.c_str()); } else { error_msg += "Allowed values are: "; bool is_first = true; Filter::iterator iter2; while (! filter.empty()) { if (is_first) { is_first = false; } else { if (filter.size() == 1) error_msg += " and "; else error_msg += ", "; } iter2 = filter.begin(); error_msg += iter2->first; filter.erase(iter2); } error_msg += "."; } return (false); } return (true); } string AllowOptionsCommand::str() const { string tmp; tmp = c_format("AllowOptionsCommand:\n"); map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; const Filter& filter = iter->second; tmp += c_format("\tVarname: %s Allowed values:\n", varname.c_str()); Filter::const_iterator iter2; for (iter2 = filter.begin(); iter2 != filter.end(); ++iter2) { tmp += c_format("\t\tvalue: \"%s\"\thelp: \"%s\"\n", iter2->first.c_str(), iter2->second.c_str()); } } return tmp; } // ---------------------------------------------------------------------------- // AllowOperatorsCommand implementation AllowOperatorsCommand::AllowOperatorsCommand(TemplateTreeNode& ttn, const string& cmd_name) : AllowCommand(ttn, cmd_name) { } bool AllowOperatorsCommand::expand_actions(string& error_msg) { UNUSED(error_msg); return (true); // XXX: nothing to do } bool AllowOperatorsCommand::check_referred_variables(string& error_msg) const { UNUSED(error_msg); return (true); // XXX: nothing to do } void AllowOperatorsCommand::add_action(const list& action) throw (ParseError) { string error_msg; size_t min_expected_parameters_n = 1; // // Check the number of parameters // if (action.size() < min_expected_parameters_n) { error_msg = c_format("%%allow-operator command with invalid number of " "parameters: %u (expected at least %u)", XORP_UINT_CAST(action.size()), XORP_UINT_CAST(min_expected_parameters_n)); xorp_throw(ParseError, error_msg); } // // Extract the parameters and add them // list::const_iterator iter; for (iter = action.begin(); iter != action.end(); ++iter) { ConfigOperator op; string op_str = unquote(*iter); try { op = lookup_operator(op_str); } catch (const ParseError& e) { error_msg = c_format("%%allow-operator command with invalid " "operator: %s", op_str.c_str()); xorp_throw(ParseError, error_msg); } if (find(_allowed_operators.begin(), _allowed_operators.end(), op) == _allowed_operators.end()) { _allowed_operators.push_back(op); } } } bool AllowOperatorsCommand::verify_variables(const ConfigTreeNode& ctn, string& error_msg) const { if (ctn.get_operator() == OP_NONE) { error_msg = c_format("Missing operator"); return (false); } string op_str = ctn.show_operator(); return (verify_variable_by_value(ctn, op_str, error_msg)); } bool AllowOperatorsCommand::verify_variable_by_value(const ConfigTreeNode& ctn, const string& value, string& error_msg) const { ConfigOperator op; string op_str = unquote(value); try { op = lookup_operator(op_str); } catch (const ParseError& e) { error_msg = c_format("Invalid operator: %s", op_str.c_str()); return (false); } list::const_iterator iter; for (iter = _allowed_operators.begin(); iter != _allowed_operators.end(); ++iter) { if (op == *iter) return (true); // OK } // Error: variable value is not allowed error_msg = c_format("Operator \"%s\" is not a valid value for node %s. ", value.c_str(), ctn.segname().c_str()); list values = _allowed_operators; if (values.size() == 1) { error_msg += c_format("The only value allowed is %s.", operator_to_str(values.front()).c_str()); } else { error_msg += "Allowed values are: "; error_msg += operator_to_str(values.front()).c_str(); values.pop_front(); bool is_first = true; while (! values.empty()) { if (is_first) { is_first = false; } else { if (values.size() == 1) error_msg += " and "; else error_msg += ", "; } error_msg += operator_to_str(values.front()).c_str(); values.pop_front(); } error_msg += "."; } return (false); } list AllowOperatorsCommand::allowed_operators() const { list result; list::const_iterator iter; for (iter = _allowed_operators.begin(); iter != _allowed_operators.end(); ++iter) { const ConfigOperator& config_operator = *iter; result.push_back(config_operator); } return result; } string AllowOperatorsCommand::str() const { string tmp; tmp = "AllowOperatorsCommand: Allowed values: "; list::const_iterator iter; for (iter = _allowed_operators.begin(); iter != _allowed_operators.end(); ++iter) { tmp += " " + operator_to_str(*iter); } tmp += "\n"; return tmp; } // ---------------------------------------------------------------------------- // AllowRangeCommand implementation AllowRangeCommand::AllowRangeCommand(TemplateTreeNode& ttn, const string& cmd_name) : AllowCommand(ttn, cmd_name) { } bool AllowRangeCommand::expand_actions(string& error_msg) { map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; TemplateTreeNode* ttn; ttn = template_tree_node().find_varname_node(varname); if (ttn == NULL) { // Error: invalid variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } const Filter& filter = iter->second; Filter::const_iterator iter2; for (iter2 = filter.begin(); iter2 != filter.end(); ++iter2) { const pair& pair = iter2->first; int64_t lower_value = pair.first; int64_t upper_value = pair.second; const string& help = iter2->second; ttn->add_allowed_range(lower_value, upper_value, help); } } return (true); } bool AllowRangeCommand::check_referred_variables(string& error_msg) const { map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; const TemplateTreeNode* ttn; ttn = template_tree_node().find_const_varname_node(varname); if (ttn == NULL) { // Error: invalid variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } } return (true); } void AllowRangeCommand::add_action(const list& action) throw (ParseError) { string error_msg; string new_varname, new_lower_str, new_upper_str; string new_help_keyword, new_help_str; size_t expected_parameters_n = 5; list unparsed_action = action; int64_t new_lower_value, new_upper_value; // // Check the number of parameters // if (action.size() != expected_parameters_n) { error_msg = c_format("%%allow-range command with invalid number of " "parameters: %u (expected %u)", XORP_UINT_CAST(action.size()), XORP_UINT_CAST(expected_parameters_n)); xorp_throw(ParseError, error_msg); } // // Extract each parameter // new_varname = unparsed_action.front(); unparsed_action.pop_front(); new_lower_str = unquote(unparsed_action.front()); unparsed_action.pop_front(); new_upper_str = unquote(unparsed_action.front()); unparsed_action.pop_front(); new_help_keyword = unparsed_action.front(); unparsed_action.pop_front(); new_help_str = unquote(unparsed_action.front()); unparsed_action.pop_front(); // // Verify all parameters // if (new_help_keyword != "%help") { error_msg = c_format("Invalid %%allow-range argument: %s " "(expected \"%%help:\")", new_help_keyword.c_str()); xorp_throw(ParseError, error_msg); } // // Insert the new entry // map::iterator iter; iter = _filters.find(new_varname); if (iter == _filters.end()) { // Insert a new map Filter new_filter; _filters.insert(make_pair(new_varname, new_filter)); iter = _filters.find(new_varname); } XLOG_ASSERT(iter != _filters.end()); Filter& filter = iter->second; new_lower_value = strtoll(new_lower_str.c_str(), (char **)NULL, 10); new_upper_value = strtoll(new_upper_str.c_str(), (char **)NULL, 10); if (new_lower_value > new_upper_value) swap(new_lower_value, new_upper_value); pair new_range(new_lower_value, new_upper_value); // XXX: insert the new pair even if we overwrite an existing one filter.insert(make_pair(new_range, new_help_str)); } bool AllowRangeCommand::verify_variables(const ConfigTreeNode& ctn, string& error_msg) const { string value; map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; if (ctn.expand_variable(varname, value, false) != true) { // Error: cannot expand the variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } Filter filter = iter->second; // XXX: create a copy we can manipulate bool is_accepted = true; Filter::iterator iter2; int64_t ival = strtoll(value.c_str(), (char **)NULL, 10); int64_t lower_value = 0; int64_t upper_value = 0; if (! filter.empty()) is_accepted = false; // // XXX: it is sufficient for the variable's value to belong to any // of the allowed ranges. // for (iter2 = filter.begin(); iter2 != filter.end(); ++iter2) { const pair& range = iter2->first; lower_value = range.first; upper_value = range.second; if ((ival >= lower_value) && (ival <= upper_value)) { is_accepted = true; break; } } if (is_accepted) continue; // This variable is OK // Error: variable value is not allowed string full_varname; if (ctn.expand_variable_to_full_varname(varname, full_varname) != true) { // Error: cannot expand the variable error_msg = c_format("Variable \"%s\" is not defined.", varname.c_str()); return (false); } error_msg = c_format("Value \"%s\" is not a valid value for " "variable \"%s\". ", value.c_str(), full_varname.c_str()); if (filter.size() == 1) { const pair& range = filter.begin()->first; error_msg += c_format("The only range allowed is "); ostringstream ost; ost << "[" << range.first << ".." << range.second << "]"; error_msg += c_format("%s.", ost.str().c_str()); } else { error_msg += "Allowed ranges are: "; bool is_first = true; while (! filter.empty()) { if (is_first) { is_first = false; } else { if (filter.size() == 1) error_msg += " and "; else error_msg += ", "; } map, string>::iterator iter2; iter2 = filter.begin(); const pair& range = iter2->first; ostringstream ost; ost << "[" << range.first << ".." << range.second << "]"; error_msg += c_format("%s.", ost.str().c_str()); filter.erase(iter2); } error_msg += "."; } return (false); } return (true); } string AllowRangeCommand::str() const { string tmp; tmp = c_format("AllowRangeCommand:\n"); map::const_iterator iter; for (iter = _filters.begin(); iter != _filters.end(); ++iter) { const string& varname = iter->first; const Filter& filter = iter->second; tmp += c_format("\tVarname: %s Allowed ranges:\n", varname.c_str()); Filter::const_iterator iter2; for (iter2 = filter.begin(); iter2 != filter.end(); ++iter2) { const pair& range = iter2->first; ostringstream ost; ost << "[" << range.first << ".." << range.second << "]"; tmp += c_format("\t\trange: %s\thelp: \"%s\"\n", ost.str().c_str(), iter2->second.c_str()); } } return (tmp); }