Added cli multiple callback and expand support.
This commit is contained in:
parent
41680474c7
commit
2db346abc8
11 changed files with 301 additions and 87 deletions
|
|
@ -29,6 +29,12 @@
|
||||||
#
|
#
|
||||||
# ***** END LICENSE BLOCK *****
|
# ***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
- Added cli multiple callback and expand support. Use options
|
||||||
|
CLICON_CLIGEN_CALLBACK_SINGLE_ARG and CLICON_CLIGEN_EXPAND_SINGLE_ARG
|
||||||
|
to control these.
|
||||||
|
The multiple support for expand callbacks is enabled but not for callbacks
|
||||||
|
since this causes problems for legacy applications.
|
||||||
|
|
||||||
- Added --with-cligen and --with-qdbm configure options
|
- Added --with-cligen and --with-qdbm configure options
|
||||||
- Added union type check for non-cli (eg xml) input
|
- Added union type check for non-cli (eg xml) input
|
||||||
- Empty yang type. Relaxed yang types for unions, eg two strings with different length.
|
- Empty yang type. Relaxed yang types for unions, eg two strings with different length.
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ config_terminate(clicon_handle h)
|
||||||
if (sockpath)
|
if (sockpath)
|
||||||
unlink(sockpath);
|
unlink(sockpath);
|
||||||
backend_handle_exit(h); /* Cannot use h after this */
|
backend_handle_exit(h); /* Cannot use h after this */
|
||||||
|
event_exit();
|
||||||
clicon_log_register_callback(NULL, NULL);
|
clicon_log_register_callback(NULL, NULL);
|
||||||
clicon_debug(1, "%s done", __FUNCTION__);
|
clicon_debug(1, "%s done", __FUNCTION__);
|
||||||
if (debug)
|
if (debug)
|
||||||
|
|
@ -104,6 +105,7 @@ static void
|
||||||
config_sig_term(int arg)
|
config_sig_term(int arg)
|
||||||
{
|
{
|
||||||
static int i=0;
|
static int i=0;
|
||||||
|
|
||||||
if (i++ == 0)
|
if (i++ == 0)
|
||||||
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
|
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
|
||||||
__PROGRAM__, __FUNCTION__, getpid(), arg);
|
__PROGRAM__, __FUNCTION__, getpid(), arg);
|
||||||
|
|
|
||||||
|
|
@ -485,7 +485,7 @@ compare_dbs(clicon_handle h, cvec *cvv, cg_var *arg)
|
||||||
/*! Modify xml database from a callback using xml key format strings
|
/*! Modify xml database from a callback using xml key format strings
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cvv Vector of cli string and instantiated variables
|
* @param[in] cvv Vector of cli string and instantiated variables
|
||||||
* @param[in] arg An xml key format string, eg /aaa/%s
|
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
|
||||||
* @param[in] op Operation to perform on database
|
* @param[in] op Operation to perform on database
|
||||||
* Cvv will contain forst the complete cli string, and then a set of optional
|
* Cvv will contain forst the complete cli string, and then a set of optional
|
||||||
* instantiated variables.
|
* instantiated variables.
|
||||||
|
|
@ -493,10 +493,91 @@ compare_dbs(clicon_handle h, cvec *cvv, cg_var *arg)
|
||||||
* cvv[0] = "set interfaces interface eth0 type bgp"
|
* cvv[0] = "set interfaces interface eth0 type bgp"
|
||||||
* cvv[1] = "eth0"
|
* cvv[1] = "eth0"
|
||||||
* cvv[2] = "bgp"
|
* cvv[2] = "bgp"
|
||||||
* arg = "/interfaces/interface/%s/type"
|
* argv[0] = "/interfaces/interface/%s/type"
|
||||||
* op: OP_MERGE
|
* op: OP_MERGE
|
||||||
* @see cli_callback_generate where arg is generated
|
* @see cli_callback_generate where arg is generated
|
||||||
*/
|
*/
|
||||||
|
static int
|
||||||
|
cli_dbxmlv(clicon_handle h,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv,
|
||||||
|
enum operation_type op)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *str = NULL;
|
||||||
|
char *xkfmt; /* xml key format */
|
||||||
|
char *xk = NULL; /* xml key */
|
||||||
|
cg_var *cval;
|
||||||
|
int len;
|
||||||
|
cg_var *arg;
|
||||||
|
|
||||||
|
if (cvec_len(argv) == 1){
|
||||||
|
clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format stringf");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
arg = cvec_i(argv, 0);
|
||||||
|
xkfmt = cv_string_get(arg);
|
||||||
|
if (xmlkeyfmt2key(xkfmt, cvv, &xk) < 0)
|
||||||
|
goto done;
|
||||||
|
len = cvec_len(cvv);
|
||||||
|
if (len > 1){
|
||||||
|
cval = cvec_i(cvv, len-1);
|
||||||
|
if ((str = cv2str_dup(cval)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clicon_rpc_change(h, "candidate", op, xk, str) < 0)
|
||||||
|
goto done;
|
||||||
|
if (clicon_autocommit(h)) {
|
||||||
|
if (clicon_rpc_commit(h, "candidate", "running", 0, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (str)
|
||||||
|
free(str);
|
||||||
|
if (xk)
|
||||||
|
free(xk);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cli_setv(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = 1;
|
||||||
|
|
||||||
|
if (cli_dbxmlv(h, cvv, argv, OP_REPLACE) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cli_mergev(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
if (cli_dbxmlv(h, cvv, argv, OP_MERGE) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cli_delv(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
|
||||||
|
if (cli_dbxmlv(h, cvv, argv, OP_REMOVE) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cli_dbxml(clicon_handle h,
|
cli_dbxml(clicon_handle h,
|
||||||
cvec *cvv,
|
cvec *cvv,
|
||||||
|
|
@ -572,6 +653,7 @@ cli_del(clicon_handle h, cvec *cvv, cg_var *arg)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Load a configuration file to candidate database
|
/*! Load a configuration file to candidate database
|
||||||
* Utility function used by cligen spec file
|
* Utility function used by cligen spec file
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
/* clicon */
|
/* Clicon */
|
||||||
#include <clixon/clixon.h>
|
#include <clixon/clixon.h>
|
||||||
|
|
||||||
#include "clixon_cli_api.h"
|
#include "clixon_cli_api.h"
|
||||||
|
|
@ -65,9 +65,10 @@
|
||||||
|
|
||||||
/* This is the default callback function. But this is typically overwritten */
|
/* This is the default callback function. But this is typically overwritten */
|
||||||
#define GENERATE_CALLBACK "cli_set"
|
#define GENERATE_CALLBACK "cli_set"
|
||||||
|
#define GENERATE_CALLBACKV "cli_setv"
|
||||||
|
|
||||||
/* variable expand function */
|
/* variable expand function */
|
||||||
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
|
#define GENERATE_EXPAND_XMLDB "expandv_dbvar"
|
||||||
|
|
||||||
/*=====================================================================
|
/*=====================================================================
|
||||||
* YANG generate CLI
|
* YANG generate CLI
|
||||||
|
|
@ -127,7 +128,7 @@ cli_expand_var_generate(clicon_handle h,
|
||||||
cv_type2str(cvtype));
|
cv_type2str(cvtype));
|
||||||
if (options & YANG_OPTIONS_FRACTION_DIGITS)
|
if (options & YANG_OPTIONS_FRACTION_DIGITS)
|
||||||
cprintf(cb0, " fraction-digits:%u", fraction_digits);
|
cprintf(cb0, " fraction-digits:%u", fraction_digits);
|
||||||
cprintf(cb0, " %s(\"candidate %s\")>",
|
cprintf(cb0, " %s(\"candidate\",\"%s\")>",
|
||||||
GENERATE_EXPAND_XMLDB,
|
GENERATE_EXPAND_XMLDB,
|
||||||
xkfmt);
|
xkfmt);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -153,7 +154,10 @@ cli_callback_generate(clicon_handle h,
|
||||||
|
|
||||||
if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0)
|
if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1)
|
||||||
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt);
|
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt);
|
||||||
|
else
|
||||||
|
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACKV, xkfmt);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (xkfmt)
|
if (xkfmt)
|
||||||
|
|
@ -658,10 +662,14 @@ yang2cli(clicon_handle h,
|
||||||
"yang2cli", ptnew, globals) < 0)
|
"yang2cli", ptnew, globals) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cvec_free(globals);
|
cvec_free(globals);
|
||||||
/* handle=NULL for global namespace, this means expand callbacks must be in
|
/* Resolve the expand callback functions in the generated syntax.
|
||||||
CLICON namespace, not in a cli frontend plugin. */
|
This "should" only be GENERATE_EXPAND_XMLDB
|
||||||
if (cligen_expand_str2fn(*ptnew, expand_str2fn, NULL) < 0)
|
handle=NULL for global namespace, this means expand callbacks must be in
|
||||||
|
CLICON namespace, not in a cli frontend plugin.
|
||||||
|
*/
|
||||||
|
if (cligen_expandv_str2fn(*ptnew, (expandv_str2fn_t*)clixon_str2fn, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
cbuf_free(cbuf);
|
cbuf_free(cbuf);
|
||||||
|
|
|
||||||
|
|
@ -216,65 +216,27 @@ syntax_unload(clicon_handle h)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Dynamic linking loader string to function mapper
|
||||||
/*! Dynamic string to function mapper
|
|
||||||
*
|
*
|
||||||
* The cli load function uses this function to map from strings to names.
|
* Maps strings from the CLI specification file to real funtions using dlopen
|
||||||
* handle is the dlopen handle, so it only looks in the current plugin being
|
* mapping.
|
||||||
* loaded. It should also look in libraries?
|
* First look for function name in local namespace if handle given (given plugin)
|
||||||
|
* Then check global namespace, i.e.m lib*.so
|
||||||
*
|
*
|
||||||
* Returns a function pointer to the callback. Beware that this pointer
|
|
||||||
* can theoretically be NULL depending on where the callback is loaded
|
|
||||||
* into memory. Caller must check the error string which is non-NULL is
|
|
||||||
* an error occured
|
|
||||||
*
|
|
||||||
* Compare with expand_str2fn - essentially identical.
|
|
||||||
*/
|
|
||||||
cg_fnstype_t *
|
|
||||||
load_str2fn(char *name, void *handle, char **error)
|
|
||||||
{
|
|
||||||
cg_fnstype_t *fn = NULL;
|
|
||||||
|
|
||||||
/* Reset error */
|
|
||||||
*error = NULL;
|
|
||||||
|
|
||||||
/* First check given plugin if any */
|
|
||||||
if (handle) {
|
|
||||||
dlerror(); /* Clear any existing error */
|
|
||||||
fn = dlsym(handle, name);
|
|
||||||
if ((*error = (char*)dlerror()) == NULL)
|
|
||||||
return fn; /* If no error we found the address of the callback */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now check global namespace which includes any shared object loaded
|
|
||||||
* into the global namespace. I.e. all lib*.so as well as the
|
|
||||||
* master plugin if it exists
|
|
||||||
*/
|
|
||||||
dlerror(); /* Clear any existing error */
|
|
||||||
fn = dlsym(NULL, name);
|
|
||||||
if ((*error = (char*)dlerror()) == NULL)
|
|
||||||
return fn; /* If no error we found the address of the callback */
|
|
||||||
|
|
||||||
/* Return value not really relevant here as the error string is set to
|
|
||||||
* signal an error. However, just checking the function pointer for NULL
|
|
||||||
* should work in most cases, although it's not 100% correct.
|
|
||||||
*/
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* expand_str2fn
|
|
||||||
* maps strings from the CLI specification file to real funtions using dlopen
|
|
||||||
* mapping. One could do something more elaborate with namespaces and plugins:
|
|
||||||
* x::a, x->a, but this is not done yet.
|
|
||||||
* Compare with load_str2fn - essentially identical.
|
|
||||||
* @param[in] name Name of function
|
* @param[in] name Name of function
|
||||||
* @param[in] handle Handle to plugin .so module as returned by dlopen, see cli_plugin_load
|
* @param[in] handle Handle to plugin .so module as returned by dlopen
|
||||||
|
* @param[out] error Static error string, if set indicates error
|
||||||
|
* @retval fn Function pointer
|
||||||
|
* @retval NULL FUnction not found or symbol NULL (check error for proper handling)
|
||||||
|
* @see see cli_plugin_load where (optional) handle opened
|
||||||
|
* @note the returned function is not type-checked which may result in segv at runtime
|
||||||
*/
|
*/
|
||||||
expand_cb *
|
void *
|
||||||
expand_str2fn(char *name, void *handle, char **error)
|
clixon_str2fn(char *name,
|
||||||
|
void *handle,
|
||||||
|
char **error)
|
||||||
{
|
{
|
||||||
expand_cb *fn = NULL;
|
void *fn = NULL;
|
||||||
|
|
||||||
/* Reset error */
|
/* Reset error */
|
||||||
*error = NULL;
|
*error = NULL;
|
||||||
|
|
@ -303,7 +265,6 @@ expand_str2fn(char *name, void *handle, char **error)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Load a dynamic plugin object and call it's init-function
|
* Load a dynamic plugin object and call it's init-function
|
||||||
* Note 'file' may be destructively modified
|
* Note 'file' may be destructively modified
|
||||||
|
|
@ -410,16 +371,27 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve callback names to function pointers.
|
/* Resolve callback names to function pointers. */
|
||||||
* XXX: consider using cligen_callback_str2fnv instead */
|
if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1){
|
||||||
if (cligen_callback_str2fn(pt, load_str2fn, handle) < 0){
|
if (cligen_callback_str2fn(pt, (cg_str2fn_t*)clixon_str2fn, handle) < 0){
|
||||||
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
|
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
|
||||||
filename, plgnam, plgnam);
|
filename, plgnam, plgnam);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (cligen_expand_str2fn(pt, expand_str2fn, handle) < 0)
|
}
|
||||||
|
else
|
||||||
|
if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){
|
||||||
|
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
|
||||||
|
filename, plgnam, plgnam);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (clicon_option_int(h, "CLICON_CLIGEN_EXPAND_SINGLE_ARG")==1){
|
||||||
|
if (cligen_expand_str2fn(pt, (expand_str2fn_t*)clixon_str2fn, handle) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
||||||
/* Make sure we have a syntax mode specified */
|
/* Make sure we have a syntax mode specified */
|
||||||
if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */
|
if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ typedef struct {
|
||||||
} cli_syntax_t;
|
} cli_syntax_t;
|
||||||
|
|
||||||
|
|
||||||
expand_cb *expand_str2fn(char *name, void *handle, char **error);
|
void *clixon_str2fn(char *name, void *handle, char **error);
|
||||||
|
|
||||||
int cli_plugin_start(clicon_handle, int argc, char **argv);
|
int cli_plugin_start(clicon_handle, int argc, char **argv);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,13 +85,121 @@ static int xml2csv(FILE *f, cxobj *x, cvec *cvv);
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] name Name of this function (eg "expand_dbvar")
|
* @param[in] name Name of this function (eg "expand_dbvar")
|
||||||
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
* @param[in] cvv The command so far. Eg: cvec [0]:"a 5 b"; [1]: x=5;
|
||||||
* @param[in] arg Argument given at the callback "<db> <xmlkeyfmt>"
|
* @param[in] argv Arguments given at the callback ("<db>" "<xmlkeyfmt>")
|
||||||
* @param[out] len len of return commands & helptxt
|
* @param[out] len len of return commands & helptxt
|
||||||
* @param[out] commands vector of function pointers to callback functions
|
* @param[out] commands vector of function pointers to callback functions
|
||||||
* @param[out] helptxt vector of pointers to helptexts
|
* @param[out] helptxt vector of pointers to helptexts
|
||||||
* @see cli_expand_var_generate This is where arg is generated
|
* @see cli_expand_var_generate This is where arg is generated
|
||||||
|
* XXX: helptexts?
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
expandv_dbvar(void *h,
|
||||||
|
char *name,
|
||||||
|
cvec *cvv,
|
||||||
|
cvec *argv,
|
||||||
|
cvec *commands,
|
||||||
|
cvec *helptexts)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *xkfmt;
|
||||||
|
char *dbstr;
|
||||||
|
cxobj *xt = NULL;
|
||||||
|
char *xkpath = NULL;
|
||||||
|
cxobj **xvec = NULL;
|
||||||
|
size_t xlen = 0;
|
||||||
|
cxobj *x;
|
||||||
|
char *bodystr;
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int k;
|
||||||
|
cg_var *cv;
|
||||||
|
|
||||||
|
if (argv == NULL || cvec_len(argv) != 2){
|
||||||
|
clicon_err(OE_PLUGIN, 0, "%s: requires arguments: <db> <xmlkeyfmt>",
|
||||||
|
__FUNCTION__);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cv = cvec_i(argv, 0)) == NULL){
|
||||||
|
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <db>");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
dbstr = cv_string_get(cv);
|
||||||
|
if (strcmp(dbstr, "running") != 0 &&
|
||||||
|
strcmp(dbstr, "candidate") != 0){
|
||||||
|
clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cv = cvec_i(argv, 1)) == NULL){
|
||||||
|
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <xkfmt>");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
xkfmt = cv_string_get(cv);
|
||||||
|
/* xkfmt = /interface/%s/address/%s
|
||||||
|
--> ^/interface/eth0/address/.*$
|
||||||
|
--> /interface/[name=eth0]/address
|
||||||
|
*/
|
||||||
|
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xmldb_get(h, dbstr, xkpath, 1, &xt, &xvec, &xlen) < 0)
|
||||||
|
goto done;
|
||||||
|
/* One round to detect duplicates
|
||||||
|
* XXX The code below would benefit from some cleanup
|
||||||
|
*/
|
||||||
|
j = 0;
|
||||||
|
for (i = 0; i < xlen; i++) {
|
||||||
|
char *str;
|
||||||
|
x = xvec[i];
|
||||||
|
if (xml_type(x) == CX_BODY)
|
||||||
|
bodystr = xml_value(x);
|
||||||
|
else
|
||||||
|
bodystr = xml_body(x);
|
||||||
|
if (bodystr == NULL){
|
||||||
|
clicon_err(OE_CFG, 0, "No xml body");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* detect duplicates */
|
||||||
|
for (k=0; k<j; k++){
|
||||||
|
if (xml_type(xvec[k]) == CX_BODY)
|
||||||
|
str = xml_value(xvec[k]);
|
||||||
|
else
|
||||||
|
str = xml_body(xvec[k]);
|
||||||
|
if (strcmp(str, bodystr)==0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (k==j) /* not duplicate */
|
||||||
|
xvec[j++] = x;
|
||||||
|
}
|
||||||
|
xlen = j;
|
||||||
|
for (i = 0; i < xlen; i++) {
|
||||||
|
x = xvec[i];
|
||||||
|
if (xml_type(x) == CX_BODY)
|
||||||
|
bodystr = xml_value(x);
|
||||||
|
else
|
||||||
|
bodystr = xml_body(x);
|
||||||
|
if (bodystr == NULL){
|
||||||
|
clicon_err(OE_CFG, 0, "No xml body");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
/* XXX RFC3986 decode */
|
||||||
|
cvec_add_string(commands, NULL, bodystr);
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
unchunk_group(__FUNCTION__);
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
|
if (xt)
|
||||||
|
xml_free(xt);
|
||||||
|
if (xkpath)
|
||||||
|
free(xkpath);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! This is obsolete version of expandv_dbvar
|
||||||
|
* If CLICON_CLIGEN_EXPAND_SINGLE_ARG is set
|
||||||
|
*/
|
||||||
|
int
|
||||||
expand_dbvar(void *h,
|
expand_dbvar(void *h,
|
||||||
char *name,
|
char *name,
|
||||||
cvec *cvv,
|
cvec *cvv,
|
||||||
|
|
@ -197,7 +305,6 @@ expand_dbvar(void *h,
|
||||||
if (xkpath)
|
if (xkpath)
|
||||||
free(xkpath);
|
free(xkpath);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! List files in a directory
|
/*! List files in a directory
|
||||||
|
|
|
||||||
|
|
@ -71,10 +71,15 @@ cligen_handle cli_cligen(clicon_handle h);
|
||||||
int init_candidate_db(clicon_handle h);
|
int init_candidate_db(clicon_handle h);
|
||||||
int exit_candidate_db(clicon_handle h);
|
int exit_candidate_db(clicon_handle h);
|
||||||
#define cli_output cligen_output
|
#define cli_output cligen_output
|
||||||
int cli_set (clicon_handle h, cvec *vars, cg_var *arg);
|
int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_merge (clicon_handle h, cvec *vars, cg_var *arg);
|
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_del(clicon_handle h, cvec *vars, cg_var *argv);
|
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_debug_cli(clicon_handle h, cvec *vars, cg_var *argv);
|
|
||||||
|
int cli_set(clicon_handle h, cvec *vars, cg_var *arg);
|
||||||
|
int cli_merge(clicon_handle h, cvec *vars, cg_var *arg);
|
||||||
|
int cli_del(clicon_handle h, cvec *vars, cg_var *arg);
|
||||||
|
|
||||||
|
int cli_debug_cli(clicon_handle h, cvec *vars, cg_var *arg);
|
||||||
int cli_debug_backend(clicon_handle h, cvec *vars, cg_var *argv);
|
int cli_debug_backend(clicon_handle h, cvec *vars, cg_var *argv);
|
||||||
int cli_record(clicon_handle h, cvec *vars, cg_var *argv);
|
int cli_record(clicon_handle h, cvec *vars, cg_var *argv);
|
||||||
int isrecording(void);
|
int isrecording(void);
|
||||||
|
|
@ -97,8 +102,8 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo
|
||||||
|
|
||||||
/* In cli_show.c */
|
/* In cli_show.c */
|
||||||
int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail);
|
int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail);
|
||||||
int expand_dbvar(void *h, char *name, cvec *cvv, cg_var *arg,
|
int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
||||||
int *nr, char ***commands, char ***helptexts);
|
cvec *commands, cvec *helptexts);
|
||||||
|
|
||||||
int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg);
|
int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg);
|
||||||
int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg);
|
int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg);
|
||||||
|
|
|
||||||
|
|
@ -138,4 +138,15 @@ CLICON_XMLDB_DIR localstatedir/APPNAME
|
||||||
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
|
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
|
||||||
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
|
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
|
||||||
|
|
||||||
|
# Set if you want to use old obsolete cligen expand variable syntax
|
||||||
|
# Migration: Set to 0 and change all user-defined cli completion callbacks
|
||||||
|
# E.g. expand_dbvar("db fmt") ->expandv_dbvar("db","fmt") in all your cli spec files
|
||||||
|
CLICON_CLIGEN_EXPAND_SINGLE_ARG 0
|
||||||
|
|
||||||
|
# Set if you want to use old obsolete cligen callback variable syntax
|
||||||
|
# Migration: Set to 0 and change all user-defined cli callbacks in your cli spec files
|
||||||
|
# E.g cmd, callback("single arg"); -> cmd, callback("two" "args");
|
||||||
|
# But there are still many pre-defined in callbacks, eg in cli_common.c that are not made
|
||||||
|
# for this.
|
||||||
|
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,6 @@ int event_unreg_timeout(int (*fn)(int, void*), void *arg);
|
||||||
|
|
||||||
int event_loop(void);
|
int event_loop(void);
|
||||||
|
|
||||||
|
int event_exit(void);
|
||||||
|
|
||||||
#endif /* _CLIXON_EVENT_H_ */
|
#endif /* _CLIXON_EVENT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -327,3 +327,22 @@ event_loop(void)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
event_exit(void)
|
||||||
|
{
|
||||||
|
struct event_data *e, *e_next;
|
||||||
|
|
||||||
|
e_next = ee;
|
||||||
|
while ((e = e_next) != NULL){
|
||||||
|
e_next = e->e_next;
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
ee = NULL;
|
||||||
|
e_next = ee_timers;
|
||||||
|
while ((e = e_next) != NULL){
|
||||||
|
e_next = e->e_next;
|
||||||
|
free(e);
|
||||||
|
}
|
||||||
|
ee_timers = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue