From 79e3fbdaa95393b07f06b66223b0a3b035192f1c Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 2 Apr 2018 10:38:53 +0200 Subject: [PATCH] * Restructure and more generic plugin API (cli,backend,restconf,netconf) * For preparation for authorization RFC8341 * Plugins add clixon_plugin_init() and api struct for function pointers, eg: ``` static const struct clixon_plugin_api api = { "example", clixon_plugin_init, ... } clixon_plugin_api *clixon_plugin_init(clicon_handle h) { return (void*)&api; } ``` * Moved specific plugin functions from apps/ to generic functions in lib/ * New generic plugin load function: clixon_plugins_load() * Removed client-local netconf plugins netconf_plugin_callbacks() * This was code used before generic YANG rpc calls * Added username to clixon handle: * clicon_username_get() / clicon_username_set() * Added authentication plugin callback * Removed some obscure plugin code that seem not to be used (please report if needed!) * CLI parse hook * CLICON_FIND_PLUGIN * clicon_valcb() * Removed username to rpc calls (added below) --- CHANGELOG.md | 29 ++- README.md | 4 +- apps/backend/backend_plugin.c | 36 +--- apps/backend/clixon_backend_handle.h | 2 - apps/backend/clixon_backend_transaction.h | 1 - apps/cli/cli_common.c | 8 +- apps/cli/cli_plugin.c | 96 --------- apps/cli/cli_plugin.h | 5 +- apps/cli/cli_show.c | 6 +- apps/netconf/Makefile.in | 2 +- apps/netconf/netconf_main.c | 11 +- apps/netconf/netconf_plugin.c | 237 ---------------------- apps/netconf/netconf_plugin.h | 56 ----- apps/netconf/netconf_rpc.c | 9 +- apps/restconf/Makefile.in | 2 +- apps/restconf/clixon_restconf.h | 5 - apps/restconf/restconf_lib.c | 123 ----------- apps/restconf/restconf_lib.h | 5 - apps/restconf/restconf_main.c | 52 +++-- apps/restconf/restconf_methods.c | 40 ++-- apps/restconf/restconf_methods.h | 16 +- example/routing_cli.c | 2 +- example/routing_netconf.c | 31 +-- example/routing_restconf.c | 58 +++--- lib/clixon/clixon_handle.h | 3 + lib/clixon/clixon_options.h | 5 + lib/clixon/clixon_plugin.h | 78 +++++-- lib/clixon/clixon_proto_client.h | 4 +- lib/src/clixon_handle.c | 32 +-- lib/src/clixon_options.c | 31 ++- lib/src/clixon_plugin.c | 211 ++++++++++++++++--- lib/src/clixon_proto_client.c | 17 +- lib/src/clixon_xml_db.c | 4 +- lib/src/clixon_xml_map.c | 8 +- lib/src/clixon_yang.c | 1 + lib/src/clixon_yang_type.c | 1 + test/lib.sh | 2 +- test/test_netconf.sh | 3 +- test/test_perf.sh | 2 +- test/test_restconf2.sh | 2 +- yang/clixon-config@2018-02-12.yang | 2 +- 41 files changed, 470 insertions(+), 772 deletions(-) delete mode 100644 apps/netconf/netconf_plugin.c delete mode 100644 apps/netconf/netconf_plugin.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c08b5de..fc05764f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,32 @@ ### Major changes: +* Restructure and more generic plugin API (cli,backend,restconf,netconf) + * For preparation for authorization RFC8341 + * Plugins add clixon_plugin_init() and api struct for function pointers, eg: +``` +static const struct clixon_plugin_api api = { + "example", + clixon_plugin_init, + ... +} +clixon_plugin_api *clixon_plugin_init(clicon_handle h) +{ + return (void*)&api; +} +``` + * Moved specific plugin functions from apps/ to generic functions in lib/ + * New generic plugin load function: clixon_plugins_load() + * Removed client-local netconf plugins netconf_plugin_callbacks() + * This was code used before generic YANG rpc calls + * Added username to clixon handle: + * clicon_username_get() / clicon_username_set() + * Added authentication plugin callback + * Removed some obscure plugin code that seem not to be used (please report if needed!) + * CLI parse hook + * CLICON_FIND_PLUGIN + * clicon_valcb() + * Added Clixon Restconf library * Builds and installs a new restconf library: libclixon_restconf.so and clixon_restconf.h * The restconf library can be included by a restconf plugin. @@ -20,6 +46,7 @@ ### Minor changes: +* 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. * Add username to rpc calls to prepare for authorization for backend: @@ -92,7 +119,7 @@ enables saved files to be used as datastore without any editing. Thanks Matt. * New CLICON_XML_SORT configuration option. Default is true. Disable by setting to false. * Added yang ordered-by user. The default (ordered-by system) will now sort lists and leaf-lists alphabetically to increase search performance. Note that this may change outputs. * If you need legacy order, either set CLICON_XML_SORT to false, or set that list to "ordered-by user". - * This replaces XML hash experimental code, ie xml_child_hash variables and all xml_hash_ functions have been removed. + * This replaces XML hash experimental code, ie xml_child_hash variables and all xmlv_hash_ functions have been removed. * Implementation detail: Cached keys are stored in in yang Y_LIST nodes as cligen vector, see ys_populate_list() * Datastore cache introduced: cache XML tree in memory for faster get access. diff --git a/README.md b/README.md index 04ef36c5..6f4e3ee3 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ Clixon is an automatic configuration manager where you generate interactive CLI, NETCONF, RESTCONF and embedded databases with transaction support from a YANG specification. -Topics -====== * [Background](#background) * [Frequently asked questions](doc/FAQ.md) * [Installation](#installation) @@ -38,7 +36,7 @@ Users of clixon currently include: * [CloudMon360](http://cloudmon360.com) * [Grideye](http://hagsand.se/grideye) * [Netclean](https://www.netclean.com/solutions/whitebox) # only CLIgen - * [Prosilient's PTAnalyzer](http://www.prosilient.com) # only CLIgen + * [Prosilient's PTAnalyzer](https://prosilient.com) # only CLIgen See also [Clicon project page](http://clicon.org). diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index fe3e5a6a..ffe76941 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -71,7 +71,7 @@ * @note the following should match the prototypes in clixon_backend.h */ #define PLUGIN_RESET "plugin_reset" -typedef int (plgreset_t)(clicon_handle h, const char *db); /* Reset system status */ + /*! Plugin callback, if defined called to get state data from plugin * @param[in] h Clicon handle @@ -82,7 +82,7 @@ typedef int (plgreset_t)(clicon_handle h, const char *db); /* Reset system statu * @see xmldb_get */ #define PLUGIN_STATEDATA "plugin_statedata" -typedef int (plgstatedata_t)(clicon_handle h, char *xpath, cxobj *xtop); + #define PLUGIN_TRANS_BEGIN "transaction_begin" #define PLUGIN_TRANS_VALIDATE "transaction_validate" @@ -92,8 +92,6 @@ typedef int (plgstatedata_t)(clicon_handle h, char *xpath, cxobj *xtop); #define PLUGIN_TRANS_ABORT "transaction_abort" -typedef int (trans_cb_t)(clicon_handle h, transaction_data td); /* Transaction cbs */ - /* Backend (config) plugins */ struct plugin { char p_name[PATH_MAX]; /* Plugin name */ @@ -118,28 +116,6 @@ struct plugin { static int _nplugins = 0; static struct plugin *_plugins = NULL; -/*! Find a plugin by name and return the dlsym handl - * Used by libclicon code to find callback funcctions in plugins. - * @param[in] h Clicon handle - * @param[in] h Name of plugin - * @retval handle Plugin handle if found - * @retval NULL Not found - */ -static void * -config_find_plugin(clicon_handle h, - char *name) -{ - int i; - struct plugin *p; - - for (i = 0; i < _nplugins; i++){ - p = &_plugins[i]; - if (strcmp(p->p_name, name) == 0) - return p->p_handle; - } - return NULL; -} - /*! Initialize plugin code (not the plugins themselves) * @param[in] h Clicon handle * @retval 0 OK @@ -148,14 +124,6 @@ config_find_plugin(clicon_handle h, int backend_plugin_init(clicon_handle h) { - find_plugin_t *fp = config_find_plugin; - clicon_hash_t *data = clicon_data(h); - - /* Register CLICON_FIND_PLUGIN in data hash */ - if (hash_add(data, "CLICON_FIND_PLUGIN", &fp, sizeof(fp)) == NULL) { - clicon_err(OE_UNIX, errno, "failed to register CLICON_FIND_PLUGIN"); - return -1; - } return 0; } diff --git a/apps/backend/clixon_backend_handle.h b/apps/backend/clixon_backend_handle.h index 2edb61b8..fa596ed0 100644 --- a/apps/backend/clixon_backend_handle.h +++ b/apps/backend/clixon_backend_handle.h @@ -92,8 +92,6 @@ int subscription_delete(clicon_handle h, char *stream, struct handle_subscription *subscription_each(clicon_handle h, struct handle_subscription *hprev); -/* XXX backward compat */ -#define backend_netconf_register_callback(a,b,c,d) backend_rpc_cb_register(a,b,c,d) int backend_rpc_cb_register(clicon_handle h, backend_rpc_cb cb, void *arg, char *tag); diff --git a/apps/backend/clixon_backend_transaction.h b/apps/backend/clixon_backend_transaction.h index 2647a45c..1d2a523e 100644 --- a/apps/backend/clixon_backend_transaction.h +++ b/apps/backend/clixon_backend_transaction.h @@ -55,7 +55,6 @@ typedef int (*downcall_cb)(clicon_handle h, uint16_t op, uint16_t len, * (defined in config_dbdep.c) * @see transaction_data_t internal structure */ -typedef void *transaction_data; uint64_t transaction_id(transaction_data td); void *transaction_arg(transaction_data td); cxobj *transaction_src(transaction_data td); diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index a4ec0130..4979f412 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -654,13 +654,13 @@ compare_dbs(clicon_handle h, astext = cv_int32_get(cvec_i(argv, 0)); else astext = 0; - if (clicon_rpc_get_config(h, "running", "/", NULL, &xc1) < 0) + if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0) goto done; if ((xerr = xpath_first(xc1, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); goto done; } - if (clicon_rpc_get_config(h, "candidate", "/", NULL, &xc2) < 0) + if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0) goto done; if ((xerr = xpath_first(xc2, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); @@ -827,7 +827,7 @@ save_config_file(clicon_handle h, goto done; } filename = cv_string_get(cv); - if (clicon_rpc_get_config(h, dbstr,"/", NULL, &xt) < 0) + if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0) goto done; if (xt == NULL){ clicon_err(OE_CFG, 0, "get config: empty tree"); /* Shouldnt happen */ @@ -1180,7 +1180,7 @@ cli_copy_config(clicon_handle h, cprintf(cb, xpath, keyname, fromname); /* Get from object configuration and store in x1 */ - if (clicon_rpc_get_config(h, db, cbuf_get(cb), NULL, &x1) < 0) + if (clicon_rpc_get_config(h, db, cbuf_get(cb), &x1) < 0) goto done; if ((xerr = xpath_first(x1, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index dd399950..bc7f9904 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -66,13 +66,11 @@ #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_PARSE_HOOK "plugin_parse_hook" #define PLUGIN_SUSP_HOOK "plugin_susp_hook" /* @@ -380,7 +378,6 @@ cli_plugin_load_dir(clicon_handle h, 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"); @@ -403,8 +400,6 @@ cli_plugin_load_dir(clicon_handle h, /* Look up certain call-backs in master plugin */ stx->stx_prompt_hook = dlsym(cp->cp_handle, PLUGIN_PROMPT_HOOK); - stx->stx_parse_hook = - dlsym(cp->cp_handle, PLUGIN_PARSE_HOOK); stx->stx_susp_hook = dlsym(cp->cp_handle, PLUGIN_SUSP_HOOK); INSQ(cp, stx->stx_plugins); @@ -679,15 +674,6 @@ clicon_parse(clicon_handle h, goto done; case CG_NOMATCH: /* no match */ smode = NULL; - if (stx->stx_parse_hook) { - /* Try to find a match in upper modes, a'la IOS. */ - if ((modename = stx->stx_parse_hook(h, cmd, modename)) != NULL) { - if ((smode = syntax_mode_find(stx, modename, 0)) != NULL) - continue; - else - cli_output(f, "Can't find syntax mode '%s'\n", modename); - } - } /* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s", cmd, cli_nomatch(h));*/ cli_output(f, "CLI syntax error: \"%s\": %s\n", @@ -745,42 +731,14 @@ clicon_cliread(clicon_handle h) return ret; } -/* - * cli_find_plugin - * Find a plugin by name and return the dlsym handl - * Used by libclicon code to find callback funcctions in plugins. - */ -static void * -cli_find_plugin(clicon_handle h, char *plugin) -{ - struct cli_plugin *p; - - p = plugin_find_cli(cli_syntax(h), plugin); - if (p) - return p->cp_handle; - - return NULL; -} - - /*! Initialize plugin code (not the plugins themselves) */ int cli_plugin_init(clicon_handle h) { - find_plugin_t *fp = cli_find_plugin; - clicon_hash_t *data = clicon_data(h); - - /* Register CLICON_FIND_PLUGIN in data hash */ - if (hash_add(data, "CLICON_FIND_PLUGIN", &fp, sizeof(fp)) == NULL) { - clicon_err(OE_UNIX, errno, "failed to register CLICON_FIND_PLUGIN"); - return -1; - } - return 0; } - /* * * CLI PLUGIN INTERFACE, PUBLIC SECTION @@ -816,7 +774,6 @@ cli_syntax_mode(clicon_handle h) return csm->csm_name; } - /* * Callback from cli_set_prompt(). Set prompt format for syntax mode * Arguments: @@ -880,7 +837,6 @@ prompt_fmt (char *prompt, size_t plen, char *fmt, ...) cprintf(cb, "%c", *s); s++; } - done: if (cb) fmt = cbuf_get(cb); @@ -905,55 +861,3 @@ cli_prompt(char *fmt) return prompt; } -/*! Find a cli plugin based on name and resolve a function pointer in it. - * Callback from clicon_dbvars_parse() - * Find a cli plugin based on name if given and use dlsym to resolve a - * function pointer in it. - * Call the resolved function to get the cgv populated - */ -int -clicon_valcb(void *arg, cvec *vars, cg_var *cgv, char *fname, cg_var *funcarg) -{ - char *func; - char *plgnam = NULL; - void *handle; - struct cli_plugin *p; - cli_valcb_t *cb; - clicon_handle h = (clicon_handle)arg; - - /* Make copy */ - if ((fname = strdup(fname)) == NULL) { - clicon_err(OE_UNIX, errno, "strdup"); - return -1; - } - - /* Extract plugin name if any */ - if ((func = strstr(fname, "::")) != NULL) { - *func = '\0'; - func += 2; - plgnam = fname; - } - else - func = fname; - - /* If we have specified a plugin name, find the handle to be used - * with dlsym() - */ - handle = NULL; - if (plgnam && (p = plugin_find_cli(cli_syntax(h), plgnam))) - handle = p->cp_handle; - - /* Look up function pointer */ - if ((cb = dlsym(handle, func)) == NULL) { - clicon_err(OE_UNIX, errno, "unable to find %s()", func); - free(fname); - return -1; - } - free(fname); - - if (cb(vars, cgv, funcarg) < 0) - return -1; - - return 0; -} - diff --git a/apps/cli/cli_plugin.h b/apps/cli/cli_plugin.h index a81f057b..081b3332 100644 --- a/apps/cli/cli_plugin.h +++ b/apps/cli/cli_plugin.h @@ -77,10 +77,9 @@ 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_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_parsehook_t *stx_parse_hook; /* Parse mode hook */ cli_susphook_t *stx_susp_hook; /* Ctrl-Z hook from getline() */ } cli_syntax_t; diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 7106965f..a13296b8 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -156,7 +156,7 @@ expand_dbvar(void *h, goto done; /* XXX read whole configuration, why not send xpath? */ - if (clicon_rpc_get_config(h, dbstr, "/", NULL, &xt) < 0) + if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0) goto done; if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); @@ -487,7 +487,7 @@ cli_show_config(clicon_handle h, else cprintf(cbxpath, "%s", xpath); /* Get configuration from database */ - if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), NULL, &xt) < 0) + if (clicon_rpc_get_config(h, db, cbuf_get(cbxpath), &xt) < 0) goto done; if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); @@ -571,7 +571,7 @@ show_conf_xpath(clicon_handle h, } cv = cvec_find_var(cvv, "xpath"); xpath = cv_string_get(cv); - if (clicon_rpc_get_config(h, str, xpath, NULL, &xt) < 0) + if (clicon_rpc_get_config(h, str, xpath, &xt) < 0) goto done; if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ clicon_rpc_generate_error("Get configuration", xerr); diff --git a/apps/netconf/Makefile.in b/apps/netconf/Makefile.in index 8adf51a2..17c09072 100644 --- a/apps/netconf/Makefile.in +++ b/apps/netconf/Makefile.in @@ -74,7 +74,7 @@ MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX) MYLIB = $(MYLIBLINK).$(CLIXON_MAJOR).$(CLIXON_MINOR) MYLIBSO = $(MYLIBLINK).$(CLIXON_MAJOR) -LIBSRC = netconf_hello.c netconf_rpc.c netconf_filter.c netconf_lib.c netconf_plugin.c +LIBSRC = netconf_hello.c netconf_rpc.c netconf_filter.c netconf_lib.c LIBOBJS = $(LIBSRC:.c=.o) all: $(MYLIB) $(APPL) diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 87167585..2e5ba214 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -67,7 +67,6 @@ #include "clixon_netconf.h" #include "netconf_lib.h" #include "netconf_hello.h" -#include "netconf_plugin.h" #include "netconf_rpc.h" /* Command line options to be passed to getopt(3) */ @@ -307,6 +306,7 @@ main(int argc, int quiet = 0; clicon_handle h; int use_syslog; + char *dir; /* Defaults */ use_syslog = 0; @@ -383,13 +383,14 @@ main(int argc, goto done; /* Initialize plugins group */ - if (netconf_plugin_load(h) < 0) - goto done; + if ((dir = clicon_netconf_dir(h)) != NULL) + if (clixon_plugins_load(h, dir) < 0) + goto done; /* Call start function is all plugins before we go interactive */ tmp = *(argv-1); *(argv-1) = argv0; - netconf_plugin_start(h, argc+1, argv-1); + clixon_plugin_start(h, argc+1, argv-1); *(argv-1) = tmp; if (!quiet) @@ -401,7 +402,7 @@ main(int argc, if (event_loop() < 0) goto done; done: - netconf_plugin_unload(h); + clixon_plugin_unload(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()); diff --git a/apps/netconf/netconf_plugin.c b/apps/netconf/netconf_plugin.c deleted file mode 100644 index b601969f..00000000 --- a/apps/netconf/netconf_plugin.c +++ /dev/null @@ -1,237 +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 ***** - - * - * handling netconf plugins - */ - -#ifdef HAVE_CONFIG_H -#include "clixon_config.h" /* generated by config & autoconf */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* cligen */ -#include - -/* clicon */ -#include - -/* clicon netconf*/ -#include "clixon_netconf.h" -#include "netconf_lib.h" -#include "netconf_plugin.h" - -/* Database dependency description */ -struct netconf_reg { - qelem_t nr_qelem; /* List header */ - netconf_cb_t nr_callback; /* Validation/Commit Callback */ - void *nr_arg; /* Application specific argument to cb */ - char *nr_tag; /* Xml tag when matched, callback called */ -}; -typedef struct netconf_reg netconf_reg_t; - -static int nplugins = 0; -static plghndl_t *plugins = NULL; -static netconf_reg_t *deps = NULL; - -/*! Load all plugins you can find in CLICON_NETCONF_DIR - */ -int -netconf_plugin_load(clicon_handle h) -{ - int retval = -1; - char *dir; - int ndp; - struct dirent *dp = NULL; - int i; - char filename[MAXPATHLEN]; - plghndl_t *handle; - - /* If no DIR defined, then dont load plugins */ - if ((dir = clicon_netconf_dir(h)) == NULL){ - retval = 0; - goto quit; - } - - /* Get plugin objects names from plugin directory */ - if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0) - goto quit; - - /* Load all plugins */ - for (i = 0; i < ndp; i++) { - snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name); - clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...", - (int)strlen(filename), filename); - if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL) - goto quit; - if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) { - clicon_err(OE_UNIX, errno, "realloc"); - goto quit; - } - plugins[nplugins++] = handle; - } - retval = 0; -quit: - if (dp) - free(dp); - return retval; -} - -/*! Unload all netconf plugins */ -int -netconf_plugin_unload(clicon_handle h) -{ - int i; - netconf_reg_t *nr; - - while((nr = deps) != NULL) { - DELQ(nr, deps, netconf_reg_t *); - if (nr->nr_tag) - free(nr->nr_tag); - free(nr); - } - for (i = 0; i < nplugins; i++) - plugin_unload(h, plugins[i]); - if (plugins){ - free(plugins); - plugins = NULL; - } - nplugins = 0; - return 0; -} - -/*! Call plugin_start in all plugins - */ -int -netconf_plugin_start(clicon_handle h, int argc, char **argv) -{ - int i; - plgstart_t *startfn; - - for (i = 0; i < nplugins; i++) { - /* Call exit function is it exists */ - if ((startfn = dlsym(plugins[i], PLUGIN_START)) == NULL) - break; - optind = 0; - if (startfn(h, argc, argv) < 0) { - clicon_debug(1, "plugin_start() failed\n"); - return -1; - } - } - return 0; -} - - -/*! Register netconf callback - * Called from plugin to register a callback for a specific netconf XML tag. - */ -int -netconf_register_callback(clicon_handle h, - netconf_cb_t cb, /* Callback called */ - void *arg, /* Arg to send to callback */ - char *tag) /* Xml tag when callback is made */ -{ - netconf_reg_t *nr; - - if ((nr = malloc(sizeof(netconf_reg_t))) == NULL) { - clicon_err(OE_DB, errno, "malloc: %s", strerror(errno)); - goto catch; - } - memset (nr, 0, sizeof (*nr)); - nr->nr_callback = cb; - nr->nr_arg = arg; - nr->nr_tag = strdup(tag); /* strdup */ - INSQ(nr, deps); - return 0; -catch: - if (nr){ - if (nr->nr_tag) - free(nr->nr_tag); - free(nr); - } - return -1; -} - -/*! See if there is any callback registered for this tag - * - * Look for local (client-side) netconf plugins. This feature may no - * longer be necessary as generic RPC:s should be handled by backend. - * - * @param[in] h clicon handle - * @param[in] xn Sub-tree (under xorig) at child of rpc: . - * @param[out] xret Return XML, error or OK - * - * @retval -1 Error - * @retval 0 OK, not found handler. - * @retval 1 OK, handler called - */ -int -netconf_plugin_callbacks(clicon_handle h, - cxobj *xn, - cxobj **xret) -{ - int retval = -1; - netconf_reg_t *nreg; - - if (deps != NULL){ - nreg = deps; - do { - if (strcmp(nreg->nr_tag, xml_name(xn)) == 0){ - if ((retval = nreg->nr_callback(h, xn, xret, nreg->nr_arg)) < 0) - goto done; - retval = 1; /* handled */ - goto done; - } - nreg = NEXTQ(netconf_reg_t *, nreg); - } while (nreg != deps); - } - retval = 0; - done: - return retval; -} - diff --git a/apps/netconf/netconf_plugin.h b/apps/netconf/netconf_plugin.h deleted file mode 100644 index 96cf2921..00000000 --- a/apps/netconf/netconf_plugin.h +++ /dev/null @@ -1,56 +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 ***** - - * - * handling netconf plugins - *****************************************************************************/ -#ifndef _NETCONF_PLUGIN_H_ -#define _NETCONF_PLUGIN_H_ - -/* - * Types - */ - - -/* - * Prototypes - */ -int netconf_plugin_load(clicon_handle h); - -int netconf_plugin_start(clicon_handle h, int argc, char **argv); - -int netconf_plugin_unload(clicon_handle h); - -int netconf_plugin_callbacks(clicon_handle h, cxobj *xn, cxobj **xret); - -#endif /* _NETCONF_PLUGIN_H_ */ diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index a87a8bfb..8cc23e1b 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -65,7 +65,6 @@ #include "clixon_netconf.h" #include "netconf_lib.h" #include "netconf_filter.h" -#include "netconf_plugin.h" #include "netconf_rpc.h" /* @@ -1018,14 +1017,8 @@ netconf_rpc_dispatch(clicon_handle h, } /* Others */ else { - /* Look for local (client-side) netconf plugins. This feature may no - * longer be necessary as generic RPC:s should be handled by backend. - */ - if ((retval = netconf_plugin_callbacks(h, xe, xret)) < 0) + if ((retval = netconf_application_rpc(h, xe, xret)) < 0) goto done; - if (retval == 0) - if ((retval = netconf_application_rpc(h, xe, xret)) < 0) - goto done; if (retval == 0){ /* not handled by callback */ xml_parse_va(xret, NULL, "" "operation-failed" diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in index b4b98057..1f7e3fff 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -62,7 +62,7 @@ LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB) -CPPFLAGS = @CPPFLAGS@ +CPPFLAGS = @CPPFLAGS@ -fPIC INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h index 06c2efc3..554458f3 100644 --- a/apps/restconf/clixon_restconf.h +++ b/apps/restconf/clixon_restconf.h @@ -60,11 +60,6 @@ int notimplemented(FCGX_Request *r); int clicon_debug_xml(int dbglevel, char *str, cxobj *cx); int test(FCGX_Request *r, int dbg); cbuf *readdata(FCGX_Request *r); - -int restconf_plugin_load(clicon_handle h); -int restconf_plugin_start(clicon_handle h, int argc, char **argv); -int restconf_plugin_unload(clicon_handle h); -int restconf_credentials(clicon_handle h, FCGX_Request *r, char **user); int get_user_cookie(char *cookiestr, char *attribute, char **val); diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 258a6d98..211a3e14 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -358,129 +358,6 @@ readdata(FCGX_Request *r) return cb; } - -static int nplugins = 0; -static plghndl_t *plugins = NULL; -static plgcredentials_t *_credentials_fn = NULL; /* Credentials callback */ - -/*! Load all plugins you can find in CLICON_RESTCONF_DIR - */ -int -restconf_plugin_load(clicon_handle h) -{ - int retval = -1; - char *dir; - int ndp; - struct dirent *dp = NULL; - int i; - plghndl_t *handle; - char filename[MAXPATHLEN]; - - clicon_debug(1, "%s", __FUNCTION__); - if ((dir = clicon_restconf_dir(h)) == NULL){ - retval = 0; - goto quit; - } - /* Get plugin objects names from plugin directory */ - if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0) - goto quit; - /* Load all plugins */ - for (i = 0; i < ndp; i++) { - snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name); - clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...", - (int)strlen(filename), filename); - if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL) - goto quit; - if ((_credentials_fn = dlsym(handle, PLUGIN_CREDENTIALS)) == NULL) - clicon_debug(1, "Failed to load %s", PLUGIN_CREDENTIALS); - else - clicon_debug(1, "%s callback loaded", PLUGIN_CREDENTIALS); - if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) { - clicon_err(OE_UNIX, errno, "realloc"); - goto quit; - } - plugins[nplugins++] = handle; - } - retval = 0; -quit: - if (dp) - free(dp); - return retval; -} - - -/*! Unload all restconf plugins */ -int -restconf_plugin_unload(clicon_handle h) -{ - int i; - - for (i = 0; i < nplugins; i++) - plugin_unload(h, plugins[i]); - if (plugins){ - free(plugins); - plugins = NULL; - } - nplugins = 0; - return 0; -} - -/*! Call plugin_start in all plugins - */ -int -restconf_plugin_start(clicon_handle h, - int argc, - char **argv) -{ - int i; - plgstart_t *startfn; - - for (i = 0; i < nplugins; i++) { - /* Call exit function is it exists */ - if ((startfn = dlsym(plugins[i], PLUGIN_START)) == NULL) - break; - optind = 0; - if (startfn(h, argc, argv) < 0) { - clicon_debug(1, "plugin_start() failed\n"); - return -1; - } - } - return 0; -} - -/*! Run the restconf user-defined credentials callback if present - * The callback is expected to return the authenticated user, or NULL if not - * authenticasted. - * If no callback exists, return user "none" - * @param[in] h Clicon handle - * @param[in] r Fastcgi request handle - * @param[out] user The authenticated user (or NULL). Malloced, must be freed. - */ -int -restconf_credentials(clicon_handle h, - FCGX_Request *r, - char **user) -{ - int retval = -1; - - clicon_debug(1, "%s", __FUNCTION__); - /* If no authentication callback then allow anything. Is this OK? */ - if (_credentials_fn == NULL){ - if ((*user = strdup("none")) == NULL){ - clicon_err(OE_XML, errno, "strdup"); - goto done; - } - goto ok; - } - if (_credentials_fn(h, r, user) < 0) - *user = NULL; - ok: - retval = 0; - done: - clicon_debug(1, "%s retval:%d user:%s", __FUNCTION__, retval, *user); - return retval; -} - /*! Parse a cookie string and return value of cookie attribute * @param[in] cookiestr cookie string according to rfc6265 (modified) * @param[in] attribute cookie attribute diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index 59e381f4..8162ff9c 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -57,11 +57,6 @@ int notimplemented(FCGX_Request *r); int clicon_debug_xml(int dbglevel, char *str, cxobj *cx); int test(FCGX_Request *r, int dbg); cbuf *readdata(FCGX_Request *r); - -int restconf_plugin_load(clicon_handle h); -int restconf_plugin_start(clicon_handle h, int argc, char **argv); -int restconf_plugin_unload(clicon_handle h); -int restconf_credentials(clicon_handle h, FCGX_Request *r, char **user); int get_user_cookie(char *cookiestr, char *attribute, char **val); diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 309c5223..62f8b6f1 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -92,7 +92,6 @@ * @param[in] pi Offset, where to start pcvec * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] dvec Stream input daat - * @param[in] username Authenticated user */ static int api_data(clicon_handle h, @@ -101,8 +100,7 @@ api_data(clicon_handle h, cvec *pcvec, int pi, cvec *qvec, - char *data, - char *username) + char *data) { int retval = -1; char *request_method; @@ -127,17 +125,17 @@ api_data(clicon_handle h, if (strcmp(request_method, "OPTIONS")==0) retval = api_data_options(h, r); else if (strcmp(request_method, "HEAD")==0) - retval = api_data_head(h, r, pcvec, pi, qvec, username, pretty, use_xml); + retval = api_data_head(h, r, pcvec, pi, qvec, pretty, use_xml); else if (strcmp(request_method, "GET")==0) - retval = api_data_get(h, r, pcvec, pi, qvec, username, pretty, use_xml); + retval = api_data_get(h, r, pcvec, pi, qvec, pretty, use_xml); else if (strcmp(request_method, "POST")==0) - retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, username, pretty, use_xml, parse_xml); + retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml); else if (strcmp(request_method, "PUT")==0) - retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, username, pretty, use_xml, parse_xml); + retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml); else if (strcmp(request_method, "PATCH")==0) - retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data, username); + retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data); else if (strcmp(request_method, "DELETE")==0) - retval = api_data_delete(h, r, api_path, pi, username, pretty, use_xml); + retval = api_data_delete(h, r, api_path, pi, pretty, use_xml); else retval = notfound(r); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); @@ -152,7 +150,6 @@ api_data(clicon_handle h, * @param[in] pi Offset, where to start pcvec * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data - * @param[in] username Authenticated user */ static int api_operations(clicon_handle h, @@ -161,8 +158,7 @@ api_operations(clicon_handle h, cvec *pcvec, int pi, cvec *qvec, - char *data, - char *username) + char *data) { int retval = -1; char *request_method; @@ -185,9 +181,9 @@ api_operations(clicon_handle h, parse_xml++; if (strcmp(request_method, "GET")==0) - retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, username, pretty, use_xml); + retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, use_xml); else if (strcmp(request_method, "POST")==0) - retval = api_operations_post(h, r, path, pcvec, pi, qvec, data, username, + retval = api_operations_post(h, r, path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml); else retval = notfound(r); @@ -338,7 +334,7 @@ api_restconf(clicon_handle h, cvec *pcvec = NULL; /* for rest api */ cbuf *cb = NULL; char *data; - char *username = NULL; + int authenticated = 0; clicon_debug(1, "%s", __FUNCTION__); path = FCGX_GetParam("REQUEST_URI", r->envp); @@ -384,12 +380,14 @@ api_restconf(clicon_handle h, /* If present, check credentials. See "plugin_credentials" in plugin * See RFC 8040 section 2.5 */ - if (restconf_credentials(h, r, &username) < 0) + if ((authenticated = clixon_plugin_auth(h, r)) < 0) goto done; - clicon_debug(1, "%s username:%s", __FUNCTION__, username); - clicon_debug(1, "%s credentials ok username:%s (should be non-NULL)", - __FUNCTION__, username); - if (username == NULL){ + /* If set but no user, we set a dummy user */ + if (authenticated){ + if (clicon_username_get(h) == NULL) + clicon_username_set(h, "none"); + } + else{ unauthorized(r); goto ok; } @@ -398,11 +396,11 @@ api_restconf(clicon_handle h, goto done; } else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */ - if (api_data(h, r, path, pcvec, 2, qvec, data, username) < 0) + if (api_data(h, r, path, pcvec, 2, qvec, data) < 0) goto done; } else if (strcmp(method, "operations") == 0){ /* rpc */ - if (api_operations(h, r, path, pcvec, 2, qvec, data, username) < 0) + if (api_operations(h, r, path, pcvec, 2, qvec, data) < 0) goto done; } else if (strcmp(method, "test") == 0) @@ -423,8 +421,6 @@ api_restconf(clicon_handle h, cvec_free(pcvec); if (cb) cbuf_free(cb); - if (username) - free(username); return retval; } @@ -500,6 +496,7 @@ main(int argc, char *path; clicon_handle h; char *yangspec=NULL; + char *dir; /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_SYSLOG); @@ -556,8 +553,9 @@ main(int argc, clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", yangspec); /* Initialize plugins group */ - if (restconf_plugin_load(h) < 0) - return -1; + if ((dir = clicon_restconf_dir(h)) != NULL) + if (clixon_plugins_load(h, clicon_restconf_dir(h)) < 0) + return -1; /* Parse yang database spec file */ if (yang_spec_main(h) == NULL) @@ -605,7 +603,7 @@ main(int argc, } retval = 0; done: - restconf_plugin_unload(h); + clixon_plugin_unload(h); restconf_terminate(h); return retval; } diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index fde9c4dc..729d01c6 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -227,7 +227,6 @@ api_return_err(clicon_handle h, * @param[in] pcvec Vector of path ie DOCUMENT_URI element * @param[in] pi Offset, where path starts * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML * @param[in] head If 1 is HEAD, otherwise GET @@ -254,7 +253,6 @@ api_data_get2(clicon_handle h, cvec *pcvec, int pi, cvec *qvec, - char *username, int pretty, int use_xml, int head) @@ -284,7 +282,7 @@ api_data_get2(clicon_handle h, } path = cbuf_get(cbpath); clicon_debug(1, "%s path:%s", __FUNCTION__, path); - if (clicon_rpc_get(h, path, username, &xret) < 0){ + if (clicon_rpc_get(h, path, &xret) < 0){ notfound(r); goto ok; } @@ -362,7 +360,6 @@ api_data_get2(clicon_handle h, * @param[in] pcvec Vector of path ie DOCUMENT_URI element * @param[in] pi Offset, where path starts * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML * @@ -377,11 +374,10 @@ api_data_head(clicon_handle h, cvec *pcvec, int pi, cvec *qvec, - char *username, int pretty, int use_xml) { - return api_data_get2(h, r, pcvec, pi, qvec, username, pretty, use_xml, 1); + return api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 1); } /*! REST GET method @@ -391,7 +387,6 @@ api_data_head(clicon_handle h, * @param[in] pcvec Vector of path ie DOCUMENT_URI element * @param[in] pi Offset, where path starts * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML * @code @@ -416,11 +411,10 @@ api_data_get(clicon_handle h, cvec *pcvec, int pi, cvec *qvec, - char *username, int pretty, int use_xml) { - return api_data_get2(h, r, pcvec, pi, qvec, username, pretty, use_xml, 0); + return api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 0); } /*! Generic REST POST method @@ -431,7 +425,6 @@ api_data_get(clicon_handle h, * @param[in] pi Offset, where to start pcvec * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML for output data * @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data @@ -464,7 +457,6 @@ api_data_post(clicon_handle h, int pi, cvec *qvec, char *data, - char *username, int pretty, int use_xml, int parse_xml) @@ -484,6 +476,7 @@ api_data_post(clicon_handle h, cxobj *xret = NULL; cxobj *xretcom = NULL; cxobj *xerr; + char *username; clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"", __FUNCTION__, @@ -501,7 +494,7 @@ api_data_post(clicon_handle h, xbot = xtop; /* For internal XML protocol: add username attribute for backend access control */ - if (username){ + if ((username = clicon_username_get(h)) != NULL){ if ((xu = xml_new("username", xtop, NULL)) == NULL) goto done; xml_type_set(xu, CX_ATTR); @@ -645,7 +638,6 @@ match_list_keys(yang_stmt *y, * @param[in] pi Offset, where to start pcvec * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML for output data * @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data @@ -670,7 +662,6 @@ api_data_put(clicon_handle h, int pi, cvec *qvec, char *data, - char *username, int pretty, int use_xml, int parse_xml) @@ -692,6 +683,7 @@ api_data_put(clicon_handle h, cxobj *xret = NULL; cxobj *xretcom = NULL; cxobj *xerr; + char *username; clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"", __FUNCTION__, api_path0, data); @@ -709,7 +701,7 @@ api_data_put(clicon_handle h, xbot = xtop; /* For internal XML protocol: add username attribute for backend access control */ - if (username){ + if ((username = clicon_username_get(h)) != NULL){ if ((xu = xml_new("username", xtop, NULL)) == NULL) goto done; xml_type_set(xu, CX_ATTR); @@ -824,7 +816,6 @@ api_data_put(clicon_handle h, * @param[in] pi Offset, where to start pcvec * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data - * @param[in] username Authenticated user * Netconf: (nc:operation="merge") * See RFC8040 Sec 4.6 */ @@ -835,8 +826,7 @@ api_data_patch(clicon_handle h, cvec *pcvec, int pi, cvec *qvec, - char *data, - char *username) + char *data) { notimplemented(r); return 0; @@ -847,7 +837,6 @@ api_data_patch(clicon_handle h, * @param[in] r Fastcgi request handle * @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040) * @param[in] pi Offset, where path starts - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML * See RFC 8040 Sec 4.7 @@ -860,7 +849,6 @@ api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi, - char *username, int pretty, int use_xml) { @@ -877,6 +865,7 @@ api_data_delete(clicon_handle h, cxobj *xret = NULL; cxobj *xretcom = NULL; cxobj *xerr; + char *username; clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); if ((yspec = clicon_dbspec_yang(h)) == NULL){ @@ -891,7 +880,7 @@ api_data_delete(clicon_handle h, xbot = xtop; /* For internal XML protocol: add username attribute for backend access control */ - if (username){ + if ((username = clicon_username_get(h)) != NULL){ if ((xu = xml_new("username", xtop, NULL)) == NULL) goto done; xml_type_set(xu, CX_ATTR); @@ -955,7 +944,6 @@ api_data_delete(clicon_handle h, * @param[in] pi Offset, where path starts * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML * @@ -976,7 +964,6 @@ api_operations_get(clicon_handle h, int pi, cvec *qvec, char *data, - char *username, int pretty, int use_xml) { @@ -1047,7 +1034,6 @@ api_operations_get(clicon_handle h, * @param[in] pi Offset, where to start pcvec * @param[in] qvec Vector of query string (QUERY_STRING) * @param[in] data Stream input data - * @param[in] username Authenticated user * @param[in] pretty Set to 1 for pretty-printed xml/json output * @param[in] use_xml Set to 0 for JSON and 1 for XML for output data * @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data @@ -1063,7 +1049,6 @@ api_operations_post(clicon_handle h, int pi, cvec *qvec, char *data, - char *username, int pretty, int use_xml, int parse_xml) @@ -1085,7 +1070,8 @@ api_operations_post(clicon_handle h, cxobj *xoutput; cxobj *x; cxobj *xa; - + char *username; + clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path); if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); @@ -1112,7 +1098,7 @@ api_operations_post(clicon_handle h, xbot = xtop; /* For internal XML protocol: add username attribute for backend access control */ - if (username){ + if ((username = clicon_username_get(h)) != NULL){ if ((xa = xml_new("username", xtop, NULL)) == NULL) goto done; xml_type_set(xa, CX_ATTR); diff --git a/apps/restconf/restconf_methods.h b/apps/restconf/restconf_methods.h index b482d9ce..11daaa26 100644 --- a/apps/restconf/restconf_methods.h +++ b/apps/restconf/restconf_methods.h @@ -46,31 +46,31 @@ */ int api_data_options(clicon_handle h, FCGX_Request *r); int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi, - cvec *qvec, char *username, int pretty, int use_xml); + cvec *qvec, int pretty, int use_xml); int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi, - cvec *qvec, char *username, int pretty, int use_xml); + cvec *qvec, int pretty, int use_xml); int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi, - cvec *qvec, char *data, char *username, + cvec *qvec, char *data, int pretty, int use_xml, int parse_xml); int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi, - cvec *qvec, char *data, char *username, + cvec *qvec, char *data, int pretty, int use_xml, int parse_xml); int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path, cvec *pcvec, int pi, - cvec *qvec, char *data, char *username); + cvec *qvec, char *data); int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi, - char *username, int pretty, int use_xml); + int pretty, int use_xml); int api_operations_get(clicon_handle h, FCGX_Request *r, char *path, - cvec *pcvec, int pi, cvec *qvec, char *data, char *username, + cvec *pcvec, int pi, cvec *qvec, char *data, int pretty, int use_xml); int api_operations_post(clicon_handle h, FCGX_Request *r, char *path, cvec *pcvec, int pi, cvec *qvec, char *data, - char *username, int pretty, int use_xml, int parse_xml); + int pretty, int use_xml, int parse_xml); #endif /* _RESTCONF_METHODS_H_ */ diff --git a/example/routing_cli.c b/example/routing_cli.c index 62e2bc37..8610bd01 100644 --- a/example/routing_cli.c +++ b/example/routing_cli.c @@ -81,7 +81,7 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv) /* Show eth0 interfaces config using XPATH */ if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]", - NULL, &xret) < 0) + &xret) < 0) goto done; xml_print(stdout, xret); diff --git a/example/routing_netconf.c b/example/routing_netconf.c index 6ee613fe..65883e2c 100644 --- a/example/routing_netconf.c +++ b/example/routing_netconf.c @@ -46,18 +46,7 @@ #include #include - -/* - * Plugin initialization - */ -int -plugin_init(clicon_handle h) -{ - return 0; -} - -/* - * Plugin start +/*! Plugin start * Called once everything has been initialized, right before * the main event loop is entered. */ @@ -73,3 +62,21 @@ plugin_exit(clicon_handle h) return 0; } +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 +}; + +/*! Netconf plugin initialization + */ +clixon_plugin_api * +clixon_plugin_init(clicon_handle h) +{ + return (void*)&api; +} + diff --git a/example/routing_restconf.c b/example/routing_restconf.c index 5ecc475d..d3340a2f 100644 --- a/example/routing_restconf.c +++ b/example/routing_restconf.c @@ -187,15 +187,16 @@ b64_decode(const char *src, * @param[in] r Fastcgi request handle * @param[out] username Malloced username, or NULL. * @retval -1 Fatal error - * @retval 0 OK + * @retval 0 Unauth + * @retval 1 Auth * For grideye, return "u" entry name if it has a valid "user" entry. */ int plugin_credentials(clicon_handle h, - FCGX_Request *r, - char **username) + void *arg) { int retval = -1; + FCGX_Request *r = (FCGX_Request *)arg; cxobj *xt = NULL; cxobj *x; char *xbody; @@ -208,19 +209,18 @@ plugin_credentials(clicon_handle h, int ret; clicon_debug(1, "%s", __FUNCTION__); - *username = NULL; /* unauthorized */ /* Check if basic_auth set, if not return OK */ - if (clicon_rpc_get_config(h, "running", "/", NULL, &xt) < 0) + if (clicon_rpc_get_config(h, "running", "/", &xt) < 0) goto done; if ((x = xpath_first(xt, "basic_auth")) == NULL) - goto none; + goto ok; if ((xbody = xml_body(x)) == NULL) - goto none; + goto ok; if (strcmp(xbody, "true")) - goto none; + goto ok; /* At this point in the code we must use HTTP basic authentication */ if ((auth = FCGX_GetParam("HTTP_AUTHORIZATION", r->envp)) == NULL) - goto done; + goto fail; if (strlen(auth) < strlen("Basic ")) goto fail; if (strncmp("Basic ", auth, strlen("Basic "))) @@ -245,17 +245,15 @@ plugin_credentials(clicon_handle h, cprintf(cb, "auth[user=%s]", user); if ((x = xpath_first(xt, cbuf_get(cb))) == NULL) goto fail; - passwd2 = xml_find_body(x, "password"); if (strcmp(passwd, passwd2)) goto fail; - if ((*username = strdup(user)) == NULL){ - clicon_err(OE_UNIX, errno, "strdup"); + retval = 1; + if (clicon_username_set(h, user) < 0) goto done; - } - fail: - retval = 0; - done: + ok: /* authenticated */ + retval = 1; + done: /* error */ clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); if (user) free(user); @@ -264,24 +262,26 @@ plugin_credentials(clicon_handle h, if (xt) xml_free(xt); return retval; - none: /* basic_auth is not enabled, harcode authenticated user "none" */ - if ((*username = strdup("none")) == NULL){ - clicon_err(OE_XML, errno, "strdup"); - goto done; - } - goto fail; + fail: /* unauthenticated */ + retval = 0; + goto done; } +clixon_plugin_api * clixon_plugin_init(clicon_handle h); + +static const struct clixon_plugin_api api = { + "example", + clixon_plugin_init, + NULL, + NULL, + plugin_credentials, +}; /*! Restconf plugin initialization */ -int -plugin_init(clicon_handle h) +clixon_plugin_api * +clixon_plugin_init(clicon_handle h) { - int retval = -1; - clicon_debug(1, "%s restconf", __FUNCTION__); - retval = 0; - // done: - return retval; + return (void*)&api; } diff --git a/lib/clixon/clixon_handle.h b/lib/clixon/clixon_handle.h index d56b31dd..897560c3 100644 --- a/lib/clixon/clixon_handle.h +++ b/lib/clixon/clixon_handle.h @@ -50,6 +50,9 @@ typedef struct {float a;} *clicon_handle; typedef void *clicon_handle; #endif +/* The dynamicically loadable plugin object handle (should be in clixon_plugin.h) */ +typedef void *plghndl_t; + /* * Prototypes */ diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 6727c157..490e152c 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -179,4 +179,9 @@ int clicon_xmldb_api_set(clicon_handle h, void *xa_api); void *clicon_xmldb_handle_get(clicon_handle h); int clicon_xmldb_handle_set(clicon_handle h, void *xh); +/**/ +/* Set and get authorized user name */ +char *clicon_username_get(clicon_handle h); +int clicon_username_set(clicon_handle h, void *username); + #endif /* _CLIXON_OPTIONS_H_ */ diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index 48169df9..3c9fb28f 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -44,13 +44,6 @@ /* The dynamicically loadable plugin object handle */ typedef void *plghndl_t; -/* Find plugin by name callback. XXX Should be clicon internal */ -typedef void *(find_plugin_t)(clicon_handle, char *); - - - - - /* * Prototypes */ @@ -64,6 +57,7 @@ typedef void *(find_plugin_t)(clicon_handle, char *); * @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. @@ -77,36 +71,86 @@ typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */ #define PLUGIN_EXIT "plugin_exit" typedef int (plgexit_t)(clicon_handle); /* Plugin exit */ -/*! Called by restconf to ceck credentials and return username +/*! Called by restconf to check credentials and return username */ #define PLUGIN_CREDENTIALS "plugin_credentials" -/* Plugin credentials +/* Plugin authorization. Set username option (or not) * @param[in] Clicon handle * @param[in] void*, eg Fastcgihandle request restconf - * @param[out] username should be freed after use * @retval 0 if credentials OK * @retval -1 credentials not OK */ -typedef int (plgcredentials_t)(clicon_handle, void *, char **username); +typedef int (plgauth_t)(clicon_handle, void *); +typedef int (plgreset_t)(clicon_handle h, const char *db); /* Reset system status */ +typedef int (plgstatedata_t)(clicon_handle h, char *xpath, cxobj *xtop); -/* grideye agent plugin init struct for the api - * Note: Implicit init function, see PLUGIN_INIT_FN_V2 +typedef void *transaction_data; + +/* Transaction callbacks */ +typedef int (trans_cb_t)(clicon_handle h, transaction_data td); + +/* plugin init struct for the api + * Note: Implicit init function */ +struct clixon_plugin_api; +typedef struct clixon_plugin_api* (plginit2_t)(clicon_handle); /* Clixon plugin Init */ + struct clixon_plugin_api{ - plgcredentials_t *cp_auth; + 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 */ }; +struct clixon_backend_api{ + char ca_name[PATH_MAX]; /* Name of plugin */ + plginit2_t *ca_init; /* Clixon plugin Init (implicit) */ + plgstart_t *ca_start; /* Plugin start */ + plgexit_t *ca_exit; /* Plugin exit */ + plgauth_t *ca_auth; /* Auth credentials */ + /*--Above here common fields with clixon_netconf_api, clixon_restconf_api, + * etc ----------*/ + plgreset_t *ca_reset; /* Reset system status (backend only) */ + + 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 */ + trans_cb_t *ca_trans_complete; /* Transaction validation complete */ + trans_cb_t *ca_trans_commit; /* Transaction commit */ + trans_cb_t *ca_trans_end; /* Transaction completed */ + trans_cb_t *ca_trans_abort; /* Transaction aborted */ +}; +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 + */ + +/* Internal plugin structure with dlopen() handle and plugin_api + */ +struct clixon_plugin{ + plghndl_t cp_handle; /* Handle to plugin using dlopen(3) */ + struct clixon_plugin_api cp_api; +}; +typedef struct clixon_plugin clixon_plugin; + /* * Prototypes */ - -/* Find a function in global namespace or a plugin. XXX clicon internal */ -void *clicon_find_func(clicon_handle h, char *plugin, char *func); +int clixon_plugins_load(clicon_handle h, char *dir); plghndl_t plugin_load (clicon_handle h, char *file, int dlflags); 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_auth(clicon_handle h, void *arg); + #endif /* _CLIXON_PLUGIN_H_ */ diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index 7adccc68..ac917d77 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -45,14 +45,14 @@ int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0, int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp); int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp); int clicon_rpc_generate_error(char *format, cxobj *xerr); -int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, char *username, cxobj **xret); +int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, cxobj **xret); int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op, char *xml); int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2); int clicon_rpc_delete_config(clicon_handle h, char *db); int clicon_rpc_lock(clicon_handle h, char *db); int clicon_rpc_unlock(clicon_handle h, char *db); -int clicon_rpc_get(clicon_handle h, char *xpath, char *username, cxobj **xret); +int clicon_rpc_get(clicon_handle h, char *xpath, cxobj **xret); int clicon_rpc_close_session(clicon_handle h); int clicon_rpc_kill_session(clicon_handle h, int session_id); int clicon_rpc_validate(clicon_handle h, char *db); diff --git a/lib/src/clixon_handle.c b/lib/src/clixon_handle.c index beff1c41..edac1382 100644 --- a/lib/src/clixon_handle.c +++ b/lib/src/clixon_handle.c @@ -50,9 +50,9 @@ #include "clixon_queue.h" #include "clixon_hash.h" #include "clixon_handle.h" +#include "clixon_log.h" #include "clixon_err.h" #include "clixon_yang.h" -#include "clixon_plugin.h" #include "clixon_options.h" #define CLICON_MAGIC 0x99aafabe @@ -61,15 +61,19 @@ /*! Internal structure of basic handle. Also header of all other handles. * @note If you change here, you must also change the structs below: - * @see struct cli_handle, struct backend_handle + * @see struct cli_handle + * @see struct backend_handle */ struct clicon_handle { - int ch_magic; /* magic (HDR) */ - clicon_hash_t *ch_copt; /* clicon option list (HDR) */ - clicon_hash_t *ch_data; /* internal clicon data (HDR) */ + int ch_magic; /* magic (HDR) */ + clicon_hash_t *ch_copt; /* clicon option list (HDR) */ + clicon_hash_t *ch_data; /* internal clicon data (HDR) */ }; /*! Internal call to allocate a CLICON handle. + * + * @param[in] size Size of handle (internal) struct. + * @retval h Clicon handle * * There may be different variants of handles with some common options. * So far the only common options is a MAGIC cookie for sanity checks and @@ -102,6 +106,7 @@ clicon_handle_init0(int size) /*! Basic CLICON init functions returning a handle for API access. * + * @retval h Clicon handle * This is the first call to CLICON basic API which returns a handle to be * used in the API functions. There are other clicon_init functions for more * elaborate applications (cli/backend/netconf). This should be used by the most @@ -114,6 +119,7 @@ clicon_handle_init(void) } /*! Deallocate clicon handle, including freeing handle data. + * @param[in] h Clicon handle * @Note: handle 'h' cannot be used in calls after this */ int @@ -131,9 +137,10 @@ clicon_handle_exit(clicon_handle h) return 0; } -/* - * Check struct magic number for sanity checks - * return 0 if OK, -1 if fail. +/*! Check struct magic number for sanity checks + * @param[in] h Clicon handle + * @retval 0 Sanity check OK + * @retval -1 Sanity check failed */ int clicon_handle_check(clicon_handle h) @@ -144,8 +151,8 @@ clicon_handle_check(clicon_handle h) return ch->ch_magic == CLICON_MAGIC ? 0 : -1; } -/* - * Return clicon options (hash-array) given a handle. +/*! Return clicon options (hash-array) given a handle. + * @param[in] h Clicon handle */ clicon_hash_t * clicon_options(clicon_handle h) @@ -155,8 +162,8 @@ clicon_options(clicon_handle h) return ch->ch_copt; } -/* - * Return clicon data (hash-array) given a handle. +/*! Return clicon data (hash-array) given a handle. + * @param[in] h Clicon handle */ clicon_hash_t * clicon_data(clicon_handle h) @@ -165,4 +172,3 @@ clicon_data(clicon_handle h) return ch->ch_data; } - diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index 6c528662..43611c6a 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -64,9 +64,9 @@ #include "clixon_handle.h" #include "clixon_log.h" #include "clixon_yang.h" -#include "clixon_plugin.h" #include "clixon_options.h" #include "clixon_xml.h" +#include "clixon_plugin.h" #include "clixon_xsl.h" #include "clixon_xml_map.h" @@ -715,3 +715,32 @@ clicon_xmldb_handle_set(clicon_handle h, } +/*! Get authorized user name + * @param[in] h Clicon handle + * @retval xh XMLDB storage handle. If not connected return NULL + */ +char * +clicon_username_get(clicon_handle h) +{ + clicon_hash_t *cdat = clicon_data(h); + + return (char*)hash_value(cdat, "username", NULL); +} + +/*! Set authorized user name + * @param[in] h Clicon handle + * @param[in] xh XMLDB storage handle. If NULL reset it + * @note Just keep note of it, dont allocate it or so. + */ +int +clicon_username_set(clicon_handle h, + void *username) +{ + clicon_hash_t *cdat = clicon_data(h); + + if (username == NULL) + return hash_del(cdat, "username"); + return hash_add(cdat, "username", username, strlen(username)+1)==NULL?-1:0; +} + + diff --git a/lib/src/clixon_plugin.c b/lib/src/clixon_plugin.c index 39e78e7f..6a5f589e 100644 --- a/lib/src/clixon_plugin.c +++ b/lib/src/clixon_plugin.c @@ -38,52 +38,130 @@ #include #include +#include #include #include #include +#include + +#include +#include + +/* cligen */ +#include #include "clixon_err.h" #include "clixon_queue.h" #include "clixon_hash.h" #include "clixon_log.h" +#include "clixon_file.h" #include "clixon_handle.h" +#include "clixon_yang.h" +#include "clixon_xml.h" #include "clixon_plugin.h" +/* XXX The below should be placed in clixon handle when done */ +static clixon_plugin *_clixon_plugins = NULL; /* List of plugins (of client) */ +static int _clixon_nplugins = 0; /* Number of plugins */ -static find_plugin_t * -clicon_find_plugin(clicon_handle h) +/*! Load a dynamic plugin object and call its init-function + * @param[in] h Clicon handle + * @param[in] file Which plugin to load + * @param[in] dlflags See man(3) dlopen + * @retval cp Clixon plugin structure + * @retval NULL Error + * @see clixon_plugins_load Load all plugins + */ +static clixon_plugin * +plugin_load_one(clicon_handle h, + char *file, + int dlflags) { - void *p; - find_plugin_t *fp = NULL; - clicon_hash_t *data = clicon_data(h); - - if ((p = hash_value(data, "CLICON_FIND_PLUGIN", NULL)) != NULL) - memcpy(&fp, p, sizeof(fp)); + char *error; + void *handle = NULL; + plginit2_t *initfn; + clixon_plugin_api *api = NULL; + clixon_plugin *cp = NULL; - return fp; + 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, CLIXON_PLUGIN_INIT)) == NULL){ + clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file); + goto err; + } + if ((error = (char*)dlerror()) != NULL) { + clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error); + goto done; + } + if ((api = initfn(h)) == NULL) { + 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; + } + if ((cp = (clixon_plugin *)malloc(sizeof(*cp))) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + cp->cp_handle = handle; + cp->cp_api = *api; + clicon_debug(1, "%s", __FUNCTION__); + done: + return cp; + err: + if (handle) + dlclose(handle); + return NULL; } - -/*! Return a function pointer based on name of plugin and function. - * If plugin is specified, ask daemon registered function to return - * the dlsym handle of the plugin. +/*! Load a set of plugin objects from a directory and and call their init-function + * @param[in] h Clicon handle + * @param[in] dir Directory. .so files in this dir will be loaded. + * @retval 0 OK + * @retval -1 Error */ -void * -clicon_find_func(clicon_handle h, char *plugin, char *func) +int +clixon_plugins_load(clicon_handle h, + char *dir) { - find_plugin_t *plgget; - void *dlhandle = NULL; + int retval = -1; + int ndp; + struct dirent *dp = NULL; + int i; + char filename[MAXPATHLEN]; + clixon_plugin *cp; - if (plugin) { - /* find clicon_plugin_get() in global namespace */ - if ((plgget = clicon_find_plugin(h)) == NULL) { - clicon_err(OE_UNIX, errno, "Specified plugin not supported"); - return NULL; + clicon_debug(1, "%s", __FUNCTION__); + /* Get plugin objects names from plugin directory */ + if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0) + goto done; + /* Load all plugins */ + for (i = 0; i < ndp; i++) { + 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) + goto done; + _clixon_nplugins++; + if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) { + clicon_err(OE_UNIX, errno, "realloc"); + goto done; } - dlhandle = plgget(h, plugin); + _clixon_plugins[_clixon_nplugins-1] = *cp; + free(cp); } - - return dlsym(dlhandle, func); + retval = 0; +done: + if (dp) + free(dp); + return retval; } /*! Load a dynamic plugin object and call its init-function @@ -91,6 +169,7 @@ clicon_find_func(clicon_handle h, char *plugin, char *func) * @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 */ plghndl_t plugin_load(clicon_handle h, @@ -103,14 +182,14 @@ plugin_load(clicon_handle h, 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, PLUGIN_INIT)) == NULL){ - clicon_err(OE_PLUGIN, errno, "Failed to find plugin_init when loading restconf plugin %s", file); + clicon_err(OE_PLUGIN, errno, "Failed to find plugin_init when loading clixon plugin %s", file); goto err; } if ((error = (char*)dlerror()) != NULL) { @@ -158,3 +237,81 @@ 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 + */ +int +clixon_plugin_start(clicon_handle h, + int argc, + char **argv) +{ + clixon_plugin *cp; + int i; + plgstart_t *startfn; /* Plugin start */ + + for (i = 0; i < _clixon_nplugins; i++) { + cp = &_clixon_plugins[i]; + if ((startfn = cp->cp_api.ca_start) == NULL) + continue; + if (startfn(h, argc, argv) < 0) { + clicon_debug(1, "plugin_start() failed\n"); + return -1; + } + } + 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 + * @param[in] h Clicon handle + * @param[in] arg Argument, such as fastcgi handler for restconf + * @retval -1 Error + * @retval 0 Not authenticated + * @retval 1 Authenticated + * @note If authenticated either a callback was called and clicon_username_set() + * Or no callback was found. + */ +int +clixon_plugin_auth(clicon_handle h, + void *arg) +{ + clixon_plugin *cp; + int i; + plgauth_t *authfn; /* Plugin auth */ + int retval = 1; + + for (i = 0; i < _clixon_nplugins; i++) { + cp = &_clixon_plugins[i]; + if ((authfn = cp->cp_api.ca_auth) == NULL) + continue; + if ((retval = authfn(h, arg)) < 0) { + clicon_debug(1, "plugin_start() failed\n"); + return -1; + } + break; + } + return retval; +} diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index cb150c0d..7fd7e08f 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -60,9 +61,9 @@ #include "clixon_hash.h" #include "clixon_handle.h" #include "clixon_yang.h" -#include "clixon_plugin.h" #include "clixon_options.h" #include "clixon_xml.h" +#include "clixon_plugin.h" #include "clixon_xsl.h" #include "clixon_proto.h" #include "clixon_err.h" @@ -236,14 +237,13 @@ clicon_rpc_generate_error(char *format, * @param[in] h CLICON handle * @param[in] db Name of database * @param[in] xpath XPath (or "") - * @param[in] username Authenticated user (extra attribute) * @param[out] xt XML tree. Free with xml_free. * Either or . * @retval 0 OK * @retval -1 Error, fatal or xml * @code * cxobj *xt = NULL; - * if (clicon_rpc_get_config(h, "running", "/", username, &xt) < 0) + * if (clicon_rpc_get_config(h, "running", "/", &xt) < 0) * err; * if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){ * clicon_rpc_generate_error("", xerr); @@ -258,7 +258,6 @@ int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, - char *username, cxobj **xt) { int retval = -1; @@ -266,11 +265,12 @@ clicon_rpc_get_config(clicon_handle h, cbuf *cb = NULL; cxobj *xret = NULL; cxobj *xd; - + char *username; + if ((cb = cbuf_new()) == NULL) goto done; cprintf(cb, "<%s/>", db); if (xpath && strlen(xpath)) @@ -498,7 +498,6 @@ clicon_rpc_unlock(clicon_handle h, /*! Get database configuration and state data * @param[in] h CLICON handle * @param[in] xpath XPath (or "") - * @param[in] username Authenticated user (extra attribute) * @param[out] xt XML tree. Free with xml_free. * Either or . * @retval 0 OK @@ -519,7 +518,6 @@ clicon_rpc_unlock(clicon_handle h, int clicon_rpc_get(clicon_handle h, char *xpath, - char *username, cxobj **xt) { int retval = -1; @@ -527,11 +525,12 @@ clicon_rpc_get(clicon_handle h, cbuf *cb = NULL; cxobj *xret = NULL; cxobj *xd; + char *username; if ((cb = cbuf_new()) == NULL) goto done; cprintf(cb, ""); if (xpath && strlen(xpath)) diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index 42e5c870..8702910d 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -43,9 +43,11 @@ #include #include #include +#include #include #include -#include +#include + /* cligen */ #include diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index 577fd298..bf64d04d 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -61,10 +61,11 @@ #include #include #include -#include -#include #include #include +#include +#include +#include #include /* cligen */ @@ -79,9 +80,9 @@ #include "clixon_string.h" #include "clixon_yang.h" #include "clixon_yang_type.h" -#include "clixon_plugin.h" #include "clixon_options.h" #include "clixon_xml.h" +#include "clixon_plugin.h" #include "clixon_xsl.h" #include "clixon_log.h" #include "clixon_err.h" @@ -833,7 +834,6 @@ yang2api_path_fmt(yang_stmt *ys, return retval; } - /*! Transform an xml key format and a vector of values to an XML key * Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey() * Example: diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 2ecae84b..c704134f 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -66,6 +66,7 @@ #include "clixon_file.h" #include "clixon_yang.h" #include "clixon_hash.h" +#include "clixon_xml.h" #include "clixon_plugin.h" #include "clixon_options.h" #include "clixon_yang_type.h" diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 2494cf5c..1d7f57d4 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -63,6 +63,7 @@ #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_hash.h" +#include "clixon_xml.h" #include "clixon_plugin.h" #include "clixon_options.h" #include "clixon_yang.h" diff --git a/test/lib.sh b/test/lib.sh index 971b1088..6a6f0e0b 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -11,7 +11,7 @@ clixon_cli=clixon_cli # For memcheck / performance #clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" -# clixon_netconf="valgrind --tool=callgrind clixon_netconf +#clixon_netconf="valgrind --tool=callgrind clixon_netconf" clixon_netconf=clixon_netconf # How to run restconf stand-alone and using valgrind diff --git a/test/test_netconf.sh b/test/test_netconf.sh index dae18487..71578145 100755 --- a/test/test_netconf.sh +++ b/test/test_netconf.sh @@ -39,8 +39,7 @@ if [ $? -ne 0 ]; then err fi -new "netconf tests" - +new "netconf get-config" expecteof "$clixon_netconf -qf $cfg" ']]>]]>' '^]]>]]>$' new "Add subtree eth/0/0 using none which should not change anything" diff --git a/test/test_perf.sh b/test/test_perf.sh index ece6a3b6..dcbaa95f 100755 --- a/test/test_perf.sh +++ b/test/test_perf.sh @@ -72,7 +72,7 @@ new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df $cfg -y $fyang +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang # -D sleep 1 diff --git a/test/test_restconf2.sh b/test/test_restconf2.sh index 7f8bdb7a..b31f662b 100755 --- a/test/test_restconf2.sh +++ b/test/test_restconf2.sh @@ -59,7 +59,7 @@ new "kill old restconf daemon" sudo pkill -u www-data clixon_restconf new "start restconf daemon" -sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df $cfg +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg # -D sleep 1 diff --git a/yang/clixon-config@2018-02-12.yang b/yang/clixon-config@2018-02-12.yang index 277c204d..d710fae3 100644 --- a/yang/clixon-config@2018-02-12.yang +++ b/yang/clixon-config@2018-02-12.yang @@ -241,7 +241,7 @@ module clixon-config { type string; default "master"; description - "Name of master plugin (both frontend and backend). + "Name of master plugin (cli, netconf, restconf and backend). Master plugin has special callbacks for frontends. See clicon user manual for more info. (Obsolete?)"; }