Change internal protocol from clicon_proto.h to netconf.

This commit is contained in:
Olof hagsand 2017-03-25 11:10:50 +01:00
parent 2e09f54d12
commit 2fcefda831
66 changed files with 3012 additions and 5141 deletions

View file

@ -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 \

View file

@ -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");}

View file

@ -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.
*/

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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";
}
}

View file

@ -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

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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;

View file

@ -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;

View file

@ -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 ?
* 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;