Restructure and more generic plugin API for backend

This commit is contained in:
Olof hagsand 2018-04-02 19:27:57 +02:00
parent 7fbd95d491
commit b9a54f07f3
19 changed files with 570 additions and 629 deletions

View file

@ -6,28 +6,55 @@
### Major changes:
* 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()`.
* 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/
* Added authentication plugin callback (ca_auth)
* 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!)
* Client-local netconf plugins netconf_plugin_callbacks()
* CLI parse hook
* CLICON_FIND_PLUGIN
* 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 = {
"example",
clixon_plugin_init,
...
}
clixon_plugin_api *clixon_plugin_init(clicon_handle h){
return (void*)&api;
}
/* This is old style with hardcoded function names (eg plugin_start) */
int plugin_start(clicon_handle h, int argc, char **argv)
{
return 0;
}
int
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
@ -42,8 +69,12 @@
### 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
* 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)
* 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.

View file

@ -83,7 +83,7 @@ Extending
=========
Clixon provides a core system and can be used as-is using available
Yang specifications. However, an application very quickly needs to
specialize 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
are also available.

View file

@ -55,8 +55,6 @@ CLIXON_MINOR = @CLIXON_VERSION_MINOR@
# Use this clixon lib for linking
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
# Location of system plugins
CLIXON_BACKEND_SYSDIR = $(libdir)/clixon/plugins/backend
# 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.
@ -119,7 +117,7 @@ install-include: clixon_backend.h clixon_backend_handle.h clixon_backend_transac
.SUFFIXES: .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
test.c :

View file

@ -289,7 +289,7 @@ from_client_get(clicon_handle h,
/* Get state data from plugins as defined by plugin_statedata(), if any */
assert(xret);
clicon_err_reset();
if ((ret = backend_statedata_call(h, selector, xret)) < 0)
if ((ret = clixon_plugin_statedata(h, selector, xret)) < 0)
goto done;
if (ret == 0){ /* OK */
cprintf(cbret, "<rpc-reply>");

View file

@ -86,7 +86,7 @@ backend_terminate(clicon_handle h)
clicon_debug(1, "%s", __FUNCTION__);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
plugin_finish(h);
clixon_plugin_exit(h);
/* Delete all backend plugin RPC callbacks */
backend_rpc_cb_delete_all();
if (pidfile)
@ -254,7 +254,7 @@ plugin_start_useroptions(clicon_handle h,
tmp = *(argv-1);
*(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;
*(argv-1) = tmp;
return 0;
@ -370,7 +370,7 @@ startup_mode_running(clicon_handle h,
if (db_reset(h, "tmp") < 0)
goto done;
/* 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;
/* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0)
@ -443,7 +443,7 @@ startup_mode_startup(clicon_handle h,
if (db_reset(h, "tmp") < 0)
goto done;
/* 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;
/* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0)

View file

@ -64,58 +64,6 @@
#include "backend_plugin.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)
* @param[in] h Clicon handle
* @retval 0 OK
@ -127,263 +75,6 @@ backend_plugin_init(clicon_handle h)
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.
* @param[in] h Clicon handle
* @retval 0 OK
@ -394,42 +85,115 @@ plugin_initiate(clicon_handle h)
{
char *dir;
/* First load CLICON system plugins */
if (backend_plugin_load_dir(h, CLIXON_BACKEND_SYSDIR) < 0)
return -1;
/* 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;
/* Load application plugins */
if ((dir = clicon_backend_dir(h)) == NULL)
return 0;
return clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir);
}
/*! 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] db Name of database
* @retval 0 OK
* @retval -1 Error
*/
int
plugin_finish(clicon_handle h)
clixon_plugin_reset(clicon_handle h,
char *db)
{
int i;
struct plugin *p;
for (i = 0; i < _nplugins; i++) {
p = &_plugins[i];
backend_plugin_unload(h, p);
clixon_plugin *cp = NULL;
plgreset_t *resetfn; /* Plugin auth */
int retval = 1;
while ((cp = plugin_each(cp)) != NULL) {
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){
free(_plugins);
_plugins = NULL;
}
_nplugins = 0;
return 0;
return retval;
}
/*! 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 */
transaction_data_t *
transaction_new(void)
@ -478,21 +242,20 @@ int
plugin_transaction_begin(clicon_handle h,
transaction_data_t *td)
{
int i;
int retval = 0;
struct plugin *p;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
for (i = 0; i < _nplugins; i++) {
p = &_plugins[i];
if (p->p_trans_begin)
if ((retval = (p->p_trans_begin)(h, (transaction_data)td)) < 0){
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",
__FUNCTION__, p->p_name, PLUGIN_TRANS_BEGIN);
while ((cp = plugin_each(cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_begin) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_begin callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
break;
}
}
}
return retval;
}
@ -508,20 +271,18 @@ plugin_transaction_validate(clicon_handle h,
transaction_data_t *td)
{
int retval = 0;
int i;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
struct plugin *p;
for (i = 0; i < _nplugins; i++){
p = &_plugins[i];
if (p->p_trans_validate)
if ((retval = (p->p_trans_validate)(h, (transaction_data)td)) < 0){
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",
__FUNCTION__, p->p_name, PLUGIN_TRANS_VALIDATE);
break;
}
while ((cp = plugin_each(cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_validate) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' transaction_validate callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
break;
}
}
return retval;
}
@ -538,20 +299,20 @@ int
plugin_transaction_complete(clicon_handle h,
transaction_data_t *td)
{
int i;
int retval = 0;
struct plugin *p;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
for (i = 0; i < _nplugins; i++){
p = &_plugins[i];
if (p->p_trans_complete)
if ((retval = (p->p_trans_complete)(h, (transaction_data)td)) < 0){
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",
__FUNCTION__, p->p_name, PLUGIN_TRANS_COMPLETE);
break;
}
while ((cp = plugin_each(cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_complete) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_complete callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
break;
}
}
return retval;
}
@ -572,9 +333,9 @@ plugin_transaction_revert(clicon_handle h,
{
int retval = 0;
transaction_data_t tr; /* revert transaction */
int i;
struct plugin *p;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
/* Create a new reversed transaction from the original where src and target
are swapped */
memcpy(&tr, td, sizeof(tr));
@ -588,14 +349,14 @@ plugin_transaction_revert(clicon_handle h,
tr.td_scvec = td->td_tcvec;
tr.td_tcvec = td->td_scvec;
for (i = nr-1; i>=0; i--){
p = &_plugins[i];
if (p->p_trans_commit)
if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){
clicon_log(LOG_NOTICE, "Plugin '%s' %s revert callback failed",
p->p_name, PLUGIN_TRANS_COMMIT);
while ((cp = plugin_each_revert(cp, nr)) != NULL) {
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit revert callback failed",
__FUNCTION__, cp->cp_name);
break;
}
}
}
return retval; /* ignore errors */
}
@ -614,20 +375,22 @@ plugin_transaction_commit(clicon_handle h,
transaction_data_t *td)
{
int retval = 0;
int i;
struct plugin *p;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
int i=0;
for (i = 0; i < _nplugins; i++){
p = &_plugins[i];
if (p->p_trans_commit)
if ((retval = (p->p_trans_commit)(h, (transaction_data)td)) < 0){
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",
__FUNCTION__, p->p_name, PLUGIN_TRANS_COMMIT);
/* Make an effort to revert transaction */
plugin_transaction_revert(h, td, i);
break;
}
while ((cp = plugin_each(cp)) != NULL) {
i++;
if ((fn = cp->cp_api.ca_trans_commit) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_commit callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
/* Make an effort to revert transaction */
plugin_transaction_revert(h, td, i-1);
break;
}
}
return retval;
}
@ -643,19 +406,18 @@ plugin_transaction_end(clicon_handle h,
transaction_data_t *td)
{
int retval = 0;
int i;
struct plugin *p;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
for (i = 0; i < _nplugins; i++) {
p = &_plugins[i];
if (p->p_trans_end)
if ((retval = (p->p_trans_end)(h, (transaction_data)td)) < 0){
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",
__FUNCTION__, p->p_name, PLUGIN_TRANS_END);
break;
}
while ((cp = plugin_each(cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_end) == NULL)
continue;
if ((retval = fn(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_end callback does not make clicon_err call on error",
__FUNCTION__, cp->cp_name);
break;
}
}
return retval;
}
@ -671,94 +433,14 @@ plugin_transaction_abort(clicon_handle h,
transaction_data_t *td)
{
int retval = 0;
int i;
struct plugin *p;
clixon_plugin *cp = NULL;
trans_cb_t *fn;
for (i = 0; i < _nplugins; i++) {
p = &_plugins[i];
if (p->p_trans_abort)
(p->p_trans_abort)(h, (transaction_data)td); /* dont abort on error */
while ((cp = plugin_each(cp)) != NULL) {
if ((fn = cp->cp_api.ca_trans_abort) == NULL)
continue;
fn(h, (transaction_data)td); /* dont abort on error */
}
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;
}

View file

@ -69,12 +69,10 @@ typedef struct {
*/
int backend_plugin_init(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 plugin_start_argv(clicon_handle h, int argc, char **argv);
int clixon_plugin_reset(clicon_handle h, char *db);
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);
int transaction_free(transaction_data_t *);

View file

@ -384,7 +384,7 @@ main(int argc,
/* Initialize plugins group */
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;
/* Call start function is all plugins before we go interactive */
@ -402,7 +402,7 @@ main(int argc,
if (event_loop() < 0)
goto done;
done:
clixon_plugin_unload(h);
clixon_plugin_exit(h);
netconf_terminate(h);
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());

View file

@ -554,7 +554,7 @@ main(int argc,
/* Initialize plugins group */
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;
/* Parse yang database spec file */
@ -603,7 +603,7 @@ main(int argc,
}
retval = 0;
done:
clixon_plugin_unload(h);
clixon_plugin_exit(h);
restconf_terminate(h);
return retval;
}

View file

@ -45,11 +45,12 @@ CFLAGS = @CFLAGS@ -rdynamic -fPIC
INCLUDES = -I$(includedir) @INCLUDES@
BE_PLUGIN = $(APPNAME)_backend.so
BE2_PLUGIN = $(APPNAME)_backend_secondary.so
CLI_PLUGIN = $(APPNAME)_cli.so
NETCONF_PLUGIN = $(APPNAME)_netconf.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)
@ -74,6 +75,12 @@ BE_OBJ = $(BE_SRC:%.c=%.o)
$(BE_PLUGIN): $(BE_OBJ)
$(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_SRC = $(APPNAME)_cli.c
CLI_OBJ = $(CLI_SRC:%.c=%.o)
@ -92,8 +99,8 @@ RESTCONF_OBJ = $(RESTCONF_SRC:%.c=%.o)
$(RESTCONF_PLUGIN): $(RESTCONF_OBJ)
$(CC) -Wall -shared -o $@ -lc $^
SRC = $(BE_SRC) $(CLI_SRC) $(NETCONF_SRC) $(RESTCONF_SRC)
OBJS = $(BE_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) $(RESTCONF_OBJ)
SRC = $(BE_SRC) $(BE2_SRC) $(CLI_SRC) $(NETCONF_SRC) $(RESTCONF_SRC)
OBJS = $(BE_OBJ) $(BE2_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) $(RESTCONF_OBJ)
clean:
rm -f $(PLUGINS) $(OBJS)
@ -103,7 +110,7 @@ distclean: clean
rm -f Makefile *~ .depend
(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 $(APPNAME).xml $(DESTDIR)$(clixon_SYSCONFDIR)
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 $(CLI_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/cli;
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 $(NETCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/netconf;
install -d $(DESTDIR)$(clixon_LIBDIR)/restconf

View file

@ -11,6 +11,7 @@ routing example. It contains the following files:
* notification,
* rpc handler
* 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_netconf.c Netconf callback plugin
* Makefile.in Example makefile where plugins are built and installed

View file

@ -77,6 +77,7 @@ transaction_commit(clicon_handle h,
int i;
size_t len;
clicon_debug(1, "%s", __FUNCTION__);
/* Get all added i/fs */
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
return -1;
@ -191,39 +192,6 @@ plugin_statedata(clicon_handle h,
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.
* Called in each backend plugin. plugin_reset is called after all plugins
* have been initialized. This give the application a chance to reset
@ -279,3 +247,54 @@ plugin_start(clicon_handle h,
{
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;
}

View 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;
}

View file

@ -64,19 +64,23 @@ plugin_exit(clicon_handle h)
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
static const struct clixon_plugin_api api = {
"example",
clixon_plugin_init,
plugin_start,
plugin_exit,
NULL
static struct clixon_plugin_api api = {
"example", /* name */
clixon_plugin_init, /* init */
plugin_start, /* start */
plugin_exit, /* exit */
NULL /* auth */
};
/*! 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_init(clicon_handle h)
{
return (void*)&api;
clicon_debug(1, "%s restconf", __FUNCTION__);
return &api;
}

View file

@ -269,19 +269,22 @@ plugin_credentials(clicon_handle h,
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
static const struct clixon_plugin_api api = {
"example",
clixon_plugin_init,
NULL,
NULL,
plugin_credentials,
static clixon_plugin_api api = {
"example", /* name */
clixon_plugin_init, /* init */
NULL, /* start */
NULL, /* exit */
plugin_credentials /* auth */
};
/*! 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_init(clicon_handle h)
{
clicon_debug(1, "%s restconf", __FUNCTION__);
return (void*)&api;
return &api;
}

View file

@ -73,7 +73,6 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
/*! Called by restconf to check credentials and return username
*/
#define PLUGIN_CREDENTIALS "plugin_credentials"
/* Plugin authorization. Set username option (or not)
* @param[in] Clicon handle
@ -98,21 +97,12 @@ struct clixon_plugin_api;
typedef struct clixon_plugin_api* (plginit2_t)(clicon_handle); /* Clixon plugin Init */
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) */
plgstart_t *ca_start; /* Plugin start */
plgexit_t *ca_exit; /* Plugin exit */
plgauth_t *ca_auth; /* Auth credentials */
};
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 ----------*/
/*--Above here common fields w clixon_backend_api ----------*/
plgreset_t *ca_reset; /* Reset system status (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;
#define CLIXON_PLUGIN_INIT "clixon_plugin_init" /* Nextgen */
/*! Called when plugin loaded. Only mandadory callback. All others optional
* @see plginit_t
*/
@ -133,24 +122,43 @@ typedef struct clixon_plugin_api clixon_plugin_api;
/* Internal plugin structure with dlopen() handle and plugin_api
*/
struct clixon_plugin{
char cp_name[PATH_MAX]; /* Plugin filename. Note api ca_name is given by plugin itself */
plghndl_t cp_handle; /* Handle to plugin using dlopen(3) */
struct clixon_plugin_api cp_api;
};
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
*/
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);
/* obsolete */
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_exit(clicon_handle h);
int clixon_plugin_auth(clicon_handle h, void *arg);
#endif /* _CLIXON_PLUGIN_H_ */

View file

@ -62,8 +62,7 @@
#include "clixon_string.h"
#include "clixon_file.h"
/*
* qsort function
/*! qsort "compar" for directory alphabetically sorting, see qsort(3)
*/
static int
clicon_file_dirent_sort(const void* arg1,
@ -79,8 +78,7 @@ clicon_file_dirent_sort(const void* arg1,
#endif /* HAVE_STRVERSCMP */
}
/*! Return sorted matching files from a directory
/*! Return alphabetically sorted files from a directory matching regexp
* @param[in] dir Directory path
* @param[out] ent Entries pointer, will be filled in with dir entries. Free
* after use
@ -120,16 +118,13 @@ clicon_file_dirent(const char *dir,
struct dirent *new = NULL;
struct dirent *dvecp = NULL;
*ent = NULL;
nent = 0;
if (regexp && (res = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
regerror(res, &re, errbuf, sizeof(errbuf));
clicon_err(OE_DB, 0, "regcomp: %s", errbuf);
return -1;
}
if ((dirp = opendir (dir)) == NULL) {
if (errno == ENOENT) /* Dir does not exist -> return 0 matches */
retval = 0;
@ -137,7 +132,6 @@ clicon_file_dirent(const char *dir,
clicon_err(OE_UNIX, errno, "opendir(%s)", dir);
goto quit;
}
for (res = readdir_r (dirp, &dent, &dresp);
dresp;
res = readdir_r (dirp, &dent, &dresp)) {
@ -162,7 +156,6 @@ clicon_file_dirent(const char *dir,
if ((type & st.st_mode) == 0)
continue;
}
if ((tmp = realloc(new, (nent+1)*sizeof(*dvecp))) == NULL) {
clicon_err(OE_UNIX, errno, "realloc");
goto quit;

View file

@ -64,9 +64,83 @@
static clixon_plugin *_clixon_plugins = NULL; /* List of plugins (of client) */
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
* @param[in] h Clicon handle
* @param[in] file Which plugin to load
* @param[in] function Which function symbol to load and call
* @param[in] dlflags See man(3) dlopen
* @retval cp Clixon plugin structure
* @retval NULL Error
@ -74,7 +148,8 @@ static int _clixon_nplugins = 0; /* Number of plugins */
*/
static clixon_plugin *
plugin_load_one(clicon_handle h,
char *file,
char *file,
char *function,
int dlflags)
{
char *error;
@ -82,16 +157,17 @@ plugin_load_one(clicon_handle h,
plginit2_t *initfn;
clixon_plugin_api *api = NULL;
clixon_plugin *cp = NULL;
char *name;
clicon_debug(1, "%s", __FUNCTION__);
dlerror(); /* Clear any existing error */
if ((handle = dlopen (file, dlflags)) == NULL) {
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, CLIXON_PLUGIN_INIT)) == NULL){
/* call plugin_init() if defined, eg CLIXON_PLUGIN_INIT or CLIXON_BACKEND_INIT */
if ((initfn = dlsym(handle, function)) == NULL){
clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file);
goto err;
}
@ -106,11 +182,15 @@ plugin_load_one(clicon_handle h,
file);
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");
goto done;
}
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;
clicon_debug(1, "%s", __FUNCTION__);
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
* @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.
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_plugins_load(clicon_handle h,
char *function,
char *dir)
{
int retval = -1;
@ -147,7 +229,7 @@ clixon_plugins_load(clicon_handle h,
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
(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;
_clixon_nplugins++;
if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
@ -169,7 +251,7 @@ done:
* @param[in] h Clicon handle
* @param[in] file Which plugin to load
* @param[in] dlflags See man(3) dlopen
* @see plugin_load_one for netxgen, this is soon OBSOLETE
* @note OBSOLETE
*/
plghndl_t
plugin_load(clicon_handle h,
@ -211,10 +293,10 @@ plugin_load(clicon_handle h,
return NULL;
}
/*! Unload a plugin
* @param[in] h Clicon handle
* @param[in] handle Clicon handle
* @note OBSOLETE
*/
int
plugin_unload(clicon_handle h,
@ -238,27 +320,6 @@ plugin_unload(clicon_handle h,
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
* @param[in] h Clicon handle
*/
@ -275,6 +336,7 @@ clixon_plugin_start(clicon_handle h,
cp = &_clixon_plugins[i];
if ((startfn = cp->cp_api.ca_start) == NULL)
continue;
// optind = 0;
if (startfn(h, argc, argv) < 0) {
clicon_debug(1, "plugin_start() failed\n");
return -1;
@ -283,6 +345,39 @@ clixon_plugin_start(clicon_handle h,
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
* Find first authentication callback and call that, then return.
* The callback is to set the authenticated user

View file

@ -433,7 +433,7 @@ xml_child_i_set(cxobj *xt,
/*! 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
* index used. It works as long as the same object is not iterated concurrently.
*