xmldb
This commit is contained in:
parent
ca18b7f49e
commit
0a812696c2
47 changed files with 1818 additions and 1783 deletions
|
|
@ -55,7 +55,7 @@ SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
|
|||
clixon_yang.c clixon_yang_type.c \
|
||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_encode.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 clixon_xml_db_rpc.c
|
||||
|
||||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ clicon_err_reset(void)
|
|||
* - Logs to syslog with LOG_ERR
|
||||
* - Set global error variable name clicon_errno
|
||||
* - Set global reason string clicon_err_reason
|
||||
* NOTE: err direction (syslog and/or stderr) controlled by clicon_log_init()
|
||||
* @note: err direction (syslog and/or stderr) controlled by clicon_log_init()
|
||||
*
|
||||
* @param fn Inline function name (when called from clicon_err() macro)
|
||||
* @param line Inline file line number (when called from clicon_err() macro)
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ clicon_file_dirent(const char *dir,
|
|||
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
|
||||
goto quit;
|
||||
}
|
||||
res = lstat (filename, &st);
|
||||
res = lstat(filename, &st);
|
||||
unchunk (filename);
|
||||
if (res != 0) {
|
||||
clicon_err(OE_UNIX, 0, "lstat: %s", strerror(errno));
|
||||
|
|
@ -287,12 +287,13 @@ clicon_tmpfile(const char *label)
|
|||
return (char *)chunkdup(file, strlen(file)+1, label);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a copy of file src
|
||||
* On error returns -1 and sets errno.
|
||||
/*! Make a copy of file src
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
file_cp(char *src, char *target)
|
||||
clicon_file_copy(char *src,
|
||||
char *target)
|
||||
{
|
||||
int inF = 0, ouF = 0;
|
||||
int err = 0;
|
||||
|
|
@ -301,16 +302,22 @@ file_cp(char *src, char *target)
|
|||
struct stat st;
|
||||
int retval = -1;
|
||||
|
||||
if (stat(src, &st) != 0)
|
||||
if (stat(src, &st) != 0){
|
||||
clicon_err(OE_UNIX, errno, "stat");
|
||||
return -1;
|
||||
if((inF = open(src, O_RDONLY)) == -1)
|
||||
}
|
||||
if((inF = open(src, O_RDONLY)) == -1) {
|
||||
clicon_err(OE_UNIX, errno, "open");
|
||||
return -1;
|
||||
}
|
||||
if((ouF = open(target, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode)) == -1) {
|
||||
clicon_err(OE_UNIX, errno, "open");
|
||||
err = errno;
|
||||
goto error;
|
||||
}
|
||||
while((bytes = read(inF, line, sizeof(line))) > 0)
|
||||
if (write(ouF, line, bytes) < 0){
|
||||
clicon_err(OE_UNIX, errno, "write");
|
||||
err = errno;
|
||||
goto error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,8 +65,11 @@ static FILE *_debugfile = NULL;
|
|||
* @param[in] ident prefix that appears on syslog (eg 'cli')
|
||||
* @param[in] upto log priority, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG (see syslog(3)).
|
||||
* @param[in] flags bitmask: if CLICON_LOG_STDERR, then print logs to stderr
|
||||
if CLICON_LOG_SYSLOG, then print logs to syslog
|
||||
You can do a combination of both
|
||||
* if CLICON_LOG_SYSLOG, then print logs to syslog
|
||||
* You can do a combination of both
|
||||
* @code
|
||||
* clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
clicon_log_init(char *ident, int upto, int flags)
|
||||
|
|
|
|||
|
|
@ -212,14 +212,6 @@ clicon_option_sanity(clicon_hash_t *copt)
|
|||
clicon_err(OE_UNIX, 0, "CLICON_NETCONF_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_RUNNING_DB")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_RUNNING_DB not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_CANDIDATE_DB")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_CANDIDATE_DB not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_YANG_DIR")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_YANG_DIR not defined in config file");
|
||||
goto done;
|
||||
|
|
@ -404,20 +396,6 @@ clicon_yang_module_revision(clicon_handle h)
|
|||
return clicon_option_str(h, "CLICON_YANG_MODULE_REVISION");
|
||||
}
|
||||
|
||||
/* candidate database: get name */
|
||||
char *
|
||||
clicon_candidate_db(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_CANDIDATE_DB");
|
||||
}
|
||||
|
||||
/* running database: get name */
|
||||
char *
|
||||
clicon_running_db(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_RUNNING_DB");
|
||||
}
|
||||
|
||||
char *
|
||||
clicon_backend_dir(clicon_handle h)
|
||||
{
|
||||
|
|
@ -472,14 +450,14 @@ clicon_sock_family(clicon_handle h)
|
|||
return AF_UNIX; /* default */
|
||||
}
|
||||
|
||||
/* get information about socket: unix domain filepath, or addr:path */
|
||||
/*! Get information about socket: unix domain filepath, or addr:path */
|
||||
char *
|
||||
clicon_sock(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_SOCK");
|
||||
}
|
||||
|
||||
/* get port for backend socket in case of AF_INET or AF_INET6 */
|
||||
/*! Get port for backend socket in case of AF_INET or AF_INET6 */
|
||||
int
|
||||
clicon_sock_port(clicon_handle h)
|
||||
{
|
||||
|
|
@ -608,8 +586,43 @@ clicon_cli_genmodel_completion(clicon_handle h)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get dbspec (YANG variant)
|
||||
/* Where are "running" and "candidate" databases? */
|
||||
char *
|
||||
clicon_xmldb_dir(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_XMLDB_DIR");
|
||||
}
|
||||
|
||||
/*! Set if xmldb runs in a separate process (clixon_xmldb). */
|
||||
int
|
||||
clicon_xmldb_rpc(clicon_handle h)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if ((s = clicon_option_str(h, "CLICON_XMLDB_RPC")) == NULL)
|
||||
return -1;
|
||||
return atoi(s);
|
||||
}
|
||||
|
||||
/*! Get xmldb inet address */
|
||||
char *
|
||||
clicon_xmldb_addr(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_XMLDB_ADDR");
|
||||
}
|
||||
|
||||
/*! Get port for xmldb address in case of AF_INET or AF_INET6 */
|
||||
uint16_t
|
||||
clicon_xmldb_port(clicon_handle h)
|
||||
{
|
||||
char *s;
|
||||
|
||||
if ((s = clicon_option_str(h, "CLICON_XMLDB_PORT")) == NULL)
|
||||
return -1;
|
||||
return atoi(s);
|
||||
}
|
||||
|
||||
/*! Get YANG specification
|
||||
* Must use hash functions directly since they are not strings.
|
||||
*/
|
||||
yang_spec *
|
||||
|
|
|
|||
|
|
@ -72,11 +72,6 @@ static const struct map_type2str msgmap[] = {
|
|||
{CLICON_MSG_CHANGE, "change"},
|
||||
{CLICON_MSG_SAVE, "save"},
|
||||
{CLICON_MSG_LOAD, "load"},
|
||||
{CLICON_MSG_COPY, "copy"},
|
||||
{CLICON_MSG_RM, "rm"},
|
||||
{CLICON_MSG_INITDB, "initdb"},
|
||||
{CLICON_MSG_LOCK, "lock"},
|
||||
{CLICON_MSG_UNLOCK, "unlock"},
|
||||
{CLICON_MSG_KILL, "kill"},
|
||||
{CLICON_MSG_DEBUG, "debug"},
|
||||
{CLICON_MSG_CALL, "call"},
|
||||
|
|
@ -193,7 +188,8 @@ msg_dump(struct clicon_msg *msg)
|
|||
}
|
||||
|
||||
int
|
||||
clicon_msg_send(int s, struct clicon_msg *msg)
|
||||
clicon_msg_send(int s,
|
||||
struct clicon_msg *msg)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
|
|
@ -337,8 +333,8 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
|
|||
int *sock0,
|
||||
const char *label)
|
||||
{
|
||||
int retval = -1;
|
||||
int s = -1;
|
||||
int retval = -1;
|
||||
int s = -1;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
clicon_debug(1, "Send %s msg to %s:%hu",
|
||||
|
|
|
|||
|
|
@ -284,116 +284,6 @@ clicon_rpc_load(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a request to backend to copy a file from one location to another
|
||||
* Note this assumes the backend can access these files and (usually) assumes
|
||||
* clients and servers have the access to the same filesystem.
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] filename1 src file
|
||||
* @param[in] filename2 dst file
|
||||
*/
|
||||
int
|
||||
clicon_rpc_copy(clicon_handle h,
|
||||
char *filename1,
|
||||
char *filename2)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_copy_encode(filename1, filename2,
|
||||
__FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a request to remove a file from backend file system
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] filename File to remove
|
||||
*/
|
||||
int
|
||||
clicon_rpc_rm(clicon_handle h,
|
||||
char *filename)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_rm_encode(filename, __FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a database initialization request to backend server
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
*/
|
||||
int
|
||||
clicon_rpc_initdb(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_initdb_encode(db, __FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a database lock request to backend server
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
*/
|
||||
int
|
||||
clicon_rpc_lock(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_lock_encode(db, __FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a database unlock request to backend server
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
*/
|
||||
int
|
||||
clicon_rpc_unlock(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_unlock_encode(db, __FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a kill session request to backend server
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] session_id Id of session to kill
|
||||
|
|
|
|||
|
|
@ -58,52 +58,6 @@
|
|||
#include "clixon_proto.h"
|
||||
#include "clixon_proto_encode.h"
|
||||
|
||||
/* Generic encode/decode functions for exactly one C-string (str)
|
||||
*/
|
||||
static struct clicon_msg *
|
||||
clicon_msg_1str_encode(char *str, enum clicon_msg_type op, const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
int hdrlen = sizeof(*msg);
|
||||
uint16_t len;
|
||||
int p;
|
||||
|
||||
assert(str);
|
||||
p = 0;
|
||||
len = sizeof(*msg) + strlen(str) + 1;
|
||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
memset(msg, 0, len);
|
||||
/* hdr */
|
||||
msg->op_type = htons(op);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
strncpy(msg->op_body+p, str, len-p-hdrlen);
|
||||
p += strlen(str)+1;
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int
|
||||
clicon_msg_1str_decode(struct clicon_msg *msg,
|
||||
char **str,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
if ((*str = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*str)+1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
|
||||
uint32_t snapshot, uint32_t startup,
|
||||
|
|
@ -544,141 +498,6 @@ clicon_msg_load_decode(struct clicon_msg *msg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_initdb_encode(char *filename, const char *label)
|
||||
{
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, filename);
|
||||
return clicon_msg_1str_encode(filename, CLICON_MSG_INITDB, label);
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_initdb_decode(struct clicon_msg *msg,
|
||||
char **filename,
|
||||
const char *label)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = clicon_msg_1str_decode(msg, filename, label);
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, *filename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_rm_encode(char *filename, const char *label)
|
||||
{
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, filename);
|
||||
return clicon_msg_1str_encode(filename, CLICON_MSG_RM, label);
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_rm_decode(struct clicon_msg *msg,
|
||||
char **filename,
|
||||
const char *label)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = clicon_msg_1str_decode(msg, filename, label);
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, *filename);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_copy_encode(char *filename_src, char *filename_dst,
|
||||
const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
int hdrlen = sizeof(*msg);
|
||||
uint16_t len;
|
||||
int p;
|
||||
|
||||
clicon_debug(2, "%s: filename_src: %s filename_dst: %s",
|
||||
__FUNCTION__,
|
||||
filename_src, filename_dst);
|
||||
p = 0;
|
||||
len = hdrlen + strlen(filename_src) + 1 + strlen(filename_dst) + 1;
|
||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
||||
return NULL;
|
||||
}
|
||||
memset(msg, 0, len);
|
||||
/* hdr */
|
||||
msg->op_type = htons(CLICON_MSG_COPY);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
strncpy(msg->op_body+p, filename_src, len-p-hdrlen);
|
||||
p += strlen(filename_src)+1;
|
||||
strncpy(msg->op_body+p, filename_dst, len-p-hdrlen);
|
||||
p += strlen(filename_dst)+1;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_copy_decode(struct clicon_msg *msg,
|
||||
char **filename_src, char **filename_dst,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
if ((*filename_src = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*filename_src)+1;
|
||||
|
||||
if ((*filename_dst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*filename_dst)+1;
|
||||
clicon_debug(2, "%s: filename_src: %s filename_dst: %s",
|
||||
__FUNCTION__,
|
||||
*filename_src, *filename_dst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_lock_encode(char *db, const char *label)
|
||||
{
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, db);
|
||||
return clicon_msg_1str_encode(db, CLICON_MSG_LOCK, label);
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_lock_decode(struct clicon_msg *msg,
|
||||
char **db,
|
||||
const char *label)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = clicon_msg_1str_decode(msg, db, label);
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, *db);
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_unlock_encode(char *db, const char *label)
|
||||
{
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, db);
|
||||
return clicon_msg_1str_encode(db, CLICON_MSG_UNLOCK, label);
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_unlock_decode(struct clicon_msg *msg,
|
||||
char **db,
|
||||
const char *label)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = clicon_msg_1str_decode(msg, db, label);
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, *db);
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_kill_encode(uint32_t session_id, const char *label)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,25 @@
|
|||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
* @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
|
||||
|
|
@ -33,10 +52,10 @@
|
|||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
#include <regex.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#if defined(HAVE_DEPOT_H) || defined(HAVE_QDBM_DEPOT_H)
|
||||
#ifdef HAVE_DEPOT_H
|
||||
#include <depot.h> /* qdb api */
|
||||
#else /* HAVE_QDBM_DEPOT_H */
|
||||
|
|
@ -57,7 +76,8 @@
|
|||
* @param[in] omode see man dpopen
|
||||
*/
|
||||
static int
|
||||
db_init_mode(char *file, int omode)
|
||||
db_init_mode(char *file,
|
||||
int omode)
|
||||
{
|
||||
DEPOT *dp;
|
||||
|
||||
|
|
@ -85,6 +105,16 @@ db_init(char *file)
|
|||
return db_init_mode(file, DP_OWRITER | DP_OCREAT ); /* DP_OTRUNC? */
|
||||
}
|
||||
|
||||
int
|
||||
db_delete(char *file)
|
||||
{
|
||||
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
|
||||
|
|
@ -99,7 +129,7 @@ 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){
|
||||
if ((dp = dpopen(file, DP_OWRITER|DP_OLCKNB , 0)) == NULL){
|
||||
clicon_err(OE_DB, 0, "db_set: dpopen(%s): %s",
|
||||
file,
|
||||
dperrmsg(dpecode));
|
||||
|
|
@ -263,7 +293,6 @@ db_del(char *file, char *key)
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Check if entry in database exists
|
||||
* @param[in] file database file
|
||||
* @param[in] key database key
|
||||
|
|
@ -457,6 +486,78 @@ db_sanitize(char *rx, const char *label)
|
|||
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, dpecode, "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, dpecode, "dbopen: %s",
|
||||
dperrmsg(dpecode));
|
||||
return -1;
|
||||
}
|
||||
sleep(1000000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* Test program */
|
||||
|
||||
#endif /* DEPOT */
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ struct db_pair {
|
|||
*/
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -45,19 +45,23 @@
|
|||
* The given string is split into a vector where the delimiter can be
|
||||
* any of the characters in the specified delimiter string.
|
||||
*
|
||||
* See also clicon_strsplit() which is similar by operates on a full string
|
||||
* delimiter rather than individual character delimiters.
|
||||
*
|
||||
* The vector returned is one single memory chunk that must be unchunked
|
||||
* by the caller
|
||||
*
|
||||
* @param string String to be split
|
||||
* @param delim String of delimiter characters
|
||||
* @param nvec Number of entries in returned vector
|
||||
* @param label Chunk label for returned vector
|
||||
* @param[in] string String to be split
|
||||
* @param[in] delim String of delimiter characters
|
||||
* @param[out] nvec Number of entries in returned vector
|
||||
* @param[in] label Chunk label for returned vector
|
||||
* @retval vec Vector of strings. Free with unchunk
|
||||
* @retval NULL Error
|
||||
* @see clicon_strsplit Operates on full string delimiters rather than
|
||||
* individual character delimiters.
|
||||
*/
|
||||
char **
|
||||
clicon_sepsplit (char *string, char *delim, int *nvec, const char *label)
|
||||
clicon_sepsplit (char *string,
|
||||
char *delim,
|
||||
int *nvec,
|
||||
const char *label)
|
||||
{
|
||||
int idx;
|
||||
size_t siz;
|
||||
|
|
@ -94,19 +98,25 @@ clicon_sepsplit (char *string, char *delim, int *nvec, const char *label)
|
|||
* the full delimiter string. The matched delimiters are not part of the
|
||||
* resulting vector.
|
||||
*
|
||||
* See also clicon_sepsplit() which is similar by operates on individual
|
||||
* character delimiters rather then a full string delimiter.
|
||||
* See also clicon_sepsplit() which is similar
|
||||
*
|
||||
* The vector returned is one single memory chunk that must be unchunked
|
||||
* by the caller
|
||||
*
|
||||
* @param string String to be split
|
||||
* @param delim String of delimiter characters
|
||||
* @param nvec Number of entries in returned vector
|
||||
* @param label Chunk label for returned vector
|
||||
* @param[in] string String to be split
|
||||
* @param[in] delim String of delimiter characters
|
||||
* @param[out] nvec Number of entries in returned vector
|
||||
* @param[in] label Chunk label for returned vector
|
||||
* @retval vec Vector of strings. Free with unchunk
|
||||
* @retval NULL Error
|
||||
* @see clicon_sepsplit Operates on individual character delimiters rather
|
||||
* than full string delimiter.
|
||||
*/
|
||||
char **
|
||||
clicon_strsplit (char *string, char *delim, int *nvec, const char *label)
|
||||
clicon_strsplit (char *string,
|
||||
char *delim,
|
||||
int *nvec,
|
||||
const char *label)
|
||||
{
|
||||
int idx;
|
||||
size_t siz;
|
||||
|
|
@ -142,15 +152,18 @@ clicon_strsplit (char *string, char *delim, int *nvec, const char *label)
|
|||
return vec;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Concatenate elements of a string array into a string. An optiona delimiter
|
||||
* string can be specified which will be inserted betwen each element.
|
||||
* Resulting string is chunk:ed using the specified group label and need to be
|
||||
* unchunked by the caller
|
||||
/*! Concatenate elements of a string array into a string.
|
||||
* An optional delimiter string can be specified which will be inserted betwen
|
||||
* each element.
|
||||
* @param[in] label Chunk label for returned vector
|
||||
* @retval str Joined string. Free with unchunk()
|
||||
* @retval NULL Failure
|
||||
*/
|
||||
char *
|
||||
clicon_strjoin (int argc, char **argv, char *delim, const char *label)
|
||||
clicon_strjoin (int argc,
|
||||
char **argv,
|
||||
char *delim,
|
||||
const char *label)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
|
|
@ -176,12 +189,15 @@ clicon_strjoin (int argc, char **argv, char *delim, const char *label)
|
|||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Trim of whitespace in beginning and end of string.
|
||||
* A new string is returned, chunked with specified label
|
||||
/*! Trim whitespace in beginning and end of string.
|
||||
*
|
||||
* @param[in] label Chunk label for returned vector
|
||||
* @retval str Trimmed string. Free with unchunk()
|
||||
* @retval NULL Failure
|
||||
*/
|
||||
char *
|
||||
clicon_strtrim(char *str, const char *label)
|
||||
clicon_strtrim(char *str,
|
||||
const char *label)
|
||||
{
|
||||
char *start, *end, *new;
|
||||
|
||||
|
|
@ -200,15 +216,18 @@ clicon_strtrim(char *str, const char *label)
|
|||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* clicon_sep
|
||||
* given a string s, on format: a[b], separate it into two parts: a and b
|
||||
/*! Given a string s, on format: a[b], separate it into two parts: a and b
|
||||
* [] are separators.
|
||||
* alterative use:
|
||||
* a/b -> a and b (where sep = "/")
|
||||
* @param[in] label Chunk label for returned vector
|
||||
*/
|
||||
int
|
||||
clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0)
|
||||
clicon_sep(char *s,
|
||||
const char sep[2],
|
||||
const char *label,
|
||||
char **a0,
|
||||
char **b0)
|
||||
{
|
||||
char *a = NULL;
|
||||
char *b = NULL;
|
||||
|
|
@ -246,12 +265,12 @@ clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* strndup() for systems without it, such as xBSD
|
||||
/*! strndup() for systems without it, such as xBSD
|
||||
*/
|
||||
#ifndef HAVE_STRNDUP
|
||||
char *
|
||||
clicon_strndup (const char *str, size_t len)
|
||||
clicon_strndup (const char *str,
|
||||
size_t len)
|
||||
{
|
||||
char *new;
|
||||
size_t slen;
|
||||
|
|
@ -270,16 +289,19 @@ clicon_strndup (const char *str, size_t len)
|
|||
}
|
||||
#endif /* ! HAVE_STRNDUP */
|
||||
|
||||
/*
|
||||
* clicon_strmatch - Match string against regexp.
|
||||
/*! Match string against regexp.
|
||||
*
|
||||
* Returns -1 on failure, 0 on no matach or >0 (length of matching substring)
|
||||
* in case of a match. If a match pointer is given, the matching substring
|
||||
* If a match pointer is given, the matching substring
|
||||
* will be allocated 'match' will be pointing to it. The match string must
|
||||
* be free:ed by the application.
|
||||
* @retval -1 Failure
|
||||
* @retval 0 No match
|
||||
* @retval >0 Match: Length of matching substring
|
||||
*/
|
||||
int
|
||||
clicon_strmatch(const char *str, const char *regexp, char **match)
|
||||
clicon_strmatch(const char *str,
|
||||
const char *regexp,
|
||||
char **match)
|
||||
{
|
||||
size_t len;
|
||||
int status;
|
||||
|
|
@ -316,12 +338,14 @@ clicon_strmatch(const char *str, const char *regexp, char **match)
|
|||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* clicon_strsub - substitute pattern in string.
|
||||
* Returns new malloc:ed string on success or NULL on failure.
|
||||
/*! Substitute pattern in string.
|
||||
* @retval str Malloc:ed string on success, use free to deallocate
|
||||
* @retval NULL Failure.
|
||||
*/
|
||||
char *
|
||||
clicon_strsub(char *str, char *from, char *to)
|
||||
clicon_strsub(char *str,
|
||||
char *from,
|
||||
char *to)
|
||||
{
|
||||
char **vec;
|
||||
int nvec;
|
||||
|
|
|
|||
|
|
@ -588,7 +588,9 @@ xml_find_body(cxobj *xn, char *name)
|
|||
* (otherwise it will not)
|
||||
*/
|
||||
int
|
||||
xml_prune(cxobj *xparent, cxobj *xchild, int purge)
|
||||
xml_prune(cxobj *xparent,
|
||||
cxobj *xchild,
|
||||
int purge)
|
||||
{
|
||||
int i;
|
||||
cxobj *xc = NULL;
|
||||
|
|
@ -648,10 +650,13 @@ xml_free(cxobj *x)
|
|||
* @param[in] xn clicon xml tree
|
||||
* @param[in] level how many spaces to insert before each line
|
||||
* @param[in] prettyprint insert \n and spaces tomake the xml more readable.
|
||||
* See also clicon_xml2cbuf
|
||||
* @see clicon_xml2cbuf
|
||||
*/
|
||||
int
|
||||
clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint)
|
||||
clicon_xml2file(FILE *f,
|
||||
cxobj *xn,
|
||||
int level,
|
||||
int prettyprint)
|
||||
{
|
||||
cbuf *cb;
|
||||
int retval = -1;
|
||||
|
|
@ -689,7 +694,10 @@ clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint)
|
|||
* See also clicon_xml2file
|
||||
*/
|
||||
int
|
||||
clicon_xml2cbuf(cbuf *cb, cxobj *cx, int level, int prettyprint)
|
||||
clicon_xml2cbuf(cbuf *cb,
|
||||
cxobj *cx,
|
||||
int level,
|
||||
int prettyprint)
|
||||
{
|
||||
cxobj *xc;
|
||||
|
||||
|
|
@ -797,7 +805,9 @@ FSM(char *tag, char ch, int state)
|
|||
* May block
|
||||
*/
|
||||
int
|
||||
clicon_xml_parse_file(int fd, cxobj **cx, char *endtag)
|
||||
clicon_xml_parse_file(int fd,
|
||||
cxobj **cx,
|
||||
char *endtag)
|
||||
{
|
||||
int len = 0;
|
||||
char ch;
|
||||
|
|
@ -869,7 +879,8 @@ clicon_xml_parse_file(int fd, cxobj **cx, char *endtag)
|
|||
* cxobj *cx = NULL;
|
||||
* str = strdup(...);
|
||||
* str0 = str;
|
||||
* clicon_xml_parse_string(&str0, &cx)
|
||||
* if (clicon_xml_parse_string(&str0, &cx) < 0)
|
||||
* err;
|
||||
* free(str0);
|
||||
* xml_free(cx);
|
||||
* @endcode
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
570
lib/src/clixon_xml_db_rpc.c
Normal file
570
lib/src/clixon_xml_db_rpc.c
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
CLIXON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLIXON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
* XML database
|
||||
* TODO: xmldb_del: or dbxml_put_xkey delete
|
||||
*/
|
||||
#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 <assert.h>
|
||||
#include <syslog.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_qdb.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
#include "clixon_xml_db.h"
|
||||
#include "clixon_xml_db_rpc.h"
|
||||
|
||||
static int
|
||||
xmldb_rpc(clicon_handle h,
|
||||
char *data,
|
||||
size_t len,
|
||||
char *retdata,
|
||||
size_t *retlen
|
||||
)
|
||||
{
|
||||
int retval = -1;
|
||||
char *dst;
|
||||
uint16_t port;
|
||||
int s = -1;
|
||||
struct sockaddr_in addr;
|
||||
|
||||
if ((dst = clicon_xmldb_addr(h)) == NULL){
|
||||
clicon_err(OE_CFG, errno, "CLICON_XMLDB_ADDR option not set");
|
||||
goto done;
|
||||
}
|
||||
if ((port = clicon_xmldb_port(h)) == 0){
|
||||
clicon_err(OE_CFG, errno, "CLICON_XMLDB_PORT option not set");
|
||||
goto done;
|
||||
}
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
if (inet_pton(addr.sin_family, dst, &addr.sin_addr) != 1)
|
||||
goto done; /* Could check getaddrinfo */
|
||||
if ((s = socket(addr.sin_family, SOCK_STREAM, 0)) < 0) {
|
||||
clicon_err(OE_CFG, errno, "socket");
|
||||
return -1;
|
||||
}
|
||||
if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0){
|
||||
clicon_err(OE_CFG, errno, "connecting socket inet4");
|
||||
close(s);
|
||||
goto done;
|
||||
}
|
||||
if (write(s, data, len) < 0){
|
||||
clicon_err(OE_UNIX, errno, "write");
|
||||
goto done;
|
||||
}
|
||||
if ((*retlen = read(s, retdata, *retlen)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "write");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
if (debug > 1)
|
||||
fprintf(stderr, "%s: \"%s\"\n", __FUNCTION__, retdata);
|
||||
done:
|
||||
if (s != -1)
|
||||
close(s);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send put request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running|candidate
|
||||
* @retval 0
|
||||
*/
|
||||
int
|
||||
xmldb_put_rpc(clicon_handle h,
|
||||
char *db,
|
||||
cxobj *xt,
|
||||
enum operation_type op)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
char *opstr;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><put>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
if (op){
|
||||
switch (op){
|
||||
case OP_REPLACE:
|
||||
opstr = "replace";
|
||||
break;
|
||||
case OP_MERGE:
|
||||
opstr = "merge";
|
||||
break;
|
||||
case OP_NONE:
|
||||
opstr = "none";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cprintf(cb, "<default-operation>%s</default-operation>", opstr);
|
||||
}
|
||||
cprintf(cb, "<config>");
|
||||
if (clicon_xml2cbuf(cb, xt, 0, 1) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "</config>");
|
||||
cprintf(cb, "</put></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send put xkey request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running|candidate
|
||||
* @retval 0
|
||||
*/
|
||||
int
|
||||
xmldb_put_xkey_rpc(clicon_handle h,
|
||||
char *db,
|
||||
char *xk,
|
||||
char *val,
|
||||
enum operation_type op)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
char *opstr;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><put-xkey>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
if (op){
|
||||
switch (op){
|
||||
case OP_REPLACE:
|
||||
opstr = "replace";
|
||||
break;
|
||||
case OP_MERGE:
|
||||
opstr = "merge";
|
||||
break;
|
||||
case OP_NONE:
|
||||
opstr = "none";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cprintf(cb, "<default-operation>%s</default-operation>", opstr);
|
||||
}
|
||||
cprintf(cb, "<xkey>%s</xkey>", xk);
|
||||
cprintf(cb, "<value>%s</value>", val);
|
||||
cprintf(cb, "</put-xkey></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send get request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running|candidate
|
||||
* @retval 0
|
||||
*/
|
||||
int
|
||||
xmldb_get_rpc(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
int vector,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
cxobj *xt=NULL;
|
||||
cxobj *xc;
|
||||
int i;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><get>");
|
||||
cprintf(cb, "<source><%s/></source>", db);
|
||||
if (xpath)
|
||||
cprintf(cb, "<xpath>%s</xpath>", xpath);
|
||||
if (vector)
|
||||
cprintf(cb, "<vector/>");
|
||||
cprintf(cb, "</get></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
goto done;
|
||||
if (vector){
|
||||
i=0;
|
||||
if ((*xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
||||
(*xvec)[i++] = xc;
|
||||
}
|
||||
*xlen = i;
|
||||
*xtop = xt;
|
||||
xt = NULL;
|
||||
}
|
||||
else{
|
||||
if ((xc = xml_child_i(xt, 0)) != NULL){
|
||||
xml_prune(xt, xc, 0); /* kludge to remove top-level tag (eg top/clicon) */
|
||||
xml_parent_set(xc, NULL);
|
||||
if (debug > 1)
|
||||
clicon_xml2file(stderr, xc, 0, 1);
|
||||
}
|
||||
*xtop = xc;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! 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_rpc(clicon_handle h,
|
||||
char *from,
|
||||
char *to)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
cxobj *xt = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><copy>");
|
||||
cprintf(cb, "<source><%s/></source>", from);
|
||||
cprintf(cb, "<target><%s/></target>", to);
|
||||
cprintf(cb, "</copy></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 0;
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
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_rpc(clicon_handle h,
|
||||
char *db,
|
||||
int id)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
cxobj *xt = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><lock>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
cprintf(cb, "<id><%u/></id>", id);
|
||||
cprintf(cb, "</lock></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 0;
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
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_rpc(clicon_handle h,
|
||||
char *db,
|
||||
int id)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
cxobj *xt = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><unlock>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
cprintf(cb, "<id><%u/></id>", id);
|
||||
cprintf(cb, "</unlock></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 0;
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Check if database is locked
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Database
|
||||
* @retval -1 Error
|
||||
* @retval pid Process id if locked
|
||||
*/
|
||||
int
|
||||
xmldb_islocked_rpc(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
cxobj *xt = NULL;
|
||||
cxobj *x;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><islocked>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
cprintf(cb, "</islocked></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//unlocked"))
|
||||
retval = 0;
|
||||
else
|
||||
if ((x=xpath_first(xt, "//locked")) != NULL)
|
||||
retval = atoi(xml_body(x));
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send exists request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running|candidate
|
||||
* @retval -1 Error
|
||||
* @retval 0 No it does not exist
|
||||
* @retval 1 Yes it exists
|
||||
*/
|
||||
int
|
||||
xmldb_exists_rpc(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
cxobj *xt = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><exists>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
cprintf(cb, "</exists></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 1;
|
||||
else
|
||||
retval = 0;
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Send delete request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running|candidate
|
||||
* @retval 0
|
||||
*/
|
||||
int
|
||||
xmldb_delete_rpc(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><delete>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
cprintf(cb, "</delete></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send init request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running|candidate
|
||||
* @retval 0
|
||||
*/
|
||||
int
|
||||
xmldb_init_rpc(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
char retbuf[BUFSIZ];
|
||||
char *rb = retbuf;
|
||||
size_t retlen = sizeof(retbuf);
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><init>");
|
||||
cprintf(cb, "<target><%s/></target>", db);
|
||||
cprintf(cb, "</init></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1910,7 +1910,6 @@ yang_config(yang_stmt *ys)
|
|||
* @param f file to print to (if one of print options are enabled)
|
||||
* @param printspec print database (YANG) specification as read from file
|
||||
*/
|
||||
|
||||
int
|
||||
yang_spec_main(clicon_handle h,
|
||||
FILE *f,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue