// -*- 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_commands.cc,v 1.69 2007/02/16 22:47:25 pavlin Exp $"
#include <list>
#include "rtrmgr_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/utils.hh"
#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 "xrldb.hh"
#include "libxipc/xrl_atom_encoding.hh"
Action::Action(TemplateTreeNode& template_tree_node,
const list<string>& action)
: _action(action),
_template_tree_node(template_tree_node)
{
string cur("\n");
enum char_type { VAR, NON_VAR, QUOTE };
char_type mode = NON_VAR;
list<string>::const_iterator iter;
//
// We need to split the command into variable names and the rest so
// that when we later expand the variables, we can do them quickly
// and with no risk of accidentally doing multiple expansions.
//
iter = action.begin();
while (iter != action.end()) {
string word = unquote(*iter);
char c;
for (size_t i = 0; i < word.length(); i++) {
c = word[i];
switch (mode) {
case VAR:
if (word[i] == ')') {
mode = NON_VAR;
cur += ')';
_split_cmd.push_back(cur);
cur = "";
break;
}
cur += word[i];
break;
case NON_VAR:
if (word[i] == '$') {
mode = VAR;
if (cur != "")
_split_cmd.push_back(cur);
cur = c;
break;
}
if (word[i] == '`') {
mode = QUOTE;
if (cur != "")
_split_cmd.push_back(cur);
cur = c;
break;
}
cur += word[i];
break;
case QUOTE:
if (word[i] == '`') {
mode = NON_VAR;
cur += '`';
_split_cmd.push_back(cur);
cur = "";
break;
}
cur += word[i];
break;
}
}
if ((cur != "") && (cur != "\n"))
_split_cmd.push_back(cur);
cur = "\n";
++iter;
}
}
string
Action::str() const
{
string str;
list<string>::const_iterator iter;
for (iter = _split_cmd.begin(); iter != _split_cmd.end(); ++iter) {
const string& cmd = *iter;
if (str == "") {
str = cmd.substr(1, cmd.size() - 1);
} else {
if (cmd[0] == '\n')
str += " " + cmd.substr(1, cmd.size() - 1);
else
str += cmd;
}
}
return str;
}
bool
Action::expand_action(string& error_msg)
{
// XXX: nothing to do in the default case
UNUSED(error_msg);
return (true);
}
bool
Action::check_referred_variables(string& error_msg) const
{
list<string>::const_iterator iter;
for (iter = _referred_variables.begin();
iter != _referred_variables.end();
++iter) {
const string& varname = *iter;
if (_template_tree_node.find_varname_node(varname) == NULL) {
error_msg = c_format("Syntax error in action %s: "
"invalid variable name %s",
str().c_str(), varname.c_str());
return false;
}
}
return true;
}
/***********************************************************************/
XrlAction::XrlAction(TemplateTreeNode& template_tree_node,
const list<string>& action, const XRLdb& xrldb)
throw (ParseError)
: Action(template_tree_node, action),
_xrldb(xrldb)
{
list<string> xrl_parts = _split_cmd;
debug_msg("XrlAction constructor\n");
XLOG_ASSERT(action.front() == "xrl");
// Print debug info
{
list<string>::const_iterator si;
int j = 0;
UNUSED(j);
for (si = xrl_parts.begin(); si != xrl_parts.end(); ++si)
debug_msg("Seg %d: >%s< \n", j++, si->c_str());
debug_msg("\n");
}
// Trim off the "xrl" command part
xrl_parts.pop_front();
if (xrl_parts.empty())
xorp_throw(ParseError, "bad XrlAction syntax");
bool request_done = false;
size_t seg_count = 0;
while (xrl_parts.empty() == false) {
if (xrl_parts.front().size() == 0) {
xrl_parts.pop_front();
continue;
}
string segment = xrl_parts.front();
debug_msg("segment: %s\n", segment.c_str());
string orig_segment = segment;
if (orig_segment[0] == '\n') {
// Strip the magic "\n" off
if (seg_count == 0)
segment = segment.substr(1, segment.size() - 1);
else
segment = " " + segment.substr(1, segment.size() - 1);
}
string::size_type start = segment.find("->");
debug_msg("start=%u\n", XORP_UINT_CAST(start));
if (start != string::npos) {
debug_msg("found return spec\n");
string::size_type orig_start = orig_segment.find("->");
if (request_done)
xorp_throw(ParseError, "Two responses in one XRL");
request_done = true;
_request += segment.substr(0, start);
if (orig_start != 0)
_split_request.push_back(orig_segment.substr(0, orig_start));
segment = segment.substr(start + 2, segment.size() - (start + 2));
orig_segment = orig_segment.substr(orig_start + 2,
orig_segment.size()
- (orig_start + 2));
}
if (request_done) {
_response += segment;
if (!orig_segment.empty())
_split_response.push_back(orig_segment);
} else {
_request += segment;
if (!orig_segment.empty())
_split_request.push_back(orig_segment);
}
xrl_parts.pop_front();
seg_count++;
}
// Print debug output
{
list<string>::const_iterator iter;
debug_msg("XrlAction:\n");
debug_msg("Request: >%s<\n", _request.c_str());
for (iter = _split_request.begin();
iter != _split_request.end();
++iter) {
debug_msg(">%s< ", (*iter).c_str());
}
debug_msg("\n");
debug_msg("Response: >%s<\n", _response.c_str());
for (iter = _split_response.begin();
iter != _split_response.end();
++iter) {
debug_msg(">%s< ", (*iter).c_str());
}
debug_msg("\n");
}
}
bool
XrlAction::expand_action(string& error_msg)
{
debug_msg("XrlAction::expand_action()\n");
XLOG_ASSERT(_action.front() == "xrl");
_module_name = _template_tree_node.module_name();
if (_module_name.empty()) {
error_msg = c_format("Empty module name for action in template %s",
_template_tree_node.path().c_str());
return false;
}
if (check_xrl_is_valid(_action, _xrldb, error_msg) != true)
return false;
return true;
}
bool
XrlAction::check_xrl_is_valid(const list<string>& action, const XRLdb& xrldb,
string& error_msg)
{
const string module_name = template_tree_node().module_name();
XLOG_ASSERT(action.front() == "xrl");
list<string>::const_iterator xrl_pos = ++action.begin();
if (xrl_pos == action.end()) {
error_msg = c_format("Expected XRL in module %s but none supplied",
module_name.c_str());
return false;
}
const string& xrl_str = *xrl_pos;
debug_msg("checking XRL: %s\n", xrl_str.c_str());
//
// We need to go through the XRL template, and remove the "=$(VARNAME)"
// instances to produce a generic version of the XRL.
// Then we can check it is a valid XRL as known by the XRLdb.
//
string cleaned_xrl;
// Trim quotes from around the XRL
size_t start = 0;
size_t stop = xrl_str.length();
if (xrl_str[start] == '"' && xrl_str[stop - 1] == '"') {
start++;
stop--;
}
//
// If the target name is a variable, then replace the target name
// with the default target name (for verification purpose only).
//
if (xrl_str[start] == '$') {
// Find the target name variable
string::size_type target_name_end = xrl_str.find("/", start);
if (target_name_end == string::npos) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"no target name",
module_name.c_str(), xrl_str.c_str());
return false;
}
string target_name_var = xrl_str.substr(start, target_name_end - 1);
if (target_name_var.empty()) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"empty XRL target",
module_name.c_str(), xrl_str.c_str());
return false;
}
// Find the default target name
string default_target_name;
default_target_name = template_tree_node().get_default_target_name_by_variable(target_name_var);
if (default_target_name.empty()) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"the module has no default target name",
module_name.c_str(), xrl_str.c_str());
return false;
}
// Add the default target name to the cleaned XRL
cleaned_xrl += default_target_name;
// Advance the start pointer to point to the first symbol after
// the target name, which should be '/'.
while (xrl_str[start] != '/') {
start++;
XLOG_ASSERT(start < stop);
}
}
debug_msg("XrlAction before cleaning:\n%s\n", xrl_str.c_str());
//
// Copy the XRL, omitting the "=$(VARNAME)" parts.
// In the mean time, build the list of encountered "$(VARNAME)" variables.
//
list<ActionCharType> mode_stack;
mode_stack.push_front(NON_VAR);
for (size_t i = start; i < stop; i++) {
switch (mode_stack.front()) {
case VAR:
if (xrl_str[i] == '$' || xrl_str[i] == '`') {
error_msg = c_format("Syntax error in module %s XRL %s: "
"bad variable definition",
module_name.c_str(), xrl_str.c_str());
return false;
}
if (xrl_str[i] == ')') {
mode_stack.pop_front();
}
break;
case NON_VAR:
if (xrl_str[i] == '=') {
mode_stack.push_front(ASSIGN);
break;
}
cleaned_xrl += xrl_str[i];
// FALLTHROUGH
case QUOTE:
if (xrl_str[i] == '`') {
mode_stack.pop_front();
}
break;
case ASSIGN:
if (xrl_str[i] == '$') {
//
// Get the variable name and add it to the list of referred
// variables.
//
{
string varname;
bool varname_end_found = false;
for (size_t j = i; j < stop; j++) {
varname += xrl_str[j];
if (xrl_str[j] == ')') {
varname_end_found = true;
break;
}
}
if (! varname_end_found) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"bad variable syntax",
module_name.c_str(),
xrl_str.c_str());
return false;
}
list<string>::const_iterator iter;
iter = find(_referred_variables.begin(),
_referred_variables.end(),
varname);
if (iter == _referred_variables.end()) {
_referred_variables.push_back(varname);
}
}
mode_stack.push_front(VAR);
break;
}
if (xrl_str[i] == '`') {
mode_stack.push_front(QUOTE);
break;
}
if (xrl_str[i] == '&') {
mode_stack.pop_front();
if (mode_stack.front() != NON_VAR) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"invalid XRL syntax",
module_name.c_str(), xrl_str.c_str());
return false;
}
cleaned_xrl += xrl_str[i];
break;
}
if ((xrl_str.size() > i)
&& (xrl_str[i] == '-') && (xrl_str[i+1] == '>')) {
/* it's the start of the return spec */
cleaned_xrl += xrl_str[i];
mode_stack.pop_front();
if (mode_stack.front() != NON_VAR) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"invalid XRL syntax",
module_name.c_str(), xrl_str.c_str());
return false;
}
}
break;
}
}
debug_msg("XrlAction after cleaning:\n%s\n", cleaned_xrl.c_str());
if (xrldb.check_xrl_syntax(cleaned_xrl) == false) {
error_msg = c_format("Syntax error in module %s XRL %s: "
"invalid XRL syntax",
module_name.c_str(), cleaned_xrl.c_str());
return false;
}
XRLMatchType match = xrldb.check_xrl_exists(cleaned_xrl);
switch (match) {
case MATCH_FAIL:
case MATCH_RSPEC: {
error_msg = c_format("Error in module %s XRL %s: "
"the XRL is not specified in the XRL targets "
"directory",
module_name.c_str(), cleaned_xrl.c_str());
return false;
}
case MATCH_XRL: {
error_msg = c_format("Error in module %s XRL %s: "
"the XRL has different return specification from "
"that in the XRL targets directory",
module_name.c_str(), cleaned_xrl.c_str());
return false;
}
case MATCH_ALL:
break;
}
return true;
}
int
XrlAction::execute(const MasterConfigTreeNode& ctn,
TaskManager& task_manager,
XrlRouter::XrlCallback cb) const
{
list<string> expanded_cmd;
list<string>::const_iterator iter;
string word;
//
// First, go back through and merge all the separate words in the
// command back together.
//
for (iter = _split_cmd.begin(); iter != _split_cmd.end(); ++iter) {
string segment = *iter;
// "\n" at start of segment indicates start of a word
if (segment[0] == '\n') {
// Store the previous word
if (word != "") {
expanded_cmd.push_back(word);
word = "";
}
// Strip the magic "\n" off
segment = segment.substr(1, segment.size() - 1);
}
word += segment;
}
// Store the last word
if (word != "")
expanded_cmd.push_back(word);
// Go through the expanded version and copy to an array
vector<string> args;
for (iter = expanded_cmd.begin(); iter != expanded_cmd.end(); ++iter) {
args.push_back(*iter);
}
if (args.empty())
return (XORP_ERROR);
// Now we're ready to begin...
int result;
if (args[0] == "xrl") {
if (args.size() < 2) {
string err = c_format("XRL command is missing the XRL on node %s",
ctn.path().c_str());
XLOG_WARNING("%s", err.c_str());
}
string xrl_str = unquote(args[1]);
debug_msg("CALL XRL: %s\n", xrl_str.c_str());
UnexpandedXrl uxrl(ctn, *this);
task_manager.add_xrl(related_module(), uxrl, cb);
result = XORP_OK;
debug_msg("result = %d\n", result);
} else {
XLOG_ERROR("Bad command: %s\n", args[0].c_str());
return XORP_ERROR;
}
return result;
}
template<class TreeNode>
Xrl*
XrlAction::expand_xrl_variables(const TreeNode& tn,
string& error_msg) const
{
string word;
string expanded_var;
string command;
list<string> args;
debug_msg("expand_xrl_variables() node %s XRL %s\n",
tn.segname().c_str(), _request.c_str());
/* split the request into command and separate args */
bool escaped = false;
for (size_t i = 0; i < _request.size(); i++) {
if (escaped == false) {
if (_request[i] == '\\') {
escaped = true;
word += '\\';
} else if (_request[i] == '?') {
if (command == "") {
command = word;
word = "";
} else {
error_msg = c_format("unescaped '?' in XRL args \"%s\" "
"associated with node \"%s\"",
_request.c_str(), tn.path().c_str());
return NULL;
}
} else if (_request[i] == '&') {
args.push_back(word);
word = "";
} else {
word += _request[i];
}
} else {
word += _request[i];
}
}
if (word != "") {
if (command.empty()) {
command = word;
} else {
args.push_back(word);
}
}
debug_msg("target/command part: %s\n", command.c_str());
list <string>::const_iterator i2;
for (i2 = args.begin(); i2 != args.end(); ++i2) {
debug_msg("arg part: %s\n", (*i2).c_str());
}
string expanded_command;
if (!expand_vars(tn, command, expanded_command)) {
error_msg = expanded_command;
return NULL;
}
debug_msg("expanded target/command part: %s\n", expanded_command.c_str());
// find the command name
list <string> cmd_parts = split(expanded_command, '/');
if (cmd_parts.size() < 2) {
error_msg = c_format("bad XRL \"%s\" "
"associated with node \"%s\"",
_request.c_str(), tn.path().c_str());
return NULL;
}
command = cmd_parts.back();
cmd_parts.pop_back();
// put the target name back together again
string target;
while (!cmd_parts.empty()) {
if (!target.empty()) {
target += '/';
}
target += cmd_parts.front();
cmd_parts.pop_front();
}
// now process the args.
XrlArgs xrl_args;
list<string>::const_iterator iter;
for (iter = args.begin(); iter != args.end(); ++iter) {
// split each arg into argname, argtype and argvalue
string arg = *iter;
string name, type, value;
for (size_t i = 0; i < arg.size(); i++) {
if (arg[i] == ':') {
name = strip_empty_spaces(arg.substr(0,i));
arg = arg.substr(i + 1, arg.size() - (i + 1));
break;
}
}
for (size_t i = 0; i < arg.size(); i++) {
if (arg[i] == '=') {
type = strip_empty_spaces(arg.substr(0,i));
value = arg.substr(i + 1, arg.size() - (i + 1));
break;
}
}
// check that this is a legal XrlAtom type
// it really shouldn't be possible for this to fail given
// earlier checks
XrlAtomType arg_type = XrlAtom::lookup_type(type.c_str());
if (arg_type == xrlatom_no_type) {
error_msg = c_format("bad XRL syntax \"%s\" "
"associated with node \"%s\"",
_request.c_str(), tn.path().c_str());
return NULL;
}
string expanded_value;
if (!expand_vars(tn, value, expanded_value)) {
error_msg = expanded_value;
return NULL;
}
// At this point we've expanded all the variables.
// Now it's time to build an XrlAtom
try {
debug_msg("Atom: %s\n", expanded_value.c_str());
XrlAtom atom(name, arg_type,
xrlatom_encode_value(expanded_value.c_str(),
expanded_value.size()));
xrl_args.add(atom);
} catch (InvalidString) {
error_msg = c_format("Bad xrl arg \"%s\" "
"associated with node \"%s\"",
name.c_str(), tn.path().c_str());
return NULL;
}
}
// Now we've got a arg list. Time to build an Xrl
Xrl* xrl = new Xrl(target, command, xrl_args);
debug_msg("Xrl expanded to %s\n", xrl->str().c_str());
return xrl;
}
template<class TreeNode>
bool
XrlAction::expand_vars(const TreeNode& tn,
const string& value, string& result) const {
debug_msg("expand_vars: %s\n", value.c_str());
bool escaped = false;
string varname;
for (size_t i = 0; i < value.size(); i++) {
if (escaped) {
escaped = false;
result += value[i];
} else if (value[i] == '\\') {
if (varname.empty()) {
result += '\\';
escaped = true;
} else {
// can't escape in a varname
result = c_format("bad varname \"%s\" "
"associated with node \"%s\"",
_request.c_str(), tn.path().c_str());
return false;
}
} else if ((i != 0) && (! varname.empty())
&& varname[0] == '$' && value[i] == ')') {
varname += ')';
// expand variable
string expanded_var;
debug_msg("expanding varname: %s\n", varname.c_str());
bool expand_done = tn.expand_variable(varname, expanded_var,
false);
if (expand_done) {
debug_msg("expanded to: %s\n", expanded_var.c_str());
// expanded_var = xrlatom_encode_value(expanded_var);
result += unquote(expanded_var);
} else {
// Error
result = c_format("failed to expand variable \"%s\" "
"associated with node \"%s\"",
varname.c_str(), tn.segname().c_str());
return false;
}
varname = "";
} else if ((i != 0) && (! varname.empty())
&& varname[0] == '`' && value[i] == '`' ) {
varname += '`';
// expand expression
string expanded_var;
bool expand_done = tn.expand_expression(varname, expanded_var);
if (expand_done) {
// expanded_var = xrlatom_encode_value(expanded_var);
result += unquote(expanded_var);
} else {
// Error
result = c_format("failed to expand expression \"%s\" "
"associated with node \"%s\"",
varname.c_str(), tn.path().c_str());
return false;
}
varname = "";
} else if (value[i] == '$') {
varname += '$';
} else if (value[i] == '`') {
varname += '`';
} else if (varname.empty()) {
// we're not building up a varname
result += value[i];
} else {
varname += value[i];
}
}
return true;
}
string
XrlAction::related_module() const
{
return (_module_name);
}
string
XrlAction::affected_module() const
{
string::size_type end = _request.find("/");
if (end == string::npos) {
XLOG_WARNING("Failed to find XRL target in XrlAction");
return "";
}
string target_name = _request.substr(0, end);
if (target_name.empty()) {
XLOG_WARNING("Empty XRL target in XrlAction");
return "";
}
if (target_name[0] == '$') {
//
// This is a variable that needs to be expanded to get the
// real target name. We use this variable name to get the module name.
//
return (template_tree_node().get_module_name_by_variable(target_name));
}
//
// XXX: the target name is hardcoded in the XRL, hence we assume that
// the target name is same as the module name.
//
return target_name;
}
/***********************************************************************/
ProgramAction::ProgramAction(TemplateTreeNode& template_tree_node,
const list<string>& action) throw (ParseError)
: Action(template_tree_node, action)
{
list<string> program_parts = _split_cmd;
debug_msg("ProgramAction constructor\n");
XLOG_ASSERT(action.front() == "program");
// Print debug info
{
list<string>::const_iterator si;
int j = 0;
UNUSED(j);
for (si = program_parts.begin(); si != program_parts.end(); ++si)
debug_msg("Seg %d: >%s< \n", j++, si->c_str());
debug_msg("\n");
}
// Trim off the "program" command part
program_parts.pop_front();
if (program_parts.empty())
xorp_throw(ParseError, "bad ProgramAction syntax");
bool request_done = false;
size_t seg_count = 0;
while (program_parts.empty() == false) {
if (program_parts.front().size() == 0) {
program_parts.pop_front();
continue;
}
string segment = program_parts.front();
debug_msg("segment: %s\n", segment.c_str());
string orig_segment = segment;
if (orig_segment[0] == '\n') {
// Strip the magic "\n" off
if (seg_count == 0)
segment = segment.substr(1, segment.size() - 1);
else
segment = " " + segment.substr(1, segment.size() - 1);
}
string::size_type start = segment.find("->");
debug_msg("start=%u\n", XORP_UINT_CAST(start));
if (start != string::npos) {
debug_msg("found return spec\n");
string::size_type orig_start = orig_segment.find("->");
if (request_done)
xorp_throw(ParseError, "Two responses in one program");
request_done = true;
_request += segment.substr(0, start);
if (orig_start != 0)
_split_request.push_back(orig_segment.substr(0, orig_start));
segment = segment.substr(start + 2, segment.size() - (start + 2));
orig_segment = orig_segment.substr(orig_start + 2,
orig_segment.size()
- (orig_start + 2));
}
if (request_done) {
_response += segment;
if (!orig_segment.empty())
_split_response.push_back(orig_segment);
} else {
_request += segment;
if (!orig_segment.empty())
_split_request.push_back(orig_segment);
}
program_parts.pop_front();
seg_count++;
}
//
// Find the variable names that will contain the stdout and the stderr
// of the program.
//
if (! _response.empty()) {
string part1, part2;
string::size_type pos = _response.find("&");
part1 = _response.substr(0, pos);
if (pos != string::npos)
part2 = _response.substr(pos + 1);
part1 = strip_empty_spaces(part1);
part2 = strip_empty_spaces(part2);
if (part2.find("&") != string::npos) {
xorp_throw(ParseError,
"Too many components in the program response");
}
parse_program_response(part1);
parse_program_response(part2);
}
// Print debug output
{
list<string>::const_iterator iter;
debug_msg("ProgramAction:\n");
debug_msg("Request: >%s<\n", _request.c_str());
for (iter = _split_request.begin();
iter != _split_request.end();
++iter) {
debug_msg(">%s< ", (*iter).c_str());
}
debug_msg("\n");
debug_msg("Response: >%s<\n", _response.c_str());
for (iter = _split_response.begin();
iter != _split_response.end();
++iter) {
debug_msg(">%s< ", (*iter).c_str());
}
debug_msg("\n");
}
}
bool
ProgramAction::expand_action(string& error_msg)
{
debug_msg("ProgramAction::expand_action()\n");
XLOG_ASSERT(_action.front() == "program");
_module_name = _template_tree_node.module_name();
if (_module_name.empty()) {
error_msg = c_format("Empty module name for action in template %s",
_template_tree_node.path().c_str());
return false;
}
if (check_program_is_valid(_action, error_msg) != true)
return false;
return true;
}
void
ProgramAction::parse_program_response(const string& part) throw (ParseError)
{
string::size_type pos;
if (part.empty())
return;
pos = part.find("=");
if (pos == string::npos) {
xorp_throw(ParseError,
"Missing '=' in program response specification");
}
string l, r;
l = part.substr(0, pos);
r = part.substr(pos + 1);
if ((l != "stdout") && (l != "stderr")) {
string error_msg = c_format("Unrecognized keyword in program "
"response specification: %s", l.c_str());
xorp_throw(ParseError, error_msg);
}
if (l == "stdout") {
if (! _stdout_variable_name.empty()) {
xorp_throw(ParseError,
"Repeated \"stdout\" keyword in program response "
"specification");
}
_stdout_variable_name = r;
}
if (l == "stderr") {
if (! _stderr_variable_name.empty()) {
xorp_throw(ParseError,
"Repeated \"stderr\" keyword in program response "
"specification");
}
_stderr_variable_name = r;
}
}
bool
ProgramAction::check_program_is_valid(const list<string>& action,
string& error_msg)
{
XLOG_ASSERT(action.front() == "program");
list<string>::const_iterator program_pos = ++action.begin();
if (program_pos == action.end()) {
error_msg = "Expected program but none supplied";
return false;
}
const string& program_str = *program_pos;
debug_msg("checking program: %s\n", program_str.c_str());
//
// We need to go through the program template and perform some basic
// validations.
// Note that we cannot verify that the program is valid and exists,
// because it may contain variable names and we don't know the values
// of those variables yet.
//
string cleaned_program;
// Trim quotes from around the program
size_t start = 0;
size_t stop = program_str.length();
if (program_str[start] == '"' && program_str[stop - 1] == '"') {
start++;
stop--;
}
debug_msg("ProgramAction before cleaning:\n%s\n", program_str.c_str());
//
// Copy the program and perform some basic validations.
// In the mean time, build the list of encountered "$(VARNAME)" variables.
//
list<ActionCharType> mode_stack;
mode_stack.push_front(NON_VAR);
for (size_t i = start; i < stop; i++) {
switch (mode_stack.front()) {
case VAR:
if (program_str[i] == '$' || program_str[i] == '`') {
error_msg = c_format("Syntax error in program %s: "
"bad variable definition",
program_str.c_str());
return false;
}
if (program_str[i] == ')') {
mode_stack.pop_front();
}
break;
case NON_VAR:
if (program_str[i] == '$') {
//
// Get the variable name and add it to the list of referred
// variables.
//
{
string varname;
bool varname_end_found = false;
for (size_t j = i; j < stop; j++) {
varname += program_str[j];
if (program_str[j] == ')') {
varname_end_found = true;
break;
}
}
if (! varname_end_found) {
error_msg = c_format("Syntax error in program %s: "
"bad variable syntax",
program_str.c_str());
return false;
}
list<string>::const_iterator iter;
iter = find(_referred_variables.begin(),
_referred_variables.end(),
varname);
if (iter == _referred_variables.end()) {
_referred_variables.push_back(varname);
}
}
mode_stack.push_front(VAR);
break;
}
if (program_str[i] == '`') {
mode_stack.push_front(QUOTE);
break;
}
cleaned_program += program_str[i];
break;
case QUOTE:
if (program_str[i] == '`') {
mode_stack.pop_front();
}
break;
case ASSIGN:
// XXX: not used
XLOG_UNREACHABLE();
break;
}
}
debug_msg("ProgramAction after cleaning:\n%s\n", cleaned_program.c_str());
if (cleaned_program.empty()) {
error_msg = c_format("Syntax error in program specification %s: "
"empty program",
program_str.c_str());
return false;
}
return true;
}
int
ProgramAction::execute(const MasterConfigTreeNode& ctn,
TaskManager& task_manager,
TaskProgramItem::ProgramCallback program_cb) const
{
list<string> expanded_cmd;
list<string>::const_iterator iter;
string word;
//
// First, go back through and merge all the separate words in the
// command back together.
//
for (iter = _split_cmd.begin(); iter != _split_cmd.end(); ++iter) {
string segment = *iter;
// "\n" at start of segment indicates start of a word
if (segment[0] == '\n') {
// Store the previous word
if (word != "") {
expanded_cmd.push_back(word);
word = "";
}
// Strip the magic "\n" off
segment = segment.substr(1, segment.size() - 1);
}
word += segment;
}
// Store the last word
if (word != "")
expanded_cmd.push_back(word);
// Go through the expanded version and copy to an array
vector<string> args;
for (iter = expanded_cmd.begin(); iter != expanded_cmd.end(); ++iter) {
args.push_back(*iter);
}
if (args.empty())
return (XORP_ERROR);
// Now we're ready to begin...
int result;
if (args[0] == "program") {
if (args.size() < 2) {
string err = c_format("Program command is missing the program "
"on node %s",
ctn.path().c_str());
XLOG_WARNING("%s", err.c_str());
}
string program_str = unquote(args[1]);
debug_msg("CALL program: %s\n", program_str.c_str());
UnexpandedProgram uprogram(ctn, *this);
task_manager.add_program(related_module(), uprogram, program_cb);
result = XORP_OK;
debug_msg("result = %d\n", result);
} else {
XLOG_ERROR("Bad command: %s\n", args[0].c_str());
return XORP_ERROR;
}
return result;
}
template<class TreeNode>
int
ProgramAction::expand_program_variables(const TreeNode& tn,
string& result,
string& error_msg) const
{
string word;
string expanded_var;
list<string> expanded_cmd;
debug_msg("expand_program_variables() node %s program %s\n",
tn.segname().c_str(), _request.c_str());
//
// Go through the split command, doing variable substitution
// put split words back together, and remove special "\n" characters
//
list<string>::const_iterator iter = _split_request.begin();
while (iter != _split_request.end()) {
string segment = *iter;
// "\n" at start of segment indicates start of a word
if (segment[0] == '\n') {
// Store the previous word
if (word != "") {
expanded_cmd.push_back(word);
word = "";
}
// Strip the magic "\n" off
segment = segment.substr(1, segment.size() - 1);
if (segment.empty()) {
++iter;
continue;
}
}
// Do variable expansion
bool expand_done = false;
if (segment[0] == '`') {
expand_done = tn.expand_expression(segment, expanded_var);
if (expand_done) {
word += unquote(expanded_var);
} else {
// Error
error_msg = c_format("failed to expand expression \"%s\" "
"associated with node \"%s\"",
segment.c_str(), tn.path().c_str());
return (XORP_ERROR);
}
} else if (segment[0] == '$') {
expand_done = tn.expand_variable(segment, expanded_var, false);
if (expand_done) {
word += unquote(expanded_var);
} else {
// Error
error_msg = c_format("failed to expand variable \"%s\" "
"associated with node \"%s\"",
segment.c_str(), tn.segname().c_str());
return (XORP_ERROR);
}
} else {
word += segment;
}
++iter;
}
// Store the last word
if (word != "")
expanded_cmd.push_back(word);
XLOG_ASSERT(expanded_cmd.size() >= 1);
result = unquote(expanded_cmd.front());
return (XORP_OK);
}
string
ProgramAction::related_module() const
{
return (_module_name);
}
string
ProgramAction::affected_module() const
{
return (_module_name);
}
/***********************************************************************/
Command::Command(TemplateTreeNode& template_tree_node, const string& cmd_name)
: BaseCommand(template_tree_node, cmd_name)
{
debug_msg("Command constructor: %s\n", cmd_name.c_str());
}
Command::~Command()
{
list<Action*>::const_iterator iter;
for (iter = _actions.begin(); iter != _actions.end(); ++iter) {
delete *iter;
}
}
void
Command::add_action(const list<string>& action, const XRLdb& xrldb)
throw (ParseError)
{
string action_type;
string error_msg;
if (action.empty())
return; // XXX: no action to perform
action_type = action.front();
if (action_type == "xrl") {
_actions.push_back(new XrlAction(_template_tree_node, action, xrldb));
return;
}
if (action_type == "program") {
_actions.push_back(new ProgramAction(_template_tree_node, action));
return;
}
// Unknown action
error_msg = c_format("Unknown action \"%s\". Expected actions: "
"\"%s\", \"%s\".",
action_type.c_str(), "xrl", "program");
xorp_throw(ParseError, error_msg);
}
int
Command::execute(MasterConfigTreeNode& ctn, TaskManager& task_manager) const
{
int result = 0;
int actions = 0;
list<Action*>::const_iterator iter;
for (iter = _actions.begin(); iter != _actions.end(); ++iter) {
do {
const XrlAction* xa = dynamic_cast<const XrlAction*>(*iter);
if (xa != NULL) {
result = xa->execute(
ctn, task_manager,
callback(this, &Command::xrl_action_complete, &ctn, *iter));
break;
}
const ProgramAction* pa = dynamic_cast<const ProgramAction*>(*iter);
if (pa != NULL) {
result = pa->execute(
ctn, task_manager,
callback(this, &Command::program_action_complete, &ctn,
pa->stdout_variable_name(),
pa->stderr_variable_name()));
break;
}
// Unrecognized command
XLOG_FATAL("Execute on unimplemented action type on node %s",
ctn.str().c_str());
break;
} while (false);
if (result < 0) {
debug_msg("command execute returning %d\n", result);
// XXX: how do we communicate this error back up
// return result;
}
actions++;
}
debug_msg("command execute returning XORP_OK\n");
return actions;
}
void
Command::xrl_action_complete(const XrlError& err,
XrlArgs* xrl_args,
MasterConfigTreeNode* ctn,
Action* action) const
{
debug_msg("Command::xrl_action_complete\n");
if (err != XrlError::OKAY()) {
ctn->command_status_callback(this, false);
return;
}
if (process_xrl_action_return_arguments(xrl_args, ctn, action) != true) {
ctn->command_status_callback(this, false);
return;
}
ctn->command_status_callback(this, true);
}
bool
Command::process_xrl_action_return_arguments(XrlArgs* xrl_args,
MasterConfigTreeNode *ctn,
Action* action) const
{
if ((xrl_args == NULL) || xrl_args->empty())
return (true);
//
// Handle the XRL arguments
//
debug_msg("ARGS: %s\n", xrl_args->str().c_str());
// Create a list with the return arguments
list<string> spec_args;
XrlAction* xa = dynamic_cast<XrlAction*>(action);
XLOG_ASSERT(xa != NULL);
string s = xa->xrl_return_spec();
while (true) {
string::size_type start = s.find("&");
if (start == string::npos) {
spec_args.push_back(s);
break;
}
spec_args.push_back(s.substr(0, start));
debug_msg("spec_args: %s\n", s.substr(0, start).c_str());
s = s.substr(start + 1, s.size() - (start + 1));
}
list<string>::const_iterator iter;
for (iter = spec_args.begin(); iter != spec_args.end(); ++iter) {
string::size_type eq = iter->find("=");
if (eq == string::npos)
continue;
XrlAtom atom(iter->substr(0, eq).c_str());
debug_msg("atom name=%s\n", atom.name().c_str());
string varname = iter->substr(eq + 1, iter->size() - (eq + 1));
debug_msg("varname=%s\n", varname.c_str());
XrlAtom returned_atom;
try {
returned_atom = xrl_args->item(atom.name());
} catch (const XrlArgs::XrlAtomNotFound& x) {
// TODO: XXX: IMPLEMENT IT!!
XLOG_UNFINISHED();
}
string value = returned_atom.value();
debug_msg("found atom = %s\n", returned_atom.str().c_str());
debug_msg("found value = %s\n", value.c_str());
ctn->set_variable(varname, value);
}
return (true);
}
void
Command::program_action_complete(bool success,
const string& command_stdout,
const string& command_stderr,
bool do_exec,
MasterConfigTreeNode* ctn,
string stdout_variable_name,
string stderr_variable_name) const
{
debug_msg("Command::program_action_complete\n");
if (do_exec) {
if (! stdout_variable_name.empty())
if (ctn->set_variable(stdout_variable_name, command_stdout)
!= true) {
XLOG_ERROR("Failed to write the stdout of a program action "
"to variable %s", stdout_variable_name.c_str());
}
if (! stderr_variable_name.empty()) {
if (ctn->set_variable(stderr_variable_name, command_stderr)
!= true) {
XLOG_ERROR("Failed to write the stderr of a program action "
"to variable %s", stderr_variable_name.c_str());
}
}
}
if (success) {
ctn->command_status_callback(this, true);
} else {
ctn->command_status_callback(this, false);
}
}
set<string>
Command::affected_modules() const
{
set<string> modules_set;
list<Action*>::const_iterator iter;
for (iter = _actions.begin(); iter != _actions.end(); ++iter) {
Action *a = (*iter);
XrlAction* xa = dynamic_cast<XrlAction*>(a);
if (xa != NULL) {
string affected = xa->affected_module();
modules_set.insert(affected);
continue;
}
ProgramAction* pa = dynamic_cast<ProgramAction*>(a);
if (pa != NULL) {
string affected = pa->affected_module();
modules_set.insert(affected);
continue;
}
}
return modules_set;
}
bool
Command::affects_module(const string& module) const
{
// XXX: if we don't specify a module, we mean any module
if (module == "")
return true;
set<string> modules_set = affected_modules();
if (modules_set.find(module) == modules_set.end()) {
return false;
}
return true;
}
string
Command::str() const
{
string tmp = _cmd_name + ": ";
list<Action*>::const_iterator iter;
for (iter = _actions.begin(); iter != _actions.end(); ++iter) {
tmp += (*iter)->str() + ", ";
}
return tmp;
}
bool
Command::expand_actions(string& error_msg)
{
list<Action *>::iterator iter;
for (iter = _actions.begin(); iter != _actions.end(); ++iter) {
Action* action = *iter;
if (action->expand_action(error_msg) != true)
return false;
}
return true;
}
bool
Command::check_referred_variables(string& error_msg) const
{
list<Action *>::const_iterator iter;
for (iter = _actions.begin(); iter != _actions.end(); ++iter) {
const Action* action = *iter;
if (action->check_referred_variables(error_msg) != true)
return false;
}
return true;
}
//
// Template explicit instatiation
//
template Xrl* XrlAction::expand_xrl_variables<class MasterConfigTreeNode>(
const MasterConfigTreeNode& ctn,
string& error_msg) const;
template Xrl* XrlAction::expand_xrl_variables<class TemplateTreeNode>(
const TemplateTreeNode& ttn,
string& error_msg) const;
template bool XrlAction::expand_vars<class MasterConfigTreeNode>(
const MasterConfigTreeNode& ctn,
const string& value, string& result) const;
template bool XrlAction::expand_vars<class TemplateTreeNode>(
const TemplateTreeNode& ctn,
const string& value, string& result) const;
template int ProgramAction::expand_program_variables<class MasterConfigTreeNode>(
const MasterConfigTreeNode& ctn,
string& result,
string& error_msg) const;
template int ProgramAction::expand_program_variables<class TemplateTreeNode>(
const TemplateTreeNode& ttn,
string& result,
string& error_msg) const;
syntax highlighted by Code2HTML, v. 0.9.1