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.