diff --git a/CHANGELOG.md b/CHANGELOG.md index bf3fb545..59dc36e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,12 @@ ## 4.5.0 Expected: May 2020 +### C-API changes on existing features (you may need to change your plugin C-code) + +* CLI + * `clicon_parse()`: Changed signature due to new cligen error and result handling: + * Removed: `cli_nomatch()` + ## 4.4.0 5 April 2020 diff --git a/apps/cli/cli_handle.c b/apps/cli/cli_handle.c index 7b0ebc79..535327da 100644 --- a/apps/cli/cli_handle.c +++ b/apps/cli/cli_handle.c @@ -2,7 +2,9 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -191,14 +193,6 @@ cli_interrupt_hook(clicon_handle h, return cligen_interrupt_hook(ch, fn); } -char * -cli_nomatch(clicon_handle h) -{ - cligen_handle ch = cligen(h); - - return cligen_nomatch(ch); -} - int cli_prompt_set(clicon_handle h, char *prompt) diff --git a/apps/cli/cli_handle.h b/apps/cli/cli_handle.h index c7fd728b..50667d4b 100644 --- a/apps/cli/cli_handle.h +++ b/apps/cli/cli_handle.h @@ -2,7 +2,9 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -51,8 +53,6 @@ int cli_susp_hook(clicon_handle h, cligen_susp_cb_t *fn); int cli_interrupt_hook(clicon_handle h, cligen_interrupt_cb_t *fn); -char *cli_nomatch(clicon_handle h); - int cli_prompt_set(clicon_handle h, char *prompt); int cli_logsyntax_set(clicon_handle h, int status); diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index f00948bc..8c49a64e 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -212,11 +212,10 @@ cli_signal_init (clicon_handle h) static int cli_interactive(clicon_handle h) { - int retval = -1; - int res; - char *cmd; - char *new_mode; - int eval; + int retval = -1; + char *cmd; + char *new_mode; + cligen_result result; /* Loop through all commands */ while(!cligen_exiting(cli_cligen(h))) { @@ -225,8 +224,9 @@ cli_interactive(clicon_handle h) cligen_exiting_set(cli_cligen(h), 1); /* EOF */ goto ok; /* EOF should not be -1 error? */ } - if ((res = clicon_parse(h, cmd, &new_mode, &eval)) < 0) + if (clicon_parse(h, cmd, &new_mode, &result, NULL) < 0) goto done; + /* Why not check result? */ } ok: retval = 0; @@ -587,8 +587,10 @@ main(int argc, char **argv) /* Join rest of argv to a single command */ restarg = clicon_strjoin(argc, argv, " "); +#if 0 /* Unsure for why this is enabled by default, turned off in clixon 4.5? */ /* If several cligen object variables match same preference, select first */ cligen_preference_mode_set(cli_cligen(h), 1); +#endif /* Call start function in all plugins before we go interactive */ @@ -603,13 +605,15 @@ main(int argc, char **argv) cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8")); /* Launch interfactive event loop, unless -1 */ if (restarg != NULL && strlen(restarg)){ - char *mode = cli_syntax_mode(h); - int result; + char *mode = cli_syntax_mode(h); + cligen_result result; /* match result */ + int evalresult = 0; /* if result == 1, calback result */ - /* */ - if (clicon_parse(h, restarg, &mode, &result) != 1) + if (clicon_parse(h, restarg, &mode, &result, &evalresult) < 0) goto done; - if (result < 0) + if (result != 1) /* Not unique match */ + goto done; + if (evalresult < 0) goto done; } diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index c8886019..74f0fc63 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -2,7 +2,9 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -465,10 +467,10 @@ cli_handler_err(FILE *f) } /*! Evaluate a matched command - * @param[in] h Clicon handle - * @param[in] cmd The command string - * @retval int If there is a callback, the return value of the callback is returned, - * @retval 0 otherwise + * @param[in] h Clicon handle + * @param[in] cmd The command string + * @retval int If there is a callback, the return value of the callback is returned, + * @retval 0 otherwise */ int clicon_eval(clicon_handle h, @@ -501,35 +503,32 @@ clicon_eval(clicon_handle h, * match is found in another mode, the mode variable is updated to point at * the new mode string. * - * @param[in] h Clicon handle - * @param[in] cmd Command string - * @param[in,out] modenamep Pointer to the mode string pointer - * @param[out] evalres Evaluation result if retval=1 - * -2 On eof (shouldnt happen) - * -1 On parse error - * >=0 Number of matches - * @retval -2 Eof CG_EOF - * @retval -1 Error CG_ERROR - * @retval 0 No match CG_NOMATCH - * @retval 1 Exactly one match CG_MATCH - * @retval 2+ Multiple matches + * @param[in] h Clicon handle + * @param[in] cmd Command string + * @param[in,out] modenamep Pointer to the mode string pointer + * @param[out] result CLIgen match result, < 0: errors, >=0 number of matches + * @param[out] evalres Evaluation result if result=1 + * @retval 0 OK + * @retval -1 Error */ int -clicon_parse(clicon_handle h, - char *cmd, - char **modenamep, - int *evalres) +clicon_parse(clicon_handle h, + char *cmd, + char **modenamep, + cligen_result *result, + int *evalres) { - int retval = -1; - char *modename; - char *modename0; - int r; + int retval = -1; + char *modename; + char *modename0; + int r; cli_syntax_t *stx = NULL; cli_syntaxmode_t *smode; - parse_tree *pt; /* Orig */ - cg_obj *match_obj; - cvec *cvv = NULL; - FILE *f; + parse_tree *pt; /* Orig */ + cg_obj *match_obj; + cvec *cvv = NULL; + FILE *f; + char *reason = NULL; if (clicon_get_logflags()&CLICON_LOG_STDOUT) f = stdout; @@ -562,22 +561,21 @@ clicon_parse(clicon_handle h, clicon_err(OE_UNIX, errno, "cvec_new"); goto done;; } - retval = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv); - if (retval != CG_MATCH) + if (cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv, result, &reason) < 0) + goto done; + if (*result != CG_MATCH) pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */ if (modename0){ cligen_tree_active_set(cli_cligen(h), modename0); modename0 = NULL; } - switch (retval) { + switch (*result) { case CG_EOF: /* eof */ case CG_ERROR: fprintf(f, "CLI parse error: %s\n", cmd); break; case CG_NOMATCH: /* no match */ - /* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s", - cmd, cli_nomatch(h));*/ - fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, cli_nomatch(h)); + fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, reason); break; case CG_MATCH: if (strcmp(modename, *modenamep)){ /* Command in different mode */ @@ -593,9 +591,12 @@ clicon_parse(clicon_handle h, default: fprintf(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd); break; - } /* switch retval */ + } /* switch result */ } + retval = 0; done: + if (reason) + free(reason); if (cvv) cvec_free(cvv); return retval; diff --git a/apps/cli/cli_plugin.h b/apps/cli/cli_plugin.h index 75aa99c2..365085f7 100644 --- a/apps/cli/cli_plugin.h +++ b/apps/cli/cli_plugin.h @@ -2,7 +2,9 @@ * ***** BEGIN LICENSE BLOCK ***** - Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren + Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren + Copyright (C) 2017-2019 Olof Hagsand + Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC This file is part of CLIXON. @@ -65,7 +67,7 @@ void *clixon_str2fn(char *name, void *handle, char **error); int clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr); -int clicon_parse(clicon_handle h, char *cmd, char **mode, int *result); +int clicon_parse(clicon_handle h, char *cmd, char **mode, cligen_result *result, int *evalres); char *clicon_cliread(clicon_handle h); diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 5eca2db1..dcabb69f 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -62,10 +62,6 @@ int xml_namespace_change(cxobj *x, char *namespace, char *prefix); int xml_default(cxobj *x, void *arg); int xml_sanity(cxobj *x, void *arg); int xml_non_config_data(cxobj *xt, void *arg); -int xml_bind_yang_rpc(cxobj *xrpc, yang_stmt *yspec, cxobj **xerr); -int xml_bind_yang_rpc_reply(cxobj *xrpc, char *name, yang_stmt *yspec, cxobj **xerr); -int xml_bind_yang0(cxobj *xt, yang_bind yb, yang_stmt *yspec, cxobj **xerr); -int xml_bind_yang(cxobj *xt, yang_bind yb, yang_stmt *yspec, cxobj **xerr); int xml2xpath(cxobj *x, char **xpath); int assign_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p); diff --git a/lib/src/clixon_datastore.c b/lib/src/clixon_datastore.c index 1c9debde..575bf4f8 100644 --- a/lib/src/clixon_datastore.c +++ b/lib/src/clixon_datastore.c @@ -118,7 +118,7 @@ xmldb_db2file(clicon_handle h, return retval; } -/*! Validate database name +/*! Ensure database name is correct * @param[in] db Name of database * @retval 0 OK * @retval -1 Failed validate, xret set to error diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 124d69d6..73f37668 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1022,406 +1022,6 @@ xml_non_config_data(cxobj *xt, return retval; } -/*! Associate XML node x with x:s parents yang:s matching child - * - * @param[in] xt XML tree node - * @param[out] xerr Reason for failure, or NULL - * @retval 2 OK yang assignment made - * @retval 1 OK, Yang assignment not made because yang parent is anyxml or anydata - * @retval 0 Yang assigment not made and xerr set - * @retval -1 Error - * @see populate_self_top - */ -static int -populate_self_parent(cxobj *xt, - cxobj **xerr) -{ - int retval = -1; - yang_stmt *y = NULL; /* yang node */ - yang_stmt *yparent; /* yang parent */ - cxobj *xp = NULL; /* xml parent */ - char *name; - char *ns = NULL; /* XML namespace of xt */ - char *nsy = NULL; /* Yang namespace of xt */ - - xp = xml_parent(xt); - name = xml_name(xt); - if (xp == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing parent") < 0) - goto done; - goto fail; - } - if ((yparent = xml_spec(xp)) == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing parent yang node") < 0) - goto done; - goto fail; - } - if (yang_keyword_get(yparent) == Y_ANYXML || yang_keyword_get(yparent) == Y_ANYDATA){ - retval = 1; - goto done; - } - if ((y = yang_find_datanode(yparent, name)) == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing matching yang node") < 0) - goto done; - goto fail; - } - if (xml2ns(xt, xml_prefix(xt), &ns) < 0) - goto done; - nsy = yang_find_mynamespace(y); - if (ns == NULL || nsy == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0) - goto done; - goto fail; - } - /* Assign spec only if namespaces match */ - if (strcmp(ns, nsy) != 0){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0) - goto done; - goto fail; - } - xml_spec_set(xt, y); -#ifdef XML_EXPLICIT_INDEX - if (xml_search_index_p(xt)) - xml_search_child_insert(xp, xt); -#endif - retval = 2; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! Associate XML node x with yang spec y by going through all top-level modules and finding match - * - * @param[in] xt XML tree node - * @param[in] yspec Yang spec - * @param[out] xerr Reason for failure, or NULL - * @retval 2 OK yang assignment made - * @retval 1 OK, Yang assignment not made because yang parent is anyxml or anydata - * @retval 0 yang assigment not made and xerr set - * @retval -1 Error - * @see populate_self_parent - */ -static int -populate_self_top(cxobj *xt, - yang_stmt *yspec, - cxobj **xerr) -{ - int retval = -1; - yang_stmt *y = NULL; /* yang node */ - yang_stmt *ymod; /* yang module */ - char *name; - char *ns = NULL; /* XML namespace of xt */ - char *nsy = NULL; /* Yang namespace of xt */ - - name = xml_name(xt); - if (yspec == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing yang spec") < 0) - goto done; - goto fail; - } - if (ys_module_by_xml(yspec, xt, &ymod) < 0) - goto done; - /* ymod is "real" module, name may belong to included submodule */ - if (ymod == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "No such yang module") < 0) - goto done; - goto fail; - } - if ((y = yang_find_schemanode(ymod, name)) == NULL){ /* also rpc */ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing matching yang node") < 0) - goto done; - goto fail; - } - if (xml2ns(xt, xml_prefix(xt), &ns) < 0) - goto done; - nsy = yang_find_mynamespace(y); - if (ns == NULL || nsy == NULL){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0) - goto done; - goto fail; - } - /* Assign spec only if namespaces match */ - if (strcmp(ns, nsy) != 0){ - if (xerr && - netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0) - goto done; - goto fail; - } - xml_spec_set(xt, y); - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! After yang binding, bodies of containers and lists are stripped from XML bodies - * May apply to other nodes? - */ -static int -strip_whitespace(cxobj *xt) -{ - yang_stmt *yt; - enum rfc_6020 keyword; - cxobj *xc; - - if ((yt = xml_spec(xt)) != NULL){ - keyword = yang_keyword_get(yt); - if (keyword == Y_LIST || keyword == Y_CONTAINER){ - xc = NULL; - while ((xc = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL) - xml_purge(xc); - } - } - return 0; -} - -/*! Find yang spec association of tree of XML nodes - * - * Populate xt:s children as top-level symbols - * This may be unnecessary if yspec is set on manual creation. Also note that for incoming or - * outgoing RPC need specialized function. maybe it can be built into the same function, but - * you dont know whether it is input or output rpc. - * @param[in] xt XML tree node - * @param[in] yspec Yang spec - * @param[out] xerr Reason for failure, or NULL - * @retval 1 OK yang assignment made - * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set - * @retval -1 Error - * @code - * if (xml_bind_yang(x, YB_MODULE, yspec, NULL) < 0) - * err; - * @endcode - * @note For subs to anyxml nodes will not have spec set - * There are several functions in the API family - * @see xml_bind_yang_rpc for incoming rpc - * @see xml_bind_yang0 If the calling xml object should also be populated - */ -int -xml_bind_yang(cxobj *xt, - yang_bind yb, - yang_stmt *yspec, - cxobj **xerr) -{ - int retval = -1; - cxobj *xc; /* xml child */ - int ret; - int failed = 0; /* we continue loop after failure, should we stop at fail?`*/ - - strip_whitespace(xt); - xc = NULL; /* Apply on children */ - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - if ((ret = xml_bind_yang0(xc, yb, yspec, xerr)) < 0) - goto done; - if (ret == 0) - failed++; - } - if (failed) - goto fail; - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! Find yang spec association of tree of XML nodes - * - * @param[in] xt XML tree node - * @param[in] yb How to bind yang to XML top-level when parsing - * @param[in] yspec Yang spec - * @param[out] xerr Reason for failure, or NULL - * @retval 1 OK yang assignment made - * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set - * @retval -1 Error - * Populate xt as top-level node - */ -int -xml_bind_yang0(cxobj *xt, - yang_bind yb, - yang_stmt *yspec, - cxobj **xerr) -{ - int retval = -1; - cxobj *xc; /* xml child */ - int ret; - int failed = 0; /* we continue loop after failure, should we stop at fail?`*/ - - switch (yb){ - case YB_MODULE: - if ((ret = populate_self_top(xt, yspec, xerr)) < 0) - goto done; - break; - case YB_PARENT: - if ((ret = populate_self_parent(xt, xerr)) < 0) - goto done; - break; - default: - clicon_err(OE_XML, EINVAL, "Invalid yang binding: %d", yb); - goto done; - break; - } - if (ret == 0) - goto fail; - strip_whitespace(xt); - xc = NULL; /* Apply on children */ - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - if ((ret = xml_bind_yang0(xc, YB_PARENT, yspec, xerr)) < 0) - goto done; - if (ret == 0) - failed++; - } - if (failed) - goto fail; - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! Find yang spec association of XML node for incoming RPC starting with - * - * Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang - * @param[in] xrpc XML rpc node - * @param[in] yspec Yang spec - * @param[out] xerr Reason for failure, or NULL - * @retval 1 OK yang assignment made - * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set - * @retval -1 Error - * The - * @code - * if (xml_bind_yang_rpc(h, x, NULL) < 0) - * err; - * @endcode - * @see xml_bind_yang For other generic cases - * @see xml_bind_yang_rpc_reply - */ -int -xml_bind_yang_rpc(cxobj *xrpc, - yang_stmt *yspec, - cxobj **xerr) -{ - int retval = -1; - yang_stmt *yrpc = NULL; /* yang node */ - yang_stmt *ymod=NULL; /* yang module */ - yang_stmt *yi = NULL; /* input */ - cxobj *x; - int ret; - - if ((strcmp(xml_name(xrpc), "rpc")) != 0){ - clicon_err(OE_UNIX, EINVAL, "RPC expected"); - goto done; - } - x = NULL; - while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) { - if (ys_module_by_xml(yspec, x, &ymod) < 0) - goto done; - if (ymod != NULL) - yrpc = yang_find(ymod, Y_RPC, xml_name(x)); - /* Non-strict semantics: loop through all modules to find the node - */ - if (yrpc){ - xml_spec_set(x, yrpc); - if ((yi = yang_find(yrpc, Y_INPUT, NULL)) != NULL){ - /* xml_bind_yang need to have parent with yang spec for - * recursive population to work. Therefore, assign input yang - * to rpc level although not 100% intuitive */ - xml_spec_set(x, yi); - if ((ret = xml_bind_yang(x, YB_PARENT, NULL, xerr)) < 0) - goto done; - if (ret == 0) - goto fail; - } - } - } - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - -/*! Find yang spec association of XML node for outgoing RPC starting with - * - * Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang - * @param[in] xrpc XML rpc node - * @param[in] name Name of RPC (not seen in output/reply) - * @param[in] yspec Yang spec - * @param[out] xerr Reason for failure, or NULL - * @retval 1 OK yang assignment made - * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set - * @retval -1 Error - * - * @code - * if (xml_bind_yang_rpc_reply(x, "get-config", yspec, name) < 0) - * err; - * @endcode - * @see xml_bind_yang For other generic cases - */ -int -xml_bind_yang_rpc_reply(cxobj *xrpc, - char *name, - yang_stmt *yspec, - cxobj **xerr) -{ - int retval = -1; - yang_stmt *yrpc = NULL; /* yang node */ - yang_stmt *ymod=NULL; /* yang module */ - yang_stmt *yo = NULL; /* output */ - cxobj *x; - int ret; - - if (strcmp(xml_name(xrpc), "rpc-reply")){ - clicon_err(OE_UNIX, EINVAL, "rpc-reply expected"); - goto done; - } - x = NULL; - while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) { - if (ys_module_by_xml(yspec, x, &ymod) < 0) - goto done; - if (ymod == NULL) - continue; - if ((yrpc = yang_find(ymod, Y_RPC, name)) == NULL) - continue; - // xml_spec_set(xrpc, yrpc); - if ((yo = yang_find(yrpc, Y_OUTPUT, NULL)) == NULL) - continue; - /* xml_bind_yang need to have parent with yang spec for - * recursive population to work. Therefore, assign input yang - * to rpc level although not 100% intuitive */ - break; - } - if (yo != NULL){ - xml_spec_set(xrpc, yo); - if ((ret = xml_bind_yang(xrpc, YB_MODULE, yspec, xerr)) < 0) - goto done; - if (ret == 0) - goto fail; - } - retval = 1; - done: - return retval; - fail: - retval = 0; - goto done; -} - /*! Given an XML node, build an xpath to root, internal function * @retval 0 OK * @retval -1 Error. eg XML malformed