This commit is contained in:
Olof hagsand 2019-12-18 23:27:52 +01:00
commit 241ae26e55
137 changed files with 1558 additions and 1405 deletions

View file

@ -3,7 +3,6 @@ language: c
branches:
only:
- master
- develop
before_script:
- sudo apt-get install -y libfcgi-dev
- ./test/travis/before_script.sh

View file

@ -1,5 +1,35 @@
# Clixon Changelog
## 4.3.0 (Expected: December 2019)
### Minor changes
* Added "canonical" global namespace context: `nsctx_global`
* This is a normalized XML prefix:namespace pair vector computed from all loaded Yang modules. Useful when writing XML and XPATH expressions in callbacks.
* Get it with `clicon_nsctx_global_get(h)`
* Added wildcard `*` as a mode to `CLICON_MODE` in clispec files
* If you set "CLICON_MODE="*";" in a clispec file it means that syntax will appear in all CLI spec modes.
* State callbacks provided by user are validated. If they are invalid an internal error is returned.
* Fixed multi-namespace for augmented state which was not covered in 4.2.0.
### API changes on existing features (you may need to change your code)
* Yang files reorganized into three classes: clixon, mandatory, optional (previous "standard" split into mandatory and optional).
* Clixon and mandatory yang spec are always installed
* Optional yang files are loaded only if configured with `--enable-optyangs` (flipped logic and changed from `disable-stdyangs`). NOTE: you must do this to run examples and tests.
* Optional yang files can be installed in a separate dir with `--with-opt-yang-installdir=DIR` (renamed from `with-std-yang-installdir`)
* C-API
* Added namespace-context parameter `nsc` to `xpath_first` and `xpath_vec`, (`xpath_vec_nsc` and xpath_first_nsc` are removed).
* Added clicon_handle as parameter to all `clicon_connect_` functions to get better error message
* Added nsc parameter to `xmldb_get()`
* The multi-namespace augment state may rearrange the XML namespace attributes.
* Main example yang changed to incorporate augmented state, new revision is 2019-11-15.
### Corrected Bugs
* [filter in netconf - one specific entry #100](https://github.com/clicon/clixon/issues/100)
* [xpath_tree2cbuf() changes integers into floating point representations #99](https://github.com/clicon/clixon/issues/99)
* [xml_parse_string() is slow for a long XML string #96](https://github.com/clicon/clixon/issues/96)
* Mandatory variables can no longer be deleted.
* [Add missing includes](https://github.com/clicon/clixon/pulls)
## 4.2.0 (October 27 2019)
### Summary
@ -12,7 +42,7 @@ The main improvement in thus release concerns security in terms of priveleges an
* use `-U <user>` clixon_backend command-line option to drop to `user`
* Generic options are the following:
* `CLICON_BACKEND_USER` sets the user to drop priveleges to
* CLICON_BACKEND_PRIVELEGES can have the following values:
* `CLICON_BACKEND_PRIVELEGES` can have the following values:
* `none` Make no drop/change in privileges. This is currently the default.
* `drop_perm` After initialization, drop privileges permanently
* `drop_perm` After initialization, drop privileges temporarily (to a euid)

View file

@ -170,8 +170,8 @@ client_get_capabilities(clicon_handle h,
int retval = -1;
cxobj *xrstate = NULL; /* xml restconf-state node */
cxobj *xcap = NULL; /* xml capabilities node */
if ((xrstate = xpath_first(*xret, "restconf-state")) == NULL){
if ((xrstate = xpath_first(*xret, NULL, "restconf-state")) == NULL){
clicon_err(OE_YANG, ENOENT, "restconf-state not found in config node");
goto done;
}
@ -321,10 +321,10 @@ client_statedata(clicon_handle h,
if (ret == 0)
goto fail;
/* Code complex to filter out anything that is outside of xpath
* Actually this is a safety catch, should realy be done in plugins
* Actually this is a safety catch, should really be done in plugins
* and modules_state functions.
*/
if (xpath_vec_nsc(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(*xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found and
* then filter out everything else,
@ -434,7 +434,7 @@ from_client_get_config(clicon_handle h,
if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0)
goto done;
if (ret == 0){ /* Do NACM validation */
if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* NACM datanode/module read validation */
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
@ -529,14 +529,14 @@ from_client_edit_config(clicon_handle h,
goto done;
goto ok;
}
if ((x = xpath_first(xn, "default-operation")) != NULL){
if ((x = xpath_first(xn, NULL, "default-operation")) != NULL){
if (xml_operation(xml_body(x), &operation) < 0){
if (netconf_invalid_value(cbret, "protocol", "Wrong operation")< 0)
goto done;
goto ok;
}
}
if ((xc = xpath_first(xn, "config")) == NULL){
if ((xc = xpath_first(xn, NULL, "config")) == NULL){
if (netconf_missing_element(cbret, "protocol", "config", NULL) < 0)
goto done;
goto ok;
@ -967,7 +967,7 @@ from_client_get(clicon_handle h,
if ((ret = nacm_access_pre(h, username, NACM_DATA, &xnacm)) < 0)
goto done;
if (ret == 0){ /* Do NACM validation */
if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* NACM datanode/module read validation */
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
@ -1122,9 +1122,9 @@ from_client_create_subscription(clicon_handle h,
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netmod:notification")) == NULL)
goto done;
if ((x = xpath_first_nsc(xe, nsc, "//stream")) != NULL)
if ((x = xpath_first(xe, nsc, "//stream")) != NULL)
stream = xml_find_value(x, "body");
if ((x = xpath_first_nsc(xe, nsc, "//stopTime")) != NULL){
if ((x = xpath_first(xe, nsc, "//stopTime")) != NULL){
if ((stoptime = xml_find_value(x, "body")) != NULL &&
str2time(stoptime, &stop) < 0){
if (netconf_bad_element(cbret, "application", "stopTime", "Expected timestamp") < 0)
@ -1132,7 +1132,7 @@ from_client_create_subscription(clicon_handle h,
goto ok;
}
}
if ((x = xpath_first_nsc(xe, nsc, "//startTime")) != NULL){
if ((x = xpath_first(xe, nsc, "//startTime")) != NULL){
if ((starttime = xml_find_value(x, "body")) != NULL &&
str2time(starttime, &start) < 0){
if (netconf_bad_element(cbret, "application", "startTime", "Expected timestamp") < 0)
@ -1140,7 +1140,7 @@ from_client_create_subscription(clicon_handle h,
goto ok;
}
}
if ((xfilter = xpath_first_nsc(xe, nsc, "//filter")) != NULL){
if ((xfilter = xpath_first(xe, nsc, "//filter")) != NULL){
if ((ftype = xml_find_value(xfilter, "type")) != NULL){
/* Only accept xpath as filter type */
if (strcmp(ftype, "xpath") != 0){
@ -1367,8 +1367,8 @@ from_client_msg(clicon_handle h,
goto reply;
}
if ((x = xpath_first_nsc(xt, NULL, "/rpc")) == NULL){
if ((x = xpath_first_nsc(xt, NULL, "/hello")) != NULL){
if ((x = xpath_first(xt, NULL, "/rpc")) == NULL){
if ((x = xpath_first(xt, NULL, "/hello")) != NULL){
if ((ret = from_client_hello(h, x, ce, cbret)) <0)
goto done;
goto reply;

View file

@ -118,10 +118,14 @@ generic_validate(clicon_handle h,
for (i=0; i<td->td_dlen; i++){
x1 = td->td_dvec[i];
ys = xml_spec(x1);
if (ys && yang_mandatory(ys) && yang_config(ys)==0){
if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "Missing mandatory variable") < 0)
goto done;
goto fail;
if (ys && yang_mandatory(ys) && yang_config(ys)==1){
yang_stmt *yp =yang_parent_get(ys);
if (yp== NULL ||
(yang_keyword_get(yp)!=Y_MODULE && yang_keyword_get(yp)!=Y_SUBMODULE)){
if (netconf_missing_element_xml(xret, "protocol", xml_name(x1), "May not remove mandatory variable") < 0)
goto done;
goto fail;
}
}
}
/* added entries */

View file

@ -92,6 +92,7 @@ backend_terminate(clicon_handle h)
cxobj *x;
struct stat st;
int ss;
cvec *nsctx;
clicon_debug(1, "%s", __FUNCTION__);
if ((ss = clicon_socket_get(h)) != -1)
@ -110,6 +111,8 @@ backend_terminate(clicon_handle h)
yspec_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
yspec_free(yspec);
if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
cvec_free(nsctx);
if ((x = clicon_nacm_ext(h)) != NULL)
xml_free(x);
if ((x = clicon_conf_xml(h)) != NULL)
@ -443,7 +446,6 @@ main(int argc,
char *nacm_mode;
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
yang_stmt *yspec = NULL;
yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */
char *str;
int ss = -1; /* server socket */
cbuf *cbret = NULL; /* startup cbuf if invalid */
@ -451,6 +453,7 @@ main(int argc,
int ret;
char *dir;
gid_t gid = -1;
cvec *nsctx_global = NULL; /* Global namespace context */
/* In the startup, logs to stderr & syslog and debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -505,17 +508,13 @@ main(int argc,
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(debug, NULL);
/* Create configure yang-spec */
if ((yspecfg = yspec_new()) == NULL)
goto done;
/* Find and read configfile */
if (clicon_options_main(h, yspecfg) < 0){
if (clicon_options_main(h) < 0){
if (help)
usage(h, argv[0]);
return -1;
}
clicon_config_yang_set(h, yspecfg);
/* External NACM file? */
nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
if (nacm_mode && strcmp(nacm_mode, "external") == 0)
@ -722,10 +721,10 @@ main(int argc,
if ((str = clicon_yang_main_dir(h)) != NULL)
if (yang_spec_load_dir(h, str, yspec) < 0)
goto done;
/* Load clixon lib yang module */
/* Load clixon lib yang module */
if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
goto done;
/* Load yang module library, RFC7895 */
/* Load yang module library, RFC7895 */
if (yang_modules_init(h) < 0)
goto done;
/* Add netconf yang spec, used by netconf client and as internal protocol
@ -736,13 +735,21 @@ main(int argc,
if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0)
goto done;
/* Load yang Restconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
/* Load yang Netconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
goto done;
/* Load yang Netconf stream discovery */
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Initialize server socket and save it to handle */
if (backend_rpc_init(h) < 0)
goto done;
@ -895,7 +902,7 @@ main(int argc,
goto done;
ok:
retval = 0;
done:
done:
if (cbret)
cbuf_free(cbret);
clicon_log(LOG_NOTICE, "%s: %u Terminated retval:%d", __PROGRAM__, getpid(), retval);

View file

@ -111,9 +111,11 @@ clixon_plugin_statedata(clicon_handle h,
{
int retval = -1;
int ret;
cxobj *xerr = NULL;
cxobj *x = NULL;
clixon_plugin *cp = NULL;
plgstatedata_t *fn; /* Plugin statedata fn */
cbuf *cberr = NULL;
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if ((fn = cp->cp_api.ca_statedata) == NULL)
@ -124,6 +126,38 @@ clixon_plugin_statedata(clicon_handle h,
goto fail; /* Dont quit here on user callbacks */
if (xml_apply(x, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
/* Check XML from state callback by validating it. return internal
* error with error cause
*/
if ((ret = xml_yang_validate_all_top(h, x, &xerr)) < 0)
goto done;
if (ret > 0 && (ret = xml_yang_validate_add(h, x, &xerr)) < 0)
goto done;
if (ret == 0){
if ((cberr = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cberr, "Internal error: state callback returned invalid XML: ");
if (netconf_err2cb(xpath_first(xerr, NULL, "rpc-error"), cberr) < 0)
goto done;
if (*xret){
xml_free(*xret);
*xret = NULL;
}
if (netconf_operation_failed_xml(xret, "application", cbuf_get(cberr))< 0)
goto done;
goto fail;
}
#if 1
if (debug){
cbuf *ccc=cbuf_new();
if (clicon_xml2cbuf(ccc, x, 0, 0, -1) < 0)
goto done;
clicon_debug(1, "%s MERGE: %s", __FUNCTION__, cbuf_get(ccc));
cbuf_free(ccc);
}
#endif
if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
goto done;
if (ret == 0)
@ -135,8 +169,12 @@ clixon_plugin_statedata(clicon_handle h,
}
retval = 1;
done:
if (cberr)
cbuf_free(cberr);
if (x)
xml_free(x);
if (xerr)
xml_free(xerr);
return retval;
fail:
retval = 0;

View file

@ -270,10 +270,12 @@ backend_accept_client(int fd,
#error "Need getsockopt O_PEERCRED or getpeereid for unix socket peer cred"
#endif
if (name != NULL){
if ((ce->ce_username = strdup(name)) == NULL){
if ((ce->ce_username = name) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
name = NULL;
goto done;
}
name = NULL;
}
break;
case AF_INET:
@ -291,5 +293,7 @@ backend_accept_client(int fd,
goto done;
retval = 0;
done:
if (name)
free(name);
return retval;
}

View file

@ -241,7 +241,7 @@ startup_extraxml(clicon_handle h,
* It should be empty if extra-xml is null and reset plugins did nothing
* then skip validation.
*/
if (xmldb_get(h, tmp_db, NULL, &xt0) < 0)
if (xmldb_get(h, tmp_db, NULL, NULL, &xt0) < 0)
goto done;
if (xt0==NULL || xml_child_nr(xt0)==0)
goto ok;

View file

@ -46,7 +46,6 @@
#include <errno.h>
#include <unistd.h>
#include <assert.h>
#include <fnmatch.h>
#include <sys/types.h>
#include <sys/time.h>
#include <regex.h>

View file

@ -714,13 +714,13 @@ compare_dbs(clicon_handle h,
astext = 0;
if (clicon_rpc_get_config(h, NULL, "running", "/", NULL, &xc1) < 0)
goto done;
if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xc1, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
if (clicon_rpc_get_config(h, NULL, "candidate", "/", NULL, &xc2) < 0)
goto done;
if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xc2, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
@ -884,7 +884,7 @@ save_config_file(clicon_handle h,
clicon_err(OE_CFG, 0, "get config: empty tree"); /* Shouldnt happen */
goto done;
}
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
@ -992,7 +992,7 @@ cli_notification_cb(int s,
}
if (clicon_msg_decode(reply, NULL, NULL, &xt) < 0) /* XXX pass yang_spec */
goto done;
if ((xe = xpath_first(xt, "//event")) != NULL){
if ((xe = xpath_first(xt, NULL, "//event")) != NULL){
x = NULL;
while ((x = xml_child_each(xe, x, -1)) != NULL) {
switch (format){
@ -1224,7 +1224,7 @@ cli_copy_config(clicon_handle h,
/* Get from object configuration and store in x1 */
if (clicon_rpc_get_config(h, NULL, db, cbuf_get(cb), nsc, &x1) < 0)
goto done;
if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){
if ((xerr = xpath_first(x1, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
@ -1243,7 +1243,7 @@ cli_copy_config(clicon_handle h,
xml_name_set(x2, "config");
cprintf(cb, "/%s", keyname);
if ((x = xpath_first_nsc(x2, nsc, "%s", cbuf_get(cb))) == NULL){
if ((x = xpath_first(x2, nsc, "%s", cbuf_get(cb))) == NULL){
clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname);
goto done;
}

View file

@ -162,10 +162,10 @@ cli_cligen(clicon_handle h)
int
cli_parse_file(clicon_handle h,
FILE *f,
char *name, /* just for errs */
parse_tree *pt,
cvec *globals)
FILE *f,
char *name, /* just for errs */
parse_tree *pt,
cvec *globals)
{
cligen_handle ch = cligen(h);
@ -182,7 +182,7 @@ cli_susp_hook(clicon_handle h,
return cligen_susp_hook(ch, fn);
}
int
cli_interrupt_hook(clicon_handle h,
cli_interrupt_hook(clicon_handle h,
cligen_interrupt_cb_t *fn)
{
cligen_handle ch = cligen(h);
@ -200,14 +200,16 @@ cli_nomatch(clicon_handle h)
}
int
cli_prompt_set(clicon_handle h, char *prompt)
cli_prompt_set(clicon_handle h,
char *prompt)
{
cligen_handle ch = cligen(h);
return cligen_prompt_set(ch, prompt);
}
int
cli_logsyntax_set(clicon_handle h, int status)
cli_logsyntax_set(clicon_handle h,
int status)
{
cligen_handle ch = cligen(h);
return cligen_logsyntax_set(ch, status);

View file

@ -163,6 +163,7 @@ static int
cli_terminate(clicon_handle h)
{
yang_stmt *yspec;
cvec *nsctx;
cxobj *x;
clicon_rpc_close_session(h);
@ -170,6 +171,8 @@ cli_terminate(clicon_handle h)
yspec_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
yspec_free(yspec);
if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
cli_plugin_finish(h);
@ -280,12 +283,12 @@ main(int argc, char **argv)
int logdst = CLICON_LOG_STDERR;
char *restarg = NULL; /* what remains after options */
yang_stmt *yspec;
yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */
struct passwd *pw;
char *str;
int tabmode;
char *dir;
uint32_t id = 0;
cvec *nsctx_global = NULL; /* Global namespace context */
/* Defaults */
once = 0;
@ -348,16 +351,13 @@ main(int argc, char **argv)
clicon_debug_init(debug, NULL);
/* Create top-level yang spec and store as option */
if ((yspecfg = yspec_new()) == NULL)
goto done;
/* Find and read configfile */
if (clicon_options_main(h, yspecfg) < 0){
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0){
if (help)
usage(h, argv[0]);
return -1;
}
clicon_config_yang_set(h, yspecfg);
/* Now rest of options */
opterr = 0;
optind = 1;
@ -513,6 +513,14 @@ main(int argc, char **argv)
if (netconf_module_load(h) < 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Create tree generated from dataspec. If no other trees exists, this is
* the only one.
* The following code creates the tree @datamodel
@ -533,7 +541,7 @@ main(int argc, char **argv)
cligen_tree_add(cli_cligen(h), treeref, pt);
if (printgen)
cligen_print(stdout, pt, 1);
cligen_print(stdout, pt, 1); /* pt_print */
}
/* Initialize cli syntax */

View file

@ -45,7 +45,6 @@
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <assert.h>
#include <dlfcn.h>
#include <dirent.h>
#include <libgen.h>
@ -209,27 +208,29 @@ clixon_str2fn(char *name,
return NULL;
}
/*! Append to syntax mode from file
* @param[in] h Clixon handle
* @param[in] filename Name of file where syntax is specified (in syntax-group dir)
* @param[in] dir Name of dir, or NULL
/*! Load a file containing syntax and append to specified modes, also load C plugin
* @param[in] h Clixon handle
* @param[in] filename Name of file where syntax is specified (in syntax-group dir)
* @param[in] dir Name of dir, or NULL
* @param[out] allpt Universal CLIgen parse tree: apply to all modes
*/
static int
cli_load_syntax(clicon_handle h,
const char *filename,
const char *dir)
cli_load_syntax_file(clicon_handle h,
const char *filename,
const char *dir,
parse_tree *ptall)
{
void *handle = NULL; /* Handle to plugin .so module */
char *mode = NULL; /* Name of syntax mode to append new syntax */
parse_tree pt = {0,};
int retval = -1;
FILE *f;
char filepath[MAXPATHLEN];
cvec *cvv = NULL;
char *prompt = NULL;
char **vec = NULL;
int i, nvec;
char *plgnam;
void *handle = NULL; /* Handle to plugin .so module */
char *mode = NULL; /* Name of syntax mode to append new syntax */
parse_tree pt = {0,};
int retval = -1;
FILE *f;
char filepath[MAXPATHLEN];
cvec *cvv = NULL;
char *prompt = NULL;
char **vec = NULL;
int i, nvec;
char *plgnam;
clixon_plugin *cp;
if (dir)
@ -253,10 +254,18 @@ cli_load_syntax(clicon_handle h,
goto done;
}
fclose(f);
/* Get CLICON specific global variables */
/* Get CLICON specific global variables:
* CLICON_MODE: which mode(s) this syntax applies to
* CLICON_PROMPT: Cli prompt in this mode
* CLICON_PLUGIN: Name of C API plugin
* Note: the base case is that it is:
* (1) a single mode or
* (2) "*" all modes or "m1:m2" - a list of modes
* but for (2), prompt and plgnam may have unclear semantics
*/
mode = cvec_find_str(cvv, "CLICON_MODE");
prompt = cvec_find_str(cvv, "CLICON_PROMPT");
plgnam = cvec_find_str(cvv, "CLICON_PLUGIN");
mode = cvec_find_str(cvv, "CLICON_MODE");
if (plgnam != NULL) { /* Find plugin for callback resolving */
if ((cp = clixon_plugin_find(h, plgnam)) != NULL)
@ -288,8 +297,19 @@ cli_load_syntax(clicon_handle h,
goto done;
}
}
/* Find all modes in CLICON_MODE string: where to append the pt syntax tree */
if ((vec = clicon_strsep(mode, ":", &nvec)) == NULL)
goto done;
if (nvec == 1 && strcmp(vec[0], "*") == 0){
/* Special case: Add this to all modes. Add to special "universal" syntax
* and add to all syntaxes after all files have been loaded. At this point
* all modes may not be known (not yet loaded)
*/
if (cligen_parsetree_merge(ptall, NULL, pt) < 0)
return -1;
}
else {
for (i = 0; i < nvec; i++) {
if (syntax_append(h,
cli_syntax(h),
@ -300,6 +320,7 @@ cli_load_syntax(clicon_handle h,
if (prompt)
cli_set_prompt(h, vec[i], prompt);
}
}
cligen_parsetree_free(pt, 1);
retval = 0;
@ -329,6 +350,7 @@ cli_syntax_load(clicon_handle h)
cligen_susp_cb_t *fns = NULL;
cligen_interrupt_cb_t *fni = NULL;
clixon_plugin *cp;
parse_tree ptall = {0,}; /* Universal CLIgen parse tree all modes */
/* Syntax already loaded. XXX should we re-load?? */
if ((stx = cli_syntax(h)) != NULL)
@ -347,30 +369,38 @@ cli_syntax_load(clicon_handle h)
cli_syntax_set(h, stx);
/* Load single specific clispec file */
if (clispec_file){
if (cli_load_syntax(h, clispec_file, NULL) < 0)
if (cli_load_syntax_file(h, clispec_file, NULL, &ptall) < 0)
goto done;
}
/* Load all clispec .cli files in directory */
if (clispec_dir){
/* load syntaxfiles */
/* Get directory list of files */
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0)
goto done;
/* Load the rest */
/* Load the syntax parse trees into cli_syntax stx structure */
for (i = 0; i < ndp; i++) {
clicon_debug(1, "DEBUG: Loading syntax '%.*s'",
(int)strlen(dp[i].d_name)-4, dp[i].d_name);
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
if (cli_load_syntax_file(h, dp[i].d_name, clispec_dir, &ptall) < 0)
goto done;
}
}
/* Did we successfully load any syntax modes? */
/* Were any syntax modes successfully loaded? If not, leave */
if (stx->stx_nmodes <= 0) {
retval = 0;
goto done;
}
/* Parse syntax tree for all modes */
/* Go thorugh all modes and :
* 1) Add the universal syntax
* 2) add syntax tree (of those modes - "activate" syntax from stx to CLIgen)
*/
m = stx->stx_modes;
do {
if (cligen_parsetree_merge(&m->csm_pt, NULL, ptall) < 0)
return -1;
if (gen_parse_tree(h, m) != 0)
goto done;
m = NEXTQ(cli_syntaxmode_t *, m);
@ -389,13 +419,13 @@ cli_syntax_load(clicon_handle h)
/* All good. We can now proudly return a new group */
retval = 0;
done:
if (retval != 0) {
clixon_plugin_exit(h);
cli_syntax_unload(h);
cli_syntax_set(h, NULL);
}
cligen_parsetree_free(ptall, 1);
if (dp)
free(dp);
return retval;

View file

@ -157,7 +157,7 @@ expand_dbvar(void *h,
/* Get configuration */
if (clicon_rpc_get_config(h, NULL, dbstr, xpath, nsc, &xt) < 0) /* XXX */
goto done;
if ((xe = xpath_first(xt, "/rpc-error")) != NULL){
if ((xe = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xe);
goto ok;
}
@ -204,12 +204,12 @@ expand_dbvar(void *h,
fprintf(stderr, "%s\n", reason);
goto done;
}
if ((xcur = xpath_first_nsc(xt, nsc, "%s", xpath)) == NULL){
if ((xcur = xpath_first(xt, nsc, "%s", xpath)) == NULL){
clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath);
goto done;
}
}
if (xpath_vec_nsc(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 0)
if (xpath_vec(xcur, nsc, "%s", &xvec, &xlen, xpathcur) < 0)
goto done;
/* Loop for inserting into commands cvec.
* Detect duplicates: for ordered-by system assume list is ordered, so you need
@ -486,7 +486,7 @@ cli_show_config1(clicon_handle h,
if (clicon_rpc_get(h, cbuf_get(cbxpath), nsc, CONTENT_ALL, -1, &xt) < 0)
goto done;
}
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
@ -634,12 +634,12 @@ show_conf_xpath(clicon_handle h,
goto done;
if (clicon_rpc_get_config(h, NULL, str, xpath, nsc, &xt) < 0)
goto done;
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
if (xpath_vec_nsc(xt, nsc, "%s", &xv, &xlen, xpath) < 0)
if (xpath_vec(xt, nsc, "%s", &xv, &xlen, xpath) < 0)
goto done;
for (i=0; i<xlen; i++)
xml_print(stdout, xv[i]);
@ -737,11 +737,11 @@ cli_show_auto1(clicon_handle h,
goto done;
}
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
if ((xerr = xpath_first(xt, NULL, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
if ((xp = xpath_first_nsc(xt, nsc, "%s", xpath)) != NULL)
if ((xp = xpath_first(xt, nsc, "%s", xpath)) != NULL)
/* Print configuration according to format */
switch (format){
case FORMAT_XML:

View file

@ -121,7 +121,6 @@ xml_filter_recursive(cxobj *xfilter,
int remove_s;
*remove_me = 0;
assert(xfilter && xparent && strcmp(xml_name(xfilter), xml_name(xparent))==0);
/* 1. Check selection */
if (xml_child_nr(xfilter) == 0)
goto match;

View file

@ -164,14 +164,14 @@ netconf_get_target(cxobj *xn,
cxobj *x;
char *target = NULL;
if ((x = xpath_first(xn, "%s", path)) != NULL){
if (xpath_first(x, "candidate") != NULL)
if ((x = xpath_first(xn, NULL, "%s", path)) != NULL){
if (xpath_first(x, NULL, "candidate") != NULL)
target = "candidate";
else
if (xpath_first(x, "running") != NULL)
if (xpath_first(x, NULL, "running") != NULL)
target = "running";
else
if (xpath_first(x, "startup") != NULL)
if (xpath_first(x, NULL, "startup") != NULL)
target = "startup";
}
return target;

View file

@ -98,7 +98,7 @@ netconf_hello_dispatch(cxobj *xn)
cxobj *xp;
int retval = -1;
if ((xp = xpath_first(xn, "//hello")) != NULL)
if ((xp = xpath_first(xn, NULL, "//hello")) != NULL)
retval = netconf_hello(xp);
return retval;
}
@ -147,7 +147,7 @@ netconf_input_packet(clicon_handle h,
goto done;
}
free(str0);
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
if ((xrpc=xpath_first(xreq, NULL, "//rpc")) != NULL){
isrpc++;
if (xml_spec_populate_rpc(h, xrpc, yspec) < 0)
goto done;
@ -160,7 +160,7 @@ netconf_input_packet(clicon_handle h,
}
}
else
if (xpath_first(xreq, "//hello") != NULL)
if (xpath_first(xreq, NULL, "//hello") != NULL)
;
else{
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropped");
@ -323,6 +323,7 @@ static int
netconf_terminate(clicon_handle h)
{
yang_stmt *yspec;
cvec *nsctx;
cxobj *x;
clixon_plugin_exit(h);
@ -332,6 +333,8 @@ netconf_terminate(clicon_handle h)
yspec_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
yspec_free(yspec);
if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
event_exit();
@ -392,9 +395,9 @@ main(int argc,
struct passwd *pw;
struct timeval tv = {0,}; /* timeout */
yang_stmt *yspec = NULL;
yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */
char *str;
uint32_t id;
cvec *nsctx_global = NULL; /* Global namespace context */
/* Create handle */
if ((h = clicon_handle_init()) == NULL)
@ -439,13 +442,10 @@ main(int argc,
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(debug, NULL);
/* Create configure yang-spec */
if ((yspecfg = yspec_new()) == NULL)
goto done;
/* Find and read configfile */
if (clicon_options_main(h, yspecfg) < 0)
/* Find, read and parse configfile */
if (clicon_options_main(h) < 0)
return -1;
clicon_config_yang_set(h, yspecfg);
/* Now rest of options */
optind = 1;
opterr = 0;
@ -554,6 +554,14 @@ main(int argc,
/* Add netconf yang spec, used by netconf client and as internal protocol */
if (netconf_module_load(h) < 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Call start function is all plugins before we go interactive */
if (clixon_plugin_start(h) < 0)
goto done;

View file

@ -80,6 +80,31 @@
</rpc>
*/
static int
netconf_get_config_subtree(clicon_handle h,
cxobj *xfilter,
cxobj **xret)
{
int retval = -1;
cxobj *xdata;
/* a subtree filter is comprised of zero or more element subtrees*/
if ((xdata = xpath_first(*xret, NULL, "/rpc-reply/data")) == NULL)
goto ok;
if (xml_filter(xfilter, xdata) < 0){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>applicatio</error-type>"
"<error-severity>error</error-severity>"
"<error-info>filtering</error-info>"
"</rpc-error></rpc-reply>");
}
ok:
retval = 0;
// done:
return retval;
}
/*! Get configuration
* @param[in] h Clicon handle
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
@ -136,37 +161,22 @@ netconf_get_config(clicon_handle h,
cxobj *xfilter; /* filter */
int retval = -1;
char *ftype = NULL;
cxobj *xfilterconf;
cxobj *xconf;
/* ie <filter>...</filter> */
if ((xfilter = xpath_first(xn, "filter")) != NULL)
if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL)
ftype = xml_find_value(xfilter, "type");
if (ftype == NULL || strcmp(ftype, "xpath")==0){
if (xfilter == NULL || ftype == NULL || strcmp(ftype, "xpath")==0){
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
}
else if (strcmp(ftype, "subtree")==0){
/* Default rfc filter is subtree. I prefer xpath and use it internally.
Get whole subtree and then filter aftwerwards. This is suboptimal.
Therefore please use xpath.
/* Get whole config first, then filter. This is suboptimal
*/
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
if (xfilter &&
(xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL &&
(xconf = xpath_first(*xret, "/rpc-reply/data")) != NULL){
/* xml_filter removes parts of xml tree not matching */
if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) ||
xml_filter(xfilterconf, xconf) < 0){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>applicatio</error-type>"
"<error-severity>error</error-severity>"
"<error-info>filtering</error-info>"
"</rpc-error></rpc-reply>");
}
}
/* Now filter on whole tree */
if (netconf_get_config_subtree(h, xfilter, xret) < 0)
goto done;
}
else{
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
@ -208,7 +218,7 @@ get_edit_opts(cxobj *xn,
cxobj *x;
char *optstr;
if ((x = xpath_first(xn, "test-option")) != NULL){
if ((x = xpath_first(xn, NULL, "test-option")) != NULL){
if ((optstr = xml_body(x)) != NULL){
if (strcmp(optstr, "test-then-set") == 0)
*testopt = TEST_THEN_SET;
@ -220,7 +230,7 @@ get_edit_opts(cxobj *xn,
goto parerr;
}
}
if ((x = xpath_first(xn, "error-option")) != NULL){
if ((x = xpath_first(xn, NULL, "error-option")) != NULL){
if ((optstr = xml_body(x)) != NULL){
if (strcmp(optstr, "stop-on-error") == 0)
*erropt = STOP_ON_ERROR;
@ -348,37 +358,22 @@ netconf_get(clicon_handle h,
cxobj *xfilter; /* filter */
int retval = -1;
char *ftype = NULL;
cxobj *xfilterconf;
cxobj *xconf;
/* ie <filter>...</filter> */
if ((xfilter = xpath_first(xn, "filter")) != NULL)
if ((xfilter = xpath_first(xn, NULL, "filter")) != NULL)
ftype = xml_find_value(xfilter, "type");
if (ftype == NULL || strcmp(ftype, "xpath")==0){
if (xfilter == NULL || ftype == NULL || strcmp(ftype, "xpath")==0){
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
}
else if (strcmp(ftype, "subtree")==0){
/* Default rfc filter is subtree. I prefer xpath and use it internally.
Get whole subtree and then filter aftwerwards. This is suboptimal.
Therefore please use xpath.
/* Get whole config + state first, then filter. This is suboptimal
*/
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done;
if (xfilter &&
(xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL &&
(xconf = xpath_first(*xret, "/rpc-reply/data")) != NULL){
/* xml_filter removes parts of xml tree not matching */
if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) ||
xml_filter(xfilterconf, xconf) < 0){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>applicatio</error-type>"
"<error-severity>error</error-severity>"
"<error-info>filtering</error-info>"
"</rpc-error></rpc-reply>");
}
}
/* Now filter on whole tree */
if (netconf_get_config_subtree(h, xfilter, xret) < 0)
goto done;
}
else{
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
@ -448,7 +443,7 @@ netconf_notification_cb(int s,
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:netconf:notification:1.0")) == NULL)
goto done;
if ((xn = xpath_first_nsc(xt, nsc, "notification")) == NULL)
if ((xn = xpath_first(xt, nsc, "notification")) == NULL)
goto ok;
/* create netconf message */
if ((cb = cbuf_new()) == NULL){
@ -500,7 +495,7 @@ netconf_create_subscription(clicon_handle h,
int s;
char *ftype;
if ((xfilter = xpath_first(xn, "//filter")) != NULL){
if ((xfilter = xpath_first(xn, NULL, "//filter")) != NULL){
if ((ftype = xml_find_value(xfilter, "type")) != NULL){
if (strcmp(ftype, "xpath") != 0){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
@ -516,7 +511,7 @@ netconf_create_subscription(clicon_handle h,
}
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, &s) < 0)
goto done;
if (xpath_first(*xret, "rpc-reply/rpc-error") != NULL)
if (xpath_first(*xret, NULL, "rpc-reply/rpc-error") != NULL)
goto ok;
if (event_reg_fd(s,
netconf_notification_cb,
@ -622,7 +617,7 @@ netconf_application_rpc(clicon_handle h,
*/
if (0)
if ((youtput = yang_find(yrpc, Y_OUTPUT, NULL)) != NULL){
xoutput=xpath_first(*xret, "/");
xoutput=xpath_first(*xret, NULL, "/");
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;

View file

@ -476,7 +476,7 @@ api_return_err(clicon_handle h,
clicon_debug(1, "%s", __FUNCTION__);
if ((cb = cbuf_new()) == NULL)
goto done;
if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){
if ((xtag = xpath_first(xerr, NULL, "//error-tag")) == NULL){
restconf_notfound(r);
goto ok;
}
@ -593,6 +593,7 @@ int
restconf_terminate(clicon_handle h)
{
yang_stmt *yspec;
cvec *nsctx;
cxobj *x;
int fs; /* fgcx socket */
@ -606,6 +607,8 @@ restconf_terminate(clicon_handle h)
yspec_free(yspec);
if ((yspec = clicon_config_yang(h)) != NULL)
yspec_free(yspec);
if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
cvec_free(nsctx);
if ((x = clicon_conf_xml(h)) != NULL)
xml_free(x);
clicon_handle_exit(h);

View file

@ -418,7 +418,7 @@ api_restconf(clicon_handle h,
else{
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -582,13 +582,13 @@ main(int argc,
char *dir;
int logdst = CLICON_LOG_SYSLOG;
yang_stmt *yspec = NULL;
yang_stmt *yspecfg = NULL; /* For config XXX clixon bug */
char *stream_path;
int finish = 0;
int start = 1;
char *str;
clixon_plugin *cp = NULL;
uint32_t id = 0;
cvec *nsctx_global = NULL; /* Global namespace context */
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -641,13 +641,10 @@ main(int argc,
goto done;
}
/* Create configure yang-spec */
if ((yspecfg = yspec_new()) == NULL)
goto done;
/* Find and read configfile */
if (clicon_options_main(h, yspecfg) < 0)
if (clicon_options_main(h) < 0)
goto done;
clicon_config_yang_set(h, yspecfg);
stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
/* Now rest of options, some overwrite option file */
optind = 1;
@ -760,6 +757,14 @@ main(int argc,
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done;
/* Here all modules are loaded
* Compute and set canonical namespace context
*/
if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
goto done;
if (clicon_nsctx_global_set(h, nsctx_global) < 0)
goto done;
/* Dump configuration options on debug */
if (debug)
clicon_option_dump(h, debug);

View file

@ -283,7 +283,7 @@ api_data_write(clicon_handle h,
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0)
goto done;
if (ret == 0){
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -296,7 +296,7 @@ api_data_write(clicon_handle h,
"candidate", cbuf_get(cbpath), nsc, &xret) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -341,7 +341,7 @@ api_data_write(clicon_handle h,
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -358,7 +358,7 @@ api_data_write(clicon_handle h,
if (data == NULL || strlen(data) == 0){
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -373,7 +373,7 @@ api_data_write(clicon_handle h,
if (xml_parse_string(data, yspec, &xdata0) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -392,7 +392,7 @@ api_data_write(clicon_handle h,
if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -401,7 +401,7 @@ api_data_write(clicon_handle h,
goto ok;
}
if (ret == 0){
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -422,7 +422,7 @@ api_data_write(clicon_handle h,
if (xml_child_nr(xdata0) != 1){
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -450,7 +450,7 @@ api_data_write(clicon_handle h,
if (ymoddata != ymodapi){
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -493,7 +493,7 @@ api_data_write(clicon_handle h,
if (strcmp(dname, xml_name(xbot))){
if (netconf_operation_failed_xml(&xerr, "protocol", "Not same symbol in api-path as data") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -518,7 +518,7 @@ api_data_write(clicon_handle h,
if (match_list_keys(ybot, xdata, xbot) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -542,7 +542,7 @@ api_data_write(clicon_handle h,
if (parbod == NULL || strcmp(parbod, xml_body(xdata))){
if (netconf_operation_failed_xml(&xerr, "protocol", "api-path keys do not match data keys") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -603,7 +603,7 @@ api_data_write(clicon_handle h,
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -616,14 +616,14 @@ api_data_write(clicon_handle h,
cprintf(cbx, "<commit/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
cprintf(cbx, "<discard-changes/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
@ -646,7 +646,7 @@ api_data_write(clicon_handle h,
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
/* If copy-config failed, log and ignore (already committed) */
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
}
@ -840,7 +840,7 @@ api_data_delete(clicon_handle h,
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -871,7 +871,7 @@ api_data_delete(clicon_handle h,
cprintf(cbx, "</edit-config></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -885,14 +885,14 @@ api_data_delete(clicon_handle h,
cprintf(cbx, "<commit/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", clicon_nacm_recovery_user(h));
cprintf(cbx, "<discard-changes/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
@ -915,7 +915,7 @@ api_data_delete(clicon_handle h,
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
/* If copy-config failed, log and ignore (already committed) */
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
}

View file

@ -130,7 +130,7 @@ api_data_get2(clicon_handle h,
if (netconf_bad_attribute_xml(&xerr, "application",
"<bad-attribute>content</bad-attribute>", "Unrecognized value of content attribute") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -152,7 +152,7 @@ api_data_get2(clicon_handle h,
if (netconf_bad_attribute_xml(&xerr, "application",
"<bad-attribute>depth</bad-attribute>", "Unrecognized value of depth attribute") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -172,7 +172,7 @@ api_data_get2(clicon_handle h,
if ((ret = api_path2xpath_cvv(pcvec, pi, yspec, cbpath, &nsc, &xerr)) < 0)
goto done;
if (ret == 0){
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -196,7 +196,7 @@ api_data_get2(clicon_handle h,
if (ret < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -218,7 +218,7 @@ api_data_get2(clicon_handle h,
}
#endif
/* Check if error return */
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -247,10 +247,10 @@ api_data_get2(clicon_handle h,
}
}
else{
if (xpath_vec_nsc(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath) < 0){
if (netconf_operation_failed_xml(&xerr, "application", clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}

View file

@ -147,7 +147,7 @@ api_data_post(clicon_handle h,
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -171,7 +171,7 @@ api_data_post(clicon_handle h,
if (data == NULL || strlen(data) == 0){
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -186,7 +186,7 @@ api_data_post(clicon_handle h,
if (xml_parse_string(data, NULL, &xdata0) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -208,7 +208,7 @@ api_data_post(clicon_handle h,
if ((ret = json_parse_str(data, yspec, &xdata0, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -217,7 +217,7 @@ api_data_post(clicon_handle h,
goto ok;
}
if (ret == 0){
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -237,7 +237,7 @@ api_data_post(clicon_handle h,
if (xml_child_nr(xdata0) != 1){
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -268,7 +268,7 @@ api_data_post(clicon_handle h,
if (ys_real_module(ydata) != ymoddata){
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -282,7 +282,7 @@ api_data_post(clicon_handle h,
if (ybot && yang_parent_get(ydata) != ybot){
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -338,7 +338,7 @@ api_data_post(clicon_handle h,
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, "//rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -352,14 +352,14 @@ api_data_post(clicon_handle h,
cprintf(cbx, "<commit/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
cbuf_reset(cbx);
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
cprintf(cbx, "<discard-changes/></rpc>");
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretdis, NULL) < 0)
goto done;
/* log errors from discard, but ignore */
if ((xpath_first(xretdis, "//rpc-error")) != NULL)
if ((xpath_first(xretdis, NULL, "//rpc-error")) != NULL)
clicon_log(LOG_WARNING, "%s: discard-changes failed which may lead candidate in an inconsistent state", __FUNCTION__);
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0) /* Use original xe */
goto done;
@ -382,7 +382,7 @@ api_data_post(clicon_handle h,
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
goto done;
/* If copy-config failed, log and ignore (already committed) */
if ((xe = xpath_first(xretcom, "//rpc-error")) != NULL){
if ((xe = xpath_first(xretcom, NULL, "//rpc-error")) != NULL){
clicon_log(LOG_WARNING, "%s: copy-config running->startup failed", __FUNCTION__);
}
@ -466,7 +466,7 @@ api_operations_post_input(clicon_handle h,
if (xml_parse_string(data, yspec, &xdata) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -479,7 +479,7 @@ api_operations_post_input(clicon_handle h,
if ((ret = json_parse_str(data, yspec, &xdata, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clicon_err_reason) < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -488,7 +488,7 @@ api_operations_post_input(clicon_handle h,
goto fail;
}
if (ret == 0){
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -527,7 +527,7 @@ api_operations_post_input(clicon_handle h,
else
if (netconf_malformed_message_xml(&xerr, "restconf RPC has malformed input statement (multiple or not called input)") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -601,7 +601,7 @@ api_operations_post_output(clicon_handle h,
xml_child_nr_type(xret, CX_ELMNT) != 1){
if (netconf_malformed_message_xml(&xerr, "restconf RPC does not have single input") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -639,7 +639,7 @@ api_operations_post_output(clicon_handle h,
(ret = xml_yang_validate_add(h, xoutput, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if ((xe = xpath_first(xerr, "rpc-reply/rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-reply/rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -770,7 +770,7 @@ api_operations_post(clicon_handle h,
if (oppath == NULL || strcmp(oppath,"/")==0){
if (netconf_operation_failed_xml(&xerr, "protocol", "Operation name expected") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -789,7 +789,7 @@ api_operations_post(clicon_handle h,
if ((ys = yang_find(yspec, Y_MODULE, prefix)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -800,7 +800,7 @@ api_operations_post(clicon_handle h,
if ((yrpc = yang_find(ys, Y_RPC, id)) == NULL){
if (netconf_missing_element_xml(&xerr, "application", id, "RPC not defined") < 0)
goto done;
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -826,7 +826,7 @@ api_operations_post(clicon_handle h,
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if ((xe = xpath_first(xerr, "rpc-error")) == NULL){
if ((xe = xpath_first(xerr, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto done;
}
@ -867,7 +867,7 @@ api_operations_post(clicon_handle h,
if ((ret = xml_yang_validate_rpc(h, xtop, &xret)) < 0)
goto done;
if (ret == 0){
if ((xe = xpath_first(xret, "rpc-error")) == NULL){
if ((xe = xpath_first(xret, NULL, "rpc-error")) == NULL){
clicon_err(OE_XML, EINVAL, "rpc-error not found (internal error)");
goto ok;
}
@ -900,7 +900,7 @@ api_operations_post(clicon_handle h,
if (xml_parse_string(cbuf_get(cbret), NULL, &xret) < 0)
goto done;
/* Local error: return it and quit */
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -909,7 +909,7 @@ api_operations_post(clicon_handle h,
else { /* Send to backend */
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
goto done;
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;

View file

@ -187,11 +187,11 @@ restconf_stream_cb(int s,
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if ((xn = xpath_first(xtop, "notification")) == NULL)
if ((xn = xpath_first(xtop, NULL, "notification")) == NULL)
goto ok;
#ifdef notused
xt = xpath_first(xn, "eventTime");
if ((xe = xpath_first(xn, "event")) == NULL) /* event can depend on yang? */
xt = xpath_first(xn, NULL, "eventTime");
if ((xe = xpath_first(xn, NULL, "event")) == NULL) /* event can depend on yang? */
goto ok;
if (xt)
@ -268,7 +268,7 @@ restconf_stream(clicon_handle h,
cprintf(cb, "</create-subscription></rpc>]]>]]>");
if (clicon_rpc_netconf(h, cbuf_get(cb), &xret, &s) < 0)
goto done;
if ((xe = xpath_first(xret, "rpc-reply/rpc-error")) != NULL){
if ((xe = xpath_first(xret, NULL, "rpc-reply/rpc-error")) != NULL){
if (api_return_err(h, r, xe, pretty, media_out, 0) < 0)
goto done;
goto ok;
@ -417,7 +417,7 @@ api_stream(clicon_handle h,
else{
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
if (api_return_err(h, r, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;

59
configure vendored
View file

@ -621,7 +621,7 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
LIBOBJS
STD_YANG_INSTALLDIR
OPT_YANG_INSTALLDIR
YANG_INSTALLDIR
EGREP
GREP
@ -633,7 +633,7 @@ YACC
CPP
wwwuser
wwwdir
enable_stdyangs
enable_optyangs
with_restconf
SH_SUFFIX
CLIXON_DEFAULT_CONFIG
@ -711,14 +711,14 @@ ac_user_opts='
enable_option_checking
enable_debug
with_cligen
enable_stdyangs
enable_optyangs
enable_publish
with_restconf
with_wwwuser
with_configfile
with_libxml2
with_yang_installdir
with_std_yang_installdir
with_opt_yang_installdir
'
ac_precious_vars='build_alias
host_alias
@ -1355,8 +1355,8 @@ Optional Features:
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-debug Build with debug symbols, default: no
--disable-stdyangs Include standard yang files in clixon install,
default: yes
--enable-optyangs Include standard yang files in clixon install,
default: no
--enable-publish Enable publish of notification streams using SSE and
curl
@ -1369,7 +1369,7 @@ Optional Packages:
--with-configfile=FILE set default path to config file
--with-libxml2 use gnome/libxml2 regex engine
--with-yang-installdir=DIR Install Clixon yang files here (default: ${prefix}/share/clixon)
--with-std-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon)
--with-opt-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon)
Some influential environment variables:
CC C compiler command
@ -2172,9 +2172,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="4"
CLIXON_VERSION_MINOR="2"
CLIXON_VERSION_MINOR="3"
CLIXON_VERSION_PATCH="0"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\""
# Check CLIgen
if test "$prefix" = "NONE"; then
@ -4247,22 +4247,22 @@ fi
# Disable/enable standard Yang files.
# If enable - include yang/standard/*.yang in clixon yang files (default)
# If disable - get standard yang files from elsewhere
# Check whether --enable-stdyangs was given.
if test "${enable_stdyangs+set}" = set; then :
enableval=$enable_stdyangs;
# Check whether --enable-optyangs was given.
if test "${enable_optyangs+set}" = set; then :
enableval=$enable_optyangs;
if test "$enableval" = no; then
enable_stdyangs=no
enable_optyangs=no
else
enable_stdyangs=yes
enable_optyangs=yes
fi
else
enable_stdyangs=yes
enable_optyangs=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: stdyangs is $enable_stdyangs" >&5
$as_echo "stdyangs is $enable_stdyangs" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: optyangs is $enable_optyangs" >&5
$as_echo "optyangs is $enable_optyangs" >&6; }
# Experimental: Curl publish notification stream to eg Nginx nchan.
# Check whether --enable-publish was given.
@ -4933,8 +4933,8 @@ $as_echo "Have getsockopt SO_PEERCRED" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
# YANG_INSTALLDIR is where clixon installs the Clixon yang files
# (the files in in yang/clixon)
# YANG_INSTALLDIR is where clixon installs the Clixon yang files and mandatory
# standard yang files: the files in in yang/clixon and yang/mandatory
# Each application designer may need to place YANG_INSTALLDIR in their config:
# <CLICON_YANG_DIR>$YANG_INSTALLDIR</CLICON_YANG_DIR>
@ -4950,22 +4950,22 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Clixon yang files are installed in ${YANG_INSTALLDIR}" >&5
$as_echo "Clixon yang files are installed in ${YANG_INSTALLDIR}" >&6; }
# STD_YANG_INSTALLDIR is where clixon installs standard yang files
# (the files in in yang/standard)
# OPT_YANG_INSTALLDIR is where clixon installs standard yang files
# ( the files in in yang/standard)
# that Clixon needs to run (or examples rely on). These may be retreived from
# elsewhere (eg yangmodels repo)
# Check whether --with-std-yang-installdir was given.
if test "${with_std_yang_installdir+set}" = set; then :
withval=$with_std_yang_installdir; STD_YANG_INSTALLDIR="$withval"
# Check whether --with-opt-yang-installdir was given.
if test "${with_opt_yang_installdir+set}" = set; then :
withval=$with_opt_yang_installdir; OPT_YANG_INSTALLDIR="$withval"
else
STD_YANG_INSTALLDIR="${prefix}/share/clixon"
OPT_YANG_INSTALLDIR="${prefix}/share/clixon"
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Standard yang files are installed in ${STD_YANG_INSTALLDIR}" >&5
$as_echo "Standard yang files are installed in ${STD_YANG_INSTALLDIR}" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Optional yang files are installed in ${OPT_YANG_INSTALLDIR} (if enabled)" >&5
$as_echo "Optional yang files are installed in ${OPT_YANG_INSTALLDIR} (if enabled)" >&6; }
# Default location for config file
@ -4976,7 +4976,7 @@ _ACEOF
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/hello/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/Makefile"
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/hello/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile yang/optional/Makefile doc/Makefile test/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@ -5692,7 +5692,8 @@ do
"util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;;
"yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;;
"yang/clixon/Makefile") CONFIG_FILES="$CONFIG_FILES yang/clixon/Makefile" ;;
"yang/standard/Makefile") CONFIG_FILES="$CONFIG_FILES yang/standard/Makefile" ;;
"yang/mandatory/Makefile") CONFIG_FILES="$CONFIG_FILES yang/mandatory/Makefile" ;;
"yang/optional/Makefile") CONFIG_FILES="$CONFIG_FILES yang/optional/Makefile" ;;
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;

View file

@ -43,9 +43,9 @@ AC_INIT(lib/clixon/clixon.h.in)
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="4"
CLIXON_VERSION_MINOR="2"
CLIXON_VERSION_MINOR="3"
CLIXON_VERSION_PATCH="0"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}.PRE\""
# Check CLIgen
if test "$prefix" = "NONE"; then
@ -89,7 +89,7 @@ AC_SUBST(CLIXON_DEFAULT_CONFIG)
AC_SUBST(LIBS)
AC_SUBST(SH_SUFFIX)
AC_SUBST(with_restconf) # If yes, compile apps/restconf
AC_SUBST(enable_stdyangs)
AC_SUBST(enable_optyangs)
AC_SUBST(wwwdir,/www-data)
AC_SUBST(wwwuser,www-data)
@ -151,16 +151,16 @@ fi
# Disable/enable standard Yang files.
# If enable - include yang/standard/*.yang in clixon yang files (default)
# If disable - get standard yang files from elsewhere
AC_ARG_ENABLE(stdyangs, AS_HELP_STRING([--disable-stdyangs],[Include standard yang files in clixon install, default: yes]),[
AC_ARG_ENABLE(optyangs, AS_HELP_STRING([--enable-optyangs],[Include optional yang files for examples and testing in clixon install, default: no]),[
if test "$enableval" = no; then
enable_stdyangs=no
enable_optyangs=no
else
enable_stdyangs=yes
enable_optyangs=yes
fi
],
[ enable_stdyangs=yes])
[ enable_optyangs=no])
AC_MSG_RESULT(stdyangs is $enable_stdyangs)
AC_MSG_RESULT(optyangs is $enable_optyangs)
# Experimental: Curl publish notification stream to eg Nginx nchan.
AC_ARG_ENABLE(publish, AS_HELP_STRING([--enable-publish],[Enable publish of notification streams using SSE and curl]),[
@ -229,8 +229,8 @@ AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versi
AC_TRY_COMPILE([#include <sys/socket.h>], [getsockopt(1, SOL_SOCKET, SO_PEERCRED, 0, 0);], [AC_DEFINE(HAVE_SO_PEERCRED, 1, [Have getsockopt SO_PEERCRED])
AC_MSG_RESULT(Have getsockopt SO_PEERCRED)])
# YANG_INSTALLDIR is where clixon installs the Clixon yang files
# (the files in in yang/clixon)
# YANG_INSTALLDIR is where clixon installs the Clixon yang files and mandatory
# standard yang files: the files in in yang/clixon and yang/mandatory
# Each application designer may need to place YANG_INSTALLDIR in their config:
# <CLICON_YANG_DIR>$YANG_INSTALLDIR</CLICON_YANG_DIR>
AC_ARG_WITH(yang-installdir,
@ -241,17 +241,17 @@ AC_ARG_WITH(yang-installdir,
AC_SUBST(YANG_INSTALLDIR)
AC_MSG_RESULT(Clixon yang files are installed in ${YANG_INSTALLDIR})
# STD_YANG_INSTALLDIR is where clixon installs standard yang files
# (the files in in yang/standard)
# OPT_YANG_INSTALLDIR is where clixon installs standard yang files
# ( the files in in yang/standard)
# that Clixon needs to run (or examples rely on). These may be retreived from
# elsewhere (eg yangmodels repo)
AC_ARG_WITH(std-yang-installdir,
[ --with-std-yang-installdir=DIR Install standard yang files here (default: ${prefix}/share/clixon) ],
[STD_YANG_INSTALLDIR="$withval"],
[STD_YANG_INSTALLDIR="${prefix}/share/clixon"]
AC_ARG_WITH(opt-yang-installdir,
[ --with-opt-yang-installdir=DIR Install optional yang files here (default: ${prefix}/share/clixon) ],
[OPT_YANG_INSTALLDIR="$withval"],
[OPT_YANG_INSTALLDIR="${prefix}/share/clixon"]
)
AC_SUBST(STD_YANG_INSTALLDIR)
AC_MSG_RESULT(Standard yang files are installed in ${STD_YANG_INSTALLDIR})
AC_SUBST(OPT_YANG_INSTALLDIR)
AC_MSG_RESULT(Optional yang files are installed in ${OPT_YANG_INSTALLDIR} (if enabled))
# Default location for config file
AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${CLIXON_DEFAULT_CONFIG}",[Location for apps to find default config file])
@ -280,7 +280,8 @@ AC_OUTPUT(Makefile
util/Makefile
yang/Makefile
yang/clixon/Makefile
yang/standard/Makefile
yang/mandatory/Makefile
yang/optional/Makefile
doc/Makefile
test/Makefile
)

View file

@ -122,6 +122,12 @@ After release:
```
* Run autoconf
Create release branch:
```
git checkout -b release-4.2 4.2.0
git push origin release-4.2
```
## Use of constants etc
Use MAXPATHLEN (not PATH_MAX)
Use MAXPATHLEN (not PATH_MAX)

View file

@ -77,12 +77,14 @@ One of the examples is [a hello world example](../example/hello). Please start w
## How do you build and install Clixon?
Clixon:
```
./configure;
./configure --enable-optyang;
make;
sudo make install;
sudo make install-include
```
The main example:
(note: optyang enable only if you need to run the main example, otherwise it is not necessary).
The main example:
```
cd example;
make;

View file

@ -62,7 +62,7 @@ RUN adduser -D -H www-data
RUN apk add --update nginx
# Configure, build and install clixon
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-wwwuser=www-data
RUN ./configure --prefix=/clixon/build --with-cligen=/clixon/build --with-wwwuser=www-data --enable-optyangs
RUN make
RUN make install
RUN make install-include

View file

@ -23,7 +23,7 @@ PORT=${PORT:-80}
STORE=${STORE:-}
CONFIG0=$(cat <<EOF
<config>
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>/usr/local/etc/example.xml</CLICON_CONFIGFILE>
<CLICON_FEATURE>*:*</CLICON_FEATURE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
@ -42,7 +42,7 @@ CONFIG0=$(cat <<EOF
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
<CLICON_NACM_MODE>disabled</CLICON_NACM_MODE>
</config>
</clixon-config>
EOF
)

View file

@ -82,7 +82,7 @@ Start restconf daemon
Start sending restconf commands (using Curl):
```
olof@vandal> curl -X POST http://localhost/restconf/data -d '{"clixon-hello:hello":{"world":null}}'
olof@vandal> curl -X POST http://localhost/restconf/data -H "Content-Type: application/yang-data+json" -d '{"clixon-hello:hello":{"world":null}}'
olof@vandal> curl -X GET http://localhost/restconf/data
{
"data": {

View file

@ -79,7 +79,7 @@ all: $(PLUGINS)
CLISPECS = $(APPNAME)_cli.cli
YANGSPECS = clixon-example@2019-07-23.yang
YANGSPECS = clixon-example@2019-11-05.yang
# Backend plugin
BE_SRC = $(APPNAME)_backend.c

View file

@ -30,13 +30,14 @@ This directory contains a Clixon example used primarily for testing. It can be u
* `example_netconf.c` Netconf callback plugin
* `Makefile.in` Example makefile where plugins are built and installed
## Compile and run
Before you start,
* You must configure with: `--enable-optyangs` to run the main example.
* Make [group setup](../../doc/FAQ.md#do-i-need-to-setup-anything-important)
* Setup [restconf](../../doc/FAQ.md#how-do-i-use-restconf)
```
cd example
make && sudo make install
@ -206,7 +207,7 @@ clixon_netconf -qf /usr/local/etc/example.xml
Restconf (assuming nginx started):
```
sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data&
curl -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":"ipv4"}}'
curl -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"ipv4"}}'
{
"clixon-example:output": {
"x": "ipv4",

View file

@ -2,6 +2,9 @@ module clixon-example {
yang-version 1.1;
namespace "urn:example:clixon";
prefix ex;
revision 2019-11-05 {
description "Augment interface. Released in Clixon 4.3.0";
}
revision 2019-07-23 {
description "Extension e4. Released in Clixon 4.1.0";
}
@ -42,6 +45,18 @@ module clixon-example {
type string;
}
}
augment "/if:interfaces/if:interface" {
container my-status {
config false;
description "For testing augment+state";
leaf int {
type int32;
}
leaf str {
type string;
}
}
}
/* yang extension implemented by the example backend code. */
extension e4 {
description

View file

@ -317,30 +317,23 @@ example_statedata(clicon_handle h,
* state information. In this case adding dummy interface operation state
* to configured interfaces.
* Get config according to xpath */
if (xmldb_get0(h, "running", nsc, xpath, 1, &xt, NULL) < 0)
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
goto done;
if (yang_find_module_by_namespace(yspec, "urn:ietf:params:xml:ns:yang:ietf-interfaces") != NULL){
/* Here a separate namespace context nsc1 is created. The original nsc
* created by the system cannot be used trivially, since we dont know
* the prefixes, although we could by a complex mechanism find the prefix
* (if it exists) and use that when creating our xpath.
* But it is easier creating a new namespace context nsc1.
*/
if ((nsc1 = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-interfaces")) == NULL)
goto done;
if (xpath_vec_nsc(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
goto done;
if (xlen){
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
for (i=0; i<xlen; i++){
name = xml_body(xvec[i]);
cprintf(cb, "<interface><name>%s</name><oper-status>up</oper-status></interface>", name);
}
cprintf(cb, "</interfaces>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
if (xmldb_get0(h, "running", nsc1, "/interfaces/interface/name", 1, &xt, NULL) < 0)
goto done;
if (xpath_vec(xt, nsc1, "/interfaces/interface/name", &xvec, &xlen) < 0)
goto done;
if (xlen){
cprintf(cb, "<interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">");
for (i=0; i<xlen; i++){
name = xml_body(xvec[i]);
cprintf(cb, "<interface xmlns:ex=\"urn:example:clixon\"><name>%s</name><type>ex:eth</type><oper-status>up</oper-status>", name);
cprintf(cb, "<ex:my-status><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status>");
cprintf(cb, "</interface>");
}
cprintf(cb, "</interfaces>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
}
/* State in test_yang.sh , test_restconf.sh and test_order.sh */
if (yang_find_module_by_namespace(yspec, "urn:example:clixon") != NULL){
@ -356,32 +349,14 @@ example_statedata(clicon_handle h,
* (2) event-count is XOR on name, so is not 42 and 4
*/
if (yang_find_module_by_namespace(yspec, "urn:example:events") != NULL){
if ((nsc2 = xml_nsctx_init(NULL, "urn:example:events")) == NULL)
cbuf_reset(cb);
cprintf(cb, "<events xmlns=\"urn:example:events\">");
cprintf(cb, "<event><name>interface-down</name><event-count>90</event-count></event>");
cprintf(cb, "<event><name>interface-up</name><event-count>77</event-count></event>");
cprintf(cb, "</events>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
if (xvec){
free(xvec);
xvec = NULL;
}
if (xpath_vec_nsc(xt, nsc2, "/events/event/name", &xvec, &xlen) < 0)
goto done;
if (xlen){
int j = 0;
int c;
cprintf(cb, "<events xmlns=\"urn:example:events\">");
for (i=0; i<xlen; i++){
name = xml_body(xvec[i]);
c = 0;
for (j=0; j<strlen(name); j++)
c ^= name[j];
cprintf(cb, "<event><name>%s</name><event-count>%d</event-count></event>", name, c);
}
cprintf(cb, "</events>");
if (xml_parse_string(cbuf_get(cb), NULL, &xstate) < 0)
goto done;
}
}
ok:
retval = 0;
done:
@ -500,7 +475,7 @@ upgrade_2016(clicon_handle h,
if ((name = xml_find_body(xi, "name")) == NULL)
continue; /* shouldnt happen */
/* Get corresponding /interfaces/interface entry */
xif = xpath_first(xt, "/interfaces/interface[name=\"%s\"]", name);
xif = xpath_first(xt, NULL, "/interfaces/interface[name=\"%s\"]", name);
/* - Move /if:interfaces-state/if:interface/if:admin-status to
* /if:interfaces/if:interface/ */
if ((x = xml_find(xi, "admin-status")) != NULL && xif){
@ -603,7 +578,7 @@ upgrade_2018(clicon_handle h,
/* Change type /interfaces/interface/statistics/in-octets to
* decimal64 with fraction-digits 3 and divide values with 1000
*/
if ((x = xpath_first(xi, "statistics/in-octets")) != NULL){
if ((x = xpath_first(xi, NULL, "statistics/in-octets")) != NULL){
if ((xb = xml_body_get(x)) != NULL){
uint64_t u64;
cbuf *cb = cbuf_new();

View file

@ -87,7 +87,7 @@ nacm_validate(clicon_handle h,
if (_transaction_log){
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
if (_transaction_error_toggle==0 &&
xpath_first(transaction_target(td), "%s", _transaction_xpath)){
xpath_first(transaction_target(td), NULL, "%s", _transaction_xpath)){
_transaction_error_toggle=1; /* toggle if triggered */
clicon_err(OE_XML, 0, "User error");
return -1; /* induce fail */
@ -116,7 +116,7 @@ nacm_commit(clicon_handle h,
if (_transaction_log){
transaction_log(h, td, LOG_NOTICE, __FUNCTION__);
if (_transaction_error_toggle==1 &&
xpath_first(target, "%s", _transaction_xpath)){
xpath_first(target, NULL, "%s", _transaction_xpath)){
_transaction_error_toggle=0; /* toggle if triggered */
clicon_err(OE_XML, 0, "User error");
return -1; /* induce fail */

View file

@ -43,7 +43,6 @@
#include <sys/time.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <fnmatch.h> /* matching strings */
#include <signal.h> /* matching strings */
/* clicon */
@ -110,7 +109,7 @@ example_client_rpc(clicon_handle h,
/* Send to backend */
if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
if ((xerr = xpath_first(xret, NULL, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}

View file

@ -51,20 +51,14 @@
#include <sys/param.h> /* MAXPATHLEN */
/*
* CLIXON version macros
* CLIXON version macros, set in configure and resolved when expanding to
* clixon.h
*/
#undef CLIXON_VERSION_STRING
#undef CLIXON_VERSION_MAJOR
#undef CLIXON_VERSION_MINOR
#undef CLIXON_VERSION_PATCH
/*
* Use this constant to disable some prototypes that should not be visible outside the lib.
* This is an alternative to use separate internal include files.
*/
#define LIBCLIXON_API 1
#include <clixon/clixon_sig.h>
#include <clixon/clixon_uid.h>
#include <clixon/clixon_err.h>

View file

@ -40,12 +40,6 @@
#ifndef _CLIXON_DATA_H_
#define _CLIXON_DATA_H_
/*
* Constants
*/
/* default group membership to access config unix socket */
#define CLICON_SOCK_GROUP "clicon"
/*
* Types
*/
@ -61,12 +55,15 @@ typedef struct {
yang_stmt * clicon_dbspec_yang(clicon_handle h);
int clicon_dbspec_yang_set(clicon_handle h, yang_stmt *ys);
cxobj * clicon_nacm_ext(clicon_handle h);
int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
yang_stmt * clicon_config_yang(clicon_handle h);
int clicon_config_yang_set(clicon_handle h, yang_stmt *ys);
cvec *clicon_nsctx_global_get(clicon_handle h);
int clicon_nsctx_global_set(clicon_handle h, cvec *nsctx);
cxobj * clicon_nacm_ext(clicon_handle h);
int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
cxobj *clicon_conf_xml(clicon_handle h);
int clicon_conf_xml_set(clicon_handle h, cxobj *x);

View file

@ -49,9 +49,9 @@ int xmldb_validate_db(const char *db);
int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h);
/* in clixon_datastore_read.[ch] */
int xmldb_get(clicon_handle h, const char *db, char *xpath, cxobj **xtop);
int xmldb_get(clicon_handle h, const char *db, cvec *nsc, char *xpath, cxobj **xtop);
int xmldb_get0(clicon_handle h, const char *db,
cvec *nc, char *xpath,
cvec *nsc, char *xpath,
int copy, cxobj **xtop, modstate_diff_t *msd);
int xmldb_get0_clear(clicon_handle h, cxobj *x);
int xmldb_get0_free(clicon_handle h, cxobj **xp);

View file

@ -90,7 +90,7 @@ int netconf_trymerge(cxobj *x, yang_stmt *yspec, cxobj **xret);
int netconf_module_features(clicon_handle h);
int netconf_module_load(clicon_handle h);
char *netconf_db_find(cxobj *xn, char *name);
int netconf_err2cb(cxobj *xerr, cbuf **cberr);
int netconf_err2cb(cxobj *xerr, cbuf *cberr);
const netconf_content netconf_content_str2int(char *str);
const char *netconf_content_int2str(netconf_content nr);
int netconf_hello_server(clicon_handle h, cbuf *cb, uint32_t session_id);

View file

@ -38,12 +38,6 @@
#ifndef _CLIXON_OPTIONS_H_
#define _CLIXON_OPTIONS_H_
/*
* Constants
*/
/* default group membership to access config unix socket */
#define CLICON_SOCK_GROUP "clicon"
/*
* Types
*/
@ -115,7 +109,7 @@ int clicon_option_dump(clicon_handle h, int dblevel);
int clicon_option_add(clicon_handle h, char *name, char *value);
/* Initialize options: set defaults, read config-file, etc */
int clicon_options_main(clicon_handle h, yang_stmt *yspec);
int clicon_options_main(clicon_handle h);
/*! Check if a clicon option has a value */
int clicon_option_exists(clicon_handle h, const char *name);

View file

@ -69,14 +69,17 @@ struct clicon_msg *clicon_msg_encode(uint32_t id, char *format, ...);
#endif
int clicon_msg_decode(struct clicon_msg *msg, yang_stmt *yspec, uint32_t *id, cxobj **xml);
int clicon_connect_unix(char *sockpath);
int clicon_connect_unix(clicon_handle h, char *sockpath);
int clicon_rpc_connect_unix(struct clicon_msg *msg,
int clicon_rpc_connect_unix(clicon_handle h,
struct clicon_msg *msg,
char *sockpath,
char **ret,
int *sock0);
int clicon_rpc_connect_inet(struct clicon_msg *msg,
int clicon_rpc_connect_inet(clicon_handle h,
struct clicon_msg *msg,
char *dst,
uint16_t port,
char **ret,

View file

@ -44,7 +44,7 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
int *sock0);
int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp);
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
int clicon_rpc_generate_error(char *format, cxobj *xerr);
int clicon_rpc_generate_error(const char *format, cxobj *xerr);
int clicon_rpc_get_config(clicon_handle h, char *username, char *db, char *xpath, cvec *nsc, cxobj **xret);
int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op,
char *xml);

View file

@ -58,6 +58,10 @@ typedef struct map_str2int map_str2int;
/*! A malloc version that aligns on 4 bytes. To avoid warning from valgrind */
#define align4(s) (((s)/4)*4 + 4)
/* Required for the inline to compile */
#include <stdlib.h>
#include <string.h>
/*! A strdup version that aligns on 4 bytes. To avoid warning from valgrind */
static inline char * strdup4(char *str)
{

View file

@ -129,7 +129,7 @@ int xml_flag_reset(cxobj *xn, uint16_t flag);
char *xml_value(cxobj *xn);
int xml_value_set(cxobj *xn, char *val);
char *xml_value_append(cxobj *xn, char *val);
int xml_value_append(cxobj *xn, char *val);
enum cxobj_type xml_type(cxobj *xn);
int xml_type_set(cxobj *xn, enum cxobj_type type);

View file

@ -78,6 +78,7 @@ int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
int xml2xpath(cxobj *x, char **xpath);
int xml2api_path_1(cxobj *x, cbuf *cb);
int check_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_enum_int_value(cxobj *node, int32_t *val);

View file

@ -55,5 +55,6 @@ int xml_nsctx_get_prefix(cvec *cvv, char *namespace, char **prefix);
int xml_nsctx_add(cvec *nsc, char *prefix, char *namespace);
int xml_nsctx_node(cxobj *x, cvec **ncp);
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
#endif /* _CLIXON_XML_NSCTX_H */

View file

@ -106,6 +106,7 @@ struct xpath_tree{
enum xp_type xs_type;
int xs_int; /* step-> axis-type */
double xs_double;
char *xs_strnr; /* original string xs_double: numeric value */
char *xs_s0;
char *xs_s1;
struct xpath_tree *xs_c0; /* child 0 */
@ -142,21 +143,13 @@ int xpath_vec_flag(cxobj *xcur, cvec *nsc, char *xpformat, uint16_t flags,
* If you do not know what a namespace context is, see README.md#xml-and-xpath
*/
#if defined(__GNUC__) && __GNUC__ >= 3
cxobj *xpath_first_nsc(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4)));
int xpath_vec_nsc(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6)));
cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *xpformat, ...) __attribute__ ((format (printf, 3, 4)));
int xpath_vec(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6)));
#else
cxobj *xpath_first_nsc(cxobj *xcur, cvec *nsc, char *xpformat, ...);
int xpath_vec_nsc(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...);
cxobj *xpath_first(cxobj *xcur, cvec *nsc, char *xpformat, ...);
int xpath_vec(cxobj *xcur, cvec *nsc, char *xpformat, cxobj ***vec, size_t *veclen, ...);
#endif
/* Functions with nsc == NULL (implicit xpath context). */
#if defined(__GNUC__) && __GNUC__ >= 3
cxobj *xpath_first(cxobj *xcur, char *xpformat, ...) __attribute__ ((format (printf, 2, 3)));
int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5)));
#else
cxobj *xpath_first(cxobj *xcur, char *xpformat, ...);
int xpath_vec(cxobj *xcur, char *xpformat, cxobj ***vec, size_t *veclen, ...);
#endif
int xpath2canonical(char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1);
#endif /* _CLIXON_XPATH_H */

View file

@ -73,8 +73,11 @@
#include "clixon_xpath.h"
#include "clixon_data.h"
/*! Get YANG specification for application
/*! Get YANG specification for application specs
* Must use hash functions directly since they are not strings.
* @param[in] h Clicon handle
* @retval yspec Yang spec
* @see clicon_config_yang for the configuration yang
*/
yang_stmt *
clicon_dbspec_yang(clicon_handle h)
@ -88,8 +91,10 @@ clicon_dbspec_yang(clicon_handle h)
return NULL;
}
/*! Set yang specification for application
* ys must be a malloced pointer
/*! Set yang specification for application specifications
* @param[in] h Clicon handle
* @param[in] yspec Yang spec (malloced pointer)
* @see clicon_config_yang_set for the configuration yang
*/
int
clicon_dbspec_yang_set(clicon_handle h,
@ -105,6 +110,82 @@ clicon_dbspec_yang_set(clicon_handle h,
return 0;
}
/*! Get YANG specification for clixon config (separate from application yangs)
* @param[in] h Clicon handle
* @retval yspec Yang spec
* @see clicon_dbspec_yang for the application specs
*/
yang_stmt *
clicon_config_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = clicon_hash_value(cdat, "control_yang", &len)) != NULL)
return *(yang_stmt **)p;
return NULL;
}
/*! Set yang specification for configuration
* @param[in] h Clicon handle
* @param[in] yspec Yang spec (malloced pointer)
* @see clicon_dbspec_yang_set for the application specs
*/
int
clicon_config_yang_set(clicon_handle h,
yang_stmt *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (clicon_hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
/*! Get Global "canonical" namespace context
* Canonical: use prefix and namespace specified in the yang modules.
* @param[in] h Clicon handle
* @retval nsctx Namespace context (malloced)
* @code
* cvec *nsctx;
* nsctx = clicon_nsctx_global_get(h);
* @endcode
*/
cvec *
clicon_nsctx_global_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = clicon_hash_value(cdat, "nsctx_global", &len)) != NULL)
return *(cvec **)p;
return NULL;
}
/*! Set global "canonical" namespace context
* Canonical: use prefix and namespace specified in the yang modules.
* @param[in] h Clicon handle
* @param[in] nsctx Namespace context (malloced)
*/
int
clicon_nsctx_global_set(clicon_handle h,
cvec *nsctx)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to cvec that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (clicon_hash_add(cdat, "nsctx_global", &nsctx, sizeof(nsctx)) == NULL)
return -1;
return 0;
}
/*! Get NACM (rfc 8341) XML parse tree if external not in std xml config
* @param[in] h Clicon handle
* @retval xn XML NACM tree, or NULL
@ -146,41 +227,6 @@ clicon_nacm_ext_set(clicon_handle h,
return 0;
}
#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
/*! Get YANG specification for clixon config
* Must use hash functions directly since they are not strings.
*/
yang_stmt *
clicon_config_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = clicon_hash_value(cdat, "control_yang", &len)) != NULL)
return *(yang_stmt **)p;
return NULL;
}
/*! Set yang specification for control
* ys must be a malloced pointer
*/
int
clicon_config_yang_set(clicon_handle h,
yang_stmt *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (clicon_hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
#endif
/*! Get YANG specification for Clixon system options and features
* Must use hash functions directly since they are not strings.
* Example: features are typically accessed directly in the config tree.

View file

@ -42,7 +42,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -264,7 +263,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;
@ -426,7 +425,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_nsc(xt, nsc, "%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
@ -532,7 +531,7 @@ xmldb_get_cache(clicon_handle h,
*/
/* Here xt looks like: <config>...</config> */
if (xpath_vec_nsc(x0t, nsc, "%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 */
@ -621,7 +620,7 @@ xmldb_get_zerocopy(clicon_handle h,
else
x0t = de->de_xml;
/* Here xt looks like: <config>...</config> */
if (xpath_vec_nsc(x0t, nsc, "%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
@ -648,12 +647,13 @@ xmldb_get_zerocopy(clicon_handle h,
/*! Get content of datastore and return a copy of the XML tree
* @param[in] h Clicon handle
* @param[in] db Name of database to search in (filename including dir path
* @param[in] nsc XML namespace context for XPATH
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xret Single return XML tree. Free with xml_free()
* @retval 0 OK
* @retval -1 Error
* @code
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt) < 0)
* if (xmldb_get(xh, "running", NULL, "/interfaces/interface[name="eth"]", &xt) < 0)
* err;
* xml_free(xt);
* @endcode
@ -662,10 +662,11 @@ xmldb_get_zerocopy(clicon_handle h,
int
xmldb_get(clicon_handle h,
const char *db,
cvec *nsc,
char *xpath,
cxobj **xret)
{
return xmldb_get0(h, db, NULL, xpath, 1, xret, NULL);
return xmldb_get0(h, db, nsc, xpath, 1, xret, NULL);
}
/*! Zero-copy variant of get content of database

View file

@ -43,7 +43,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -133,138 +132,6 @@ attr_ns_value(cxobj *x,
goto done;
}
/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace
* @param[in] x0 Source XML tree
* @param[in] x1 Target XML tree
* 1. Find N=namespace(x0)
* 2. Detect if N is declared in x1 parent
* 3. If yes, assign prefix to x1
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
* 5. Add prefix to x1, if any
* 6. Ensure x1 cache is updated
* @note switch use of x0 and x1 compared to datastore text_modify
* @see xml2ns
* XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
goto done;
*/
static int
check_namespaces(cxobj *x0,
cxobj *x1,
cxobj *x1p)
{
int retval = -1;
char *namespace = NULL;
char *prefix0 = NULL;;
char *prefix10 = NULL; /* extra just for malloc problem */
char *prefix1 = NULL;;
char *prefixb = NULL; /* identityref body prefix */
cvec *nsc0 = NULL;
cvec *nsc = NULL;
cxobj *xa = NULL;
cxobj *x;
int isroot;
/* XXX: need to identify root better than hiereustics and strcmp,... */
isroot = xml_parent(x1p)==NULL &&
strcmp(xml_name(x1p), "config") == 0 &&
xml_prefix(x1p)==NULL;
/* 1. Find N=namespace(x0) */
prefix0 = xml_prefix(x0);
if (xml2ns(x0, prefix0, &namespace) < 0)
goto done;
if (namespace == NULL){
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
prefix0?prefix0:"NULL");
goto done;
}
/* 2. Detect if namespace is declared in x1:s parent */
if (xml2prefix(x1p, namespace, &prefix10) == 1){
if (prefix10){
if ((prefix1 = strdup(prefix10)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
else
prefix1 = NULL;
/* 3. If yes, assign prefix to x1 */
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
goto done;
/* And copy namespace context from parent to child */
if ((nsc0 = nscache_get_all(x1p)) != NULL){
if ((nsc = cvec_dup(nsc0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
nscache_replace(x1, nsc);
}
/* Just in case */
if (nscache_set(x1, prefix1, namespace) < 0)
goto done;
}
else{
/* 4. If no, create new prefix/namespace binding and assign that to x1p
* use modules own default prefix (some chance for clash)
*/
if (prefix0 == NULL && !isroot){
assert(xml_spec(x1) != NULL);
prefix0 = yang_find_myprefix(xml_spec(x1));
}
if (prefix0)
if ((prefix1 = strdup(prefix0)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
/* Add binding to x1p. We add to parent due to heurestics, so we dont
* end up in adding it to large number of siblings
*/
if (isroot)
x = x1;
else
x = x1p;
if (nscache_set(x, prefix1, namespace) < 0)
goto done;
if (x == x1p){
if ((nsc0 = nscache_get_all(x1p)) != NULL)
if ((nsc = cvec_dup(nsc0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
/* Copy x1p cache to x1 */
nscache_replace(x1, nsc);
}
/* Create xmlns attribute to x1p/x1 XXX same code v */
if (prefix1){
if ((xa = xml_new(prefix1, x, NULL)) == NULL)
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done;
}
else{
if ((xa = xml_new("xmlns", x, NULL)) == NULL)
goto done;
}
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, namespace) < 0)
goto done;
xml_sort(x, NULL); /* Ensure attr is first / XXX xml_insert? */
/* 5. Add prefix to x1, if any */
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
goto done;
}
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
retval = 0;
done:
if (prefixb)
free(prefixb);
if (prefix1)
free(prefix1);
return retval;
}
static int
check_identityref(cxobj *x0,
cxobj *x1,
@ -1020,7 +887,7 @@ xmldb_put(clicon_handle h,
if ((nsc = xml_nsctx_init(NULL, "urn:ietf:params:xml:ns:yang:ietf-netconf-acm")) == NULL)
goto done;
if (xnacm0 != NULL &&
(xnacm = xpath_first_nsc(xnacm0, nsc, "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(h, mode, xnacm, username)) < 0)

View file

@ -47,7 +47,6 @@
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <syslog.h>
#include <assert.h>

View file

@ -228,7 +228,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
if ((xn = xml_new("body", jy->jy_current, NULL)) == NULL)
goto done;
xml_type_set(xn, CX_BODY);
if (value && xml_value_append(xn, value)==NULL)
if (value && xml_value_append(xn, value) < 0)
goto done;
retval = 0;
done:

View file

@ -44,7 +44,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
@ -214,7 +213,7 @@ nacm_rpc(char *rpc,
goto step10;
/* User's group */
if (xpath_vec_nsc(xnacm, nsc, "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)
@ -223,14 +222,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_nsc(xnacm, nsc, "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_nsc(rlist, nsc, ".[group='%s']", gname)!=NULL)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
@ -239,7 +238,7 @@ nacm_rpc(char *rpc,
until a rule that matches the requested access operation is
found.
*/
if (xpath_vec_nsc(rlist, nsc, "rule", &rvec, &rlen) < 0)
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
for (j=0; j<rlen; j++){
xrule = rvec[j];
@ -390,7 +389,7 @@ nacm_rule_datanode(cxobj *xt,
}
/* Here module is matched, now check for path if any NYI */
if (path){
if ((xpath = xpath_first_nsc(xt, nsc, "%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:
@ -447,7 +446,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_nsc(rlist, nsc, ".[group='%s']", gname)!=NULL)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
@ -456,7 +455,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_nsc(rlist, nsc, "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];
@ -588,7 +587,7 @@ nacm_datanode_read(cxobj *xt,
if (username == NULL)
goto step9;
/* User's group */
if (xpath_vec_nsc(xnacm, nsc, "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. */
@ -596,7 +595,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_nsc(xnacm, nsc, "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){
@ -713,7 +712,7 @@ nacm_datanode_write(cxobj *xt,
if (username == NULL)
goto step9;
/* User's group */
if (xpath_vec_nsc(xnacm, nsc, "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)
@ -722,19 +721,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_nsc(xnacm, nsc, "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_nsc(rlist, nsc, ".[group='%s']", gname)!=NULL)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
continue;
if (xpath_vec_nsc(rlist, nsc, "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
@ -863,7 +862,7 @@ nacm_access(clicon_handle h,
* RFC8341 3.4 */
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
operation is permitted. */
if ((x = xpath_first_nsc(xnacm, nsc, "enable-nacm")) == NULL)
if ((x = xpath_first(xnacm, nsc, "enable-nacm")) == NULL)
goto permit;
enabled = xml_body(x);
if (strcmp(enabled, "true") != 0)
@ -938,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_nsc(xnacm0, nsc, "nacm")) == NULL)
if ((xnacm = xpath_first(xnacm0, nsc, "nacm")) == NULL)
goto permit;
if (xml_rootchild_node(xnacm0, xnacm) < 0)
goto done;

View file

@ -46,7 +46,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
@ -1339,41 +1338,37 @@ netconf_db_find(cxobj *xn,
}
/*! Generate netconf error msg to cbuf to use in string printout or logs
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
* @param[out] cberr Translation from netconf err to cbuf. Free with cbuf_free.
* @param[in] xerr Netconf error message on the level: <rpc-error>
* @param[in,out] cberr Translation from netconf err to cbuf.
* @retval 0 OK, with cberr set
* @retval -1 Error
* @code
* cbuf *cb = NULL;
* if (netconf_err2cb(xerr, &cb) < 0)
* cbuf *cb = NULL;
* if ((cb = cbuf_new()) ==NULL){
* err;
* if (netconf_err2cb(xerr, cb) < 0)
* err;
* printf("%s", cbuf_get(cb));
* cbuf_free(cb);
* @endcode
* @see clicon_rpc_generate_error
*/
int
netconf_err2cb(cxobj *xerr,
cbuf **cberr)
cbuf *cberr)
{
int retval = -1;
cbuf *cb = NULL;
cxobj *x;
if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((x=xpath_first(xerr, "error-type"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-message"))!=NULL)
cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-info"))!=NULL)
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0, -1);
*cberr = cb;
if ((x=xpath_first(xerr, NULL, "error-type"))!=NULL)
cprintf(cberr, "%s ", xml_body(x));
if ((x=xpath_first(xerr, NULL, "error-tag"))!=NULL)
cprintf(cberr, "%s ", xml_body(x));
if ((x=xpath_first(xerr, NULL, "error-message"))!=NULL)
cprintf(cberr, "%s ", xml_body(x));
if ((x=xpath_first(xerr, NULL, "error-info"))!=NULL)
clicon_xml2cbuf(cberr, xml_child_i(x,0), 0, 0, -1);
retval = 0;
done:
return retval;
}

View file

@ -246,7 +246,7 @@ parse_configfile(clicon_handle h,
/* Hard-coded config for < 3.10 and clixon-config for >= 3.10 */
if ((nsc = xml_nsctx_init(NULL, CLIXON_CONF_NS)) == NULL)
goto done;
if ((xc = xpath_first_nsc(xt, nsc, "clixon-config")) == NULL){
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\">", filename, CLIXON_CONF_NS);
goto done;
@ -256,7 +256,11 @@ parse_configfile(clicon_handle h,
if ((ret = xml_yang_validate_add(h, xc, &xret)) < 0)
goto done;
if (ret == 0){
if (netconf_err2cb(xret, &cbret) < 0)
if ((cbret = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (netconf_err2cb(xret, cbret) < 0)
goto done;
clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
goto done;
@ -351,8 +355,7 @@ clicon_option_add(clicon_handle h,
* other yang modules.
*/
int
clicon_options_main(clicon_handle h,
yang_stmt *yspec)
clicon_options_main(clicon_handle h)
{
int retval = -1;
char *configfile;
@ -360,7 +363,11 @@ clicon_options_main(clicon_handle h,
char *suffix;
char xml = 0; /* Configfile is xml, otherwise legacy */
cxobj *xconfig = NULL;
yang_stmt *yspec = NULL;
/* Create configure yang-spec */
if ((yspec = yspec_new()) == NULL)
goto done;
/*
* Set configure file if not set by command-line above
*/
@ -409,8 +416,12 @@ clicon_options_main(clicon_handle h,
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;
}
/* Set yang config spec (must store to free at exit, since conf_xml below uses it) */
if (clicon_config_yang_set(h, yspec) < 0)
goto done;
/* Set clixon_conf pointer to handle */
clicon_conf_xml_set(h, xconfig);
if (clicon_conf_xml_set(h, xconfig) < 0)
goto done;
retval = 0;
done:

View file

@ -71,6 +71,7 @@
#include "clixon_yang.h"
#include "clixon_sig.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_proto.h"
static int _atomicio_sig = 0;
@ -191,12 +192,14 @@ clicon_msg_decode(struct clicon_msg *msg,
}
/*! Open local connection using unix domain sockets
* @param[in] h Clicon handle
* @param[in] sockpath Unix domain file path
* @retval s socket
* @retval -1 error
* @retval s socket
* @retval -1 error
*/
int
clicon_connect_unix(char *sockpath)
clicon_connect_unix(clicon_handle h,
char *sockpath)
{
struct sockaddr_un addr;
int retval = -1;
@ -213,9 +216,9 @@ clicon_connect_unix(char *sockpath)
clicon_debug(2, "%s: connecting to %s", __FUNCTION__, addr.sun_path);
if (connect(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){
if (errno == EACCES)
clicon_err(OE_CFG, errno, "connecting unix socket: %s."
"Client should be member of group $CLICON_SOCK_GROUP: ",
sockpath);
clicon_err(OE_CFG, errno, "connecting unix socket: %s. "
"Is user not member of group: \"%s\"?",
sockpath, clicon_sock_group(h));
else
clicon_err(OE_CFG, errno, "connecting unix socket: %s", sockpath);
close(s);
@ -394,6 +397,7 @@ clicon_msg_rcv(int s,
/*! Connect to server, send a clicon_msg message and wait for result using unix socket
*
* @param[in] h Clicon handle
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[in] sockpath Unix domain file path
* @param[out] retdata Returned data as string netconf xml tree.
@ -403,7 +407,8 @@ clicon_msg_rcv(int s,
* @see clicon_rpc But this is one-shot rpc: open, send, get reply and close.
*/
int
clicon_rpc_connect_unix(struct clicon_msg *msg,
clicon_rpc_connect_unix(clicon_handle h,
struct clicon_msg *msg,
char *sockpath,
char **retdata,
int *sock0)
@ -422,7 +427,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
clicon_err(OE_PROTO, EIO, "%s: Not unix socket", sockpath);
goto done;
}
if ((s = clicon_connect_unix(sockpath)) < 0)
if ((s = clicon_connect_unix(h, sockpath)) < 0)
goto done;
if (clicon_rpc(s, msg, retdata) < 0)
goto done;
@ -436,7 +441,8 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
}
/*! Connect to server, send a clicon_msg message and wait for result using an inet socket
* This uses unix domain socket communication
*
* @param[in] h Clicon handle
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[in] dst IPv4 address
* @param[in] port TCP port
@ -447,7 +453,8 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
* @see clicon_rpc But this is one-shot rpc: open, send, get reply and close.
*/
int
clicon_rpc_connect_inet(struct clicon_msg *msg,
clicon_rpc_connect_inet(clicon_handle h,
struct clicon_msg *msg,
char *dst,
uint16_t port,
char **retdata,
@ -597,9 +604,9 @@ send_msg_notify(int s,
/*! Send a clicon_msg NOTIFY message asynchronously to client
*
* @param[in] h Clicon handle
* @param[in] s Socket to communicate with client
* @param[in] level
* @param[in] xml Event as XML
* @param[in] xev Event as XML
* @retval 0 OK
* @retval -1 Error
* @see send_msg_notify XXX beauty contest

View file

@ -108,7 +108,7 @@ clicon_rpc_msg(clicon_handle h,
/* What to do if inet socket? */
switch (clicon_sock_family(h)){
case AF_UNIX:
if (clicon_rpc_connect_unix(msg, sock, &retdata, sock0) < 0){
if (clicon_rpc_connect_unix(h, msg, sock, &retdata, sock0) < 0){
#if 0
if (errno == ESHUTDOWN)
/* Maybe could reconnect on a higher layer, but lets fail
@ -127,7 +127,7 @@ clicon_rpc_msg(clicon_handle h,
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
goto done;
}
if (clicon_rpc_connect_inet(msg, sock, port, &retdata, sock0) < 0)
if (clicon_rpc_connect_inet(h, msg, sock, port, &retdata, sock0) < 0)
goto done;
break;
}
@ -227,16 +227,20 @@ clicon_rpc_netconf_xml(clicon_handle h,
/*! Generate and log clicon error function call from Netconf error message
* @param[in] prefix Print this string (if given) before: "<prefix>: <error>"
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
* @param[in] xerr Netconf error message on the level: <rpc-error>
*/
int
clicon_rpc_generate_error(char *prefix,
cxobj *xerr)
clicon_rpc_generate_error(const char *prefix,
cxobj *xerr)
{
int retval = -1;
cbuf *cb = NULL;
if (netconf_err2cb(xerr, &cb) < 0)
if ((cb = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (netconf_err2cb(xerr, cb) < 0)
goto done;
if (prefix)
clicon_log(LOG_ERR, "%s: %s", prefix, cbuf_get(cb));
@ -268,7 +272,7 @@ clicon_rpc_generate_error(char *prefix,
* err;
* if (clicon_rpc_get_config(h, NULL, "running", "/hello/world", nsc, &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;
* }
@ -324,9 +328,9 @@ clicon_rpc_get_config(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
/* Send xml error back: first check error, then ok */
if ((xd = xpath_first(xret, "/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){
@ -390,7 +394,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;
}
@ -437,7 +441,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;
}
@ -477,7 +481,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;
}
@ -513,7 +517,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;
}
@ -548,7 +552,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;
}
@ -582,7 +586,7 @@ clicon_rpc_unlock(clicon_handle h,
* err;
* if (clicon_rpc_get(h, "/hello/world", nsc, CONTENT_ALL, -1, &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;
* }
@ -646,9 +650,9 @@ clicon_rpc_get(clicon_handle h,
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
/* Send xml error back: first check error, then ok */
if ((xd = xpath_first(xret, "/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){
@ -689,7 +693,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;
}
@ -725,7 +729,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;
}
@ -760,7 +764,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;
}
@ -793,7 +797,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;
}
@ -826,7 +830,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;
}
@ -872,7 +876,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;
}
@ -907,11 +911,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;
}
@ -950,11 +954,11 @@ clicon_hello_req(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("Hello", xerr);
goto done;
}
if ((x = xpath_first(xret, "hello/session-id")) == NULL){
if ((x = xpath_first(xret, NULL, "hello/session-id")) == NULL){
clicon_err(OE_XML, 0, "hello session-id");
goto done;
}

View file

@ -514,7 +514,7 @@ stream_notify1(clicon_handle h,
else{ /* xpath match */
if (ss->ss_xpath == NULL ||
strlen(ss->ss_xpath)==0 ||
xpath_first(xevent, "%s", ss->ss_xpath) != NULL)
xpath_first(xevent, NULL, "%s", ss->ss_xpath) != NULL)
if ((*ss->ss_fn)(h, 0, xevent, ss->ss_arg) < 0)
goto done;
ss = NEXTQ(struct stream_subscription *, ss);

View file

@ -121,7 +121,7 @@ name2uid(const char *name,
/*! Translate uid to user name
* @param[in] uid User id
* @param[out] name User name
* @param[out] name User name (Malloced, need to be freed)
* @retval 0 OK
* @retval -1 Error. or not found
*/
@ -131,7 +131,7 @@ uid2name(const uid_t uid,
{
int retval = -1;
char buf[1024];
struct passwd pwbuf;
struct passwd pwbuf = {0,};
struct passwd *pwbufp = NULL;
if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pwbufp) != 0){
@ -139,17 +139,21 @@ uid2name(const uid_t uid,
goto done;
}
if (pwbufp == NULL){
clicon_err(OE_UNIX, 0, "No such user: %u", uid);
clicon_err(OE_UNIX, ENOENT, "No such user: %u", uid);
goto done;
}
if (name)
*name = pwbufp->pw_name;
if (name){
if ((*name = strdup(pwbufp->pw_name)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
retval = 0;
done:
return retval;
}
/* Privileges drop perm, temp and restore
* @see https://www.usenix.org/legacy/events/sec02/full_papers/chen/chen.pdf
*/

View file

@ -46,7 +46,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
@ -79,6 +78,8 @@
#define XML_TOP_SYMBOL "top"
/* How many XML children to start with if any (and then add exponentialy) */
#define XML_CHILDVEC_MAX_DEFAULT 4
/* Initial length of x_value malloced string */
#define XML_VALUE_MAX_DEFAULT 32
/*
* Types
@ -125,7 +126,7 @@ struct xml{
int x_childvec_len;/* Number of children */
int x_childvec_max;/* Length of allocated vector */
enum cxobj_type x_type; /* type of node: element, attribute, body */
char *x_value; /* attribute and body nodes have values */
cbuf *x_value_cb; /* attribute and body nodes have values */
int x_flags; /* Flags according to XML_FLAG_* */
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */
@ -150,7 +151,6 @@ static const map_str2int xsmap[] = {
{NULL, -1}
};
/*! Translate from xml type in enum form to string keyword
* @param[in] type Xml type
* @retval str String keyword
@ -400,7 +400,7 @@ xml2ns(cxobj *x,
/*! Add a namespace attribute to an XML node, either default or specific prefix
* @param[in] x XML tree
* @param[in] prefix prefix/ns localname. If NULL then set default xmlns
* @param[out] namespace URI namespace (or NULL). Will be copied
* @param[in] ns URI namespace (or NULL). Will be copied
* @retval 0 OK
* @retval -1 Error
* @see xml2ns
@ -621,7 +621,7 @@ xml_flag_reset(cxobj *xn,
char*
xml_value(cxobj *xn)
{
return xn->x_value;
return xn->x_value_cb?cbuf_get(xn->x_value_cb):NULL;
}
/*! Set value of xml node, value is copied
@ -634,17 +634,20 @@ int
xml_value_set(cxobj *xn,
char *val)
{
if (xn->x_value){
free(xn->x_value);
xn->x_value = NULL;
}
if (val){
if ((xn->x_value = strdup(val)) == NULL){
clicon_err(OE_XML, errno, "strdup");
return -1;
int retval = -1;
if (xn->x_value_cb == NULL){
if ((xn->x_value_cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
}
return 0;
else
cbuf_reset(xn->x_value_cb);
cprintf(xn->x_value_cb, "%s", val);
retval = 0;
done:
return retval;
}
/*! Append value of xnode, value is copied
@ -653,23 +656,25 @@ xml_value_set(cxobj *xn,
* @retval NULL on error with clicon-err set, or if value is set to NULL
* @retval new value
*/
char *
int
xml_value_append(cxobj *xn,
char *val)
{
int len0;
int len;
int retval = -1;
len0 = xn->x_value?strlen(xn->x_value):0;
if (val){
len = len0 + strlen(val);
if ((xn->x_value = realloc(xn->x_value, len+1)) == NULL){
clicon_err(OE_XML, errno, "realloc");
return NULL;
if (xn->x_value_cb == NULL){
if ((xn->x_value_cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
strncpy(xn->x_value + len0, val, len-len0+1);
}
return xn->x_value;
if (cprintf(xn->x_value_cb, "%s", val) < 0){
clicon_err(OE_XML, errno, "cprintf");
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get type of xnode
@ -1594,8 +1599,8 @@ xml_free(cxobj *x)
if (x->x_name)
free(x->x_name);
if (x->x_value)
free(x->x_value);
if (x->x_value_cb)
cbuf_free(x->x_value_cb);
if (x->x_prefix)
free(x->x_prefix);
for (i=0; i<x->x_childvec_len; i++){
@ -2142,22 +2147,23 @@ int
xml_copy_one(cxobj *x0,
cxobj *x1)
{
int retval = -1;
char *s;
xml_type_set(x1, xml_type(x0));
if ((s = xml_value(x0))){ /* malloced string */
if ((x1->x_value = strdup(s)) == NULL){
clicon_err(OE_XML, errno, "strdup");
return -1;
}
if (xml_value_set(x1, s) < 0)
goto done;
}
if ((s = xml_name(x0))) /* malloced string */
if ((xml_name_set(x1, s)) < 0)
return -1;
goto done;
if ((s = xml_prefix(x0))) /* malloced string */
if ((xml_prefix_set(x1, s)) < 0)
return -1;
return 0;
goto done;
retval = 0;
done:
return retval;
}
/*! Copy xml tree x0 to other existing tree x1
@ -2618,8 +2624,10 @@ clicon_log_xml(int level,
int retval = -1;
/* Print xml as cbuf */
if ((cb = cbuf_new()) == NULL)
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, x, 0, 0, -1) < 0)
goto done;

View file

@ -45,7 +45,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
@ -200,7 +199,7 @@ changelog_move(clicon_handle h,
int retval = -1;
cxobj *xp; /* destination parent node */
if ((xp = xpath_first_nsc(xt, nsc, "%s", dst)) == NULL){
if ((xp = xpath_first(xt, nsc, "%s", dst)) == NULL){
clicon_err(OE_XML, 0, "path required");
goto done;
}
@ -253,7 +252,7 @@ changelog_op(clicon_handle h,
if ((wxpath = xml_find_body(xi, "where")) == NULL)
goto ok;
/* Get vector of target nodes meeting the where requirement */
if (xpath_vec_nsc(xt, nsc, "%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];
@ -328,7 +327,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++){
@ -392,7 +391,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]*/
@ -452,7 +451,11 @@ clixon_xml_changelog_init(clicon_handle h)
if (ret==1 && (ret = xml_yang_validate_add(h, xt, &xret)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (netconf_err2cb(xret, &cbret) < 0)
if ((cbret = cbuf_new()) ==NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (netconf_err2cb(xret, cbret) < 0)
goto done;
clicon_err(OE_YANG, 0, "validation failed: %s", cbuf_get(cbret));
goto done;

View file

@ -305,7 +305,7 @@ validate_leafref(cxobj *xt,
/* XXX see comment above regarding typeref or not */
if (xml_nsctx_yang(ytype, &nsc) < 0)
goto done;
if (xpath_vec_nsc(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
@ -2153,6 +2153,46 @@ xml_tree_prune_flagged(cxobj *xt,
return retval;
}
/*! Add prefix:namespace pair to xml node, set cache, prefix, etc
*/
static int
add_namespace(cxobj *x1, /* target */
cxobj *x1p,
char *prefix1,
char *namespace)
{
int retval = -1;
cxobj *xa = NULL;
/* Add binding to x1p. We add to parent due to heurestics, so we dont
* end up in adding it to large number of siblings
*/
if (nscache_set(x1, prefix1, namespace) < 0)
goto done;
/* Create xmlns attribute to x1p/x1 XXX same code v */
if (prefix1){
if ((xa = xml_new(prefix1, x1, NULL)) == NULL)
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done;
}
else{
if ((xa = xml_new("xmlns", x1, NULL)) == NULL)
goto done;
}
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, namespace) < 0)
goto done;
xml_sort(x1, NULL); /* Ensure attr is first / XXX xml_insert? */
/* 5. Add prefix to x1, if any */
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Add default values (if not set)
* @param[in] xt XML tree with some node marked
* @param[in] arg Ignored
@ -2175,6 +2215,7 @@ xml_default(cxobj *xt,
int added=0;
char *namespace;
char *prefix;
int ret;
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
retval = 0;
@ -2195,9 +2236,20 @@ xml_default(cxobj *xt,
/* assign right prefix */
if ((namespace = yang_find_mynamespace(y)) != NULL){
prefix = NULL;
if (xml2prefix(xt, namespace, &prefix))
if ((ret = xml2prefix(xt, namespace, &prefix)) < 0)
goto done;
if (ret){
if (xml_prefix_set(xc, prefix) < 0)
goto done;
}
else{ /* namespace does not exist in target, use source prefix */
if ((prefix = yang_find_myprefix(y)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
if (add_namespace(xc, xt, prefix, namespace) < 0)
goto done;
}
}
xml_flag_set(xc, XML_FLAG_DEFAULT);
@ -3190,6 +3242,124 @@ xmlns_assign(cxobj *x)
return retval;
}
/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace
* @param[in] x0 Source XML tree
* @param[in] x1 Target XML tree
* 1. Find N=namespace(x0)
* 2. Detect if N is declared in x1 parent
* 3. If yes, assign prefix to x1
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
* 5. Add prefix to x1, if any
* 6. Ensure x1 cache is updated
* @note switch use of x0 and x1 compared to datastore text_modify
* @see xml2ns
* XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
goto done;
*/
int
check_namespaces(cxobj *x0, /* source */
cxobj *x1, /* target */
cxobj *x1p)
{
int retval = -1;
char *namespace = NULL;
char *prefix0 = NULL;;
char *prefix1 = NULL;
char *prefixb = NULL; /* identityref body prefix */
cvec *nsc0 = NULL;
cvec *nsc = NULL;
int isroot;
char *pexist = NULL;
yang_stmt *y;
/* XXX: need to identify root better than hiereustics and strcmp,... */
isroot = xml_parent(x1p)==NULL &&
(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
xml_prefix(x1p)==NULL;
/* 1. Find N=namespace(x0) */
prefix0 = xml_prefix(x0);
if (xml2ns(x0, prefix0, &namespace) < 0)
goto done;
if (namespace == NULL){
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
prefix0?prefix0:"NULL");
goto done;
}
/* 2a. Detect if namespace is declared in x1 target parent */
if (xml2prefix(x1p, namespace, &pexist) == 1){
/* Yes, and it has prefix pexist */
if (pexist){
if ((prefix1 = strdup(pexist)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
else
prefix1 = NULL;
/* 3. If yes, assign prefix to x1 */
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
goto done;
/* And copy namespace context from parent to child */
if ((nsc0 = nscache_get_all(x1p)) != NULL){
if ((nsc = cvec_dup(nsc0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_dup");
goto done;
}
nscache_replace(x1, nsc);
}
/* Just in case */
if (nscache_set(x1, prefix1, namespace) < 0)
goto done;
}
else{ /* No, namespace does not exist in x1 _parent_
* Check if it is exists in x1 itself */
if (nscache_get_prefix(x1, namespace, &pexist) == 1){
/* Yes it exists, but is it equal? */
if ((pexist == NULL && prefix0 == NULL) ||
(pexist && prefix0 &&
strcmp(pexist, prefix0)==0)){ /* Equal, reuse */
;
}
else{ /* namespace exist, but not equal, use existing */
/* Add prefix to x1, if any */
if (pexist && xml_prefix_set(x1, pexist) < 0)
goto done;
}
goto ok; /* skip */
}
else
{ /* namespace does not exist in target x1, use source prefix
* use the prefix defined in the module
*/
if (isroot){
if (prefix0 && (prefix1 = strdup(prefix0)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
else{
y = xml_spec(x0);
if ((prefix1 = strdup(yang_find_myprefix(y))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
}
if (add_namespace(x1, x1p, prefix1, namespace) < 0)
goto done;
}
ok:
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
retval = 0;
done:
if (prefixb)
free(prefixb);
if (prefix1)
free(prefix1);
return retval;
}
/*! Merge a base tree x0 with x1 with yang spec y
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
@ -3201,20 +3371,18 @@ xmlns_assign(cxobj *x)
* Assume x0 and x1 are same on entry and that y is the spec
*/
static int
xml_merge1(cxobj *x0,
xml_merge1(cxobj *x0, /* the target */
yang_stmt *y0,
cxobj *x0p,
cxobj *x1,
cxobj *x1, /* the source */
char **reason)
{
int retval = -1;
char *x1name;
char *x1cname; /* child name */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
cxobj *x0a; /* x0 xmlns attribute */
cxobj *x1a; /* x1 xmlns attribute */
char *x1name;
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cbuf *cbr = NULL; /* Reason buffer */
@ -3243,13 +3411,16 @@ xml_merge1(cxobj *x0,
if (xml_value_set(x0b, x1bstr) < 0)
goto done;
}
if (check_namespaces(x1, x0, x0p) < 0)
goto done;
} /* if LEAF|LEAF_LIST */
else { /* eg Y_CONTAINER, Y_LIST */
if (x0==NULL){
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
if ((x0 = xml_new(x1name, NULL, (yang_stmt*)y0)) == NULL)
goto done;
}
if (check_namespaces(x1, x0, x0p) < 0)
goto done;
/* Loop through children of the modification tree */
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
@ -3277,19 +3448,11 @@ xml_merge1(cxobj *x0,
goto done;
if (*reason != NULL)
goto ok;
}
} /* while */
if (xml_parent(x0) == NULL &&
xml_insert(x0p, x0, INS_LAST, NULL, NULL) < 0)
goto done;
} /* else Y_CONTAINER */
assert(x0);
/* Copy xmlns attributes (if it does not already exist) */
if ((x1a = xml_find_type(x1, NULL, "xmlns", CX_ATTR)) != NULL)
if (xml_find_type(x0, NULL, "xmlns", CX_ATTR)==NULL){
if ((x0a = xml_dup(x1a)) == NULL)
goto done;
if (xml_addsub(x0, x0a) < 0)
goto done;
}
ok:
retval = 0;
done:

View file

@ -50,7 +50,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
@ -183,7 +182,6 @@ xml_nsctx_add(cvec *cvv,
return retval;
}
static int
xml_nsctx_node1(cxobj *xn,
cvec *nsc)
@ -269,10 +267,10 @@ xml_nsctx_node(cxobj *xn,
return retval;
}
/*! Create and initialize XML namespace from Yang node
/*! Create and initialize XML namespace context 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[in] yn Yang statement in module tree (or module itself)
* @param[out] ncp XML namespace context
* @retval 0 OK
* @retval -1 Error
@ -355,3 +353,47 @@ xml_nsctx_yang(yang_stmt *yn,
return retval;
}
/*! Create and initialize XML namespace context from Yang spec
*
* That is, create a "canonical" XML namespace mapping from all loaded yang
* modules which are children of the yang specification.
* ALso add netconf base namespace: nc , urn:ietf:params:xml:ns:netconf:base:1.0
* Fully explore all prefix:namespace pairs of all yang modules
* @param[in] yspec Yang spec
* @param[out] ncp XML namespace context
*/
int
xml_nsctx_yangspec(yang_stmt *yspec,
cvec **ncp)
{
int retval = -1;
cvec *nc = NULL;
yang_stmt *ymod = NULL;
yang_stmt *yprefix;
yang_stmt *ynamespace;
if ((nc = cvec_new(0)) == NULL){
clicon_err(OE_XML, errno, "cvec_new");
goto done;
}
ymod = NULL;
while ((ymod = yn_each(yspec, ymod)) != NULL){
if (yang_keyword_get(ymod) != Y_MODULE)
continue;
if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL)
continue;
if ((ynamespace = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL)
continue;
if (xml_nsctx_add(nc, yang_argument_get(yprefix), yang_argument_get(ynamespace)) < 0)
goto done;
}
/* Add base netconf namespace as default and "nc" prefix */
if (xml_nsctx_add(nc, NULL, NETCONF_BASE_NAMESPACE) < 0)
goto done;
if (xml_nsctx_add(nc, NETCONF_BASE_PREFIX, NETCONF_BASE_NAMESPACE) < 0)
goto done;
*ncp = nc;
retval = 0;
done:
return retval;
}

View file

@ -94,6 +94,7 @@ ncname {namestart}{namechar}*
%s STATEA
%s AMPERSAND
%s CDATA
%s CDATAEND
%s CMNT
%s STR
%s TEXTDECL
@ -145,9 +146,13 @@ ncname {namestart}{namechar}*
<AMPERSAND>"apos;" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;}
<AMPERSAND>"quot;" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "\""; return CHARDATA;}
<CDATA>. { clixon_xml_parselval.string = yytext; return CHARDATA;}
<CDATA>\n { clixon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHARDATA);}
<CDATA>"]]>" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = yytext; return CHARDATA;}
<CDATA>\] { BEGIN(CDATAEND);clixon_xml_parselval.string = yytext;return (CHARDATA);}
<CDATA>[^]\n]+ { clixon_xml_parselval.string = yytext; return CHARDATA;}
<CDATAEND>\n { clixon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHARDATA);}
<CDATAEND>"]>" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = yytext; return CHARDATA;}
<CDATAEND>. { BEGIN(CDATA); clixon_xml_parselval.string = yytext;return (CHARDATA);}
<CMNT>"-->" { BEGIN(START); return ECOMMENT; }
<CMNT>.

View file

@ -104,7 +104,7 @@ xml_parse_content(struct xml_parse_yacc_arg *ya,
goto done;
xml_type_set(xn, CX_BODY);
}
if (xml_value_append(xn, str)==NULL)
if (xml_value_append(xn, str) < 0)
goto done;
ya->ya_xelement = xn;
retval = 0;
@ -140,7 +140,7 @@ xml_parse_whitespace(struct xml_parse_yacc_arg *ya,
goto done;
xml_type_set(xn, CX_BODY);
}
if (xml_value_append(xn, str)==NULL)
if (xml_value_append(xn, str) < 0)
goto done;
ya->ya_xelement = xn;
ok:
@ -148,7 +148,6 @@ xml_parse_whitespace(struct xml_parse_yacc_arg *ya,
done:
return retval;
}
static int
xml_parse_version(struct xml_parse_yacc_arg *ya,

View file

@ -44,7 +44,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
@ -539,7 +538,7 @@ xml_insert_userorder(cxobj *xp,
else{
switch (yang_keyword_get(yn)){
case Y_LEAF_LIST:
if ((xc = xpath_first_nsc(xp, nsc_key, "%s[.='%s']", xml_name(xn), key_val)) == NULL)
if ((xc = xpath_first(xp, nsc_key, "%s[.='%s']", xml_name(xn), key_val)) == NULL)
clicon_err(OE_YANG, 0, "bad-attribute: value, missing-instance: %s", key_val);
else {
if ((i = xml_child_order(xp, xc)) < 0)
@ -549,7 +548,7 @@ xml_insert_userorder(cxobj *xp,
}
break;
case Y_LIST:
if ((xc = xpath_first_nsc(xp, nsc_key, "%s%s", xml_name(xn), key_val)) == NULL)
if ((xc = xpath_first(xp, nsc_key, "%s%s", xml_name(xn), key_val)) == NULL)
clicon_err(OE_YANG, 0, "bad-attribute: key, missing-instance: %s", key_val);
else {
if ((i = xml_child_order(xp, xc)) < 0)
@ -618,8 +617,8 @@ xml_insert2(cxobj *xp,
xc = xml_child_i(xp, mid);
if ((yc = xml_spec(xc)) == NULL){
if (xml_type(xc) != CX_ELMNT)
clicon_err(OE_XML, 0, "No spec found %s (wrong xml type:%d)",
xml_name(xc), xml_type(xc));
clicon_err(OE_XML, 0, "No spec found %s (wrong xml type:%s)",
xml_name(xc), xml_type2str(xml_type(xc)));
else
clicon_err(OE_XML, 0, "No spec found %s", xml_name(xc));
goto done;

Binary file not shown.

View file

@ -65,7 +65,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
@ -181,8 +180,8 @@ xpath_tree_print0(cbuf *cb,
cprintf(cb, "%d ", xs->xs_int);
break;
}
if (xs->xs_double)
cprintf(cb,"%lf ", xs->xs_double);
if (xs->xs_strnr)
cprintf(cb,"%s ", xs->xs_strnr);
cprintf(cb, "\n");
if (xs->xs_c0)
xpath_tree_print0(cb, xs->xs_c0,level+1);
@ -229,7 +228,7 @@ xpath_tree_print(FILE *f,
/*! Create an xpath string from an xpath tree, ie "unparsing"
* @param[in] xs XPATH tree
* @param[out] xpath Xpath string as CLIgen buf
* @param[out] xpath XPath string as CLIgen buf
* @see xpath_tree_print
* @note NOT COMPLETE, just simple xpaths eg a/b
*/
@ -254,7 +253,7 @@ xpath_tree2cbuf(xpath_tree *xs,
cprintf(xcb, "'%s'", xs->xs_s0);
break;
case XP_PRIME_NR:
cprintf(xcb, "%lf", xs->xs_double);
cprintf(xcb, "%s", xs->xs_strnr?xs->xs_strnr:"0");
break;
case XP_STEP:
switch (xs->xs_int){
@ -314,6 +313,8 @@ xpath_tree2cbuf(xpath_tree *xs,
int
xpath_tree_free(xpath_tree *xs)
{
if (xs->xs_strnr)
free(xs->xs_strnr);
if (xs->xs_s0)
free(xs->xs_s0);
if (xs->xs_s1)
@ -328,7 +329,7 @@ xpath_tree_free(xpath_tree *xs)
/*! Given xpath, parse it, and return structured xpath tree
* @param[in] xpath String with XPATH 1.0 syntax
* @param[out] xptree Xpath-tree, parsed, structured XPATH, free:xpath_tree_free
* @param[out] xptree XPath-tree, parsed, structured XPATH, free:xpath_tree_free
* @retval 0 OK
* @retval -1 Error
* @code
@ -427,7 +428,7 @@ xpath_vec_ctx(cxobj *xcur,
return retval;
}
/*! Xpath nodeset function where only the first matching entry is returned
/*! XPath nodeset function where only the first matching entry is returned
*
* @param[in] xcur XML tree where to search
* @param[in] nsc External XML namespace context, or NULL
@ -438,7 +439,7 @@ xpath_vec_ctx(cxobj *xcur,
* @code
* cxobj *x;
* cvec *nsc; // namespace context
* if ((x = xpath_first_nsc(xtop, nsc, "//symbol/foo")) != NULL) {
* if ((x = xpath_first(xtop, nsc, "//symbol/foo")) != NULL) {
* ...
* }
* @endcode
@ -447,65 +448,8 @@ xpath_vec_ctx(cxobj *xcur,
* @see also xpath_vec.
*/
cxobj *
xpath_first_nsc(cxobj *xcur,
cvec *nsc,
char *xpformat,
...)
{
cxobj *cx = NULL;
va_list ap;
size_t len;
char *xpath = NULL;
xp_ctx *xr = NULL;
va_start(ap, xpformat);
len = vsnprintf(NULL, 0, xpformat, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, xpformat);
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
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:
if (xr)
ctx_free(xr);
if (xpath)
free(xpath);
return cx;
}
/*! Xpath nodeset function where only the first matching entry is returned
*
* @param[in] xcur XML tree where to search
* @param[in] xpformat Format string for 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) {
* ...
* }
* @endcode
* @note the returned pointer points into the original tree so should not be freed after use.
* @note return value does not see difference between error and not found
* @see also xpath_vec.
* @see xpath_first_nsc which is more generic with namespace context
*/
cxobj *
xpath_first(cxobj *xcur,
cvec *nsc,
char *xpformat,
...)
{
@ -531,7 +475,7 @@ xpath_first(cxobj *xcur,
goto done;
}
va_end(ap);
if (xpath_vec_ctx(xcur, NULL, 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];
@ -556,7 +500,7 @@ xpath_first(cxobj *xcur,
* cvec *nsc; // namespace context
* cxobj **vec;
* size_t veclen;
* if (xpath_vec_nsc(xcur, nsc, "//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];
@ -566,76 +510,8 @@ xpath_first(cxobj *xcur,
* @endcode
*/
int
xpath_vec_nsc(cxobj *xcur,
cvec *nsc,
char *xpformat,
cxobj ***vec,
size_t *veclen,
...)
{
int retval = -1;
va_list ap;
size_t len;
char *xpath = NULL;
xp_ctx *xr = NULL;
va_start(ap, veclen);
len = vsnprintf(NULL, 0, xpformat, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, veclen);
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
*vec=NULL;
*veclen = 0;
if (xpath_vec_ctx(xcur, nsc, xpath, &xr) < 0)
goto done;
if (xr && xr->xc_type == XT_NODESET){
*vec = xr->xc_nodeset;
xr->xc_nodeset = NULL;
*veclen = xr->xc_size;
}
retval = 0;
done:
if (xr)
ctx_free(xr);
if (xpath)
free(xpath);
return retval;
}
/*! 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] xpformat Format string for XPATH syntax
* @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj **vec;
* size_t veclen;
* if (xpath_vec(xcur, "//symbol/foo", &vec, &veclen) < 0)
* goto err;
* for (i=0; i<veclen; i++){
* xn = vec[i];
* ...
* }
* free(vec);
* @endcode
* @see xpath_vec_nsc which is more generic with namespace context
*/
int
xpath_vec(cxobj *xcur,
cvec *nsc,
char *xpformat,
cxobj ***vec,
size_t *veclen,
@ -665,7 +541,7 @@ xpath_vec(cxobj *xcur,
va_end(ap);
*vec=NULL;
*veclen = 0;
if (xpath_vec_ctx(xcur, NULL, 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;
@ -681,7 +557,7 @@ xpath_vec(cxobj *xcur,
return retval;
}
/* Xpath that returns a vector of matches (only nodes marked with flags)
/* XPath that returns a vector of matches (only nodes marked with flags)
* @param[in] xcur xml-tree where to search
* @param[in] xpformat Format string for XPATH syntax
* @param[in] nsc External XML namespace context, or NULL

View file

@ -40,7 +40,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>

View file

@ -63,7 +63,6 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>

View file

@ -140,7 +140,7 @@ real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; }
<TOKEN>\' { BEGIN(ALITERAL); return APOST; }
<TOKEN>\-?({integer}|{real}) { sscanf(yytext,"%lf",&clixon_xpath_parselval.dval); return NUMBER;}
<TOKEN>\-?({integer}|{real}) { clixon_xpath_parselval.string = strdup(yytext); return NUMBER; }
<TOKEN>[0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
}

View file

@ -44,7 +44,6 @@
%union {
int intval;
double dval;
char *string;
void *stack; /* xpath_tree */
}
@ -54,8 +53,7 @@
%token <intval> ADDOP
%token <intval> RELOP
%token <dval> NUMBER
%token <string> NUMBER
%token <string> X_EOF
%token <string> QUOTE
%token <string> APOST
@ -105,7 +103,6 @@
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <fnmatch.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
@ -162,13 +159,13 @@ xpath_parse_exit(struct clicon_xpath_yacc_arg *xy)
static xpath_tree *
xp_new(enum xp_type type,
int i0,
double d0,
char *numstr,
char *s0,
char *s1,
xpath_tree *c0,
xpath_tree *c1)
{
xpath_tree *xs = NULL;
xpath_tree *xs = NULL;
if ((xs = malloc(sizeof(xpath_tree))) == NULL){
clicon_err(OE_XML, errno, "malloc");
@ -177,7 +174,15 @@ xp_new(enum xp_type type,
memset(xs, 0, sizeof(*xs));
xs->xs_type = type;
xs->xs_int = i0;
xs->xs_double = d0;
if (numstr){
xs->xs_strnr = numstr;
if (sscanf(numstr, "%lf", &xs->xs_double) == EOF){
clicon_err(OE_XML, errno, "sscanf");
goto done;
}
}
else
xs->xs_double = 0.0;
xs->xs_s0 = s0;
xs->xs_s1 = s1;
xs->xs_c0 = c0;
@ -197,50 +202,50 @@ start : expr X_EOF { _XY->xy_top=$1;clicon_debug(2,"start->expr");
| locationpath X_EOF { _XY->xy_top=$1;clicon_debug(2,"start->locationpath"); YYACCEPT; }
;
expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"expr->expr or andexpr"); }
| andexpr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"expr-> andexpr"); }
expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"expr->expr or andexpr"); }
| andexpr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"expr-> andexpr"); }
;
andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"andexpr-> andexpr and relexpr"); }
| relexpr { $$=xp_new(XP_AND,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"andexpr-> relexpr"); }
andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"andexpr-> andexpr and relexpr"); }
| relexpr { $$=xp_new(XP_AND,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"andexpr-> relexpr"); }
;
relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"relexpr-> relexpr relop addexpr"); }
| addexpr { $$=xp_new(XP_RELEX,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"relexpr-> addexpr"); }
relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"relexpr-> relexpr relop addexpr"); }
| addexpr { $$=xp_new(XP_RELEX,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"relexpr-> addexpr"); }
;
addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,0.0,NULL,NULL,$1, $3);clicon_debug(2,"addexpr-> addexpr ADDOP unionexpr"); }
| unionexpr { $$=xp_new(XP_ADD,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"addexpr-> unionexpr"); }
addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,NULL,NULL,NULL,$1, $3);clicon_debug(2,"addexpr-> addexpr ADDOP unionexpr"); }
| unionexpr { $$=xp_new(XP_ADD,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"addexpr-> unionexpr"); }
;
/* node-set */
unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(2,"unionexpr-> unionexpr | pathexpr"); }
| pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"unionexpr-> pathexpr"); }
unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,NULL,NULL,NULL,$1, $3);clicon_debug(2,"unionexpr-> unionexpr | pathexpr"); }
| pathexpr { $$=xp_new(XP_UNION,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"unionexpr-> pathexpr"); }
;
pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> locationpath"); }
| primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> primaryexpr"); }
pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> locationpath"); }
| primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,NULL,NULL,NULL,$1, NULL);clicon_debug(2,"pathexpr-> primaryexpr"); }
;
/* location path returns a node-set */
locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> rellocpath"); }
| abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> abslocpath"); }
locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> rellocpath"); }
| abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"locationpath-> abslocpath"); }
;
abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,NULL, NULL);clicon_debug(2,"abslocpath-> /"); }
| '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,$2, NULL);clicon_debug(2,"abslocpath->/ rellocpath");}
abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,NULL,NULL,NULL,NULL, NULL);clicon_debug(2,"abslocpath-> /"); }
| '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,NULL,NULL,NULL,$2, NULL);clicon_debug(2,"abslocpath->/ rellocpath");}
/* // is short for /descendant-or-self::node()/ */
| DOUBLESLASH rellocpath {$$=xp_new(XP_ABSPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$2, NULL); clicon_debug(2,"abslocpath-> // rellocpath"); }
| DOUBLESLASH rellocpath {$$=xp_new(XP_ABSPATH,A_DESCENDANT_OR_SELF,NULL,NULL,NULL,$2, NULL); clicon_debug(2,"abslocpath-> // rellocpath"); }
;
rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(2,"rellocpath-> step"); }
| rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(2,"rellocpath-> rellocpath / step"); }
| rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$1, $3); clicon_debug(2,"rellocpath-> rellocpath // step"); }
rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,NULL,NULL,NULL,$1, NULL); clicon_debug(2,"rellocpath-> step"); }
| rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,NULL,NULL,NULL,$1, $3);clicon_debug(2,"rellocpath-> rellocpath / step"); }
| rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,NULL,NULL,NULL,$1, $3); clicon_debug(2,"rellocpath-> rellocpath // step"); }
;
step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(2,"step->axisspec(%d) nodetest", $1); }
| '.' predicates { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, $2); clicon_debug(2,"step-> ."); }
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, $2); clicon_debug(2,"step-> .."); }
step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,NULL, NULL, NULL, $2, $3);clicon_debug(2,"step->axisspec(%d) nodetest", $1); }
| '.' predicates { $$=xp_new(XP_STEP,A_SELF, NULL,NULL, NULL, NULL, $2); clicon_debug(2,"step-> ."); }
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, NULL,NULL, NULL, NULL, $2); clicon_debug(2,"step-> .."); }
;
axisspec : AXISNAME { clicon_debug(2,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;}
@ -248,31 +253,31 @@ axisspec : AXISNAME { clicon_debug(2,"axisspec-> AXISNAME(%d) ::", $1); $$=$1
| { clicon_debug(2,"axisspec-> "); $$=A_CHILD;}
;
nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(2,"nodetest-> *"); }
| NAME { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, $1, NULL, NULL); clicon_debug(2,"nodetest-> name(%s)",$1); }
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,0.0, $1, $3, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : name(%s)", $1, $3); }
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : *", $1); }
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype():%s", $1); }
nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(2,"nodetest-> *"); }
| NAME { $$=xp_new(XP_NODE,A_NAN,NULL, NULL, $1, NULL, NULL); clicon_debug(2,"nodetest-> name(%s)",$1); }
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,NULL, $1, $3, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : name(%s)", $1, $3); }
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,NULL, $1, NULL, NULL, NULL);clicon_debug(2,"nodetest-> name(%s) : *", $1); }
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,NULL, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype():%s", $1); }
;
/* evaluates to boolean */
predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, $1, $3); clicon_debug(2,"predicates-> [ expr ]"); }
| { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(2,"predicates->"); }
predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, $1, $3); clicon_debug(2,"predicates-> [ expr ]"); }
| { $$=xp_new(XP_PRED,A_NAN,NULL, NULL, NULL, NULL, NULL); clicon_debug(2,"predicates->"); }
;
primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,0.0, NULL, NULL, $2, NULL); clicon_debug(2,"primaryexpr-> ( expr )"); }
| NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> NUMBER(%lf)", $1); }
| QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" string \""); }
| QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" \""); }
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' string '"); }
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' '"); }
| FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); }
| FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, $3, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); }
primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,NULL, NULL, NULL, $2, NULL); clicon_debug(2,"primaryexpr-> ( expr )"); }
| NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> NUMBER(%s)", $1); /*XXX*/}
| QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" string \""); }
| QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> \" \""); }
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, $2, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' string '"); }
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,NULL, NULL, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> ' '"); }
| FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, NULL, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); }
| FUNCTIONNAME '(' args ')' { $$=xp_new(XP_PRIME_FN,A_NAN,NULL, $1, NULL, $3, NULL);clicon_debug(2,"primaryexpr-> functionname ( arguments )"); }
;
args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, $3);
args : args ',' expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, $3);
clicon_debug(2,"args -> args expr");}
| expr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);
| expr { $$=xp_new(XP_EXP,A_NAN,NULL,NULL,NULL,$1, NULL);
clicon_debug(2,"args -> expr "); }
;

View file

@ -766,7 +766,7 @@ yang_find_myprefix(yang_stmt *ys)
}
if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL)
goto done;
prefix = yprefix->ys_argument;
prefix = yang_argument_get(yprefix);
done:
return prefix;
}

View file

@ -306,7 +306,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;
}
@ -338,7 +338,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_nsc(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
if (xpath_vec(x, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
if (xvec != NULL){
for (i=0; i<xlen; i++)

View file

@ -1490,6 +1490,7 @@ notification_substmt : if_feature_stmt { clicon_debug(2,"notification-substmt -
| typedef_stmt { clicon_debug(2,"notification-substmt -> typedef-stmt"); }
| grouping_stmt { clicon_debug(2,"notification-substmt -> grouping-stmt"); }
| data_def_stmt { clicon_debug(2,"notification-substmt -> data-def-stmt"); }
| unknown_stmt { clicon_debug(2,"notification-substmt -> unknown-stmt");}
| { clicon_debug(2,"notification-substmt -> "); }
;

View file

@ -213,7 +213,7 @@ start_restconf(){
}
stop_restconf(){
sudo pkill -u $wwwuser -f "$clixon_restconf"
sudo pkill -u $wwwuser -f clixon_restconf # Dont use $clixon_restoconf doesnt work in valgrind
if [ $valgrindtest -eq 3 ]; then
sleep 1
checkvalgrind

View file

@ -12,6 +12,8 @@
# 2. example-augment - urn:example:augment - mymod
# (augmented): mandatory-leaf, me, other,
# (uses/grouping): ip, port, lid, lport
# Note augment+state not tested here (need plugin), simple test in test_restconf.sh
#
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -167,7 +169,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg
@ -217,13 +219,12 @@ new "restconf get augment json"
expectpart "$(curl -s -i -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}} '
new "restconf get augment xml"
expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e2</name><type>fddi</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:other>if:fddi</mymod:other><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e3</name><type>fddi</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:me>mymod:you</mymod:me><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface></interfaces>'
expectpart "$(curl -s -i -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK ' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface><name>e2</name><type>fddi</type><mymod:mandatory-leaf xmlns:mymod="urn:example:augment">true</mymod:mandatory-leaf><mymod:other xmlns:mymod="urn:example:augment">if:fddi</mymod:other><mymod:port xmlns:mymod="urn:example:augment">80</mymod:port><mymod:lport xmlns:mymod="urn:example:augment">8080</mymod:lport></interface><interface><name>e3</name><type>fddi</type><mymod:mandatory-leaf xmlns:mymod="urn:example:augment">true</mymod:mandatory-leaf><mymod:me xmlns:mymod="urn:example:augment">mymod:you</mymod:me><mymod:port xmlns:mymod="urn:example:augment">80</mymod:port><mymod:lport xmlns:mymod="urn:example:augment">8080</mymod:lport></interface></interfaces>'
#<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
XML=$(cat <<EOF
<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces" xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
EOF
)
@ -240,10 +241,10 @@ new "restconf POST augment multi-namespace path e2 (middle path)"
expectpart "$(curl -s -X POST -H 'Content-Type: application/yang-data+xml' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 ''
new "restconf GET augment multi-namespace top"
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
new "restconf GET augment multi-namespace level 1"
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","example-augment:ospf":{"reference-bandwidth":23},"example-augment:port":80,"example-augment:lport":8080}\]}'
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080}\]}'
new "restconf GET augment multi-namespace cross"
expectpart "$(curl -si -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1/example-augment:ospf)" 0 'HTTP/1.1 200 OK' '{"example-augment:ospf":{"reference-bandwidth":23}}'

View file

@ -111,7 +111,7 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then
err
fi
sudo pkill clixon_backend # to be sure
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
@ -121,7 +121,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg

125
test/test_cli_submodes.sh Executable file
View file

@ -0,0 +1,125 @@
#!/usr/bin/env bash
# CLIgen mode tests
# Have two modes: AAA and BBB
# Have the following clispec files with syntax for:
# 1) * 2) AAA, 3) BBB, 4) CCC, 5) AAA:BBB, 6) BBB:CCC
# Verify then that modes AAA and BBB have right syntax (also negative)
# AAA should have syntax from 1,2,5 (and not 3,4,6)
# BBB should have syntax from 1,3,5,6 (and not 2,4)
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
# include err() and new() functions and creates $dir
cfg=$dir/conf_yang.xml
clidir=$dir/clidir
if [ ! -d $clidir ]; then
mkdir $clidir
fi
# Use yang in example
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>clixon-example</CLICON_YANG_MODULE_MAIN>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_CLISPEC_DIR>$clidir</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF
# clispec files 1..6 for submodes AAA and BBB as described in top comment
cat <<EOF > $clidir/cli1.cli
CLICON_MODE="*";
cmd1;
EOF
cat <<EOF > $clidir/cli2.cli
CLICON_MODE="AAA";
cmd2;
EOF
cat <<EOF > $clidir/cli3.cli
CLICON_MODE="BBB";
cmd3;
EOF
cat <<EOF > $clidir/cli4.cli
CLICON_MODE="CCC";
cmd4;
EOF
cat <<EOF > $clidir/cli5.cli
CLICON_MODE="AAA:BBB";
cmd5;
EOF
cat <<EOF > $clidir/cli6.cli
CLICON_MODE="BBB:CCC";
cmd6;
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -z -f $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
new "waiting"
wait_backend
fi
m=AAA
# Tests using mode AAA that should pass
for c in 1 2 5; do
new "cli mode $m 1 cmd$c OK"
expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 0 "^$"
done
# Tests using mode AAA that should fail
for c in 3 4 6; do
new "cli mode $m 1 cmd$c Not OK"
expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 255 "^$"
done
m=BBB
# Tests using mode BBB that should pass
for c in 1 3 5 6; do
new "cli mode $m 1 cmd$c OK"
expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 0 "^$"
done
# Tests using mode BBB that should fail
for c in 2 4; do
new "cli mode $m 1 cmd$c Not OK"
expectfn "$clixon_cli -1 -m $m -f $cfg cmd$c" 255 "^$"
done
if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
rm -rf $dir

View file

@ -155,7 +155,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg

View file

@ -127,7 +127,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a

View file

@ -103,7 +103,7 @@ EOF
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a

View file

@ -147,7 +147,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable http basic auth)"
start_restconf -f $cfg -- -a

View file

@ -141,7 +141,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a

View file

@ -149,7 +149,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a

View file

@ -150,7 +150,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a

View file

@ -102,7 +102,7 @@ cat <<EOF > $tmp # new
EOF
new "netconf get config xpath"
expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:ip="urn:ietf:params:xml:ns:yang:ietf-ip"><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><enabled>true</enabled></interface></interfaces></data></rpc-reply>]]>]]>$'
# Too many quotes
cat <<EOF > $tmp # new
@ -110,7 +110,7 @@ cat <<EOF > $tmp # new
EOF
new "netconf get config xpath parent"
expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name><enabled>true</enabled></interface><interface xmlns:ip="urn:ietf:params:xml:ns:yang:ietf-ip"><name>eth1</name><enabled>true</enabled><ip:ipv4><ip:enabled>true</ip:enabled><ip:forwarding>false</ip:forwarding><ip:address><ip:ip>9.2.3.4</ip:ip><ip:prefix-length>24</ip:prefix-length></ip:address></ip:ipv4></interface></interfaces></data></rpc-reply>]]>]]>$'
expecteof "$clixon_netconf -qf $cfg" 0 "$(cat $tmp)" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth/0/0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ip:ipv4 xmlns:ip="urn:ietf:params:xml:ns:yang:ietf-ip"><ip:enabled>true</ip:enabled><ip:forwarding>false</ip:forwarding><ip:address><ip:ip>9.2.3.4</ip:ip><ip:prefix-length>24</ip:prefix-length></ip:address></ip:ipv4></interface></interfaces></data></rpc-reply>]]>]]>$'
new "netconf validate missing type"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
@ -136,7 +136,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><validate><source><candidate/></sou
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config merge"
new "netconf edit config merge eth2"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth2</name><type>ex:eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
# Note, the type here is non-existant identityref, fails on validation
@ -163,7 +163,7 @@ new "netconf edit state operation should fail"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e0</name><oper-status>up</oper-status></interface></interfaces></config></edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error><error-type>protocol</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>State data not allowed</error-message></rpc-error></rpc-reply>]]>]]>"
new "netconf get state operation"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></rpc>]]>]]>" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type=\"xpath\" select=\"/if:interfaces\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\" /></get></rpc>]]>]]>" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status><ex:my-status xmlns:ex="urn:example:clixon"><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status></interface></interfaces></data></rpc-reply>]]>]]>$'
new "netconf lock/unlock"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"

96
test/test_netconf_filter.sh Executable file
View file

@ -0,0 +1,96 @@
#!/usr/bin/env bash
# Test netconf filter, subtree and xpath
# Note subtree namespaces not implemented
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
cfg=$dir/conf_yang.xml
fyang=$dir/filter.yang
cat <<EOF > $cfg
<clixon-config xmlns="http://clicon.org/config">
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
</clixon-config>
EOF
cat <<EOF > $fyang
module filter{
yang-version 1.1;
namespace "urn:example:filter";
prefix fi;
container x{
list y {
key a;
leaf a{
type string;
}
leaf b{
type string;
}
}
}
}
EOF
new "test params: -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
new "waiting"
wait_backend
new "Add two entries"
expecteof "$clixon_netconf -qf $cfg" 0 '<rpc><edit-config><target><candidate/></target><config><x xmlns="urn:example:filter"><y><a>1</a><b>1</b></y><y><a>2</a><b>2</b></y></x></config></edit-config></rpc>]]>]]>' '^<rpc-reply><ok/></rpc-reply>]]>]]>$'
new "netconf commit"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "wrong filter type"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type='foo'><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get></rpc>]]>]]>" '^<rpc-reply><rpc-error><error-tag>operation-failed</error-tag><error-type>applicatio</error-type><error-severity>error</error-severity><error-message>filter type not supported</error-message><error-info>type</error-info></rpc-error></rpc-reply>]]>]]>$'
new "get-config subtree one"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get-config><source><running/></source><filter type='subtree'><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get-config></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:filter"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$'
new "get subtree one"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type='subtree'><x xmlns='urn:example:filter'><y><a>1</a></y></x></filter></get></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:filter"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$'
new "get-config xpath one"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type='xpath' select=\"/fi:x/fi:y[fi:a='1']\" xmlns:fi='urn:example:filter' /></get></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:filter"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$'
new "get xpath one"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><get><filter type='xpath' select=\"/fi:x/fi:y[fi:a='1']\" xmlns:fi='urn:example:filter' /></get></rpc>]]>]]>" '^<rpc-reply><data><x xmlns="urn:example:filter"><y><a>1</a><b>1</b></y></x></data></rpc-reply>]]>]]>$'
if [ $BE -eq 0 ]; then
exit # BE
fi
new "Kill backend"
# Check if premature kill
pid=$(pgrep -u root -f clixon_backend)
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
stop_backend -f $cfg
rm -rf $dir

View file

@ -37,6 +37,7 @@ cat <<EOF > $cfg
<CLICON_YANG_DIR>$OCDIR/lacp</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$OCDIR/lldp</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$OCDIR/local-routing</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$OCDIR/macsec</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$OCDIR/mpls</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$OCDIR/multicast</CLICON_YANG_DIR>
<CLICON_YANG_DIR>$OCDIR/network-instance</CLICON_YANG_DIR>

View file

@ -83,7 +83,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg

View file

@ -61,7 +61,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg
@ -90,7 +90,7 @@ expecteof "time $clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-
new "netconf get test single req"
sel="/if:interfaces/if:interface[if:name='e1']"
msg="<rpc><get><filter type=\"xpath\" select=\"$sel\" xmlns:if=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\"/></get></rpc>]]>]]>"
expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status></interface></interfaces></data></rpc-reply>]]>]]>$'
expecteof "$clixon_netconf -qf $cfg" 0 "$msg" '^<rpc-reply><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><type>ex:eth</type><enabled>true</enabled><oper-status>up</oper-status><ex:my-status xmlns:ex="urn:example:clixon"><ex:int>42</ex:int><ex:str>foo</ex:str></ex:my-status></interface></interfaces></data></rpc-reply>]]>]]>$'
new "netconf get $perfreq single reqs"
{ time -p for (( i=0; i<$perfreq; i++ )); do
@ -101,7 +101,7 @@ done | $clixon_netconf -qf $cfg > /dev/null; } 2>&1 | awk '/real/ {print $2}'
# RESTCONF get
new "restconf get test single req"
expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface":[{"name":"e1","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}
expecteq "$(curl -s -X GET http://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 '{"ietf-interfaces:interface":[{"name":"e1","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}
'
new "restconf get $perfreq single reqs"

28
test/test_perf_xml.sh Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Test: XML performance test
# See https://github.com/clicon/clixon/issues/96
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
: ${clixon_util_xml:="clixon_util_xml"}
# Number of list/leaf-list entries in file
: ${perfnr:=30000}
fxml=$dir/long.xml
new "generate long file $fxml"
echo -n "<rpc-reply><stdout><![CDATA[" > $fxml
for (( i=0; i<$perfnr; i++ )); do
echo "*>i10.0.0.$i/32 10.255.0.20 0 100 0 i" >> $fxml
done
echo "]]></stdout></rpc-reply>" >> $fxml
# 32-bit i386:
#0.37user 1.94system 0:02.47elapsed 93%CPU (0avgtext+0avgdata 9336maxresident)k
#256inputs+0outputs (2major+2049minor)pagefaults 0swa
new "xml parse long CDATA"
expecteof_file "time $clixon_util_xml" 0 "$fxml"
rm -rf $dir

View file

@ -60,7 +60,7 @@ testrun(){
err
fi
# Kill all backends regardless of user or pid files (we mess with them in this test)
sudo pkill clixon_backend
sudo pkill -f clixon_backend
# start backend as user

View file

@ -43,7 +43,7 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then
err
fi
sudo pkill clixon_backend # to be sure
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg -- -s"
start_backend -s init -f $cfg -- -s
fi
@ -52,7 +52,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg
@ -194,12 +194,16 @@ new "restconf Add interfaces subtree eth/0/0 using POST"
expectpart "$(curl -s -X POST http://localhost/restconf/data/ietf-interfaces:interfaces -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}')" 0 ""
new "restconf Check eth/0/0 added config"
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up"}\]}}
'
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}}'
new "restconf Check eth/0/0 GET augmented state level 1"
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 '{"ietf-interfaces:interface":\[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}\]}'
new "restconf Check eth/0/0 GET augmented state level 2"
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0/clixon-example:my-status)" 0 '{"clixon-example:my-status":{"int":42,"str":"foo"}}'
new "restconf Check eth/0/0 added state"
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":\["42","41","43"\]}}
'
expectpart "$(curl -s -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":\["42","41","43"\]}}'
new "restconf Re-post eth/0/0 which should generate error"
expectpart "$(curl -s -X POST -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"data-exists","error-severity":"error","error-message":"Data already exists; cannot create new resource"}}} '
@ -211,7 +215,7 @@ new "Add nothing using POST (expect fail)"
expectpart "$(curl -is -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 'HTTP/1.1 400 Bad Request' '{"ietf-restconf:errors":{"error":{"error-type":"rpc","error-tag":"malformed-message","error-severity":"error","error-message":"The message-body MUST contain exactly one instance of the expected data resource"}}}'
new "restconf Check description added"
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}}
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","description":"The-first-interface","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}}
'
new "restconf delete eth/0/0"
@ -227,7 +231,7 @@ new "restconf Add subtree eth/0/0 using PUT"
expecteq "$(curl -s -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-interfaces:interface":{"name":"eth/0/0","type":"clixon-example:eth","enabled":true}}' http://localhost/restconf/data/ietf-interfaces:interfaces/interface=eth%2f0%2f0)" 0 ""
new "restconf get subtree"
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up"}]}}
expecteq "$(curl -s -G http://localhost/restconf/data/ietf-interfaces:interfaces)" 0 '{"ietf-interfaces:interfaces":{"interface":[{"name":"eth/0/0","type":"clixon-example:eth","enabled":true,"oper-status":"up","clixon-example:my-status":{"int":42,"str":"foo"}}]}}
'
new "restconf rpc using POST json"

View file

@ -74,7 +74,7 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then
err
fi
sudo pkill clixon_backend # to be sure
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
@ -83,7 +83,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg

View file

@ -120,7 +120,7 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then
err
fi
sudo pkill clixon_backend # to be sure
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
fi
@ -129,7 +129,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg

View file

@ -69,7 +69,7 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then
err
fi
sudo pkill clixon_backend # to be sure
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg -- -s"
start_backend -s init -f "$cfg" -- -s
fi
@ -78,7 +78,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg
@ -99,7 +99,7 @@ expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://loca
# This just catches the header and the jukebox module, the RFC has foo and bar which
# seems wrong to recreate
new "B.1.2. Retrieve the Server Module Information"
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":' '"module":\[{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"}'
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+json' http://localhost/restconf/data/ietf-yang-library:modules-state)" 0 "HTTP/1.1 200 OK" 'Cache-Control: no-cache' "Content-Type: application/yang-data+json" '{"ietf-yang-library:modules-state":{"module-set-id":"0","module":\[{"name":"clixon-lib","revision":"2019-08-13","namespace":"http://clicon.org/lib","conformance-type":"implement"},{"name":"clixon-rfc5277","revision":"2008-07-01","namespace":"urn:ietf:params:xml:ns:netmod:notification","conformance-type":"implement"},{"name":"example-events","revision":\[null\],"namespace":"urn:example:events","conformance-type":"implement"},{"name":"example-jukebox","revision":"2016-08-15","namespace":"http://example.com/ns/example-jukebox","conformance-type":"implement"'
new "B.1.3. Retrieve the Server Capability Information"
expectpart "$(curl -si -X GET -H 'Accept: application/yang-data+xml' http://localhost/restconf/data/ietf-restconf-monitoring:restconf-state/capabilities)" 0 "HTTP/1.1 200 OK" "Content-Type: application/yang-data+xml" 'Cache-Control: no-cache' '<capabilities xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf-monitoring"><capability>urn:ietf:params:restconf:capability:defaults:1.0?basic-mode=explicit</capability><capability>urn:ietf:params:restconf:capability:depth</capability>

View file

@ -71,7 +71,7 @@ if [ $BE -ne 0 ]; then
if [ $? -ne 0 ]; then
err
fi
sudo pkill clixon_backend # to be sure
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg"
start_backend -s init -f $cfg
@ -81,7 +81,7 @@ new "waiting"
wait_backend
new "kill old restconf daemon"
sudo pkill -u $wwwuser clixon_restconf
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon"
start_restconf -f $cfg

Some files were not shown because too many files have changed in this diff Show more