This commit is contained in:
Olof hagsand 2016-03-07 20:55:55 +01:00
parent ca18b7f49e
commit 0a812696c2
47 changed files with 1818 additions and 1783 deletions

View file

@ -20,7 +20,6 @@
* XML database
* TODO: xmldb_del: or dbxml_put_xkey delete
* TODO: xmldb_get: xpath: only load partial tree
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
@ -34,6 +33,9 @@
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <syslog.h>
@ -43,6 +45,7 @@
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_file.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_chunk.h"
@ -52,8 +55,10 @@
#include "clixon_yang.h"
#include "clixon_handle.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_xsl.h"
#include "clixon_xml_parse.h"
#include "clixon_xml_db_rpc.h"
#include "clixon_xml_db.h"
/*
@ -331,7 +336,95 @@ xmlkeyfmt2xpath(char *xkfmt,
return retval;
}
/*! Database locking for candidate and running non-persistent
* Store an integer for running and candidate containing
* the session-id of the client holding the lock.
*/
static int _running_locked = 0;
static int _candidate_locked = 0;
/*! Lock database
*/
static int
db_lock(char *db,
int pid)
{
if (strcmp("running", db) == 0)
_running_locked = pid;
else if (strcmp("candidate", db) == 0)
_candidate_locked = pid;
clicon_debug(1, "%s: locked by %u", db, pid);
return 0;
}
/*! Unlock database
*/
static int
db_unlock(char *db)
{
if (strcmp("running", db) == 0)
_running_locked = 0;
else if (strcmp("candidate", db) == 0)
_candidate_locked = 0;
return 0;
}
/*! returns id of locker
* @retval 0 Not locked
* @retval >0 Id of locker
*/
static int
db_islocked(char *db)
{
if (strcmp("running", db) == 0)
return (_running_locked);
else if (strcmp("candidate", db) == 0)
return(_candidate_locked);
return 0;
}
/*! Translate from symbolic database name to actual filename in file-system
* @param[in] h Clicon 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
* The filename reside in CLICON_XMLDB_DIR option
*/
static int
db2file(clicon_handle h,
char *db,
char **filename)
{
int retval = -1;
cbuf *cb;
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, "CLICON_XMLDB_DIR not set");
goto done;
}
if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 &&
strcmp(db, "tmp") != 0){
clicon_err(OE_XML, 0, "Unexpected database: %s", db);
goto done;
}
cprintf(cb, "%s/%s_db", dir, db);
if ((*filename = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Help function to append key values from an xml list to a cbuf
* Example, a yang node x with keys a and b results in "x/a/b"
@ -415,7 +508,6 @@ create_keyvalues(cxobj *x,
return retval;
}
/*! Prune everything that has not been marked
* @param[in] xt XML tree with some node marked
* @param[out] upmark Set if a child (recursively) has marked set.
@ -672,22 +764,16 @@ xml_default(cxobj *x,
* @param[in] dbname Name of database to search in (filename including dir path
* @param[in] xpath String with XPATH syntax (or NULL for all)
* @param[in] yspec Yang specification
* @param[in] vector If set, return list of results in xvec
* @param[out] xtop XML tree. Freed by xml_free()
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* yang_spec *yspec = clicon_dbspec_yang(h);
* if (xmldb_get(dbname, "/interfaces/interface[name="eth*"]", yspec, &xt) < 0)
* err;
* xml_free(xt);
* @endcode
*/
int
xmldb_get(char *dbname,
char *xpath,
yang_spec *yspec,
cxobj **xtop)
static int
xmldb_get_tree(char *dbname,
char *xpath,
yang_spec *yspec,
cxobj **xtop)
{
int retval = -1;
int i;
@ -780,7 +866,7 @@ xmldb_get(char *dbname,
* @see xpath_vec
* @see xmldb_get
*/
int
static int
xmldb_get_vec(char *dbname,
char *xpath,
yang_spec *yspec,
@ -803,6 +889,7 @@ xmldb_get_vec(char *dbname,
for (i = 0; i < npairs; i++)
fprintf(stderr, "%s %s\n", pairs[i].dp_key, pairs[i].dp_val?pairs[i].dp_val:"");
/* Read in whole tree */
for (i = 0; i < npairs; i++) {
if (get(dbname,
yspec,
@ -827,7 +914,86 @@ xmldb_get_vec(char *dbname,
return retval;
}
/*! Local variant of xmldb_get */
static int
xmldb_get_local(clicon_handle h,
char *db,
char *xpath,
int vector,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
yang_spec *yspec;
char *dbname = NULL;
if (db2file(h, db, &dbname) < 0)
goto done;
if (dbname==NULL){
clicon_err(OE_XML, 0, "dbname NULL");
goto done;
}
yspec = clicon_dbspec_yang(h);
if (vector){
if (xmldb_get_vec(dbname, xpath, yspec, xtop, xvec, xlen) < 0)
goto done;
}
else
if (xmldb_get_tree(dbname, xpath, yspec, xtop) < 0)
goto done;
retval = 0;
done:
if (dbname)
free(dbname);
return retval;
}
/*! Get content of database using xpath.
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @param[in] h Clicon handle
* @param[in] db running | candidate
* @param[in] xpath String with XPATH syntax (or NULL for all)
* @param[in] vector If set, return list of results in xvec, else single tree.
* @param[out] xtop XML tree. Freed by xml_free()
* @param[out] xvec Vector of xml trees. Free after use
* @param[out] xlen Length of vector.
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
* yang_spec *yspec = clicon_dbspec_yang(h);
* if (xmldb_get(dbname, "/interfaces/interface[name="eth"]", yspec,
* 1, &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* xml_free(xt);
* free(xvec);
* @endcode
* @see xpath_vec
* @endcode
*/
int
xmldb_get(clicon_handle h,
char *db,
char *xpath,
int vector,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
{
if (clicon_xmldb_rpc(h))
return xmldb_get_rpc(h, db, xpath, vector, xtop, xvec, xlen);
else
return xmldb_get_local(h, db, xpath, vector, xtop, xvec, xlen);
}
/*! Get value of the "operation" attribute and change op if given
* @param[in] xn XML node
@ -950,13 +1116,57 @@ put(char *dbname,
return retval;
}
/*! Local variant of xmldb_put */
static int
xmldb_put_local(clicon_handle h,
char *db,
cxobj *xt,
enum operation_type op)
{
int retval = -1;
cxobj *x = NULL;
yang_stmt *ys;
yang_spec *yspec;
char *dbfilename = NULL;
yspec = clicon_dbspec_yang(h);
if (db2file(h, db, &dbfilename) < 0)
goto done;
if (op == OP_REPLACE){
if (db_delete(dbfilename) < 0)
goto done;
if (db_init(dbfilename) < 0)
goto done;
}
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
goto done;
}
if (put(dbfilename, /* database name */
x, /* xml root node */
ys, /* yang statement of xml node */
op, /* operation, eg merge/delete */
"" /* aggregate xml key */
) < 0)
goto done;
}
retval = 0;
done:
if (dbfilename)
free(dbfilename);
return retval;
}
/*! Modify database provided an xml tree and an operation
* @param[in] dbname Name of database to search in (filename including dir path)
* @param[in] h CLICON handle
* @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] yspec Yang specification
* @param[in] op OP_MERGE: just add it.
OP_REPLACE: first delete whole database
OP_NONE: operation attribute in xml determines operation
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
@ -968,59 +1178,24 @@ put(char *dbname,
* @endcode
*/
int
xmldb_put(char *dbname,
xmldb_put(clicon_handle h,
char *db,
cxobj *xt,
yang_spec *yspec,
enum operation_type op)
{
int retval = -1;
cxobj *x = NULL;
yang_stmt *ys;
if (op == OP_REPLACE){
unlink(dbname);
if (db_init(dbname) < 0)
goto done;
}
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
goto done;
}
if (put(dbname, /* database name */
x, /* xml root node */
ys, /* yang statement of xml node */
op, /* operation, eg merge/delete */
"" /* aggregate xml key */
) < 0)
goto done;
}
retval = 0;
done:
return retval;
if (clicon_xmldb_rpc(h))
return xmldb_put_rpc(h, db, xt, op);
else
return xmldb_put_local(h, db, xt, op);
}
/*! Modify database provided an XML database key and an operation
* @param[in] dbname Name of database to search in (filename including dir path)
* @param[in] xk XML Key, eg /aa/bb/17/name
* @param[in] val Key value, eg "17"
* @param[in] yspec Yang specification
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @retval 0 OK
* @retval -1 Error
* @code
* yang_spec *yspec = clicon_dbspec_yang(h);
* if (xmldb_put_xkey(dbname, "/aa/bb/17/name", "17", yspec, OP_MERGE) < 0)
* err;
* @endcode
*/
int
xmldb_put_xkey(char *dbname,
char *xk,
char *val,
yang_spec *yspec,
enum operation_type op)
/*! Local variant of xmldb_put_xkey */
static int
xmldb_put_xkey_local(clicon_handle h,
char *db,
char *xk,
char *val,
enum operation_type op)
{
int retval = -1;
cxobj *x = NULL;
@ -1040,7 +1215,12 @@ xmldb_put_xkey(char *dbname,
int exists;
int npairs;
struct db_pair *pairs;
yang_spec *yspec;
char *filename = NULL;
yspec = clicon_dbspec_yang(h);
if (db2file(h, db, &filename) < 0)
goto done;
if (xk == NULL || *xk!='/'){
clicon_err(OE_DB, 0, "Invalid key: %s", xk);
goto done;
@ -1109,13 +1289,13 @@ xmldb_put_xkey(char *dbname,
cbuf_reset(csubkey);
cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname);
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
if (db_set(dbname, cbuf_get(csubkey), val2, strlen(val2)+1) < 0)
if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0)
goto done;
break;
}
default:
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
if (db_set(dbname, cbuf_get(ckey), NULL, 0) < 0)
if (db_set(filename, cbuf_get(ckey), NULL, 0) < 0)
goto done;
break;
}
@ -1124,7 +1304,7 @@ xmldb_put_xkey(char *dbname,
/* final key */
switch (op){
case OP_CREATE:
if ((exists = db_exists(dbname, xk)) < 0)
if ((exists = db_exists(filename, xk)) < 0)
goto done;
if (exists == 1){
clicon_err(OE_DB, 0, "OP_CREATE: %s already exists in database", xk);
@ -1133,15 +1313,15 @@ xmldb_put_xkey(char *dbname,
case OP_MERGE:
case OP_REPLACE:
if (y->ys_keyword == Y_LEAF || y->ys_keyword == Y_LEAF_LIST){
if (db_set(dbname, xk, val, strlen(val)+1) < 0)
if (db_set(filename, xk, val, strlen(val)+1) < 0)
goto done;
}
else
if (db_set(dbname, xk, NULL, 0) < 0)
if (db_set(filename, xk, NULL, 0) < 0)
goto done;
break;
case OP_DELETE:
if ((exists = db_exists(dbname, xk)) < 0)
if ((exists = db_exists(filename, xk)) < 0)
goto done;
if (exists == 0){
clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk);
@ -1152,10 +1332,10 @@ xmldb_put_xkey(char *dbname,
if ((crx = cbuf_new()) == NULL)
goto done;
cprintf(crx, "^%s.*$", xk);
if ((npairs = db_regexp(dbname, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0)
if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0)
goto done;
for (i = 0; i < npairs; i++) {
if (db_del(dbname, pairs[i].dp_key) < 0)
if (db_del(filename, pairs[i].dp_key) < 0)
goto done;
}
break;
@ -1164,6 +1344,8 @@ xmldb_put_xkey(char *dbname,
}
retval = 0;
done:
if (filename)
free(filename);
if (ckey)
cbuf_free(ckey);
if (csubkey)
@ -1174,17 +1356,45 @@ xmldb_put_xkey(char *dbname,
return retval;
}
/*! Raw dump of database, just keys and values, no xml interpretation
* param[in] f File
* param[in] dbname Name of database
* param[in] rxkey Key regexp, eg "^.*$"
/*! Modify database provided an XML database key and an operation
* @param[in] dbname Name of database to search in (filename including dir path)
* @param[in] xk XML Key, eg /aa/bb/17/name
* @param[in] val Key value, eg "17"
* @param[in] yspec Yang specification
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @retval 0 OK
* @retval -1 Error
* @code
* if (xmldb_put_xkey(h, db, "/aa/bb/17/name", "17", OP_MERGE) < 0)
* err;
* @endcode
*/
int
xmldb_dump(FILE *f,
char *dbname,
char *rxkey)
xmldb_put_xkey(clicon_handle h,
char *db,
char *xk,
char *val,
enum operation_type op)
{
int retval = 0;
if (clicon_xmldb_rpc(h))
return xmldb_put_xkey_rpc(h, db, xk, val, op);
else
return xmldb_put_xkey_local(h, db, xk, val, op);
}
/*! Raw dump of database, just keys and values, no xml interpretation
* @param[in] f File
* @param[in] dbfile File-name of database. This is a local file
* @param[in] rxkey Key regexp, eg "^.*$"
* @note This function can only be called locally.
*/
int
xmldb_dump(FILE *f,
char *dbfilename,
char *rxkey)
{
int retval = -1;
int npairs;
struct db_pair *pairs;
@ -1193,29 +1403,270 @@ xmldb_dump(FILE *f,
rxkey = "^.*$";
/* Get all keys/values for vector */
if ((npairs = db_regexp(dbname, rxkey, __FUNCTION__, &pairs, 0)) < 0)
return -1;
if ((npairs = db_regexp(dbfilename, rxkey, __FUNCTION__, &pairs, 0)) < 0)
goto done;
for (npairs--; npairs >= 0; npairs--)
fprintf(f, "%s %s\n", pairs[npairs].dp_key,
pairs[npairs].dp_val?pairs[npairs].dp_val:"");
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
int
xmldb_init(char *file)
/*! Local variant of xmldb_copy */
static int
xmldb_copy_local(clicon_handle h,
char *from,
char *to)
{
return db_init(file);
int retval = -1;
char *fromfile = NULL;
char *tofile = NULL;
/* XXX lock */
if (db2file(h, from, &fromfile) < 0)
goto done;
if (db2file(h, to, &tofile) < 0)
goto done;
if (clicon_file_copy(fromfile, tofile) < 0)
goto done;
retval = 0;
done:
if (fromfile)
free(fromfile);
if (tofile)
free(tofile);
return retval;
}
#if 1 /* Test program */
/*! Copy database
* @param[in] h Clicon handle
* @param[in] from Source database copy
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_copy(clicon_handle h,
char *from,
char *to)
{
if (clicon_xmldb_rpc(h))
return xmldb_copy_rpc(h, from, to);
else
return xmldb_copy_local(h, from, to);
}
/* Local variant of xmldb_lock */
static int
xmldb_lock_local(clicon_handle h,
char *db,
int pid)
{
int retval = -1;
if (db_islocked(db)){
if (pid != db_islocked(db)){
clicon_err(OE_DB, 0, "lock failed: locked by %d", db_islocked(db));
goto done;
}
}
else
db_lock(db, pid);
retval = 0;
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,
char *db,
int pid)
{
if (clicon_xmldb_rpc(h))
return xmldb_lock_rpc(h, db, pid);
else
return xmldb_lock_local(h, db, pid);
}
/*! Local variant of xmldb_unlock */
static int
xmldb_unlock_local(clicon_handle h,
char *db,
int pid)
{
int retval = -1;
int pid1;
pid1 = db_islocked(db);
if (pid1){
if (pid == pid1)
db_unlock(db);
else{
clicon_err(OE_DB, 0, "unlock failed: locked by %d", pid1);
goto done;
}
}
retval = 0;
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
*/
int
xmldb_unlock(clicon_handle h,
char *db,
int pid)
{
if (clicon_xmldb_rpc(h))
return xmldb_unlock_rpc(h, db, pid);
else
return xmldb_unlock_local(h, db, pid);
}
/*! Local variant of xmldb_islocked */
static int
xmldb_islocked_local(clicon_handle h,
char *db)
{
return db_islocked(db);
}
/*! 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,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_islocked_rpc(h, db);
else
return xmldb_islocked_local(h, db);
}
/*! Local variant of xmldb_exists */
static int
xmldb_exists_local(clicon_handle h,
char *db)
{
int retval = -1;
char *filename = NULL;
struct stat sb;
if (db2file(h, db, &filename) < 0)
goto done;
if (lstat(filename, &sb) < 0)
retval = 0;
else
retval = 1;
done:
if (filename)
free(filename);
return retval;
}
/*! Check if db exists
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
xmldb_exists(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_exists_rpc(h, db);
else
return xmldb_exists_local(h, db);
}
/*! Local variant of xmldb_delete */
static int
xmldb_delete_local(clicon_handle h,
char *db)
{
int retval = -1;
char *filename = NULL;
if (db2file(h, db, &filename) < 0)
goto done;
if (db_delete(filename) < 0)
goto done;
retval = 0;
done:
if (filename)
free(filename);
return retval;
}
/*! Delete database. Remove file */
int
xmldb_delete(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_delete_rpc(h, db);
else
return xmldb_delete_local(h, db);
}
/*! Local variant of xmldb_init */
static int
xmldb_init_local(clicon_handle h,
char *db)
{
int retval = -1;
char *filename = NULL;
if (db2file(h, db, &filename) < 0)
goto done;
if (db_init(filename) < 0)
goto done;
retval = 0;
done:
if (filename)
free(filename);
return retval;
}
/*! Initialize database */
int
xmldb_init(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_init_rpc(h, db);
else
return xmldb_init_local(h, db);
}
#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 xmldb -I. -I../clicon ./clicon_xmldb.c -lclicon -lcligen
gcc -g -o xmldb -I. -I../clixon ./clixon_xmldb.c -lclixon -lcligen
*/
static int
@ -1260,7 +1711,7 @@ main(int argc, char **argv)
if (argc < 5)
usage(argv[0]);
xpath = argc>5?argv[5]:NULL;
if (xmldb_get(db, xpath, yspec, &xt) < 0)
if (xmldb_get(h, db, xpath, 0, &xt, NULL, NULL) < 0)
goto done;
clicon_xml2file(stdout, xt, 0, 1);
}
@ -1283,7 +1734,7 @@ main(int argc, char **argv)
op = OP_REMOVE;
else
usage(argv[0]);
if (xmldb_put(db, xn, yspec, op) < 0)
if (xmldb_put(h, db, xn, op) < 0)
goto done;
}
else