diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c
index 6bdd458d..a4a3e651 100644
--- a/apps/backend/backend_client.c
+++ b/apps/backend/backend_client.c
@@ -252,126 +252,49 @@ from_client_get_config(clicon_handle h,
return retval;
}
-
-#ifdef notused
-/*! Internal message: Change entries as XML
- * @param[in] h Clicon handle
- * @param[in] s Socket where request arrived, and where replies are sent
- * @param[in] pid Unix process id
- * @param[in] msg Message
- * @param[in] label Memory chunk
- * @retval 0 OK
- * @retval -1 Error. Send error message back to client.
- */
-static int
-from_client_xmlput(clicon_handle h,
- int s,
- int pid,
- struct clicon_msg *msg,
- const char *label)
-{
- int retval = -1;
- char *db;
- enum operation_type op;
- cvec *cvv = NULL;
- char *str = NULL;
- char *api_path = NULL;
- char *xml = NULL;
- cxobj *xt = NULL;
- int piddb;
- cxobj *x;
-
- if (clicon_msg_xmlput_decode(msg,
- &db,
- &op,
- &api_path,
- &xml,
- label) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- /* candidate is locked by other client */
- if (strcmp(db, "candidate") == 0){
- piddb = xmldb_islocked(h, db);
- if (piddb && pid != piddb){
- send_msg_err(s, OE_DB, 0,
- "lock failed: locked by %d", piddb);
- goto done;
- }
- }
- if (clicon_xml_parse_string(&xml, &xt) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- if (strlen(api_path)){
- if (xt && xml_child_nr(xt)){
- x = NULL;
- while ((x = xml_child_each(xt, x, -1)) != NULL) {
- if (xmldb_put(h, db, op, api_path, x) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- }
- }
- else
- if (xmldb_put(h, db, op, api_path, NULL) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- }
- else if (xmldb_put(h, db, op, NULL, xt) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- if (send_msg_netconf_reply(s, "") < 0)
- goto done;
- retval = 0;
- done:
- if (str)
- free(str);
- if (cvv)
- cvec_free (cvv);
- if (xt)
- xml_free(xt);
- return retval;
-}
-#endif
-
-
/*! Internal message: edit-config
*
-
- * @param[in] h Clicon handle
- * @param[in] xe Netconf request xml tree
- * @param[out] cbret Return xml value cligen buffer
- * @see from_client_xmlput
+ * @param[in] h Clicon handle
+ * @param[in] xe Netconf request xml tree
+ * @param[in] mypid Process/session id of calling client
+ * @param[out] cbret Return xml value cligen buffer
* CLIXON addition:
*
*/
static int
from_client_edit_config(clicon_handle h,
cxobj *xn,
+ int mypid,
cbuf *cbret)
{
- int retval = -1;
- char *target;
- cbuf *cb = NULL;
- cxobj *xret = NULL;
- cxobj *xc;
- cxobj *xfilter;
- cxobj *x;
+ int retval = -1;
+ char *target;
+ cbuf *cb = NULL;
+ cxobj *xret = NULL;
+ cxobj *xc;
+ cxobj *xfilter;
+ cxobj *x;
enum operation_type operation = OP_MERGE;
- char *api_path = NULL;
+ char *api_path = NULL;
+ int piddb;
if ((target = netconf_db_find(xn, "target")) == NULL){
clicon_err(OE_XML, 0, "db not found");
goto done;
}
+ /* Check if target locked by other client */
+ piddb = xmldb_islocked(h, target);
+ if (piddb && mypid != piddb){
+ cprintf(cbret, ""
+ "lock-denied"
+ "protocol"
+ "error"
+ "Operation failed, lock is already held"
+ "%d"
+ "",
+ piddb);
+ goto ok;
+ }
/* ie /> */
if ((xfilter = xpath_first(xn, "filter")) != NULL)
api_path = xml_find_value(xfilter, "select");
@@ -387,26 +310,6 @@ from_client_edit_config(clicon_handle h,
}
if ((xc = xpath_first(xn, "config")) != NULL){
/* XXX see from_client_xmlput() */
- if (api_path){
- cbuf *cb;
- cb=cbuf_new();
- clicon_xml2cbuf(cb, xc, 0, 1);
- fprintf(stderr, "%s: api_path:%s xml:%s\n",
- __FUNCTION__, api_path, cbuf_get(cb));
- cbuf_free(cb);
- }
- if (xml_body(xc)!= NULL){
- if (xmldb_put_xkey(h, target, operation, api_path, xml_body(xc)) < 0){
- cprintf(cbret, ""
- "operation-failed"
- "protocol"
- "error"
- "%s"
- "", clicon_err_reason);
- goto ok;
- }
- }
- else
if (xmldb_put(h, target, operation, api_path, xc) < 0){
cprintf(cbret, ""
"operation-failed"
@@ -440,14 +343,14 @@ from_client_edit_config(clicon_handle h,
/*! Internal message: Lock database
*
* @param[in] h Clicon handle
- * @param[in] pid Unix process id
* @param[in] xe Netconf request xml tree
+ * @param[in] pid Unix process id
* @param[out] cbret Return xml value cligen buffer
*/
static int
from_client_lock(clicon_handle h,
- int pid,
cxobj *xe,
+ int pid,
cbuf *cbret)
{
int retval = -1;
@@ -494,14 +397,14 @@ from_client_lock(clicon_handle h,
/*! Internal message: Unlock database
*
* @param[in] h Clicon handle
- * @param[in] pid Unix process id
* @param[in] xe Netconf request xml tree
+ * @param[in] pid Unix process id
* @param[out] cbret Return xml value cligen buffer
*/
static int
from_client_unlock(clicon_handle h,
- int pid,
cxobj *xe,
+ int pid,
cbuf *cbret)
{
int retval = -1;
@@ -614,23 +517,25 @@ from_client_kill_session(clicon_handle h,
}
/*! Internal message: Copy database from db1 to db2
- * @param[in] h Clicon handle
- * @param[in] xe Netconf request xml tree
- * @param[out] cbret Return xml value cligen buffer
-
- * @retval 0 OK
- * @retval -1 Error. Send error message back to client.
+ * @param[in] h Clicon handle
+ * @param[in] xe Netconf request xml tree
+ * @param[in] mypid Process/session id of calling client
+ * @param[out] cbret Return xml value cligen buffer
+ * @retval 0 OK
+ * @retval -1 Error. Send error message back to client.
*/
static int
from_client_copy_config(clicon_handle h,
cxobj *xe,
+ int mypid,
cbuf *cbret)
{
- char *db1;
- char *db2;
+ char *source;
+ char *target;
int retval = -1;
+ int piddb;
- if ((db1 = netconf_db_find(xe, "source")) == NULL){
+ if ((source = netconf_db_find(xe, "source")) == NULL){
cprintf(cbret, ""
"missing-element"
"protocol"
@@ -639,7 +544,7 @@ from_client_copy_config(clicon_handle h,
"");
goto ok;
}
- if ((db2 = netconf_db_find(xe, "target")) == NULL){
+ if ((target = netconf_db_find(xe, "target")) == NULL){
cprintf(cbret, ""
"missing-element"
"protocol"
@@ -648,7 +553,21 @@ from_client_copy_config(clicon_handle h,
"");
goto ok;
}
- if (xmldb_copy(h, db1, db2) < 0){
+ /* Check if target locked by other client */
+ piddb = xmldb_islocked(h, target);
+ if (piddb && mypid != piddb){
+ cprintf(cbret, ""
+ "lock-denied"
+ "protocol"
+ "error"
+ "Operation failed, lock is already held"
+ "%d"
+ "",
+ piddb);
+ goto ok;
+ }
+
+ if (xmldb_copy(h, source, target) < 0){
cprintf(cbret, ""
"operation-failed"
"application"
@@ -667,18 +586,20 @@ from_client_copy_config(clicon_handle h,
/*! Internal message: Delete database
* @param[in] h Clicon handle
* @param[in] xe Netconf request xml tree
+ * @param[in] mypid Process/session id of calling client
* @param[out] cbret Return xml value cligen buffer
-
* @retval 0 OK
* @retval -1 Error. Send error message back to client.
*/
static int
from_client_delete_config(clicon_handle h,
cxobj *xe,
+ int mypid,
cbuf *cbret)
{
- char *target;
int retval = -1;
+ char *target;
+ int piddb;
if ((target = netconf_db_find(xe, "target")) == NULL||
strcmp(target, "running")==0){
@@ -690,6 +611,20 @@ from_client_delete_config(clicon_handle h,
"");
goto ok;
}
+ /* Check if target locked by other client */
+ piddb = xmldb_islocked(h, target);
+ if (piddb && mypid != piddb){
+ cprintf(cbret, ""
+ "lock-denied"
+ "protocol"
+ "error"
+ "Operation failed, lock is already held"
+ "%d"
+ "",
+ piddb);
+ goto ok;
+ }
+
if (xmldb_delete(h, target) < 0){
cprintf(cbret, ""
"operation-failed"
@@ -807,30 +742,28 @@ from_client_debug(clicon_handle h,
return retval;
}
-/*! Internal clicon netconf message has arrived from a client.
- * @param[in] h Socket where message arrived. read from this.
- * @param[in] ce Client session entry
- * @param[in] msg Clicon message. Contains internal netconf xml message.
- * @retval 0 OK. May be ok or error netconf reply
- * @retval -1 Error
+/*! An internal clicon message has arrived from a client. Receive and dispatch.
+ * @param[in] s Socket where message arrived. read from this.
+ * @param[in] arg Client entry (from).
+ * @retval 0 OK
+ * @retval -1 Error Terminates backend and is never called). Instead errors are
+ * propagated back to client.
*/
static int
-from_client_netconf(clicon_handle h,
- struct client_entry *ce,
- struct clicon_msg *msg)
+from_client_msg(clicon_handle h,
+ struct client_entry *ce,
+ struct clicon_msg *msg)
{
- int retval = -1;
- cxobj *xt = NULL;
- cxobj *x;
- cxobj *xe;
- char *name;
- char *db;
- cbuf *cbret; /* Return cligen buffer */
- int s;
- int pid;
- int ret;
+ int retval = -1;
+ cxobj *xt = NULL;
+ cxobj *x;
+ cxobj *xe;
+ char *name;
+ char *db;
+ cbuf *cbret = NULL; /* return message */
+ int pid;
+ int ret;
- s = ce->ce_s;
pid = ce->ce_pid;
/* Return netconf message. Should be filled in by the dispatch(sub) functions
* as wither rpc-error or by positive response.
@@ -839,7 +772,7 @@ from_client_netconf(clicon_handle h,
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
- if (clicon_msg_netconf_decode(msg, &xt) < 0){
+ if (clicon_msg_decode(msg, &xt) < 0){
cprintf(cbret, ""
"operation-failed"
"rpc"
@@ -847,7 +780,7 @@ from_client_netconf(clicon_handle h,
"rpc expected"
"Not recognized"
"");
- goto ok;
+ goto reply;
}
if ((x = xpath_first(xt, "/rpc")) == NULL){
cprintf(cbret, ""
@@ -857,7 +790,7 @@ from_client_netconf(clicon_handle h,
"rpc expected"
"Not recognized"
"");
- goto ok;
+ goto reply;
}
xe = NULL;
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
@@ -867,23 +800,23 @@ from_client_netconf(clicon_handle h,
goto done;
}
else if (strcmp(name, "edit-config") == 0){
- if (from_client_edit_config(h, xe, cbret) <0)
+ if (from_client_edit_config(h, xe, pid, cbret) <0)
goto done;
}
else if (strcmp(name, "copy-config") == 0){
- if (from_client_copy_config(h, xe, cbret) <0)
+ if (from_client_copy_config(h, xe, pid, cbret) <0)
goto done;
}
else if (strcmp(name, "delete-config") == 0){
- if (from_client_delete_config(h, xe, cbret) <0)
+ if (from_client_delete_config(h, xe, pid, cbret) <0)
goto done;
}
else if (strcmp(name, "lock") == 0){
- if (from_client_lock(h, pid, xe, cbret) < 0)
+ if (from_client_lock(h, xe, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "unlock") == 0){
- if (from_client_unlock(h, pid, xe, cbret) < 0)
+ if (from_client_unlock(h, xe, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "close-session") == 0){
@@ -902,17 +835,17 @@ from_client_netconf(clicon_handle h,
"error"
"source"
"");
- goto ok;
+ goto reply;
}
if (from_client_validate(h, db, cbret) < 0)
goto done;
}
else if (strcmp(name, "commit") == 0){
- if (from_client_commit(h, cbret) < 0)
+ if (from_client_commit(h, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "discard-changes") == 0){
- if (from_client_discard_changes(h, cbret) < 0)
+ if (from_client_discard_changes(h, pid, cbret) < 0)
goto done;
}
else if (strcmp(name, "create-subscription") == 0){
@@ -937,82 +870,21 @@ from_client_netconf(clicon_handle h,
name);
}
}
- ok:
+ reply:
assert(cbuf_len(cbret));
- if (send_msg_reply(s, CLICON_MSG_NETCONF,
- cbuf_get(cbret), cbuf_len(cbret)+1) < 0){
+ if (send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1) < 0){
if (errno == ECONNRESET)
clicon_log(LOG_WARNING, "client rpc reset");
goto done;
}
+ // ok:
retval = 0;
- done:
+ done:
if (xt)
xml_free(xt);
if (cbret)
cbuf_free(cbret);
- return retval;
-}
-
-/*! Internal message: Change entry set/delete in database xmldb variant
- * @param[in] h Clicon handle
- * @param[in] s Socket where request arrived, and where replies are sent
- * @param[in] pid Unix process id
- * @param[in] msg Message
- * @param[in] label Memory chunk
- * @retval 0 OK
- * @retval -1 Error. Send error message back to client.
- */
-static int
-from_client_change(clicon_handle h,
- int s,
- int pid,
- struct clicon_msg *msg,
- const char *label)
-{
- int retval = -1;
- uint32_t len;
- char *xk;
- char *db;
- enum operation_type op;
- char *str = NULL;
- char *val=NULL;
- int piddb;
-
- if (clicon_msg_change_decode(msg,
- &db,
- &op,
- &xk,
- &val,
- &len,
- label) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- fprintf(stderr, "%s api_path:%s val:%s\n", __FUNCTION__, xk, val);
- /* candidate is locked by other client */
- if (strcmp(db, "candidate") == 0){
- piddb = xmldb_islocked(h, db);
- if (piddb && pid != piddb){
- send_msg_err(s, OE_DB, 0,
- "lock failed: locked by %d", piddb);
- goto done;
- }
- }
- /* Update database */
- if (xmldb_put_xkey(h, db, op, xk, val) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- if (send_msg_ok(s, NULL) < 0)
- goto done;
- retval = 0;
- done:
- if (str)
- free(str);
- return retval;
+ return retval;// -1 here terminates backend
}
/*! An internal clicon message has arrived from a client. Receive and dispatch.
@@ -1027,42 +899,22 @@ from_client(int s,
void* arg)
{
int retval = -1;
+ struct clicon_msg *msg = NULL;
struct client_entry *ce = (struct client_entry *)arg;
clicon_handle h = ce->ce_handle;
- struct clicon_msg *msg = NULL;
- enum clicon_msg_type type;
int eof;
- assert(s == ce->ce_s);
+ // assert(s == ce->ce_s);
if (clicon_msg_rcv(ce->ce_s, &msg, &eof) < 0)
goto done;
- if (eof){
- // xmldb_unlock_all(h, ce->ce_pid);
+ if (eof)
backend_client_rm(h, ce);
- goto ok;
- }
- type = ntohs(msg->op_type);
- switch (type){
- case CLICON_MSG_NETCONF:
- if (from_client_netconf(h, ce, msg) < 0)
+ else
+ if (from_client_msg(h, ce, msg) < 0)
goto done;
- break;
- case CLICON_MSG_CHANGE:
- if (from_client_change(h, ce->ce_s, ce->ce_pid, msg,
- (char *)__FUNCTION__) < 0)
- goto done;
- break;
- default:
- send_msg_err(s, OE_PROTO, 0, "Unexpected message: %d", type);
- goto done;
- }
- ok:
retval = 0;
done:
if (msg)
free(msg);
- unchunk_group(__FUNCTION__);
- if (0) return retval;
- return 0; // -1 here terminates
+ return retval;
}
-
diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c
index c6850d58..1f9ba7d7 100644
--- a/apps/backend/backend_commit.c
+++ b/apps/backend/backend_commit.c
@@ -256,7 +256,7 @@ candidate_commit(clicon_handle h,
return retval;
}
-/*! Discard all changes in candidate / revert to running
+/*! Commit changes from candidate to running
* @param[in] h Clicon handle
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
@@ -264,10 +264,25 @@ candidate_commit(clicon_handle h,
*/
int
from_client_commit(clicon_handle h,
+ int mypid,
cbuf *cbret)
-
{
int retval = -1;
+ int piddb;
+
+ /* Check if target locked by other client */
+ piddb = xmldb_islocked(h, "running");
+ if (piddb && mypid != piddb){
+ cprintf(cbret, ""
+ "lock-denied"
+ "protocol"
+ "error"
+ "Operation failed, lock is already held"
+ "%d"
+ "",
+ piddb);
+ goto ok;
+ }
if (candidate_commit(h, "candidate") < 0){
clicon_debug(1, "Commit candidate failed");
@@ -290,17 +305,32 @@ from_client_commit(clicon_handle h,
/*! Discard all changes in candidate / revert to running
* @param[in] h Clicon handle
+ * @param[in] mypid Process/session id of calling client
* @param[out] cbret Return xml value cligen buffer
- * @retval 0 OK. This may indicate both ok and err msg back to client
- * @retval -1 (Local) Error
+ * @retval 0 OK. This may indicate both ok and err msg back to client
+ * @retval -1 (Local) Error
*/
int
from_client_discard_changes(clicon_handle h,
+ int mypid,
cbuf *cbret)
-
{
- int retval = -1;
+ int retval = -1;
+ int piddb;
+ /* Check if target locked by other client */
+ piddb = xmldb_islocked(h, "candidate");
+ if (piddb && mypid != piddb){
+ cprintf(cbret, ""
+ "lock-denied"
+ "protocol"
+ "error"
+ "Operation failed, lock is already held"
+ "%d"
+ "",
+ piddb);
+ goto ok;
+ }
if (xmldb_copy(h, "running", "candidate") < 0){
cprintf(cbret, ""
"operation-failed"
@@ -317,8 +347,6 @@ from_client_discard_changes(clicon_handle h,
return retval; /* may be zero if we ignoring errors from commit */
}
-
-
/*! Handle an incoming validate message from a client.
* @param[in] h Clicon handle
* @param[in] db Database name
diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h
index 0fc3661d..98b76f74 100644
--- a/apps/backend/backend_commit.h
+++ b/apps/backend/backend_commit.h
@@ -41,8 +41,8 @@
* Prototypes
*/
int from_client_validate(clicon_handle h, char *db, cbuf *cbret);
-int from_client_commit(clicon_handle h, cbuf *cbret);
-int from_client_discard_changes(clicon_handle h, cbuf *cbret);
+int from_client_commit(clicon_handle h, int pid, cbuf *cbret);
+int from_client_discard_changes(clicon_handle h, int pid, cbuf *cbret);
int candidate_commit(clicon_handle h, char *db);
#endif /* _BACKEND_COMMIT_H_ */
diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c
index 53c43363..1f6518c8 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -208,6 +208,7 @@ cli_dbxmlv(clicon_handle h,
cg_var *cval;
int len;
cg_var *arg;
+ cbuf *cb = NULL;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element to be xml key format string", __FUNCTION__);
@@ -225,7 +226,15 @@ cli_dbxmlv(clicon_handle h,
goto done;
}
}
- if (clicon_rpc_change(h, "candidate", op, xk, str) < 0)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ if (str)
+ cprintf(cb, "%s", str);
+ else
+ cprintf(cb, "");
+ if (clicon_rpc_edit_config(h, "candidate", op, xk, cbuf_get(cb)) < 0)
goto done;
if (clicon_autocommit(h)) {
if (clicon_rpc_commit(h) < 0)
@@ -233,6 +242,8 @@ cli_dbxmlv(clicon_handle h,
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (str)
free(str);
if (xk)
@@ -771,7 +782,7 @@ discard_changesv(clicon_handle h,
cvec *cvv,
cvec *argv)
{
- return clicon_rpc_copy_config(h, "running", "candidate");
+ return clicon_rpc_discard_changes(h);
}
/*! Copy from one database to another, eg running->startup
@@ -805,7 +816,6 @@ cli_notification_cb(int s,
void *arg)
{
struct clicon_msg *reply = NULL;
- enum clicon_msg_type type;
int eof;
int retval = -1;
char *eventstr = NULL;
@@ -826,17 +836,14 @@ cli_notification_cb(int s,
}
if (format == NULL)
goto done;
- type = ntohs(reply->op_type);
- switch (type){
- case CLICON_MSG_NETCONF:
- if (clicon_msg_netconf_decode(reply, &xt) < 0)
- goto done;
- if ((xe = xpath_first(xt, "//event")) != NULL)
- eventstr = xml_body(xe);
- if (strcmp(format, SHOWAS_TXT) == 0){
- fprintf(stdout, "%s\n", eventstr);
- }
- else
+ if (clicon_msg_decode(reply, &xt) < 0)
+ goto done;
+ if ((xe = xpath_first(xt, "//event")) != NULL)
+ eventstr = xml_body(xe);
+ if (strcmp(format, SHOWAS_TXT) == 0){
+ fprintf(stdout, "%s\n", eventstr);
+ }
+ else
if (strcmp(format, SHOWAS_XML) == 0){
if (clicon_xml_parse_string(&eventstr, &xt) < 0)
goto done;
@@ -857,12 +864,7 @@ cli_notification_cb(int s,
goto done;
}
}
- break;
- default:
- clicon_err(OE_PROTO, 0, "unexpected reply: %d", type);
- goto done;
- break;
- }
+
retval = 0;
done:
if (xt)
diff --git a/apps/dbctrl/dbctrl_main.c b/apps/dbctrl/dbctrl_main.c
index 5616e753..4910b958 100644
--- a/apps/dbctrl/dbctrl_main.c
+++ b/apps/dbctrl/dbctrl_main.c
@@ -214,9 +214,17 @@ main(int argc, char **argv)
goto done;
}
}
- if (addent) /* add entry */
- if (xmldb_put_xkey(h, db, OP_REPLACE, NULL, addstr) < 0)
+ if (addent){ /* add entry */
+ cxobj *xml = NULL;
+
+ if (clicon_xml_parse(&xml, "%s", addstr) < 0)
goto done;
+ if (xmldb_put(h, db, OP_REPLACE, NULL, xml) < 0)
+ goto done;
+ if (xml)
+ xml_free(xml);
+
+ }
if (rment)
if (remove_entry(db, rmkey) < 0)
goto done;
diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c
index 4b9e6994..80a92834 100644
--- a/apps/netconf/netconf_rpc.c
+++ b/apps/netconf/netconf_rpc.c
@@ -684,7 +684,6 @@ netconf_notification_cb(int s,
cbuf *cb;
cxobj *xe = NULL; /* event xml */
cxobj *xt = NULL; /* top xml */
- enum clicon_msg_type type;
if (0){
fprintf(stderr, "%s\n", __FUNCTION__); /* debug */
@@ -703,47 +702,35 @@ netconf_notification_cb(int s,
xml_free(xfilter);
goto done;
}
- /* multiplex on message type: we only expect notify */
- type = ntohs(reply->op_type);
- switch (type){
- case CLICON_MSG_NETCONF:
- if (clicon_msg_netconf_decode(reply, &xt) < 0)
- goto done;
- if ((xe = xpath_first(xt, "//event")) != NULL)
- event = xml_body(xe);
-
- /* parse event */
- if (0){ /* XXX CLICON events are not xml */
- /* find and apply filter */
- if ((selector = xml_find_value(xfilter, "select")) == NULL)
- goto done;
- if (xpath_first(xe, selector) == NULL) {
- fprintf(stderr, "%s no match\n", __FUNCTION__); /* debug */
- break;
- }
- }
- /* create netconf message */
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_PLUGIN, errno, "%s: cbuf_new", __FUNCTION__);
- goto done;
- }
- add_preamble(cb); /* Make it well-formed netconf xml */
- cprintf(cb, "%s", event);
- add_postamble(cb);
- /* Send it to listening client on stdout */
- if (netconf_output(1, cb, "notification") < 0){
- cbuf_free(cb);
- goto done;
- }
- fflush(stdout);
- cbuf_free(cb);
- break;
- default:
- clicon_err(OE_PROTO, 0, "%s: unexpected reply: %d",
- __FUNCTION__, type);
+ if (clicon_msg_decode(reply, &xt) < 0)
goto done;
- break;
+ if ((xe = xpath_first(xt, "//event")) != NULL)
+ event = xml_body(xe);
+
+ /* parse event */
+ if (0){ /* XXX CLICON events are not xml */
+ /* find and apply filter */
+ if ((selector = xml_find_value(xfilter, "select")) == NULL)
+ goto done;
+ if (xpath_first(xe, selector) == NULL) {
+ fprintf(stderr, "%s no match\n", __FUNCTION__); /* debug */
+ }
}
+ /* create netconf message */
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_PLUGIN, errno, "%s: cbuf_new", __FUNCTION__);
+ goto done;
+ }
+ add_preamble(cb); /* Make it well-formed netconf xml */
+ cprintf(cb, "%s", event);
+ add_postamble(cb);
+ /* Send it to listening client on stdout */
+ if (netconf_output(1, cb, "notification") < 0){
+ cbuf_free(cb);
+ goto done;
+ }
+ fflush(stdout);
+ cbuf_free(cb);
retval = 0;
done:
if (xt != NULL)
diff --git a/configure b/configure
index 0158ff79..3e5cf739 100755
--- a/configure
+++ b/configure
@@ -4315,7 +4315,7 @@ fi
-ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile apps/xmldb/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile doc/Makefile"
+ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile doc/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@@ -5018,7 +5018,6 @@ do
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
"apps/dbctrl/Makefile") CONFIG_FILES="$CONFIG_FILES apps/dbctrl/Makefile" ;;
- "apps/xmldb/Makefile") CONFIG_FILES="$CONFIG_FILES apps/xmldb/Makefile" ;;
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
diff --git a/example/routing_cli.c b/example/routing_cli.c
index f2b1edd8..3515408a 100644
--- a/example/routing_cli.c
+++ b/example/routing_cli.c
@@ -110,7 +110,7 @@ downcall(clicon_handle h,
if ((cv = cvec_i(vars, 1)) != NULL)
str = cv_string_get(cv);
}
- if ((msg = clicon_msg_netconf_encode("%s", str)) == NULL)
+ if ((msg = clicon_msg_encode("%s", str)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
diff --git a/example/routing_cli.cli b/example/routing_cli.cli
index fe608b25..b596ee22 100644
--- a/example/routing_cli.cli
+++ b/example/routing_cli.cli
@@ -14,7 +14,7 @@ commit("Commit the changes"), cli_commitv();
quit("Quit Hello"), cli_quitv();
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_allv("candidate");
-startup("Store running as startup config"), db_copy("running","startup");
+startup("Store running as startup config"), db_copy("running", "startup");
no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cliv((int32)0);
debug("Debugging parts of the system"), cli_debug_cliv((int32)1);{
level("Set debug level: 1..n") ("Set debug level (0..n)"), cli_debug_backendv();
@@ -29,23 +29,23 @@ show("Show a particular state of the system"){
xml("Show comparison in xml"), compare_dbsv((int32)0);
text("Show comparison in text"), compare_dbsv((int32)1);
}
- configuration("Show configuration"), show_confv_as_text("candidate","/");{
- xml("Show configuration as XML"), show_confv_as_xml("candidate","/");
- netconf("Show configuration as netconf edit-config operation"), show_confv_as_netconf("candidate","/");
+ configuration("Show configuration"), show_confv_as_text("candidate", "/");{
+ xml("Show configuration as XML"), show_confv_as_xml("candidate", "/");
+ netconf("Show configuration as netconf edit-config operation"), show_confv_as_netconf("candidate", "/");
text("Show configuration as text"), show_confv_as_text("candidate","/");
- cli("Show configuration as cli commands"), show_confv_as_cli("candidate","/");
- json("Show configuration as cli commands"), show_confv_as_json("candidate","/");
+ cli("Show configuration as cli commands"), show_confv_as_cli("candidate", "/");
+ json("Show configuration as cli commands"), show_confv_as_json("candidate", "/");
}
}
save("Save candidate configuration to XML file") ("Filename (local filename)"), save_config_filev("candidate","filename");
-load("Load configuration from XML file") ("Filename (local filename)"),load_config_filev("filename","replace");{
- replace("Replace candidate with file contents"), load_config_filev("filename","replace");
- merge("Merge file with existent candidate"), load_config_filev("filename","merge");
+load("Load configuration from XML file") ("Filename (local filename)"),load_config_filev("filename", "replace");{
+ replace("Replace candidate with file contents"), load_config_filev("filename", "replace");
+ merge("Merge file with existent candidate"), load_config_filev("filename", "merge");
}
example("This is a comment") ("Just a random number"), mycallback("myarg");
downcall("This is a downcall") , downcall();
-notify("Get notifications from backend"), cli_notifyv("ROUTING","1","txt");
-no("Negate") notify("Get notifications from backend"), cli_notifyv("ROUTING","0","xml");
+notify("Get notifications from backend"), cli_notifyv("ROUTING", "1", "txt");
+no("Negate") notify("Get notifications from backend"), cli_notifyv("ROUTING", "0", "xml");
lock,cli_lock("candidate");
unlock,cli_unlock("candidate");
\ No newline at end of file
diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h
index b83bdf45..e892788f 100644
--- a/lib/clixon/clixon_proto.h
+++ b/lib/clixon/clixon_proto.h
@@ -46,31 +46,18 @@ enum format_enum{
MSG_NOTIFY_XML, /* means filter works on xml */
};
-/* See also map_type2str in clicon_proto.c */
-enum clicon_msg_type{
- CLICON_MSG_NETCONF = 1, /* Generic netconf message (lock/unlock/..) can all
- msgs go to this?
- 1. string: netconf message
- */
- CLICON_MSG_CHANGE, /* Change a (single) database entry:
- 1. uint32: operation: OP_MERGE/OP_REPLACE/OP_REMOVE
- 2. uint32: length of value string
- 3. string: name of database to change (eg "running")
- 4. string: key
- 5. string: value (can be NULL)
- */
-};
-
/* Protocol message header */
struct clicon_msg {
uint16_t op_len; /* length of message. */
- uint16_t op_type; /* message type, see enum clicon_msg_type */
char op_body[0]; /* rest of message, actual data */
};
/*
* Prototypes
*/
+struct clicon_msg *clicon_msg_encode(char *format, ...);
+int clicon_msg_decode(struct clicon_msg *msg, cxobj **xml);
+
int clicon_connect_unix(char *sockpath);
int clicon_rpc_connect_unix(struct clicon_msg *msg,
@@ -92,13 +79,6 @@ int clicon_msg_rcv(int s, struct clicon_msg **msg, int *eof);
int send_msg_notify(int s, int level, char *event);
-int send_msg_reply(int s, uint16_t type, char *data, uint16_t datalen);
-
-int send_msg_ok(int s, char *data);
-
-int send_msg_err(int s, int err, int suberr, char *format, ...);
-
-int send_msg_netconf_reply(int s, char *format, ...);
-
+int send_msg_reply(int s, char *data, uint16_t datalen);
#endif /* _CLIXON_PROTO_H_ */
diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h
index 985fdcce..37420ca7 100644
--- a/lib/clixon/clixon_proto_client.h
+++ b/lib/clixon/clixon_proto_client.h
@@ -42,6 +42,7 @@
int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
int *sock0);
+int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp);
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
int clicon_rpc_generate_error(cxobj *xerr);
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, cxobj **xret);
@@ -55,7 +56,7 @@ int clicon_rpc_close_session(clicon_handle h);
int clicon_rpc_kill_session(clicon_handle h, int session_id);
int clicon_rpc_validate(clicon_handle h, char *db);
int clicon_rpc_commit(clicon_handle h);
-// discard-changes
+int clicon_rpc_discard_changes(clicon_handle h);
int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter,
int *s);
diff --git a/lib/clixon/clixon_proto_encode.h b/lib/clixon/clixon_proto_encode.h
deleted file mode 100644
index 17eed052..00000000
--- a/lib/clixon/clixon_proto_encode.h
+++ /dev/null
@@ -1,70 +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 *****
-
- *
- * Protocol to communicate between clients (eg clixon_cli, clixon_netconf)
- * and server (clicon_backend)
- */
-
-#ifndef _CLIXON_PROTO_ENCODE_H_
-#define _CLIXON_PROTO_ENCODE_H_
-
-/*
- * Prototypes
- */
-struct clicon_msg *clicon_msg_netconf_encode(char *format, ...);
-struct clicon_msg *clicon_msg_netconf_encode_xml(cxobj *xml);
-
-int clicon_msg_netconf_decode(struct clicon_msg *msg, cxobj **xml);
-
-struct clicon_msg *
-clicon_msg_change_encode(char *db, uint32_t op, char *key,
- char *lvec, uint32_t lvec_len);
-
-int
-clicon_msg_change_decode(struct clicon_msg *msg,
- char **db, uint32_t *op, char **key,
- char **lvec, uint32_t *lvec_len,
- const char *label);
-
-struct clicon_msg *
-clicon_msg_dbitems_get_reply_encode(cvec **cvecv,
- int cveclen);
-int
-clicon_msg_dbitems_get_reply_decode(char *data,
- uint16_t datalen,
- cvec ***cvecv,
- size_t *cveclen,
- const char *label);
-
-#endif /* _CLIXON_PROTO_ENCODE_H_ */
diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h
index f6c470b7..8889a8d8 100644
--- a/lib/clixon/clixon_xml_db.h
+++ b/lib/clixon/clixon_xml_db.h
@@ -47,9 +47,6 @@ int xmldb_get(clicon_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
char *api_path, cxobj *xt);
-int xmldb_put_xkey(clicon_handle h, char *db, enum operation_type op,
- char *xkey, char *val);
-
int xmldb_dump(FILE *f, char *dbfilename, char *rxkey);
int xmldb_copy(clicon_handle h, char *from, char *to);
int xmldb_lock(clicon_handle h, char *db, int pid);
diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in
index 8fd6c3a7..ac869185 100644
--- a/lib/src/Makefile.in
+++ b/lib/src/Makefile.in
@@ -68,7 +68,7 @@ SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
clixon_json.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_proto.c clixon_proto_client.c \
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
diff --git a/lib/src/clixon_err.c b/lib/src/clixon_err.c
index 59b2a43c..fd47a155 100644
--- a/lib/src/clixon_err.c
+++ b/lib/src/clixon_err.c
@@ -94,6 +94,7 @@ static struct errvec EV[] = {
{"UNIX error", OE_UNIX},
{"Syslog error", OE_SYSLOG},
{"Routing demon error", OE_ROUTING},
+ {"XML error", OE_XML},
{"Plugins", OE_PLUGIN},
{"Yang error", OE_YANG},
{"FATAL", OE_FATAL},
diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c
index 7ecd32f8..43816194 100644
--- a/lib/src/clixon_proto.c
+++ b/lib/src/clixon_proto.c
@@ -71,31 +71,59 @@
#include "clixon_xml.h"
#include "clixon_xsl.h"
#include "clixon_proto.h"
-#include "clixon_proto_encode.h"
static int _atomicio_sig = 0;
-struct map_type2str{
- enum clicon_msg_type mt_type;
- char *mt_str; /* string as in 4.2.4 in RFC 6020 */
-};
-
-/* Mapping between yang keyword string <--> clicon constants */
-static const struct map_type2str msgmap[] = {
- {CLICON_MSG_NETCONF, "netconf"},
- {CLICON_MSG_CHANGE, "change"},
- {-1, NULL},
-};
-
-static char *
-msg_type2str(enum clicon_msg_type type)
+/*! Encode a clicon netconf message
+ * @param[in] param Variable agrument list format an XML netconf string
+ * @retval msg Clicon message to send to eg clicon_msg_send()
+ */
+struct clicon_msg *
+clicon_msg_encode(char *format, ...)
{
- const struct map_type2str *mt;
+ va_list args;
+ int xmllen;
+ int len;
+ struct clicon_msg *msg = NULL;
+ int hdrlen = sizeof(*msg);
- for (mt = &msgmap[0]; mt->mt_str; mt++)
- if (mt->mt_type == type)
- return mt->mt_str;
- return NULL;
+ 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_len = htons(len);
+
+ /* body */
+ va_start(args, format);
+ vsnprintf(msg->op_body, xmllen, format, args);
+ va_end(args);
+
+ return msg;
+}
+
+/*! Decode a clicon netconf message
+ */
+int
+clicon_msg_decode(struct clicon_msg *msg,
+ cxobj **xml)
+{
+ 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;
}
/*! Open local connection using unix domain sockets
@@ -198,8 +226,8 @@ clicon_msg_send(int s,
{
int retval = -1;
- clicon_debug(2, "%s: send msg seq=%d len=%d",
- __FUNCTION__, ntohs(msg->op_type), ntohs(msg->op_len));
+ clicon_debug(2, "%s: send msg len=%d",
+ __FUNCTION__, ntohs(msg->op_len));
if (debug > 2)
msg_dump(msg);
if (atomicio((ssize_t (*)(int, void *, size_t))write,
@@ -258,8 +286,8 @@ clicon_msg_rcv(int s,
goto done;
}
mlen = ntohs(hdr.op_len);
- clicon_debug(2, "%s: rcv msg seq=%d, len=%d",
- __FUNCTION__, ntohs(hdr.op_type), mlen);
+ clicon_debug(2, "%s: rcv msg len=%d",
+ __FUNCTION__, mlen);
if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){
clicon_err(OE_CFG, errno, "malloc");
goto done;
@@ -303,8 +331,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
int s = -1;
struct stat sb;
- clicon_debug(1, "Send %s msg on %s",
- msg_type2str(ntohs(msg->op_type)), sockpath);
+ clicon_debug(1, "Send msg on %s", sockpath);
/* special error handling to get understandable messages (otherwise ENOENT) */
if (stat(sockpath, &sb) < 0){
clicon_err(OE_PROTO, errno, "%s: config daemon not running?", sockpath);
@@ -349,8 +376,7 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
int s = -1;
struct sockaddr_in addr;
- clicon_debug(1, "Send %s msg to %s:%hu",
- msg_type2str(ntohs(msg->op_type)), dst, port);
+ clicon_debug(1, "Send msg to %s:%hu", dst, port);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@@ -400,7 +426,6 @@ clicon_rpc(int s,
int retval = -1;
struct clicon_msg *reply;
int eof;
- enum clicon_msg_type type;
char *data = NULL;
cxobj *cx = NULL;
@@ -414,17 +439,7 @@ clicon_rpc(int s,
errno = ESHUTDOWN;
goto done;
}
- type = ntohs(reply->op_type);
- switch (type){
- 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: %s",
- __FUNCTION__, msg_type2str(type));
- goto done;
- break;
- }
+ data = reply->op_body; /* assume string */
if (ret && data)
if ((*ret = strdup(data)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
@@ -442,7 +457,6 @@ clicon_rpc(int s,
/*! 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
@@ -450,7 +464,6 @@ clicon_rpc(int s,
*/
int
send_msg_reply(int s,
- uint16_t type,
char *data,
uint16_t datalen)
{
@@ -462,7 +475,6 @@ send_msg_reply(int s,
if ((reply = (struct clicon_msg *)chunk(len, __FUNCTION__)) == NULL)
goto done;
memset(reply, 0, len);
- reply->op_type = htons(type);
reply->op_len = htons(len);
if (datalen > 0)
memcpy(reply->op_body, data, datalen);
@@ -490,7 +502,7 @@ send_msg_notify(int s,
int retval = -1;
struct clicon_msg *msg = NULL;
- if ((msg=clicon_msg_netconf_encode("%s", event)) == NULL)
+ if ((msg=clicon_msg_encode("%s", event)) == NULL)
goto done;
if (clicon_msg_send(s, msg) < 0)
goto done;
@@ -500,134 +512,3 @@ send_msg_notify(int s,
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_ok(int s,
- char *data)
-{
- int retval = -1;
- cbuf *cb = NULL;
-
- if ((cb = cbuf_new()) == NULL)
- goto done;
- if (data)
- cprintf(cb, "%s", data);
- else
- cprintf(cb, "");
- 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 *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 ((info = malloc(len)) == NULL){
- clicon_err(OE_UNIX, errno, "malloc");
- goto done;
- }
- memset(info, 0, len);
- va_start(args, format);
- vsnprintf(info, len, format, args);
- va_end(args);
- if ((cb = cbuf_new()) == NULL)
- goto done;
- cprintf(cb, ""
- "clixon"
- "%d"
- "%d"
- "%s"
- "",
- 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:
- 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;
-}
diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c
index fcbf6b24..5ef3fcc2 100644
--- a/lib/src/clixon_proto_client.c
+++ b/lib/src/clixon_proto_client.c
@@ -66,10 +66,9 @@
#include "clixon_xsl.h"
#include "clixon_proto.h"
#include "clixon_err.h"
-#include "clixon_proto_encode.h"
#include "clixon_proto_client.h"
-/*! Internal rpc function
+/*! Send internal netconf rpc from client to backend
* @param[in] h CLICON handle
* @param[in] msg Encoded message. Deallocate woth free
* @param[out] xret Return value from backend as netconf xml tree. Free w xml_free
@@ -88,11 +87,6 @@ clicon_rpc_msg(clicon_handle h,
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");
@@ -124,38 +118,9 @@ clicon_rpc_msg(clicon_handle h,
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 (retdata &&
+ clicon_xml_parse_str(retdata, &xret) < 0)
+ goto done;
if (xret0){
*xret0 = xret;
xret = NULL;
@@ -172,20 +137,21 @@ clicon_rpc_msg(clicon_handle h,
/*! 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[in] xmlstr XML netconf tree as string
+ * @param[out] xret Return XML netconf tree, error or OK
* @param[out] sp Socket pointer for notification, otherwise NULL
+ * @see clicon_rpc_netconf_xml xml as tree instead of string
*/
int
-clicon_rpc_netconf_xml(clicon_handle h,
- cxobj *xin,
- cxobj **xret,
- int *sp)
+clicon_rpc_netconf(clicon_handle h,
+ char *xmlstr,
+ cxobj **xret,
+ int *sp)
{
int retval = -1;
struct clicon_msg *msg = NULL;
- if ((msg = clicon_msg_netconf_encode_xml(xin)) == NULL)
+ if ((msg = clicon_msg_encode("%s", xmlstr)) < 0)
goto done;
if (clicon_rpc_msg(h, msg, xret, sp) < 0)
goto done;
@@ -196,25 +162,64 @@ clicon_rpc_netconf_xml(clicon_handle h,
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] xml XML netconf tree
+ * @param[out] xret Return XML netconf tree, error or OK
+ * @param[out] sp Socket pointer for notification, otherwise NULL
+ * @see clicon_rpc_netconf xml as string instead of tree
+ */
+int
+clicon_rpc_netconf_xml(clicon_handle h,
+ cxobj *xml,
+ cxobj **xret,
+ int *sp)
+{
+ int retval = -1;
+ cbuf *cb = NULL;
+
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
+ if (clicon_xml2cbuf(cb, xml, 0, 0) < 0)
+ goto done;
+ if (clicon_rpc_netconf(h, cbuf_get(cb), xret, sp) < 0)
+ goto done;
+ retval = 0;
+ done:
+ if (cb)
+ cbuf_free(cb);
+ return retval;
+}
+
+/*! Generate clicon error function call from Netconf error message
+ * @param[in] xerr Netconf error message on the level:
+ */
int
clicon_rpc_generate_error(cxobj *xerr)
{
+ int retval = -1;
+ cbuf *cb = NULL;
cxobj *x;
- char *etype="";
- char *etag="";
- char *emsg="";
- char *einfo="";
+ if ((cb = cbuf_new()) ==NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
+ goto done;
+ }
if ((x=xpath_first(xerr, "error-type"))!=NULL)
- etype = xml_body(x);
+ cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
- etag = xml_body(x);
+ cprintf(cb, "%s ", xml_body(x));
if ((x=xpath_first(xerr, "error-message"))!=NULL)
- emsg = xml_body(x);
+ cprintf(cb, "%s ", 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;
+ clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
+ clicon_err_fn("Clixon", 0, OE_XML, 0, "%s", cbuf_get(cb));
+ retval = 0;
+ done:
+ return retval;
}
/*! Get database configuration
@@ -252,7 +257,7 @@ clicon_rpc_get_config(clicon_handle h,
if (xpath && strlen(xpath))
cprintf(cb, "", xpath);
cprintf(cb, "");
- if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL)
+ if ((msg = clicon_msg_encode(cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -280,14 +285,14 @@ clicon_rpc_get_config(clicon_handle h,
}
/*! 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: .....
+ * @param[in] xml XML string. Ex: .....
* @retval 0 OK
* @retval -1 Error
+ * @note xml arg need to have as top element
* @code
* if (clicon_rpc_edit_config(h, "running", OP_MERGE, "/",
* "4") < 0)
@@ -299,7 +304,7 @@ clicon_rpc_edit_config(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
- char *xml)
+ char *xmlstr)
{
int retval = -1;
struct clicon_msg *msg = NULL;
@@ -314,10 +319,10 @@ clicon_rpc_edit_config(clicon_handle h,
xml_operation2str(op));
if (api_path && strlen(api_path))
cprintf(cb, "", api_path);
- if (xml)
- cprintf(cb, "%s", xml);
+ if (xmlstr)
+ cprintf(cb, "%s", xmlstr);
cprintf(cb, "");
- if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL)
+ if ((msg = clicon_msg_encode(cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -357,7 +362,7 @@ clicon_rpc_copy_config(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("<%s/><%s/>", db1, db2)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/><%s/>", db1, db2)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -391,7 +396,7 @@ clicon_rpc_delete_config(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -408,6 +413,10 @@ clicon_rpc_delete_config(clicon_handle h,
return retval;
}
+/*! Lock a database
+ * @param[in] h CLICON handle
+ * @param[in] db database, eg "running"
+ */
int
clicon_rpc_lock(clicon_handle h,
char *db)
@@ -417,7 +426,7 @@ clicon_rpc_lock(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -434,6 +443,10 @@ clicon_rpc_lock(clicon_handle h,
return retval;
}
+/*! Unlock a database
+ * @param[in] h CLICON handle
+ * @param[in] db database, eg "running"
+ */
int
clicon_rpc_unlock(clicon_handle h,
char *db)
@@ -443,7 +456,7 @@ clicon_rpc_unlock(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -460,6 +473,9 @@ clicon_rpc_unlock(clicon_handle h,
return retval;
}
+/*! Close a (user) session
+ * @param[in] h CLICON handle
+ */
int
clicon_rpc_close_session(clicon_handle h)
{
@@ -468,7 +484,7 @@ clicon_rpc_close_session(clicon_handle h)
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("")) == NULL)
+ if ((msg = clicon_msg_encode("")) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -485,6 +501,10 @@ clicon_rpc_close_session(clicon_handle h)
return retval;
}
+/*! Kill other user sessions
+ * @param[in] h CLICON handle
+ * @param[in] session_id Session id of other user session
+ */
int
clicon_rpc_kill_session(clicon_handle h,
int session_id)
@@ -494,7 +514,7 @@ clicon_rpc_kill_session(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("%d", session_id)) == NULL)
+ if ((msg = clicon_msg_encode("%d", session_id)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -511,7 +531,6 @@ clicon_rpc_kill_session(clicon_handle h,
return retval;
}
-
/*! Send validate request to backend daemon
* @param[in] h CLICON handle
* @param[in] db Name of database
@@ -527,7 +546,7 @@ clicon_rpc_validate(clicon_handle h,
cxobj *xerr;
cxobj *x;
- if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -547,8 +566,6 @@ clicon_rpc_validate(clicon_handle h,
/*! Commit changes send a commit request to backend daemon
* @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 OK
*/
int
@@ -559,7 +576,36 @@ clicon_rpc_commit(clicon_handle h)
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("")) == NULL)
+ if ((msg = clicon_msg_encode("")) == 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;
+}
+
+/*! Discard all changes in candidate / revert to running
+ * @param[in] h CLICON handle
+ * @retval 0 OK
+ */
+int
+clicon_rpc_discard_changes(clicon_handle h)
+{
+ int retval = -1;
+ struct clicon_msg *msg = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr;
+
+ if ((msg = clicon_msg_encode("")) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
@@ -594,11 +640,11 @@ clicon_rpc_create_subscription(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode(""
- "%s"
- "%s"
- "",
- stream?stream:"", filter?filter:"")) == NULL)
+ if ((msg = clicon_msg_encode(""
+ "%s"
+ "%s"
+ "",
+ stream?stream:"", filter?filter:"")) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
goto done;
@@ -615,43 +661,6 @@ clicon_rpc_create_subscription(clicon_handle h,
return retval;
}
-/*! Send database change request to backend daemon, variant for xmldb
- * Same as clicon_proto_change just with a string
- * @param[in] h CLICON handle
- * @param[in] db Name of database
- * @param[in] op Operation on database item: set, delete, (merge?)
- * @param[in] key Database key
- * @param[in] value value as string
- * @retval 0 OK
- * @retval -1 Error
- * @note special case: remove all: key:"/" op:OP_REMOVE
- */
-int
-clicon_rpc_change(clicon_handle h,
- char *db,
- enum operation_type op,
- char *key,
- char *val)
-{
- int retval = -1;
- struct clicon_msg *msg = NULL;
-
- if ((msg = clicon_msg_change_encode(db,
- op,
- key,
- val,
- val?strlen(val)+1:0)) == NULL)
- goto done;
- if (clicon_rpc_msg(h, msg, NULL, NULL) < 0)
- goto done;
- retval = 0;
- done:
- if (msg)
- free(msg);
- return retval;
-}
-
-
/*! Send a debug request to backend server
* @param[in] h CLICON handle
* @param[in] level Debug level
@@ -665,7 +674,7 @@ clicon_rpc_debug(clicon_handle h,
cxobj *xret = NULL;
cxobj *xerr;
- if ((msg = clicon_msg_netconf_encode("%d", level)) == NULL)
+ if ((msg = clicon_msg_encode("%d", level)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
diff --git a/lib/src/clixon_proto_encode.c b/lib/src/clixon_proto_encode.c
deleted file mode 100644
index 1fabf62c..00000000
--- a/lib/src/clixon_proto_encode.c
+++ /dev/null
@@ -1,244 +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 *****
-
- *
- * Protocol to communicate between clients (eg clicon_cli, clicon_netconf)
- * and server (clicon_backend)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "clixon_config.h" /* generated by config & autoconf */
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-/* cligen */
-#include
-
-/* clicon */
-#include "clixon_err.h"
-#include "clixon_log.h"
-#include "clixon_queue.h"
-#include "clixon_chunk.h"
-#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_netconf_encode(char *format, ...)
-{
- va_list args;
- int xmllen;
- int len;
- struct clicon_msg *msg = NULL;
- int hdrlen = sizeof(*msg);
-
- 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_NETCONF);
- msg->op_len = htons(len);
-
- /* body */
- va_start(args, format);
- vsnprintf(msg->op_body, xmllen, format, args);
- va_end(args);
-
- return msg;
-}
-
-struct clicon_msg *
-clicon_msg_netconf_encode_xml(cxobj *xml)
-{
- struct clicon_msg *msg = NULL;
- cbuf *cb = NULL;
-
- if ((cb = cbuf_new()) == NULL){
- clicon_err(OE_XML, errno, "cbuf_new");
- goto done;
- }
- 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_netconf_decode(struct clicon_msg *msg,
- cxobj **xml)
-{
- 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)
-{
- struct clicon_msg *msg;
- uint16_t len;
- int hdrlen = sizeof(*msg);
- int p;
- uint32_t tmp;
-
- clicon_debug(2, "%s: op: %d str_len: %d db: %s key: '%s'",
- __FUNCTION__,
- op, str_len, db, key);
- p = 0;
- len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(db) + 1 +
- strlen(key) + str_len;
- if (str_len)
- len++; /* if str not null add end of string */
- 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_CHANGE);
- msg->op_len = htons(len);
-
- /* body */
- tmp = htonl(op);
- memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
- p += sizeof(uint32_t);
-
- tmp = htonl(str_len);
- 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, key, len-p-hdrlen);
- p += strlen(key)+1;
- if (str_len){
- memcpy(msg->op_body+p, str, str_len);
- p += str_len;
- }
- return msg;
-}
-
-int
-clicon_msg_change_decode(struct clicon_msg *msg,
- char **db,
- uint32_t *op,
- char **key,
- char **str,
- uint32_t *str_len,
- 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);
-
- memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
- *str_len = 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 ((*key = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
- clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
- __FUNCTION__);
- return -1;
- }
- p += strlen(*key)+1;
-
- if (*str_len){
- if ((*str = chunk(*str_len, label)) == NULL){
- clicon_err(OE_PROTO, errno, "%s: chunk",
- __FUNCTION__);
- return -1;
- }
- memcpy(*str, msg->op_body+p, *str_len);
- p += *str_len;
- }
- else
- *str = NULL;
- clicon_debug(2, "%s: op: %d str_len: %d db: %s key: '%s'",
- __FUNCTION__,
- *op, *str_len, *db, *key);
- return 0;
-}
-
-
-
diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c
index 77a11271..9249e33b 100644
--- a/lib/src/clixon_xml.c
+++ b/lib/src/clixon_xml.c
@@ -1110,7 +1110,7 @@ clicon_xml_parse_str(char *str,
*
* @code
* cxobj *cx = NULL;
- * if (clicon_xml_parse(&cx, "%s%d", 22) < 0)
* err;
* xml_free(cx);
* @endcode
diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c
index fd24f6f5..6b6052bf 100644
--- a/lib/src/clixon_xml_db.c
+++ b/lib/src/clixon_xml_db.c
@@ -230,6 +230,7 @@ yang2xmlkeyfmt(yang_stmt *ys,
return retval;
}
+
/*! Transform an xml key format and a vector of values to an XML key
* Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey()
* Example:
@@ -1183,6 +1184,225 @@ put(char *dbname,
return retval;
}
+/*! Modify database provided an XML database key and an operation
+ * @param[in] h CLICON handle
+ * @param[in] db Database name
+ * @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
+ * @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(clicon_handle h,
+ char *db,
+ enum operation_type op,
+ char *xk,
+ char *val)
+
+{
+ int retval = -1;
+ cxobj *x = NULL;
+ yang_stmt *y = NULL;
+ yang_stmt *ykey;
+ char **vec;
+ int nvec;
+ char **valvec;
+ int nvalvec;
+ int i;
+ int j;
+ char *name;
+ char *restval;
+ cg_var *cvi;
+ cvec *cvk = NULL; /* vector of index keys */
+ char *val2 = NULL;
+ cbuf *ckey=NULL; /* partial keys */
+ cbuf *csubkey=NULL; /* partial keys */
+ cbuf *crx=NULL; /* partial keys */
+ char *keyname;
+ int exists;
+ int npairs;
+ struct db_pair *pairs;
+ yang_spec *yspec;
+ char *filename = NULL;
+
+ yspec = clicon_dbspec_yang(h);
+ if (db2file(h, db, &filename) < 0)
+ goto done;
+ if (xk == NULL || *xk!='/'){
+ clicon_err(OE_DB, 0, "Invalid key: %s", xk);
+ goto done;
+ }
+ if ((ckey = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ if ((csubkey = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL)
+ goto done;
+ if (nvec < 2){
+ clicon_err(OE_XML, 0, "Malformed key: %s", xk);
+ goto done;
+ }
+ i = 1;
+ while (i name:x restval=1,2 */
+ if ((restval = index(name, '=')) != NULL){
+ *restval = '\0';
+ restval++;
+ }
+ if (i==1){
+ if (strlen(name)==0 && (op==OP_DELETE || op == OP_REMOVE)){
+ /* Special handling of "/" */
+ cprintf(ckey, "/");
+ break;
+ }
+ else
+ if ((y = yang_find_topnode(yspec, name)) == NULL){
+ clicon_err(OE_UNIX, errno, "No yang node found: %s", x?xml_name(x):"");
+ goto done;
+ }
+ }
+ else
+ if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
+ clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
+ goto done;
+ }
+ if ((op==OP_DELETE || op == OP_REMOVE) &&
+ y->ys_keyword == Y_LEAF &&
+ y->ys_parent->yn_keyword == Y_LIST &&
+ yang_key_match(y->ys_parent, y->ys_argument))
+ /* Special rule if key, dont write last key-name, rm whole*/;
+ else
+ cprintf(ckey, "/%s", name);
+ i++;
+ switch (y->ys_keyword){
+ case Y_LEAF_LIST:
+ if (restval==NULL){
+ clicon_err(OE_XML, 0, "malformed key, expected '='");
+ goto done;
+ }
+ cprintf(ckey, "=%s", restval);
+ break;
+ case Y_LIST:
+ if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
+ clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
+ __FUNCTION__, y->ys_argument);
+ goto done;
+ }
+ /* The value is a list of keys: [ ]* */
+ if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
+ goto done;
+ if (restval==NULL){
+ clicon_err(OE_XML, 0, "malformed key, expected '='");
+ goto done;
+ }
+ if ((valvec = clicon_strsplit(restval, ",", &nvalvec, __FUNCTION__)) == NULL)
+ goto done;
+
+ if (cvec_len(cvk) != nvalvec){
+ clicon_err(OE_XML, errno, "List %s key length mismatch", name);
+ goto done;
+ }
+ cvi = NULL;
+ /* Iterate over individual yang keys */
+ j = 0;
+ while ((cvi = cvec_each(cvk, cvi)) != NULL) {
+ keyname = cv_string_get(cvi);
+ if (j)
+ cprintf(ckey, ",");
+ else
+ cprintf(ckey, "=");
+ val2 = valvec[j++];
+
+ 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)
+ if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0)
+ goto done;
+ }
+ if (cvk){
+ cvec_free(cvk);
+ cvk = NULL;
+ }
+ break;
+ default:
+ if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
+ if (db_set(filename, cbuf_get(ckey), NULL, 0) < 0)
+ goto done;
+ break;
+ }
+ }
+ xk = cbuf_get(ckey);
+ /* final key */
+ switch (op){
+ case OP_CREATE:
+ if ((exists = db_exists(filename, xk)) < 0)
+ goto done;
+ if (exists == 1){
+ clicon_err(OE_DB, 0, "OP_CREATE: %s already exists in database", xk);
+ goto done;
+ }
+ case OP_MERGE:
+ case OP_REPLACE:
+ if (y->ys_keyword == Y_LEAF || y->ys_keyword == Y_LEAF_LIST){
+ if (db_set(filename, xk, val, val?strlen(val)+1:0) < 0)
+ goto done;
+ }
+ else
+ if (db_set(filename, xk, NULL, 0) < 0)
+ goto done;
+ break;
+ case OP_DELETE:
+ if ((exists = db_exists(filename, xk)) < 0)
+ goto done;
+ if (exists == 0){
+ clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk);
+ goto done;
+ }
+ case OP_REMOVE:
+ /* Read in complete database (this can be optimized) */
+ if ((crx = cbuf_new()) == NULL){
+ clicon_err(OE_UNIX, errno, "cbuf_new");
+ goto done;
+ }
+ cprintf(crx, "^%s.*$", xk);
+ if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0)
+ goto done;
+ for (i = 0; i < npairs; i++) {
+ if (db_del(filename, pairs[i].dp_key) < 0)
+ goto done;
+ }
+ break;
+ default:
+ break;
+ }
+ retval = 0;
+ done:
+ if (filename)
+ free(filename);
+ if (ckey)
+ cbuf_free(ckey);
+ if (csubkey)
+ cbuf_free(csubkey);
+ if (crx)
+ cbuf_free(crx);
+ if (cvk)
+ cvec_free(cvk);
+ unchunk_group(__FUNCTION__);
+ return retval;
+}
+
/*! Modify database provided an xml tree, a restconf api_path and an operation
*
* @param[in] h CLICON handle
@@ -1430,6 +1650,8 @@ xmldb_put(clicon_handle h,
yang_spec *yspec;
char *dbfilename = NULL;
+ if (xml_child_nr(xt)==0 || xml_body(xt)!= NULL)
+ return xmldb_put_xkey(h, db, op, api_path, xml_body(xt));
yspec = clicon_dbspec_yang(h);
if (db2file(h, db, &dbfilename) < 0)
goto done;
@@ -1464,224 +1686,6 @@ xmldb_put(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] 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
- * @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
- */
-int
-xmldb_put_xkey(clicon_handle h,
- char *db,
- enum operation_type op,
- char *xk,
- char *val)
-
-{
- int retval = -1;
- cxobj *x = NULL;
- yang_stmt *y = NULL;
- yang_stmt *ykey;
- char **vec;
- int nvec;
- char **valvec;
- int nvalvec;
- int i;
- int j;
- char *name;
- char *restval;
- cg_var *cvi;
- cvec *cvk = NULL; /* vector of index keys */
- char *val2 = NULL;
- cbuf *ckey=NULL; /* partial keys */
- cbuf *csubkey=NULL; /* partial keys */
- cbuf *crx=NULL; /* partial keys */
- char *keyname;
- int exists;
- int npairs;
- struct db_pair *pairs;
- yang_spec *yspec;
- char *filename = NULL;
-
- yspec = clicon_dbspec_yang(h);
- if (db2file(h, db, &filename) < 0)
- goto done;
- if (xk == NULL || *xk!='/'){
- clicon_err(OE_DB, 0, "Invalid key: %s", xk);
- goto done;
- }
- if ((ckey = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- if ((csubkey = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL)
- goto done;
- if (nvec < 2){
- clicon_err(OE_XML, 0, "Malformed key: %s", xk);
- goto done;
- }
- i = 1;
- while (i name:x restval=1,2 */
- if ((restval = index(name, '=')) != NULL){
- *restval = '\0';
- restval++;
- }
- if (i==1){
- if (strlen(name)==0 && (op==OP_DELETE || op == OP_REMOVE)){
- /* Special handling of "/" */
- cprintf(ckey, "/");
- break;
- }
- else
- if ((y = yang_find_topnode(yspec, name)) == NULL){
- clicon_err(OE_UNIX, errno, "No yang node found: %s", x?xml_name(x):"");
- goto done;
- }
- }
- else
- if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
- clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
- goto done;
- }
- if ((op==OP_DELETE || op == OP_REMOVE) &&
- y->ys_keyword == Y_LEAF &&
- y->ys_parent->yn_keyword == Y_LIST &&
- yang_key_match(y->ys_parent, y->ys_argument))
- /* Special rule if key, dont write last key-name, rm whole*/;
- else
- cprintf(ckey, "/%s", name);
- i++;
- switch (y->ys_keyword){
- case Y_LEAF_LIST:
- if (restval==NULL){
- clicon_err(OE_XML, 0, "malformed key, expected '='");
- goto done;
- }
- cprintf(ckey, "=%s", restval);
- break;
- case Y_LIST:
- if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
- clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
- __FUNCTION__, y->ys_argument);
- goto done;
- }
- /* The value is a list of keys: [ ]* */
- if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
- goto done;
- if (restval==NULL){
- clicon_err(OE_XML, 0, "malformed key, expected '='");
- goto done;
- }
- if ((valvec = clicon_strsplit(restval, ",", &nvalvec, __FUNCTION__)) == NULL)
- goto done;
-
- if (cvec_len(cvk) != nvalvec){
- clicon_err(OE_XML, errno, "List %s key length mismatch", name);
- goto done;
- }
- cvi = NULL;
- /* Iterate over individual yang keys */
- j = 0;
- while ((cvi = cvec_each(cvk, cvi)) != NULL) {
- keyname = cv_string_get(cvi);
- if (j)
- cprintf(ckey, ",");
- else
- cprintf(ckey, "=");
- val2 = valvec[j++];
-
- 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)
- if (db_set(filename, cbuf_get(csubkey), val2, strlen(val2)+1) < 0)
- goto done;
- }
- if (cvk){
- cvec_free(cvk);
- cvk = NULL;
- }
- break;
- default:
- if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
- if (db_set(filename, cbuf_get(ckey), NULL, 0) < 0)
- goto done;
- break;
- }
- }
- xk = cbuf_get(ckey);
- /* final key */
- switch (op){
- case OP_CREATE:
- if ((exists = db_exists(filename, xk)) < 0)
- goto done;
- if (exists == 1){
- clicon_err(OE_DB, 0, "OP_CREATE: %s already exists in database", xk);
- goto done;
- }
- case OP_MERGE:
- case OP_REPLACE:
- if (y->ys_keyword == Y_LEAF || y->ys_keyword == Y_LEAF_LIST){
- if (db_set(filename, xk, val, val?strlen(val)+1:0) < 0)
- goto done;
- }
- else
- if (db_set(filename, xk, NULL, 0) < 0)
- goto done;
- break;
- case OP_DELETE:
- if ((exists = db_exists(filename, xk)) < 0)
- goto done;
- if (exists == 0){
- clicon_err(OE_DB, 0, "OP_DELETE: %s does not exists in database", xk);
- goto done;
- }
- case OP_REMOVE:
- /* Read in complete database (this can be optimized) */
- if ((crx = cbuf_new()) == NULL){
- clicon_err(OE_UNIX, errno, "cbuf_new");
- goto done;
- }
- cprintf(crx, "^%s.*$", xk);
- if ((npairs = db_regexp(filename, cbuf_get(crx), __FUNCTION__, &pairs, 0)) < 0)
- goto done;
- for (i = 0; i < npairs; i++) {
- if (db_del(filename, pairs[i].dp_key) < 0)
- goto done;
- }
- break;
- default:
- break;
- }
- retval = 0;
- done:
- if (filename)
- free(filename);
- if (ckey)
- cbuf_free(ckey);
- if (csubkey)
- cbuf_free(csubkey);
- if (crx)
- cbuf_free(crx);
- if (cvk)
- cvec_free(cvk);
- unchunk_group(__FUNCTION__);
- return retval;
-}
/*! Raw dump of database, just keys and values, no xml interpretation
* @param[in] f File
diff --git a/test/lib.sh b/test/lib.sh
index f5ee47dc..ba9672a4 100755
--- a/test/lib.sh
+++ b/test/lib.sh
@@ -57,7 +57,7 @@ EOF
fi
match=`echo "$ret" | grep -Eo "$expect"`
if [ -z "$match" ]; then
- err "
+ err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\""
fi
}
diff --git a/test/test1.sh b/test/test1.sh
index c5a1fbe7..656f9a65 100755
--- a/test/test1.sh
+++ b/test/test1.sh
@@ -22,6 +22,18 @@ sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
+new "cli configure top"
+expectfn "clixon_cli -1f $clixon_cf set interfaces" ""
+
+new "cli show configuration top"
+expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces$"
+
+new "cli configure delete top"
+expectfn "clixon_cli -1f $clixon_cf delete interfaces" ""
+
+new "cli show configuration delete top"
+expectfn "clixon_cli -1f $clixon_cf show conf cli" ""
+
new "cli configure"
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0" ""
@@ -34,8 +46,21 @@ expectfn "clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"
new "cli configure more"
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 ipv4 address 1.2.3.4 prefix-length 24" ""
+expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 description mydesc" ""
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
+new "cli show xpath description"
+expectfn "clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" "mydesc"
+
+new "cli delete description"
+expectfn "clixon_cli -1f $clixon_cf -l o delete interfaces interface eth0 description mydesc"
+
+new "cli show xpath no description"
+expectfn "clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" ""
+
+new "cli success validate"
+expectfn "clixon_cli -1f $clixon_cf -l o validate" ""
+
new "cli commit"
expectfn "clixon_cli -1f $clixon_cf -l o commit" ""