From edbbb43e1f178010f61d8f1932efe628b0e6fc82 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 9 Oct 2021 15:50:13 +0200 Subject: [PATCH] Removed remaining and replaced pagination-mode with locked parameter Dispatcher: Added dispatcher_free(), fixed mem-leaks and malloc return checks --- CHANGELOG.md | 3 +- apps/backend/backend_get.c | 15 +- apps/backend/backend_main.c | 2 +- apps/backend/backend_plugin.c | 36 +++-- apps/backend/clixon_backend_plugin.h | 18 ++- apps/backend/clixon_backend_transaction.c | 28 ++-- apps/backend/clixon_backend_transaction.h | 3 +- example/main/example_backend.c | 43 +++-- lib/clixon/clixon_dispatcher.h | 2 + lib/clixon/clixon_plugin.h | 20 --- lib/src/clixon_dispatcher.c | 187 ++++++++++++++-------- lib/src/clixon_string.c | 1 + test/test_augment_state.sh | 5 +- test/test_pagination_state.sh | 2 +- 14 files changed, 204 insertions(+), 161 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30a0b6aa..b2f67581 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,8 @@ Thanks netgate for providing the dispatcher code! * Register callback with: `clixon_pagination_cb_register()` * Use accessor functions `pagination_offset()`, `pagination_limit()`, etc * 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 Users may have to change how they access the system diff --git a/apps/backend/backend_get.c b/apps/backend/backend_get.c index 7157a0a9..586dc364 100644 --- a/apps/backend/backend_get.c +++ b/apps/backend/backend_get.c @@ -467,11 +467,8 @@ get_list_pagination(clicon_handle h, char *xpath2; /* With optional pagination predicate */ int ret; uint32_t iddb; /* DBs lock, if any */ - pagination_mode_t pagmode; + int locked; cxobj *x1 = NULL; - cxobj *xcache = NULL; - uint32_t total = 0; - uint32_t remaining = 0; /* Check if list/leaf-list */ if (yang_path_arg(yspec, xpath, &ylist) < 0) @@ -588,6 +585,7 @@ get_list_pagination(clicon_handle h, }/* switch content */ if (list_config){ +#ifdef LIST_PAGINATION_REMAINING /* Get total/remaining * XXX: Works only for cache */ @@ -597,15 +595,16 @@ get_list_pagination(clicon_handle h, if (total >= (offset + limit)) remaining = total - (offset + limit); } +#endif } else {/* Check if running locked (by this session) */ if ((iddb = xmldb_islocked(h, "running")) != 0 && iddb == ce->ce_id) - pagmode = PAGINATION_LOCK; + locked = 1; else - pagmode = PAGINATION_STATELESS; - if ((ret = clixon_pagination_cb_call(h, xpath, pagmode, - offset, limit, &remaining, + locked = 0; + if ((ret = clixon_pagination_cb_call(h, xpath, locked, + offset, limit, xret)) < 0) goto done; if (ret == 0) diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index d494f6bc..5e393c4a 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -131,7 +131,7 @@ backend_terminate(clicon_handle h) clixon_process_delete_all(h); xpath_optimize_exit(); - + clixon_pagination_free(h); if (pidfile) unlink(pidfile); if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0) diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index 45d1108f..e9d39588 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -452,23 +452,24 @@ clixon_plugin_lockdb_all(clicon_handle h, * @param[in] xpath Registered XPath using canonical prefixes */ int -clixon_pagination_cb_call(clicon_handle h, - char *xpath, - pagination_mode_t pagmode, - uint32_t offset, - uint32_t limit, - uint32_t *remaining, - cxobj *xstate) +clixon_pagination_cb_call(clicon_handle h, + char *xpath, + int locked, + uint32_t offset, + uint32_t limit, + cxobj *xstate) { int retval = -1; - pagination_data_t pd = {pagmode, offset, limit, 0, xstate}; + pagination_data_t pd; 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); if (htable && dispatcher_call_handlers(htable, h, xpath, &pd) < 0) goto done; - if (remaining) - *remaining = pd.pd_remaining; retval = 1; done: return retval; @@ -503,6 +504,21 @@ clixon_pagination_cb_register(clicon_handle h, 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 * @retval td New alloced transaction, * @retval NULL Error diff --git a/apps/backend/clixon_backend_plugin.h b/apps/backend/clixon_backend_plugin.h index 98daa126..a32a5bda 100644 --- a/apps/backend/clixon_backend_plugin.h +++ b/apps/backend/clixon_backend_plugin.h @@ -68,17 +68,22 @@ typedef struct { } transaction_data_t; /*! 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] limit Limit, for list pagination - * @param[out] remaining Remaining elements (if limit is non-zero) * see also pagination_data in clixon_plugin.h */ typedef struct { - pagination_mode_t pd_pagmode; /* Pagination mode, stateless or locked */ uint32_t pd_offset; /* Start of pagination interval */ 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 */ } 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_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, - uint32_t offset, uint32_t limit, uint32_t *remaining, +int clixon_pagination_cb_call(clicon_handle h, char *xpath, int locked, + uint32_t offset, uint32_t limit, cxobj *xstate); +int clixon_pagination_free(clicon_handle h); transaction_data_t * transaction_new(void); int transaction_free(transaction_data_t *); diff --git a/apps/backend/clixon_backend_transaction.c b/apps/backend/clixon_backend_transaction.c index 40931a7f..e7af1c7c 100644 --- a/apps/backend/clixon_backend_transaction.c +++ b/apps/backend/clixon_backend_transaction.c @@ -285,16 +285,6 @@ transaction_log(clicon_handle h, 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 * @@ -318,16 +308,22 @@ pagination_limit(pagination_data pd) return ((pagination_data_t *)pd)->pd_limit; } -/*! Set pagination data: remaining nr of elements +/*! Get pagination data: locked parameter * - * @param[in] pd Pagination userdata - * @param[in] remaining If limit, then remaining nr of elements + * 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] pd Pagination userdata + * @retval locked 0: unlocked/stateless 1: locked by this caller */ int -pagination_remaining_set(pagination_data pd, - uint32_t remaining) +pagination_locked(pagination_data pd) { - return ((pagination_data_t *)pd)->pd_remaining = remaining; + return ((pagination_data_t *)pd)->pd_locked; } /*! Get pagination data: Returned xml state tree diff --git a/apps/backend/clixon_backend_transaction.h b/apps/backend/clixon_backend_transaction.h index a14d7ccf..00431c6d 100644 --- a/apps/backend/clixon_backend_transaction.h +++ b/apps/backend/clixon_backend_transaction.h @@ -67,10 +67,9 @@ int transaction_log(clicon_handle h, transaction_data th, int level, const char /* Pagination callbacks * @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_limit(pagination_data pd); -int pagination_remaining_set(pagination_data pd, uint32_t remaining); cxobj *pagination_xstate(pagination_data pd); #endif /* _CLIXON_BACKEND_TRANSACTION_H_ */ diff --git a/example/main/example_backend.c b/example/main/example_backend.c index db5a816c..ae2fe8a1 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -95,7 +95,7 @@ static char *_state_file = NULL; static char *_state_xpath = NULL; /*! Read state file init on startup instead of on request - * Primarily for testing + * Primarily for testing: -i * Start backend with -- -siS */ static int _state_file_cached = 0; @@ -551,10 +551,9 @@ example_pagination(void *h0, { int retval = -1; clicon_handle h = (clicon_handle)h0; - pagination_mode_t pagmode; + int locked; uint32_t offset; uint32_t limit; - uint32_t remaining; cxobj *xstate; cxobj **xvec = NULL; size_t xlen = 0; @@ -566,13 +565,13 @@ example_pagination(void *h0, uint32_t lower; uint32_t upper; int ret; - cvec *nsc; + cvec *nsc = NULL; /* If -S is set, then read state data from file */ if (!_state || !_state_file) goto ok; - pagmode = pagination_pagmode(pd); + locked = pagination_locked(pd); offset = pagination_offset(pd); limit = pagination_limit(pd); xstate = pagination_xstate(pd); @@ -599,25 +598,12 @@ example_pagination(void *h0, xt = _state_xml_cache; if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, xpath) < 0) goto done; - lower = 0; - upper = xlen; - switch (pagmode){ - case PAGINATION_NONE: - lower = 0; + lower = offset; + if (limit == 0) upper = xlen; - break; - case PAGINATION_STATELESS: - case PAGINATION_LOCK: - lower = offset; - if (limit == 0) + else{ + if ((upper = offset+limit) > xlen) upper = xlen; - else{ - if ((upper = offset+limit)>xlen) - upper = xlen; - remaining = xlen - upper; - pagination_remaining_set(pd, remaining); - } - break; } /* Mark elements to copy: * For every node found in x0, mark the tree as changed @@ -636,9 +622,10 @@ example_pagination(void *h0, /* Unmark returned state tree */ if (xml_apply(xstate, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0) goto done; - if (_state_file_cached) + if (_state_file_cached){ xt = NULL; /* ensure cache is not cleared */ - if (pagmode == PAGINATION_LOCK) + } + if (locked) _state_file_transaction++; ok: retval = 0; @@ -649,6 +636,8 @@ example_pagination(void *h0, xml_free(xt); if (xvec) free(xvec); + if (nsc) + cvec_free(nsc); return retval; } @@ -1173,7 +1162,7 @@ example_daemon(clicon_handle h) yang_stmt *yspec; /* 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); if ((fp = fopen(_state_file, "r")) == NULL){ clicon_err(OE_UNIX, errno, "open(%s)", _state_file); @@ -1192,6 +1181,10 @@ example_daemon(clicon_handle h) int example_exit(clicon_handle h) { + if (_state_xml_cache){ + xml_free(_state_xml_cache); + _state_xml_cache = NULL; + } return 0; } diff --git a/lib/clixon/clixon_dispatcher.h b/lib/clixon/clixon_dispatcher.h index fe4350ec..6a3ddd01 100644 --- a/lib/clixon/clixon_dispatcher.h +++ b/lib/clixon/clixon_dispatcher.h @@ -46,6 +46,7 @@ struct _dispatcher_entry { /* * peer_head points at leftmost peer at this level * 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; @@ -71,5 +72,6 @@ struct _dispatcher_entry { */ int dispatcher_register_handler(dispatcher_entry_t **root, dispatcher_definition *x); int dispatcher_call_handlers(dispatcher_entry_t *root, void *handle, char *path, void *user_args); +int dispatcher_free(dispatcher_entry_t *root); #endif /* DISPATCH_DISPATCHER_H */ diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index 338b21ef..bad00a11 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.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); -/*! 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 * @param[in] h Clicon handle * @param[in] xpath Part of state requested diff --git a/lib/src/clixon_dispatcher.c b/lib/src/clixon_dispatcher.c index 18b13f66..c11cef84 100644 --- a/lib/src/clixon_dispatcher.c +++ b/lib/src/clixon_dispatcher.c @@ -71,33 +71,41 @@ #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 '/' * characters. it is expected that api-paths are URI encoded, so no need * to deal with unescaped special characters like ', ", and / * - * @param path [input] path string - * @param plist [output] pointer to split path array - * @param plist_len [output] pointer to storage space for path array length + * @param[in] path Path string + * @param[in] plist Pointer to split path array + * @param[out] plist_len Pointer to storage space for path array length + * @retval 0 OK + * @retval -1 Error + * XXX consider using clixon_strsep */ - -static void split_path(char *path, char ***plist, size_t *plist_len) +static int +split_path(char *path, + char ***plist, + size_t *plist_len) { + int retval = -1; size_t allocated = PATH_CHUNKS; - - /* don't modify the original copy */ - char *work = strdup(path); - - char **list = malloc(allocated * sizeof(char *)); - memset(list, 0, allocated * sizeof(char *)); - + char *work; /* don't modify the original copy */ + char **list; 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 == '/') { - char *new_element = strdup("/"); + if ((new_element = strdup("/")) == NULL) + goto done; list[len++] = new_element; ptr++; } @@ -108,10 +116,12 @@ static void split_path(char *path, char ***plist, size_t *plist_len) if (len > allocated) { /* we've run out of space, allocate a bigger list */ 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; ptr = strtok(NULL, "/"); @@ -121,16 +131,19 @@ static void split_path(char *path, char ***plist, size_t *plist_len) *plist_len = len; 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 len [input] length of split path array + * @param[in] list pointer to split path array + * @param[in] len length of split path array */ - -static void split_path_free(char **list, size_t len) +static void +split_path_free(char **list, + size_t len) { size_t i; @@ -140,23 +153,25 @@ static void split_path_free(char **list, size_t len) 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 * - * @param node [input] pointer to a node in the peer list - * @param node_name [input] name of node we're looking for - * @return pointer to found node or NULL + * @param[in] node Pointer to a node in the peer list + * @param[in] node_name Name of node we're looking for + * @retval pointer Pointer to found node or NULL + * @retval NULL */ - -static dispatcher_entry_t *find_peer(dispatcher_entry_t *node, char *node_name) +static dispatcher_entry_t * +find_peer(dispatcher_entry_t *node, char *node_name) { + dispatcher_entry_t *i; + if ((node == NULL) || (node_name == NULL)) { /* protect against idiot users */ return NULL; } - dispatcher_entry_t *i = node->peer_head; + i = node->peer_head; while (i != NULL) { 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; } -/** - * 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 name [input] name of new node - * @return pointer to added/existing node + * @param[in] node Pointer to an element of the peer list + * @param[in] name Name of new node + * @retval pointer Pointer to added/existing node + * @retval NULL Error */ - -static dispatcher_entry_t *add_peer_node(dispatcher_entry_t *node, char *name) +static dispatcher_entry_t * +add_peer_node(dispatcher_entry_t *node, + char *name) { - dispatcher_entry_t *new_node = malloc(sizeof(dispatcher_entry_t)); - memset(new_node, 0, sizeof(dispatcher_entry_t)); + dispatcher_entry_t *new_node = NULL; + 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) { /* 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 */ /* search for existing, or get tail end of list */ - dispatcher_entry_t *eptr = node->peer_head; + eptr = node->peer_head; while (eptr->peer != NULL) { if (strcmp(eptr->node_name, name) == 0) { 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 * pointer to the head_peer of the children list where the node was * added. * - * @param node [input] pointer to parent node of children list - * @param name [input] name of child node - * @return pointer to head of children list + * @param[in] node Pointer to parent node of children list + * @param[in] name Name of child node + * @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; return child_ptr; @@ -242,18 +267,21 @@ static dispatcher_entry_t *add_child_node(dispatcher_entry_t *node, char *name) * * @param root * @param path - * @return + * @retval + * @retval NULL Error */ - -static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path) +static dispatcher_entry_t * +get_entry(dispatcher_entry_t *root, + char *path) { - char **split_path_list = NULL; - size_t split_path_len = 0; + char **split_path_list = NULL; + size_t split_path_len = 0; dispatcher_entry_t *ptr = root; dispatcher_entry_t *best = root; /* 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 */ 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++) { char *query = split_path_list[i]; - ptr = find_peer(ptr, query); - - if (ptr == NULL) { + if ((ptr = find_peer(ptr, query)) == NULL) { + split_path_free(split_path_list, split_path_len); /* we ran out of matches, use last found handler */ return best; } @@ -283,6 +310,9 @@ static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path) ptr = ptr->children; } + /* clean up */ + split_path_free(split_path_list, split_path_len); + return best; } @@ -292,7 +322,7 @@ static dispatcher_entry_t *get_entry(dispatcher_entry_t *root, char *path) * * @param entry * @param path - * @return + * @retval */ static int call_handler_helper(dispatcher_entry_t *entry, @@ -330,12 +360,13 @@ int dispatcher_register_handler(dispatcher_entry_t **root, dispatcher_definition *x) { - char **split_path_list = NULL; - size_t split_path_len = 0; + char **split_path_list = NULL; + size_t split_path_len = 0; + dispatcher_entry_t *ptr; if (*x->dd_path != '/') { 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; } @@ -343,20 +374,23 @@ dispatcher_register_handler(dispatcher_entry_t **root, * get the path from the dispatcher_definition, break it * 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 */ - 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) { *root = ptr; } 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 */ @@ -366,8 +400,8 @@ dispatcher_register_handler(dispatcher_entry_t **root, * you could make this an error optionally */ if (ptr->handler != NULL) { - printf("%s: warning: replacing existing handler: (%s) %p -> %p\n", __func__, - ptr->node_name, ptr->handler, x->dd_handler); + // fprintf(stderr, "%s: warning: replacing existing handler: (%s) %p -> %p\n", __func__, + // ptr->node_name, ptr->handler, x->dd_handler); } ptr->handler = x->dd_handler; } @@ -409,3 +443,20 @@ dispatcher_call_handlers(dispatcher_entry_t *root, } 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; +} diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 524bdaa4..737e3180 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -77,6 +77,7 @@ * @param[out] nvec Number of entries in returned vector * @retval vec Vector of strings. NULL terminated. Free after use * @retval NULL Error * + * @see clicon_strsplit */ char ** clicon_strsep(char *string, diff --git a/test/test_augment_state.sh b/test/test_augment_state.sh index 9c0da067..00f40713 100755 --- a/test/test_augment_state.sh +++ b/test/test_augment_state.sh @@ -140,13 +140,12 @@ if [ $BE -ne 0 ]; then if [ $? -ne 0 ]; then err fi - new "start backend -s init -f $cfg -- -sS $fstate -x /lib:global-state" - 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 fi new "waiting" wait_backend - #----------------------------- new "1. Empty config/state, expect global default state" diff --git a/test/test_pagination_state.sh b/test/test_pagination_state.sh index ef1fef37..60574333 100755 --- a/test/test_pagination_state.sh +++ b/test/test_pagination_state.sh @@ -146,7 +146,7 @@ testrun_stop #---------------------------- 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 "$DEFAULTHELLOtrue010]]>]]>" "^alicepublic345678bobpublic13141516]]>]]>$" testrun_stop