* 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)
This commit is contained in:
Olof hagsand 2018-04-02 10:38:53 +02:00
parent b8e35742b9
commit 79e3fbdaa9
41 changed files with 470 additions and 772 deletions

View file

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

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

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

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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());

View file

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#include <assert.h>
#include <dlfcn.h>
#include <dirent.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
/* 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: <rpc><xn></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;
}

View file

@ -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_ */

View file

@ -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, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"

View file

@ -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@

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

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

View file

@ -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: <edit-config> (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);

View file

@ -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_ */