diff --git a/CHANGELOG.md b/CHANGELOG.md index 560857f1..81dc1a13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,10 @@ Developers may need to change their code ### Minor features +* Plugin context check before and after all callbacks. + * Check blocked signals and signal handlers + * Check termios settings + * Any changes to context are logged at loglevel WARNING * Added set/get pointer API to clixon_data: * clicon_ptr_get(), clicon_ptr_set(), * Restconf YANG PATCH according to RFC 8072 diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 8abcbcbc..0f71b19e 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -931,16 +931,21 @@ from_client_restart_one(clicon_handle h, yang_stmt *yspec; int i; cxobj *xn; + plugin_context_t *pc = NULL; yspec = clicon_dbspec_yang(h); if (xmldb_db_reset(h, db) < 0) goto done; /* Application may define extra xml in its reset function*/ if ((resetfn = clixon_plugin_api_get(cp)->ca_reset) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if ((retval = resetfn(h, db)) < 0) { clicon_debug(1, "plugin_start() failed"); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } /* 1. Start transaction */ if ((td = transaction_new()) == NULL) @@ -1021,6 +1026,8 @@ from_client_restart_one(clicon_handle h, goto fail; retval = 1; done: + if (pc) + free(pc); if (td){ xmldb_get0_free(h, &td->td_target); transaction_free(td); 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..6d1c1daf 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -78,19 +78,26 @@ clixon_plugin_reset_one(clixon_plugin_t *cp, clicon_handle h, char *db) { - int retval = -1; - plgreset_t *fn; /* callback */ + int retval = -1; + plgreset_t *fn; /* callback */ + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_reset) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, db) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Reset callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -108,6 +115,7 @@ clixon_plugin_reset_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); /* Loop through all plugins, call callbacks in each */ while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (clixon_plugin_reset_one(cp, h, db) < 0) @@ -130,8 +138,12 @@ clixon_plugin_pre_daemon_one(clixon_plugin_t *cp, { int retval = -1; plgdaemon_t *fn; /* Daemonize plugin callback function */ + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_pre_daemon) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; + if (fn(h) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Pre-daemon callback in plugin:\ @@ -139,9 +151,13 @@ clixon_plugin_pre_daemon_one(clixon_plugin_t *cp, __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -160,6 +176,7 @@ clixon_plugin_pre_daemon_all(clicon_handle h) int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); /* Loop through all plugins, call callbacks in each */ while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (clixon_plugin_pre_daemon_one(cp, h) < 0) @@ -182,17 +199,24 @@ clixon_plugin_daemon_one(clixon_plugin_t *cp, { int retval = -1; plgdaemon_t *fn; /* Daemonize plugin callback function */ + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_daemon) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Daemon callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -213,6 +237,7 @@ clixon_plugin_daemon_all(clicon_handle h) int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); /* Loop through all plugins, call callbacks in each */ while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (clixon_plugin_daemon_one(cp, h) < 0) @@ -258,22 +283,28 @@ clixon_plugin_statedata_one(clixon_plugin_t *cp, int retval = -1; plgstatedata_t *fn; /* Plugin statedata fn */ cxobj *x = NULL; + plugin_context_t *pc = NULL; - clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp)); if ((fn = clixon_plugin_api_get(cp)->ca_statedata) != NULL){ if ((x = xml_new(DATASTORE_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL) goto done; + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, nsc, xpath, x) < 0){ if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: State callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, clixon_plugin_name_get(cp)); goto fail; /* Dont quit here on user callbacks */ } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } if (xp && x) *xp = x; retval = 1; done: + if (pc) + free(pc); return retval; fail: retval = 0; @@ -406,14 +437,20 @@ clixon_plugin_lockdb_one(clixon_plugin_t *cp, { int retval = -1; plglockdb_t *fn; /* Plugin statedata fn */ + plugin_context_t *pc = NULL; - clicon_debug(1, "%s %s", __FUNCTION__, clixon_plugin_name_get(cp)); if ((fn = clixon_plugin_api_get(cp)->ca_lockdb) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, db, lock, id) < 0) goto done; + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -576,17 +613,24 @@ plugin_transaction_begin_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_trans_begin) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -605,6 +649,7 @@ plugin_transaction_begin_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (plugin_transaction_begin_one(cp, h, td) < 0) goto done; @@ -628,17 +673,24 @@ plugin_transaction_validate_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; - + plugin_context_t *pc = NULL; + if ((fn = clixon_plugin_api_get(cp)->ca_trans_validate) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -679,17 +731,24 @@ plugin_transaction_complete_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_trans_complete) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -762,17 +821,24 @@ plugin_transaction_commit_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -821,17 +887,24 @@ plugin_transaction_commit_done_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_trans_commit_done) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -872,17 +945,24 @@ plugin_transaction_end_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_trans_end) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -899,6 +979,7 @@ plugin_transaction_end_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (plugin_transaction_end_one(cp, h, td) < 0) goto done; @@ -915,17 +996,24 @@ plugin_transaction_abort_one(clixon_plugin_t *cp, { int retval = -1; trans_cb_t *fn; + plugin_context_t *pc = NULL; if ((fn = clixon_plugin_api_get(cp)->ca_trans_abort) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, (transaction_data)td) < 0){ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error", __FUNCTION__, clixon_plugin_name_get(cp)); goto done; } + if (plugin_context_check(pc, clixon_plugin_name_get(cp), __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -942,6 +1030,7 @@ plugin_transaction_abort_all(clicon_handle h, int retval = -1; clixon_plugin_t *cp = NULL; + clicon_debug(1, "%s", __FUNCTION__); while ((cp = clixon_plugin_each(h, cp)) != NULL) { if (plugin_transaction_abort_one(cp, h, td) < 0) ; /* dont abort on error */ diff --git a/apps/backend/backend_plugin_restconf.c b/apps/backend/backend_plugin_restconf.c index c7db8249..3d04d2fa 100644 --- a/apps/backend/backend_plugin_restconf.c +++ b/apps/backend/backend_plugin_restconf.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index dd9714de..f4eb77ac 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include diff --git a/apps/backend/clixon_backend_transaction.c b/apps/backend/clixon_backend_transaction.c index e7af1c7c..0a7889ce 100644 --- a/apps/backend/clixon_backend_transaction.c +++ b/apps/backend/clixon_backend_transaction.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include diff --git a/apps/cli/cli_auto.c b/apps/cli/cli_auto.c index 79fea435..5e4e4f6d 100644 --- a/apps/cli/cli_auto.c +++ b/apps/cli/cli_auto.c @@ -171,7 +171,7 @@ cli_xml2file(cxobj *xn, if (xn == NULL) goto ok; /* Look for autocli-op defined in clixon-lib.yang */ - if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, &opext) < 0) { + if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) { goto ok; } if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ @@ -273,13 +273,13 @@ cli_xml2txt(cxobj *xn, clicon_err(OE_XML, EINVAL, "xn or fn is NULL"); goto done; } - /* Look for autocli-op defined in clixon-lib.yang */ - if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, &opext) < 0) { - goto ok; - } - if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ - goto ok; - } + /* Look for autocli-op defined in clixon-lib.yang */ + if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) { + goto ok; + } + if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ + goto ok; + } xc = NULL; /* count children (elements and bodies, not attributes) */ while ((xc = xml_child_each(xn, xc, -1)) != NULL) if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY) @@ -342,7 +342,7 @@ cli_xml2cli(cxobj *xn, if ((ys = xml_spec(xn)) == NULL) goto ok; /* Look for autocli-op defined in clixon-lib.yang */ - if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, &opext) < 0) { + if (yang_extension_value(xml_spec(xn), "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) { goto ok; } if ((opext != NULL) && ((strcmp(opext, "hide-database") == 0) || (strcmp(opext, "hide-database-auto-completion") == 0))){ diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index d65a150a..e32509f2 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -165,17 +165,17 @@ cli_signal_flush(clicon_handle h) sigfn_t h1, h2, h3, h4; - set_signal (SIGTSTP, SIG_IGN, &h1); - set_signal (SIGQUIT, SIG_IGN, &h2); - set_signal (SIGCHLD, SIG_IGN, &h3); - set_signal (SIGINT, SIG_IGN, &h4); + set_signal(SIGTSTP, SIG_IGN, &h1); + set_signal(SIGQUIT, SIG_IGN, &h2); + set_signal(SIGCHLD, SIG_IGN, &h3); + set_signal(SIGINT, SIG_IGN, &h4); cli_signal_unblock (h); - set_signal (SIGTSTP, h1, NULL); - set_signal (SIGQUIT, h2, NULL); - set_signal (SIGCHLD, h3, NULL); - set_signal (SIGINT, h4, NULL); + set_signal(SIGTSTP, h1, NULL); + set_signal(SIGQUIT, h2, NULL); + set_signal(SIGCHLD, h3, NULL); + set_signal(SIGINT, h4, NULL); cli_signal_block (h); } @@ -562,9 +562,10 @@ cli_start_shell(clicon_handle h, int retval = -1; char bcmd[128]; cg_var *cv1 = cvec_i(vars, 1); - + sigset_t oldsigset; + struct sigaction oldsigaction[32] = {0,}; + cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL); - if ((pw = getpwuid(getuid())) == NULL){ clicon_err(OE_UNIX, errno, "getpwuid"); goto done; @@ -575,6 +576,9 @@ cli_start_shell(clicon_handle h, goto done; } endpwent(); + + if (clixon_signal_save(&oldsigset, oldsigaction) < 0) + goto done; cli_signal_flush(h); cli_signal_unblock(h); if (cmd){ @@ -598,6 +602,8 @@ cli_start_shell(clicon_handle h, goto done; } #endif + if (clixon_signal_restore(&oldsigset, oldsigaction) < 0) + goto done; retval = 0; done: return retval; diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index 10de13e2..2f042ead 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -78,6 +78,7 @@ You can see which CLISPEC it generates via clixon_cli -D 2: #include #include #include +#include #include /* cligen */ @@ -120,7 +121,7 @@ cli_expand_var_generate(clicon_handle h, int retval = -1; char *api_path_fmt = NULL, *opext = NULL; - if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0) + if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) goto done; if (opext && strcmp(opext, "hide-database") == 0) { retval = 1; @@ -761,7 +762,7 @@ yang2cli_leaf(clicon_handle h, } cprintf(cb, "%*s", level*3, ""); /* Look for autocli-op defined in clixon-lib.yang */ - if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0) + if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) goto done; if (gt == GT_VARS || gt == GT_ALL || gt == GT_HIDE || gt == GT_OC_COMPRESS){ cprintf(cb, "%s", yang_argument_get(ys)); @@ -866,14 +867,14 @@ yang2cli_container(clicon_handle h, if (cli_callback_generate(h, ys, cb) < 0) goto done; - /* Look for autocli-op defined in clixon-lib.yang */ - if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0) - goto done; - if (opext != NULL && strcmp(opext, "hide") == 0){ - cprintf(cb, ",hide"); - } + /* Look for autocli-op defined in clixon-lib.yang */ + if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) + goto done; + if (opext != NULL && strcmp(opext, "hide") == 0){ + cprintf(cb, ",hide"); + } if (opext != NULL && strcmp(opext, "hide-database-auto-completion") == 0){ - cprintf(cb, ",hide-database-auto-completion"); + cprintf(cb, ",hide-database-auto-completion"); } cprintf(cb, ";{\n"); } @@ -882,10 +883,10 @@ yang2cli_container(clicon_handle h, while ((yc = yn_each(ys, yc)) != NULL) if (yang2cli_stmt(h, yc, gt, level+1, state, show_tree, cb) < 0) goto done; - if (hide == 0 && hide_oc == 0) - cprintf(cb, "%*s}\n", level*3, ""); - retval = 0; - done: + if (hide == 0 && hide_oc == 0) + cprintf(cb, "%*s}\n", level*3, ""); + retval = 0; + done: if (helptext) free(helptext); return retval; @@ -933,7 +934,7 @@ yang2cli_list(clicon_handle h, yang2cli_helptext(cb, helptext); } /* Look for autocli-op defined in clixon-lib.yang */ - if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, &opext) < 0) + if (yang_extension_value(ys, "autocli-op", CLIXON_LIB_NS, NULL, &opext) < 0) goto done; if (opext != NULL && strcmp(opext, "hide") == 0){ cprintf(cb, ",hide"); diff --git a/apps/cli/cli_handle.c b/apps/cli/cli_handle.c index e4fddeb1..5e992a08 100644 --- a/apps/cli/cli_handle.c +++ b/apps/cli/cli_handle.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 89f9294f..77b60cbe 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -69,7 +70,6 @@ #include "cli_handle.h" #include "cli_generate.h" - /* * Constants */ @@ -522,6 +522,53 @@ cli_handler_err(FILE *f) return 0; } +/*! Variant of eval for context checking + * @see cligen_eval + */ +int +cligen_clixon_eval(cligen_handle h, + cg_obj *co, + cvec *cvv) +{ + struct cg_callback *cc; + int retval = 0; + cvec *argv; + plugin_context_t *pc = NULL; + + if (h) + cligen_co_match_set(h, co); + for (cc = co->co_callbacks; cc; cc=cc->cc_next){ + /* Vector cvec argument to callback */ + if (cc->cc_fn_vec){ + argv = cc->cc_cvec ? cvec_dup(cc->cc_cvec) : NULL; + cligen_fn_str_set(h, cc->cc_fn_str); + if ((pc = plugin_context_get()) == NULL) + break; + if ((retval = (*cc->cc_fn_vec)( + cligen_userhandle(h)?cligen_userhandle(h):h, + cvv, + argv)) < 0){ + if (argv != NULL) + cvec_free(argv); + cligen_fn_str_set(h, NULL); + break; + } + if (plugin_context_check(pc, "CLIgen", cc->cc_fn_str) < 0) + break; + if (pc){ + free(pc); + pc = NULL; + } + if (argv != NULL) + cvec_free(argv); + cligen_fn_str_set(h, NULL); + } + } + if (pc) + free(pc); + return retval; +} + /*! Evaluate a matched command * @param[in] h Clicon handle * @param[in] cmd The command string @@ -541,7 +588,8 @@ clicon_eval(clicon_handle h, cli_output_reset(); if (!cligen_exiting(cli_cligen(h))) { clicon_err_reset(); - if ((retval = cligen_eval(cli_cligen(h), match_obj, cvv)) < 0) { + + if ((retval = cligen_clixon_eval(cli_cligen(h), match_obj, cvv)) < 0) { #if 0 /* This is removed since we get two error messages on failure. But maybe only sometime? Both a real log when clicon_err is called, and the here again. 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/apps/restconf/restconf_api_native.c b/apps/restconf/restconf_api_native.c index 956f33bd..981a4e98 100644 --- a/apps/restconf/restconf_api_native.c +++ b/apps/restconf/restconf_api_native.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include diff --git a/apps/restconf/restconf_handle.c b/apps/restconf/restconf_handle.c index ef413d08..2573e237 100644 --- a/apps/restconf/restconf_handle.c +++ b/apps/restconf/restconf_handle.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include 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/example/main/example_netconf.c b/example/main/example_netconf.c index 2a35c939..00a21913 100644 --- a/example/main/example_netconf.c +++ b/example/main/example_netconf.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include diff --git a/lib/clixon/clixon_dispatcher.h b/lib/clixon/clixon_dispatcher.h index 88bde49d..c27e1881 100644 --- a/lib/clixon/clixon_dispatcher.h +++ b/lib/clixon/clixon_dispatcher.h @@ -1,12 +1,43 @@ /* - * Copyright 2021 Rubicon Communications LLC (Netgate) + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2021 Rubicon Communications, LLC(Netgate) + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + * @see https://github.com/dcornejo/dispatcher */ -#ifndef DISPATCH_DISPATCHER_H -#define DISPATCH_DISPATCHER_H +#ifndef _CLIXON_DISPATCH_DISPATCHER_H +#define _CLIXON_DISPATCH_DISPATCHER_H -/*! prototype for a function to handle a path +/*! Prototype for a function to handle a path * minimally needs the path it's working on, but probably * we want to hand down cached data somehow * @param[in] h Generic handler @@ -74,5 +105,6 @@ struct _dispatcher_entry { int dispatcher_register_handler(dispatcher_entry_t **root, dispatcher_definition *x); int dispatcher_call_handlers(dispatcher_entry_t *root, void *handle, char *path, void *user_args); int dispatcher_free(dispatcher_entry_t *root); +int dispatcher_print(FILE *f, int level, dispatcher_entry_t *root); -#endif /* DISPATCH_DISPATCHER_H */ +#endif /* _CLIXON_DISPATCH_DISPATCHER_H */ diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index 62168e2d..c7294881 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -50,7 +50,6 @@ /* * Types */ - /*! Registered RPC callback function * @param[in] h Clicon handle @@ -201,8 +200,8 @@ typedef int (plgreset_t)(clicon_handle h, const char *db); * A complete valid XML tree is created by the plugin and sent back via xtop, which is merged * into a complete state tree by the system. * The plugin should ensure that xpath is matched (using namspace context nsc) - * This callback may be replaced with a "dispatcher" type API in the future where the - * XPath binding is stricter, similar to the pagination API. + * XXX: This callback may be replaced with a "dispatcher" type API in the future where the + * XPath binding is stricter, similar to the pagination API. * * @param[in] h Clicon handle * @param[in] xpath Part of state requested @@ -338,6 +337,7 @@ struct clixon_plugin_api{ #define ca_trans_abort u.cau_backend.cb_trans_abort #define ca_datastore_upgrade u.cau_backend.cb_datastore_upgrade + /* * Macros */ @@ -349,6 +349,10 @@ typedef struct clixon_plugin_api clixon_plugin_api; * The internal struct is defined in clixon_plugin.c */ typedef struct clixon_plugin clixon_plugin_t; +/*! Structure for checking status before and after a plugin call + * The internal struct is defined in clixon_plugin.c */ +typedef struct plugin_context plugin_context_t; + /* * Prototypes */ @@ -361,7 +365,6 @@ typedef struct clixon_plugin clixon_plugin_t; */ clixon_plugin_api *clixon_plugin_init(clicon_handle h); - clixon_plugin_api *clixon_plugin_api_get(clixon_plugin_t *cp); char *clixon_plugin_name_get(clixon_plugin_t *cp); plghndl_t clixon_plugin_handle_get(clixon_plugin_t *cp); @@ -376,6 +379,9 @@ int clixon_plugins_load(clicon_handle h, const char *function, const char *dir, int clixon_pseudo_plugin(clicon_handle h, const char *name, clixon_plugin_t **cpp); +plugin_context_t * plugin_context_get(void); +int plugin_context_check(plugin_context_t *pc, const char *name, const char *fn); + int clixon_plugin_start_one(clixon_plugin_t *cp, clicon_handle h); int clixon_plugin_start_all(clicon_handle h); diff --git a/lib/clixon/clixon_sig.h b/lib/clixon/clixon_sig.h index 317c7079..07249374 100644 --- a/lib/clixon/clixon_sig.h +++ b/lib/clixon/clixon_sig.h @@ -47,6 +47,8 @@ typedef void (*sigfn_t)(int); * Prototypes */ int set_signal(int signo, void (*handler)(int), void (**oldhandler)(int)); +int clixon_signal_save(sigset_t *sigset, struct sigaction sigaction_vec[32]); +int clixon_signal_restore(sigset_t *sigset, struct sigaction sigaction_vec[32]); void clicon_signal_block(int); void clicon_signal_unblock(int); diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index 879e6b8c..96084036 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -270,6 +270,6 @@ int yang_type_cache_get(yang_stmt *ytype, yang_stmt **resolved, int *opti int yang_type_cache_set(yang_stmt *ys, yang_stmt *resolved, int options, cvec *cvv, cvec *patterns, uint8_t fraction); yang_stmt *yang_anydata_add(yang_stmt *yp, char *name); -int yang_extension_value(yang_stmt *ys, char *name, char *ns, char **value); +int yang_extension_value(yang_stmt *ys, char *name, char *ns, int *exist, char **value); #endif /* _CLIXON_YANG_H_ */ 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 b38e78f9..b3793fb5 100644 --- a/lib/src/clixon_dispatcher.c +++ b/lib/src/clixon_dispatcher.c @@ -1,5 +1,36 @@ /* - * Copyright 2021 Rubicon Communications LLC (Netgate) + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2021 Rubicon Communications, LLC(Netgate) + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + * @see https://github.com/dcornejo/dispatcher */ @@ -37,10 +68,6 @@ * [b=] * [b] * - * NOTE 1: there is not a mechanism to free the created structures since - * it is intended that this tree is created only at startup. if use case - * changes, this function is trivial. - * * NOTE 2: there is no attempt to optimize list searching here, sorry. I * do not think that the known use cases will get big enough to make the * tree get too large. I do not recommend that you encode every possible @@ -285,11 +312,8 @@ get_entry(dispatcher_entry_t *root, /* some elements may have keys defined, strip them off */ for (int i = 0; i < split_path_len; i++) { - char *kptr = strchr(split_path_list[i], '='); - - if ((kptr != NULL) && (*kptr == '=')) { - *(kptr + 1) = 0; - } + char *kptr = split_path_list[i]; + strsep(&kptr, "=[]"); } /* search down the tree */ @@ -411,10 +435,10 @@ dispatcher_register_handler(dispatcher_entry_t **root, * * @param[in] handle * @param[in] root - * @param[in] path - * @retval 1 OK - * @retval 0 Invalid - * @retval -1 Error + * @param[in] path Note must be on the form: /a/b (no keys) + * @retval 1 OK + * @retval 0 Invalid + * @retval -1 Error */ int dispatcher_call_handlers(dispatcher_entry_t *root, @@ -423,8 +447,12 @@ dispatcher_call_handlers(dispatcher_entry_t *root, void *user_args) { int ret = 0; - dispatcher_entry_t *best = get_entry(root, path); + dispatcher_entry_t *best; + if ((best = get_entry(root, path)) == NULL){ + errno = ENOENT; + return -1; + } if (best->children != NULL) { call_handler_helper(best->children, handle, path, user_args); } @@ -450,3 +478,24 @@ dispatcher_free(dispatcher_entry_t *root) free(root); return 0; } + +/*! Pretty-print dispatcher tree + */ +#define INDENT 3 +int +dispatcher_print(FILE *f, + int level, + dispatcher_entry_t *de) +{ + fprintf(f, "%*s%s", level*INDENT, "", de->node_name); + if (de->handler) + fprintf(f, " %p", de->handler); + if (de->arg) + fprintf(f, " (%p)", de->arg); + fprintf(f, "\n"); + if (de->children) + dispatcher_print(f, level+1, de->children); + if (de->peer) + dispatcher_print(f, level, de->peer); + return 0; +} diff --git a/lib/src/clixon_event.c b/lib/src/clixon_event.c index 736c7541..9dad4dfe 100644 --- a/lib/src/clixon_event.c +++ b/lib/src/clixon_event.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 20ea2a99..2bebbcf3 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -43,10 +43,12 @@ #include #include #include +#include #include #include #include - +#include +#include #include #include @@ -69,6 +71,15 @@ /* * Private types */ +/*! Structure for checking status before and after a plugin call + * Currently signal settings: blocked and handlers, and termios + * @see plugin_context_check + */ +struct plugin_context { + sigset_t pc_sigset; /* See sigprocmask(2) */ + struct sigaction pc_sigaction_vec[32]; /* See sigaction(2) */ + struct termios pc_termios; /* See termios(3) */ +}; /* Internal plugin structure with dlopen() handle and plugin_api * This is an internal type, not exposed in the API @@ -327,9 +338,10 @@ plugin_load_one(clicon_handle h, void *handle = NULL; plginit2_t *initfn; clixon_plugin_api *api = NULL; - clixon_plugin_t *cp = NULL; + clixon_plugin_t *cp = NULL; char *name; char *p; + plugin_context_t *pc = NULL; clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function); dlerror(); /* Clear any existing error */ @@ -348,6 +360,9 @@ plugin_load_one(clicon_handle h, goto done; } clicon_err_reset(); + if ((pc = plugin_context_get()) < 0) + goto done; + if ((api = initfn(h)) == NULL) { if (!clicon_errno){ /* if clicon_err() is not called then log and continue */ clicon_log(LOG_DEBUG, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file); @@ -359,6 +374,9 @@ plugin_load_one(clicon_handle h, goto done; } } + if (plugin_context_check(pc, file, __FUNCTION__) < 0) + goto done; + /* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */ if ((cp = (clixon_plugin_t *)malloc(sizeof(struct clixon_plugin))) == NULL){ clicon_err(OE_UNIX, errno, "malloc"); @@ -382,6 +400,8 @@ plugin_load_one(clicon_handle h, retval = 1; done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (pc) + free(pc); if (retval != 1 && handle) dlclose(handle); if (cp) @@ -483,6 +503,157 @@ done: return retval; } +/*! Get system context, eg signal procmask (for blocking) and sigactions + * Call this before a plugin + * @retval pc Plugin context structure, use free() to deallocate + * @retval NULL Error + * @see plugin_context_check + * */ +plugin_context_t * +plugin_context_get(void) +{ + int i; + struct plugin_context *pc = NULL; + + if ((pc = malloc(sizeof(*pc))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(pc, 0, sizeof(*pc)); + + if (sigprocmask(0, NULL, &pc->pc_sigset) < 0){ + clicon_err(OE_UNIX, errno, "sigprocmask"); + goto done; + } + for (i=1; i<32; i++){ + if (sigaction(i, NULL, &pc->pc_sigaction_vec[i]) < 0){ + clicon_err(OE_UNIX, errno, "sigaction"); + goto done; + } + /* Mask SA_RESTORER: Not intended for application use. + * Note that it may not be included in user space so may be hardcoded below + */ +#ifdef SA_RESTORER + pc->pc_sigaction_vec[i].sa_flags &= ~SA_RESTORER; +#else + pc->pc_sigaction_vec[i].sa_flags &= ~0x04000000; +#endif + } + if (isatty(0) && tcgetattr(0, &pc->pc_termios) < 0){ + clicon_err(OE_UNIX, errno, "tcgetattr %d", errno); + goto done; + } + return pc; + done: + if (pc) + free(pc); + return NULL; +} + +/*! Given an existing, old plugin context, check if anything has changed + * + * Make a new check and compare with the old (procided as in-parameter). + * Log if there is a difference at loglevel WARNING. + * You can modify the code to also fail with assert if you want early fail. + * + * @param[in,out] oldpc Old plugin context + * @param[in] name Name of plugin for logging. Can be other name, context dependent + * @param[in] fn Typically name of callback, or caller function + * @retval -1 Error + * @retval 0 Fail, log on syslog using LOG_WARNING + * @retval 1 OK + * @note name and fn are context dependent, since the env of callback calls are very different + * @see plugin_context_get + */ +int +plugin_context_check(plugin_context_t *oldpc0, + const char *name, + const char *fn) +{ + int retval = -1; + int failed = 0; + int i; + struct plugin_context *oldpc = oldpc0; + struct plugin_context *newpc = NULL; + + if ((newpc = plugin_context_get()) == NULL) + goto done; + if (oldpc->pc_termios.c_iflag != newpc->pc_termios.c_iflag){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios input modes from 0x%x to 0x%x", __FUNCTION__, + name, fn, + oldpc->pc_termios.c_iflag, + newpc->pc_termios.c_iflag); + failed++; + } + if (oldpc->pc_termios.c_oflag != newpc->pc_termios.c_oflag){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios output modes from 0x%x to 0x%x", __FUNCTION__, + name, fn, + oldpc->pc_termios.c_oflag, + newpc->pc_termios.c_oflag); + failed++; + } + if (oldpc->pc_termios.c_cflag != newpc->pc_termios.c_cflag){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios control modes from 0x%x to 0x%x", __FUNCTION__, + name, fn, + oldpc->pc_termios.c_cflag, + newpc->pc_termios.c_cflag); + failed++; + } + if (oldpc->pc_termios.c_lflag != newpc->pc_termios.c_lflag){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed termios local modes from 0x%x to 0x%x", __FUNCTION__, + name, fn, + oldpc->pc_termios.c_lflag, + newpc->pc_termios.c_lflag); + failed++; + } + /* XXX pc_termios.cc_t c_cc[NCCS] not checked */ +#if 0 + /* In case you want early detection and crash. But otherwise it is recommended that + * the caller looks for retval == 0 */ + assert(failed == 0); +#endif + for (i=1; i<32; i++){ + if (sigismember(&oldpc->pc_sigset, i) != sigismember(&newpc->pc_sigset, i)){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed blocking of signal %s(%d) from %d to %d", __FUNCTION__, + name, fn, strsignal(i), i, + sigismember(&oldpc->pc_sigset, i), + sigismember(&newpc->pc_sigset, i) + ); + failed++; + } + if (oldpc->pc_sigaction_vec[i].sa_flags != newpc->pc_sigaction_vec[i].sa_flags){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed flags of signal %s(%d) from 0x%x to 0x%x", __FUNCTION__, + name, fn, strsignal(i), i, + oldpc->pc_sigaction_vec[i].sa_flags, + newpc->pc_sigaction_vec[i].sa_flags);; + failed++; + } + if (oldpc->pc_sigaction_vec[i].sa_sigaction != newpc->pc_sigaction_vec[i].sa_sigaction){ + clicon_log(LOG_WARNING, "%s Plugin context %s %s: Changed action of signal %s(%d) from %p to %p", __FUNCTION__, + name, fn, strsignal(i), i, + oldpc->pc_sigaction_vec[i].sa_sigaction, + newpc->pc_sigaction_vec[i].sa_sigaction); + failed++; + } +#if 0 + /* In case you want early detection and crash. But otherwise it is recommended that + * the caller looks for retval == 0 */ + assert(failed == 0); +#endif + } + if (failed) + goto fail; + + retval = 1; /* OK */ + done: + if (newpc) + free(newpc); + return retval; + fail: + retval = 0; + goto done; +} + /*! Call single plugin start callback * @param[in] cp Plugin handle * @param[in] h Clixon handle @@ -493,19 +664,26 @@ int clixon_plugin_start_one(clixon_plugin_t *cp, clicon_handle h) { - int retval = -1; - plgstart_t *fn; /* Plugin start */ + int retval = -1; + plgstart_t *fn; /* Plugin start */ + plugin_context_t *pc = NULL; if ((fn = cp->cp_api.ca_start) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Start callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, cp->cp_name); goto done; } + if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -540,17 +718,22 @@ static int clixon_plugin_exit_one(clixon_plugin_t *cp, clicon_handle h) { - int retval = -1; - char *error; - plgexit_t *fn; - + int retval = -1; + char *error; + plgexit_t *fn; + plugin_context_t *pc = NULL; + if ((fn = cp->cp_api.ca_exit) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Exit callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, cp->cp_name); goto done; } + if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0) + goto done; if (dlclose(cp->cp_handle) != 0) { error = (char*)dlerror(); clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error"); @@ -558,6 +741,8 @@ clixon_plugin_exit_one(clixon_plugin_t *cp, } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -606,21 +791,28 @@ clixon_plugin_auth_one(clixon_plugin_t *cp, clixon_auth_type_t auth_type, char **authp) { - int retval = -1; - plgauth_t *fn; /* Plugin auth */ + int retval = -1; + plgauth_t *fn; /* Plugin auth */ + plugin_context_t *pc = NULL; clicon_debug(1, "%s", __FUNCTION__); if ((fn = cp->cp_api.ca_auth) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if ((retval = fn(h, req, auth_type, authp)) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Auth callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, cp->cp_name); goto done; } + if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0) + goto done; } else retval = 0; /* Ignored / no callback */ done: + if (pc) + free(pc); clicon_debug(1, "%s retval:%d auth:%s", __FUNCTION__, retval, *authp); return retval; } @@ -684,19 +876,26 @@ clixon_plugin_extension_one(clixon_plugin_t *cp, yang_stmt *yext, yang_stmt *ys) { - int retval = 1; - plgextension_t *fn; /* Plugin extension fn */ + int retval = 1; + plgextension_t *fn; /* Plugin extension fn */ + plugin_context_t *pc = NULL; if ((fn = cp->cp_api.ca_extension) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, yext, ys) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Extension callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, cp->cp_name); goto done; } + if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -750,17 +949,24 @@ clixon_plugin_datastore_upgrade_one(clixon_plugin_t *cp, { int retval = -1; datastore_upgrade_t *fn; + plugin_context_t *pc = NULL; if ((fn = cp->cp_api.ca_datastore_upgrade) != NULL){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (fn(h, db, xt, msd) < 0) { if (clicon_errno < 0) clicon_log(LOG_WARNING, "%s: Internal error: Datastore upgrade callback in plugin: %s returned -1 but did not make a clicon_err call", __FUNCTION__, cp->cp_name); goto done; } + if (plugin_context_check(pc, cp->cp_name, __FUNCTION__) < 0) + goto done; } retval = 0; done: + if (pc) + free(pc); return retval; } @@ -906,12 +1112,13 @@ rpc_callback_call(clicon_handle h, void *arg) { int retval = -1; - rpc_callback_t *rc; - char *name; - char *prefix; - char *ns; - int nr = 0; /* How many callbacks */ + rpc_callback_t *rc; + char *name; + char *prefix; + char *ns; + int nr = 0; /* How many callbacks */ plugin_module_struct *ms = plugin_module_struct_get(h); + plugin_context_t *pc = NULL; if (ms == NULL){ clicon_err(OE_PLUGIN, EINVAL, "plugin module not initialized"); @@ -925,17 +1132,27 @@ rpc_callback_call(clicon_handle h, if (strcmp(rc->rc_name, name) == 0 && ns && rc->rc_namespace && strcmp(rc->rc_namespace, ns) == 0){ + if ((pc = plugin_context_get()) == NULL) + goto done; if (rc->rc_callback(h, xe, cbret, arg, rc->rc_arg) < 0){ clicon_debug(1, "%s Error in: %s", __FUNCTION__, rc->rc_name); goto done; } nr++; + if (plugin_context_check(pc, rc->rc_name, __FUNCTION__) < 0) + goto done; + if (pc){ + free(pc); + pc = NULL; + } } rc = NEXTQ(rpc_callback_t *, rc); } while (rc != ms->ms_rpc_callbacks); retval = nr; /* 0: none found, >0 nr of handlers called */ done: clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (pc) + free(pc); return retval; } diff --git a/lib/src/clixon_sig.c b/lib/src/clixon_sig.c index 16cb3176..611a27a8 100644 --- a/lib/src/clixon_sig.c +++ b/lib/src/clixon_sig.c @@ -121,6 +121,58 @@ clicon_signal_unblock(int sig) sigprocmask(SIG_UNBLOCK, &set, NULL); } +/*! Save complete signal context + */ +int +clixon_signal_save(sigset_t *sigset, + struct sigaction sigaction_vec[32]) +{ + int retval = -1; + int i; + + if (sigprocmask(0, NULL, sigset) < 0){ + clicon_err(OE_UNIX, errno, "sigprocmask"); + goto done; + } + for (i=1; i<32; i++){ + if (sigaction(i, NULL, &sigaction_vec[i]) < 0){ + clicon_err(OE_UNIX, errno, "sigaction"); + goto done; + } + } + retval = 0; + done: + return retval; +} + +/*! Restore complete signal context + * + * Note: sigaction may not restore SIGKILL or SIGSTOP, which cannot be caught or ignored. + */ +int +clixon_signal_restore(sigset_t *sigset, + struct sigaction sigaction_vec[32]) +{ + int retval = -1; + int i; + + if (sigprocmask(0, sigset, NULL) < 0){ + clicon_err(OE_UNIX, errno, "sigprocmask"); + goto done; + } + for (i=1; i<32; i++){ + if (i == SIGKILL || i == SIGSTOP) + continue; + if (sigaction(i, &sigaction_vec[i], NULL) < 0){ + clicon_err(OE_UNIX, errno, "sigaction"); + goto done; + } + } + retval = 0; + done: + return retval; +} + /*! Read pidfile and return pid using file descriptor * * @param[in] pidfile Name of pidfile diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 72dfdc1b..b4b364fa 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -3553,15 +3553,20 @@ yang_anydata_add(yang_stmt *yp, return ys; } -/*! Find extension argument and return extension argument value +/*! Find extension argument and return if extension exists and its argument value + * * @param[in] ys Yang statement * @param[in] name Name of the extension * @param[in] ns The namespace + * @param[out] exist The extension exists. * @param[out] value clispec operator (hide/none) - direct pointer into yang, dont free + * @retval 0 OK: Look in exist and value for return value + * @retval -1 Error * This is for extensions with an argument * @code * char *value = NULL; - * if (yang_extension_value(ys, "mymode", "urn:example:lib", &value) < 0) + * int exist = 0; + * if (yang_extension_value(ys, "mymode", "urn:example:lib", &exist, &value) < 0) * err; * if (value != NULL){ * // use extension value @@ -3572,6 +3577,7 @@ int yang_extension_value(yang_stmt *ys, char *name, char *ns, + int *exist, char **value) { int retval = -1; @@ -3585,7 +3591,7 @@ yang_extension_value(yang_stmt *ys, clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - yext = NULL; /* This loop gets complicated in trhe case the extension is augmented */ + yext = NULL; /* This loop gets complicated in the case the extension is augmented */ while ((yext = yn_each(ys, yext)) != NULL) { if (yang_keyword_get(yext) != Y_UNKNOWN) continue; @@ -3593,15 +3599,17 @@ yang_extension_value(yang_stmt *ys, continue; if (yang_find_prefix_by_namespace(ymod, ns, &prefix) < 0) goto ok; + cbuf_reset(cb); cprintf(cb, "%s:%s", prefix, name); if (strcmp(yang_argument_get(yext), cbuf_get(cb)) != 0) continue; break; } if (yext != NULL){ /* Found */ - if ((cv = yang_cv_get(yext)) == NULL) - goto ok; - if (value) + if (exist) + *exist = 1; + if (value && + (cv = yang_cv_get(yext)) != NULL) *value = cv_string_get(cv); } ok: diff --git a/test/test_dispatcher.sh b/test/test_dispatcher.sh new file mode 100755 index 00000000..2735a9ad --- /dev/null +++ b/test/test_dispatcher.sh @@ -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 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 diff --git a/util/Makefile.in b/util/Makefile.in index ed34e1ef..fe6936f8 100644 --- a/util/Makefile.in +++ b/util/Makefile.in @@ -91,7 +91,8 @@ APPSRC += clixon_util_path.c APPSRC += clixon_util_datastore.c APPSRC += clixon_util_regexp.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_client.c ifdef with_restconf @@ -102,7 +103,6 @@ APPSRC += clixon_util_ssl.c # requires http/2 #APPSRC += clixon_util_grpc.c # work in progress endif - APPS = $(APPSRC:.c=) all: $(APPS) @@ -149,6 +149,9 @@ clixon_util_socket: clixon_util_socket.c $(LIBDEPS) clixon_util_validate: clixon_util_validate.c $(LIBDEPS) $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ -l clixon_backend -o $@ $(LIBS) +clixon_util_dispatcher: clixon_util_dispatcher.c $(LIBDEPS) + $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ -l clixon_backend -o $@ $(LIBS) + ifdef with_restconf clixon_util_stream: clixon_util_stream.c $(LIBDEPS) $(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ $(LIBS) -lcurl -o $@ @@ -163,7 +166,7 @@ endif distclean: clean rm -f Makefile *~ .depend -install: +install: $(APPS) install -d -m 0755 $(DESTDIR)$(bindir) install -m 0755 $(INSTALLFLAGS) $(APPS) $(DESTDIR)$(bindir) diff --git a/util/clixon_util_datastore.c b/util/clixon_util_datastore.c index 65e611f2..92d6401d 100644 --- a/util/clixon_util_datastore.c +++ b/util/clixon_util_datastore.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/util/clixon_util_dispatcher.c b/util/clixon_util_dispatcher.c new file mode 100644 index 00000000..2cd00ddb --- /dev/null +++ b/util/clixon_util_dispatcher.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* 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 \t Debug - print dispatch tree\n" + "\t-a \t Argument to callback (default: NULL)\n" + "\t-i \t Function index: 1..3 (default: 1)\n" + "\t-p \t Registered path (default: /)\n" + "\t-r \t Register callback (based on -a/-i/-p setting)\n" + "\t-c \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; +} diff --git a/util/clixon_util_json.c b/util/clixon_util_json.c index 389b27ca..43a4ff5a 100644 --- a/util/clixon_util_json.c +++ b/util/clixon_util_json.c @@ -50,6 +50,7 @@ #include #include #include +#include /* cligen */ #include diff --git a/util/clixon_util_path.c b/util/clixon_util_path.c index f75a36da..7f7a034e 100644 --- a/util/clixon_util_path.c +++ b/util/clixon_util_path.c @@ -49,6 +49,7 @@ #include #include #include +#include #include /* cligen */ diff --git a/util/clixon_util_regexp.c b/util/clixon_util_regexp.c index edf5255c..0d02c55b 100644 --- a/util/clixon_util_regexp.c +++ b/util/clixon_util_regexp.c @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef HAVE_LIBXML2 /* Actually it should check for a header file */ #include diff --git a/util/clixon_util_socket.c b/util/clixon_util_socket.c index 28ce05b0..409e30f1 100644 --- a/util/clixon_util_socket.c +++ b/util/clixon_util_socket.c @@ -52,6 +52,7 @@ #include #include #include +#include #include #include diff --git a/util/clixon_util_ssl.c b/util/clixon_util_ssl.c index 68a17aa1..7e4b17ef 100644 --- a/util/clixon_util_ssl.c +++ b/util/clixon_util_ssl.c @@ -49,6 +49,7 @@ #include #include #include +#include #include /* gethostbyname */ #include /* inet_pton */ #include /* TCP_NODELAY */ diff --git a/util/clixon_util_stream.c b/util/clixon_util_stream.c index ebf3df83..41fd7d07 100644 --- a/util/clixon_util_stream.c +++ b/util/clixon_util_stream.c @@ -50,6 +50,7 @@ #include #include #include +#include #include /* cligen */ diff --git a/util/clixon_util_validate.c b/util/clixon_util_validate.c index a64465fe..415c2dda 100644 --- a/util/clixon_util_validate.c +++ b/util/clixon_util_validate.c @@ -53,6 +53,7 @@ #include #include #include +#include #include #include diff --git a/util/clixon_util_xml.c b/util/clixon_util_xml.c index 87a35d9c..b59ae889 100644 --- a/util/clixon_util_xml.c +++ b/util/clixon_util_xml.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include diff --git a/util/clixon_util_xml_mod.c b/util/clixon_util_xml_mod.c index 7347842c..49db916e 100644 --- a/util/clixon_util_xml_mod.c +++ b/util/clixon_util_xml_mod.c @@ -58,6 +58,7 @@ #include #include #include +#include /* cligen */ #include diff --git a/util/clixon_util_xpath.c b/util/clixon_util_xpath.c index ec611f0a..9726ae1c 100644 --- a/util/clixon_util_xpath.c +++ b/util/clixon_util_xpath.c @@ -50,6 +50,7 @@ See https://www.w3.org/TR/xpath/ #include #include #include +#include #include /* cligen */ @@ -126,7 +127,7 @@ main(int argc, int len; char *buf = NULL; int ret; - FILE *fp = stdin; /* unless overriden by argv[1] */ + FILE *fp = stdin; /* unless overriden by -f */ char *yang_file_dir = NULL; yang_stmt *yspec = NULL; char *xpath = NULL; @@ -169,7 +170,7 @@ main(int argc, case 'f': /* XML file */ filename = optarg; if ((fp = fopen(filename, "r")) == NULL){ - clicon_err(OE_UNIX, errno, "open(%s)", argv[1]); + clicon_err(OE_UNIX, errno, "fopen(%s)", optarg); goto done; } break; diff --git a/util/clixon_util_yang.c b/util/clixon_util_yang.c index 7f7ea051..f85a811c 100644 --- a/util/clixon_util_yang.c +++ b/util/clixon_util_yang.c @@ -51,11 +51,11 @@ #include #include #include +#include #include #include #include - /* cligen */ #include