Ongoing: xmldb datastore plugin framework
This commit is contained in:
parent
05edace630
commit
4169bd8d30
18 changed files with 2588 additions and 1924 deletions
|
|
@ -61,15 +61,14 @@ CPPFLAGS = @CPPFLAGS@
|
|||
|
||||
INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir)
|
||||
|
||||
SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
clixon_chunk.c clixon_proc.c \
|
||||
clixon_string.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c \
|
||||
clixon_yang.c clixon_yang_type.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c \
|
||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_client.c \
|
||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
|
||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
|
||||
|
||||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
|
|
|
|||
|
|
@ -237,6 +237,10 @@ clicon_option_sanity(clicon_hash_t *copt)
|
|||
clicon_err(OE_UNIX, 0, "CLICON_ARCHIVE_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_XMLDB_DIR")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_XMLDB_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_SOCK")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_SOCK not defined in config file");
|
||||
goto done;
|
||||
|
|
@ -453,6 +457,12 @@ clicon_archive_dir(clicon_handle h)
|
|||
return clicon_option_str(h, "CLICON_ARCHIVE_DIR");
|
||||
}
|
||||
|
||||
char *
|
||||
clicon_xmldb_plugin(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_XMLDB_PLUGIN");
|
||||
}
|
||||
|
||||
/* get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */
|
||||
int
|
||||
clicon_sock_family(clicon_handle h)
|
||||
|
|
|
|||
|
|
@ -1,589 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* @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_log.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_queue.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(OE_DB, "%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 */
|
||||
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
#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_ */
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -66,6 +66,7 @@
|
|||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -89,9 +90,6 @@
|
|||
/* Something to do with reverse engineering of junos syntax? */
|
||||
#undef SPECIAL_TREATMENT_OF_NAME
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A node is a leaf if it contains a body.
|
||||
*/
|
||||
|
|
@ -791,3 +789,288 @@ xml_diff(yang_spec *yspec,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Construct an xml key format from yang statement using wildcards for keys
|
||||
* Recursively construct it to the top.
|
||||
* Example:
|
||||
* yang: container a -> list b -> key c -> leaf d
|
||||
* xpath: /a/b/%s/d
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] inclkey If inclkey then include key leaf (eg last leaf d in ex)
|
||||
* @param[out] cbuf keyfmt
|
||||
*/
|
||||
static int
|
||||
yang2xmlkeyfmt_1(yang_stmt *ys,
|
||||
int inclkey,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_node *yp; /* parent */
|
||||
yang_stmt *ykey;
|
||||
int i;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
int retval = -1;
|
||||
|
||||
yp = ys->ys_parent;
|
||||
if (yp != NULL &&
|
||||
yp->yn_keyword != Y_MODULE &&
|
||||
yp->yn_keyword != Y_SUBMODULE){
|
||||
if (yang2xmlkeyfmt_1((yang_stmt *)yp, 1, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (inclkey){
|
||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||
cprintf(cb, "/%s", ys->ys_argument);
|
||||
}
|
||||
else{
|
||||
if (ys->ys_keyword == Y_LEAF && yp && yp->yn_keyword == Y_LIST){
|
||||
if (yang_key_match(yp, ys->ys_argument) == 0)
|
||||
cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
|
||||
}
|
||||
else
|
||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||
cprintf(cb, "/%s", ys->ys_argument);
|
||||
}
|
||||
|
||||
switch (ys->ys_keyword){
|
||||
case Y_LIST:
|
||||
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
if (cvec_len(cvk))
|
||||
cprintf(cb, "=");
|
||||
/* Iterate over individual keys */
|
||||
for (i=0; i<cvec_len(cvk); i++){
|
||||
if (i)
|
||||
cprintf(cb, ",");
|
||||
cprintf(cb, "%%s");
|
||||
}
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
cprintf(cb, "=%%s");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} /* switch */
|
||||
retval = 0;
|
||||
done:
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Construct an xml key format from yang statement using wildcards for keys
|
||||
* Recursively construct it to the top.
|
||||
* Example:
|
||||
* yang: container a -> list b -> key c -> leaf d
|
||||
* xpath: /a/b=%s/d
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] inclkey If !inclkey then dont include key leaf
|
||||
* @param[out] xkfmt XML key format. Needs to be freed after use.
|
||||
*/
|
||||
int
|
||||
yang2xmlkeyfmt(yang_stmt *ys,
|
||||
int inclkey,
|
||||
char **xkfmt)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (yang2xmlkeyfmt_1(ys, inclkey, cb) < 0)
|
||||
goto done;
|
||||
if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Transform an xml key format and a vector of values to an XML key
|
||||
* Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey()
|
||||
* Example:
|
||||
* xmlkeyfmt: /aaa/%s
|
||||
* cvv: key=17
|
||||
* xmlkey: /aaa/17
|
||||
* @param[in] xkfmt XML key format, eg /aaa/%s
|
||||
* @param[in] cvv cligen variable vector, one for every wildchar in xkfmt
|
||||
* @param[out] xk XML key, eg /aaa/17. Free after use
|
||||
* @note first and last elements of cvv are not used,..
|
||||
* @see cli_dbxml where this function is called
|
||||
*/
|
||||
int
|
||||
xmlkeyfmt2key(char *xkfmt,
|
||||
cvec *cvv,
|
||||
char **xk)
|
||||
{
|
||||
int retval = -1;
|
||||
char c;
|
||||
int esc=0;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
int j;
|
||||
char *str;
|
||||
char *strenc=NULL;
|
||||
|
||||
|
||||
/* Sanity check */
|
||||
#if 1
|
||||
j = 0; /* Count % */
|
||||
for (i=0; i<strlen(xkfmt); i++)
|
||||
if (xkfmt[i] == '%')
|
||||
j++;
|
||||
if (j+2 < cvec_len(cvv)) {
|
||||
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
|
||||
xkfmt,
|
||||
j,
|
||||
cvec_len(cvv),
|
||||
cv_string_get(cvec_i(cvv, 0)));
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
j = 1; /* j==0 is cli string */
|
||||
for (i=0; i<strlen(xkfmt); i++){
|
||||
c = xkfmt[i];
|
||||
if (esc){
|
||||
esc = 0;
|
||||
if (c!='s')
|
||||
continue;
|
||||
if ((str = cv2str_dup(cvec_i(cvv, j++))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if ((strenc = curl_easy_escape(NULL, str, 0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "curl_easy_escape");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%s", strenc);
|
||||
curl_free(strenc);
|
||||
free(str);
|
||||
}
|
||||
else
|
||||
if (c == '%')
|
||||
esc++;
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
if ((*xk = strdup(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Transform an xml key format and a vector of values to an XML path
|
||||
* Used to input xmldb_get() or xmldb_get_vec
|
||||
* Add .* in last %s position.
|
||||
* Example:
|
||||
* xmlkeyfmt: /interface/%s/address/%s OLDXXX
|
||||
* xmlkeyfmt: /interface=%s/address=%s
|
||||
* cvv: name=eth0
|
||||
* xmlkey: /interface/[name=eth0]/address
|
||||
* Example2:
|
||||
* xmlkeyfmt: /ip/me/%s (if key)
|
||||
* cvv: -
|
||||
* xmlkey: /ipv4/me/a
|
||||
* @param[in] xkfmt XML key format
|
||||
* @param[in] cvv cligen variable vector, one for every wildchar in xkfmt
|
||||
* @param[out] xk XPATH
|
||||
*/
|
||||
int
|
||||
xmlkeyfmt2xpath(char *xkfmt,
|
||||
cvec *cvv,
|
||||
char **xk)
|
||||
{
|
||||
int retval = -1;
|
||||
char c;
|
||||
int esc=0;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
int j;
|
||||
char *str;
|
||||
cg_var *cv;
|
||||
int skip = 0;
|
||||
|
||||
/* Sanity check: count '%' */
|
||||
#if 1
|
||||
j = 0; /* Count % */
|
||||
for (i=0; i<strlen(xkfmt); i++)
|
||||
if (xkfmt[i] == '%')
|
||||
j++;
|
||||
if (j < cvec_len(cvv)-1) {
|
||||
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
|
||||
xkfmt,
|
||||
j,
|
||||
cvec_len(cvv),
|
||||
cv_string_get(cvec_i(cvv, 0)));
|
||||
// goto done;
|
||||
}
|
||||
#endif
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
j = 1; /* j==0 is cli string */
|
||||
for (i=0; i<strlen(xkfmt); i++){
|
||||
c = xkfmt[i];
|
||||
if (esc){
|
||||
esc = 0;
|
||||
if (c!='s')
|
||||
continue;
|
||||
|
||||
if (j == cvec_len(cvv)) /* last element */
|
||||
//skip++;
|
||||
;
|
||||
else{
|
||||
cv = cvec_i(cvv, j++);
|
||||
if ((str = cv2str_dup(cv)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "[%s=%s]", cv_name_get(cv), str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
else /* regular char */
|
||||
if (c == '%')
|
||||
esc++;
|
||||
else{
|
||||
if (skip)
|
||||
skip=0;
|
||||
else
|
||||
if ((c == '=' || c == ',') && xkfmt[i+1]=='%')
|
||||
; /* skip */
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
}
|
||||
if ((*xk = strdup4(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue