Added connect/disconnect/getopt/setopt and handle to xmldb API; Added datastore 'text'; Moved apps/dbctrl to datastore/

This commit is contained in:
Olof hagsand 2017-04-15 13:53:58 +02:00
parent 85af4342dc
commit d26a801bc0
25 changed files with 1501 additions and 912 deletions

View file

@ -29,6 +29,10 @@
# #
# ***** END LICENSE BLOCK ***** # ***** END LICENSE BLOCK *****
- Moved apps/dbctrl to datastore/
- Added connect/disconnect/getopt/setopt and handle to xmldb API
- Added datastore 'text' - Added datastore 'text'
- Configure (autoconf) changes - Configure (autoconf) changes

View file

@ -43,7 +43,6 @@ SHELL = /bin/sh
SUBDIRS = backend SUBDIRS = backend
SUBDIRS += cli SUBDIRS += cli
SUBDIRS += dbctrl
SUBDIRS += netconf SUBDIRS += netconf
ifeq ($(with_restconf),yes) ifeq ($(with_restconf),yes)
SUBDIRS += restconf SUBDIRS += restconf

View file

@ -41,7 +41,7 @@
* Prototypes * Prototypes
* not exported. * not exported.
*/ */
/* backend handles */ /* backend handles. Defined in clixon_backend_handle.c */
clicon_handle backend_handle_init(void); clicon_handle backend_handle_init(void);
int backend_handle_exit(clicon_handle h); int backend_handle_exit(clicon_handle h);

View file

@ -254,19 +254,20 @@ server_socket(clicon_handle h)
* log event. * log event.
*/ */
static int static int
config_log_cb(int level, char *msg, void *arg) config_log_cb(int level,
char *msg,
void *arg)
{ {
int retval = -1;
size_t n; size_t n;
char *ptr; char *ptr;
char *nptr; char *nptr;
char *newmsg = NULL; char *newmsg = NULL;
int retval = -1;
/* backend_notify() will go through all clients and see if any has registered "CLICON", /* backend_notify() will go through all clients and see if any has
and if so make a clicon_proto notify message to those clients. */ registered "CLICON", and if so make a clicon_proto notify message to
those clients.
Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
/* Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
At this stage all formatting is already done */ At this stage all formatting is already done */
n = 0; n = 0;
for(ptr=msg; *ptr; ptr++) for(ptr=msg; *ptr; ptr++)
@ -281,7 +282,6 @@ config_log_cb(int level, char *msg, void *arg)
if (*ptr == '%') if (*ptr == '%')
*nptr++ = '%'; *nptr++ = '%';
} }
retval = backend_notify(arg, "CLICON", level, newmsg); retval = backend_notify(arg, "CLICON", level, newmsg);
free(newmsg); free(newmsg);
@ -509,13 +509,21 @@ main(int argc, char **argv)
clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).\n"); clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).\n");
goto done; goto done;
} }
if (xmldb_plugin_load(xmldb_plugin) < 0) if (xmldb_plugin_load(h, xmldb_plugin) < 0)
goto done;
/* Connect to plugin to get a handle */
if (xmldb_connect(h) < 0)
goto done; goto done;
/* Parse db spec file */ /* Parse db spec file */
if (yang_spec_main(h, stdout, printspec) < 0) if (yang_spec_main(h, stdout, printspec) < 0)
goto done; goto done;
/* Set options: database dir aqnd yangspec (could be hidden in connect?)*/
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
goto done;
if (xmldb_setopt(h, "yangspec", clicon_dbspec_yang(h)) < 0)
goto done;
/* First check for startup config /* First check for startup config
XXX the options below have become out-of-hand. XXX the options below have become out-of-hand.
Too complex, need to simplify*/ Too complex, need to simplify*/

View file

@ -75,14 +75,21 @@
* This file should only contain access functions for the _specific_ * This file should only contain access functions for the _specific_
* entries in the struct below. * entries in the struct below.
*/ */
/*! Backend specific handle added to header CLICON handle
* This file should only contain access functions for the _specific_
* entries in the struct below.
* @note The top part must be equivalent to struct clicon_handle in clixon_handle.c
* @see struct clicon_handle, struct cli_handle
*/
struct backend_handle { struct backend_handle {
int cb_magic; /* magic (HDR)*/ int bh_magic; /* magic (HDR)*/
clicon_hash_t *cb_copt; /* clicon option list (HDR) */ clicon_hash_t *bh_copt; /* clicon option list (HDR) */
clicon_hash_t *cb_data; /* internal clicon data (HDR) */ clicon_hash_t *bh_data; /* internal clicon data (HDR) */
void *bh_xmldb; /* XMLDB storage handle, uie xmldb_handle */
/* ------ end of common handle ------ */ /* ------ end of common handle ------ */
struct client_entry *cb_ce_list; /* The client list */ struct client_entry *bh_ce_list; /* The client list */
int cb_ce_nr; /* Number of clients, just increment */ int bh_ce_nr; /* Number of clients, just increment */
struct handle_subscription *cb_subscription; /* Event subscription list */ struct handle_subscription *bh_subscription; /* Event subscription list */
}; };
/*! Creates and returns a clicon config handle for other CLICON API calls /*! Creates and returns a clicon config handle for other CLICON API calls
@ -257,11 +264,17 @@ backend_notify_xml(clicon_handle h,
} }
/*! Add new client, typically frontend such as cli, netconf, restconf
* @param[in] h Clicon handle
* @param[in] addr Address of client
* @retval ce Client entry
* @retval NULL Error
*/
struct client_entry * struct client_entry *
backend_client_add(clicon_handle h, backend_client_add(clicon_handle h,
struct sockaddr *addr) struct sockaddr *addr)
{ {
struct backend_handle *cb = handle(h); struct backend_handle *bh = handle(h);
struct client_entry *ce; struct client_entry *ce;
if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){ if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){
@ -269,24 +282,28 @@ backend_client_add(clicon_handle h,
return NULL; return NULL;
} }
memset(ce, 0, sizeof(*ce)); memset(ce, 0, sizeof(*ce));
ce->ce_nr = cb->cb_ce_nr++; ce->ce_nr = bh->bh_ce_nr++;
memcpy(&ce->ce_addr, addr, sizeof(*addr)); memcpy(&ce->ce_addr, addr, sizeof(*addr));
ce->ce_next = cb->cb_ce_list; ce->ce_next = bh->bh_ce_list;
cb->cb_ce_list = ce; bh->bh_ce_list = ce;
return ce; return ce;
} }
/*! Return client list
* @param[in] h Clicon handle
* @retval ce_list Client entry list (all sessions)
*/
struct client_entry * struct client_entry *
backend_client_list(clicon_handle h) backend_client_list(clicon_handle h)
{ {
struct backend_handle *cb = handle(h); struct backend_handle *bh = handle(h);
return cb->cb_ce_list; return bh->bh_ce_list;
} }
/*! Actually remove client from client list /*! Actually remove client from client list
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] ce Client hadnle * @param[in] ce Client handle
* @see backend_client_rm which is more high-level * @see backend_client_rm which is more high-level
*/ */
int int
@ -295,9 +312,9 @@ backend_client_delete(clicon_handle h,
{ {
struct client_entry *c; struct client_entry *c;
struct client_entry **ce_prev; struct client_entry **ce_prev;
struct backend_handle *cb = handle(h); struct backend_handle *bh = handle(h);
ce_prev = &cb->cb_ce_list; ce_prev = &bh->bh_ce_list;
for (c = *ce_prev; c; c = c->ce_next){ for (c = *ce_prev; c; c = c->ce_next){
if (c == ce){ if (c == ce){
*ce_prev = c->ce_next; *ce_prev = c->ce_next;
@ -328,7 +345,7 @@ subscription_add(clicon_handle h,
subscription_fn_t fn, subscription_fn_t fn,
void *arg) void *arg)
{ {
struct backend_handle *cb = handle(h); struct backend_handle *bh = handle(h);
struct handle_subscription *hs = NULL; struct handle_subscription *hs = NULL;
if ((hs = malloc(sizeof(*hs))) == NULL){ if ((hs = malloc(sizeof(*hs))) == NULL){
@ -339,10 +356,10 @@ subscription_add(clicon_handle h,
hs->hs_stream = strdup(stream); hs->hs_stream = strdup(stream);
hs->hs_format = format; hs->hs_format = format;
hs->hs_filter = filter?strdup(filter):NULL; hs->hs_filter = filter?strdup(filter):NULL;
hs->hs_next = cb->cb_subscription; hs->hs_next = bh->bh_subscription;
hs->hs_fn = fn; hs->hs_fn = fn;
hs->hs_arg = arg; hs->hs_arg = arg;
cb->cb_subscription = hs; bh->bh_subscription = hs;
done: done:
return hs; return hs;
} }
@ -362,11 +379,11 @@ subscription_delete(clicon_handle h,
subscription_fn_t fn, subscription_fn_t fn,
void *arg) void *arg)
{ {
struct backend_handle *cb = handle(h); struct backend_handle *bh = handle(h);
struct handle_subscription *hs; struct handle_subscription *hs;
struct handle_subscription **hs_prev; struct handle_subscription **hs_prev;
hs_prev = &cb->cb_subscription; /* this points to stack and is not real backpointer */ hs_prev = &bh->bh_subscription; /* this points to stack and is not real backpointer */
for (hs = *hs_prev; hs; hs = hs->hs_next){ for (hs = *hs_prev; hs; hs = hs->hs_next){
/* XXX arg == hs->hs_arg */ /* XXX arg == hs->hs_arg */
if (strcmp(hs->hs_stream, stream)==0 && hs->hs_fn == fn){ if (strcmp(hs->hs_stream, stream)==0 && hs->hs_fn == fn){
@ -404,15 +421,16 @@ struct handle_subscription *
subscription_each(clicon_handle h, subscription_each(clicon_handle h,
struct handle_subscription *hprev) struct handle_subscription *hprev)
{ {
struct backend_handle *cb = handle(h); struct backend_handle *bh = handle(h);
struct handle_subscription *hs = NULL; struct handle_subscription *hs = NULL;
if (hprev) if (hprev)
hs = hprev->hs_next; hs = hprev->hs_next;
else else
hs = cb->cb_subscription; hs = bh->bh_subscription;
return hs; return hs;
} }
/* Database dependency description */ /* Database dependency description */
struct backend_netconf_reg { struct backend_netconf_reg {
qelem_t nr_qelem; /* List header */ qelem_t nr_qelem; /* List header */
@ -456,10 +474,9 @@ catch:
/*! See if there is any callback registered for this tag /*! See if there is any callback registered for this tag
* *
* @param[in] h clicon handle * @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>. * @param[in] xe Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
* @param[out] cb Output xml stream. For reply * @param[in] ce Client (session) entry
* @param[out] cb_err Error xml stream. For error reply * @param[out] cbret Return XML, error or OK as cbuf
* @param[out] xret Return XML, error or OK
* *
* @retval -1 Error * @retval -1 Error
* @retval 0 OK, not found handler. * @retval 0 OK, not found handler.

View file

@ -69,17 +69,17 @@
#define handle(h) (assert(clicon_handle_check(h)==0),(struct cli_handle *)(h)) #define handle(h) (assert(clicon_handle_check(h)==0),(struct cli_handle *)(h))
#define cligen(h) (handle(h)->cl_cligen) #define cligen(h) (handle(h)->cl_cligen)
/* /*! CLI specific handle added to header CLICON handle
* cli_handle
* first part of this is header, same for clicon_handle and config_handle.
* Access functions for common fields are found in clicon lib: clicon_options.[ch]
* This file should only contain access functions for the _specific_ * This file should only contain access functions for the _specific_
* entries in the struct below. * entries in the struct below.
* @note The top part must be equivalent to struct clicon_handle in clixon_handle.c
* @see struct clicon_handle, struct backend_handle
*/ */
struct cli_handle { struct cli_handle {
int cl_magic; /* magic (HDR)*/ int cl_magic; /* magic (HDR)*/
clicon_hash_t *cl_copt; /* clicon option list (HDR) */ clicon_hash_t *cl_copt; /* clicon option list (HDR) */
clicon_hash_t *cl_data; /* internal clicon data (HDR) */ clicon_hash_t *cl_data; /* internal clicon data (HDR) */
void *cl_xmldb; /* XMLDB storage handle, uie xmldb_handle */
/* ------ end of common handle ------ */ /* ------ end of common handle ------ */
cligen_handle cl_cligen; /* cligen handle */ cligen_handle cl_cligen; /* cligen handle */

View file

@ -1,108 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2017 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 *****
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
CC = @CC@
CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
prefix = @prefix@
exec_prefix = @exec_prefix@
bindir = @bindir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
# Use this clixon lib for linking
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
# For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB)
CPPFLAGS = @CPPFLAGS@
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
SRC =
OBJS = $(SRC:.c=.o)
APPSRC = dbctrl_main.c
APPOBJ = $(APPSRC:.c=.o)
APPL = clixon_dbctrl
all: $(APPL)
clean:
rm -f $(OBJS) *.core $(APPL) $(APPOBJ)
distclean: clean
rm -f Makefile *~ .depend
# Put demon in bin
# Put other executables in libexec/
# Also create a libexec/ directory for writeable/temporary files.
# Put config file in etc/
install: $(APPL)
install -d $(DESTDIR)$(bindir)
install $(APPL) $(DESTDIR)$(bindir)
install-include:
uninstall:
rm -f $(bindir)/$(APPL)
.SUFFIXES:
.SUFFIXES: .c .o
.c.o:
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $<
$(APPL) : $(APPOBJ) $(OBJS) $(LIBDEPS)
$(CC) $(LDFLAGS) $(APPOBJ) $(OBJS) $(LIBS) -o $@
TAGS:
find . -name '*.[chyl]' -print | etags -
depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) $(APPSRC) > .depend
#include .depend

View file

@ -1,241 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2017 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 *****
*/
#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 <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
/* Command line options to be passed to getopt(3) */
#define DBCTRL_OPTS "hDSd:pbn:r:m:Zi"
/*
* remove_entry
*/
static int
remove_entry(char *dbname, char *key)
{
#ifdef NOTYET /* This assumes direct access to database */
return db_del(dbname, key);
#else
return 0;
#endif
}
/*! usage
*/
static void
usage(char *argv0)
{
fprintf(stderr, "usage:%s\n"
"where options are\n"
"\t-h\t\tHelp\n"
"\t-D\t\tDebug\n"
"\t-S\t\tLog on syslog\n"
"\t-d <db>\t\tDatabase name (default: running)\n"
"\t-p\t\tDump database on stdout\n"
"\t-b\t\tBrief output, just print keys. Combine with -p or -m\n"
"\t-n \"<key> <val>\" Add database entry\n"
"\t-r <key>\tRemove database entry\n"
"\t-m <regexp key>\tMatch regexp key in database\n"
"\t-Z\t\tDelete database\n"
"\t-i\t\tInit database\n",
argv0
);
exit(0);
}
int
main(int argc, char **argv)
{
char c;
int zapdb;
int initdb;
int dumpdb;
int addent;
int rment;
char *matchkey = NULL;
char *addstr;
char rmkey[MAXPATHLEN];
int brief;
char db[MAXPATHLEN] = {0,};
int use_syslog;
clicon_handle h;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
/* Defaults */
zapdb = 0;
initdb = 0;
dumpdb = 0;
addent = 0;
rment = 0;
brief = 0;
use_syslog = 0;
addstr = NULL;
memcpy(db, "running", strlen("running")+1);
memset(rmkey, '\0', sizeof(rmkey));
if ((h = clicon_handle_init()) == NULL)
goto done;
/* getopt in two steps, first find config-file before over-riding options. */
while ((c = getopt(argc, argv, DBCTRL_OPTS)) != -1)
switch (c) {
case '?' :
case 'h' : /* help */
usage(argv[0]);
break;
case 'D' : /* debug */
debug = 1;
break;
case 'S': /* Log on syslog */
use_syslog = 1;
break;
}
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO,
use_syslog?CLICON_LOG_SYSLOG:CLICON_LOG_STDERR);
clicon_debug_init(debug, NULL);
/* Now rest of options */
optind = 1;
while ((c = getopt(argc, argv, DBCTRL_OPTS)) != -1)
switch (c) {
case 'Z': /* Zap database */
zapdb++;
break;
case 'i': /* Init database */
initdb++;
break;
case 'p': /* Dump/print database */
dumpdb++;
break;
case 'b': /* Dump/print/match database brief (combone w -p or -m) */
brief++;
break;
case 'd': /* db either db filename or symbolic: running|candidate */
if (!optarg || sscanf(optarg, "%s", db) != 1)
usage(argv[0]);
break;
case 'n': /* add database entry */
if (!optarg || !strlen(optarg) || (addstr = strdup(optarg)) == NULL)
usage(argv[0]);
/* XXX addign both key and value, for now only key */
addent++;
break;
case 'r':
if (!optarg || sscanf(optarg, "%s", rmkey) != 1)
usage(argv[0]);
rment++;
break;
case 'm':
if (!optarg || !strlen(optarg) || (matchkey = strdup(optarg)) == NULL)
usage(argv[0]);
dumpdb++;
break;
case 'D': /* Processed earlier, ignore now. */
case 'S':
break;
default:
usage(argv[0]);
break;
}
argc -= optind;
argv += optind;
if (*db == '\0'){
clicon_err(OE_FATAL, 0, "database not specified (with -d <db>): %s");
goto done;
}
if (dumpdb){
/* Here db must be local file-path */
if (xmldb_dump(stdout, db, matchkey)) {
fprintf(stderr, "Match error\n");
goto done;
}
}
if (addent){ /* add entry */
cxobj *xml = NULL;
if (clicon_xml_parse(&xml, "<config>%s</config>", addstr) < 0)
goto done;
if (xmldb_put(h, db, OP_REPLACE, NULL, xml) < 0)
goto done;
if (xml)
xml_free(xml);
}
if (rment)
if (remove_entry(db, rmkey) < 0)
goto done;
if (zapdb) /* remove databases */
if (xmldb_delete(h, db) < 0){
clicon_err(OE_FATAL, errno, "delete %s", db);
goto done;
}
if (initdb)
if (xmldb_init(h, db) < 0)
goto done;
done:
return 0;
}

5
configure vendored
View file

@ -1333,7 +1333,7 @@ Optional Packages:
--with-cligen=dir Use CLIGEN here --with-cligen=dir Use CLIGEN here
--without-restconf disable support for restconf --without-restconf disable support for restconf
--without-keyvalue disable support for key-value xmldb datastore --without-keyvalue disable support for key-value xmldb datastore
--with-qdbm=dir Use QDBM here --with-qdbm=dir Use QDBM here, if keyvalue
Some influential environment variables: Some influential environment variables:
CC C compiler command CC C compiler command
@ -4266,7 +4266,7 @@ done
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile datastore/Makefile datastore/keyvalue/Makefile datastore/text/Makefile doc/Makefile" ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile datastore/Makefile datastore/keyvalue/Makefile datastore/text/Makefile doc/Makefile"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure # This file is a shell script that caches the results of configure
@ -4968,7 +4968,6 @@ do
"apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;; "apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;;
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;; "apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;; "apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
"apps/dbctrl/Makefile") CONFIG_FILES="$CONFIG_FILES apps/dbctrl/Makefile" ;;
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;; "etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;; "etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;

View file

@ -183,7 +183,6 @@ AC_OUTPUT(Makefile
apps/backend/Makefile apps/backend/Makefile
apps/netconf/Makefile apps/netconf/Makefile
apps/restconf/Makefile apps/restconf/Makefile
apps/dbctrl/Makefile
include/Makefile include/Makefile
etc/Makefile etc/Makefile
etc/clixonrc etc/clixonrc

View file

@ -31,8 +31,18 @@
# ***** END LICENSE BLOCK ***** # ***** END LICENSE BLOCK *****
# #
VPATH = @srcdir@ VPATH = @srcdir@
prefix = @prefix@
datarootdir = @datarootdir@
srcdir = @srcdir@ srcdir = @srcdir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
dbdir = @prefix@/db
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
CC = @CC@ CC = @CC@
CFLAGS = @CFLAGS@ CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@ LDFLAGS = @LDFLAGS@
@ -40,7 +50,22 @@ LIBS = @LIBS@
with_restconf = @with_restconf@ with_restconf = @with_restconf@
with_keyvalue = @with_keyvalue@ with_keyvalue = @with_keyvalue@
SHELL = /bin/sh SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
# Use this clixon lib for linking
CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
# For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB)
CPPFLAGS = @CPPFLAGS@
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
SUBDIRS = text SUBDIRS = text
ifeq ($(with_keyvalue),yes) ifeq ($(with_keyvalue),yes)
@ -49,7 +74,23 @@ endif
.PHONY: all clean depend install $(SUBDIRS) .PHONY: all clean depend install $(SUBDIRS)
all: $(SUBDIRS) APPSRC = datastore_client.c
APPOBJ = $(APPSRC:.c=.o)
APPL = datastore_client
all: $(SUBDIRS) $(APPL)
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
.SUFFIXES:
.SUFFIXES: .c .o
.c.o:
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $<
$(APPL) : $(APPOBJ) $(LIBDEPS)
$(CC) $(LDFLAGS) $(APPOBJ) $(LIBS) -o $@
depend: depend:
for i in $(SUBDIRS); \ for i in $(SUBDIRS); \
@ -62,15 +103,19 @@ install-include:
for i in $(SUBDIRS); \ for i in $(SUBDIRS); \
do (cd $$i ; $(MAKE) $(MFLAGS) $@); done; do (cd $$i ; $(MAKE) $(MFLAGS) $@); done;
install: install: $(APPL)
install -d $(DESTDIR)$(bindir)
install $(APPL) $(DESTDIR)$(bindir)
for i in $(SUBDIRS); \ for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done do (cd $$i; $(MAKE) $(MFLAGS) $@); done
uninstall: uninstall:
rm -f $(bindir)/$(APPL)
for i in $(SUBDIRS); \ for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done do (cd $$i; $(MAKE) $(MFLAGS) $@); done
clean: clean:
rm -f *.core $(APPL) $(APPOBJ)
for i in $(SUBDIRS); \ for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done do (cd $$i; $(MAKE) $(MFLAGS) $@); done

View file

@ -0,0 +1,275 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2017 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 *****
*/
#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 <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
/* Command line options to be passed to getopt(3) */
#define DATASTORE_OPTS "hDd:p:b:y:m:"
/*! usage
*/
static void
usage(char *argv0)
{
fprintf(stderr, "usage:%s <options>* [<command>]\n"
"where options are\n"
"\t-h\t\tHelp\n"
"\t-D\t\tDebug\n"
"\t-d <db>\t\tDatabase name. Default: running. Alt: candidate,startup\n"
"\t-b <dir>\tDatabase directory. Mandatory\n"
"\t-p <plugin>\tDatastore plugin. Mandatory\n"
"\t-y <dir>\tYang directory (where modules are stored). Mandatory\n"
"\t-m <module>\tYang module. Mandatory\n"
"and command is either:\n"
"\tget <xpath>\n"
"\tput (set|merge|delete) <api_path> \tXML on stdin\n"
"\tcopy <fromdb> <todb>\n"
"\tlock <pid>\n"
"\tunlock <pid>\n"
"\tunlock_all <pid>\n"
"\tislocked\n"
"\texists\n"
"\tdelete\n"
"\tinit\n"
,
argv0
);
exit(0);
}
int
main(int argc, char **argv)
{
char c;
clicon_handle h;
char *argv0;
char *db = "running";
char *plugin = NULL;
char *cmd = NULL;
yang_spec *yspec = NULL;
char *yangdir = NULL;
char *yangmodule = NULL;
char *dbdir = NULL;
int ret;
int pid;
enum operation_type op;
cxobj *xt;
cxobj *xn;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
argv0 = argv[0];
/* Defaults */
if ((h = clicon_handle_init()) == NULL)
goto done;
/* getopt in two steps, first find config-file before over-riding options. */
while ((c = getopt(argc, argv, DATASTORE_OPTS)) != -1)
switch (c) {
case '?' :
case 'h' : /* help */
usage(argv0);
break;
case 'D' : /* debug */
debug = 1;
break;
case 'd': /* db symbolic: running|candidate|startup */
if (!optarg)
usage(argv0);
db = optarg;
break;
case 'p': /* datastore plugin */
if (!optarg)
usage(argv0);
plugin = optarg;
break;
case 'b': /* db directory */
if (!optarg)
usage(argv0);
dbdir = optarg;
break;
case 'y': /* Yang directory */
if (!optarg)
usage(argv0);
yangdir = optarg;
break;
case 'm': /* Yang module */
if (!optarg)
usage(argv0);
yangmodule = optarg;
break;
}
/*
* Logs, error and debug to stderr, set debug level
*/
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, CLICON_LOG_STDERR);
clicon_debug_init(debug, NULL);
argc -= optind;
argv += optind;
if (argc < 1)
usage(argv0);
cmd = argv[0];
if (plugin == NULL){
clicon_err(OE_DB, 0, "Missing plugin -p option");
goto done;
}
if (dbdir == NULL){
clicon_err(OE_DB, 0, "Missing dbdir -b option");
goto done;
}
if (yangdir == NULL){
clicon_err(OE_YANG, 0, "Missing yangdir -y option");
goto done;
}
if (yangmodule == NULL){
clicon_err(OE_YANG, 0, "Missing yang module -m option");
goto done;
}
/* Load datastore plugin */
if (xmldb_plugin_load(h, plugin) < 0)
goto done;
/* Connect to plugin to get a handle */
if (xmldb_connect(h) < 0)
goto done;
/* Create yang spec */
if ((yspec = yspec_new()) == NULL)
goto done;
/* Parse yang spec from given file */
if (yang_parse(h, yangdir, yangmodule, NULL, yspec) < 0)
goto done;
/* Set database directory option */
if (xmldb_setopt(h, "dbdir", dbdir) < 0)
goto done;
/* Set yang spec option */
if (xmldb_setopt(h, "yangspec", yspec) < 0)
goto done;
if (strcmp(cmd, "get")==0){
if (argc < 2)
usage(argv0);
if (xmldb_get(h, db, argv[1], &xt, NULL, 0) < 0)
goto done;
clicon_xml2file(stdout, xt, 0, 1);
}
else if (strcmp(cmd, "put")==0){
if (argc < 3)
usage(argv0);
if (xml_operation(argv[1], &op) < 0)
usage(argv0);
if (clicon_xml_parse_file(0, &xt, "</clicon>") < 0)
goto done;
if (xml_rootchild(xt, 0, &xn) < 0)
goto done;
if (xmldb_put(h, db, op, argv[2], xn) < 0)
goto done;
if (xt)
xml_free(xt);
}
else if (strcmp(cmd, "copy")==0){
if (argc < 3)
usage(argv0);
if (xmldb_copy(h, argv[1], argv[2]) < 0)
goto done;
}
else if (strcmp(cmd, "lock")==0){
if (argc < 2)
usage(argv0);
pid = atoi(argv[1]);
if (xmldb_lock(h, db, pid) < 0)
goto done;
}
else if (strcmp(cmd, "unlock")==0){
if (argc < 2)
usage(argv0);
pid = atoi(argv[1]);
if (xmldb_unlock(h, db, pid) < 0)
goto done;
}
else if (strcmp(cmd, "unlock_all")==0){
if (argc < 2)
usage(argv0);
pid = atoi(argv[1]);
if (xmldb_unlock_all(h, pid) < 0)
goto done;
}
else if (strcmp(cmd, "islocked")==0){
if ((ret = xmldb_islocked(h, db)) < 0)
goto done;
fprintf(stdout, "islocked: %d\n", ret);
}
else if (strcmp(cmd, "exists")==0){
if ((ret = xmldb_exists(h, db)) < 0)
goto done;
fprintf(stdout, "exists: %d\n", ret);
}
else if (strcmp(cmd, "delete")==0){
if (xmldb_delete(h, db) < 0)
goto done;
}
else if (strcmp(cmd, "init")==0){
if (xmldb_init(h, db) < 0)
goto done;
}
done:
return 0;
}

View file

@ -30,6 +30,7 @@
# #
# ***** END LICENSE BLOCK ***** # ***** END LICENSE BLOCK *****
# #
VPATH = @srcdir@
prefix = @prefix@ prefix = @prefix@
datarootdir = @datarootdir@ datarootdir = @datarootdir@
srcdir = @srcdir@ srcdir = @srcdir@

View file

@ -81,6 +81,10 @@
* - restconf * - restconf
* - expand_dbvar * - expand_dbvar
* - show_conf_xpath * - show_conf_xpath
*
* dependency on clixon handle:
* clixon_xmldb_dir()
* clicon_dbspec_yang(h)
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -111,6 +115,31 @@
#include "clixon_qdb.h" #include "clixon_qdb.h"
#include "clixon_keyvalue.h" #include "clixon_keyvalue.h"
#define handle(xh) (assert(kv_handle_check(xh)==0),(struct kv_handle *)(xh))
/* Magic to ensure plugin sanity. */
#define KV_HANDLE_MAGIC 0xfa61a402
/*! Internal structure of keyvalue datastore handle.
*/
struct kv_handle {
int kh_magic; /* magic */
char *kh_dbdir; /* Directory of database files */
yang_spec *kh_yangspec; /* Yang spec if this datastore */
};
/*! Check struct magic number for sanity checks
* return 0 if OK, -1 if fail.
*/
static int
kv_handle_check(xmldb_handle xh)
{
/* Dont use handle macro to avoid recursion */
struct kv_handle *kh = (struct kv_handle *)(xh);
return kh->kh_magic == KV_HANDLE_MAGIC ? 0 : -1;
}
/*! Database locking for candidate and running non-persistent /*! Database locking for candidate and running non-persistent
* Store an integer for running and candidate containing * Store an integer for running and candidate containing
* the session-id of the client holding the lock. * the session-id of the client holding the lock.
@ -120,7 +149,7 @@ static int _candidate_locked = 0;
static int _startup_locked = 0; static int _startup_locked = 0;
/*! Translate from symbolic database name to actual filename in file-system /*! Translate from symbolic database name to actual filename in file-system
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Symbolic database name, eg "candidate", "running" * @param[in] db Symbolic database name, eg "candidate", "running"
* @param[out] filename Filename. Unallocate after use with free() * @param[out] filename Filename. Unallocate after use with free()
* @retval 0 OK * @retval 0 OK
@ -131,9 +160,9 @@ static int _startup_locked = 0;
* The filename reside in CLICON_XMLDB_DIR option * The filename reside in CLICON_XMLDB_DIR option
*/ */
static int static int
db2file(clicon_handle h, db2file(struct kv_handle *kh,
char *db, char *db,
char **filename) char **filename)
{ {
int retval = -1; int retval = -1;
cbuf *cb; cbuf *cb;
@ -143,8 +172,8 @@ db2file(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new"); clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
} }
if ((dir = clicon_xmldb_dir(h)) == NULL){ if ((dir = kh->kh_dbdir) == NULL){
clicon_err(OE_XML, errno, "CLICON_XMLDB_DIR not set"); clicon_err(OE_XML, errno, "dbdir not set");
goto done; goto done;
} }
if (strcmp(db, "running") != 0 && if (strcmp(db, "running") != 0 &&
@ -244,49 +273,6 @@ create_keyvalues(cxobj *x,
return retval; return retval;
} }
/*! Prune everything that has not been marked
* @param[in] xt XML tree with some node marked
* @param[out] upmark Set if a child (recursively) has marked set.
* The function removes all branches that does not contain a marked child
* XXX: maybe key leafs should not be purged if list is not purged?
* XXX: consider move to clicon_xml
*/
static int
xml_tree_prune_unmarked(cxobj *xt,
int *upmark)
{
int retval = -1;
int submark;
int mark;
cxobj *x;
cxobj *xprev;
mark = 0;
x = NULL;
xprev = x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, XML_FLAG_MARK)){
mark++;
xprev = x;
continue; /* mark and stop here */
}
if (xml_tree_prune_unmarked(x, &submark) < 0)
goto done;
if (submark)
mark++;
else{ /* Safe with xml_child_each if last */
if (xml_purge(x) < 0)
goto done;
x = xprev;
}
xprev = x;
}
retval = 0;
done:
if (upmark)
*upmark = mark;
return retval;
}
/*! /*!
* @param[in] xk xmlkey * @param[in] xk xmlkey
@ -476,135 +462,116 @@ get(char *dbname,
return retval; return retval;
} }
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer /*! Connect to a datastore plugin
* @retval handle Use this handle for other API calls
* @retval NULL Error
* @note You can do several connects, and have multiple connections to the same
* datastore
*/ */
static int xmldb_handle
xml_sanity(cxobj *x, kv_connect(void)
void *arg)
{ {
int retval = -1; struct kv_handle *kh;
yang_stmt *ys; xmldb_handle xh = NULL;
int size;
ys = (yang_stmt*)xml_spec(x); size = sizeof(struct kv_handle);
if (ys==NULL){ if ((kh = malloc(size)) == NULL){
clicon_err(OE_XML, 0, "No spec for xml node %s", xml_name(x)); clicon_err(OE_UNIX, errno, "malloc");
goto done; goto done;
} }
if (strstr(ys->ys_argument, xml_name(x))==NULL){ memset(kh, 0, size);
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'", kh->kh_magic = KV_HANDLE_MAGIC;
xml_name(x), ys->ys_argument); xh = (xmldb_handle)kh;
goto done; done:
} return xh;
retval = 0;
done:
return retval;
} }
/*! Add default values (if not set) /*! Disconnect from a datastore plugin and deallocate handle
*/ * @param[in] handle Disconect and deallocate from this handle
static int * @retval 0 OK
xml_default(cxobj *x, */
void *arg) int
kv_disconnect(xmldb_handle xh)
{ {
int retval = -1; int retval = -1;
yang_stmt *ys; struct kv_handle *kh = handle(xh);
yang_stmt *y;
int i;
cxobj *xc;
cxobj *xb;
char *str;
ys = (yang_stmt*)xml_spec(x); if (kh){
/* Check leaf defaults */ if (kh->kh_dbdir)
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){ free(kh->kh_dbdir);
for (i=0; i<ys->ys_len; i++){ free(kh);
y = ys->ys_stmt[i];
if (y->ys_keyword != Y_LEAF)
continue;
assert(y->ys_cv);
if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */
if (!xml_find(x, y->ys_argument)){
if ((xc = xml_new_spec(y->ys_argument, x, y)) == NULL)
goto done;
if ((xb = xml_new("body", xc)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if ((str = cv2str_dup(y->ys_cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
if (xml_value_set(xb, str) < 0)
goto done;
free(str);
}
}
}
}
retval = 0;
done:
return retval;
}
/*! Order XML children according to YANG
*/
static int
xml_order(cxobj *x,
void *arg)
{
int retval = -1;
yang_stmt *y;
yang_stmt *yc;
int i;
int j0;
int j;
cxobj *xc;
cxobj *xj;
char *yname; /* yang child name */
char *xname; /* xml child name */
y = (yang_stmt*)xml_spec(x);
j0 = 0;
/* Go through xml children and ensure they are same order as yspec children */
for (i=0; i<y->ys_len; i++){
yc = y->ys_stmt[i];
if (!yang_is_syntax(yc))
continue;
yname = yc->ys_argument;
/* First go thru xml children with same name */
for (;j0<xml_child_nr(x); j0++){
xc = xml_child_i(x, j0);
if (xml_type(xc) != CX_ELMNT)
continue;
xname = xml_name(xc);
if (strcmp(xname, yname))
break;
}
/* Now we have children not with same name */
for (j=j0; j<xml_child_nr(x); j++){
xc = xml_child_i(x, j);
if (xml_type(xc) != CX_ELMNT)
continue;
xname = xml_name(xc);
if (strcmp(xname, yname))
continue;
/* reorder */
xj = xml_child_i(x, j0);
xml_child_i_set(x, j0, xc);
xml_child_i_set(x, j, xj);
j0++;
}
} }
retval = 0; retval = 0;
// done: // done:
return retval; return retval;
} }
/*! Get value of generic plugin option. Type of value is givenby context
* @param[in] xh XMLDB handle
* @param[in] optname Option name
* @param[out] value Pointer to Value of option
* @retval 0 OK
* @retval -1 Error
*/
int
kv_getopt(xmldb_handle xh,
char *optname,
void **value)
{
int retval = -1;
struct kv_handle *kh = handle(xh);
if (strcmp(optname, "yangspec") == 0)
*value = kh->kh_yangspec;
else if (strcmp(optname, "dbdir") == 0)
*value = kh->kh_dbdir;
else{
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Set value of generic plugin option. Type of value is givenby context
* @param[in] xh XMLDB handle
* @param[in] optname Option name
* @param[in] value Value of option
* @retval 0 OK
* @retval -1 Error
*/
int
kv_setopt(xmldb_handle xh,
char *optname,
void *value)
{
int retval = -1;
struct kv_handle *kh = handle(xh);
if (strcmp(optname, "yangspec") == 0)
kh->kh_yangspec = (yang_spec*)value;
else if (strcmp(optname, "dbdir") == 0){
if (value && (kh->kh_dbdir = strdup((char*)value)) == NULL){
clicon_err(OE_UNIX, 0, "strdup");
goto done;
}
}
else{
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees /*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match * The function returns a minimal tree that includes all sub-trees that match
* xpath. * xpath.
* @param[in] dbname Name of database to search in (filename including dir path * @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] yspec Yang specification
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free() * @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use. * @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector. * @param[out] xlen Length of vector.
@ -614,8 +581,7 @@ xml_order(cxobj *x,
* cxobj *xt; * cxobj *xt;
* cxobj **xvec; * cxobj **xvec;
* size_t xlen; * size_t xlen;
* yang_spec *yspec = clicon_dbspec_yang(h); * if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0) * &xt, &xvec, &xlen) < 0)
* err; * err;
* for (i=0; i<xlen; i++){ * for (i=0; i<xlen; i++){
@ -630,16 +596,17 @@ xml_order(cxobj *x,
* @see xmldb_get * @see xmldb_get
*/ */
int int
kv_get(clicon_handle h, kv_get(xmldb_handle xh,
char *db, char *db,
char *xpath, char *xpath,
cxobj **xtop, cxobj **xtop,
cxobj ***xvec0, cxobj ***xvec0,
size_t *xlen0) size_t *xlen0)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh);
yang_spec *yspec; yang_spec *yspec;
char *dbname = NULL; char *dbfile = NULL;
cxobj **xvec = NULL; cxobj **xvec = NULL;
size_t xlen; size_t xlen;
int i; int i;
@ -647,25 +614,26 @@ kv_get(clicon_handle h,
struct db_pair *pairs; struct db_pair *pairs;
cxobj *xt = NULL; cxobj *xt = NULL;
clicon_debug(2, "%s", __FUNCTION__); clicon_debug(2, "%s", __FUNCTION__);
if (db2file(h, db, &dbname) < 0) if (db2file(kh, db, &dbfile) < 0)
goto done; goto done;
if (dbname==NULL){ if (dbfile==NULL){
clicon_err(OE_XML, 0, "dbname NULL"); clicon_err(OE_XML, 0, "dbfile NULL");
goto done; goto done;
} }
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = kh->kh_yangspec) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec"); clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done; goto done;
} }
/* Read in complete database (this can be optimized) */ /* Read in complete database (this can be optimized) */
if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0) if ((npairs = db_regexp(dbfile, "", __FUNCTION__, &pairs, 0)) < 0)
goto done; goto done;
if ((xt = xml_new_spec("clicon", NULL, yspec)) == NULL) if ((xt = xml_new_spec("clicon", NULL, yspec)) == NULL)
goto done; goto done;
/* Translate to complete xml tree */ /* Translate to complete xml tree */
for (i = 0; i < npairs; i++) { for (i = 0; i < npairs; i++) {
if (get(dbname, if (get(dbfile,
yspec, yspec,
pairs[i].dp_key, /* xml key */ pairs[i].dp_key, /* xml key */
pairs[i].dp_val, /* may be NULL */ pairs[i].dp_val, /* may be NULL */
@ -693,7 +661,6 @@ kv_get(clicon_handle h,
*xlen0 = xlen; *xlen0 = xlen;
xlen = 0; xlen = 0;
} }
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0) if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done; goto done;
/* XXX does not work for top-level */ /* XXX does not work for top-level */
@ -706,8 +673,8 @@ kv_get(clicon_handle h,
*xtop = xt; *xtop = xt;
retval = 0; retval = 0;
done: done:
if (dbname) if (dbfile)
free(dbname); free(dbfile);
if (xvec) if (xvec)
free(xvec); free(xvec);
unchunk_group(__FUNCTION__); unchunk_group(__FUNCTION__);
@ -716,7 +683,7 @@ kv_get(clicon_handle h,
} }
/*! Add data to database internal recursive function /*! Add data to database internal recursive function
* @param[in] dbname Name of database to search in (filename incl dir path) * @param[in] dbfile Name of database to search in (filename incl dir path)
* @param[in] xt xml-node. * @param[in] xt xml-node.
* @param[in] ys Yang statement corresponding to xml-node * @param[in] ys Yang statement corresponding to xml-node
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
@ -726,7 +693,7 @@ kv_get(clicon_handle h,
* @note XXX op only supports merge * @note XXX op only supports merge
*/ */
static int static int
put(char *dbname, put(char *dbfile,
cxobj *xt, cxobj *xt,
yang_stmt *ys, yang_stmt *ys,
enum operation_type op, enum operation_type op,
@ -774,7 +741,7 @@ put(char *dbname,
/* Write to database, key and a vector of variables */ /* Write to database, key and a vector of variables */
switch (op){ switch (op){
case OP_CREATE: case OP_CREATE:
if ((exists = db_exists(dbname, xk)) < 0) if ((exists = db_exists(dbfile, xk)) < 0)
goto done; goto done;
if (exists == 1){ if (exists == 1){
clicon_err(OE_DB, 0, "OP_CREATE: %s already exists in database", xk); clicon_err(OE_DB, 0, "OP_CREATE: %s already exists in database", xk);
@ -782,18 +749,18 @@ put(char *dbname,
} }
case OP_MERGE: case OP_MERGE:
case OP_REPLACE: case OP_REPLACE:
if (db_set(dbname, xk, body?body:NULL, body?strlen(body)+1:0) < 0) if (db_set(dbfile, xk, body?body:NULL, body?strlen(body)+1:0) < 0)
goto done; goto done;
break; break;
case OP_DELETE: case OP_DELETE:
if ((exists = db_exists(dbname, xk)) < 0) if ((exists = db_exists(dbfile, xk)) < 0)
goto done; goto done;
if (exists == 0){ if (exists == 0){
clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk); clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk);
goto done; goto done;
} }
case OP_REMOVE: case OP_REMOVE:
if (db_del(dbname, xk) < 0) if (db_del(dbfile, xk) < 0)
goto done; goto done;
break; break;
case OP_NONE: case OP_NONE:
@ -805,7 +772,7 @@ put(char *dbname,
clicon_err(OE_UNIX, 0, "No yang node found: %s", xml_name(x)); clicon_err(OE_UNIX, 0, "No yang node found: %s", xml_name(x));
goto done; goto done;
} }
if (put(dbname, x, y, op, xk) < 0) if (put(dbfile, x, y, op, xk) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;
@ -818,7 +785,7 @@ put(char *dbname,
} }
/*! Modify database provided an XML database key and an operation /*! Modify database provided an XML database key and an operation
* @param[in] h CLICON handle * @param[in] kh Keyvalue handle
* @param[in] db Database name * @param[in] db Database name
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[in] xk XML Key, eg /aa/bb=17/name * @param[in] xk XML Key, eg /aa/bb=17/name
@ -833,7 +800,7 @@ put(char *dbname,
* @see xmldb_put with xml-tree, no path * @see xmldb_put with xml-tree, no path
*/ */
static int static int
xmldb_put_xkey(clicon_handle h, xmldb_put_xkey(struct kv_handle *kh,
char *db, char *db,
enum operation_type op, enum operation_type op,
char *xk, char *xk,
@ -865,8 +832,11 @@ xmldb_put_xkey(clicon_handle h,
yang_spec *yspec; yang_spec *yspec;
char *filename = NULL; char *filename = NULL;
yspec = clicon_dbspec_yang(h); if ((yspec = kh->kh_yangspec) == NULL){
if (db2file(h, db, &filename) < 0) clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (db2file(kh, db, &filename) < 0)
goto done; goto done;
if (xk == NULL || *xk!='/'){ if (xk == NULL || *xk!='/'){
clicon_err(OE_DB, 0, "Invalid key: %s", xk); clicon_err(OE_DB, 0, "Invalid key: %s", xk);
@ -1045,7 +1015,7 @@ xmldb_put_xkey(clicon_handle h,
/*! Modify database provided an xml tree, a restconf api_path and an operation /*! Modify database provided an xml tree, a restconf api_path and an operation
* *
* @param[in] h CLICON handle * @param[in] kh Keyvalue handle
* @param[in] db running or candidate * @param[in] db running or candidate
* @param[in] op OP_MERGE: just add it. * @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database * OP_REPLACE: first delete whole database
@ -1063,7 +1033,7 @@ xmldb_put_xkey(clicon_handle h,
* @see xmldb_put * @see xmldb_put
*/ */
static int static int
xmldb_put_restconf_api_path(clicon_handle h, xmldb_put_restconf_api_path(struct kv_handle *kh,
char *db, char *db,
enum operation_type op, enum operation_type op,
char *api_path, char *api_path,
@ -1092,8 +1062,11 @@ xmldb_put_restconf_api_path(clicon_handle h,
char *key; char *key;
char *keys; char *keys;
yspec = clicon_dbspec_yang(h); if ((yspec = kh->kh_yangspec) == NULL){
if (db2file(h, db, &filename) < 0) clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (db2file(kh, db, &filename) < 0)
goto done; goto done;
if (api_path == NULL || *api_path!='/'){ if (api_path == NULL || *api_path!='/'){
clicon_err(OE_DB, 0, "Invalid api path: %s", api_path); clicon_err(OE_DB, 0, "Invalid api path: %s", api_path);
@ -1260,7 +1233,7 @@ xmldb_put_restconf_api_path(clicon_handle h,
/*! Modify database provided an xml tree and an operation /*! Modify database provided an xml tree and an operation
* *
* @param[in] h CLICON handle * @param[in] xh XMLDB handle
* @param[in] db running or candidate * @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy * @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it. * @param[in] op OP_MERGE: just add it.
@ -1280,22 +1253,26 @@ xmldb_put_restconf_api_path(clicon_handle h,
* @see xmldb_put_xkey for single key * @see xmldb_put_xkey for single key
*/ */
int int
kv_put(clicon_handle h, kv_put(xmldb_handle xh,
char *db, char *db,
enum operation_type op, enum operation_type op,
char *api_path, char *api_path,
cxobj *xt) cxobj *xt)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh);
cxobj *x = NULL; cxobj *x = NULL;
yang_stmt *ys; yang_stmt *ys;
yang_spec *yspec; yang_spec *yspec;
char *dbfilename = NULL; char *dbfilename = NULL;
if (xml_child_nr(xt)==0 || xml_body(xt)!= NULL) if (xml_child_nr(xt)==0 || xml_body(xt)!= NULL)
return xmldb_put_xkey(h, db, op, api_path, xml_body(xt)); return xmldb_put_xkey(kh, db, op, api_path, xml_body(xt));
yspec = clicon_dbspec_yang(h); if ((yspec = kh->kh_yangspec) == NULL){
if (db2file(h, db, &dbfilename) < 0) clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (db2file(kh, db, &dbfilename) < 0)
goto done; goto done;
if (op == OP_REPLACE){ if (op == OP_REPLACE){
if (db_delete(dbfilename) < 0) if (db_delete(dbfilename) < 0)
@ -1305,7 +1282,7 @@ kv_put(clicon_handle h,
} }
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){ while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if (api_path && strlen(api_path)){ if (api_path && strlen(api_path)){
if (xmldb_put_restconf_api_path(h, db, op, api_path, x) < 0) if (xmldb_put_restconf_api_path(kh, db, op, api_path, x) < 0)
goto done; goto done;
continue; continue;
} }
@ -1328,58 +1305,27 @@ kv_put(clicon_handle h,
return retval; return retval;
} }
/*! Raw dump of database, just keys and values, no xml interpretation
* @param[in] f File
* @param[in] dbfile File-name of database. This is a local file
* @param[in] rxkey Key regexp, eg "^.*$"
* @note This function can only be called locally.
*/
int
kv_dump(FILE *f,
char *dbfilename,
char *rxkey)
{
int retval = -1;
int npairs;
struct db_pair *pairs;
/* Default is match all */
if (rxkey == NULL)
rxkey = "^.*$";
/* Get all keys/values for vector */
if ((npairs = db_regexp(dbfilename, rxkey, __FUNCTION__, &pairs, 0)) < 0)
goto done;
for (npairs--; npairs >= 0; npairs--)
fprintf(f, "%s %s\n", pairs[npairs].dp_key,
pairs[npairs].dp_val?pairs[npairs].dp_val:"");
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Copy database from db1 to db2 /*! Copy database from db1 to db2
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] from Source database copy * @param[in] from Source database copy
* @param[in] to Destination database * @param[in] to Destination database
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
*/ */
int int
kv_copy(clicon_handle h, kv_copy(xmldb_handle xh,
char *from, char *from,
char *to) char *to)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh);
char *fromfile = NULL; char *fromfile = NULL;
char *tofile = NULL; char *tofile = NULL;
/* XXX lock */ /* XXX lock */
if (db2file(h, from, &fromfile) < 0) if (db2file(kh, from, &fromfile) < 0)
goto done; goto done;
if (db2file(h, to, &tofile) < 0) if (db2file(kh, to, &tofile) < 0)
goto done; goto done;
if (clicon_file_copy(fromfile, tofile) < 0) if (clicon_file_copy(fromfile, tofile) < 0)
goto done; goto done;
@ -1393,17 +1339,19 @@ kv_copy(clicon_handle h,
} }
/*! Lock database /*! Lock database
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @param[in] pid Process id * @param[in] pid Process id
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
*/ */
int int
kv_lock(clicon_handle h, kv_lock(xmldb_handle xh,
char *db, char *db,
int pid) int pid)
{ {
// struct kv_handle *kh = handle(xh);
if (strcmp("running", db) == 0) if (strcmp("running", db) == 0)
_running_locked = pid; _running_locked = pid;
else if (strcmp("candidate", db) == 0) else if (strcmp("candidate", db) == 0)
@ -1415,7 +1363,7 @@ kv_lock(clicon_handle h,
} }
/*! Unlock database /*! Unlock database
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @param[in] pid Process id * @param[in] pid Process id
* @retval -1 Error * @retval -1 Error
@ -1423,10 +1371,12 @@ kv_lock(clicon_handle h,
* Assume all sanity checks have been made * Assume all sanity checks have been made
*/ */
int int
kv_unlock(clicon_handle h, kv_unlock(xmldb_handle xh,
char *db, char *db,
int pid) int pid)
{ {
// struct kv_handle *kh = handle(xh);
if (strcmp("running", db) == 0) if (strcmp("running", db) == 0)
_running_locked = 0; _running_locked = 0;
else if (strcmp("candidate", db) == 0) else if (strcmp("candidate", db) == 0)
@ -1437,15 +1387,17 @@ kv_unlock(clicon_handle h,
} }
/*! Unlock all databases locked by pid (eg process dies) /*! Unlock all databases locked by pid (eg process dies)
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] pid Process / Session id * @param[in] pid Process / Session id
* @retval -1 Error * @retval -1 Error
* @retval 0 Ok * @retval 0 Ok
*/ */
int int
kv_unlock_all(clicon_handle h, kv_unlock_all(xmldb_handle xh,
int pid) int pid)
{ {
// struct kv_handle *kh = handle(xh);
if (_running_locked == pid) if (_running_locked == pid)
_running_locked = 0; _running_locked = 0;
if (_candidate_locked == pid) if (_candidate_locked == pid)
@ -1456,16 +1408,18 @@ kv_unlock_all(clicon_handle h,
} }
/*! Check if database is locked /*! Check if database is locked
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval -1 Error * @retval -1 Error
* @retval 0 Not locked * @retval 0 Not locked
* @retval >0 Id of locker * @retval >0 Id of locker
*/ */
int int
kv_islocked(clicon_handle h, kv_islocked(xmldb_handle xh,
char *db) char *db)
{ {
// struct kv_handle *kh = handle(xh);
if (strcmp("running", db) == 0) if (strcmp("running", db) == 0)
return (_running_locked); return (_running_locked);
else if (strcmp("candidate", db) == 0) else if (strcmp("candidate", db) == 0)
@ -1476,21 +1430,22 @@ kv_islocked(clicon_handle h,
} }
/*! Check if db exists /*! Check if db exists
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval -1 Error * @retval -1 Error
* @retval 0 No it does not exist * @retval 0 No it does not exist
* @retval 1 Yes it exists * @retval 1 Yes it exists
*/ */
int int
kv_exists(clicon_handle h, kv_exists(xmldb_handle xh,
char *db) char *db)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh);
char *filename = NULL; char *filename = NULL;
struct stat sb; struct stat sb;
if (db2file(h, db, &filename) < 0) if (db2file(kh, db, &filename) < 0)
goto done; goto done;
if (lstat(filename, &sb) < 0) if (lstat(filename, &sb) < 0)
retval = 0; retval = 0;
@ -1503,19 +1458,20 @@ kv_exists(clicon_handle h,
} }
/*! Delete database. Remove file /*! Delete database. Remove file
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
*/ */
int int
kv_delete(clicon_handle h, kv_delete(xmldb_handle xh,
char *db) char *db)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh);
char *filename = NULL; char *filename = NULL;
if (db2file(h, db, &filename) < 0) if (db2file(kh, db, &filename) < 0)
goto done; goto done;
if (db_delete(filename) < 0) if (db_delete(filename) < 0)
goto done; goto done;
@ -1527,19 +1483,20 @@ kv_delete(clicon_handle h,
} }
/*! Initialize database /*! Initialize database
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int int
kv_init(clicon_handle h, kv_init(xmldb_handle xh,
char *db) char *db)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh);
char *filename = NULL; char *filename = NULL;
if (db2file(h, db, &filename) < 0) if (db2file(kh, db, &filename) < 0)
goto done; goto done;
if (db_init(filename) < 0) if (db_init(filename) < 0)
goto done; goto done;
@ -1578,9 +1535,12 @@ static const struct xmldb_api api = {
XMLDB_API_MAGIC, XMLDB_API_MAGIC,
clixon_xmldb_plugin_init, clixon_xmldb_plugin_init,
kv_plugin_exit, kv_plugin_exit,
kv_connect,
kv_disconnect,
kv_getopt,
kv_setopt,
kv_get, kv_get,
kv_put, kv_put,
kv_dump,
kv_copy, kv_copy,
kv_lock, kv_lock,
kv_unlock, kv_unlock,
@ -1598,80 +1558,46 @@ static const struct xmldb_api api = {
* Usage: clicon_xpath [<xpath>] * Usage: clicon_xpath [<xpath>]
* read xml from input * read xml from input
* Example compile: * Example compile:
gcc -g -o xmldb -I. -I../clixon ./clixon_xmldb.c -lclixon -lcligen gcc -g -o keyvalue -I. -I../../lib ./clixon_keyvalue.c clixon_chunk.c clixon_qdb.c -lclixon -lcligen -lqdbm
*/ */
static int /*! Raw dump of database, just keys and values, no xml interpretation
usage(char *argv0) * @param[in] f File
{ * @param[in] dbfile File-name of database. This is a local file
fprintf(stderr, "usage:\n%s\tget <db> <yangdir> <yangmod> [<xpath>]\t\txml on stdin\n", argv0); * @param[in] rxkey Key regexp, eg "^.*$"
fprintf(stderr, "\tput <db> <yangdir> <yangmod> set|merge|delete\txml to stdout\n"); * @note This function can only be called locally.
exit(0); */
}
int int
main(int argc, char **argv) main(int argc,
char **argv)
{ {
cxobj *xt; int retval = -1;
cxobj *xn; int npairs;
char *xpath; struct db_pair *pairs;
enum operation_type op; char *rxkey = NULL;
char *cmd; char *dbfilename;
char *db;
char *yangdir;
char *yangmod;
yang_spec *yspec = NULL;
clicon_handle h;
if ((h = clicon_handle_init()) == NULL) if (argc != 2 && argc != 3){
goto done; fprintf(stderr, "usage: %s <dbfile> [rxkey]\n", argv[0]);
clicon_log_init("xmldb", LOG_DEBUG, CLICON_LOG_STDERR);
if (argc < 4){
usage(argv[0]);
goto done; goto done;
} }
cmd = argv[1]; dbfilename = argv[1];
db = argv[2]; if (argc == 3)
yangdir = argv[3]; rxkey = argv[2];
yangmod = argv[4];
db_init(db);
if ((yspec = yspec_new()) == NULL)
goto done
if (yang_parse(h, yangdir, yangmod, NULL, yspec) < 0)
goto done;
if (strcmp(cmd, "get")==0){
if (argc < 5)
usage(argv[0]);
xpath = argc>5?argv[5]:NULL;
if (xmldb_get(h, db, xpath, &xt, NULL, NULL) < 0)
goto done;
clicon_xml2file(stdout, xt, 0, 1);
}
else else
if (strcmp(cmd, "put")==0){ rxkey = "^.*$"; /* Default is match all */
if (argc != 6)
usage(argv[0]); /* Get all keys/values for vector */
if (clicon_xml_parse_file(0, &xt, "</clicon>") < 0) if ((npairs = db_regexp(dbfilename, rxkey, __FUNCTION__, &pairs, 0)) < 0)
goto done; goto done;
if (xml_rootchild(xt, 0, &xn) < 0)
goto done; for (npairs--; npairs >= 0; npairs--)
if (strcmp(argv[5], "set") == 0) fprintf(stdout, "%s %s\n", pairs[npairs].dp_key,
op = OP_REPLACE; pairs[npairs].dp_val?pairs[npairs].dp_val:"");
else retval = 0;
if (strcmp(argv[4], "merge") == 0)
op = OP_MERGE;
else if (strcmp(argv[5], "delete") == 0)
op = OP_REMOVE;
else
usage(argv[0]);
if (xmldb_put(h, db, op, NULL, xn) < 0)
goto done;
}
else
usage(argv[0]);
printf("\n");
done: done:
return 0; unchunk_group(__FUNCTION__);
return retval;
} }
#endif /* Test program */ #endif /* Test program */

View file

@ -39,18 +39,18 @@
/* /*
* Prototypes * Prototypes
*/ */
int kv_get(clicon_handle h, char *db, char *xpath, int kv_get(xmldb_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
int kv_put(clicon_handle h, char *db, enum operation_type op, int kv_put(xmldb_handle h, char *db, enum operation_type op,
char *api_path, cxobj *xt); char *api_path, cxobj *xt);
int kv_dump(FILE *f, char *dbfilename, char *rxkey); int kv_dump(FILE *f, char *dbfilename, char *rxkey);
int kv_copy(clicon_handle h, char *from, char *to); int kv_copy(xmldb_handle h, char *from, char *to);
int kv_lock(clicon_handle h, char *db, int pid); int kv_lock(xmldb_handle h, char *db, int pid);
int kv_unlock(clicon_handle h, char *db, int pid); int kv_unlock(xmldb_handle h, char *db, int pid);
int kv_unlock_all(clicon_handle h, int pid); int kv_unlock_all(xmldb_handle h, int pid);
int kv_islocked(clicon_handle h, char *db); int kv_islocked(xmldb_handle h, char *db);
int kv_exists(clicon_handle h, char *db); int kv_exists(xmldb_handle h, char *db);
int kv_delete(clicon_handle h, char *db); int kv_delete(xmldb_handle h, char *db);
int kv_init(clicon_handle h, char *db); int kv_init(xmldb_handle h, char *db);
#endif /* _CLIXON_KEYVALUE_H */ #endif /* _CLIXON_KEYVALUE_H */

View file

@ -44,11 +44,13 @@
#include <limits.h> #include <limits.h>
#include <fnmatch.h> #include <fnmatch.h>
#include <stdint.h> #include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <dirent.h> #include <dirent.h>
#include <assert.h> #include <assert.h>
#include <syslog.h> #include <syslog.h>
#include <fcntl.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -58,16 +60,42 @@
#include "clixon_xmldb_text.h" #include "clixon_xmldb_text.h"
#define handle(xh) (assert(text_handle_check(xh)==0),(struct text_handle *)(xh))
/* Magic to ensure plugin sanity. */
#define TEXT_HANDLE_MAGIC 0x7f54da29
/*! Internal structure of text datastore handle.
*/
struct text_handle {
int th_magic; /* magic */
char *th_dbdir; /* Directory of database files */
yang_spec *th_yangspec; /* Yang spec if this datastore */
};
/*! Check struct magic number for sanity checks
* return 0 if OK, -1 if fail.
*/
static int
text_handle_check(xmldb_handle xh)
{
/* Dont use handle macro to avoid recursion */
struct text_handle *th = (struct text_handle *)(xh);
return th->th_magic == TEXT_HANDLE_MAGIC ? 0 : -1;
}
/*! Database locking for candidate and running non-persistent /*! Database locking for candidate and running non-persistent
* Store an integer for running and candidate containing * Store an integer for running and candidate containing
* the session-id of the client holding the lock. * the session-id of the client holding the lock.
* @note This should probably be on file-system
*/ */
static int _running_locked = 0; static int _running_locked = 0;
static int _candidate_locked = 0; static int _candidate_locked = 0;
static int _startup_locked = 0; static int _startup_locked = 0;
/*! Translate from symbolic database name to actual filename in file-system /*! Translate from symbolic database name to actual filename in file-system
* @param[in] h Clicon handle * @param[in] th text handle handle
* @param[in] db Symbolic database name, eg "candidate", "running" * @param[in] db Symbolic database name, eg "candidate", "running"
* @param[out] filename Filename. Unallocate after use with free() * @param[out] filename Filename. Unallocate after use with free()
* @retval 0 OK * @retval 0 OK
@ -78,9 +106,9 @@ static int _startup_locked = 0;
* The filename reside in CLICON_XMLDB_DIR option * The filename reside in CLICON_XMLDB_DIR option
*/ */
static int static int
db2file(clicon_handle h, db2file(struct text_handle *th,
char *db, char *db,
char **filename) char **filename)
{ {
int retval = -1; int retval = -1;
cbuf *cb; cbuf *cb;
@ -90,8 +118,8 @@ db2file(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new"); clicon_err(OE_XML, errno, "cbuf_new");
goto done; goto done;
} }
if ((dir = clicon_xmldb_dir(h)) == NULL){ if ((dir = th->th_dbdir) == NULL){
clicon_err(OE_XML, errno, "CLICON_XMLDB_DIR not set"); clicon_err(OE_XML, errno, "dbdir not set");
goto done; goto done;
} }
if (strcmp(db, "running") != 0 && if (strcmp(db, "running") != 0 &&
@ -113,12 +141,116 @@ db2file(clicon_handle h,
return retval; return retval;
} }
/*! Connect to a datastore plugin
* @retval handle Use this handle for other API calls
* @retval NULL Error
*/
xmldb_handle
text_connect(void)
{
struct text_handle *th;
xmldb_handle xh = NULL;
int size;
size = sizeof(struct text_handle);
if ((th = malloc(size)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(th, 0, size);
th->th_magic = TEXT_HANDLE_MAGIC;
xh = (xmldb_handle)th;
done:
return xh;
}
/*! Disconnect from to a datastore plugin and deallocate handle
* @param[in] xh XMLDB handle, disconect and deallocate from this handle
* @retval 0 OK
*/
int
text_disconnect(xmldb_handle xh)
{
int retval = -1;
struct text_handle *th = handle(xh);
if (th){
if (th->th_dbdir)
free(th->th_dbdir);
free(th);
}
retval = 0;
// done:
return retval;
}
/*! Get value of generic plugin option. Type of value is givenby context
* @param[in] xh XMLDB handle
* @param[in] optname Option name
* @param[out] value Pointer to Value of option
* @retval 0 OK
* @retval -1 Error
*/
int
text_getopt(xmldb_handle xh,
char *optname,
void **value)
{
int retval = -1;
struct text_handle *th = handle(xh);
if (strcmp(optname, "yangspec") == 0)
*value = th->th_yangspec;
else if (strcmp(optname, "dbdir") == 0)
*value = th->th_dbdir;
else{
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Set value of generic plugin option. Type of value is givenby context
* @param[in] xh XMLDB handle
* @param[in] optname Option name
* @param[in] value Value of option
* @retval 0 OK
* @retval -1 Error
*/
int
text_setopt(xmldb_handle xh,
char *optname,
void *value)
{
int retval = -1;
struct text_handle *th = handle(xh);
if (strcmp(optname, "yangspec") == 0)
th->th_yangspec = (yang_spec*)value;
else if (strcmp(optname, "dbdir") == 0){
if (value && (th->th_dbdir = strdup((char*)value)) == NULL){
clicon_err(OE_UNIX, 0, "strdup");
goto done;
}
}
else{
clicon_err(OE_PLUGIN, 0, "Option %s not implemented by plugin", optname);
goto done;
}
retval = 0;
done:
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees /*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match * The function returns a minimal tree that includes all sub-trees that match
* xpath. * xpath.
* @param[in] xh XMLDB handle
* @param[in] dbname Name of database to search in (filename including dir path * @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all * @param[in] xpath String with XPATH syntax. or NULL for all
* @param[in] yspec Yang specification
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free() * @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use. * @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector. * @param[out] xlen Length of vector.
@ -128,8 +260,7 @@ db2file(clicon_handle h,
* cxobj *xt; * cxobj *xt;
* cxobj **xvec; * cxobj **xvec;
* size_t xlen; * size_t xlen;
* yang_spec *yspec = clicon_dbspec_yang(h); * if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0) * &xt, &xvec, &xlen) < 0)
* err; * err;
* for (i=0; i<xlen; i++){ * for (i=0; i<xlen; i++){
@ -144,25 +275,85 @@ db2file(clicon_handle h,
* @see xmldb_get * @see xmldb_get
*/ */
int int
text_get(clicon_handle h, text_get(xmldb_handle xh,
char *db, char *db,
char *xpath, char *xpath,
cxobj **xtop, cxobj **xtop,
cxobj ***xvec0, cxobj ***xvec0,
size_t *xlen0) size_t *xlen0)
{ {
int retval = -1; int retval = -1;
char *dbfile = NULL;
yang_spec *yspec;
cxobj *xt = NULL;
int fd = -1;
cxobj **xvec = NULL;
size_t xlen;
int i;
struct text_handle *th = handle(xh);
if (db2file(th, db, &dbfile) < 0)
goto done;
if (dbfile==NULL){
clicon_err(OE_XML, 0, "dbfile NULL");
goto done;
}
if ((yspec = th->th_yangspec) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if ((fd = open(dbfile, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", dbfile);
goto done;
}
if ((clicon_xml_parse_file(fd, &xt, "</clicon>")) < 0)
goto done;
/* XXX Maybe the below is general function and should be moved to xmldb? */
if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
goto done;
/* If vectors are specified then filter out everything else,
* otherwise return complete tree.
*/
if (xvec != NULL){
for (i=0; i<xlen; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
}
/* Top is special case */
if (!xml_flag(xt, XML_FLAG_MARK))
if (xml_tree_prune_unmarked(xt, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
if (xvec0 && xlen0){
*xvec0 = xvec;
xvec = NULL;
*xlen0 = xlen;
xlen = 0;
}
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done;
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
goto done;
if (debug>1)
clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt;
retval = 0; retval = 0;
// done: done:
// xml_free(xt);
if (fd != -1)
close(fd);
return retval; return retval;
} }
/*! Modify database provided an xml tree and an operation /*! Modify database provided an xml tree and an operation
* *
* @param[in] h CLICON handle * @param[in] xh XMLDB handle
* @param[in] db running or candidate * @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy * @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it. * @param[in] op OP_MERGE: just add it.
@ -182,30 +373,14 @@ text_get(clicon_handle h,
* @see xmldb_put_xkey for single key * @see xmldb_put_xkey for single key
*/ */
int int
text_put(clicon_handle h, text_put(xmldb_handle xh,
char *db, char *db,
enum operation_type op, enum operation_type op,
char *api_path, char *api_path,
cxobj *xt) cxobj *xt)
{ {
int retval = -1; int retval = -1;
retval = 0; // struct text_handle *th = handle(xh);
// done:
return retval;
}
/*! Raw dump of database, just keys and values, no xml interpretation
* @param[in] f File
* @param[in] dbfile File-name of database. This is a local file
* @param[in] rxkey Key regexp, eg "^.*$"
* @note This function can only be called locally.
*/
int
text_dump(FILE *f,
char *dbfilename,
char *rxkey)
{
int retval = -1;
retval = 0; retval = 0;
// done: // done:
@ -213,35 +388,39 @@ text_dump(FILE *f,
} }
/*! Copy database from db1 to db2 /*! Copy database from db1 to db2
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] from Source database copy * @param[in] from Source database copy
* @param[in] to Destination database * @param[in] to Destination database
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
*/ */
int int
text_copy(clicon_handle h, text_copy(xmldb_handle xh,
char *from, char *from,
char *to) char *to)
{ {
int retval = -1; int retval = -1;
// struct text_handle *th = handle(xh);
retval = 0; retval = 0;
// done: // done:
return retval; return retval;
} }
/*! Lock database /*! Lock database
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @param[in] pid Process id * @param[in] pid Process id
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
*/ */
int int
text_lock(clicon_handle h, text_lock(xmldb_handle xh,
char *db, char *db,
int pid) int pid)
{ {
// struct text_handle *th = handle(xh);
if (strcmp("running", db) == 0) if (strcmp("running", db) == 0)
_running_locked = pid; _running_locked = pid;
else if (strcmp("candidate", db) == 0) else if (strcmp("candidate", db) == 0)
@ -253,7 +432,7 @@ text_lock(clicon_handle h,
} }
/*! Unlock database /*! Unlock database
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @param[in] pid Process id * @param[in] pid Process id
* @retval -1 Error * @retval -1 Error
@ -261,10 +440,12 @@ text_lock(clicon_handle h,
* Assume all sanity checks have been made * Assume all sanity checks have been made
*/ */
int int
text_unlock(clicon_handle h, text_unlock(xmldb_handle xh,
char *db, char *db,
int pid) int pid)
{ {
// struct text_handle *th = handle(xh);
if (strcmp("running", db) == 0) if (strcmp("running", db) == 0)
_running_locked = 0; _running_locked = 0;
else if (strcmp("candidate", db) == 0) else if (strcmp("candidate", db) == 0)
@ -275,15 +456,17 @@ text_unlock(clicon_handle h,
} }
/*! Unlock all databases locked by pid (eg process dies) /*! Unlock all databases locked by pid (eg process dies)
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] pid Process / Session id * @param[in] pid Process / Session id
* @retval -1 Error * @retval -1 Error
* @retval 0 Ok * @retval 0 Ok
*/ */
int int
text_unlock_all(clicon_handle h, text_unlock_all(xmldb_handle xh,
int pid) int pid)
{ {
// struct text_handle *th = handle(xh);
if (_running_locked == pid) if (_running_locked == pid)
_running_locked = 0; _running_locked = 0;
if (_candidate_locked == pid) if (_candidate_locked == pid)
@ -294,16 +477,18 @@ text_unlock_all(clicon_handle h,
} }
/*! Check if database is locked /*! Check if database is locked
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval -1 Error * @retval -1 Error
* @retval 0 Not locked * @retval 0 Not locked
* @retval >0 Id of locker * @retval >0 Id of locker
*/ */
int int
text_islocked(clicon_handle h, text_islocked(xmldb_handle xh,
char *db) char *db)
{ {
// struct text_handle *th = handle(xh);
if (strcmp("running", db) == 0) if (strcmp("running", db) == 0)
return (_running_locked); return (_running_locked);
else if (strcmp("candidate", db) == 0) else if (strcmp("candidate", db) == 0)
@ -314,21 +499,23 @@ text_islocked(clicon_handle h,
} }
/*! Check if db exists /*! Check if db exists
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval -1 Error * @retval -1 Error
* @retval 0 No it does not exist * @retval 0 No it does not exist
* @retval 1 Yes it exists * @retval 1 Yes it exists
*/ */
int int
text_exists(clicon_handle h, text_exists(xmldb_handle xh,
char *db) char *db)
{ {
int retval = -1;
char *filename = NULL;
struct stat sb;
if (db2file(h, db, &filename) < 0) int retval = -1;
struct text_handle *th = handle(xh);
char *filename = NULL;
struct stat sb;
if (db2file(th, db, &filename) < 0)
goto done; goto done;
if (lstat(filename, &sb) < 0) if (lstat(filename, &sb) < 0)
retval = 0; retval = 0;
@ -341,16 +528,17 @@ text_exists(clicon_handle h,
} }
/*! Delete database. Remove file /*! Delete database. Remove file
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval -1 Error * @retval -1 Error
* @retval 0 OK * @retval 0 OK
*/ */
int int
text_delete(clicon_handle h, text_delete(xmldb_handle xh,
char *db) char *db)
{ {
int retval = -1; int retval = -1;
// struct text_handle *th = handle(xh);
retval = 0; retval = 0;
// done: // done:
@ -358,16 +546,17 @@ text_delete(clicon_handle h,
} }
/*! Initialize database /*! Initialize database
* @param[in] h Clicon handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int int
text_init(clicon_handle h, text_init(xmldb_handle xh,
char *db) char *db)
{ {
int retval = -1; int retval = -1;
// struct text_handle *th = handle(xh);
retval = 0; retval = 0;
// done: // done:
@ -402,9 +591,12 @@ static const struct xmldb_api api = {
XMLDB_API_MAGIC, XMLDB_API_MAGIC,
clixon_xmldb_plugin_init, clixon_xmldb_plugin_init,
text_plugin_exit, text_plugin_exit,
text_connect,
text_disconnect,
text_getopt,
text_setopt,
text_get, text_get,
text_put, text_put,
text_dump,
text_copy, text_copy,
text_lock, text_lock,
text_unlock, text_unlock,

View file

@ -39,18 +39,18 @@
/* /*
* Prototypes * Prototypes
*/ */
int text_get(clicon_handle h, char *db, char *xpath, int text_get(xmldb_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
int text_put(clicon_handle h, char *db, enum operation_type op, int text_put(xmldb_handle h, char *db, enum operation_type op,
char *api_path, cxobj *xt); char *api_path, cxobj *xt);
int text_dump(FILE *f, char *dbfilename, char *rxkey); int text_dump(FILE *f, char *dbfilename, char *rxkey);
int text_copy(clicon_handle h, char *from, char *to); int text_copy(xmldb_handle h, char *from, char *to);
int text_lock(clicon_handle h, char *db, int pid); int text_lock(xmldb_handle h, char *db, int pid);
int text_unlock(clicon_handle h, char *db, int pid); int text_unlock(xmldb_handle h, char *db, int pid);
int text_unlock_all(clicon_handle h, int pid); int text_unlock_all(xmldb_handle h, int pid);
int text_islocked(clicon_handle h, char *db); int text_islocked(xmldb_handle h, char *db);
int text_exists(clicon_handle h, char *db); int text_exists(xmldb_handle h, char *db);
int text_delete(clicon_handle h, char *db); int text_delete(xmldb_handle h, char *db);
int text_init(clicon_handle h, char *db); int text_init(xmldb_handle h, char *db);
#endif /* _CLIXON_XMLDB_TEXT_H */ #endif /* _CLIXON_XMLDB_TEXT_H */

View file

@ -71,4 +71,10 @@ clicon_hash_t *clicon_options(clicon_handle h);
/* Return internal clicon data (hash-array) given a handle.*/ /* Return internal clicon data (hash-array) given a handle.*/
clicon_hash_t *clicon_data(clicon_handle h); clicon_hash_t *clicon_data(clicon_handle h);
/* Set or reset XMLDB storage handle */
int clicon_handle_xmldb_set(clicon_handle h, void *xh); /* ie xmldb_handle */
/* Get XMLDB storage handle */
void *clicon_handle_xmldb_get(clicon_handle h);
#endif /* _CLIXON_HANDLE_H_ */ #endif /* _CLIXON_HANDLE_H_ */

View file

@ -34,6 +34,19 @@
#ifndef _CLIXON_XML_DB_H #ifndef _CLIXON_XML_DB_H
#define _CLIXON_XML_DB_H #define _CLIXON_XML_DB_H
/* The XMLDB has a handle to keep connected state. To users of the API it is
* a void* but it may have structure within a specific plugin.
* The handle is independent from clicon so that (in principle) the datastore
* can work in other contexts than clicon.
* The connect API call sets the handle and disconnect resets it. In principle
* there can be several handles at one time.
*/
#if 1 /* SANITY CHECK */
typedef struct {int16_t a;} *xmldb_handle;
#else
typedef void *xmldb_handle;
#endif
/* Version of clixon datastore plugin API. */ /* Version of clixon datastore plugin API. */
#define XMLDB_API_VERSION 1 #define XMLDB_API_VERSION 1
@ -49,40 +62,49 @@ typedef void * (plugin_init_t)(int version);
/* Type of plugin exit function */ /* Type of plugin exit function */
typedef int (plugin_exit_t)(void); typedef int (plugin_exit_t)(void);
/* Type of xmldb connect function */
typedef xmldb_handle (xmldb_connect_t)(void);
/* Type of xmldb disconnect function */
typedef int (xmldb_disconnect_t)(xmldb_handle xh);
/* Type of xmldb getopt function */
typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
/* Type of xmldb setopt function */
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
/* Type of xmldb get function */ /* Type of xmldb get function */
typedef int (xmldb_get_t)(clicon_handle h, char *db, char *xpath, typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
/* Type of xmldb put function */ /* Type of xmldb put function */
typedef int (xmldb_put_t)(clicon_handle h, char *db, enum operation_type op, typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op,
char *api_path, cxobj *xt); char *api_path, cxobj *xt);
/* Type of xmldb dump function */
typedef int (xmldb_dump_t)(FILE *f, char *dbfilename, char *rxkey);
/* Type of xmldb copy function */ /* Type of xmldb copy function */
typedef int (xmldb_copy_t)(clicon_handle h, char *from, char *to); typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to);
/* Type of xmldb lock function */ /* Type of xmldb lock function */
typedef int (xmldb_lock_t)(clicon_handle h, char *db, int pid); typedef int (xmldb_lock_t)(xmldb_handle xh, char *db, int pid);
/* Type of xmldb unlock function */ /* Type of xmldb unlock function */
typedef int (xmldb_unlock_t)(clicon_handle h, char *db, int pid); typedef int (xmldb_unlock_t)(xmldb_handle xh, char *db, int pid);
/* Type of xmldb unlock_all function */ /* Type of xmldb unlock_all function */
typedef int (xmldb_unlock_all_t)(clicon_handle h, int pid); typedef int (xmldb_unlock_all_t)(xmldb_handle xh, int pid);
/* Type of xmldb islocked function */ /* Type of xmldb islocked function */
typedef int (xmldb_islocked_t)(clicon_handle h, char *db); typedef int (xmldb_islocked_t)(xmldb_handle xh, char *db);
/* Type of xmldb exists function */ /* Type of xmldb exists function */
typedef int (xmldb_exists_t)(clicon_handle h, char *db); typedef int (xmldb_exists_t)(xmldb_handle xh, char *db);
/* Type of xmldb delete function */ /* Type of xmldb delete function */
typedef int (xmldb_delete_t)(clicon_handle h, char *db); typedef int (xmldb_delete_t)(xmldb_handle xh, char *db);
/* Type of xmldb init function */ /* Type of xmldb init function */
typedef int (xmldb_init_t)(clicon_handle h, char *db); typedef int (xmldb_init_t)(xmldb_handle xh, char *db);
/* grideye agent plugin init struct for the api */ /* grideye agent plugin init struct for the api */
struct xmldb_api{ struct xmldb_api{
@ -90,9 +112,12 @@ struct xmldb_api{
int xa_magic; int xa_magic;
plugin_init_t *xa_plugin_init_fn; /* XMLDB_PLUGIN_INIT_FN */ plugin_init_t *xa_plugin_init_fn; /* XMLDB_PLUGIN_INIT_FN */
plugin_exit_t *xa_plugin_exit_fn; plugin_exit_t *xa_plugin_exit_fn;
xmldb_connect_t *xa_connect_fn;
xmldb_disconnect_t *xa_disconnect_fn;
xmldb_getopt_t *xa_getopt_fn;
xmldb_setopt_t *xa_setopt_fn;
xmldb_get_t *xa_get_fn; xmldb_get_t *xa_get_fn;
xmldb_put_t *xa_put_fn; xmldb_put_t *xa_put_fn;
xmldb_dump_t *xa_dump_fn;
xmldb_copy_t *xa_copy_fn; xmldb_copy_t *xa_copy_fn;
xmldb_lock_t *xa_lock_fn; xmldb_lock_t *xa_lock_fn;
xmldb_unlock_t *xa_unlock_fn; xmldb_unlock_t *xa_unlock_fn;
@ -105,14 +130,19 @@ struct xmldb_api{
/* /*
* Prototypes * Prototypes
* API
*/ */
int xmldb_plugin_load(char *filename); int xmldb_plugin_load(clicon_handle h, char *filename);
int xmldb_plugin_unload(clicon_handle h);
int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h);
int xmldb_getopt(clicon_handle h, char *optname, void **value);
int xmldb_setopt(clicon_handle h, char *optname, void *value);
int xmldb_get(clicon_handle h, char *db, char *xpath, int xmldb_get(clicon_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put(clicon_handle h, char *db, enum operation_type op, int xmldb_put(clicon_handle h, char *db, enum operation_type op,
char *api_path, cxobj *xt); char *api_path, cxobj *xt);
int xmldb_dump(FILE *f, char *dbfilename, char *rxkey);
int xmldb_copy(clicon_handle h, char *from, char *to); int xmldb_copy(clicon_handle h, char *from, char *to);
int xmldb_lock(clicon_handle h, char *db, int pid); int xmldb_lock(clicon_handle h, char *db, int pid);
int xmldb_unlock(clicon_handle h, char *db, int pid); int xmldb_unlock(clicon_handle h, char *db, int pid);

View file

@ -64,5 +64,9 @@ int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt); int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk); int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk); int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
int xml_tree_prune_unmarked(cxobj *xt, int *upmark);
int xml_default(cxobj *x, void *arg);
int xml_order(cxobj *x, void *arg);
int xml_sanity(cxobj *x, void *arg);
#endif /* _CLIXON_XML_MAP_H_ */ #endif /* _CLIXON_XML_MAP_H_ */

View file

@ -58,18 +58,17 @@
#define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h)) #define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h))
/* /*! Internal structure of basic handle. Also header of all other handles.
* clicon_handle * @note If you change here, you must also change the structs below:
* Internal structire of basic handle. Also header of all other handles. * @see struct cli_handle, struct backend_handle
* see struct clicon_cli_handle, struct clicon_backend_handle, etc
*/ */
struct clicon_handle { struct clicon_handle {
int ch_magic; /* magic (HDR) */ int ch_magic; /* magic (HDR) */
clicon_hash_t *ch_copt; /* clicon option list (HDR) */ clicon_hash_t *ch_copt; /* clicon option list (HDR) */
clicon_hash_t *ch_data; /* internal clicon data (HDR) */ clicon_hash_t *ch_data; /* internal clicon data (HDR) */
void *ch_xmldb; /* XMLDB storage handle, uie xmldb_handle */
}; };
/*! Internal call to allocate a CLICON handle. /*! Internal call to allocate a CLICON handle.
* *
* There may be different variants of handles with some common options. * There may be different variants of handles with some common options.
@ -166,3 +165,31 @@ clicon_data(clicon_handle h)
return ch->ch_data; return ch->ch_data;
} }
/*! Set or reset XMLDB storage handle
* @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_handle_xmldb_set(clicon_handle h,
void *xh)
{
struct clicon_handle *ch = handle(h);
ch->ch_xmldb = xh;
return 0;
}
/*! Get XMLDB storage handle
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
*/
void *
clicon_handle_xmldb_get(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
return ch->ch_xmldb;
}

View file

@ -1008,6 +1008,7 @@ FSM(char *tag,
* Note, xt will add a top-level symbol called "top" meaning that <tree../> will look as: * Note, xt will add a top-level symbol called "top" meaning that <tree../> will look as:
* <top><tree.../></tree> * <top><tree.../></tree>
* XXX: There is a potential leak here on some return values. * XXX: There is a potential leak here on some return values.
* XXX: What happens if endtag is different?
* May block * May block
*/ */
int int
@ -1023,15 +1024,16 @@ clicon_xml_parse_file(int fd,
int maxbuf = BUFLEN; int maxbuf = BUFLEN;
int endtaglen = strlen(endtag); int endtaglen = strlen(endtag);
int state = 0; int state = 0;
int oldmaxbuf;
if (endtag == NULL){ if (endtag == NULL){
clicon_err(OE_XML, 0, "%s: endtag required\n", __FUNCTION__); clicon_err(OE_XML, 0, "%s: endtag required\n", __FUNCTION__);
return -1; goto done;
} }
*cx = NULL; *cx = NULL;
if ((xmlbuf = malloc(maxbuf)) == NULL){ if ((xmlbuf = malloc(maxbuf)) == NULL){
clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__); clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
return -1; goto done;
} }
memset(xmlbuf, 0, maxbuf); memset(xmlbuf, 0, maxbuf);
ptr = xmlbuf; ptr = xmlbuf;
@ -1050,24 +1052,33 @@ clicon_xml_parse_file(int fd,
state = 0; state = 0;
if ((*cx = xml_new("top", NULL)) == NULL) if ((*cx = xml_new("top", NULL)) == NULL)
break; break;
if (xml_parse(ptr, *cx) < 0) if (xml_parse(ptr, *cx) < 0){
goto done;
return -1; return -1;
}
break; break;
} }
if (len>=maxbuf-1){ /* Space: one for the null character */ if (len>=maxbuf-1){ /* Space: one for the null character */
int oldmaxbuf = maxbuf; oldmaxbuf = maxbuf;
maxbuf *= 2; maxbuf *= 2;
if ((xmlbuf = realloc(xmlbuf, maxbuf)) == NULL){ if ((xmlbuf = realloc(xmlbuf, maxbuf)) == NULL){
clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__); clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
return -1; goto done;
} }
memset(xmlbuf+oldmaxbuf, 0, maxbuf-oldmaxbuf); memset(xmlbuf+oldmaxbuf, 0, maxbuf-oldmaxbuf);
ptr = xmlbuf; ptr = xmlbuf;
} }
} /* while */ } /* while */
free(xmlbuf); retval = 0;
return (*cx)?0:-1; done:
if (retval < 0 && *cx){
free(*cx);
*cx = NULL;
}
if (xmlbuf)
free(xmlbuf);
return retval;
// return (*cx)?0:-1;
} }
/*! Read an XML definition from string and parse it into a parse-tree. /*! Read an XML definition from string and parse it into a parse-tree.
@ -1461,8 +1472,12 @@ xml_body_uint32(cxobj *xb,
} }
/*! Map xml operation from string to enumeration /*! Map xml operation from string to enumeration
* @param[in] xn XML node * @param[in] opstr String, eg "merge"
* @param[out] op "operation" attribute may change operation * @param[out] op Enumeration, eg OP_MERGE
* @code
* enum operation_type op;
* xml_operation("replace", &op)
* @endcode
*/ */
int int
xml_operation(char *opstr, xml_operation(char *opstr,
@ -1487,6 +1502,14 @@ xml_operation(char *opstr,
return 0; return 0;
} }
/*! Map xml operation from enumeration to string
* @param[in] op enumeration operation, eg OP_MERGE,...
* @retval str String, eg "merge". Static string, no free necessary
* @code
* enum operation_type op;
* xml_operation("replace", &op)
* @endcode
*/
char * char *
xml_operation2str(enum operation_type op) xml_operation2str(enum operation_type op)
{ {
@ -1510,3 +1533,4 @@ xml_operation2str(enum operation_type op)
return "none"; return "none";
} }
} }

View file

@ -58,6 +58,8 @@
#include "clixon_hash.h" #include "clixon_hash.h"
#include "clixon_handle.h" #include "clixon_handle.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_yang.h"
#include "clixon_options.h"
#include "clixon_xml_db.h" #include "clixon_xml_db.h"
static struct xmldb_api *_xa_api = NULL; static struct xmldb_api *_xa_api = NULL;
@ -65,12 +67,13 @@ static struct xmldb_api *_xa_api = NULL;
/*! Load a specific plugin, call its init function and add it to plugins list /*! Load a specific plugin, call its init function and add it to plugins list
* If init function fails (not found, wrong version, etc) print a log and dont * If init function fails (not found, wrong version, etc) print a log and dont
* add it. * add it.
* @param[in] name Name of plugin * @param[in] name Filename (complete path) of plugin
* @param[in] filename Actual filename with path * @param[in] filename Actual filename with path
* @param[out] plugin Plugin data structure for invoking. Dealloc with free * @param[out] plugin Plugin data structure for invoking. Dealloc with free
*/ */
int int
xmldb_plugin_load(char *filename) xmldb_plugin_load(clicon_handle h,
char *filename)
{ {
int retval = -1; int retval = -1;
char *dlerrcode; char *dlerrcode;
@ -117,11 +120,179 @@ xmldb_plugin_load(char *filename)
goto done; goto done;
} }
int /*! XXX: fixme */
xmldb_get(clicon_handle h, char *db, char *xpath, int
cxobj **xtop, cxobj ***xvec, size_t *xlen) xmldb_plugin_unload(clicon_handle h)
{ {
int retval = -1; return 0;
}
/*! Connect to a datastore plugin
* @retval handle Use this handle for other API calls
* @retval NULL Error
* @note You can do several connects, and have multiple connections to the same
* datastore. Note also that the xmldb handle is hidden in the clicon
* handle, the clixon user does not need to handle it. Note also that
* typically only the backend invokes the datastore.
* XXX what args does connect have?
*/
int
xmldb_connect(clicon_handle h)
{
int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (_xa_api->xa_connect_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = _xa_api->xa_connect_fn()) == NULL)
goto done;
clicon_handle_xmldb_set(h, xh);
retval = 0;
done:
return retval;
}
/*! Disconnect from a datastore plugin and deallocate handle
* @param[in] handle Disconect and deallocate from this handle
* @retval 0 OK
*/
int
xmldb_disconnect(clicon_handle h)
{
int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (_xa_api->xa_disconnect_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Already disconnected from datastore plugin");
goto done;
}
if (_xa_api->xa_disconnect_fn(xh) < 0)
goto done;
clicon_handle_xmldb_set(h, NULL);
retval = 0;
done:
return retval;
}
/*! Get value of generic plugin option. Type of value is givenby context
* @param[in] xh XMLDB handle
* @param[in] optname Option name
* @param[out] value Pointer to Value of option
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_getopt(clicon_handle h,
char *optname,
void **value)
{
int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (_xa_api->xa_getopt_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_getopt_fn(xh, optname, value);
done:
return retval;
}
/*! Set value of generic plugin option. Type of value is givenby context
* @param[in] xh XMLDB handle
* @param[in] optname Option name
* @param[in] value Value of option
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_setopt(clicon_handle h,
char *optname,
void *value)
{
int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (_xa_api->xa_setopt_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_setopt_fn(xh, optname, value);
done:
return retval;
}
/*! Get content of database using xpath. return a set of matching sub-trees
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @param[in] h Clicon handle
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax. or NULL for all
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
* @param[out] xvec Vector of xml trees. Free after use.
* @param[out] xlen Length of vector.
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* xml_free(xt);
* free(xvec);
* @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec
* @see xmldb_get
*/
int
xmldb_get(clicon_handle h,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -131,7 +302,11 @@ xmldb_get(clicon_handle h, char *db, char *xpath,
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_get_fn(h, db, xpath, xtop, xvec, xlen); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_get_fn(xh, db, xpath, xtop, xvec, xlen);
done: done:
return retval; return retval;
} }
@ -152,16 +327,20 @@ xmldb_get(clicon_handle h, char *db, char *xpath,
* cxobj *xt; * cxobj *xt;
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0) * if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
* err; * err;
* if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0) * if (xmldb_put(xh, "running", OP_MERGE, NULL, xt) < 0)
* err; * err;
* @endcode * @endcode
* @see xmldb_put_xkey for single key * @see xmldb_put_xkey for single key
*/ */
int int
xmldb_put(clicon_handle h, char *db, enum operation_type op, xmldb_put(clicon_handle h,
char *api_path, cxobj *xt) char *db,
enum operation_type op,
char *api_path,
cxobj *xt)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -171,32 +350,11 @@ xmldb_put(clicon_handle h, char *db, enum operation_type op,
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_put_fn(h, db, op, api_path, xt); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
done: clicon_err(OE_DB, 0, "Not connected to datastore plugin");
return retval;
}
/*! Raw dump of database, in internal format (depends on datastore)
* @param[in] f File
* @param[in] dbfile File-name of database. This is a local file
* @param[in] pattern Key regexp, eg "^.*$"
*/
int
xmldb_dump(FILE *f,
char *dbfilename,
char *pattern)
{
int retval = -1;
if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done; goto done;
} }
if (_xa_api->xa_dump_fn == NULL){ retval = _xa_api->xa_put_fn(xh, db, op, api_path, xt);
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
retval = _xa_api->xa_dump_fn(f, dbfilename, pattern);
done: done:
return retval; return retval;
} }
@ -209,9 +367,12 @@ xmldb_dump(FILE *f,
* @retval 0 OK * @retval 0 OK
*/ */
int int
xmldb_copy(clicon_handle h, char *from, char *to) xmldb_copy(clicon_handle h,
char *from,
char *to)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -221,7 +382,11 @@ xmldb_copy(clicon_handle h, char *from, char *to)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_copy_fn(h, from, to); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_copy_fn(xh, from, to);
done: done:
return retval; return retval;
} }
@ -234,9 +399,12 @@ xmldb_copy(clicon_handle h, char *from, char *to)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xmldb_lock(clicon_handle h, char *db, int pid) xmldb_lock(clicon_handle h,
char *db,
int pid)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -246,7 +414,11 @@ xmldb_lock(clicon_handle h, char *db, int pid)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_lock_fn(h, db, pid); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_lock_fn(xh, db, pid);
done: done:
return retval; return retval;
} }
@ -260,9 +432,12 @@ xmldb_lock(clicon_handle h, char *db, int pid)
* Assume all sanity checks have been made * Assume all sanity checks have been made
*/ */
int int
xmldb_unlock(clicon_handle h, char *db, int pid) xmldb_unlock(clicon_handle h,
char *db,
int pid)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -272,7 +447,11 @@ xmldb_unlock(clicon_handle h, char *db, int pid)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_unlock_fn(h, db, pid); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_unlock_fn(xh, db, pid);
done: done:
return retval; return retval;
} }
@ -284,9 +463,11 @@ xmldb_unlock(clicon_handle h, char *db, int pid)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xmldb_unlock_all(clicon_handle h, int pid) xmldb_unlock_all(clicon_handle h,
int pid)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -296,7 +477,11 @@ xmldb_unlock_all(clicon_handle h, int pid)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval =_xa_api->xa_unlock_all_fn(h, pid); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval =_xa_api->xa_unlock_all_fn(xh, pid);
done: done:
return retval; return retval;
} }
@ -309,9 +494,11 @@ xmldb_unlock_all(clicon_handle h, int pid)
* @retval >0 Id of locker * @retval >0 Id of locker
*/ */
int int
xmldb_islocked(clicon_handle h, char *db) xmldb_islocked(clicon_handle h,
char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -321,7 +508,11 @@ xmldb_islocked(clicon_handle h, char *db)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval =_xa_api->xa_islocked_fn(h, db); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval =_xa_api->xa_islocked_fn(xh, db);
done: done:
return retval; return retval;
} }
@ -334,9 +525,11 @@ xmldb_islocked(clicon_handle h, char *db)
* @retval 1 Yes it exists * @retval 1 Yes it exists
*/ */
int int
xmldb_exists(clicon_handle h, char *db) xmldb_exists(clicon_handle h,
char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -346,7 +539,11 @@ xmldb_exists(clicon_handle h, char *db)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_exists_fn(h, db); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_exists_fn(xh, db);
done: done:
return retval; return retval;
} }
@ -358,9 +555,11 @@ xmldb_exists(clicon_handle h, char *db)
* @retval 0 OK * @retval 0 OK
*/ */
int int
xmldb_delete(clicon_handle h, char *db) xmldb_delete(clicon_handle h,
char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -370,21 +569,27 @@ xmldb_delete(clicon_handle h, char *db)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_delete_fn(h, db); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_delete_fn(xh, db);
done: done:
return retval; return retval;
} }
/*! Initialize database /*! Initialize database. Open database for writing.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db Database * @param[in] db Database
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */
int int
xmldb_init(clicon_handle h, char *db) xmldb_init(clicon_handle h,
char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh;
if (_xa_api == NULL){ if (_xa_api == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin"); clicon_err(OE_DB, 0, "No xmldb plugin");
@ -394,7 +599,11 @@ xmldb_init(clicon_handle h, char *db)
clicon_err(OE_DB, 0, "No xmldb function"); clicon_err(OE_DB, 0, "No xmldb function");
goto done; goto done;
} }
retval = _xa_api->xa_init_fn(h, db); if ((xh = clicon_handle_xmldb_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = _xa_api->xa_init_fn(xh, db);
done: done:
return retval; return retval;
} }

View file

@ -1074,3 +1074,176 @@ xmlkeyfmt2xpath(char *xkfmt,
cbuf_free(cb); cbuf_free(cb);
return retval; return retval;
} }
/*! Prune everything that has not been marked
* @param[in] xt XML tree with some node marked
* @param[out] upmark Set if a child (recursively) has marked set.
* The function removes all branches that does not contain a marked child
* XXX: maybe key leafs should not be purged if list is not purged?
* XXX: consider move to clicon_xml
*/
int
xml_tree_prune_unmarked(cxobj *xt,
int *upmark)
{
int retval = -1;
int submark;
int mark;
cxobj *x;
cxobj *xprev;
mark = 0;
x = NULL;
xprev = x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (xml_flag(x, XML_FLAG_MARK)){
mark++;
xprev = x;
continue; /* mark and stop here */
}
if (xml_tree_prune_unmarked(x, &submark) < 0)
goto done;
if (submark)
mark++;
else{ /* Safe with xml_child_each if last */
if (xml_purge(x) < 0)
goto done;
x = xprev;
}
xprev = x;
}
retval = 0;
done:
if (upmark)
*upmark = mark;
return retval;
}
/*! Add default values (if not set)
* @param[in] xt XML tree with some node marked
*/
int
xml_default(cxobj *xt,
void *arg)
{
int retval = -1;
yang_stmt *ys;
yang_stmt *y;
int i;
cxobj *xc;
cxobj *xb;
char *str;
ys = (yang_stmt*)xml_spec(xt);
/* Check leaf defaults */
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){
for (i=0; i<ys->ys_len; i++){
y = ys->ys_stmt[i];
if (y->ys_keyword != Y_LEAF)
continue;
assert(y->ys_cv);
if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */
if (!xml_find(xt, y->ys_argument)){
if ((xc = xml_new_spec(y->ys_argument, xt, y)) == NULL)
goto done;
if ((xb = xml_new("body", xc)) == NULL)
goto done;
xml_type_set(xb, CX_BODY);
if ((str = cv2str_dup(y->ys_cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
if (xml_value_set(xb, str) < 0)
goto done;
free(str);
}
}
}
}
retval = 0;
done:
return retval;
}
/*! Order XML children according to YANG
* @param[in] xt XML top of tree
*/
int
xml_order(cxobj *xt,
void *arg)
{
int retval = -1;
yang_stmt *y;
yang_stmt *yc;
int i;
int j0;
int j;
cxobj *xc;
cxobj *xj;
char *yname; /* yang child name */
char *xname; /* xml child name */
y = (yang_stmt*)xml_spec(xt);
j0 = 0;
/* Go through xml children and ensure they are same order as yspec children */
for (i=0; i<y->ys_len; i++){
yc = y->ys_stmt[i];
if (!yang_is_syntax(yc))
continue;
yname = yc->ys_argument;
/* First go thru xml children with same name */
for (; j0<xml_child_nr(xt); j0++){
xc = xml_child_i(xt, j0);
if (xml_type(xc) != CX_ELMNT)
continue;
xname = xml_name(xc);
if (strcmp(xname, yname))
break;
}
/* Now we have children not with same name */
for (j=j0; j<xml_child_nr(xt); j++){
xc = xml_child_i(xt, j);
if (xml_type(xc) != CX_ELMNT)
continue;
xname = xml_name(xc);
if (strcmp(xname, yname))
continue;
/* reorder */
xj = xml_child_i(xt, j0);
xml_child_i_set(xt, j0, xc);
xml_child_i_set(xt, j, xj);
j0++;
}
}
retval = 0;
// done:
return retval;
}
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer
* @param[in] xt XML top of tree
*/
int
xml_sanity(cxobj *xt,
void *arg)
{
int retval = -1;
yang_stmt *ys;
char *name;
ys = (yang_stmt*)xml_spec(xt);
name = xml_name(xt);
if (ys==NULL){
clicon_err(OE_XML, 0, "No spec for xml node %s", name);
goto done;
}
if (strstr(ys->ys_argument, name)==NULL){
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
name, ys->ys_argument);
goto done;
}
retval = 0;
done:
return retval;
}

View file

@ -1540,11 +1540,11 @@ yang_parse1(clicon_handle h,
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree /*! Parse top yang module including all its sub-modules. Expand and populate yang tree
* *
* @param h CLICON handle * @param[in] h CLICON handle
* @param yang_dir Directory where all YANG module files reside * @param[in] yang_dir Directory where all YANG module files reside
* @param module Name of main YANG module. More modules may be parsed if imported * @param[in] module Name of main YANG module. More modules may be parsed if imported
* @param revision Optional module revision date * @param[in] revision Optional module revision date
* @param ysp Yang specification. Should ave been created by caller using yspec_new * @param[out] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval 0 Everything OK * @retval 0 Everything OK
* @retval -1 Error encountered * @retval -1 Error encountered
* The database symbols are inserted in alphabetical order. * The database symbols are inserted in alphabetical order.