* NACM extension (RFC8341)

* NACM module support (RFC8341 A1+A2)
   * Recovery user "_nacm_recovery" added.
     * Example use is restconf PUT when NACM edit-config is permitted, then automatic commit and discard are permitted using recovery user.
   * Example user changed adm1 to andy to comply with RFC8341 example

 * Yang code upgrade (RFC7950)
   * RPC method input parameters validated
     * see https://github.com/clicon/clixon/issues/4
* Correct XML namespace handling
   * XML multiple modules was based on "loose" semantics so that yang modules were found by iterating thorugh namespaces until a match was made. This did not adhere to proper [XML namespace handling](https://www.w3.org/TR/2009/REC-xml-names-20091208), and causes problems with overlapping names and false positives. Below see XML accepted (but wrong), and correct namespace declaration:
```
      <rpc><my-own-method></rpc> # Wrong but accepted
      <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"> # Correct
        <my-own-method xmlns="http://example.net/me/my-own/1.0">
      </rpc>
```
   * To keep old loose semantics set config option CLICON_XML_NS_ITERATE (true by default)
   * XML to JSON translator support for mapping xmlns attribute to module name prefix.
   * Default namespace is still "urn:ietf:params:xml:ns:netconf:base:1.0"
   * See https://github.com/clicon/clixon/issues/49
* Changed all make tags --> make TAGS
* Keyvalue datastore removed (it has been disabled since 3.3.3)
* debug rpc added in example application (should be in clixon-config).
This commit is contained in:
Olof hagsand 2018-12-16 19:46:26 +01:00
parent e5c0b06cf9
commit ae1af8da9e
63 changed files with 1852 additions and 3492 deletions

View file

@ -49,7 +49,6 @@ INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
with_restconf = @with_restconf@
with_keyvalue = @with_keyvalue@
SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
@ -117,5 +116,5 @@ distclean: clean
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
tags:
TAGS:
find $(srcdir) -name '*.[chyl]' -print | etags -

View file

@ -2,8 +2,7 @@
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 a key-value plugin based on qdbm and a plain
text-file datastore.
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.
@ -38,7 +37,7 @@ int xmldb_create(clicon_handle h, char *db);
To use the API, a client needs the following:
- A clicon handle.
- A datastore plugin, such as a text.so or keyvalue.so. These are normally built and installed at Clixon make.
- 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.

View file

@ -31,14 +31,6 @@
***** END LICENSE BLOCK *****
* Examples:
./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/example/yang -m ietf-ip get /
sudo ./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/example/yang -m ietf-ip put merge /interfaces/interface=eth66 '<config>eth66</config>'
sudo ./datastore_client -d candidate -b /usr/local/var/example -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/example/yang -m ietf-ip put merge / '<config><interfaces><interface><name>eth0</name><enabled>true</enabled></interface></interfaces></config>'
*/
#ifdef HAVE_CONFIG_H
@ -247,6 +239,7 @@ main(int argc, char **argv)
clicon_err(OE_DB, 0, "Unrecognized operation: %s", argv[1]);
usage(argv0);
}
_CLICON_XML_NS_ITERATE = 1;
if (xml_parse_string(argv[2], NULL, &xt) < 0)
goto done;
if (xml_rootchild(xt, 0, &xt) < 0)

View file

@ -1,98 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the Apache License version 2 or the GPL.
#
# ***** END LICENSE BLOCK *****
#
VPATH = @srcdir@
prefix = @prefix@
datarootdir = @datarootdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
dbdir = @prefix@/db
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
sysconfdir = @sysconfdir@
VPATH = @srcdir@
CC = @CC@
CFLAGS = @CFLAGS@ -rdynamic -fPIC
INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
DATASTORE = keyvalue
CPPFLAGS = @CPPFLAGS@
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
PLUGIN = $(DATASTORE).so
SRC = clixon_keyvalue.c clixon_qdb.c clixon_chunk.c
OBJS = $(SRC:.c=.o)
all: $(PLUGIN)
$(PLUGIN): $(SRC)
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -o $@ -lc $^ $(LIBS)
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,794 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2, indicate
your decision by deleting the provisions above and replace them with the
notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
/* Error handling: dont use clicon_err, treat as unix system calls. That is,
ensure errno is set and return -1/NULL */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/types.h>
/* clicon */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "clixon_chunk.h"
/*
* The chunk head array for the predefined chunk sizes.
*/
static chunk_head_t chunk_heads[CHUNK_HEADS];
/*
* Did we initialize the chunk heads yet?
*/
static int chunk_initialized = 0;
/*
* The pagesize of this system
*/
static int chunk_pagesz;
/*
* List of chunk groups
*/
static chunk_group_t *chunk_grp;
/*
* Hack to tell unchunk() not to remove chunk_group if empty
*/
static int dont_unchunk_group;
/*
* Initialize chunk library
*/
static void
chunk_initialize ()
{
int pgs;
register int idx;
chunk_pagesz = getpagesize();
bzero (&chunk_heads, sizeof(chunk_heads));
for (idx = 0; idx < CHUNK_HEADS; idx++) {
chunk_head_t *chead = &chunk_heads[idx];
/*
* Calculate the size of a block
*/
pgs = (0x01lu << (CHUNK_BASE + idx)) / chunk_pagesz;
if (pgs == 0)
pgs = 1;
chead->ch_blksz = pgs * chunk_pagesz;
/*
* Chunks per block is 1 for all size above a page. For sizes
* (including the chunk header) less than a page it's as many
* as fits
*/
chead->ch_nchkperblk = chead->ch_blksz / (0x01lu << (CHUNK_BASE + idx));
/*
* Size of each chunk is:
* (size + chnkhdr) * ncnkperblk = blksiz - blkhdr
*/
chead->ch_size =
(chead->ch_blksz / chead->ch_nchkperblk)
- sizeof(chunk_t);
}
/* Zero misc variables */
chunk_grp = NULL;
dont_unchunk_group = 0;
chunk_initialized = 1;
}
/*
* chunk_new_block() - Allocate new block, initialize it and it's chunks.
*/
static int
chunk_new_block (chunk_head_t *chead)
{
register int idx;
register char *c;
chunk_block_t *blk;
chunk_t *cnk;
/* Map block header mem */
blk = (chunk_block_t *)
mmap(NULL, sizeof(chunk_block_t),
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (blk == MAP_FAILED)
return -1;
memset((void *)blk, 0, sizeof(*blk));
/* Allocate chunk block */
blk->cb_blk = (void *)
mmap(NULL, chead->ch_blksz,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (blk->cb_blk == MAP_FAILED) {
munmap(blk, chead->ch_blksz);
return -1;
}
memset(blk->cb_blk, 0, chead->ch_blksz);
/* Initialize chunk header */
blk->cb_head = chead;
INSQ(blk, chead->ch_blks);
chead->ch_nblks++;
/* Initialize chunks */
c = ((char *)blk->cb_blk);
for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
cnk = (chunk_t *)c;
cnk->c_blk = blk;
INSQ(cnk, chead->ch_free);
chead->ch_nfree++;
c += (chead->ch_size + sizeof(chunk_t));
}
return 0;
}
/*
* chunk_release_block() - Unqueue a block, it's chunks and free mem
*/
static void
chunk_release_block(chunk_block_t *cblk)
{
int idx;
char *c;
chunk_t *cnk;
chunk_head_t *chead;
chead = cblk->cb_head;
/*
* Dequeue block
*/
DELQ(cblk, chead->ch_blks, chunk_block_t *);
chead->ch_nblks--;
/*
* Dequeue all chunks in the block
*/
c = (char *)cblk->cb_blk;
for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
cnk = (chunk_t *)c;
DELQ(cnk, chead->ch_free, chunk_t *);
chead->ch_nfree--;
c += (chead->ch_size + sizeof(chunk_t));
}
/*
* Free block
*/
munmap((void *)cblk->cb_blk, chead->ch_blksz);
munmap((void *)cblk, sizeof(*cblk));
}
/*
* chunk_alloc() - Map new chunk of memory
*/
static void *
chunk_alloc(size_t len)
{
register int idx;
chunk_head_t *chead;
chunk_t *cnk;
if (!len)
return (void *)NULL;
/* Find sufficient sized block head */
for (idx = 0; idx < CHUNK_HEADS; idx++)
if (chunk_heads[idx].ch_size >= len)
break;
/* Too large chunk? */
if (idx >= CHUNK_HEADS) {
errno = ENOMEM;
return (void *)NULL;
}
chead = &chunk_heads[idx];
/* Get new block if necessary */
if (!chead->ch_nfree)
if (chunk_new_block(chead))
return (void *)NULL;
/* Move a free chunk to the in-use list */
cnk = chead->ch_free;
DELQ(cnk, chead->ch_free, chunk_t *);
chead->ch_nfree--;
INSQ(cnk, chead->ch_cnks);
/* Add reference to the corresponding block */
cnk->c_blk->cb_ref++;
#ifdef CHUNK_DIAG
/* Clear diag info */
bzero((void *)&cnk->c_diag, sizeof(cnk->c_diag));
#endif /* CHUNK_DIAG */
/* Return pointer to first byte after the chunk header */
return (void *) (cnk + 1);
}
/*
* chunk() - Map new chunk of memory in group
*/
void *
#ifdef CHUNK_DIAG
_chunk(size_t len, const char *name, const char *file, int line)
#else
chunk(size_t len, const char *name)
#endif
{
int newgrp = 0;
void *ptr = NULL;
chunk_t *cnk;
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
chunk_grpent_t *ent = NULL;
/* Make sure chunk_heads are initialized */
if (!chunk_initialized)
chunk_initialize();
if (!len)
return (void *)NULL;
/* Get actual chunk
*/
ptr = chunk_alloc(len);
if (!ptr)
goto error;
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
#ifdef CHUNK_DIAG
/* Store who reuested us
*/
cnk->c_diag.cd_file = file;
cnk->c_diag.cd_line = line;
#endif /* CHUNK_DIAG */
/* No name given. Get an ungrouped chunk
*/
if (!name)
return ptr;
/* Try to find already existing entry
*/
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp(tmp->cg_name, name)) {
grp = tmp;
break;
}
tmp = NEXTQ(chunk_group_t *, tmp);
} while (tmp != chunk_grp);
}
/* New group.
*/
if ( !grp ) {
grp = (chunk_group_t *) chunk_alloc(sizeof(chunk_group_t));
if (!grp)
goto error;
bzero(grp, sizeof(chunk_group_t));
grp->cg_name = (char *) chunk_alloc(strlen(name) + 1);
if (!grp->cg_name)
goto error;
bcopy(name, grp->cg_name, strlen(name)+1);
newgrp = 1;
}
/* Get new entry.
*/
ent = (chunk_grpent_t *) chunk_alloc(sizeof(chunk_grpent_t));
if (!ent)
goto error;
bzero(ent, sizeof(chunk_grpent_t));
/* Now put everything together
*/
cnk->c_grpent = ent;
ent->ce_cnk = cnk;
ent->ce_grp = grp;
INSQ(ent, grp->cg_ent);
if (newgrp)
INSQ(grp, chunk_grp);
return (ptr);
error:
if (grp && newgrp) {
if (grp->cg_name)
unchunk(grp->cg_name);
unchunk(grp);
}
if (ent)
unchunk(ent);
if (ptr)
unchunk(ptr);
return (void *) NULL;
}
/*
* rechunk() - Resize previously allocated chunk.
*/
void *
#ifdef CHUNK_DIAG
_rechunk(void *ptr, size_t len, const char *name, const char *file, int line)
#else
rechunk(void *ptr, size_t len, const char *name)
#endif
{
int idx;
void *new;
chunk_t *cnk;
chunk_t *newcnk;
chunk_head_t *chead;
chunk_head_t *newchead;
/* No previoud chunk, get new
*/
if (!ptr) {
#ifdef CHUNK_DIAG
return _chunk(len, name, file, line);
#else
return chunk(len, name);
#endif
}
/* Zero length, free chunk
*/
if (len == 0) {
unchunk(ptr);
return (void *) NULL;
}
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
chead = cnk->c_blk->cb_head;
/* Find sufficient sized block head
*/
for (idx = 0; idx < CHUNK_HEADS; idx++)
if (chunk_heads[idx].ch_size >= len)
break;
/* Too large chunk? */
if (idx >= CHUNK_HEADS) {
errno = ENOMEM;
return (void *)NULL;
}
/* Check if chunk size remains unchanged
*/
if (chunk_heads[idx].ch_size == chead->ch_size)
return (ptr);
/* Get new chunk
*/
#ifdef CHUNK_DIAG
new = _chunk(len, name, file, line);
#else
new = chunk(len, name);
#endif
if (!new)
return (void *) NULL;
newcnk = (chunk_t *) (((char *)new) - sizeof(chunk_t));
newchead = newcnk->c_blk->cb_head;
/* Copy contents to new chunk
*/
bcopy(ptr, new, MIN(newchead->ch_size, chead->ch_size));
/* Free old chunk
*/
unchunk(ptr);
return (new);
}
/*
* unchunk() - Release chunk
*/
void
unchunk(void *ptr)
{
chunk_t *cnk;
chunk_head_t *chead;
chunk_block_t *cblk;
chunk_grpent_t *ent;
chunk_group_t *grp;
if (!chunk_initialized)
return;
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
cblk = cnk->c_blk;
chead = cblk->cb_head;
/* Move chunk back to free list
*/
DELQ(cnk, chead->ch_cnks, chunk_t *);
INSQ(cnk, chead->ch_free);
chead->ch_nfree++;
/* If chunk is grouped, remove from group.
*/
ent = cnk->c_grpent;
if (ent) {
grp = ent->ce_grp;
DELQ(ent, grp->cg_ent, chunk_grpent_t *);
unchunk(ent);
cnk->c_grpent = NULL;
/* Group empty? */
if (!dont_unchunk_group && !grp->cg_ent) {
DELQ(grp, chunk_grp, chunk_group_t *);
unchunk(grp->cg_name);
unchunk(grp);
}
}
/* Check block refs is nil, if so free it
*/
cblk->cb_ref--;
if (cblk->cb_ref == 0)
chunk_release_block (cblk);
}
/*
* unchunk_group() - Release all group chunks.
*/
void
unchunk_group(const char *name)
{
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
chunk_t *cnk;
if (!chunk_initialized)
return;
/* Try to find already existing entry
*/
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp(tmp->cg_name, name)) {
grp = tmp;
break;
}
tmp = NEXTQ(chunk_group_t *, tmp);
} while (tmp != chunk_grp);
}
if (!grp)
return;
/* Walk through all chunks in group an free them
*/
dont_unchunk_group = 1;
while (grp->cg_ent) {
cnk = grp->cg_ent->ce_cnk;
unchunk((chunk_t *)(((char *)cnk) + sizeof(chunk_t)));
}
dont_unchunk_group = 0;
/* Remove group from list and free it
*/
DELQ(grp, chunk_grp, chunk_group_t *);
unchunk(grp->cg_name);
unchunk(grp);
}
/*
* chunkdup() - Copy block of data to a new chunk of memory in group
*/
void *
#ifdef CHUNK_DIAG
_chunkdup(const void *ptr, size_t len, const char *name, const char *file, int line)
#else
chunkdup(const void *ptr, size_t len, const char *name)
#endif
{
void *new;
/* No input data or no length
*/
if (!ptr || len <= 0)
return (void *)NULL;
/* Get new chunk
*/
#ifdef CHUNK_DIAG
new = _chunk(len, name, file, line);
#else
new = chunk(len, name);
#endif
if (!new)
return (void *)NULL;
/* Copy data to new chunk
*/
memcpy(new, ptr, len);
return (new);
}
/*
* chunksize() - Return size of memory chunk.
*/
size_t
chunksize(void *ptr)
{
chunk_t *cnk;
chunk_head_t *chead;
chunk_block_t *cblk;
if (!chunk_initialized)
return -1;
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
cblk = cnk->c_blk;
chead = cblk->cb_head;
return chead->ch_size;
}
/*
* chunk_strncat() - Concatenate 'n' characters to a chunk allocated string. If
* 'n' is zero, do the whole src string.
*
*/
char *
#ifdef CHUNK_DIAG
_chunk_strncat(const char *dst, const char *src, size_t n, const char *name,
const char *file, int line)
#else
chunk_strncat(const char *dst, const char *src, size_t n, const char *name)
#endif
{
size_t len;
char *new;
void *ptr = (void *)dst;
if (n == 0) /* zero length means cat whole string */
n = strlen(src);
len = (dst ? strlen(dst) : 0) + n + 1;
#ifdef CHUNK_DIAG
ptr = _rechunk(ptr, len, name, file, line);
#else
ptr = rechunk(ptr, len, name);
#endif
if (ptr == NULL)
return NULL;
new = (char *)ptr;
new += strlen(new);
while (n-- > 0 && *src)
*new++ = *src++;
*new = '\0';
return (char *)ptr;
}
/*
* chunk_sprintf() - Format string into new chunk.
*/
char *
#ifdef CHUNK_DIAG
_chunk_sprintf(const char *name, const char *file,
int line, const char *fmt, ...)
#else
chunk_sprintf(const char *name, char *fmt, ...)
#endif
{
size_t len;
char *str;
va_list args;
/* Calculate formatted string length */
va_start(args, fmt);
len = vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args);
/* get chunk */
#ifdef CHUNK_DIAG
str = _chunk(len, name, file, line);
#else
str = chunk(len, name);
#endif
if (str == NULL)
return NULL;
/* Format string */
va_start(args, fmt);
len = vsnprintf(str, len, fmt, args);
va_end(args);
return str;
}
#ifdef CHUNK_DIAG
/*
* chunk_check() - Report all non-freed chunk for given group (if any)
*/
void
chunk_check(FILE *fout, const char *name)
{
int idx;
chunk_t *cnk;
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
chunk_grpent_t *ent;
if (!chunk_initialized)
return;
/* No name given, walk through everything
*/
if (name == (const char *)NULL) {
for (idx = 0; idx < CHUNK_HEADS; idx++) {
chunk_head_t *chead = &chunk_heads[idx];
cnk = chead->ch_cnks;
if (cnk == (chunk_t *)NULL)
continue;
do {
/* If no file name it's an internal chunk */
if (cnk->c_diag.cd_file)
clicon_debug(0,
"%s:%d,\t%zu bytes (%p), group \"%s\"\n",
cnk->c_diag.cd_file,
cnk->c_diag.cd_line,
cnk->c_blk->cb_head->ch_size,
(cnk +1),
cnk->c_grpent ?
cnk->c_grpent->ce_grp->cg_name :
"NULL");
cnk = NEXTQ(chunk_t *, cnk);
} while (cnk != chead->ch_cnks);
}
}
/* Walk through group
*/
else {
/* Try to find already existing entry
*/
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp(tmp->cg_name, name)) {
grp = tmp;
break;
}
tmp = NEXTQ(chunk_group_t *, tmp);
} while (tmp != chunk_grp);
}
if (!grp)
return;
ent = grp->cg_ent;
do {
cnk = ent->ce_cnk;
fprintf(fout ? fout : stdout,
"%s:%d,\t%zu bytes (%p), group \"%s\"\n",
cnk->c_diag.cd_file,
cnk->c_diag.cd_line,
cnk->c_blk->cb_head->ch_size,
(cnk +1),
cnk->c_grpent ?
cnk->c_grpent->ce_grp->cg_name :
"NULL");
ent = NEXTQ(chunk_grpent_t *, ent);
} while (ent != grp->cg_ent);
}
}
#else /* CHUNK_DIAG */
void
chunk_check(FILE *fout, const char *name)
{
}
#endif /* CHUNK_DIAG */

View file

@ -1,177 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*
*/
#ifndef _CLIXON_CHUNK_H_
#define _CLIXON_CHUNK_H_
/*
* Compile with chunk diagnostics. XXX Should be in Makefile.in ??
*/
#undef CHUNK_DIAG
/*
* Base number of bits to shift getting the size of a chunk_head.
*/
#define CHUNK_BASE 6
/*
* Number of predefined chunk sizes. I.e. the number of chunk heads in the
* chunk_heads vector.
*/
#define CHUNK_HEADS (32 - CHUNK_BASE)
#ifdef CHUNK_DIAG
/*
* Chunk diagnostics
*/
typedef struct _chunk_diag_t {
const char *cd_file; /* File which requested chunk */
int cd_line; /* Line in requesting file */
} chunk_diag_t;
#endif /* CHUNK_DIAG */
/*
* The block header.
*/
struct _chunk_head_t;
typedef struct _chunk_head_t chunk_head_t;
typedef struct _chunk_block_t {
qelem_t cb_qelem; /* Circular queue of blocks */
chunk_head_t *cb_head; /* The chunk head I belong to */
void *cb_blk; /* Allocated memory block */
uint16_t cb_ref; /* Number of used chunks of block */
} chunk_block_t;
/*
* The chunk header.
*/
struct _chunk_grpent_t;
typedef struct _chunk_grpent_t chunk_grpent_t;
typedef struct _chunk_t {
qelem_t c_qelem; /* Circular queue of chunks */
chunk_block_t *c_blk; /* The block I belong to */
#ifdef CHUNK_DIAG
chunk_diag_t c_diag; /* The diagnostics structure */
#endif /* CHUNK_DIAG */
chunk_grpent_t *c_grpent;
} chunk_t;
/*
* The head of a chunk size. Each predefined size has it's own head keeping
* track of all blocks and chunks for the size.
*/
struct _chunk_head_t {
size_t ch_size; /* Chunk size */
int ch_nchkperblk; /* Number pf chunks per block */
size_t ch_blksz; /* Size of a block */
int ch_nblks; /* Number of allocated blocks */
chunk_block_t *ch_blks; /* Circular list of blocks */
chunk_t *ch_cnks; /* Circular list of chunks in use */
size_t ch_nfree; /* Number of free chunks */
chunk_t *ch_free; /* Circular list of free chunks */
};
/*
* The chunk group structure.
*/
typedef struct _chunk_group_t {
qelem_t cg_qelem; /* List of chunk groups */
char *cg_name; /* Name of group */
chunk_grpent_t *cg_ent; /* List of chunks in the group */
} chunk_group_t;
/*
* The chunk group entry structure.
*/
struct _chunk_grpent_t {
qelem_t ce_qelem; /* Circular list of entries */
chunk_group_t *ce_grp; /* The group I belong to */
chunk_t *ce_cnk; /* Pointer to the chunk */
};
/*
* Public function declarations
*/
#ifdef CHUNK_DIAG
void *_chunk (size_t, const char *, const char *, int);
#define chunk(siz,label) _chunk((siz),(label),__FILE__,__LINE__)
void *_rechunk (void *, size_t, const char *, const char *, int);
#define rechunk(ptr,siz,label) _rechunk((ptr),(siz),(label),__FILE__,__LINE__)
void *_chunkdup (const void *, size_t, const char *, const char *, int);
#define chunkdup(ptr,siz,label) _chunkdup((ptr),(siz),(label),__FILE__,__LINE__)
char *_chunk_strncat (const char *, const char *, size_t, const char *, const char *, int);
#define chunk_strncat(str,new,n,label) _chunk_strncat((str),(new),(n),(label),__FILE__,__LINE__)
char *_chunk_sprintf (const char *, const char *, int, const char *, ...);
#define chunk_sprintf(label,fmt,...) _chunk_sprintf((label),__FILE__,__LINE__,(fmt),__VA_ARGS__)
#else /* CHUNK_DIAG */
void *chunk (size_t, const char *);
void *rechunk (void *, size_t, const char *);
void *chunkdup (const void *, size_t, const char *);
char *chunk_strncat (const char *, const char *, size_t, const char *);
char *chunk_sprintf (const char *, char *, ...);
#endif /* CHUNK_DIAG */
void unchunk (void *);
void unchunk_group (const char *);
void chunk_check (FILE *, const char *);
size_t chunksize (void *);
#endif /* _CLIXON_CHUNK_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,54 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
Key-value store
*/
#ifndef _CLIXON_KEYVALUE_H
#define _CLIXON_KEYVALUE_H
/*
* Prototypes
*/
int kv_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int kv_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt);
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
int kv_copy(xmldb_handle h, const char *from, const char *to);
int kv_lock(xmldb_handle h, const char *db, int pid);
int kv_unlock(xmldb_handle h, const char *db);
int kv_unlock_all(xmldb_handle h, int pid);
int kv_islocked(xmldb_handle h, const char *db);
int kv_exists(xmldb_handle h, const char *db);
int kv_delete(xmldb_handle h, const char *db);
int kv_init(xmldb_handle h, const char *db);
#endif /* _CLIXON_KEYVALUE_H */

