diff --git a/apps/backend/backend_get.c b/apps/backend/backend_get.c index 586dc364..1d814516 100644 --- a/apps/backend/backend_get.c +++ b/apps/backend/backend_get.c @@ -284,9 +284,10 @@ get_client_statedata(clicon_handle h, * @param[in] h Clicon handle * @param[in] yspec Yang spec * @param[in] xret Result XML tree + * @param[in] xvec xpath lookup result on xret + * @param[in] xlen length of xvec * @param[in] xpath XPath point to object to get * @param[in] nsc Namespace context of xpath - * @param[out] x1p Pointer to first matching object if any * @retval 0 OK * @retval -1 Error */ @@ -294,17 +295,14 @@ static int filter_xpath_again(clicon_handle h, yang_stmt *yspec, cxobj *xret, + cxobj **xvec, + size_t xlen, char *xpath, - cvec *nsc, - cxobj **x1p) + cvec *nsc) { int retval = -1; - cxobj **xvec = NULL; - size_t xlen; int i; - if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) - goto done; /* If vectors are specified then mark the nodes found and * then filter out everything else, * otherwise return complete tree. @@ -326,12 +324,8 @@ filter_xpath_again(clicon_handle h, /* Add default recursive values */ if (xml_default_recurse(xret, 0) < 0) goto done; - if (xlen && x1p) - *x1p = xvec[0]; retval = 0; done: - if (xvec) - free(xvec); return retval; } @@ -339,6 +333,8 @@ filter_xpath_again(clicon_handle h, * * @param[in] h Clicon handle * @param[in] xret Result XML tree + * @param[in] xvec xpath lookup result on xret + * @param[in] xlen length of xvec * @param[in] xpath XPath point to object to get * @param[in] nsc Namespace context of xpath * @param[in] username User name for NACM access @@ -350,6 +346,8 @@ filter_xpath_again(clicon_handle h, static int get_nacm_and_reply(clicon_handle h, cxobj *xret, + cxobj **xvec, + size_t xlen, char *xpath, cvec *nsc, char *username, @@ -357,15 +355,11 @@ get_nacm_and_reply(clicon_handle h, cbuf *cbret) { int retval = -1; - cxobj **xvec = NULL; - size_t xlen; cxobj *xnacm = NULL; /* Pre-NACM access step */ xnacm = clicon_nacm_cache(h); if (xnacm != NULL){ /* Do NACM validation */ - if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) - goto done; /* NACM datanode/module read validation */ if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0) goto done; @@ -383,8 +377,6 @@ get_nacm_and_reply(clicon_handle h, cprintf(cbret, ""); retval = 0; done: - if (xvec) - free(xvec); return retval; } @@ -454,11 +446,7 @@ get_list_pagination(clicon_handle h, int retval = -1; uint32_t offset = 0; uint32_t limit = 0; - char *direction = NULL; - char *sort = NULL; - char *where = NULL; cbuf *cbpath = NULL; - cxobj *x; int list_config; yang_stmt *ylist; cxobj *xerr = NULL; @@ -468,7 +456,15 @@ get_list_pagination(clicon_handle h, int ret; uint32_t iddb; /* DBs lock, if any */ int locked; - cxobj *x1 = NULL; + cbuf *cberr = NULL; + cxobj **xvec = NULL; + size_t xlen; +#ifdef NOTYET + cxobj *x; + char *direction = NULL; + char *sort = NULL; + char *where = NULL; +#endif /* Check if list/leaf-list */ if (yang_path_arg(yspec, xpath, &ylist) < 0) @@ -504,11 +500,6 @@ get_list_pagination(clicon_handle h, goto done; goto ok; } -#if 0 /* XXX For now state lists are not implemenetd */ - if (netconf_operation_not_supported(cbret, "protocol", "List pagination for state lists is not yet implemented") < 0) - goto done; - goto ok; -#endif } /* offset */ if ((ret = element2value(h, xe, "offset", "none", cbret, &offset)) < 0) @@ -516,6 +507,7 @@ get_list_pagination(clicon_handle h, /* limit */ if (ret && (ret = element2value(h, xe, "limit", "unbounded", cbret, &limit)) < 0) goto done; +#ifdef NOTYET /* direction */ if (ret && (x = xml_find_type(xe, NULL, "direction", CX_ELMNT)) != NULL){ direction = xml_body(x); @@ -535,7 +527,7 @@ get_list_pagination(clicon_handle h, /* where */ if (ret && (x = xml_find_type(xe, NULL, "where", CX_ELMNT)) != NULL) where = xml_body(x); - +#endif /* Read config */ switch (content){ case CONTENT_CONFIG: /* config data only */ @@ -553,8 +545,10 @@ get_list_pagination(clicon_handle h, cprintf(cbpath, "%s", xpath); else cprintf(cbpath, "/"); +#ifdef NOTYET if (where) cprintf(cbpath, "[%s]", where); +#endif if (offset){ cprintf(cbpath, "[%u <= position()", offset); if (limit) @@ -607,11 +601,40 @@ get_list_pagination(clicon_handle h, offset, limit, xret)) < 0) goto done; - if (ret == 0) + if (ret == 0){ + if ((cberr = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } + /* error reason should be in clicon_err_reason */ + cprintf(cberr, "Internal error, pagination state callback invalid return : %s", + clicon_err_reason); + if (netconf_operation_failed_xml(&xerr, "application", cbuf_get(cberr)) < 0) + goto done; + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + goto done; goto ok; + } + + /* System makes the binding */ + if ((ret = xml_bind_yang(xret, YB_MODULE, yspec, &xerr)) < 0) + goto done; + if (ret == 0){ + if (clicon_debug_get() && xret) + clicon_log_xml(LOG_DEBUG, xret, "Yang bind pagination state"); + if (clixon_netconf_internal_error(xerr, + ". Internal error, state callback returned invalid XML", + NULL) < 0) + goto done; + if (clicon_xml2cbuf(cbret, xerr, 0, 0, -1) < 0) + goto done; + goto ok; + } } + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) + goto done; /* Help function to filter out anything that is outside of xpath */ - if (filter_xpath_again(h, yspec, xret, xpath, nsc, &x1) < 0) + if (filter_xpath_again(h, yspec, xret, xvec, xlen, xpath, nsc) < 0) goto done; #ifdef LIST_PAGINATION_REMAINING /* Add remaining attribute Sec 3.1.5: @@ -639,17 +662,21 @@ get_list_pagination(clicon_handle h, cbuf_free(cba); } #endif /* LIST_PAGINATION_REMAINING */ - if (get_nacm_and_reply(h, xret, xpath, nsc, username, depth, cbret) < 0) + if (get_nacm_and_reply(h, xret, xvec, xlen, xpath, nsc, username, depth, cbret) < 0) goto done; ok: retval = 0; done: + if (xvec) + free(xvec); if (cbmsg) cbuf_free(cbmsg); if (cbpath) cbuf_free(cbpath); if (xerr) xml_free(xerr); + if (cberr) + cbuf_free(cberr); if (xret) xml_free(xret); return retval; @@ -696,6 +723,10 @@ get_common(clicon_handle h, int list_pagination = 0; char *valstr; cxobj *x; +#if 1 + cxobj **xvec = NULL; + size_t xlen; +#endif clicon_debug(1, "%s", __FUNCTION__); username = clicon_username_get(h); @@ -852,14 +883,18 @@ get_common(clicon_handle h, if (xml_apply(xret, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0) goto done; } - if (filter_xpath_again(h, yspec, xret, xpath, nsc, NULL) < 0) + if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) goto done; - if (get_nacm_and_reply(h, xret, xpath, nsc, username, depth, cbret) < 0) + if (filter_xpath_again(h, yspec, xret, xvec, xlen, xpath, nsc) < 0) + goto done; + if (get_nacm_and_reply(h, xret, xvec, xlen, xpath, nsc, username, depth, cbret) < 0) goto done; ok: retval = 0; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (xvec) + free(xvec); if (xret) xml_free(xret); if (cbreason) diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index a3602170..c8bff188 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -108,6 +108,7 @@ clixon_plugin_reset_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); /* Loop through all plugins, call callbacks in each */ while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (clixon_plugin_reset_one(cp, h, db) < 0) @@ -160,6 +161,7 @@ clixon_plugin_pre_daemon_all(clicon_handle h) int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); /* Loop through all plugins, call callbacks in each */ while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (clixon_plugin_pre_daemon_one(cp, h) < 0) @@ -213,6 +215,7 @@ clixon_plugin_daemon_all(clicon_handle h) int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); /* Loop through all plugins, call callbacks in each */ while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (clixon_plugin_daemon_one(cp, h) < 0) @@ -259,7 +262,6 @@ clixon_plugin_statedata_one(clixon_plugin_t *cp, plgstatedata_t *fn; /* Plugin statedata fn */ cxobj *x = NULL; - clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp)); if ((fn = clixon_plugin_api_get(cp)->ca_statedata) != NULL){ if ((x = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) goto done; @@ -407,7 +409,6 @@ clixon_plugin_lockdb_one(clixon_plugin_t *cp, int retval = -1; plglockdb_t *fn; /* Plugin statedata fn */ - clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp)); if ((fn = clixon_plugin_api_get(cp)->ca_lockdb) != NULL){ if (fn(h, db, lock, id) < 0) goto done; @@ -605,6 +606,7 @@ plugin_transaction_begin_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (plugin_transaction_begin_one(cp, h, td) < 0) goto done; @@ -899,6 +901,7 @@ plugin_transaction_end_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (plugin_transaction_end_one(cp, h, td) < 0) goto done; @@ -942,6 +945,7 @@ plugin_transaction_abort_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (plugin_transaction_abort_one(cp, h, td) < 0) ; /* dont abort on error */ diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 76c2166d..07ba04ec 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -919,6 +919,7 @@ cli_pagination(clicon_handle h, uint32_t limit = 0; cxobj **xvec = NULL; size_t xlen; + int locked = 0; if (cvec_len(argv) != 5){ clicon_err(OE_PLUGIN, 0, "Expected usage: "); @@ -950,6 +951,7 @@ cli_pagination(clicon_handle h, goto done; if (clicon_rpc_lock(h, "running") < 0) goto done; + locked++; for (i = 0;; i++){ if (clicon_rpc_get_pageable_list(h, "running", xpath, nsc, CONTENT_ALL, @@ -1000,10 +1002,10 @@ cli_pagination(clicon_handle h, xvec = NULL; } } /* for i */ - if (clicon_rpc_unlock(h, "running") < 0) - goto done; retval = 0; done: + if (locked) + clicon_rpc_unlock(h, "running"); if (xvec) free(xvec); if (xret) diff --git a/example/main/example_backend.c b/example/main/example_backend.c index ae2fe8a1..947d9d70 100644 --- a/example/main/example_backend.c +++ b/example/main/example_backend.c @@ -514,7 +514,10 @@ example_statefile(clicon_handle h, xml_flag_set(x1, XML_FLAG_MARK); xml_apply_ancestor(x1, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); } - if (xml_copy_marked(xt, xstate) < 0) /* Copy the marked elements */ + /* Copy the marked elements: + * note is yang-aware for copying of keys which means XML must be bound + */ + if (xml_copy_marked(xt, xstate) < 0) goto done; /* Unmark original tree */ if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)(XML_FLAG_MARK|XML_FLAG_CHANGE)) < 0) @@ -614,6 +617,9 @@ example_pagination(void *h0, xml_flag_set(x1, XML_FLAG_MARK); xml_apply_ancestor(x1, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE); } + /* Copy the marked elements: + * note is yang-aware for copying of keys which means XML must be bound + */ if (xml_copy_marked(xt, xstate) < 0) /* Copy the marked elements */ goto done; /* Unmark original tree */ @@ -1160,6 +1166,7 @@ example_daemon(clicon_handle h) int ret; FILE *fp = NULL; yang_stmt *yspec; + cxobj *xerr = NULL; /* Read state file (or should this be in init/start?) */ if (_state && _state_file && _state_file_cached){ @@ -1168,8 +1175,14 @@ example_daemon(clicon_handle h) clicon_err(OE_UNIX, errno, "open(%s)", _state_file); goto done; } - if ((ret = clixon_xml_parse_file(fp, YB_MODULE, yspec, &_state_xml_cache, NULL)) < 1) + /* Need to be yang bound for eg xml_copy_marked() in example_pagination + */ + if ((ret = clixon_xml_parse_file(fp, YB_MODULE, yspec, &_state_xml_cache, &xerr)) < 0) goto done; + if (ret == 0){ + xml_print(stderr, xerr); + goto done; + } } retval = 0; done: diff --git a/lib/src/clixon_data.c b/lib/src/clixon_data.c index 1c0970e3..7f21ec6e 100644 --- a/lib/src/clixon_data.c +++ b/lib/src/clixon_data.c @@ -227,6 +227,11 @@ clicon_data_cvec_set(clicon_handle h, const char *name, cvec *cvv) { + cvec *cvv0 = NULL; + + clicon_ptr_get(h, name, (void**)&cvv0); + if (cvv0) + cvec_free(cvv0); return clicon_ptr_set(h, name, cvv); } @@ -238,6 +243,11 @@ int clicon_data_cvec_del(clicon_handle h, const char *name) { + cvec *cvv = NULL; + + clicon_ptr_get(h, name, (void**)&cvv); + if (cvv) + cvec_free(cvv); return clicon_ptr_del(h, name); } @@ -377,9 +387,13 @@ clicon_nacm_ext(clicon_handle h) */ int clicon_nacm_ext_set(clicon_handle h, - cxobj *xn) + cxobj *x) { - return clicon_ptr_set(h, "nacm_xml", xn); + cxobj *x0 = NULL; + + if ((x0 = clicon_nacm_ext(h)) != NULL) + xml_free(x0); + return clicon_ptr_set(h, "nacm_xml", x); } /*! Get NACM (rfc 8341) XML parse tree cache diff --git a/lib/src/clixon_dispatcher.c b/lib/src/clixon_dispatcher.c index 05b3f911..b3793fb5 100644 --- a/lib/src/clixon_dispatcher.c +++ b/lib/src/clixon_dispatcher.c @@ -68,10 +68,6 @@ * [b=] * [b] * - * NOTE 1: there is not a mechanism to free the created structures since - * it is intended that this tree is created only at startup. if use case - * changes, this function is trivial. - * * NOTE 2: there is no attempt to optimize list searching here, sorry. I * do not think that the known use cases will get big enough to make the * tree get too large. I do not recommend that you encode every possible @@ -439,10 +435,10 @@ dispatcher_register_handler(dispatcher_entry_t **root, * * @param[in] handle * @param[in] root - * @param[in] path - * @retval 1 OK - * @retval 0 Invalid - * @retval -1 Error + * @param[in] path Note must be on the form: /a/b (no keys) + * @retval 1 OK + * @retval 0 Invalid + * @retval -1 Error */ int dispatcher_call_handlers(dispatcher_entry_t *root, diff --git a/test/test_pagination_state.sh b/test/test_pagination_state.sh index e9ef8490..5ba12df0 100755 --- a/test/test_pagination_state.sh +++ b/test/test_pagination_state.sh @@ -17,8 +17,6 @@ cfg=$dir/conf.xml fexample=$dir/example-social.yang fstate=$dir/mystate.xml -xpath=/es:audit-logs/es:audit-log - # For 1M test,m use an external file since the generation takes considerable time #fstate=~/tmp/mystate.xml @@ -110,7 +108,7 @@ function testrun_start() fi sudo pkill -f clixon_backend # to be sure - new "start backend -s init -f $cfg -- -siS $fstate -X $xpath" + new "start backend -s init -f $cfg -- -siS $fstate -x $xpath" start_backend -s init -f $cfg -- -siS $fstate -x $xpath fi @@ -132,19 +130,26 @@ function testrun_stop() fi } -testrun_start "/es:members/es:member[es:member-id='alice']/es:stats/es:numbers" +xpath0="/es:members/es:member[es:member-id='alice']/es:stats" +xpath="$xpath0/es:numbers" +testrun_start $xpath new "NETCONF get leaf-list member/numbers 0-10 alice" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOtrue010]]>]]>" "^alicepublic345678]]>]]>$" # negative new "NETCONF get container, expect fail" -expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOtrue010]]>]]>" "^applicationinvalid-valueerrorlist-pagination is enabled but target is not list or leaf-list]]>]]>$" +expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOtrue010]]>]]>" "^applicationinvalid-valueerrorlist-pagination is enabled but target is not list or leaf-list]]>]]>$" + +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 "$DEFAULTHELLOtrue010]]>]]>" "^bobpublic131415161718]]>]]>$" testrun_stop #---------------------------- -testrun_start "/es:members/es:member/es:stats/es:numbers" +xpath="/es:members/es:member/es:stats/es:numbers" +testrun_start $xpath new "NETCONF get leaf-list member/numbers all" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOtrue010]]>]]>" "^alicepublic345678bobpublic13141516]]>]]>$" @@ -169,6 +174,7 @@ fi # interactive unset validatexml unset perfnr unset xpath +unset xpath0 rm -rf $dir