Restructure and more generic plugin API for backend
This commit is contained in:
parent
7fbd95d491
commit
b9a54f07f3
19 changed files with 570 additions and 629 deletions
57
CHANGELOG.md
57
CHANGELOG.md
|
|
@ -6,28 +6,55 @@
|
||||||
### Major changes:
|
### Major changes:
|
||||||
* Restructure and more generic plugin API (cli,backend,restconf,netconf) as preparation for authorization RFC8341
|
* Restructure and more generic plugin API (cli,backend,restconf,netconf) as preparation for authorization RFC8341
|
||||||
* New design with single `clixon_plugin_init()` returning an api struct with function pointers, see example below. This means that there are no hardcoded plugin functions, except `clixon_plugin_init()`.
|
* New design with single `clixon_plugin_init()` returning an api struct with function pointers, see example below. This means that there are no hardcoded plugin functions, except `clixon_plugin_init()`.
|
||||||
|
* Master plugins have been removed. Plugins are loaded alphabetically. You can ensure plugin load order by prefixing them with an ordering number, for example.
|
||||||
* Moved specific plugin functions from apps/ to generic functions in lib/
|
* Moved specific plugin functions from apps/ to generic functions in lib/
|
||||||
* Added authentication plugin callback (ca_auth)
|
* Added authentication plugin callback (ca_auth)
|
||||||
* Added clicon_username_get() / clicon_username_set()
|
* Added clicon_username_get() / clicon_username_set()
|
||||||
* Authorization
|
|
||||||
* Example extended with authorization
|
|
||||||
* Test added with http basic authorization (test/test_auth.sh)
|
|
||||||
* Documentation in FAQ.md
|
|
||||||
* Removed some obscure plugin code that seem not to be used (please report if needed!)
|
* Removed some obscure plugin code that seem not to be used (please report if needed!)
|
||||||
* Client-local netconf plugins netconf_plugin_callbacks()
|
* Client-local netconf plugins netconf_plugin_callbacks()
|
||||||
* CLI parse hook
|
* CLI parse hook
|
||||||
* CLICON_FIND_PLUGIN
|
* CLICON_FIND_PLUGIN
|
||||||
* clicon_valcb()
|
* clicon_valcb()
|
||||||
* Example of new plugin module:
|
* backend system plugins (CLIXON_BACKEND_SYSDIR)
|
||||||
|
* CLICON_MASTER_PLUGIN config variable
|
||||||
|
* Example of migrating a backend plugin module:
|
||||||
|
* Add all callbacks in a clixon_plugin_api struct
|
||||||
|
* Rename plugin_init() -> clixon_plugin_init() and return api as function value
|
||||||
```
|
```
|
||||||
static const struct clixon_plugin_api api = {
|
/* This is old style with hardcoded function names (eg plugin_start) */
|
||||||
"example",
|
int plugin_start(clicon_handle h, int argc, char **argv)
|
||||||
clixon_plugin_init,
|
{
|
||||||
...
|
return 0;
|
||||||
}
|
}
|
||||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h){
|
int
|
||||||
return (void*)&api;
|
plugin_init(clicon_handle h)
|
||||||
}
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is new style with all function names in api struct */
|
||||||
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
|
static clixon_plugin_api api = {
|
||||||
|
"example", /* name */
|
||||||
|
clixon_plugin_init,
|
||||||
|
plugin_start,
|
||||||
|
plugin_exit,
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return &api; /* Return NULL on error */
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* Added Clixon Restconf library
|
* Added Clixon Restconf library
|
||||||
|
|
@ -42,8 +69,12 @@
|
||||||
|
|
||||||
### Minor changes:
|
### Minor changes:
|
||||||
|
|
||||||
|
* Authentication
|
||||||
|
* Example extended with http basic authentication for restconf
|
||||||
|
* Documentation in FAQ.md
|
||||||
* Updated ietf-netconf-acm to ietf-netconf-acm@2018-02-14.yang from RFC 8341
|
* Updated ietf-netconf-acm to ietf-netconf-acm@2018-02-14.yang from RFC 8341
|
||||||
* The Clixon example has changed name from "routing" to "example" affecting all config files, plugins, tests, etc.
|
* The Clixon example has changed name from "routing" to "example" affecting all config files, plugins, tests, etc.
|
||||||
|
* Secondary backend plugin added
|
||||||
* Removed username to rpc calls (added below)
|
* Removed username to rpc calls (added below)
|
||||||
* README.md extended with new yang, netconf, restconf, datastore, and auth sections.
|
* README.md extended with new yang, netconf, restconf, datastore, and auth sections.
|
||||||
* The key-value datastore is no longer supported. Use the default text datastore.
|
* The key-value datastore is no longer supported. Use the default text datastore.
|
||||||
|
|
|
||||||
|
|
@ -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 funxtions. Clixon is extended by (most commonly) writing
|
specialize functions. Clixon is extended by (most commonly) 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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,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_BACKEND_SYSDIR = $(libdir)/clixon/plugins/backend
|
|
||||||
|
|
||||||
# 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.
|
||||||
|
|
@ -119,7 +117,7 @@ install-include: clixon_backend.h clixon_backend_handle.h clixon_backend_transac
|
||||||
.SUFFIXES: .c .o
|
.SUFFIXES: .c .o
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" -DCLIXON_BACKEND_SYSDIR=\"$(CLIXON_BACKEND_SYSDIR)\" $(CPPFLAGS) $(CFLAGS) -c $<
|
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $<
|
||||||
|
|
||||||
# Just link test programs
|
# Just link test programs
|
||||||
test.c :
|
test.c :
|
||||||
|
|
|
||||||
|
|
@ -289,7 +289,7 @@ from_client_get(clicon_handle h,
|
||||||
/* Get state data from plugins as defined by plugin_statedata(), if any */
|
/* Get state data from plugins as defined by plugin_statedata(), if any */
|
||||||
assert(xret);
|
assert(xret);
|
||||||
clicon_err_reset();
|
clicon_err_reset();
|
||||||
if ((ret = backend_statedata_call(h, selector, xret)) < 0)
|
if ((ret = clixon_plugin_statedata(h, selector, xret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* OK */
|
if (ret == 0){ /* OK */
|
||||||
cprintf(cbret, "<rpc-reply>");
|
cprintf(cbret, "<rpc-reply>");
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ backend_terminate(clicon_handle h)
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
plugin_finish(h);
|
clixon_plugin_exit(h);
|
||||||
/* Delete all backend plugin RPC callbacks */
|
/* Delete all backend plugin RPC callbacks */
|
||||||
backend_rpc_cb_delete_all();
|
backend_rpc_cb_delete_all();
|
||||||
if (pidfile)
|
if (pidfile)
|
||||||
|
|
@ -254,7 +254,7 @@ plugin_start_useroptions(clicon_handle h,
|
||||||
|
|
||||||
tmp = *(argv-1);
|
tmp = *(argv-1);
|
||||||
*(argv-1) = argv0;
|
*(argv-1) = argv0;
|
||||||
if (plugin_start_argv(h, argc+1, argv-1) < 0)
|
if (clixon_plugin_start(h, argc+1, argv-1) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
*(argv-1) = tmp;
|
*(argv-1) = tmp;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -370,7 +370,7 @@ startup_mode_running(clicon_handle h,
|
||||||
if (db_reset(h, "tmp") < 0)
|
if (db_reset(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Application may define extra xml in its reset function*/
|
/* Application may define extra xml in its reset function*/
|
||||||
if (plugin_reset_state(h, "tmp") < 0)
|
if (clixon_plugin_reset(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Get application extra xml from file */
|
/* Get application extra xml from file */
|
||||||
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
||||||
|
|
@ -443,7 +443,7 @@ startup_mode_startup(clicon_handle h,
|
||||||
if (db_reset(h, "tmp") < 0)
|
if (db_reset(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Application may define extra xml in its reset function*/
|
/* Application may define extra xml in its reset function*/
|
||||||
if (plugin_reset_state(h, "tmp") < 0)
|
if (clixon_plugin_reset(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Get application extra xml from file */
|
/* Get application extra xml from file */
|
||||||
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
if (load_extraxml(h, extraxml_file, "tmp") < 0)
|
||||||
|
|
|
||||||
|
|
@ -64,58 +64,6 @@
|
||||||
#include "backend_plugin.h"
|
#include "backend_plugin.h"
|
||||||
#include "backend_commit.h"
|
#include "backend_commit.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* Types
|
|
||||||
*/
|
|
||||||
/* Following are specific to backend. For common see clicon_plugin.h
|
|
||||||
* @note the following should match the prototypes in clixon_backend.h
|
|
||||||
*/
|
|
||||||
#define PLUGIN_RESET "plugin_reset"
|
|
||||||
|
|
||||||
|
|
||||||
/*! Plugin callback, if defined called to get state data from plugin
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
|
||||||
* @param[in] xtop XML tree, <config/> on entry.
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* @see xmldb_get
|
|
||||||
*/
|
|
||||||
#define PLUGIN_STATEDATA "plugin_statedata"
|
|
||||||
|
|
||||||
|
|
||||||
#define PLUGIN_TRANS_BEGIN "transaction_begin"
|
|
||||||
#define PLUGIN_TRANS_VALIDATE "transaction_validate"
|
|
||||||
#define PLUGIN_TRANS_COMPLETE "transaction_complete"
|
|
||||||
#define PLUGIN_TRANS_COMMIT "transaction_commit"
|
|
||||||
#define PLUGIN_TRANS_END "transaction_end"
|
|
||||||
#define PLUGIN_TRANS_ABORT "transaction_abort"
|
|
||||||
|
|
||||||
|
|
||||||
/* Backend (config) plugins */
|
|
||||||
struct plugin {
|
|
||||||
char p_name[PATH_MAX]; /* Plugin name */
|
|
||||||
void *p_handle; /* Dynamic object handle */
|
|
||||||
plginit_t *p_init; /* Init */
|
|
||||||
plgstart_t *p_start; /* Start */
|
|
||||||
plgexit_t *p_exit; /* Exit */
|
|
||||||
plgreset_t *p_reset; /* Reset state */
|
|
||||||
plgstatedata_t *p_statedata; /* State-data callback */
|
|
||||||
trans_cb_t *p_trans_begin; /* Transaction start */
|
|
||||||
trans_cb_t *p_trans_validate; /* Transaction validation */
|
|
||||||
trans_cb_t *p_trans_complete; /* Transaction validation complete */
|
|
||||||
trans_cb_t *p_trans_commit; /* Transaction commit */
|
|
||||||
trans_cb_t *p_trans_end; /* Transaction completed */
|
|
||||||
trans_cb_t *p_trans_abort; /* Transaction aborted */
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Local variables
|
|
||||||
*/
|
|
||||||
static int _nplugins = 0;
|
|
||||||
static struct plugin *_plugins = NULL;
|
|
||||||
|
|
||||||
/*! Initialize plugin code (not the plugins themselves)
|
/*! Initialize plugin code (not the plugins themselves)
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
|
@ -127,263 +75,6 @@ backend_plugin_init(clicon_handle h)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Unload a plugin
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] plg Plugin structure
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
backend_plugin_unload(clicon_handle h,
|
|
||||||
struct plugin *plg)
|
|
||||||
{
|
|
||||||
int retval=-1;
|
|
||||||
char *error;
|
|
||||||
|
|
||||||
/* Call exit function is it exists */
|
|
||||||
if (plg->p_exit)
|
|
||||||
plg->p_exit(h);
|
|
||||||
|
|
||||||
dlerror(); /* Clear any existing error */
|
|
||||||
if (dlclose(plg->p_handle) != 0) {
|
|
||||||
error = (char*)dlerror();
|
|
||||||
clicon_err(OE_UNIX, 0, "dlclose: %s", error?error:"Unknown error");
|
|
||||||
goto done;
|
|
||||||
/* Just report */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
clicon_debug(1, "Plugin '%s' unloaded.", plg->p_name);
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Load a dynamic plugin and call its init-function
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] file The plugin (.so) to load
|
|
||||||
* @param[in] dlflags Arguments to dlopen(3)
|
|
||||||
* @retval plugin Plugin struct
|
|
||||||
* @retval NULL Error
|
|
||||||
*/
|
|
||||||
static struct plugin *
|
|
||||||
backend_plugin_load (clicon_handle h,
|
|
||||||
char *file,
|
|
||||||
int dlflags)
|
|
||||||
{
|
|
||||||
void *handle;
|
|
||||||
char *name;
|
|
||||||
struct plugin *new = NULL;
|
|
||||||
|
|
||||||
if ((handle = plugin_load(h, file, dlflags)) == NULL)
|
|
||||||
goto done;
|
|
||||||
if ((new = malloc(sizeof(*new))) == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "dhunk: %s", strerror(errno));
|
|
||||||
dlclose(handle);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(new, 0, sizeof(*new));
|
|
||||||
name = strrchr(file, '/') ? strrchr(file, '/')+1 : file;
|
|
||||||
clicon_debug(2, "Loading plugin '%s'.", name);
|
|
||||||
snprintf(new->p_name, sizeof(new->p_name), "%*s",
|
|
||||||
(int)strlen(name)-2, name);
|
|
||||||
new->p_handle = handle;
|
|
||||||
if ((new->p_start = dlsym(handle, PLUGIN_START)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_START);
|
|
||||||
if ((new->p_exit = dlsym(handle, PLUGIN_EXIT)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_EXIT);
|
|
||||||
if ((new->p_reset = dlsym(handle, PLUGIN_RESET)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_RESET);
|
|
||||||
if ((new->p_statedata = dlsym(handle, PLUGIN_STATEDATA)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_STATEDATA);
|
|
||||||
if ((new->p_trans_begin = dlsym(handle, PLUGIN_TRANS_BEGIN)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_BEGIN);
|
|
||||||
if ((new->p_trans_validate = dlsym(handle, PLUGIN_TRANS_VALIDATE)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_VALIDATE);
|
|
||||||
if ((new->p_trans_complete = dlsym(handle, PLUGIN_TRANS_COMPLETE)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_COMPLETE);
|
|
||||||
if ((new->p_trans_commit = dlsym(handle, PLUGIN_TRANS_COMMIT)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_COMMIT);
|
|
||||||
if ((new->p_trans_end = dlsym(handle, PLUGIN_TRANS_END)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_END);
|
|
||||||
if ((new->p_trans_abort = dlsym(handle, PLUGIN_TRANS_ABORT)) != NULL)
|
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_ABORT);
|
|
||||||
clicon_debug(2, "Plugin '%s' loaded.\n", name);
|
|
||||||
done:
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Request plugins to reset system state
|
|
||||||
* The system 'state' should be the same as the contents of running_db
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] dbname Name of database
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
plugin_reset_state(clicon_handle h,
|
|
||||||
const char *db)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct plugin *p;
|
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++) {
|
|
||||||
p = &_plugins[i];
|
|
||||||
if (p->p_reset) {
|
|
||||||
clicon_debug(1, "Calling plugin_reset() for %s\n",
|
|
||||||
p->p_name);
|
|
||||||
if (((p->p_reset)(h, db)) < 0) {
|
|
||||||
clicon_err(OE_FATAL, 0, "plugin_reset() failed for %s\n",
|
|
||||||
p->p_name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Call plugin_start in all plugins
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] argc Command-line arguments
|
|
||||||
* @param[in] argv Command-line arguments
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
plugin_start_argv(clicon_handle h,
|
|
||||||
int argc,
|
|
||||||
char **argv)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
struct plugin *p;
|
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++) {
|
|
||||||
p = &_plugins[i];
|
|
||||||
if (p->p_start) {
|
|
||||||
optind = 0;
|
|
||||||
if (((p->p_start)(h, argc, argv)) < 0) {
|
|
||||||
clicon_err(OE_FATAL, 0, "plugin_start() failed for %s\n",
|
|
||||||
p->p_name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Append plugin to list
|
|
||||||
* @param[in] p Plugin
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
plugin_append(struct plugin *p)
|
|
||||||
{
|
|
||||||
struct plugin *new;
|
|
||||||
|
|
||||||
if ((new = realloc(_plugins, (_nplugins+1) * sizeof (*p))) == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "realloc");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset (&new[_nplugins], 0, sizeof(new[_nplugins]));
|
|
||||||
memcpy (&new[_nplugins], p, sizeof(new[_nplugins]));
|
|
||||||
_plugins = new;
|
|
||||||
_nplugins++;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Load backend plugins found in a directory
|
|
||||||
* The plugins must have the '.so' suffix
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @param[in] dir Backend plugin directory
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
backend_plugin_load_dir(clicon_handle h,
|
|
||||||
const char *dir)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
int i;
|
|
||||||
int np = 0;
|
|
||||||
int ndp;
|
|
||||||
struct stat st;
|
|
||||||
char filename[MAXPATHLEN];
|
|
||||||
struct dirent *dp = NULL;
|
|
||||||
struct plugin *new;
|
|
||||||
struct plugin *p = NULL;
|
|
||||||
char master[MAXPATHLEN];
|
|
||||||
char *master_plugin;
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
/* Allocate plugin group object */
|
|
||||||
/* Get plugin objects names from plugin directory */
|
|
||||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0)
|
|
||||||
goto quit;
|
|
||||||
|
|
||||||
/* reset num plugins */
|
|
||||||
np = 0;
|
|
||||||
|
|
||||||
/* Master plugin must be loaded first if it exists. */
|
|
||||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, master);
|
|
||||||
if (stat(filename, &st) == 0) {
|
|
||||||
clicon_debug(1, "Loading master plugin '%.*s' ...",
|
|
||||||
(int)strlen(filename), filename);
|
|
||||||
|
|
||||||
new = backend_plugin_load(h, filename, RTLD_NOW|RTLD_GLOBAL);
|
|
||||||
if (new == NULL)
|
|
||||||
goto quit;
|
|
||||||
if (plugin_append(new) < 0)
|
|
||||||
goto quit;
|
|
||||||
free(new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now load the rest. Note plugins is the global variable */
|
|
||||||
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, "Loading plugin '%.*s' ...", (int)strlen(filename), filename);
|
|
||||||
new = backend_plugin_load(h, filename, RTLD_NOW);
|
|
||||||
if (new == NULL)
|
|
||||||
goto quit;
|
|
||||||
/* Append to 'plugins' */
|
|
||||||
if (plugin_append(new) < 0)
|
|
||||||
goto quit;
|
|
||||||
free(new);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All good. */
|
|
||||||
retval = 0;
|
|
||||||
|
|
||||||
quit:
|
|
||||||
if (retval != 0) {
|
|
||||||
/* XXX p is always NULL */
|
|
||||||
if (_plugins) {
|
|
||||||
while (--np >= 0){
|
|
||||||
if ((p = &_plugins[np]) == NULL)
|
|
||||||
continue;
|
|
||||||
backend_plugin_unload(h, p);
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
free(_plugins);
|
|
||||||
_plugins=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dp)
|
|
||||||
free(dp);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Load a plugin group.
|
/*! Load a plugin group.
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
|
@ -394,42 +85,115 @@ plugin_initiate(clicon_handle h)
|
||||||
{
|
{
|
||||||
char *dir;
|
char *dir;
|
||||||
|
|
||||||
/* First load CLICON system plugins */
|
/* Load application plugins */
|
||||||
if (backend_plugin_load_dir(h, CLIXON_BACKEND_SYSDIR) < 0)
|
if ((dir = clicon_backend_dir(h)) == NULL)
|
||||||
return -1;
|
return 0;
|
||||||
|
return clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir);
|
||||||
/* Then load application plugins */
|
|
||||||
dir = clicon_backend_dir(h);
|
|
||||||
/* If backend directory, load the backend plugisn */
|
|
||||||
if (dir && backend_plugin_load_dir(h, dir) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Unload and deallocate all backend plugins
|
/*! Request plugins to reset system state
|
||||||
|
* The system 'state' should be the same as the contents of running_db
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] db Name of database
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
plugin_finish(clicon_handle h)
|
clixon_plugin_reset(clicon_handle h,
|
||||||
|
char *db)
|
||||||
{
|
{
|
||||||
int i;
|
clixon_plugin *cp = NULL;
|
||||||
struct plugin *p;
|
plgreset_t *resetfn; /* Plugin auth */
|
||||||
|
int retval = 1;
|
||||||
for (i = 0; i < _nplugins; i++) {
|
|
||||||
p = &_plugins[i];
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
backend_plugin_unload(h, p);
|
if ((resetfn = cp->cp_api.ca_reset) == NULL)
|
||||||
|
continue;
|
||||||
|
if ((retval = resetfn(h, db)) < 0) {
|
||||||
|
clicon_debug(1, "plugin_start() failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (_plugins){
|
return retval;
|
||||||
free(_plugins);
|
|
||||||
_plugins = NULL;
|
|
||||||
}
|
|
||||||
_nplugins = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Go through all backend statedata callbacks and collect state data
|
||||||
|
* This is internal system call, plugin is invoked (does not call) this function
|
||||||
|
* Backend plugins can register
|
||||||
|
* @param[in] h clicon handle
|
||||||
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
|
* @param[in,out] xml XML tree.
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval 1 Statedata callback failed
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clixon_plugin_statedata(clicon_handle h,
|
||||||
|
char *xpath,
|
||||||
|
cxobj *xtop)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int i;
|
||||||
|
cxobj *x = NULL;
|
||||||
|
yang_spec *yspec;
|
||||||
|
cxobj **xvec = NULL;
|
||||||
|
size_t xlen;
|
||||||
|
clixon_plugin *cp = NULL;
|
||||||
|
plgstatedata_t *fn; /* Plugin statedata fn */
|
||||||
|
|
||||||
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (xtop==NULL){
|
||||||
|
clicon_err(OE_CFG, ENOENT, "XML tree expected");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
|
if ((fn = cp->cp_api.ca_statedata) == NULL)
|
||||||
|
continue;
|
||||||
|
if ((x = xml_new("config", NULL, NULL)) == NULL)
|
||||||
|
goto done;
|
||||||
|
if (fn(h, xpath, x) < 0){
|
||||||
|
retval = 1;
|
||||||
|
goto done; /* Dont quit here on user callbacks */
|
||||||
|
}
|
||||||
|
if (xml_merge(xtop, x, yspec) < 0)
|
||||||
|
goto done;
|
||||||
|
if (x){
|
||||||
|
xml_free(x);
|
||||||
|
x = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Code complex to filter out anything that is outside of xpath */
|
||||||
|
if (xpath_vec(xtop, xpath?xpath:"/", &xvec, &xlen) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* If vectors are specified then mark the nodes found and
|
||||||
|
* then filter out everything else,
|
||||||
|
* otherwise return complete tree.
|
||||||
|
*/
|
||||||
|
if (xvec != NULL){
|
||||||
|
for (i=0; i<xlen; i++)
|
||||||
|
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
||||||
|
}
|
||||||
|
/* Remove everything that is not marked */
|
||||||
|
if (!xml_flag(xtop, XML_FLAG_MARK))
|
||||||
|
if (xml_tree_prune_flagged_sub(xtop, XML_FLAG_MARK, 1, NULL) < 0)
|
||||||
|
goto done;
|
||||||
|
/* reset flag */
|
||||||
|
if (xml_apply(xtop, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (x)
|
||||||
|
xml_free(x);
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Create and initialize transaction */
|
/*! Create and initialize transaction */
|
||||||
transaction_data_t *
|
transaction_data_t *
|
||||||
transaction_new(void)
|
transaction_new(void)
|
||||||
|
|
@ -478,21 +242,20 @@ int
|
||||||
plugin_transaction_begin(clicon_handle h,
|
plugin_transaction_begin(clicon_handle h,
|
||||||
transaction_data_t *td)
|
transaction_data_t *td)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
struct plugin *p;
|
clixon_plugin *cp = NULL;
|
||||||
|
trans_cb_t *fn;
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++) {
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
p = &_plugins[i];
|
if ((fn = cp->cp_api.ca_trans_begin) == NULL)
|
||||||
if (p->p_trans_begin)
|
continue;
|
||||||
if ((retval = (p->p_trans_begin)(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||||
clicon_log(LOG_NOTICE, "%s: Plugin '%s' %s callback does not make clicon_err call on error",
|
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_begin callback does not make clicon_err call on error",
|
||||||
__FUNCTION__, p->p_name, PLUGIN_TRANS_BEGIN);
|
__FUNCTION__, cp->cp_name);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
@ -508,20 +271,18 @@ plugin_transaction_validate(clicon_handle h,
|
||||||
transaction_data_t *td)
|
transaction_data_t *td)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
int i;
|
clixon_plugin *cp = NULL;
|
||||||
|
trans_cb_t *fn;
|
||||||
|
|
||||||
struct plugin *p;
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
|
if ((fn = cp->cp_api.ca_trans_validate) == NULL)
|
||||||
for (i = 0; i < _nplugins; i++){
|
continue;
|
||||||
p = &_plugins[i];
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
if (p->p_trans_validate)
|
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||||
if ((retval = (p->p_trans_validate)(h, (transaction_data)td)) < 0){
|
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_validate callback does not make clicon_err call on error",
|
||||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
__FUNCTION__, cp->cp_name);
|
||||||
clicon_log(LOG_NOTICE, "%s: Plugin '%s' %s callback does not make clicon_err call on error",
|
break;
|
||||||
__FUNCTION__, p->p_name, PLUGIN_TRANS_VALIDATE);
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
@ -538,20 +299,20 @@ int
|
||||||
plugin_transaction_complete(clicon_handle h,
|
plugin_transaction_complete(clicon_handle h,
|
||||||
transaction_data_t *td)
|
transaction_data_t *td)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
struct plugin *p;
|
clixon_plugin *cp = NULL;
|
||||||
|
trans_cb_t *fn;
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++){
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
p = &_plugins[i];
|
if ((fn = cp->cp_api.ca_trans_complete) == NULL)
|
||||||
if (p->p_trans_complete)
|
continue;
|
||||||
if ((retval = (p->p_trans_complete)(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||||
clicon_log(LOG_NOTICE, "%s: Plugin '%s' %s callback does not make clicon_err call on error",
|
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_complete callback does not make clicon_err call on error",
|
||||||
__FUNCTION__, p->p_name, PLUGIN_TRANS_COMPLETE);
|
__FUNCTION__, cp->cp_name);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
@ -572,9 +333,9 @@ plugin_transaction_revert(clicon_handle h,
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
transaction_data_t tr; /* revert transaction */
|
transaction_data_t tr; /* revert transaction */
|
||||||
int i;
|
clixon_plugin *cp = NULL;
|
||||||
struct plugin *p;
|
trans_cb_t *fn;
|
||||||
|
|
||||||
/* Create a new reversed transaction from the original where src and target
|
/* Create a new reversed transaction from the original where src and target
|
||||||
are swapped */
|
are swapped */
|
||||||
memcpy(&tr, td, sizeof(tr));
|
memcpy(&tr, td, sizeof(tr));
|
||||||
|
|
@ -588,14 +349,14 @@ 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;
|
||||||
|
|
||||||
for (i = nr-1; i>=0; i--){
|
while ((cp = plugin_each_revert(cp, nr)) != NULL) {
|
||||||
p = &_plugins[i];
|
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
||||||
if (p->p_trans_commit)
|
continue;
|
||||||
if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
clicon_log(LOG_NOTICE, "Plugin '%s' %s revert callback failed",
|
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit revert callback failed",
|
||||||
p->p_name, PLUGIN_TRANS_COMMIT);
|
__FUNCTION__, cp->cp_name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retval; /* ignore errors */
|
return retval; /* ignore errors */
|
||||||
}
|
}
|
||||||
|
|
@ -614,20 +375,22 @@ plugin_transaction_commit(clicon_handle h,
|
||||||
transaction_data_t *td)
|
transaction_data_t *td)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
int i;
|
clixon_plugin *cp = NULL;
|
||||||
struct plugin *p;
|
trans_cb_t *fn;
|
||||||
|
int i=0;
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++){
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
p = &_plugins[i];
|
i++;
|
||||||
if (p->p_trans_commit)
|
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
|
||||||
if ((retval = (p->p_trans_commit)(h, (transaction_data)td)) < 0){
|
continue;
|
||||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
clicon_log(LOG_NOTICE, "%s: Plugin '%s' %s callback does not make clicon_err call on error",
|
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||||
__FUNCTION__, p->p_name, PLUGIN_TRANS_COMMIT);
|
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit callback does not make clicon_err call on error",
|
||||||
/* Make an effort to revert transaction */
|
__FUNCTION__, cp->cp_name);
|
||||||
plugin_transaction_revert(h, td, i);
|
/* Make an effort to revert transaction */
|
||||||
break;
|
plugin_transaction_revert(h, td, i-1);
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
@ -643,19 +406,18 @@ plugin_transaction_end(clicon_handle h,
|
||||||
transaction_data_t *td)
|
transaction_data_t *td)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
int i;
|
clixon_plugin *cp = NULL;
|
||||||
struct plugin *p;
|
trans_cb_t *fn;
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++) {
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
p = &_plugins[i];
|
if ((fn = cp->cp_api.ca_trans_end) == NULL)
|
||||||
if (p->p_trans_end)
|
continue;
|
||||||
if ((retval = (p->p_trans_end)(h, (transaction_data)td)) < 0){
|
if ((retval = fn(h, (transaction_data)td)) < 0){
|
||||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||||
clicon_log(LOG_NOTICE, "%s: Plugin '%s' %s callback does not make clicon_err call on error",
|
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_end callback does not make clicon_err call on error",
|
||||||
__FUNCTION__, p->p_name, PLUGIN_TRANS_END);
|
__FUNCTION__, cp->cp_name);
|
||||||
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
@ -671,94 +433,14 @@ plugin_transaction_abort(clicon_handle h,
|
||||||
transaction_data_t *td)
|
transaction_data_t *td)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
int i;
|
clixon_plugin *cp = NULL;
|
||||||
struct plugin *p;
|
trans_cb_t *fn;
|
||||||
|
|
||||||
for (i = 0; i < _nplugins; i++) {
|
while ((cp = plugin_each(cp)) != NULL) {
|
||||||
p = &_plugins[i];
|
if ((fn = cp->cp_api.ca_trans_abort) == NULL)
|
||||||
if (p->p_trans_abort)
|
continue;
|
||||||
(p->p_trans_abort)(h, (transaction_data)td); /* dont abort on error */
|
fn(h, (transaction_data)td); /* dont abort on error */
|
||||||
}
|
}
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------
|
|
||||||
* Backend state data callbacks
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! Go through all backend statedata callbacks and collect state data
|
|
||||||
* This is internal system call, plugin is invoked (does not call) this function
|
|
||||||
* Backend plugins can register
|
|
||||||
* @param[in] h clicon handle
|
|
||||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
|
||||||
* @param[in,out] xml XML tree.
|
|
||||||
* @retval -1 Error
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval 1 Statedata callback failed
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
backend_statedata_call(clicon_handle h,
|
|
||||||
char *xpath,
|
|
||||||
cxobj *xtop)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
struct plugin *p;
|
|
||||||
int i;
|
|
||||||
cxobj *x = NULL;
|
|
||||||
yang_spec *yspec;
|
|
||||||
cxobj **xvec = NULL;
|
|
||||||
size_t xlen;
|
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (xtop==NULL){
|
|
||||||
clicon_err(OE_CFG, ENOENT, "XML tree expected");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
for (i = 0; i < _nplugins; i++) {
|
|
||||||
p = &_plugins[i];
|
|
||||||
if (p->p_statedata) {
|
|
||||||
if ((x = xml_new("config", NULL, NULL)) == NULL)
|
|
||||||
goto done;
|
|
||||||
if ((p->p_statedata)(h, xpath, x) < 0){
|
|
||||||
retval = 1;
|
|
||||||
goto done; /* Dont quit here on user callbacks */
|
|
||||||
}
|
|
||||||
if (xml_merge(xtop, x, yspec) < 0)
|
|
||||||
goto done;
|
|
||||||
if (x){
|
|
||||||
xml_free(x);
|
|
||||||
x = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Code complex to filter out anything that is outside of xpath */
|
|
||||||
if (xpath_vec(xtop, xpath?xpath:"/", &xvec, &xlen) < 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* If vectors are specified then mark the nodes found and
|
|
||||||
* then filter out everything else,
|
|
||||||
* otherwise return complete tree.
|
|
||||||
*/
|
|
||||||
if (xvec != NULL){
|
|
||||||
for (i=0; i<xlen; i++)
|
|
||||||
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
|
||||||
}
|
|
||||||
/* Remove everything that is not marked */
|
|
||||||
if (!xml_flag(xtop, XML_FLAG_MARK))
|
|
||||||
if (xml_tree_prune_flagged_sub(xtop, XML_FLAG_MARK, 1, NULL) < 0)
|
|
||||||
goto done;
|
|
||||||
/* reset flag */
|
|
||||||
if (xml_apply(xtop, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (x)
|
|
||||||
xml_free(x);
|
|
||||||
if (xvec)
|
|
||||||
free(xvec);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,10 @@ typedef struct {
|
||||||
*/
|
*/
|
||||||
int backend_plugin_init(clicon_handle h);
|
int backend_plugin_init(clicon_handle h);
|
||||||
int plugin_initiate(clicon_handle h);
|
int plugin_initiate(clicon_handle h);
|
||||||
int plugin_finish(clicon_handle h);
|
|
||||||
|
|
||||||
int plugin_reset_state(clicon_handle h, const char *db);
|
int clixon_plugin_reset(clicon_handle h, char *db);
|
||||||
int plugin_start_argv(clicon_handle h, int argc, char **argv);
|
|
||||||
|
|
||||||
int backend_statedata_call(clicon_handle h, char *xpath, cxobj *xml);
|
int clixon_plugin_statedata(clicon_handle h, char *xpath, cxobj *xml);
|
||||||
|
|
||||||
transaction_data_t * transaction_new(void);
|
transaction_data_t * transaction_new(void);
|
||||||
int transaction_free(transaction_data_t *);
|
int transaction_free(transaction_data_t *);
|
||||||
|
|
|
||||||
|
|
@ -384,7 +384,7 @@ main(int argc,
|
||||||
|
|
||||||
/* Initialize plugins group */
|
/* Initialize plugins group */
|
||||||
if ((dir = clicon_netconf_dir(h)) != NULL)
|
if ((dir = clicon_netconf_dir(h)) != NULL)
|
||||||
if (clixon_plugins_load(h, dir) < 0)
|
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Call start function is all plugins before we go interactive */
|
/* Call start function is all plugins before we go interactive */
|
||||||
|
|
@ -402,7 +402,7 @@ main(int argc,
|
||||||
if (event_loop() < 0)
|
if (event_loop() < 0)
|
||||||
goto done;
|
goto done;
|
||||||
done:
|
done:
|
||||||
clixon_plugin_unload(h);
|
clixon_plugin_exit(h);
|
||||||
netconf_terminate(h);
|
netconf_terminate(h);
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,7 @@ main(int argc,
|
||||||
|
|
||||||
/* Initialize plugins group */
|
/* Initialize plugins group */
|
||||||
if ((dir = clicon_restconf_dir(h)) != NULL)
|
if ((dir = clicon_restconf_dir(h)) != NULL)
|
||||||
if (clixon_plugins_load(h, clicon_restconf_dir(h)) < 0)
|
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Parse yang database spec file */
|
/* Parse yang database spec file */
|
||||||
|
|
@ -603,7 +603,7 @@ main(int argc,
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clixon_plugin_unload(h);
|
clixon_plugin_exit(h);
|
||||||
restconf_terminate(h);
|
restconf_terminate(h);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,12 @@ CFLAGS = @CFLAGS@ -rdynamic -fPIC
|
||||||
INCLUDES = -I$(includedir) @INCLUDES@
|
INCLUDES = -I$(includedir) @INCLUDES@
|
||||||
|
|
||||||
BE_PLUGIN = $(APPNAME)_backend.so
|
BE_PLUGIN = $(APPNAME)_backend.so
|
||||||
|
BE2_PLUGIN = $(APPNAME)_backend_secondary.so
|
||||||
CLI_PLUGIN = $(APPNAME)_cli.so
|
CLI_PLUGIN = $(APPNAME)_cli.so
|
||||||
NETCONF_PLUGIN = $(APPNAME)_netconf.so
|
NETCONF_PLUGIN = $(APPNAME)_netconf.so
|
||||||
RESTCONF_PLUGIN = $(APPNAME)_restconf.so
|
RESTCONF_PLUGIN = $(APPNAME)_restconf.so
|
||||||
|
|
||||||
PLUGINS = $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN)
|
PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN)
|
||||||
|
|
||||||
all: $(PLUGINS)
|
all: $(PLUGINS)
|
||||||
|
|
||||||
|
|
@ -74,6 +75,12 @@ BE_OBJ = $(BE_SRC:%.c=%.o)
|
||||||
$(BE_PLUGIN): $(BE_OBJ)
|
$(BE_PLUGIN): $(BE_OBJ)
|
||||||
$(CC) -Wall -shared -o $@ -lc $<
|
$(CC) -Wall -shared -o $@ -lc $<
|
||||||
|
|
||||||
|
# Secondary backend plugin
|
||||||
|
BE2_SRC = $(APPNAME)_backend_secondary.c
|
||||||
|
BE2_OBJ = $(BE2_SRC:%.c=%.o)
|
||||||
|
$(BE2_PLUGIN): $(BE2_OBJ)
|
||||||
|
$(CC) -Wall -shared -o $@ -lc $<
|
||||||
|
|
||||||
# CLI frontend plugin
|
# CLI frontend plugin
|
||||||
CLI_SRC = $(APPNAME)_cli.c
|
CLI_SRC = $(APPNAME)_cli.c
|
||||||
CLI_OBJ = $(CLI_SRC:%.c=%.o)
|
CLI_OBJ = $(CLI_SRC:%.c=%.o)
|
||||||
|
|
@ -92,8 +99,8 @@ RESTCONF_OBJ = $(RESTCONF_SRC:%.c=%.o)
|
||||||
$(RESTCONF_PLUGIN): $(RESTCONF_OBJ)
|
$(RESTCONF_PLUGIN): $(RESTCONF_OBJ)
|
||||||
$(CC) -Wall -shared -o $@ -lc $^
|
$(CC) -Wall -shared -o $@ -lc $^
|
||||||
|
|
||||||
SRC = $(BE_SRC) $(CLI_SRC) $(NETCONF_SRC) $(RESTCONF_SRC)
|
SRC = $(BE_SRC) $(BE2_SRC) $(CLI_SRC) $(NETCONF_SRC) $(RESTCONF_SRC)
|
||||||
OBJS = $(BE_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) $(RESTCONF_OBJ)
|
OBJS = $(BE_OBJ) $(BE2_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) $(RESTCONF_OBJ)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PLUGINS) $(OBJS)
|
rm -f $(PLUGINS) $(OBJS)
|
||||||
|
|
@ -103,7 +110,7 @@ distclean: clean
|
||||||
rm -f Makefile *~ .depend
|
rm -f Makefile *~ .depend
|
||||||
(cd docker && $(MAKE) $(MFLAGS) $@)
|
(cd docker && $(MAKE) $(MFLAGS) $@)
|
||||||
|
|
||||||
install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) $(APPNAME).xml
|
install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) $(APPNAME).xml
|
||||||
install -d $(DESTDIR)$(clixon_SYSCONFDIR)
|
install -d $(DESTDIR)$(clixon_SYSCONFDIR)
|
||||||
install $(APPNAME).xml $(DESTDIR)$(clixon_SYSCONFDIR)
|
install $(APPNAME).xml $(DESTDIR)$(clixon_SYSCONFDIR)
|
||||||
install -d $(DESTDIR)$(clixon_DBSPECDIR)/yang
|
install -d $(DESTDIR)$(clixon_DBSPECDIR)/yang
|
||||||
|
|
@ -111,7 +118,7 @@ install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $
|
||||||
install -d $(DESTDIR)$(clixon_LIBDIR)/cli
|
install -d $(DESTDIR)$(clixon_LIBDIR)/cli
|
||||||
install $(CLI_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/cli;
|
install $(CLI_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/cli;
|
||||||
install -d $(DESTDIR)$(clixon_LIBDIR)/backend
|
install -d $(DESTDIR)$(clixon_LIBDIR)/backend
|
||||||
install $(BE_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/backend;
|
install $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/backend;
|
||||||
install -d $(DESTDIR)$(clixon_LIBDIR)/netconf
|
install -d $(DESTDIR)$(clixon_LIBDIR)/netconf
|
||||||
install $(NETCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/netconf;
|
install $(NETCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/netconf;
|
||||||
install -d $(DESTDIR)$(clixon_LIBDIR)/restconf
|
install -d $(DESTDIR)$(clixon_LIBDIR)/restconf
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ routing example. It contains the following files:
|
||||||
* notification,
|
* notification,
|
||||||
* rpc handler
|
* rpc handler
|
||||||
* state-data handler, ie non-config data
|
* state-data handler, ie non-config data
|
||||||
|
* example_backend_secondary.c Secondary backend plugin. Plugins are loaded alphabetically.
|
||||||
* example_restconf.c Restconf callback plugin containing an HTTP basic authentication callback
|
* example_restconf.c Restconf callback plugin containing an HTTP basic authentication callback
|
||||||
* example_netconf.c Netconf callback plugin
|
* example_netconf.c Netconf callback plugin
|
||||||
* Makefile.in Example makefile where plugins are built and installed
|
* Makefile.in Example makefile where plugins are built and installed
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,7 @@ transaction_commit(clicon_handle h,
|
||||||
int i;
|
int i;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
/* Get all added i/fs */
|
/* Get all added i/fs */
|
||||||
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -191,39 +192,6 @@ plugin_statedata(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Plugin initialization. Create rpc callbacks
|
|
||||||
* plugin_init is called as soon as the plugin has been loaded and is
|
|
||||||
* assumed initialize the plugin's internal state if any as well as register
|
|
||||||
* any callbacks, configuration dependencies.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
plugin_init(clicon_handle h)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
if (notification_timer_setup(h) < 0)
|
|
||||||
goto done;
|
|
||||||
/* Register callback for routing rpc calls */
|
|
||||||
if (backend_rpc_cb_register(h, fib_route,
|
|
||||||
NULL,
|
|
||||||
"fib-route"/* Xml tag when callback is made */
|
|
||||||
) < 0)
|
|
||||||
goto done;
|
|
||||||
if (backend_rpc_cb_register(h, route_count,
|
|
||||||
NULL,
|
|
||||||
"route-count"/* Xml tag when callback is made */
|
|
||||||
) < 0)
|
|
||||||
goto done;
|
|
||||||
if (backend_rpc_cb_register(h, empty,
|
|
||||||
NULL,
|
|
||||||
"empty"/* Xml tag when callback is made */
|
|
||||||
) < 0)
|
|
||||||
goto done;
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Plugin state reset. Add xml or set state in backend machine.
|
/*! Plugin state reset. Add xml or set state in backend machine.
|
||||||
* Called in each backend plugin. plugin_reset is called after all plugins
|
* Called in each backend plugin. plugin_reset is called after all plugins
|
||||||
* have been initialized. This give the application a chance to reset
|
* have been initialized. This give the application a chance to reset
|
||||||
|
|
@ -279,3 +247,54 @@ plugin_start(clicon_handle h,
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
|
static clixon_plugin_api api = {
|
||||||
|
"example", /* name */
|
||||||
|
clixon_plugin_init, /* init */
|
||||||
|
plugin_start, /* start */
|
||||||
|
NULL, /* exit */
|
||||||
|
NULL, /* auth */
|
||||||
|
plugin_reset, /* reset */
|
||||||
|
plugin_statedata, /* statedata */
|
||||||
|
NULL, /* trans begin */
|
||||||
|
transaction_validate,/* trans validate */
|
||||||
|
NULL, /* trans complete */
|
||||||
|
transaction_commit, /* trans commit */
|
||||||
|
NULL, /* trans end */
|
||||||
|
NULL /* trans abort */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Backend 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)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s backend", __FUNCTION__);
|
||||||
|
if (notification_timer_setup(h) < 0)
|
||||||
|
goto done;
|
||||||
|
/* Register callback for routing rpc calls */
|
||||||
|
if (backend_rpc_cb_register(h, fib_route,
|
||||||
|
NULL,
|
||||||
|
"fib-route"/* Xml tag when callback is made */
|
||||||
|
) < 0)
|
||||||
|
goto done;
|
||||||
|
if (backend_rpc_cb_register(h, route_count,
|
||||||
|
NULL,
|
||||||
|
"route-count"/* Xml tag when callback is made */
|
||||||
|
) < 0)
|
||||||
|
goto done;
|
||||||
|
if (backend_rpc_cb_register(h, empty,
|
||||||
|
NULL,
|
||||||
|
"empty"/* Xml tag when callback is made */
|
||||||
|
) < 0)
|
||||||
|
goto done;
|
||||||
|
return &api;
|
||||||
|
done:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
102
example/example_backend_secondary.c
Normal file
102
example/example_backend_secondary.c
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
***** 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 *****
|
||||||
|
|
||||||
|
*
|
||||||
|
* IETF yang routing example
|
||||||
|
* Secondary backend for testing more than one backend plugin
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
/* clicon */
|
||||||
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
|
/* Clicon library functions. */
|
||||||
|
#include <clixon/clixon.h>
|
||||||
|
|
||||||
|
/* These include signatures for plugin and transaction callbacks. */
|
||||||
|
#include <clixon/clixon_backend.h>
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
transaction_commit_2(clicon_handle h,
|
||||||
|
transaction_data td)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
plugin_start_2(clicon_handle h,
|
||||||
|
int argc,
|
||||||
|
char **argv)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
|
static clixon_plugin_api api = {
|
||||||
|
"secondary", /* name */
|
||||||
|
clixon_plugin_init, /* init */
|
||||||
|
plugin_start_2, /* start */
|
||||||
|
NULL, /* exit */
|
||||||
|
NULL, /* auth */
|
||||||
|
NULL, /* reset */
|
||||||
|
NULL, /* statedata */
|
||||||
|
NULL, /* trans begin */
|
||||||
|
NULL, /* trans validate */
|
||||||
|
NULL, /* trans complete */
|
||||||
|
transaction_commit_2,/* trans commit */
|
||||||
|
NULL, /* trans end */
|
||||||
|
NULL /* trans abort */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! Backend 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)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s backend secondary", __FUNCTION__);
|
||||||
|
return &api;
|
||||||
|
}
|
||||||
|
|
@ -64,19 +64,23 @@ plugin_exit(clicon_handle h)
|
||||||
|
|
||||||
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static const struct clixon_plugin_api api = {
|
static struct clixon_plugin_api api = {
|
||||||
"example",
|
"example", /* name */
|
||||||
clixon_plugin_init,
|
clixon_plugin_init, /* init */
|
||||||
plugin_start,
|
plugin_start, /* start */
|
||||||
plugin_exit,
|
plugin_exit, /* exit */
|
||||||
NULL
|
NULL /* auth */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Netconf plugin initialization
|
/*! Netconf 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_api *
|
||||||
clixon_plugin_init(clicon_handle h)
|
clixon_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
return (void*)&api;
|
clicon_debug(1, "%s restconf", __FUNCTION__);
|
||||||
|
return &api;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -269,19 +269,22 @@ plugin_credentials(clicon_handle h,
|
||||||
|
|
||||||
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static const struct clixon_plugin_api api = {
|
static clixon_plugin_api api = {
|
||||||
"example",
|
"example", /* name */
|
||||||
clixon_plugin_init,
|
clixon_plugin_init, /* init */
|
||||||
NULL,
|
NULL, /* start */
|
||||||
NULL,
|
NULL, /* exit */
|
||||||
plugin_credentials,
|
plugin_credentials /* auth */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Restconf plugin initialization
|
/*! Restconf 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_api *
|
||||||
clixon_plugin_init(clicon_handle h)
|
clixon_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
clicon_debug(1, "%s restconf", __FUNCTION__);
|
clicon_debug(1, "%s restconf", __FUNCTION__);
|
||||||
return (void*)&api;
|
return &api;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,6 @@ 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
|
||||||
*/
|
*/
|
||||||
#define PLUGIN_CREDENTIALS "plugin_credentials"
|
|
||||||
|
|
||||||
/* Plugin authorization. Set username option (or not)
|
/* Plugin authorization. Set username option (or not)
|
||||||
* @param[in] Clicon handle
|
* @param[in] Clicon handle
|
||||||
|
|
@ -98,21 +97,12 @@ 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{
|
||||||
char ca_name[PATH_MAX]; /* Name of 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 ----------*/
|
||||||
|
|
||||||
struct clixon_backend_api{
|
|
||||||
char ca_name[PATH_MAX]; /* Name of 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 with clixon_netconf_api, clixon_restconf_api,
|
|
||||||
* etc ----------*/
|
|
||||||
plgreset_t *ca_reset; /* Reset system status (backend 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) */
|
||||||
|
|
@ -125,7 +115,6 @@ struct clixon_backend_api{
|
||||||
};
|
};
|
||||||
typedef struct clixon_plugin_api clixon_plugin_api;
|
typedef struct clixon_plugin_api clixon_plugin_api;
|
||||||
|
|
||||||
#define CLIXON_PLUGIN_INIT "clixon_plugin_init" /* Nextgen */
|
|
||||||
/*! Called when plugin loaded. Only mandadory callback. All others optional
|
/*! Called when plugin loaded. Only mandadory callback. All others optional
|
||||||
* @see plginit_t
|
* @see plginit_t
|
||||||
*/
|
*/
|
||||||
|
|
@ -133,24 +122,43 @@ typedef struct clixon_plugin_api clixon_plugin_api;
|
||||||
/* 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 */
|
||||||
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;
|
struct 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
|
||||||
*/
|
*/
|
||||||
int clixon_plugins_load(clicon_handle h, char *dir);
|
clixon_plugin *plugin_each(clixon_plugin *cpprev);
|
||||||
|
clixon_plugin *plugin_each_revert(clixon_plugin *cpprev, int nr);
|
||||||
|
|
||||||
|
int clixon_plugins_load(clicon_handle h, char *function, char *dir);
|
||||||
|
|
||||||
|
/* obsolete */
|
||||||
plghndl_t plugin_load (clicon_handle h, char *file, int dlflags);
|
plghndl_t plugin_load (clicon_handle h, char *file, int dlflags);
|
||||||
|
|
||||||
|
/* obsolete */
|
||||||
int plugin_unload(clicon_handle h, plghndl_t *handle);
|
int plugin_unload(clicon_handle h, plghndl_t *handle);
|
||||||
|
|
||||||
int clixon_plugin_unload(clicon_handle h);
|
|
||||||
|
|
||||||
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_auth(clicon_handle h, void *arg);
|
int clixon_plugin_auth(clicon_handle h, void *arg);
|
||||||
|
|
||||||
#endif /* _CLIXON_PLUGIN_H_ */
|
#endif /* _CLIXON_PLUGIN_H_ */
|
||||||
|
|
|
||||||
|
|
@ -62,8 +62,7 @@
|
||||||
#include "clixon_string.h"
|
#include "clixon_string.h"
|
||||||
#include "clixon_file.h"
|
#include "clixon_file.h"
|
||||||
|
|
||||||
/*
|
/*! qsort "compar" for directory alphabetically sorting, see qsort(3)
|
||||||
* qsort function
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
clicon_file_dirent_sort(const void* arg1,
|
clicon_file_dirent_sort(const void* arg1,
|
||||||
|
|
@ -79,8 +78,7 @@ clicon_file_dirent_sort(const void* arg1,
|
||||||
#endif /* HAVE_STRVERSCMP */
|
#endif /* HAVE_STRVERSCMP */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Return alphabetically sorted files from a directory matching regexp
|
||||||
/*! Return sorted matching files from a directory
|
|
||||||
* @param[in] dir Directory path
|
* @param[in] dir Directory path
|
||||||
* @param[out] ent Entries pointer, will be filled in with dir entries. Free
|
* @param[out] ent Entries pointer, will be filled in with dir entries. Free
|
||||||
* after use
|
* after use
|
||||||
|
|
@ -120,16 +118,13 @@ clicon_file_dirent(const char *dir,
|
||||||
struct dirent *new = NULL;
|
struct dirent *new = NULL;
|
||||||
struct dirent *dvecp = NULL;
|
struct dirent *dvecp = NULL;
|
||||||
|
|
||||||
|
|
||||||
*ent = NULL;
|
*ent = NULL;
|
||||||
nent = 0;
|
nent = 0;
|
||||||
|
|
||||||
if (regexp && (res = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
|
if (regexp && (res = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
|
||||||
regerror(res, &re, errbuf, sizeof(errbuf));
|
regerror(res, &re, errbuf, sizeof(errbuf));
|
||||||
clicon_err(OE_DB, 0, "regcomp: %s", errbuf);
|
clicon_err(OE_DB, 0, "regcomp: %s", errbuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((dirp = opendir (dir)) == NULL) {
|
if ((dirp = opendir (dir)) == NULL) {
|
||||||
if (errno == ENOENT) /* Dir does not exist -> return 0 matches */
|
if (errno == ENOENT) /* Dir does not exist -> return 0 matches */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -137,7 +132,6 @@ clicon_file_dirent(const char *dir,
|
||||||
clicon_err(OE_UNIX, errno, "opendir(%s)", dir);
|
clicon_err(OE_UNIX, errno, "opendir(%s)", dir);
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (res = readdir_r (dirp, &dent, &dresp);
|
for (res = readdir_r (dirp, &dent, &dresp);
|
||||||
dresp;
|
dresp;
|
||||||
res = readdir_r (dirp, &dent, &dresp)) {
|
res = readdir_r (dirp, &dent, &dresp)) {
|
||||||
|
|
@ -162,7 +156,6 @@ clicon_file_dirent(const char *dir,
|
||||||
if ((type & st.st_mode) == 0)
|
if ((type & st.st_mode) == 0)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((tmp = realloc(new, (nent+1)*sizeof(*dvecp))) == NULL) {
|
if ((tmp = realloc(new, (nent+1)*sizeof(*dvecp))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "realloc");
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,83 @@
|
||||||
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 */
|
||||||
|
|
||||||
|
/*! Iterator over clixon plugins
|
||||||
|
*
|
||||||
|
* @note Never manipulate the plugin during operation or using the
|
||||||
|
* same object recursively
|
||||||
|
*
|
||||||
|
* @param[in] plugin previous plugin, or NULL on init
|
||||||
|
* @code
|
||||||
|
* clicon_plugin *cp = NULL;
|
||||||
|
* while ((cp = plugin_each(cp)) != NULL) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* @note Not optimized, alwasy iterates from the start of the list
|
||||||
|
*/
|
||||||
|
clixon_plugin *
|
||||||
|
plugin_each(clixon_plugin *cpprev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
clixon_plugin *cp;
|
||||||
|
clixon_plugin *cpnext = NULL;
|
||||||
|
|
||||||
|
if (cpprev == NULL)
|
||||||
|
cpnext = _clixon_plugins;
|
||||||
|
else{
|
||||||
|
for (i = 0; i < _clixon_nplugins; i++) {
|
||||||
|
cp = &_clixon_plugins[i];
|
||||||
|
if (cp == cpprev)
|
||||||
|
break;
|
||||||
|
cp = NULL;
|
||||||
|
}
|
||||||
|
if (cp && i < _clixon_nplugins-1)
|
||||||
|
cpnext = &_clixon_plugins[i+1];
|
||||||
|
}
|
||||||
|
return cpnext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Reverse iterator over clixon plugins, iterater from nr to 0
|
||||||
|
*
|
||||||
|
* @note Never manipulate the plugin during operation or using the
|
||||||
|
* same object recursively
|
||||||
|
*
|
||||||
|
* @param[in] plugin previous plugin, or NULL on init
|
||||||
|
* @code
|
||||||
|
* clicon_plugin *cp = NULL;
|
||||||
|
* while ((cp = plugin_each_revert(cp, nr)) != NULL) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* @note Not optimized, alwasy iterates from the start of the list
|
||||||
|
*/
|
||||||
|
clixon_plugin *
|
||||||
|
plugin_each_revert(clixon_plugin *cpprev,
|
||||||
|
int nr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
clixon_plugin *cp;
|
||||||
|
clixon_plugin *cpnext = NULL;
|
||||||
|
|
||||||
|
if (cpprev == NULL)
|
||||||
|
cpnext = &_clixon_plugins[nr-1];
|
||||||
|
else{
|
||||||
|
for (i = nr-1; i >= 0; i--) {
|
||||||
|
cp = &_clixon_plugins[i];
|
||||||
|
if (cp == cpprev)
|
||||||
|
break;
|
||||||
|
cp = NULL;
|
||||||
|
}
|
||||||
|
if (cp && i > 0)
|
||||||
|
cpnext = &_clixon_plugins[i-1];
|
||||||
|
}
|
||||||
|
return cpnext;
|
||||||
|
}
|
||||||
|
|
||||||
/*! 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
|
||||||
|
* @param[in] function Which function symbol to load and call
|
||||||
* @param[in] dlflags See man(3) dlopen
|
* @param[in] dlflags See man(3) dlopen
|
||||||
* @retval cp Clixon plugin structure
|
* @retval cp Clixon plugin structure
|
||||||
* @retval NULL Error
|
* @retval NULL Error
|
||||||
|
|
@ -74,7 +148,8 @@ static int _clixon_nplugins = 0; /* Number of plugins */
|
||||||
*/
|
*/
|
||||||
static clixon_plugin *
|
static clixon_plugin *
|
||||||
plugin_load_one(clicon_handle h,
|
plugin_load_one(clicon_handle h,
|
||||||
char *file,
|
char *file,
|
||||||
|
char *function,
|
||||||
int dlflags)
|
int dlflags)
|
||||||
{
|
{
|
||||||
char *error;
|
char *error;
|
||||||
|
|
@ -82,16 +157,17 @@ plugin_load_one(clicon_handle h,
|
||||||
plginit2_t *initfn;
|
plginit2_t *initfn;
|
||||||
clixon_plugin_api *api = NULL;
|
clixon_plugin_api *api = NULL;
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
|
char *name;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
dlerror(); /* Clear any existing error */
|
dlerror(); /* Clear any existing error */
|
||||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
if ((handle = dlopen(file, dlflags)) == NULL) {
|
||||||
error = (char*)dlerror();
|
error = (char*)dlerror();
|
||||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* call plugin_init() if defined */
|
/* call plugin_init() if defined, eg CLIXON_PLUGIN_INIT or CLIXON_BACKEND_INIT */
|
||||||
if ((initfn = dlsym(handle, CLIXON_PLUGIN_INIT)) == NULL){
|
if ((initfn = dlsym(handle, function)) == NULL){
|
||||||
clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file);
|
clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
@ -106,11 +182,15 @@ plugin_load_one(clicon_handle h,
|
||||||
file);
|
file);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if ((cp = (clixon_plugin *)malloc(sizeof(*cp))) == NULL){
|
/* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
|
||||||
|
if ((cp = (clixon_plugin *)malloc(sizeof(struct clixon_plugin))) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "malloc");
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cp->cp_handle = handle;
|
cp->cp_handle = handle;
|
||||||
|
name = strrchr(file, '/') ? strrchr(file, '/')+1 : file;
|
||||||
|
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
|
||||||
|
(int)strlen(name)-2, name);
|
||||||
cp->cp_api = *api;
|
cp->cp_api = *api;
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
done:
|
done:
|
||||||
|
|
@ -123,12 +203,14 @@ plugin_load_one(clicon_handle h,
|
||||||
|
|
||||||
/*! Load a set of plugin objects from a directory and and call their init-function
|
/*! Load a set of plugin objects from a directory and and call their init-function
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] function Which function symbol to load and call (eg CLIXON_PLUGIN_INIT)
|
||||||
* @param[in] dir Directory. .so files in this dir will be loaded.
|
* @param[in] dir Directory. .so files in this dir will be loaded.
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clixon_plugins_load(clicon_handle h,
|
clixon_plugins_load(clicon_handle h,
|
||||||
|
char *function,
|
||||||
char *dir)
|
char *dir)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -147,7 +229,7 @@ clixon_plugins_load(clicon_handle h,
|
||||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||||
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||||
(int)strlen(filename), filename);
|
(int)strlen(filename), filename);
|
||||||
if ((cp = plugin_load_one(h, filename, RTLD_NOW)) == NULL)
|
if ((cp = plugin_load_one(h, filename, function, RTLD_NOW)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
_clixon_nplugins++;
|
_clixon_nplugins++;
|
||||||
if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
|
if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
|
||||||
|
|
@ -169,7 +251,7 @@ done:
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] file Which plugin to load
|
* @param[in] file Which plugin to load
|
||||||
* @param[in] dlflags See man(3) dlopen
|
* @param[in] dlflags See man(3) dlopen
|
||||||
* @see plugin_load_one for netxgen, this is soon OBSOLETE
|
* @note OBSOLETE
|
||||||
*/
|
*/
|
||||||
plghndl_t
|
plghndl_t
|
||||||
plugin_load(clicon_handle h,
|
plugin_load(clicon_handle h,
|
||||||
|
|
@ -211,10 +293,10 @@ plugin_load(clicon_handle h,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Unload a plugin
|
/*! Unload a plugin
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] handle Clicon handle
|
* @param[in] handle Clicon handle
|
||||||
|
* @note OBSOLETE
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
plugin_unload(clicon_handle h,
|
plugin_unload(clicon_handle h,
|
||||||
|
|
@ -238,27 +320,6 @@ plugin_unload(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Unload all plugins
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
clixon_plugin_unload(clicon_handle h)
|
|
||||||
{
|
|
||||||
clixon_plugin *cp;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < _clixon_nplugins; i++) {
|
|
||||||
cp = &_clixon_plugins[i];
|
|
||||||
plugin_unload(h, cp->cp_handle);
|
|
||||||
}
|
|
||||||
if (_clixon_plugins){
|
|
||||||
free(_clixon_plugins);
|
|
||||||
_clixon_plugins = NULL;
|
|
||||||
}
|
|
||||||
_clixon_nplugins = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Call plugin_start in all plugins
|
/*! Call plugin_start in all plugins
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
*/
|
*/
|
||||||
|
|
@ -275,6 +336,7 @@ clixon_plugin_start(clicon_handle h,
|
||||||
cp = &_clixon_plugins[i];
|
cp = &_clixon_plugins[i];
|
||||||
if ((startfn = cp->cp_api.ca_start) == NULL)
|
if ((startfn = cp->cp_api.ca_start) == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
// optind = 0;
|
||||||
if (startfn(h, argc, argv) < 0) {
|
if (startfn(h, argc, argv) < 0) {
|
||||||
clicon_debug(1, "plugin_start() failed\n");
|
clicon_debug(1, "plugin_start() failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -283,6 +345,39 @@ clixon_plugin_start(clicon_handle h,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Unload all plugins: call exit function and close shared handle
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clixon_plugin_exit(clicon_handle h)
|
||||||
|
{
|
||||||
|
clixon_plugin *cp;
|
||||||
|
plgexit_t *exitfn;
|
||||||
|
int i;
|
||||||
|
char *error;
|
||||||
|
|
||||||
|
for (i = 0; i < _clixon_nplugins; i++) {
|
||||||
|
cp = &_clixon_plugins[i];
|
||||||
|
if ((exitfn = cp->cp_api.ca_exit) == NULL)
|
||||||
|
continue;
|
||||||
|
if (exitfn(h) < 0) {
|
||||||
|
clicon_debug(1, "plugin_exit() failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (dlclose(cp->cp_handle) != 0) {
|
||||||
|
error = (char*)dlerror();
|
||||||
|
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_clixon_plugins){
|
||||||
|
free(_clixon_plugins);
|
||||||
|
_clixon_plugins = NULL;
|
||||||
|
}
|
||||||
|
_clixon_nplugins = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Run the restconf user-defined credentials callback if present
|
/*! Run the restconf user-defined credentials callback if present
|
||||||
* Find first authentication callback and call that, then return.
|
* Find first authentication callback and call that, then return.
|
||||||
* The callback is to set the authenticated user
|
* The callback is to set the authenticated user
|
||||||
|
|
|
||||||
|
|
@ -433,7 +433,7 @@ xml_child_i_set(cxobj *xt,
|
||||||
|
|
||||||
/*! Iterator over xml children objects
|
/*! Iterator over xml children objects
|
||||||
*
|
*
|
||||||
* NOTE: Never manipulate the child-list during operation or using the
|
* @note Never manipulate the child-list during operation or using the
|
||||||
* same object recursively, the function uses an internal field to remember the
|
* same object recursively, the function uses an internal field to remember the
|
||||||
* index used. It works as long as the same object is not iterated concurrently.
|
* index used. It works as long as the same object is not iterated concurrently.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue