// -*- 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 #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& 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::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::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::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& action, const XRLdb& xrldb) throw (ParseError) : Action(template_tree_node, action), _xrldb(xrldb) { list xrl_parts = _split_cmd; debug_msg("XrlAction constructor\n"); XLOG_ASSERT(action.front() == "xrl"); // Print debug info { list::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::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& action, const XRLdb& xrldb, string& error_msg) { const string module_name = template_tree_node().module_name(); XLOG_ASSERT(action.front() == "xrl"); list::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 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::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 expanded_cmd; list::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 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 Xrl* XrlAction::expand_xrl_variables(const TreeNode& tn, string& error_msg) const { string word; string expanded_var; string command; list 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 ::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 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::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 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& action) throw (ParseError) : Action(template_tree_node, action) { list program_parts = _split_cmd; debug_msg("ProgramAction constructor\n"); XLOG_ASSERT(action.front() == "program"); // Print debug info { list::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::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& action, string& error_msg) { XLOG_ASSERT(action.front() == "program"); list::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 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::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 expanded_cmd; list::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 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 int ProgramAction::expand_program_variables(const TreeNode& tn, string& result, string& error_msg) const { string word; string expanded_var; list 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::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::const_iterator iter; for (iter = _actions.begin(); iter != _actions.end(); ++iter) { delete *iter; } } void Command::add_action(const list& 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::const_iterator iter; for (iter = _actions.begin(); iter != _actions.end(); ++iter) { do { const XrlAction* xa = dynamic_cast(*iter); if (xa != NULL) { result = xa->execute( ctn, task_manager, callback(this, &Command::xrl_action_complete, &ctn, *iter)); break; } const ProgramAction* pa = dynamic_cast(*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 spec_args; XrlAction* xa = dynamic_cast(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::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 Command::affected_modules() const { set modules_set; list::const_iterator iter; for (iter = _actions.begin(); iter != _actions.end(); ++iter) { Action *a = (*iter); XrlAction* xa = dynamic_cast(a); if (xa != NULL) { string affected = xa->affected_module(); modules_set.insert(affected); continue; } ProgramAction* pa = dynamic_cast(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 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::const_iterator iter; for (iter = _actions.begin(); iter != _actions.end(); ++iter) { tmp += (*iter)->str() + ", "; } return tmp; } bool Command::expand_actions(string& error_msg) { list::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::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( const MasterConfigTreeNode& ctn, string& error_msg) const; template Xrl* XrlAction::expand_xrl_variables( const TemplateTreeNode& ttn, string& error_msg) const; template bool XrlAction::expand_vars( const MasterConfigTreeNode& ctn, const string& value, string& result) const; template bool XrlAction::expand_vars( const TemplateTreeNode& ctn, const string& value, string& result) const; template int ProgramAction::expand_program_variables( const MasterConfigTreeNode& ctn, string& result, string& error_msg) const; template int ProgramAction::expand_program_variables( const TemplateTreeNode& ttn, string& result, string& error_msg) const;