The Clixon API has been extended with namespaces, or namespace contexts in the following cases:

* CLIspec functions have added namespace parameter:
    * `cli_show_config <db> <format> <xpath>` --> `cli_show_config <db> <format> <xpath> <namespace>`
    * `cli_copy_config <db> <xpath> ...` --> `cli_copy_config <db> <xpath> <namespace> ...`
  * 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, ...)`
    * `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:
    * `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);`
  * rpc get and get-config api function has an added namespace argument:
    * `clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *namespace, cxobj **xt);`
    * `int clicon_rpc_get(clicon_handle h, char *xpath, char *namespace, cxobj **xt);`
This commit is contained in:
Olof hagsand 2019-07-08 10:36:37 +02:00
parent 73d8e97a01
commit 67b8685bab
78 changed files with 1507 additions and 538 deletions

View file

@ -70,7 +70,7 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_string.c clixon_regex.c clixon_handle.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
clixon_yang_cardinality.c clixon_xml_changelog.c \
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \

View file

@ -75,6 +75,7 @@
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
#include "clixon_xml_map.h"
#include "clixon_xml_nsctx.h"
#include "clixon_datastore.h"
#include "clixon_datastore_read.h"
@ -257,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, "module[name=\"%s\"]", name)) == NULL){
if ((xs = xpath_first(xmcache, NULL, "module[name=\"%s\"]", name)) == NULL){
// fprintf(stderr, "%s: Module %s: not in system\n", __FUNCTION__, name);
if ((xm2 = xml_dup(xm)) == NULL)
goto done;
@ -385,6 +386,7 @@ xmldb_readfile(clicon_handle h,
* This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences
@ -395,6 +397,7 @@ xmldb_readfile(clicon_handle h,
static int
xmldb_get_nocache(clicon_handle h,
const char *db,
cvec *nsc,
char *xpath,
cxobj **xtop,
modstate_diff_t *msd)
@ -417,7 +420,7 @@ xmldb_get_nocache(clicon_handle h,
goto done;
/* Here xt looks like: <config>...</config> */
/* Given the xpath, return a vector of matches in xvec */
if (xpath_vec(xt, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found with all ancestors
@ -467,6 +470,7 @@ xmldb_get_nocache(clicon_handle h,
* This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences
@ -475,11 +479,12 @@ xmldb_get_nocache(clicon_handle h,
* @see xmldb_get the generic API function
*/
static int
xmldb_get_cache(clicon_handle h,
const char *db,
char *xpath,
cxobj **xtop,
modstate_diff_t *msd)
xmldb_get_cache(clicon_handle h,
const char *db,
cvec *nsc,
char *xpath,
cxobj **xtop,
modstate_diff_t *msd)
{
int retval = -1;
yang_stmt *yspec;
@ -521,7 +526,7 @@ xmldb_get_cache(clicon_handle h,
*/
/* Here xt looks like: <config>...</config> */
if (xpath_vec(x0t, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(x0t, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* Make new tree by copying top-of-tree from x0t to x1t */
@ -565,6 +570,7 @@ xmldb_get_cache(clicon_handle h,
* This is a clixon datastore plugin of the the xmldb api
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free()
@ -573,11 +579,12 @@ xmldb_get_cache(clicon_handle h,
* @retval -1 Error
*/
static int
xmldb_get_zerocopy(clicon_handle h,
const char *db,
char *xpath,
cxobj **xtop,
modstate_diff_t *msd)
xmldb_get_zerocopy(clicon_handle h,
const char *db,
cvec *nsc,
char *xpath,
cxobj **xtop,
modstate_diff_t *msd)
{
int retval = -1;
yang_stmt *yspec;
@ -612,7 +619,7 @@ xmldb_get_zerocopy(clicon_handle h,
else
x0t = de->de_xml;
/* Here xt looks like: <config>...</config> */
if (xpath_vec(x0t, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(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
@ -656,7 +663,7 @@ xmldb_get(clicon_handle h,
char *xpath,
cxobj **xret)
{
return xmldb_get0(h, db, xpath, 1, xret, NULL);
return xmldb_get0(h, db, NULL, xpath, 1, xret, NULL);
}
/*! Zero-copy variant of get content of database
@ -669,6 +676,7 @@ xmldb_get(clicon_handle h,
* freeing tree must be made after use.
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] nsc External XML namespace context, or NULL
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] copy Force copy. Overrides cache_zerocopy -> cache
* @param[out] xret Single return XML tree. Free with xml_free()
@ -677,17 +685,19 @@ xmldb_get(clicon_handle h,
* @retval -1 Error
* @code
* cxobj *xt;
* if (xmldb_get0(xh, "running", "/interface[name="eth"]", 0, &xt, NULL) < 0)
* if (xmldb_get0(xh, "running", nsc, "/interface[name="eth"]", 0, &xt, NULL) < 0)
* err;
* ...
* xmldb_get0_clear(h, xt); # Clear tree from default values and flags
* xmldb_get0_free(h, &xt); # Free tree
* @endcode
* @see xml_nsctx_node to get a XML namespace context from XML tree
* @see xmldb_get for a copy version (old-style)
*/
int
xmldb_get0(clicon_handle h,
const char *db,
cvec *nsc,
char *xpath,
int copy,
cxobj **xret,
@ -701,7 +711,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy
* Copy deleted by xmldb_free
*/
retval = xmldb_get_nocache(h, db, xpath, xret, msd);
retval = xmldb_get_nocache(h, db, nsc, xpath, xret, msd);
break;
case DATASTORE_CACHE_ZEROCOPY:
/* Get cache (file if empty) mark xpath match in original tree
@ -709,7 +719,7 @@ xmldb_get0(clicon_handle h,
* Default values and markings removed in xmldb_clear
*/
if (!copy){
retval = xmldb_get_zerocopy(h, db, xpath, xret, msd);
retval = xmldb_get_zerocopy(h, db, nsc, xpath, xret, msd);
break;
}
/* fall through */
@ -718,7 +728,7 @@ xmldb_get0(clicon_handle h,
* Add default values in copy, return copy
* Copy deleted by xmldb_free
*/
retval = xmldb_get_cache(h, db, xpath, xret, msd);
retval = xmldb_get_cache(h, db, nsc, xpath, xret, msd);
break;
}
return retval;

View file

@ -75,6 +75,7 @@
#include "clixon_nacm.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h"
#include "clixon_datastore.h"
@ -614,6 +615,7 @@ xmldb_put(clicon_handle h,
cxobj *x;
int permit = 0; /* nacm permit all */
char *format;
cvec *nsc = NULL; /* nacm namespace context */
if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is NULL");
@ -656,8 +658,11 @@ xmldb_put(clicon_handle h,
else if (strcmp(mode, "internal")==0)
xnacm0 = x0;
}
/* Create namespace context for with nacm namespace as default */
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, "nacm")) != NULL){
(xnacm = xpath_first(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)
@ -744,6 +749,8 @@ xmldb_put(clicon_handle h,
done:
if (f != NULL)
fclose(f);
if (nsc)
xml_nsctx_free(nsc);
if (dbfile)
free(dbfile);
if (cb)

View file

@ -68,8 +68,12 @@
#include "clixon_xpath.h"
#include "clixon_yang_module.h"
#include "clixon_datastore.h"
#include "clixon_xml_nsctx.h"
#include "clixon_nacm.h"
/* NACM namespace for use with xml namespace contexts and xpath */
#define NACM_NS "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"
/*! Match nacm access operations according to RFC8341 3.4.4.
* Incoming RPC Message Validation Step 7 (c)
* The rule's "access-operations" leaf has the "exec" bit set or
@ -191,7 +195,11 @@ nacm_rpc(char *rpc,
char *gname;
char *action;
int match= 0;
cvec *nsc = NULL;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
/* 3. If the requested operation is the NETCONF <close-session>
protocol operation, then the protocol operation is permitted.
*/
@ -204,8 +212,9 @@ nacm_rpc(char *rpc,
transport layer.) */
if (username == NULL)
goto step10;
/* User's group */
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
if (xpath_vec(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)
@ -214,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, "rule-list", &rlistvec, &rlistlen) < 0)
if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0)
goto done;
for (i=0; i<rlistlen; i++){
rlist = rlistvec[i];
/* Loop through user's group to find match in this rule-list */
for (j=0; j<glen; j++){
gname = xml_find_body(gvec[j], "name");
if (xpath_first(rlist, ".[group='%s']", gname)!=NULL)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
@ -230,7 +239,7 @@ nacm_rpc(char *rpc,
until a rule that matches the requested access operation is
found.
*/
if (xpath_vec(rlist, "rule", &rvec, &rlen) < 0)
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
for (j=0; j<rlen; j++){
xrule = rvec[j];
@ -283,6 +292,8 @@ nacm_rpc(char *rpc,
retval = 1;
done:
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
if (nsc)
xml_nsctx_free(nsc);
if (gvec)
free(gvec);
if (rlistvec)
@ -321,7 +332,11 @@ nacm_rule_datanode(cxobj *xt,
char *module;
cxobj *xpath; /* xpath match */
cxobj *xp; /* parent */
cvec *nsc = NULL;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
* the YANG module where the requested data node is defined. */
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
@ -375,7 +390,7 @@ nacm_rule_datanode(cxobj *xt,
}
/* Here module is matched, now check for path if any NYI */
if (path){
if ((xpath = xpath_first(xt, "%s", path)) == NULL)
if ((xpath = xpath_first(xt, nsc, "%s", path)) == NULL)
goto nomatch;
/* The requested node xr is the node specified by the path or is a
* descendant node of the path:
@ -390,6 +405,8 @@ nacm_rule_datanode(cxobj *xt,
match:
retval = 1;
done:
if (nsc)
xml_nsctx_free(nsc);
return retval;
nomatch:
retval = 0;
@ -403,7 +420,8 @@ nacm_rule_datanode(cxobj *xt,
* @param[in] glen Length of gvec
* @param[in] rlistvec NACM rule-list entries
* @param[in] rlistlen Length of rlistvec
* @param[out] xrulep If set, then points to matching rule
* @param[in] nsc NACM namespace context for xpaths
* @param[out] xrulep If set, then points to matching rule
*/
static int
nacm_data_read_xr(cxobj *xt,
@ -412,6 +430,7 @@ nacm_data_read_xr(cxobj *xt,
size_t glen,
cxobj **rlistvec,
size_t rlistlen,
cvec *nsc,
cxobj **xrulep)
{
int retval = -1;
@ -428,7 +447,7 @@ nacm_data_read_xr(cxobj *xt,
/* Loop through user's group to find match in this rule-list */
for (j=0; j<glen; j++){
gname = xml_find_body(gvec[j], "name");
if (xpath_first(rlist, ".[group='%s']", gname)!=NULL)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
@ -437,7 +456,7 @@ nacm_data_read_xr(cxobj *xt,
until a rule that matches the requested access operation is
found. (see 6 sub rules in nacm_rule_datanode
*/
if (xpath_vec(rlist, "rule", &rvec, &rlen) < 0)
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
for (j=0; j<rlen; j++){ /* Loop through rules */
xrule = rvec[j];
@ -556,7 +575,11 @@ nacm_datanode_read(cxobj *xt,
char *read_default = NULL;
cxobj *xrule;
char *action;
cvec *nsc = NULL;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
/* 3. Check all the "group" entries to see if any of them contain a
"user-name" entry that equals the username for the session
making the request. (If the "enable-external-groups" leaf is
@ -565,7 +588,7 @@ nacm_datanode_read(cxobj *xt,
if (username == NULL)
goto step9;
/* User's group */
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
goto done;
/* 4. If no groups are found (glen=0), continue and check read-default
in step 11. */
@ -573,7 +596,7 @@ nacm_datanode_read(cxobj *xt,
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, "rule-list", &rlistvec, &rlistlen) < 0)
if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0)
goto done;
/* read-default has default permit so should never be NULL */
if ((read_default = xml_find_body(xnacm, "read-default")) == NULL){
@ -587,7 +610,7 @@ nacm_datanode_read(cxobj *xt,
xrule = NULL;
/* Skip if no groups */
if (glen && nacm_data_read_xr(xt, xr, gvec, glen, rlistvec, rlistlen,
&xrule) < 0)
nsc, &xrule) < 0)
goto done;
if (xrule){ /* xrule match requested node xr */
if ((action = xml_find_body(xrule, "action")) == NULL)
@ -628,6 +651,8 @@ nacm_datanode_read(cxobj *xt,
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (nsc)
xml_nsctx_free(nsc);
if (gvec)
free(gvec);
if (rlistvec)
@ -673,7 +698,11 @@ nacm_datanode_write(cxobj *xt,
int match = 0;
char *action;
char *write_default = NULL;
cvec *nsc = NULL;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
if (xnacm == NULL)
goto permit;
/* 3. Check all the "group" entries to see if any of them contain a
@ -684,7 +713,7 @@ nacm_datanode_write(cxobj *xt,
if (username == NULL)
goto step9;
/* User's group */
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
if (xpath_vec(xnacm, nsc, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
goto done;
/* 4. If no groups are found, continue with step 9. */
if (glen == 0)
@ -693,19 +722,19 @@ nacm_datanode_write(cxobj *xt,
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, "rule-list", &rlistvec, &rlistlen) < 0)
if (xpath_vec(xnacm, nsc, "rule-list", &rlistvec, &rlistlen) < 0)
goto done;
for (i=0; i<rlistlen; i++){
rlist = rlistvec[i];
/* Loop through user's group to find match in this rule-list */
for (j=0; j<glen; j++){
gname = xml_find_body(gvec[j], "name");
if (xpath_first(rlist, ".[group='%s']", gname)!=NULL)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
continue;
if (xpath_vec(rlist, "rule", &rvec, &rlen) < 0)
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
/* 6. For each rule-list entry found, process all rules, in order,
until a rule that matches the requested access operation is
@ -765,6 +794,8 @@ nacm_datanode_write(cxobj *xt,
retval = 1;
done:
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
if (nsc)
xml_nsctx_free(nsc);
if (gvec)
free(gvec);
if (rlistvec)
@ -811,8 +842,11 @@ nacm_access(char *mode,
cxobj *xnacm0 = NULL;
char *enabled;
cxobj *x;
cvec *nsc = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
if (mode == NULL || strcmp(mode, "disabled") == 0)
goto permit;
/* 0. If nacm-mode is external, get NACM defintion from separet tree,
@ -828,7 +862,7 @@ nacm_access(char *mode,
* RFC8341 3.4 */
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
operation is permitted. */
if ((x = xpath_first(xnacm, "enable-nacm")) == NULL)
if ((x = xpath_first(xnacm, nsc, "enable-nacm")) == NULL)
goto permit;
enabled = xml_body(x);
if (strcmp(enabled, "true") != 0)
@ -840,6 +874,8 @@ nacm_access(char *mode,
retval = 0; /* not permitted yet. continue with next NACM step */
done:
if (nsc)
xml_nsctx_free(nsc);
if (retval != 0 && xnacm0)
xml_free(xnacm0);
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
@ -882,7 +918,10 @@ nacm_access_pre(clicon_handle h,
cxobj *x;
cxobj *xnacm0 = NULL;
cxobj *xnacm = NULL;
cvec *nsc = NULL;
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
if ((mode = clicon_option_str(h, "CLICON_NACM_MODE")) != NULL){
if (strcmp(mode, "external")==0){
if ((x = clicon_nacm_ext(h)))
@ -890,7 +929,7 @@ nacm_access_pre(clicon_handle h,
goto done;
}
else if (strcmp(mode, "internal")==0){
if (xmldb_get(h, "running", "nacm", &xnacm0) < 0)
if (xmldb_get0(h, "running", nsc, "nacm", 1, &xnacm0, NULL) < 0)
goto done;
}
}
@ -898,7 +937,7 @@ nacm_access_pre(clicon_handle h,
if (xnacm0 == NULL)
goto permit;
/* If config does not exist then the operation is permitted(?) */
if ((xnacm = xpath_first(xnacm0, "nacm")) == NULL)
if ((xnacm = xpath_first(xnacm0, nsc, "nacm")) == NULL)
goto permit;
if (xml_rootchild_node(xnacm0, xnacm) < 0)
goto done;
@ -910,6 +949,8 @@ nacm_access_pre(clicon_handle h,
xnacm = NULL;
}
done:
if (nsc)
xml_nsctx_free(nsc);
if (xnacm0)
xml_free(xnacm0);
else if (xnacm)

View file

@ -1274,13 +1274,13 @@ netconf_err2cb(cxobj *xerr,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((x=xpath_first(xerr, "error-type"))!=NULL)
if ((x=xpath_first(xerr, NULL, "error-type"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
if ((x=xpath_first(xerr, NULL, "error-tag"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-message"))!=NULL)
if ((x=xpath_first(xerr, NULL, "error-message"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-info"))!=NULL)
if ((x=xpath_first(xerr, NULL, "error-info"))!=NULL)
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
*cberr = cb;
retval = 0;

View file

@ -73,8 +73,15 @@
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h"
/*! Clixon configuration namespace
* Probably should be defined somewhere else or extracted from yang
* @see clixon-config.yang
*/
#define CLIXON_CONF_NS "http://clicon.org/config"
/* Mapping between Clicon startup modes string <--> constants,
see clixon-config.yang type startup_mode */
static const map_str2int startup_mode_map[] = {
@ -165,6 +172,7 @@ parse_configfile(clicon_handle h,
cbuf *cbret = NULL;
cxobj *xret = NULL;
int ret;
cvec *nsc = NULL;
if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified");
@ -191,25 +199,12 @@ parse_configfile(clicon_handle h,
goto done;
}
/* Hard-coded config for < 3.10 and clixon-config for >= 3.10 */
if ((xc = xpath_first(xt, "clixon-config")) == NULL){
/* Backward compatible code to accept "config" as top-level symbol.
This cannot be controlled by config option due to bootstrap */
#if 0
if ((xc = xpath_first(xt, "config")) != NULL){
if (xml_name_set(xc, "clixon-config") < 0)
goto done;
if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply0(xc, CX_ELMNT, xml_sort, h) < 0)
goto done;
}
else
#endif
{
clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: <clixon-config xmlns=\"http://clicon.org/config\" (See Changelog in Clixon 3.10)>", filename);
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: <clixon-config xmlns=\"%s\" (See Changelog in Clixon 3.10)>", filename, CLIXON_CONF_NS);
goto done;
}
goto done;
}
if (xml_apply0(xc, CX_ELMNT, xml_default, h) < 0)
goto done;
@ -249,6 +244,8 @@ parse_configfile(clicon_handle h,
*xconfig = xt;
xt = NULL;
done:
if (nsc)
xml_nsctx_free(nsc);
if (cbret)
cbuf_free(cbret);
if (xret)
@ -364,7 +361,7 @@ clicon_options_main(clicon_handle h,
if (xml_rootchild(xconfig, 0, &xconfig) < 0)
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: <clixon-config xmlns=\"http://clicon.org/config\"> or clixon-config.yang not found?", configfile);
clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: <clixon-config xmlns=\"%s\"> or clixon-config.yang not found?", configfile, CLIXON_CONF_NS);
goto done;
}

View file

@ -253,15 +253,16 @@ clicon_rpc_generate_error(char *prefix,
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] xpath XPath (or "")
* @param[in] namespace Namespace associated w xpath
* @param[out] xt XML tree. Free with xml_free.
* Either <config> or <rpc-error>.
* @retval 0 OK
* @retval -1 Error, fatal or xml
* @code
* cxobj *xt = NULL;
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
* if (clicon_rpc_get_config(h, "running", "/hello/world", "urn:example:hello", &xt) < 0)
* err;
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
* if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
* clicon_rpc_generate_error("", xerr);
* err;
* }
@ -274,6 +275,7 @@ int
clicon_rpc_get_config(clicon_handle h,
char *db,
char *xpath,
char *namespace,
cxobj **xt)
{
int retval = -1;
@ -288,18 +290,25 @@ clicon_rpc_get_config(clicon_handle h,
cprintf(cb, "<rpc");
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
if (namespace)
cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, "><get-config><source><%s/></source>", db);
if (xpath && strlen(xpath))
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
if (xpath && strlen(xpath)){
if (namespace)
cprintf(cb, "<nc:filter nc:type=\"xpath\" nc:select=\"%s\" xmlns=\"%s\"/>",
xpath, namespace);
else /* XXX shouldnt happen */
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
}
cprintf(cb, "</get-config></rpc>");
if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL)
goto done;
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, "/rpc-reply/rpc-error")) != NULL)
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
xd = xml_parent(xd); /* point to rpc-reply */
else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL)
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL)
if ((xd = xml_new("data", NULL, NULL)) == NULL)
goto done;
if (xt){
@ -347,7 +356,7 @@ clicon_rpc_edit_config(clicon_handle h,
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc %s", DEFAULT_XMLNS);
cprintf(cb, "<rpc xmlns=\"%s\"", NETCONF_BASE_NAMESPACE);
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
cprintf(cb, "><edit-config><target><%s/></target>", db);
@ -360,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Editing configuration", xerr);
goto done;
}
@ -406,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Copying configuration", xerr);
goto done;
}
@ -445,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Deleting configuration", xerr);
goto done;
}
@ -480,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Locking configuration", xerr);
goto done;
}
@ -514,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Configuration unlock", xerr);
goto done;
}
@ -528,17 +537,20 @@ clicon_rpc_unlock(clicon_handle h,
}
/*! Get database configuration and state data
* @param[in] h CLICON handle
* @param[in] xpath XPath (or "")
* @param[out] xt XML tree. Free with xml_free.
* Either <config> or <rpc-error>.
* @retval 0 OK
* @retval -1 Error, fatal or xml
* @param[in] h Clicon handle
* @param[in] xpath XPath in a filter stmt (or NULL/"" for no filter)
* @param[in] namespace Namespace associated w xpath
* @param[out] xt XML tree. Free with xml_free.
* Either <config> or <rpc-error>.
* @retval 0 OK
* @retval -1 Error, fatal or xml
* @note if xpath is set but namespace is NULL, the default, netconf base
* namespace will be used which is most probably wrong.
* @code
* cxobj *xt = NULL;
* if (clicon_rpc_get(h, "/", &xt) < 0)
* if (clicon_rpc_get(h, "/hello/world", "urn:example:hello", &xt) < 0)
* err;
* if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
* if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
* clicon_rpc_generate_error(xerr);
* err;
* }
@ -550,6 +562,7 @@ clicon_rpc_unlock(clicon_handle h,
int
clicon_rpc_get(clicon_handle h,
char *xpath,
char *namespace,
cxobj **xt)
{
int retval = -1;
@ -564,18 +577,25 @@ clicon_rpc_get(clicon_handle h,
cprintf(cb, "<rpc");
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
if (namespace)
cprintf(cb, " xmlns:nc=\"%s\"", NETCONF_BASE_NAMESPACE);
cprintf(cb, "><get>");
if (xpath && strlen(xpath))
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
if (xpath && strlen(xpath)) {
if (namespace)
cprintf(cb, "<nc:filter nc:type=\"xpath\" nc:select=\"%s\" xmlns=\"%s\"/>",
xpath, namespace);
else /* XXX shouldnt happen */
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
}
cprintf(cb, "</get></rpc>");
if ((msg = clicon_msg_encode("%s", cbuf_get(cb))) == NULL)
goto done;
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, "/rpc-reply/rpc-error")) != NULL)
if ((xd = xpath_first(xret, NULL, "/rpc-reply/rpc-error")) != NULL)
xd = xml_parent(xd); /* point to rpc-reply */
else if ((xd = xpath_first(xret, "/rpc-reply/data")) == NULL)
else if ((xd = xpath_first(xret, NULL, "/rpc-reply/data")) == NULL)
if ((xd = xml_new("data", NULL, NULL)) == NULL)
goto done;
if (xt){
@ -614,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Close session", xerr);
goto done;
}
@ -649,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Kill session", xerr);
goto done;
}
@ -683,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error(CLIXON_ERRSTR_VALIDATE_FAILED, xerr);
goto done;
}
@ -715,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error(CLIXON_ERRSTR_COMMIT_FAILED, xerr);
goto done;
}
@ -747,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Discard changes", xerr);
goto done;
}
@ -792,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Create subscription", xerr);
goto done;
}
@ -827,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, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Debug",xerr);
goto done;
}
if (xpath_first(xret, "//rpc-reply/ok") == NULL){
if (xpath_first(xret, NULL, "//rpc-reply/ok") == NULL){
clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */
goto done;
}

View file

@ -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, "%s", ss->ss_xpath) != NULL)
xpath_first(xevent, NULL, "%s", ss->ss_xpath) != NULL)
if ((*ss->ss_fn)(h, 0, xevent, ss->ss_arg) < 0)
goto done;
ss = NEXTQ(struct stream_subscription *, ss);

View file

@ -66,6 +66,7 @@
#include "clixon_xml_map.h" /* xml_spec_populate */
#include "clixon_xml_sort.h"
#include "clixon_xml_parse.h"
#include "clixon_xml_nsctx.h"
/*
* Constants
@ -130,7 +131,7 @@ struct xml{
reference, dont free */
cg_var *x_cv; /* Cached value as cligen variable
(eg xml_cmp) */
char *x_ns_cache; /* Cached namespace */
cvec *x_ns_cache; /* Cached vector of namespaces */
int _x_vector_i; /* internal use: xml_child_each */
int _x_i; /* internal use for sorting:
see xml_enumerate and xml_cmp */
@ -229,6 +230,47 @@ xml_prefix_set(cxobj *xn,
return 0;
}
/*! Get cached namespace
* @param[in] x XML node
* @param[in] prefix Namespace prefix, or NULL for default
* @retval ns Cached namespace
* @retval NULL No namespace found (not cached or not found)
* @note may want to distinguish between not set cache and no namespace?
*/
static char*
nscache_get(cxobj *x,
char *prefix)
{
if (x->x_ns_cache != NULL)
return xml_nsctx_get(x->x_ns_cache, prefix);
return NULL;
}
/*! Set cached namespace. Replace if necessary
* @param[in] x XML node
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] namespace Cached namespace to set (assume non-null?)
* @retval 0 OK
* @retval -1 Error
*/
static int
nscache_set(cxobj *x,
char *prefix,
char *namespace)
{
int retval = -1;
if (x->x_ns_cache == NULL){
if ((x->x_ns_cache = xml_nsctx_init(prefix, namespace)) == NULL)
goto done;
}
else
return xml_nsctx_set(x->x_ns_cache, prefix, namespace);
retval = 0;
done:
return retval;
}
/*! Given an xml tree return URI namespace recursively : default or localname given
*
* Given an XML tree and a prefix (or NULL) return URI namespace.
@ -238,7 +280,7 @@ xml_prefix_set(cxobj *xn,
* @retval 0 OK
* @retval -1 Error
* @see xmlns_check XXX can these be merged?
* @see xml2ns_set cache is set
* @see xmlns_set cache is set
* @note, this function uses a cache.
*/
int
@ -250,13 +292,13 @@ xml2ns(cxobj *x,
char *ns = NULL;
cxobj *xp;
if ((ns = x->x_ns_cache) != NULL)
if ((ns = nscache_get(x, prefix)) != NULL)
goto ok;
if (prefix != NULL) /* xmlns:<prefix>="<uri>" */
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
else /* xmlns="<uri>" */
else{ /* xmlns="<uri>" */
ns = xml_find_type_value(x, NULL, "xmlns", CX_ATTR);
}
/* namespace not found, try parent */
if (ns == NULL){
if ((xp = xml_parent(x)) != NULL){
@ -264,15 +306,15 @@ xml2ns(cxobj *x,
goto done;
}
/* If no parent, return default namespace if defined */
#if defined(DEFAULT_XML_RPC_NAMESPACE)
#ifdef USE_NETCONF_NS_AS_DEFAULT
else
ns = DEFAULT_XML_RPC_NAMESPACE;
ns = NETCONF_BASE_NAMESPACE;
#endif
}
if (ns && (x->x_ns_cache = strdup(ns)) == NULL){
clicon_err(OE_XML, errno, "strdup");
/* Set default namespace cache (since code is at this point,
* no cache was found */
if (ns && nscache_set(x, prefix, ns) < 0)
goto done;
}
ok:
if (namespace)
*namespace = ns;
@ -311,12 +353,8 @@ xmlns_set(cxobj *x,
if (xml_value_set(xa, ns) < 0)
goto done;
/* (re)set namespace cache (as used in xml2ns) */
if (x->x_ns_cache)
free(x->x_ns_cache);
if ((x->x_ns_cache = strdup(ns)) == NULL){
clicon_err(OE_XML, errno, "strdup");
if (ns && nscache_set(x, prefix, ns) < 0)
goto done;
}
retval = 0;
done:
return retval;
@ -1406,7 +1444,7 @@ xml_free(cxobj *x)
if (x->x_cv)
cv_free(x->x_cv);
if (x->x_ns_cache)
free(x->x_ns_cache);
xml_nsctx_free(x->x_ns_cache);
free(x);
return 0;
}

View file

@ -68,6 +68,7 @@
#include "clixon_data.h"
#include "clixon_yang_module.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h"
#include "clixon_xml_changelog.h"
#include "clixon_xpath_ctx.h"
@ -77,6 +78,7 @@ static int
changelog_rename(clicon_handle h,
cxobj *xt,
cxobj *xw,
cvec *nsc,
char *tag)
{
int retval = -1;
@ -87,7 +89,7 @@ changelog_rename(clicon_handle h,
clicon_err(OE_XML, 0, "tag required");
goto done;
}
if (xpath_vec_ctx(xw, tag, &xctx) < 0)
if (xpath_vec_ctx(xw, nsc, tag, &xctx) < 0)
goto done;
if (ctx2string(xctx, &str) < 0)
goto done;
@ -192,12 +194,13 @@ static int
changelog_move(clicon_handle h,
cxobj *xt,
cxobj *xw,
cvec *nsc,
char *dst)
{
int retval = -1;
cxobj *xp; /* destination parent node */
if ((xp = xpath_first(xt, "%s", dst)) == NULL){
if ((xp = xpath_first(xt, nsc, "%s", dst)) == NULL){
clicon_err(OE_XML, 0, "path required");
goto done;
}
@ -235,7 +238,11 @@ changelog_op(clicon_handle h,
int ret;
xp_ctx *xctx = NULL;
int i;
cvec *nsc = NULL;
/* Get namespace context from changelog item */
if (xml_nsctx_node(xi, &nsc) < 0)
goto done;
if ((op = xml_find_body(xi, "op")) == NULL)
goto ok;
/* get common variables that may be used in the operations below */
@ -246,13 +253,13 @@ 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, "%s", &wvec, &wlen, wxpath) < 0)
if (xpath_vec(xt, nsc, "%s", &wvec, &wlen, wxpath) < 0)
goto done;
for (i=0; i<wlen; i++){
xw = wvec[i];
/* If 'when' exists and is false, skip this target */
if (whenxpath){
if (xpath_vec_ctx(xw, whenxpath, &xctx) < 0)
if (xpath_vec_ctx(xw, nsc, whenxpath, &xctx) < 0)
goto done;
if ((ret = ctx2boolean(xctx)) < 0)
goto done;
@ -265,7 +272,7 @@ changelog_op(clicon_handle h,
}
/* Now switch on operation */
if (strcmp(op, "rename") == 0){
ret = changelog_rename(h, xt, xw, tag);
ret = changelog_rename(h, xt, xw, nsc, tag);
}
else if (strcmp(op, "replace") == 0){
ret = changelog_replace(h, xt, xw, xnew);
@ -277,7 +284,7 @@ changelog_op(clicon_handle h,
ret = changelog_delete(h, xt, xw);
}
else if (strcmp(op, "move") == 0){
ret = changelog_move(h, xt, xw, dst);
ret = changelog_move(h, xt, xw, nsc, dst);
}
else{
clicon_err(OE_XML, 0, "Unknown operation: %s", op);
@ -288,10 +295,11 @@ changelog_op(clicon_handle h,
if (ret == 0)
goto fail;
}
ok:
retval = 1;
done:
if (nsc)
xml_nsctx_free(nsc);
if (wvec)
free(wvec);
if (xctx)
@ -320,7 +328,7 @@ changelog_iterate(clicon_handle h,
int ret;
int i;
if (xpath_vec(xch, "step", &vec, &veclen) < 0)
if (xpath_vec(xch, NULL, "step", &vec, &veclen) < 0)
goto done;
/* Iterate through changelog items */
for (i=0; i<veclen; i++){
@ -384,7 +392,7 @@ xml_changelog_upgrade(clicon_handle h,
* - find all changelogs in the interval: [from, to]
* - note it t=0 then no changelog is applied
*/
if (xpath_vec(xchlog, "changelog[namespace=\"%s\"]",
if (xpath_vec(xchlog, NULL, "changelog[namespace=\"%s\"]",
&vec, &veclen, namespace) < 0)
goto done;
/* Get all changelogs in the interval [from,to]*/

View file

@ -83,6 +83,7 @@
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_log.h"
@ -252,6 +253,14 @@ xml2cli(FILE *f,
* @retval 1 Validation OK
* @retval 0 Validation failed
* @retval -1 Error
* From rfc7950 Sec 9.9.2
* The "path" XPath expression is evaluated in the following context,
* in addition to the definition in Section 6.4.1:
* o If the "path" statement is defined within a typedef, the context
* node is the leaf or leaf-list node in the data tree that
* references the typedef.
* o Otherwise, the context node is the node in the data tree for which
* the "path" statement is defined.
*/
static int
validate_leafref(cxobj *xt,
@ -266,7 +275,8 @@ validate_leafref(cxobj *xt,
size_t xlen = 0;
char *leafrefbody;
char *leafbody;
cvec *nsc = NULL;
if ((leafrefbody = xml_body(xt)) == NULL)
goto ok;
if ((ypath = yang_find(ytype, Y_PATH, NULL)) == NULL){
@ -274,7 +284,10 @@ validate_leafref(cxobj *xt,
goto done;
goto fail;
}
if (xpath_vec(xt, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
/* XXX see comment above regarding typeref or not */
if (xml_nsctx_yang(ytype, &nsc) < 0)
goto done;
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
@ -291,6 +304,8 @@ validate_leafref(cxobj *xt,
ok:
retval = 1;
done:
if (nsc)
xml_nsctx_free(nsc);
if (xvec)
free(xvec);
return retval;
@ -1231,7 +1246,7 @@ xml_yang_validate_all(clicon_handle h,
if (yc->ys_keyword != Y_MUST)
continue;
xpath = yc->ys_argument; /* "must" has xpath argument */
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
goto done;
if (!nr){
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
@ -1244,7 +1259,7 @@ xml_yang_validate_all(clicon_handle h,
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
xpath = yc->ys_argument; /* "when" has xpath argument */
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
goto done;
if (!nr){
if (netconf_operation_failed_xml(xret, "application",
@ -1768,7 +1783,6 @@ yang2api_path_fmt(yang_stmt *ys,
* @param[in] cvv cligen variable vector, one for every wildchar in
* api_path_fmt
* @param[out] api_path api_path, eg /aaa/17. Free after use
* @param[out] yang_arg yang-stmt argument name. Free after use
* @note first and last elements of cvv are not used,..
* @see api_path_fmt2xpath
* @example
@ -1866,7 +1880,9 @@ api_path_fmt2api_path(char *api_path_fmt,
* api_path_fmt: /subif-entry=%s,%s/subid
* cvv: foo
* xpath: /subif-entry[if-name=foo]/subid"
*
* @example
* api_path_fmt: /a:b/c
* xpath : /b/c prefix:a
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
*/
int
@ -2259,13 +2275,14 @@ xml_spec_populate(cxobj *x,
/*! Translate from restconf api-path in cvv form to xml xpath
* eg a/b=c -> a/[b=c]
* eg example:a/b -> ex:a/b
* @param[in] yspec Yang spec
* @param[in] cvv api-path as cvec
* @param[in] offset Offset of cvec, where api-path starts
* @param[out] xpath The xpath as cbuf variable string, must be initializeed
* @retval 1 OK
* @retval 0 Invalid api_path or associated XML, clicon_err called
* @retval -1 Fatal error, clicon_err called
* @param[in] api_path api-path as cvec
* @param[in] offset Offset of cvec, where api-path starts
* @param[in] yspec Yang spec
* @param[in,out] xpath The xpath as cbuf (must be created and may have content)
* @param[out] namespace Namespace of xpath (direct pointer don't free)
* @retval 1 OK
* @retval 0 Invalid api_path or associated XML, clicon_err called
* @retval -1 Fatal error, clicon_err called
*
* @note both retval 0 and -1 set clicon_err, but the later is fatal
* @note Not proper namespace translation from api-path 2 xpath
@ -2279,7 +2296,7 @@ xml_spec_populate(cxobj *x,
* cvec *cvv = NULL;
* if (str2cvec("www.foo.com/restconf/a/b=c", '/', '=', &cvv) < 0)
* err;
* if ((ret = api_path2xpath(yspec, cvv, 0, cxpath)) < 0)
* if ((ret = api_path2xpath(yspec, cvv, 0, cxpath, NULL)) < 0)
* err;
* if (ret == 0){
* ... access error string in clicon_err_reason
@ -2293,10 +2310,11 @@ xml_spec_populate(cxobj *x,
* @see api_path2xml For api-path to xml tree
*/
int
api_path2xpath(yang_stmt *yspec,
cvec *cvv,
int offset,
cbuf *xpath)
api_path2xpath_cvv(cvec *api_path,
int offset,
yang_stmt *yspec,
cbuf *xpath,
char **namespace)
{
int retval = -1;
int i;
@ -2306,13 +2324,13 @@ api_path2xpath(yang_stmt *yspec,
char *name = NULL;
cvec *cvk = NULL; /* vector of index keys */
yang_stmt *y = NULL;
yang_stmt *ymod;
yang_stmt *ymod = NULL;
char *val;
char *v;
cg_var *cvi;
for (i=offset; i<cvec_len(cvv); i++){
cv = cvec_i(cvv, i);
for (i=offset; i<cvec_len(api_path); i++){
cv = cvec_i(api_path, i);
nodeid = cv_name_get(cv);
if (nodeid_split(nodeid, &prefix, &name) < 0)
goto done;
@ -2368,6 +2386,9 @@ api_path2xpath(yang_stmt *yspec,
name = NULL;
}
} /* for */
/* return values: yang module */
if (namespace)
*namespace = yang_find_mynamespace(ymod);
retval = 1; /* OK */
done:
if (prefix)
@ -2380,6 +2401,48 @@ api_path2xpath(yang_stmt *yspec,
goto done;
}
/*! Translate from restconf api-path to xml xpath as cbuf and yang module
* @retval 1 OK
* @retval 0 Invalid api_path or associated XML, clicon_err called
* @retval -1 Fatal error, clicon_err called
*/
int
api_path2xpath(char *api_path,
yang_stmt *yspec,
char **xpathp,
char **namespace)
{
int retval = -1;
cvec *cvv = NULL; /* api-path vector */
cbuf *xpath = NULL; /* xpath as cbuf (sub-function uses that) */
/* Split api-path into cligen variable vector */
if (str2cvec(api_path, '/', '=', &cvv) < 0)
goto done;
if ((xpath = cbuf_new()) == NULL)
goto done;
if ((retval = api_path2xpath_cvv(cvv, 0, yspec, xpath, namespace)) < 0)
goto done;
if (retval == 0)
goto fail;
/* prepare output xpath parameter */
if (xpathp)
if ((*xpathp = strdup(cbuf_get(xpath))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
retval = 1;
done:
if (cvv)
cvec_free(cvv);
if (xpath)
cbuf_free(xpath);
return retval;
fail:
retval = 0; /* Validation failed */
goto done;
}
/*! Create xml tree from api-path as vector
* @param[in] vec APIpath as char* vector
* @param[in] nvec Length of vec
@ -2711,7 +2774,7 @@ xml2xpath(cxobj *x,
xt = xml_parent(xt);
xcp = xml_parent(xt);
xml_parent_set(xt, NULL);
x2 = xpath_first(xt, "%s", xpath); /* +1: skip first / */
x2 = xpath_first(xt, NULL, "%s", xpath); /* +1: skip first / */
xml_parent_set(xt, xcp);
assert(x2 && x==x2);
if (x==x2)

355
lib/src/clixon_xml_nsctx.c Normal file
View file

@ -0,0 +1,355 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* XML support functions.
* @see https://www.w3.org/TR/2009/REC-xml-names-20091208
* An xml namespace context is a cligen variable vector containing a list of
* <prefix,namespace> pairs.
* It is encoded in a cvv as a list of string values, where the c name is the
* prefix and the string values are the namespace URI.
* The default namespace is decoded as having the name NULL
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
/*! Get namespace given prefix (or NULL for default) from namespace context
* @param[in] cvv Namespace context
* @param[in] prefix Namespace prefix, or NULL for default
* @retval ns Cached namespace
* @retval NULL No namespace found (not cached or not found)
*/
char*
xml_nsctx_get(cvec *cvv,
char *prefix)
{
cg_var *cv;
if ((cv = cvec_find(cvv, prefix)) != NULL)
return cv_string_get(cv);
return NULL;
}
/*! Reverse get prefix given namespace
* @param[in] cvv Namespace context
* @param[in] ns Namespace
* @param[out] prefix Prefix (direct pointer)
* @retval 0 No prefix found
* @retval 1 Prefix found
* @note NULL is a valid prefix (default)
*/
int
xml_nsctx_get_prefix(cvec *cvv,
char *namespace,
char **prefix)
{
cg_var *cv = NULL;
char *ns = NULL;
while ((cv = cvec_each(cvv, cv)) != NULL){
if ((ns = cv_string_get(cv)) != NULL &&
strcmp(ns, namespace) == 0){
*prefix = cv_name_get(cv); /* can be NULL */
return 1;
}
}
*prefix = NULL;
return 0;
}
/*! Set or replace namespace in namespace context
* @param[in] cvv Namespace context
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] namespace Cached namespace to set (assume non-null?)
* @retval 0 OK
* @retval -1 Error
*/
int
xml_nsctx_set(cvec *cvv,
char *prefix,
char *namespace)
{
int retval = -1;
cg_var *cv;
if ((cv = cvec_find(cvv, prefix)) != NULL) /* found, replace that */
cv_string_set(cv, namespace);
else /* cvec exists, but not prefix */
cvec_add_string(cvv, prefix, namespace);
retval = 0;
// done:
return retval;
}
/*! Create and initialize XML namespace context
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] namespace Cached namespace to set (assume non-null?)
* @retval nsc Return namespace context in form of a cvec
* @retval NULL Error
* @code
* cvec *nsc = NULL;
* if ((nsc = xml_nsctx_init(NULL, "urn:example:example")) == NULL)
* err;
* ...
* xml_nsctx_free(nsc);
* @endcode
* @see xml_nsctx_node Use namespace context of an existing XML node
* @see xml_nsctx_free Free the reutned handle
*/
cvec *
xml_nsctx_init(char *prefix,
char *namespace)
{
cvec *cvv = NULL;
if ((cvv = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new");
goto done;
}
if (xml_nsctx_set(cvv, prefix, namespace) < 0)
goto done;
done:
return cvv;
}
static int
xml_nsctx_node1(cxobj *xn,
cvec *ncs)
{
int retval = -1;
cxobj *xa = NULL;
char *pf; /* prefix */
char *nm; /* name */
char *val; /* value */
cxobj *xp; /* parent */
/* xmlns:t="<ns1>" prefix:xmlns, name:t
* xmlns="<ns2>" prefix:NULL name:xmlns
*/
while ((xa = xml_child_each(xn, xa, CX_ATTR)) != NULL){
pf = xml_prefix(xa);
nm = xml_name(xa);
if (pf == NULL){
if (strcmp(nm, "xmlns")==0 && /* set default namespace context */
xml_nsctx_get(ncs, NULL) == NULL){
val = xml_value(xa);
if (xml_nsctx_set(ncs, NULL, val) < 0)
goto done;
}
}
else
if (strcmp(pf, "xmlns")==0 && /* set prefixed namespace context */
xml_nsctx_get(ncs, nm) == NULL){
val = xml_value(xa);
if (xml_nsctx_set(ncs, nm, val) < 0)
goto done;
}
}
if ((xp = xml_parent(xn)) == NULL){
#ifdef USE_NETCONF_NS_AS_DEFAULT
/* If not default namespace defined, use the base netconf ns as default */
if (xml_nsctx_get(ncs, NULL) == NULL)
if (xml_nsctx_set(ncs, NULL, NETCONF_BASE_NAMESPACE) < 0)
goto done;
#endif
}
else
if (xml_nsctx_node1(xp, ncs) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Create and initialize XML namespace from XML node context
* Fully explore all prefix:namespace pairs from context of one node
* @param[in] xn XML node
* @param[out] ncp XML namespace context
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *x; // must initialize
* cvec *nsc = NULL;
* if (xml_nsctx_node(x, &nsc) < 0)
* err
* ...
* xml_nsctx_free(nsc)
* @endcode
* @see xml_nsctx_init
* @see xml_nsctx_free Free the reutned handle
*/
int
xml_nsctx_node(cxobj *xn,
cvec **ncp)
{
int retval = -1;
cvec *nc = NULL;
if ((nc = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new");
goto done;
}
if (xml_nsctx_node1(xn, nc) < 0)
goto done;
*ncp = nc;
retval = 0;
done:
return retval;
}
/*! Create and initialize XML namespace from Yang node
* Primary use is Yang path statements, eg leafrefs and others
* Fully explore all prefix:namespace pairs from context of one node
* @param[in] xn XML node
* @param[out] ncp XML namespace context
* @retval 0 OK
* @retval -1 Error
* @code
* yang_stmt *y; // must initialize
* cvec *nsc = NULL;
* if (xml_nsctx_yang(y, &nsc) < 0)
* err
* ...
* xml_nsctx_free(nsc)
* @endcode
* @see RFC7950 Sections 6.4.1 (and 9.9.2?)
*/
int
xml_nsctx_yang(yang_stmt *yn,
cvec **ncp)
{
int retval = -1;
cvec *nc = NULL;
yang_stmt *yspec;
yang_stmt *ymod; /* yang main module/submodule node */
yang_stmt *yp; /* yang prefix node */
yang_stmt *ym; /* yang imported module */
yang_stmt *yns; /* yang namespace */
yang_stmt *y;
char *name;
char *namespace;
char *prefix;
char *mynamespace;
char *myprefix;
if ((nc = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new");
goto done;
}
if ((myprefix = yang_find_myprefix(yn)) == NULL){
clicon_err(OE_YANG, ENOENT, "My yang prefix not found");
goto done;
}
if ((mynamespace = yang_find_mynamespace(yn)) == NULL){
clicon_err(OE_YANG, ENOENT, "My yang namespace not found");
goto done;
}
/* Add my prefix and default namespace (from real module) */
if (xml_nsctx_set(nc, NULL, mynamespace) < 0)
goto done;
if (xml_nsctx_set(nc, myprefix, mynamespace) < 0)
goto done;
/* Find top-most module or sub-module and get prefixes from that */
if ((ymod = ys_module(yn)) == NULL){
clicon_err(OE_YANG, ENOENT, "My yang module not found");
goto done;
}
yspec = yang_parent_get(ymod); /* Assume yspec exists */
/* Iterate over module and register all import prefixes
*/
y = NULL;
while ((y = yn_each(ymod, y)) != NULL) {
if (yang_keyword_get(y) == Y_IMPORT){
if ((name = yang_argument_get(y)) == NULL)
continue; /* Just skip - shouldnt happen) */
if ((yp = yang_find(y, Y_PREFIX, NULL)) == NULL)
continue;
if ((prefix = yang_argument_get(yp)) == NULL)
continue;
if ((ym = yang_find(yspec, Y_MODULE, name)) == NULL)
continue;
if ((yns = yang_find(ym, Y_NAMESPACE, NULL)) == NULL)
continue;
if ((namespace = yang_argument_get(yns)) == NULL)
continue;
if (xml_nsctx_set(nc, prefix, namespace) < 0)
goto done;
}
}
*ncp = nc;
retval = 0;
done:
return retval;
}
/*! Free XML namespace context
* @param[in] prefix Namespace prefix, or NULL for default
* @param[in] namespace Cached namespace to set (assume non-null?)
* @retval nsc Return namespace context in form of a cvec
* @retval NULL Error
*/
int
xml_nsctx_free(cvec *ncs)
{
cvec *cvv = (cvec*)ncs;
if (cvv)
cvec_free(cvv);
return 0;
}

View file

@ -32,6 +32,56 @@
***** END LICENSE BLOCK *****
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
*
* Some notes on namespace extensions in Netconf/Yang
* RFC6241 8.9.1
* The set of namespace declarations are those in scope on the <filter> element.
* <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
* <get-config>
* <filter xmlns:t="http://example.com/schema/1.2/config"
* type="xpath"
* select="/t:top/t:users/t:user[t:name='fred']"/>
* </get-config>
* We need to add namespace context to the cpath tree, typically in eval. How do
* we do that?
* One observation is that the namespace context is static, so it can not be a part
* of the xpath-tree, which is context-dependent.
* Best is to send it as a (read-only) parameter to the xp_eval family of functions
* as an exlicit namespace context.
* For that you need an API to get/set namespaces: clixon_xml_nscache.c?
* Then you need to fix API functions and this is the real work:
* - Replace all existing functions or create new?
* - Expose explicit namespace parameter, or xml object, or default namespace?
*
* On namespaces and xpath
* =======================
* XPATHs may contain prefixes such as /if:a/if:b
* XPATHs excecutes in a "namespace context" (nsc)
* The namespace context is either:
* (1) the same as the xml that is evaluated, typical for basic XML, or
* (2) separate from the XML that is evaluated. typical netconf and yang.
* 1. Same nsc as XML
* This happens in base XML (not yang), where the nsc is given implicitly by
* the XML being evaluated. In node comparisons (eg of ip:a) only name and
* prefixes are compared.
* XML: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
* XPATH: /if:a/ip:b
* When you call an xpath function, then call it with nsc=NULL.
* 2. Separate nsc.
* This happens if the namespace context is independent from the XML. It can
* happen for example in NETCONF GET using :xpath when the XML is not known
* so that xpath and xml may use different prefixes for the same namespace.
* In that case you cannot rely on the prefix but must compare namespaces.
* The namespace context of the XML is given (by the XML), but the xpath
* nsc must then be explicitly given in the xpath call.
* Example:
* XML: <if:a xmlns:if="urn:example:if" xmlns:ip="urn:example:ip"><ip:b/></if>
* NETCONF:<get-config><filter select="/x:a/y:b"
* xmlns:x="urn:example:if" xmlns:y="urn:example:ip/>
* Here, x,y are prefixes used for two namespaces that are given by if,ip in
* the xml. In this case, the namespaces (eg urn:example:if) must be compared
* instead.
* Another case is Yang path expressions.
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@ -62,6 +112,7 @@
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_parse.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
@ -69,6 +120,9 @@
/*
* Variables
*/
/* XXX assert break if xpath dont match. Set to 0 if ypu want it to pass */
int xpatherrordiff=1;
/* Mapping between XPATH operator string <--> int */
const map_str2int xpopmap[] = {
{"and", XO_AND},
@ -141,8 +195,7 @@ xpath_tree_print(cbuf *cb,
}
static int
xpath_tree_free(
xpath_tree *xs)
xpath_tree_free(xpath_tree *xs)
{
if (xs->xs_s0)
free(xs->xs_s0);
@ -156,45 +209,118 @@ xpath_tree_free(
return 0;
}
/*!
* @retval -1 Error XXX: retval -1 not properly handled
* @retval 0 No match
* @retval 1 Match
*/
static int
nodetest_eval_node(cxobj *x,
xpath_tree *xs,
cvec *nsc)
{
int retval = -1;
char *name1 = xml_name(x);
char *prefix1 = xml_prefix(x);
char *nsxml = NULL; /* xml body namespace */
char *nsxpath = NULL; /* xpath context namespace */
char *prefix2 = NULL;
char *name2 = NULL;
/* Namespaces is s0, name is s1 */
if (strcmp(xs->xs_s1, "*")==0)
return 1;
/* get namespace of xml tree */
if (xml2ns(x, prefix1, &nsxml) < 0)
goto done;
prefix2 = xs->xs_s0;
name2 = xs->xs_s1;
/* Before going into namespaces, check name equality and filter out noteq */
if (strcmp(name1, name2) != 0){
retval = 0; /* no match */
goto done;
}
/* here names are equal
* Now look for namespaces
* 1) prefix1 and prefix2 point to same namespace <<-- try this first
* 2) prefix1 is equal to prefix2 <<-- then try this
* (1) is strict yang xml
* (2) without yang
*/
if (nsc != NULL) { /* solution (1) */
nsxpath = xml_nsctx_get(nsc, prefix2);
if (nsxml != NULL && nsxpath != NULL)
retval = (strcmp(nsxml, nsxpath) == 0);
else
retval = (nsxml == nsxpath); /* True only if both are NULL */
}
else{ /* solution (2) */
if (prefix1 == NULL && prefix2 == NULL)
retval = 1;
else if (prefix1 == NULL || prefix2 == NULL)
retval = 0;
else
retval = strcmp(prefix1, prefix2) == 0;
}
/* If retval == 0 here, then there is name match, but not ns match */
if (retval == 0){
fprintf(stderr, "%s NOMATCH xml: (%s)%s\n\t\t xpath: (%s)%s\n", __FUNCTION__,
name1, nsxml,
name2, nsxpath);
if (xpatherrordiff)
assert(retval == 1);
}
done:
return retval;
}
/*! Make a nodetest
* @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
* @retval 0 Match
* @retval 1 No match
* @param[in] xs XPATH stack of type XP_NODE or XP_NODE_FN
* @param[in] nsc XML Namespace context
* @retval -1 Error
* @retval 0 No match
* @retval 1 Match
* - node() is true for any node of any type whatsoever.
* - text() is true for any text node.
*/
static int
nodetest_eval(cxobj *x,
xpath_tree *xs)
xpath_tree *xs,
cvec *nsc)
{
int retval = 0; /* NB: no match is default (not error) */
char *fn;
if (xs->xs_type == XP_NODE){
/* Namespaces is s0, name is s1 */
if (strcmp(xs->xs_s1, "*")==0)
return 1;
else if (strcmp(xml_name(x), xs->xs_s1)==0)
return 1;
else
return 0;
}
if (xs->xs_type == XP_NODE)
retval = nodetest_eval_node(x, xs, nsc);
else if (xs->xs_type == XP_NODE_FN){
fn = xs->xs_s0;
if (strcmp(fn, "node")==0)
return 1;
retval = 1;
else if (strcmp(fn, "text")==0)
return 1;
retval = 1;
}
return 0;
/* note, retval set by previous statement */
return retval;
}
/*!
* @param[in] xn
* @param[in] nodetest XPATH stack
* @param[in] node_type
* @param[in] flags
* @param[in] nsc XML Namespace context
* @param[out] vec0
* @param[out] vec0len
*/
int
nodetest_recursive(cxobj *xn,
nodetest_recursive(cxobj *xn,
xpath_tree *nodetest,
int node_type,
uint16_t flags,
cxobj ***vec0,
size_t *vec0len)
int node_type,
uint16_t flags,
cvec *nsc,
cxobj ***vec0,
size_t *vec0len)
{
int retval = -1;
cxobj *xsub;
@ -203,14 +329,14 @@ nodetest_recursive(cxobj *xn,
xsub = NULL;
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
if (nodetest_eval(xsub, nodetest) == 1){
if (nodetest_eval(xsub, nodetest, nsc) == 1){
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
if (flags==0x0 || xml_flag(xsub, flags))
if (cxvec_append(xsub, &vec, &veclen) < 0)
goto done;
// continue; /* Dont go deeper */
}
if (nodetest_recursive(xsub, nodetest, node_type, flags, &vec, &veclen) < 0)
if (nodetest_recursive(xsub, nodetest, node_type, flags, nsc, &vec, &veclen) < 0)
goto done;
}
retval = 0;
@ -220,12 +346,13 @@ nodetest_recursive(cxobj *xn,
return retval;
}
static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp);
static int xp_eval(xp_ctx *xc, xpath_tree *xs, cvec *nsc, xp_ctx **xrp);
/*! Evaluate xpath step rule of an XML tree
*
* @param[in] xc0 Incoming context
* @param[in] xs XPATH node tree
* @param[in] nsc XML Namespace context
* @param[out] xrp Resulting context
*
* - A node test that is a QName is true if and only if the type of the node (see [5 Data Model])
@ -237,6 +364,7 @@ static int xp_eval(xp_ctx *xc, xpath_tree *xs, xp_ctx **xrp);
static int
xp_eval_step(xp_ctx *xc0,
xpath_tree *xs,
cvec *nsc,
xp_ctx **xrp)
{
int retval = -1;
@ -263,7 +391,7 @@ xp_eval_step(xp_ctx *xc0,
if (xc->xc_descendant){
for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, &vec, &veclen) < 0)
if (nodetest_recursive(xv, nodetest, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
goto done;
}
xc->xc_descendant = 0;
@ -280,7 +408,7 @@ xp_eval_step(xp_ctx *xc0,
x = NULL;
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
/* xs->xs_c0 is nodetest */
if (nodetest == NULL || nodetest_eval(x, nodetest))
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc) == 1)
if (cxvec_append(x, &vec, &veclen) < 0)
goto done;
}
@ -292,7 +420,7 @@ xp_eval_step(xp_ctx *xc0,
case A_DESCENDANT_OR_SELF:
for (i=0; i<xc->xc_size; i++){
xv = xc->xc_nodeset[i];
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, &vec, &veclen) < 0)
if (nodetest_recursive(xv, xs->xs_c0, CX_ELMNT, 0x0, nsc, &vec, &veclen) < 0)
goto done;
}
ctx_nodeset_replace(xc, vec, veclen);
@ -331,7 +459,7 @@ xp_eval_step(xp_ctx *xc0,
break;
}
if (xs->xs_c1){
if (xp_eval(xc, xs->xs_c1, xrp) < 0)
if (xp_eval(xc, xs->xs_c1, nsc, xrp) < 0)
goto done;
}
else{
@ -351,6 +479,7 @@ xp_eval_step(xp_ctx *xc0,
* pred -> pred expr
* @param[in] xc Incoming context
* @param[in] xs XPATH node tree
* @param[in] nsc XML Namespace context
* @param[out] xrp Resulting context
*
* A predicate filters a node-set with respect to an axis to produce a new
@ -371,6 +500,7 @@ xp_eval_step(xp_ctx *xc0,
static int
xp_eval_predicate(xp_ctx *xc,
xpath_tree *xs,
cvec *nsc,
xp_ctx **xrp)
{
int retval = -1;
@ -386,7 +516,7 @@ xp_eval_predicate(xp_ctx *xc,
goto done;
}
else{ /* eval previous predicates */
if (xp_eval(xc, xs->xs_c0, &xr0) < 0)
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
goto done;
}
if (xs->xs_c1){
@ -415,7 +545,7 @@ xp_eval_predicate(xp_ctx *xc,
* evaluated with that node as the context node */
if (cxvec_append(x, &xcc->xc_nodeset, &xcc->xc_size) < 0)
goto done;
if (xp_eval(xcc, xs->xs_c1, &xrc) < 0)
if (xp_eval(xcc, xs->xs_c1, nsc, &xrc) < 0)
goto done;
if (xcc)
ctx_free(xcc);
@ -834,13 +964,16 @@ xp_union(xp_ctx *xc1,
* Each node in that set is used as a context node for the following step.
* @param[in] xc Incoming context
* @param[in] xs XPATH node tree
* @param[in] nsc XML Namespace context
* @param[out] xrp Resulting context
* @retval 0 OK
* @retval -1 Error
*/
static int
xp_eval(xp_ctx *xc,
xpath_tree *xs,
cvec *nsc,
xp_ctx **xrp)
{
int retval = -1;
cxobj *x;
@ -880,12 +1013,12 @@ xp_eval(xp_ctx *xc,
break;
case XP_STEP: /* XP_NODE is first argument -not called explicitly */
if (xp_eval_step(xc, xs, xrp) < 0)
if (xp_eval_step(xc, xs, nsc, xrp) < 0)
goto done;
goto ok;
break;
case XP_PRED:
if (xp_eval_predicate(xc, xs, xrp) < 0)
if (xp_eval_predicate(xc, xs, nsc, xrp) < 0)
goto done;
goto ok;
break;
@ -895,7 +1028,7 @@ xp_eval(xp_ctx *xc,
/* Eval first child c0
*/
if (xs->xs_c0){
if (xp_eval(xc, xs->xs_c0, &xr0) < 0)
if (xp_eval(xc, xs->xs_c0, nsc, &xr0) < 0)
goto done;
}
/* Actions between first and second child
@ -973,7 +1106,7 @@ xp_eval(xp_ctx *xc,
* Note, some operators like locationpath, need transitive context (use_xr0)
*/
if (xs->xs_c1)
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, &xr1) < 0)
if (xp_eval(use_xr0?xr0:xc, xs->xs_c1, nsc, &xr1) < 0)
goto done;
/* Actions after second child
*/
@ -1032,19 +1165,20 @@ xp_eval(xp_ctx *xc,
if (xr0)
ctx_free(xr0);
return retval;
}
} /* xp_eval */
/*! Given XML tree and xpath, returns xpath context
/*! Given XML tree and xpath, parse xpath, eval it and return xpath context,
* This is a raw form of xpath where you can do type conversion, etc,
* not just a nodeset.
* @param[in] xcur XML-tree where to search
* @param[in] xpath String with XPATH 1.0 syntax
* @param[in] nsc XML Namespace context
* @param[out] xrp Return XPATH context
* @retval 0 OK
* @retval -1 Error
* @code
* xp_ctx *xc = NULL;
* if (xpath_vec_ctx(x, xpath, &xc) < 0)
* if (xpath_vec_ctx(x, NULL, xpath, &xc) < 0)
* err;
* if (xc)
* ctx_free(xc);
@ -1052,6 +1186,7 @@ xp_eval(xp_ctx *xc,
*/
int
xpath_vec_ctx(cxobj *xcur,
cvec *nsc,
char *xpath,
xp_ctx **xrp)
{
@ -1084,7 +1219,7 @@ xpath_vec_ctx(cxobj *xcur,
xc.xc_initial = xcur;
if (cxvec_append(xcur, &xc.xc_nodeset, &xc.xc_size) < 0)
goto done;
if (xp_eval(&xc, xy.xy_top, xrp) < 0)
if (xp_eval(&xc, xy.xy_top, nsc, xrp) < 0)
goto done;
if (xc.xc_nodeset){
free(xc.xc_nodeset);
@ -1102,23 +1237,27 @@ xpath_vec_ctx(cxobj *xcur,
/*! Xpath nodeset function where only the first matching entry is returned
* args:
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @retval xml-tree of first match
* @retval NULL Error or not found
* @param[in] xcur XML tree where to search
* @param[in] nsc XML Namespace context
* @param[in] format string with XPATH syntax
* @retval xml-tree XML tree of first match
* @retval NULL Error or not found
*
* @code
* cxobj *x;
* if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) {
* cvec *nsc; // namespace context
* if ((x = xpath_first(xtop, nsc, "//symbol/foo")) != NULL) {
* ...
* }
* @endcode
* @note the returned pointer points into the original tree so should not be freed fter use.
* @note return value does not see difference between error and not found
* @see also xpath_vec.
* @experimental
*/
cxobj *
xpath_first(cxobj *xcur,
cvec *nsc,
char *format,
...)
{
@ -1144,9 +1283,8 @@ xpath_first(cxobj *xcur,
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET && xr->xc_size)
cx = xr->xc_nodeset[0];
done:
@ -1157,18 +1295,21 @@ xpath_first(cxobj *xcur,
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] xpath stdarg string with XPATH 1.0 syntax
* @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
* @code
* cvec *nsc; // namespace context
* cxobj **vec;
* size_t veclen;
* if (xpath_vec(xcur, "//symbol/foo", &vec, &veclen) < 0)
* if (xpath_vec(xcur, nsc, "//symbol/foo", &vec, &veclen) < 0)
* goto err;
* for (i=0; i<veclen; i++){
* xn = vec[i];
@ -1176,9 +1317,11 @@ xpath_first(cxobj *xcur,
* }
* free(vec);
* @endcode
* @note Namespace prefix checking is not properly implemented
*/
int
xpath_vec(cxobj *xcur,
cvec *nsc,
char *format,
cxobj ***vec,
size_t *veclen,
@ -1188,8 +1331,8 @@ xpath_vec(cxobj *xcur,
va_list ap;
size_t len;
char *xpath = NULL;
xp_ctx *xr = NULL;
xp_ctx *xr = NULL;
va_start(ap, veclen);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
@ -1208,7 +1351,7 @@ xpath_vec(cxobj *xcur,
va_end(ap);
*vec=NULL;
*veclen = 0;
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET){
*vec = xr->xc_nodeset;
@ -1227,6 +1370,7 @@ xpath_vec(cxobj *xcur,
/* 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
@ -1235,7 +1379,8 @@ xpath_vec(cxobj *xcur,
* @code
* cxobj **vec;
* size_t veclen;
* if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
* cvec *nsc; // namespace context (not NULL)
* if (xpath_vec_flag(xcur, nsc, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
* goto err;
* for (i=0; i<veclen; i++){
* xn = vec[i];
@ -1249,8 +1394,9 @@ xpath_vec(cxobj *xcur,
*/
int
xpath_vec_flag(cxobj *xcur,
cvec *nsc,
char *format,
uint16_t flags,
uint16_t flags,
cxobj ***vec,
size_t *veclen,
...)
@ -1281,7 +1427,7 @@ xpath_vec_flag(cxobj *xcur,
va_end(ap);
*vec=NULL;
*veclen = 0;
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET){
for (i=0; i<xr->xc_size; i++){
@ -1303,14 +1449,16 @@ 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
*/
int
xpath_vec_bool(cxobj *xcur,
char *format,
xpath_vec_bool(cxobj *xcur,
cvec *nsc,
char *format,
...)
{
int retval = -1;
@ -1335,7 +1483,7 @@ xpath_vec_bool(cxobj *xcur,
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, xpath, &xr) < 0)
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
goto done;
if (xr)
retval = ctx2boolean(xr);

View file

@ -32,7 +32,7 @@
***** END LICENSE BLOCK *****
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
* This file defines XPATH contexts using in traversing the XPATH parse tree.
* This file defines XPATH contexts used in traversing the XPATH parse tree.
*/
#include <stdio.h>
#include <stdlib.h>

View file

@ -31,6 +31,7 @@
***** END LICENSE BLOCK *****
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
*/
#ifndef _CLIXON_XPATH_PARSE_H_
#define _CLIXON_XPATH_PARSE_H_
@ -50,7 +51,7 @@ enum xp_type{
XP_ABSPATH,
XP_RELLOCPATH,
XP_STEP,
XP_NODE,
XP_NODE, /* s0 is namespace prefix, s1 is name */
XP_NODE_FN,
XP_PRED,
XP_PRI0,

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.
@ -789,12 +789,12 @@ yang_find_mynamespace(yang_stmt *ys)
char *namespace = NULL;
if ((ymod = ys_real_module(ys)) == NULL){
clicon_err(OE_YANG, 0, "My yang module not found");
clicon_err(OE_YANG, ENOENT, "My yang module not found");
goto done;
}
if ((ynamespace = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL)
goto done;
namespace = ynamespace->ys_argument;
namespace = yang_argument_get(ynamespace);
done:
return namespace;
}
@ -1176,17 +1176,17 @@ yang_find_module_by_prefix(yang_stmt *ys,
}
yimport = NULL;
while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
if (yimport->ys_keyword != Y_IMPORT &&
yimport->ys_keyword != Y_INCLUDE)
if (yang_keyword_get(yimport) != Y_IMPORT &&
yang_keyword_get(yimport) != Y_INCLUDE)
continue;
if ((yprefix = yang_find(yimport, Y_PREFIX, NULL)) != NULL &&
strcmp(yprefix->ys_argument, prefix) == 0){
strcmp(yang_argument_get(yprefix), prefix) == 0){
break;
}
}
if (yimport){
if ((ymod = yang_find(yspec, Y_MODULE, yimport->ys_argument)) == NULL &&
(ymod = yang_find(yspec, Y_SUBMODULE, yimport->ys_argument)) == NULL){
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yimport))) == NULL &&
(ymod = yang_find(yspec, Y_SUBMODULE, yang_argument_get(yimport))) == NULL){
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
prefix);
yimport = NULL;
@ -1222,7 +1222,7 @@ yang_find_module_by_namespace(yang_stmt *yspec,
return ymod;
}
/*! Given a yang spec and a module name, return yang module
/*! Given a yang spec and a module name, return yang module or submodule
*
* @param[in] yspec A yang specification
* @param[in] name Name of module

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.

View file

@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.
@ -80,7 +80,7 @@ struct yang_stmt{
char *ys_argument; /* String / argument depending on keyword */
int ys_flags; /* Flags according to YANG_FLAG_* above */
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
nodes can belong to other
nodes can belong to other
modules than the ancestor module */
char *ys_extra; /* For unknown */

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.
@ -69,6 +69,7 @@
#include "clixon_file.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_options.h"
@ -253,6 +254,7 @@ yms_build(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in] xpath XML Xpath
* @param[in] nsc XML Namespace context for xpath
* @param[in] brief Just name, revision and uri (no cache)
* @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal)
@ -281,6 +283,7 @@ int
yang_modules_state_get(clicon_handle h,
yang_stmt *yspec,
char *xpath,
cvec *nsc,
int brief,
cxobj **xret)
{
@ -302,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, "%s", xpath)){
if (xpath_first(xw, NULL, "%s", xpath)){
if ((x = xml_dup(xc)) == NULL) /* Make copy and use below */
goto done;
}
@ -334,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, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
if (xvec != NULL){
for (i=0; i<xlen; i++)

View file

@ -5,7 +5,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.

View file

@ -1,7 +1,7 @@
/*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.

View file

@ -2,7 +2,7 @@
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
Copyright (C) 2009-2019 Olof Hagsand
This file is part of CLIXON.