Merge branch 'master' into feature_openconfig_compress

This commit is contained in:
Olof Hagsand 2021-10-22 09:23:48 +02:00 committed by GitHub
commit 9d8d9ac042
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 997 additions and 139 deletions

View file

@ -68,6 +68,10 @@ Developers may need to change their code
### Minor features
* Plugin context check before and after all callbacks.
* Check blocked signals and signal handlers
* Check termios settings
* Any changes to context are logged at loglevel WARNING
* Added set/get pointer API to clixon_data:
* clicon_ptr_get(), clicon_ptr_set(),
* Restconf YANG PATCH according to RFC 8072

View file

@ -931,16 +931,21 @@ from_client_restart_one(clicon_handle h,
yang_stmt *yspec;
int i;
cxobj *xn;
plugin_context_t *pc = NULL;
yspec = clicon_dbspec_yang(h);
if (xmldb_db_reset(h, db) < 0)
goto done;
/* Application may define extra xml in its reset function*/
if ((resetfn = clixon_plugin_api_get(cp)->ca_reset) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if ((retval = resetfn(h, db)) < 0) {
clicon_debug(1, "plugin_start() failed");
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
@ -1021,6 +1026,8 @@ from_client_restart_one(clicon_handle h,
goto fail;
retval = 1;
done:
if (pc)
free(pc);
if (td){
xmldb_get0_free(h, &td->td_target);
transaction_free(td);

View file

@ -284,9 +284,10 @@ get_client_statedata(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @param[in] xret Result XML tree
* @param[in] xvec xpath lookup result on xret
* @param[in] xlen length of xvec
* @param[in] xpath XPath point to object to get
* @param[in] nsc Namespace context of xpath
* @param[out] x1p Pointer to first matching object if any
* @retval 0 OK
* @retval -1 Error
*/
@ -294,17 +295,14 @@ static int
filter_xpath_again(clicon_handle h,
yang_stmt *yspec,
cxobj *xret,
cxobj **xvec,
size_t xlen,
char *xpath,
cvec *nsc,
cxobj **x1p)
cvec *nsc)
{
int retval = -1;
cxobj **xvec = NULL;
size_t xlen;
int i;
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,
* otherwise return complete tree.
@ -326,12 +324,8 @@ filter_xpath_again(clicon_handle h,
/* Add default recursive values */
if (xml_default_recurse(xret, 0) < 0)
goto done;
if (xlen && x1p)
*x1p = xvec[0];
retval = 0;
done:
if (xvec)
free(xvec);
return retval;
}
@ -339,6 +333,8 @@ filter_xpath_again(clicon_handle h,
*
* @param[in] h Clicon handle
* @param[in] xret Result XML tree
* @param[in] xvec xpath lookup result on xret
* @param[in] xlen length of xvec
* @param[in] xpath XPath point to object to get
* @param[in] nsc Namespace context of xpath
* @param[in] username User name for NACM access
@ -350,6 +346,8 @@ filter_xpath_again(clicon_handle h,
static int
get_nacm_and_reply(clicon_handle h,
cxobj *xret,
cxobj **xvec,
size_t xlen,
char *xpath,
cvec *nsc,
char *username,
@ -357,15 +355,11 @@ get_nacm_and_reply(clicon_handle h,
cbuf *cbret)
{
int retval = -1;
cxobj **xvec = NULL;
size_t xlen;
cxobj *xnacm = NULL;
/* Pre-NACM access step */
xnacm = clicon_nacm_cache(h);
if (xnacm != NULL){ /* Do NACM validation */
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* NACM datanode/module read validation */
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
goto done;
@ -383,8 +377,6 @@ get_nacm_and_reply(clicon_handle h,
cprintf(cbret, "</rpc-reply>");
retval = 0;
done:
if (xvec)
free(xvec);
return retval;
}
@ -454,11 +446,7 @@ get_list_pagination(clicon_handle h,
int retval = -1;
uint32_t offset = 0;
uint32_t limit = 0;
char *direction = NULL;
char *sort = NULL;
char *where = NULL;
cbuf *cbpath = NULL;
cxobj *x;
int list_config;
yang_stmt *ylist;
cxobj *xerr = NULL;
@ -468,7 +456,15 @@ get_list_pagination(clicon_handle h,
int ret;
uint32_t iddb; /* DBs lock, if any */
int locked;
cxobj *x1 = NULL;
cbuf *cberr = NULL;
cxobj **xvec = NULL;
size_t xlen;
#ifdef NOTYET
cxobj *x;
char *direction = NULL;
char *sort = NULL;
char *where = NULL;
#endif
/* Check if list/leaf-list */
if (yang_path_arg(yspec, xpath, &ylist) < 0)
@ -504,11 +500,6 @@ get_list_pagination(clicon_handle h,
goto done;
goto ok;
}
#if 0 /* XXX For now state lists are not implemenetd */
if (netconf_operation_not_supported(cbret, "protocol", "List pagination for state lists is not yet implemented") < 0)
goto done;
goto ok;
#endif
}
/* offset */
if ((ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0)
@ -516,6 +507,7 @@ get_list_pagination(clicon_handle h,
/* limit */
if (ret && (ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0)
goto done;
#ifdef NOTYET
/* direction */
if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){
direction = xml_body(x);
@ -535,7 +527,7 @@ get_list_pagination(clicon_handle h,
/* where */
if (ret && (x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL)
where = xml_body(x);
#endif
/* Read config */
switch (content){
case CONTENT_CONFIG: /* config data only */
@ -553,8 +545,10 @@ get_list_pagination(clicon_handle h,
cprintf(cbpath, "%s", xpath);
else
cprintf(cbpath, "/");
#ifdef NOTYET
if (where)
cprintf(cbpath, "[%s]", where);
#endif
if (offset){
cprintf(cbpath, "[%u <= position()", offset);
if (limit)
@ -607,11 +601,40 @@ get_list_pagination(clicon_handle h,
offset, limit,
xret)) < 0)
goto done;
if (ret == 0)
if (ret == 0){
if ((cberr = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* error reason should be in clicon_err_reason */
cprintf(cberr, "Internal error, pagination state callback invalid return : %s",
clicon_err_reason);
if (netconf_operation_failed_xml(&xerr, "application", cbuf_get(cberr)) < 0)
goto done;
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
goto ok;
}
/* System makes the binding */
if ((ret = xml_bind_yang(xret, YB_MODULE, yspec, &xerr)) < 0)
goto done;
if (ret == 0){
if (clicon_debug_get() && xret)
clicon_log_xml(LOG_DEBUG, xret, "Yang bind pagination state");
if (clixon_netconf_internal_error(xerr,
". Internal error, state callback returned invalid XML",
NULL) < 0)
goto done;
if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0)
goto done;
goto ok;
}
}
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* Help function to filter out anything that is outside of xpath */
if (filter_xpath_again(h, yspec, xret, xpath, nsc, &x1) < 0)
if (filter_xpath_again(h, yspec, xret, xvec, xlen, xpath, nsc) < 0)
goto done;
#ifdef LIST_PAGINATION_REMAINING
/* Add remaining attribute Sec 3.1.5:
@ -639,17 +662,21 @@ get_list_pagination(clicon_handle h,
cbuf_free(cba);
}
#endif /* LIST_PAGINATION_REMAINING */
if (get_nacm_and_reply(h, xret, xpath, nsc, username, depth, cbret) < 0)
if (get_nacm_and_reply(h, xret, xvec, xlen, xpath, nsc, username, depth, cbret) < 0)
goto done;
ok:
retval = 0;
done:
if (xvec)
free(xvec);
if (cbmsg)
cbuf_free(cbmsg);
if (cbpath)
cbuf_free(cbpath);
if (xerr)
xml_free(xerr);
if (cberr)
cbuf_free(cberr);
if (xret)
xml_free(xret);
return retval;
@ -696,6 +723,10 @@ get_common(clicon_handle h,
int list_pagination = 0;
char *valstr;
cxobj *x;
#if 1
cxobj **xvec = NULL;
size_t xlen;
#endif
clicon_debug(1, "%s", __FUNCTION__);
username = clicon_username_get(h);
@ -852,14 +883,18 @@ get_common(clicon_handle h,
if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
}
if (filter_xpath_again(h, yspec, xret, xpath, nsc, NULL) < 0)
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
if (get_nacm_and_reply(h, xret, xpath, nsc, username, depth, cbret) < 0)
if (filter_xpath_again(h, yspec, xret, xvec, xlen, xpath, nsc) < 0)
goto done;
if (get_nacm_and_reply(h, xret, xvec, xlen, xpath, nsc, username, depth, cbret) < 0)
goto done;
ok:
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec)
free(xvec);
if (xret)
xml_free(xret);
if (cbreason)

View file

@ -78,19 +78,26 @@ clixon_plugin_reset_one(clixon_plugin_t *cp,
clicon_handle h,
char *db)
{
int retval = -1;
plgreset_t *fn; /* callback */
int retval = -1;
plgreset_t *fn; /* callback */
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_reset) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, db) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Reset callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -108,6 +115,7 @@ clixon_plugin_reset_all(clicon_handle h,
int retval = -1;
clixon_plugin_t *cp = NULL;
clicon_debug(1, "%s", __FUNCTION__);
/* Loop through all plugins, call callbacks in each */
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_reset_one(cp, h, db) < 0)
@ -130,8 +138,12 @@ clixon_plugin_pre_daemon_one(clixon_plugin_t *cp,
{
int retval = -1;
plgdaemon_t *fn; /* Daemonize plugin callback function */
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_pre_daemon) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Pre-daemon callback in plugin:\
@ -139,9 +151,13 @@ clixon_plugin_pre_daemon_one(clixon_plugin_t *cp,
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -160,6 +176,7 @@ clixon_plugin_pre_daemon_all(clicon_handle h)
int retval = -1;
clixon_plugin_t *cp = NULL;
clicon_debug(1, "%s", __FUNCTION__);
/* Loop through all plugins, call callbacks in each */
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_pre_daemon_one(cp, h) < 0)
@ -182,17 +199,24 @@ clixon_plugin_daemon_one(clixon_plugin_t *cp,
{
int retval = -1;
plgdaemon_t *fn; /* Daemonize plugin callback function */
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_daemon) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Daemon callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -213,6 +237,7 @@ clixon_plugin_daemon_all(clicon_handle h)
int retval = -1;
clixon_plugin_t *cp = NULL;
clicon_debug(1, "%s", __FUNCTION__);
/* Loop through all plugins, call callbacks in each */
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (clixon_plugin_daemon_one(cp, h) < 0)
@ -258,22 +283,28 @@ clixon_plugin_statedata_one(clixon_plugin_t *cp,
int retval = -1;
plgstatedata_t *fn; /* Plugin statedata fn */
cxobj *x = NULL;
plugin_context_t *pc = NULL;
clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp));
if ((fn = clixon_plugin_api_get(cp)->ca_statedata) != NULL){
if ((x = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
goto done;
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, nsc, xpath, x) < 0){
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: State callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, clixon_plugin_name_get(cp));
goto fail; /* Dont quit here on user callbacks */
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
if (xp && x)
*xp = x;
retval = 1;
done:
if (pc)
free(pc);
return retval;
fail:
retval = 0;
@ -406,14 +437,20 @@ clixon_plugin_lockdb_one(clixon_plugin_t *cp,
{
int retval = -1;
plglockdb_t *fn; /* Plugin statedata fn */
plugin_context_t *pc = NULL;
clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp));
if ((fn = clixon_plugin_api_get(cp)->ca_lockdb) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, db, lock, id) < 0)
goto done;
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -576,17 +613,24 @@ plugin_transaction_begin_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_begin) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -605,6 +649,7 @@ plugin_transaction_begin_all(clicon_handle h,
int retval = -1;
clixon_plugin_t *cp = NULL;
clicon_debug(1, "%s", __FUNCTION__);
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (plugin_transaction_begin_one(cp, h, td) < 0)
goto done;
@ -628,17 +673,24 @@ plugin_transaction_validate_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_validate) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -679,17 +731,24 @@ plugin_transaction_complete_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_complete) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -762,17 +821,24 @@ plugin_transaction_commit_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -821,17 +887,24 @@ plugin_transaction_commit_done_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit_done) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -872,17 +945,24 @@ plugin_transaction_end_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_end) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -899,6 +979,7 @@ plugin_transaction_end_all(clicon_handle h,
int retval = -1;
clixon_plugin_t *cp = NULL;
clicon_debug(1, "%s", __FUNCTION__);
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (plugin_transaction_end_one(cp, h, td) < 0)
goto done;
@ -915,17 +996,24 @@ plugin_transaction_abort_one(clixon_plugin_t *cp,
{
int retval = -1;
trans_cb_t *fn;
plugin_context_t *pc = NULL;
if ((fn = clixon_plugin_api_get(cp)->ca_trans_abort) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, (transaction_data)td) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
__FUNCTION__, clixon_plugin_name_get(cp));
goto done;
}
if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -942,6 +1030,7 @@ plugin_transaction_abort_all(clicon_handle h,
int retval = -1;
clixon_plugin_t *cp = NULL;
clicon_debug(1, "%s", __FUNCTION__);
while ((cp = clixon_plugin_each(h, cp)) != NULL) {
if (plugin_transaction_abort_one(cp, h, td) < 0)
; /* dont abort on error */

View file

@ -44,6 +44,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#include <sys/stat.h>

View file

@ -52,6 +52,7 @@
#include <sys/time.h>
#include <regex.h>
#include <syslog.h>
#include <signal.h>
#include <netinet/in.h>
#include <limits.h>

View file

@ -51,6 +51,7 @@
#include <ctype.h>
#include <sys/types.h>
#include <regex.h>
#include <signal.h>
#include <netinet/in.h>
#include <limits.h>

View file

@ -171,7 +171,7 @@ cli_xml2file(cxobj *xn,
if (xn == NULL)
goto ok;
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, &opext) < 0) {
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) {
goto ok;
}
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
@ -273,13 +273,13 @@ cli_xml2txt(cxobj *xn,
clicon_err(OE_XML, EINVAL, "xn or fn is NULL");
goto done;
}
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, &opext) < 0) {
goto ok;
}
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
goto ok;
}
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) {
goto ok;
}
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
goto ok;
}
xc = NULL; /* count children (elements and bodies, not attributes) */
while ((xc = xml_child_each(xn, xc, -1)) != NULL)
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
@ -342,7 +342,7 @@ cli_xml2cli(cxobj *xn,
if ((ys = xml_spec(xn)) == NULL)
goto ok;
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, &opext) < 0) {
if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) {
goto ok;
}
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){

View file

@ -165,17 +165,17 @@ cli_signal_flush(clicon_handle h)
sigfn_t h1, h2, h3, h4;
set_signal (SIGTSTP, SIG_IGN, &h1);
set_signal (SIGQUIT, SIG_IGN, &h2);
set_signal (SIGCHLD, SIG_IGN, &h3);
set_signal (SIGINT, SIG_IGN, &h4);
set_signal(SIGTSTP, SIG_IGN, &h1);
set_signal(SIGQUIT, SIG_IGN, &h2);
set_signal(SIGCHLD, SIG_IGN, &h3);
set_signal(SIGINT, SIG_IGN, &h4);
cli_signal_unblock (h);
set_signal (SIGTSTP, h1, NULL);
set_signal (SIGQUIT, h2, NULL);
set_signal (SIGCHLD, h3, NULL);
set_signal (SIGINT, h4, NULL);
set_signal(SIGTSTP, h1, NULL);
set_signal(SIGQUIT, h2, NULL);
set_signal(SIGCHLD, h3, NULL);
set_signal(SIGINT, h4, NULL);
cli_signal_block (h);
}
@ -562,9 +562,10 @@ cli_start_shell(clicon_handle h,
int retval = -1;
char bcmd[128];
cg_var *cv1 = cvec_i(vars, 1);
sigset_t oldsigset;
struct sigaction oldsigaction[32] = {0,};
cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL);
if ((pw = getpwuid(getuid())) == NULL){
clicon_err(OE_UNIX, errno, "getpwuid");
goto done;
@ -575,6 +576,9 @@ cli_start_shell(clicon_handle h,
goto done;
}
endpwent();
if (clixon_signal_save(&oldsigset, oldsigaction) < 0)
goto done;
cli_signal_flush(h);
cli_signal_unblock(h);
if (cmd){
@ -598,6 +602,8 @@ cli_start_shell(clicon_handle h,
goto done;
}
#endif
if (clixon_signal_restore(&oldsigset, oldsigaction) < 0)
goto done;
retval = 0;
done:
return retval;

View file

@ -78,6 +78,7 @@ You can see which CLISPEC it generates via clixon_cli -D 2:
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <signal.h>
#include <sys/param.h>
/* cligen */
@ -120,7 +121,7 @@ cli_expand_var_generate(clicon_handle h,
int retval = -1;
char *api_path_fmt = NULL, *opext = NULL;
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0)
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0)
goto done;
if (opext && strcmp(opext, "hide-database") == 0) {
retval = 1;
@ -761,7 +762,7 @@ yang2cli_leaf(clicon_handle h,
}
cprintf(cb, "%*s", level*3, "");
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0)
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0)
goto done;
if (gt == GT_VARS || gt == GT_ALL || gt == GT_HIDE || gt == GT_OC_COMPRESS){
cprintf(cb, "%s", yang_argument_get(ys));
@ -866,14 +867,14 @@ yang2cli_container(clicon_handle h,
if (cli_callback_generate(h, ys, cb) < 0)
goto done;
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0)
goto done;
if (opext != NULL && strcmp(opext, "hide") == 0){
cprintf(cb, ",hide");
}
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0)
goto done;
if (opext != NULL && strcmp(opext, "hide") == 0){
cprintf(cb, ",hide");
}
if (opext != NULL && strcmp(opext, "hide-database-auto-completion") == 0){
cprintf(cb, ",hide-database-auto-completion");
cprintf(cb, ",hide-database-auto-completion");
}
cprintf(cb, ";{\n");
}
@ -882,10 +883,10 @@ yang2cli_container(clicon_handle h,
while ((yc = yn_each(ys, yc)) != NULL)
if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0)
goto done;
if (hide == 0 && hide_oc == 0)
cprintf(cb, "%*s}\n", level*3, "");
retval = 0;
done:
if (hide == 0 && hide_oc == 0)
cprintf(cb, "%*s}\n", level*3, "");
retval = 0;
done:
if (helptext)
free(helptext);
return retval;
@ -933,7 +934,7 @@ yang2cli_list(clicon_handle h,
yang2cli_helptext(cb, helptext);
}
/* Look for autocli-op defined in clixon-lib.yang */
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0)
if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0)
goto done;
if (opext != NULL && strcmp(opext, "hide") == 0){
cprintf(cb, ",hide");

View file

@ -51,6 +51,7 @@
#include <dlfcn.h>
#include <dirent.h>
#include <grp.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>

View file

@ -52,6 +52,7 @@
#include <dirent.h>
#include <libgen.h>
#include <grp.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
@ -69,7 +70,6 @@
#include "cli_handle.h"
#include "cli_generate.h"
/*
* Constants
*/
@ -522,6 +522,53 @@ cli_handler_err(FILE *f)
return 0;
}
/*! Variant of eval for context checking
* @see cligen_eval
*/
int
cligen_clixon_eval(cligen_handle h,
cg_obj *co,
cvec *cvv)
{
struct cg_callback *cc;
int retval = 0;
cvec *argv;
plugin_context_t *pc = NULL;
if (h)
cligen_co_match_set(h, co);
for (cc = co->co_callbacks; cc; cc=cc->cc_next){
/* Vector cvec argument to callback */
if (cc->cc_fn_vec){
argv = cc->cc_cvec ? cvec_dup(cc->cc_cvec) : NULL;
cligen_fn_str_set(h, cc->cc_fn_str);
if ((pc = plugin_context_get()) == NULL)
break;
if ((retval = (*cc->cc_fn_vec)(
cligen_userhandle(h)?cligen_userhandle(h):h,
cvv,
argv)) < 0){
if (argv != NULL)
cvec_free(argv);
cligen_fn_str_set(h, NULL);
break;
}
if (plugin_context_check(pc, "CLIgen", cc->cc_fn_str) < 0)
break;
if (pc){
free(pc);
pc = NULL;
}
if (argv != NULL)
cvec_free(argv);
cligen_fn_str_set(h, NULL);
}
}
if (pc)
free(pc);
return retval;
}
/*! Evaluate a matched command
* @param[in] h Clicon handle
* @param[in] cmd The command string
@ -541,7 +588,8 @@ clicon_eval(clicon_handle h,
cli_output_reset();
if (!cligen_exiting(cli_cligen(h))) {
clicon_err_reset();
if ((retval = cligen_eval(cli_cligen(h), match_obj, cvv)) < 0) {
if ((retval = cligen_clixon_eval(cli_cligen(h), match_obj, cvv)) < 0) {
#if 0 /* This is removed since we get two error messages on failure.
But maybe only sometime?
Both a real log when clicon_err is called, and the here again.

View file

@ -919,6 +919,7 @@ cli_pagination(clicon_handle h,
uint32_t limit = 0;
cxobj **xvec = NULL;
size_t xlen;
int locked = 0;
if (cvec_len(argv) != 5){
clicon_err(OE_PLUGIN, 0, "Expected usage: <xpath> <prefix> <namespace> <format> <limit>");
@ -950,6 +951,7 @@ cli_pagination(clicon_handle h,
goto done;
if (clicon_rpc_lock(h, "running") < 0)
goto done;
locked++;
for (i = 0;; i++){
if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc,
CONTENT_ALL,
@ -1000,10 +1002,10 @@ cli_pagination(clicon_handle h,
xvec = NULL;
}
} /* for i */
if (clicon_rpc_unlock(h, "running") < 0)
goto done;
retval = 0;
done:
if (locked)
clicon_rpc_unlock(h, "running");
if (xvec)
free(xvec);
if (xret)

View file

@ -44,6 +44,7 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>

View file

@ -47,6 +47,7 @@
#include <dirent.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/time.h>

View file

@ -514,7 +514,10 @@ example_statefile(clicon_handle h,
xml_flag_set(x1, XML_FLAG_MARK);
xml_apply_ancestor(x1, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
if (xml_copy_marked(xt, xstate) < 0) /* Copy the marked elements */
/* Copy the marked elements:
* note is yang-aware for copying of keys which means XML must be bound
*/
if (xml_copy_marked(xt, xstate) < 0)
goto done;
/* Unmark original tree */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
@ -614,6 +617,9 @@ example_pagination(void *h0,
xml_flag_set(x1, XML_FLAG_MARK);
xml_apply_ancestor(x1, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
/* Copy the marked elements:
* note is yang-aware for copying of keys which means XML must be bound
*/
if (xml_copy_marked(xt, xstate) < 0) /* Copy the marked elements */
goto done;
/* Unmark original tree */
@ -1160,6 +1166,7 @@ example_daemon(clicon_handle h)
int ret;
FILE *fp = NULL;
yang_stmt *yspec;
cxobj *xerr = NULL;
/* Read state file (or should this be in init/start?) */
if (_state && _state_file && _state_file_cached){
@ -1168,8 +1175,14 @@ example_daemon(clicon_handle h)
clicon_err(OE_UNIX, errno, "open(%s)", _state_file);
goto done;
}
if ((ret = clixon_xml_parse_file(fp, YB_MODULE, yspec, &_state_xml_cache, NULL)) < 1)
/* Need to be yang bound for eg xml_copy_marked() in example_pagination
*/
if ((ret = clixon_xml_parse_file(fp, YB_MODULE, yspec, &_state_xml_cache, &xerr)) < 0)
goto done;
if (ret == 0){
xml_print(stderr, xerr);
goto done;
}
}
retval = 0;
done:

View file

@ -40,6 +40,7 @@
#include <errno.h>
#include <syslog.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/param.h>

View file

@ -1,12 +1,43 @@
/*
* Copyright 2021 Rubicon Communications LLC (Netgate)
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2021 Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* @see https://github.com/dcornejo/dispatcher
*/
#ifndef DISPATCH_DISPATCHER_H
#define DISPATCH_DISPATCHER_H
#ifndef _CLIXON_DISPATCH_DISPATCHER_H
#define _CLIXON_DISPATCH_DISPATCHER_H
/*! prototype for a function to handle a path
/*! Prototype for a function to handle a path
* minimally needs the path it's working on, but probably
* we want to hand down cached data somehow
* @param[in] h Generic handler
@ -74,5 +105,6 @@ struct _dispatcher_entry {
int dispatcher_register_handler(dispatcher_entry_t **root, dispatcher_definition *x);
int dispatcher_call_handlers(dispatcher_entry_t *root, void *handle, char *path, void *user_args);
int dispatcher_free(dispatcher_entry_t *root);
int dispatcher_print(FILE *f, int level, dispatcher_entry_t *root);
#endif /* DISPATCH_DISPATCHER_H */
#endif /* _CLIXON_DISPATCH_DISPATCHER_H */

View file

@ -51,7 +51,6 @@
* Types
*/
/*! Registered RPC callback function
* @param[in] h Clicon handle
* @param[in] xn Request: <rpc><xn></rpc>
@ -201,8 +200,8 @@ typedef int (plgreset_t)(clicon_handle h, const char *db);
* A complete valid XML tree is created by the plugin and sent back via xtop, which is merged
* into a complete state tree by the system.
* The plugin should ensure that xpath is matched (using namspace context nsc)
* This callback may be replaced with a "dispatcher" type API in the future where the
* XPath binding is stricter, similar to the pagination API.
* XXX: This callback may be replaced with a "dispatcher" type API in the future where the
* XPath binding is stricter, similar to the pagination API.
*
* @param[in] h Clicon handle
* @param[in] xpath Part of state requested
@ -338,6 +337,7 @@ struct clixon_plugin_api{
#define ca_trans_abort u.cau_backend.cb_trans_abort
#define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade
/*
* Macros
*/
@ -349,6 +349,10 @@ typedef struct clixon_plugin_api clixon_plugin_api;
* The internal struct is defined in clixon_plugin.c */
typedef struct clixon_plugin clixon_plugin_t;
/*! Structure for checking status before and after a plugin call
* The internal struct is defined in clixon_plugin.c */
typedef struct plugin_context plugin_context_t;
/*
* Prototypes
*/
@ -361,7 +365,6 @@ typedef struct clixon_plugin clixon_plugin_t;
*/
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
clixon_plugin_api *clixon_plugin_api_get(clixon_plugin_t *cp);
char *clixon_plugin_name_get(clixon_plugin_t *cp);
plghndl_t clixon_plugin_handle_get(clixon_plugin_t *cp);
@ -376,6 +379,9 @@ int clixon_plugins_load(clicon_handle h, const char *function, const char *dir,
int clixon_pseudo_plugin(clicon_handle h, const char *name, clixon_plugin_t **cpp);
plugin_context_t * plugin_context_get(void);
int plugin_context_check(plugin_context_t *pc, const char *name, const char *fn);
int clixon_plugin_start_one(clixon_plugin_t *cp, clicon_handle h);
int clixon_plugin_start_all(clicon_handle h);

View file

@ -47,6 +47,8 @@ typedef void (*sigfn_t)(int);
* Prototypes
*/
int set_signal(int signo, void (*handler)(int), void (**oldhandler)(int));
int clixon_signal_save(sigset_t *sigset, struct sigaction sigaction_vec[32]);
int clixon_signal_restore(sigset_t *sigset, struct sigaction sigaction_vec[32]);
void clicon_signal_block(int);
void clicon_signal_unblock(int);

View file

@ -270,6 +270,6 @@ int yang_type_cache_get(yang_stmt *ytype, yang_stmt **resolved, int *opti
int yang_type_cache_set(yang_stmt *ys, yang_stmt *resolved, int options, cvec *cvv,
cvec *patterns, uint8_t fraction);
yang_stmt *yang_anydata_add(yang_stmt *yp, char *name);
int yang_extension_value(yang_stmt *ys, char *name, char *ns, char **value);
int yang_extension_value(yang_stmt *ys, char *name, char *ns, int *exist, char **value);
#endif /* _CLIXON_YANG_H_ */

View file

@ -227,6 +227,11 @@ clicon_data_cvec_set(clicon_handle h,
const char *name,
cvec *cvv)
{
cvec *cvv0 = NULL;
clicon_ptr_get(h, name, (void**)&cvv0);
if (cvv0)
cvec_free(cvv0);
return clicon_ptr_set(h, name, cvv);
}
@ -238,6 +243,11 @@ int
clicon_data_cvec_del(clicon_handle h,
const char *name)
{
cvec *cvv = NULL;
clicon_ptr_get(h, name, (void**)&cvv);
if (cvv)
cvec_free(cvv);
return clicon_ptr_del(h, name);
}
@ -377,9 +387,13 @@ clicon_nacm_ext(clicon_handle h)
*/
int
clicon_nacm_ext_set(clicon_handle h,
cxobj *xn)
cxobj *x)
{
return clicon_ptr_set(h, "nacm_xml", xn);
cxobj *x0 = NULL;
if ((x0 = clicon_nacm_ext(h)) != NULL)
xml_free(x0);
return clicon_ptr_set(h, "nacm_xml", x);
}
/*! Get NACM (rfc 8341) XML parse tree cache

View file

@ -1,5 +1,36 @@
/*
* Copyright 2021 Rubicon Communications LLC (Netgate)
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2021 Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* @see https://github.com/dcornejo/dispatcher
*/
@ -37,10 +68,6 @@
* [b=]
* [b]
*
* NOTE 1: there is not a mechanism to free the created structures since
* it is intended that this tree is created only at startup. if use case
* changes, this function is trivial.
*
* NOTE 2: there is no attempt to optimize list searching here, sorry. I
* do not think that the known use cases will get big enough to make the
* tree get too large. I do not recommend that you encode every possible
@ -285,11 +312,8 @@ get_entry(dispatcher_entry_t *root,
/* some elements may have keys defined, strip them off */
for (int i = 0; i < split_path_len; i++) {
char *kptr = strchr(split_path_list[i], '=');
if ((kptr != NULL) && (*kptr == '=')) {
*(kptr + 1) = 0;
}
char *kptr = split_path_list[i];
strsep(&kptr, "=[]");
}
/* search down the tree */
@ -411,10 +435,10 @@ dispatcher_register_handler(dispatcher_entry_t **root,
*
* @param[in] handle
* @param[in] root
* @param[in] path
* @retval 1 OK
* @retval 0 Invalid
* @retval -1 Error
* @param[in] path Note must be on the form: /a/b (no keys)
* @retval 1 OK
* @retval 0 Invalid
* @retval -1 Error
*/
int
dispatcher_call_handlers(dispatcher_entry_t *root,
@ -423,8 +447,12 @@ dispatcher_call_handlers(dispatcher_entry_t *root,
void *user_args)
{
int ret = 0;
dispatcher_entry_t *best = get_entry(root, path);
dispatcher_entry_t *best;
if ((best = get_entry(root, path)) == NULL){
errno = ENOENT;
return -1;
}
if (best->children != NULL) {
call_handler_helper(best->children, handle, path, user_args);
}
@ -450,3 +478,24 @@ dispatcher_free(dispatcher_entry_t *root)
free(root);
return 0;
}
/*! Pretty-print dispatcher tree
*/
#define INDENT 3
int
dispatcher_print(FILE *f,
int level,
dispatcher_entry_t *de)
{
fprintf(f, "%*s%s", level*INDENT, "", de->node_name);
if (de->handler)
fprintf(f, " %p", de->handler);
if (de->arg)
fprintf(f, " (%p)", de->arg);
fprintf(f, "\n");
if (de->children)
dispatcher_print(f, level+1, de->children);
if (de->peer)
dispatcher_print(f, level, de->peer);
return 0;
}

View file

@ -48,6 +48,7 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/time.h>

View file

@ -43,10 +43,12 @@
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <dlfcn.h>
#include <dirent.h>
#include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/param.h>
@ -69,6 +71,15 @@
/*
* Private types
*/
/*! Structure for checking status before and after a plugin call
* Currently signal settings: blocked and handlers, and termios
* @see plugin_context_check
*/
struct plugin_context {
sigset_t pc_sigset; /* See sigprocmask(2) */
struct sigaction pc_sigaction_vec[32]; /* See sigaction(2) */
struct termios pc_termios; /* See termios(3) */
};
/* Internal plugin structure with dlopen() handle and plugin_api
* This is an internal type, not exposed in the API
@ -327,9 +338,10 @@ plugin_load_one(clicon_handle h,
void *handle = NULL;
plginit2_t *initfn;
clixon_plugin_api *api = NULL;
clixon_plugin_t *cp = NULL;
clixon_plugin_t *cp = NULL;
char *name;
char *p;
plugin_context_t *pc = NULL;
clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function);
dlerror(); /* Clear any existing error */
@ -348,6 +360,9 @@ plugin_load_one(clicon_handle h,
goto done;
}
clicon_err_reset();
if ((pc = plugin_context_get()) < 0)
goto done;
if ((api = initfn(h)) == NULL) {
if (!clicon_errno){ /* if clicon_err() is not called then log and continue */
clicon_log(LOG_DEBUG, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
@ -359,6 +374,9 @@ plugin_load_one(clicon_handle h,
goto done;
}
}
if (plugin_context_check(pc, file, __FUNCTION__) < 0)
goto done;
/* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
if ((cp = (clixon_plugin_t *)malloc(sizeof(struct clixon_plugin))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
@ -382,6 +400,8 @@ plugin_load_one(clicon_handle h,
retval = 1;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (pc)
free(pc);
if (retval != 1 && handle)
dlclose(handle);
if (cp)
@ -483,6 +503,157 @@ done:
return retval;
}
/*! Get system context, eg signal procmask (for blocking) and sigactions
* Call this before a plugin
* @retval pc Plugin context structure, use free() to deallocate
* @retval NULL Error
* @see plugin_context_check
* */
plugin_context_t *
plugin_context_get(void)
{
int i;
struct plugin_context *pc = NULL;
if ((pc = malloc(sizeof(*pc))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(pc, 0, sizeof(*pc));
if (sigprocmask(0, NULL, &pc->pc_sigset) < 0){
clicon_err(OE_UNIX, errno, "sigprocmask");
goto done;
}
for (i=1; i<32; i++){
if (sigaction(i, NULL, &pc->pc_sigaction_vec[i]) < 0){
clicon_err(OE_UNIX, errno, "sigaction");
goto done;
}
/* Mask SA_RESTORER: Not intended for application use.
* Note that it may not be included in user space so may be hardcoded below
*/
#ifdef SA_RESTORER
pc->pc_sigaction_vec[i].sa_flags &= ~SA_RESTORER;
#else
pc->pc_sigaction_vec[i].sa_flags &= ~0x04000000;
#endif
}
if (isatty(0) && tcgetattr(0, &pc->pc_termios) < 0){
clicon_err(OE_UNIX, errno, "tcgetattr %d", errno);
goto done;
}
return pc;
done:
if (pc)
free(pc);
return NULL;
}
/*! Given an existing, old plugin context, check if anything has changed
*
* Make a new check and compare with the old (procided as in-parameter).
* Log if there is a difference at loglevel WARNING.
* You can modify the code to also fail with assert if you want early fail.
*
* @param[in,out] oldpc Old plugin context
* @param[in] name Name of plugin for logging. Can be other name, context dependent
* @param[in] fn Typically name of callback, or caller function
* @retval -1 Error
* @retval 0 Fail, log on syslog using LOG_WARNING
* @retval 1 OK
* @note name and fn are context dependent, since the env of callback calls are very different
* @see plugin_context_get
*/
int
plugin_context_check(plugin_context_t *oldpc0,
const char *name,
const char *fn)
{
int retval = -1;
int failed = 0;
int i;
struct plugin_context *oldpc = oldpc0;
struct plugin_context *newpc = NULL;
if ((newpc = plugin_context_get()) == NULL)
goto done;
if (oldpc->pc_termios.c_iflag != newpc->pc_termios.c_iflag){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios input modes from 0x%x to 0x%x", __FUNCTION__,
name, fn,
oldpc->pc_termios.c_iflag,
newpc->pc_termios.c_iflag);
failed++;
}
if (oldpc->pc_termios.c_oflag != newpc->pc_termios.c_oflag){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios output modes from 0x%x to 0x%x", __FUNCTION__,
name, fn,
oldpc->pc_termios.c_oflag,
newpc->pc_termios.c_oflag);
failed++;
}
if (oldpc->pc_termios.c_cflag != newpc->pc_termios.c_cflag){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios control modes from 0x%x to 0x%x", __FUNCTION__,
name, fn,
oldpc->pc_termios.c_cflag,
newpc->pc_termios.c_cflag);
failed++;
}
if (oldpc->pc_termios.c_lflag != newpc->pc_termios.c_lflag){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios local modes from 0x%x to 0x%x", __FUNCTION__,
name, fn,
oldpc->pc_termios.c_lflag,
newpc->pc_termios.c_lflag);
failed++;
}
/* XXX pc_termios.cc_t c_cc[NCCS] not checked */
#if 0
/* In case you want early detection and crash. But otherwise it is recommended that
* the caller looks for retval == 0 */
assert(failed == 0);
#endif
for (i=1; i<32; i++){
if (sigismember(&oldpc->pc_sigset, i) != sigismember(&newpc->pc_sigset, i)){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed blocking of signal %s(%d) from %d to %d", __FUNCTION__,
name, fn, strsignal(i), i,
sigismember(&oldpc->pc_sigset, i),
sigismember(&newpc->pc_sigset, i)
);
failed++;
}
if (oldpc->pc_sigaction_vec[i].sa_flags != newpc->pc_sigaction_vec[i].sa_flags){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed flags of signal %s(%d) from 0x%x to 0x%x", __FUNCTION__,
name, fn, strsignal(i), i,
oldpc->pc_sigaction_vec[i].sa_flags,
newpc->pc_sigaction_vec[i].sa_flags);;
failed++;
}
if (oldpc->pc_sigaction_vec[i].sa_sigaction != newpc->pc_sigaction_vec[i].sa_sigaction){
clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed action of signal %s(%d) from %p to %p", __FUNCTION__,
name, fn, strsignal(i), i,
oldpc->pc_sigaction_vec[i].sa_sigaction,
newpc->pc_sigaction_vec[i].sa_sigaction);
failed++;
}
#if 0
/* In case you want early detection and crash. But otherwise it is recommended that
* the caller looks for retval == 0 */
assert(failed == 0);
#endif
}
if (failed)
goto fail;
retval = 1; /* OK */
done:
if (newpc)
free(newpc);
return retval;
fail:
retval = 0;
goto done;
}
/*! Call single plugin start callback
* @param[in] cp Plugin handle
* @param[in] h Clixon handle
@ -493,19 +664,26 @@ int
clixon_plugin_start_one(clixon_plugin_t *cp,
clicon_handle h)
{
int retval = -1;
plgstart_t *fn; /* Plugin start */
int retval = -1;
plgstart_t *fn; /* Plugin start */
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_start) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Start callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -540,17 +718,22 @@ static int
clixon_plugin_exit_one(clixon_plugin_t *cp,
clicon_handle h)
{
int retval = -1;
char *error;
plgexit_t *fn;
int retval = -1;
char *error;
plgexit_t *fn;
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_exit) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Exit callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
if (dlclose(cp->cp_handle) != 0) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error");
@ -558,6 +741,8 @@ clixon_plugin_exit_one(clixon_plugin_t *cp,
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -606,21 +791,28 @@ clixon_plugin_auth_one(clixon_plugin_t *cp,
clixon_auth_type_t auth_type,
char **authp)
{
int retval = -1;
plgauth_t *fn; /* Plugin auth */
int retval = -1;
plgauth_t *fn; /* Plugin auth */
plugin_context_t *pc = NULL;
clicon_debug(1, "%s", __FUNCTION__);
if ((fn = cp->cp_api.ca_auth) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if ((retval = fn(h, req, auth_type, authp)) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Auth callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
}
else
retval = 0; /* Ignored / no callback */
done:
if (pc)
free(pc);
clicon_debug(1, "%s retval:%d auth:%s", __FUNCTION__, retval, *authp);
return retval;
}
@ -684,19 +876,26 @@ clixon_plugin_extension_one(clixon_plugin_t *cp,
yang_stmt *yext,
yang_stmt *ys)
{
int retval = 1;
plgextension_t *fn; /* Plugin extension fn */
int retval = 1;
plgextension_t *fn; /* Plugin extension fn */
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_extension) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, yext, ys) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Extension callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -750,17 +949,24 @@ clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp,
{
int retval = -1;
datastore_upgrade_t *fn;
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_datastore_upgrade) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, db, xt, msd) < 0) {
if (clicon_errno < 0)
clicon_log(LOG_WARNING, "%s: Internal error: Datastore upgrade callback in plugin: %s returned -1 but did not make a clicon_err call",
__FUNCTION__, cp->cp_name);
goto done;
}
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
}
retval = 0;
done:
if (pc)
free(pc);
return retval;
}
@ -906,12 +1112,13 @@ rpc_callback_call(clicon_handle h,
void *arg)
{
int retval = -1;
rpc_callback_t *rc;
char *name;
char *prefix;
char *ns;
int nr = 0; /* How many callbacks */
rpc_callback_t *rc;
char *name;
char *prefix;
char *ns;
int nr = 0; /* How many callbacks */
plugin_module_struct *ms = plugin_module_struct_get(h);
plugin_context_t *pc = NULL;
if (ms == NULL){
clicon_err(OE_PLUGIN, EINVAL, "plugin module not initialized");
@ -925,17 +1132,27 @@ rpc_callback_call(clicon_handle h,
if (strcmp(rc->rc_name, name) == 0 &&
ns && rc->rc_namespace &&
strcmp(rc->rc_namespace, ns) == 0){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (rc->rc_callback(h, xe, cbret, arg, rc->rc_arg) < 0){
clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name);
goto done;
}
nr++;
if (plugin_context_check(pc, rc->rc_name, __FUNCTION__) < 0)
goto done;
if (pc){
free(pc);
pc = NULL;
}
}
rc = NEXTQ(rpc_callback_t *, rc);
} while (rc != ms->ms_rpc_callbacks);
retval = nr; /* 0: none found, >0 nr of handlers called */
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (pc)
free(pc);
return retval;
}

View file

@ -121,6 +121,58 @@ clicon_signal_unblock(int sig)
sigprocmask(SIG_UNBLOCK, &set, NULL);
}
/*! Save complete signal context
*/
int
clixon_signal_save(sigset_t *sigset,
struct sigaction sigaction_vec[32])
{
int retval = -1;
int i;
if (sigprocmask(0, NULL, sigset) < 0){
clicon_err(OE_UNIX, errno, "sigprocmask");
goto done;
}
for (i=1; i<32; i++){
if (sigaction(i, NULL, &sigaction_vec[i]) < 0){
clicon_err(OE_UNIX, errno, "sigaction");
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Restore complete signal context
*
* Note: sigaction may not restore SIGKILL or SIGSTOP, which cannot be caught or ignored.
*/
int
clixon_signal_restore(sigset_t *sigset,
struct sigaction sigaction_vec[32])
{
int retval = -1;
int i;
if (sigprocmask(0, sigset, NULL) < 0){
clicon_err(OE_UNIX, errno, "sigprocmask");
goto done;
}
for (i=1; i<32; i++){
if (i == SIGKILL || i == SIGSTOP)
continue;
if (sigaction(i, &sigaction_vec[i], NULL) < 0){
clicon_err(OE_UNIX, errno, "sigaction");
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Read pidfile and return pid using file descriptor
*
* @param[in] pidfile Name of pidfile

View file

@ -3553,15 +3553,20 @@ yang_anydata_add(yang_stmt *yp,
return ys;
}
/*! Find extension argument and return extension argument value
/*! Find extension argument and return if extension exists and its argument value
*
* @param[in] ys Yang statement
* @param[in] name Name of the extension
* @param[in] ns The namespace
* @param[out] exist The extension exists.
* @param[out] value clispec operator (hide/none) - direct pointer into yang, dont free
* @retval 0 OK: Look in exist and value for return value
* @retval -1 Error
* This is for extensions with an argument
* @code
* char *value = NULL;
* if (yang_extension_value(ys, "mymode", "urn:example:lib", &value) < 0)
* int exist = 0;
* if (yang_extension_value(ys, "mymode", "urn:example:lib", &exist, &value) < 0)
* err;
* if (value != NULL){
* // use extension value
@ -3572,6 +3577,7 @@ int
yang_extension_value(yang_stmt *ys,
char *name,
char *ns,
int *exist,
char **value)
{
int retval = -1;
@ -3585,7 +3591,7 @@ yang_extension_value(yang_stmt *ys,
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
yext = NULL; /* This loop gets complicated in trhe case the extension is augmented */
yext = NULL; /* This loop gets complicated in the case the extension is augmented */
while ((yext = yn_each(ys, yext)) != NULL) {
if (yang_keyword_get(yext) != Y_UNKNOWN)
continue;
@ -3593,15 +3599,17 @@ yang_extension_value(yang_stmt *ys,
continue;
if (yang_find_prefix_by_namespace(ymod, ns, &prefix) < 0)
goto ok;
cbuf_reset(cb);
cprintf(cb, "%s:%s", prefix, name);
if (strcmp(yang_argument_get(yext), cbuf_get(cb)) != 0)
continue;
break;
}
if (yext != NULL){ /* Found */
if ((cv = yang_cv_get(yext)) == NULL)
goto ok;
if (value)
if (exist)
*exist = 1;
if (value &&
(cv = yang_cv_get(yext)) != NULL)
*value = cv_string_get(cv);
}
ok:

54
test/test_dispatcher.sh Executable file
View file

@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Test of path dispatcher
# 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_dispatcher:="clixon_util_dispatcher"}
new "null test"
expectpart "$($clixon_util_dispatcher)" 0 "^$"
new "path /, nothing regged. Expect fail"
expectpart "$($clixon_util_dispatcher -c /)" 255 "^$"
new "reg /, path / arg foo"
expectpart "$($clixon_util_dispatcher -a foo -p / -r -c /)" 0 "cb1 foo"
new "reg /foo and /bar same cb1, call /"
expectpart "$($clixon_util_dispatcher -a foo -p /foo -r -a bar -p /bar -r -c /)" 0 "cb1 foo" "cb1 bar"
new "reg /foo and /bar different cb, call /"
expectpart "$($clixon_util_dispatcher -i 1 -a foo -p /foo -r -a bar -p /bar -i 2 -r -c /)" 0 "cb1 foo" "cb2 bar"
new "reg /foo and /bar call /foo"
expectpart "$($clixon_util_dispatcher -i 1 -a foo -p /foo -r -a bar -p /bar -i 2 -r -c /foo)" 0 "cb1 foo"
new "reg /foo and /bar call /bar"
expectpart "$($clixon_util_dispatcher -i 1 -a foo -p /foo -r -a bar -p /bar -i 2 -r -c /bar)" 0 "cb2 bar"
new "reg /route-table ipv4 and ipv6 call /route-table"
expectpart "$($clixon_util_dispatcher -i 1 -a ipv4 -p /route-table/ipv4 -r -a ipv6 -p /route-table -i 2 -r -c /route-table)" 0 "cb1 ipv4" "cb2 ipv6"
new "reg /route-table/ ipv4,ipv6 call /route-table/ipv4"
expectpart "$($clixon_util_dispatcher -i 1 -a ipv4 -p /route-table/ipv4 -r -a ipv6 -p /route-table/ipv6 -i 2 -r -c /route-table/ipv4)" 0 "cb1 ipv4" --not-- cb2
new "reg /route-table/ ipv4,ipv6 call /route-table/ipv6"
expectpart "$($clixon_util_dispatcher -i 1 -a ipv4 -p /route-table/ipv4 -r -a ipv6 -p /route-table/ipv6 -i 2 -r -c /route-table/ipv6)" 0 "cb2 ipv6" --not-- cb1
new "reg /route-table/ ipv4,ipv6 call /route-table[proto='ipv4']/ipv4"
expectpart "$($clixon_util_dispatcher -i 1 -a ipv4 -p /route-table/ipv4 -r -a ipv6 -p /route-table/ipv6 -i 2 -r -c /route-table[proto='ipv4']/ipv4)" 0 "cb1 ipv4" --not-- "cb2 ipv6"
new "reg /route-table/ ipv4,ipv6 call /route-table[proto='ipv6']/ipv6"
expectpart "$($clixon_util_dispatcher -i 1 -a ipv4 -p /route-table/ipv4 -r -a ipv6 -p /route-table/ipv6 -i 2 -r -c /route-table[proto='ipv6']/ipv6)" 0 "cb2 ipv6" --not-- "cb1 ipv4"
new "reg /route-table/ ipv4,ipv6 call /route-table=/ipv4"
expectpart "$($clixon_util_dispatcher -i 1 -a ipv4 -p /route-table/ipv4 -r -a ipv6 -p /route-table/ipv6 -i 2 -r -c /route-table=/ipv4)" 0 "cb1 ipv4" --not-- "cb2 ipv6"
# unset conditional parameters
unset clixon_util_dispatcher
rm -rf $dir
new "endtest"
endtest

View file

@ -17,8 +17,6 @@ cfg=$dir/conf.xml
fexample=$dir/example-social.yang
fstate=$dir/mystate.xml
xpath=/es:audit-logs/es:audit-log
# For 1M test,m use an external file since the generation takes considerable time
#fstate=~/tmp/mystate.xml
@ -110,7 +108,7 @@ function testrun_start()
fi
sudo pkill -f clixon_backend # to be sure
new "start backend -s init -f $cfg -- -siS $fstate -X $xpath"
new "start backend -s init -f $cfg -- -siS $fstate -x $xpath"
start_backend -s init -f $cfg -- -siS $fstate -x $xpath
fi
@ -132,19 +130,26 @@ function testrun_stop()
fi
}
testrun_start "/es:members/es:member[es:member-id='alice']/es:stats/es:numbers"
xpath0="/es:members/es:member[es:member-id='alice']/es:stats"
xpath="$xpath0/es:numbers"
testrun_start $xpath
new "NETCONF get leaf-list member/numbers 0-10 alice"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"$xpath\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination><offset xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">0</offset><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">10</limit></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>alice</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><stats><numbers>3</numbers><numbers>4</numbers><numbers>5</numbers><numbers>6</numbers><numbers>7</numbers><numbers>8</numbers></stats></member></members></data></rpc-reply>]]>]]>$"
# negative
new "NETCONF get container, expect fail"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"/es:members/es:member[es:member-id='alice']/es:stats\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination><offset xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">0</offset><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">10</limit></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>list-pagination is enabled but target is not list or leaf-list</error-message></rpc-error></rpc-reply>]]>]]>$"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"$xpath0\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination><offset xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">0</offset><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">10</limit></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>invalid-value</error-tag><error-severity>error</error-severity><error-message>list-pagination is enabled but target is not list or leaf-list</error-message></rpc-error></rpc-reply>]]>]]>$"
xpath="/es:members/es:member[es:member-id='bob']/es:stats/es:numbers"
new "NETCONF get leaf-list member/numbers 0-10 bob"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"$xpath\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination><offset xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">0</offset><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">10</limit></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>bob</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><stats><numbers>13</numbers><numbers>14</numbers><numbers>15</numbers><numbers>16</numbers><numbers>17</numbers><numbers>18</numbers></stats></member></members></data></rpc-reply>]]>]]>$"
testrun_stop
#----------------------------
testrun_start "/es:members/es:member/es:stats/es:numbers"
xpath="/es:members/es:member/es:stats/es:numbers"
testrun_start $xpath
new "NETCONF get leaf-list member/numbers all"
expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO<rpc $DEFAULTNS><get content=\"nonconfig\"><filter type=\"xpath\" select=\"$xpath\" xmlns:es=\"http://example.com/ns/example-social\"/><list-pagination xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">true</list-pagination><offset xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">0</offset><limit xmlns=\"http://clicon.org/clixon-netconf-list-pagination\">10</limit></get></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><members xmlns=\"http://example.com/ns/example-social\"><member><member-id>alice</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><stats><numbers>3</numbers><numbers>4</numbers><numbers>5</numbers><numbers>6</numbers><numbers>7</numbers><numbers>8</numbers></stats></member><member><member-id>bob</member-id><privacy-settings><post-visibility>public</post-visibility></privacy-settings><stats><numbers>13</numbers><numbers>14</numbers><numbers>15</numbers><numbers>16</numbers></stats></member></members></data></rpc-reply>]]>]]>$"
@ -169,6 +174,7 @@ fi # interactive
unset validatexml
unset perfnr
unset xpath
unset xpath0
rm -rf $dir

View file

@ -92,6 +92,7 @@ APPSRC += clixon_util_datastore.c
APPSRC += clixon_util_regexp.c
APPSRC += clixon_util_socket.c
APPSRC += clixon_util_validate.c
APPSRC += clixon_util_dispatcher.c
APPSRC += clixon_netconf_ssh_callhome.c
APPSRC += clixon_netconf_ssh_callhome_client.c
ifdef with_restconf
@ -102,7 +103,6 @@ APPSRC += clixon_util_ssl.c # requires http/2
#APPSRC += clixon_util_grpc.c # work in progress
endif
APPS = $(APPSRC:.c=)
all: $(APPS)
@ -149,6 +149,9 @@ clixon_util_socket: clixon_util_socket.c $(LIBDEPS)
clixon_util_validate: clixon_util_validate.c $(LIBDEPS)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ -l clixon_backend -o $@ $(LIBS)
clixon_util_dispatcher: clixon_util_dispatcher.c $(LIBDEPS)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ -l clixon_backend -o $@ $(LIBS)
ifdef with_restconf
clixon_util_stream: clixon_util_stream.c $(LIBDEPS)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ $(LIBS) -lcurl -o $@
@ -163,7 +166,7 @@ endif
distclean: clean
rm -f Makefile *~ .depend
install:
install: $(APPS)
install -d -m 0755 $(DESTDIR)$(bindir)
install -m 0755 $(INSTALLFLAGS) $(APPS) $(DESTDIR)$(bindir)

View file

@ -48,6 +48,7 @@
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/socket.h>

View file

@ -0,0 +1,191 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2021 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Utility for testing path dispatcher
* Everything is run by options and order is significant which makes it a little special.
* For example:
* clixon_util_dispatcher -r -c / :
* Register cb1 with default path "/" and arg NULL, call with path /
* clixon_util_dispatcher -i 2 -p /foo -a bar -r -c /bar -c /fie
* Register cb2 with path "/foo" and arg bar, call with path /bar then /fie
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon/clixon.h"
#include "clixon/clixon_backend.h"
/* Command line options to be passed to getopt(3) */
#define DISPATCHER_OPTS "hD:a:i:p:rc:"
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
"\t-h \t\tHelp\n"
"\t-D <level> \t Debug - print dispatch tree\n"
"\t-a <string>\t Argument to callback (default: NULL)\n"
"\t-i <int> \t Function index: 1..3 (default: 1)\n"
"\t-p <path> \t Registered path (default: /)\n"
"\t-r \t Register callback (based on -a/-i/-p setting)\n"
"\t-c <path> \t Call dispatcher with path\n",
argv0
);
exit(0);
}
/*! Function to handle a path
*
* @param[in] h Generic handler
* @param[in] xpath Registered XPath using canonical prefixes
* @param[in] userargs Per-call user arguments
* @param[in] arg Per-path user argument
*(
/ * Make a CB() macro to generate simple callbacks that just prints the path and arg
*/
#define CB(i) static int cb##i(void *h0, char *xpath, void *userargs, void *arg) { fprintf(stdout, "%s %s\n", __FUNCTION__, (char*)arg); return 0; }
CB(1)
CB(2)
int
main(int argc,
char **argv)
{
int retval = -1;
char *argv0 = argv[0];
int logdst = CLICON_LOG_STDERR;
int dbg = 0;
int c;
char *arg = NULL;
handler_function fn = cb1;
dispatcher_entry_t *htable = NULL;
int ret;
char *regpath = "/"; /* Register path */
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init("dispatcher", LOG_DEBUG, logdst);
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, DISPATCHER_OPTS)) != -1)
switch (c) {
case 'h':
usage(argv0);
break;
case 'D':
if (sscanf(optarg, "%d", &dbg) != 1)
usage(argv0);
break;
case 'a' :
case 'i' :
case 'p' :
case 'r' :
case 'c' :
break;
default:
usage(argv[0]);
break;
}
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init("xpath", dbg?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(dbg, NULL);
/* Now rest of options */
opterr = 0;
optind = 1;
while ((c = getopt(argc, argv, DISPATCHER_OPTS)) != -1){
switch (c) {
case 'D' : /* debug */
break; /* see above */
case 'a' : /* arg string */
arg = optarg;
break;
case 'i' : /* dispatcher function: 1..3 */
switch (atoi(optarg)){
case 1: fn = cb1; break;
case 2: fn = cb2; break;
// case 3: fn = cb3; break;
}
break;
case 'p' : /* register path */
regpath = optarg;
break;
case 'r' :{ /* register callback based on -a/-i/-p*/
dispatcher_definition x = {regpath, fn, arg};
if (dispatcher_register_handler(&htable, &x) < 0)
goto done;
break;
}
case 'c':{ /* Execute a call using path */
char *path = optarg;
if ((ret = dispatcher_call_handlers(htable, NULL, path, NULL)) < 0)
goto done;
fprintf(stderr, "path:%s ret:%d\n", path, ret);
break;
}
default:
usage(argv[0]);
break;
}
}
if (dbg)
dispatcher_print(stderr, 0, htable);
dispatcher_free(htable);
retval = 0;
done:
return retval;
}

View file

@ -50,6 +50,7 @@
#include <limits.h>
#include <stdint.h>
#include <syslog.h>
#include <signal.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -49,6 +49,7 @@
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
/* cligen */

View file

@ -47,6 +47,7 @@
#include <syslog.h>
#include <stdlib.h>
#include <limits.h>
#include <signal.h>
#ifdef HAVE_LIBXML2 /* Actually it should check for a header file */
#include <libxml/xmlregexp.h>

View file

@ -52,6 +52,7 @@
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>

View file

@ -49,6 +49,7 @@
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <signal.h>
#include <netdb.h> /* gethostbyname */
#include <arpa/inet.h> /* inet_pton */
#include <netinet/tcp.h> /* TCP_NODELAY */

View file

@ -50,6 +50,7 @@
#include <limits.h>
#include <stdint.h>
#include <syslog.h>
#include <signal.h>
#include <curl/curl.h>
/* cligen */

View file

@ -53,6 +53,7 @@
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>

View file

@ -56,6 +56,7 @@
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>

View file

@ -58,6 +58,7 @@
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -50,6 +50,7 @@ See https://www.w3.org/TR/xpath/
#include <stdint.h>
#include <syslog.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
/* cligen */
@ -126,7 +127,7 @@ main(int argc,
int len;
char *buf = NULL;
int ret;
FILE *fp = stdin; /* unless overriden by argv[1] */
FILE *fp = stdin; /* unless overriden by -f */
char *yang_file_dir = NULL;
yang_stmt *yspec = NULL;
char *xpath = NULL;
@ -169,7 +170,7 @@ main(int argc,
case 'f': /* XML file */
filename = optarg;
if ((fp = fopen(filename, "r")) == NULL){
clicon_err(OE_UNIX, errno, "open(%s)", argv[1]);
clicon_err(OE_UNIX, errno, "fopen(%s)", optarg);
goto done;
}
break;

View file

@ -51,11 +51,11 @@
#include <regex.h>
#include <dirent.h>
#include <syslog.h>
#include <signal.h>
#include <sys/stat.h>
#include <libgen.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>