Removed remaining and replaced pagination-mode with locked parameter
Dispatcher: Added dispatcher_free(), fixed mem-leaks and malloc return checks
This commit is contained in:
parent
ce06f25be7
commit
edbbb43e1f
14 changed files with 204 additions and 161 deletions
|
|
@ -43,6 +43,7 @@ Thanks netgate for providing the dispatcher code!
|
||||||
* Register callback with: `clixon_pagination_cb_register()`
|
* Register callback with: `clixon_pagination_cb_register()`
|
||||||
* Use accessor functions `pagination_offset()`, `pagination_limit()`, etc
|
* Use accessor functions `pagination_offset()`, `pagination_limit()`, etc
|
||||||
* Reverted state data callback API to pre-5.3 (see C/CLI API changes below)
|
* Reverted state data callback API to pre-5.3 (see C/CLI API changes below)
|
||||||
|
* See https://clixon-docs.readthedocs.io/en/latest/pagination.html
|
||||||
|
|
||||||
### API changes on existing protocol/config features
|
### API changes on existing protocol/config features
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -467,11 +467,8 @@ get_list_pagination(clicon_handle h,
|
||||||
char *xpath2; /* With optional pagination predicate */
|
char *xpath2; /* With optional pagination predicate */
|
||||||
int ret;
|
int ret;
|
||||||
uint32_t iddb; /* DBs lock, if any */
|
uint32_t iddb; /* DBs lock, if any */
|
||||||
pagination_mode_t pagmode;
|
int locked;
|
||||||
cxobj *x1 = NULL;
|
cxobj *x1 = NULL;
|
||||||
cxobj *xcache = NULL;
|
|
||||||
uint32_t total = 0;
|
|
||||||
uint32_t remaining = 0;
|
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
@ -588,6 +585,7 @@ get_list_pagination(clicon_handle h,
|
||||||
}/* switch content */
|
}/* switch content */
|
||||||
|
|
||||||
if (list_config){
|
if (list_config){
|
||||||
|
#ifdef LIST_PAGINATION_REMAINING
|
||||||
/* Get total/remaining
|
/* Get total/remaining
|
||||||
* XXX: Works only for cache
|
* XXX: Works only for cache
|
||||||
*/
|
*/
|
||||||
|
|
@ -597,15 +595,16 @@ get_list_pagination(clicon_handle h,
|
||||||
if (total >= (offset + limit))
|
if (total >= (offset + limit))
|
||||||
remaining = total - (offset + limit);
|
remaining = total - (offset + limit);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else {/* Check if running locked (by this session) */
|
else {/* Check if running locked (by this session) */
|
||||||
if ((iddb = xmldb_islocked(h, "running")) != 0 &&
|
if ((iddb = xmldb_islocked(h, "running")) != 0 &&
|
||||||
iddb == ce->ce_id)
|
iddb == ce->ce_id)
|
||||||
pagmode = PAGINATION_LOCK;
|
locked = 1;
|
||||||
else
|
else
|
||||||
pagmode = PAGINATION_STATELESS;
|
locked = 0;
|
||||||
if ((ret = clixon_pagination_cb_call(h, xpath, pagmode,
|
if ((ret = clixon_pagination_cb_call(h, xpath, locked,
|
||||||
offset, limit, &remaining,
|
offset, limit,
|
||||||
xret)) < 0)
|
xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ backend_terminate(clicon_handle h)
|
||||||
clixon_process_delete_all(h);
|
clixon_process_delete_all(h);
|
||||||
|
|
||||||
xpath_optimize_exit();
|
xpath_optimize_exit();
|
||||||
|
clixon_pagination_free(h);
|
||||||
if (pidfile)
|
if (pidfile)
|
||||||
unlink(pidfile);
|
unlink(pidfile);
|
||||||
if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0)
|
if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0)
|
||||||
|
|
|
||||||
|
|
@ -452,23 +452,24 @@ clixon_plugin_lockdb_all(clicon_handle h,
|
||||||
* @param[in] xpath Registered XPath using canonical prefixes
|
* @param[in] xpath Registered XPath using canonical prefixes
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clixon_pagination_cb_call(clicon_handle h,
|
clixon_pagination_cb_call(clicon_handle h,
|
||||||
char *xpath,
|
char *xpath,
|
||||||
pagination_mode_t pagmode,
|
int locked,
|
||||||
uint32_t offset,
|
uint32_t offset,
|
||||||
uint32_t limit,
|
uint32_t limit,
|
||||||
uint32_t *remaining,
|
cxobj *xstate)
|
||||||
cxobj *xstate)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
pagination_data_t pd = {pagmode, offset, limit, 0, xstate};
|
pagination_data_t pd;
|
||||||
dispatcher_entry_t *htable = NULL;
|
dispatcher_entry_t *htable = NULL;
|
||||||
|
|
||||||
|
pd.pd_offset = offset;
|
||||||
|
pd.pd_limit = limit;
|
||||||
|
pd.pd_locked = locked;
|
||||||
|
pd.pd_xstate = xstate;
|
||||||
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
|
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
|
||||||
if (htable && dispatcher_call_handlers(htable, h, xpath, &pd) < 0)
|
if (htable && dispatcher_call_handlers(htable, h, xpath, &pd) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (remaining)
|
|
||||||
*remaining = pd.pd_remaining;
|
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -503,6 +504,21 @@ clixon_pagination_cb_register(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Free pagination callback structure
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clixon_pagination_free(clicon_handle h)
|
||||||
|
{
|
||||||
|
dispatcher_entry_t *htable = NULL;
|
||||||
|
|
||||||
|
clicon_ptr_get(h, "pagination-entries", (void**)&htable);
|
||||||
|
if (htable)
|
||||||
|
dispatcher_free(htable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Create and initialize a validate/commit transaction
|
/*! Create and initialize a validate/commit transaction
|
||||||
* @retval td New alloced transaction,
|
* @retval td New alloced transaction,
|
||||||
* @retval NULL Error
|
* @retval NULL Error
|
||||||
|
|
|
||||||
|
|
@ -68,17 +68,22 @@ typedef struct {
|
||||||
} transaction_data_t;
|
} transaction_data_t;
|
||||||
|
|
||||||
/*! Pagination userdata
|
/*! Pagination userdata
|
||||||
* @param[in] pagmode List pagination mode
|
* Pagination can use a lock/transaction mechanism
|
||||||
|
* If locking is not used, the plugin cannot expect more pagination calls, and no state or
|
||||||
|
* caching should be used
|
||||||
|
* If locking is used, the pagination is part of a session transaction and the plugin may cache
|
||||||
|
* state (such as a cache) and can expect more pagination calls until the running db-lock is
|
||||||
|
* released, (see ca_lockdb)
|
||||||
|
* The transaction is the regular lock/unlock db of running-db of a specific session.
|
||||||
|
* @param[in] locked "running" datastore is locked by this caller
|
||||||
* @param[in] offset Offset, for list pagination
|
* @param[in] offset Offset, for list pagination
|
||||||
* @param[in] limit Limit, for list pagination
|
* @param[in] limit Limit, for list pagination
|
||||||
* @param[out] remaining Remaining elements (if limit is non-zero)
|
|
||||||
* see also pagination_data in clixon_plugin.h
|
* see also pagination_data in clixon_plugin.h
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
pagination_mode_t pd_pagmode; /* Pagination mode, stateless or locked */
|
|
||||||
uint32_t pd_offset; /* Start of pagination interval */
|
uint32_t pd_offset; /* Start of pagination interval */
|
||||||
uint32_t pd_limit; /* Number of elemenents (limit) */
|
uint32_t pd_limit; /* Number of elemenents (limit) */
|
||||||
uint32_t pd_remaining; /* If limit, then remaining nr of elements */
|
int pd_locked; /* Running datastore is locked by this caller */
|
||||||
cxobj *pd_xstate; /* Returned xml state tree */
|
cxobj *pd_xstate; /* Returned xml state tree */
|
||||||
} pagination_data_t;
|
} pagination_data_t;
|
||||||
|
|
||||||
|
|
@ -96,9 +101,10 @@ int clixon_plugin_statedata_all(clicon_handle h, yang_stmt *yspec, cvec *nsc, ch
|
||||||
int clixon_plugin_lockdb_all(clicon_handle h, char *db, int lock, int id);
|
int clixon_plugin_lockdb_all(clicon_handle h, char *db, int lock, int id);
|
||||||
|
|
||||||
int clixon_pagination_cb_register(clicon_handle h, handler_function fn, char *path, void *arg);
|
int clixon_pagination_cb_register(clicon_handle h, handler_function fn, char *path, void *arg);
|
||||||
int clixon_pagination_cb_call(clicon_handle h, char *xpath, pagination_mode_t pagmode,
|
int clixon_pagination_cb_call(clicon_handle h, char *xpath, int locked,
|
||||||
uint32_t offset, uint32_t limit, uint32_t *remaining,
|
uint32_t offset, uint32_t limit,
|
||||||
cxobj *xstate);
|
cxobj *xstate);
|
||||||
|
int clixon_pagination_free(clicon_handle h);
|
||||||
|
|
||||||
transaction_data_t * transaction_new(void);
|
transaction_data_t * transaction_new(void);
|
||||||
int transaction_free(transaction_data_t *);
|
int transaction_free(transaction_data_t *);
|
||||||
|
|
|
||||||
|
|
@ -285,16 +285,6 @@ transaction_log(clicon_handle h,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Get pagination data: mode parameter
|
|
||||||
*
|
|
||||||
* @param[in] pd Pagination userdata
|
|
||||||
* @retval mode Pagination mode, stateless or locked
|
|
||||||
*/
|
|
||||||
pagination_mode_t
|
|
||||||
pagination_pagmode(pagination_data pd)
|
|
||||||
{
|
|
||||||
return ((pagination_data_t *)pd)->pd_pagmode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Get pagination data: offset parameter
|
/*! Get pagination data: offset parameter
|
||||||
*
|
*
|
||||||
|
|
@ -318,16 +308,22 @@ pagination_limit(pagination_data pd)
|
||||||
return ((pagination_data_t *)pd)->pd_limit;
|
return ((pagination_data_t *)pd)->pd_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Set pagination data: remaining nr of elements
|
/*! Get pagination data: locked parameter
|
||||||
*
|
*
|
||||||
* @param[in] pd Pagination userdata
|
* Pagination can use a lock/transaction mechanism
|
||||||
* @param[in] remaining If limit, then remaining nr of elements
|
* If locking is not used, the plugin cannot expect more pagination calls, and no state or
|
||||||
|
* caching should be used
|
||||||
|
* If locking is used, the pagination is part of a session transaction and the plugin may cache
|
||||||
|
* state (such as a cache) and can expect more pagination calls until the running db-lock is
|
||||||
|
* released, (see ca_lockdb)
|
||||||
|
* The transaction is the regular lock/unlock db of running-db of a specific session.
|
||||||
|
* @param[in] pd Pagination userdata
|
||||||
|
* @retval locked 0: unlocked/stateless 1: locked by this caller
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
pagination_remaining_set(pagination_data pd,
|
pagination_locked(pagination_data pd)
|
||||||
uint32_t remaining)
|
|
||||||
{
|
{
|
||||||
return ((pagination_data_t *)pd)->pd_remaining = remaining;
|
return ((pagination_data_t *)pd)->pd_locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Get pagination data: Returned xml state tree
|
/*! Get pagination data: Returned xml state tree
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,9 @@ int transaction_log(clicon_handle h, transaction_data th, int level, const char
|
||||||
/* Pagination callbacks
|
/* Pagination callbacks
|
||||||
* @see pagination_data_t internal structure
|
* @see pagination_data_t internal structure
|
||||||
*/
|
*/
|
||||||
pagination_mode_t pagination_pagmode(pagination_data pd);
|
int pagination_locked(pagination_data pd);
|
||||||
uint32_t pagination_offset(pagination_data pd);
|
uint32_t pagination_offset(pagination_data pd);
|
||||||
uint32_t pagination_limit(pagination_data pd);
|
uint32_t pagination_limit(pagination_data pd);
|
||||||
int pagination_remaining_set(pagination_data pd, uint32_t remaining);
|
|
||||||
cxobj *pagination_xstate(pagination_data pd);
|
cxobj *pagination_xstate(pagination_data pd);
|
||||||
|
|
||||||
#endif /* _CLIXON_BACKEND_TRANSACTION_H_ */
|
#endif /* _CLIXON_BACKEND_TRANSACTION_H_ */
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ static char *_state_file = NULL;
|
||||||
static char *_state_xpath = NULL;
|
static char *_state_xpath = NULL;
|
||||||
|
|
||||||
/*! Read state file init on startup instead of on request
|
/*! Read state file init on startup instead of on request
|
||||||
* Primarily for testing
|
* Primarily for testing: -i
|
||||||
* Start backend with -- -siS <file>
|
* Start backend with -- -siS <file>
|
||||||
*/
|
*/
|
||||||
static int _state_file_cached = 0;
|
static int _state_file_cached = 0;
|
||||||
|
|
@ -551,10 +551,9 @@ example_pagination(void *h0,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
clicon_handle h = (clicon_handle)h0;
|
clicon_handle h = (clicon_handle)h0;
|
||||||
pagination_mode_t pagmode;
|
int locked;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t limit;
|
uint32_t limit;
|
||||||
uint32_t remaining;
|
|
||||||
cxobj *xstate;
|
cxobj *xstate;
|
||||||
cxobj **xvec = NULL;
|
cxobj **xvec = NULL;
|
||||||
size_t xlen = 0;
|
size_t xlen = 0;
|
||||||
|
|
@ -566,13 +565,13 @@ example_pagination(void *h0,
|
||||||
uint32_t lower;
|
uint32_t lower;
|
||||||
uint32_t upper;
|
uint32_t upper;
|
||||||
int ret;
|
int ret;
|
||||||
cvec *nsc;
|
cvec *nsc = NULL;
|
||||||
|
|
||||||
/* If -S is set, then read state data from file */
|
/* If -S is set, then read state data from file */
|
||||||
if (!_state || !_state_file)
|
if (!_state || !_state_file)
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
||||||
pagmode = pagination_pagmode(pd);
|
locked = pagination_locked(pd);
|
||||||
offset = pagination_offset(pd);
|
offset = pagination_offset(pd);
|
||||||
limit = pagination_limit(pd);
|
limit = pagination_limit(pd);
|
||||||
xstate = pagination_xstate(pd);
|
xstate = pagination_xstate(pd);
|
||||||
|
|
@ -599,25 +598,12 @@ example_pagination(void *h0,
|
||||||
xt = _state_xml_cache;
|
xt = _state_xml_cache;
|
||||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
lower = 0;
|
lower = offset;
|
||||||
upper = xlen;
|
if (limit == 0)
|
||||||
switch (pagmode){
|
|
||||||
case PAGINATION_NONE:
|
|
||||||
lower = 0;
|
|
||||||
upper = xlen;
|
upper = xlen;
|
||||||
break;
|
else{
|
||||||
case PAGINATION_STATELESS:
|
if ((upper = offset+limit) > xlen)
|
||||||
case PAGINATION_LOCK:
|
|
||||||
lower = offset;
|
|
||||||
if (limit == 0)
|
|
||||||
upper = xlen;
|
upper = xlen;
|
||||||
else{
|
|
||||||
if ((upper = offset+limit)>xlen)
|
|
||||||
upper = xlen;
|
|
||||||
remaining = xlen - upper;
|
|
||||||
pagination_remaining_set(pd, remaining);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
/* Mark elements to copy:
|
/* Mark elements to copy:
|
||||||
* For every node found in x0, mark the tree as changed
|
* For every node found in x0, mark the tree as changed
|
||||||
|
|
@ -636,9 +622,10 @@ example_pagination(void *h0,
|
||||||
/* Unmark returned state tree */
|
/* Unmark returned state tree */
|
||||||
if (xml_apply(xstate, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
|
if (xml_apply(xstate, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (_state_file_cached)
|
if (_state_file_cached){
|
||||||
xt = NULL; /* ensure cache is not cleared */
|
xt = NULL; /* ensure cache is not cleared */
|
||||||
if (pagmode == PAGINATION_LOCK)
|
}
|
||||||
|
if (locked)
|
||||||
_state_file_transaction++;
|
_state_file_transaction++;
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -649,6 +636,8 @@ example_pagination(void *h0,
|
||||||
xml_free(xt);
|
xml_free(xt);
|
||||||
if (xvec)
|
if (xvec)
|
||||||
free(xvec);
|
free(xvec);
|
||||||
|
if (nsc)
|
||||||
|
cvec_free(nsc);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1173,7 +1162,7 @@ example_daemon(clicon_handle h)
|
||||||
yang_stmt *yspec;
|
yang_stmt *yspec;
|
||||||
|
|
||||||
/* 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 && 0){
|
if (_state && _state_file && _state_file_cached){
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
if ((fp = fopen(_state_file, "r")) == NULL){
|
if ((fp = fopen(_state_file, "r")) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "open(%s)", _state_file);
|
clicon_err(OE_UNIX, errno, "open(%s)", _state_file);
|
||||||
|
|
@ -1192,6 +1181,10 @@ example_daemon(clicon_handle h)
|
||||||
int
|
int
|
||||||
example_exit(clicon_handle h)
|
example_exit(clicon_handle h)
|
||||||
{
|
{
|
||||||
|
if (_state_xml_cache){
|
||||||
|
xml_free(_state_xml_cache);
|
||||||
|
_state_xml_cache = NULL;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ struct _dispatcher_entry {
|
||||||
/*
|
/*
|
||||||
* peer_head points at leftmost peer at this level
|
* peer_head points at leftmost peer at this level
|
||||||
* if NULL, then this is the leftmost and first on the list
|
* if NULL, then this is the leftmost and first on the list
|
||||||
|
* XXX: it seems it points to itself if it is first on the list?
|
||||||
*/
|
*/
|
||||||
dispatcher_entry_t *peer_head;
|
dispatcher_entry_t *peer_head;
|
||||||
|
|
||||||
|
|
@ -71,5 +72,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);
|
||||||
|
|
||||||
#endif /* DISPATCH_DISPATCHER_H */
|
#endif /* DISPATCH_DISPATCHER_H */
|
||||||
|
|
|
||||||
|
|
@ -196,26 +196,6 @@ typedef int (plgauth_t)(clicon_handle h, void *req, clixon_auth_type_t auth_type
|
||||||
*/
|
*/
|
||||||
typedef int (plgreset_t)(clicon_handle h, const char *db);
|
typedef int (plgreset_t)(clicon_handle h, const char *db);
|
||||||
|
|
||||||
/*! List pagination status in the plugin state data callback
|
|
||||||
*
|
|
||||||
* List pagination is either enabled or not.
|
|
||||||
* If pagination is enabled, the xpath addresses a list/ leaf-list and the plugin should return
|
|
||||||
* entries according to the values of offset and limit.
|
|
||||||
* Pagination can use a lock/transaction mechanism
|
|
||||||
* If locking is not used, the plugin cannot expect more pagination calls, and no state or caching
|
|
||||||
* should be used
|
|
||||||
* If locking is used, the pagination is part of a session transaction and the plugin may cache
|
|
||||||
* state (such as a cache) and can expect more pagination calls until the running db-lock is released,
|
|
||||||
* (see ca_lockdb)
|
|
||||||
* The transaction is the regular lock/unlock db of running-db of a specific session.
|
|
||||||
*/
|
|
||||||
enum pagination_mode{
|
|
||||||
PAGINATION_NONE, /* No list pagination: limit/offset are no-ops */
|
|
||||||
PAGINATION_STATELESS, /* Stateless list pagination, dont expect more pagination calls */
|
|
||||||
PAGINATION_LOCK /* Transactional list pagination, can expect more pagination until lock release */
|
|
||||||
};
|
|
||||||
typedef enum pagination_mode pagination_mode_t;
|
|
||||||
|
|
||||||
/* Plugin statedata
|
/* Plugin statedata
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] xpath Part of state requested
|
* @param[in] xpath Part of state requested
|
||||||
|
|
|
||||||
|
|
@ -71,33 +71,41 @@
|
||||||
|
|
||||||
#define PATH_CHUNKS 32
|
#define PATH_CHUNKS 32
|
||||||
|
|
||||||
/**
|
/*! Spilt a path into elements
|
||||||
* spilt a path into elements
|
|
||||||
*
|
*
|
||||||
* given an api-path, break it up into chunks separated by '/'
|
* given an api-path, break it up into chunks separated by '/'
|
||||||
* characters. it is expected that api-paths are URI encoded, so no need
|
* characters. it is expected that api-paths are URI encoded, so no need
|
||||||
* to deal with unescaped special characters like ', ", and /
|
* to deal with unescaped special characters like ', ", and /
|
||||||
*
|
*
|
||||||
* @param path [input] path string
|
* @param[in] path Path string
|
||||||
* @param plist [output] pointer to split path array
|
* @param[in] plist Pointer to split path array
|
||||||
* @param plist_len [output] pointer to storage space for path array length
|
* @param[out] plist_len Pointer to storage space for path array length
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* XXX consider using clixon_strsep
|
||||||
*/
|
*/
|
||||||
|
static int
|
||||||
static void split_path(char *path, char ***plist, size_t *plist_len)
|
split_path(char *path,
|
||||||
|
char ***plist,
|
||||||
|
size_t *plist_len)
|
||||||
{
|
{
|
||||||
|
int retval = -1;
|
||||||
size_t allocated = PATH_CHUNKS;
|
size_t allocated = PATH_CHUNKS;
|
||||||
|
char *work; /* don't modify the original copy */
|
||||||
/* don't modify the original copy */
|
char **list;
|
||||||
char *work = strdup(path);
|
|
||||||
|
|
||||||
char **list = malloc(allocated * sizeof(char *));
|
|
||||||
memset(list, 0, allocated * sizeof(char *));
|
|
||||||
|
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
char *ptr;
|
||||||
|
char *new_element;
|
||||||
|
|
||||||
char *ptr = work;
|
if ((work = strdup(path)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((list = malloc(allocated * sizeof(char *))) == NULL)
|
||||||
|
goto done;
|
||||||
|
memset(list, 0, allocated * sizeof(char *));
|
||||||
|
ptr = work;
|
||||||
if (*ptr == '/') {
|
if (*ptr == '/') {
|
||||||
char *new_element = strdup("/");
|
if ((new_element = strdup("/")) == NULL)
|
||||||
|
goto done;
|
||||||
list[len++] = new_element;
|
list[len++] = new_element;
|
||||||
ptr++;
|
ptr++;
|
||||||
}
|
}
|
||||||
|
|
@ -108,10 +116,12 @@ static void split_path(char *path, char ***plist, size_t *plist_len)
|
||||||
if (len > allocated) {
|
if (len > allocated) {
|
||||||
/* we've run out of space, allocate a bigger list */
|
/* we've run out of space, allocate a bigger list */
|
||||||
allocated += PATH_CHUNKS;
|
allocated += PATH_CHUNKS;
|
||||||
list = realloc(list, allocated * sizeof(char *));
|
if ((list = realloc(list, allocated * sizeof(char *))) == NULL)
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *new_element = strdup(ptr);
|
if ((new_element = strdup(ptr)) == NULL)
|
||||||
|
goto done;
|
||||||
list[len++] = new_element;
|
list[len++] = new_element;
|
||||||
|
|
||||||
ptr = strtok(NULL, "/");
|
ptr = strtok(NULL, "/");
|
||||||
|
|
@ -121,16 +131,19 @@ static void split_path(char *path, char ***plist, size_t *plist_len)
|
||||||
*plist_len = len;
|
*plist_len = len;
|
||||||
|
|
||||||
free(work);
|
free(work);
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*! Free a split path structure
|
||||||
* free a split path structure
|
|
||||||
*
|
*
|
||||||
* @param list [input] pointer to split path array
|
* @param[in] list pointer to split path array
|
||||||
* @param len [input] length of split path array
|
* @param[in] len length of split path array
|
||||||
*/
|
*/
|
||||||
|
static void
|
||||||
static void split_path_free(char **list, size_t len)
|
split_path_free(char **list,
|
||||||
|
size_t len)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
|
@ -140,23 +153,25 @@ static void split_path_free(char **list, size_t len)
|
||||||
free(list);
|
free(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*! Find a peer of this node by name
|
||||||
* find a peer of this node by name
|
|
||||||
* search through the list pointed at by peer
|
* search through the list pointed at by peer
|
||||||
*
|
*
|
||||||
* @param node [input] pointer to a node in the peer list
|
* @param[in] node Pointer to a node in the peer list
|
||||||
* @param node_name [input] name of node we're looking for
|
* @param[in] node_name Name of node we're looking for
|
||||||
* @return pointer to found node or NULL
|
* @retval pointer Pointer to found node or NULL
|
||||||
|
* @retval NULL
|
||||||
*/
|
*/
|
||||||
|
static dispatcher_entry_t *
|
||||||
static dispatcher_entry_t *find_peer(dispatcher_entry_t *node, char *node_name)
|
find_peer(dispatcher_entry_t *node, char *node_name)
|
||||||
{
|
{
|
||||||
|
dispatcher_entry_t *i;
|
||||||
|
|
||||||
if ((node == NULL) || (node_name == NULL)) {
|
if ((node == NULL) || (node_name == NULL)) {
|
||||||
/* protect against idiot users */
|
/* protect against idiot users */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatcher_entry_t *i = node->peer_head;
|
i = node->peer_head;
|
||||||
|
|
||||||
while (i != NULL) {
|
while (i != NULL) {
|
||||||
if (strcmp(node_name, i->node_name) == 0) {
|
if (strcmp(node_name, i->node_name) == 0) {
|
||||||
|
|
@ -168,19 +183,24 @@ static dispatcher_entry_t *find_peer(dispatcher_entry_t *node, char *node_name)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*! Add a node as the last node in peer list
|
||||||
* add a node as the last node in peer list
|
|
||||||
*
|
*
|
||||||
* @param node [input] pointer to an element of the peer list
|
* @param[in] node Pointer to an element of the peer list
|
||||||
* @param name [input] name of new node
|
* @param[in] name Name of new node
|
||||||
* @return pointer to added/existing node
|
* @retval pointer Pointer to added/existing node
|
||||||
|
* @retval NULL Error
|
||||||
*/
|
*/
|
||||||
|
static dispatcher_entry_t *
|
||||||
static dispatcher_entry_t *add_peer_node(dispatcher_entry_t *node, char *name)
|
add_peer_node(dispatcher_entry_t *node,
|
||||||
|
char *name)
|
||||||
{
|
{
|
||||||
dispatcher_entry_t *new_node = malloc(sizeof(dispatcher_entry_t));
|
dispatcher_entry_t *new_node = NULL;
|
||||||
memset(new_node, 0, sizeof(dispatcher_entry_t));
|
dispatcher_entry_t *eptr;
|
||||||
|
|
||||||
|
if ((new_node = malloc(sizeof(dispatcher_entry_t))) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memset(new_node, 0, sizeof(dispatcher_entry_t));
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
/* this is a new node */
|
/* this is a new node */
|
||||||
|
|
||||||
|
|
@ -194,7 +214,7 @@ static dispatcher_entry_t *add_peer_node(dispatcher_entry_t *node, char *name)
|
||||||
/* possibly adding to the list */
|
/* possibly adding to the list */
|
||||||
|
|
||||||
/* search for existing, or get tail end of list */
|
/* search for existing, or get tail end of list */
|
||||||
dispatcher_entry_t *eptr = node->peer_head;
|
eptr = node->peer_head;
|
||||||
while (eptr->peer != NULL) {
|
while (eptr->peer != NULL) {
|
||||||
if (strcmp(eptr->node_name, name) == 0) {
|
if (strcmp(eptr->node_name, name) == 0) {
|
||||||
return eptr;
|
return eptr;
|
||||||
|
|
@ -218,21 +238,26 @@ static dispatcher_entry_t *add_peer_node(dispatcher_entry_t *node, char *name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*! Add a node as a child of this node
|
||||||
* add a node as a child of this node
|
|
||||||
*
|
*
|
||||||
* this is different from add_peer_node() in that it returns a
|
* this is different from add_peer_node() in that it returns a
|
||||||
* pointer to the head_peer of the children list where the node was
|
* pointer to the head_peer of the children list where the node was
|
||||||
* added.
|
* added.
|
||||||
*
|
*
|
||||||
* @param node [input] pointer to parent node of children list
|
* @param[in] node Pointer to parent node of children list
|
||||||
* @param name [input] name of child node
|
* @param[in] name Name of child node
|
||||||
* @return pointer to head of children list
|
* @retval pointer Pointer to head of children list
|
||||||
|
* @retval NULL Error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static dispatcher_entry_t *add_child_node(dispatcher_entry_t *node, char *name)
|
static dispatcher_entry_t *
|
||||||
|
add_child_node(dispatcher_entry_t *node,
|
||||||
|
char *name)
|
||||||
{
|
{
|
||||||
dispatcher_entry_t *child_ptr = add_peer_node(node->children, name);
|
dispatcher_entry_t *child_ptr;
|
||||||
|
|
||||||
|
if ((child_ptr = add_peer_node(node->children, name)) == NULL)
|
||||||
|
return NULL;
|
||||||
node->children = child_ptr->peer_head;
|
node->children = child_ptr->peer_head;
|
||||||
|
|
||||||
return child_ptr;
|
return child_ptr;
|
||||||
|
|
@ -242,18 +267,21 @@ static dispatcher_entry_t *add_child_node(dispatcher_entry_t *node, char *name)
|
||||||
*
|
*
|
||||||
* @param root
|
* @param root
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* @retval
|
||||||
|
* @retval NULL Error
|
||||||
*/
|
*/
|
||||||
|
static dispatcher_entry_t *
|
||||||
static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path)
|
get_entry(dispatcher_entry_t *root,
|
||||||
|
char *path)
|
||||||
{
|
{
|
||||||
char **split_path_list = NULL;
|
char **split_path_list = NULL;
|
||||||
size_t split_path_len = 0;
|
size_t split_path_len = 0;
|
||||||
dispatcher_entry_t *ptr = root;
|
dispatcher_entry_t *ptr = root;
|
||||||
dispatcher_entry_t *best = root;
|
dispatcher_entry_t *best = root;
|
||||||
|
|
||||||
/* cut the path up into individual elements */
|
/* cut the path up into individual elements */
|
||||||
split_path(path, &split_path_list, &split_path_len);
|
if (split_path(path, &split_path_list, &split_path_len) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/* 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++) {
|
||||||
|
|
@ -268,9 +296,8 @@ static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path)
|
||||||
for (int i = 0; i < split_path_len; i++) {
|
for (int i = 0; i < split_path_len; i++) {
|
||||||
|
|
||||||
char *query = split_path_list[i];
|
char *query = split_path_list[i];
|
||||||
ptr = find_peer(ptr, query);
|
if ((ptr = find_peer(ptr, query)) == NULL) {
|
||||||
|
split_path_free(split_path_list, split_path_len);
|
||||||
if (ptr == NULL) {
|
|
||||||
/* we ran out of matches, use last found handler */
|
/* we ran out of matches, use last found handler */
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
@ -283,6 +310,9 @@ static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path)
|
||||||
ptr = ptr->children;
|
ptr = ptr->children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clean up */
|
||||||
|
split_path_free(split_path_list, split_path_len);
|
||||||
|
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,7 +322,7 @@ static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path)
|
||||||
*
|
*
|
||||||
* @param entry
|
* @param entry
|
||||||
* @param path
|
* @param path
|
||||||
* @return
|
* @retval
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
call_handler_helper(dispatcher_entry_t *entry,
|
call_handler_helper(dispatcher_entry_t *entry,
|
||||||
|
|
@ -330,12 +360,13 @@ int
|
||||||
dispatcher_register_handler(dispatcher_entry_t **root,
|
dispatcher_register_handler(dispatcher_entry_t **root,
|
||||||
dispatcher_definition *x)
|
dispatcher_definition *x)
|
||||||
{
|
{
|
||||||
char **split_path_list = NULL;
|
char **split_path_list = NULL;
|
||||||
size_t split_path_len = 0;
|
size_t split_path_len = 0;
|
||||||
|
dispatcher_entry_t *ptr;
|
||||||
|
|
||||||
if (*x->dd_path != '/') {
|
if (*x->dd_path != '/') {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
fprintf(stderr, "%s: part '%s' must start at root\n", __func__, x->dd_path);
|
// fprintf(stderr, "%s: part '%s' must start at root\n", __func__, x->dd_path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -343,20 +374,23 @@ dispatcher_register_handler(dispatcher_entry_t **root,
|
||||||
* get the path from the dispatcher_definition, break it
|
* get the path from the dispatcher_definition, break it
|
||||||
* up to create the elements of the dispatcher table
|
* up to create the elements of the dispatcher table
|
||||||
*/
|
*/
|
||||||
split_path(x->dd_path, &split_path_list, &split_path_len);
|
if (split_path(x->dd_path, &split_path_list, &split_path_len) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the first element is always a peer to the top level
|
* the first element is always a peer to the top level
|
||||||
*/
|
*/
|
||||||
dispatcher_entry_t *ptr = *root;
|
ptr = *root;
|
||||||
|
|
||||||
ptr = add_peer_node(ptr, split_path_list[0]);
|
if ((ptr = add_peer_node(ptr, split_path_list[0])) == NULL)
|
||||||
|
return -1;
|
||||||
if (*root == NULL) {
|
if (*root == NULL) {
|
||||||
*root = ptr;
|
*root = ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 1; i < split_path_len; i++) {
|
for (size_t i = 1; i < split_path_len; i++) {
|
||||||
ptr = add_child_node(ptr, split_path_list[i]);
|
if ((ptr = add_child_node(ptr, split_path_list[i])) == NULL)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* when we get here, ptr points at last entry added */
|
/* when we get here, ptr points at last entry added */
|
||||||
|
|
@ -366,8 +400,8 @@ dispatcher_register_handler(dispatcher_entry_t **root,
|
||||||
* you could make this an error optionally
|
* you could make this an error optionally
|
||||||
*/
|
*/
|
||||||
if (ptr->handler != NULL) {
|
if (ptr->handler != NULL) {
|
||||||
printf("%s: warning: replacing existing handler: (%s) %p -> %p\n", __func__,
|
// fprintf(stderr, "%s: warning: replacing existing handler: (%s) %p -> %p\n", __func__,
|
||||||
ptr->node_name, ptr->handler, x->dd_handler);
|
// ptr->node_name, ptr->handler, x->dd_handler);
|
||||||
}
|
}
|
||||||
ptr->handler = x->dd_handler;
|
ptr->handler = x->dd_handler;
|
||||||
}
|
}
|
||||||
|
|
@ -409,3 +443,20 @@ dispatcher_call_handlers(dispatcher_entry_t *root,
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Free a dispatcher tree
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
dispatcher_free(dispatcher_entry_t *root)
|
||||||
|
{
|
||||||
|
if (root == NULL)
|
||||||
|
return 0;
|
||||||
|
if (root->children)
|
||||||
|
dispatcher_free(root->children);
|
||||||
|
if (root->peer)
|
||||||
|
dispatcher_free(root->peer);
|
||||||
|
if (root->node_name)
|
||||||
|
free(root->node_name);
|
||||||
|
free(root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@
|
||||||
* @param[out] nvec Number of entries in returned vector
|
* @param[out] nvec Number of entries in returned vector
|
||||||
* @retval vec Vector of strings. NULL terminated. Free after use
|
* @retval vec Vector of strings. NULL terminated. Free after use
|
||||||
* @retval NULL Error *
|
* @retval NULL Error *
|
||||||
|
* @see clicon_strsplit
|
||||||
*/
|
*/
|
||||||
char **
|
char **
|
||||||
clicon_strsep(char *string,
|
clicon_strsep(char *string,
|
||||||
|
|
|
||||||
|
|
@ -140,13 +140,12 @@ if [ $BE -ne 0 ]; then
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
new "start backend -s init -f $cfg -- -sS $fstate -x /lib:global-state"
|
new "start backend -s init -f $cfg -- -sS $fstate"
|
||||||
start_backend -s init -f $cfg -- -sS $fstate -x /lib:global-state
|
start_backend -s init -f $cfg -- -sS $fstate
|
||||||
fi
|
fi
|
||||||
new "waiting"
|
new "waiting"
|
||||||
wait_backend
|
wait_backend
|
||||||
|
|
||||||
|
|
||||||
#-----------------------------
|
#-----------------------------
|
||||||
new "1. Empty config/state, expect global default state"
|
new "1. Empty config/state, expect global default state"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ testrun_stop
|
||||||
#----------------------------
|
#----------------------------
|
||||||
testrun_start "/es:members/es:member/es:stats/es:numbers"
|
testrun_start "/es:members/es:member/es:stats/es:numbers"
|
||||||
|
|
||||||
new "NETCONF get leaf-list member/numbers 0-10 alice"
|
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>]]>]]>$"
|
||||||
|
|
||||||
testrun_stop
|
testrun_stop
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue