From eec5896797ad82e44758874ed582a2dc5dfb04b3 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 25 Mar 2017 18:24:52 +0100 Subject: [PATCH] internal netconf mods --- apps/backend/backend_client.c | 380 ++++++++------------------ apps/backend/backend_commit.c | 44 +++- apps/backend/backend_commit.h | 4 +- apps/cli/cli_common.c | 42 +-- apps/dbctrl/dbctrl_main.c | 12 +- apps/netconf/netconf_rpc.c | 67 ++--- configure | 3 +- example/routing_cli.c | 2 +- example/routing_cli.cli | 22 +- lib/clixon/clixon_proto.h | 28 +- lib/clixon/clixon_proto_client.h | 3 +- lib/clixon/clixon_proto_encode.h | 70 ----- lib/clixon/clixon_xml_db.h | 3 - lib/src/Makefile.in | 2 +- lib/src/clixon_err.c | 1 + lib/src/clixon_proto.c | 231 ++++------------ lib/src/clixon_proto_client.c | 243 +++++++++-------- lib/src/clixon_proto_encode.c | 244 ----------------- lib/src/clixon_xml.c | 2 +- lib/src/clixon_xml_db.c | 440 ++++++++++++++++--------------- test/lib.sh | 2 +- test/test1.sh | 25 ++ 22 files changed, 665 insertions(+), 1205 deletions(-) delete mode 100644 lib/clixon/clixon_proto_encode.h delete mode 100644 lib/src/clixon_proto_encode.c 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" ""