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