View file

@ -1,588 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* @note Some unclarities with locking. man dpopen defines the following flags
* with dpopen:
* `DP_ONOLCK', which means it opens a database file without
* file locking,
* `DP_OLCKNB', which means locking is performed without blocking.
*
* While connecting as a writer, an exclusive lock is invoked to
* the database file. While connecting as a reader, a shared lock is
* invoked to the database file. The thread blocks until the lock is
* achieved. If `DP_ONOLCK' is used, the application is responsible
* for exclusion control.
* The code below uses for
* write, delete: DP_OLCKNB
* read: DP_OLCKNB
* This means that a write fails if one or many reads are occurring, and
* a read or write fails if a write is occurring, and
* QDBM allows a single write _or_ multiple readers, but
* not both. This is obviously extremely limiting.
* NOTE, the locking in netconf and xmldb is a write lock.
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <limits.h>
#include <regex.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/param.h>
#ifdef HAVE_DEPOT_H
#include <depot.h> /* qdb api */
#else /* HAVE_QDBM_DEPOT_H */
#include <qdbm/depot.h> /* qdb api */
#endif
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
#include "clixon_chunk.h"
#include "clixon_qdb.h"
/*! Initialize database
* @param[in] file database file
* @param[in] omode see man dpopen
*/
static int
db_init_mode(char *file,
int omode)
{
DEPOT *dp;
/* Open database for writing */
if ((dp = dpopen(file, omode | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "dpopen(%s): %s",
file,
dperrmsg(dpecode));
return -1;
}
clicon_debug(1, "db_init(%s)", file);
if (dpclose(dp) == 0){
clicon_err(OE_DB, errno, "db_set: dpclose: %s",
dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Open database for reading and writing
* @param[in] file database file
*/
int
db_init(char *file)
{
return db_init_mode(file, DP_OWRITER | DP_OCREAT ); /* DP_OTRUNC? */
}
/*! Remove database by removing file, if it exists *
* @param[in] file database file
*/
int
db_delete(char *file)
{
struct stat sb;
if (stat(file, &sb) < 0){
return 0;
}
if (unlink(file) < 0){
clicon_err(OE_DB, errno, "unlink %s", file);
return -1;
}
return 0;
}
/*! Write data to database
* @param[in] file database file
* @param[in] key database key
* @param[out] data Buffer containing content
* @param[out] datalen Length of buffer
* @retval 0 if OK: value returned. If not found, zero string returned
* @retval -1 on error
*/
int
db_set(char *file,
char *key,
void *data,
size_t datalen)
{
DEPOT *dp;
/* Open database for writing */
if ((dp = dpopen(file, DP_OWRITER|DP_OLCKNB , 0)) == NULL){
clicon_err(OE_DB, errno, "db_set: dpopen(%s): %s",
file,
dperrmsg(dpecode));
return -1;
}
clicon_debug(2, "%s: db_put(%s, len:%d)",
file, key, (int)datalen);
if (dpput(dp, key, -1, data, datalen, DP_DOVER) == 0){
clicon_err(OE_DB, errno, "%s: db_set: dpput(%s, %d): %s",
file,
key,
datalen,
dperrmsg(dpecode));
dpclose(dp);
return -1;
}
if (dpclose(dp) == 0){
clicon_err(OE_DB, 0, "db_set: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Get data from database
* @param[in] file database file
* @param[in] key database key
* @param[out] data Pre-allocated buffer where data corresponding key is placed
* @param[out] datalen Length of pre-allocated buffer
* @retval 0 if OK: value returned. If not found, zero string returned
* @retval -1 on error
* @see db_get_alloc Allocates memory
*/
int
db_get(char *file,
char *key,
void *data,
size_t *datalen)
{
DEPOT *dp;
int len;
/* Open database for readinf */
if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "%s: db_get(%s, %d): dpopen: %s",
file,
key,
datalen,
dperrmsg(dpecode));
return -1;
}
len = dpgetwb(dp, key, -1, 0, *datalen, data);
if (len < 0){
if (dpecode == DP_ENOITEM){
data = NULL;
*datalen = 0;
}
else{
clicon_err(OE_DB, errno, "db_get: dpgetwb: %s (%d)",
dperrmsg(dpecode), dpecode);
dpclose(dp);
return -1;
}
}
else
*datalen = len;
clicon_debug(2, "db_get(%s, %s)=%s", file, key, (char*)data);
if (dpclose(dp) == 0){
clicon_err(OE_DB, errno, "db_get: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Get data from database and allocates memory
* Similar to db_get but returns a malloced pointer to the data instead
* of copying data to pre-allocated buffer. This is necessary if the
* length of the data is not known when calling the function.
* @param[in] file database file
* @param[in] key database key
* @param[out] data Allocated buffer where data corresponding key is placed
* @param[out] datalen Length of pre-allocated buffer
* @retval 0 if OK: value returned. If not found, zero string returned
* @retval -1 on error
* @note: *data needs to be freed after use.
* @code
* char *lvec = NULL;
* size_t len = 0;
* if (db_get-alloc(dbname, "a.0", &val, &vlen) == NULL)
* return -1;
* ..do stuff..
* if (val) free(val);
* @endcode
* @see db_get Pre-allocates memory
*/
int
db_get_alloc(char *file,
char *key,
void **data,
size_t *datalen)
{
DEPOT *dp;
int len;
/* Open database for writing */
if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "%s: dpopen(%s): %s",
__FUNCTION__,
file,
dperrmsg(dpecode));
return -1;
}
if ((*data = dpget(dp, key, -1, 0, -1, &len)) == NULL){
if (dpecode == DP_ENOITEM){
*datalen = 0;
*data = NULL;
len = 0;
}
else{
/* No entry vs error? */
clicon_err(OE_DB, errno, "db_get_alloc: dpgetwb: %s (%d)",
dperrmsg(dpecode), dpecode);
dpclose(dp);
return -1;
}
}
*datalen = len;
if (dpclose(dp) == 0){
clicon_err(OE_DB, errno, "db_get_alloc: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return 0;
}
/*! Delete database entry
* @param[in] file database file
* @param[in] key database key
* @retval -1 on failure,
* @retval 0 if key did not exist
* @retval 1 if successful.
*/
int
db_del(char *file, char *key)
{
int retval = 0;
DEPOT *dp;
/* Open database for writing */
if ((dp = dpopen(file, DP_OWRITER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "db_del: dpopen(%s): %s",
file,
dperrmsg(dpecode));
return -1;
}
if (dpout(dp, key, -1)) {
retval = 1;
}
if (dpclose(dp) == 0){
clicon_err(OE_DB, errno, "db_del: dpclose: %s", dperrmsg(dpecode));
return -1;
}
return retval;
}
/*! Check if entry in database exists
* @param[in] file database file
* @param[in] key database key
* @retval 1 if key exists in database
* @retval 0 key does not exist in database
* @retval -1 error
*/
int
db_exists(char *file,
char *key)
{
DEPOT *dp;
int len;
/* Open database for reading */
if ((dp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "%s: dpopen: %s",
__FUNCTION__, dperrmsg(dpecode));
return -1;
}
len = dpvsiz(dp, key, -1);
if (len < 0 && dpecode != DP_ENOITEM)
clicon_err(OE_DB, errno, "^s: dpvsiz: %s (%d)",
__FUNCTION__, dperrmsg(dpecode), dpecode);
if (dpclose(dp) == 0) {
clicon_err(OE_DB, errno, "%s: dpclose: %s", dperrmsg(dpecode),__FUNCTION__);
return -1;
}
return (len < 0) ? 0 : 1;
}
/*! Return all entries in database that match a regular expression.
* @param[in] file database file
* @param[in] regexp regular expression for database keys
* @param[in] label for memory/chunk allocation
* @param[out] pairs Vector of database keys and values
* @param[in] noval If set don't retreive values, just keys
* @retval -1 on error
* @retval n Number of pairs
* @code
* struct db_pair *pairs;
* int npairs;
* if ((npairs = db_regexp(dbname, "^/test/kalle$", __FUNCTION__,
* &pairs, 0)) < 0)
* err;
*
* @endcode
*/
int
db_regexp(char *file,
char *regexp,
const char *label,
struct db_pair **pairs,
int noval)
{
int npairs;
int status;
int retval = -1;
int vlen = 0;
char *key = NULL;
void *val = NULL;
char errbuf[512];
struct db_pair *pair;
struct db_pair *newpairs;
regex_t iterre;
DEPOT *iterdp = NULL;
regmatch_t pmatch[1];
size_t nmatch = 1;
npairs = 0;
*pairs = NULL;
if (regexp) {
if ((status = regcomp(&iterre, regexp, REG_EXTENDED)) != 0) {
regerror(status, &iterre, errbuf, sizeof(errbuf));
clicon_err(OE_DB, errno, "%s: regcomp: %s", __FUNCTION__, errbuf);
return -1;
}
}
/* Open database for reading */
if ((iterdp = dpopen(file, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, 0, "%s: dpopen(%s): %s",
__FUNCTION__, file, dperrmsg(dpecode));
goto quit;
}
/* Initiate iterator */
if(dpiterinit(iterdp) == 0) {
clicon_err(OE_DB, errno, "%s: dpiterinit: %s", __FUNCTION__, dperrmsg(dpecode));
goto quit;
}
/* Iterate through DB */
while((key = dpiternext(iterdp, NULL)) != NULL) {
if (regexp && regexec(&iterre, key, nmatch, pmatch, 0) != 0) {
free(key);
continue;
}
/* Retrieve value if required */
if ( ! noval) {
if((val = dpget(iterdp, key, -1, 0, -1, &vlen)) == NULL) {
clicon_log(LOG_WARNING, "%s: dpget: %s", __FUNCTION__, dperrmsg(dpecode));
goto quit;
}
}
/* Resize and populate resulting array */
newpairs = rechunk(*pairs, (npairs+1) * sizeof(struct db_pair), label);
if (newpairs == NULL) {
clicon_err(OE_DB, errno, "%s: rechunk", __FUNCTION__);
goto quit;
}
pair = &newpairs[npairs];
memset(pair, 0, sizeof(*pair));
pair->dp_key = chunk_sprintf(label, "%s", key);
if (regexp)
pair->dp_matched = chunk_sprintf(label, "%.*s",
pmatch[0].rm_eo - pmatch[0].rm_so,
key + pmatch[0].rm_so);
else
pair->dp_matched = chunk_sprintf(label, "%s", key);
if (pair->dp_key == NULL || pair->dp_matched == NULL) {
clicon_err(OE_DB, errno, "%s: chunk_sprintf");
goto quit;
}
if ( ! noval) {
if (vlen){
pair->dp_val = chunkdup(val, vlen, label);
if (pair->dp_val == NULL) {
clicon_err(OE_DB, errno, "%s: chunkdup", __FUNCTION__);
goto quit;
}
}
pair->dp_vlen = vlen;
free(val);
val = NULL;
}
(*pairs) = newpairs;
npairs++;
free(key);
}
retval = npairs;
quit:
if (key)
free(key);
if (val)
free(val);
if (regexp)
regfree(&iterre);
if (iterdp)
dpclose(iterdp);
if (retval < 0)
unchunk_group(label);
return retval;
}
/*! Sanitize regexp string. Escape '\' etc.
*/
char *
db_sanitize(char *rx, const char *label)
{
char *new;
char *k, *p, *s;
k = chunk_sprintf(__FUNCTION__, "%s", "");
p = rx;
while((s = strstr(p, "\\"))) {
if ((k = chunk_sprintf(__FUNCTION__, "%s%.*s\\\\", k, s-p, p)) == NULL)
goto quit;
p = s+1;
}
if ((k = chunk_strncat(k, p, strlen(p), __FUNCTION__)) == NULL)
goto quit;
new = (char *)chunkdup(k, strlen(k)+1, label);
unchunk_group(__FUNCTION__);
return new;
quit:
unchunk_group(__FUNCTION__);
return NULL;
}
#if 0 /* Test program */
/*
* Turn this on to get an xpath test program
* Usage: clicon_xpath [<xpath>]
* read xml from input
* Example compile:
gcc -g -o qdb -I. -I../clixon ./clixon_qdb.c -lclixon -lcligen -lqdbm
*/
static int
usage(char *argv0)
{
fprintf(stderr, "usage:\n");
fprintf(stderr, "\t%s init <filename>\n", argv0);
fprintf(stderr, "\t%s read <filename> <key>\n", argv0);
fprintf(stderr, "\t%s write <filename> <key> <val>\n", argv0);
fprintf(stderr, "\t%s openread <filename>\n", argv0);
fprintf(stderr, "\t%s openwrite <filename>\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
char *verb;
char *filename;
char *key;
char *val;
size_t len;
DEPOT *dp;
if (argc < 3)
usage(argv[0]);
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
verb = argv[1];
filename = argv[2];
if (strcmp(verb, "init")==0){
db_init(filename);
}
else if (strcmp(verb, "read")==0){
if (argc < 4)
usage(argv[0]);
key = argv[3];
db_get_alloc(filename, key, (void**)&val, &len);
fprintf(stdout, "%s\n", val);
}
else if (strcmp(verb, "write")==0){
if (argc < 5)
usage(argv[0]);
key = argv[3];
val = argv[4];
db_set(filename, key, val, strlen(val)+1);
}
else if (strcmp(verb, "openread")==0){
if ((dp = dpopen(filename, DP_OREADER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "dbopen: %s",
dperrmsg(dpecode));
return -1;
}
sleep(1000000);
}
else if (strcmp(verb, "openwrite")==0){
if ((dp = dpopen(filename, DP_OWRITER | DP_OLCKNB, 0)) == NULL){
clicon_err(OE_DB, errno, "dbopen: %s",
dperrmsg(dpecode));
return -1;
}
sleep(1000000);
}
return 0;
}
#endif /* Test program */

View file

@ -1,73 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifndef _CLIXON_QDB_H_
#define _CLIXON_QDB_H_
/*
* Low level API
*/
struct db_pair {
char *dp_key; /* database key */
char *dp_matched; /* Matched component of key */
char *dp_val; /* pointer to vector of lvalues */
int dp_vlen; /* length of vector of lvalues */
};
/*
* Prototypes
*/
int db_init(char *file);
int db_delete(char *file);
int db_set(char *file, char *key, void *data, size_t datalen);
int db_get(char *file, char *key, void *data, size_t *datalen);
int db_get_alloc(char *file, char *key, void **data, size_t *datalen);
int db_del(char *file, char *key);
int db_exists(char *file, char *key);
int db_regexp(char *file, char *regexp, const char *label,
struct db_pair **pairs, int noval);
char *db_sanitize(char *rx, const char *label);
#endif /* _CLIXON_QDB_H_ */

View file

@ -748,7 +748,8 @@ text_modify(cxobj *x0,
* @see text_modify
*/
static int
text_modify_top(cxobj *x0,
text_modify_top(struct text_handle *th,
cxobj *x0,
cxobj *x1,
yang_spec *yspec,
enum operation_type op,
@ -759,6 +760,7 @@ text_modify_top(cxobj *x0,
cxobj *x0c; /* base child */
cxobj *x1c; /* mod child */
yang_stmt *yc; /* yang child */
yang_stmt *ymod;/* yang module */
char *opstr;
/* Assure top-levels are 'config' */
@ -805,10 +807,23 @@ text_modify_top(cxobj *x0,
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
/* Get yang spec of the child */
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
clicon_err(OE_YANG, ENOENT, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?",
xml_name(x1), x1cname);
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 && _CLICON_XML_NS_ITERATE){
int i;
for (i=0; i<yspec->yp_len; i++){
ymod = yspec->yp_stmt[i];
if ((yc = yang_find_datanode((yang_node*)ymod, x1cname)) != NULL)
break;
}
}
if (yc == NULL){
if (netconf_operation_failed(cbret, "application", "Validation failed")< 0)
goto done;
goto ok;
}
/* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, &x0c, yc) < 0)
@ -942,11 +957,12 @@ text_put(xmldb_handle xh,
xml_name(x0));
goto done;
}
/* Add yang specification backpointer to all XML nodes */
/* XXX: where is this created? Add yspec */
#if 0
/* Add yang specification backpointer to all XML nodes
* This is already done in from_client_edit_config() */
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
#endif
#if 0 /* debug */
if (xml_child_sort && xml_apply0(x1, -1, xml_sort_verify, NULL) < 0)
clicon_log(LOG_NOTICE, "%s: verify failed #1", __FUNCTION__);
@ -955,7 +971,7 @@ text_put(xmldb_handle xh,
* Modify base tree x with modification x1. This is where the
* new tree is made.
*/
if (text_modify_top(x0, x1, yspec, op, cbret) < 0)
if (text_modify_top(th, x0, x1, yspec, op, cbret) < 0)
goto done;
/* If xml return - ie netconf error xml tree, then stop and return OK */
if (cbuf_len(cbret))