summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cli_shell_api.cpp55
-rw-r--r--src/cnode/cnode-algorithm.cpp115
-rw-r--r--src/cnode/cnode-algorithm.hpp6
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_ */