diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 6b28a139..993727db 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -29,6 +29,10 @@
#
# ***** END LICENSE BLOCK *****
+- Moved apps/dbctrl to datastore/
+
+- Added connect/disconnect/getopt/setopt and handle to xmldb API
+
- Added datastore 'text'
- Configure (autoconf) changes
diff --git a/apps/Makefile.in b/apps/Makefile.in
index 7f7ca9c0..a67233bc 100644
--- a/apps/Makefile.in
+++ b/apps/Makefile.in
@@ -43,7 +43,6 @@ SHELL = /bin/sh
SUBDIRS = backend
SUBDIRS += cli
-SUBDIRS += dbctrl
SUBDIRS += netconf
ifeq ($(with_restconf),yes)
SUBDIRS += restconf
diff --git a/apps/backend/backend_handle.h b/apps/backend/backend_handle.h
index 80251f08..4a81af16 100644
--- a/apps/backend/backend_handle.h
+++ b/apps/backend/backend_handle.h
@@ -41,7 +41,7 @@
* Prototypes
* not exported.
*/
-/* backend handles */
+/* backend handles. Defined in clixon_backend_handle.c */
clicon_handle backend_handle_init(void);
int backend_handle_exit(clicon_handle h);
diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c
index f417e2ec..badf84f2 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -254,19 +254,20 @@ server_socket(clicon_handle h)
* log event.
*/
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;
- char *ptr;
- char *nptr;
- char *newmsg = NULL;
- int retval = -1;
+ char *ptr;
+ char *nptr;
+ char *newmsg = NULL;
- /* backend_notify() will go through all clients and see if any has registered "CLICON",
- and if so make a clicon_proto notify message to those clients. */
-
-
- /* Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
+ /* backend_notify() will go through all clients and see if any has
+ registered "CLICON", and if so make a clicon_proto notify message to
+ those clients.
+ Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
At this stage all formatting is already done */
n = 0;
for(ptr=msg; *ptr; ptr++)
@@ -281,7 +282,6 @@ config_log_cb(int level, char *msg, void *arg)
if (*ptr == '%')
*nptr++ = '%';
}
-
retval = backend_notify(arg, "CLICON", level, 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");
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;
-
/* Parse db spec file */
if (yang_spec_main(h, stdout, printspec) < 0)
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
XXX the options below have become out-of-hand.
Too complex, need to simplify*/
diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c
index 720337e8..85610654 100644
--- a/apps/backend/clixon_backend_handle.c
+++ b/apps/backend/clixon_backend_handle.c
@@ -75,14 +75,21 @@
* This file should only contain access functions for the _specific_
* 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 {
- int cb_magic; /* magic (HDR)*/
- clicon_hash_t *cb_copt; /* clicon option list (HDR) */
- clicon_hash_t *cb_data; /* internal clicon data (HDR) */
+ int bh_magic; /* magic (HDR)*/
+ clicon_hash_t *bh_copt; /* clicon option list (HDR) */
+ clicon_hash_t *bh_data; /* internal clicon data (HDR) */
+ void *bh_xmldb; /* XMLDB storage handle, uie xmldb_handle */
/* ------ end of common handle ------ */
- struct client_entry *cb_ce_list; /* The client list */
- int cb_ce_nr; /* Number of clients, just increment */
- struct handle_subscription *cb_subscription; /* Event subscription list */
+ struct client_entry *bh_ce_list; /* The client list */
+ int bh_ce_nr; /* Number of clients, just increment */
+ struct handle_subscription *bh_subscription; /* Event subscription list */
};
/*! 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 *
backend_client_add(clicon_handle h,
struct sockaddr *addr)
{
- struct backend_handle *cb = handle(h);
+ struct backend_handle *bh = handle(h);
struct client_entry *ce;
if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){
@@ -269,24 +282,28 @@ backend_client_add(clicon_handle h,
return NULL;
}
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));
- ce->ce_next = cb->cb_ce_list;
- cb->cb_ce_list = ce;
+ ce->ce_next = bh->bh_ce_list;
+ bh->bh_ce_list = ce;
return ce;
}
+/*! Return client list
+ * @param[in] h Clicon handle
+ * @retval ce_list Client entry list (all sessions)
+ */
struct client_entry *
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
* @param[in] h Clicon handle
- * @param[in] ce Client hadnle
+ * @param[in] ce Client handle
* @see backend_client_rm which is more high-level
*/
int
@@ -295,9 +312,9 @@ backend_client_delete(clicon_handle h,
{
struct client_entry *c;
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){
if (c == ce){
*ce_prev = c->ce_next;
@@ -328,7 +345,7 @@ subscription_add(clicon_handle h,
subscription_fn_t fn,
void *arg)
{
- struct backend_handle *cb = handle(h);
+ struct backend_handle *bh = handle(h);
struct handle_subscription *hs = NULL;
if ((hs = malloc(sizeof(*hs))) == NULL){
@@ -339,10 +356,10 @@ subscription_add(clicon_handle h,
hs->hs_stream = strdup(stream);
hs->hs_format = format;
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_arg = arg;
- cb->cb_subscription = hs;
+ bh->bh_subscription = hs;
done:
return hs;
}
@@ -362,11 +379,11 @@ subscription_delete(clicon_handle h,
subscription_fn_t fn,
void *arg)
{
- struct backend_handle *cb = handle(h);
+ struct backend_handle *bh = handle(h);
struct handle_subscription *hs;
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){
/* XXX arg == hs->hs_arg */
if (strcmp(hs->hs_stream, stream)==0 && hs->hs_fn == fn){
@@ -404,15 +421,16 @@ struct handle_subscription *
subscription_each(clicon_handle h,
struct handle_subscription *hprev)
{
- struct backend_handle *cb = handle(h);
+ struct backend_handle *bh = handle(h);
struct handle_subscription *hs = NULL;
if (hprev)
hs = hprev->hs_next;
else
- hs = cb->cb_subscription;
+ hs = bh->bh_subscription;
return hs;
}
+
/* Database dependency description */
struct backend_netconf_reg {
qelem_t nr_qelem; /* List header */
@@ -456,10 +474,9 @@ catch:
/*! See if there is any callback registered for this tag
*
* @param[in] h clicon handle
- * @param[in] xn Sub-tree (under xorig) at child of rpc: .
- * @param[out] cb Output xml stream. For reply
- * @param[out] cb_err Error xml stream. For error reply
- * @param[out] xret Return XML, error or OK
+ * @param[in] xe Sub-tree (under xorig) at child of rpc: .
+ * @param[in] ce Client (session) entry
+ * @param[out] cbret Return XML, error or OK as cbuf
*
* @retval -1 Error
* @retval 0 OK, not found handler.
diff --git a/apps/cli/cli_handle.c b/apps/cli/cli_handle.c
index 62c96c11..bc63a179 100644
--- a/apps/cli/cli_handle.c
+++ b/apps/cli/cli_handle.c
@@ -69,17 +69,17 @@
#define handle(h) (assert(clicon_handle_check(h)==0),(struct cli_handle *)(h))
#define cligen(h) (handle(h)->cl_cligen)
-/*
- * 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]
+/*! CLI 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 backend_handle
*/
struct cli_handle {
int cl_magic; /* magic (HDR)*/
clicon_hash_t *cl_copt; /* clicon option list (HDR) */
clicon_hash_t *cl_data; /* internal clicon data (HDR) */
+ void *cl_xmldb; /* XMLDB storage handle, uie xmldb_handle */
/* ------ end of common handle ------ */
cligen_handle cl_cligen; /* cligen handle */
diff --git a/apps/dbctrl/Makefile.in b/apps/dbctrl/Makefile.in
deleted file mode 100644
index 2041e7d7..00000000
--- a/apps/dbctrl/Makefile.in
+++ /dev/null
@@ -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
-
diff --git a/apps/dbctrl/dbctrl_main.c b/apps/dbctrl/dbctrl_main.c
deleted file mode 100644
index 4910b958..00000000
--- a/apps/dbctrl/dbctrl_main.c
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* cligen */
-#include
-
-/* clicon */
-#include
-
-/* 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 \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 \" \" Add database entry\n"
- "\t-r \tRemove database entry\n"
- "\t-m \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 ): %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, "%s", 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;
-}
diff --git a/configure b/configure
index f0d9de95..4f7dfcbd 100755
--- a/configure
+++ b/configure
@@ -1333,7 +1333,7 @@ Optional Packages:
--with-cligen=dir Use CLIGEN here
--without-restconf disable support for restconf
--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:
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
# 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/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/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" ;;
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
diff --git a/configure.ac b/configure.ac
index 9b12e28d..e5c562bf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -183,7 +183,6 @@ AC_OUTPUT(Makefile
apps/backend/Makefile
apps/netconf/Makefile
apps/restconf/Makefile
- apps/dbctrl/Makefile
include/Makefile
etc/Makefile
etc/clixonrc
diff --git a/datastore/Makefile.in b/datastore/Makefile.in
index 76e7a461..907321e3 100644
--- a/datastore/Makefile.in
+++ b/datastore/Makefile.in
@@ -31,8 +31,18 @@
# ***** END LICENSE BLOCK *****
#
VPATH = @srcdir@
+prefix = @prefix@
+datarootdir = @datarootdir@
srcdir = @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@
CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
@@ -40,7 +50,22 @@ LIBS = @LIBS@
with_restconf = @with_restconf@
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
ifeq ($(with_keyvalue),yes)
@@ -49,7 +74,23 @@ endif
.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:
for i in $(SUBDIRS); \
@@ -62,15 +103,19 @@ install-include:
for i in $(SUBDIRS); \
do (cd $$i ; $(MAKE) $(MFLAGS) $@); done;
-install:
+install: $(APPL)
+ install -d $(DESTDIR)$(bindir)
+ install $(APPL) $(DESTDIR)$(bindir)
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
uninstall:
+ rm -f $(bindir)/$(APPL)
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
clean:
+ rm -f *.core $(APPL) $(APPOBJ)
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
diff --git a/datastore/datastore_client.c b/datastore/datastore_client.c
new file mode 100644
index 00000000..c43303f7
--- /dev/null
+++ b/datastore/datastore_client.c
@@ -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
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* cligen */
+#include
+
+/* clicon */
+#include
+
+/* 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 * []\n"
+ "where options are\n"
+ "\t-h\t\tHelp\n"
+ "\t-D\t\tDebug\n"
+ "\t-d \t\tDatabase name. Default: running. Alt: candidate,startup\n"
+ "\t-b \tDatabase directory. Mandatory\n"
+ "\t-p \tDatastore plugin. Mandatory\n"
+ "\t-y \tYang directory (where modules are stored). Mandatory\n"
+ "\t-m \tYang module. Mandatory\n"
+ "and command is either:\n"
+ "\tget \n"
+ "\tput (set|merge|delete) \tXML on stdin\n"
+ "\tcopy \n"
+ "\tlock \n"
+ "\tunlock \n"
+ "\tunlock_all \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, "") < 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;
+}
+
diff --git a/datastore/keyvalue/Makefile.in b/datastore/keyvalue/Makefile.in
index 7db2ef56..9a116997 100644
--- a/datastore/keyvalue/Makefile.in
+++ b/datastore/keyvalue/Makefile.in
@@ -30,6 +30,7 @@
#
# ***** END LICENSE BLOCK *****
#
+VPATH = @srcdir@
prefix = @prefix@
datarootdir = @datarootdir@
srcdir = @srcdir@
diff --git a/datastore/keyvalue/clixon_keyvalue.c b/datastore/keyvalue/clixon_keyvalue.c
index 68e67a51..ab8565b3 100644
--- a/datastore/keyvalue/clixon_keyvalue.c
+++ b/datastore/keyvalue/clixon_keyvalue.c
@@ -81,6 +81,10 @@
* - restconf
* - expand_dbvar
* - show_conf_xpath
+ *
+ * dependency on clixon handle:
+ * clixon_xmldb_dir()
+ * clicon_dbspec_yang(h)
*/
#ifdef HAVE_CONFIG_H
@@ -111,6 +115,31 @@
#include "clixon_qdb.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
* Store an integer for running and candidate containing
* the session-id of the client holding the lock.
@@ -120,7 +149,7 @@ static int _candidate_locked = 0;
static int _startup_locked = 0;
/*! 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[out] filename Filename. Unallocate after use with free()
* @retval 0 OK
@@ -131,9 +160,9 @@ static int _startup_locked = 0;
* The filename reside in CLICON_XMLDB_DIR option
*/
static int
-db2file(clicon_handle h,
- char *db,
- char **filename)
+db2file(struct kv_handle *kh,
+ char *db,
+ char **filename)
{
int retval = -1;
cbuf *cb;
@@ -143,8 +172,8 @@ db2file(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- if ((dir = clicon_xmldb_dir(h)) == NULL){
- clicon_err(OE_XML, errno, "CLICON_XMLDB_DIR not set");
+ if ((dir = kh->kh_dbdir) == NULL){
+ clicon_err(OE_XML, errno, "dbdir not set");
goto done;
}
if (strcmp(db, "running") != 0 &&
@@ -244,49 +273,6 @@ create_keyvalues(cxobj *x,
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
@@ -476,135 +462,116 @@ get(char *dbname,
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
-xml_sanity(cxobj *x,
- void *arg)
+xmldb_handle
+kv_connect(void)
{
- int retval = -1;
- yang_stmt *ys;
+ struct kv_handle *kh;
+ xmldb_handle xh = NULL;
+ int size;
- ys = (yang_stmt*)xml_spec(x);
- if (ys==NULL){
- clicon_err(OE_XML, 0, "No spec for xml node %s", xml_name(x));
+ size = sizeof(struct kv_handle);
+ if ((kh = malloc(size)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
- if (strstr(ys->ys_argument, xml_name(x))==NULL){
- clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
- xml_name(x), ys->ys_argument);
- goto done;
- }
- retval = 0;
- done:
- return retval;
+ memset(kh, 0, size);
+ kh->kh_magic = KV_HANDLE_MAGIC;
+ xh = (xmldb_handle)kh;
+ done:
+ return xh;
}
-/*! Add default values (if not set)
- */
-static int
-xml_default(cxobj *x,
- void *arg)
+/*! Disconnect from a datastore plugin and deallocate handle
+ * @param[in] handle Disconect and deallocate from this handle
+ * @retval 0 OK
+ */
+int
+kv_disconnect(xmldb_handle xh)
{
- int retval = -1;
- yang_stmt *ys;
- yang_stmt *y;
- int i;
- cxobj *xc;
- cxobj *xb;
- char *str;
+ int retval = -1;
+ struct kv_handle *kh = handle(xh);
- ys = (yang_stmt*)xml_spec(x);
- /* Check leaf defaults */
- if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){
- for (i=0; iys_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(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; iys_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 (;j0kh_dbdir)
+ free(kh->kh_dbdir);
+ free(kh);
}
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
+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
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @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] yspec Yang specification
* @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.
@@ -614,8 +581,7 @@ xml_order(cxobj *x,
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
- * yang_spec *yspec = clicon_dbspec_yang(h);
- * if (xmldb_get("running", "/interfaces/interface[name="eth"]",
+ * if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; ikh_yangspec) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
/* 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;
if ((xt = xml_new_spec("clicon", NULL, yspec)) == NULL)
goto done;
/* Translate to complete xml tree */
for (i = 0; i < npairs; i++) {
- if (get(dbname,
+ if (get(dbfile,
yspec,
pairs[i].dp_key, /* xml key */
pairs[i].dp_val, /* may be NULL */
@@ -693,7 +661,6 @@ kv_get(clicon_handle h,
*xlen0 = xlen;
xlen = 0;
}
-
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
@@ -706,8 +673,8 @@ kv_get(clicon_handle h,
*xtop = xt;
retval = 0;
done:
- if (dbname)
- free(dbname);
+ if (dbfile)
+ free(dbfile);
if (xvec)
free(xvec);
unchunk_group(__FUNCTION__);
@@ -716,7 +683,7 @@ kv_get(clicon_handle h,
}
/*! 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] ys Yang statement corresponding to xml-node
* @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
*/
static int
-put(char *dbname,
+put(char *dbfile,
cxobj *xt,
yang_stmt *ys,
enum operation_type op,
@@ -774,7 +741,7 @@ put(char *dbname,
/* Write to database, key and a vector of variables */
switch (op){
case OP_CREATE:
- if ((exists = db_exists(dbname, xk)) < 0)
+ if ((exists = db_exists(dbfile, xk)) < 0)
goto done;
if (exists == 1){
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_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;
break;
case OP_DELETE:
- if ((exists = db_exists(dbname, xk)) < 0)
+ if ((exists = db_exists(dbfile, xk)) < 0)
goto done;
if (exists == 0){
clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk);
goto done;
}
case OP_REMOVE:
- if (db_del(dbname, xk) < 0)
+ if (db_del(dbfile, xk) < 0)
goto done;
break;
case OP_NONE:
@@ -805,7 +772,7 @@ put(char *dbname,
clicon_err(OE_UNIX, 0, "No yang node found: %s", xml_name(x));
goto done;
}
- if (put(dbname, x, y, op, xk) < 0)
+ if (put(dbfile, x, y, op, xk) < 0)
goto done;
}
retval = 0;
@@ -818,7 +785,7 @@ put(char *dbname,
}
/*! 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] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @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
*/
static int
-xmldb_put_xkey(clicon_handle h,
+xmldb_put_xkey(struct kv_handle *kh,
char *db,
enum operation_type op,
char *xk,
@@ -865,8 +832,11 @@ xmldb_put_xkey(clicon_handle h,
yang_spec *yspec;
char *filename = NULL;
- yspec = clicon_dbspec_yang(h);
- if (db2file(h, db, &filename) < 0)
+ if ((yspec = kh->kh_yangspec) == NULL){
+ clicon_err(OE_YANG, ENOENT, "No yang spec");
+ goto done;
+ }
+ if (db2file(kh, db, &filename) < 0)
goto done;
if (xk == NULL || *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
*
- * @param[in] h CLICON handle
+ * @param[in] kh Keyvalue handle
* @param[in] db running or candidate
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
@@ -1063,7 +1033,7 @@ xmldb_put_xkey(clicon_handle h,
* @see xmldb_put
*/
static int
-xmldb_put_restconf_api_path(clicon_handle h,
+xmldb_put_restconf_api_path(struct kv_handle *kh,
char *db,
enum operation_type op,
char *api_path,
@@ -1092,8 +1062,11 @@ xmldb_put_restconf_api_path(clicon_handle h,
char *key;
char *keys;
- yspec = clicon_dbspec_yang(h);
- if (db2file(h, db, &filename) < 0)
+ if ((yspec = kh->kh_yangspec) == NULL){
+ clicon_err(OE_YANG, ENOENT, "No yang spec");
+ goto done;
+ }
+ if (db2file(kh, db, &filename) < 0)
goto done;
if (api_path == NULL || *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
*
- * @param[in] h CLICON handle
+ * @param[in] xh XMLDB handle
* @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy
* @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
*/
int
-kv_put(clicon_handle h,
+kv_put(xmldb_handle xh,
char *db,
enum operation_type op,
char *api_path,
cxobj *xt)
{
int retval = -1;
+ struct kv_handle *kh = handle(xh);
cxobj *x = NULL;
yang_stmt *ys;
yang_spec *yspec;
char *dbfilename = NULL;
if (xml_child_nr(xt)==0 || xml_body(xt)!= NULL)
- return xmldb_put_xkey(h, db, op, api_path, xml_body(xt));
- yspec = clicon_dbspec_yang(h);
- if (db2file(h, db, &dbfilename) < 0)
+ return xmldb_put_xkey(kh, db, op, api_path, xml_body(xt));
+ if ((yspec = kh->kh_yangspec) == NULL){
+ clicon_err(OE_YANG, ENOENT, "No yang spec");
+ goto done;
+ }
+ if (db2file(kh, db, &dbfilename) < 0)
goto done;
if (op == OP_REPLACE){
if (db_delete(dbfilename) < 0)
@@ -1305,7 +1282,7 @@ kv_put(clicon_handle h,
}
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
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;
continue;
}
@@ -1328,58 +1305,27 @@ kv_put(clicon_handle h,
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
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] from Source database copy
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
-kv_copy(clicon_handle h,
+kv_copy(xmldb_handle xh,
char *from,
char *to)
{
int retval = -1;
+ struct kv_handle *kh = handle(xh);
char *fromfile = NULL;
char *tofile = NULL;
/* XXX lock */
- if (db2file(h, from, &fromfile) < 0)
+ if (db2file(kh, from, &fromfile) < 0)
goto done;
- if (db2file(h, to, &tofile) < 0)
+ if (db2file(kh, to, &tofile) < 0)
goto done;
if (clicon_file_copy(fromfile, tofile) < 0)
goto done;
@@ -1393,17 +1339,19 @@ kv_copy(clicon_handle h,
}
/*! Lock database
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
-kv_lock(clicon_handle h,
+kv_lock(xmldb_handle xh,
char *db,
int pid)
{
+ // struct kv_handle *kh = handle(xh);
+
if (strcmp("running", db) == 0)
_running_locked = pid;
else if (strcmp("candidate", db) == 0)
@@ -1415,7 +1363,7 @@ kv_lock(clicon_handle h,
}
/*! Unlock database
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
@@ -1423,10 +1371,12 @@ kv_lock(clicon_handle h,
* Assume all sanity checks have been made
*/
int
-kv_unlock(clicon_handle h,
+kv_unlock(xmldb_handle xh,
char *db,
int pid)
{
+ // struct kv_handle *kh = handle(xh);
+
if (strcmp("running", db) == 0)
_running_locked = 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)
- * @param[in] h Clicon handle
- * @param[in] pid Process / Session id
- * @retval -1 Error
- * @retval 0 Ok
+ * @param[in] xh XMLDB handle
+ * @param[in] pid Process / Session id
+ * @retval -1 Error
+ * @retval 0 Ok
*/
int
-kv_unlock_all(clicon_handle h,
+kv_unlock_all(xmldb_handle xh,
int pid)
{
+ // struct kv_handle *kh = handle(xh);
+
if (_running_locked == pid)
_running_locked = 0;
if (_candidate_locked == pid)
@@ -1456,16 +1408,18 @@ kv_unlock_all(clicon_handle h,
}
/*! Check if database is locked
- * @param[in] h Clicon handle
- * @param[in] db Database
- * @retval -1 Error
- * @retval 0 Not locked
- * @retval >0 Id of locker
+ * @param[in] xh XMLDB handle
+ * @param[in] db Database
+ * @retval -1 Error
+ * @retval 0 Not locked
+ * @retval >0 Id of locker
*/
int
-kv_islocked(clicon_handle h,
+kv_islocked(xmldb_handle xh,
char *db)
{
+ // struct kv_handle *kh = handle(xh);
+
if (strcmp("running", db) == 0)
return (_running_locked);
else if (strcmp("candidate", db) == 0)
@@ -1476,21 +1430,22 @@ kv_islocked(clicon_handle h,
}
/*! Check if db exists
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
-kv_exists(clicon_handle h,
+kv_exists(xmldb_handle xh,
char *db)
{
int retval = -1;
+ struct kv_handle *kh = handle(xh);
char *filename = NULL;
struct stat sb;
- if (db2file(h, db, &filename) < 0)
+ if (db2file(kh, db, &filename) < 0)
goto done;
if (lstat(filename, &sb) < 0)
retval = 0;
@@ -1503,19 +1458,20 @@ kv_exists(clicon_handle h,
}
/*! Delete database. Remove file
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 OK
*/
int
-kv_delete(clicon_handle h,
+kv_delete(xmldb_handle xh,
char *db)
{
int retval = -1;
+ struct kv_handle *kh = handle(xh);
char *filename = NULL;
- if (db2file(h, db, &filename) < 0)
+ if (db2file(kh, db, &filename) < 0)
goto done;
if (db_delete(filename) < 0)
goto done;
@@ -1527,19 +1483,20 @@ kv_delete(clicon_handle h,
}
/*! Initialize database
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval 0 OK
* @retval -1 Error
*/
int
-kv_init(clicon_handle h,
+kv_init(xmldb_handle xh,
char *db)
{
int retval = -1;
+ struct kv_handle *kh = handle(xh);
char *filename = NULL;
- if (db2file(h, db, &filename) < 0)
+ if (db2file(kh, db, &filename) < 0)
goto done;
if (db_init(filename) < 0)
goto done;
@@ -1578,9 +1535,12 @@ static const struct xmldb_api api = {
XMLDB_API_MAGIC,
clixon_xmldb_plugin_init,
kv_plugin_exit,
+ kv_connect,
+ kv_disconnect,
+ kv_getopt,
+ kv_setopt,
kv_get,
kv_put,
- kv_dump,
kv_copy,
kv_lock,
kv_unlock,
@@ -1598,80 +1558,46 @@ static const struct xmldb_api api = {
* Usage: clicon_xpath []
* read xml from input
* 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
-usage(char *argv0)
-{
- fprintf(stderr, "usage:\n%s\tget []\t\txml on stdin\n", argv0);
- fprintf(stderr, "\tput set|merge|delete\txml to stdout\n");
- exit(0);
-}
-
+/*! 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
-main(int argc, char **argv)
+main(int argc,
+ char **argv)
{
- cxobj *xt;
- cxobj *xn;
- char *xpath;
- enum operation_type op;
- char *cmd;
- char *db;
- char *yangdir;
- char *yangmod;
- yang_spec *yspec = NULL;
- clicon_handle h;
+ int retval = -1;
+ int npairs;
+ struct db_pair *pairs;
+ char *rxkey = NULL;
+ char *dbfilename;
- if ((h = clicon_handle_init()) == NULL)
- goto done;
- clicon_log_init("xmldb", LOG_DEBUG, CLICON_LOG_STDERR);
- if (argc < 4){
- usage(argv[0]);
+ if (argc != 2 && argc != 3){
+ fprintf(stderr, "usage: %s [rxkey]\n", argv[0]);
goto done;
}
- cmd = argv[1];
- db = argv[2];
- yangdir = argv[3];
- 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);
- }
+ dbfilename = argv[1];
+ if (argc == 3)
+ rxkey = argv[2];
else
- if (strcmp(cmd, "put")==0){
- if (argc != 6)
- usage(argv[0]);
- if (clicon_xml_parse_file(0, &xt, "") < 0)
- goto done;
- if (xml_rootchild(xt, 0, &xn) < 0)
- goto done;
- if (strcmp(argv[5], "set") == 0)
- op = OP_REPLACE;
- else
- 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");
+ rxkey = "^.*$"; /* Default is match all */
+
+ /* Get all keys/values for vector */
+ if ((npairs = db_regexp(dbfilename, rxkey, __FUNCTION__, &pairs, 0)) < 0)
+ goto done;
+
+ for (npairs--; npairs >= 0; npairs--)
+ fprintf(stdout, "%s %s\n", pairs[npairs].dp_key,
+ pairs[npairs].dp_val?pairs[npairs].dp_val:"");
+ retval = 0;
done:
- return 0;
+ unchunk_group(__FUNCTION__);
+ return retval;
}
#endif /* Test program */
diff --git a/datastore/keyvalue/clixon_keyvalue.h b/datastore/keyvalue/clixon_keyvalue.h
index ed5cea51..85e6d8ab 100644
--- a/datastore/keyvalue/clixon_keyvalue.h
+++ b/datastore/keyvalue/clixon_keyvalue.h
@@ -39,18 +39,18 @@
/*
* 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);
-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);
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
-int kv_copy(clicon_handle h, char *from, char *to);
-int kv_lock(clicon_handle h, char *db, int pid);
-int kv_unlock(clicon_handle h, char *db, int pid);
-int kv_unlock_all(clicon_handle h, int pid);
-int kv_islocked(clicon_handle h, char *db);
-int kv_exists(clicon_handle h, char *db);
-int kv_delete(clicon_handle h, char *db);
-int kv_init(clicon_handle h, char *db);
+int kv_copy(xmldb_handle h, char *from, char *to);
+int kv_lock(xmldb_handle h, char *db, int pid);
+int kv_unlock(xmldb_handle h, char *db, int pid);
+int kv_unlock_all(xmldb_handle h, int pid);
+int kv_islocked(xmldb_handle h, char *db);
+int kv_exists(xmldb_handle h, char *db);
+int kv_delete(xmldb_handle h, char *db);
+int kv_init(xmldb_handle h, char *db);
#endif /* _CLIXON_KEYVALUE_H */
diff --git a/datastore/text/clixon_xmldb_text.c b/datastore/text/clixon_xmldb_text.c
index 2b4926eb..f9c67fd7 100644
--- a/datastore/text/clixon_xmldb_text.c
+++ b/datastore/text/clixon_xmldb_text.c
@@ -44,11 +44,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
-#include
+#include
+#include
/* cligen */
#include
@@ -58,16 +60,42 @@
#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
* Store an integer for running and candidate containing
* the session-id of the client holding the lock.
+ * @note This should probably be on file-system
*/
static int _running_locked = 0;
static int _candidate_locked = 0;
static int _startup_locked = 0;
/*! 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[out] filename Filename. Unallocate after use with free()
* @retval 0 OK
@@ -78,9 +106,9 @@ static int _startup_locked = 0;
* The filename reside in CLICON_XMLDB_DIR option
*/
static int
-db2file(clicon_handle h,
- char *db,
- char **filename)
+db2file(struct text_handle *th,
+ char *db,
+ char **filename)
{
int retval = -1;
cbuf *cb;
@@ -90,8 +118,8 @@ db2file(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- if ((dir = clicon_xmldb_dir(h)) == NULL){
- clicon_err(OE_XML, errno, "CLICON_XMLDB_DIR not set");
+ if ((dir = th->th_dbdir) == NULL){
+ clicon_err(OE_XML, errno, "dbdir not set");
goto done;
}
if (strcmp(db, "running") != 0 &&
@@ -113,12 +141,116 @@ db2file(clicon_handle h,
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
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
+ * @param[in] xh XMLDB 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[in] yspec Yang specification
* @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.
@@ -128,8 +260,7 @@ db2file(clicon_handle h,
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
- * yang_spec *yspec = clicon_dbspec_yang(h);
- * if (xmldb_get("running", "/interfaces/interface[name="eth"]",
+ * if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; ith_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, "")) < 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; i1)
+ clicon_xml2file(stderr, xt, 0, 1);
+ *xtop = xt;
retval = 0;
- // done:
+ done:
+ // xml_free(xt);
+ if (fd != -1)
+ close(fd);
return retval;
}
-
/*! 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] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it.
@@ -182,30 +373,14 @@ text_get(clicon_handle h,
* @see xmldb_put_xkey for single key
*/
int
-text_put(clicon_handle h,
+text_put(xmldb_handle xh,
char *db,
enum operation_type op,
char *api_path,
cxobj *xt)
{
- int retval = -1;
- retval = 0;
- // 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;
+ int retval = -1;
+ // struct text_handle *th = handle(xh);
retval = 0;
// done:
@@ -213,35 +388,39 @@ text_dump(FILE *f,
}
/*! Copy database from db1 to db2
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] from Source database copy
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
-text_copy(clicon_handle h,
- char *from,
- char *to)
+text_copy(xmldb_handle xh,
+ char *from,
+ char *to)
{
- int retval = -1;
+ int retval = -1;
+ // struct text_handle *th = handle(xh);
+
retval = 0;
// done:
return retval;
}
/*! Lock database
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
-text_lock(clicon_handle h,
- char *db,
- int pid)
+text_lock(xmldb_handle xh,
+ char *db,
+ int pid)
{
+ // struct text_handle *th = handle(xh);
+
if (strcmp("running", db) == 0)
_running_locked = pid;
else if (strcmp("candidate", db) == 0)
@@ -253,7 +432,7 @@ text_lock(clicon_handle h,
}
/*! Unlock database
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
@@ -261,10 +440,12 @@ text_lock(clicon_handle h,
* Assume all sanity checks have been made
*/
int
-text_unlock(clicon_handle h,
- char *db,
- int pid)
+text_unlock(xmldb_handle xh,
+ char *db,
+ int pid)
{
+ // struct text_handle *th = handle(xh);
+
if (strcmp("running", db) == 0)
_running_locked = 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)
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] pid Process / Session id
* @retval -1 Error
* @retval 0 Ok
*/
int
-text_unlock_all(clicon_handle h,
- int pid)
+text_unlock_all(xmldb_handle xh,
+ int pid)
{
+ // struct text_handle *th = handle(xh);
+
if (_running_locked == pid)
_running_locked = 0;
if (_candidate_locked == pid)
@@ -294,16 +477,18 @@ text_unlock_all(clicon_handle h,
}
/*! Check if database is locked
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 Not locked
* @retval >0 Id of locker
*/
int
-text_islocked(clicon_handle h,
- char *db)
+text_islocked(xmldb_handle xh,
+ char *db)
{
+ // struct text_handle *th = handle(xh);
+
if (strcmp("running", db) == 0)
return (_running_locked);
else if (strcmp("candidate", db) == 0)
@@ -314,21 +499,23 @@ text_islocked(clicon_handle h,
}
/*! Check if db exists
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
-text_exists(clicon_handle h,
- char *db)
+text_exists(xmldb_handle xh,
+ 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;
if (lstat(filename, &sb) < 0)
retval = 0;
@@ -341,16 +528,17 @@ text_exists(clicon_handle h,
}
/*! Delete database. Remove file
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 OK
*/
int
-text_delete(clicon_handle h,
- char *db)
+text_delete(xmldb_handle xh,
+ char *db)
{
- int retval = -1;
+ int retval = -1;
+// struct text_handle *th = handle(xh);
retval = 0;
// done:
@@ -358,16 +546,17 @@ text_delete(clicon_handle h,
}
/*! Initialize database
- * @param[in] h Clicon handle
+ * @param[in] xh XMLDB handle
* @param[in] db Database
* @retval 0 OK
* @retval -1 Error
*/
int
-text_init(clicon_handle h,
+text_init(xmldb_handle xh,
char *db)
{
int retval = -1;
+ // struct text_handle *th = handle(xh);
retval = 0;
// done:
@@ -402,9 +591,12 @@ static const struct xmldb_api api = {
XMLDB_API_MAGIC,
clixon_xmldb_plugin_init,
text_plugin_exit,
+ text_connect,
+ text_disconnect,
+ text_getopt,
+ text_setopt,
text_get,
text_put,
- text_dump,
text_copy,
text_lock,
text_unlock,
diff --git a/datastore/text/clixon_xmldb_text.h b/datastore/text/clixon_xmldb_text.h
index b2ca183d..83f5db43 100644
--- a/datastore/text/clixon_xmldb_text.h
+++ b/datastore/text/clixon_xmldb_text.h
@@ -39,18 +39,18 @@
/*
* 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);
-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);
int text_dump(FILE *f, char *dbfilename, char *rxkey);
-int text_copy(clicon_handle h, char *from, char *to);
-int text_lock(clicon_handle h, char *db, int pid);
-int text_unlock(clicon_handle h, char *db, int pid);
-int text_unlock_all(clicon_handle h, int pid);
-int text_islocked(clicon_handle h, char *db);
-int text_exists(clicon_handle h, char *db);
-int text_delete(clicon_handle h, char *db);
-int text_init(clicon_handle h, char *db);
+int text_copy(xmldb_handle h, char *from, char *to);
+int text_lock(xmldb_handle h, char *db, int pid);
+int text_unlock(xmldb_handle h, char *db, int pid);
+int text_unlock_all(xmldb_handle h, int pid);
+int text_islocked(xmldb_handle h, char *db);
+int text_exists(xmldb_handle h, char *db);
+int text_delete(xmldb_handle h, char *db);
+int text_init(xmldb_handle h, char *db);
#endif /* _CLIXON_XMLDB_TEXT_H */
diff --git a/lib/clixon/clixon_handle.h b/lib/clixon/clixon_handle.h
index 207d076f..c4f601b6 100644
--- a/lib/clixon/clixon_handle.h
+++ b/lib/clixon/clixon_handle.h
@@ -71,4 +71,10 @@ clicon_hash_t *clicon_options(clicon_handle h);
/* Return internal clicon data (hash-array) given a handle.*/
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_ */
diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h
index 04325360..3cad3679 100644
--- a/lib/clixon/clixon_xml_db.h
+++ b/lib/clixon/clixon_xml_db.h
@@ -34,6 +34,19 @@
#ifndef _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. */
#define XMLDB_API_VERSION 1
@@ -49,40 +62,49 @@ typedef void * (plugin_init_t)(int version);
/* Type of plugin exit function */
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 */
-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);
/* Type of xmldb put function */
-typedef int (xmldb_put_t)(clicon_handle h, char *db, enum operation_type op,
- char *api_path, cxobj *xt);
-
-/* Type of xmldb dump function */
-typedef int (xmldb_dump_t)(FILE *f, char *dbfilename, char *rxkey);
+typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op,
+ char *api_path, cxobj *xt);
/* 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 */
-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 */
-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 */
-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 */
-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 */
-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 */
-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 */
-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 */
struct xmldb_api{
@@ -90,9 +112,12 @@ struct xmldb_api{
int xa_magic;
plugin_init_t *xa_plugin_init_fn; /* XMLDB_PLUGIN_INIT_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_put_t *xa_put_fn;
- xmldb_dump_t *xa_dump_fn;
xmldb_copy_t *xa_copy_fn;
xmldb_lock_t *xa_lock_fn;
xmldb_unlock_t *xa_unlock_fn;
@@ -105,14 +130,19 @@ struct xmldb_api{
/*
* 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,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
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_lock(clicon_handle h, char *db, int pid);
int xmldb_unlock(clicon_handle h, char *db, int pid);
diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h
index 7cbc4680..dcc5f0ee 100644
--- a/lib/clixon/clixon_xml_map.h
+++ b/lib/clixon/clixon_xml_map.h
@@ -64,5 +64,9 @@ int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
int xmlkeyfmt2key(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_ */
diff --git a/lib/src/clixon_handle.c b/lib/src/clixon_handle.c
index ade4350d..47accd83 100644
--- a/lib/src/clixon_handle.c
+++ b/lib/src/clixon_handle.c
@@ -58,18 +58,17 @@
#define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h))
-/*
- * clicon_handle
- * Internal structire of basic handle. Also header of all other handles.
- * see struct clicon_cli_handle, struct clicon_backend_handle, etc
+/*! Internal structure of basic handle. Also header of all other handles.
+ * @note If you change here, you must also change the structs below:
+ * @see struct cli_handle, struct backend_handle
*/
struct clicon_handle {
int ch_magic; /* magic (HDR) */
clicon_hash_t *ch_copt; /* clicon option list (HDR) */
clicon_hash_t *ch_data; /* internal clicon data (HDR) */
+ void *ch_xmldb; /* XMLDB storage handle, uie xmldb_handle */
};
-
/*! Internal call to allocate a CLICON handle.
*
* There may be different variants of handles with some common options.
@@ -166,3 +165,31 @@ clicon_data(clicon_handle h)
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;
+}
diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c
index bfcbcd61..22a575a3 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -1008,6 +1008,7 @@ FSM(char *tag,
* Note, xt will add a top-level symbol called "top" meaning that will look as:
*
* XXX: There is a potential leak here on some return values.
+ * XXX: What happens if endtag is different?
* May block
*/
int
@@ -1023,15 +1024,16 @@ clicon_xml_parse_file(int fd,
int maxbuf = BUFLEN;
int endtaglen = strlen(endtag);
int state = 0;
+ int oldmaxbuf;
if (endtag == NULL){
clicon_err(OE_XML, 0, "%s: endtag required\n", __FUNCTION__);
- return -1;
+ goto done;
}
*cx = NULL;
if ((xmlbuf = malloc(maxbuf)) == NULL){
clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
- return -1;
+ goto done;
}
memset(xmlbuf, 0, maxbuf);
ptr = xmlbuf;
@@ -1050,24 +1052,33 @@ clicon_xml_parse_file(int fd,
state = 0;
if ((*cx = xml_new("top", NULL)) == NULL)
break;
- if (xml_parse(ptr, *cx) < 0)
+ if (xml_parse(ptr, *cx) < 0){
+ goto done;
return -1;
+ }
break;
}
if (len>=maxbuf-1){ /* Space: one for the null character */
- int oldmaxbuf = maxbuf;
-
+ oldmaxbuf = maxbuf;
maxbuf *= 2;
if ((xmlbuf = realloc(xmlbuf, maxbuf)) == NULL){
clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
- return -1;
+ goto done;
}
memset(xmlbuf+oldmaxbuf, 0, maxbuf-oldmaxbuf);
ptr = xmlbuf;
}
} /* while */
- free(xmlbuf);
- return (*cx)?0:-1;
+ retval = 0;
+ 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.
@@ -1461,8 +1472,12 @@ xml_body_uint32(cxobj *xb,
}
/*! Map xml operation from string to enumeration
- * @param[in] xn XML node
- * @param[out] op "operation" attribute may change operation
+ * @param[in] opstr String, eg "merge"
+ * @param[out] op Enumeration, eg OP_MERGE
+ * @code
+ * enum operation_type op;
+ * xml_operation("replace", &op)
+ * @endcode
*/
int
xml_operation(char *opstr,
@@ -1487,6 +1502,14 @@ xml_operation(char *opstr,
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 *
xml_operation2str(enum operation_type op)
{
@@ -1510,3 +1533,4 @@ xml_operation2str(enum operation_type op)
return "none";
}
}
+
diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c
index 62d724f3..97ef6dd3 100644
--- a/lib/src/clixon_xml_db.c
+++ b/lib/src/clixon_xml_db.c
@@ -58,6 +58,8 @@
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_xml.h"
+#include "clixon_yang.h"
+#include "clixon_options.h"
#include "clixon_xml_db.h"
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
* If init function fails (not found, wrong version, etc) print a log and dont
* add it.
- * @param[in] name Name of plugin
+ * @param[in] name Filename (complete path) of plugin
* @param[in] filename Actual filename with path
* @param[out] plugin Plugin data structure for invoking. Dealloc with free
*/
int
-xmldb_plugin_load(char *filename)
+xmldb_plugin_load(clicon_handle h,
+ char *filename)
{
int retval = -1;
char *dlerrcode;
@@ -117,11 +120,179 @@ xmldb_plugin_load(char *filename)
goto done;
}
-int
-xmldb_get(clicon_handle h, char *db, char *xpath,
- cxobj **xtop, cxobj ***xvec, size_t *xlen)
+/*! XXX: fixme */
+int
+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; ixa_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:
return retval;
}
@@ -152,16 +327,20 @@ xmldb_get(clicon_handle h, char *db, char *xpath,
* cxobj *xt;
* if (clicon_xml_parse_str("17", &xt) < 0)
* err;
- * if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0)
+ * if (xmldb_put(xh, "running", OP_MERGE, NULL, xt) < 0)
* err;
* @endcode
* @see xmldb_put_xkey for single key
*/
int
-xmldb_put(clicon_handle h, char *db, enum operation_type op,
- char *api_path, cxobj *xt)
+xmldb_put(clicon_handle h,
+ char *db,
+ enum operation_type op,
+ char *api_path,
+ cxobj *xt)
{
- int retval = -1;
+ int retval = -1;
+ xmldb_handle xh;
if (_xa_api == NULL){
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");
goto done;
}
- retval = _xa_api->xa_put_fn(h, db, op, api_path, xt);
- done:
- 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");
+ if ((xh = clicon_handle_xmldb_get(h)) == NULL){
+ clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
- if (_xa_api->xa_dump_fn == NULL){
- clicon_err(OE_DB, 0, "No xmldb function");
- goto done;
- }
- retval = _xa_api->xa_dump_fn(f, dbfilename, pattern);
+ retval = _xa_api->xa_put_fn(xh, db, op, api_path, xt);
done:
return retval;
}
@@ -209,9 +367,12 @@ xmldb_dump(FILE *f,
* @retval 0 OK
*/
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){
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");
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:
return retval;
}
@@ -234,9 +399,12 @@ xmldb_copy(clicon_handle h, char *from, char *to)
* @retval 0 OK
*/
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){
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");
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:
return retval;
}
@@ -260,9 +432,12 @@ xmldb_lock(clicon_handle h, char *db, int pid)
* Assume all sanity checks have been made
*/
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){
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");
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:
return retval;
}
@@ -284,9 +463,11 @@ xmldb_unlock(clicon_handle h, char *db, int pid)
* @retval 0 OK
*/
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){
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");
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:
return retval;
}
@@ -309,9 +494,11 @@ xmldb_unlock_all(clicon_handle h, int pid)
* @retval >0 Id of locker
*/
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){
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");
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:
return retval;
}
@@ -334,9 +525,11 @@ xmldb_islocked(clicon_handle h, char *db)
* @retval 1 Yes it exists
*/
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){
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");
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:
return retval;
}
@@ -358,9 +555,11 @@ xmldb_exists(clicon_handle h, char *db)
* @retval 0 OK
*/
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){
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");
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:
return retval;
}
-/*! Initialize database
+/*! Initialize database. Open database for writing.
* @param[in] h Clicon handle
* @param[in] db Database
* @retval 0 OK
* @retval -1 Error
*/
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){
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");
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:
return retval;
}
diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c
index c08deb3a..f0609294 100644
--- a/lib/src/clixon_xml_map.c
+++ b/lib/src/clixon_xml_map.c
@@ -1074,3 +1074,176 @@ xmlkeyfmt2xpath(char *xkfmt,
cbuf_free(cb);
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; iys_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; iys_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 (; j0ys_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;
+}
+
diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c
index af1f33ea..b8e421e0 100644
--- a/lib/src/clixon_yang.c
+++ b/lib/src/clixon_yang.c
@@ -1540,11 +1540,11 @@ yang_parse1(clicon_handle h,
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
*
- * @param h CLICON handle
- * @param yang_dir Directory where all YANG module files reside
- * @param module Name of main YANG module. More modules may be parsed if imported
- * @param revision Optional module revision date
- * @param ysp Yang specification. Should ave been created by caller using yspec_new
+ * @param[in] h CLICON handle
+ * @param[in] yang_dir Directory where all YANG module files reside
+ * @param[in] module Name of main YANG module. More modules may be parsed if imported
+ * @param[in] revision Optional module revision date
+ * @param[out] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval 0 Everything OK
* @retval -1 Error encountered
* The database symbols are inserted in alphabetical order.