CLI plugin API restructuring completed. Now all plugin APIs have the generic form
documented in README and FAQ.
This commit is contained in:
parent
afb6aa31db
commit
2e00411621
19 changed files with 317 additions and 551 deletions
|
|
@ -17,7 +17,8 @@
|
|||
* CLI parse hook
|
||||
* CLICON_FIND_PLUGIN
|
||||
* clicon_valcb()
|
||||
* backend system plugins (CLIXON_BACKEND_SYSDIR)
|
||||
* CLIXON_BACKEND_SYSDIR
|
||||
* CLIXON_CLI_SYSDIR
|
||||
* CLICON_MASTER_PLUGIN config variable
|
||||
* Example of migrating a backend plugin module:
|
||||
* Add all callbacks in a clixon_plugin_api struct
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ Extending
|
|||
=========
|
||||
Clixon provides a core system and can be used as-is using available
|
||||
Yang specifications. However, an application very quickly needs to
|
||||
specialize functions. Clixon is extended by (most commonly) writing
|
||||
specialize functions. Clixon is extended by writing
|
||||
plugins for cli and backend. Extensions for netconf and restconf
|
||||
are also available.
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ clixon_plugin_reset(clicon_handle h,
|
|||
plgreset_t *resetfn; /* Plugin auth */
|
||||
int retval = 1;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((resetfn = cp->cp_api.ca_reset) == NULL)
|
||||
continue;
|
||||
if ((retval = resetfn(h, db)) < 0) {
|
||||
|
|
@ -150,7 +150,7 @@ clixon_plugin_statedata(clicon_handle h,
|
|||
clicon_err(OE_CFG, ENOENT, "XML tree expected");
|
||||
goto done;
|
||||
}
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_statedata) == NULL)
|
||||
continue;
|
||||
if ((x = xml_new("config", NULL, NULL)) == NULL)
|
||||
|
|
@ -246,7 +246,7 @@ plugin_transaction_begin(clicon_handle h,
|
|||
clixon_plugin *cp = NULL;
|
||||
trans_cb_t *fn;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_trans_begin) == NULL)
|
||||
continue;
|
||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||
|
|
@ -274,7 +274,7 @@ plugin_transaction_validate(clicon_handle h,
|
|||
clixon_plugin *cp = NULL;
|
||||
trans_cb_t *fn;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_trans_validate) == NULL)
|
||||
continue;
|
||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||
|
|
@ -303,7 +303,7 @@ plugin_transaction_complete(clicon_handle h,
|
|||
clixon_plugin *cp = NULL;
|
||||
trans_cb_t *fn;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_trans_complete) == NULL)
|
||||
continue;
|
||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||
|
|
@ -349,7 +349,7 @@ plugin_transaction_revert(clicon_handle h,
|
|||
tr.td_scvec = td->td_tcvec;
|
||||
tr.td_tcvec = td->td_scvec;
|
||||
|
||||
while ((cp = plugin_each_revert(cp, nr)) != NULL) {
|
||||
while ((cp = plugin_each_revert(h, cp, nr)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
||||
continue;
|
||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||
|
|
@ -379,7 +379,7 @@ plugin_transaction_commit(clicon_handle h,
|
|||
trans_cb_t *fn;
|
||||
int i=0;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
i++;
|
||||
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
||||
continue;
|
||||
|
|
@ -409,7 +409,7 @@ plugin_transaction_end(clicon_handle h,
|
|||
clixon_plugin *cp = NULL;
|
||||
trans_cb_t *fn;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_trans_end) == NULL)
|
||||
continue;
|
||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||
|
|
@ -436,7 +436,7 @@ plugin_transaction_abort(clicon_handle h,
|
|||
clixon_plugin *cp = NULL;
|
||||
trans_cb_t *fn;
|
||||
|
||||
while ((cp = plugin_each(cp)) != NULL) {
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_trans_abort) == NULL)
|
||||
continue;
|
||||
fn(h, (transaction_data)td); /* dont abort on error */
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@ CLIXON_MINOR = @CLIXON_VERSION_MINOR@
|
|||
|
||||
# Use this clixon lib for linking
|
||||
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
||||
# Location of system plugins
|
||||
CLIXON_CLI_SYSDIR = $(libdir)/clixon/plugins/cli
|
||||
|
||||
# For dependency. A little strange that we rely on it being built in the src dir
|
||||
# even though it may exist in $(libdir). But the new version may not have been installed yet.
|
||||
|
|
@ -127,7 +125,7 @@ uninstall:
|
|||
.SUFFIXES: .c .o
|
||||
|
||||
.c.o:
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" -DCLIXON_CLI_SYSDIR=\"$(CLIXON_CLI_SYSDIR)\" $(CFLAGS) -c $<
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" $(CFLAGS) -c $<
|
||||
|
||||
# Just link test programs
|
||||
test.c :
|
||||
|
|
|
|||
|
|
@ -172,12 +172,22 @@ cli_parse_file(clicon_handle h,
|
|||
}
|
||||
|
||||
int
|
||||
cli_susp_hook(clicon_handle h, cli_susphook_t *fn)
|
||||
cli_susp_hook(clicon_handle h,
|
||||
cligen_susp_cb_t *fn)
|
||||
{
|
||||
cligen_handle ch = cligen(h);
|
||||
|
||||
/* This assume first arg of fn can be treated as void* */
|
||||
return cligen_susp_hook(ch, (cligen_susp_cb_t*)fn);
|
||||
return cligen_susp_hook(ch, fn);
|
||||
}
|
||||
int
|
||||
cli_interrupt_hook(clicon_handle h,
|
||||
cligen_interrupt_cb_t *fn)
|
||||
{
|
||||
cligen_handle ch = cligen(h);
|
||||
|
||||
/* This assume first arg of fn can be treated as void* */
|
||||
return cligen_interrupt_hook(ch, fn);
|
||||
}
|
||||
|
||||
char *
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
/*
|
||||
* Prototypes
|
||||
* Internal prototypes. For exported functions see clicon_cli_api.h
|
||||
* Internal prototypes. For exported functions see clixon_cli_api.h
|
||||
*/
|
||||
int cli_parse_file(clicon_handle h,
|
||||
FILE *f,
|
||||
|
|
@ -47,7 +47,9 @@ int cli_parse_file(clicon_handle h,
|
|||
parse_tree *pt,
|
||||
cvec *globals);
|
||||
|
||||
int cli_susp_hook(clicon_handle h, cli_susphook_t *fn);
|
||||
int cli_susp_hook(clicon_handle h, cligen_susp_cb_t *fn);
|
||||
|
||||
int cli_interrupt_hook(clicon_handle h, cligen_interrupt_cb_t *fn);
|
||||
|
||||
char *cli_nomatch(clicon_handle h);
|
||||
|
||||
|
|
@ -56,8 +58,8 @@ int cli_prompt_set(clicon_handle h, char *prompt);
|
|||
int cli_logsyntax_set(clicon_handle h, int status);
|
||||
|
||||
/* Internal functions for handling cli groups */
|
||||
|
||||
cli_syntax_t *cli_syntax(clicon_handle h);
|
||||
|
||||
int cli_syntax_set(clicon_handle h, cli_syntax_t *stx);
|
||||
|
||||
#endif /* _CLI_HANDLE_H_ */
|
||||
|
|
|
|||
|
|
@ -460,7 +460,7 @@ main(int argc, char **argv)
|
|||
*/
|
||||
tmp = *(argv-1);
|
||||
*(argv-1) = argv0;
|
||||
cli_plugin_start(h, argc+1, argv-1);
|
||||
clixon_plugin_start(h, argc+1, argv-1);
|
||||
*(argv-1) = tmp;
|
||||
|
||||
cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING"));
|
||||
|
|
|
|||
|
|
@ -66,13 +66,6 @@
|
|||
#include "cli_plugin.h"
|
||||
#include "cli_handle.h"
|
||||
|
||||
/*! Name of master plugin functions
|
||||
* More in clicon_plugin.h
|
||||
* @note not really used consider documenting or remove
|
||||
*/
|
||||
#define PLUGIN_PROMPT_HOOK "plugin_prompt_hook"
|
||||
#define PLUGIN_SUSP_HOOK "plugin_susp_hook"
|
||||
|
||||
/*
|
||||
*
|
||||
* CLI PLUGIN INTERFACE, INTERNAL SECTION
|
||||
|
|
@ -82,7 +75,9 @@
|
|||
/*! Find syntax mode named 'mode'. Create if specified
|
||||
*/
|
||||
static cli_syntaxmode_t *
|
||||
syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
|
||||
syntax_mode_find(cli_syntax_t *stx,
|
||||
const char *mode,
|
||||
int create)
|
||||
{
|
||||
cli_syntaxmode_t *m;
|
||||
|
||||
|
|
@ -111,40 +106,26 @@ syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
|
|||
return m;
|
||||
}
|
||||
|
||||
/*! Find plugin by name
|
||||
*/
|
||||
static struct cli_plugin *
|
||||
plugin_find_cli(cli_syntax_t *stx, char *plgnam)
|
||||
{
|
||||
struct cli_plugin *p;
|
||||
|
||||
if ((p = stx->stx_plugins) != NULL)
|
||||
do {
|
||||
if (strcmp (p->cp_name, plgnam) == 0)
|
||||
return p;
|
||||
p = NEXTQ(struct cli_plugin *, p);
|
||||
} while (p && p != stx->stx_plugins);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Generate parse tree for syntax mode
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] m Syntax mode struct
|
||||
*/
|
||||
static int
|
||||
gen_parse_tree(clicon_handle h, cli_syntaxmode_t *m)
|
||||
gen_parse_tree(clicon_handle h,
|
||||
cli_syntaxmode_t *m)
|
||||
{
|
||||
cligen_tree_add(cli_cligen(h), m->csm_name, m->csm_pt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Append syntax
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
static int
|
||||
syntax_append(clicon_handle h,
|
||||
cli_syntax_t *stx,
|
||||
const char *name,
|
||||
parse_tree pt)
|
||||
const char *name,
|
||||
parse_tree pt)
|
||||
{
|
||||
cli_syntaxmode_t *m;
|
||||
|
||||
|
|
@ -157,27 +138,18 @@ syntax_append(clicon_handle h,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Unload all plugins in a group
|
||||
/*! Remove all cligen syntax modes
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
static int
|
||||
cli_syntax_unload(clicon_handle h)
|
||||
{
|
||||
cli_syntax_t *stx = cli_syntax(h);
|
||||
struct cli_plugin *p;
|
||||
cli_syntaxmode_t *m;
|
||||
|
||||
if (stx == NULL)
|
||||
return 0;
|
||||
|
||||
while (stx->stx_nplugins > 0) {
|
||||
p = stx->stx_plugins;
|
||||
plugin_unload(h, p->cp_handle);
|
||||
clicon_debug(1, "DEBUG: Plugin '%s' unloaded.", p->cp_name);
|
||||
DELQ(p, stx->stx_plugins, struct cli_plugin *);
|
||||
if (p)
|
||||
free(p);
|
||||
stx->stx_nplugins--;
|
||||
}
|
||||
while (stx->stx_nmodes > 0) {
|
||||
m = stx->stx_modes;
|
||||
DELQ(m, stx->stx_modes, cli_syntaxmode_t *);
|
||||
|
|
@ -237,34 +209,6 @@ clixon_str2fn(char *name,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*! Load a dynamic plugin object and call it's init-function
|
||||
* Note 'file' may be destructively modified
|
||||
* @retval plugin-handle should be freed after use
|
||||
*/
|
||||
static plghndl_t
|
||||
cli_plugin_load(clicon_handle h,
|
||||
char *file,
|
||||
int dlflags)
|
||||
{
|
||||
char *name;
|
||||
plghndl_t handle = NULL;
|
||||
struct cli_plugin *cp = NULL;
|
||||
|
||||
if ((handle = plugin_load(h, file, dlflags)) == NULL)
|
||||
goto quit;
|
||||
if ((cp = malloc(sizeof (struct cli_plugin))) == NULL) {
|
||||
perror("malloc");
|
||||
goto quit;
|
||||
}
|
||||
memset (cp, 0, sizeof(*cp));
|
||||
name = basename(file);
|
||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%.*s", (int)strlen(name)-3, name);
|
||||
cp->cp_handle = handle;
|
||||
|
||||
quit:
|
||||
return cp;
|
||||
}
|
||||
|
||||
/*! Append to syntax mode from file
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] filename Name of file where syntax is specified (in syntax-group dir)
|
||||
|
|
@ -286,7 +230,7 @@ cli_load_syntax(clicon_handle h,
|
|||
char **vec = NULL;
|
||||
int i, nvec;
|
||||
char *plgnam;
|
||||
struct cli_plugin *p;
|
||||
clixon_plugin *cp;
|
||||
|
||||
if (dir)
|
||||
snprintf(filepath, MAXPATHLEN-1, "%s/%s", dir, filename);
|
||||
|
|
@ -316,8 +260,8 @@ cli_load_syntax(clicon_handle h,
|
|||
mode = cvec_find_str(cvv, "CLICON_MODE");
|
||||
|
||||
if (plgnam != NULL) { /* Find plugin for callback resolving */
|
||||
if ((p = plugin_find_cli (cli_syntax(h), plgnam)) != NULL)
|
||||
handle = p->cp_handle;
|
||||
if ((cp = plugin_find(h, plgnam)) != NULL)
|
||||
handle = cp->cp_handle;
|
||||
if (handle == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "CLICON_PLUGIN set to '%s' in %s but plugin %s.so not found in %s\n",
|
||||
plgnam, filename, plgnam,
|
||||
|
|
@ -325,7 +269,6 @@ cli_load_syntax(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolve callback names to function pointers. */
|
||||
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)",
|
||||
|
|
@ -343,7 +286,10 @@ cli_load_syntax(clicon_handle h,
|
|||
if ((vec = clicon_strsep(mode, ":", &nvec)) == NULL)
|
||||
goto done;
|
||||
for (i = 0; i < nvec; i++) {
|
||||
if (syntax_append(h, cli_syntax(h), vec[i], pt) < 0) {
|
||||
if (syntax_append(h,
|
||||
cli_syntax(h),
|
||||
vec[i],
|
||||
pt) < 0) {
|
||||
goto done;
|
||||
}
|
||||
if (prompt)
|
||||
|
|
@ -361,74 +307,7 @@ done:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Load plugins within a directory
|
||||
*/
|
||||
static int
|
||||
cli_plugin_load_dir(clicon_handle h,
|
||||
char *dir,
|
||||
cli_syntax_t *stx)
|
||||
{
|
||||
int i;
|
||||
int ndp;
|
||||
struct dirent *dp = NULL;
|
||||
char *master_plugin;
|
||||
char master[MAXPATHLEN];
|
||||
char filename[MAXPATHLEN];
|
||||
struct cli_plugin *cp;
|
||||
struct stat st;
|
||||
int retval = -1;
|
||||
|
||||
/* Format master plugin path */
|
||||
if ((master_plugin = clicon_master_plugin(h)) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "clicon_master_plugin option not set");
|
||||
goto quit;
|
||||
}
|
||||
snprintf(master, MAXPATHLEN-1, "%s.so", master_plugin);
|
||||
|
||||
/* Get plugin objects names from plugin directory */
|
||||
ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG);
|
||||
if (ndp < 0)
|
||||
goto quit;
|
||||
|
||||
/* Load master plugin first */
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, master);
|
||||
if (stat(filename, &st) == 0) {
|
||||
clicon_debug(1, "DEBUG: Loading master plugin '%s'", master);
|
||||
cp = cli_plugin_load(h, filename, RTLD_NOW|RTLD_GLOBAL);
|
||||
if (cp == NULL)
|
||||
goto quit;
|
||||
/* Look up certain call-backs in master plugin */
|
||||
stx->stx_prompt_hook =
|
||||
dlsym(cp->cp_handle, PLUGIN_PROMPT_HOOK);
|
||||
stx->stx_susp_hook =
|
||||
dlsym(cp->cp_handle, PLUGIN_SUSP_HOOK);
|
||||
INSQ(cp, stx->stx_plugins);
|
||||
stx->stx_nplugins++;
|
||||
}
|
||||
|
||||
/* Load the rest */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
if (strcmp (dp[i].d_name, master) == 0)
|
||||
continue; /* Skip master now */
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "DEBUG: Loading plugin '%s'", dp[i].d_name);
|
||||
|
||||
if ((cp = cli_plugin_load (h, filename, RTLD_NOW)) == NULL)
|
||||
goto quit;
|
||||
INSQ(cp, stx->stx_plugins);
|
||||
stx->stx_nplugins++;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
|
||||
quit:
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Load a syntax group.
|
||||
/*! Load a syntax group. Includes both CLI plugin and CLIgen spec syntax files.
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
int
|
||||
|
|
@ -443,6 +322,9 @@ cli_syntax_load (clicon_handle h)
|
|||
struct dirent *dp = NULL;
|
||||
cli_syntax_t *stx;
|
||||
cli_syntaxmode_t *m;
|
||||
cligen_susp_cb_t *fns = NULL;
|
||||
cligen_interrupt_cb_t *fni = NULL;
|
||||
clixon_plugin *cp;
|
||||
|
||||
/* Syntax already loaded. XXX should we re-load?? */
|
||||
if ((stx = cli_syntax(h)) != NULL)
|
||||
|
|
@ -456,59 +338,62 @@ cli_syntax_load (clicon_handle h)
|
|||
/* Allocate plugin group object */
|
||||
if ((stx = malloc(sizeof(*stx))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto quit;
|
||||
goto done;
|
||||
}
|
||||
memset (stx, 0, sizeof (*stx)); /* Zero out all */
|
||||
|
||||
cli_syntax_set(h, stx);
|
||||
|
||||
/* First load CLICON system plugins. CLIXON_CLI_SYSDIR is defined
|
||||
in Makefile*/
|
||||
if (cli_plugin_load_dir(h, CLIXON_CLI_SYSDIR, stx) < 0)
|
||||
goto quit;
|
||||
|
||||
/* Then load application plugins */
|
||||
if (plugin_dir && cli_plugin_load_dir(h, plugin_dir, stx) < 0)
|
||||
goto quit;
|
||||
|
||||
/* Load cli plugins */
|
||||
if (plugin_dir &&
|
||||
clixon_plugins_load(h, CLIXON_PLUGIN_INIT, plugin_dir)< 0)
|
||||
goto done;
|
||||
if (clispec_file){
|
||||
if (cli_load_syntax(h, clispec_file, NULL) < 0)
|
||||
goto quit;
|
||||
goto done;
|
||||
}
|
||||
if (clispec_dir){
|
||||
/* load syntaxfiles */
|
||||
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0)
|
||||
goto quit;
|
||||
goto done;
|
||||
/* Load the rest */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
clicon_debug(1, "DEBUG: Loading syntax '%.*s'",
|
||||
(int)strlen(dp[i].d_name)-4, dp[i].d_name);
|
||||
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
|
||||
goto quit;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Did we successfully load any syntax modes? */
|
||||
if (stx->stx_nmodes <= 0) {
|
||||
retval = 0;
|
||||
goto quit;
|
||||
goto done;
|
||||
}
|
||||
/* Parse syntax tree for all modes */
|
||||
m = stx->stx_modes;
|
||||
do {
|
||||
if (gen_parse_tree(h, m) != 0)
|
||||
goto quit;
|
||||
goto done;
|
||||
m = NEXTQ(cli_syntaxmode_t *, m);
|
||||
} while (m && m != stx->stx_modes);
|
||||
|
||||
|
||||
/* Set callbacks into CLIgen */
|
||||
cli_susp_hook(h, cli_syntax(h)->stx_susp_hook);
|
||||
/* Set susp and interrupt callbacks into CLIgen */
|
||||
cp = NULL;
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if (fns==NULL && (fns = cp->cp_api.ca_suspend) != NULL)
|
||||
if (cli_susp_hook(h, fns) < 0)
|
||||
goto done;
|
||||
if (fni==NULL && (fni = cp->cp_api.ca_interrupt) != NULL)
|
||||
if (cli_susp_hook(h, fns) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* All good. We can now proudly return a new group */
|
||||
retval = 0;
|
||||
|
||||
quit:
|
||||
done:
|
||||
if (retval != 0) {
|
||||
clixon_plugin_exit(h);
|
||||
cli_syntax_unload(h);
|
||||
cli_syntax_set(h, NULL);
|
||||
}
|
||||
|
|
@ -517,34 +402,15 @@ quit:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Call plugin_start() in all plugins
|
||||
*/
|
||||
int
|
||||
cli_plugin_start(clicon_handle h, int argc, char **argv)
|
||||
{
|
||||
struct cli_plugin *p;
|
||||
cli_syntax_t *stx;
|
||||
plgstart_t *startfun;
|
||||
// XXX int (*startfun)(clicon_handle, int, char **);
|
||||
|
||||
stx = cli_syntax(h);
|
||||
|
||||
if ((p = stx->stx_plugins) != NULL)
|
||||
do {
|
||||
startfun = dlsym(p->cp_handle, PLUGIN_START);
|
||||
if (dlerror() == NULL)
|
||||
startfun(h, argc, argv);
|
||||
p = NEXTQ(struct cli_plugin *, p);
|
||||
} while (p && p != stx->stx_plugins);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
/*! Remove syntax modes and remove syntax
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
int
|
||||
cli_plugin_finish(clicon_handle h)
|
||||
{
|
||||
/* Remove all CLI plugins */
|
||||
clixon_plugin_exit(h);
|
||||
/* Remove all cligen syntax modes */
|
||||
cli_syntax_unload(h);
|
||||
cli_syntax_set(h, NULL);
|
||||
return 0;
|
||||
|
|
@ -553,6 +419,7 @@ cli_plugin_finish(clicon_handle h)
|
|||
/*! Help function to print a meaningful error string.
|
||||
* Sometimes the libraries specify an error string, if so print that.
|
||||
* Otherwise just print 'command error'.
|
||||
* @param[in] f File handler to write error to.
|
||||
*/
|
||||
int
|
||||
cli_handler_err(FILE *f)
|
||||
|
|
@ -704,22 +571,30 @@ done:
|
|||
}
|
||||
|
||||
/*! Read command from CLIgen's cliread() using current syntax mode.
|
||||
* @param[in] h Clicon handle
|
||||
* @retval string char* buffer containing CLIgen command
|
||||
* @retval NULL Fatal error
|
||||
*/
|
||||
char *
|
||||
clicon_cliread(clicon_handle h)
|
||||
{
|
||||
char *ret;
|
||||
char *pfmt = NULL;
|
||||
char *ret;
|
||||
char *pfmt = NULL;
|
||||
cli_syntaxmode_t *mode;
|
||||
cli_syntax_t *stx;
|
||||
|
||||
cli_syntax_t *stx;
|
||||
cli_prompthook_t *fn;
|
||||
clixon_plugin *cp;
|
||||
|
||||
stx = cli_syntax(h);
|
||||
mode = stx->stx_active_mode;
|
||||
|
||||
if (stx->stx_prompt_hook)
|
||||
pfmt = stx->stx_prompt_hook(h, mode->csm_name);
|
||||
/* Get prompt from plugin callback? */
|
||||
cp = NULL;
|
||||
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
if ((fn = cp->cp_api.ca_prompt) == NULL)
|
||||
continue;
|
||||
pfmt = fn(h, mode->csm_name);
|
||||
break;
|
||||
}
|
||||
if (clicon_quiet_mode(h))
|
||||
cli_prompt_set(h, "");
|
||||
else
|
||||
|
|
@ -732,6 +607,7 @@ clicon_cliread(clicon_handle h)
|
|||
}
|
||||
|
||||
/*! Initialize plugin code (not the plugins themselves)
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
int
|
||||
cli_plugin_init(clicon_handle h)
|
||||
|
|
@ -746,11 +622,12 @@ cli_plugin_init(clicon_handle h)
|
|||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Set syntax mode mode for existing current plugin group.
|
||||
/*! Set syntax mode mode for existing current plugin group.
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
int
|
||||
cli_set_syntax_mode(clicon_handle h, const char *name)
|
||||
cli_set_syntax_mode(clicon_handle h,
|
||||
const char *name)
|
||||
{
|
||||
cli_syntaxmode_t *mode;
|
||||
|
||||
|
|
@ -761,8 +638,8 @@ cli_set_syntax_mode(clicon_handle h, const char *name)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get syntax mode name
|
||||
/*! Get syntax mode name
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
char *
|
||||
cli_syntax_mode(clicon_handle h)
|
||||
|
|
@ -774,14 +651,15 @@ cli_syntax_mode(clicon_handle h)
|
|||
return csm->csm_name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback from cli_set_prompt(). Set prompt format for syntax mode
|
||||
* Arguments:
|
||||
* name : Name of syntax mode
|
||||
* prompt : Prompt format
|
||||
/*! Callback from cli_set_prompt(). Set prompt format for syntax mode
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Name of syntax mode
|
||||
* @param[in] prompt Prompt format
|
||||
*/
|
||||
int
|
||||
cli_set_prompt(clicon_handle h, const char *name, const char *prompt)
|
||||
cli_set_prompt(clicon_handle h,
|
||||
const char *name,
|
||||
const char *prompt)
|
||||
{
|
||||
cli_syntaxmode_t *m;
|
||||
|
||||
|
|
@ -793,9 +671,14 @@ cli_set_prompt(clicon_handle h, const char *name, const char *prompt)
|
|||
}
|
||||
|
||||
/*! Format prompt
|
||||
* @param[out] prompt Prompt string to be written
|
||||
* @param[in] plen Length of prompt string
|
||||
* @param[in] fmt Stdarg fmt string
|
||||
*/
|
||||
static int
|
||||
prompt_fmt (char *prompt, size_t plen, char *fmt, ...)
|
||||
prompt_fmt (char *prompt,
|
||||
size_t plen,
|
||||
char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *s = fmt;
|
||||
|
|
@ -849,6 +732,7 @@ done:
|
|||
}
|
||||
|
||||
/*! Return a formatted prompt string
|
||||
* @param[in] fmt Format string
|
||||
*/
|
||||
char *
|
||||
cli_prompt(char *fmt)
|
||||
|
|
|
|||
|
|
@ -43,51 +43,26 @@
|
|||
/* clicon generic callback pointer */
|
||||
typedef void (clicon_callback_t)(clicon_handle h);
|
||||
|
||||
/* clicon_set value callback */
|
||||
typedef int (cli_valcb_t)(cvec *vars, cg_var *cgv, cg_var *arg);
|
||||
|
||||
/* specific to cli. For common see clicon_plugin.h */
|
||||
/* Hook to get prompt format before each getline */
|
||||
typedef char *(cli_prompthook_t)(clicon_handle, char *mode);
|
||||
|
||||
/* Ctrl-Z hook from getline() */
|
||||
typedef int (cli_susphook_t)(clicon_handle, char *, int, int *);
|
||||
|
||||
/* CLIgen parse failure hook. Retry other mode? */
|
||||
typedef char *(cli_parsehook_t)(clicon_handle, char *, char *);
|
||||
|
||||
/* List of syntax modes */
|
||||
typedef struct {
|
||||
qelem_t csm_qelem; /* List header */
|
||||
char csm_name[256]; /* Syntax mode name */
|
||||
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
||||
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
||||
parse_tree csm_pt; /* CLIgen parse tree */
|
||||
qelem_t csm_qelem; /* List header */
|
||||
char csm_name[256]; /* Syntax mode name */
|
||||
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
||||
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
||||
parse_tree csm_pt; /* CLIgen parse tree */
|
||||
|
||||
} cli_syntaxmode_t;
|
||||
|
||||
/* A plugin list object */
|
||||
struct cli_plugin {
|
||||
qelem_t cp_qelem; /* List header */
|
||||
char cp_name[256]; /* Plugin name */
|
||||
void *cp_handle; /* Dynamic object handle */
|
||||
};
|
||||
|
||||
/* Plugin group object */
|
||||
/* Plugin group object. Just a single object, not list. part of cli_handle */
|
||||
typedef struct {
|
||||
int stx_nplugins; /* Number of plugins */
|
||||
struct cli_plugin *stx_plugins; /* List of plugins */
|
||||
int stx_nmodes; /* Number of syntax modes */
|
||||
cli_syntaxmode_t *stx_active_mode; /* Current active syntax mode */
|
||||
cli_syntaxmode_t *stx_modes; /* List of syntax modes */
|
||||
cli_prompthook_t *stx_prompt_hook; /* Prompt hook */
|
||||
cli_susphook_t *stx_susp_hook; /* Ctrl-Z hook from getline() */
|
||||
} cli_syntax_t;
|
||||
|
||||
|
||||
void *clixon_str2fn(char *name, void *handle, char **error);
|
||||
|
||||
int cli_plugin_start(clicon_handle, int argc, char **argv);
|
||||
|
||||
int cli_plugin_init(clicon_handle h);
|
||||
|
||||
int clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr);
|
||||
|
|
|
|||
|
|
@ -1,129 +0,0 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
#
|
||||
# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
|
||||
#
|
||||
# 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 *****
|
||||
#
|
||||
|
||||
#
|
||||
# CLIXON options - Default values
|
||||
# The origin of this file is run a _first_ time through a pre-processor at
|
||||
# clixon make install time causing autoconf constants (such as "prefix" and
|
||||
# "localstatedir") to be replaced with their installed values.
|
||||
# It should be run a _second_ time as a part of installation of the application,
|
||||
# in case clixon.mk is included in the application include file, and
|
||||
# "$(APPNAME).conf" rule is accessed.
|
||||
#
|
||||
# See clicon_tutorial for more documentation
|
||||
|
||||
# Location of configuration-file for default values (this file)
|
||||
CLICON_CONFIGFILE sysconfdir/APPNAME.conf
|
||||
|
||||
# Location of YANG module and submodule files.
|
||||
CLICON_YANG_DIR prefix/share/APPNAME/yang
|
||||
|
||||
# Main yang module or absolute filename. If module then search as follows:
|
||||
# <yangdir>/<module>[@<revision>]
|
||||
# CLICON_YANG_MODULE_MAIN clicon
|
||||
|
||||
# Option used to construct initial yang file:
|
||||
# <module>[@<revision>]
|
||||
CLICON_YANG_MODULE_REVISION
|
||||
|
||||
# Location of backend .so plugins
|
||||
CLICON_BACKEND_DIR libdir/APPNAME/backend
|
||||
|
||||
# Location of netconf (frontend) .so plugins
|
||||
CLICON_NETCONF_DIR libdir/APPNAME/netconf
|
||||
|
||||
# Location of restconf (frontend) .so plugins
|
||||
CLICON_RESTCONF_DIR libdir/APPNAME/restconf
|
||||
|
||||
# Location of cli frontend .so plugins
|
||||
CLICON_CLI_DIR libdir/APPNAME/cli
|
||||
|
||||
# Location of frontend .cli cligen spec files
|
||||
CLICON_CLISPEC_DIR libdir/APPNAME/clispec
|
||||
|
||||
# Enabled uses "startup" configuration on boot
|
||||
CLICON_USE_STARTUP_CONFIG 0
|
||||
|
||||
# Address family for communicating with clixon_backend (UNIX|IPv4|IPv6)
|
||||
CLICON_SOCK_FAMILY UNIX
|
||||
|
||||
# If family above is AF_UNIX: Unix socket for communicating with clixon_backend
|
||||
# If family above is AF_INET: IPv4 address
|
||||
CLICON_SOCK localstatedir/APPNAME/APPNAME.sock
|
||||
|
||||
# Inet socket port for communicating with clixon_backend (only IPv4|IPv6)
|
||||
CLICON_SOCK_PORT 4535
|
||||
|
||||
# Process-id file
|
||||
CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
|
||||
|
||||
# Group membership to access clixon_backend unix socket
|
||||
# CLICON_SOCK_GROUP clicon
|
||||
|
||||
# Set if all configuration changes are committed directly, commit command unnecessary
|
||||
# CLICON_AUTOCOMMIT 0
|
||||
|
||||
# Name of master plugin (both frontend and backend). Master plugin has special
|
||||
# callbacks for frontends. See clicon user manual for more info.
|
||||
# CLICON_MASTER_PLUGIN master
|
||||
|
||||
# Startup CLI mode. This should match the CLICON_MODE in your startup clispec file
|
||||
# CLICON_CLI_MODE base
|
||||
|
||||
# Generate code for CLI completion of existing db symbols. Add name="myspec" in
|
||||
# datamodel spec and reference as @myspec.
|
||||
# CLICON_CLI_GENMODEL 1
|
||||
|
||||
# Generate code for CLI completion of existing db symbols
|
||||
# CLICON_CLI_GENMODEL_COMPLETION 1
|
||||
|
||||
# How to generate and show CLI syntax: VARS|ALL
|
||||
# CLICON_CLI_GENMODEL_TYPE VARS
|
||||
|
||||
# Directory where "running", "candidate" and "startup" are placed
|
||||
CLICON_XMLDB_DIR localstatedir/APPNAME
|
||||
|
||||
# XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])
|
||||
CLICON_XMLDB_PLUGIN libdir/xmldb/text.so
|
||||
|
||||
# Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored
|
||||
# CLICON_CLI_VARONLY 1
|
||||
|
||||
# Set to 0 if you want CLI to wrap to next line.
|
||||
# Set to 1 if you want CLI to scroll sideways when approaching right margin
|
||||
# CLICON_CLI_LINESCROLLING 1
|
||||
|
||||
# FastCGI unix socket. Should be specified in webserver
|
||||
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
|
||||
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
|
||||
|
||||
43
doc/FAQ.md
43
doc/FAQ.md
|
|
@ -1,4 +1,4 @@
|
|||
# Clixon FAQ
|
||||
i# Clixon FAQ
|
||||
|
||||
## What is Clixon?
|
||||
|
||||
|
|
@ -197,6 +197,7 @@ The second way is by programming the plugin_reset() in the backend
|
|||
plugin. The example code contains an example on how to do this (see plugin_reset() in example_backend.c).
|
||||
|
||||
## I want to program. How do I extend the example?
|
||||
See [../apps/example]
|
||||
- example.xml - Change the configuration file
|
||||
- The yang specifications - This is the central part. It changes the XML, database and the config cli.
|
||||
- example_cli.cli - Change the fixed part of the CLI commands
|
||||
|
|
@ -205,6 +206,25 @@ plugin. The example code contains an example on how to do this (see plugin_reset
|
|||
- example_netconf.c - Netconf plugin
|
||||
- example_restconf.c - Add restconf authentication, etc.
|
||||
|
||||
## How is a plugin initiated?
|
||||
Each plugin is initiated with an API struct followed by a plugin init function as follows:
|
||||
```
|
||||
static clixon_plugin_api api = {
|
||||
"example", /* name */
|
||||
clixon_plugin_init,
|
||||
plugin_start,
|
||||
... /* more functions here */
|
||||
}
|
||||
clixon_plugin_api *
|
||||
clixon_plugin_init(clicon_handle h)
|
||||
{
|
||||
...
|
||||
return &api; /* Return NULL on error */
|
||||
}
|
||||
```
|
||||
For more info see [../example/README.md]
|
||||
|
||||
|
||||
## How do I write a commit function?
|
||||
In the example, you write a commit function in example_backend.c.
|
||||
Every time a commit is made, transaction_commit() is called in the
|
||||
|
|
@ -284,10 +304,10 @@ implement the RFC, you need to register an RPC callback in the backend plugin:
|
|||
Example:
|
||||
```
|
||||
int
|
||||
plugin_init(clicon_handle h)
|
||||
clixon_plugin_init(clicon_handle h)
|
||||
{
|
||||
...
|
||||
backend_rpc_cb_register(h, fib_route, NULL, "fib-route");
|
||||
rpc_callback_register(h, fib_route, NULL, "fib-route");
|
||||
...
|
||||
}
|
||||
```
|
||||
|
|
@ -296,9 +316,9 @@ And then define the callback itself:
|
|||
static int
|
||||
fib_route(clicon_handle h, /* Clicon handle */
|
||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||
struct client_entry *ce, /* Client session */
|
||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||
void *arg) /* Argument given at register */
|
||||
void *arg, /* Client session */
|
||||
void *regarg) /* Argument given at register */
|
||||
{
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
return 0;
|
||||
|
|
@ -313,13 +333,14 @@ You can specify an authentication callback for restconf as follows:
|
|||
```
|
||||
int
|
||||
plugin_credentials(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char **username)
|
||||
void *arg)
|
||||
{
|
||||
FCGX_Request *r = (FCGX_Request *)arg;
|
||||
...
|
||||
clicon_username_set(h, user);
|
||||
```
|
||||
|
||||
If a plugin is provided, it needs to supply a username. If not, the
|
||||
request is unauthorized. the function mallocs a username and returns
|
||||
it.
|
||||
To authenticate, the callback needs to return the value 1 and supply a username.
|
||||
|
||||
See (../apps/example/example_restconf.c) plugin_credentials() for
|
||||
See [../apps/example/example_restconf.c] plugin_credentials() for
|
||||
an example of HTTP basic auth.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,43 @@ Routing notification
|
|||
...
|
||||
```
|
||||
|
||||
## Initializing a plugin
|
||||
|
||||
The example includes a restonf, netconf, CLI and two backend plugins.
|
||||
Each plugin is initiated with an API struct followed by a plugin init function.
|
||||
The content of the API struct is different depending on what kind of plugin it is. Some fields are
|
||||
meaningful only for some plugins.
|
||||
The plugin init function may also include registering RPC functions.
|
||||
```
|
||||
static clixon_plugin_api api = {
|
||||
"example", /* name */
|
||||
clixon_plugin_init,
|
||||
plugin_start,
|
||||
plugin_exit,
|
||||
NULL, /* cli prompt N/A for backend */
|
||||
NULL, /* cli suspend N/A for backend */
|
||||
NULL, /* cli interrupt N/A for backend */
|
||||
NULL, /* auth N/A for backend */
|
||||
plugin_reset,
|
||||
plugin_statedata,
|
||||
transaction_begin,
|
||||
transaction_validate,
|
||||
transaction_complete,
|
||||
transaction_commit,
|
||||
transaction_end,
|
||||
transaction_abort
|
||||
};
|
||||
|
||||
clixon_plugin_api *
|
||||
clixon_plugin_init(clicon_handle h)
|
||||
{
|
||||
/* Optional callback registration for RPC calls */
|
||||
rpc_callback_register(h, fib_route, NULL, "fib-route");
|
||||
/* Return plugin API */
|
||||
return &api; /* Return NULL on error */
|
||||
}
|
||||
```
|
||||
|
||||
## Operation data
|
||||
|
||||
Clixon implements Yang RPC operations by an extension mechanism. The
|
||||
|
|
@ -119,18 +156,18 @@ In the backend, a callback is registered (fib_route()) which handles the RPC.
|
|||
static int
|
||||
fib_route(clicon_handle h,
|
||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||
struct client_entry *ce, /* Client session */
|
||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
||||
void *arg) /* Argument given at register */
|
||||
void *arg, /* Client session */
|
||||
void *regarg) /* Argument given at register */
|
||||
{
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
return 0;
|
||||
}
|
||||
int
|
||||
plugin_init(clicon_handle h)
|
||||
clixon_plugin_init(clicon_handle h)
|
||||
{
|
||||
...
|
||||
backend_rpc_cb_register(h, fib_route, NULL, "fib-route");
|
||||
rpc_callback_register(h, fib_route, NULL, "fib-route");
|
||||
...
|
||||
}
|
||||
```
|
||||
|
|
|
|||
|
|
@ -256,6 +256,9 @@ static clixon_plugin_api api = {
|
|||
plugin_start, /* start */
|
||||
NULL, /* exit */
|
||||
NULL, /* auth */
|
||||
NULL, /* cli prompt */
|
||||
NULL, /* cli suspend */
|
||||
NULL, /* cli interrupt */
|
||||
plugin_reset, /* reset */
|
||||
plugin_statedata, /* statedata */
|
||||
NULL, /* trans begin */
|
||||
|
|
@ -293,6 +296,7 @@ clixon_plugin_init(clicon_handle h)
|
|||
"empty"/* Xml tag when callback is made */
|
||||
) < 0)
|
||||
goto done;
|
||||
/* Return plugin API */
|
||||
return &api;
|
||||
done:
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -52,19 +52,6 @@
|
|||
#include <clixon/clixon.h>
|
||||
#include <clixon/clixon_cli.h>
|
||||
|
||||
/*
|
||||
* Plugin initialization
|
||||
*/
|
||||
int
|
||||
plugin_init(clicon_handle h)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Example cli function */
|
||||
int
|
||||
|
|
@ -125,3 +112,29 @@ fib_route_rpc(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
static clixon_plugin_api api = {
|
||||
"example", /* name */
|
||||
clixon_plugin_init, /* init */
|
||||
NULL, /* start */
|
||||
NULL, /* exit */
|
||||
NULL, /* auth */
|
||||
NULL, /* cli_prompthook_t */
|
||||
NULL, /* cligen_susp_cb_t */
|
||||
NULL, /* cligen_interrupt_cb_t */
|
||||
};
|
||||
|
||||
/*! CLI plugin initialization
|
||||
* @param[in] h Clixon handle
|
||||
* @retval NULL Error with clicon_err set
|
||||
* @retval api Pointer to API struct
|
||||
*/
|
||||
clixon_plugin_api *
|
||||
clixon_plugin_init(clicon_handle h)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
return &api;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,11 +268,12 @@ plugin_credentials(clicon_handle h,
|
|||
|
||||
/*! Local example restconf rpc callback
|
||||
*/
|
||||
int restconf_client_rpc(clicon_handle h,
|
||||
cxobj *xn,
|
||||
cbuf *cbret,
|
||||
void *arg,
|
||||
void *regarg)
|
||||
int
|
||||
restconf_client_rpc(clicon_handle h,
|
||||
cxobj *xn,
|
||||
cbuf *cbret,
|
||||
void *arg,
|
||||
void *regarg)
|
||||
{
|
||||
// FCGX_Request *r = (FCGX_Request *)arg;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@
|
|||
*/
|
||||
/* default group membership to access config unix socket */
|
||||
#define CLICON_SOCK_GROUP "clicon"
|
||||
/* Default name of master plugin */
|
||||
#define CLICON_MASTER_PLUGIN "master"
|
||||
|
||||
/*
|
||||
* Types
|
||||
|
|
@ -137,9 +135,6 @@ static inline char *clicon_sock_group(clicon_handle h){
|
|||
static inline char *clicon_backend_pidfile(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_BACKEND_PIDFILE");
|
||||
}
|
||||
static inline char *clicon_master_plugin(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_MASTER_PLUGIN");
|
||||
}
|
||||
static inline char *clicon_xmldb_dir(clicon_handle h){
|
||||
return clicon_option_str(h, "CLICON_XMLDB_DIR");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,16 @@
|
|||
#ifndef _CLIXON_PLUGIN_H_
|
||||
#define _CLIXON_PLUGIN_H_
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
/* Hardcoded plugin symbol. Must exist in all plugins to kickstart */
|
||||
#define CLIXON_PLUGIN_INIT "clixon_plugin_init"
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
/* The dynamicically loadable plugin object handle */
|
||||
/* Dynamicically loadable plugin object handle. @see return value of dlopen(3) */
|
||||
typedef void *plghndl_t;
|
||||
|
||||
/* Registered RPC callback function */
|
||||
|
|
@ -62,22 +68,13 @@ typedef int (*clicon_rpc_cb)(
|
|||
* Backend see config_plugin.c
|
||||
*/
|
||||
|
||||
/*! Called when plugin loaded. Only mandadory callback. All others optional
|
||||
* @see plginit_t
|
||||
*/
|
||||
#define PLUGIN_INIT "plugin_init"
|
||||
|
||||
typedef void * (plginit_t)(clicon_handle); /* Clixon plugin Init */
|
||||
|
||||
/* Called when backend started with cmd-line arguments from daemon call.
|
||||
* @see plgstart_t
|
||||
*/
|
||||
#define PLUGIN_START "plugin_start"
|
||||
typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */
|
||||
|
||||
/* Called just before plugin unloaded.
|
||||
*/
|
||||
#define PLUGIN_EXIT "plugin_exit"
|
||||
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
||||
|
||||
/*! Called by restconf to check credentials and return username
|
||||
|
|
@ -99,6 +96,14 @@ typedef void *transaction_data;
|
|||
/* Transaction callbacks */
|
||||
typedef int (trans_cb_t)(clicon_handle h, transaction_data td);
|
||||
|
||||
/* Hook to override default prompt with explicit function
|
||||
* Format prompt before each getline
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] mode Cligen syntax mode
|
||||
* @retval prompt Prompt to prepend all CLigen command lines
|
||||
*/
|
||||
typedef char *(cli_prompthook_t)(clicon_handle, char *mode);
|
||||
|
||||
/* plugin init struct for the api
|
||||
* Note: Implicit init function
|
||||
*/
|
||||
|
|
@ -106,14 +111,20 @@ struct clixon_plugin_api;
|
|||
typedef struct clixon_plugin_api* (plginit2_t)(clicon_handle); /* Clixon plugin Init */
|
||||
|
||||
struct clixon_plugin_api{
|
||||
/*--- Common fields. ---*/
|
||||
char ca_name[PATH_MAX]; /* Name of plugin (given by plugin) */
|
||||
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
||||
plgstart_t *ca_start; /* Plugin start */
|
||||
plgexit_t *ca_exit; /* Plugin exit */
|
||||
plgauth_t *ca_auth; /* Auth credentials */
|
||||
/*--Above here common fields w clixon_backend_api ----------*/
|
||||
plgreset_t *ca_reset; /* Reset system status (backend only) */
|
||||
|
||||
/*--- CLI plugin-only ---*/
|
||||
cli_prompthook_t *ca_prompt; /* Prompt hook */
|
||||
cligen_susp_cb_t *ca_suspend; /* Ctrl-Z hook, see cligen getline */
|
||||
cligen_interrupt_cb_t *ca_interrupt; /* Ctrl-C, see cligen getline */
|
||||
|
||||
/*--- Backend plugin only ---*/
|
||||
plgreset_t *ca_reset; /* Reset system status (backend only) */
|
||||
plgstatedata_t *ca_statedata; /* Get state data from plugin (backend only) */
|
||||
trans_cb_t *ca_trans_begin; /* Transaction start */
|
||||
trans_cb_t *ca_trans_validate; /* Transaction validation */
|
||||
|
|
@ -124,46 +135,34 @@ struct clixon_plugin_api{
|
|||
};
|
||||
typedef struct clixon_plugin_api clixon_plugin_api;
|
||||
|
||||
/*! Called when plugin loaded. Only mandadory callback. All others optional
|
||||
* @see plginit_t
|
||||
*/
|
||||
|
||||
/* Internal plugin structure with dlopen() handle and plugin_api
|
||||
*/
|
||||
struct clixon_plugin{
|
||||
char cp_name[PATH_MAX]; /* Plugin filename. Note api ca_name is given by plugin itself */
|
||||
plghndl_t cp_handle; /* Handle to plugin using dlopen(3) */
|
||||
struct clixon_plugin_api cp_api;
|
||||
char cp_name[PATH_MAX]; /* Plugin filename. Note api ca_name is given by plugin itself */
|
||||
plghndl_t cp_handle; /* Handle to plugin using dlopen(3) */
|
||||
clixon_plugin_api cp_api;
|
||||
};
|
||||
typedef struct clixon_plugin clixon_plugin;
|
||||
|
||||
/*
|
||||
* Pseudo-Prototypes
|
||||
* User-defineed plugins, not in library code
|
||||
*/
|
||||
#define CLIXON_PLUGIN_INIT "clixon_plugin_init" /* Nextgen */
|
||||
|
||||
/*! Plugin initialization
|
||||
* @param[in] h Clixon handle
|
||||
* @retval NULL Error with clicon_err set
|
||||
* @retval api Pointer to API struct
|
||||
*/
|
||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
clixon_plugin *plugin_each(clixon_plugin *cpprev);
|
||||
clixon_plugin *plugin_each_revert(clixon_plugin *cpprev, int nr);
|
||||
|
||||
/*! Plugin initialization function. Must appear in all plugins
|
||||
* @param[in] h Clixon handle
|
||||
* @retval api Pointer to API struct
|
||||
* @see CLIXON_PLUGIN_INIT default symbol
|
||||
*/
|
||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||
|
||||
clixon_plugin *plugin_each(clicon_handle h, clixon_plugin *cpprev);
|
||||
|
||||
clixon_plugin *plugin_each_revert(clicon_handle h, clixon_plugin *cpprev, int nr);
|
||||
|
||||
clixon_plugin *plugin_find(clicon_handle h, char *name);
|
||||
|
||||
int clixon_plugins_load(clicon_handle h, char *function, char *dir);
|
||||
|
||||
/* obsolete */
|
||||
plghndl_t plugin_load (clicon_handle h, char *file, int dlflags);
|
||||
|
||||
/* obsolete */
|
||||
int plugin_unload(clicon_handle h, plghndl_t *handle);
|
||||
|
||||
int clixon_plugin_start(clicon_handle h, int argc, char **argv);
|
||||
|
||||
int clixon_plugin_exit(clicon_handle h);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,10 @@
|
|||
#include "clixon_xml.h"
|
||||
#include "clixon_plugin.h"
|
||||
|
||||
/* XXX The below should be placed in clixon handle when done */
|
||||
/* List of plugins XXX
|
||||
* 1. Place in clixon handle not global variables
|
||||
* 2. Use qelem circular lists
|
||||
*/
|
||||
static clixon_plugin *_clixon_plugins = NULL; /* List of plugins (of client) */
|
||||
static int _clixon_nplugins = 0; /* Number of plugins */
|
||||
|
||||
|
|
@ -69,17 +72,19 @@ static int _clixon_nplugins = 0; /* Number of plugins */
|
|||
* @note Never manipulate the plugin during operation or using the
|
||||
* same object recursively
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] plugin previous plugin, or NULL on init
|
||||
* @code
|
||||
* clicon_plugin *cp = NULL;
|
||||
* while ((cp = plugin_each(cp)) != NULL) {
|
||||
* while ((cp = plugin_each(h, cp)) != NULL) {
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
* @note Not optimized, alwasy iterates from the start of the list
|
||||
*/
|
||||
clixon_plugin *
|
||||
plugin_each(clixon_plugin *cpprev)
|
||||
plugin_each(clicon_handle h,
|
||||
clixon_plugin *cpprev)
|
||||
{
|
||||
int i;
|
||||
clixon_plugin *cp;
|
||||
|
|
@ -105,17 +110,19 @@ plugin_each(clixon_plugin *cpprev)
|
|||
* @note Never manipulate the plugin during operation or using the
|
||||
* same object recursively
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] plugin previous plugin, or NULL on init
|
||||
* @code
|
||||
* clicon_plugin *cp = NULL;
|
||||
* while ((cp = plugin_each_revert(cp, nr)) != NULL) {
|
||||
* while ((cp = plugin_each_revert(h, cp, nr)) != NULL) {
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
* @note Not optimized, alwasy iterates from the start of the list
|
||||
*/
|
||||
clixon_plugin *
|
||||
plugin_each_revert(clixon_plugin *cpprev,
|
||||
plugin_each_revert(clicon_handle h,
|
||||
clixon_plugin *cpprev,
|
||||
int nr)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -137,6 +144,27 @@ plugin_each_revert(clixon_plugin *cpprev,
|
|||
return cpnext;
|
||||
}
|
||||
|
||||
/*! Find plugin by name
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] name Plugin name
|
||||
* @retval p Plugin if found
|
||||
* @retval NULL Not found
|
||||
*/
|
||||
clixon_plugin *
|
||||
plugin_find(clicon_handle h,
|
||||
char *name)
|
||||
{
|
||||
int i;
|
||||
clixon_plugin *cp = NULL;
|
||||
|
||||
for (i = 0; i < _clixon_nplugins; i++) {
|
||||
cp = &_clixon_plugins[i];
|
||||
if (strcmp(cp->cp_name, name) == 0)
|
||||
return cp;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Load a dynamic plugin object and call its init-function
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] file Which plugin to load
|
||||
|
|
@ -158,6 +186,7 @@ plugin_load_one(clicon_handle h,
|
|||
clixon_plugin_api *api = NULL;
|
||||
clixon_plugin *cp = NULL;
|
||||
char *name;
|
||||
char *p;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
dlerror(); /* Clear any existing error */
|
||||
|
|
@ -187,10 +216,18 @@ plugin_load_one(clicon_handle h,
|
|||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(cp, 0, sizeof(struct clixon_plugin));
|
||||
cp->cp_handle = handle;
|
||||
/* Extract string after last '/' in filename, if any */
|
||||
name = strrchr(file, '/') ? strrchr(file, '/')+1 : file;
|
||||
/* strip extension, eg .so from name */
|
||||
if ((p=strrchr(name, '.')) != NULL)
|
||||
*p = '\0';
|
||||
/* Copy name to struct */
|
||||
memcpy(cp->cp_name, name, strlen(name)+1);
|
||||
|
||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
|
||||
(int)strlen(name)-2, name);
|
||||
(int)strlen(name), name);
|
||||
cp->cp_api = *api;
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
done:
|
||||
|
|
@ -246,80 +283,6 @@ done:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Load a dynamic plugin object and call its init-function
|
||||
* Note 'file' may be destructively modified
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] file Which plugin to load
|
||||
* @param[in] dlflags See man(3) dlopen
|
||||
* @note OBSOLETE
|
||||
*/
|
||||
plghndl_t
|
||||
plugin_load(clicon_handle h,
|
||||
char *file,
|
||||
int dlflags)
|
||||
{
|
||||
char *error;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfn;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen(file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
goto done;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfn = dlsym(handle, PLUGIN_INIT)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to find plugin_init when loading clixon plugin %s", file);
|
||||
goto err;
|
||||
}
|
||||
if ((error = (char*)dlerror()) != NULL) {
|
||||
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
|
||||
goto done;
|
||||
}
|
||||
if (initfn(h) != 0) {
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||
clicon_err(OE_DB, 0, "Unknown error: %s: plugin_init does not make clicon_err call on error",
|
||||
file);
|
||||
goto err;
|
||||
}
|
||||
done:
|
||||
return handle;
|
||||
err:
|
||||
if (handle)
|
||||
dlclose(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Unload a plugin
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] handle Clicon handle
|
||||
* @note OBSOLETE
|
||||
*/
|
||||
int
|
||||
plugin_unload(clicon_handle h,
|
||||
plghndl_t *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfn;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfn = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfn(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Call plugin_start in all plugins
|
||||
* @param[in] h Clicon handle
|
||||
*/
|
||||
|
|
@ -425,7 +388,7 @@ clixon_plugin_auth(clicon_handle h,
|
|||
*/
|
||||
typedef struct {
|
||||
qelem_t rc_qelem; /* List header */
|
||||
clicon_rpc_cb rc_callback; /* RPC Callback */
|
||||
clicon_rpc_cb rc_callback; /* RPC Callback */
|
||||
void *rc_arg; /* Application specific argument to cb */
|
||||
char *rc_tag; /* Xml/json tag when matched, callback called */
|
||||
} rpc_callback_t;
|
||||
|
|
|
|||
|
|
@ -237,14 +237,6 @@ module clixon-config {
|
|||
"Set if all configuration changes are committed automatically
|
||||
on every edit change. Explicit commit commands unnecessary";
|
||||
}
|
||||
leaf CLICON_MASTER_PLUGIN {
|
||||
type string;
|
||||
default "master";
|
||||
description
|
||||
"Name of master plugin (cli, netconf, restconf and backend).
|
||||
Master plugin has special callbacks for frontends.
|
||||
See clicon user manual for more info. (Obsolete?)";
|
||||
}
|
||||
leaf CLICON_XMLDB_DIR {
|
||||
type string;
|
||||
mandatory true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue