* Structural change: removed datastore plugin and directory, and merged into regulat clixon lib code.

* Moved out code from clixon_options.[ch] into a new file: clixon_data.[ch]
This commit is contained in:
Olof hagsand 2019-03-31 18:17:40 +02:00
parent a269e26c0d
commit 98a5ebc76e
38 changed files with 1400 additions and 3640 deletions

View file

@ -35,6 +35,11 @@
### API changes on existing features (you may need to change your code) ### API changes on existing features (you may need to change your code)
* Structural change: removed datastore plugin and directory, and merged into regulat clixon lib code.
* The CLICON_XMLDB_PLUGIN config option is obsolete, you should remove it from your config file
* The datastore directory is removed, code is moved to lib/src/clixon_datastore*.c
* removed clixon_backend -x <plugin> command-line options
* Moved out code from clixon_options.[ch] into a new file: clixon_data.[ch] where non-option data resides.
* Directory change: Moved example to example/main to make room for other examples. * Directory change: Moved example to example/main to make room for other examples.
* Removed argc/argv parameters from ca_start plugin API function: * Removed argc/argv parameters from ca_start plugin API function:
* You may need to change signatures of your startup in your plugins, eg from: * You may need to change signatures of your startup in your plugins, eg from:

View file

@ -52,7 +52,7 @@ INSTALL = @INSTALL@
INCLUDES = -I. -I@srcdir@ @INCLUDES@ INCLUDES = -I. -I@srcdir@ @INCLUDES@
SHELL = /bin/sh SHELL = /bin/sh
SUBDIRS = lib apps include etc datastore yang SUBDIRS = lib apps include etc yang
.PHONY: doc example all clean depend $(SUBDIRS) install loc TAGS .config.status docker test .PHONY: doc example all clean depend $(SUBDIRS) install loc TAGS .config.status docker test

View file

@ -111,7 +111,6 @@ ce_event_cb(clicon_handle h,
break; break;
} }
} }
clicon_debug(1, "%s retval:0", __FUNCTION__);
return 0; return 0;
} }
@ -245,7 +244,6 @@ client_statedata(clicon_handle h,
/* Code complex to filter out anything that is outside of xpath */ /* Code complex to filter out anything that is outside of xpath */
if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0) if (xpath_vec(*xret, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done; goto done;
/* If vectors are specified then mark the nodes found and /* If vectors are specified then mark the nodes found and
* then filter out everything else, * then filter out everything else,
* otherwise return complete tree. * otherwise return complete tree.
@ -263,6 +261,7 @@ client_statedata(clicon_handle h,
goto done; goto done;
retval = 0; /* OK */ retval = 0; /* OK */
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (xvec) if (xvec)
free(xvec); free(xvec);
return retval; return retval;
@ -812,6 +811,7 @@ from_client_get(clicon_handle h,
ok: ok:
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (xnacm) if (xnacm)
xml_free(xnacm); xml_free(xnacm);
if (xvec) if (xvec)

View file

@ -172,6 +172,7 @@ startup_common(clicon_handle h,
int ret; int ret;
modstate_diff_t *msd = NULL; modstate_diff_t *msd = NULL;
cxobj *xt = NULL; cxobj *xt = NULL;
cxobj *x;
/* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with /* If CLICON_XMLDB_MODSTATE is enabled, then get the db XML with
* potentially non-matching module-state in msd * potentially non-matching module-state in msd
@ -198,8 +199,11 @@ startup_common(clicon_handle h,
goto done; goto done;
/* Handcraft transition with with only add tree */ /* Handcraft transition with with only add tree */
td->td_target = xt; td->td_target = xt;
if (cxvec_append(td->td_target, &td->td_avec, &td->td_alen) < 0) x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if (cxvec_append(x, &td->td_avec, &td->td_alen) < 0)
goto done; goto done;
}
/* 4. Call plugin transaction start callbacks */ /* 4. Call plugin transaction start callbacks */
if (plugin_transaction_begin(h, td) < 0) if (plugin_transaction_begin(h, td) < 0)

View file

@ -74,7 +74,7 @@
#include "backend_startup.h" #include "backend_startup.h"
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define BACKEND_OPTS "hD:f:l:d:p:b:Fza:u:P:1s:c:g:y:x:o:" #define BACKEND_OPTS "hD:f:l:d:p:b:Fza:u:P:1s:c:g:y:o:"
#define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log" #define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log"
@ -96,8 +96,14 @@ backend_terminate(clicon_handle h)
clicon_debug(1, "%s", __FUNCTION__); clicon_debug(1, "%s", __FUNCTION__);
if ((ss = clicon_socket_get(h)) != -1) if ((ss = clicon_socket_get(h)) != -1)
close(ss); close(ss);
if ((x = clicon_module_state_get(h)) != NULL) /* Disconnect datastore */
xmldb_disconnect(h);
/* Clear module state caches */
if ((x = clicon_modst_cache_get(h, 0)) != NULL)
xml_free(x); xml_free(x);
if ((x = clicon_modst_cache_get(h, 1)) != NULL)
xml_free(x);
/* Free changelog */
if ((x = clicon_xml_changelog_get(h)) != NULL) if ((x = clicon_xml_changelog_get(h)) != NULL)
xml_free(x); xml_free(x);
if ((yspec = clicon_dbspec_yang(h)) != NULL) if ((yspec = clicon_dbspec_yang(h)) != NULL)
@ -119,8 +125,6 @@ backend_terminate(clicon_handle h)
unlink(pidfile); unlink(pidfile);
if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0) if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0)
unlink(sockpath); unlink(sockpath);
xmldb_plugin_unload(h); /* unload storage plugin */
backend_handle_exit(h); /* Also deletes streams. Cannot use h after this. */ backend_handle_exit(h); /* Also deletes streams. Cannot use h after this. */
event_exit(); event_exit();
clicon_debug(1, "%s done", __FUNCTION__); clicon_debug(1, "%s done", __FUNCTION__);
@ -289,7 +293,6 @@ usage(clicon_handle h,
"\t-g <group>\tClient membership required to this group (default: %s)\n" "\t-g <group>\tClient membership required to this group (default: %s)\n"
"\t-y <file>\tLoad yang spec file (override yang main module)\n" "\t-y <file>\tLoad yang spec file (override yang main module)\n"
"\t-x <plugin>\tXMLDB plugin\n"
"\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n", "\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
argv0, argv0,
plgdir ? plgdir : "none", plgdir ? plgdir : "none",
@ -320,9 +323,6 @@ main(int argc,
char *pidfile; char *pidfile;
char *sock; char *sock;
int sockfamily; int sockfamily;
char *xmldb_plugin;
int xml_cache;
char *xml_format;
char *nacm_mode; char *nacm_mode;
int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR; int logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
yang_spec *yspec = NULL; yang_spec *yspec = NULL;
@ -471,10 +471,6 @@ main(int argc,
if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0) if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0)
goto done; goto done;
break; break;
case 'x' : /* xmldb plugin */
if (clicon_option_add(h, "CLICON_XMLDB_PLUGIN", optarg) < 0)
goto done;
break;
case 'o':{ /* Configuration option */ case 'o':{ /* Configuration option */
char *val; char *val;
if ((val = index(optarg, '=')) == NULL) if ((val = index(optarg, '=')) == NULL)
@ -563,12 +559,6 @@ main(int argc,
if (clicon_option_exists(h, "CLICON_STREAM_PUB") && if (clicon_option_exists(h, "CLICON_STREAM_PUB") &&
stream_publish_init() < 0) stream_publish_init() < 0)
goto done; goto done;
if ((xmldb_plugin = clicon_xmldb_plugin(h)) == NULL){
clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).");
goto done;
}
if (xmldb_plugin_load(h, xmldb_plugin) < 0)
goto done;
/* Connect to plugin to get a handle */ /* Connect to plugin to get a handle */
if (xmldb_connect(h) < 0) if (xmldb_connect(h) < 0)
goto done; goto done;
@ -608,24 +598,6 @@ main(int argc,
if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
goto done; goto done;
/* Set options: database dir and 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;
if ((xml_cache = clicon_option_bool(h, "CLICON_XMLDB_CACHE")) >= 0)
if (xmldb_setopt(h, "xml_cache", (void*)(intptr_t)xml_cache) < 0)
goto done;
if ((xml_format = clicon_option_str(h, "CLICON_XMLDB_FORMAT")) >= 0)
if (xmldb_setopt(h, "format", (void*)xml_format) < 0)
goto done;
if (xmldb_setopt(h, "pretty", (void*)(intptr_t)clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
goto done;
if (xmldb_setopt(h, "nacm_mode", (void*)nacm_mode) < 0)
goto done;
if (xmldb_setopt(h, "nacm_xtree", (void*)clicon_nacm_ext(h)) < 0)
goto done;
/* Initialize server socket and save it to handle */ /* Initialize server socket and save it to handle */
if (backend_rpc_init(h) < 0) if (backend_rpc_init(h) < 0)
goto done; goto done;

View file

@ -334,14 +334,10 @@ startup_module_state(clicon_handle h,
if (!clicon_option_bool(h, "CLICON_XMLDB_MODSTATE")) if (!clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
goto ok; goto ok;
/* Set up cache
* Now, access brief module cache with clicon_modst_cache_get(h, 1) */
if (yang_modules_state_get(h, yspec, NULL, 1, &x) < 0) if (yang_modules_state_get(h, yspec, NULL, 1, &x) < 0)
goto done; goto done;
if (x){
if (xml_rootchild(x, 0, &x) < 0)
goto done;
if (xmldb_setopt(h, "modules_state", (void*)x) < 0)
goto done;
}
ok: ok:
retval = 0; retval = 0;
done: done:

View file

@ -86,6 +86,7 @@ struct backend_handle {
int bh_magic; /* magic (HDR)*/ int bh_magic; /* magic (HDR)*/
clicon_hash_t *bh_copt; /* clicon option list (HDR) */ clicon_hash_t *bh_copt; /* clicon option list (HDR) */
clicon_hash_t *bh_data; /* internal clicon data (HDR) */ clicon_hash_t *bh_data; /* internal clicon data (HDR) */
clicon_hash_t *ch_db_elmnt; /* xml datastore element cache data */
event_stream_t *bh_stream; /* notification streams, see clixon_stream.[ch] */ event_stream_t *bh_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */ /* ------ end of common handle ------ */

View file

@ -79,6 +79,7 @@ struct cli_handle {
int cl_magic; /* magic (HDR)*/ int cl_magic; /* magic (HDR)*/
clicon_hash_t *cl_copt; /* clicon option list (HDR) */ clicon_hash_t *cl_copt; /* clicon option list (HDR) */
clicon_hash_t *cl_data; /* internal clicon data (HDR) */ clicon_hash_t *cl_data; /* internal clicon data (HDR) */
clicon_hash_t *ch_db_elmnt; /* xml datastore element cache data */
event_stream_t *cl_stream; /* notification streams, see clixon_stream.[ch] */ event_stream_t *cl_stream; /* notification streams, see clixon_stream.[ch] */
/* ------ end of common handle ------ */ /* ------ end of common handle ------ */

3
configure vendored
View file

@ -4447,7 +4447,7 @@ _ACEOF
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/main/Makefile extras/rpm/Makefile docker/Makefile docker/system/Makefile docker/base/Makefile datastore/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/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/main/Makefile extras/rpm/Makefile docker/Makefile docker/system/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/Makefile"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure # This file is a shell script that caches the results of configure
@ -5159,7 +5159,6 @@ do
"docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;; "docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;;
"docker/system/Makefile") CONFIG_FILES="$CONFIG_FILES docker/system/Makefile" ;; "docker/system/Makefile") CONFIG_FILES="$CONFIG_FILES docker/system/Makefile" ;;
"docker/base/Makefile") CONFIG_FILES="$CONFIG_FILES docker/base/Makefile" ;; "docker/base/Makefile") CONFIG_FILES="$CONFIG_FILES docker/base/Makefile" ;;
"datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;;
"util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;; "util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;;
"yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;; "yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;;
"yang/clixon/Makefile") CONFIG_FILES="$CONFIG_FILES yang/clixon/Makefile" ;; "yang/clixon/Makefile") CONFIG_FILES="$CONFIG_FILES yang/clixon/Makefile" ;;

View file

@ -253,7 +253,6 @@ AC_OUTPUT(Makefile
docker/Makefile docker/Makefile
docker/system/Makefile docker/system/Makefile
docker/base/Makefile docker/base/Makefile
datastore/Makefile
util/Makefile util/Makefile
yang/Makefile yang/Makefile
yang/clixon/Makefile yang/clixon/Makefile

View file

@ -1,101 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2019 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 *****
#
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@
HOST_VENDOR = @host_vendor@
VPATH = @srcdir@
CC = @CC@
CFLAGS = @CFLAGS@ -rdynamic -fPIC
INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
DATASTORE = text
CPPFLAGS = @CPPFLAGS@
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
PLUGIN = $(DATASTORE).so
SRC = clixon_xmldb_text.c
OBJS = $(SRC:.c=.o)
all: $(PLUGIN)
$(PLUGIN): $(SRC)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ -lc $^ $(LIBS)
else
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -o $@ -lc $^ $(LIBS)
endif
clean:
rm -f $(PLUGIN) $(OBJS) *.core
distclean: clean
rm -f Makefile *~ .depend
.SUFFIXES:
.SUFFIXES: .c .o
.c.o: $(SRC)
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $<
install: $(PLUGIN)
install -d -m 0755 $(DESTDIR)$(libdir)/xmldb
install -m 0644 $(INSTALLFLAGS) $(PLUGIN) $(DESTDIR)$(libdir)/xmldb
install-include:
uninstall:
rm -rf $(DESTDIR)$(libdir)/xmldb/$(PLUGIN)
TAGS:
find . -name '*.[chyl]' -print | etags -
depend:
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
#include .depend

View file

@ -1,93 +0,0 @@
# Clixon datastore
The Clixon datastore is a stand-alone XML based datastore. The idea is
to be able to use different datastores backends with the same
API. There is currently only a plain text-file datastore.
The datastore is primarily designed to be used by Clixon but can be used
separately.
A datastore is a dynamic plugin that is loaded at runtime with a
well-defined API. This means it is possible to create your own
datastore and plug it in a Clixon backend at runtime.
### The functional API
```
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_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 xmldb_unlock_all(clicon_handle h, int pid);
int xmldb_islocked(clicon_handle h, char *db);
int xmldb_exists(clicon_handle h, char *db);
int xmldb_delete(clicon_handle h, char *db);
int xmldb_create(clicon_handle h, char *db);
```
### Using the API
To use the API, a client needs the following:
- A clicon handle.
- A datastore plugin, such as a text.so. These are normally built and installed at Clixon make.
- A directory where to store databases
- A yang specification. This needs to be parsed using the Clixon yang_parse() method.
A client calling the API needs to:
1. Load a plugin and
2. Connect to a datastore.
You can connect to several datastores, even concurrently,
but in practice in Clixon, you connect to a single store.
After connecting to a datastore, you can create and modify databases
within the datastore, and set and get options of the datastore itself.
When done, you disconnect from the datastore and unload the plugin.
Within a datastore, the following four databases may exist:
- running
- candidate
- startup
- tmp
Initially, a database does not exist but is created by
xmldb_create(). It is deleted by xmldb_delete(). You may check for
existence with xmldb_exists(). You need to create a database before
you can perform any data access on it.
You may lock a database for exclusive modification according to
Netconf semantics. You may also unlock a single dabase, unlock all frm
a specific session.
You can read a database with xmldb_get() and modify a database with
xmldb_put(), and xmldb_copy().
A typical datastore session can be as follows, see the source code of
[datastore_client.c](datastore_client.c) for a more elaborate example.
```
h = clicon_handle_init();
xmldb_plugin_load(h, plugin);
xmldb_connect(h);
xmldb_setopt(h, "dbdir", dbdir);
xmldb_setopt(h, "yangspec", yspec);
/* From here databases in the datastore may be accessed */
xmldb_create(h, "candidate");
xmldb_copy(h, "running", "candidate");
xmldb_lock(h, "candidate", 7878);
xmldb_put(h, "candidate", OP_CREATE, "/interfaces/interface=eth0", xml);
xmldb_unlock(h, "candidate");
xmldb_get(h, "candidate", "/", &xml, &xvec, &xlen);
xmldb_disconnect(h)
xmdlb_plugin_unload(h);
```

File diff suppressed because it is too large Load diff

View file

@ -83,8 +83,9 @@
#include <clixon/clixon_proto_client.h> #include <clixon/clixon_proto_client.h>
#include <clixon/clixon_plugin.h> #include <clixon/clixon_plugin.h>
#include <clixon/clixon_options.h> #include <clixon/clixon_options.h>
#include <clixon/clixon_data.h>
#include <clixon/clixon_xml_map.h> #include <clixon/clixon_xml_map.h>
#include <clixon/clixon_xml_db.h> #include <clixon/clixon_datastore.h>
#include <clixon/clixon_xpath_ctx.h> #include <clixon/clixon_xpath_ctx.h>
#include <clixon/clixon_xpath.h> #include <clixon/clixon_xpath.h>
#include <clixon/clixon_json.h> #include <clixon/clixon_json.h>

View file

@ -0,0 +1,62 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 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 *****
* Clixon Datastore (XMLDB)
* Saves Clixon data as clear-text XML (or JSON)
*/
#ifndef _CLIXON_DATASTORE_H
#define _CLIXON_DATASTORE_H
/*
* Prototypes
* API
*/
/* Internal functions */
int xmldb_db2file(clicon_handle h, const char *db, char **filename);
/* API */
int xmldb_validate_db(const char *db);
int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h);
int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop, modstate_diff_t *msd);
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret); /* in clixon_datastore_write.[ch] */
int xmldb_copy(clicon_handle h, const char *from, const char *to);
int xmldb_lock(clicon_handle h, const char *db, int pid);
int xmldb_unlock(clicon_handle h, const char *db);
int xmldb_unlock_all(clicon_handle h, int pid);
int xmldb_islocked(clicon_handle h, const char *db);
int xmldb_exists(clicon_handle h, const char *db);
int xmldb_delete(clicon_handle h, const char *db);
int xmldb_create(clicon_handle h, const char *db);
#endif /* _CLIXON_DATASTORE_H */

View file

@ -31,6 +31,7 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
* This file requires dirent.h
*/ */
#ifndef _CLIXON_FILE_H_ #ifndef _CLIXON_FILE_H_

View file

@ -74,6 +74,9 @@ clicon_hash_t *clicon_options(clicon_handle h);
/* Return internal clicon data (hash-array) given a handle.*/ /* Return internal clicon data (hash-array) given a handle.*/
clicon_hash_t *clicon_data(clicon_handle h); clicon_hash_t *clicon_data(clicon_handle h);
/* Return internal clicon db_elmnt (hash-array) given a handle.*/
clicon_hash_t *clicon_db_elmnt(clicon_handle h);
/* Return internal stream hash-array given a handle.*/ /* Return internal stream hash-array given a handle.*/
struct event_stream *clicon_stream(clicon_handle h); struct event_stream *clicon_stream(clicon_handle h);
struct event_stream; struct event_stream;

View file

@ -47,7 +47,6 @@
/* /*
* Types * Types
*/ */
/*! Controls how keywords a generated in CLI syntax / prints from object model /*! Controls how keywords a generated in CLI syntax / prints from object model
* Example YANG: * Example YANG:
* list a { * list a {
@ -154,9 +153,6 @@ static inline char *clicon_backend_pidfile(clicon_handle h){
static inline char *clicon_xmldb_dir(clicon_handle h){ static inline char *clicon_xmldb_dir(clicon_handle h){
return clicon_option_str(h, "CLICON_XMLDB_DIR"); return clicon_option_str(h, "CLICON_XMLDB_DIR");
} }
static inline char *clicon_xmldb_plugin(clicon_handle h){
return clicon_option_str(h, "CLICON_XMLDB_PLUGIN");
}
/*-- Specific option access functions for YANG options w type conversion--*/ /*-- Specific option access functions for YANG options w type conversion--*/
int clicon_cli_genmodel(clicon_handle h); int clicon_cli_genmodel(clicon_handle h);
@ -172,50 +168,4 @@ int clicon_startup_mode(clicon_handle h);
int clicon_quiet_mode(clicon_handle h); int clicon_quiet_mode(clicon_handle h);
int clicon_quiet_mode_set(clicon_handle h, int val); int clicon_quiet_mode_set(clicon_handle h, int val);
yang_spec * clicon_dbspec_yang(clicon_handle h);
int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
cxobj * clicon_nacm_ext(clicon_handle h);
int clicon_nacm_ext_set(clicon_handle h, cxobj *xn);
yang_spec * clicon_config_yang(clicon_handle h);
int clicon_config_yang_set(clicon_handle h, struct yang_spec *ys);
cxobj *clicon_conf_xml(clicon_handle h);
int clicon_conf_xml_set(clicon_handle h, cxobj *x);
plghndl_t clicon_xmldb_plugin_get(clicon_handle h);
int clicon_xmldb_plugin_set(clicon_handle h, plghndl_t handle);
void *clicon_xmldb_api_get(clicon_handle h);
int clicon_xmldb_api_set(clicon_handle h, void *xa_api);
void *clicon_xmldb_handle_get(clicon_handle h);
int clicon_xmldb_handle_set(clicon_handle h, void *xh);
/**/
/* Set and get authorized user name */
char *clicon_username_get(clicon_handle h);
int clicon_username_set(clicon_handle h, void *username);
/* Set and get startup status */
enum startup_status clicon_startup_status_get(clicon_handle h);
int clicon_startup_status_set(clicon_handle h, enum startup_status status);
/* Set and get socket fd (ie backend server socket / restconf fcgx socket */
int clicon_socket_get(clicon_handle h);
int clicon_socket_set(clicon_handle h, int s);
/*! Set and get module state cache */
cxobj *clicon_module_state_get(clicon_handle h);
int clicon_module_state_set(clicon_handle h, cxobj *xms);
/*! Set and get yang/xml module revision changelog */
cxobj *clicon_xml_changelog_get(clicon_handle h);
int clicon_xml_changelog_set(clicon_handle h, cxobj *xchlog);
/*! Set and get user command-line options (after --) */
int clicon_argv_get(clicon_handle h, int *argc, char ***argv);
int clicon_argv_set(clicon_handle h, char *argv0, int argc, char **argv);
#endif /* _CLIXON_OPTIONS_H_ */ #endif /* _CLIXON_OPTIONS_H_ */

View file

@ -1,152 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 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 *****
*/
#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
/* Magic to ensure plugin sanity. */
#define XMLDB_API_MAGIC 0xf386f730
/* Name of plugin init function (must be called this) */
#define XMLDB_PLUGIN_INIT_FN "clixon_xmldb_plugin_init"
/* Type of plugin init function */
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)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop, modstate_diff_t *msd);
/* Type of xmldb put function */
typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
/* Type of xmldb copy function */
typedef int (xmldb_copy_t)(xmldb_handle xh, const char *from, const char *to);
/* Type of xmldb lock function */
typedef int (xmldb_lock_t)(xmldb_handle xh, const char *db, int pid);
/* Type of xmldb unlock function */
typedef int (xmldb_unlock_t)(xmldb_handle xh, const char *db);
/* Type of xmldb unlock_all function */
typedef int (xmldb_unlock_all_t)(xmldb_handle xh, int pid);
/* Type of xmldb islocked function */
typedef int (xmldb_islocked_t)(xmldb_handle xh, const char *db);
/* Type of xmldb exists function */
typedef int (xmldb_exists_t)(xmldb_handle xh, const char *db);
/* Type of xmldb delete function */
typedef int (xmldb_delete_t)(xmldb_handle xh, const char *db);
/* Type of xmldb init function */
typedef int (xmldb_create_t)(xmldb_handle xh, const char *db);
/* plugin init struct for the api */
struct xmldb_api{
int xa_version;
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_copy_t *xa_copy_fn;
xmldb_lock_t *xa_lock_fn;
xmldb_unlock_t *xa_unlock_fn;
xmldb_unlock_all_t *xa_unlock_all_fn;
xmldb_islocked_t *xa_islocked_fn;
xmldb_exists_t *xa_exists_fn;
xmldb_delete_t *xa_delete_fn;
xmldb_create_t *xa_create_fn;
};
/*
* Prototypes
* API
*/
int xmldb_plugin_load(clicon_handle h, char *filename);
int xmldb_plugin_unload(clicon_handle h);
int xmldb_validate_db(const char *db);
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, const char *db, char *xpath, int config, cxobj **xtop, modstate_diff_t *msd);
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
int xmldb_copy(clicon_handle h, const char *from, const char *to);
int xmldb_lock(clicon_handle h, const char *db, int pid);
int xmldb_unlock(clicon_handle h, const char *db);
int xmldb_unlock_all(clicon_handle h, int pid);
int xmldb_islocked(clicon_handle h, const char *db);
int xmldb_exists(clicon_handle h, const char *db);
int xmldb_delete(clicon_handle h, const char *db);
int xmldb_create(clicon_handle h, const char *db);
#endif /* _CLIXON_XML_DB_H */

