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:
parent
73d8e97a01
commit
67b8685bab
78 changed files with 1507 additions and 538 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]*/
|
||||
|
|
|
|||
|
|
@ -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
355
lib/src/clixon_xml_nsctx.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue