Change internal protocol from clicon_proto.h to netconf.
This commit is contained in:
parent
2e09f54d12
commit
2fcefda831
66 changed files with 3012 additions and 5141 deletions
|
|
@ -69,7 +69,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_xml_db_rpc.c
|
||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
|
||||
|
||||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
|
|||
*/
|
||||
|
||||
/* top: json -> value is also possible */
|
||||
json : object J_EOF { clicon_debug(1,"json->object"); YYACCEPT; }
|
||||
json : value J_EOF { clicon_debug(1,"json->object"); YYACCEPT; }
|
||||
;
|
||||
|
||||
value : J_TRUE { json_current_body(_JY, "true");}
|
||||
|
|
|
|||
|
|
@ -196,10 +196,6 @@ clicon_option_default(clicon_hash_t *copt)
|
|||
if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0)
|
||||
goto catch;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_XMLDB_RPC")){
|
||||
if (hash_add(copt, "CLICON_XMLDB_RPC", "0", strlen("0")+1) < 0)
|
||||
goto catch;
|
||||
}
|
||||
retval = 0;
|
||||
catch:
|
||||
unchunk_group(__FUNCTION__);
|
||||
|
|
@ -616,35 +612,6 @@ 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 0; /* default 0 */
|
||||
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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@
|
|||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_sig.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
#include "clixon_proto.h"
|
||||
#include "clixon_proto_encode.h"
|
||||
|
||||
|
|
@ -80,20 +82,8 @@ struct map_type2str{
|
|||
|
||||
/* Mapping between yang keyword string <--> clicon constants */
|
||||
static const struct map_type2str msgmap[] = {
|
||||
{CLICON_MSG_COMMIT, "commit"},
|
||||
{CLICON_MSG_VALIDATE, "validate"},
|
||||
{CLICON_MSG_NETCONF, "netconf"},
|
||||
{CLICON_MSG_CHANGE, "change"},
|
||||
{CLICON_MSG_XMLPUT, "xmlput"},
|
||||
{CLICON_MSG_SAVE, "save"},
|
||||
{CLICON_MSG_LOAD, "load"},
|
||||
{CLICON_MSG_COPY, "copy"},
|
||||
{CLICON_MSG_KILL, "kill"},
|
||||
{CLICON_MSG_DEBUG, "debug"},
|
||||
{CLICON_MSG_CALL, "call"},
|
||||
{CLICON_MSG_SUBSCRIPTION, "subscription"},
|
||||
{CLICON_MSG_OK, "ok"},
|
||||
{CLICON_MSG_NOTIFY, "notify"},
|
||||
{CLICON_MSG_ERR, "err"},
|
||||
{-1, NULL},
|
||||
};
|
||||
|
||||
|
|
@ -234,17 +224,14 @@ clicon_msg_send(int s,
|
|||
* Now, ^C will interrupt the whole process, and this may not be what you want.
|
||||
*
|
||||
* @param[in] s UNIX domain socket to communicate with backend
|
||||
* @param[out] msg CLICON msg data reply structure. allocated using CLICON chunks,
|
||||
* freed by caller with unchunk*(...,label)
|
||||
* @param[out] msg CLICON msg data reply structure. Free with free()
|
||||
* @param[out] eof Set if eof encountered
|
||||
* @param[in] label Label used in chunk allocation and deallocation.
|
||||
* Note: caller must ensure that s is closed if eof is set after call.
|
||||
*/
|
||||
int
|
||||
clicon_msg_rcv(int s,
|
||||
struct clicon_msg **msg,
|
||||
int *eof,
|
||||
const char *label)
|
||||
struct clicon_msg **msg,
|
||||
int *eof)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg hdr;
|
||||
|
|
@ -273,8 +260,8 @@ clicon_msg_rcv(int s,
|
|||
mlen = ntohs(hdr.op_len);
|
||||
clicon_debug(2, "%s: rcv msg seq=%d, len=%d",
|
||||
__FUNCTION__, ntohs(hdr.op_type), mlen);
|
||||
if ((*msg = (struct clicon_msg *)chunk(mlen, label)) == NULL){
|
||||
clicon_err(OE_CFG, errno, "%s: chunk", __FUNCTION__);
|
||||
if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){
|
||||
clicon_err(OE_CFG, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memcpy(*msg, &hdr, hlen);
|
||||
|
|
@ -296,17 +283,21 @@ clicon_msg_rcv(int s,
|
|||
}
|
||||
|
||||
|
||||
/*! Connect to server, send an clicon_msg message and wait for result.
|
||||
* Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close.
|
||||
* NOTE: this is dependent on unix domain
|
||||
/*! Connect to server, send a clicon_msg message and wait for result using unix socket
|
||||
*
|
||||
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
|
||||
* @param[in] sockpath Unix domain file path
|
||||
* @param[out] retdata Returned data as string netconf xml tree.
|
||||
* @param[out] sock0 Return socket in case of asynchronous notify
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see clicon_rpc But this is one-shot rpc: open, send, get reply and close.
|
||||
*/
|
||||
int
|
||||
clicon_rpc_connect_unix(struct clicon_msg *msg,
|
||||
char *sockpath,
|
||||
char **data,
|
||||
uint16_t *datalen,
|
||||
int *sock0,
|
||||
const char *label)
|
||||
char **retdata,
|
||||
int *sock0)
|
||||
{
|
||||
int retval = -1;
|
||||
int s = -1;
|
||||
|
|
@ -325,7 +316,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
|
|||
}
|
||||
if ((s = clicon_connect_unix(sockpath)) < 0)
|
||||
goto done;
|
||||
if (clicon_rpc(s, msg, data, datalen, label) < 0)
|
||||
if (clicon_rpc(s, msg, retdata) < 0)
|
||||
goto done;
|
||||
if (sock0 != NULL)
|
||||
*sock0 = s;
|
||||
|
|
@ -336,17 +327,23 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Connect to server, send an clicon_msg message and wait for result using an inet socket
|
||||
* Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close.
|
||||
/*! Connect to server, send a clicon_msg message and wait for result using an inet socket
|
||||
* This uses unix domain socket communication
|
||||
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
|
||||
* @param[in] dst IPv4 address
|
||||
* @param[in] port TCP port
|
||||
* @param[out] retdata Returned data as string netconf xml tree.
|
||||
* @param[out] sock0 Return socket in case of asynchronous notify
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see clicon_rpc But this is one-shot rpc: open, send, get reply and close.
|
||||
*/
|
||||
int
|
||||
clicon_rpc_connect_inet(struct clicon_msg *msg,
|
||||
char *dst,
|
||||
uint16_t port,
|
||||
char **data,
|
||||
uint16_t *datalen,
|
||||
int *sock0,
|
||||
const char *label)
|
||||
char **retdata,
|
||||
int *sock0)
|
||||
{
|
||||
int retval = -1;
|
||||
int s = -1;
|
||||
|
|
@ -371,7 +368,7 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
|
|||
close(s);
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc(s, msg, data, datalen, label) < 0)
|
||||
if (clicon_rpc(s, msg, retdata) < 0)
|
||||
goto done;
|
||||
if (sock0 != NULL)
|
||||
*sock0 = s;
|
||||
|
|
@ -391,29 +388,25 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
|
|||
*
|
||||
* @param[in] s Socket to communicate with backend
|
||||
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
|
||||
* @param[out] data Returned data as byte-strin exclusing header.
|
||||
* Deallocate w unchunk...(..., label)
|
||||
* @param[out] datalen Length of returned data
|
||||
* @param[in] label Label used in chunk allocation.
|
||||
* @param[out] xret Returned data as netconf xml tree.
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clicon_rpc(int s,
|
||||
struct clicon_msg *msg,
|
||||
char **data,
|
||||
uint16_t *datalen,
|
||||
const char *label)
|
||||
clicon_rpc(int s,
|
||||
struct clicon_msg *msg,
|
||||
char **ret)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *reply;
|
||||
int eof;
|
||||
uint32_t err;
|
||||
uint32_t suberr;
|
||||
char *reason;
|
||||
enum clicon_msg_type type;
|
||||
char *data = NULL;
|
||||
cxobj *cx = NULL;
|
||||
|
||||
if (clicon_msg_send(s, msg) < 0)
|
||||
goto done;
|
||||
if (clicon_msg_rcv(s, &reply, &eof, label) < 0)
|
||||
if (clicon_msg_rcv(s, &reply, &eof) < 0)
|
||||
goto done;
|
||||
if (eof){
|
||||
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
|
||||
|
|
@ -423,32 +416,38 @@ clicon_rpc(int s,
|
|||
}
|
||||
type = ntohs(reply->op_type);
|
||||
switch (type){
|
||||
case CLICON_MSG_OK:
|
||||
if (data != NULL) {
|
||||
*data = reply->op_body;
|
||||
*datalen = ntohs(reply->op_len) - sizeof(*reply);
|
||||
}
|
||||
break;
|
||||
case CLICON_MSG_ERR:
|
||||
if (clicon_msg_err_decode(reply, &err, &suberr, &reason, label) < 0)
|
||||
goto done;
|
||||
if (debug)
|
||||
clicon_err(err, suberr, "%s msgtype:%hu", reason, ntohs(msg->op_type));
|
||||
else
|
||||
clicon_err(err, suberr, "%s", reason);
|
||||
goto done;
|
||||
case CLICON_MSG_NETCONF: /* ok or rpc-error expected */
|
||||
data = reply->op_body; /* assume string */
|
||||
break;
|
||||
default:
|
||||
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %hu",
|
||||
__FUNCTION__, type);
|
||||
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %s",
|
||||
__FUNCTION__, msg_type2str(type));
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
if (ret && data)
|
||||
if ((*ret = strdup(data)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cx)
|
||||
xml_free(cx);
|
||||
if (reply)
|
||||
free(reply);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a clicon_msg message as reply to a clicon rpc request
|
||||
*
|
||||
* @param[in] s Socket to communicate with client
|
||||
* @param[in] type Clicon msg operation, see enum clicon_msg_type
|
||||
* @param[in] data Returned data as byte-string.
|
||||
* @param[in] datalen Length of returned data XXX may be unecessary if always string?
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
send_msg_reply(int s,
|
||||
uint16_t type,
|
||||
|
|
@ -475,57 +474,160 @@ send_msg_reply(int s,
|
|||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
send_msg_ok(int s)
|
||||
{
|
||||
return send_msg_reply(s, CLICON_MSG_OK, NULL, 0);
|
||||
}
|
||||
|
||||
/*! Send a clicon_msg NOTIFY message asynchronously to client
|
||||
*
|
||||
* @param[in] s Socket to communicate with client
|
||||
* @param[in] level
|
||||
* @param[in] event
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
send_msg_notify(int s,
|
||||
int level,
|
||||
char *event)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
|
||||
if ((msg=clicon_msg_notify_encode(level, event, __FUNCTION__)) == NULL)
|
||||
if ((msg=clicon_msg_netconf_encode("<notification><event>%s</event></notification>", event)) == NULL)
|
||||
goto done;
|
||||
if (clicon_msg_send(s, msg) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a clicon_msg OK message as reply to a clicon rpc request
|
||||
*
|
||||
* @param[in] s Socket to communicate with client
|
||||
* @param[in] data Returned data as byte-string.
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note send as netconf message XXX remove clicon header
|
||||
*/
|
||||
int
|
||||
send_msg_err(int s, int err, int suberr, char *format, ...)
|
||||
send_msg_ok(int s,
|
||||
char *data)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if (data)
|
||||
cprintf(cb, "<rpc-reply><ok>%s</ok></rpc-reply>", data);
|
||||
else
|
||||
cprintf(cb, "<rpc-reply><ok/></rpc-reply>");
|
||||
if (send_msg_reply(s, CLICON_MSG_NETCONF, cbuf_get(cb), cbuf_len(cb)+1) < 0){
|
||||
if (errno == ECONNRESET)
|
||||
clicon_log(LOG_WARNING, "client rpc reset");
|
||||
goto done;
|
||||
}
|
||||
retval=0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a clicon_msg Error message as reply to a clicon rpc request
|
||||
*
|
||||
* @param[in] s Socket to communicate with client
|
||||
* @param[in] data Returned data as byte-string.
|
||||
* @param[in] datalen Length of returned data
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note send as netconf message XXX remove clicon header
|
||||
*/
|
||||
int
|
||||
send_msg_err(int s,
|
||||
int err,
|
||||
int suberr,
|
||||
char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *reason;
|
||||
int len;
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
char *info = NULL;
|
||||
int len;
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
va_start(args, format);
|
||||
len = vsnprintf(NULL, 0, format, args) + 1;
|
||||
va_end(args);
|
||||
if ((reason = (char *)chunk(len, __FUNCTION__)) == NULL)
|
||||
return -1;
|
||||
memset(reason, 0, len);
|
||||
if ((info = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(info, 0, len);
|
||||
va_start(args, format);
|
||||
vsnprintf(reason, len, format, args);
|
||||
vsnprintf(info, len, format, args);
|
||||
va_end(args);
|
||||
if ((msg=clicon_msg_err_encode(clicon_errno, clicon_suberrno,
|
||||
reason, __FUNCTION__)) == NULL)
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if (clicon_msg_send(s, msg) < 0)
|
||||
cprintf(cb, "<rpc-reply><rpc-error>"
|
||||
"<error-type>clixon</error-type>"
|
||||
"<error-tag>%d</error-tag>"
|
||||
"<error-message>%d</error-message>"
|
||||
"<error-info>%s</error-info>"
|
||||
"</rpc-error></rpc-reply>",
|
||||
err, suberr, info);
|
||||
if (send_msg_reply(s, CLICON_MSG_NETCONF, cbuf_get(cb), cbuf_len(cb)+1) < 0){
|
||||
if (errno == ECONNRESET)
|
||||
clicon_log(LOG_WARNING, "client rpc reset");
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (info)
|
||||
free(info);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a clicon_msg Error message as reply to a clicon rpc request
|
||||
*
|
||||
* @param[in] s Socket to communicate with client
|
||||
* @param[in] data Returned data as byte-string.
|
||||
* @param[in] datalen Length of returned data
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note send as netconf message XXX remove clicon header
|
||||
* XXX: see clicon_xml_parse
|
||||
*/
|
||||
int
|
||||
send_msg_netconf_reply(int s,
|
||||
char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
char *str = NULL;
|
||||
int len;
|
||||
int retval = -1;
|
||||
|
||||
va_start(args, format);
|
||||
len = vsnprintf(NULL, 0, format, args) + 1;
|
||||
va_end(args);
|
||||
if ((str = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(str, 0, len);
|
||||
va_start(args, format);
|
||||
len = vsnprintf(str, len, format, args);
|
||||
va_end(args);
|
||||
if (send_msg_reply(s, CLICON_MSG_NETCONF, str, len) < 0){
|
||||
if (errno == ECONNRESET)
|
||||
clicon_log(LOG_WARNING, "client rpc reset");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (str)
|
||||
free(str);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -56,38 +57,42 @@
|
|||
/* clicon */
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
#include "clixon_proto.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_proto_encode.h"
|
||||
#include "clixon_proto_client.h"
|
||||
|
||||
/*! Internal rpc function
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] msg Encoded message
|
||||
* @param[out] ret Return value from backend server (reply)
|
||||
* @param[out] retlen Length of return value
|
||||
* @param[in] msg Encoded message. Deallocate woth free
|
||||
* @param[out] xret Return value from backend as netconf xml tree. Free w xml_free
|
||||
* @param[inout] sock0 If pointer exists, do not close socket to backend on success
|
||||
* and return it here. For keeping a notify socket open
|
||||
* @param[in] label Chunk label for deallocating return values
|
||||
* Deallocate with unchunk_group(label)
|
||||
* Note: sock0 is if connection should be persistent, like a notification/subscribe api
|
||||
*/
|
||||
static int
|
||||
int
|
||||
clicon_rpc_msg(clicon_handle h,
|
||||
struct clicon_msg *msg,
|
||||
char **ret,
|
||||
uint16_t *retlen,
|
||||
int *sock0,
|
||||
const char *label)
|
||||
cxobj **xret0,
|
||||
int *sock0)
|
||||
{
|
||||
int retval = -1;
|
||||
char *sock;
|
||||
int port;
|
||||
char *retdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *x;
|
||||
uint32_t err;
|
||||
uint32_t suberr;
|
||||
char *errmsg = NULL;
|
||||
char *etype = NULL;
|
||||
|
||||
if ((sock = clicon_sock(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
|
||||
|
|
@ -96,7 +101,7 @@ clicon_rpc_msg(clicon_handle h,
|
|||
/* What to do if inet socket? */
|
||||
switch (clicon_sock_family(h)){
|
||||
case AF_UNIX:
|
||||
if (clicon_rpc_connect_unix(msg, sock, ret, retlen, sock0, label) < 0){
|
||||
if (clicon_rpc_connect_unix(msg, sock, &retdata, sock0) < 0){
|
||||
#if 0
|
||||
if (errno == ESHUTDOWN)
|
||||
/* Maybe could reconnect on a higher layer, but lets fail
|
||||
|
|
@ -115,12 +120,428 @@ clicon_rpc_msg(clicon_handle h,
|
|||
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
|
||||
goto done;
|
||||
}
|
||||
if (clicon_rpc_connect_inet(msg, sock, port, ret, retlen, sock0, label) < 0)
|
||||
if (clicon_rpc_connect_inet(msg, sock, port, &retdata, sock0) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
||||
#if 1 /* Parse return data and intercept clixon errors */
|
||||
if (retdata) {
|
||||
if (clicon_xml_parse_str(retdata, &xret) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xret, "/rpc-reply/ok") != NULL ||
|
||||
xpath_first(xret, "/rpc-reply/data") != NULL)
|
||||
;
|
||||
else if (xpath_first(xret, "/rpc-reply/rpc-error") != NULL){
|
||||
if ((x=xpath_first(xret, "//error-type"))!=NULL)
|
||||
etype = xml_body(x);
|
||||
if (etype&&strcmp(etype, "clixon")==0){
|
||||
if ((x=xpath_first(xret, "//error-tag"))!=NULL)
|
||||
err = atoi(xml_body(x));
|
||||
if ((x=xpath_first(xret, "//error-message"))!=NULL)
|
||||
suberr = atoi(xml_body(x));
|
||||
if ((x=xpath_first(xret, "//error-info"))!=NULL)
|
||||
errmsg = xml_body(x);
|
||||
if (debug)
|
||||
clicon_err(err, suberr, "%s msgtype:%hu", errmsg, ntohs(msg->op_type));
|
||||
else
|
||||
clicon_err(err, suberr, "%s", errmsg);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_PROTO, 0, "%s: unexpected reply",
|
||||
__FUNCTION__);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
if (xret0){
|
||||
*xret0 = xret;
|
||||
xret = NULL;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (retdata)
|
||||
free(retdata);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic xml netconf clicon rpc
|
||||
* Want to go over to use netconf directly between client and server,...
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] xi XML netconf tree
|
||||
* @param[out] xret Return XML, error or OK
|
||||
* @param[out] sp Socket pointer for notification, otherwise NULL
|
||||
*/
|
||||
int
|
||||
clicon_rpc_netconf_xml(clicon_handle h,
|
||||
cxobj *xin,
|
||||
cxobj **xret,
|
||||
int *sp)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode_xml(xin)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, xret, sp) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_rpc_generate_error(cxobj *xerr)
|
||||
{
|
||||
cxobj *x;
|
||||
char *etype="";
|
||||
char *etag="";
|
||||
char *emsg="";
|
||||
char *einfo="";
|
||||
|
||||
if ((x=xpath_first(xerr, "error-type"))!=NULL)
|
||||
etype = xml_body(x);
|
||||
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
|
||||
etag = xml_body(x);
|
||||
if ((x=xpath_first(xerr, "error-message"))!=NULL)
|
||||
emsg = xml_body(x);
|
||||
if ((x=xpath_first(xerr, "error-info"))!=NULL)
|
||||
einfo = xml_body(x);
|
||||
clicon_err(OE_XML, 0, "%s %s %s %s", etype, etag, emsg, einfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get database configuration
|
||||
* Same as clicon_proto_change just with a cvec instead of lvec
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @param[in] xpath XPath (or "")
|
||||
* @param[out] xt XML tree. must be freed by caller with xml_free
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error, fatal or xml
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
|
||||
* err;
|
||||
* if (xt)
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
clicon_rpc_get_config(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
cxobj **xt)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xd;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><get-config><source><%s/></source>", db);
|
||||
if (xpath && strlen(xpath))
|
||||
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
|
||||
cprintf(cb, "</get-config></rpc>");
|
||||
if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
if ((xd = xpath_first(xret, "//data/config")) == NULL)
|
||||
if ((xd = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
if (xt){
|
||||
if (xml_rm(xd) < 0)
|
||||
goto done;
|
||||
*xt = xd;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send database entries as XML to backend daemon
|
||||
* Same as clicon_proto_change just with a cvec instead of lvec
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
|
||||
* @param[in] api_path restconf API Path (or "")
|
||||
* @param[in] xml XML string. Ex: <a>..</a><b>...</b>
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* if (clicon_rpc_edit_config(h, "running", OP_MERGE, "/",
|
||||
* "<config><a>4</a></config>") < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
clicon_rpc_edit_config(clicon_handle h,
|
||||
char *db,
|
||||
enum operation_type op,
|
||||
char *api_path,
|
||||
char *xml)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cb, "<rpc><edit-config><target><%s/></target>", db);
|
||||
cprintf(cb, "<default-operation>%s</default-operation>",
|
||||
xml_operation2str(op));
|
||||
if (api_path && strlen(api_path))
|
||||
cprintf(cb, "<filter type=\"restconf\" select=\"%s\"/>", api_path);
|
||||
if (xml)
|
||||
cprintf(cb, "%s", xml);
|
||||
cprintf(cb, "</edit-config></rpc>");
|
||||
if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (msg)
|
||||
free(msg);
|
||||
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] db1 src database, eg "running"
|
||||
* @param[in] db2 dst database, eg "startup"
|
||||
* @code
|
||||
* if (clicon_rpc_copy_config(h, "running", "startup") < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
clicon_rpc_copy_config(clicon_handle h,
|
||||
char *db1,
|
||||
char *db2)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>", db1, db2)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send a request to backend to delete a config database
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db database, eg "running"
|
||||
* @code
|
||||
* if (clicon_rpc_delete_config(h, "startup") < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
clicon_rpc_delete_config(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><delete-config><target><%s/></target></delete-config></rpc>", db)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_rpc_lock(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><lock><target><%s/></target></lock></rpc>", db)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_rpc_unlock(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><unlock><target><%s/></target></unlock></rpc>", db)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_rpc_close_session(clicon_handle h)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><close-session/></rpc>")) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_rpc_kill_session(clicon_handle h,
|
||||
int session_id)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><kill-session><session-id>%d</session-id></kill-session></rpc>", session_id)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Send validate request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
clicon_rpc_validate(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *x;
|
||||
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><validate><source><%s/></source></validate></rpc>", db)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
x=xpath_first(xerr, "error-message");
|
||||
clicon_log(LOG_ERR, "Validate failed: \"%s\". Edit and try again or discard changes", x?xml_body(x):"");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (msg)
|
||||
free(msg);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -128,45 +549,69 @@ clicon_rpc_msg(clicon_handle h,
|
|||
* @param[in] h CLICON handle
|
||||
* @param[in] from name of 'from' database (eg "candidate")
|
||||
* @param[in] db name of 'to' database (eg "running")
|
||||
* @retval 0 Copy current->candidate
|
||||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
clicon_rpc_commit(clicon_handle h,
|
||||
char *from,
|
||||
char *to)
|
||||
clicon_rpc_commit(clicon_handle h)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg=clicon_msg_commit_encode(from, to, __FUNCTION__)) == NULL)
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><commit/></rpc>")) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Send validate request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @retval 0
|
||||
/*! Create a new notification subscription
|
||||
* @param[in] h Clicon handle
|
||||
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
|
||||
* @param{in] filter message filter, eg xpath for xml notifications
|
||||
* @param[out] s0 socket returned where notification mesages will appear
|
||||
* @note When using netconf create-subsrciption,status and format is not supported
|
||||
*/
|
||||
int
|
||||
clicon_rpc_validate(clicon_handle h,
|
||||
char *db)
|
||||
clicon_rpc_create_subscription(clicon_handle h,
|
||||
char *stream,
|
||||
char *filter,
|
||||
int *s0)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg=clicon_msg_validate_encode(db, __FUNCTION__)) == NULL)
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><create-subscription>"
|
||||
"<stream>%s</stream>"
|
||||
"<filter>%s</filter>"
|
||||
"</create-subscription></rpc>",
|
||||
stream?stream:"", filter?filter:"")) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -189,160 +634,24 @@ clicon_rpc_change(clicon_handle h,
|
|||
char *val)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
struct clicon_msg *msg = NULL;
|
||||
|
||||
if ((msg = clicon_msg_change_encode(db,
|
||||
op,
|
||||
key,
|
||||
val,
|
||||
val?strlen(val)+1:0,
|
||||
__FUNCTION__)) == NULL)
|
||||
val?strlen(val)+1:0)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (msg)
|
||||
free(msg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Send database entries as XML to backend daemon
|
||||
* Same as clicon_proto_change just with a cvec instead of lvec
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
|
||||
* @param[in] api_path restconf API Path (or "")
|
||||
* @param[in] xml XML string. Ex: <a>..</a><b>...</b>
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clicon_rpc_xmlput(clicon_handle h,
|
||||
char *db,
|
||||
enum operation_type op,
|
||||
char *api_path,
|
||||
char *xml)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg = clicon_msg_xmlput_encode(db,
|
||||
(uint32_t)op,
|
||||
api_path,
|
||||
xml,
|
||||
__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 database save request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Name of database
|
||||
* @param[in] snapshot Save to snapshot file
|
||||
* @param[in] filename Save to file (backend file-system)
|
||||
*/
|
||||
int
|
||||
clicon_rpc_save(clicon_handle h,
|
||||
char *db,
|
||||
int snapshot,
|
||||
char *filename)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_save_encode(db, snapshot, 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 database load request to backend daemon
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] replace 0: merge with existing data, 1:replace completely
|
||||
* @param[in] db Name of database
|
||||
* @param[in] filename Load from file (backend file-system)
|
||||
*/
|
||||
int
|
||||
clicon_rpc_load(clicon_handle h,
|
||||
int replace,
|
||||
char *db,
|
||||
char *filename)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_load_encode(replace, db, 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 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] db1 src database, eg "candidate"
|
||||
* @param[in] db2 dst database, eg "running"
|
||||
*/
|
||||
int
|
||||
clicon_rpc_copy(clicon_handle h,
|
||||
char *db1,
|
||||
char *db2)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_copy_encode(db1, db2, __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
|
||||
*/
|
||||
int
|
||||
clicon_rpc_kill(clicon_handle h,
|
||||
int session_id)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg=clicon_msg_kill_encode(session_id, __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 debug request to backend server
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] level Debug level
|
||||
|
|
@ -352,107 +661,28 @@ clicon_rpc_debug(clicon_handle h,
|
|||
int level)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
struct clicon_msg *msg = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
if ((msg=clicon_msg_debug_encode(level, __FUNCTION__)) == NULL)
|
||||
if ((msg = clicon_msg_netconf_encode("<rpc><debug><level>%d</level></debug></rpc>", level)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! An rpc call from a frontend module to a function in a backend module
|
||||
*
|
||||
* A CLI/netconf frontend module can make a functional call to a backend
|
||||
* module and get return value back.
|
||||
* The backend module needs to be specified (XXX would be nice to avoid this)
|
||||
* parameters can be sent, and value returned.
|
||||
* A function (func) must be defined in the backend module (plugin)
|
||||
* An example signature of such a downcall function is:
|
||||
* @code
|
||||
* char name[16];
|
||||
* char *ret;
|
||||
* uint16_t retlen;
|
||||
* clicon_rpc_call(h, 0, "my-backend-plugin", "my_fn", name, 16,
|
||||
* &ret, &retlen, __FUNCTION__);
|
||||
* unchunk_group(__FUNCTION__); # deallocate 'ret'
|
||||
* @endcode
|
||||
* Backend example function:
|
||||
* @code
|
||||
int
|
||||
downcall(clicon_handle h, uint16_t op, uint16_t len, void *arg,
|
||||
uint16_t *reply_data_len, void **reply_data)
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] op Generic application-defined operation
|
||||
* @param[in] plugin Name of backend plugin (XXX look in backend plugin dir)
|
||||
* @param[in] func Name of function i backend (ie downcall above) as string
|
||||
* @param[in] param Input parameter given to function (void* arg in downcall)
|
||||
* @param[in] paramlen Length of input parameter
|
||||
* @param[out] ret Returned data as byte-string. Deallocate w unchunk...(..., label)
|
||||
* @param[out] retlen Length of returned data
|
||||
* @param[in] label Label used in chunk (de)allocation. Use:
|
||||
* unchunk_group(label) to deallocate
|
||||
*/
|
||||
int
|
||||
clicon_rpc_call(clicon_handle h,
|
||||
uint16_t op,
|
||||
char *plugin,
|
||||
char *func,
|
||||
void *param,
|
||||
uint16_t paramlen,
|
||||
char **ret,
|
||||
uint16_t *retlen,
|
||||
const void *label)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *msg;
|
||||
|
||||
if ((msg = clicon_msg_call_encode(op, plugin, func,
|
||||
paramlen, param,
|
||||
label)) == NULL)
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
clicon_rpc_generate_error(xerr);
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, (char**)ret, retlen, NULL, label) < 0)
|
||||
}
|
||||
if (xpath_first(xret, "//rpc-reply/ok") == NULL){
|
||||
clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Create a new notification subscription
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] status 0: stop existing notification stream 1: start new stream.
|
||||
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
|
||||
* @param{in] filter message filter, eg xpath for xml notifications
|
||||
* @param[out] s0 socket returned where notification mesages will appear
|
||||
*/
|
||||
int
|
||||
clicon_rpc_subscription(clicon_handle h,
|
||||
int status,
|
||||
char *stream,
|
||||
enum format_enum format,
|
||||
char *filter,
|
||||
int *s0)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
int retval = -1;
|
||||
|
||||
if ((msg=clicon_msg_subscription_encode(status, stream, format, filter,
|
||||
__FUNCTION__)) == NULL)
|
||||
goto done;
|
||||
if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0)
|
||||
goto done;
|
||||
if (status == 0 && s0){
|
||||
close(*s0);
|
||||
*s0 = -1;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
done:
|
||||
if (msg)
|
||||
free(msg);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,107 +68,83 @@
|
|||
#include "clixon_sig.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_proto.h"
|
||||
#include "clixon_proto_encode.h"
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
|
||||
const char *label)
|
||||
clicon_msg_netconf_encode(char *format, ...)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
va_list args;
|
||||
int xmllen;
|
||||
int len;
|
||||
struct clicon_msg *msg = NULL;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int p;
|
||||
|
||||
clicon_debug(2, "%s: dbsrc: %s dbdst: %s",
|
||||
__FUNCTION__,
|
||||
dbsrc, dbdst);
|
||||
p = 0;
|
||||
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(dbsrc) + 1 +
|
||||
strlen(dbdst) + 1;
|
||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
||||
va_start(args, format);
|
||||
xmllen = vsnprintf(NULL, 0, format, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
len = hdrlen + xmllen;
|
||||
if ((msg = (struct clicon_msg *)malloc(len)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset(msg, 0, len);
|
||||
/* hdr */
|
||||
msg->op_type = htons(CLICON_MSG_COMMIT);
|
||||
msg->op_type = htons(CLICON_MSG_NETCONF);
|
||||
msg->op_len = htons(len);
|
||||
|
||||
/* body */
|
||||
strncpy(msg->op_body+p, dbsrc, len-p-hdrlen);
|
||||
p += strlen(dbsrc)+1;
|
||||
strncpy(msg->op_body+p, dbdst, len-p-hdrlen);
|
||||
p += strlen(dbdst)+1;
|
||||
va_start(args, format);
|
||||
vsnprintf(msg->op_body, xmllen, format, args);
|
||||
va_end(args);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_commit_decode(struct clicon_msg *msg,
|
||||
char **dbsrc, char **dbdst,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
if ((*dbsrc = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*dbsrc)+1;
|
||||
if ((*dbdst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*dbdst)+1;
|
||||
clicon_debug(2, "%s: dbsrc: %s dbdst: %s", __FUNCTION__, *dbsrc, *dbdst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_validate_encode(char *db, const char *label)
|
||||
clicon_msg_netconf_encode_xml(cxobj *xml)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int hdrlen = sizeof(*msg);
|
||||
struct clicon_msg *msg = NULL;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, db);
|
||||
len = sizeof(*msg) + strlen(db) + 1;
|
||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
||||
return NULL;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
memset(msg, 0, len);
|
||||
/* hdr */
|
||||
msg->op_type = htons(CLICON_MSG_VALIDATE);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
strncpy(msg->op_body, db, len-hdrlen);
|
||||
if (clicon_xml2cbuf(cb, xml, 0, 0) < 0)
|
||||
goto done;
|
||||
if ((msg = clicon_msg_netconf_encode("%s", cbuf_get(cb))) < 0)
|
||||
goto done;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_validate_decode(struct clicon_msg *msg, char **db, const char *label)
|
||||
clicon_msg_netconf_decode(struct clicon_msg *msg,
|
||||
cxobj **xml)
|
||||
{
|
||||
/* body */
|
||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, *db);
|
||||
return 0;
|
||||
}
|
||||
int retval = -1;
|
||||
char *xmlstr;
|
||||
|
||||
/* body */
|
||||
xmlstr = msg->op_body;
|
||||
if (clicon_xml_parse_str(xmlstr, xml) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_change_encode(char *db,
|
||||
uint32_t op,
|
||||
char *key,
|
||||
char *str,
|
||||
uint32_t str_len,
|
||||
const char *label)
|
||||
uint32_t str_len)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
|
|
@ -184,8 +160,8 @@ clicon_msg_change_encode(char *db,
|
|||
strlen(key) + str_len;
|
||||
if (str_len)
|
||||
len++; /* if str not null add end of string */
|
||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
||||
if ((msg = (struct clicon_msg *)malloc(len)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset(msg, 0, len);
|
||||
|
|
@ -264,661 +240,5 @@ clicon_msg_change_decode(struct clicon_msg *msg,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Encode xmlput / edit of database content
|
||||
* @param[in] db Name of database
|
||||
* @param[in] op set|merge|delete. See lv_op_t
|
||||
* @param[in] api_path restconf api path
|
||||
* @param[in] xml XML data string
|
||||
* @param[in] label Memory chunk label
|
||||
* @retval msg Encoded message
|
||||
* @retval NULL Error
|
||||
*/
|
||||
struct clicon_msg *
|
||||
clicon_msg_xmlput_encode(char *db,
|
||||
uint32_t op,
|
||||
char *api_path,
|
||||
char *xml,
|
||||
const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s",
|
||||
__FUNCTION__,
|
||||
op, db, api_path, xml);
|
||||
p = 0;
|
||||
hdrlen = sizeof(*msg);
|
||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(api_path) + 1 +strlen(xml) + 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_XMLPUT);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(op);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
||||
p += strlen(db)+1;
|
||||
strncpy(msg->op_body+p, api_path, len-p-hdrlen);
|
||||
p += strlen(api_path)+1;
|
||||
strncpy(msg->op_body+p, xml, len-p-hdrlen);
|
||||
p += strlen(xml)+1;
|
||||
return msg;
|
||||
}
|
||||
|
||||
/*! Decode xmlput / edit of database content
|
||||
* @param[in] msg Incoming message to be decoded
|
||||
* @param[out] db Name of database
|
||||
* @param[out] op set|merge|delete. See lv_op_t
|
||||
* @param[out] api_path restconf api path
|
||||
* @param[out] xml XML data string
|
||||
* @param[in] label Memory chunk label
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clicon_msg_xmlput_decode(struct clicon_msg *msg,
|
||||
char **db,
|
||||
uint32_t *op,
|
||||
char **api_path,
|
||||
char **xml,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*op = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*db)+1;
|
||||
if ((*api_path = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*api_path)+1;
|
||||
if ((*xml = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*xml)+1;
|
||||
clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s",
|
||||
__FUNCTION__,
|
||||
*op, *db, *api_path, *xml);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename,
|
||||
const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
clicon_debug(2, "%s: snapshot: %d db: %s filename: %s",
|
||||
__FUNCTION__,
|
||||
snapshot, db, filename);
|
||||
p = 0;
|
||||
hdrlen = sizeof(*msg);
|
||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1;
|
||||
if (!snapshot)
|
||||
len += strlen(filename) + 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_SAVE);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(snapshot);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
||||
p += strlen(db)+1;
|
||||
if (!snapshot){
|
||||
strncpy(msg->op_body+p, filename, len-p-hdrlen);
|
||||
p += strlen(filename)+1;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_save_decode(struct clicon_msg *msg,
|
||||
char **db, uint32_t *snapshot, char **filename,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*snapshot = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*db)+1;
|
||||
if (*snapshot == 0){
|
||||
if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*filename)+1;
|
||||
}
|
||||
clicon_debug(2, "%s: snapshot: %d db: %s filename: %s",
|
||||
__FUNCTION__,
|
||||
*snapshot, *db, *filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_load_encode(int replace, char *db, char *filename, const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
int hdrlen = sizeof(*msg);
|
||||
uint16_t len;
|
||||
uint32_t tmp;
|
||||
int p;
|
||||
|
||||
clicon_debug(2, "%s: replace: %d db: %s filename: %s",
|
||||
__FUNCTION__,
|
||||
replace, db, filename);
|
||||
p = 0;
|
||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(filename) + 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_LOAD);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(replace);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
||||
p += strlen(db)+1;
|
||||
strncpy(msg->op_body+p, filename, len-p-hdrlen);
|
||||
p += strlen(filename)+1;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_load_decode(struct clicon_msg *msg,
|
||||
int *replace,
|
||||
char **db,
|
||||
char **filename,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*replace = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*db)+1;
|
||||
if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*filename)+1;
|
||||
clicon_debug(2, "%s: %d db: %s filename: %s",
|
||||
__FUNCTION__,
|
||||
ntohs(msg->op_type),
|
||||
*db, *filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_copy_encode(char *db_src, char *db_dst,
|
||||
const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
int hdrlen = sizeof(*msg);
|
||||
uint16_t len;
|
||||
int p;
|
||||
|
||||
clicon_debug(2, "%s: db_src: %s db_dst: %s",
|
||||
__FUNCTION__,
|
||||
db_src, db_dst);
|
||||
p = 0;
|
||||
len = hdrlen + strlen(db_src) + 1 + strlen(db_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, db_src, len-p-hdrlen);
|
||||
p += strlen(db_src)+1;
|
||||
strncpy(msg->op_body+p, db_dst, len-p-hdrlen);
|
||||
p += strlen(db_dst)+1;
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_copy_decode(struct clicon_msg *msg,
|
||||
char **db_src, char **db_dst,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
if ((*db_src = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*db_src)+1;
|
||||
|
||||
if ((*db_dst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*db_dst)+1;
|
||||
clicon_debug(2, "%s: db_src: %s db_dst: %s",
|
||||
__FUNCTION__,
|
||||
*db_src, *db_dst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_kill_encode(uint32_t session_id, const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
clicon_debug(2, "%s: %d", __FUNCTION__, session_id);
|
||||
p = 0;
|
||||
len = sizeof(*msg) + sizeof(uint32_t);
|
||||
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_KILL);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(session_id);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
return msg;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_kill_decode(struct clicon_msg *msg,
|
||||
uint32_t *session_id,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*session_id = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *session_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_debug_encode(uint32_t level, const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
clicon_debug(2, "%s: %d", __FUNCTION__, label);
|
||||
p = 0;
|
||||
len = sizeof(*msg) + sizeof(uint32_t);
|
||||
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_DEBUG);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(level);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
return msg;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_debug_decode(struct clicon_msg *msg,
|
||||
uint32_t *level,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*level = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *level);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_call_encode(uint16_t op,
|
||||
char *plugin,
|
||||
char *func,
|
||||
uint16_t arglen,
|
||||
void *arg,
|
||||
const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
struct clicon_msg_call_req *req;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int len;
|
||||
|
||||
clicon_debug(2, "%s: %d plugin: %s func: %s arglen: %d",
|
||||
__FUNCTION__, op, plugin, func, arglen);
|
||||
len =
|
||||
hdrlen +
|
||||
sizeof(struct clicon_msg_call_req) +
|
||||
strlen(plugin) + 1 +
|
||||
strlen(func) + 1 +
|
||||
arglen;
|
||||
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_CALL);
|
||||
msg->op_len = htons(len);
|
||||
/* req */
|
||||
req = (struct clicon_msg_call_req *)msg->op_body;
|
||||
req->cr_len = htons(len - hdrlen);
|
||||
req->cr_op = htons(op);
|
||||
req->cr_plugin = req->cr_data;
|
||||
strncpy(req->cr_plugin, plugin, strlen(plugin));
|
||||
req->cr_func = req->cr_plugin + strlen(req->cr_plugin) + 1;
|
||||
strncpy(req->cr_func, func, strlen(func));
|
||||
req->cr_arglen = htons(arglen);
|
||||
req->cr_arg = req->cr_func + strlen(req->cr_func) + 1;
|
||||
memcpy(req->cr_arg, arg, arglen);
|
||||
|
||||
return msg;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_call_decode(struct clicon_msg *msg,
|
||||
struct clicon_msg_call_req **req,
|
||||
const char *label)
|
||||
{
|
||||
uint16_t len;
|
||||
struct clicon_msg_call_req *r;
|
||||
|
||||
r = (struct clicon_msg_call_req *)msg->op_body;
|
||||
len = ntohs(r->cr_len);
|
||||
if ((*req = chunk(len, label)) == NULL) {
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
memcpy(*req, r, len);
|
||||
(*req)->cr_len = ntohs(r->cr_len);
|
||||
(*req)->cr_op = ntohs(r->cr_op);
|
||||
(*req)->cr_arglen = ntohs(r->cr_arglen);
|
||||
(*req)->cr_plugin = (*req)->cr_data;
|
||||
(*req)->cr_func = (*req)->cr_plugin + strlen((*req)->cr_plugin) +1;
|
||||
(*req)->cr_arg = (*req)->cr_func + strlen((*req)->cr_func) +1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_subscription_encode(int status,
|
||||
char *stream,
|
||||
enum format_enum format,
|
||||
char *filter,
|
||||
const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int p;
|
||||
int tmp;
|
||||
|
||||
clicon_debug(2, "%s: %d %d %s %s", __FUNCTION__, status, format, stream, filter);
|
||||
p = 0;
|
||||
assert(filter);
|
||||
len = hdrlen + sizeof(uint32_t) + sizeof(uint32_t) + strlen(stream) + 1 + strlen(filter) + 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_SUBSCRIPTION);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(status);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(int));
|
||||
p += sizeof(int);
|
||||
|
||||
tmp = htonl(format);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(int));
|
||||
p += sizeof(int);
|
||||
|
||||
strncpy(msg->op_body+p, stream, len-p-hdrlen);
|
||||
p += strlen(stream)+1;
|
||||
|
||||
strncpy(msg->op_body+p, filter, len-p-hdrlen);
|
||||
p += strlen(filter)+1;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_subscription_decode(struct clicon_msg *msg,
|
||||
int *status,
|
||||
char **stream,
|
||||
enum format_enum *format,
|
||||
char **filter,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
int tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(int));
|
||||
*status = ntohl(tmp);
|
||||
p += sizeof(int);
|
||||
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(int));
|
||||
*format = ntohl(tmp);
|
||||
p += sizeof(int);
|
||||
|
||||
if ((*stream = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*stream)+1;
|
||||
|
||||
if ((*filter = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*filter)+1;
|
||||
clicon_debug(2, "%s: %d %s %d %s", __FUNCTION__, *status, *filter, *stream, *filter);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_notify_encode(int level, char *event, const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int p;
|
||||
int tmp;
|
||||
|
||||
clicon_debug(2, "%s: %d %s", __FUNCTION__, level, event);
|
||||
p = 0;
|
||||
hdrlen = sizeof(*msg);
|
||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(event) + 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_NOTIFY);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(level);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(int));
|
||||
p += sizeof(int);
|
||||
strncpy(msg->op_body+p, event, len-p-hdrlen);
|
||||
p += strlen(event)+1;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_notify_decode(struct clicon_msg *msg,
|
||||
int *level, char **event,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
int tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(int));
|
||||
*level = ntohl(tmp);
|
||||
p += sizeof(int);
|
||||
if ((*event = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*event)+1;
|
||||
clicon_debug(2, "%s: %d %s", __FUNCTION__, *level, *event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct clicon_msg *
|
||||
clicon_msg_err_encode(uint32_t err, uint32_t suberr, char *reason, const char *label)
|
||||
{
|
||||
struct clicon_msg *msg;
|
||||
uint16_t len;
|
||||
int hdrlen = sizeof(*msg);
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
clicon_debug(2, "%s: %d %d %s", __FUNCTION__, err, suberr, reason);
|
||||
p = 0;
|
||||
hdrlen = sizeof(*msg);
|
||||
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(reason) + 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_ERR);
|
||||
msg->op_len = htons(len);
|
||||
/* body */
|
||||
tmp = htonl(err);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
tmp = htonl(suberr);
|
||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
strncpy(msg->op_body+p, reason, len-p-hdrlen);
|
||||
p += strlen(reason)+1;
|
||||
|
||||
return msg;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
clicon_msg_err_decode(struct clicon_msg *msg,
|
||||
uint32_t *err, uint32_t *suberr, char **reason,
|
||||
const char *label)
|
||||
{
|
||||
int p;
|
||||
uint32_t tmp;
|
||||
|
||||
p = 0;
|
||||
/* body */
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*err = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
||||
*suberr = ntohl(tmp);
|
||||
p += sizeof(uint32_t);
|
||||
if ((*reason = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
p += strlen(*reason)+1;
|
||||
clicon_debug(2, "%s: %d %d %s",
|
||||
__FUNCTION__,
|
||||
*err, *suberr, *reason);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -465,6 +465,12 @@ xml_childvec_set(cxobj *x,
|
|||
return 0;
|
||||
}
|
||||
|
||||
cxobj **
|
||||
xml_childvec_get(cxobj *x)
|
||||
{
|
||||
return x->x_childvec;
|
||||
}
|
||||
|
||||
/*! Create new xml node given a name and parent. Free it with xml_free().
|
||||
*
|
||||
* @param[in] name Name of new
|
||||
|
|
@ -913,24 +919,26 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
cprintf(cb, "%s:", xml_namespace(cx));
|
||||
cprintf(cb, "%s", xml_name(cx));
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
|
||||
if (xml_type(xc) != CX_ATTR)
|
||||
continue;
|
||||
while ((xc = xml_child_each(cx, xc, CX_ATTR)) != NULL)
|
||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
||||
/* Check for special case <a/> instead of <a></a> */
|
||||
if (xml_body(cx)==NULL && xml_child_nr(cx)==0)
|
||||
cprintf(cb, "/>");
|
||||
else{
|
||||
cprintf(cb, ">");
|
||||
if (prettyprint && xml_body(cx)==NULL)
|
||||
cprintf(cb, "\n");
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
|
||||
if (xml_type(xc) == CX_ATTR)
|
||||
continue;
|
||||
else
|
||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
||||
}
|
||||
if (prettyprint && xml_body(cx)==NULL)
|
||||
cprintf(cb, "%*s", level*XML_INDENT, "");
|
||||
cprintf(cb, "</%s>", xml_name(cx));
|
||||
}
|
||||
cprintf(cb, ">");
|
||||
if (prettyprint && xml_body(cx)==NULL)
|
||||
cprintf(cb, "\n");
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
|
||||
if (xml_type(xc) == CX_ATTR)
|
||||
continue;
|
||||
else
|
||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
||||
}
|
||||
if (prettyprint && xml_body(cx)==NULL)
|
||||
cprintf(cb, "%*s", level*XML_INDENT, "");
|
||||
cprintf(cb, "</%s>", xml_name(cx));
|
||||
if (prettyprint)
|
||||
cprintf(cb, "\n");
|
||||
break;
|
||||
|
|
@ -956,7 +964,7 @@ xml_parse(char *str,
|
|||
}
|
||||
ya.ya_xparent = x_up;
|
||||
if (clixon_xml_parsel_init(&ya) < 0)
|
||||
goto done;
|
||||
goto done;
|
||||
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
|
||||
goto done;
|
||||
|
||||
|
|
@ -1079,7 +1087,6 @@ clicon_xml_parse_file(int fd,
|
|||
* @endcode
|
||||
* @see clicon_xml_parse_file
|
||||
* @note you need to free the xml parse tree after use, using xml_free()
|
||||
* Update: with yacc parser I dont think it changes,....
|
||||
*/
|
||||
int
|
||||
clicon_xml_parse_str(char *str,
|
||||
|
|
@ -1090,6 +1097,57 @@ clicon_xml_parse_str(char *str,
|
|||
return xml_parse(str, *cxtop);
|
||||
}
|
||||
|
||||
|
||||
/*! Read XML definition from variable argument string and parse it into parse-tree.
|
||||
*
|
||||
* Utility function using stdarg instead of static string.
|
||||
* @param[out] xml_top Top of XML parse tree. Will add extra top element called 'top'.
|
||||
* you must free it after use, using xml_free()
|
||||
* @param[in] format Pointer to string containing XML definition.
|
||||
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error with clicon_err called
|
||||
*
|
||||
* @code
|
||||
* cxobj *cx = NULL;
|
||||
* if (clicon_xml_parse(&cx, "<xml>%s</xml", 22) < 0)
|
||||
* err;
|
||||
* xml_free(cx);
|
||||
* @endcode
|
||||
* @see clicon_xml_parse_str
|
||||
* @note you need to free the xml parse tree after use, using xml_free()
|
||||
*/
|
||||
int
|
||||
clicon_xml_parse(cxobj **cxtop,
|
||||
char *format, ...)
|
||||
{
|
||||
int retval = -1;
|
||||
va_list args;
|
||||
char *str = NULL;
|
||||
int len;
|
||||
|
||||
va_start(args, format);
|
||||
len = vsnprintf(NULL, 0, format, args) + 1;
|
||||
va_end(args);
|
||||
if ((str = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(str, 0, len);
|
||||
va_start(args, format);
|
||||
len = vsnprintf(str, len, format, args) + 1;
|
||||
va_end(args);
|
||||
if ((*cxtop = xml_new("top", NULL)) == NULL)
|
||||
return -1;
|
||||
if (xml_parse(str, *cxtop) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (str)
|
||||
free(str);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Copy single xml node without copying children
|
||||
*/
|
||||
static int
|
||||
|
|
@ -1402,3 +1460,52 @@ xml_body_uint32(cxobj *xb,
|
|||
cv_free(cv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Map xml operation from string to enumeration
|
||||
* @param[in] xn XML node
|
||||
* @param[out] op "operation" attribute may change operation
|
||||
*/
|
||||
int
|
||||
xml_operation(char *opstr,
|
||||
enum operation_type *op)
|
||||
{
|
||||
if (strcmp("merge", opstr) == 0)
|
||||
*op = OP_MERGE;
|
||||
else if (strcmp("replace", opstr) == 0)
|
||||
*op = OP_REPLACE;
|
||||
else if (strcmp("create", opstr) == 0)
|
||||
*op = OP_CREATE;
|
||||
else if (strcmp("delete", opstr) == 0)
|
||||
*op = OP_DELETE;
|
||||
else if (strcmp("remove", opstr) == 0)
|
||||
*op = OP_REMOVE;
|
||||
else{
|
||||
clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
xml_operation2str(enum operation_type op)
|
||||
{
|
||||
switch (op){
|
||||
case OP_MERGE:
|
||||
return "merge";
|
||||
break;
|
||||
case OP_REPLACE:
|
||||
return "replace";
|
||||
break;
|
||||
case OP_CREATE:
|
||||
return "create";
|
||||
break;
|
||||
case OP_DELETE:
|
||||
return "delete";
|
||||
break;
|
||||
case OP_REMOVE:
|
||||
return "remove";
|
||||
break;
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,7 +121,6 @@
|
|||
#include "clixon_options.h"
|
||||
#include "clixon_xsl.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
#include "clixon_xml_db_rpc.h"
|
||||
#include "clixon_xml_db.h"
|
||||
|
||||
/*! Construct an xml key format from yang statement using wildcards for keys
|
||||
|
|
@ -417,6 +416,8 @@ static int _candidate_locked = 0;
|
|||
static int _startup_locked = 0;
|
||||
|
||||
/*! Lock database
|
||||
* @param[in] db Database name
|
||||
* @param[in] pid process/session-id
|
||||
*/
|
||||
static int
|
||||
db_lock(char *db,
|
||||
|
|
@ -433,6 +434,7 @@ db_lock(char *db,
|
|||
}
|
||||
|
||||
/*! Unlock database
|
||||
* @param[in] db Database name
|
||||
*/
|
||||
static int
|
||||
db_unlock(char *db)
|
||||
|
|
@ -446,6 +448,21 @@ db_unlock(char *db)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Unlock all databases locked by pid (eg process dies)
|
||||
* @param[in] pid process/session-id
|
||||
*/
|
||||
static int
|
||||
db_unlock_all(int pid)
|
||||
{
|
||||
if (_running_locked == pid)
|
||||
_running_locked = 0;
|
||||
if (_candidate_locked == pid)
|
||||
_candidate_locked = 0;
|
||||
if (_startup_locked == pid)
|
||||
_startup_locked = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! returns id of locker
|
||||
* @retval 0 Not locked
|
||||
* @retval >0 Id of locker
|
||||
|
|
@ -462,6 +479,8 @@ db_islocked(char *db)
|
|||
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"
|
||||
|
|
@ -959,8 +978,8 @@ xml_order(cxobj *x,
|
|||
* cxobj **xvec;
|
||||
* size_t xlen;
|
||||
* yang_spec *yspec = clicon_dbspec_yang(h);
|
||||
* if (xmldb_get_vec(dbname, "/interfaces/interface[name="eth"]", yspec,
|
||||
* &xt, &xvec, &xlen) < 0)
|
||||
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
|
||||
* &xt, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xv[i];
|
||||
|
|
@ -973,13 +992,13 @@ xml_order(cxobj *x,
|
|||
* @see xpath_vec
|
||||
* @see xmldb_get
|
||||
*/
|
||||
static int
|
||||
xmldb_get_local(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec0,
|
||||
size_t *xlen0)
|
||||
int
|
||||
xmldb_get(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec0,
|
||||
size_t *xlen0)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
|
|
@ -1021,9 +1040,10 @@ xmldb_get_local(clicon_handle h,
|
|||
/* If vectors are specified then filter out everything else,
|
||||
* otherwise return complete tree.
|
||||
*/
|
||||
if (xvec != NULL)
|
||||
if (xvec != NULL){
|
||||
for (i=0; i<xlen; i++)
|
||||
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
||||
}
|
||||
/* Top is special case */
|
||||
if (!xml_flag(xt, XML_FLAG_MARK))
|
||||
if (xml_tree_prune_unmarked(xt, NULL) < 0)
|
||||
|
|
@ -1036,6 +1056,7 @@ xmldb_get_local(clicon_handle h,
|
|||
*xlen0 = xlen;
|
||||
xlen = 0;
|
||||
}
|
||||
|
||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||
goto done;
|
||||
/* XXX does not work for top-level */
|
||||
|
|
@ -1045,6 +1066,7 @@ xmldb_get_local(clicon_handle h,
|
|||
goto done;
|
||||
if (debug)
|
||||
clicon_xml2file(stdout, xt, 0, 1);
|
||||
|
||||
*xtop = xt;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -1057,87 +1079,6 @@ xmldb_get_local(clicon_handle h,
|
|||
|
||||
}
|
||||
|
||||
/*! Get content of database using path.
|
||||
|
||||
* 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
|
||||
* @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;
|
||||
*
|
||||
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
|
||||
* &xt, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xv[i];
|
||||
* ...
|
||||
* }
|
||||
* xml_free(xt);
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see xpath_vec
|
||||
*/
|
||||
int
|
||||
xmldb_get(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if (clicon_xmldb_rpc(h))
|
||||
retval = xmldb_get_rpc(h, db, xpath, xtop, xvec, xlen);
|
||||
else
|
||||
retval = xmldb_get_local(h, db, xpath, xtop, xvec, xlen);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Get value of the "operation" attribute and change op if given
|
||||
* @param[in] xn XML node
|
||||
* @param[out] op "operation" attribute may change operation
|
||||
*/
|
||||
static int
|
||||
get_operation(cxobj *xn,
|
||||
enum operation_type *op)
|
||||
{
|
||||
char *opstr;
|
||||
|
||||
if ((opstr = xml_find_value(xn, "operation")) != NULL){
|
||||
if (strcmp("merge", opstr) == 0)
|
||||
*op = OP_MERGE;
|
||||
else
|
||||
if (strcmp("replace", opstr) == 0)
|
||||
*op = OP_REPLACE;
|
||||
else
|
||||
if (strcmp("create", opstr) == 0)
|
||||
*op = OP_CREATE;
|
||||
else
|
||||
if (strcmp("delete", opstr) == 0)
|
||||
*op = OP_DELETE;
|
||||
else
|
||||
if (strcmp("remove", opstr) == 0)
|
||||
*op = OP_REMOVE;
|
||||
else{
|
||||
clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Add data to database internal recursive function
|
||||
* @param[in] dbname Name of database to search in (filename incl dir path)
|
||||
* @param[in] xt xml-node.
|
||||
|
|
@ -1163,14 +1104,16 @@ put(char *dbname,
|
|||
yang_stmt *y;
|
||||
int exists;
|
||||
char *bodyenc=NULL;
|
||||
char *opstr;
|
||||
|
||||
clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument);
|
||||
if (debug){
|
||||
xml_print(stderr, xt);
|
||||
// yang_print(stderr, (yang_node*)ys, 0);
|
||||
}
|
||||
if (get_operation(xt, &op) < 0)
|
||||
goto done;
|
||||
if ((opstr = xml_find_value(xt, "operation")) != NULL)
|
||||
if (xml_operation(opstr, &op) < 0)
|
||||
goto done;
|
||||
body = xml_body(xt);
|
||||
if ((cbxk = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
|
|
@ -1240,95 +1183,31 @@ 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)
|
||||
/*! Modify database provided an xml tree, a restconf api_path and an operation
|
||||
*
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running or candidate
|
||||
* @param[in] xt xml-tree. Top-level symbol is dummy
|
||||
* @param[in] op OP_MERGE: just add it.
|
||||
* OP_REPLACE: first delete whole database
|
||||
* OP_NONE: operation attribute in xml determines operation
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
|
||||
* @param[in] xt xml-tree. Top-level symbol is dummy
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* The xml may contain the "operation" attribute which defines the operation.
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
|
||||
* err;
|
||||
* if (xmldb_put(h, "running", xt, OP_MERGE) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xmldb_put_xkey for single key
|
||||
*/
|
||||
int
|
||||
xmldb_put(clicon_handle h,
|
||||
char *db,
|
||||
cxobj *xt,
|
||||
enum operation_type op)
|
||||
{
|
||||
if (clicon_xmldb_rpc(h))
|
||||
return xmldb_put_rpc(h, db, xt, op);
|
||||
else
|
||||
return xmldb_put_local(h, db, xt, op);
|
||||
}
|
||||
|
||||
/*! XXX: replace xk with xt
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
|
||||
* e.g container top {
|
||||
list list1 {
|
||||
key "key1 key2 key3";
|
||||
* is referenced as
|
||||
/restconf/data/top/list1=a,,foo
|
||||
* example:
|
||||
* container top {
|
||||
* list list1 {
|
||||
* key "key1 key2 key3";
|
||||
* is referenced as
|
||||
* /restconf/data/top/list1=a,,foo
|
||||
* @see xmldb_put
|
||||
*/
|
||||
static int
|
||||
xmldb_put_tree_local(clicon_handle h,
|
||||
char *db,
|
||||
char *api_path,
|
||||
cxobj *xt,
|
||||
enum operation_type op)
|
||||
xmldb_put_restconf_api_path(clicon_handle h,
|
||||
char *db,
|
||||
enum operation_type op,
|
||||
char *api_path,
|
||||
cxobj *xt)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *y = NULL;
|
||||
|
|
@ -1431,7 +1310,7 @@ xmldb_put_tree_local(clicon_handle h,
|
|||
clicon_err(OE_XML, errno, "List %s without argument", name);
|
||||
goto done;
|
||||
}
|
||||
cprintf(ckey, "/%s", val2);
|
||||
cprintf(ckey, "=%s", val2);
|
||||
cbuf_reset(csubkey);
|
||||
cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname);
|
||||
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
|
||||
|
|
@ -1515,41 +1394,98 @@ xmldb_put_tree_local(clicon_handle h,
|
|||
cvec_free(cvk);
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
/*! Modify database provided an xml tree and an operation
|
||||
*
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db running or candidate
|
||||
* @param[in] xt xml-tree. Top-level symbol is dummy
|
||||
* @param[in] op OP_MERGE: just add it.
|
||||
* OP_REPLACE: first delete whole database
|
||||
* OP_NONE: operation attribute in xml determines operation
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* The xml may contain the "operation" attribute which defines the operation.
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
|
||||
* err;
|
||||
* if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xmldb_put_xkey for single key
|
||||
*/
|
||||
int
|
||||
xmldb_put_tree(clicon_handle h,
|
||||
char *db,
|
||||
char *api_path,
|
||||
cxobj *xt,
|
||||
enum operation_type op)
|
||||
xmldb_put(clicon_handle h,
|
||||
char *db,
|
||||
enum operation_type op,
|
||||
char *api_path,
|
||||
cxobj *xt)
|
||||
{
|
||||
if (clicon_xmldb_rpc(h))
|
||||
return -1; /* XXX */
|
||||
else
|
||||
return xmldb_put_tree_local(h, db, api_path, xt, 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 (api_path && strlen(api_path)){
|
||||
if (xmldb_put_restconf_api_path(h, db, op, api_path, x) < 0)
|
||||
goto done;
|
||||
continue;
|
||||
}
|
||||
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 database key and an operation
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Database name
|
||||
* @param[in] xk XML Key, eg /aa/bb/17/name
|
||||
* @param[in] val Key value, eg "17"
|
||||
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
|
||||
* @param[in] xk XML Key, eg /aa/bb=17/name
|
||||
* @param[in] val Key value, eg "17"
|
||||
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* Local variant of xmldb_put_xkey
|
||||
* @code
|
||||
* if (xmldb_put_xkey(h, db, OP_MERGE, "/aa/bb=17/name", "17") < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @see xmldb_put with xml-tree, no path
|
||||
*/
|
||||
static int
|
||||
xmldb_put_xkey_local(clicon_handle h,
|
||||
char *db,
|
||||
char *xk,
|
||||
char *val,
|
||||
enum operation_type op)
|
||||
int
|
||||
xmldb_put_xkey(clicon_handle h,
|
||||
char *db,
|
||||
enum operation_type op,
|
||||
char *xk,
|
||||
char *val)
|
||||
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x = NULL;
|
||||
|
|
@ -1747,34 +1683,6 @@ xmldb_put_xkey_local(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Modify database provided an XML database key and an operation
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] db Database name
|
||||
* @param[in] xk XML Key, eg /aa/bb=17/name
|
||||
* @param[in] val Key value, eg "17"
|
||||
* @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
|
||||
* @see xmldb_put with xml-tree, no path
|
||||
*/
|
||||
int
|
||||
xmldb_put_xkey(clicon_handle h,
|
||||
char *db,
|
||||
char *xk,
|
||||
char *val,
|
||||
enum operation_type op)
|
||||
{
|
||||
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
|
||||
|
|
@ -1782,7 +1690,7 @@ xmldb_put_xkey(clicon_handle h,
|
|||
* @note This function can only be called locally.
|
||||
*/
|
||||
int
|
||||
xmldb_dump_local(FILE *f,
|
||||
xmldb_dump(FILE *f,
|
||||
char *dbfilename,
|
||||
char *rxkey)
|
||||
{
|
||||
|
|
@ -1807,10 +1715,15 @@ xmldb_dump_local(FILE *f,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Local variant of xmldb_copy */
|
||||
static int
|
||||
xmldb_copy_local(clicon_handle h,
|
||||
/*! Copy database from db1 to db2
|
||||
* @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)
|
||||
{
|
||||
|
|
@ -1834,27 +1747,15 @@ xmldb_copy_local(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Copy database
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] from Source database copy
|
||||
* @param[in] to Destination database
|
||||
/*! Lock database
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Database
|
||||
* @param[in] pid Process id
|
||||
* @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,
|
||||
xmldb_lock(clicon_handle h,
|
||||
char *db,
|
||||
int pid)
|
||||
{
|
||||
|
|
@ -1873,27 +1774,15 @@ xmldb_lock_local(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Lock database
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Database
|
||||
/*! Unlock 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,
|
||||
xmldb_unlock(clicon_handle h,
|
||||
char *db,
|
||||
int pid)
|
||||
{
|
||||
|
|
@ -1914,30 +1803,13 @@ xmldb_unlock_local(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Unlock database
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Database
|
||||
* @param[in] pid Process id
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK
|
||||
*/
|
||||
/*! Unlock all databases locked by pid (eg process dies)
|
||||
*/
|
||||
int
|
||||
xmldb_unlock(clicon_handle h,
|
||||
char *db,
|
||||
int pid)
|
||||
xmldb_unlock_all(clicon_handle h,
|
||||
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);
|
||||
return db_unlock_all(pid);
|
||||
}
|
||||
|
||||
/*! Check if database is locked
|
||||
|
|
@ -1951,16 +1823,19 @@ 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);
|
||||
return db_islocked(db);
|
||||
}
|
||||
|
||||
/*! Local variant of xmldb_exists */
|
||||
static int
|
||||
xmldb_exists_local(clicon_handle h,
|
||||
char *db)
|
||||
/*! 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,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
char *filename = NULL;
|
||||
|
|
@ -1978,25 +1853,15 @@ xmldb_exists_local(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Check if db exists
|
||||
/*! Delete database. Remove file
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Database
|
||||
* @retval -1 Error
|
||||
* @retval 0 No it does not exist
|
||||
* @retval 1 Yes it exists
|
||||
*/
|
||||
* @retval 0 OK
|
||||
*/
|
||||
int
|
||||
xmldb_exists(clicon_handle h,
|
||||
xmldb_delete(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;
|
||||
|
|
@ -2012,23 +1877,15 @@ xmldb_delete_local(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Delete database. Remove file
|
||||
* Should not be called from client. Use change("/", OP_REMOVE) instead.
|
||||
/*! Initialize database
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] db Database
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
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)
|
||||
xmldb_init(clicon_handle h,
|
||||
char *db)
|
||||
{
|
||||
int retval = -1;
|
||||
char *filename = NULL;
|
||||
|
|
@ -2044,16 +1901,6 @@ xmldb_init_local(clicon_handle h,
|
|||
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 */
|
||||
/*
|
||||
|
|
@ -2127,7 +1974,7 @@ main(int argc, char **argv)
|
|||
op = OP_REMOVE;
|
||||
else
|
||||
usage(argv[0]);
|
||||
if (xmldb_put(h, db, xn, op) < 0)
|
||||
if (xmldb_put(h, db, op, NULL, xn) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,576 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* 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"
|
||||
|
||||
/*! Make an rpc call to xmldb daemon
|
||||
*/
|
||||
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:
|
||||
default:
|
||||
opstr = "none";
|
||||
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:
|
||||
default:
|
||||
opstr = "none";
|
||||
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,
|
||||
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);
|
||||
cprintf(cb, "</get></rpc>]]>]]>");
|
||||
if (xmldb_rpc(h,
|
||||
cbuf_get(cb),
|
||||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xvec){
|
||||
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 (xml_rootchild(xt, 0, xtop) < 0)
|
||||
goto done;
|
||||
xt = NULL;
|
||||
}
|
||||
|
||||
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_str(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_str(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_str(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_str(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_str(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;
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
#ifndef _CLIXON_XML_DB_RPC_H_
|
||||
#define _CLIXON_XML_DB_RPC_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int xmldb_get_rpc(clicon_handle h, char *db,
|
||||
char *xpath,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
int xmldb_put_rpc(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
|
||||
int xmldb_put_xkey_rpc(clicon_handle h, char *db, char *xk, char *val,
|
||||
enum operation_type op);
|
||||
int xmldb_copy_rpc(clicon_handle h, char *from, char *to);
|
||||
int xmldb_lock_rpc(clicon_handle h, char *db, int pid);
|
||||
int xmldb_unlock_rpc(clicon_handle h, char *db, int pid);
|
||||
int xmldb_islocked_rpc(clicon_handle h, char *db);
|
||||
|
||||
int xmldb_exists_rpc(clicon_handle h, char *db);
|
||||
int xmldb_delete_rpc(clicon_handle h, char *db);
|
||||
int xmldb_init_rpc(clicon_handle h, char *db);
|
||||
|
||||
#endif /* _CLIXON_XML_DB_RPC_H_ */
|
||||
|
|
@ -46,6 +46,7 @@ struct xml_parse_yacc_arg{
|
|||
|
||||
cxobj *ya_xelement; /* xml active element */
|
||||
cxobj *ya_xparent; /* xml parent element*/
|
||||
int ya_skipspace; /* If set, translate successive space, \t \n with single space */
|
||||
};
|
||||
|
||||
extern char *clixon_xml_parsetext;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,8 @@ xml_attr_new(struct xml_parse_yacc_arg *ya,
|
|||
there may also be some leakage here on NULL return
|
||||
*/
|
||||
static int
|
||||
xml_parse_content(struct xml_parse_yacc_arg *ya, char *str)
|
||||
xml_parse_content(struct xml_parse_yacc_arg *ya,
|
||||
char *str)
|
||||
{
|
||||
int sz;
|
||||
char s0;
|
||||
|
|
@ -112,14 +113,14 @@ xml_parse_content(struct xml_parse_yacc_arg *ya, char *str)
|
|||
s0 = str[0];
|
||||
if (xn != NULL){
|
||||
sz = strlen(xml_value(xn));
|
||||
if (s0 == ' ' || s0 == '\n' || s0 == '\t'){
|
||||
if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t')){
|
||||
str[0] = ' ';
|
||||
if (xml_value(xn)[sz-1] == ' ')
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (s0 == ' ' || s0 == '\n' || s0 == '\t')
|
||||
if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t'))
|
||||
goto ok;
|
||||
if ((xn = xml_new("body", xp)) == NULL)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -552,12 +552,6 @@ xpath_expr(char *predicate_expression,
|
|||
* @param[in] flags if != 0, only match xml nodes matching flags
|
||||
* @param[out] vec2 Result XML node vector
|
||||
* @param[out] vec2len Length of result vector.
|
||||
* XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1
|
||||
* vec0 --> vec
|
||||
* Det är nog bra om vec0 inte ändras, är input parameter
|
||||
* Vid utgång ska vec1 innehålla resultatet.
|
||||
* Internt då?
|
||||
* XXX: hantering av (input)vec0-->vec-->vec2-->vec1 (resultat)
|
||||
*/
|
||||
static int
|
||||
xpath_find(struct xpath_element *xe,
|
||||
|
|
@ -767,6 +761,7 @@ xpath_choice(cxobj *xtop,
|
|||
cxobj **vec0 = NULL;
|
||||
size_t vec0len = 0;
|
||||
|
||||
|
||||
if ((s0 = strdup(xpath0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
goto done;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue