* 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

@ -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_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.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_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 \
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

@ -0,0 +1,48 @@
/*
*
***** 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 *****
* Datastore text-based XML write functions
*/
#ifndef _CLIXON_DATASTORE_WRITE_H
#define _CLIXON_DATASTORE_WRITE_H
/*
* Types
*/
/*
* Prototypes
*/
int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt, char *username, cbuf *cbret);
#endif /* _CLIXON_DATASTORE_WRITE_H */

View file

@ -56,6 +56,7 @@
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_stream.h"
#include "clixon_data.h"
#include "clixon_options.h"
#define CLICON_MAGIC 0x99aafabe
@ -64,8 +65,6 @@
/*! Internal structure of basic handle. Also header of all other handles.
* @note If you change here, you must also change the structs below:
* @see struct cli_handle
* @see struct backend_handle
* 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.
* Some details:
@ -77,11 +76,17 @@
* Alternatively, these could be accessed via clicon_conf_xml()
* 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.
* 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 {
int ch_magic; /* magic (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] */
};
@ -114,6 +119,10 @@ clicon_handle_init0(int size)
clicon_handle_exit((clicon_handle)ch);
goto done;
}
if ((ch->ch_db_elmnt = hash_init()) == NULL){
clicon_handle_exit((clicon_handle)ch);
goto done;
}
h = (clicon_handle)ch;
done:
return h;
@ -140,6 +149,7 @@ clicon_handle_init(void)
int
clicon_handle_exit(clicon_handle h)
{
int retval = -1;
struct clicon_handle *ch = handle(h);
clicon_hash_t *ha;
@ -147,9 +157,13 @@ clicon_handle_exit(clicon_handle h)
hash_free(ha);
if ((ha = clicon_data(h)) != NULL)
hash_free(ha);
if ((ha = clicon_db_elmnt(h)) != NULL)
hash_free(ha);
stream_delete_all(h, 1);
free(ch);
return 0;
retval = 0;
return retval;
}
/*! Check struct magic number for sanity checks
@ -188,6 +202,17 @@ clicon_data(clicon_handle h)
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.
* @param[in] h Clicon handle
*/

View file

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

View file

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

View file

@ -68,9 +68,8 @@
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
#include "clixon_options.h"
#include "clixon_plugin.h"
#include "clixon_data.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.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);
}
/*! 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_xml.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_plugin.h"
#include "clixon_string.h"
#include "clixon_xpath_ctx.h"

View file

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

View file

@ -687,6 +687,8 @@ xml_childvec_set(cxobj *x,
return 0;
}
/*! Get the children of an XML node as an XML vector
*/
cxobj **
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
* Before: xp --> xc # specific child
* Before: xp --> xc # specific child (xp can be NULL)
* After: xp --> xt(tag) --> xc
* @param[in] xp Parent xml node
* @param[in] tag Name of new xml child

View file

@ -65,6 +65,7 @@
#include "clixon_log.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_xml_map.h"
#include "clixon_yang_module.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_xml.h"
#include "clixon_plugin.h"
#include "clixon_data.h"
#include "clixon_options.h"
#include "clixon_yang_type.h"
#include "clixon_yang_parse.h"

View file

@ -72,6 +72,7 @@
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_options.h"
#include "clixon_data.h"
#include "clixon_plugin.h"
#include "clixon_netconf_lib.h"
#include "clixon_yang_module.h"
@ -157,60 +158,6 @@ yang_modules_revision(clicon_handle h)
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
*/
static int
@ -295,11 +242,12 @@ yms_build(clicon_handle h,
done:
return retval;
}
/*! Get modules state according to RFC 7895
* @param[in] h Clicon handle
* @param[in] yspec Yang spec
* @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
* @retval -1 Error (fatal)
* @retval 0 OK
@ -331,15 +279,15 @@ yang_modules_state_get(clicon_handle h,
cxobj **xret)
{
int retval = -1;
cxobj *x = NULL;
cxobj *x = NULL; /* Top tree, some juggling w top symbol */
char *msid; /* modules-set-id */
cxobj *x1;
cbuf *cb = NULL;
msid = clicon_option_str(h, "CLICON_MODULE_SET_ID");
if (!brief && modules_state_cache_get(h, msid, &x) < 0)
goto done;
if (x != NULL){ /* Yes a cache (but no duplicate) */
if ((x = clicon_modst_cache_get(h, brief)) != NULL){
/* x is here: <modules-state>...
* and x is original tree, need to copy */
if (xpath_first(x, "%s", xpath)){
if ((x1 = xml_dup(x)) == NULL)
goto done;
@ -353,21 +301,32 @@ yang_modules_state_get(clicon_handle h,
clicon_err(OE_UNIX, 0, "clicon buffer");
goto done;
}
/* Build a cb string: <modules-state>... */
if (yms_build(h, yspec, msid, brief, cb) < 0)
goto done;
/* Parse cb, x is on the form: <top><modules-state>... */
if (xml_parse_string(cbuf_get(cb), yspec, &x) < 0){
if (netconf_operation_failed_xml(xret, "protocol", clicon_err_reason)< 0)
goto done;
retval = 1;
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;
}
if (x){
/* Wrap x (again) with new top-level node "top" which merge wants */
if ((x = xml_wrap(x, "top")) < 0)
goto done;
if (netconf_trymerge(x, yspec, xret) < 0)
goto done;
}
if (x && netconf_trymerge(x, yspec, xret) < 0)
goto done;
retval = 0;
done:
clicon_debug(1, "%s %d", __FUNCTION__, retval);
if (cb)
cbuf_free(cb);
if (x)