View file

@ -60,12 +60,13 @@ typedef struct {
*/ */
modstate_diff_t * modstate_diff_new(void); modstate_diff_t * modstate_diff_new(void);
int modstate_diff_free(modstate_diff_t *); int modstate_diff_free(modstate_diff_t *);
int modules_state_cache_set(clicon_handle h, cxobj *msx);
int yang_modules_init(clicon_handle h); int yang_modules_init(clicon_handle h);
char *yang_modules_revision(clicon_handle h); char *yang_modules_revision(clicon_handle h);
int yang_modules_state_get(clicon_handle h, yang_spec *yspec, char *xpath, int yang_modules_state_get(clicon_handle h, yang_spec *yspec, char *xpath,
int brief, cxobj **xret); int brief, cxobj **xret);
int clixon_module_upgrade(clicon_handle h, cxobj *xt, modstate_diff_t *msd, cbuf *cb); int clixon_module_upgrade(clicon_handle h, cxobj *xt, modstate_diff_t *msd, cbuf *cb);
#endif /* _CLIXON_YANG_MODULE_H_ */ #endif /* _CLIXON_YANG_MODULE_H_ */

View file

@ -71,10 +71,11 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \ clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_file.c \
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \ clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
clixon_yang_cardinality.c clixon_xml_changelog.c \ clixon_yang_cardinality.c clixon_xml_changelog.c \
clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \ clixon_proto.c clixon_proto_client.c \
clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \ clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
clixon_xml_db.c clixon_netconf_lib.c clixon_stream.c clixon_nacm.c clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
clixon_netconf_lib.c clixon_stream.c clixon_nacm.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \

479
lib/src/clixon_datastore.c Normal file
View file

@ -0,0 +1,479 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 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 *****
* Clixon Datastore (XMLDB)
* Saves Clixon data as clear-text XML (or JSON)
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <libgen.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/param.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_string.h"
#include "clixon_file.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_yang_module.h"
#include "clixon_datastore.h"
#include "clixon_datastore_write.h"
#include "clixon_datastore_read.h"
/*! Translate from symbolic database name to actual filename in file-system
* @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
* @retval -1 Error
* @note Could need a way to extend which databases exists, eg to register new.
* The currently allowed databases are:
* candidate, tmp, running, result
* The filename reside in CLICON_XMLDB_DIR option
*/
int
xmldb_db2file(clicon_handle h,
const char *db,
char **filename)
{
int retval = -1;
cbuf *cb = NULL;
char *dir;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if ((dir = clicon_xmldb_dir(h)) == NULL){
clicon_err(OE_XML, errno, "dbdir not set");
goto done;
}
cprintf(cb, "%s/%s_db", dir, db);
if ((*filename = strdup4(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Validate database name
* @param[in] db Name of database
* @retval 0 OK
* @retval -1 Failed validate, xret set to error
* XXX why is this function here? should be handled by netconf yang validation
*/
int
xmldb_validate_db(const char *db)
{
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "startup") != 0 &&
strcmp(db, "tmp") != 0)
return -1;
return 0;
}
/*! Connect to a datastore plugin, allocate resources to be used in API calls
* @param[in] h Clicon handle
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_connect(clicon_handle h)
{
return 0;
}
/*! Disconnect from a datastore plugin and deallocate resources
* @param[in] handle Disconect and deallocate from this handle
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_disconnect(clicon_handle h)
{
int retval = -1;
char **keys = NULL;
size_t klen;
int i;
db_elmnt *de;
if (hash_keys(clicon_db_elmnt(h), &keys, &klen) < 0)
goto done;
for(i = 0; i < klen; i++)
if ((de = hash_value(clicon_db_elmnt(h), keys[i], NULL)) != NULL){
if (de->de_xml){
xml_free(de->de_xml);
de->de_xml = NULL;
}
}
retval = 0;
done:
if (keys)
free(keys);
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[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt, NULL) < 0)
* err;
* xml_free(xt);
* @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec
*/
int
xmldb_get(clicon_handle h,
const char *db,
char *xpath,
int config,
cxobj **xret,
modstate_diff_t *msd)
{
int retval = -1;
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE"))
retval = xmldb_get_cache(h, db, xpath, config, xret, msd);
else
retval = xmldb_get_nocache(h, db, xpath, config, xret, msd);
return retval;
}
/*! Copy database from db1 to db2
* @param[in] h Clicon handle
* @param[in] from Source database
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_copy(clicon_handle h,
const char *from,
const char *to)
{
int retval = -1;
char *fromfile = NULL;
char *tofile = NULL;
db_elmnt *de1 = NULL;
db_elmnt *de2 = NULL;
db_elmnt de0 = {0,};
cxobj *x1 = NULL;
cxobj *x2 = NULL;
/* XXX lock */
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
/* 1. "to" xml tree in x1 */
if ((de1 = clicon_db_elmnt_get(h, from)) != NULL)
x1 = de1->de_xml;
if ((de2 = clicon_db_elmnt_get(h, to)) != NULL)
x2 = de2->de_xml;
if (x1 == NULL && x2 == NULL){
/* do nothing */
}
else if (x1 == NULL){ /* free x2 and set to NULL */
xml_free(x2);
x2 = NULL;
}
else if (x2 == NULL){ /* create x2 and copy x1 to it */
if ((x2 = xml_new(xml_name(x1), NULL, xml_spec(x1))) == NULL)
goto done;
if (xml_copy(x1, x2) < 0)
goto done;
}
else{ /* copy x1 to x2 */
xml_free(x2);
if ((x2 = xml_new(xml_name(x1), NULL, xml_spec(x1))) == NULL)
goto done;
if (xml_copy(x1, x2) < 0)
goto done;
}
if (x1 || x2){
if (de2)
de0 = *de2;
de0.de_xml = x2; /* The new tree */
clicon_db_elmnt_set(h, to, &de0);
}
}
if (xmldb_db2file(h, from, &fromfile) < 0)
goto done;
if (xmldb_db2file(h, to, &tofile) < 0)
goto done;
/* Copy the files themselves (above only in-memory cache) */
if (clicon_file_copy(fromfile, tofile) < 0)
goto done;
retval = 0;
done:
if (fromfile)
free(fromfile);
if (tofile)
free(tofile);
return retval;
}
/*! Lock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_lock(clicon_handle h,
const char *db,
int pid)
{
db_elmnt *de = NULL;
db_elmnt de0 = {0,};
if ((de = clicon_db_elmnt_get(h, db)) != NULL)
de0 = *de;
de0.de_pid = pid;
clicon_db_elmnt_set(h, db, &de0);
clicon_debug(1, "%s: locked by %u", db, pid);
return 0;
}
/*! Unlock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
* Assume all sanity checks have been made
*/
int
xmldb_unlock(clicon_handle h,
const char *db)
{
db_elmnt *de = NULL;
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
de->de_pid = 0;
clicon_db_elmnt_set(h, db, de);
}
return 0;
}
/*! 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
*/
int
xmldb_unlock_all(clicon_handle h,
int pid)
{
int retval = -1;
char **keys = NULL;
size_t klen;
int i;
db_elmnt *de;
if (hash_keys(clicon_db_elmnt(h), &keys, &klen) < 0)
goto done;
for (i = 0; i < klen; i++)
if ((de = clicon_db_elmnt_get(h, keys[i])) != NULL &&
de->de_pid == pid){
de->de_pid = 0;
clicon_db_elmnt_set(h, keys[i], de);
}
retval = 0;
done:
if (keys)
free(keys);
return retval;
}
/*! 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
*/
int
xmldb_islocked(clicon_handle h,
const char *db)
{
db_elmnt *de;
if ((de = clicon_db_elmnt_get(h, db)) == NULL)
return 0;
return de->de_pid;
}
/*! Check if db exists
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
xmldb_exists(clicon_handle h,
const char *db)
{
int retval = -1;
char *filename = NULL;
struct stat sb;
if (xmldb_db2file(h, db, &filename) < 0)
goto done;
if (lstat(filename, &sb) < 0)
retval = 0;
else
retval = 1;
done:
if (filename)
free(filename);
return retval;
}
/*! Delete database. Remove file
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_delete(clicon_handle h,
const char *db)
{
int retval = -1;
char *filename = NULL;
db_elmnt *de = NULL;
cxobj *xt = NULL;
struct stat sb;
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
if ((xt = de->de_xml) != NULL){
xml_free(xt);
de->de_xml = NULL;
}
}
}
if (xmldb_db2file(h, db, &filename) < 0)
goto done;
if (lstat(filename, &sb) == 0)
if (unlink(filename) < 0){
clicon_err(OE_DB, errno, "unlink %s", filename);
goto done;
}
retval = 0;
done:
if (filename)
free(filename);
return retval;
}
/*! Create a database. Open database for writing.
* @param[in] h Clicon handle
* @param[in] db Database
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_create(clicon_handle h,
const char *db)
{
int retval = -1;
char *filename = NULL;
int fd = -1;
db_elmnt *de = NULL;
cxobj *xt = NULL;
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){ /* XXX This should not really happen? */
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
if ((xt = de->de_xml) != NULL){
xml_free(xt);
de->de_xml = NULL;
}
}
}
if (xmldb_db2file(h, db, &filename) < 0)
goto done;
if ((fd = open(filename, O_CREAT|O_WRONLY, S_IRWXU)) == -1) {
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
}
retval = 0;
done:
if (filename)
free(filename);
if (fd != -1)
close(fd);
return retval;
}

View file

@ -0,0 +1,740 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 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 *****
1000 entries
valgrind --tool=callgrind datastore_client -d candidate -b /tmp/text -p ../datastore/text/text.so -y /tmp -m ietf-ip mget 300 /x/y[a=574][b=574] > /dev/null
xml_copy_marked 87% 200x
yang_key_match 81% 600K
yang_arg2cvec 52% 400K
cvecfree 23% 400K
10000 entries
valgrind --tool=callgrind datastore_client -d candidate -b /tmp/text -p ../datastore/text/text.so -y /tmp -m ietf-ip mget 10 /x/y[a=574][b=574] > /dev/null
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_file.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xml_map.h"
#include "clixon_json.h"
#include "clixon_nacm.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
#include "clixon_datastore.h"
#include "clixon_datastore_write.h"
#include "clixon_datastore_read.h"
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
* @param[in] th Datastore text handle
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
* @param[in] x0p Parent of x0
* @param[in] x1 XML tree which modifies base
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[in] username User name of requestor for nacm
* @param[in] xnacm NACM XML tree (only if !permit)
* @param[in] permit If set, no NACM tests using xnacm required
* @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
* @retval -1 Error
* @retval 0 Failed (cbret set)
* @retval 1 OK
* Assume x0 and x1 are same on entry and that y is the spec
* @see text_modify_top
*/
static int
text_modify(clicon_handle h,
cxobj *x0,
yang_node *y0,
cxobj *x0p,
cxobj *x1,
enum operation_type op,
char *username,
cxobj *xnacm,
int permit,
cbuf *cbret)
{
int retval = -1;
char *opstr;
char *x1name;
char *x1cname; /* child name */
cxobj *x0a; /* attribute */
cxobj *x1a; /* attribute */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
char *xns; /* namespace */
char *x0bstr; /* mod body string */
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cxobj **x0vec = NULL;
int i;
int ret;
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
/* Check for operations embedded in tree according to netconf */
if ((opstr = xml_find_value(x1, "operation")) != NULL)
if (xml_operation(opstr, &op) < 0)
goto done;
x1name = xml_name(x1);
if (y0->yn_keyword == Y_LEAF_LIST || y0->yn_keyword == Y_LEAF){
x1bstr = xml_body(x1);
switch(op){
case OP_CREATE:
if (x0){
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
goto fail;
}
case OP_NONE: /* fall thru */
case OP_MERGE:
case OP_REPLACE:
if (x0==NULL){
if ((op != OP_NONE) && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
permit = 1;
}
// int iamkey=0;
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
/* Copy xmlns attributes */
x1a = NULL;
while ((x1a = xml_child_each(x1, x1a, CX_ATTR)) != NULL)
if (strcmp(xml_name(x1a),"xmlns")==0 ||
((xns = xml_prefix(x1a)) && strcmp(xns, "xmlns")==0)){
if ((x0a = xml_dup(x1a)) == NULL)
goto done;
if (xml_addsub(x0, x0a) < 0)
goto done;
}
#if 0
/* If it is key I dont want to mark it */
if ((iamkey=yang_key_match(y0->yn_parent, x1name)) < 0)
goto done;
if (!iamkey && op==OP_NONE)
#else
if (op==OP_NONE)
#endif
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
if (x1bstr){ /* empty type does not have body */
if ((x0b = xml_new("body", x0, NULL)) == NULL)
goto done;
xml_type_set(x0b, CX_BODY);
}
}
if (x1bstr){
if ((x0b = xml_body_get(x0)) != NULL){
x0bstr = xml_value(x0b);
if (x0bstr==NULL || strcmp(x0bstr, x1bstr)){
if ((op != OP_NONE) && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1,
x0bstr==NULL?NACM_CREATE:NACM_UPDATE,
username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
if (xml_value_set(x0b, x1bstr) < 0)
goto done;
}
}
}
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
case OP_REMOVE: /* fall thru */
if (x0){
if ((op != OP_NONE) && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
if (xml_purge(x0) < 0)
goto done;
}
break;
default:
break;
} /* switch op */
} /* if LEAF|LEAF_LIST */
else { /* eg Y_CONTAINER, Y_LIST, Y_ANYXML */
switch(op){
case OP_CREATE:
if (x0){
if (netconf_data_exists(cbret, "Data already exists; cannot create new resource") < 0)
goto done;
goto fail;
}
case OP_REPLACE: /* fall thru */
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
permit = 1;
}
if (x0){
xml_purge(x0);
x0 = NULL;
}
case OP_MERGE: /* fall thru */
case OP_NONE:
/* Special case: anyxml, just replace tree,
See rfc6020 7.10.3:n
An anyxml node is treated as an opaque chunk of data. This data
can be modified in its entirety only.
Any "operation" attributes present on subelements of an anyxml
node are ignored by the NETCONF server.*/
if (y0->yn_keyword == Y_ANYXML || y0->yn_keyword == Y_ANYDATA){
if (op == OP_NONE)
break;
if (op==OP_MERGE && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
permit = 1;
}
if (x0){
xml_purge(x0);
}
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
if (xml_copy(x1, x0) < 0)
goto done;
break;
}
if (x0==NULL){
if (op==OP_MERGE && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
permit = 1;
}
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
goto done;
/* Copy xmlns attributes */
x1a = NULL;
while ((x1a = xml_child_each(x1, x1a, CX_ATTR)) != NULL)
if (strcmp(xml_name(x1a),"xmlns")==0 ||
((xns = xml_prefix(x1a)) && strcmp(xns, "xmlns")==0)){
if ((x0a = xml_dup(x1a)) == NULL)
goto done;
if (xml_addsub(x0, x0a) < 0)
goto done;
}
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
}
/* First pass: Loop through children of the x1 modification tree
* collect matching nodes from x0 in x0vec (no changes to x0 children)
*/
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
/* Get yang spec of the child */
if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
clicon_err(OE_YANG, errno, "No yang node found: %s", x1cname);
goto done;
}
/* See if there is a corresponding node in the base tree */
x0c = NULL;
if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
#if 1
if (x0c && (yc != xml_spec(x0c))){
/* There is a match but is should be replaced (choice)*/
if (xml_purge(x0c) < 0)
goto done;
x0c = NULL;
}
#endif
x0vec[i++] = x0c; /* != NULL if x0c is matching x1c */
}
/* Second pass: Loop through children of the x1 modification tree again
* Now potentially modify x0:s children
* Here x0vec contains one-to-one matching nodes of x1:s children.
*/
x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
x0c = x0vec[i++];
yc = yang_find_datanode(y0, x1cname);
if ((ret = text_modify(h, x0c, (yang_node*)yc, x0, x1c, op,
username, xnacm, permit, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (ret == 0)
goto fail;
}
break;
case OP_DELETE:
if (x0==NULL){
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
}
case OP_REMOVE: /* fall thru */
if (x0){
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
}
if (xml_purge(x0) < 0)
goto done;
}
break;
default:
break;
} /* CONTAINER switch op */
} /* else Y_CONTAINER */
xml_sort(x0p, NULL);
retval = 1;
done:
if (x0vec)
free(x0vec);
return retval;
fail: /* cbret set */
retval = 0;
goto done;
} /* text_modify */
/*! Modify a top-level base tree x0 with modification tree x1
* @param[in] th Datastore text handle
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] x1 XML tree which modifies base
* @param[in] yspec Top-level yang spec (if y is NULL)
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[in] username User name of requestor for nacm
* @param[in] xnacm NACM XML tree (only if !permit)
* @param[in] permit If set, no NACM tests using xnacm required
* @param[out] cbret Initialized cligen buffer. Contains return XML if retval is 0.
* @retval -1 Error
* @retval 0 Failed (cbret set)
* @retval 1 OK
* @see text_modify
*/
static int
text_modify_top(clicon_handle h,
cxobj *x0,
cxobj *x1,
yang_spec *yspec,
enum operation_type op,
char *username,
cxobj *xnacm,
int permit,
cbuf *cbret)
{
int retval = -1;
char *x1cname; /* child name */
cxobj *x0c; /* base child */
cxobj *x1c; /* mod child */
yang_stmt *yc; /* yang child */
yang_stmt *ymod;/* yang module */
char *opstr;
int ret;
/* Assure top-levels are 'config' */
assert(x0 && strcmp(xml_name(x0),"config")==0);
assert(x1 && strcmp(xml_name(x1),"config")==0);
/* Check for operations embedded in tree according to netconf */
if ((opstr = xml_find_value(x1, "operation")) != NULL)
if (xml_operation(opstr, &op) < 0)
goto done;
/* Special case if x1 is empty, top-level only <config/> */
if (xml_child_nr_type(x1, CX_ELMNT) == 0){
if (xml_child_nr_type(x0, CX_ELMNT)){ /* base tree not empty */
switch(op){
case OP_DELETE:
case OP_REMOVE:
case OP_REPLACE:
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
permit = 1;
}
while ((x0c = xml_child_i(x0, 0)) != 0)
if (xml_purge(x0c) < 0)
goto done;
break;
default:
break;
}
}
else /* base tree empty */
switch(op){
#if 0 /* According to RFC6020 7.5.8 you cant delete a non-existing object.
On the other hand, the top-level cannot be removed anyway.
Additionally, I think this is irritating so I disable it.
I.e., curl -u andy:bar -sS -X DELETE http://localhost/restconf/data
*/
case OP_DELETE:
if (netconf_data_missing(cbret, "Data does not exist; cannot delete resource") < 0)
goto done;
goto fail;
break;
#endif
default:
break;
}
}
/* Special case top-level replace */
else if (op == OP_REPLACE || op == OP_DELETE){
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, NACM_UPDATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
permit = 1;
}
while ((x0c = xml_child_i(x0, 0)) != 0)
if (xml_purge(x0c) < 0)
goto done;
}
/* Loop through children of the modification tree */
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
/* Get yang spec of the child */
yc = NULL;
if (ys_module_by_xml(yspec, x1c, &ymod) <0)
goto done;
if (ymod != NULL)
yc = yang_find_datanode((yang_node*)ymod, x1cname);
if (yc == NULL){
if (netconf_unknown_element(cbret, "application", x1cname, "Unassigned yang spec") < 0)
goto done;
goto fail;
}
/* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, yc, &x0c) < 0)
goto done;
#if 1
if (x0c && (yc != xml_spec(x0c))){
/* There is a match but is should be replaced (choice)*/
if (xml_purge(x0c) < 0)
goto done;
x0c = NULL;
}
#endif
if ((ret = text_modify(h, x0c, (yang_node*)yc, x0, x1c, op,
username, xnacm, permit, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (ret == 0)
goto fail;
}
// ok:
retval = 1;
done:
return retval;
fail: /* cbret set */
retval = 0;
goto done;
} /* text_modify_top */
/*! For containers without presence and no children(except attrs), remove
* @param[in] x XML tree node
* See section 7.5.1 in rfc6020bis-02.txt:
* No presence:
* those that exist only for organizing the hierarchy of data nodes:
* the container has no meaning of its own, existing
* only to contain child nodes. This is the default style.
* (Remove these if no children)
* Presence:
* the presence of the container itself is
* configuration data, representing a single bit of configuration data.
* The container acts as both a configuration knob and a means of
* organizing related configuration. These containers are explicitly
* created and deleted.
* (Dont touch these)
*/
static int
xml_container_presence(cxobj *x,
void *arg)
{
int retval = -1;
yang_stmt *y; /* yang node */
if ((y = (yang_stmt*)xml_spec(x)) == NULL){
retval = 0;
goto done;
}
/* Mark node that is: container, have no children, dont have presence */
if (y->ys_keyword == Y_CONTAINER &&
xml_child_nr_notype(x, CX_ATTR)==0 &&
yang_find((yang_node*)y, Y_PRESENCE, NULL) == NULL)
xml_flag_set(x, XML_FLAG_MARK); /* Mark, remove later */
retval = 0;
done:
return retval;
}
/*! Modify database given an xml tree and an operation
*
* @param[in] h CLICON handle
* @param[in] db running or candidate
* @param[in] op Top-level operation, can be superceded by other op in tree
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] username User name for nacm
* @param[out] cbret Initialized cligen buffer. On exit contains XML if retval == 0
* @retval 1 OK
* @retval 0 Failed, cbret contains error xml message
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* cxobj *xret = NULL;
* if (xml_parse_string("<a>17</a>", yspec, &xt) < 0)
* err;
* if ((ret = xmldb_put(h, "running", OP_MERGE, xt, username, cbret)) < 0)
* err;
* if (ret==0)
* cbret contains netconf error message
* @endcode
* @note that you can add both config data and state data. In comparison,
* xmldb_get has a parameter to get config data only.
* @note if xret is non-null, it may contain error message
*/
int
xmldb_put(clicon_handle h,
const char *db,
enum operation_type op,
cxobj *x1,
char *username,
cbuf *cbret)
{
int retval = -1;
char *dbfile = NULL;
FILE *f = NULL;
cbuf *cb = NULL;
yang_spec *yspec;
cxobj *x0 = NULL;
db_elmnt *de = NULL;
int ret;
cxobj *xnacm = NULL;
char *mode;
cxobj *xnacm0 = NULL;
cxobj *xmodst = NULL;
cxobj *x;
int permit = 0; /* nacm permit all */
char *format;
if (cbret == NULL){
clicon_err(OE_XML, EINVAL, "cbret is NULL");
goto done;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No yang spec");
goto done;
}
if (x1 && strcmp(xml_name(x1),"config")!=0){
clicon_err(OE_XML, 0, "Top-level symbol of modification tree is %s, expected \"config\"",
xml_name(x1));
goto done;
}
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
if ((de = clicon_db_elmnt_get(h, db)) != NULL)
x0 = de->de_xml;
}
/* If there is no xml x0 tree (in cache), then read it from file */
if (x0 == NULL){
if (xmldb_readfile(h, db, yspec, &x0, NULL) < 0)
goto done;
}
if (strcmp(xml_name(x0), "config")!=0){
clicon_err(OE_XML, 0, "Top-level symbol is %s, expected \"config\"",
xml_name(x0));
goto done;
}
/* Here x0 looks like: <config>...</config> */
#if 0 /* debug */
if (xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #1", __FUNCTION__);
#endif
mode = clicon_option_str(h, "CLICON_NACM_MODE");
if (mode){
if (strcmp(mode, "external")==0)
xnacm0 = clicon_nacm_ext(h);
else if (strcmp(mode, "internal")==0)
xnacm0 = x0;
}
if (xnacm0 != NULL &&
(xnacm = xpath_first(xnacm0, "nacm")) != NULL){
/* Pre-NACM access step, if permit, then dont do any nacm checks in
* text_modify_* below */
if ((permit = nacm_access(mode, xnacm, username)) < 0)
goto done;
}
/* Here assume if xnacm is set and !permit do NACM */
/*
* Modify base tree x with modification x1. This is where the
* new tree is made.
*/
if ((ret = text_modify_top(h, x0, x1, yspec, op, username, xnacm, permit, cbret)) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (ret == 0)
goto fail;
/* Remove NONE nodes if all subs recursively are also NONE */
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
goto done;
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
(void*)XML_FLAG_NONE) < 0)
goto done;
/* Mark non-presence containers that do not have children */
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_container_presence, NULL) < 0)
goto done;
/* Remove (prune) nodes that are marked (non-presence containers w/o children) */
if (xml_tree_prune_flagged(x0, XML_FLAG_MARK, 1) < 0)
goto done;
#if 0 /* debug */
if (xml_apply0(x0, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #3", __FUNCTION__);
#endif
/* Write back to datastore cache if first time */
if (clicon_option_bool(h, "CLICON_XMLDB_CACHE")){
db_elmnt de0 = {0,};
if (de != NULL)
de0 = *de;
if (de0.de_xml == NULL){
de0.de_xml = x0;
clicon_db_elmnt_set(h, db, &de0);
}
}
if (xmldb_db2file(h, db, &dbfile) < 0)
goto done;
if (dbfile==NULL){
clicon_err(OE_XML, 0, "dbfile NULL");
goto done;
}
/* Add module revision info before writing to file)
* Only if CLICON_XMLDB_MODSTATE is set
*/
if ((x = clicon_modst_cache_get(h, 1)) != NULL){
if ((xmodst = xml_dup(x)) == NULL)
goto done;
if (xml_addsub(x0, xmodst) < 0)
goto done;
}
if ((f = fopen(dbfile, "w")) == NULL){
clicon_err(OE_CFG, errno, "Creating file %s", dbfile);
goto done;
}
format = clicon_option_str(h, "CLICON_XMLDB_FORMAT");
if (format && strcmp(format,"json")==0){
if (xml2json(f, x0, clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
goto done;
}
else if (clicon_xml2file(f, x0, 0, clicon_option_bool(h, "CLICON_XMLDB_PRETTY")) < 0)
goto done;
/* Remove modules state after writing to file
*/
if (xmodst && xml_purge(xmodst) < 0)
goto done;
retval = 1;
done:
if (f != NULL)
fclose(f);
if (dbfile)
free(dbfile);
if (cb)
cbuf_free(cb);
if (!clicon_option_bool(h, "CLICON_XMLDB_CACHE") && x0)
xml_free(x0);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -31,23 +31,18 @@
***** END LICENSE BLOCK ***** ***** END LICENSE BLOCK *****
Key-value store * Datastore text-based XML write functions
*/
#ifndef _CLIXON_DATASTORE_WRITE_H
#define _CLIXON_DATASTORE_WRITE_H
/*
* Types
*/ */
#ifndef _CLIXON_XMLDB_TEXT_H
#define _CLIXON_XMLDB_TEXT_H
/* /*
* Prototypes * Prototypes
*/ */
int text_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop, modstate_diff_t *xms); int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
int text_dump(FILE *f, char *dbfilename, char *rxkey);
int text_copy(xmldb_handle h, const char *from, const char *to);
int text_lock(xmldb_handle h, const char *db, int pid);
int text_unlock(xmldb_handle h, const char *db);
int text_unlock_all(xmldb_handle h, int pid);
int text_islocked(xmldb_handle h, const char *db);
int text_exists(xmldb_handle h, const char *db);
int text_delete(xmldb_handle h, const char *db);
#endif /* _CLIXON_XMLDB_TEXT_H */ #endif /* _CLIXON_DATASTORE_WRITE_H */

View file

@ -56,6 +56,7 @@
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_stream.h" #include "clixon_stream.h"
#include "clixon_data.h"
#include "clixon_options.h" #include "clixon_options.h"
#define CLICON_MAGIC 0x99aafabe #define CLICON_MAGIC 0x99aafabe
@ -64,8 +65,6 @@
/*! Internal structure of basic handle. Also header of all other handles. /*! Internal structure of basic handle. Also header of all other handles.
* @note If you change here, you must also change the structs below: * @note If you change here, you must also change the structs below:
* @see struct cli_handle
* @see struct backend_handle
* This is the internal definition of a "Clixon handle" which in its external * This is the internal definition of a "Clixon handle" which in its external
* form is "clicon_handle" and is used in most Clixon API calls. * form is "clicon_handle" and is used in most Clixon API calls.
* Some details: * Some details:
@ -77,11 +76,17 @@
* Alternatively, these could be accessed via clicon_conf_xml() * Alternatively, these could be accessed via clicon_conf_xml()
* 3) ch_data accessed via clicon_data() is more general purpose for any data. * 3) ch_data accessed via clicon_data() is more general purpose for any data.
* that is, not only strings. And has separate namespace from options. * that is, not only strings. And has separate namespace from options.
* 4) ch_db_elmnt. Only reason it is not in ch_data is its own namespace and
* need to dump all hashes
* XXX: put ch_stream under ch_data
* @see struct cli_handle
* @see struct backend_handle
*/ */
struct clicon_handle { struct clicon_handle {
int ch_magic; /* magic (HDR) */ int ch_magic; /* magic (HDR) */
clicon_hash_t *ch_copt; /* clicon option list (HDR) */ clicon_hash_t *ch_copt; /* clicon option list (HDR) */
clicon_hash_t *ch_data; /* internal clicon data (HDR) Unclear why two? */ clicon_hash_t *ch_data; /* internal clicon data (HDR) */
clicon_hash_t *ch_db_elmnt; /* xml datastore element cache data */
event_stream_t *ch_stream; /* notification streams, see clixon_stream.[ch] */ event_stream_t *ch_stream; /* notification streams, see clixon_stream.[ch] */
}; };
@ -114,6 +119,10 @@ clicon_handle_init0(int size)
clicon_handle_exit((clicon_handle)ch); clicon_handle_exit((clicon_handle)ch);
goto done; goto done;
} }
if ((ch->ch_db_elmnt = hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
h = (clicon_handle)ch; h = (clicon_handle)ch;
done: done:
return h; return h;
@ -140,6 +149,7 @@ clicon_handle_init(void)
int int
clicon_handle_exit(clicon_handle h) clicon_handle_exit(clicon_handle h)
{ {
int retval = -1;
struct clicon_handle *ch = handle(h); struct clicon_handle *ch = handle(h);
clicon_hash_t *ha; clicon_hash_t *ha;
@ -147,9 +157,13 @@ clicon_handle_exit(clicon_handle h)
hash_free(ha); hash_free(ha);
if ((ha = clicon_data(h)) != NULL) if ((ha = clicon_data(h)) != NULL)
hash_free(ha); hash_free(ha);
if ((ha = clicon_db_elmnt(h)) != NULL)
hash_free(ha);
stream_delete_all(h, 1); stream_delete_all(h, 1);
free(ch); free(ch);
return 0; retval = 0;
return retval;
} }
/*! Check struct magic number for sanity checks /*! Check struct magic number for sanity checks
@ -188,6 +202,17 @@ clicon_data(clicon_handle h)
return ch->ch_data; return ch->ch_data;
} }
/*! Return clicon db_elmnt (hash-array) given a handle.
* @param[in] h Clicon handle
*/
clicon_hash_t *
clicon_db_elmnt(clicon_handle h)
{
struct clicon_handle *ch = handle(h);
return ch->ch_db_elmnt;
}
/*! Return stream hash-array given a clicon handle. /*! Return stream hash-array given a clicon handle.
* @param[in] h Clicon handle * @param[in] h Clicon handle
*/ */

View file

@ -62,11 +62,12 @@
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
#include "clixon_xml_db.h" #include "clixon_datastore.h"
#include "clixon_nacm.h" #include "clixon_nacm.h"
/*! Match nacm access operations according to RFC8341 3.4.4. /*! Match nacm access operations according to RFC8341 3.4.4.

View file

@ -61,6 +61,7 @@
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"

View file

@ -68,9 +68,8 @@
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_plugin.h" #include "clixon_data.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
@ -660,464 +659,3 @@ clicon_quiet_mode_set(clicon_handle h, int val)
return clicon_option_int_set(h, "CLICON_QUIET", val); return clicon_option_int_set(h, "CLICON_QUIET", val);
} }
/*! Get YANG specification for application
* Must use hash functions directly since they are not strings.
*/
yang_spec *
clicon_dbspec_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "dbspec_yang", &len)) != NULL)
return *(yang_spec **)p;
return NULL;
}
/*! Set yang specification for application
* ys must be a malloced pointer
*/
int
clicon_dbspec_yang_set(clicon_handle h,
struct yang_spec *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "dbspec_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
/*! Get NACM (rfc 8341) XML parse tree if external not in std xml config
* @param[in] h Clicon handle
* @retval xn XML NACM tree, or NULL
* @note only used if config option CLICON_NACM_MODE is external
* @see clicon_nacm_ext_set
*/
cxobj *
clicon_nacm_ext(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "nacm_xml", &len)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set NACM (rfc 8341) external XML parse tree, free old if any
* @param[in] h Clicon handle
* @param[in] xn XML Nacm tree
* @note only used if config option CLICON_NACM_MODE is external
* @see clicon_nacm_ext
*/
int
clicon_nacm_ext_set(clicon_handle h,
cxobj *xn)
{
clicon_hash_t *cdat = clicon_data(h);
cxobj *xo;
if ((xo = clicon_nacm_ext(h)) != NULL)
xml_free(xo);
/* It is the pointer to xn that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "nacm_xml", &xn, sizeof(xn)) == NULL)
return -1;
return 0;
}
#if 1 /* Temporary function until "Top-level Yang symbol cannot be called "config"" is fixed */
/*! Get YANG specification for clixon config
* Must use hash functions directly since they are not strings.
*/
yang_spec *
clicon_config_yang(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "control_yang", &len)) != NULL)
return *(yang_spec **)p;
return NULL;
}
/*! Set yang specification for control
* ys must be a malloced pointer
*/
int
clicon_config_yang_set(clicon_handle h,
struct yang_spec *ys)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to ys that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "control_yang", &ys, sizeof(ys)) == NULL)
return -1;
return 0;
}
#endif
/*! Get YANG specification for Clixon system options and features
* Must use hash functions directly since they are not strings.
* Example: features are typically accessed directly in the config tree.
*/
cxobj *
clicon_conf_xml(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "clixon_conf", &len)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set YANG specification for Clixon system options and features
* ys must be a malloced pointer
*/
int
clicon_conf_xml_set(clicon_handle h,
cxobj *x)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to x that should be copied by hash,
* so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "clixon_conf", &x, sizeof(x)) == NULL)
return -1;
return 0;
}
/*! Get xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
plghndl_t
clicon_xmldb_plugin_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *p;
if ((p = hash_value(cdat, "xmldb_plugin", &len)) != NULL)
return *(plghndl_t*)p;
return NULL;
}
/*! Set xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
int
clicon_xmldb_plugin_set(clicon_handle h,
plghndl_t handle)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "xmldb_plugin", &handle, sizeof(void*)) == NULL)
return -1;
return 0;
}
/*! Get XMLDB API struct pointer
* @param[in] h Clicon handle
* @retval xa XMLDB API struct
* @note xa is really of type struct xmldb_api*
*/
void *
clicon_xmldb_api_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *xa;
if ((xa = hash_value(cdat, "xmldb_api", &len)) != NULL)
return *(void**)xa;
return NULL;
}
/*! Set or reset XMLDB API struct pointer
* @param[in] h Clicon handle
* @param[in] xa XMLDB API struct
* @note xa is really of type struct xmldb_api*
*/
int
clicon_xmldb_api_set(clicon_handle h,
void *xa)
{
clicon_hash_t *cdat = clicon_data(h);
/* It is the pointer to xa_api that should be copied by hash,
so we send a ptr to the ptr to indicate what to copy.
*/
if (hash_add(cdat, "xmldb_api", &xa, sizeof(void*)) == NULL)
return -1;
return 0;
}
/*! Get XMLDB storage handle
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
*/
void *
clicon_xmldb_handle_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
size_t len;
void *xh;
if ((xh = hash_value(cdat, "xmldb_handle", &len)) != NULL)
return *(void**)xh;
return NULL;
}
/*! 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_xmldb_handle_set(clicon_handle h,
void *xh)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "xmldb_handle", &xh, sizeof(void*)) == NULL)
return -1;
return 0;
}
/*! Get authorized user name
* @param[in] h Clicon handle
* @retval xh XMLDB storage handle. If not connected return NULL
*/
char *
clicon_username_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
return (char*)hash_value(cdat, "username", NULL);
}
/*! Set authorized user name
* @param[in] h Clicon handle
* @param[in] xh XMLDB storage handle. If NULL reset it
* @note Just keep note of it, dont allocate it or so.
*/
int
clicon_username_set(clicon_handle h,
void *username)
{
clicon_hash_t *cdat = clicon_data(h);
if (username == NULL)
return hash_del(cdat, "username");
return hash_add(cdat, "username", username, strlen(username)+1)==NULL?-1:0;
}
/*! Get backend daemon startup status
* @param[in] h Clicon handle
* @retval status Startup status
*/
enum startup_status
clicon_startup_status_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "startup_status", NULL)) != NULL)
return *(enum startup_status *)p;
return STARTUP_ERR;
}
/*! Set backend daemon startup status
* @param[in] h Clicon handle
* @param[in] status Startup status
* @retval 0 OK
* @retval -1 Error (when setting value)
*/
int
clicon_startup_status_set(clicon_handle h,
enum startup_status status)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "startup_status", &status, sizeof(status))==NULL)
return -1;
return 0;
}
/*! Get socket fd (ie backend server socket / restconf fcgx socket)
* @param[in] h Clicon handle
* @retval -1 No open socket
* @retval s Socket
*/
int
clicon_socket_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "socket", NULL)) == NULL)
return -1;
return *(int*)p;
}
/*! Set socket fd (ie backend server socket / restconf fcgx socket)
* @param[in] h Clicon handle
* @param[in] s Open socket (or -1 to close)
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_socket_set(clicon_handle h,
int s)
{
clicon_hash_t *cdat = clicon_data(h);
if (s == -1)
return hash_del(cdat, "socket");
return hash_add(cdat, "socket", &s, sizeof(int))==NULL?-1:0;
}
/*! Get module state cache
* @param[in] h Clicon handle
* @retval xms Module state cache XML tree
*/
cxobj *
clicon_module_state_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "module_state_cache", NULL)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set module state cache
* @param[in] h Clicon handle
* @param[in] xms Module state cache XML tree
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_module_state_set(clicon_handle h,
cxobj *xms)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "module_state_cache", &xms, sizeof(xms))==NULL)
return -1;
return 0;
}
/*! Get yang module changelog
* @param[in] h Clicon handle
* @retval xch Module revision changelog XML tree
* @see draft-wang-netmod-module-revision-management-01
*/
cxobj *
clicon_xml_changelog_get(clicon_handle h)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if ((p = hash_value(cdat, "xml-changelog", NULL)) != NULL)
return *(cxobj **)p;
return NULL;
}
/*! Set xml module changelog
* @param[in] h Clicon handle
* @param[in] s Module revision changelog XML tree
* @retval 0 OK
* @retval -1 Error
* @see draft-wang-netmod-module-revision-management-01
*/
int
clicon_xml_changelog_set(clicon_handle h,
cxobj *xchlog)
{
clicon_hash_t *cdat = clicon_data(h);
if (hash_add(cdat, "xml-changelog", &xchlog, sizeof(xchlog))==NULL)
return -1;
return 0;
}
/*! Get user clicon command-line options argv, argc (after --)
* @param[in] h Clicon handle
* @param[out] argc
* @param[out] argv
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_argv_get(clicon_handle h,
int *argc,
char ***argv)
{
clicon_hash_t *cdat = clicon_data(h);
void *p;
if (argc){
if ((p = hash_value(cdat, "argc", NULL)) == NULL)
return -1;
*argc = *(int*)p;
}
if (argv){
if ((p = hash_value(cdat, "argv", NULL)) == NULL)
return -1;
*argv = (char**)p;
}
return 0;
}
/*! Set clicon user command-line options argv, argc (after --)
* @param[in] h Clicon handle
* @param[in] prog argv[0] - the program name
* @param[in] argc Length of argv
* @param[in] argv Array of command-line options or NULL
* @retval 0 OK
* @retval -1 Error
* @note If argv=NULL deallocate allocated argv vector if exists.
*/
int
clicon_argv_set(clicon_handle h,
char *prgm,
int argc,
char **argv)
{
int retval = -1;
clicon_hash_t *cdat = clicon_data(h);
char **argvv = NULL;
size_t len;
/* add space for null-termination and argv[0] program name */
len = argc+2;
if ((argvv = calloc(len, sizeof(char*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
memcpy(argvv+1, argv, argc*sizeof(char*));
argvv[0] = prgm;
/* Note the value is the argv vector (which is copied) */
if (hash_add(cdat, "argv", argvv, len*sizeof(char*))==NULL)
goto done;
argc += 1;
if (hash_add(cdat, "argc", &argc, sizeof(argc))==NULL)
goto done;
retval = 0;
done:
if (argvv)
free(argvv);
return retval;
}

View file

@ -63,6 +63,7 @@
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_plugin.h" #include "clixon_plugin.h"
#include "clixon_string.h" #include "clixon_string.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"

View file

@ -78,6 +78,7 @@
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_stream.h" #include "clixon_stream.h"

View file

@ -687,6 +687,8 @@ xml_childvec_set(cxobj *x,
return 0; return 0;
} }
/*! Get the children of an XML node as an XML vector
*/
cxobj ** cxobj **
xml_childvec_get(cxobj *x) xml_childvec_get(cxobj *x)
{ {
@ -863,7 +865,7 @@ xml_wrap_all(cxobj *xp,
} }
/*! Wrap a new element above a single xml node (xc) with new tag /*! Wrap a new element above a single xml node (xc) with new tag
* Before: xp --> xc # specific child * Before: xp --> xc # specific child (xp can be NULL)
* After: xp --> xt(tag) --> xc * After: xp --> xt(tag) --> xc
* @param[in] xp Parent xml node * @param[in] xp Parent xml node
* @param[in] tag Name of new xml child * @param[in] tag Name of new xml child

View file

@ -65,6 +65,7 @@
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
#include "clixon_xml_changelog.h" #include "clixon_xml_changelog.h"

View file

@ -1,701 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2019 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 *****
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <syslog.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include <libgen.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/param.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_options.h"
#include "clixon_yang_module.h"
#include "clixon_xml_db.h"
/* Set to log get and put requests */
#define DEBUG 0
/*! Load an xmldb storage plugin according to filename
* If init function fails (not found, wrong version, etc) print a log and dont
* add it.
* @param[in] h Clicon handle
* @param[in] filename Actual filename including path
*/
int
xmldb_plugin_load(clicon_handle h,
char *filename)
{
int retval = -1;
char *dlerrcode;
plugin_init_t *initfun;
plghndl_t handle = NULL;
char *error;
struct xmldb_api *xa = NULL;
dlerror(); /* Clear any existing error */
if ((handle = dlopen(filename, RTLD_NOW|RTLD_GLOBAL)) == NULL) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlopen: %s", error ? error : "Unknown error");
goto done;
}
/* Try v1 */
initfun = dlsym(handle, XMLDB_PLUGIN_INIT_FN);
if ((dlerrcode = (char*)dlerror()) != NULL) {
clicon_log(LOG_WARNING, "Error when loading init function %s: %s",
XMLDB_PLUGIN_INIT_FN, dlerrcode);
goto fail;
}
if ((xa = initfun(XMLDB_API_VERSION)) == NULL) {
clicon_log(LOG_WARNING, "%s: failed when running init function %s: %s",
filename, XMLDB_PLUGIN_INIT_FN, errno?strerror(errno):"");
goto fail;
}
if (xa->xa_version != XMLDB_API_VERSION){
clicon_log(LOG_WARNING, "%s: Unexpected plugin version number: %d",
filename, xa->xa_version);
goto fail;
}
if (xa->xa_magic != XMLDB_API_MAGIC){
clicon_log(LOG_WARNING, "%s: Wrong plugin magic number: %x",
filename, xa->xa_magic);
goto fail;
}
/* Add plugin */
if (clicon_xmldb_plugin_set(h, handle) < 0)
goto done;
/* Add API */
if (clicon_xmldb_api_set(h, xa) < 0)
goto done;
clicon_log(LOG_DEBUG, "xmldb plugin %s loaded", filename);
retval = 0;
done:
if (retval < 0 && handle)
dlclose(handle);
return retval;
fail: /* plugin load failed, continue */
retval = 0;
goto done;
}
/*! Unload the xmldb storage plugin
* @param[in] h Clicon handle
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_plugin_unload(clicon_handle h)
{
int retval = -1;
plghndl_t handle;
struct xmldb_api *xa;
xmldb_handle xh;
char *error;
if ((handle = clicon_xmldb_plugin_get(h)) == NULL)
goto ok; /* OK, may not have been initialized */
/* If connected storage handle then disconnect */
if ((xh = clicon_xmldb_handle_get(h)) != NULL)
xmldb_disconnect(h); /* sets xmldb handle to NULL */
/* Deregister api */
if ((xa = clicon_xmldb_api_get(h)) != NULL){
/* Call plugin_exit */
if (xa->xa_plugin_exit_fn != NULL)
xa->xa_plugin_exit_fn();
/* Deregister API (it is allocated in plugin) */
clicon_xmldb_api_set(h, NULL);
}
/* Unload plugin */
dlerror(); /* Clear any existing error */
if (dlclose(handle) != 0) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error");
/* Just report no -1 return*/
}
ok:
retval = 0;
// done:
return retval;
}
/*! Validate database name
* @param[in] db Name of database
* @param[out] xret Return value as cligen buffer containing xml netconf return
* @retval 0 OK
* @retval -1 Failed validate, xret set to error
*/
int
xmldb_validate_db(const char *db)
{
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "startup") != 0 &&
strcmp(db, "tmp") != 0)
return -1;
return 0;
}
/*! Connect to a datastore plugin, allocate handle to be used in API calls
* @param[in] h Clicon handle
* @retval 0 OK
* @retval -1 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.
*/
int
xmldb_connect(clicon_handle h)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_connect_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = xa->xa_connect_fn()) == NULL)
goto done;
clicon_xmldb_handle_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
* @retval -1 Error
*/
int
xmldb_disconnect(clicon_handle h)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
clicon_debug(1, "%s", __FUNCTION__);
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_disconnect_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Already disconnected from datastore plugin");
goto done;
}
if (xa->xa_disconnect_fn(xh) < 0)
goto done;
clicon_xmldb_handle_set(h, NULL);
retval = 0;
done:
return retval;
}
/*! Get value of generic plugin option. Type of value is givenby context
* @param[in] h Clicon 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;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_getopt_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_getopt_fn(xh, optname, value);
done:
return retval;
}
/*! Set value of generic plugin option. Type of value is givenby context
* @param[in] h Clicon 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;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_setopt_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->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[in] config If set only configuration data, else also state
* @param[out] xret Single return XML tree. Free with xml_free()
* @param[out] msd If set, return modules-state differences
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt, NULL) < 0)
* err;
* xml_free(xt);
* @endcode
* @note if xvec is given, then purge tree, if not return whole tree.
* @see xpath_vec
*/
int
xmldb_get(clicon_handle h,
const char *db,
char *xpath,
int config,
cxobj **xret,
modstate_diff_t *msd)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_get_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_get_fn(xh, db, xpath, config, xret, msd);
#if DEBUG
if (retval == 0) {
cbuf *cb = cbuf_new();
clicon_xml2cbuf(cb, *xret, 0, 0);
clicon_log(LOG_WARNING, "%s: db:%s xpath:%s xml:%s",
__FUNCTION__, db, xpath, cbuf_get(cb));
cbuf_free(cb);
}
#endif
done:
return retval;
}
/*! Modify database given an xml tree and an operation
*
* @param[in] h CLICON handle
* @param[in] db running or candidate
* @param[in] op Top-level operation, can be superceded by other op in tree
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] username User name for nacm
* @param[out] cbret Initialized cligen buffer. On exit contains XML if retval == 0
* @retval 1 OK
* @retval 0 Failed, cbret contains error xml message
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* cxobj *xret = NULL;
* if (xml_parse_string("<a>17</a>", yspec, &xt) < 0)
* err;
* if ((ret = xmldb_put(xh, "running", OP_MERGE, xt, username, cbret)) < 0)
* err;
* if (ret==0)
* cbret contains netconf error message
* @endcode
* @note that you can add both config data and state data. In comparison,
* xmldb_get has a parameter to get config data only.
* @note if xret is non-null, it may contain error message
*
*/
int
xmldb_put(clicon_handle h,
const char *db,
enum operation_type op,
cxobj *xt,
char *username,
cbuf *cbret)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_put_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
#if DEBUG
{
cbuf *cb = cbuf_new();
if (xt)
if (clicon_xml2cbuf(cb, xt, 0, 0) < 0)
goto done;
clicon_log(LOG_WARNING, "%s: db:%s op:%d xml:%s", __FUNCTION__,
db, op, cbuf_get(cb));
cbuf_free(cb);
}
#endif
retval = xa->xa_put_fn(xh, db, op, xt, username, cbret);
done:
return retval;
}
/*! Copy database from db1 to db2
* @param[in] h Clicon handle
* @param[in] from Source database
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_copy(clicon_handle h,
const char *from,
const char *to)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_copy_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_copy_fn(xh, from, to);
done:
return retval;
}
/*! Lock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_lock(clicon_handle h,
const char *db,
int pid)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_lock_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_lock_fn(xh, db, pid);
done:
return retval;
}
/*! Unlock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
* Assume all sanity checks have been made
*/
int
xmldb_unlock(clicon_handle h,
const char *db)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_unlock_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_unlock_fn(xh, db);
done:
return retval;
}
/*! 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
*/
int
xmldb_unlock_all(clicon_handle h,
int pid)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_unlock_all_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_unlock_all_fn(xh, pid);
done:
return retval;
}
/*! 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
*/
int
xmldb_islocked(clicon_handle h,
const char *db)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_islocked_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_islocked_fn(xh, db);
done:
return retval;
}
/*! Check if db exists
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
xmldb_exists(clicon_handle h,
const char *db)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_exists_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_exists_fn(xh, db);
done:
return retval;
}
/*! Delete database. Remove file
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_delete(clicon_handle h,
const char *db)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_delete_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_delete_fn(xh, db);
done:
return retval;
}
/*! Create a database. Open database for writing.
* @param[in] h Clicon handle
* @param[in] db Database
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_create(clicon_handle h,
const char *db)
{
int retval = -1;
xmldb_handle xh;
struct xmldb_api *xa;
if ((xa = clicon_xmldb_api_get(h)) == NULL){
clicon_err(OE_DB, 0, "No xmldb plugin");
goto done;
}
if (xa->xa_create_fn == NULL){
clicon_err(OE_DB, 0, "No xmldb function");
goto done;
}
if ((xh = clicon_xmldb_handle_get(h)) == NULL){
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done;
}
retval = xa->xa_create_fn(xh, db);
done:
return retval;
}

View file

@ -85,6 +85,7 @@
#include "clixon_hash.h" #include "clixon_hash.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_plugin.h" #include "clixon_plugin.h"
#include "clixon_data.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_yang_type.h" #include "clixon_yang_type.h"
#include "clixon_yang_parse.h" #include "clixon_yang_parse.h"

View file

@ -72,6 +72,7 @@
#include "clixon_xpath_ctx.h" #include "clixon_xpath_ctx.h"
#include "clixon_xpath.h" #include "clixon_xpath.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_plugin.h" #include "clixon_plugin.h"
#include "clixon_netconf_lib.h" #include "clixon_netconf_lib.h"
#include "clixon_yang_module.h" #include "clixon_yang_module.h"
@ -157,60 +158,6 @@ yang_modules_revision(clicon_handle h)
return revision; return revision;
} }
/*! Get modules state cache associated with module_set_id, or NULL if none
* @param[in] h Clixon handle
* @param[in] msid Module set id. Cache stored per id
* @param[out] xms XML tree for module state cache. Note need to xml_dup it.
* @retval 0 OK, x is either NULL or set
* @retval -1 Error
*/
static int
modules_state_cache_get(clicon_handle h,
char *msid,
cxobj **xms)
{
cxobj *x; /* module state cache XML */
cxobj *xmsid; /* module state id of cache XML */
if ((x = clicon_module_state_get(h)) == NULL)
return 0;
if ((xmsid = xpath_first(x, "modules-state/module-set-id")) == NULL)
return 0;
if (strcmp(xml_body(xmsid), msid) == 0) /* return cache */
*xms = x;
return 0;
}
/*! Set modules state cache associated with msid, or NULL if none
* @param[in] h Clixon handle
* @param[in] msid Module set id. Cache stored per id
* @param[out] xms XML tree for module state cache. Note need to xml_dup it.
* @retval 0 OK
* @retval -1 Error
*/
int
modules_state_cache_set(clicon_handle h,
cxobj *msx)
{
int retval = -1;
cxobj *x; /* module state cache XML */
if ((x = clicon_module_state_get(h)) != NULL)
xml_free(x);
clicon_module_state_set(h, NULL);
if (msx == NULL)
goto ok;
/* Duplicate XML tree from original. */
if ((x = xml_dup(msx)) == NULL)
goto done;
clicon_module_state_set(h, x);
ok:
retval = 0;
done:
return retval;
}
/*! Actually build the yang modules state XML tree /*! Actually build the yang modules state XML tree
*/ */
static int static int
@ -295,11 +242,12 @@ yms_build(clicon_handle h,
done: done:
return retval; return retval;
} }
/*! Get modules state according to RFC 7895 /*! Get modules state according to RFC 7895
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] xpath XML Xpath * @param[in] xpath XML Xpath
* @param[in] brief Just name,revision and uri (no cache) * @param[in] brief Just name, revision and uri (no cache)
* @param[in,out] xret Existing XML tree, merge x into this * @param[in,out] xret Existing XML tree, merge x into this
* @retval -1 Error (fatal) * @retval -1 Error (fatal)
* @retval 0 OK * @retval 0 OK
@ -331,15 +279,15 @@ yang_modules_state_get(clicon_handle h,
cxobj **xret) cxobj **xret)
{ {
int retval = -1; int retval = -1;
cxobj *x = NULL; cxobj *x = NULL; /* Top tree, some juggling w top symbol */
char *msid; /* modules-set-id */ char *msid; /* modules-set-id */
cxobj *x1; cxobj *x1;
cbuf *cb = NULL; cbuf *cb = NULL;
msid = clicon_option_str(h, "CLICON_MODULE_SET_ID"); msid = clicon_option_str(h, "CLICON_MODULE_SET_ID");
if (!brief && modules_state_cache_get(h, msid, &x) < 0) if ((x = clicon_modst_cache_get(h, brief)) != NULL){
goto done; /* x is here: <modules-state>...
if (x != NULL){ /* Yes a cache (but no duplicate) */ * and x is original tree, need to copy */
if (xpath_first(x, "%s", xpath)){ if (xpath_first(x, "%s", xpath)){
if ((x1 = xml_dup(x)) == NULL) if ((x1 = xml_dup(x)) == NULL)
goto done; goto done;
@ -353,21 +301,32 @@ yang_modules_state_get(clicon_handle h,
clicon_err(OE_UNIX, 0, "clicon buffer"); clicon_err(OE_UNIX, 0, "clicon buffer");
goto done; goto done;
} }
/* Build a cb string: <modules-state>... */
if (yms_build(h, yspec, msid, brief, cb) < 0) if (yms_build(h, yspec, msid, brief, cb) < 0)
goto done; goto done;
/* Parse cb, x is on the form: <top><modules-state>... */
if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){ if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0) if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
goto done; goto done;
retval = 1; retval = 1;
goto done; goto done;
} }
if (!brief && modules_state_cache_set(h, x) < 0) /* move to fn above? */ if (xml_rootchild(x, 0, &x) < 0)
goto done;
/* x is now: <modules-state>... */
if (clicon_modst_cache_set(h, brief, x) < 0) /* move to fn above? */
goto done; goto done;
} }
if (x && netconf_trymerge(x, yspec, xret) < 0) if (x){
/* Wrap x (again) with new top-level node "top" which merge wants */
if ((x = xml_wrap(x, "top")) < 0)
goto done; goto done;
if (netconf_trymerge(x, yspec, xret) < 0)
goto done;
}
retval = 0; retval = 0;
done: done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (cb) if (cb)
cbuf_free(cb); cbuf_free(cb);
if (x) if (x)

View file

@ -9,7 +9,6 @@ fyang=$dir/ietf-ip.yang
# If set, enable debugging (of backend) # If set, enable debugging (of backend)
: ${clixon_util_datastore:=clixon_util_datastore} : ${clixon_util_datastore:=clixon_util_datastore}
: ${clixon_datastore_lib:=/usr/local/lib/xmldb/text.so}
cat <<EOF > $fyang cat <<EOF > $fyang
module ietf-ip{ module ietf-ip{
@ -62,8 +61,7 @@ if [ ! -d $mydir ]; then
fi fi
rm -rf $mydir/* rm -rf $mydir/*
# XXX static link conf="-d candidate -b $mydir -y $dir/ietf-ip.yang"
conf="-d candidate -b $mydir -p $clixon_datastore_lib -y $dir/ietf-ip.yang"
new "datastore init" new "datastore init"
expectfn "$clixon_util_datastore $conf init" 0 "" expectfn "$clixon_util_datastore $conf init" 0 ""
@ -154,7 +152,7 @@ new "datastore create leaf"
expectfn "$clixon_util_datastore $conf put create <config><x><y><a>1</a><b>3</b><c>newentry</c></y></x></config>" 0 "" expectfn "$clixon_util_datastore $conf put create <config><x><y><a>1</a><b>3</b><c>newentry</c></y></x></config>" 0 ""
new "datastore other db init" new "datastore other db init"
expectfn "$clixon_util_datastore -d kalle -b $mydir -p $clixon_datastore_lib -y $dir/ietf-ip.yang init" 0 "" expectfn "$clixon_util_datastore -d kalle -b $mydir -y $dir/ietf-ip.yang init" 0 ""
new "datastore other db copy" new "datastore other db copy"
expectfn "$clixon_util_datastore $conf copy kalle" 0 "" expectfn "$clixon_util_datastore $conf copy kalle" 0 ""

View file

@ -63,7 +63,7 @@
#include <clixon/clixon.h> #include <clixon/clixon.h>
/* Command line options to be passed to getopt(3) */ /* Command line options to be passed to getopt(3) */
#define DATASTORE_OPTS "hDd:p:b:y:" #define DATASTORE_OPTS "hDd:b:y:"
/*! usage /*! usage
*/ */
@ -76,7 +76,6 @@ usage(char *argv0)
"\t-D\t\tDebug\n" "\t-D\t\tDebug\n"
"\t-d <db>\t\tDatabase name. Default: running. Alt: candidate,startup\n" "\t-d <db>\t\tDatabase name. Default: running. Alt: candidate,startup\n"
"\t-b <dir>\tDatabase directory. Mandatory\n" "\t-b <dir>\tDatabase directory. Mandatory\n"
"\t-p <plugin>\tDatastore plugin. Mandatory\n"
"\t-y <file>\tYang file. Mandatory\n" "\t-y <file>\tYang file. Mandatory\n"
"and command is either:\n" "and command is either:\n"
"\tget [<xpath>]\n" "\tget [<xpath>]\n"
@ -103,7 +102,6 @@ main(int argc, char **argv)
clicon_handle h; clicon_handle h;
char *argv0; char *argv0;
char *db = "running"; char *db = "running";
char *plugin = NULL;
char *cmd = NULL; char *cmd = NULL;
yang_spec *yspec = NULL; yang_spec *yspec = NULL;
char *yangfilename = NULL; char *yangfilename = NULL;
@ -138,11 +136,6 @@ main(int argc, char **argv)
usage(argv0); usage(argv0);
db = optarg; db = optarg;
break; break;
case 'p': /* datastore plugin */
if (!optarg)
usage(argv0);
plugin = optarg;
break;
case 'b': /* db directory */ case 'b': /* db directory */
if (!optarg) if (!optarg)
usage(argv0); usage(argv0);
@ -165,10 +158,6 @@ main(int argc, char **argv)
if (argc < 1) if (argc < 1)
usage(argv0); usage(argv0);
cmd = argv[0]; cmd = argv[0];
if (plugin == NULL){
clicon_err(OE_DB, 0, "Missing plugin -p option");
goto done;
}
if (dbdir == NULL){ if (dbdir == NULL){
clicon_err(OE_DB, 0, "Missing dbdir -b option"); clicon_err(OE_DB, 0, "Missing dbdir -b option");
goto done; goto done;
@ -177,9 +166,6 @@ main(int argc, char **argv)
clicon_err(OE_YANG, 0, "Missing yang filename -y option"); clicon_err(OE_YANG, 0, "Missing yang filename -y option");
goto done; goto done;
} }
/* Load datastore plugin */
if (xmldb_plugin_load(h, plugin) < 0)
goto done;
/* Connect to plugin to get a handle */ /* Connect to plugin to get a handle */
if (xmldb_connect(h) < 0) if (xmldb_connect(h) < 0)
goto done; goto done;
@ -189,12 +175,8 @@ main(int argc, char **argv)
/* Parse yang spec from given file */ /* Parse yang spec from given file */
if (yang_spec_parse_file(h, yangfilename, yspec) < 0) if (yang_spec_parse_file(h, yangfilename, yspec) < 0)
goto done; goto done;
/* Set database directory option */ clicon_option_str_set(h, "CLICON_XMLDB_DIR", dbdir);
if (xmldb_setopt(h, "dbdir", dbdir) < 0) clicon_dbspec_yang_set(h, yspec);
goto done;
/* Set yang spec option */
if (xmldb_setopt(h, "yangspec", yspec) < 0)
goto done;
if (strcmp(cmd, "get")==0){ if (strcmp(cmd, "get")==0){
if (argc != 1 && argc != 2) if (argc != 1 && argc != 2)
usage(argv0); usage(argv0);
@ -309,8 +291,6 @@ main(int argc, char **argv)
} }
if (xmldb_disconnect(h) < 0) if (xmldb_disconnect(h) < 0)
goto done; goto done;
if (xmldb_plugin_unload(h) < 0)
goto done;
done: done:
if (cbret) if (cbret)
cbuf_free(cbret); cbuf_free(cbret);

View file

@ -354,14 +354,15 @@ module clixon-config {
type string; type string;
mandatory true; mandatory true;
description description
"Directory where \"running\", \"candidate\" and \"startup\" are placed"; "Directory where \"running\", \"candidate\" and \"startup\" are placed.";
} }
leaf CLICON_XMLDB_PLUGIN { leaf CLICON_XMLDB_PLUGIN {
type string; type string;
mandatory true; status obsolete;
description description
"XMLDB datastore plugin filename "XMLDB datastore plugin filename
(see datastore/ and clixon_xml_db.[ch])"; (see datastore/ and clixon_xml_db.[ch])
Obsolete: Merged with libclixon in 3.10";
} }
leaf CLICON_XMLDB_CACHE { leaf CLICON_XMLDB_CACHE {
type boolean; type boolean;
@ -404,14 +405,6 @@ module clixon-config {
If CLICON_XML_CHANGELOG is true, Clixon If CLICON_XML_CHANGELOG is true, Clixon
reads the module changelog from this file."; reads the module changelog from this file.";
} }
leaf CLICON_USE_STARTUP_CONFIG {
type int32;
default 0;
description
"Enabled uses \"startup\" configuration on boot. It is called
startup_db and exists in XMLDB_DIR.
NOTE: Obsolete with 1.3.3 and CLICON_STARTUP_MODE";
}
leaf CLICON_STARTUP_MODE { leaf CLICON_STARTUP_MODE {
type startup_mode; type startup_mode;
description "Which method to boot/start clicon backend"; description "Which method to boot/start clicon backend";