diff options
-rw-r--r-- | src/cli_shell_api.cpp | 55 | ||||
-rw-r--r-- | src/cnode/cnode-algorithm.cpp | 115 | ||||
-rw-r--r-- | src/cnode/cnode-algorithm.hpp | 6 |
3 files changed, 176 insertions, 0 deletions
diff --git a/src/cli_shell_api.cpp b/src/cli_shell_api.cpp index 363a38f..a701a71 100644 --- a/src/cli_shell_api.cpp +++ b/src/cli_shell_api.cpp @@ -471,6 +471,57 @@ loadFile(Cstore& cstore, const Cpath& args) } } +/* the following "cf" functions form the "config file" shell API, which + * allows shell scripts to "query" the "config" represented by a config + * file in a way similar to how they query the active/working config. + * usage example: + * + * cli-shell-api cfExists /config/config.boot service ssh allow-root + * + * the above command will exit with 0 (success) if the "allow-root" node + * is present in the specified config file (or exit with 1 if it's not). + */ +static void +cfExists(Cstore& cstore, const Cpath& args) +{ + Cpath path; + for (size_t i = 1; i < args.size(); i++) { + path.push(args[i]); + } + cnode::CfgNode *root = cparse::parse_file(args[0], cstore); + exit(cnode::findCfgNode(root, path) ? 0 : 1); +} + +static void +cfReturnValue(Cstore& cstore, const Cpath& args) +{ + Cpath path; + for (size_t i = 1; i < args.size(); i++) { + path.push(args[i]); + } + cnode::CfgNode *root = cparse::parse_file(args[0], cstore); + string value; + if (!cnode::getCfgNodeValue(root, path, value)) { + exit(1); + } + printf("%s", value.c_str()); +} + +static void +cfReturnValues(Cstore& cstore, const Cpath& args) +{ + Cpath path; + for (size_t i = 1; i < args.size(); i++) { + path.push(args[i]); + } + cnode::CfgNode *root = cparse::parse_file(args[0], cstore); + vector<string> values; + if (!cnode::getCfgNodeValues(root, path, values)) { + exit(1); + } + print_vec(values, " ", "'"); +} + #define OP(name, exact, exact_err, min, min_err, use_edit) \ { #name, exact, exact_err, min, min_err, use_edit, &name } @@ -517,6 +568,10 @@ static OpT ops[] = { OP(showConfig, -1, NULL, -1, NULL, true), OP(loadFile, 1, "Must specify config file", -1, NULL, false), + OP(cfExists, -1, NULL, 2, "Must specify config file and path", false), + OP(cfReturnValue, -1, NULL, 2, "Must specify config file and path", false), + OP(cfReturnValues, -1, NULL, 2, "Must specify config file and path", false), + {NULL, -1, NULL, -1, NULL, NULL, false} }; #define OP_exact_args ops[op_idx].op_exact_args diff --git a/src/cnode/cnode-algorithm.cpp b/src/cnode/cnode-algorithm.cpp index 602d9fb..607d5d2 100644 --- a/src/cnode/cnode-algorithm.cpp +++ b/src/cnode/cnode-algorithm.cpp @@ -946,3 +946,118 @@ cnode::showConfig(const string& cfg1, const string& cfg2, } } +/* find and return pointer to the CfgNode corresponding to specified path in + * the tree rooted at root. return NULL if not found. + * + * if path ends in value, the actual "node" (i.e., at path minus value) + * is returned *if* the value actually exists (in which case is_value is + * set to true). otherwise return NULL. + */ +CfgNode * +cnode::findCfgNode(CfgNode *root, const Cpath& path, bool& is_value) +{ + is_value = false; + if (path.size() < 1) { + return NULL; + } + + CfgNode *node = root; + for (size_t i = 0; i < path.size(); i++) { + if (node->isLeaf()) { + // reached a leaf node + if (i != (path.size() - 1)) { + // there's more after the current comp => can't exist + return NULL; + } + + // look for value + if (node->isMulti()) { + // multi-value + const vector<string>& vals = node->getValues(); + for (size_t j = 0; j < vals.size(); j++) { + if (vals[j] == path[i]) { + is_value = true; + return node; + } + } + return NULL; + } else { + // single-value + if (node->getValue() == path[i]) { + is_value = true; + return node; + } + return NULL; + } + } + + const vector<CfgNode *>& cnodes = node->getChildNodes(); + bool found = false; + for (size_t j = 0; j < cnodes.size(); j++) { + if (cnodes[j]->isValue()) { + // tag value + found = (cnodes[j]->getValue() == path[i]); + } else { + // others + found = (cnodes[j]->getName() == path[i]); + } + if (found) { + node = cnodes[j]; + break; + } + } + if (!found) { + return NULL; + } + } + + return node; +} + +// wrapper for above +CfgNode * +cnode::findCfgNode(CfgNode *root, const Cpath& path) +{ + bool dummy; + return findCfgNode(root, path, dummy); +} + +bool +cnode::getCfgNodeValue(CfgNode *root, const Cpath& path, string& value) +{ + bool is_val; + CfgNode *node = findCfgNode(root, path, is_val); + if (!node) { + // doesn't exist + return false; + } + if (is_val || !node->isLeaf() || node->isMulti()) { + // invalid path (already value, non-leaf, or multi-value) + fprintf(stderr, "Invalid path used with cnode::getCfgNodeValue()\n"); + return false; + } + + value = node->getValue(); + return true; +} + +bool +cnode::getCfgNodeValues(CfgNode *root, const Cpath& path, + vector<string>& values) +{ + bool is_val; + CfgNode *node = findCfgNode(root, path, is_val); + if (!node) { + // doesn't exist + return false; + } + if (is_val || !node->isLeaf() || !node->isMulti()) { + // invalid path (already value, non-leaf, or single-value) + fprintf(stderr, "Invalid path used with cnode::getCfgNodeValues()\n"); + return false; + } + + values = node->getValues(); + return true; +} + diff --git a/src/cnode/cnode-algorithm.hpp b/src/cnode/cnode-algorithm.hpp index 3985b9f..547b55c 100644 --- a/src/cnode/cnode-algorithm.hpp +++ b/src/cnode/cnode-algorithm.hpp @@ -44,6 +44,12 @@ void showConfig(const string& cfg1, const string& cfg2, bool hide_secret = false, bool context_diff = false, bool show_cmds = false, bool ignore_edit = false); +CfgNode *findCfgNode(CfgNode *root, const Cpath& path, bool& is_value); +CfgNode *findCfgNode(CfgNode *root, const Cpath& path); +bool getCfgNodeValue(CfgNode *root, const Cpath& path, string& value); +bool getCfgNodeValues(CfgNode *root, const Cpath& path, + vector<string>& values); + } // namespace cnode #endif /* _CNODE_ALGORITHM_HPP_ */ |