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 ### 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: * Added set/get pointer API to clixon_data:
* clicon_ptr_get(), clicon_ptr_set(), * clicon_ptr_get(), clicon_ptr_set(),
* Restconf YANG PATCH according to RFC 8072 * Restconf YANG PATCH according to RFC 8072

View file

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

View file

@ -284,9 +284,10 @@ get_client_statedata(clicon_handle h,
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] xret Result XML tree * @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] xpath XPath point to object to get
* @param[in] nsc Namespace context of xpath * @param[in] nsc Namespace context of xpath
* @param[out] x1p Pointer to first matching object if any
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
@ -294,17 +295,14 @@ static int
filter_xpath_again(clicon_handle h, filter_xpath_again(clicon_handle h,
yang_stmt *yspec, yang_stmt *yspec,
cxobj *xret, cxobj *xret,
cxobj **xvec,
size_t xlen,
char *xpath, char *xpath,
cvec *nsc, cvec *nsc)
cxobj **x1p)
{ {
int retval = -1; int retval = -1;
cxobj **xvec = NULL;
size_t xlen;
int i; 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 /* If vectors are specified then mark the nodes found and
* then filter out everything else, * then filter out everything else,
* otherwise return complete tree. * otherwise return complete tree.
@ -326,12 +324,8 @@ filter_xpath_again(clicon_handle h,
/* Add default recursive values */ /* Add default recursive values */
if (xml_default_recurse(xret, 0) < 0) if (xml_default_recurse(xret, 0) < 0)
goto done; goto done;
if (xlen && x1p)
*x1p = xvec[0];
retval = 0; retval = 0;
done: done:
if (xvec)
free(xvec);
return retval; return retval;
} }
@ -339,6 +333,8 @@ filter_xpath_again(clicon_handle h,
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xret Result XML tree * @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] xpath XPath point to object to get
* @param[in] nsc Namespace context of xpath * @param[in] nsc Namespace context of xpath
* @param[in] username User name for NACM access * @param[in] username User name for NACM access
@ -350,6 +346,8 @@ filter_xpath_again(clicon_handle h,
static int static int
get_nacm_and_reply(clicon_handle h, get_nacm_and_reply(clicon_handle h,
cxobj *xret, cxobj *xret,
cxobj **xvec,
size_t xlen,
char *xpath, char *xpath,
cvec *nsc, cvec *nsc,
char *username, char *username,
@ -357,15 +355,11 @@ get_nacm_and_reply(clicon_handle h,
cbuf *cbret) cbuf *cbret)
{ {
int retval = -1; int retval = -1;
cxobj **xvec = NULL;
size_t xlen;
cxobj *xnacm = NULL; cxobj *xnacm = NULL;
/* Pre-NACM access step */ /* Pre-NACM access step */
xnacm = clicon_nacm_cache(h); xnacm = clicon_nacm_cache(h);
if (xnacm != NULL){ /* Do NACM validation */ if (xnacm != NULL){ /* Do NACM validation */
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* NACM datanode/module read validation */ /* NACM datanode/module read validation */
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0) if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
goto done; goto done;
@ -383,8 +377,6 @@ get_nacm_and_reply(clicon_handle h,
cprintf(cbret, "</rpc-reply>"); cprintf(cbret, "</rpc-reply>");
retval = 0; retval = 0;
done: done:
if (xvec)
free(xvec);
return retval; return retval;
} }
@ -454,11 +446,7 @@ get_list_pagination(clicon_handle h,
int retval = -1; int retval = -1;
uint32_t offset = 0; uint32_t offset = 0;
uint32_t limit = 0; uint32_t limit = 0;
char *direction = NULL;
char *sort = NULL;
char *where = NULL;
cbuf *cbpath = NULL; cbuf *cbpath = NULL;
cxobj *x;
int list_config; int list_config;
yang_stmt *ylist; yang_stmt *ylist;
cxobj *xerr = NULL; cxobj *xerr = NULL;
@ -468,7 +456,15 @@ get_list_pagination(clicon_handle h,
int ret; int ret;
uint32_t iddb; /* DBs lock, if any */ uint32_t iddb; /* DBs lock, if any */
int locked; 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 */ /* Check if list/leaf-list */
if (yang_path_arg(yspec, xpath, &ylist) < 0) if (yang_path_arg(yspec, xpath, &ylist) < 0)
@ -504,11 +500,6 @@ get_list_pagination(clicon_handle h,
goto done; goto done;
goto ok; 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 */ /* offset */
if ((ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0) if ((ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0)
@ -516,6 +507,7 @@ get_list_pagination(clicon_handle h,
/* limit */ /* limit */
if (ret && (ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0) if (ret && (ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0)
goto done; goto done;
#ifdef NOTYET
/* direction */ /* direction */
if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){ if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){
direction = xml_body(x); direction = xml_body(x);
@ -535,7 +527,7 @@ get_list_pagination(clicon_handle h,
/* where */ /* where */
if (ret && (x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL) if (ret && (x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL)
where = xml_body(x); where = xml_body(x);
#endif
/* Read config */ /* Read config */
switch (content){ switch (content){
case CONTENT_CONFIG: /* config data only */ case CONTENT_CONFIG: /* config data only */
@ -553,8 +545,10 @@ get_list_pagination(clicon_handle h,
cprintf(cbpath, "%s", xpath); cprintf(cbpath, "%s", xpath);
else else
cprintf(cbpath, "/"); cprintf(cbpath, "/");
#ifdef NOTYET
if (where) if (where)
cprintf(cbpath, "[%s]", where); cprintf(cbpath, "[%s]", where);
#endif
if (offset){ if (offset){
cprintf(cbpath, "[%u <= position()", offset); cprintf(cbpath, "[%u <= position()", offset);
if (limit) if (limit)
@ -607,11 +601,40 @@ get_list_pagination(clicon_handle h,
offset, limit, offset, limit,
xret)) < 0) xret)) < 0)
goto done; 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; 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 */ /* 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; goto done;
#ifdef LIST_PAGINATION_REMAINING #ifdef LIST_PAGINATION_REMAINING
/* Add remaining attribute Sec 3.1.5: /* Add remaining attribute Sec 3.1.5:
@ -639,17 +662,21 @@ get_list_pagination(clicon_handle h,
cbuf_free(cba); cbuf_free(cba);
} }
#endif /* LIST_PAGINATION_REMAINING */ #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; goto done;
ok: ok:
retval = 0; retval = 0;
done: done:
if (xvec)
free(xvec);
if (cbmsg) if (cbmsg)
cbuf_free(cbmsg); cbuf_free(cbmsg);
if (cbpath) if (cbpath)
cbuf_free(cbpath); cbuf_free(cbpath);
if (xerr) if (xerr)
xml_free(xerr); xml_free(xerr);
if (cberr)
cbuf_free(cberr);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
return retval; return retval;
@ -696,6 +723,10 @@ get_common(clicon_handle h,
int list_pagination = 0; int list_pagination = 0;
char *valstr; char *valstr;
cxobj *x; cxobj *x;
#if 1
cxobj **xvec = NULL;
size_t xlen;
#endif
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
username = clicon_username_get(h); 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) if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done; 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; 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; goto done;
ok: ok:
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xvec)
free(xvec);
if (xret) if (xret)
xml_free(xret); xml_free(xret);
if (cbreason) if (cbreason)

View file

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

View file

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

View file

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

View file

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

View file

@ -171,7 +171,7 @@ cli_xml2file(cxobj *xn,
if (xn == NULL) if (xn == NULL)
goto ok; goto ok;
/* Look for autocli-op defined in clixon-lib.yang */ /* 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; goto ok;
} }
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
@ -274,7 +274,7 @@ cli_xml2txt(cxobj *xn,
goto done; goto done;
} }
/* Look for autocli-op defined in clixon-lib.yang */ /* 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; goto ok;
} }
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){
@ -342,7 +342,7 @@ cli_xml2cli(cxobj *xn,
if ((ys = xml_spec(xn)) == NULL) if ((ys = xml_spec(xn)) == NULL)
goto ok; goto ok;
/* Look for autocli-op defined in clixon-lib.yang */ /* 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; goto ok;
} }
if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){

View file

@ -562,9 +562,10 @@ cli_start_shell(clicon_handle h,
int retval = -1; int retval = -1;
char bcmd[128]; char bcmd[128];
cg_var *cv1 = cvec_i(vars, 1); 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); cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL);
if ((pw = getpwuid(getuid())) == NULL){ if ((pw = getpwuid(getuid())) == NULL){
clicon_err(OE_UNIX, errno, "getpwuid"); clicon_err(OE_UNIX, errno, "getpwuid");
goto done; goto done;
@ -575,6 +576,9 @@ cli_start_shell(clicon_handle h,
goto done; goto done;
} }
endpwent(); endpwent();
if (clixon_signal_save(&oldsigset, oldsigaction) < 0)
goto done;
cli_signal_flush(h); cli_signal_flush(h);
cli_signal_unblock(h); cli_signal_unblock(h);
if (cmd){ if (cmd){
@ -598,6 +602,8 @@ cli_start_shell(clicon_handle h,
goto done; goto done;
} }
#endif #endif
if (clixon_signal_restore(&oldsigset, oldsigaction) < 0)
goto done;
retval = 0; retval = 0;
done: done:
return retval; return retval;

View file

@ -78,6 +78,7 @@ You can see which CLISPEC it generates via clixon_cli -D 2:
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <syslog.h> #include <syslog.h>
#include <signal.h>
#include <sys/param.h> #include <sys/param.h>
/* cligen */ /* cligen */
@ -120,7 +121,7 @@ cli_expand_var_generate(clicon_handle h,
int retval = -1; int retval = -1;
char *api_path_fmt = NULL, *opext = NULL; 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; goto done;
if (opext && strcmp(opext, "hide-database") == 0) { if (opext && strcmp(opext, "hide-database") == 0) {
retval = 1; retval = 1;
@ -761,7 +762,7 @@ yang2cli_leaf(clicon_handle h,
} }
cprintf(cb, "%*s", level*3, ""); cprintf(cb, "%*s", level*3, "");
/* Look for autocli-op defined in clixon-lib.yang */ /* 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; goto done;
if (gt == GT_VARS || gt == GT_ALL || gt == GT_HIDE || gt == GT_OC_COMPRESS){ if (gt == GT_VARS || gt == GT_ALL || gt == GT_HIDE || gt == GT_OC_COMPRESS){
cprintf(cb, "%s", yang_argument_get(ys)); cprintf(cb, "%s", yang_argument_get(ys));
@ -867,7 +868,7 @@ yang2cli_container(clicon_handle h,
goto done; goto done;
/* Look for autocli-op defined in clixon-lib.yang */ /* 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; goto done;
if (opext != NULL && strcmp(opext, "hide") == 0){ if (opext != NULL && strcmp(opext, "hide") == 0){
cprintf(cb, ",hide"); cprintf(cb, ",hide");
@ -933,7 +934,7 @@ yang2cli_list(clicon_handle h,
yang2cli_helptext(cb, helptext); yang2cli_helptext(cb, helptext);
} }
/* Look for autocli-op defined in clixon-lib.yang */ /* 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; goto done;
if (opext != NULL && strcmp(opext, "hide") == 0){ if (opext != NULL && strcmp(opext, "hide") == 0){
cprintf(cb, ",hide"); cprintf(cb, ",hide");

View file

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

View file

@ -52,6 +52,7 @@
#include <dirent.h> #include <dirent.h>
#include <libgen.h> #include <libgen.h>
#include <grp.h> #include <grp.h>
#include <signal.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h> #include <sys/param.h>
@ -69,7 +70,6 @@
#include "cli_handle.h" #include "cli_handle.h"
#include "cli_generate.h" #include "cli_generate.h"
/* /*
* Constants * Constants
*/ */
@ -522,6 +522,53 @@ cli_handler_err(FILE *f)
return 0; 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 /*! Evaluate a matched command
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] cmd The command string * @param[in] cmd The command string
@ -541,7 +588,8 @@ clicon_eval(clicon_handle h,
cli_output_reset(); cli_output_reset();
if (!cligen_exiting(cli_cligen(h))) { if (!cligen_exiting(cli_cligen(h))) {
clicon_err_reset(); 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. #if 0 /* This is removed since we get two error messages on failure.
But maybe only sometime? But maybe only sometime?
Both a real log when clicon_err is called, and the here again. 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; uint32_t limit = 0;
cxobj **xvec = NULL; cxobj **xvec = NULL;
size_t xlen; size_t xlen;
int locked = 0;
if (cvec_len(argv) != 5){ if (cvec_len(argv) != 5){
clicon_err(OE_PLUGIN, 0, "Expected usage: <xpath> <prefix> <namespace> <format> <limit>"); clicon_err(OE_PLUGIN, 0, "Expected usage: <xpath> <prefix> <namespace> <format> <limit>");
@ -950,6 +951,7 @@ cli_pagination(clicon_handle h,
goto done; goto done;
if (clicon_rpc_lock(h, "running") < 0) if (clicon_rpc_lock(h, "running") < 0)
goto done; goto done;
locked++;
for (i = 0;; i++){ for (i = 0;; i++){
if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc, if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc,
CONTENT_ALL, CONTENT_ALL,
@ -1000,10 +1002,10 @@ cli_pagination(clicon_handle h,
xvec = NULL; xvec = NULL;
} }
} /* for i */ } /* for i */
if (clicon_rpc_unlock(h, "running") < 0)
goto done;
retval = 0; retval = 0;
done: done:
if (locked)
clicon_rpc_unlock(h, "running");
if (xvec) if (xvec)
free(xvec); free(xvec);
if (xret) if (xret)

View file

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

View file

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

View file

@ -514,7 +514,10 @@ example_statefile(clicon_handle h,
xml_flag_set(x1, XML_FLAG_MARK); xml_flag_set(x1, XML_FLAG_MARK);
xml_apply_ancestor(x1, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); 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; goto done;
/* Unmark original tree */ /* Unmark original tree */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0) 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_flag_set(x1, XML_FLAG_MARK);
xml_apply_ancestor(x1, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); 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 */ if (xml_copy_marked(xt, xstate) < 0) /* Copy the marked elements */
goto done; goto done;
/* Unmark original tree */ /* Unmark original tree */
@ -1160,6 +1166,7 @@ example_daemon(clicon_handle h)
int ret; int ret;
FILE *fp = NULL; FILE *fp = NULL;
yang_stmt *yspec; yang_stmt *yspec;
cxobj *xerr = NULL;
/* Read state file (or should this be in init/start?) */ /* Read state file (or should this be in init/start?) */
if (_state && _state_file && _state_file_cached){ 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); clicon_err(OE_UNIX, errno, "open(%s)", _state_file);
goto done; 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; goto done;
if (ret == 0){
xml_print(stderr, xerr);
goto done;
}
} }
retval = 0; retval = 0;
done: done:

View file

@ -40,6 +40,7 @@
#include <errno.h> #include <errno.h>
#include <syslog.h> #include <syslog.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.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 * @see https://github.com/dcornejo/dispatcher
*/ */
#ifndef DISPATCH_DISPATCHER_H #ifndef _CLIXON_DISPATCH_DISPATCHER_H
#define 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 * minimally needs the path it's working on, but probably
* we want to hand down cached data somehow * we want to hand down cached data somehow
* @param[in] h Generic handler * @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_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_call_handlers(dispatcher_entry_t *root, void *handle, char *path, void *user_args);
int dispatcher_free(dispatcher_entry_t *root); 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 * Types
*/ */
/*! Registered RPC callback function /*! Registered RPC callback function
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] xn Request: <rpc><xn></rpc> * @param[in] xn Request: <rpc><xn></rpc>
@ -201,7 +200,7 @@ 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 * 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. * into a complete state tree by the system.
* The plugin should ensure that xpath is matched (using namspace context nsc) * 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 * 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. * XPath binding is stricter, similar to the pagination API.
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
@ -338,6 +337,7 @@ struct clixon_plugin_api{
#define ca_trans_abort u.cau_backend.cb_trans_abort #define ca_trans_abort u.cau_backend.cb_trans_abort
#define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade #define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade
/* /*
* Macros * Macros
*/ */
@ -349,6 +349,10 @@ typedef struct clixon_plugin_api clixon_plugin_api;
* The internal struct is defined in clixon_plugin.c */ * The internal struct is defined in clixon_plugin.c */
typedef struct clixon_plugin clixon_plugin_t; 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 * 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_init(clicon_handle h);
clixon_plugin_api *clixon_plugin_api_get(clixon_plugin_t *cp); clixon_plugin_api *clixon_plugin_api_get(clixon_plugin_t *cp);
char *clixon_plugin_name_get(clixon_plugin_t *cp); char *clixon_plugin_name_get(clixon_plugin_t *cp);
plghndl_t clixon_plugin_handle_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); 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_one(clixon_plugin_t *cp, clicon_handle h);
int clixon_plugin_start_all(clicon_handle h); int clixon_plugin_start_all(clicon_handle h);

View file

@ -47,6 +47,8 @@ typedef void (*sigfn_t)(int);
* Prototypes * Prototypes
*/ */
int set_signal(int signo, void (*handler)(int), void (**oldhandler)(int)); 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_block(int);
void clicon_signal_unblock(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, int yang_type_cache_set(yang_stmt *ys, yang_stmt *resolved, int options, cvec *cvv,
cvec *patterns, uint8_t fraction); cvec *patterns, uint8_t fraction);
yang_stmt *yang_anydata_add(yang_stmt *yp, char *name); 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_ */ #endif /* _CLIXON_YANG_H_ */

View file

@ -227,6 +227,11 @@ clicon_data_cvec_set(clicon_handle h,
const char *name, const char *name,
cvec *cvv) cvec *cvv)
{ {
cvec *cvv0 = NULL;
clicon_ptr_get(h, name, (void**)&cvv0);
if (cvv0)
cvec_free(cvv0);
return clicon_ptr_set(h, name, cvv); return clicon_ptr_set(h, name, cvv);
} }
@ -238,6 +243,11 @@ int
clicon_data_cvec_del(clicon_handle h, clicon_data_cvec_del(clicon_handle h,
const char *name) const char *name)
{ {
cvec *cvv = NULL;
clicon_ptr_get(h, name, (void**)&cvv);
if (cvv)
cvec_free(cvv);
return clicon_ptr_del(h, name); return clicon_ptr_del(h, name);
} }
@ -377,9 +387,13 @@ clicon_nacm_ext(clicon_handle h)
*/ */
int int
clicon_nacm_ext_set(clicon_handle h, 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 /*! 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 * @see https://github.com/dcornejo/dispatcher
*/ */
@ -37,10 +68,6 @@
* [b=] * [b=]
* [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 * 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 * 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 * 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 */ /* some elements may have keys defined, strip them off */
for (int i = 0; i < split_path_len; i++) { for (int i = 0; i < split_path_len; i++) {
char *kptr = strchr(split_path_list[i], '='); char *kptr = split_path_list[i];
strsep(&kptr, "=[]");
if ((kptr != NULL) && (*kptr == '=')) {
*(kptr + 1) = 0;
}
} }
/* search down the tree */ /* search down the tree */
@ -411,7 +435,7 @@ dispatcher_register_handler(dispatcher_entry_t **root,
* *
* @param[in] handle * @param[in] handle
* @param[in] root * @param[in] root
* @param[in] path * @param[in] path Note must be on the form: /a/b (no keys)
* @retval 1 OK * @retval 1 OK
* @retval 0 Invalid * @retval 0 Invalid
* @retval -1 Error * @retval -1 Error
@ -423,8 +447,12 @@ dispatcher_call_handlers(dispatcher_entry_t *root,
void *user_args) void *user_args)
{ {
int ret = 0; 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) { if (best->children != NULL) {
call_handler_helper(best->children, handle, path, user_args); call_handler_helper(best->children, handle, path, user_args);
} }
@ -450,3 +478,24 @@ dispatcher_free(dispatcher_entry_t *root)
free(root); free(root);
return 0; 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 <unistd.h>
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <signal.h>
#include <syslog.h> #include <syslog.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>

View file

@ -43,10 +43,12 @@
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <dlfcn.h> #include <dlfcn.h>
#include <dirent.h> #include <dirent.h>
#include <syslog.h> #include <syslog.h>
#include <unistd.h>
#include <termios.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h> #include <sys/param.h>
@ -69,6 +71,15 @@
/* /*
* Private types * 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 /* Internal plugin structure with dlopen() handle and plugin_api
* This is an internal type, not exposed in the API * This is an internal type, not exposed in the API
@ -330,6 +341,7 @@ plugin_load_one(clicon_handle h,
clixon_plugin_t *cp = NULL; clixon_plugin_t *cp = NULL;
char *name; char *name;
char *p; char *p;
plugin_context_t *pc = NULL;
clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function); clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function);
dlerror(); /* Clear any existing error */ dlerror(); /* Clear any existing error */
@ -348,6 +360,9 @@ plugin_load_one(clicon_handle h,
goto done; goto done;
} }
clicon_err_reset(); clicon_err_reset();
if ((pc = plugin_context_get()) < 0)
goto done;
if ((api = initfn(h)) == NULL) { if ((api = initfn(h)) == NULL) {
if (!clicon_errno){ /* if clicon_err() is not called then log and continue */ 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); 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; 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 */ /* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
if ((cp = (clixon_plugin_t *)malloc(sizeof(struct clixon_plugin))) == NULL){ if ((cp = (clixon_plugin_t *)malloc(sizeof(struct clixon_plugin))) == NULL){
clicon_err(OE_UNIX, errno, "malloc"); clicon_err(OE_UNIX, errno, "malloc");
@ -382,6 +400,8 @@ plugin_load_one(clicon_handle h,
retval = 1; retval = 1;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (pc)
free(pc);
if (retval != 1 && handle) if (retval != 1 && handle)
dlclose(handle); dlclose(handle);
if (cp) if (cp)
@ -483,6 +503,157 @@ done:
return retval; 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 /*! Call single plugin start callback
* @param[in] cp Plugin handle * @param[in] cp Plugin handle
* @param[in] h Clixon handle * @param[in] h Clixon handle
@ -495,17 +666,24 @@ clixon_plugin_start_one(clixon_plugin_t *cp,
{ {
int retval = -1; int retval = -1;
plgstart_t *fn; /* Plugin start */ plgstart_t *fn; /* Plugin start */
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_start) != NULL){ if ((fn = cp->cp_api.ca_start) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h) < 0) { if (fn(h) < 0) {
if (clicon_errno < 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", 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); __FUNCTION__, cp->cp_name);
goto done; goto done;
} }
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
} }
retval = 0; retval = 0;
done: done:
if (pc)
free(pc);
return retval; return retval;
} }
@ -543,14 +721,19 @@ clixon_plugin_exit_one(clixon_plugin_t *cp,
int retval = -1; int retval = -1;
char *error; char *error;
plgexit_t *fn; plgexit_t *fn;
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_exit) != NULL){ if ((fn = cp->cp_api.ca_exit) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h) < 0) { if (fn(h) < 0) {
if (clicon_errno < 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", 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); __FUNCTION__, cp->cp_name);
goto done; goto done;
} }
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
if (dlclose(cp->cp_handle) != 0) { if (dlclose(cp->cp_handle) != 0) {
error = (char*)dlerror(); error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error"); 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; retval = 0;
done: done:
if (pc)
free(pc);
return retval; return retval;
} }
@ -608,19 +793,26 @@ clixon_plugin_auth_one(clixon_plugin_t *cp,
{ {
int retval = -1; int retval = -1;
plgauth_t *fn; /* Plugin auth */ plgauth_t *fn; /* Plugin auth */
plugin_context_t *pc = NULL;
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((fn = cp->cp_api.ca_auth) != NULL){ 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 ((retval = fn(h, req, auth_type, authp)) < 0) {
if (clicon_errno < 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", 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); __FUNCTION__, cp->cp_name);
goto done; goto done;
} }
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
} }
else else
retval = 0; /* Ignored / no callback */ retval = 0; /* Ignored / no callback */
done: done:
if (pc)
free(pc);
clicon_debug(1, "%s retval:%d auth:%s", __FUNCTION__, retval, *authp); clicon_debug(1, "%s retval:%d auth:%s", __FUNCTION__, retval, *authp);
return retval; return retval;
} }
@ -686,17 +878,24 @@ clixon_plugin_extension_one(clixon_plugin_t *cp,
{ {
int retval = 1; int retval = 1;
plgextension_t *fn; /* Plugin extension fn */ plgextension_t *fn; /* Plugin extension fn */
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_extension) != NULL){ if ((fn = cp->cp_api.ca_extension) != NULL){
if ((pc = plugin_context_get()) == NULL)
goto done;
if (fn(h, yext, ys) < 0) { if (fn(h, yext, ys) < 0) {
if (clicon_errno < 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", 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); __FUNCTION__, cp->cp_name);
goto done; goto done;
} }
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
} }
retval = 0; retval = 0;
done: done:
if (pc)
free(pc);
return retval; return retval;
} }
@ -750,17 +949,24 @@ clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp,
{ {
int retval = -1; int retval = -1;
datastore_upgrade_t *fn; datastore_upgrade_t *fn;
plugin_context_t *pc = NULL;
if ((fn = cp->cp_api.ca_datastore_upgrade) != 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 (fn(h, db, xt, msd) < 0) {
if (clicon_errno < 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", 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); __FUNCTION__, cp->cp_name);
goto done; goto done;
} }
if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0)
goto done;
} }
retval = 0; retval = 0;
done: done:
if (pc)
free(pc);
return retval; return retval;
} }
@ -912,6 +1118,7 @@ rpc_callback_call(clicon_handle h,
char *ns; char *ns;
int nr = 0; /* How many callbacks */ int nr = 0; /* How many callbacks */
plugin_module_struct *ms = plugin_module_struct_get(h); plugin_module_struct *ms = plugin_module_struct_get(h);
plugin_context_t *pc = NULL;
if (ms == NULL){ if (ms == NULL){
clicon_err(OE_PLUGIN, EINVAL, "plugin module not initialized"); 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 && if (strcmp(rc->rc_name, name) == 0 &&
ns && rc->rc_namespace && ns && rc->rc_namespace &&
strcmp(rc->rc_namespace, ns) == 0){ 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){ if (rc->rc_callback(h, xe, cbret, arg, rc->rc_arg) < 0){
clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name); clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name);
goto done; goto done;
} }
nr++; 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); rc = NEXTQ(rpc_callback_t *, rc);
} while (rc != ms->ms_rpc_callbacks); } while (rc != ms->ms_rpc_callbacks);
retval = nr; /* 0: none found, >0 nr of handlers called */ retval = nr; /* 0: none found, >0 nr of handlers called */
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (pc)
free(pc);
return retval; return retval;
} }

View file

@ -121,6 +121,58 @@ clicon_signal_unblock(int sig)
sigprocmask(SIG_UNBLOCK, &set, NULL); 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 /*! Read pidfile and return pid using file descriptor
* *
* @param[in] pidfile Name of pidfile * @param[in] pidfile Name of pidfile

View file

@ -3553,15 +3553,20 @@ yang_anydata_add(yang_stmt *yp,
return ys; 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] ys Yang statement
* @param[in] name Name of the extension * @param[in] name Name of the extension
* @param[in] ns The namespace * @param[in] ns The namespace
* @param[out] exist The extension exists.
* @param[out] value clispec operator (hide/none) - direct pointer into yang, dont free * @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 * This is for extensions with an argument
* @code * @code
* char *value = NULL; * 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; * err;
* if (value != NULL){ * if (value != NULL){
* // use extension value * // use extension value
@ -3572,6 +3577,7 @@ int
yang_extension_value(yang_stmt *ys, yang_extension_value(yang_stmt *ys,
char *name, char *name,
char *ns, char *ns,
int *exist,
char **value) char **value)
{ {
int retval = -1; int retval = -1;
@ -3585,7 +3591,7 @@ yang_extension_value(yang_stmt *ys,
clicon_err(OE_UNIX, errno, "cbuf_new"); clicon_err(OE_UNIX, errno, "cbuf_new");
goto done; 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) { while ((yext = yn_each(ys, yext)) != NULL) {
if (yang_keyword_get(yext) != Y_UNKNOWN) if (yang_keyword_get(yext) != Y_UNKNOWN)
continue; continue;
@ -3593,15 +3599,17 @@ yang_extension_value(yang_stmt *ys,
continue; continue;
if (yang_find_prefix_by_namespace(ymod, ns, &prefix) < 0) if (yang_find_prefix_by_namespace(ymod, ns, &prefix) < 0)
goto ok; goto ok;
cbuf_reset(cb);
cprintf(cb, "%s:%s", prefix, name); cprintf(cb, "%s:%s", prefix, name);
if (strcmp(yang_argument_get(yext), cbuf_get(cb)) != 0) if (strcmp(yang_argument_get(yext), cbuf_get(cb)) != 0)
continue; continue;
break; break;
} }
if (yext != NULL){ /* Found */ if (yext != NULL){ /* Found */
if ((cv = yang_cv_get(yext)) == NULL) if (exist)
goto ok; *exist = 1;
if (value) if (value &&
(cv = yang_cv_get(yext)) != NULL)
*value = cv_string_get(cv); *value = cv_string_get(cv);
} }
ok: 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 fexample=$dir/example-social.yang
fstate=$dir/mystate.xml 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 # For 1M test,m use an external file since the generation takes considerable time
#fstate=~/tmp/mystate.xml #fstate=~/tmp/mystate.xml
@ -110,7 +108,7 @@ function testrun_start()
fi fi
sudo pkill -f clixon_backend # to be sure 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 start_backend -s init -f $cfg -- -siS $fstate -x $xpath
fi fi
@ -132,19 +130,26 @@ function testrun_stop()
fi 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" 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>]]>]]>$" 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 # negative
new "NETCONF get container, expect fail" 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_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" 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>]]>]]>$" 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 validatexml
unset perfnr unset perfnr
unset xpath unset xpath
unset xpath0
rm -rf $dir rm -rf $dir

View file

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

View file

@ -48,6 +48,7 @@
#include <signal.h> #include <signal.h>
#include <fcntl.h> #include <fcntl.h>
#include <time.h> #include <time.h>
#include <signal.h>
#include <syslog.h> #include <syslog.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/socket.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 <limits.h>
#include <stdint.h> #include <stdint.h>
#include <syslog.h> #include <syslog.h>
#include <signal.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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