diff --git a/CHANGELOG.md b/CHANGELOG.md index ed1515c2..ab2e4a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,17 +69,18 @@ ### API changes on existing features (you may need to change your code) -* The Clixon API has been extended with namespaces, or namespace contexts in the following cases [Netconf get/get-config :xpath capability does not support namespaces](https://github.com/clicon/clixon/issues/75) - * CLIspec functions have added namespace parameter: +* The Clixon API has been extended with namespaces, or namespace contexts in the following cases (see [README.md#xml-and-xpath] for explanation): + * CLIspec functions have added optional namespace parameter: * `cli_show_config ` --> `cli_show_config ` * `cli_copy_config ...` --> `cli_copy_config ...` - * Xpath API - * `xpath_first(x, format, ...)` --> `xpath_first(x, nsc, format, ...)` - * `xpath_vec(x, format, vec, veclen, ...)` --> `xpath_vec(x, nsc, format, vec, veclen, ...)` - * `xpath_vec_flag(x, format, flags, vec, veclen, ...)` --> `xpath_vec_flag(x, format, flags, vec, veclen, ...)` + * Change the following Xpath API functions (xpath_first and xpath_vec remain as-is): + * `xpath_vec_flag(x, format, flags, vec, veclen, ...)` --> `xpath_vec_flag(x, nsc, format, flags, vec, veclen, ...)` * `xpath_vec_bool(x, format, ...)` --> `xpath_vec_bool(x, nsc, format, ...)` * `xpath_vec_ctx(x, xpath, xp)` --> `xpath_vec_ctx(x, nsc, xpath, xp)` - * xmldb_get0 has an added `nsc` parameter: + * New Xpath API functions with namespace contexts: + * `xpath_first_nsc(x, nsc, format, ...)` + * `xpath_vec_nsc(x, nsc, format, vec, veclen, ...)` + * Change xmldb_get0 with added `nsc` parameter: * `xmldb_get0(h, db, xpath, copy, xret, msd)` --> `xmldb_get0(h, db, nsc, xpath, copy, xret, msd)` * The plugin statedata callback (ca_statedata) has been extended with an nsc parameter: * `int example_statedata(clicon_handle h, cvec *nsc, char *xpath, cxobj *xstate);` @@ -104,7 +105,6 @@ ``` curl -X PUT http://localhost/restconf/data/mod:a -d {"a":"x"} ``` - * Undefine `RESTCONF_NS_DATA_CHECK` in include/clixon_custom.h to disable strict check. * Many validation functions have changed error parameter from cbuf to xml tree. * XML trees are more flexible for utility tools * If you use these(mostly internal), you need to change the error function: `generic_validate, from_validate_common, xml_yang_validate_all_top, xml_yang_validate_all, xml_yang_validate_add, xml_yang_validate_rpc, xml_yang_validate_list_key_only` diff --git a/README.md b/README.md index 50263250..cf539327 100644 --- a/README.md +++ b/README.md @@ -131,14 +131,14 @@ The standards covered include: - [Namespaces in XML 1.0](https://www.w3.org/TR/2009/REC-xml-names-20091208) - [XPATH 1.0](https://www.w3.org/TR/xpath-10) -Not supported: +Not supported in the XML: - !DOCTYPE (ie DTD) -The following xpath axes are supported: -- CHILD, DESCENDANT, DESCENDANT_OR_SELF, SELF, and PARENT +The following XPATH axes are supported: +- child, descendant, descendant_or_self, self, and parent The following xpath axes are _not_ supported: -- PRECEEDING, PRECEEDING_SIBLING, NAMESPACE, FOLLOWING_SIBLING, FOLLOWING, ANCESTOR,ANCESTOR_OR_SELF, ATTRIBUTE +- preceeding, preceeding_sibling, namespace, following_sibling, following, ancestor,ancestor_or_self, and attribute Note that base netconf namespace syntax is not enforced but recommended, which means that the following two expressions are treated equivalently: ``` @@ -157,7 +157,10 @@ XPATHs may contain prefixes. Example: `/if:a/if:b`. The prefixes have associated namespaces. For example, `if` may be bound to `urn:ietf:params:xml:ns:yang:ietf-interfaces`. The prefix to namespace binding is called a _namespace context_ (nsc). +In yang, the xpath and xml prefixes may not be well-known. For example, the import statement specifies a prefix to an imported module that is local in scope. Other modules may use another prefix. The module name and namespace however are unique. + In the Clixon API, there are two variants on namespace contexts: _implicit_ (given by the XML); or _explicit_ given by an external mapping. + #### 1. Implicit namespace mapping Implicit mapping is typical for basic known XML, where the context is @@ -169,7 +172,8 @@ Example: XML: XPATH: /if:a/ip:b ``` -When you call an xpath API function, call it with nsc set to NULL. This is the default. +When you call an xpath API function, call it with nsc set to NULL, or use an API function without an nsc parameter. +This is the default and normal case. #### 2. Explicit namespace mapping @@ -184,7 +188,7 @@ call. Example: XML: NETCONF:... */ - if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL) + if ((xfilter = xpath_first(xn, "filter")) != NULL) ftype = xml_find_value(xfilter, "type"); if (ftype == NULL || strcmp(ftype, "xpath")==0){ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) @@ -154,8 +154,8 @@ netconf_get_config(clicon_handle h, if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; if (xfilter && - (xfilterconf = xpath_first(xfilter, NULL, "//configuration"))!= NULL && - (xconf = xpath_first(*xret, NULL, "/rpc-reply/data")) != NULL){ + (xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL && + (xconf = xpath_first(*xret, "/rpc-reply/data")) != NULL){ /* xml_filter removes parts of xml tree not matching */ if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) || xml_filter(xfilterconf, xconf) < 0){ @@ -208,7 +208,7 @@ get_edit_opts(cxobj *xn, cxobj *x; char *optstr; - if ((x = xpath_first(xn, NULL, "test-option")) != NULL){ + if ((x = xpath_first(xn, "test-option")) != NULL){ if ((optstr = xml_body(x)) != NULL){ if (strcmp(optstr, "test-then-set") == 0) *testopt = TEST_THEN_SET; @@ -220,7 +220,7 @@ get_edit_opts(cxobj *xn, goto parerr; } } - if ((x = xpath_first(xn, NULL, "error-option")) != NULL){ + if ((x = xpath_first(xn, "error-option")) != NULL){ if ((optstr = xml_body(x)) != NULL){ if (strcmp(optstr, "stop-on-error") == 0) *erropt = STOP_ON_ERROR; @@ -352,7 +352,7 @@ netconf_get(clicon_handle h, cxobj *xconf; /* ie ... */ - if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL) + if ((xfilter = xpath_first(xn, "filter")) != NULL) ftype = xml_find_value(xfilter, "type"); if (ftype == NULL || strcmp(ftype, "xpath")==0){ if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) @@ -366,8 +366,8 @@ netconf_get(clicon_handle h, if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; if (xfilter && - (xfilterconf = xpath_first(xfilter, NULL, "//configuration"))!= NULL && - (xconf = xpath_first(*xret, NULL, "/rpc-reply/data")) != NULL){ + (xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL && + (xconf = xpath_first(*xret, "/rpc-reply/data")) != NULL){ /* xml_filter removes parts of xml tree not matching */ if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) || xml_filter(xfilterconf, xconf) < 0){ @@ -448,7 +448,7 @@ netconf_notification_cb(int s, if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netconf:notification:1.0")) == NULL) goto done; - if ((xn = xpath_first(xt, nsc, "notification")) == NULL) + if ((xn = xpath_first_nsc(xt, nsc, "notification")) == NULL) goto ok; /* create netconf message */ if ((cb = cbuf_new()) == NULL){ @@ -500,7 +500,7 @@ netconf_create_subscription(clicon_handle h, int s; char *ftype; - if ((xfilter = xpath_first(xn, NULL, "//filter")) != NULL){ + if ((xfilter = xpath_first(xn, "//filter")) != NULL){ if ((ftype = xml_find_value(xfilter, "type")) != NULL){ if (strcmp(ftype, "xpath") != 0){ xml_parse_va(xret, NULL, "" @@ -516,7 +516,7 @@ netconf_create_subscription(clicon_handle h, } if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, &s) < 0) goto done; - if (xpath_first(*xret, NULL, "rpc-reply/rpc-error") != NULL) + if (xpath_first(*xret, "rpc-reply/rpc-error") != NULL) goto ok; if (event_reg_fd(s, netconf_notification_cb, @@ -622,7 +622,7 @@ netconf_application_rpc(clicon_handle h, */ if (0) if ((youtput = yang_find(yrpc, Y_OUTPUT, NULL)) != NULL){ - xoutput=xpath_first(*xret, NULL, "/"); + xoutput=xpath_first(*xret, "/"); xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */ if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0) goto done; diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 43f094e3..e7613f87 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -415,7 +415,7 @@ api_return_err(clicon_handle h, clicon_debug(1, "%s", __FUNCTION__); if ((cb = cbuf_new()) == NULL) goto done; - if ((xtag = xpath_first(xerr, NULL, "//error-tag")) == NULL){ + if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){ notfound(r); goto ok; } diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 8a4931f7..cc6dabef 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -396,7 +396,7 @@ api_restconf(clicon_handle h, else{ if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0) goto done; goto ok; diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 2a6d4059..cc7f9f49 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -212,7 +212,7 @@ api_data_get2(clicon_handle h, if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; clicon_err_reset(); - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -229,7 +229,7 @@ api_data_get2(clicon_handle h, if (clicon_rpc_get(h, xpath, namespace, &xret) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -251,7 +251,7 @@ api_data_get2(clicon_handle h, } #endif /* Check if error return */ - if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; @@ -276,10 +276,10 @@ api_data_get2(clicon_handle h, } } else{ - if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){ + if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){ if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -465,10 +465,8 @@ api_data_post(clicon_handle h, cxobj *xtop = NULL; /* top of api-path */ cxobj *xbot = NULL; /* bottom of api-path */ yang_stmt *ybot = NULL; /* yang of xbot */ -#ifdef RESTCONF_NS_DATA_CHECK yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */ yang_stmt *ymoddata = NULL; /* yang module of data (-d) */ -#endif yang_stmt *yspec; cxobj *xa; cxobj *xret = NULL; @@ -496,15 +494,13 @@ api_data_post(clicon_handle h, if (api_path){ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0) goto done; -#ifdef RESTCONF_NS_DATA_CHECK if (ybot) ymodapi=ys_module(ybot); -#endif if (ret == 0){ /* validation failed */ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; clicon_err_reset(); - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -518,7 +514,7 @@ api_data_post(clicon_handle h, if (xml_parse_string(data, NULL, &xdata0) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -531,7 +527,7 @@ api_data_post(clicon_handle h, if ((ret = json_parse_str(data, 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){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -540,7 +536,7 @@ api_data_post(clicon_handle h, goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -555,7 +551,7 @@ api_data_post(clicon_handle h, if (xml_child_nr(xdata0) != 1){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -564,7 +560,6 @@ api_data_post(clicon_handle h, goto ok; } xdata = xml_child_i(xdata0,0); -#ifdef RESTCONF_NS_DATA_CHECK /* If the api-path (above) defines a module, then xdata must have a prefix * and it match the module defined in api-path. * In a POST, maybe there are cornercases where xdata (which is a child) and @@ -577,7 +572,7 @@ api_data_post(clicon_handle h, if (ymoddata != ymodapi){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -594,7 +589,6 @@ api_data_post(clicon_handle h, goto ok; } } -#endif /* RESTCONF_NS_DATA_CHECK */ /* Add operation (create/replace) as attribute */ if ((xa = xml_new("operation", xdata, NULL)) == NULL) @@ -620,7 +614,7 @@ api_data_post(clicon_handle h, clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; @@ -634,14 +628,14 @@ api_data_post(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; - if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ cbuf_reset(cbx); cprintf(cbx, "", username?username:""); cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0) goto done; /* log errors from discard, but ignore */ - if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL) + if ((xpath_first(xretdis, "//rpc-error")) != NULL) clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__); if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) /* Use original xe */ goto done; @@ -664,7 +658,7 @@ api_data_post(clicon_handle h, if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; /* If copy-config failed, log and ignore (already committed) */ - if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); } @@ -809,10 +803,8 @@ api_data_put(clicon_handle h, cxobj *xtop = NULL; /* top of api-path */ cxobj *xbot = NULL; /* bottom of api-path */ yang_stmt *ybot = NULL; /* yang of xbot */ -#ifdef RESTCONF_NS_DATA_CHECK yang_stmt *ymodapi = NULL; /* yang module of api-path (if any) */ yang_stmt *ymoddata = NULL; /* yang module of data (-d) */ -#endif cxobj *xparent; yang_stmt *yp; /* yang parent */ yang_stmt *yspec; @@ -845,15 +837,13 @@ api_data_put(clicon_handle h, if (api_path){ if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot)) < 0) goto done; -#ifdef RESTCONF_NS_DATA_CHECK if (ybot) ymodapi=ys_module(ybot); -#endif if (ret == 0){ /* validation failed */ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; clicon_err_reset(); - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -868,7 +858,7 @@ api_data_put(clicon_handle h, if (xml_parse_string(data, yspec, &xdata0) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -881,7 +871,7 @@ api_data_put(clicon_handle h, if ((ret = json_parse_str(data, 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){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -890,7 +880,7 @@ api_data_put(clicon_handle h, goto ok; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -905,7 +895,7 @@ api_data_put(clicon_handle h, if (xml_child_nr(xdata0) != 1){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -914,7 +904,6 @@ api_data_put(clicon_handle h, goto ok; } xdata = xml_child_i(xdata0,0); -#ifdef RESTCONF_NS_DATA_CHECK /* If the api-path (above) defines a module, then xdata must have a prefix * and it match the module defined in api-path * This does not apply if api-path is / (no module) @@ -925,7 +914,7 @@ api_data_put(clicon_handle h, if (ymoddata != ymodapi){ if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -934,7 +923,6 @@ api_data_put(clicon_handle h, goto ok; } } -#endif /* RESTCONF_NS_DATA_CHECK */ /* Add operation (create/replace) as attribute */ if ((xa = xml_new("operation", xdata, NULL)) == NULL) @@ -965,7 +953,7 @@ api_data_put(clicon_handle h, if (strcmp(dname, xml_name(xbot))){ if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -990,7 +978,7 @@ api_data_put(clicon_handle h, if (match_list_keys(ybot, xdata, xbot) < 0){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1014,7 +1002,7 @@ api_data_put(clicon_handle h, if (parbod == NULL || strcmp(parbod, xml_body(xdata))){ if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1053,7 +1041,7 @@ api_data_put(clicon_handle h, clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; @@ -1066,14 +1054,14 @@ api_data_put(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; - if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ cbuf_reset(cbx); cprintf(cbx, "", username?username:""); cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0) goto done; /* log errors from discard, but ignore */ - if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL) + if ((xpath_first(xretdis, "//rpc-error")) != NULL) clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__); if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; @@ -1096,7 +1084,7 @@ api_data_put(clicon_handle h, if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; /* If copy-config failed, log and ignore (already committed) */ - if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); } @@ -1204,7 +1192,7 @@ api_data_delete(clicon_handle h, if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; clicon_err_reset(); - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1231,7 +1219,7 @@ api_data_delete(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "//rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; @@ -1245,14 +1233,14 @@ api_data_delete(clicon_handle h, cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; - if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ cbuf_reset(cbx); cprintf(cbx, "", NACM_RECOVERY_USER); cprintf(cbx, ""); if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0) goto done; /* log errors from discard, but ignore */ - if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL) + if ((xpath_first(xretdis, "//rpc-error")) != NULL) clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__); if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; @@ -1275,7 +1263,7 @@ api_data_delete(clicon_handle h, if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0) goto done; /* If copy-config failed, log and ignore (already committed) */ - if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){ + if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){ clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__); } @@ -1442,7 +1430,7 @@ api_operations_post_input(clicon_handle h, if (xml_parse_string(data, yspec, &xdata) < 0){ if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1455,7 +1443,7 @@ api_operations_post_input(clicon_handle h, if ((ret = json_parse_str(data, 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){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1464,7 +1452,7 @@ api_operations_post_input(clicon_handle h, goto fail; } if (ret == 0){ - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1497,7 +1485,7 @@ api_operations_post_input(clicon_handle h, else if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1571,7 +1559,7 @@ api_operations_post_output(clicon_handle h, xml_child_nr_type(xret, CX_ELMNT) != 1){ if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1608,7 +1596,7 @@ api_operations_post_output(clicon_handle h, (ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0) goto done; if (ret == 0){ /* validation failed */ - if ((xe = xpath_first(xerr, NULL, "rpc-reply/rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-reply/rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1741,7 +1729,7 @@ api_operations_post(clicon_handle h, if (oppath == NULL || strcmp(oppath,"/")==0){ if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1760,7 +1748,7 @@ api_operations_post(clicon_handle h, if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){ if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1771,7 +1759,7 @@ api_operations_post(clicon_handle h, if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){ if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0) goto done; - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1800,7 +1788,7 @@ api_operations_post(clicon_handle h, if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0) goto done; clicon_err_reset(); - if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xerr, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto done; } @@ -1840,7 +1828,7 @@ api_operations_post(clicon_handle h, if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0) goto done; if (ret == 0){ - if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){ + if ((xe = xpath_first(xret, "rpc-error")) == NULL){ clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)"); goto ok; } @@ -1872,7 +1860,7 @@ api_operations_post(clicon_handle h, if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0) goto done; /* Local error: return it and quit */ - if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; @@ -1881,7 +1869,7 @@ api_operations_post(clicon_handle h, else { /* Send to backend */ if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0) goto done; - if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; diff --git a/apps/restconf/restconf_stream.c b/apps/restconf/restconf_stream.c index 7ab03e48..5c633347 100644 --- a/apps/restconf/restconf_stream.c +++ b/apps/restconf/restconf_stream.c @@ -187,11 +187,11 @@ restconf_stream_cb(int s, clicon_err(OE_PLUGIN, errno, "cbuf_new"); goto done; } - if ((xn = xpath_first(xtop, NULL, "notification")) == NULL) + if ((xn = xpath_first(xtop, "notification")) == NULL) goto ok; #ifdef notused - xt = xpath_first(xn, NULL, "eventTime"); - if ((xe = xpath_first(xn, NULL, "event")) == NULL) /* event can depend on yang? */ + xt = xpath_first(xn, "eventTime"); + if ((xe = xpath_first(xn, "event")) == NULL) /* event can depend on yang? */ goto ok; if (xt) @@ -268,7 +268,7 @@ restconf_stream(clicon_handle h, cprintf(cb, "]]>]]>"); if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0) goto done; - if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){ + if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){ if (api_return_err(h, r, xe, pretty, use_xml, 0) < 0) goto done; goto ok; @@ -416,7 +416,7 @@ api_stream(clicon_handle h, else{ if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ if (api_return_err(h, r, xerr, pretty, use_xml, 0) < 0) goto done; goto ok; diff --git a/doc/FAQ.md b/doc/FAQ.md index 37af03a0..de9f60d0 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -480,7 +480,7 @@ You use XPATHs on the XML trees in the transaction commit callback. Suppose you want to print all added interfaces: ``` cxobj *target = transaction_target(td); # wanted XML tree - vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); /* Get added i/fs */ + vec = xpath_vec_flag(target, NULL, "//interface", &len, XML_FLAG_ADD); /* Get added i/fs */ for (i=0; i"); @@ -415,7 +415,7 @@ upgrade_2016(clicon_handle h, if ((name = xml_find_body(xi, "name")) == NULL) continue; /* shouldnt happen */ /* Get corresponding /interfaces/interface entry */ - xif = xpath_first(xt, NULL, "/interfaces/interface[name=\"%s\"]", name); + xif = xpath_first(xt, "/interfaces/interface[name=\"%s\"]", name); /* - Move /if:interfaces-state/if:interface/if:admin-status to * /if:interfaces/if:interface/ */ if ((x = xml_find(xi, "admin-status")) != NULL && xif){ @@ -518,7 +518,7 @@ upgrade_2018(clicon_handle h, /* Change type /interfaces/interface/statistics/in-octets to * decimal64 with fraction-digits 3 and divide values with 1000 */ - if ((x = xpath_first(xi, NULL, "statistics/in-octets")) != NULL){ + if ((x = xpath_first(xi, "statistics/in-octets")) != NULL){ if ((xb = xml_body_get(x)) != NULL){ uint64_t u64; cbuf *cb = cbuf_new(); diff --git a/example/main/example_backend_nacm.c b/example/main/example_backend_nacm.c index a3c42657..c0f49a40 100644 --- a/example/main/example_backend_nacm.c +++ b/example/main/example_backend_nacm.c @@ -87,7 +87,7 @@ nacm_validate(clicon_handle h, if (_transaction_log){ transaction_log(h, td, LOG_NOTICE, __FUNCTION__); if (_transaction_error_toggle==0 && - xpath_first(transaction_target(td), NULL, "%s", _transaction_xpath)){ + xpath_first(transaction_target(td), "%s", _transaction_xpath)){ _transaction_error_toggle=1; /* toggle if triggered */ clicon_err(OE_XML, 0, "User error"); return -1; /* induce fail */ @@ -116,7 +116,7 @@ nacm_commit(clicon_handle h, if (_transaction_log){ transaction_log(h, td, LOG_NOTICE, __FUNCTION__); if (_transaction_error_toggle==1 && - xpath_first(target, NULL, "%s", _transaction_xpath)){ + xpath_first(target, "%s", _transaction_xpath)){ _transaction_error_toggle=0; /* toggle if triggered */ clicon_err(OE_XML, 0, "User error"); return -1; /* induce fail */ diff --git a/example/main/example_cli.c b/example/main/example_cli.c index 39ae19ea..17cda3e8 100644 --- a/example/main/example_cli.c +++ b/example/main/example_cli.c @@ -105,7 +105,7 @@ example_client_rpc(clicon_handle h, /* Send to backend */ if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } diff --git a/include/clixon_custom.h b/include/clixon_custom.h index c454fbfa..1127dfce 100644 --- a/include/clixon_custom.h +++ b/include/clixon_custom.h @@ -48,9 +48,3 @@ */ #define USE_NETCONF_NS_AS_DEFAULT -/* Make namespace check on RESTCONF PUT and POST -d data - * Should be defined according to standard - * Undefine it if you want allow no namespace (pick first name it finds in list - * of loaded modules). - */ -#define RESTCONF_NS_DATA_CHECK diff --git a/lib/clixon/clixon_xpath.h b/lib/clixon/clixon_xpath.h index 50856b8a..ece3ecdb 100644 --- a/lib/clixon/clixon_xpath.h +++ b/lib/clixon/clixon_xpath.h @@ -116,20 +116,39 @@ char* xpath_tree_int2str(int nodetype); int xpath_tree_print(cbuf *cb, xpath_tree *xs); int xpath_tree_free(xpath_tree *xs); int xpath_parse(cvec *nsc, char *xpath, xpath_tree **xptree); +int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp); #if defined(__GNUC__) && __GNUC__ >= 3 -cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4))); -int xpath_vec(cxobj *xcur, cvec *nsc, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6))); -int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *format, uint16_t flags, +int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4))); +int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *xpformat, uint16_t flags, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 7))); -int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *format, ...) __attribute__ ((format (printf, 3, 4))); + #else -cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *format, ...); -int xpath_vec(cxobj *xcur, cvec *nsc, char *format, cxobj ***vec, size_t *veclen, ...); -int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *format, uint16_t flags, +int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *xpformat, ...); +int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *xpformat, uint16_t flags, cxobj ***vec, size_t *veclen, ...); -int xpath_vec_bool(cxobj *xcur, cvec *nsc, char *format, ...); #endif -int xpath_vec_ctx(cxobj *xcur, cvec *nsc, char *xpath, xp_ctx **xrp); + +/* Functions with explicit namespace context (nsc) set. If you do not need + * explicit namespace contexts (most do not) consider using the API functions + * below without nsc set. + * If you do not know what a namespace context is, see README.md#xml-and-xpath + */ +#if defined(__GNUC__) && __GNUC__ >= 3 +cxobj *xpath_first_nsc(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4))); +int xpath_vec_nsc(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6))); +#else +cxobj *xpath_first_nsc(cxobj *xcur, cvec *nsc, char *xpformat, ...); +int xpath_vec_nsc(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...); +#endif + +/* Functions with nsc == NULL (implicit xpath context). */ +#if defined(__GNUC__) && __GNUC__ >= 3 +cxobj *xpath_first(cxobj *xcur, char *xpformat, ...) __attribute__ ((format (printf, 2, 3))); +int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5))); +#else +cxobj *xpath_first(cxobj *xcur, char *xpformat, ...); +int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...); +#endif #endif /* _CLIXON_XPATH_H */ diff --git a/lib/src/clixon_datastore_read.c b/lib/src/clixon_datastore_read.c index 966d2bc4..8f1b16a0 100644 --- a/lib/src/clixon_datastore_read.c +++ b/lib/src/clixon_datastore_read.c @@ -258,7 +258,7 @@ text_read_modstate(clicon_handle h, if ((name = xml_find_body(xm, "name")) == NULL) continue; /* 3a) There is no such module in the system */ - if ((xs = xpath_first(xmcache, NULL, "module[name=\"%s\"]", name)) == NULL){ + if ((xs = xpath_first(xmcache, "module[name=\"%s\"]", name)) == NULL){ // fprintf(stderr, "%s: Module %s: not in system\n", __FUNCTION__, name); if ((xm2 = xml_dup(xm)) == NULL) goto done; @@ -420,7 +420,7 @@ xmldb_get_nocache(clicon_handle h, goto done; /* Here xt looks like: ... */ /* Given the xpath, return a vector of matches in xvec */ - if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec_nsc(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* If vectors are specified then mark the nodes found with all ancestors @@ -526,7 +526,7 @@ xmldb_get_cache(clicon_handle h, */ /* Here xt looks like: ... */ - if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec_nsc(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* Make new tree by copying top-of-tree from x0t to x1t */ @@ -615,7 +615,7 @@ xmldb_get_zerocopy(clicon_handle h, else x0t = de->de_xml; /* Here xt looks like: ... */ - if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec_nsc(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; /* Iterate through the match vector * For every node found in x0, mark the tree up to t1 diff --git a/lib/src/clixon_datastore_write.c b/lib/src/clixon_datastore_write.c index 1e4247ee..594a9305 100644 --- a/lib/src/clixon_datastore_write.c +++ b/lib/src/clixon_datastore_write.c @@ -662,7 +662,7 @@ xmldb_put(clicon_handle h, if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-netconf-acm")) == NULL) goto done; if (xnacm0 != NULL && - (xnacm = xpath_first(xnacm0, nsc, "nacm")) != NULL){ + (xnacm = xpath_first_nsc(xnacm0, nsc, "nacm")) != NULL){ /* Pre-NACM access step, if permit, then dont do any nacm checks in * text_modify_* below */ if ((permit = nacm_access(mode, xnacm, username)) < 0) diff --git a/lib/src/clixon_nacm.c b/lib/src/clixon_nacm.c index 27b9eea2..1e17c60f 100644 --- a/lib/src/clixon_nacm.c +++ b/lib/src/clixon_nacm.c @@ -214,7 +214,7 @@ nacm_rpc(char *rpc, goto step10; /* User's group */ - if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0) + if (xpath_vec_nsc(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0) goto done; /* 5. If no groups are found, continue with step 10. */ if (glen == 0) @@ -223,14 +223,14 @@ nacm_rpc(char *rpc, configuration. If a rule-list's "group" leaf-list does not match any of the user's groups, proceed to the next rule-list entry. */ - if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0) + if (xpath_vec_nsc(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0) goto done; for (i=0; i= 3.10 */ if ((nsc = xml_nsctx_init(NULL, CLIXON_CONF_NS)) == NULL) goto done; - if ((xc = xpath_first(xt, nsc, "clixon-config")) == NULL){ - clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: ", filename, CLIXON_CONF_NS); + if ((xc = xpath_first_nsc(xt, nsc, "clixon-config")) == NULL){ + clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: ", filename, CLIXON_CONF_NS); goto done; } @@ -362,7 +362,6 @@ clicon_options_main(clicon_handle h, goto done; if (xml_spec(xconfig) == NULL){ clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: or clixon-config.yang not found?", configfile, CLIXON_CONF_NS); - goto done; } /* Set clixon_conf pointer to handle */ diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index f0de1800..3674654e 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -262,7 +262,7 @@ clicon_rpc_generate_error(char *prefix, * cxobj *xt = NULL; * if (clicon_rpc_get_config(h, "running", "/hello/world", "urn:example:hello", &xt) < 0) * err; - * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ + * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ * clicon_rpc_generate_error("", xerr); * err; * } @@ -306,9 +306,9 @@ clicon_rpc_get_config(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; /* Send xml error back: first check error, then ok */ - if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL) + if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL) xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL) + else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL) if ((xd = xml_new("data", NULL, NULL)) == NULL) goto done; if (xt){ @@ -369,7 +369,7 @@ clicon_rpc_edit_config(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Editing configuration", xerr); goto done; } @@ -415,7 +415,7 @@ clicon_rpc_copy_config(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Copying configuration", xerr); goto done; } @@ -454,7 +454,7 @@ clicon_rpc_delete_config(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Deleting configuration", xerr); goto done; } @@ -489,7 +489,7 @@ clicon_rpc_lock(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Locking configuration", xerr); goto done; } @@ -523,7 +523,7 @@ clicon_rpc_unlock(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Configuration unlock", xerr); goto done; } @@ -550,7 +550,7 @@ clicon_rpc_unlock(clicon_handle h, * cxobj *xt = NULL; * if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", &xt) < 0) * err; - * if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){ + * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ * clicon_rpc_generate_error(xerr); * err; * } @@ -593,9 +593,9 @@ clicon_rpc_get(clicon_handle h, if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; /* Send xml error back: first check error, then ok */ - if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL) + if ((xd = xpath_first(xret, "/rpc-reply/rpc-error")) != NULL) xd = xml_parent(xd); /* point to rpc-reply */ - else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL) + else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL) if ((xd = xml_new("data", NULL, NULL)) == NULL) goto done; if (xt){ @@ -634,7 +634,7 @@ clicon_rpc_close_session(clicon_handle h) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Close session", xerr); goto done; } @@ -669,7 +669,7 @@ clicon_rpc_kill_session(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Kill session", xerr); goto done; } @@ -703,7 +703,7 @@ clicon_rpc_validate(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error(CLIXON_ERRSTR_VALIDATE_FAILED, xerr); goto done; } @@ -735,7 +735,7 @@ clicon_rpc_commit(clicon_handle h) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error(CLIXON_ERRSTR_COMMIT_FAILED, xerr); goto done; } @@ -767,7 +767,7 @@ clicon_rpc_discard_changes(clicon_handle h) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Discard changes", xerr); goto done; } @@ -812,7 +812,7 @@ clicon_rpc_create_subscription(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, s0) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Create subscription", xerr); goto done; } @@ -847,11 +847,11 @@ clicon_rpc_debug(clicon_handle h, goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){ + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ clicon_rpc_generate_error("Debug",xerr); goto done; } - if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){ + if (xpath_first(xret, "//rpc-reply/ok") == NULL){ clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */ goto done; } diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c index 6fb81be6..7cc3b119 100644 --- a/lib/src/clixon_stream.c +++ b/lib/src/clixon_stream.c @@ -514,7 +514,7 @@ stream_notify1(clicon_handle h, else{ /* xpath match */ if (ss->ss_xpath == NULL || strlen(ss->ss_xpath)==0 || - xpath_first(xevent, NULL, "%s", ss->ss_xpath) != NULL) + xpath_first(xevent, "%s", ss->ss_xpath) != NULL) if ((*ss->ss_fn)(h, 0, xevent, ss->ss_arg) < 0) goto done; ss = NEXTQ(struct stream_subscription *, ss); diff --git a/lib/src/clixon_xml_changelog.c b/lib/src/clixon_xml_changelog.c index 9216f7cb..f74ab713 100644 --- a/lib/src/clixon_xml_changelog.c +++ b/lib/src/clixon_xml_changelog.c @@ -200,7 +200,7 @@ changelog_move(clicon_handle h, int retval = -1; cxobj *xp; /* destination parent node */ - if ((xp = xpath_first(xt, nsc, "%s", dst)) == NULL){ + if ((xp = xpath_first_nsc(xt, nsc, "%s", dst)) == NULL){ clicon_err(OE_XML, 0, "path required"); goto done; } @@ -253,7 +253,7 @@ changelog_op(clicon_handle h, if ((wxpath = xml_find_body(xi, "where")) == NULL) goto ok; /* Get vector of target nodes meeting the where requirement */ - if (xpath_vec(xt, nsc, "%s", &wvec, &wlen, wxpath) < 0) + if (xpath_vec_nsc(xt, nsc, "%s", &wvec, &wlen, wxpath) < 0) goto done; for (i=0; ixc_type == XT_NODESET && xr->xc_size) + cx = xr->xc_nodeset[0]; + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return cx; +} + /*! Given XML tree and xpath, returns nodeset as xml node vector * If result is not nodeset, return empty nodeset - * @param[in] xcur xml-tree where to search - * @param[in] format stdarg string with XPATH 1.0 syntax - * @param[in] nsc XML Namespace context for XPATH - * @param[out] vec vector of xml-trees. Vector must be free():d after use - * @param[out] veclen returns length of vector in return value - * @retval 0 OK - * @retval -1 Error + * @param[in] xcur xml-tree where to search + * @param[in] nsc External XML namespace context, or NULL + * @param[in] xpformat Format string for XPATH syntax + * @param[out] vec vector of xml-trees. Vector must be free():d after use + * @param[out] veclen returns length of vector in return value + * @retval 0 OK + * @retval -1 Error * @code * cvec *nsc; // namespace context * cxobj **vec; * size_t veclen; - * if (xpath_vec(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0) + * if (xpath_vec_nsc(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0) * goto err; * for (i=0; ixc_type == XT_NODESET){ + *vec = xr->xc_nodeset; + xr->xc_nodeset = NULL; + *veclen = xr->xc_size; + } + retval = 0; + done: + if (xr) + ctx_free(xr); + if (xpath) + free(xpath); + return retval; +} + /* Xpath that returns a vector of matches (only nodes marked with flags) - * @param[in] xcur xml-tree where to search - * @param[in] xpath string with XPATH syntax - * @param[in] nsc External XML namespace context, or NULL - * @param[in] flags Set of flags that return nodes must match (0 if all) - * @param[out] vec vector of xml-trees. Vector must be free():d after use - * @param[out] veclen returns length of vector in return value - * @retval 0 OK - * @retval -1 error. + * @param[in] xcur xml-tree where to search + * @param[in] xpformat Format string for XPATH syntax + * @param[in] nsc External XML namespace context, or NULL + * @param[in] flags Set of flags that return nodes must match (0 if all) + * @param[out] vec vector of xml-trees. Vector must be free():d after use + * @param[out] veclen returns length of vector in return value + * @retval 0 OK + * @retval -1 error. * @code * cxobj **vec; * size_t veclen; @@ -434,14 +556,13 @@ xpath_vec(cxobj *xcur, * } * free(vec); * @endcode - * @Note that although the returned vector must be freed after use, the returned xml - * trees need not be. - * @see also xpath_vec This is a specialized version. + * @Note that although the returned vector must be freed after use, the returned xml trees need not be. + * @see also xpath_vec This is a specialized version. */ int xpath_vec_flag(cxobj *xcur, cvec *nsc, - char *format, + char *xpformat, uint16_t flags, cxobj ***vec, size_t *veclen, @@ -456,7 +577,7 @@ xpath_vec_flag(cxobj *xcur, cxobj *x; va_start(ap, veclen); - len = vsnprintf(NULL, 0, format, ap); + len = vsnprintf(NULL, 0, xpformat, ap); va_end(ap); /* allocate a message string exactly fitting the message length */ if ((xpath = malloc(len+1)) == NULL){ @@ -465,7 +586,7 @@ xpath_vec_flag(cxobj *xcur, } /* second round: compute write message from reason and args */ va_start(ap, veclen); - if (vsnprintf(xpath, len+1, format, ap) < 0){ + if (vsnprintf(xpath, len+1, xpformat, ap) < 0){ clicon_err(OE_UNIX, errno, "vsnprintf"); va_end(ap); goto done; @@ -494,17 +615,17 @@ xpath_vec_flag(cxobj *xcur, /*! Given XML tree and xpath, returns boolean * Returns true if the nodeset is non-empty - * @param[in] xcur xml-tree where to search - * @param[in] nsc External XML namespace context, or NULL - * @param[in] xpath stdarg string with XPATH 1.0 syntax - * @retval 1 True - * @retval 0 False - * @retval -1 Error + * @param[in] xcur xml-tree where to search + * @param[in] nsc External XML namespace context, or NULL + * @param[in] xpformat Format string for XPATH syntax + * @retval 1 True + * @retval 0 False + * @retval -1 Error */ int xpath_vec_bool(cxobj *xcur, cvec *nsc, - char *format, + char *xpformat, ...) { int retval = -1; @@ -513,8 +634,8 @@ xpath_vec_bool(cxobj *xcur, char *xpath = NULL; xp_ctx *xr = NULL; - va_start(ap, format); - len = vsnprintf(NULL, 0, format, ap); + va_start(ap, xpformat); + len = vsnprintf(NULL, 0, xpformat, ap); va_end(ap); /* allocate a message string exactly fitting the message length */ if ((xpath = malloc(len+1)) == NULL){ @@ -522,8 +643,8 @@ xpath_vec_bool(cxobj *xcur, goto done; } /* second round: compute write message from reason and args */ - va_start(ap, format); - if (vsnprintf(xpath, len+1, format, ap) < 0){ + va_start(ap, xpformat); + if (vsnprintf(xpath, len+1, xpformat, ap) < 0){ clicon_err(OE_UNIX, errno, "vsnprintf"); va_end(ap); goto done; diff --git a/lib/src/clixon_yang_module.c b/lib/src/clixon_yang_module.c index 953cb5f1..a5389275 100644 --- a/lib/src/clixon_yang_module.c +++ b/lib/src/clixon_yang_module.c @@ -305,7 +305,7 @@ yang_modules_state_get(clicon_handle h, /* xc is also original tree, need to copy it */ if ((xw = xml_wrap(xc, "top")) == NULL) goto done; - if (xpath_first(xw, NULL, "%s", xpath)){ + if (xpath_first(xw, "%s", xpath)){ if ((x = xml_dup(xc)) == NULL) /* Make copy and use below */ goto done; } @@ -337,7 +337,7 @@ yang_modules_state_get(clicon_handle h, if ((x = xml_wrap(x, "top")) < 0) goto done; /* extract xpath part of module-state tree */ - if (xpath_vec(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + if (xpath_vec_nsc(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; if (xvec != NULL){ for (i=0; i