summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rwxr-xr-xetc/bash_completion.d/20vyatta-cfg10
-rw-r--r--src/cli_shell_api.cpp50
-rw-r--r--src/cstore/cstore-node.cpp398
-rw-r--r--src/cstore/cstore-node.hpp104
5 files changed, 547 insertions, 16 deletions
diff --git a/Makefile.am b/Makefile.am
index 00a3c80..cef450f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -32,6 +32,7 @@ src_libvyatta_cfg_la_SOURCES += src/cli_val_engine.c src/cli_objects.c
src_libvyatta_cfg_la_SOURCES += src/cstore/cstore-c.cpp
src_libvyatta_cfg_la_SOURCES += src/cstore/cstore.cpp
src_libvyatta_cfg_la_SOURCES += src/cstore/cstore-varref.cpp
+src_libvyatta_cfg_la_SOURCES += src/cstore/cstore-node.cpp
src_libvyatta_cfg_la_SOURCES += src/cstore/unionfs/cstore-unionfs.cpp
CLEANFILES = src/cli_parse.c src/cli_parse.h src/cli_def.c src/cli_val.c
LDADD = src/libvyatta-cfg.la
diff --git a/etc/bash_completion.d/20vyatta-cfg b/etc/bash_completion.d/20vyatta-cfg
index 56c1272..f6b02ce 100755
--- a/etc/bash_completion.d/20vyatta-cfg
+++ b/etc/bash_completion.d/20vyatta-cfg
@@ -84,19 +84,15 @@ done
show ()
{
- local show_all=''
local -a args=()
for arg in "$@"; do
if [ "$arg" == "-all" ]; then
- show_all='-all'
+ args+=('--show-show-defaults')
else
- args[${#args[@]}]="$arg"
+ args+=("$arg")
fi
done
- local level=$(vyatta_cli_shell_api NOEVAL getEditLevelStr)
- ${vyatta_sbindir}/vyatta-output-config.pl ${show_all} \
- $level ${args[@]} \
- | eval "${VYATTA_PAGER:-cat}"
+ cli-shell-api showCfg "${args[@]}" | eval "${VYATTA_PAGER:-cat}"
}
commit ()
diff --git a/src/cli_shell_api.cpp b/src/cli_shell_api.cpp
index 1a4f469..0038866 100644
--- a/src/cli_shell_api.cpp
+++ b/src/cli_shell_api.cpp
@@ -16,14 +16,13 @@
#include <cstdio>
#include <cstring>
-#include <cerrno>
#include <vector>
#include <string>
-#include <libgen.h>
-#include <sys/mount.h>
+#include <getopt.h>
#include <cli_cstore.h>
#include <cstore/unionfs/cstore-unionfs.hpp>
+#include <cstore/cstore-node.hpp>
/* This program provides an API for shell scripts (e.g., snippets in
* templates, standalone scripts, etc.) to access the CLI cstore library.
@@ -48,6 +47,12 @@ print_vec(const vector<string>& vec, const char *sep, const char *quote)
}
}
+//// options
+// showCfg options
+int op_show_active_only = 0;
+int op_show_show_defaults = 0;
+int op_show_hide_secrets = 0;
+
typedef void (*OpFuncT)(const vector<string>& args);
typedef struct {
@@ -388,6 +393,15 @@ validateTmplValPath(const vector<string>& args)
exit(cstore.validateTmplPath(args, true) ? 0 : 1);
}
+static void
+showCfg(const vector<string>& args)
+{
+ UnionfsCstore cstore(true);
+ vector<string> nargs(args);
+ CstoreCfgNode root(cstore, nargs, op_show_active_only);
+ root.show_as_root(op_show_show_defaults, op_show_hide_secrets);
+}
+
#define OP(name, exact, exact_err, min, min_err) \
{ #name, exact, exact_err, min, min_err, &name }
@@ -430,6 +444,8 @@ static OpT ops[] = {
OP(validateTmplPath, -1, NULL, 1, "Must specify config path"),
OP(validateTmplValPath, -1, NULL, 1, "Must specify config path"),
+ OP(showCfg, -1, NULL, -1, NULL),
+
{NULL, -1, NULL, -1, NULL, NULL}
};
#define OP_exact_args ops[op_idx].op_exact_args
@@ -438,16 +454,32 @@ static OpT ops[] = {
#define OP_min_error ops[op_idx].op_min_error
#define OP_func ops[op_idx].op_func
+struct option options[] = {
+ {"show-active-only", no_argument, &op_show_active_only, 1},
+ {"show-show-defaults", no_argument, &op_show_show_defaults, 1},
+ {"show-hide-secrets", no_argument, &op_show_hide_secrets, 1},
+ {NULL, 0, NULL, 0}
+};
+
int
main(int argc, char **argv)
{
+ // handle options first
+ int c = 0;
+ while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) {
+ // nothing for now
+ }
+ int nargs = argc - optind - 1;
+ char *oname = argv[optind];
+ char **nargv = &(argv[optind + 1]);
+
int i = 0;
- if (argc < 2) {
+ if (nargs < 0) {
fprintf(stderr, "Must specify operation\n");
exit(-1);
}
while (ops[i].op_name) {
- if (strcmp(argv[1], ops[i].op_name) == 0) {
+ if (strcmp(oname, ops[i].op_name) == 0) {
op_idx = i;
break;
}
@@ -457,18 +489,18 @@ main(int argc, char **argv)
fprintf(stderr, "Invalid operation\n");
exit(-1);
}
- if (OP_exact_args >= 0 && (argc - 2) != OP_exact_args) {
+ if (OP_exact_args >= 0 && nargs != OP_exact_args) {
fprintf(stderr, "%s\n", OP_exact_error);
exit(-1);
}
- if (OP_min_args >= 0 && (argc - 2) < OP_min_args) {
+ if (OP_min_args >= 0 && nargs < OP_min_args) {
fprintf(stderr, "%s\n", OP_min_error);
exit(-1);
}
vector<string> args;
- for (int i = 2; i < argc; i++) {
- args.push_back(argv[i]);
+ for (int i = 0; i < nargs; i++) {
+ args.push_back(nargv[i]);
}
// call the op function
diff --git a/src/cstore/cstore-node.cpp b/src/cstore/cstore-node.cpp
new file mode 100644
index 0000000..85d2fda
--- /dev/null
+++ b/src/cstore/cstore-node.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2010 Vyatta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cstdio>
+#include <cstring>
+#include <vector>
+#include <string>
+#include <algorithm>
+
+#include <cli_cstore.h>
+#include <cstore/cstore-node.hpp>
+
+using namespace std;
+
+////// constants
+const string CstoreCfgNode::PFX_DIFF_ADD = "+"; // added
+const string CstoreCfgNode::PFX_DIFF_DEL = "-"; // deleted
+const string CstoreCfgNode::PFX_DIFF_UPD = ">"; // changed
+const string CstoreCfgNode::PFX_DIFF_NONE = " ";
+
+const string CstoreCfgNode::PFX_DEACT_D = "!"; // deactivated
+const string CstoreCfgNode::PFX_DEACT_DP = "D"; // deactivate pending
+const string CstoreCfgNode::PFX_DEACT_AP = "A"; // activate pending
+const string CstoreCfgNode::PFX_DEACT_NONE = " ";
+
+bool CstoreCfgNode::_init = false;
+Cstore::MapT<string, NodeStatusT> CstoreCfgNode::_st_map;
+
+////// constructors/destructors
+CstoreCfgNode::CstoreCfgNode(Cstore& cstore, vector<string>& path_comps,
+ bool active_only, const string& name,
+ NodeStatusT status)
+ : _cstore(&cstore), _name(name), _status(status),
+ _pfx_deact(PFX_DEACT_NONE.c_str()),
+ _is_tag(false), _is_leaf(false), _is_multi(false), _is_value(false),
+ _is_default(false), _is_invalid(false), _is_empty(false),
+ _active_only(false), _comment(""), _comment_status(ST_STATIC)
+{
+ init();
+
+ if (!_cstore) {
+ return;
+ }
+
+ // active_only if specified or not in session
+ _active_only = (!_cstore->inSession() || active_only);
+
+ /* first get the def (only if path is not empty). if path is empty, treat
+ * it as an intermediate node.
+ */
+ if (path_comps.size() > 0) {
+ vtw_def def;
+ if (_cstore->validateTmplPath(path_comps, false, def)) {
+ // got the def
+ if (def.is_value && !def.tag) {
+ // leaf value. should never happen (recursion should have terminated).
+ return;
+ }
+ _is_tag = (def.tag && !def.is_value);
+ _is_leaf = (!_is_tag && !def.is_value && def.def_type != ERROR_TYPE);
+ _is_multi = (_is_leaf && def.multi);
+ _is_value = def.is_value;
+
+ _is_default = _cstore->cfgPathDefault(path_comps, _active_only);
+
+ bool adeact = _cstore->cfgPathDeactivated(path_comps, true);
+ /* if active only, pretend deactivate status in working is the same
+ * as active to get the right output.
+ */
+ bool wdeact = (_active_only
+ ? adeact
+ : _cstore->cfgPathDeactivated(path_comps, false));
+ if (adeact) {
+ if (wdeact) {
+ // deactivated in both active and working => deactivated
+ _pfx_deact = PFX_DEACT_D.c_str();
+ } else {
+ // deactivated only in active => activate pending
+ _pfx_deact = PFX_DEACT_AP.c_str();
+ }
+ } else {
+ if (wdeact) {
+ // deactivated only in working => deactivate pending
+ _pfx_deact = PFX_DEACT_DP.c_str();
+ }
+ // 4th case handled by default
+ }
+ } else {
+ // not a valid node. this should only happen at root.
+ _is_invalid = true;
+ return;
+ }
+ }
+
+ // handle comment
+ string ac_str;
+ bool ac = _cstore->cfgPathGetComment(path_comps, ac_str, true);
+ string wc_str;
+ bool wc = _cstore->cfgPathGetComment(path_comps, wc_str, false);
+ if (ac) {
+ if (wc) {
+ // has comment in both active and working
+ _comment = wc_str;
+ if (ac_str != wc_str) {
+ _comment_status = ST_CHANGED;
+ }
+ } else {
+ // comment only in active
+ _comment = ac_str;
+ _comment_status = ST_DELETED;
+ }
+ } else {
+ if (wc) {
+ // comment only in working
+ _comment = wc_str;
+ _comment_status = ST_ADDED;
+ }
+ // 4th case (neither) handled by default
+ }
+
+ if (_is_leaf) {
+ /* leaf node. set the name if this is the root (note path_comps must be
+ * non-empty if this is leaf).
+ */
+ if (_name == "") {
+ _name = path_comps[path_comps.size() - 1];
+ }
+ if (_is_multi) {
+ // multi-value node
+ NodeStatusT st = (_active_only ? ST_STATIC : _status);
+ if (st == ST_STATIC || st == ST_DELETED || st == ST_ADDED) {
+ bool get_active = (st != ST_ADDED);
+ _cstore->cfgPathGetValuesDA(path_comps, _values, get_active, true);
+ // ignore return value
+
+ // all values have the same status
+ for (size_t i = 0; i < _values.size(); i++) {
+ _values_status.push_back(st);
+ }
+ } else {
+ // values changed => need to do a diff between active and working
+ // this follows the original perl logic
+ vector<string> ovals; // active values
+ vector<string> nvals; // working values
+ _cstore->cfgPathGetValuesDA(path_comps, ovals, true);
+ _cstore->cfgPathGetValuesDA(path_comps, nvals, false);
+ Cstore::MapT<string, bool> nmap;
+ for (size_t i = 0; i < nvals.size(); i++) {
+ nmap[nvals[i]] = true;
+ }
+ Cstore::MapT<string, bool> omap;
+ for (size_t i = 0; i < ovals.size(); i++) {
+ omap[ovals[i]] = true;
+ if (nmap.find(ovals[i]) == nmap.end()) {
+ _values.push_back(ovals[i]);
+ _values_status.push_back(ST_DELETED);
+ }
+ }
+ for (size_t i = 0; i < nvals.size(); i++) {
+ _values.push_back(nvals[i]);
+ if (omap.find(nvals[i]) == omap.end()) {
+ // new value not in working
+ _values_status.push_back(ST_ADDED);
+ } else if (i < ovals.size() && nvals[i] == ovals[i]) {
+ // value also in working and in the same position
+ _values_status.push_back(ST_STATIC);
+ } else {
+ // value position has changed
+ _values_status.push_back(ST_CHANGED);
+ }
+ }
+ }
+ } else {
+ // single-value node
+ // if the node has been deleted, get the value from active too.
+ bool get_active = (_active_only || _status == ST_DELETED);
+ _cstore->cfgPathGetValueDA(path_comps, _value, get_active, true);
+ // ignore return value
+ }
+ return;
+ }
+
+ // intermediate (typeless) or tag node
+ Cstore::MapT<string, string> cmap;
+ vector<string> cnodes;
+ if (_active_only) {
+ // only show active config
+ _cstore->cfgPathGetChildNodesDA(path_comps, cnodes, true, true);
+ for (size_t i = 0; i < cnodes.size(); i++) {
+ cmap[cnodes[i]] = Cstore::C_NODE_STATUS_STATIC;
+ }
+ } else {
+ // show config session
+ _cstore->cfgPathGetChildNodesStatusDA(path_comps, cmap, cnodes);
+ }
+ if (cmap.empty()) {
+ // empty subtree. finished.
+ _is_empty = true;
+ return;
+ }
+
+ for (size_t i = 0; i < cnodes.size(); i++) {
+ path_comps.push_back(cnodes[i]);
+ CstoreCfgNode *cn = new CstoreCfgNode(cstore, path_comps, _active_only,
+ cnodes[i], _st_map[cmap[cnodes[i]]]);
+ if (_is_tag && !_is_value) {
+ // tag node
+ cn->setTagName(_name);
+ _tag_values.push_back(cn);
+ } else {
+ // intermediate node or tag value
+ _child_nodes.push_back(cn);
+ }
+ path_comps.pop_back();
+ }
+}
+
+
+////// public functions
+void
+CstoreCfgNode::show_as_root(bool show_default, bool hide_secret)
+{
+ if (_is_invalid) {
+ printf("Specified configuration path is not valid\n");
+ return;
+ }
+ if (_is_empty) {
+ printf("Configuration under specified path is empty\n");
+ return;
+ }
+
+ show(-1, show_default, hide_secret);
+}
+
+
+////// private functions
+void
+CstoreCfgNode::show(int level, bool show_def, bool hide_secret)
+{
+ if (_is_leaf) {
+ // leaf node
+ if (_is_multi) {
+ // multi-value node
+ print_comment(level);
+ for (size_t i = 0; i < _values.size()
+ && i < _values_status.size(); i++) {
+ print_indent(level, _values_status[i]);
+ printf("%s ", _name.c_str());
+ print_value_str(_values[i].c_str(), hide_secret);
+ printf("\n");
+ }
+ } else {
+ // handle "default" for single-value node
+ if (show_def || !_is_default) {
+ // single-value node
+ print_comment(level);
+ print_indent(level);
+ printf("%s ", _name.c_str());
+ print_value_str(_value.c_str(), hide_secret);
+ printf("\n");
+ }
+ }
+ return;
+ }
+
+ if (_is_tag) {
+ // tag node
+ for (size_t i = 0; i < _tag_values.size(); i++) {
+ /* note: if the root is a tag node (level == -1), then need to make
+ * level 0 when calling tag values' show().
+ */
+ _tag_values[i]->show((level >= 0 ? level : 0), show_def, hide_secret);
+ }
+ } else {
+ // intermediate node or tag value
+ bool print_this = (level >= 0 && _name != "");
+ if (print_this) {
+ print_comment(level);
+ print_indent(level);
+ if (_is_value && _tag_name != "") {
+ // at tag value and there is a tag node parent => print node name
+ printf("%s ", _tag_name.c_str());
+ }
+ printf("%s {\n", _name.c_str());
+ }
+ for (size_t i = 0; i < _child_nodes.size(); i++) {
+ _child_nodes[i]->show(level + 1, show_def, hide_secret);
+ }
+ if (print_this) {
+ print_indent(level);
+ printf("}\n");
+ }
+ }
+}
+
+void
+CstoreCfgNode::print_indent(int level, NodeStatusT st, bool force_changed)
+{
+ if (st == ST_CHANGED && !force_changed && !_is_leaf) {
+ /* normally only output "changed" status for leaf nodes. in special cases
+ * (currently only for "comment"), "changed" is also needed, so only
+ * convert to "static" if not forcing "changed".
+ */
+ st = ST_STATIC;
+ }
+ print_prefix(st);
+ for (int i = 0; i < level; i++) {
+ printf(" ");
+ };
+}
+
+void
+CstoreCfgNode::print_prefix(NodeStatusT st)
+{
+ printf("%s ", _pfx_deact);
+ if (!_active_only) {
+ /* this follows the original implementation: when outputting the acitve
+ * configuration only (e.g., in op mode), only generate two columns.
+ */
+ printf("%s", get_prefix_diff(st));
+ }
+}
+
+const char *
+CstoreCfgNode::get_prefix_diff(NodeStatusT st)
+{
+ if (st == ST_DELETED) {
+ return PFX_DIFF_DEL.c_str();
+ } else if (st == ST_ADDED) {
+ return PFX_DIFF_ADD.c_str();
+ } else if (st == ST_CHANGED) {
+ return PFX_DIFF_UPD.c_str();
+ }
+ return PFX_DIFF_NONE.c_str();
+}
+
+void
+CstoreCfgNode::print_comment(int level)
+{
+ if (_comment == "") {
+ // no comment
+ return;
+ }
+ // forcing "changed" since it's needed for comment
+ print_indent(level, _comment_status, true);
+ printf("/* %s */\n", _comment.c_str());
+}
+
+/* prints a value string, double-quoting it if necessary.
+ * this follows the original perl logic, i.e., double quoting is needed if:
+ * (/^$/ or /[\s\*}{;]/)
+ *
+ * also follow the original "secret hiding" logic:
+ * /^.*(passphrase|password|pre-shared-secret|key)$/
+ */
+void
+CstoreCfgNode::print_value_str(const char *vstr, bool hide_secret)
+{
+ // handle secret hiding first
+ if (hide_secret) {
+ static const char *sname[] = { "passphrase", "password",
+ "pre-shared-secret", "key", NULL };
+ static size_t slen[] = { 10, 8, 17, 3, 0 };
+ size_t nlen = _name.length();
+ for (size_t i = 0; sname[i]; i++) {
+ if (nlen < slen[i]) {
+ // can't match
+ continue;
+ }
+ if (_name.find(sname[i], nlen - slen[i]) != _name.npos) {
+ // found secret
+ printf("****************");
+ return;
+ }
+ }
+ }
+
+ const char *quote = "";
+ size_t vlen = strlen(vstr);
+ if (*vstr == 0 || strcspn(vstr, "*}{;\011\012\013\014\015 ") < vlen) {
+ quote = "\"";
+ }
+ printf("%s%s%s", quote, vstr, quote);
+}
+
diff --git a/src/cstore/cstore-node.hpp b/src/cstore/cstore-node.hpp
new file mode 100644
index 0000000..a7aa865
--- /dev/null
+++ b/src/cstore/cstore-node.hpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 Vyatta, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _CSTORE_NODE_H_
+#define _CSTORE_NODE_H_
+#include <vector>
+#include <string>
+#include <map>
+
+#include <cstore/cstore.hpp>
+
+using namespace std;
+
+typedef enum {
+ ST_DELETED,
+ ST_ADDED,
+ ST_CHANGED,
+ ST_STATIC
+} NodeStatusT;
+
+class CstoreCfgNode {
+public:
+ CstoreCfgNode(Cstore& cstore, vector<string>& path_comps,
+ bool active_only = false, const string& name = "",
+ NodeStatusT status = ST_STATIC);
+ ~CstoreCfgNode() {};
+
+ // diff prefixes
+ static const string PFX_DIFF_ADD;
+ static const string PFX_DIFF_DEL;
+ static const string PFX_DIFF_UPD;
+ static const string PFX_DIFF_NONE;
+
+ // deactivate prefixes
+ static const string PFX_DEACT_D;
+ static const string PFX_DEACT_DP;
+ static const string PFX_DEACT_AP;
+ static const string PFX_DEACT_NONE;
+
+ void setTagName(const string& tname) { _tag_name = tname; };
+ bool isTag() { return _is_tag; };
+ void show_as_root(bool show_default = false, bool hide_secret = false);
+
+private:
+ static bool _init;
+ static Cstore::MapT<string, NodeStatusT> _st_map;
+ static void init() {
+ if (_init) {
+ return;
+ }
+ _st_map[Cstore::C_NODE_STATUS_DELETED] = ST_DELETED;
+ _st_map[Cstore::C_NODE_STATUS_ADDED] = ST_ADDED;
+ _st_map[Cstore::C_NODE_STATUS_CHANGED] = ST_CHANGED;
+ _st_map[Cstore::C_NODE_STATUS_STATIC] = ST_STATIC;
+ _init = true;
+ };
+
+ Cstore *_cstore;
+ string _name;
+ NodeStatusT _status;
+ const char *_pfx_deact;
+ bool _is_tag;
+ bool _is_leaf;
+ bool _is_multi;
+ bool _is_value;
+ bool _is_default;
+ bool _is_invalid;
+ bool _is_empty;
+ bool _active_only;
+ string _tag_name;
+ string _value;
+ vector<string> _values;
+ vector<NodeStatusT> _values_status;
+ string _comment;
+ NodeStatusT _comment_status;
+ vector<CstoreCfgNode *> _tag_values;
+ vector<CstoreCfgNode *> _child_nodes;
+
+ void show(int level, bool show_def, bool hide_secret);
+ void print_indent(int level) {
+ print_indent(level, _status);
+ };
+ void print_indent(int level, NodeStatusT st, bool force_changed = false);
+ void print_prefix(NodeStatusT st);
+ const char *get_prefix_diff(NodeStatusT st);
+ void print_comment(int level);
+ void print_value_str(const char *vstr, bool hide_secret);
+};
+
+#endif /* _CSTORE_NODE_H_ */
+