/* * ***** BEGIN LICENSE BLOCK ***** Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren Copyright (C) 2017-2019 Olof Hagsand Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate) This file is part of CLIXON. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Alternatively, the contents of this file may be used under the terms of the GNU General Public License Version 3 or later (the "GPL"), in which case the provisions of the GPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the GPL, and not to allow others to use your version of this file under the terms of Apache License version 2, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the Apache License version 2 or the GPL. ***** END LICENSE BLOCK ***** * */ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* cligen */ #include /* clicon */ #include /* Exported functions in this file are in clixon_cli_api.h */ #include "clixon_cli_api.h" #include "cli_autocli.h" #include "cli_common.h" /* internal functions */ /*! Given an xpath encoded in a cbuf, append a second xpath into the first * * The method reuses prefixes from xpath1 if they exist, otherwise the module prefix * from y is used. Unless the element is .., . * XXX: Predicates not handled * The algorithm is not fool-proof, there are many cases it may not work * To make it more complete, maybe parse the xpath to a tree and put it * back to an xpath after modifcations, something like: if (xpath_parse(yang_argument_get(ypath), &xpt) < 0) goto done; if (xpath_tree2cbuf(xpt, xcb) < 0) goto done; and traverse_canonical */ static int xpath_append(cbuf *cb0, char *xpath1, yang_stmt *y, cvec *nsc) { int retval = -1; char **vec = NULL; char *v; int nvec; int i; char *myprefix; char *id = NULL; char *prefix = NULL; int initialups = 1; /* If starts with ../../.. */ char *xpath0; if (cb0 == NULL){ clicon_err(OE_XML, EINVAL, "cb0 is NULL"); goto done; } if (xpath1 == NULL || strlen(xpath1)==0) goto ok; if ((myprefix = yang_find_myprefix(y)) == NULL) goto done; if ((vec = clicon_strsep(xpath1, "/", &nvec)) == NULL) goto done; if (xpath1[0] == '/') cbuf_reset(cb0); xpath0 = cbuf_get(cb0); for (i=0; i= 0; j--){ if (xpath0[j] != '/') continue; cbuf_trunc(cb0, j); break; } } else{ initialups = 0; cprintf(cb0, "/%s", id); } } else{ initialups = 0; cprintf(cb0, "/%s:%s", prefix?prefix:myprefix, id); } if (prefix){ free(prefix); prefix = NULL; } if (id){ free(id); id = NULL; } } ok: retval = 0; done: if (prefix) free(prefix); if (id) free(id); free(vec); return retval; } /*! Completion callback intended for automatically generated data model * * Returns an expand-type list of commands as used by cligen 'expand' * functionality. * * Assume callback given in a cligen spec: a ") * @param[in] h clicon handle * @param[in] name Name of this function (eg "expand_dbvar") * @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5; * @param[in] argv Arguments given at the callback ("" "") * @param[out] commands vector of function pointers to callback functions * @param[out] helptxt vector of pointers to helptexts * @see cli_expand_var_generate This is where arg is generated */ int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv, cvec *commands, cvec *helptexts) { int retval = -1; char *api_path_fmt; char *api_path = NULL; char *dbstr; cxobj *xt = NULL; char *xpath = NULL; cxobj **xvec = NULL; cxobj *xe; /* direct ptr */ cxobj *xerr = NULL; /* free */ size_t xlen = 0; cxobj *x; char *bodystr; int i; char *bodystr0 = NULL; /* previous */ cg_var *cv; yang_stmt *yspec; cxobj *xtop = NULL; /* xpath root */ cxobj *xbot = NULL; /* xpath, NULL if datastore */ yang_stmt *y = NULL; /* yang spec of xpath */ yang_stmt *yp; cvec *nsc = NULL; int ret; int cvvi = 0; cbuf *cbxpath = NULL; yang_stmt *ypath; yang_stmt *ytype; if (argv == NULL || cvec_len(argv) != 2){ clicon_err(OE_PLUGIN, EINVAL, "requires arguments: "); goto done; } if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } if ((cv = cvec_i(argv, 0)) == NULL){ clicon_err(OE_PLUGIN, 0, "Error when accessing argument "); goto done; } dbstr = cv_string_get(cv); if (strcmp(dbstr, "running") != 0 && strcmp(dbstr, "candidate") != 0 && strcmp(dbstr, "startup") != 0){ clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); goto done; } if ((cv = cvec_i(argv, 1)) == NULL){ clicon_err(OE_PLUGIN, 0, "Error when accessing argument "); goto done; } api_path_fmt = cv_string_get(cv); /* api_path_fmt = /interface/%s/address/%s * api_path: --> /interface/eth0/address/.* * xpath: --> /interface/[name="eth0"]/address */ if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0) goto done; /* Create config top-of-tree */ if ((xtop = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) goto done; xbot = xtop; /* This is primarily to get "y", * xpath2xml would have worked!! * XXX: but y is just the first in this list, there could be other y:s? */ if (api_path){ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ clixon_netconf_error(xerr, "Expand datastore symbol", NULL); goto done; } } if (y==NULL) goto ok; /* Transform api-path to xpath for netconf */ if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) goto done; if (nsc != NULL){ cvec_free(nsc); nsc = NULL; } if (xml_nsctx_yang(y, &nsc) < 0) goto done; if ((cbxpath = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } cprintf(cbxpath, "%s", xpath); if (clicon_option_bool(h, "CLICON_CLI_EXPAND_LEAFREF") && (ytype = yang_find(y, Y_TYPE, NULL)) != NULL && strcmp(yang_argument_get(ytype), "leafref") == 0){ /* Special case for leafref. Detect leafref via Yang-type, * Get Yang path element, tentatively add the new syntax to the whole * tree and apply the path to that. * Last, the reference point for the xpath code below is changed to * the point of the tentative new xml. * Here the whole syntax tree is loaded, and it would be better to offload * such operations to the datastore by a generic xpath function. */ /* * The syntax for a path argument is a subset of the XPath abbreviated * syntax. Predicates are used only for constraining the values for the * key nodes for list entries. Each predicate consists of exactly one * equality test per key, and multiple adjacent predicates MAY be * present if a list has multiple keys. The syntax is formally defined * by the rule "path-arg" in Section 14. * The "path" XPath expression is conceptually evaluated in the * following context, in addition to the definition in Section 6.4.1: * * - If the "path" statement is defined within a typedef, the context * node is the leaf or leaf-list node in the data tree that * references the typedef. * - Otherwise, the context node is the node in the data tree for which * the "path" statement is defined. */ if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){ clicon_err(OE_DB, 0, "Leafref %s requires path statement", yang_argument_get(ytype)); goto done; } /* Extend xpath with leafref path: Append yang_argument_get(ypath) to xpath */ if (xpath_append(cbxpath, yang_argument_get(ypath), y, nsc) < 0) goto done; } /* Get configuration based on cbxpath */ if (clicon_rpc_get_config(h, NULL, dbstr, cbuf_get(cbxpath), nsc, NULL, &xt) < 0) goto done; if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clixon_netconf_error(xe, "Get configuration", NULL); goto ok; } if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, cbuf_get(cbxpath)) < 0) goto done; /* Loop for inserting into commands cvec. * Detect duplicates: for ordered-by system assume list is ordered, so you need * just remember previous * but for ordered-by system, check the whole list */ bodystr0 = NULL; for (i = 0; i < xlen; i++) { x = xvec[i]; if (xml_type(x) == CX_BODY) bodystr = xml_value(x); else bodystr = xml_body(x); if (bodystr == NULL) continue; /* no body, cornercase */ if ((y = xml_spec(x)) != NULL && (yp = yang_parent_get(y)) != NULL && yang_keyword_get(yp) == Y_LIST && yang_find(yp, Y_ORDERED_BY, "user") != NULL){ /* Detect duplicates linearly in existing values */ { cg_var *cv = NULL; while ((cv = cvec_each(commands, cv)) != NULL) if (strcmp(cv_string_get(cv), bodystr) == 0) break; if (cv == NULL) cvec_add_string(commands, NULL, bodystr); } } else{ if (bodystr0 && strcmp(bodystr, bodystr0) == 0) continue; /* duplicate, assume sorted */ bodystr0 = bodystr; /* RFC3986 decode */ cvec_add_string(commands, NULL, bodystr); } } ok: retval = 0; done: if (cbxpath) cbuf_free(cbxpath); if (xerr) xml_free(xerr); if (nsc) xml_nsctx_free(nsc); if (api_path) free(api_path); if (xvec) free(xvec); if (xtop) xml_free(xtop); if (xt) xml_free(xt); if (xpath) free(xpath); return retval; } /*! CLI callback show yang spec. If arg given matches yang argument string */ int show_yang(clicon_handle h, cvec *cvv, cvec *argv) { yang_stmt *yn; char *str = NULL; yang_stmt *yspec; yspec = clicon_dbspec_yang(h); if (cvec_len(argv) > 0){ str = cv_string_get(cvec_i(argv, 0)); yn = yang_find(yspec, 0, str); } else yn = yspec; yang_print_cb(stdout, yn, cligen_output); /* Doesnt use cligen_output */ return 0; } /*! Common internal show routine for several show cli callbacks * * @param[in] h Clixon handle * @param[in] db Datastore * @param[in] format Output format * @param[in] pretty * @param[in] state * @param[in] withdefault RFC 6243 with-default modes * @param[in] extdefault with-defaults with propriatary extensions * @param[in] prefix CLI prefix to prepend cli syntax, eg "set " * @param[in] xpath XPath * @param[in] nsc Namespace mapping for xpath * @param[in] skiproot If set, do not show object itself, only its children */ static int cli_show_common(clicon_handle h, char *db, enum format_enum format, int pretty, int state, char *withdefault, char *extdefault, char *prefix, char *xpath, cvec *nsc, int skiproot ) { int retval = -1; cxobj *xt = NULL; cxobj *xerr; cxobj **vec = NULL; size_t veclen; cxobj *xp; yang_stmt *yp; enum rfc_6020 ys_keyword; int i; if (state && strcmp(db, "running") != 0){ clicon_err(OE_FATAL, 0, "Show state only for running database, not %s", db); goto done; } if (state == 0){ /* Get configuration-only from a database */ if (clicon_rpc_get_config(h, NULL, db, xpath, nsc, withdefault, &xt) < 0) goto done; } else { /* Get configuration and state from running */ if (clicon_rpc_get(h, xpath, nsc, CONTENT_ALL, -1, withdefault, &xt) < 0) goto done; } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ clixon_netconf_error(xerr, "Get configuration", NULL); goto done; } /* Special tagged modes: strip wd:default=true attribute and (optionally) nodes associated with it */ if (extdefault && (strcmp(extdefault, "report-all-tagged-strip") == 0 || strcmp(extdefault, "report-all-tagged-default") == 0)){ if (purge_tagged_nodes(xt, IETF_NETCONF_WITH_DEFAULTS_ATTR_NAMESPACE, "default", "true", strcmp(extdefault, "report-all-tagged-strip") ) < 0) goto done; /* Remove empty containers */ if (xml_defaults_nopresence(xt, 2) < 0) goto done; } if (xpath_vec(xt, nsc, "%s", &vec, &veclen, xpath) < 0) goto done; if (veclen){ xp = vec[0]; /* First peek to see if it is special case yang list */ if ((yp = xml_spec(xp)) != NULL) ys_keyword = yang_keyword_get(xml_spec(xp)); else ys_keyword = 0; /* Special case LIST */ if ((ys_keyword == Y_LIST || ys_keyword == Y_LEAF_LIST) && format == FORMAT_JSON){ switch (format){ case FORMAT_JSON: if (xml2json_vec(stdout, vec, veclen, pretty) < 0) // XXX cligen_output goto done; break; default: break; } } else /* Default */ for (i=0; i", NETCONF_BASE_NAMESPACE, NETCONF_MESSAGE_ID_ATTR); if (pretty) cligen_output(stdout, "\n"); } if (clixon_xml2file(stdout, xp, 2, pretty, cligen_output, skiproot, 1) < 0) goto done; if (i == veclen-1) cligen_output(stdout, "]]>]]>\n"); break; } } } retval = 0; done: if (vec) free(vec); if (xt) xml_free(xt); return retval; } /*! Common internal parse cli show format option * * @param[in] argv String vector: [] * @param[in] argc Index into argv * @param[out] format Output format * @retval 0 OK * @retval -1 Error */ static int cli_show_option_format(cvec *argv, int argc, enum format_enum *format) { int retval = -1; char *formatstr; formatstr = cv_string_get(cvec_i(argv, argc)); if ((int)(*format = format_str2int(formatstr)) < 0){ clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr); goto done; } retval = 0; done: return retval; } /*! Common internal parse cli show boolean option * * @param[in] argv String vector: [] * @param[in] argc Index into argv * @param[out] bool result boolean: 0 or 1 * @retval 0 OK * @retval -1 Error */ static int cli_show_option_bool(cvec *argv, int argc, int *bool ) { int retval = -1; char *boolstr; cg_var *boolcv = NULL; boolstr = cv_string_get(cvec_i(argv, argc)); if ((boolcv = cv_new(CGV_BOOL)) == NULL){ clicon_err(OE_UNIX, errno, "cv_new"); goto done; } if (cv_parse(boolstr, boolcv) < 0){ clicon_err(OE_UNIX, errno, "Parse boolean %s", boolstr); goto done; } *bool = cv_bool_get(boolcv); retval = 0; done: if (boolcv) cv_free(boolcv); return retval; } /*! Common internal parse cli show with-default option * * Ddefault modes accorsing to RFC6243 + three extra modes based on report-all-tagged: * 1) NULL * 2) report-all-tagged-default Strip "default" attribute (=report-all) * 3) report-all-tagged-strip Strip "default" attribute and all nodes tagged with it (=trim) * @param[in] argv String vector: [] * @param[in] argc Index into argv * @param[in] withdefault RFC 6243 with-default modes * @param[in] extdefault with-defaults with propriatary extensions * @retval 0 OK * @retval -1 Error */ static int cli_show_option_withdefault(cvec *argv, int argc, char **withdefault, char **extdefault) { int retval = -1; char *e; e = cv_string_get(cvec_i(argv, argc)); /* From extended to RFC6243 withdefault modes */ if (strcmp(e, "report-all-tagged-strip") == 0) *withdefault = "report-all-tagged"; else if (strcmp(e, "report-all-tagged-default") == 0) *withdefault = "report-all-tagged"; else if (strcmp(e, "NULL") == 0){ e = NULL; *withdefault = NULL; } else if (strcmp(e, "report-all") != 0 && strcmp(e, "trim") != 0 && strcmp(e, "explicit") != 0 && strcmp(e, "report-all-tagged") != 0){ clicon_err(OE_YANG, EINVAL, "Unexpected with-default option: %s", e); goto done; } else *withdefault = e; *extdefault = e; retval = 0; done: return retval; } /*! Generic show configuration callback * * Does not need to be used with the autocli as cli_show_auto does * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] argv String vector of show options, format: * Name of datastore, such as "running" * -- from here optional: * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum), default: xml * xpath expression, that may contain one %, eg "/sender[name='foo']" * xpath default namespace (or NULL) not needed for xpath=NULL * true|false: pretty-print or not * true|false: also print state * Retrieval mode: report-all, trim, explicit, report-all-tagged, * NULL, report-all-tagged-default, report-all-tagged-strip (extended) * CLI prefix: prepend before cli syntax output * @code * clispec: * show config, cli_show_config("running","xml"); * cli run: * > set table parameter a value x * > show config * * * a * x * *
* @endcode * @see cli_show_auto autocli with expansion * @see cli_show_auto_mode autocli with edit menu support */ int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; char *dbname; enum format_enum format = FORMAT_XML; cvec *nsc = NULL; int pretty = 1; char *prefix = NULL; int state = 0; char *withdefault = NULL; /* RFC 6243 modes */ char *extdefault = NULL; /* with extended tagged modes */ int argc = 0; int skiproot = 0; char *xpath = "/"; char *namespace = NULL; if (cvec_len(argv) < 2 || cvec_len(argv) > 8){ clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: [ ]", cvec_len(argv)); goto done; } dbname = cv_string_get(cvec_i(argv, argc++)); if (cvec_len(argv) > argc) if (cli_show_option_format(argv, argc++, &format) < 0) goto done; if (cvec_len(argv) > argc) xpath = cv_string_get(cvec_i(argv, argc++)); if (cvec_len(argv) > argc){ namespace = cv_string_get(cvec_i(argv, argc++)); /* Special symbol NULL means no namespace */ if (strcmp(namespace, "NULL") != 0) if ((nsc = xml_nsctx_init(NULL, namespace)) == NULL) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_bool(argv, argc++, &pretty) < 0) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_bool(argv, argc++, &state) < 0) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_withdefault(argv, argc++, &withdefault, &extdefault) < 0) goto done; } if (cvec_len(argv) > argc){ prefix = cv_string_get(cvec_i(argv, argc++)); } if (cli_show_common(h, dbname, format, pretty, state, withdefault, extdefault, prefix, xpath, nsc, skiproot) < 0) goto done; retval = 0; done: if (nsc) xml_nsctx_free(nsc); return retval; } /*! Show configuration and state CLIGEN callback function * * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] argv String vector of show options, format: * "running"|"candidate"|"startup" * @code * show config id , cli_show_config("running","xml","iface[name='foo']","urn:example:example"); * @endcode * @see cli_show_config_state For config and state data (not only config) */ /*! Show configuration as text given an xpath using canonical namespace * * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line must contain xpath and default namespace (if any) * @param[in] argv A string: * @note Different from cli_show_conf: values taken cvv "xpath" and "ns" instead of argv */ int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; char *dbname; char *xpath; cg_var *cv; cvec *nsc = NULL; yang_stmt *yspec; if (cvec_len(argv) != 1){ clicon_err(OE_PLUGIN, EINVAL, "Requires one element to be "); goto done; } if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } dbname = cv_string_get(cvec_i(argv, 0)); /* Look for xpath in command (kludge: cv must be called "xpath") */ if ((cv = cvec_find(cvv, "xpath")) == NULL){ clicon_err(OE_PLUGIN, EINVAL, "Requires one variable to be "); goto done; } xpath = cv_string_get(cv); /* Create canonical namespace */ if (xml_nsctx_yangspec(yspec, &nsc) < 0) goto done; /* Look for and add default namespace variable in command */ if ((cv = cvec_find(cvv, "ns")) != NULL){ if (xml_nsctx_add(nsc, NULL, cv_string_get(cv)) < 0) goto done; } if (cli_show_common(h, dbname, FORMAT_XML, 1, 0, NULL, NULL, NULL, xpath, nsc, 0) < 0) goto done; retval = 0; done: if (nsc) xml_nsctx_free(nsc); return retval; } int cli_show_version(clicon_handle h, cvec *vars, cvec *argv) { fprintf(stdout, "%s\n", CLIXON_VERSION_STRING); return 0; } /*! Show configuration callback using auto CLI syntax with expansion * * Can be used only in context of an autocli generated syntax tree, such as: * show @datamodel, cli_show_auto(); * This show command can use expansion to "TAB" inside the syntax tree to show * portions of the syntax. * @param[in] h Clixon handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] argv String vector of show options, format: * Generated API PATH * Name of datastore, such as "running" * -- from here optional: * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum), default: xml * true|false: pretty-print or not * true|false: also print state * Retrieval mode: report-all, trim, explicit, report-all-tagged, * NULL, report-all-tagged-default, report-all-tagged-strip (extended) * CLI prefix: prepend before cli syntax output * @code * clispec: * show config @datamodelshow, cli_show_auto("candidate", "xml"); * cli run: * > set table parameter a value x * > show config table parameter a * * a * x * * @endcode * @see cli_show_config with no autocli coupling * @see cli_show_auto_mode autocli with edit menu support * * XXX merge cli_show_auto and cli_show_auto_mode */ int cli_show_auto(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; char *dbname; enum format_enum format = FORMAT_XML; cvec *nsc = NULL; int pretty = 1; char *prefix = NULL; int state = 0; char *withdefault = NULL; /* RFC 6243 modes */ char *extdefault = NULL; /* with extended tagged modes */ int argc = 0; int skiproot = 0; char *xpath = NULL; yang_stmt *yspec; char *api_path = NULL; int cvvi = 0; char *api_path_fmt; /* xml key format */ if (cvec_len(argv) < 2 || cvec_len(argv) > 7){ clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected:: * [ ]", cvec_len(argv)); goto done; } api_path_fmt = cv_string_get(cvec_i(argv, argc++)); dbname = cv_string_get(cvec_i(argv, argc++)); if (cvec_len(argv) > argc) if (cli_show_option_format(argv, argc++, &format) < 0) goto done; if (cvec_len(argv) > argc){ if (cli_show_option_bool(argv, argc++, &pretty) < 0) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_bool(argv, argc++, &state) < 0) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_withdefault(argv, argc++, &withdefault, &extdefault) < 0) goto done; } if (cvec_len(argv) > argc){ prefix = cv_string_get(cvec_i(argv, argc++)); } if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path, &cvvi) < 0) goto done; if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) goto done; if (xpath == NULL){ clicon_err(OE_FATAL, 0, "Invalid api-path-fmt: %s", api_path_fmt); goto done; } if (cli_show_common(h, dbname, format, pretty, state, withdefault, extdefault, prefix, xpath, nsc, skiproot) < 0) goto done; retval = 0; done: if (nsc) xml_nsctx_free(nsc); if (xpath) free(xpath); if (api_path) free(api_path); return retval; } /*! Show configuration callback for autocli edit modes using tree working point * * Can be used together with "edit modes". The xpath is derived from * the current "cli-edit-mode" as described here: * https://clixon-docs.readthedocs.io/en/latest/cli.html#edit-modes * @param[in] h Clixon handle * @param[in] cvv Vector of variables from CLIgen command-line * @param[in] argv String vector of show options, format: * Name of datastore, such as "running" * -- from here optional: * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum), default: xml * true|false: pretty-print or not * true|false: also print state * Retrieval mode: report-all, trim, explicit, report-all-tagged, * NULL, report-all-tagged-default, report-all-tagged-strip (extended) * CLI prefix: prepend before cli syntax output * @code * clispec: * show config, cli_show_auto_mode("candidate"); * cli run: * > set table parameter a value x * > edit table * > show config * * a * x * * @endcode * @see cli_show_auto autocli with expansion * @see cli_show_config with no autocli coupling * * XXX merge cli_show_auto and cli_show_auto_mode */ int cli_show_auto_mode(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; char *dbname; enum format_enum format = FORMAT_XML; cvec *nsc = NULL; int pretty = 1; char *prefix = NULL; int state = 0; char *withdefault = NULL; /* RFC 6243 modes */ char *extdefault = NULL; /* with extended tagged modes */ int argc = 0; int skiproot = 0; char *xpath = NULL; yang_stmt *yspec; char *api_path = NULL; if (cvec_len(argv) < 2 || cvec_len(argv) > 7){ clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: [ ]", cvec_len(argv)); goto done; } dbname = cv_string_get(cvec_i(argv, argc++)); if (cvec_len(argv) > argc) if (cli_show_option_format(argv, argc++, &format) < 0) goto done; if (cvec_len(argv) > argc){ if (cli_show_option_bool(argv, argc++, &pretty) < 0) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_bool(argv, argc++, &state) < 0) goto done; } if (cvec_len(argv) > argc){ if (cli_show_option_withdefault(argv, argc++, &withdefault, &extdefault) < 0) goto done; } if (cvec_len(argv) > argc){ prefix = cv_string_get(cvec_i(argv, argc++)); } /* Store this as edit-mode */ if (clicon_data_get(h, "cli-edit-mode", &api_path) == 0 && strlen(api_path)) ; else api_path = "/"; if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; } if (api_path2xpath(api_path, yspec, &xpath, &nsc, NULL) < 0) goto done; if (xpath == NULL){ clicon_err(OE_FATAL, 0, "Invalid api-path: %s", api_path); goto done; } skiproot = (strcmp(xpath,"/") != 0); if (cli_show_common(h, dbname, format, pretty, state, withdefault, extdefault, prefix, xpath, nsc, skiproot) < 0) goto done; retval = 0; done: if (nsc) xml_nsctx_free(nsc); if (xpath) free(xpath); return retval; } /*! Obsolete Show configuration callback for autocli edit modes using tree working point * * @note Please use cli_show_auto_mode instead, * but since that function does not use treename(argv[0]) that must be stripped */ int cli_auto_show(clicon_handle h, cvec *cvv, cvec *argv0) { int retval = -1; cvec *argv1 = NULL; cg_var *cv; if ((argv1 = cvec_new(0)) == NULL){ clicon_err(OE_UNIX, errno, "cvec_new"); goto done; } cv = NULL; while ((cv = cvec_each1(argv0, cv)) != NULL) { if (cvec_append_var(argv1, cv) == NULL){ clicon_err(OE_UNIX, errno, "cvec_append_var"); goto done; } } if (cli_show_auto_mode(h, cvv, argv1) < 0) goto done; retval = 0; done: if (argv1) cvec_free(argv1); return retval; } /*! Show clixon configuration options as loaded */ int cli_show_options(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; clicon_hash_t *hash = clicon_options(h); int i; char **keys = NULL; void *val; size_t klen; size_t vlen; cxobj *x = NULL; if (clicon_hash_keys(hash, &keys, &klen) < 0) goto done; for(i = 0; i < klen; i++) { val = clicon_hash_value(hash, keys[i], &vlen); if (vlen){ if (((char*)val)[vlen-1]=='\0') /* assume string */ fprintf(stdout, "%s: \"%s\"\n", keys[i], (char*)val); else fprintf(stdout, "%s: 0x%p , length %zu\n", keys[i], val, vlen); } else fprintf(stdout, "%s: NULL\n", keys[i]); } /* Next print CLICON_FEATURE, CLICON_YANG_DIR and CLICON_SNMP_MIB from config tree * Since they are lists they are placed in the config tree. */ x = NULL; while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) { if (strcmp(xml_name(x), "CLICON_YANG_DIR") != 0) continue; fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x)); } x = NULL; while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) { if (strcmp(xml_name(x), "CLICON_FEATURE") != 0) continue; fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x)); } x = NULL; while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) { if (strcmp(xml_name(x), "CLICON_SNMP_MIB") != 0) continue; fprintf(stdout, "%s: \"%s\"\n", xml_name(x), xml_body(x)); } retval = 0; done: if (keys) free(keys); return retval; } /*! Show pagination * @param[in] h Clicon handle * @param[in] cvv Vector of cli string and instantiated variables * @param[in] argv Vector. Format: * Also, if there is a cligen variable called "xpath" it will override argv xpath arg */ int cli_pagination(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; cbuf *cb = NULL; char *xpath = NULL; char *prefix = NULL; char *namespace = NULL; cxobj *xret = NULL; cxobj *xerr; cvec *nsc = NULL; char *str; enum format_enum format; cxobj *xc; cg_var *cv; int i; int j; uint32_t limit = 0; cxobj **xvec = NULL; size_t xlen; int locked = 0; if (cvec_len(argv) != 5){ clicon_err(OE_PLUGIN, 0, "Expected usage: "); goto done; } /* prefix:variable overrides argv */ if ((cv = cvec_find(cvv, "xpath")) != NULL) xpath = cv_string_get(cv); else xpath = cvec_i_str(argv, 0); prefix = cvec_i_str(argv, 1); namespace = cvec_i_str(argv, 2); str = cv_string_get(cvec_i(argv, 3)); /* Fourthformat: output format */ if ((int)(format = format_str2int(str)) < 0){ clicon_err(OE_PLUGIN, 0, "Not valid format: %s", str); goto done; } if ((str = cv_string_get(cvec_i(argv, 4))) != NULL){ if (parse_uint32(str, &limit, NULL) < 1){ clicon_err(OE_UNIX, errno, "error parsing limit:%s", str); goto done; } } if (limit == 0){ clicon_err(OE_UNIX, EINVAL, "limit is 0"); goto done; } if ((nsc = xml_nsctx_init(prefix, namespace)) == NULL) goto done; if (clicon_rpc_lock(h, "running") < 0) goto done; locked++; for (i = 0;; i++){ if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc, CONTENT_ALL, -1, /* depth */ NULL, /* with-default */ limit*i, /* offset */ limit, /* limit */ NULL, NULL, NULL, /* nyi */ &xret) < 0){ goto done; } if ((xerr = xpath_first(xret, NULL, "/rpc-error")) != NULL){ clixon_netconf_error(xerr, "Get configuration", NULL); goto done; } if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0) goto done; for (j = 0; j