* 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

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

View file

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

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,6 +1070,7 @@ 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){
@ -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_ */

View file

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

View file

@ -46,18 +46,7 @@
#include <clixon/clixon.h>
#include <clixon/clixon_netconf.h>
/*
* 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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -38,52 +38,130 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/param.h>
/* cligen */
#include <cligen/cligen.h>
#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);
char *error;
void *handle = NULL;
plginit2_t *initfn;
clixon_plugin_api *api = NULL;
clixon_plugin *cp = NULL;
if ((p = hash_value(data, "CLICON_FIND_PLUGIN", NULL)) != NULL)
memcpy(&fp, p, sizeof(fp));
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;
}

View file

@ -47,6 +47,7 @@
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/syslog.h>
@ -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 <config> or <rpc-error>.
* @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, "<rpc");
if (username)
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
cprintf(cb, "><get-config><source><%s/></source>", 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 <config> or <rpc-error>.
* @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, "<rpc");
if (username)
if ((username = clicon_username_get(h)) != NULL)
cprintf(cb, " username=\"%s\"", username);
cprintf(cb, "><get>");
if (xpath && strlen(xpath))

View file

@ -43,9 +43,11 @@
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <libgen.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <libgen.h>
#include <sys/param.h>
/* cligen */
#include <cligen/cligen.h>

View file

@ -61,10 +61,11 @@
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <arpa/inet.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <assert.h>
#include <arpa/inet.h>
#include <sys/param.h>
#include <netinet/in.h>
/* 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:

View file

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

View file

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

View file

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

View file

@ -39,8 +39,7 @@ if [ $? -ne 0 ]; then
err
fi
new "netconf tests"
new "netconf get-config"
expecteof "$clixon_netconf -qf $cfg" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
new "Add subtree eth/0/0 using none which should not change anything"

View file

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

View file

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

View file

@ -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?)";
}