diff --git a/CHANGELOG.md b/CHANGELOG.md index 73003756..5e194713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,23 +76,28 @@ features include optimized search functions and a repair callback. * If you have used, `ys`, add `xml_spec_set(x, ys)` after the statement * If you have `xml_type_set(x, TYPE)` after the statement, you can remove it and set it directly as: `xml_new(name, xp, TYPE)` * `xml_type_set()`has been removed in the API. The type must be set at creation timw with `xml_new` +* `clicon_rpc_generate_error()` renamed to `clixon_netconf_error()` and added a category parameter * All uses of `api_path2xpath_cvv()` should be replaced by `api_path2xpath()` * `api_path2xpath()` added an `xerr` argument. -* Parse and validation API more capable - * `xml_spec_populate` has been split into a family of functions - * `xml_bind_yang_rpc()` - * `xml_bind_yang_rpc_reply()` - * `xml_bind_yang0()` - * `xml_bind_yang0_parent()` - * `xml_bind_yang()` - * `xml_bind_yang_parent()` - * All have three-value return values: -1: error, 0: parse OK, 1: parse and YANG binding OK. - * `xml_parse` and `json_parse` API changes - * Three value returns: -1: error, 0: parse OK, 1: parse and YANG binding OK. - * Extended `xml_parse_file2` and `xml_parse_string2` extended API functions with all options available. - * New concept called `yang_bind` that defines how XML symbols are bound to YANG after parsing - * Existing API same except `xml_parse_file` `endtag` argument moved to `xml_parse_file2` -* Changed `clicon_session_id_get(clicon_handle h, uint32_t *id)` +* XML and JSON parsing functions have been rearranged/cleaned up as follows: + * Three value returns: -1: error, 0: parse OK, 1: parse and YANG binding OK. + * New concept called `yang_bind` that defines how XML symbols are bound to YANG after parsing (see below) + * New XML parsing API: + * `clixon_xml_parse_string()` + * `clixon_xml_parse_file()` + * New JSON parsing API, with same signature as XML parsing: + * `clixon_json_parse_string()` + * `clixon_xml_parse_file()` +* Yang binding type has been introduced as a new concept and used in the API with the following values: + * `YB_MODULE` : Search for matching yang binding among top-level symbols of Yang modules + * `YB_PARENT` : Assume yang binding of existing parent and match its children by name + * `YB_NONE` : Don't do YANG binding +* XML YANG binding API have been rearranged as follows: + * `xml_bind_yang_rpc()` + * `xml_bind_yang_rpc_reply()` + * `xml_bind_yang()` + * `xml_bind_yang0()` + * All have three-value return values: -1: error, 0: YANG binding failed, 1: parse and YANG binding OK. ### Minor changes diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 8f83d01d..a9311c0c 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -186,7 +186,7 @@ client_get_capabilities(clicon_handle h, cprintf(cb, "urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit"); cprintf(cb, "urn:ietf:params:restconf:capability:depth:1.0"); cprintf(cb, ""); - if (xml_parse_string2(cbuf_get(cb), YB_PARENT, NULL, &xrstate, NULL) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_PARENT, NULL, &xrstate, NULL) < 0) goto done; retval = 0; done: @@ -236,7 +236,7 @@ client_get_streams(clicon_handle h, goto done; cprintf(cb,"", top); - if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ + if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &x, NULL) < 0){ if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) goto done; goto fail; @@ -320,7 +320,7 @@ clixon_stats_get(clicon_handle h, clixon_stats_get_db(h, "candidate", cb); clixon_stats_get_db(h, "startup", cb); cprintf(cb, ""); - if ((ret = xml_parse_string2(cbuf_get(cb), YB_TOP, yspec, xret, NULL)) < 0) + if ((ret = clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL)) < 0) goto done; if (ret == 0){ clicon_err(OE_XML, EINVAL, "Internal error"); @@ -376,7 +376,7 @@ client_statedata(clicon_handle h, goto done; } cprintf(cb, "", namespace); - if (xml_parse_string2(cbuf_get(cb), YB_TOP, yspec, xret, NULL) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0) goto done; if ((ret = client_get_streams(h, yspec, xpath, ymod, "netconf", xret)) < 0) goto done; @@ -394,7 +394,7 @@ client_statedata(clicon_handle h, } cbuf_reset(cb); cprintf(cb, "", namespace); - if (xml_parse_string2(cbuf_get(cb), YB_TOP, yspec, xret, NULL) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, xret, NULL) < 0) goto done; if ((ret = client_get_streams(h, yspec, xpath, ymod, "restconf-state", xret)) < 0) goto done; @@ -670,7 +670,7 @@ from_client_edit_config(clicon_handle h, xml_spec_set(xc, NULL); /* Populate XML with Yang spec (why not do this in parser?) */ - if (xml_bind_yang(xc, yspec, NULL) < 0) + if (xml_bind_yang(xc, YB_MODULE, yspec, NULL) < 0) goto done; /* Maybe validate xml here as in text_modify_top? */ if (xml_apply(xc, CX_ELMNT, xml_non_config_data, &non_config) < 0) diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index db48c410..af85797a 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -214,7 +214,7 @@ startup_common(clicon_handle h, goto done; } /* After upgrading, XML tree needs to be sorted and yang spec populated */ - if (xml_bind_yang(xt, yspec, NULL) < 0) + if (xml_bind_yang(xt, YB_MODULE, yspec, NULL) < 0) goto done; if (xml_apply0(xt, CX_ELMNT, xml_sort, h) < 0) goto done; diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index b87338a9..2c091d24 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -211,7 +211,7 @@ nacm_load_external(clicon_handle h) goto done; fd = fileno(f); /* Read configfile */ - if (xml_parse_file(fd, yspec, &xt) < 0) + if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0) goto done; if (xt == NULL){ clicon_err(OE_XML, 0, "No xml tree in %s", filename); diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index 0d754593..244196c8 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -128,7 +128,7 @@ clixon_plugin_statedata(clicon_handle h, if (debug) clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__); #endif - if (xml_bind_yang(x, yspec, NULL) < 0) + if (xml_bind_yang(x, YB_MODULE, yspec, NULL) < 0) goto done; if ((ret = netconf_trymerge(x, yspec, xret)) < 0) goto done; diff --git a/apps/backend/backend_startup.c b/apps/backend/backend_startup.c index 5ef7e9cd..e74fd5fe 100644 --- a/apps/backend/backend_startup.c +++ b/apps/backend/backend_startup.c @@ -176,7 +176,7 @@ load_extraxml(clicon_handle h, goto done; } yspec = clicon_dbspec_yang(h); - if (xml_parse_file(fd, yspec, &xt) < 0) + if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0) goto done; /* Replace parent w first child */ if (xml_rootchild(xt, 0, &xt) < 0) diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 631435d0..0545615c 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -712,13 +712,13 @@ compare_dbs(clicon_handle h, if (clicon_rpc_get_config(h, NULL, "running", "/", NULL, &xc1) < 0) goto done; if ((xerr = xpath_first(xc1, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } if (clicon_rpc_get_config(h, NULL, "candidate", "/", NULL, &xc2) < 0) goto done; if ((xerr = xpath_first(xc2, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */ @@ -795,7 +795,7 @@ load_config_file(clicon_handle h, clicon_err(OE_UNIX, errno, "open(%s)", filename); goto done; } - if (xml_parse_file(fd, NULL, &xt) < 0) + if (clixon_xml_parse_file(fd, YB_NONE, NULL, NULL, &xt, NULL) < 0) goto done; if (xt == NULL) goto done; @@ -882,7 +882,7 @@ save_config_file(clicon_handle h, goto done; } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } /* get-config returns a tree. Save as tree so it can be used @@ -1222,7 +1222,7 @@ cli_copy_config(clicon_handle h, if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cb), nsc, &x1) < 0) goto done; if ((xerr = xpath_first(x1, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 5c545614..a596bb5f 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -159,7 +159,7 @@ expand_dbvar(void *h, if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */ goto done; if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xe, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xe, "Get configuration", NULL); goto ok; } xcur = xt; /* default top-of-tree */ @@ -176,7 +176,7 @@ expand_dbvar(void *h, if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0) goto done; if (ret == 0){ - clicon_rpc_generate_error(xerr, "Expand datastore symbol", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Expand datastore symbol", NULL); goto done; } } @@ -492,7 +492,7 @@ cli_show_config1(clicon_handle h, goto done; } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } /* Print configuration according to format */ @@ -633,7 +633,7 @@ show_conf_xpath(clicon_handle h, if (clicon_rpc_get_config(h, NULL, str, xpath, nsc, &xt) < 0) goto done; if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } @@ -735,7 +735,7 @@ cli_show_auto1(clicon_handle h, goto done; } if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } if ((xp = xpath_first(xt, nsc, "%s", xpath)) != NULL) diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c index ab63825a..41558c6e 100644 --- a/apps/netconf/netconf_lib.c +++ b/apps/netconf/netconf_lib.c @@ -197,7 +197,7 @@ netconf_output(int s, clicon_debug(1, "SEND %s", msg); if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */ cxobj *xt = NULL; - if (xml_parse_string(buf, NULL, &xt) == 0){ + if (clixon_xml_parse_string(buf, YB_NONE, NULL, &xt, NULL) == 0){ clicon_xml2file(stderr, xml_child_i(xt, 0), 0, 0); fprintf(stderr, "\n"); xml_free(xt); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index cfced64f..1dafe9a8 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -138,7 +138,7 @@ netconf_input_packet(clicon_handle h, } str = str0; /* Parse incoming XML message */ - if (xml_parse_string(str, yspec, &xreq) < 0){ + if (clixon_xml_parse_string(str, YB_MODULE, yspec, &xreq, NULL) < 0){ free(str0); if (netconf_operation_failed(cbret, "rpc", clicon_err_reason)< 0) goto done; diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 249f8c5b..b7879a86 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -93,12 +93,12 @@ netconf_get_config_subtree(clicon_handle h, if ((xdata = xpath_first(*xret, NULL, "/rpc-reply/data")) == NULL) goto ok; if (xml_filter(xfilter, xdata) < 0){ - xml_parse_va(xret, NULL, "" - "operation-failed" - "applicatio" - "error" - "filtering" - ""); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-failed" + "applicatio" + "error" + "filtering" + ""); } ok: retval = 0; @@ -180,13 +180,13 @@ netconf_get_config(clicon_handle h, goto done; } else{ - xml_parse_va(xret, NULL, "" - "operation-failed" - "applicatio" - "error" - "filter type not supported" - "type" - ""); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-failed" + "applicatio" + "error" + "filter type not supported" + "type" + ""); } retval = 0; done: @@ -245,11 +245,11 @@ get_edit_opts(cxobj *xn, retval = 1; /* hunky dory */ return retval; parerr: /* parameter error, xret set */ - xml_parse_va(xret, NULL, "" - "invalid-value" - "protocol" - "error" - ""); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "invalid-value" + "protocol" + "error" + ""); return 0; } @@ -322,11 +322,11 @@ netconf_edit_config(clicon_handle h, * (implement the features before removing these checks) */ if (testopt!=TEST_THEN_SET || erropt!=STOP_ON_ERROR){ - xml_parse_va(xret, NULL, "" - "operation-not-supported" - "protocol" - "error" - ""); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-not-supported" + "protocol" + "error" + ""); goto ok; } if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) @@ -377,13 +377,13 @@ netconf_get(clicon_handle h, goto done; } else{ - xml_parse_va(xret, NULL, "" - "operation-failed" - "applicatio" - "error" - "filter type not supported" - "type" - ""); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-failed" + "applicatio" + "error" + "filter type not supported" + "type" + ""); } retval = 0; done: @@ -498,13 +498,13 @@ netconf_create_subscription(clicon_handle h, if ((xfilter = xpath_first(xn, NULL, "//filter")) != NULL){ if ((ftype = xml_find_value(xfilter, "type")) != NULL){ if (strcmp(ftype, "xpath") != 0){ - xml_parse_va(xret, NULL, "" - "operation-failed" - "application" - "error" - "only xpath filter type supported" - "type" - ""); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-failed" + "application" + "error" + "only xpath filter type supported" + "type" + ""); goto ok; } } @@ -572,13 +572,13 @@ netconf_application_rpc(clicon_handle h, if (ys_module_by_xml(yspec, xn, &ymod) < 0) goto done; if (ymod == NULL){ - xml_parse_va(xret, NULL, "" - "operation-failed" - "rpc" - "error" - "%s" - "Not recognized module" - "", xml_name(xn)); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-failed" + "rpc" + "error" + "%s" + "Not recognized module" + "", xml_name(xn)); goto ok; } yrpc = yang_find(ymod, Y_RPC, xml_name(xn)); @@ -587,7 +587,7 @@ netconf_application_rpc(clicon_handle h, /* 1. Check xn arguments with input statement. */ if ((yinput = yang_find(yrpc, Y_INPUT, NULL)) != NULL){ xml_spec_set(xn, yinput); /* needed for xml_bind_yang */ - if (xml_bind_yang(xn, yspec, NULL) < 0) + if (xml_bind_yang(xn, YB_MODULE, yspec, NULL) < 0) goto done; if ((ret = xml_yang_validate_all_top(h, xn, &xerr)) < 0) goto done; @@ -604,7 +604,7 @@ netconf_application_rpc(clicon_handle h, if ((ret = rpc_callback_call(h, xn, cbret, NULL)) < 0) goto done; if (ret > 0){ /* Handled locally */ - if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0) + if (clixon_xml_parse_string(cbuf_get(cbret), YB_NONE, NULL, xret, NULL) < 0) goto done; } else /* Send to backend */ @@ -619,7 +619,7 @@ netconf_application_rpc(clicon_handle h, if ((youtput = yang_find(yrpc, Y_OUTPUT, NULL)) != NULL){ xoutput=xpath_first(*xret, NULL, "/"); xml_spec_set(xoutput, youtput); /* needed for xml_bind_yang */ - if (xml_bind_yang(xoutput, yspec, NULL) < 0) + if (xml_bind_yang(xoutput, YB_MODULE, yspec, NULL) < 0) goto done; if ((ret = xml_yang_validate_all_top(h, xoutput, &xerr)) < 0) goto done; @@ -724,13 +724,13 @@ netconf_rpc_dispatch(clicon_handle h, if ((retval = netconf_application_rpc(h, xe, xret)) < 0) goto done; if (retval == 0){ /* not handled by callback */ - xml_parse_va(xret, NULL, "" - "operation-failed" - "rpc" - "error" - "%s" - "Not recognized" - "", xml_name(xe)); + clixon_xml_parse_va(YB_NONE, NULL, xret, NULL, "" + "operation-failed" + "rpc" + "error" + "%s" + "Not recognized" + "", xml_name(xe)); goto done; } } diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 2168d72b..ed08fd42 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -232,7 +232,9 @@ api_root(clicon_handle h, FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out)); FCGX_FPrintF(r->out, "\r\n"); - if (xml_parse_string("2016-06-21", yspec, &xt) < 0) + if (clixon_xml_parse_string("" + "2016-06-21", + YB_MODULE, yspec, &xt, NULL) < 0) goto done; if ((cb = cbuf_new()) == NULL){ clicon_err(OE_XML, errno, "cbuf_new"); @@ -283,7 +285,9 @@ api_yang_library_version(clicon_handle h, FCGX_FPrintF(r->out, "Cache-Control: no-cache\r\n"); FCGX_FPrintF(r->out, "Content-Type: %s\r\n", restconf_media_int2str(media_out)); FCGX_FPrintF(r->out, "\r\n"); - if (xml_parse_va(&xt, NULL, "%s", ietf_yang_library_revision) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL, + "%s", + ietf_yang_library_revision) < 0) goto done; if (xml_rootchild(xt, 0, &xt) < 0) goto done; diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index ffe07f18..5635f254 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -262,7 +262,7 @@ api_data_write(clicon_handle h, char *namespace = NULL; char *dname; cvec *nsc = NULL; - enum yang_bind yb; + yang_bind yb; char *xpath = NULL; clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path0); @@ -380,7 +380,7 @@ api_data_write(clicon_handle h, } } if (xml_spec(xdata0)==NULL) - yb = YB_TOP; + yb = YB_MODULE; else yb = YB_PARENT; @@ -392,7 +392,7 @@ api_data_write(clicon_handle h, */ switch (media_in){ case YANG_DATA_XML: - if ((ret = xml_parse_string2(data, yb, yspec, &xdata0, &xerr)) < 0){ + if ((ret = clixon_xml_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -414,7 +414,7 @@ api_data_write(clicon_handle h, } break; case YANG_DATA_JSON: - if ((ret = json_parse_str2(data, yb, yspec, &xdata0, &xerr)) < 0){ + if ((ret = clixon_json_parse_string(data, yb, yspec, &xdata0, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ diff --git a/apps/restconf/restconf_methods_get.c b/apps/restconf/restconf_methods_get.c index e42b5792..3c069b14 100644 --- a/apps/restconf/restconf_methods_get.c +++ b/apps/restconf/restconf_methods_get.c @@ -134,7 +134,7 @@ api_data_get2(clicon_handle h, if ((xtop = xml_new("top", NULL, CX_ELMNT)) == NULL) goto done; /* Translate api-path to xml, but to validate the api-path, note: strict=1 - * xtop and xbot unnecessary fir this function but neede by function + * xtop and xbot unnecessary for this function but needed by function */ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0) goto done; diff --git a/apps/restconf/restconf_methods_post.c b/apps/restconf/restconf_methods_post.c index 5308134b..ec703c9d 100644 --- a/apps/restconf/restconf_methods_post.c +++ b/apps/restconf/restconf_methods_post.c @@ -128,7 +128,7 @@ api_data_post(clicon_handle h, int ret; restconf_media media_in; int nrchildren0 = 0; - enum yang_bind yb; + yang_bind yb; clicon_debug(1, "%s api_path:\"%s\"", __FUNCTION__, api_path); clicon_debug(1, "%s data:\"%s\"", __FUNCTION__, data); @@ -180,7 +180,7 @@ api_data_post(clicon_handle h, xml_flag_set(x, XML_FLAG_MARK); } if (xml_spec(xbot)==NULL) - yb = YB_TOP; + yb = YB_MODULE; else yb = YB_PARENT; /* Parse input data as json or xml into xml @@ -190,7 +190,7 @@ api_data_post(clicon_handle h, media_in = restconf_content_type(r); switch (media_in){ case YANG_DATA_XML: - if ((ret = xml_parse_string2(data, yb, yspec, &xbot, &xerr)) < 0){ + if ((ret = clixon_xml_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -212,7 +212,7 @@ api_data_post(clicon_handle h, } break; case YANG_DATA_JSON: - if ((ret = json_parse_str2(data, yb, yspec, &xbot, &xerr)) < 0){ + if ((ret = clixon_json_parse_string(data, yb, yspec, &xbot, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -453,7 +453,9 @@ api_operations_post_input(clicon_handle h, media_in = restconf_content_type(r); switch (media_in){ case YANG_DATA_XML: - if (xml_parse_string(data, yspec, &xdata) < 0){ + /* XXX: Here data is on the form: and has no proper yang binding + * support */ + if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -464,9 +466,21 @@ api_operations_post_input(clicon_handle h, goto done; goto fail; } + if (ret == 0){ + if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + clicon_debug(1, "%s F", __FUNCTION__); + clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); + goto done; + } + if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) + goto done; + goto fail; + } break; case YANG_DATA_JSON: - if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){ + /* XXX: Here data is on the form: {"clixon-example:input":null} and has no proper yang binding + * support */ + if ((ret = clixon_json_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -492,6 +506,7 @@ api_operations_post_input(clicon_handle h, goto fail; break; } /* switch media_in */ + clicon_debug(1, "%s F", __FUNCTION__); xml_name_set(xdata, "data"); /* Here xdata is: * ... @@ -610,8 +625,8 @@ api_operations_post_output(clicon_handle h, */ if (youtput != NULL){ xml_spec_set(xoutput, youtput); /* needed for xml_bind_yang */ -#if 0 - if (xml_bind_yang(xoutput, yspec, NULL) < 0) +#ifdef notyet + if (xml_bind_yang(xoutput, YB_MODULE, yspec, NULL) < 0) goto done; if ((ret = xml_yang_validate_all(xoutput, &xerr)) < 0) goto done; @@ -863,7 +878,7 @@ api_operations_post(clicon_handle h, if ((ret = rpc_callback_call(h, xbot, cbret, r)) < 0) goto done; if (ret > 0){ /* Handled locally */ - if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0) + if (clixon_xml_parse_string(cbuf_get(cbret), YB_NONE, NULL, &xret, NULL) < 0) goto done; /* Local error: return it and quit */ if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ diff --git a/example/main/example_backend.c b/example/main/example_backend.c index af867823..22a7222e 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -330,7 +330,7 @@ example_statedata(clicon_handle h, clicon_err(OE_UNIX, errno, "open(%s)", _state_file); goto done; } - if (xml_parse_file(fd, yspec, &xstate) < 0) + if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xstate, NULL) < 0) goto done; } else { @@ -353,16 +353,18 @@ example_statedata(clicon_handle h, cprintf(cb, ""); } cprintf(cb, ""); - if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_NONE, NULL, &xstate, NULL) < 0) goto done; } /* State in test_yang.sh , test_restconf.sh and test_order.sh */ if (yang_find_module_by_namespace(yspec, "urn:example:clixon") != NULL){ - if (xml_parse_string("" - "42" - "41" - "43" /* should not be ordered */ - "", NULL, &xstate) < 0) + if (clixon_xml_parse_string("" + "42" + "41" + "43" /* should not be ordered */ + "", + YB_NONE, + NULL, &xstate, NULL) < 0) goto done; /* For the case when urn:example:clixon is not loaded */ } /* Event state from RFC8040 Appendix B.3.1 @@ -375,7 +377,7 @@ example_statedata(clicon_handle h, cprintf(cb, "interface-down90"); cprintf(cb, "interface-up77"); cprintf(cb, ""); - if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_NONE, NULL, &xstate, NULL) < 0) goto done; } } @@ -757,9 +759,9 @@ example_reset(clicon_handle h, if (!_reset) goto ok; /* Note not enabled by default */ - if (xml_parse_string("" - "loex:loopback" - "", NULL, &xt) < 0) + if (clixon_xml_parse_string("" + "loex:loopback" + "", YB_NONE, NULL, &xt, NULL) < 0) goto done; /* Replace parent w first child */ if (xml_rootchild(xt, 0, &xt) < 0) diff --git a/example/main/example_backend_nacm.c b/example/main/example_backend_nacm.c index a3c42657..ea080ca1 100644 --- a/example/main/example_backend_nacm.c +++ b/example/main/example_backend_nacm.c @@ -173,11 +173,11 @@ nacm_statedata(clicon_handle h, cxobj **xvec = NULL; /* Example of (static) statedata, real code would poll state */ - if (xml_parse_string("" - "0" - "0" - "0" - "", NULL, &xstate) < 0) + if (clixon_xml_parse_string("" + "0" + "0" + "0" + "", YB_NONE, NULL, &xstate, NULL) < 0) goto done; retval = 0; done: diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 52c359c7..f851c6bc 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -98,10 +98,12 @@ example_client_rpc(clicon_handle h, /* User supplied variable in CLI command */ cva = cvec_find(cvv, "a"); /* get a cligen variable from vector */ /* Create XML for example netconf RPC */ - if (xml_parse_va(&xtop, NULL, "%s", - NETCONF_BASE_NAMESPACE, - clicon_username_get(h), - cv_string_get(cva)) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xtop, NULL, + "" + "%s", + NETCONF_BASE_NAMESPACE, + clicon_username_get(h), + cv_string_get(cva)) < 0) goto done; /* Skip top-level */ xrpc = xml_child_i(xtop, 0); @@ -109,7 +111,7 @@ example_client_rpc(clicon_handle h, if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Get configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Get configuration", NULL); goto done; } /* Print result */ diff --git a/lib/clixon/clixon_err.h b/lib/clixon/clixon_err.h index 3931b48b..a77cb956 100644 --- a/lib/clixon/clixon_err.h +++ b/lib/clixon/clixon_err.h @@ -93,9 +93,9 @@ extern char clicon_err_reason[ERR_STRLEN]; */ int clicon_err_reset(void); #if defined(__GNUC__) && __GNUC__ >= 3 -int clicon_err_fn(const char *fn, const int line, int level, int err, char *format, ...) __attribute__ ((format (printf, 5, 6))); +int clicon_err_fn(const char *fn, const int line, int category, int err, char *format, ...) __attribute__ ((format (printf, 5, 6))); #else -int clicon_err_fn(const char *fn, const int line, int level, int err, char *format, ...); +int clicon_err_fn(const char *fn, const int line, int category, int err, char *format, ...); #endif char *clicon_strerror(int err); void *clicon_err_save(void); diff --git a/lib/clixon/clixon_json.h b/lib/clixon/clixon_json.h index 313e43ce..450ba30f 100644 --- a/lib/clixon/clixon_json.h +++ b/lib/clixon/clixon_json.h @@ -49,8 +49,7 @@ int xml2json_cbuf_vec(cbuf *cb, cxobj **vec, size_t veclen, int pretty); int xml2json(FILE *f, cxobj *x, int pretty); int json_print(FILE *f, cxobj *x); int xml2json_vec(FILE *f, cxobj **vec, size_t veclen, int pretty); -int json_parse_str(char *str, yang_stmt *yspec, cxobj **xt, cxobj **xret); -int json_parse_str2(char *str, enum yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); -int json_parse_file(int fd, yang_stmt *yspec, cxobj **xt, cxobj **xret); +int clixon_json_parse_string(char *str, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); +int clixon_json_parse_file(int fd, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xret); #endif /* _CLIXON_JSON_H */ diff --git a/lib/clixon/clixon_netconf_lib.h b/lib/clixon/clixon_netconf_lib.h index 06e5c83b..3f0d8a8a 100644 --- a/lib/clixon/clixon_netconf_lib.h +++ b/lib/clixon/clixon_netconf_lib.h @@ -52,6 +52,11 @@ enum netconf_content{ }; typedef enum netconf_content netconf_content; +/* + * Macros + */ +#define clixon_netconf_error(c, x, f, a) clixon_netconf_error_fn(__FUNCTION__, __LINE__, (c), (x), (f), (a)) + /* * Prototypes */ @@ -96,4 +101,7 @@ const char *netconf_content_int2str(netconf_content nr); int netconf_hello_server(clicon_handle h, cbuf *cb, uint32_t session_id); int netconf_hello_req(clicon_handle h, cbuf *cb); +int clicon_err_fn(const char *fn, const int line, int level, int err, char *format, ...); +int clixon_netconf_error_fn(const char *fn, const int line, int category, cxobj *xerr, const char *fmt, const char *arg); + #endif /* _CLIXON_NETCONF_LIB_H */ diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index b4329fe0..7656f426 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -44,7 +44,6 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0, int *sock0); int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp); int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp); -int clicon_rpc_generate_error(cxobj *xerr, const char *fmt, const char *arg); int clicon_rpc_get_config(clicon_handle h, char *username, char *db, char *xpath, cvec *nsc, cxobj **xret); int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op, char *xml); diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 29668c2d..d56d0f71 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -84,14 +84,47 @@ enum cxobj_type {CX_ERROR=-1, CX_ATTR, CX_BODY}; -/* How to bind yang to XML top-level when parsing */ +/* How to bind yang to XML top-level when parsing + * Assume an XML tree x with parent xp (or NULL) and a set of children c1,c2: + * + * (XML) xp + * | + * x + * / \ + * c1 c2 + * (1) If you make a binding using YB_MODULE, you assume there is a loaded module "ym" with a top-level + * data resource "y" that the XML node x can match to: + * + * (XML) xp ym (YANG) + * | | + * x - - - - - y + * / \ / \ + * x1 x2 - - y1 y2 + * In that case, "y" is a container, list, leaf or leaf-list with same name as "x". + * + * (2) If you make a binding using YB_PARENT, you assume xp already have a YANG binding (eg to "yp"): + * + * (XML) xp - - - - yp (YANG) + * | + * x + * so that the yang binding of "x" is a child of "yp": + * + * (XML) xp - - - - yp (YANG) + * | | + * x - - - - y + * / \ / \ + * x1 x2 - - y1 y2 + * XXX: lacks support for incoming restconf rpc, see api_operations_post_input) + */ enum yang_bind{ - YB_UNKNOWN=0, /* System derive binding: top if parent not exist or no spec, otherwise parent */ - YB_NONE, /* Dont try to do Yang binding */ - YB_PARENT, /* Get yang binding from parents yang */ - YB_TOP, /* Get yang binding from top-level modules */ - YB_RPC, /* Assume top-level xml is an netconf RPC message */ + YB_MODULE=0, /* Search for matching yang binding among top-level symbols of Yang modules */ + YB_PARENT, /* Assume yang binding of existing parent and match its children by name */ + YB_NONE, /* Dont do Yang binding */ +#ifdef NYI + YB_RPC, /* Assume top-level xml is an netconf RPC message (NYI) */ +#endif }; +typedef enum yang_bind yang_bind; #define CX_ANY CX_ERROR /* catch all and error is same */ diff --git a/lib/clixon/clixon_xml_io.h b/lib/clixon/clixon_xml_io.h index c89fb9f3..ff7f4780 100644 --- a/lib/clixon/clixon_xml_io.h +++ b/lib/clixon/clixon_xml_io.h @@ -48,18 +48,15 @@ int xml_print(FILE *f, cxobj *xn); int clicon_xml2cbuf(cbuf *cb, cxobj *x, int level, int prettyprint, int32_t depth); int xmltree2cbuf(cbuf *cb, cxobj *x, int level); -int xml_parse_file(int fd, yang_stmt *yspec, cxobj **xt); -int xml_parse_file2(int fd, enum yang_bind yb, yang_stmt *yspec, char *endtag, cxobj **xt, cxobj **xerr); -int xml_parse_string2(const char *str, enum yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr); -int xml_parse_string(const char *str, yang_stmt *yspec, cxobj **xt); +int clixon_xml_parse_file(int fd, yang_bind yb, yang_stmt *yspec, char *endtag, cxobj **xt, cxobj **xerr); +int clixon_xml_parse_string(const char *str, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr); + #if defined(__GNUC__) && __GNUC__ >= 3 -int xml_parse_va(cxobj **xt, yang_stmt *yspec, const char *format, ...) __attribute__ ((format (printf, 3, 4))); +int clixon_xml_parse_va(yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr, + const char *format, ...) __attribute__ ((format (printf, 5, 6))); #else -int xml_parse_va(cxobj **xt, yang_stmt *yspec, const char *format, ...); -#endif -#ifdef NOTUSED -int xml_body_int32(cxobj *xb, int32_t *val); -int xml_body_uint32(cxobj *xb, uint32_t *val); +int clixon_xml_parse_va(yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr, + const char *format, ...); #endif #endif /* _CLIXON_XML_IO_H_ */ diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 67a27bed..f7db0744 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -63,10 +63,8 @@ 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_stmt *yspec, cxobj **xerr); -int xml_bind_yang0_parent(cxobj *xt, cxobj **xerr); -int xml_bind_yang(cxobj *xt, yang_stmt *yspec, cxobj **xerr); -int xml_bind_yang_parent(cxobj *xt, 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_read.c b/lib/src/clixon_datastore_read.c index b1c4267e..e6e9bfb8 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -250,11 +250,13 @@ text_read_modstate(clicon_handle h, else if (xmcache && msd){ msd->md_status = 1; /* There is module state in the file */ /* Create diff trees */ - if (xml_parse_string("", yspec, &msd->md_del) < 0) + if (clixon_xml_parse_string("", + YB_MODULE, yspec, &msd->md_del, NULL) < 0) goto done; if (xml_rootchild(msd->md_del, 0, &msd->md_del) < 0) goto done; - if (xml_parse_string("", yspec, &msd->md_mod) < 0) + if (clixon_xml_parse_string("", + YB_MODULE, yspec, &msd->md_mod, NULL) < 0) goto done; if (xml_rootchild(msd->md_mod, 0, &msd->md_mod) < 0) goto done; @@ -349,10 +351,10 @@ xmldb_readfile(clicon_handle h, goto done; } if (strcmp(format, "json")==0){ - if ((ret = json_parse_file(fd, yspec, &x0, NULL)) < 0) /* XXX: ret == 0*/ + if ((ret = clixon_json_parse_file(fd, YB_MODULE, yspec, &x0, NULL)) < 0) /* XXX: ret == 0*/ goto done; } - else if ((xml_parse_file2(fd, YB_TOP, yspec, "", &x0, NULL)) < 0) + else if ((clixon_xml_parse_file(fd, YB_MODULE, yspec, "", &x0, NULL)) < 0) goto done; /* Always assert a top-level called "config". diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 9afabe94..71b84b68 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -808,7 +808,7 @@ xml_container_presence(cxobj *x, * @code * cxobj *xt; * cxobj *xret = NULL; - * if (xml_parse_string("17", yspec, &xt) < 0) + * if (clixon_xml_parse_string("17", YB_NONE, NULL, &xt, NULL) < 0) * err; * if ((ret = xmldb_put(h, "running", OP_MERGE, xt, username, cbret)) < 0) * err; diff --git a/lib/src/clixon_err.c b/lib/src/clixon_err.c index 34c68d7e..08225503 100644 --- a/lib/src/clixon_err.c +++ b/lib/src/clixon_err.c @@ -139,17 +139,16 @@ clicon_err_reset(void) * * @param[in] fn Inline function name (when called from clicon_err() macro) * @param[in] line Inline file line number (when called from clicon_err() macro) - * @param[in] err Error number, typically errno - * @param[in] suberr Sub-error number + * @param[in] category See enum clicon_err + * @param[in] errno Error number, typically errno * @param[in] reason Error string, format with argv * @see clicon_err_reser Resetting the global error variables. */ -int -clicon_err_fn(const char *fn, - const int line, - int category, - int suberr, - char *reason, ...) +int clicon_err_fn(const char *fn, + const int line, + int category, + int err, + char *format, ...) { va_list args; int len; @@ -158,11 +157,11 @@ clicon_err_fn(const char *fn, /* Set the global variables */ clicon_errno = category; - clicon_suberrno = suberr; + clicon_suberrno = errno; /* first round: compute length of error message */ - va_start(args, reason); - len = vsnprintf(NULL, 0, reason, args); + va_start(args, format); + len = vsnprintf(NULL, 0, format, args); va_end(args); /* allocate a message string exactly fitting the message length */ @@ -171,9 +170,9 @@ clicon_err_fn(const char *fn, goto done; } - /* second round: compute write message from reason and args */ - va_start(args, reason); - if (vsnprintf(msg, len+1, reason, args) < 0){ + /* second round: compute write message from format and args */ + va_start(args, format); + if (vsnprintf(msg, len+1, format, args) < 0){ va_end(args); fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */ goto done; @@ -182,14 +181,14 @@ clicon_err_fn(const char *fn, strncpy(clicon_err_reason, msg, ERR_STRLEN-1); /* Actually log it */ - if (suberr){ - /* Here we could take care of specific suberr, like application-defined errors */ + if (errno){ + /* Here we could take care of specific errno, like application-defined errors */ clicon_log(LOG_ERR, "%s: %d: %s: %s: %s", fn, line, clicon_strerror(category), msg, - suberr==XMLPARSE_ERRNO?"XML parse error":strerror(suberr)); + errno==XMLPARSE_ERRNO?"XML parse error":strerror(errno)); } else clicon_log(LOG_ERR, "%s: %d: %s: %s", diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 0b424457..07a85f67 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -1169,11 +1169,11 @@ json_xmlns_translate(yang_stmt *yspec, * @see RFC 7951 */ static int -_json_parse(char *str, - enum yang_bind yb, - yang_stmt *yspec, - cxobj *xt, - cxobj **xerr) +_json_parse(char *str, + yang_bind yb, + yang_stmt *yspec, + cxobj *xt, + cxobj **xerr) { int retval = -1; clixon_json_yacc jy = {0,}; @@ -1223,22 +1223,32 @@ _json_parse(char *str, * XXX should be xml_bind_yang0_parent() sometimes. */ switch (yb){ - case YB_RPC: - case YB_UNKNOWN: - case YB_NONE: - break; case YB_PARENT: - if ((ret = xml_bind_yang0_parent(x, xerr)) < 0) + if ((ret = xml_bind_yang0(x, yb, yspec, xerr)) < 0) goto done; if (ret == 0) failed++; break; - case YB_TOP: - if (xml_bind_yang0(x, yspec, xerr) < 0) - goto done; + case YB_MODULE: +#ifdef XMLDB_CONFIG_HACK + if (strcmp(xml_name(x),"config") == 0 || + strcmp(xml_name(x),"data") == 0){ + /* xt: nospec + * x: + * <-- populate from modules + */ + if ((ret = xml_bind_yang(x, yb, yspec, xerr)) < 0) + goto done; + } + else +#endif + if ((ret = xml_bind_yang0(x, yb, yspec, xerr)) < 0) + goto done; if (ret == 0) failed++; break; + case YB_NONE: + break; } /* Now find leafs with identityrefs (+transitive) and translate * prefixes in values to XML namespaces */ @@ -1247,9 +1257,12 @@ _json_parse(char *str, if (ret == 0) /* XXX necessary? */ goto fail; } + if (failed) + goto fail; + /* This fails if xt is not bound to yang */ if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) goto done; - retval = (failed==0) ? 1 : 0; + retval = 1; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); if (cberr) @@ -1267,29 +1280,30 @@ _json_parse(char *str, /*! Parse string containing JSON and return an XML tree * * @param[in] str String containing JSON + * @param[in] yb How to bind yang to XML top-level when parsing * @param[in] yspec Yang specification, mandatory to make module->xmlns translation * @param[in,out] xt Top object, if not exists, on success it is created with name 'top' * @param[out] xerr Reason for invalid returned as netconf err msg - * - * @code - * cxobj *cx = NULL; - * if (json_parse_str(str, yspec, &cx, &xerr) < 0) - * err; - * xml_free(cx); - * @endcode - * @note you need to free the xml parse tree after use, using xml_free() - * @see json_parse_file * @retval 1 OK and valid * @retval 0 Invalid (only if yang spec) w xerr set * @retval -1 Error with clicon_err called - * @see json_parse_file with a file descriptor (and more description) + * + * @code + * cxobj *x = NULL; + * if (clixon_json_parse_string(str, YB_MODULE, yspec, &x, &xerr) < 0) + * err; + * xml_free(x); + * @endcode + * @note you need to free the xml parse tree after use, using xml_free() + * @see clixon_xml_parse_string XML instead of JSON + * @see clixon_json_parse_file From a file */ int -json_parse_str2(char *str, - enum yang_bind yb, - yang_stmt *yspec, - cxobj **xt, - cxobj **xerr) +clixon_json_parse_string(char *str, + yang_bind yb, + yang_stmt *yspec, + cxobj **xt, + cxobj **xerr) { clicon_debug(1, "%s", __FUNCTION__); if (xt==NULL){ @@ -1303,31 +1317,6 @@ json_parse_str2(char *str, return _json_parse(str, yb, yspec, *xt, xerr); } -int -json_parse_str(char *str, - yang_stmt *yspec, - cxobj **xt, - cxobj **xerr) -{ - enum yang_bind yb = YB_PARENT; - - clicon_debug(1, "%s", __FUNCTION__); - if (xt==NULL){ - clicon_err(OE_XML, EINVAL, "xt is NULL"); - return -1; - } - if (*xt == NULL){ - yb = YB_TOP; /* ad-hoc #1 */ - if ((*xt = xml_new("top", NULL, CX_ELMNT)) == NULL) - return -1; - } - else{ - if (xml_spec(*xt) == NULL) - yb = YB_TOP; /* ad-hoc #2 */ - } - return _json_parse(str, yb, yspec, *xt, xerr); -} - /*! Read a JSON definition from file and parse it into a parse-tree. * * File will be parsed as follows: @@ -1347,7 +1336,7 @@ json_parse_str(char *str, * * @code * cxobj *xt = NULL; - * if (json_parse_file(0, yspec, &xt) < 0) + * if (clixon_json_parse_file(0, YB_MODULE, yspec, &xt) < 0) * err; * xml_free(xt); * @endcode @@ -1359,31 +1348,29 @@ json_parse_str(char *str, * @retval 0 Invalid (only if yang spec) w xerr set * @retval -1 Error with clicon_err called * - * @see json_parse_str + * @see clixon_json_parse_string * @see RFC7951 */ int -json_parse_file(int fd, - yang_stmt *yspec, - cxobj **xt, - cxobj **xerr) +clixon_json_parse_file(int fd, + yang_bind yb, + yang_stmt *yspec, + cxobj **xt, + cxobj **xerr) { - int retval = -1; - int ret; - char *jsonbuf = NULL; - int jsonbuflen = BUFLEN; /* start size */ - int oldjsonbuflen; - char *ptr; - char ch; - int len = 0; - enum yang_bind yb = YB_PARENT; + int retval = -1; + int ret; + char *jsonbuf = NULL; + int jsonbuflen = BUFLEN; /* start size */ + int oldjsonbuflen; + char *ptr; + char ch; + int len = 0; if (xt==NULL){ clicon_err(OE_XML, EINVAL, "xt is NULL"); return -1; } - if (*xt==NULL) - yb = YB_TOP; if ((jsonbuf = malloc(jsonbuflen)) == NULL){ clicon_err(OE_XML, errno, "malloc"); goto done; diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index 15e78be4..75d316d1 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -134,15 +134,16 @@ netconf_invalid_value_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "%s" - "invalid-value" - "error", type) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "%s" + "invalid-value" + "error", type) < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, "%s", - encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "%s", encstr) < 0) goto done; } retval = 0; @@ -314,16 +315,16 @@ netconf_bad_attribute_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "%s" - "bad-attribute" - "%s" - "error", type, info) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s" + "bad-attribute" + "%s" + "error", type, info) < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, "%s", - encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s", + encstr) < 0) goto done; } retval = 0; @@ -403,17 +404,17 @@ netconf_common_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "%s" - "%s" - "<%s>%s" - "error", - type, tag, infotag, element, infotag) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s" + "%s" + "<%s>%s" + "error", + type, tag, infotag, element, infotag) < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, "%s", - encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s", + encstr) < 0) goto done; } retval = 0; @@ -653,15 +654,15 @@ netconf_access_denied_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "%s" - "access-denied" - "error", type) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s" + "access-denied" + "error", type) < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, "%s", - encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s", + encstr) < 0) goto done; } retval = 0; @@ -883,24 +884,24 @@ netconf_data_missing_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, - "application" - "data-missing") < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "application" + "data-missing") < 0) goto done; if (missing_choice) /* NYI: RFC7950: 15.6 */ - if (xml_parse_va(&xerr, NULL, - "missing-choice" - "%s", - missing_choice) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "missing-choice" + "%s", + missing_choice) < 0) goto done; - if (xml_parse_va(&xerr, NULL, - "error") < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "error") < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, - "%s", encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "%s", encstr) < 0) goto done; } retval = 0; @@ -1011,16 +1012,16 @@ netconf_operation_failed_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "%s" - "operation-failed" - "error", - type) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s" + "operation-failed" + "error", + type) < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, "%s", - encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s", + encstr) < 0) goto done; } retval = 0; @@ -1090,15 +1091,15 @@ netconf_malformed_message_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "rpc" - "malformed-message" - "error") < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "rpc" + "malformed-message" + "error") < 0) goto done; if (message){ if (xml_chardata_encode(&encstr, "%s", message) < 0) goto done; - if (xml_parse_va(&xerr, NULL, "%s", - encstr) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "%s", + encstr) < 0) goto done; } retval = 0; @@ -1137,10 +1138,11 @@ netconf_data_not_unique_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "protocol" - "operation-failed" - "data-not-unique" - "error") < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, + "protocol" + "operation-failed" + "data-not-unique" + "error") < 0) goto done; if (cvec_len(cvk)){ if ((xinfo = xml_new("error-info", xerr, CX_ELMNT)) == NULL) @@ -1153,7 +1155,8 @@ netconf_data_not_unique_xml(cxobj **xret, if ((xi = xml_find(x, cv_string_get(cvi))) == NULL) continue; /* ignore, shouldnt happen */ clicon_xml2cbuf(cb, xi, 0, 0, -1); - if (xml_parse_va(&xinfo, NULL, "%s", cbuf_get(cb)) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &xinfo, NULL, + "%s", cbuf_get(cb)) < 0) goto done; cbuf_reset(cb); } @@ -1190,13 +1193,13 @@ netconf_minmax_elements_xml(cxobj **xret, goto done; if ((xerr = xml_new("rpc-error", *xret, CX_ELMNT)) == NULL) goto done; - if (xml_parse_va(&xerr, NULL, "protocol" - "operation-failed" - "too-%s-elements" - "error" - "%s", - max?"many":"few", - xml_name(x)) < 0) /* XXX should be xml2xpath */ + if (clixon_xml_parse_va(YB_NONE, NULL, &xerr, NULL, "protocol" + "operation-failed" + "too-%s-elements" + "error" + "%s", + max?"many":"few", + xml_name(x)) < 0) /* XXX should be xml2xpath */ goto done; retval = 0; done: @@ -1264,19 +1267,20 @@ netconf_module_features(clicon_handle h) { int retval = -1; cxobj *xc; - yang_stmt *yspec; - - yspec = clicon_dbspec_yang(h); + if ((xc = clicon_conf_xml(h)) == NULL){ clicon_err(OE_CFG, ENOENT, "Clicon configuration not loaded"); goto done; } /* Enable features (hardcoded here) */ - if (xml_parse_string("ietf-netconf:candidate", yspec, &xc) < 0) + if (clixon_xml_parse_string("ietf-netconf:candidate", + YB_PARENT, NULL, &xc, NULL) < 0) goto done; - if (xml_parse_string("ietf-netconf:validate", yspec, &xc) < 0) + if (clixon_xml_parse_string("ietf-netconf:validate", + YB_PARENT, NULL, &xc, NULL) < 0) goto done; - if (xml_parse_string("ietf-netconf:xpath", yspec, &xc) < 0) + if (clixon_xml_parse_string("ietf-netconf:xpath", + YB_PARENT, NULL, &xc, NULL) < 0) goto done; retval = 0; done: @@ -1320,7 +1324,7 @@ netconf_module_load(clicon_handle h) * @code * cxobj *xt = NULL; * char *db; - * xml_parse_string("source", NULL, &xt); + * clixon_xml_parse_string("source", YB_NONE, NULL, &xt, NULL); * db = netconf_db_find(xt, "target"); * @endcode */ @@ -1355,7 +1359,7 @@ netconf_db_find(cxobj *xn, * printf("%s", cbuf_get(cb)); * cbuf_free(cb); * @endcode - * @see clicon_rpc_generate_error + * @see clixon_netconf_error */ int netconf_err2cb(cxobj *xerr, @@ -1496,3 +1500,46 @@ netconf_hello_req(clicon_handle h, retval = 0; return retval; } + +/*! Generate clicon error from Netconf error message + * + * Get a text error message from netconf error message and generate error on the form: + * : "": or : + * @param[in] fn Inline function name (when called from clicon_err() macro) + * @param[in] line Inline file line number (when called from clicon_err() macro) + * @param[in] err Error number, typically errno + * @param[in] xerr Netconf error xml tree on the form: + * @param[in] format Format string + * @param[in] arg String argument to format (optional) + */ +int +clixon_netconf_error_fn(const char *fn, + const int line, + int category, + cxobj *xerr, + const char *msg, + const char *arg) +{ + int retval = -1; + cbuf *cb = NULL; + + if ((cb = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (msg){ + cprintf(cb, "%s", msg); + if (arg) + cprintf(cb, " \"%s\" ", arg); + cprintf(cb, ": "); + } + if (netconf_err2cb(xerr, cb) < 0) + goto done; + + clicon_err_fn(fn, line, category, 0, "%s", cbuf_get(cb)); + retval = 0; + done: + if (cb) + cbuf_free(cb); + return retval; +} diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 3d852fed..79d2c488 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -214,7 +214,7 @@ parse_configfile(clicon_handle h, char *body; clicon_hash_t *copt = clicon_options(h); cbuf *cbret = NULL; - cxobj *xret = NULL; + cxobj *xerr = NULL; int ret; cvec *nsc = NULL; @@ -236,8 +236,18 @@ parse_configfile(clicon_handle h, } clicon_debug(2, "%s: Reading config file %s", __FUNCTION__, filename); fd = fileno(f); - if (xml_parse_file(fd, yspec, &xt) < 0) + if ((ret = clixon_xml_parse_file(fd, yspec?YB_MODULE:YB_NONE, yspec, NULL, &xt, &xerr)) < 0) goto done; + if (ret == 0){ + if ((cbret = cbuf_new()) ==NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (netconf_err2cb(xerr, cbret) < 0) + goto done; + clixon_netconf_error(OE_CFG, xerr, NULL, NULL); + goto done; + } if (xml_child_nr(xt)==1 && xml_child_nr_type(xt, CX_BODY)==1){ clicon_err(OE_CFG, 0, "Config file %s: Expected XML but is probably old sh style", filename); goto done; @@ -252,14 +262,14 @@ parse_configfile(clicon_handle h, } if (xml_apply0(xc, CX_ELMNT, xml_default, h) < 0) goto done; - if ((ret = xml_yang_validate_add(h, xc, &xret)) < 0) + if ((ret = xml_yang_validate_add(h, xc, &xerr)) < 0) goto done; if (ret == 0){ if ((cbret = cbuf_new()) ==NULL){ clicon_err(OE_XML, errno, "cbuf_new"); goto done; } - if (netconf_err2cb(xret, cbret) < 0) + if (netconf_err2cb(xerr, cbret) < 0) goto done; clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret)); goto done; @@ -296,8 +306,8 @@ parse_configfile(clicon_handle h, xml_nsctx_free(nsc); if (cbret) cbuf_free(cbret); - if (xret) - xml_free(xret); + if (xerr) + xml_free(xerr); if (xt) xml_free(xt); if (f) @@ -330,8 +340,8 @@ clicon_option_add(clicon_handle h, clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name); goto done; } - if (xml_parse_va(&x, NULL, "<%s>%s", - name, value, name) < 0) + if (clixon_xml_parse_va(YB_NONE, NULL, &x, NULL, "<%s>%s", + name, value, name) < 0) goto done; } if (clicon_hash_add(copt, @@ -394,7 +404,7 @@ clicon_options_main(clicon_handle h) * - no default values * - no sanity checks */ - if (parse_configfile(h, configfile, yspec, &xconfig) < 0) + if (parse_configfile(h, configfile, NULL, &xconfig) < 0) goto done; if (xml_rootchild(xconfig, 0, &xconfig) < 0) goto done; diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index c312f661..edc3a52f 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -184,7 +184,7 @@ clicon_msg_decode(struct clicon_msg *msg, /* body */ xmlstr = msg->op_body; clicon_debug(1, "%s %s", __FUNCTION__, xmlstr); - if (xml_parse_string(xmlstr, yspec, xml) < 0) + if (clixon_xml_parse_string(xmlstr, yspec?YB_MODULE:YB_NONE, yspec, xml, NULL) < 0) goto done; retval = 0; done: diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 6c9905af..8d54a75f 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -143,7 +143,7 @@ clicon_rpc_msg(clicon_handle h, /* Cannot populate xret here because need to know RPC name (eg "lock") in order to associate yang * to reply. */ - if (xml_parse_string2(retdata, YB_NONE, NULL, &xret, NULL) < 0) + if (clixon_xml_parse_string(retdata, YB_NONE, NULL, &xret, NULL) < 0) goto done; } if (xret0){ @@ -280,38 +280,6 @@ clicon_rpc_netconf_xml(clicon_handle h, return retval; } -/*! Generate clicon error from Netconf error message - * - * Get a text error message from netconf error message and generate error on the form: - * : "": or : - * @param[in] xerr Netconf error xml tree on the form: - * @param[in] format Format string - * @param[in] arg String argument to format (optional) - */ -int -clicon_rpc_generate_error(cxobj *xerr, - const char *msg, - const char *arg) -{ - int retval = -1; - cbuf *cb = NULL; - - if ((cb = cbuf_new()) ==NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - goto done; - } - if (netconf_err2cb(xerr, cb) < 0) - goto done; - cprintf(cb, ". %s", msg); - if (arg) - cprintf(cb, " \"%s\" ", arg); - clicon_err(OE_NETCONF, 0, "%s", cbuf_get(cb)); - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} /*! Get database configuration * Same as clicon_proto_change just with a cvec instead of lvec @@ -333,7 +301,7 @@ clicon_rpc_generate_error(cxobj *xerr, * if (clicon_rpc_get_config(h, NULL, "running", "/hello/world", nsc, &xt) < 0) * err; * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - * clicon_rpc_generate_error(xerr, "msg", "/hello/world"); + * clixon_netconf_error(OE_NETCONF, xerr, "msg", "/hello/world"); * err; * } * if (xt) @@ -342,7 +310,7 @@ clicon_rpc_generate_error(cxobj *xerr, * xml_nsctx_free(nsc); * @endcode * @see clicon_rpc_get - * @see clicon_rpc_generate_error + * @see clixon_netconf_error * @note the netconf return message is yang populated, as well as the return data */ int @@ -403,7 +371,7 @@ clicon_rpc_get_config(clicon_handle h, } else{ yspec = clicon_dbspec_yang(h); - if ((ret = xml_bind_yang(xd, yspec, &xerr)) < 0) + if ((ret = xml_bind_yang(xd, YB_MODULE, yspec, &xerr)) < 0) goto done; if (ret == 0){ if ((xd = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -477,7 +445,7 @@ clicon_rpc_edit_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Editing configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Editing configuration", NULL); goto done; } retval = 0; @@ -527,7 +495,7 @@ clicon_rpc_copy_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Copying configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Copying configuration", NULL); goto done; } retval = 0; @@ -570,7 +538,7 @@ clicon_rpc_delete_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Deleting configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Deleting configuration", NULL); goto done; } retval = 0; @@ -609,7 +577,7 @@ clicon_rpc_lock(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Locking configuration", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Locking configuration", NULL); goto done; } retval = 0; @@ -647,7 +615,7 @@ clicon_rpc_unlock(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Configuration unlock", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Configuration unlock", NULL); goto done; } retval = 0; @@ -681,7 +649,7 @@ clicon_rpc_unlock(clicon_handle h, * if (clicon_rpc_get(h, "/hello/world", nsc, CONTENT_ALL, -1, &xt) < 0) * err; * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ - * clicon_rpc_generate_error(xerr, "clicon_rpc_get", NULL); + * clixon_netconf_error(OE_NETCONF, xerr, "clicon_rpc_get", NULL); * err; * } * if (xt) @@ -690,7 +658,7 @@ clicon_rpc_unlock(clicon_handle h, * xml_nsctx_free(nsc); * @endcode * @see clicon_rpc_get_config which is almost the same as with content=config, but you can also select dbname - * @see clicon_rpc_generate_error + * @see clixon_netconf_error * @note the netconf return message is yang populated, as well as the return data */ int @@ -759,7 +727,7 @@ clicon_rpc_get(clicon_handle h, } else{ yspec = clicon_dbspec_yang(h); - if ((ret = xml_bind_yang(xd, yspec, &xerr)) < 0) + if ((ret = xml_bind_yang(xd, YB_MODULE, yspec, &xerr)) < 0) goto done; if (ret == 0){ if ((xd = xpath_first(xerr, NULL, "rpc-error")) == NULL){ @@ -811,7 +779,7 @@ clicon_rpc_close_session(clicon_handle h) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Close session", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Close session", NULL); goto done; } retval = 0; @@ -850,7 +818,7 @@ clicon_rpc_kill_session(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Kill session", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Kill session", NULL); goto done; } retval = 0; @@ -888,7 +856,7 @@ clicon_rpc_validate(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, CLIXON_ERRSTR_VALIDATE_FAILED, NULL); + clixon_netconf_error(OE_NETCONF, xerr, CLIXON_ERRSTR_VALIDATE_FAILED, NULL); goto done; } retval = 0; @@ -924,7 +892,7 @@ clicon_rpc_commit(clicon_handle h) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, CLIXON_ERRSTR_COMMIT_FAILED, NULL); + clixon_netconf_error(OE_NETCONF, xerr, CLIXON_ERRSTR_COMMIT_FAILED, NULL); goto done; } retval = 0; @@ -960,7 +928,7 @@ clicon_rpc_discard_changes(clicon_handle h) if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Discard changes", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Discard changes", NULL); goto done; } retval = 0; @@ -1010,7 +978,7 @@ clicon_rpc_create_subscription(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, s0) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Create subscription", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Create subscription", NULL); goto done; } retval = 0; @@ -1048,7 +1016,7 @@ clicon_rpc_debug(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Debug", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Debug", NULL); goto done; } if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){ @@ -1091,7 +1059,7 @@ clicon_hello_req(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ - clicon_rpc_generate_error(xerr, "Hello", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "Hello", NULL); goto done; } if ((x = xpath_first(xret, NULL, "hello/session-id")) == NULL){ diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c index 970da91f..f1df9865 100644 --- a/lib/src/clixon_stream.c +++ b/lib/src/clixon_stream.c @@ -584,7 +584,7 @@ stream_notify(clicon_handle h, /* From RFC5277 */ cprintf(cb, "%s%s", NOTIFICATION_RFC5277_NAMESPACE, timestr, str); - if (xml_parse_string(cbuf_get(cb), yspec, &xev) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &xev, NULL) < 0) goto done; if (xml_rootchild(xev, 0, &xev) < 0) goto done; @@ -649,7 +649,7 @@ stream_notify_xml(clicon_handle h, cprintf(cb, "%sNULL", NOTIFICATION_RFC5277_NAMESPACE, timestr); /* XXX str is always NULL */ - if (xml_parse_string(cbuf_get(cb), yspec, &xev) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_NONE, yspec, &xev, NULL) < 0) goto done; if (xml_rootchild(xev, 0, &xev) < 0) goto done; diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 4892d13d..53239ed6 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1416,7 +1416,7 @@ xml_rm_children(cxobj *xp, * @retval -1 Error * @code * cxobj *xt = NULL; - * if (xml_parse_string("2", NULL, &xt) < 0) + * if (clixon_xml_parse_string("2", YB_NONE, NULL, &xt, NULL) < 0) * err; * # Here xt will be: 2 * if (xml_rootchild(xt, 0, &xt) < 0) diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c index b228c446..0ce673af 100644 --- a/lib/src/clixon_xml_changelog.c +++ b/lib/src/clixon_xml_changelog.c @@ -447,7 +447,7 @@ clixon_xml_changelog_init(clicon_handle h) clicon_err(OE_UNIX, errno, "open(%s)", filename); goto done; } - if (xml_parse_file(fd, yspec, &xt) < 0) + if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0) goto done; if (xml_rootchild(xt, 0, &xt) < 0) goto done; diff --git a/lib/src/clixon_xml_io.c b/lib/src/clixon_xml_io.c index e97a8a27..35dbf71d 100644 --- a/lib/src/clixon_xml_io.c +++ b/lib/src/clixon_xml_io.c @@ -372,18 +372,17 @@ xmltree2cbuf(cbuf *cb, * @retval 1 Parse OK and all yang assignment made * @retval 0 Parse OK but yang assigment not made (or only partial) and xerr set * @retval -1 Error with clicon_err called. Includes parse error - * @see xml_parse_file - * @see xml_parse_string - * @see xml_parse_va + * @see clixon_xml_parse_file + * @see clixon_xml_parse_string * @see _json_parse * @note special case is empty XML where the parser is not invoked. */ static int -_xml_parse(const char *str, - enum yang_bind yb, - yang_stmt *yspec, - cxobj *xt, - cxobj **xerr) +_xml_parse(const char *str, + yang_bind yb, + yang_stmt *yspec, + cxobj *xt, + cxobj **xerr) { int retval = -1; clixon_xml_yacc xy = {0,}; @@ -423,21 +422,19 @@ _xml_parse(const char *str, /* Populate, ie associate xml nodes with yang specs */ switch (yb){ - case YB_RPC: - case YB_UNKNOWN: case YB_NONE: break; case YB_PARENT: /* xt:n Has spec * x: <-- populate from parent */ - if ((ret = xml_bind_yang0_parent(x, xerr)) < 0) + if ((ret = xml_bind_yang0(x, YB_PARENT, NULL, xerr)) < 0) goto done; if (ret == 0) failed++; break; - case YB_TOP: + case YB_MODULE: /* xt: nospec * x: <-- populate from modules */ @@ -448,22 +445,25 @@ _xml_parse(const char *str, * x: * <-- populate from modules */ - if ((ret = xml_bind_yang(x, yspec, xerr)) < 0) + if ((ret = xml_bind_yang(x, YB_MODULE, yspec, xerr)) < 0) goto done; } else #endif - if ((ret = xml_bind_yang0(x, yspec, xerr)) < 0) + if ((ret = xml_bind_yang0(x, YB_MODULE, yspec, xerr)) < 0) goto done; if (ret == 0) failed++; break; } } + if (failed) + goto fail; + /* This fails if xt is not bound to yang */ /* Sort the complete tree after parsing. Sorting is less meaningful if Yang not bound */ if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0) goto done; - retval = (failed==0) ? 1 : 0; + retval = 1; done: clixon_xml_parsel_exit(&xy); if (xy.xy_parse_string != NULL) @@ -471,43 +471,9 @@ _xml_parse(const char *str, if (xy.xy_xvec) free(xy.xy_xvec); return retval; -} - -/*! Read an XML definition from file and parse it into a parse-tree. - * - * @param[in] fd A file descriptor containing the XML file (as ASCII characters) - * @param[in] yspec Yang specification, or NULL - * @param[in,out] xt Pointer to XML parse tree. If empty, create. - * @retval 1 Parse OK and all yang assignment made - * @retval 0 Parse OK but yang assigment not made (or only partial) - * @retval -1 Error with clicon_err called. Includes parse error * - * @code - * cxobj *xt = NULL; - * int fd; - * fd = open(filename, O_RDONLY); - * xml_parse_file(fd, yspec, &xt); - * xml_free(xt); - * @endcode - * @see xml_parse_string - * @see xml_parse_va - * @note, If xt empty, a top-level symbol will be added so that will be: - * @note May block on file I/O - * @see xml_parse_file2 for a more advanced API - */ -int -xml_parse_file(int fd, - yang_stmt *yspec, - cxobj **xt) -{ - enum yang_bind yb = YB_PARENT; - - if (xt==NULL){ - clicon_err(OE_XML, EINVAL, "xt is NULL"); - return -1; - } - if (*xt==NULL) - yb = YB_TOP; - return xml_parse_file2(fd, yb, yspec, NULL, xt, NULL); + fail: /* invalid */ + retval = 0; + goto done; } /*! FSM to detect substring @@ -539,22 +505,22 @@ FSM(char *tag, * cxobj *xerr = NULL; * int fd; * fd = open(filename, O_RDONLY); - * if ((ret = xml_parse_file2(fd, YB_TOP, yspec, "", &xt, &xerr)) < 0) + * if ((ret = clixon_xml_parse_file(fd, YB_MODULE, yspec, "", &xt, &xerr)) < 0) * err; * xml_free(xt); * @endcode - * @see xml_parse_string - * @see xml_parse_file + * @see clixon_xml_parse_string + * @see clixon_json_parse_file * @note, If xt empty, a top-level symbol will be added so that will be: * @note May block on file I/O */ int -xml_parse_file2(int fd, - enum yang_bind yb, - yang_stmt *yspec, - char *endtag, - cxobj **xt, - cxobj **xerr) +clixon_xml_parse_file(int fd, + yang_bind yb, + yang_stmt *yspec, + char *endtag, + cxobj **xt, + cxobj **xerr) { int retval = -1; int ret; @@ -568,6 +534,14 @@ xml_parse_file2(int fd, int oldxmlbuflen; int failed = 0; + if (xt==NULL){ + clicon_err(OE_XML, EINVAL, "xt is NULL"); + return -1; + } + if (yb == YB_MODULE && yspec == NULL){ + clicon_err(OE_XML, EINVAL, "yspec is required if yb == YB_MODULE"); + return -1; + } if (endtag != NULL) endtaglen = strlen(endtag); if ((xmlbuf = malloc(xmlbuflen)) == NULL){ @@ -635,27 +609,31 @@ xml_parse_file2(int fd, * @code * cxobj *xt = NULL; * cxobj *xerr = NULL; - * if (xml_parse_string2(str, YB_TOP, yspec, &xt, &xerr) < 0) + * if (clixon_xml_parse_string(str, YB_MODULE, yspec, &xt, &xerr) < 0) * err; * if (xml_rootchild(xt, 0, &xt) < 0) # If you want to remove TOP * err; * @endcode - * @see xml_parse_file - * @see xml_parse_va + * @see clixon_xml_parse_file + * @see clixon_xml_parse_va * @note You need to free the xml parse tree after use, using xml_free() * @note If empty on entry, a new TOP xml will be created named "top" */ int -xml_parse_string2(const char *str, - enum yang_bind yb, - yang_stmt *yspec, - cxobj **xt, - cxobj **xerr) +clixon_xml_parse_string(const char *str, + yang_bind yb, + yang_stmt *yspec, + cxobj **xt, + cxobj **xerr) { if (xt==NULL){ clicon_err(OE_XML, EINVAL, "xt is NULL"); return -1; } + if (yb == YB_MODULE && yspec == NULL){ + clicon_err(OE_XML, EINVAL, "yspec is required if yb == YB_MODULE"); + return -1; + } if (*xt == NULL){ if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) return -1; @@ -663,57 +641,15 @@ xml_parse_string2(const char *str, return _xml_parse(str, yb, yspec, *xt, xerr); } -/*! Read an XML definition from string and parse it into a parse-tree - * - * @param[in] str String containing XML definition. - * @param[in] yspec Yang specification, or NULL - * @param[in,out] xt Pointer to XML parse tree. If empty will be created. - * @retval 1 Parse OK and all yang assignment made - * @retval 0 Parse OK but yang assigment not made (or only partial) - * @retval -1 Error with clicon_err called. Includes parse error - * - * @code - * cxobj *xt = NULL; - * if (xml_parse_string(str, yspec, &xt) < 0) - * err; - * if (xml_rootchild(xt, 0, &xt) < 0) # If you want to remove TOP - * err; - * @endcode - * @see xml_parse_file - * @see xml_parse_va - * @note You need to free the xml parse tree after use, using xml_free() - * @note If xt is empty on entry, a new TOP xml will be created named "top" and yang binding - * assumed to be TOP - */ -int -xml_parse_string(const char *str, - yang_stmt *yspec, - cxobj **xt) -{ - enum yang_bind yb = YB_PARENT; - - if (xt==NULL){ - clicon_err(OE_XML, EINVAL, "xt is NULL"); - return -1; - } - if (*xt == NULL){ - yb = YB_TOP; /* ad-hoc #1 */ - if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) - return -1; - } - else{ - if (xml_spec(*xt) == NULL) - yb = YB_TOP; /* ad-hoc #2 */ - } - return _xml_parse(str, yb, yspec, *xt, NULL); -} - /*! Read XML from var-arg list and parse it into xml tree * * Utility function using stdarg instead of static string. - * @param[in,out] xtop Top of XML parse tree. If it is NULL, top element - called 'top' will be created. Call xml_free() after use + + * @param[in] yb How to bind yang to XML top-level when parsing * @param[in] yspec Yang specification, or NULL + * @param[in,out] xtop Top of XML parse tree. If it is NULL, top element + * called 'top' will be created. Call xml_free() after use + * @param[out] xerr Reason for failure (yang assignment not made) * @param[in] format Format string for stdarg according to printf(3) * @retval 1 Parse OK and all yang assignment made * @retval 0 Parse OK but yang assigment not made (or only partial) @@ -721,18 +657,20 @@ xml_parse_string(const char *str, * * @code * cxobj *xt = NULL; - * if (xml_parse_va(&xt, NULL, "%d", 22) < 0) + * if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL, "%d", 22) < 0) * err; * xml_free(xt); * @endcode - * @see xml_parse_string - * @see xml_parse_file - * @note If vararg list is empty, consider using xml_parse_string() + * @see clixon_xml_parse_string + * @see clixon_xml_parse_file + * @note If vararg list is empty, consider using clixon_xml_parse_string() */ int -xml_parse_va(cxobj **xtop, - yang_stmt *yspec, - const char *format, ...) +clixon_xml_parse_va(yang_bind yb, + yang_stmt *yspec, + cxobj **xtop, + cxobj **xerr, + const char *format, ...) { int retval = -1; va_list args; @@ -750,104 +688,10 @@ xml_parse_va(cxobj **xtop, va_start(args, format); len = vsnprintf(str, len, format, args) + 1; va_end(args); - retval = xml_parse_string(str, yspec, xtop); /* xml_parse_string2 */ + retval = clixon_xml_parse_string(str, yb, yspec, xtop, xerr); done: if (str) free(str); return retval; } -#ifdef NOTUSED -/*! Generic parse function for xml values - * @param[in] xb xml tree body node, ie containing a value to be parsed - * @param[in] type Type of value to be parsed in value - * @param[out] cvp CLIgen variable containing the parsed value - * @note free cv with cv_free after use. - * @see xml_body_int32 etc, for type-specific parse functions - * @note range check failure returns 0 - */ -static int -xml_body_parse(cxobj *xb, - enum cv_type type, - cg_var **cvp) -{ - int retval = -1; - cg_var *cv = NULL; - int cvret; - char *bstr; - char *reason = NULL; - - if ((bstr = xml_body(xb)) == NULL){ - clicon_err(OE_XML, 0, "No body found"); - goto done; - } - if ((cv = cv_new(type)) == NULL){ - clicon_err(OE_XML, errno, "cv_new"); - goto done; - } - if ((cvret = cv_parse1(bstr, cv, &reason)) < 0){ - clicon_err(OE_XML, errno, "cv_parse"); - goto done; - } - if (cvret == 0){ /* parsing failed */ - clicon_err(OE_XML, errno, "Parsing CV: %s", reason); - if (reason) - free(reason); - } - *cvp = cv; - retval = 0; - done: - if (retval < 0 && cv != NULL) - cv_free(cv); - return retval; -} - -/*! Parse an xml body as int32 - * The real parsing functions are in the cligen code - * @param[in] xb xml tree body node, ie containing a value to be parsed - * @param[out] val Value after parsing - * @retval 0 OK, parsed value in 'val' - * @retval -1 Error, one of: body not found, parse error, - * alloc error. - * @note extend to all other cligen var types and generalize - * @note use yang type info? - * @note range check failure returns 0 - */ -int -xml_body_int32(cxobj *xb, - int32_t *val) -{ - cg_var *cv = NULL; - - if (xml_body_parse(xb, CGV_INT32, &cv) < 0) - return -1; - *val = cv_int32_get(cv); - cv_free(cv); - return 0; -} - -/*! Parse an xml body as uint32 - * The real parsing functions are in the cligen code - * @param[in] xb xml tree body node, ie containing a value to be parsed - * @param[out] val Value after parsing - * @retval 0 OK, parsed value in 'val' - * @retval -1 Error, one of: body not found, parse error, - * alloc error. - * @note extend to all other cligen var types and generalize - * @note use yang type info? - * @note range check failure returns 0 - */ -int -xml_body_uint32(cxobj *xb, - uint32_t *val) -{ - cg_var *cv = NULL; - - if (xml_body_parse(xb, CGV_UINT32, &cv) < 0) - return -1; - *val = cv_uint32_get(cv); - cv_free(cv); - return 0; -} - -#endif /* NOTUSED */ diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index a3659a7c..61a3c0f2 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -1201,18 +1201,17 @@ strip_whitespace(cxobj *xt) * @retval 0 Partial or no yang assigment made (at least one failed) and xerr set * @retval -1 Error * @code - * if (xml_bind_yang(x, yspec, NULL) < 0) + * 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_yang_parent Not top-level and parent is properly yang populated * @see xml_bind_yang0 If the calling xml object should also be populated - * @see xml_bind_yang0_parent */ int xml_bind_yang(cxobj *xt, + yang_bind yb, yang_stmt *yspec, cxobj **xerr) { @@ -1224,33 +1223,7 @@ xml_bind_yang(cxobj *xt, strip_whitespace(xt); xc = NULL; /* Apply on children */ while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - if ((ret = xml_bind_yang0(xc, yspec, xerr)) < 0) - goto done; - if (ret == 0) - failed++; - } - retval = (failed==0) ? 1 : 0; - done: - return retval; -} - -/*! Find yang spec association of tree of XML nodes - * - * Populate xt:s children outgoing from that xt is populated - */ -int -xml_bind_yang_parent(cxobj *xt, - 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_parent(xc, xerr)) < 0) + if ((ret = xml_bind_yang0(xc, yb, yspec, xerr)) < 0) goto done; if (ret == 0) failed++; @@ -1263,15 +1236,22 @@ xml_bind_yang_parent(cxobj *xt, 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) { @@ -1280,49 +1260,26 @@ xml_bind_yang0(cxobj *xt, int ret; int failed = 0; /* we continue loop after failure, should we stop at fail?`*/ - if ((ret = populate_self_top(xt, yspec, xerr)) < 0) - goto done; - 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_parent(xc, xerr)) < 0) + switch (yb){ + case YB_MODULE: + if ((ret = populate_self_top(xt, 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 - * - * Populate xt as if xt:s parent is populated - */ -int -xml_bind_yang0_parent(cxobj *xt, - cxobj **xerr) -{ - int retval = -1; - cxobj *xc; /* xml child */ - int ret; - int failed = 0; /* we continue loop after failure, should we stop at fail?`*/ - - if ((ret = populate_self_parent(xt, xerr)) < 0) + 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_parent(xc, xerr)) < 0) + if ((ret = xml_bind_yang0(xc, YB_PARENT, yspec, xerr)) < 0) goto done; if (ret == 0) failed++; @@ -1356,8 +1313,8 @@ xml_bind_yang0_parent(cxobj *xt, */ int xml_bind_yang_rpc(cxobj *xrpc, - yang_stmt *yspec, - cxobj **xerr) + yang_stmt *yspec, + cxobj **xerr) { int retval = -1; yang_stmt *yrpc = NULL; /* yang node */ @@ -1385,7 +1342,7 @@ xml_bind_yang_rpc(cxobj *xrpc, * 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_parent(x, xerr)) < 0) + if ((ret = xml_bind_yang(x, YB_PARENT, NULL, xerr)) < 0) goto done; if (ret == 0) goto fail; @@ -1452,7 +1409,7 @@ xml_bind_yang_rpc_reply(cxobj *xrpc, } if (yo != NULL){ xml_spec_set(xrpc, yo); - if ((ret = xml_bind_yang(xrpc, yspec, xerr)) < 0) + if ((ret = xml_bind_yang(xrpc, YB_MODULE, yspec, xerr)) < 0) goto done; if (ret == 0) goto fail; diff --git a/lib/src/clixon_xml_sort.c b/lib/src/clixon_xml_sort.c index 95f5a0fc..2b968e4d 100644 --- a/lib/src/clixon_xml_sort.c +++ b/lib/src/clixon_xml_sort.c @@ -100,8 +100,10 @@ xml_cv_cache(cxobj *x, body=""; if ((cv = xml_cv(x)) != NULL) goto ok; - if ((y = xml_spec(x)) == NULL) - goto ok; + if ((y = xml_spec(x)) == NULL){ + clicon_err(OE_XML, EFAULT, "Yang binding missing for xml symbol %s, body:%s", xml_name(x), body); + goto done; + } if (yang_type_get(y, NULL, &yrestype, &options, NULL, NULL, NULL, &fraction) < 0) goto done; yang2cv_type(yang_argument_get(yrestype), &cvtype); @@ -195,8 +197,8 @@ xml_cmp(cxobj *x1, char *b1; char *b2; char *keyname; - cg_var *cv1; - cg_var *cv2; + cg_var *cv1 = NULL; + cg_var *cv2 = NULL; int nr1 = 0; int nr2 = 0; cxobj *x1b; @@ -1324,7 +1326,7 @@ xml_find_index_yang(cxobj *xp, if (revert) goto revert; #endif - if (xml_parse_string(cbuf_get(cb), yc, &xc) < 0) + if (clixon_xml_parse_string(cbuf_get(cb), YB_NONE, NULL, &xc, NULL) < 0) goto done; if (xml_rootchild(xc, 0, &xc) < 0) goto done; diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index 1f21e58b..6c3ebf63 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -327,7 +327,7 @@ yang_modules_state_get(clicon_handle h, /* Parse cb, x is on the form: ... * Note, list is not sorted since it is state (should not be) */ - if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ + if (clixon_xml_parse_string(cbuf_get(cb), YB_MODULE, yspec, &x, NULL) < 0){ if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) goto done; goto fail; diff --git a/test/test_cli.sh b/test/test_cli.sh index 27bea4b8..2ad9f3dc 100755 --- a/test/test_cli.sh +++ b/test/test_cli.sh @@ -73,7 +73,7 @@ new "cli configure using encoded chars name <&" expectfn "$clixon_cli -1 -f $cfg set interfaces interface fddi&< type ianaift:ethernetCsmacd" 0 "" new "cli failed validate" -expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Netconf error: application missing-element Mandatory variable type. Validate failed. Edit and try again or discard changes" +expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 255 "Netconf error: Validate failed. Edit and try again or discard changes: application missing-element Mandatory variable type" new "cli configure ip addr" expectfn "$clixon_cli -1 -f $cfg set interfaces interface eth/0/0 ipv4 address 1.2.3.4 prefix-length 24" 0 "^$" diff --git a/test/test_identity.sh b/test/test_identity.sh index 960ff01d..3e47824f 100755 --- a/test/test_identity.sh +++ b/test/test_identity.sh @@ -253,14 +253,14 @@ new "CLI set wrong acl-type" expectfn "$clixon_cli -1 -f $cfg -l o set acls acl x type undefined" 0 "^$" new "cli validate acl-type" -expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes" +expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 255 " Netconf error: Validate failed. Edit and try again or discard changes: application operation-failed Identityref validation failed, undefined not derived from acl-base" # test empty identityref list new "cli set empty" expectfn "$clixon_cli -1 -f $cfg -l o set e undefined" 0 "^$" new "cli validate empty" -expectfn "$clixon_cli -1 -f $cfg -l o validate" 255 "Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes" +expectpart "$($clixon_cli -1 -f $cfg -l o validate)" 255 "Netconf error: Validate failed. Edit and try again or discard changes: application operation-failed Identityref validation failed, undefined not derived from acl-base" new "netconf discard-changes" expecteof "$clixon_netconf -qf $cfg" 0 "]]>]]>" "^]]>]]>$" diff --git a/test/test_nacm_default.sh b/test/test_nacm_default.sh index 11da4fe7..9b573a11 100755 --- a/test/test_nacm_default.sh +++ b/test/test_nacm_default.sh @@ -28,7 +28,6 @@ cat < $cfg /usr/local/lib/$APPNAME/backend /usr/local/var/$APPNAME/$APPNAME.pidfile $dir - /usr/local/lib/xmldb/text.so false internal none diff --git a/test/test_nacm_module_write.sh b/test/test_nacm_module_write.sh index 14dbf598..b4d27b69 100755 --- a/test/test_nacm_module_write.sh +++ b/test/test_nacm_module_write.sh @@ -248,7 +248,7 @@ new "create list permit" expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate/translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val42"}]}')" 0 'HTTP/1.1 201 Created' new "default update list deny" -expectpart "$(curl -u wilma:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val99"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}' +expectpart "$(curl -u wilma:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/clixon-example:translate/translate=key42 -d '{"clixon-example:translate": [{"k":"key42","value":"val99"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}' new "default delete list deny" expectpart "$(curl -u wilma:bar -siS -X DELETE http://localhost/restconf/data/clixon-example:translate=key42)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}' diff --git a/test/test_restconf.sh b/test/test_restconf.sh index fa3391b7..fbc9787a 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -110,9 +110,12 @@ expectpart "$(curl -is -X OPTIONS http://localhost/restconf/data)" 0 "HTTP/1.1 2 new "restconf HEAD. RFC 8040 4.2" expectpart "$(curl -si -I -H "Accept: application/yang-data+json" http://localhost/restconf/data)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+json" -new "restconf empty rpc" +new "restconf empty rpc JSON" expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+json" -d {\"clixon-example:input\":null} http://localhost/restconf/operations/clixon-example:empty)" 0 "HTTP/1.1 204 No Content" +new "restconf empty rpc XML" +expectpart "$(curl -si -X POST -H "Content-Type: application/yang-data+xml" -d '' http://localhost/restconf/operations/clixon-example:empty)" 0 "HTTP/1.1 204 No Content" + new "restconf empty rpc, default media type should fail" expectpart "$(curl -si -X POST -d {\"clixon-example:input\":null} http://localhost/restconf/operations/clixon-example:empty)" 0 'HTTP/1.1 415 Unsupported Media Type' diff --git a/test/test_upgrade.sh b/test/test_upgrade.sh index 2d4a8d5d..7fc1ef44 100755 --- a/test/test_upgrade.sh +++ b/test/test_upgrade.sh @@ -332,9 +332,9 @@ runtest true startup 'always workalways work' 'old versionalways workother textbla bla' # sorted -runtest true startup 'always work' 'old versionbla blaalways workother text' # unsorted - +runtest true startup 'always work' 'old versionalways workother textbla bla' # sorted +#runtest true startup 'always work' 'old versionbla blaalways workother text' # unsorted +exit new "6. Load non-compat invalid running. Enter failsafe, startup invalid." (cd $dir; rm -f tmp_db candidate_db running_db startup_db) # remove databases (cd $dir; cp non-compat-invalid.xml running_db) @@ -346,8 +346,8 @@ runtest true running 'always work' ' new "7. Load compatible invalid startup." (cd $dir; rm -f tmp_db candidate_db running_db startup_db) # remove databases (cd $dir; cp compat-invalid.xml startup_db) -#runtest true startup 'always work' 'old versionalways workother textbla bla' # sorted -runtest true startup 'always work' 'old versionbla blaalways workother text' # unsorted +runtest true startup 'always work' 'old versionalways workother textbla bla' # sorted +#runtest true startup 'always work' 'old versionbla blaalways workother text' # unsorted # This testcase contains an error/exception of the clixon xml parser, and # I cant track down the memory leakage. diff --git a/util/clixon_util_datastore.c b/util/clixon_util_datastore.c index ff4122ba..c3e5a808 100644 --- a/util/clixon_util_datastore.c +++ b/util/clixon_util_datastore.c @@ -254,12 +254,12 @@ main(int argc, char **argv) clicon_err(OE_UNIX, errno, "open(%s)", xmlfilename); goto done; } - if (xml_parse_file(fd, yspec, &xt) < 0) + if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0) goto done; close(fd); } else - if (xml_parse_string(argv[2], yspec, &xt) < 0) + if (clixon_xml_parse_string(argv[2], YB_MODULE, yspec, &xt, NULL) < 0) goto done; if (xml_rootchild(xt, 0, &xt) < 0) goto done; diff --git a/util/clixon_util_grpc.c b/util/clixon_util_grpc.c index 00a09493..771de00a 100644 --- a/util/clixon_util_grpc.c +++ b/util/clixon_util_grpc.c @@ -40,7 +40,7 @@ * On success, nothing is printed and exitcode 0 * On failure, an error is printed on stderr and exitcode != 0 * Failure error prints are different, it would be nice to make them more - * uniform. (see clicon_rpc_generate_error) + * uniform. (see clixon_netconf_error) */ #ifdef HAVE_CONFIG_H diff --git a/util/clixon_util_insert.c b/util/clixon_util_insert.c index ee923fbc..cfc016e8 100644 --- a/util/clixon_util_insert.c +++ b/util/clixon_util_insert.c @@ -140,19 +140,14 @@ main(int argc, char **argv) clicon_debug(1, "xpath:%s", xpath); if ((yspec = yspec_new()) == NULL) goto done; -#if 1 if (yang_spec_parse_file(h, filename, yspec) < 0) goto done; -#else - if (yang_parse_file(fd, "yang test", yspec) == NULL) - goto done; -#endif /* Parse base XML */ - if (xml_parse_string(x0str, yspec, &x0) < 0){ + if (clixon_xml_parse_string(x0str, YB_MODULE, yspec, &x0, NULL) < 0){ clicon_err(OE_XML, 0, "Parsing base xml: %s", x0str); goto done; } - if (xml_bind_yang(x0, yspec, NULL) < 0) + if (xml_bind_yang(x0, YB_MODULE, yspec, NULL) < 0) goto done; if ((xb = xpath_first(x0, NULL, "%s", xpath)) == NULL){ clicon_err(OE_XML, 0, "xpath: %s not found in x0", xpath); @@ -163,11 +158,11 @@ main(int argc, char **argv) xml_print(stderr, xb); } /* Parse insert XML */ - if (xml_parse_string(xistr, yspec, &xi) < 0){ + if (clixon_xml_parse_string(xistr, YB_MODULE, yspec, &xi, NULL) < 0){ clicon_err(OE_XML, 0, "Parsing insert xml: %s", xistr); goto done; } - if (xml_bind_yang(xi, yspec, NULL) < 0) + if (xml_bind_yang(xi, YB_MODULE, yspec, NULL) < 0) goto done; if ((xi = xpath_first(xi, NULL, "%s", xpath)) == NULL){ clicon_err(OE_XML, 0, "xpath: %s not found in xi", xpath); diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index b2b56b98..18a43fea 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -134,7 +134,7 @@ main(int argc, return -1; } } - if ((ret = json_parse_file(0, yspec, &xt, &xerr)) < 0) + if ((ret = clixon_json_parse_file(0, yspec?YB_MODULE:YB_NONE, yspec, &xt, &xerr)) < 0) goto done; if (ret == 0){ xml_print(stderr, xerr); diff --git a/util/clixon_util_path.c b/util/clixon_util_path.c index 48c7b558..5a02df05 100644 --- a/util/clixon_util_path.c +++ b/util/clixon_util_path.c @@ -212,7 +212,7 @@ main(int argc, * If fd=0, then continue reading from stdin (after CR) * If fd>0, reading from file opened as argv[1] */ - if (xml_parse_file(fd, NULL, &x) < 0){ + if (clixon_xml_parse_file(fd, YB_NONE, NULL, NULL, &x, NULL) < 0){ fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason); return -1; } @@ -220,7 +220,7 @@ main(int argc, /* Validate XML as well */ if (yang_file_dir){ /* Populate */ - if (xml_bind_yang(x, yspec, NULL) < 0) + if (xml_bind_yang(x, YB_MODULE, yspec, NULL) < 0) goto done; /* Add default values */ if (xml_apply(x, CX_ELMNT, xml_default, h) < 0) diff --git a/util/clixon_util_socket.c b/util/clixon_util_socket.c index 17a48bd9..101221a2 100644 --- a/util/clixon_util_socket.c +++ b/util/clixon_util_socket.c @@ -142,7 +142,7 @@ main(int argc, } /* 2. Parse data (xml/json) */ if (jsonin){ - if ((ret = json_parse_file(fd, NULL, &xt, &xerr)) < 0) + if ((ret = clixon_json_parse_file(fd, YB_NONE, NULL, &xt, &xerr)) < 0) goto done; if (ret == 0){ fprintf(stderr, "Invalid JSON\n"); @@ -150,7 +150,7 @@ main(int argc, } } else{ - if (xml_parse_file(fd, NULL, &xt) < 0){ + if (clixon_xml_parse_file(fd, YB_NONE, NULL, NULL, &xt, NULL) < 0){ fprintf(stderr, "xml parse error: %s\n", clicon_err_reason); goto done; } diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index fcf42ad2..465bbae8 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -40,7 +40,7 @@ * On success, nothing is printed and exitcode 0 * On failure, an error is printed on stderr and exitcode != 0 * Failure error prints are different, it would be nice to make them more - * uniform. (see clicon_rpc_generate_error) + * uniform. (see clixon_netconf_error) */ #ifdef HAVE_CONFIG_H @@ -161,7 +161,7 @@ main(int argc, char *top_path = NULL; cxobj *xbot; /* Place in xtop where base cxobj is parsed */ cvec *nsc = NULL; - enum yang_bind yb; + yang_bind yb; /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR); @@ -260,10 +260,14 @@ main(int argc, clicon_err(OE_YANG, errno, "open(%s)", top_input_filename); goto done; } - if (xml_parse_file(tfd, yspec, &xtop) < 0){ + if ((ret = clixon_xml_parse_file(tfd, YB_MODULE, yspec, NULL, &xtop, &xerr)) < 0){ fprintf(stderr, "xml parse error: %s\n", clicon_err_reason); goto done; } + if (ret == 0){ + clixon_netconf_error(OE_NETCONF, xerr, "Parse top file", NULL); + goto done; + } if (validate_tree(h, xtop, yspec) < 0) goto done; @@ -284,10 +288,10 @@ main(int argc, } /* 2. Parse data (xml/json) */ if (jsonin){ - if ((ret = json_parse_file(fd, yspec, &xt, &xerr)) < 0) + if ((ret = clixon_json_parse_file(fd, top_input_filename?YB_PARENT:YB_MODULE, yspec, &xt, &xerr)) < 0) goto done; if (ret == 0){ - clicon_rpc_generate_error(xerr, "util_xml", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "util_xml", NULL); goto done; } } @@ -295,15 +299,15 @@ main(int argc, if (!yang_file_dir) yb = YB_NONE; else if (xt == NULL) - yb = YB_TOP; + yb = YB_MODULE; else yb = YB_PARENT; - if ((ret = xml_parse_file2(fd, yb, yspec, NULL, &xt, &xerr)) < 0){ + if ((ret = clixon_xml_parse_file(fd, yb, yspec, NULL, &xt, &xerr)) < 0){ fprintf(stderr, "xml parse error: %s\n", clicon_err_reason); goto done; } if (ret == 0){ - clicon_rpc_generate_error(xerr, "util_xml", NULL); + clixon_netconf_error(OE_NETCONF, xerr, "util_xml", NULL); goto done; } } diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index 9d374c77..4078d000 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -280,7 +280,7 @@ main(int argc, * If fd=0, then continue reading from stdin (after CR) * If fd>0, reading from file opened as argv[1] */ - if (xml_parse_file(fd, NULL, &x0) < 0){ + if (clixon_xml_parse_file(fd, YB_NONE, NULL, NULL, &x0, NULL) < 0){ fprintf(stderr, "Error: parsing: %s\n", clicon_err_reason); return -1; } @@ -288,7 +288,7 @@ main(int argc, /* Validate XML as well */ if (yang_file_dir){ /* Populate */ - if (xml_bind_yang(x0, yspec, NULL) < 0) + if (xml_bind_yang(x0, YB_MODULE, yspec, NULL) < 0) goto done; /* Sort */ if (xml_apply(x0, CX_ELMNT, xml_sort, h) < 0)