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
|
* CLI parse hook
|
||||||
* CLICON_FIND_PLUGIN
|
* CLICON_FIND_PLUGIN
|
||||||
* clicon_valcb()
|
* clicon_valcb()
|
||||||
* backend system plugins (CLIXON_BACKEND_SYSDIR)
|
* CLIXON_BACKEND_SYSDIR
|
||||||
|
* CLIXON_CLI_SYSDIR
|
||||||
* CLICON_MASTER_PLUGIN config variable
|
* CLICON_MASTER_PLUGIN config variable
|
||||||
* Example of migrating a backend plugin module:
|
* Example of migrating a backend plugin module:
|
||||||
* Add all callbacks in a clixon_plugin_api struct
|
* 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
|
Clixon provides a core system and can be used as-is using available
|
||||||
Yang specifications. However, an application very quickly needs to
|
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
|
plugins for cli and backend. Extensions for netconf and restconf
|
||||||
are also available.
|
are also available.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ clixon_plugin_reset(clicon_handle h,
|
||||||
plgreset_t *resetfn; /* Plugin auth */
|
plgreset_t *resetfn; /* Plugin auth */
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
|
|
||||||
while ((cp = plugin_each(cp)) != NULL) {
|
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||||
if ((resetfn = cp->cp_api.ca_reset) == NULL)
|
if ((resetfn = cp->cp_api.ca_reset) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((retval = resetfn(h, db)) < 0) {
|
if ((retval = resetfn(h, db)) < 0) {
|
||||||
|
|
@ -150,7 +150,7 @@ clixon_plugin_statedata(clicon_handle h,
|
||||||
clicon_err(OE_CFG, ENOENT, "XML tree expected");
|
clicon_err(OE_CFG, ENOENT, "XML tree expected");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
while ((cp = plugin_each(cp)) != NULL) {
|
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||||
if ((fn = cp->cp_api.ca_statedata) == NULL)
|
if ((fn = cp->cp_api.ca_statedata) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((x = xml_new("config", NULL, NULL)) == NULL)
|
if ((x = xml_new("config", NULL, NULL)) == NULL)
|
||||||
|
|
@ -246,7 +246,7 @@ plugin_transaction_begin(clicon_handle h,
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
trans_cb_t *fn;
|
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)
|
if ((fn = cp->cp_api.ca_trans_begin) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
|
|
@ -274,7 +274,7 @@ plugin_transaction_validate(clicon_handle h,
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
trans_cb_t *fn;
|
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)
|
if ((fn = cp->cp_api.ca_trans_validate) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
|
|
@ -303,7 +303,7 @@ plugin_transaction_complete(clicon_handle h,
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
trans_cb_t *fn;
|
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)
|
if ((fn = cp->cp_api.ca_trans_complete) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
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_scvec = td->td_tcvec;
|
||||||
tr.td_tcvec = td->td_scvec;
|
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)
|
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
|
|
@ -379,7 +379,7 @@ plugin_transaction_commit(clicon_handle h,
|
||||||
trans_cb_t *fn;
|
trans_cb_t *fn;
|
||||||
int i=0;
|
int i=0;
|
||||||
|
|
||||||
while ((cp = plugin_each(cp)) != NULL) {
|
while ((cp = plugin_each(h, cp)) != NULL) {
|
||||||
i++;
|
i++;
|
||||||
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -409,7 +409,7 @@ plugin_transaction_end(clicon_handle h,
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
trans_cb_t *fn;
|
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)
|
if ((fn = cp->cp_api.ca_trans_end) == NULL)
|
||||||
continue;
|
continue;
|
||||||
if ((retval = fn(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
|
|
@ -436,7 +436,7 @@ plugin_transaction_abort(clicon_handle h,
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
trans_cb_t *fn;
|
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)
|
if ((fn = cp->cp_api.ca_trans_abort) == NULL)
|
||||||
continue;
|
continue;
|
||||||
fn(h, (transaction_data)td); /* dont abort on error */
|
fn(h, (transaction_data)td); /* dont abort on error */
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,6 @@ CLIXON_MINOR = @CLIXON_VERSION_MINOR@
|
||||||
|
|
||||||
# Use this clixon lib for linking
|
# Use this clixon lib for linking
|
||||||
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
|
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
|
# 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.
|
# 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
|
.SUFFIXES: .c .o
|
||||||
|
|
||||||
.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
|
# Just link test programs
|
||||||
test.c :
|
test.c :
|
||||||
|
|
|
||||||
|
|
@ -172,12 +172,22 @@ cli_parse_file(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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);
|
cligen_handle ch = cligen(h);
|
||||||
|
|
||||||
/* This assume first arg of fn can be treated as void* */
|
/* 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 *
|
char *
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* 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,
|
int cli_parse_file(clicon_handle h,
|
||||||
FILE *f,
|
FILE *f,
|
||||||
|
|
@ -47,7 +47,9 @@ int cli_parse_file(clicon_handle h,
|
||||||
parse_tree *pt,
|
parse_tree *pt,
|
||||||
cvec *globals);
|
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);
|
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);
|
int cli_logsyntax_set(clicon_handle h, int status);
|
||||||
|
|
||||||
/* Internal functions for handling cli groups */
|
/* Internal functions for handling cli groups */
|
||||||
|
|
||||||
cli_syntax_t *cli_syntax(clicon_handle h);
|
cli_syntax_t *cli_syntax(clicon_handle h);
|
||||||
|
|
||||||
int cli_syntax_set(clicon_handle h, cli_syntax_t *stx);
|
int cli_syntax_set(clicon_handle h, cli_syntax_t *stx);
|
||||||
|
|
||||||
#endif /* _CLI_HANDLE_H_ */
|
#endif /* _CLI_HANDLE_H_ */
|
||||||
|
|
|
||||||
|
|
@ -460,7 +460,7 @@ main(int argc, char **argv)
|
||||||
*/
|
*/
|
||||||
tmp = *(argv-1);
|
tmp = *(argv-1);
|
||||||
*(argv-1) = argv0;
|
*(argv-1) = argv0;
|
||||||
cli_plugin_start(h, argc+1, argv-1);
|
clixon_plugin_start(h, argc+1, argv-1);
|
||||||
*(argv-1) = tmp;
|
*(argv-1) = tmp;
|
||||||
|
|
||||||
cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING"));
|
cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING"));
|
||||||
|
|
|
||||||
|
|
@ -66,13 +66,6 @@
|
||||||
#include "cli_plugin.h"
|
#include "cli_plugin.h"
|
||||||
#include "cli_handle.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
|
* CLI PLUGIN INTERFACE, INTERNAL SECTION
|
||||||
|
|
@ -82,7 +75,9 @@
|
||||||
/*! Find syntax mode named 'mode'. Create if specified
|
/*! Find syntax mode named 'mode'. Create if specified
|
||||||
*/
|
*/
|
||||||
static cli_syntaxmode_t *
|
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;
|
cli_syntaxmode_t *m;
|
||||||
|
|
||||||
|
|
@ -111,40 +106,26 @@ syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
|
||||||
return m;
|
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
|
/*! Generate parse tree for syntax mode
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] m Syntax mode struct
|
||||||
*/
|
*/
|
||||||
static int
|
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);
|
cligen_tree_add(cli_cligen(h), m->csm_name, m->csm_pt);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Append syntax
|
/*! Append syntax
|
||||||
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
syntax_append(clicon_handle h,
|
syntax_append(clicon_handle h,
|
||||||
cli_syntax_t *stx,
|
cli_syntax_t *stx,
|
||||||
const char *name,
|
const char *name,
|
||||||
parse_tree pt)
|
parse_tree pt)
|
||||||
{
|
{
|
||||||
cli_syntaxmode_t *m;
|
cli_syntaxmode_t *m;
|
||||||
|
|
||||||
|
|
@ -157,27 +138,18 @@ syntax_append(clicon_handle h,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Unload all plugins in a group
|
/*! Remove all cligen syntax modes
|
||||||
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
cli_syntax_unload(clicon_handle h)
|
cli_syntax_unload(clicon_handle h)
|
||||||
{
|
{
|
||||||
cli_syntax_t *stx = cli_syntax(h);
|
cli_syntax_t *stx = cli_syntax(h);
|
||||||
struct cli_plugin *p;
|
|
||||||
cli_syntaxmode_t *m;
|
cli_syntaxmode_t *m;
|
||||||
|
|
||||||
if (stx == NULL)
|
if (stx == NULL)
|
||||||
return 0;
|
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) {
|
while (stx->stx_nmodes > 0) {
|
||||||
m = stx->stx_modes;
|
m = stx->stx_modes;
|
||||||
DELQ(m, stx->stx_modes, cli_syntaxmode_t *);
|
DELQ(m, stx->stx_modes, cli_syntaxmode_t *);
|
||||||
|
|
@ -237,34 +209,6 @@ clixon_str2fn(char *name,
|
||||||
return NULL;
|
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
|
/*! Append to syntax mode from file
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] filename Name of file where syntax is specified (in syntax-group dir)
|
* @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;
|
char **vec = NULL;
|
||||||
int i, nvec;
|
int i, nvec;
|
||||||
char *plgnam;
|
char *plgnam;
|
||||||
struct cli_plugin *p;
|
clixon_plugin *cp;
|
||||||
|
|
||||||
if (dir)
|
if (dir)
|
||||||
snprintf(filepath, MAXPATHLEN-1, "%s/%s", dir, filename);
|
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");
|
mode = cvec_find_str(cvv, "CLICON_MODE");
|
||||||
|
|
||||||
if (plgnam != NULL) { /* Find plugin for callback resolving */
|
if (plgnam != NULL) { /* Find plugin for callback resolving */
|
||||||
if ((p = plugin_find_cli (cli_syntax(h), plgnam)) != NULL)
|
if ((cp = plugin_find(h, plgnam)) != NULL)
|
||||||
handle = p->cp_handle;
|
handle = cp->cp_handle;
|
||||||
if (handle == NULL){
|
if (handle == NULL){
|
||||||
clicon_err(OE_PLUGIN, 0, "CLICON_PLUGIN set to '%s' in %s but plugin %s.so not found in %s\n",
|
clicon_err(OE_PLUGIN, 0, "CLICON_PLUGIN set to '%s' in %s but plugin %s.so not found in %s\n",
|
||||||
plgnam, filename, plgnam,
|
plgnam, filename, plgnam,
|
||||||
|
|
@ -325,7 +269,6 @@ cli_load_syntax(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve callback names to function pointers. */
|
/* Resolve callback names to function pointers. */
|
||||||
if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){
|
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)",
|
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)
|
if ((vec = clicon_strsep(mode, ":", &nvec)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
for (i = 0; i < nvec; i++) {
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
if (prompt)
|
if (prompt)
|
||||||
|
|
@ -361,74 +307,7 @@ done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Load plugins within a directory
|
/*! Load a syntax group. Includes both CLI plugin and CLIgen spec syntax files.
|
||||||
*/
|
|
||||||
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.
|
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -443,6 +322,9 @@ cli_syntax_load (clicon_handle h)
|
||||||
struct dirent *dp = NULL;
|
struct dirent *dp = NULL;
|
||||||
cli_syntax_t *stx;
|
cli_syntax_t *stx;
|
||||||
cli_syntaxmode_t *m;
|
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?? */
|
/* Syntax already loaded. XXX should we re-load?? */
|
||||||
if ((stx = cli_syntax(h)) != NULL)
|
if ((stx = cli_syntax(h)) != NULL)
|
||||||
|
|
@ -456,59 +338,62 @@ cli_syntax_load (clicon_handle h)
|
||||||
/* Allocate plugin group object */
|
/* Allocate plugin group object */
|
||||||
if ((stx = malloc(sizeof(*stx))) == NULL) {
|
if ((stx = malloc(sizeof(*stx))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "malloc");
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
goto quit;
|
goto done;
|
||||||
}
|
}
|
||||||
memset (stx, 0, sizeof (*stx)); /* Zero out all */
|
memset (stx, 0, sizeof (*stx)); /* Zero out all */
|
||||||
|
|
||||||
cli_syntax_set(h, stx);
|
cli_syntax_set(h, stx);
|
||||||
|
|
||||||
/* First load CLICON system plugins. CLIXON_CLI_SYSDIR is defined
|
/* Load cli plugins */
|
||||||
in Makefile*/
|
if (plugin_dir &&
|
||||||
if (cli_plugin_load_dir(h, CLIXON_CLI_SYSDIR, stx) < 0)
|
clixon_plugins_load(h, CLIXON_PLUGIN_INIT, plugin_dir)< 0)
|
||||||
goto quit;
|
goto done;
|
||||||
|
|
||||||
/* Then load application plugins */
|
|
||||||
if (plugin_dir && cli_plugin_load_dir(h, plugin_dir, stx) < 0)
|
|
||||||
goto quit;
|
|
||||||
|
|
||||||
if (clispec_file){
|
if (clispec_file){
|
||||||
if (cli_load_syntax(h, clispec_file, NULL) < 0)
|
if (cli_load_syntax(h, clispec_file, NULL) < 0)
|
||||||
goto quit;
|
goto done;
|
||||||
}
|
}
|
||||||
if (clispec_dir){
|
if (clispec_dir){
|
||||||
/* load syntaxfiles */
|
/* load syntaxfiles */
|
||||||
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0)
|
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0)
|
||||||
goto quit;
|
goto done;
|
||||||
/* Load the rest */
|
/* Load the rest */
|
||||||
for (i = 0; i < ndp; i++) {
|
for (i = 0; i < ndp; i++) {
|
||||||
clicon_debug(1, "DEBUG: Loading syntax '%.*s'",
|
clicon_debug(1, "DEBUG: Loading syntax '%.*s'",
|
||||||
(int)strlen(dp[i].d_name)-4, dp[i].d_name);
|
(int)strlen(dp[i].d_name)-4, dp[i].d_name);
|
||||||
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
|
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
|
||||||
goto quit;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Did we successfully load any syntax modes? */
|
/* Did we successfully load any syntax modes? */
|
||||||
if (stx->stx_nmodes <= 0) {
|
if (stx->stx_nmodes <= 0) {
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto quit;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Parse syntax tree for all modes */
|
/* Parse syntax tree for all modes */
|
||||||
m = stx->stx_modes;
|
m = stx->stx_modes;
|
||||||
do {
|
do {
|
||||||
if (gen_parse_tree(h, m) != 0)
|
if (gen_parse_tree(h, m) != 0)
|
||||||
goto quit;
|
goto done;
|
||||||
m = NEXTQ(cli_syntaxmode_t *, m);
|
m = NEXTQ(cli_syntaxmode_t *, m);
|
||||||
} while (m && m != stx->stx_modes);
|
} while (m && m != stx->stx_modes);
|
||||||
|
|
||||||
|
/* Set susp and interrupt callbacks into CLIgen */
|
||||||
/* Set callbacks into CLIgen */
|
cp = NULL;
|
||||||
cli_susp_hook(h, cli_syntax(h)->stx_susp_hook);
|
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 */
|
/* All good. We can now proudly return a new group */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
||||||
quit:
|
done:
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
|
clixon_plugin_exit(h);
|
||||||
cli_syntax_unload(h);
|
cli_syntax_unload(h);
|
||||||
cli_syntax_set(h, NULL);
|
cli_syntax_set(h, NULL);
|
||||||
}
|
}
|
||||||
|
|
@ -517,34 +402,15 @@ quit:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Call plugin_start() in all plugins
|
/*! Remove syntax modes and remove syntax
|
||||||
*/
|
* @param[in] h Clicon handle
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cli_plugin_finish(clicon_handle h)
|
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_unload(h);
|
||||||
cli_syntax_set(h, NULL);
|
cli_syntax_set(h, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -553,6 +419,7 @@ cli_plugin_finish(clicon_handle h)
|
||||||
/*! Help function to print a meaningful error string.
|
/*! Help function to print a meaningful error string.
|
||||||
* Sometimes the libraries specify an error string, if so print that.
|
* Sometimes the libraries specify an error string, if so print that.
|
||||||
* Otherwise just print 'command error'.
|
* Otherwise just print 'command error'.
|
||||||
|
* @param[in] f File handler to write error to.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cli_handler_err(FILE *f)
|
cli_handler_err(FILE *f)
|
||||||
|
|
@ -704,22 +571,30 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Read command from CLIgen's cliread() using current syntax mode.
|
/*! Read command from CLIgen's cliread() using current syntax mode.
|
||||||
|
* @param[in] h Clicon handle
|
||||||
* @retval string char* buffer containing CLIgen command
|
* @retval string char* buffer containing CLIgen command
|
||||||
* @retval NULL Fatal error
|
* @retval NULL Fatal error
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
clicon_cliread(clicon_handle h)
|
clicon_cliread(clicon_handle h)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
char *pfmt = NULL;
|
char *pfmt = NULL;
|
||||||
cli_syntaxmode_t *mode;
|
cli_syntaxmode_t *mode;
|
||||||
cli_syntax_t *stx;
|
cli_syntax_t *stx;
|
||||||
|
cli_prompthook_t *fn;
|
||||||
|
clixon_plugin *cp;
|
||||||
|
|
||||||
stx = cli_syntax(h);
|
stx = cli_syntax(h);
|
||||||
mode = stx->stx_active_mode;
|
mode = stx->stx_active_mode;
|
||||||
|
/* Get prompt from plugin callback? */
|
||||||
if (stx->stx_prompt_hook)
|
cp = NULL;
|
||||||
pfmt = stx->stx_prompt_hook(h, mode->csm_name);
|
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))
|
if (clicon_quiet_mode(h))
|
||||||
cli_prompt_set(h, "");
|
cli_prompt_set(h, "");
|
||||||
else
|
else
|
||||||
|
|
@ -732,6 +607,7 @@ clicon_cliread(clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Initialize plugin code (not the plugins themselves)
|
/*! Initialize plugin code (not the plugins themselves)
|
||||||
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cli_plugin_init(clicon_handle h)
|
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
|
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;
|
cli_syntaxmode_t *mode;
|
||||||
|
|
||||||
|
|
@ -761,8 +638,8 @@ cli_set_syntax_mode(clicon_handle h, const char *name)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Get syntax mode name
|
||||||
* Get syntax mode name
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
cli_syntax_mode(clicon_handle h)
|
cli_syntax_mode(clicon_handle h)
|
||||||
|
|
@ -774,14 +651,15 @@ cli_syntax_mode(clicon_handle h)
|
||||||
return csm->csm_name;
|
return csm->csm_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Callback from cli_set_prompt(). Set prompt format for syntax mode
|
||||||
* Callback from cli_set_prompt(). Set prompt format for syntax mode
|
* @param[in] h Clicon handle
|
||||||
* Arguments:
|
* @param[in] name Name of syntax mode
|
||||||
* name : Name of syntax mode
|
* @param[in] prompt Prompt format
|
||||||
* prompt : Prompt format
|
|
||||||
*/
|
*/
|
||||||
int
|
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;
|
cli_syntaxmode_t *m;
|
||||||
|
|
||||||
|
|
@ -793,9 +671,14 @@ cli_set_prompt(clicon_handle h, const char *name, const char *prompt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Format 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
|
static int
|
||||||
prompt_fmt (char *prompt, size_t plen, char *fmt, ...)
|
prompt_fmt (char *prompt,
|
||||||
|
size_t plen,
|
||||||
|
char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *s = fmt;
|
char *s = fmt;
|
||||||
|
|
@ -849,6 +732,7 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Return a formatted prompt string
|
/*! Return a formatted prompt string
|
||||||
|
* @param[in] fmt Format string
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
cli_prompt(char *fmt)
|
cli_prompt(char *fmt)
|
||||||
|
|
|
||||||
|
|
@ -43,51 +43,26 @@
|
||||||
/* clicon generic callback pointer */
|
/* clicon generic callback pointer */
|
||||||
typedef void (clicon_callback_t)(clicon_handle h);
|
typedef void (clicon_callback_t)(clicon_handle h);
|
||||||
|
|
||||||
/* clicon_set value callback */
|
/* List of syntax modes */
|
||||||
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 *);
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
qelem_t csm_qelem; /* List header */
|
qelem_t csm_qelem; /* List header */
|
||||||
char csm_name[256]; /* Syntax mode name */
|
char csm_name[256]; /* Syntax mode name */
|
||||||
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
||||||
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
||||||
parse_tree csm_pt; /* CLIgen parse tree */
|
parse_tree csm_pt; /* CLIgen parse tree */
|
||||||
|
|
||||||
} cli_syntaxmode_t;
|
} cli_syntaxmode_t;
|
||||||
|
|
||||||
/* A plugin list object */
|
/* Plugin group object. Just a single object, not list. part of cli_handle */
|
||||||
struct cli_plugin {
|
|
||||||
qelem_t cp_qelem; /* List header */
|
|
||||||
char cp_name[256]; /* Plugin name */
|
|
||||||
void *cp_handle; /* Dynamic object handle */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Plugin group object */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int stx_nplugins; /* Number of plugins */
|
|
||||||
struct cli_plugin *stx_plugins; /* List of plugins */
|
|
||||||
int stx_nmodes; /* Number of syntax modes */
|
int stx_nmodes; /* Number of syntax modes */
|
||||||
cli_syntaxmode_t *stx_active_mode; /* Current active syntax mode */
|
cli_syntaxmode_t *stx_active_mode; /* Current active syntax mode */
|
||||||
cli_syntaxmode_t *stx_modes; /* List of syntax modes */
|
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;
|
} cli_syntax_t;
|
||||||
|
|
||||||
|
|
||||||
void *clixon_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_init(clicon_handle h);
|
int cli_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
int clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr);
|
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?
|
## 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).
|
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?
|
## I want to program. How do I extend the example?
|
||||||
|
See [../apps/example]
|
||||||
- example.xml - Change the configuration file
|
- example.xml - Change the configuration file
|
||||||
- The yang specifications - This is the central part. It changes the XML, database and the config cli.
|
- 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
|
- 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_netconf.c - Netconf plugin
|
||||||
- example_restconf.c - Add restconf authentication, etc.
|
- 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?
|
## How do I write a commit function?
|
||||||
In the example, you write a commit function in example_backend.c.
|
In the example, you write a commit function in example_backend.c.
|
||||||
Every time a commit is made, transaction_commit() is called in the
|
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:
|
Example:
|
||||||
```
|
```
|
||||||
int
|
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
|
static int
|
||||||
fib_route(clicon_handle h, /* Clicon handle */
|
fib_route(clicon_handle h, /* Clicon handle */
|
||||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
struct client_entry *ce, /* Client session */
|
|
||||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
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>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -313,13 +333,14 @@ You can specify an authentication callback for restconf as follows:
|
||||||
```
|
```
|
||||||
int
|
int
|
||||||
plugin_credentials(clicon_handle h,
|
plugin_credentials(clicon_handle h,
|
||||||
FCGX_Request *r,
|
void *arg)
|
||||||
char **username)
|
{
|
||||||
|
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
|
To authenticate, the callback needs to return the value 1 and supply a username.
|
||||||
request is unauthorized. the function mallocs a username and returns
|
|
||||||
it.
|
|
||||||
|
|
||||||
See (../apps/example/example_restconf.c) plugin_credentials() for
|
See [../apps/example/example_restconf.c] plugin_credentials() for
|
||||||
an example of HTTP basic auth.
|
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
|
## Operation data
|
||||||
|
|
||||||
Clixon implements Yang RPC operations by an extension mechanism. The
|
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
|
static int
|
||||||
fib_route(clicon_handle h,
|
fib_route(clicon_handle h,
|
||||||
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
cxobj *xe, /* Request: <rpc><xn></rpc> */
|
||||||
struct client_entry *ce, /* Client session */
|
|
||||||
cbuf *cbret, /* Reply eg <rpc-reply>... */
|
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>");
|
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int
|
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 */
|
plugin_start, /* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
NULL, /* auth */
|
NULL, /* auth */
|
||||||
|
NULL, /* cli prompt */
|
||||||
|
NULL, /* cli suspend */
|
||||||
|
NULL, /* cli interrupt */
|
||||||
plugin_reset, /* reset */
|
plugin_reset, /* reset */
|
||||||
plugin_statedata, /* statedata */
|
plugin_statedata, /* statedata */
|
||||||
NULL, /* trans begin */
|
NULL, /* trans begin */
|
||||||
|
|
@ -293,6 +296,7 @@ clixon_plugin_init(clicon_handle h)
|
||||||
"empty"/* Xml tag when callback is made */
|
"empty"/* Xml tag when callback is made */
|
||||||
) < 0)
|
) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Return plugin API */
|
||||||
return &api;
|
return &api;
|
||||||
done:
|
done:
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
||||||
|
|
@ -52,19 +52,6 @@
|
||||||
#include <clixon/clixon.h>
|
#include <clixon/clixon.h>
|
||||||
#include <clixon/clixon_cli.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 */
|
/*! Example cli function */
|
||||||
int
|
int
|
||||||
|
|
@ -125,3 +112,29 @@ fib_route_rpc(clicon_handle h,
|
||||||
return retval;
|
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
|
/*! Local example restconf rpc callback
|
||||||
*/
|
*/
|
||||||
int restconf_client_rpc(clicon_handle h,
|
int
|
||||||
cxobj *xn,
|
restconf_client_rpc(clicon_handle h,
|
||||||
cbuf *cbret,
|
cxobj *xn,
|
||||||
void *arg,
|
cbuf *cbret,
|
||||||
void *regarg)
|
void *arg,
|
||||||
|
void *regarg)
|
||||||
{
|
{
|
||||||
// FCGX_Request *r = (FCGX_Request *)arg;
|
// FCGX_Request *r = (FCGX_Request *)arg;
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,6 @@
|
||||||
*/
|
*/
|
||||||
/* default group membership to access config unix socket */
|
/* default group membership to access config unix socket */
|
||||||
#define CLICON_SOCK_GROUP "clicon"
|
#define CLICON_SOCK_GROUP "clicon"
|
||||||
/* Default name of master plugin */
|
|
||||||
#define CLICON_MASTER_PLUGIN "master"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Types
|
* Types
|
||||||
|
|
@ -137,9 +135,6 @@ static inline char *clicon_sock_group(clicon_handle h){
|
||||||
static inline char *clicon_backend_pidfile(clicon_handle h){
|
static inline char *clicon_backend_pidfile(clicon_handle h){
|
||||||
return clicon_option_str(h, "CLICON_BACKEND_PIDFILE");
|
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){
|
static inline char *clicon_xmldb_dir(clicon_handle h){
|
||||||
return clicon_option_str(h, "CLICON_XMLDB_DIR");
|
return clicon_option_str(h, "CLICON_XMLDB_DIR");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,16 @@
|
||||||
#ifndef _CLIXON_PLUGIN_H_
|
#ifndef _CLIXON_PLUGIN_H_
|
||||||
#define _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
|
* Types
|
||||||
*/
|
*/
|
||||||
/* The dynamicically loadable plugin object handle */
|
/* Dynamicically loadable plugin object handle. @see return value of dlopen(3) */
|
||||||
typedef void *plghndl_t;
|
typedef void *plghndl_t;
|
||||||
|
|
||||||
/* Registered RPC callback function */
|
/* Registered RPC callback function */
|
||||||
|
|
@ -62,22 +68,13 @@ typedef int (*clicon_rpc_cb)(
|
||||||
* Backend see config_plugin.c
|
* 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.
|
/* Called when backend started with cmd-line arguments from daemon call.
|
||||||
* @see plgstart_t
|
* @see plgstart_t
|
||||||
*/
|
*/
|
||||||
#define PLUGIN_START "plugin_start"
|
|
||||||
typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */
|
typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */
|
||||||
|
|
||||||
/* Called just before plugin unloaded.
|
/* Called just before plugin unloaded.
|
||||||
*/
|
*/
|
||||||
#define PLUGIN_EXIT "plugin_exit"
|
|
||||||
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
||||||
|
|
||||||
/*! Called by restconf to check credentials and return username
|
/*! Called by restconf to check credentials and return username
|
||||||
|
|
@ -99,6 +96,14 @@ typedef void *transaction_data;
|
||||||
/* Transaction callbacks */
|
/* Transaction callbacks */
|
||||||
typedef int (trans_cb_t)(clicon_handle h, transaction_data td);
|
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
|
/* plugin init struct for the api
|
||||||
* Note: Implicit init function
|
* Note: Implicit init function
|
||||||
*/
|
*/
|
||||||
|
|
@ -106,14 +111,20 @@ struct clixon_plugin_api;
|
||||||
typedef struct clixon_plugin_api* (plginit2_t)(clicon_handle); /* Clixon plugin Init */
|
typedef struct clixon_plugin_api* (plginit2_t)(clicon_handle); /* Clixon plugin Init */
|
||||||
|
|
||||||
struct clixon_plugin_api{
|
struct clixon_plugin_api{
|
||||||
|
/*--- Common fields. ---*/
|
||||||
char ca_name[PATH_MAX]; /* Name of plugin (given by plugin) */
|
char ca_name[PATH_MAX]; /* Name of plugin (given by plugin) */
|
||||||
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
||||||
plgstart_t *ca_start; /* Plugin start */
|
plgstart_t *ca_start; /* Plugin start */
|
||||||
plgexit_t *ca_exit; /* Plugin exit */
|
plgexit_t *ca_exit; /* Plugin exit */
|
||||||
plgauth_t *ca_auth; /* Auth credentials */
|
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) */
|
plgstatedata_t *ca_statedata; /* Get state data from plugin (backend only) */
|
||||||
trans_cb_t *ca_trans_begin; /* Transaction start */
|
trans_cb_t *ca_trans_begin; /* Transaction start */
|
||||||
trans_cb_t *ca_trans_validate; /* Transaction validation */
|
trans_cb_t *ca_trans_validate; /* Transaction validation */
|
||||||
|
|
@ -124,46 +135,34 @@ struct clixon_plugin_api{
|
||||||
};
|
};
|
||||||
typedef struct clixon_plugin_api 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
|
/* Internal plugin structure with dlopen() handle and plugin_api
|
||||||
*/
|
*/
|
||||||
struct clixon_plugin{
|
struct clixon_plugin{
|
||||||
char cp_name[PATH_MAX]; /* Plugin filename. Note api ca_name is given by plugin itself */
|
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) */
|
plghndl_t cp_handle; /* Handle to plugin using dlopen(3) */
|
||||||
struct clixon_plugin_api cp_api;
|
clixon_plugin_api cp_api;
|
||||||
};
|
};
|
||||||
typedef struct clixon_plugin clixon_plugin;
|
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
|
* 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);
|
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_start(clicon_handle h, int argc, char **argv);
|
||||||
|
|
||||||
int clixon_plugin_exit(clicon_handle h);
|
int clixon_plugin_exit(clicon_handle h);
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,10 @@
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_plugin.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 clixon_plugin *_clixon_plugins = NULL; /* List of plugins (of client) */
|
||||||
static int _clixon_nplugins = 0; /* Number of plugins */
|
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
|
* @note Never manipulate the plugin during operation or using the
|
||||||
* same object recursively
|
* same object recursively
|
||||||
*
|
*
|
||||||
|
* @param[in] h Clicon handle
|
||||||
* @param[in] plugin previous plugin, or NULL on init
|
* @param[in] plugin previous plugin, or NULL on init
|
||||||
* @code
|
* @code
|
||||||
* clicon_plugin *cp = NULL;
|
* clicon_plugin *cp = NULL;
|
||||||
* while ((cp = plugin_each(cp)) != NULL) {
|
* while ((cp = plugin_each(h, cp)) != NULL) {
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note Not optimized, alwasy iterates from the start of the list
|
* @note Not optimized, alwasy iterates from the start of the list
|
||||||
*/
|
*/
|
||||||
clixon_plugin *
|
clixon_plugin *
|
||||||
plugin_each(clixon_plugin *cpprev)
|
plugin_each(clicon_handle h,
|
||||||
|
clixon_plugin *cpprev)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
clixon_plugin *cp;
|
clixon_plugin *cp;
|
||||||
|
|
@ -105,17 +110,19 @@ plugin_each(clixon_plugin *cpprev)
|
||||||
* @note Never manipulate the plugin during operation or using the
|
* @note Never manipulate the plugin during operation or using the
|
||||||
* same object recursively
|
* same object recursively
|
||||||
*
|
*
|
||||||
|
* @param[in] h Clicon handle
|
||||||
* @param[in] plugin previous plugin, or NULL on init
|
* @param[in] plugin previous plugin, or NULL on init
|
||||||
* @code
|
* @code
|
||||||
* clicon_plugin *cp = NULL;
|
* clicon_plugin *cp = NULL;
|
||||||
* while ((cp = plugin_each_revert(cp, nr)) != NULL) {
|
* while ((cp = plugin_each_revert(h, cp, nr)) != NULL) {
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
* @endcode
|
* @endcode
|
||||||
* @note Not optimized, alwasy iterates from the start of the list
|
* @note Not optimized, alwasy iterates from the start of the list
|
||||||
*/
|
*/
|
||||||
clixon_plugin *
|
clixon_plugin *
|
||||||
plugin_each_revert(clixon_plugin *cpprev,
|
plugin_each_revert(clicon_handle h,
|
||||||
|
clixon_plugin *cpprev,
|
||||||
int nr)
|
int nr)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -137,6 +144,27 @@ plugin_each_revert(clixon_plugin *cpprev,
|
||||||
return cpnext;
|
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
|
/*! Load a dynamic plugin object and call its init-function
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] file Which plugin to load
|
* @param[in] file Which plugin to load
|
||||||
|
|
@ -158,6 +186,7 @@ plugin_load_one(clicon_handle h,
|
||||||
clixon_plugin_api *api = NULL;
|
clixon_plugin_api *api = NULL;
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
char *name;
|
char *name;
|
||||||
|
char *p;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
dlerror(); /* Clear any existing error */
|
dlerror(); /* Clear any existing error */
|
||||||
|
|
@ -187,10 +216,18 @@ plugin_load_one(clicon_handle h,
|
||||||
clicon_err(OE_UNIX, errno, "malloc");
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
memset(cp, 0, sizeof(struct clixon_plugin));
|
||||||
cp->cp_handle = handle;
|
cp->cp_handle = handle;
|
||||||
|
/* Extract string after last '/' in filename, if any */
|
||||||
name = strrchr(file, '/') ? strrchr(file, '/')+1 : file;
|
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",
|
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
|
||||||
(int)strlen(name)-2, name);
|
(int)strlen(name), name);
|
||||||
cp->cp_api = *api;
|
cp->cp_api = *api;
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
done:
|
done:
|
||||||
|
|
@ -246,80 +283,6 @@ done:
|
||||||
return retval;
|
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
|
/*! Call plugin_start in all plugins
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
|
|
@ -425,7 +388,7 @@ clixon_plugin_auth(clicon_handle h,
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
qelem_t rc_qelem; /* List header */
|
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 */
|
void *rc_arg; /* Application specific argument to cb */
|
||||||
char *rc_tag; /* Xml/json tag when matched, callback called */
|
char *rc_tag; /* Xml/json tag when matched, callback called */
|
||||||
} rpc_callback_t;
|
} rpc_callback_t;
|
||||||
|
|
|
||||||
|
|
@ -237,14 +237,6 @@ module clixon-config {
|
||||||
"Set if all configuration changes are committed automatically
|
"Set if all configuration changes are committed automatically
|
||||||
on every edit change. Explicit commit commands unnecessary";
|
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 {
|
leaf CLICON_XMLDB_DIR {
|
||||||
type string;
|
type string;
|
||||||
mandatory true;
|
mandatory true;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue