From 2fcefda831fd84cbf0dc76a1ee3b5e17472de251 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 25 Mar 2017 11:10:50 +0100 Subject: [PATCH 01/13] Change internal protocol from clicon_proto.h to netconf. --- CHANGELOG | 1 + README.develop | 3 +- apps/Makefile.in | 2 +- apps/backend/backend_client.c | 1122 +++++++++++++++----------- apps/backend/backend_client.h | 2 - apps/backend/backend_commit.c | 198 +++-- apps/backend/backend_commit.h | 7 +- apps/backend/backend_handle.h | 1 - apps/backend/backend_main.c | 42 +- apps/backend/backend_plugin.c | 50 -- apps/backend/backend_plugin.h | 3 - apps/backend/backend_socket.c | 2 +- apps/backend/clixon_backend_handle.c | 80 +- apps/backend/clixon_backend_handle.h | 17 + apps/cli/cli_common.c | 207 +++-- apps/cli/cli_main.c | 11 +- apps/cli/cli_show.c | 70 +- apps/cli/clixon_cli_api.h | 4 +- apps/dbctrl/dbctrl_main.c | 7 +- apps/netconf/clixon_netconf.h | 23 +- apps/netconf/netconf_filter.c | 18 +- apps/netconf/netconf_lib.c | 29 +- apps/netconf/netconf_lib.h | 10 +- apps/netconf/netconf_main.c | 140 ++-- apps/netconf/netconf_plugin.c | 41 +- apps/netconf/netconf_plugin.h | 16 +- apps/netconf/netconf_rpc.c | 1075 +++++++++--------------- apps/netconf/netconf_rpc.h | 17 +- apps/restconf/README | 3 + apps/restconf/restconf_main.c | 77 +- apps/xmldb/Makefile.in | 108 --- apps/xmldb/xmldb_main.c | 940 --------------------- clixon.conf.cpp.cpp | 5 - configure.ac | 1 - doc/Doxyfile.graphs | 2 +- doc/FAQ.txt | 29 +- example/README | 34 +- example/ietf-ip@2014-06-16.yang | 2 +- example/routing.conf.local | 13 +- example/routing_backend.c | 54 +- example/routing_cli.c | 51 +- example/routing_cli.cli | 10 +- lib/clixon/clixon_options.h | 3 - lib/clixon/clixon_proto.h | 109 +-- lib/clixon/clixon_proto_client.h | 35 +- lib/clixon/clixon_proto_encode.h | 117 +-- lib/clixon/clixon_xml.h | 6 +- lib/clixon/clixon_xml_db.h | 15 +- lib/src/Makefile.in | 2 +- lib/src/clixon_json_parse.y | 2 +- lib/src/clixon_options.c | 33 - lib/src/clixon_proto.c | 278 +++++-- lib/src/clixon_proto_client.c | 764 ++++++++++++------ lib/src/clixon_proto_encode.c | 774 ++---------------- lib/src/clixon_xml.c | 143 +++- lib/src/clixon_xml_db.c | 515 +++++------- lib/src/clixon_xml_db_rpc.c | 576 ------------- lib/src/clixon_xml_db_rpc.h | 56 -- lib/src/clixon_xml_parse.h | 1 + lib/src/clixon_xml_parse.y | 7 +- lib/src/clixon_xsl.c | 7 +- test/all.sh | 2 +- test/lib.sh | 24 +- test/test1.sh | 25 +- test/test2.sh | 72 +- test/test3.sh | 60 ++ 66 files changed, 3012 insertions(+), 5141 deletions(-) delete mode 100644 apps/xmldb/Makefile.in delete mode 100644 apps/xmldb/xmldb_main.c delete mode 100644 lib/src/clixon_xml_db_rpc.c delete mode 100644 lib/src/clixon_xml_db_rpc.h create mode 100755 test/test3.sh diff --git a/CHANGELOG b/CHANGELOG index 2aa49340..fffc0285 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ # # ***** END LICENSE BLOCK ***** +- Change internal protocol from clicon_proto.h to netconf. - Netconf startup configuration support. Set CLICON_USE_STARTUP_CONFIG to 1 to enable. Eg, if backend_main is started with -CIr startup will be copied to running. diff --git a/README.develop b/README.develop index 16593630..c260d2a7 100644 --- a/README.develop +++ b/README.develop @@ -26,12 +26,11 @@ This README contains information for developers: 2. How to work in git (branching) +++++++++++++++++++++++++++++++++ -Baically follows: http://nvie.com/posts/a-successful-git-branching-model/ +Basically follows: http://nvie.com/posts/a-successful-git-branching-model/ only somewhat simplified: Do commits in develop branch. When done, merge with master. - $ git checkout develop Switch to branch develop $ git add .. diff --git a/apps/Makefile.in b/apps/Makefile.in index 4619448b..a685dcaa 100644 --- a/apps/Makefile.in +++ b/apps/Makefile.in @@ -40,7 +40,7 @@ LIBS = @LIBS@ SHELL = /bin/sh -SUBDIRS = cli backend dbctrl netconf xmldb restconf +SUBDIRS = cli backend dbctrl netconf restconf .PHONY: all clean depend install $(SUBDIRS) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 3f5c9d48..6bdd458d 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -63,6 +63,7 @@ /* clicon */ #include +#include "clixon_backend_handle.h" #include "backend_commit.h" #include "backend_plugin.h" #include "backend_client.h" @@ -84,7 +85,7 @@ client_subscription_add(struct client_entry *ce, { struct client_subscription *su = NULL; - clicon_debug(1, "%s stream:%s filter:%s", __FUNCTION__, stream, filter); + fprintf(stderr, "%s stream:%s filter:%s\n", __FUNCTION__, stream, filter); if ((su = malloc(sizeof(*su))) == NULL){ clicon_err(OE_PLUGIN, errno, "malloc"); goto done; @@ -92,7 +93,7 @@ client_subscription_add(struct client_entry *ce, memset(su, 0, sizeof(*su)); su->su_stream = strdup(stream); su->su_format = format; - su->su_filter = strdup(filter); + su->su_filter = filter?strdup(filter):strdup(""); su->su_next = ce->ce_subscription; ce->ce_subscription = su; done: @@ -117,6 +118,7 @@ client_subscription_delete(struct client_entry *ce, struct client_subscription *su; struct client_subscription **su_prev; + fprintf(stderr, "%s stream:%s\n", __FUNCTION__, su0->su_stream); su_prev = &ce->ce_subscription; /* this points to stack and is not real backpointer */ for (su = *su_prev; su; su = su->su_next){ if (su == su0){ @@ -132,6 +134,7 @@ client_subscription_delete(struct client_entry *ce, return 0; } +#ifdef notused /* xxx */ static struct client_subscription * client_subscription_find(struct client_entry *ce, char *stream) { @@ -143,6 +146,7 @@ client_subscription_find(struct client_entry *ce, char *stream) return su; } +#endif /*! Remove client entry state * Close down everything wrt clients (eg sockets, subscriptions) @@ -158,7 +162,7 @@ backend_client_rm(clicon_handle h, struct client_entry *c; struct client_entry *c0; struct client_entry **ce_prev; - struct client_subscription *su; + struct client_subscription *su; c0 = backend_client_list(h); ce_prev = &c0; /* this points to stack and is not real backpointer */ @@ -178,66 +182,78 @@ backend_client_rm(clicon_handle h, return backend_client_delete(h, ce); /* actually purge it */ } -/*! 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. +/*! FInd target/source in netconf request. Assume sanity made so not finding is error */ +static char* +netconf_db_find(cxobj *xn, + char *name) +{ + cxobj *xs; /* source */ + cxobj *xi; + char *db = NULL; + + if ((xs = xml_find(xn, name)) == NULL) + goto done; + if ((xi = xml_child_i(xs, 0)) == NULL) + goto done; + db = xml_name(xi); + done: + return db; +} + +/*! Internal message: get-config + * + * @param[in] h Clicon handle + * @param[in] xe Netconf request xml tree + * @param[out] cbret Return xml value cligen buffer */ static int -from_client_change(clicon_handle h, - int s, - int pid, - struct clicon_msg *msg, - const char *label) +from_client_get_config(clicon_handle h, + cxobj *xe, + cbuf *cbret) { - 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); + int retval = -1; + char *db; + cxobj *xfilter; + char *selector = "/"; + cxobj *xret = NULL; + + if ((db = netconf_db_find(xe, "source")) == NULL){ + clicon_err(OE_XML, 0, "db not found"); 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 ((xfilter = xml_find(xe, "filter")) != NULL) + if ((selector = xml_find_value(xfilter, "select"))==NULL) + selector="/"; + if (xmldb_get(h, db, selector, &xret, NULL, NULL) < 0){ + cprintf(cbret, "" + "operation-failed" + "application" + "error" + "read-registry" + ""); + goto ok; + } + cprintf(cbret, ""); + /* if empty only , if any data then .. */ + if (xret!=NULL){ + if (xml_child_nr(xret)){ + if (xml_name_set(xret, "config") < 0) + goto done; + if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0) + goto done; } } - /* Update database */ - if (xmldb_put_xkey(h, db, xk, val, op) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; - } - if (send_msg_ok(s) < 0) - goto done; + cprintf(cbret, ""); + ok: retval = 0; - done: - if (str) - free(str); + done: + if (xret) + xml_free(xret); 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 @@ -293,7 +309,7 @@ from_client_xmlput(clicon_handle h, if (xt && xml_child_nr(xt)){ x = NULL; while ((x = xml_child_each(xt, x, -1)) != NULL) { - if (xmldb_put_tree(h, db, api_path, x, op) < 0){ + if (xmldb_put(h, db, op, api_path, x) < 0){ send_msg_err(s, clicon_errno, clicon_suberrno, clicon_err_reason); goto done; @@ -301,18 +317,18 @@ from_client_xmlput(clicon_handle h, } } else - if (xmldb_put_tree(h, db, api_path, NULL, op) < 0){ + 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, xt, op) < 0){ + 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_ok(s) < 0) + if (send_msg_netconf_reply(s, "") < 0) goto done; retval = 0; done: @@ -324,290 +340,249 @@ from_client_xmlput(clicon_handle h, xml_free(xt); return retval; } - -/* Nr of snapshots. Can be made into a dynamic option */ -#define SNAPSHOTS_NR 30 -/*! dump old running_db to snapshot file #0. move all other checkpoints - * one step up - */ -int -config_snapshot(clicon_handle h, - char *db, - char *dir) -{ - int retval = -1; - char filename0[MAXPATHLEN]; - char filename1[MAXPATHLEN]; - struct stat st; - int i; - FILE *f = NULL; - cxobj *xn; - - if (stat(dir, &st) < 0){ - clicon_err(OE_CFG, errno, "%s: stat(%s): %s\n", - __FUNCTION__, dir, strerror(errno)); - return -1; - } - if (!S_ISDIR(st.st_mode)){ - clicon_err(OE_CFG, 0, "%s: %s: not directory\n", - __FUNCTION__, dir); - return -1; - } - for (i=SNAPSHOTS_NR-1; i>0; i--){ - snprintf(filename0, MAXPATHLEN, "%s/%d", - dir, - i-1); - snprintf(filename1, MAXPATHLEN, "%s/%d", - dir, - i); - if (stat(filename0, &st) == 0) - if (rename(filename0, filename1) < 0){ - clicon_err(OE_CFG, errno, "%s: rename(%s, %s): %s\n", - __FUNCTION__, filename0, filename1, strerror(errno)); - return -1; - } - } - /* Make the most current snapshot */ - snprintf(filename0, MAXPATHLEN, "%s/0", dir); - if ((f = fopen(filename0, "wb")) == NULL){ - clicon_err(OE_CFG, errno, "Creating file %s", filename0); - return -1; - } - if (xmldb_get(h, db, "/", &xn, NULL, NULL) < 0) - goto done; - if (xml_print(f, xn) < 0) - goto done; - retval = 0; - done: - if (f != NULL) - fclose(f); - if (xn) - xml_free(xn); - return retval; -} +#endif -/*! Internal message: Dump/print database to file - * @param[in] h Clicon handle - * @param[in] s Socket where request arrived, and where replies are sent - * @param[in] msg Message - * @param[in] label Memory chunk - * @retval 0 OK - * @retval -1 Error. Send error message back to client. +/*! 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 + * CLIXON addition: + * */ static int -from_client_save(clicon_handle h, - int s, - struct clicon_msg *msg, - const char *label) +from_client_edit_config(clicon_handle h, + cxobj *xn, + cbuf *cbret) { - int retval = -1; - char *filename; - char *archive_dir; - char *db; - uint32_t snapshot; - FILE *f = NULL; - cxobj *xn = NULL; + 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; - if (clicon_msg_save_decode(msg, - &db, - &snapshot, - &filename, - label) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); + if ((target = netconf_db_find(xn, "target")) == NULL){ + clicon_err(OE_XML, 0, "db not found"); goto done; } - if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0){ - clicon_err(OE_XML, 0, "Expected running or candidate, got %s", db); - goto done; - } - if (snapshot){ - if ((archive_dir = clicon_archive_dir(h)) == NULL){ - clicon_err(OE_PLUGIN, 0, "snapshot set and clicon_archive_dir not defined"); - goto done; + /* ie /> */ + if ((xfilter = xpath_first(xn, "filter")) != NULL) + api_path = xml_find_value(xfilter, "select"); + if ((x = xpath_first(xn, "default-operation")) != NULL){ + if (xml_operation(xml_body(x), &operation) < 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + ""); + goto ok; } - if (config_snapshot(h, db, archive_dir) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - - goto done; + } + 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" + "protocol" + "error" + "%s" + "", clicon_err_reason); + goto ok; } } else{ - if ((f = fopen(filename, "wb")) == NULL){ - clicon_err(OE_CFG, errno, "Creating file %s", filename); - return -1; - } - if (xmldb_get(h, db, "/", &xn, NULL, NULL) < 0) - goto done; - if (xml_print(f, xn) < 0) - goto done; + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "config" + ""); + goto ok; } - if (send_msg_ok(s) < 0) - goto done; + cprintf(cbret, ""); + ok: retval = 0; - done: - if (f != NULL) - fclose(f); - if (xn) - xml_free(xn); + done: + if (xret) + xml_free(xret); + if (cb) + cbuf_free(cb); return retval; } -/*! Internal message: Load file into database - * @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. +/*! Internal message: Lock database + * + * @param[in] h Clicon handle + * @param[in] pid Unix process id + * @param[in] xe Netconf request xml tree + * @param[out] cbret Return xml value cligen buffer */ static int -from_client_load(clicon_handle h, - int s, - int pid, - struct clicon_msg *msg, - const char *label) - +from_client_lock(clicon_handle h, + int pid, + cxobj *xe, + cbuf *cbret) { - char *filename = NULL; - int retval = -1; - char *db = NULL; - int replace = 0; - int fd = -1; - cxobj *xt = NULL; - cxobj *xn; - int piddb; - - if (clicon_msg_load_decode(msg, - &replace, - &db, - &filename, - label) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; + int retval = -1; + char *db; + int piddb; + + if ((db = netconf_db_find(xe, "target")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - if (strcmp(db, "running") != 0 && strcmp(db, "candidate") != 0){ - clicon_err(OE_XML, 0, "Expected running or candidate, got %s", db); - goto done; + /* + * A lock MUST not be granted if either of the following conditions is true: + * 1) A lock is already held by any NETCONF session or another entity. + * 2) The target configuration is , it has already been modified, and + * these changes have not been committed or rolled back. + */ + piddb = xmldb_islocked(h, db); + if (piddb){ + cprintf(cbret, "" + "lock-denied" + "protocol" + "error" + "Lock failed, lock is already held" + "%d" + "", + piddb); + goto ok; } - /* 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; - } + else{ + xmldb_lock(h, db, pid); + cprintf(cbret, ""); } - if (replace){ - if (xmldb_delete(h, db) < 0){ - send_msg_err(s, OE_UNIX, 0, "rm %s %s", filename, strerror(errno)); - goto done; - } - if (xmldb_init(h, db) < 0) - goto done; - } - - if ((fd = open(filename, O_RDONLY)) < 0){ - clicon_err(OE_UNIX, errno, "%s: open(%s)", __FUNCTION__, filename); - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; - } - if (clicon_xml_parse_file(fd, &xt, "") < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; - } - if ((xn = xml_child_i(xt, 0)) != NULL){ - if (xmldb_put(h, db, xn, replace?OP_REPLACE:OP_MERGE) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; - } - } - if (send_msg_ok(s) < 0) - goto done; + ok: retval = 0; - done: - if (fd != -1) - close(fd); - if (xt) - xml_free(xt); + // done: return retval; } -/*! Internal message: Copy file from file1 to file2 - * @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. +/*! Internal message: Unlock database + * + * @param[in] h Clicon handle + * @param[in] pid Unix process id + * @param[in] xe Netconf request xml tree + * @param[out] cbret Return xml value cligen buffer */ static int -from_client_copy(clicon_handle h, - int s, - int pid, - struct clicon_msg *msg, - const char *label) +from_client_unlock(clicon_handle h, + int pid, + cxobj *xe, + cbuf *cbret) { - char *db1; - char *db2; - int retval = -1; + int retval = -1; + char *db; + int piddb; - if (clicon_msg_copy_decode(msg, - &db1, - &db2, - label) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; + if ((db = netconf_db_find(xe, "target")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - if (xmldb_copy(h, db1, db2) < 0) - goto done; - if (send_msg_ok(s) < 0) - goto done; + piddb = xmldb_islocked(h, db); + /* + * An unlock operation will not succeed if any of the following + * conditions are true: + * 1) the specified lock is not currently active + * 2) the session issuing the operation is not the same + * session that obtained the lock + */ + if (piddb==0 || piddb != pid){ + cprintf(cbret, "" + "lock-denied" + "protocol" + "error" + "Unlock failed, lock is already held" + "pid=%d piddb=%d" + "", + pid, piddb); + goto ok; + } + else{ + xmldb_unlock(h, db, pid); + if (cprintf(cbret, "") < 0) + goto done; + } + ok: retval = 0; - done: + done: return retval; } - /*! Internal message: Kill session (Kill the process) - * @param[in] h Clicon handle - * @param[in] s Client socket where request arrived, and where replies are sent - * @param[in] msg Message - * @param[in] label Memory chunk - * @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[out] cbret Return xml value cligen buffer + * @retval 0 OK + * @retval -1 Error. Send error message back to client. */ static int -from_client_kill(clicon_handle h, - int s, - struct clicon_msg *msg, - const char *label) +from_client_kill_session(clicon_handle h, + cxobj *xe, + cbuf *cbret) { int retval = -1; uint32_t pid; /* other pid */ + char *str; struct client_entry *ce; char *db = "running"; /* XXX */ + cxobj *x; - if (clicon_msg_kill_decode(msg, - &pid, - label) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; + if ((x = xml_find(xe, "session-id")) == NULL || + (str = xml_find_value(x, "body")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "session-id" + ""); + goto ok; } + pid = atoi(str); /* may or may not be in active client list, probably not */ - if ((ce = ce_find_bypid(backend_client_list(h), pid)) != NULL) + if ((ce = ce_find_bypid(backend_client_list(h), pid)) != NULL){ + xmldb_unlock_all(h, pid); backend_client_rm(h, ce); + } + if (kill (pid, 0) != 0 && errno == ESRCH) /* Nothing there */ ; else{ @@ -623,11 +598,173 @@ from_client_kill(clicon_handle h, xmldb_unlock(h, db, pid); } else{ /* failed to kill client */ - send_msg_err(s, OE_DB, 0, "failed to kill %d", pid); - goto done; + cprintf(cbret, "" + "operation-failed" + "application" + "error" + "Faile to kill session" + ""); + goto ok; } - if (send_msg_ok(s) < 0) + cprintf(cbret, ""); + ok: + retval = 0; + // done: + return retval; +} + +/*! 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. + */ +static int +from_client_copy_config(clicon_handle h, + cxobj *xe, + cbuf *cbret) +{ + char *db1; + char *db2; + int retval = -1; + + if ((db1 = netconf_db_find(xe, "source")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "source" + ""); + goto ok; + } + if ((db2 = netconf_db_find(xe, "target")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; + } + if (xmldb_copy(h, db1, db2) < 0){ + cprintf(cbret, "" + "operation-failed" + "application" + "error" + "read-registry" + ""); + goto ok; + } + cprintf(cbret, ""); + ok: + retval = 0; + // done: + return retval; +} + +/*! Internal message: Delete database + * @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. + */ +static int +from_client_delete_config(clicon_handle h, + cxobj *xe, + cbuf *cbret) +{ + char *target; + int retval = -1; + + if ((target = netconf_db_find(xe, "target")) == NULL|| + strcmp(target, "running")==0){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; + } + if (xmldb_delete(h, target) < 0){ + cprintf(cbret, "" + "operation-failed" + "protocol" + "error" + "Internal error" + "%s" + "", clicon_err_reason); + goto ok; + } + if (xmldb_init(h, target) < 0){ + cprintf(cbret, "" + "operation-failed" + "protocol" + "error" + "Internal error" + "%s" + "", clicon_err_reason); + goto ok; + } + cprintf(cbret, ""); + ok: + retval = 0; + // done: + return retval; +} + +/*! Internal message: Create subscription for notifications see RFC 5277 + * @param[in] h Clicon handle + * @param[in] xe Netconf request xml tree + * @param[in] ce Client entry + * @param[out] cbret Return xml value cligen buffer + * @retval 0 OK + * @retval -1 Error. Send error message back to client. + * @example: + * + * RESULT # If not present, events in the default NETCONF stream will be sent. + * XPATH-EXPR<(filter> + * # only for replay (NYI) + * # only for replay (NYI) + * + */ +static int +from_client_create_subscription(clicon_handle h, + cxobj *xe, + struct client_entry *ce, + cbuf *cbret) +{ + char *stream = "NETCONF"; + char *filter = NULL; + int retval = -1; + cxobj *x; /* Genereic xml tree */ + char *ftype; + + if ((x = xpath_first(xe, "//stream")) != NULL) + stream = xml_find_value(x, "body"); + if ((x = xpath_first(xe, "//filter")) != NULL){ + if ((ftype = xml_find_value(x, "type")) != NULL){ + /* Only accept xpath as filter type */ + if (strcmp(ftype, "xpath") != 0){ + cprintf(cbret, "" + "operation-failed" + "application" + "error" + "only xpath filter type supported" + "type" + ""); + goto ok; + } + } + } + if (client_subscription_add(ce, stream, MSG_NOTIFY_XML, filter) == NULL) goto done; + cprintf(cbret, ""); + ok: retval = 0; done: return retval; @@ -635,140 +772,246 @@ from_client_kill(clicon_handle h, /*! Internal message: Set debug level. This is global, not just for the session. * @param[in] h Clicon handle - * @param[in] s Client socket where request arrived, and where replies are sent + * @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. + */ +static int +from_client_debug(clicon_handle h, + cxobj *xe, + cbuf *cbret) +{ + int retval = -1; + uint32_t level; + char *valstr; + + if ((valstr = xml_find_body(xe, "level")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "level" + ""); + goto ok; + } + level = atoi(valstr); + + clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */ + setlogmask(LOG_UPTO(level?LOG_DEBUG:LOG_INFO)); /* for syslog */ + clicon_log(LOG_NOTICE, "%s debug:%d", __FUNCTION__, debug); + cprintf(cbret, ""); + ok: + retval = 0; + //done: + 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 + */ +static int +from_client_netconf(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; + + 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. + */ + if ((cbret = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; + } + if (clicon_msg_netconf_decode(msg, &xt) < 0){ + cprintf(cbret, "" + "operation-failed" + "rpc" + "error" + "rpc expected" + "Not recognized" + ""); + goto ok; + } + if ((x = xpath_first(xt, "/rpc")) == NULL){ + cprintf(cbret, "" + "operation-failed" + "rpc" + "error" + "rpc expected" + "Not recognized" + ""); + goto ok; + } + xe = NULL; + while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) { + name = xml_name(xe); + if (strcmp(name, "get-config") == 0){ + if (from_client_get_config(h, xe, cbret) <0) + goto done; + } + else if (strcmp(name, "edit-config") == 0){ + if (from_client_edit_config(h, xe, cbret) <0) + goto done; + } + else if (strcmp(name, "copy-config") == 0){ + if (from_client_copy_config(h, xe, cbret) <0) + goto done; + } + else if (strcmp(name, "delete-config") == 0){ + if (from_client_delete_config(h, xe, cbret) <0) + goto done; + } + else if (strcmp(name, "lock") == 0){ + if (from_client_lock(h, pid, xe, cbret) < 0) + goto done; + } + else if (strcmp(name, "unlock") == 0){ + if (from_client_unlock(h, pid, xe, cbret) < 0) + goto done; + } + else if (strcmp(name, "close-session") == 0){ + xmldb_unlock_all(h, pid); + cprintf(cbret, ""); + } + else if (strcmp(name, "kill-session") == 0){ + if (from_client_kill_session(h, xe, cbret) < 0) + goto done; + } + else if (strcmp(name, "validate") == 0){ + if ((db = netconf_db_find(xe, "source")) == NULL){ + cprintf(cbret, "" + "missing-element" + "protocol" + "error" + "source" + ""); + goto ok; + } + if (from_client_validate(h, db, cbret) < 0) + goto done; + } + else if (strcmp(name, "commit") == 0){ + if (from_client_commit(h, cbret) < 0) + goto done; + } + else if (strcmp(name, "discard-changes") == 0){ + if (from_client_discard_changes(h, cbret) < 0) + goto done; + } + else if (strcmp(name, "create-subscription") == 0){ + if (from_client_create_subscription(h, xe, ce, cbret) < 0) + goto done; + } + else if (strcmp(name, "debug") == 0){ + if (from_client_debug(h, xe, cbret) < 0) + goto done; + } + else{ + if ((ret = backend_netconf_plugin_callbacks(h, xe, ce, cbret)) < 0) + goto done; + if (ret == 0) /* not handled by callback */ + cprintf(cbret, "" + "operation-failed" + "rpc" + "error" + "%s" + "Not recognized" + "", + name); + } + } + ok: + assert(cbuf_len(cbret)); + if (send_msg_reply(s, CLICON_MSG_NETCONF, + cbuf_get(cbret), cbuf_len(cbret)+1) < 0){ + if (errno == ECONNRESET) + clicon_log(LOG_WARNING, "client rpc reset"); + goto done; + } + retval = 0; + 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_debug(clicon_handle h, - int s, - struct clicon_msg *msg, - const char *label) +from_client_change(clicon_handle h, + int s, + int pid, + struct clicon_msg *msg, + const char *label) { - int retval = -1; - uint32_t level; + 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_debug_decode(msg, - &level, - label) < 0){ + 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; } - clicon_debug_init(level, NULL); /* 0: dont debug, 1:debug */ - setlogmask(LOG_UPTO(level?LOG_DEBUG:LOG_INFO)); /* for syslog */ - - if (send_msg_ok(s) < 0) - goto done; - clicon_log(LOG_NOTICE, "%s debug:%d", __FUNCTION__, debug); - retval = 0; - done: - - return retval; -} - -/*! Internal message: downcall backend plugin - * @param[in] h Clicon handle - * @param[in] s Client socket where request arrived, and where replies are sent - * @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_call(clicon_handle h, - int s, - struct clicon_msg *msg, - const char *label) -{ - int retval = -1; - void *reply_data = NULL; - uint16_t reply_data_len = 0; - struct clicon_msg_call_req *req; - - if (clicon_msg_call_decode(msg, &req, label) < 0) { + 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; } -#ifdef notyet - if (!strlen(req->cr_plugin)) /* internal */ - internal_function(req, &reply_data_len, &reply_data); - else -#endif - if (plugin_downcall(h, req, &reply_data_len, &reply_data) < 0) { - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; - } - - if ((retval = send_msg_reply(s, CLICON_MSG_OK, (char *)reply_data, reply_data_len)) < 0){ - if (errno == ECONNRESET){ /* If cli/netconf dies during plugin downcall */ - clicon_log(LOG_WARNING, "client downcall reset"); - retval = 0; - } - else - goto done; - } - done: - if (reply_data) - free(reply_data); - return retval; -} - -/*! Internal message: Create subscription for notifications - * @param[in] h Clicon handle - * @param[in] ce Client entry (from). - * @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_subscription(clicon_handle h, - struct client_entry *ce, - struct clicon_msg *msg, - const char *label) -{ - int status; - enum format_enum format; - char *stream; - char *filter; - int retval = -1; - struct client_subscription *su; - clicon_log_notify_t *old; - - if (clicon_msg_subscription_decode(msg, - &status, - &stream, - &format, - &filter, - label) < 0){ - send_msg_err(ce->ce_s, clicon_errno, clicon_suberrno, - clicon_err_reason); + if (send_msg_ok(s, NULL) < 0) goto done; - } - - if (status){ - if ((su = client_subscription_add(ce, stream, format, filter)) == NULL){ - send_msg_err(ce->ce_s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto done; - } - } - else{ - if ((su = client_subscription_find(ce, stream)) != NULL) - client_subscription_delete(ce, su); - } - /* Avoid recursion when sending logs */ - old = clicon_log_register_callback(NULL, NULL); - if (send_msg_ok(ce->ce_s) < 0) - goto done; - clicon_log_register_callback(old, h); /* XXX: old h */ retval = 0; done: + if (str) + free(str); return retval; } @@ -780,29 +1023,28 @@ from_client_subscription(clicon_handle h, * propagated back to client. */ int -from_client(int s, void* arg) +from_client(int s, + void* arg) { + int retval = -1; struct client_entry *ce = (struct client_entry *)arg; clicon_handle h = ce->ce_handle; - struct clicon_msg *msg; + struct clicon_msg *msg = NULL; enum clicon_msg_type type; int eof; assert(s == ce->ce_s); - if (clicon_msg_rcv(ce->ce_s, &msg, &eof, __FUNCTION__) < 0) + if (clicon_msg_rcv(ce->ce_s, &msg, &eof) < 0) goto done; if (eof){ + // xmldb_unlock_all(h, ce->ce_pid); backend_client_rm(h, ce); - goto done; + goto ok; } type = ntohs(msg->op_type); switch (type){ - case CLICON_MSG_COMMIT: - if (from_client_commit(h, ce->ce_s, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_VALIDATE: - if (from_client_validate(h, ce->ce_s, msg, __FUNCTION__) < 0) + case CLICON_MSG_NETCONF: + if (from_client_netconf(h, ce, msg) < 0) goto done; break; case CLICON_MSG_CHANGE: @@ -810,47 +1052,17 @@ from_client(int s, void* arg) (char *)__FUNCTION__) < 0) goto done; break; - case CLICON_MSG_XMLPUT: - if (from_client_xmlput(h, ce->ce_s, ce->ce_pid, msg, - (char *)__FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_SAVE: - if (from_client_save(h, ce->ce_s, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_LOAD: - if (from_client_load(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_COPY: - if (from_client_copy(h, ce->ce_s, ce->ce_pid, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_KILL: - if (from_client_kill(h, ce->ce_s, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_DEBUG: - if (from_client_debug(h, ce->ce_s, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_CALL: - if (from_client_call(h, ce->ce_s, msg, __FUNCTION__) < 0) - goto done; - break; - case CLICON_MSG_SUBSCRIPTION: - if (from_client_subscription(h, ce, msg, __FUNCTION__) < 0) - goto done; - break; default: send_msg_err(s, OE_PROTO, 0, "Unexpected message: %d", type); goto done; } -// retval = 0; + ok: + retval = 0; done: + if (msg) + free(msg); unchunk_group(__FUNCTION__); -// return retval; + if (0) return retval; return 0; // -1 here terminates } diff --git a/apps/backend/backend_client.h b/apps/backend/backend_client.h index 062e97a5..8536a01b 100644 --- a/apps/backend/backend_client.h +++ b/apps/backend/backend_client.h @@ -70,8 +70,6 @@ struct client_subscription{ * Prototypes */ int backend_client_rm(clicon_handle h, struct client_entry *ce); -int config_snapshot(clicon_handle h, char *dbname, char *dir); - int from_client(int fd, void *arg); #endif /* _BACKEND_CLIENT_H_ */ diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index ba798b1e..c6850d58 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -126,6 +126,10 @@ generic_validate(yang_spec *yspec, } /*! Common code of candidate_validate and candidate_commit + * @param[in] h Clicon handle + * @param[in] candidate The candidate database. The wanted backend state + * @retval 0 OK + * @retval -1 Fatal error or netconf error XXX Differentiate */ static int validate_common(clicon_handle h, @@ -137,7 +141,6 @@ validate_common(clicon_handle h, int i; cxobj *xn; - if ((yspec = clicon_dbspec_yang(h)) == NULL){ clicon_err(OE_FATAL, 0, "No DB_SPEC"); goto done; @@ -253,126 +256,117 @@ candidate_commit(clicon_handle h, return retval; } -/*! Do a diff between candidate and running, then start a validate transaction - * - * @param[in] h Clicon handle - * @param[in] candidate: The candidate database. The wanted backend state -*/ -int -candidate_validate(clicon_handle h, - char *candidate) -{ - int retval = -1; - transaction_data_t *td = NULL; - - /* 1. Start transaction */ - if ((td = transaction_new()) == NULL) - goto done; - - /* Common steps (with commit) */ - if (validate_common(h, candidate, td) < 0) - goto done; - - retval = 0; - done: - /* In case of failure, call plugin transaction termination callbacks */ - if (retval < 0 && td) - plugin_transaction_abort(h, td); - if (td) - transaction_free(td); - return retval; - } - - -/*! Handle an incoming commit message from a client. - * XXX: If commit succeeds and snapshot/startup fails, we have strange state: - * the commit has succeeded but an error message is returned. +/*! Discard all changes in candidate / revert 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 + * @retval -1 (Local) Error */ int -from_client_commit(clicon_handle h, - int s, - struct clicon_msg *msg, - const char *label) +from_client_commit(clicon_handle h, + cbuf *cbret) + { int retval = -1; - char *candidate; - char *running; - if (clicon_msg_commit_decode(msg, - &candidate, - &running, - label) < 0) - goto err; - - if (strcmp(candidate, "candidate") && strcmp(candidate, "tmp")){ - clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp"); - goto err; - } - if (strcmp(running, "running")){ - clicon_err(OE_PLUGIN, 0, "running db is not \"running\""); - goto err; - } if (candidate_commit(h, "candidate") < 0){ - clicon_debug(1, "Commit %s failed", candidate); - retval = 0; /* We ignore errors from commit, but maybe - we should fail on fatal errors? */ - goto err; + clicon_debug(1, "Commit candidate failed"); + /* XXX: candidate_validate should have proper error handling */ + cprintf(cbret, "" + "missing-attribute" + "protocol" + "error" + "%s" + "", + clicon_err_reason); + goto ok; } - clicon_debug(1, "Commit %s", candidate); + cprintf(cbret, ""); + ok: retval = 0; - if (send_msg_ok(s) < 0) - goto done; - goto done; - err: - /* XXX: more elaborate errstring? */ - if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0) - retval = -1; - done: - unchunk_group(__FUNCTION__); - + // done: return retval; /* may be zero if we ignoring errors from commit */ } /* from_client_commit */ +/*! Discard all changes in candidate / revert 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 + * @retval -1 (Local) Error + */ +int +from_client_discard_changes(clicon_handle h, + cbuf *cbret) + +{ + int retval = -1; + + if (xmldb_copy(h, "running", "candidate") < 0){ + cprintf(cbret, "" + "operation-failed" + "application" + "error" + "read-registry" + ""); + goto ok; + } + cprintf(cbret, ""); + ok: + retval = 0; + // done: + 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 + * @param[out] cbret Return xml value cligen buffer + * @retval 0 OK. This may indicate both ok and err msg back to client (eg invalid) + * @retval -1 (Local) Error */ int -from_client_validate(clicon_handle h, - int s, - struct clicon_msg *msg, - const char *label) +from_client_validate(clicon_handle h, + char *db, + cbuf *cbret) { - int retval = -1; - char *candidate; + int retval = -1; + transaction_data_t *td = NULL; - if (clicon_msg_validate_decode(msg, - &candidate, - label) < 0){ - send_msg_err(s, clicon_errno, clicon_suberrno, - clicon_err_reason); - goto err; + if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){ + cprintf(cbret, "" + "invalid-value" + "protocol" + "error" + ""); + goto ok; } - if (strcmp(candidate, "candidate") != 0 && strcmp(candidate, "tmp") != 0){ - clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp"); - goto err; - } - clicon_debug(1, "Validate %s", candidate); - if (candidate_validate(h, candidate) < 0){ - clicon_debug(1, "Validate %s failed", candidate); - retval = 0; /* We ignore errors from commit, but maybe - we should fail on fatal errors? */ - goto err; - } - retval = 0; - if (send_msg_ok(s) < 0) + clicon_debug(1, "Validate %s", db); + + /* 1. Start transaction */ + if ((td = transaction_new()) == NULL) goto done; - goto done; - err: - /* XXX: more elaborate errstring? */ - if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0) - retval = -1; - done: - unchunk_group(__FUNCTION__); + /* Common steps (with commit) */ + if (validate_common(h, db, td) < 0){ + clicon_debug(1, "Validate %s failed", db); + /* XXX: candidate_validate should have proper error handling */ + cprintf(cbret, "" + "missing-attribute" + "protocol" + "error" + "%s" + "", + clicon_err_reason); + goto ok; + } + cprintf(cbret, ""); + ok: + retval = 0; + done: + if (retval < 0 && td) + plugin_transaction_abort(h, td); + if (td) + transaction_free(td); return retval; } /* from_client_validate */ diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h index e790b4e8..0fc3661d 100644 --- a/apps/backend/backend_commit.h +++ b/apps/backend/backend_commit.h @@ -40,8 +40,9 @@ /* * Prototypes */ -int from_client_validate(clicon_handle h, int s, struct clicon_msg *msg, const char *label); -int from_client_commit(clicon_handle h, int s, struct clicon_msg *msg, const char *label); -int candidate_commit(clicon_handle h, char *candidate); +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 candidate_commit(clicon_handle h, char *db); #endif /* _BACKEND_COMMIT_H_ */ diff --git a/apps/backend/backend_handle.h b/apps/backend/backend_handle.h index 46075d93..80251f08 100644 --- a/apps/backend/backend_handle.h +++ b/apps/backend/backend_handle.h @@ -37,7 +37,6 @@ #ifndef _BACKEND_HANDLE_H_ #define _BACKEND_HANDLE_H_ - /* * Prototypes * not exported. diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 03fe3b06..2832af7d 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -72,7 +72,7 @@ #include "backend_handle.h" /* Command line options to be passed to getopt(3) */ -#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:ptx:" +#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pt" /*! Terminate. Cannot use h after this */ static int @@ -141,8 +141,7 @@ usage(char *argv0, clicon_handle h) " -r\t\tReload running database\n" " -p \t\tPrint database yang specification\n" " -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\n" - " -g \tClient membership required to this group (default: %s)\n" - " -x \tSet CLICON_XMLDB_RPC to 0 or 1.\n", + " -g \tClient membership required to this group (default: %s)\n", argv0, plgdir ? plgdir : "none", confsock ? confsock : "none", @@ -153,11 +152,12 @@ usage(char *argv0, clicon_handle h) } static int -rundb_init(clicon_handle h) +db_reset(clicon_handle h, + char *db) { - if (xmldb_delete(h, "running") != 0 && errno != ENOENT) + if (xmldb_delete(h, db) != 0 && errno != ENOENT) return -1; - if (xmldb_init(h, "running") < 0) + if (xmldb_init(h, db) < 0) return -1; return 0; } @@ -192,7 +192,7 @@ rundb_main(clicon_handle h, if (clicon_xml_parse_file(fd, &xt, "") < 0) goto done; if ((xn = xml_child_i(xt, 0)) != NULL) - if (xmldb_put(h, "tmp", xn, OP_MERGE) < 0) + if (xmldb_put(h, "tmp", OP_MERGE, NULL, xn) < 0) goto done; if (candidate_commit(h, "tmp") < 0) goto done; @@ -426,14 +426,6 @@ main(int argc, char **argv) case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */ printalt++; break; - case 'x' : /* set xmldb rpc on */ - { - int i; - if (sscanf(optarg, "%d", &i) != 1) - usage(argv[0], h); - clicon_option_int_set(h, "CLICON_XMLDB_RPC", i); - } - break; default: usage(argv[0], h); break; @@ -503,7 +495,7 @@ main(int argc, char **argv) if (yang_spec_main(h, stdout, printspec) < 0) goto done; - /* First check for starup config + /* First check for startup config XXX the options below have become out-of-hand. Too complex, need to simplify*/ if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){ @@ -513,8 +505,12 @@ main(int argc, char **argv) goto done; } else - if (rundb_init(h) < 0) + if (db_reset(h, "running") < 0) goto done; + if (xmldb_init(h, "candidate") < 0) + goto done; + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; } /* If running exists and reload_running set, make a copy to candidate */ if (reload_running){ @@ -529,9 +525,17 @@ main(int argc, char **argv) /* Init running db * -I or if it isnt there */ - if (init_rundb || xmldb_exists(h, "running") != 1) - if (rundb_init(h) < 0) + if (init_rundb || xmldb_exists(h, "running") != 1){ + if (db_reset(h, "running") < 0) goto done; + } + /* If candidate does not exist, create it from running */ + if (xmldb_exists(h, "candidate") != 1){ + if (xmldb_init(h, "candidate") < 0) + goto done; + if (xmldb_copy(h, "running", "candidate") < 0) + goto done; + } /* Initialize plugins (also calls plugin_init() and plugin_start(argc,argv) in each plugin */ diff --git a/apps/backend/backend_plugin.c b/apps/backend/backend_plugin.c index a2c102fc..3477d5cb 100644 --- a/apps/backend/backend_plugin.c +++ b/apps/backend/backend_plugin.c @@ -470,55 +470,6 @@ plugin_finish(clicon_handle h) return 0; } -/*! Call from frontend to function 'func' in plugin 'plugin'. - * Plugin function is supposed to populate 'retlen' and 'retarg' where - * 'retarg' is malloc:ed data if non-NULL. - * @param[in] h Clicon handle - * @param[in] req Clicon message containing information about the downcall - * @param[out] retlen Length of return value - * @param[out] ret Return value - * @retval 0 OK - * @retval -1 Error - */ -int -plugin_downcall(clicon_handle h, - struct clicon_msg_call_req *req, - uint16_t *retlen, - void **retarg) -{ - int retval = -1; - int i; - downcall_cb funcp; - char name[PATH_MAX]; - char *error; - struct plugin *p; - - for (i = 0; i < nplugins; i++) { - p = &plugins[i]; - strncpy(name, p->p_name, sizeof(name)-1); - if (!strcmp(name+strlen(name)-3, ".so")) - name[strlen(name)-3] = '\0'; - /* If no plugin is given or the plugin-name matches */ - if (req->cr_plugin == NULL || strlen(req->cr_plugin)==0 || - strcmp(name, req->cr_plugin) == 0) { - funcp = dlsym(p->p_handle, req->cr_func); - if ((error = (char*)dlerror()) != NULL) { - clicon_err(OE_PROTO, ENOENT, - "Function does not exist: %s()", req->cr_func); - return -1; - } - retval = funcp(h, req->cr_op, req->cr_arglen, req->cr_arg, retlen, retarg); - goto done; - } - } - clicon_err(OE_PROTO, ENOENT,"%s: %s(): Plugin does not exist: %s", - __FUNCTION__, req->cr_func, req->cr_plugin); - return -1; - -done: - return retval; -} - /*! Create and initialize transaction */ transaction_data_t * transaction_new(void) @@ -771,4 +722,3 @@ plugin_transaction_abort(clicon_handle h, return retval; } - diff --git a/apps/backend/backend_plugin.h b/apps/backend/backend_plugin.h index dd86b7d6..ca6e474e 100644 --- a/apps/backend/backend_plugin.h +++ b/apps/backend/backend_plugin.h @@ -40,7 +40,6 @@ * Types */ - /*! Transaction data * Clicon internal, presented as void* to app's callback in the 'transaction_data' * type in clicon_backend_api.h @@ -69,8 +68,6 @@ int plugin_finish(clicon_handle h); int plugin_reset_state(clicon_handle h, char *dbname); int plugin_start_hooks(clicon_handle h, int argc, char **argv); -int plugin_downcall(clicon_handle h, struct clicon_msg_call_req *req, - uint16_t *retlen, void **retarg); transaction_data_t * transaction_new(void); int transaction_free(transaction_data_t *); diff --git a/apps/backend/backend_socket.c b/apps/backend/backend_socket.c index 637fdd8c..6065cbf7 100644 --- a/apps/backend/backend_socket.c +++ b/apps/backend/backend_socket.c @@ -265,7 +265,7 @@ config_accept_client(int fd, void *arg) /* * Here we register callbacks for actual data socket */ - if (event_reg_fd(s, from_client, (void*)ce, "client socket") < 0) + if (event_reg_fd(s, from_client, (void*)ce, "local netconf client socket") < 0) goto done; retval = 0; done: diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index aecea5ae..e5308886 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -133,6 +133,7 @@ backend_notify(clicon_handle h, struct handle_subscription *hs; int retval = -1; + clicon_debug(1, "%s %s", __FUNCTION__, stream); /* First thru all clients(sessions), and all subscriptions and find matches */ for (ce = backend_client_list(h); ce; ce = ce_next){ ce_next = ce->ce_next; @@ -257,7 +258,8 @@ backend_notify_xml(clicon_handle h, } struct client_entry * -backend_client_add(clicon_handle h, struct sockaddr *addr) +backend_client_add(clicon_handle h, + struct sockaddr *addr) { struct backend_handle *cb = handle(h); struct client_entry *ce; @@ -411,3 +413,79 @@ subscription_each(clicon_handle h, hs = cb->cb_subscription; return hs; } +/* Database dependency description */ +struct backend_netconf_reg { + qelem_t nr_qelem; /* List header */ + backend_netconf_cb_t nr_callback; /* Validation/Commit Callback */ + void *nr_arg; /* Application specific argument to cb */ + char *nr_tag; /* Xml tag when matched, callback called */ +}; +typedef struct backend_netconf_reg backend_netconf_reg_t; + +static backend_netconf_reg_t *deps = NULL; +/*! Register netconf callback + * Called from plugin to register a callback for a specific netconf XML tag. + */ +int +backend_netconf_register_callback(clicon_handle h, + backend_netconf_cb_t cb, /* Callback called */ + void *arg, /* Arg to send to callback */ + char *tag) /* Xml tag when callback is made */ +{ + backend_netconf_reg_t *nr; + + if ((nr = malloc(sizeof(backend_netconf_reg_t))) == NULL) { + clicon_err(OE_DB, errno, "malloc: %s", strerror(errno)); + goto catch; + } + memset (nr, 0, sizeof (*nr)); + nr->nr_callback = cb; + nr->nr_arg = arg; + nr->nr_tag = strdup(tag); /* strdup */ + INSQ(nr, deps); + return 0; +catch: + if (nr){ + if (nr->nr_tag) + free(nr->nr_tag); + free(nr); + } + return -1; +} + +/*! See if there is any callback registered for this tag + * + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at child of rpc: . + * @param[out] cb Output xml stream. For reply + * @param[out] cb_err Error xml stream. For error reply + * @param[out] xret Return XML, error or OK + * + * @retval -1 Error + * @retval 0 OK, not found handler. + * @retval 1 OK, handler called + */ +int +backend_netconf_plugin_callbacks(clicon_handle h, + cxobj *xe, + struct client_entry *ce, + cbuf *cbret) +{ + backend_netconf_reg_t *nreg; + int retval; + + if (deps == NULL) + return 0; + nreg = deps; + do { + if (strcmp(nreg->nr_tag, xml_name(xe)) == 0){ + if ((retval = nreg->nr_callback(h, xe, ce, cbret, nreg->nr_arg)) < 0) + return -1; + else + return 1; /* handled */ + } + nreg = NEXTQ(backend_netconf_reg_t *, nreg); + } while (nreg != deps); + return 0; +} + diff --git a/apps/backend/clixon_backend_handle.h b/apps/backend/clixon_backend_handle.h index a811b2ba..b309f2a2 100644 --- a/apps/backend/clixon_backend_handle.h +++ b/apps/backend/clixon_backend_handle.h @@ -43,6 +43,14 @@ /* * Types */ +struct client_entry; +typedef int (*backend_netconf_cb_t)( + clicon_handle h, + cxobj *xe, /* Request: */ + struct client_entry *ce, /* Client session */ + cbuf *cbret, /* Reply eg ... */ + void *arg /* Argument given at register */ +); /*! Generic downcall registration. * Enables any function to be called from (cli) frontend @@ -81,4 +89,13 @@ int subscription_delete(clicon_handle h, char *stream, struct handle_subscription *subscription_each(clicon_handle h, struct handle_subscription *hprev); + +int backend_netconf_register_callback(clicon_handle h, + backend_netconf_cb_t cb, /* Callback called */ + void *arg, /* Arg to send to callback */ + char *tag); /* Xml tag when callback is made */ + +int backend_netconf_plugin_callbacks(clicon_handle h, cxobj *xe, + struct client_entry *ce, cbuf *cbret); + #endif /* _CLIXON_BACKEND_HANDLE_H_ */ diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index aca4669d..53c43363 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -74,36 +74,6 @@ #include "cli_common.h" -/*! Initialize candidate database - * We have implemented these: - * shared - all users share a common candidate db - */ -int -init_candidate_db(clicon_handle h) -{ - int retval = -1; - - if (xmldb_exists(h, "running") != 1){ - clicon_err(OE_FATAL, 0, "Running db does not exist"); - goto err; - } - if (xmldb_exists(h, "candidate") != 1) - if (clicon_rpc_copy(h, "running", "candidate") < 0) - goto err; - retval = 0; - err: - return retval; -} - -/*! Exit candidate db - * (private canddidates should be removed?) - */ -int -exit_candidate_db(clicon_handle h) -{ - return 0; -} - /*! Register log notification stream * @param[in] h Clicon handle * @param[in] stream Event stream. CLICON is predefined, others are application-defined @@ -114,13 +84,13 @@ exit_candidate_db(clicon_handle h) * Note this calls cligen_regfd which may callback on cli command interpretator */ int -cli_notification_register(clicon_handle h, - char *stream, +cli_notification_register(clicon_handle h, + char *stream, enum format_enum format, - char *filter, - int status, - int (*fn)(int, void*), - void *arg) + char *filter, + int status, + int (*fn)(int, void*), + void *arg) { int retval = -1; char *logname; @@ -139,10 +109,10 @@ cli_notification_register(clicon_handle h, if (status){ /* start */ if (s_exist!=-1){ - clicon_err(OE_PLUGIN, 0, "%s: result log socket already exists", __FUNCTION__); + clicon_err(OE_PLUGIN, 0, "Result log socket already exists"); goto done; } - if (clicon_rpc_subscription(h, status, stream, format, filter, &s) < 0) + if (clicon_rpc_create_subscription(h, stream, filter, &s) < 0) goto done; if (cligen_regfd(s, fn, arg) < 0) goto done; @@ -154,9 +124,10 @@ cli_notification_register(clicon_handle h, cligen_unregfd(s_exist); } hash_del(cdat, logname); - if (clicon_rpc_subscription(h, status, stream, format, filter, NULL) < 0) +#if 0 /* cant turn off */ + if (clicon_rpc_create_subscription(h, status, stream, format, filter, NULL) < 0) goto done; - +#endif } retval = 0; done: @@ -214,7 +185,7 @@ cli_signal_flush(clicon_handle h) * @param[in] cvv Vector of cli string and instantiated variables * @param[in] argv Vector. First element xml key format string, eg "/aaa/%s" * @param[in] op Operation to perform on database - * Cvv will contain forst the complete cli string, and then a set of optional + * Cvv will contain first the complete cli string, and then a set of optional * instantiated variables. * Example: * cvv[0] = "set interfaces interface eth0 type bgp" @@ -257,7 +228,7 @@ cli_dbxmlv(clicon_handle h, if (clicon_rpc_change(h, "candidate", op, xk, str) < 0) goto done; if (clicon_autocommit(h)) { - if (clicon_rpc_commit(h, "candidate", "running") < 0) + if (clicon_rpc_commit(h) < 0) goto done; } retval = 0; @@ -465,9 +436,7 @@ cli_commitv(clicon_handle h, { int retval = -1; - if ((retval = clicon_rpc_commit(h, - "candidate", - "running")) < 0){ /* startup */ + if ((retval = clicon_rpc_commit(h)) < 0){ /* startup */ cli_output(stderr, "Commit failed. Edit and try again or discard changes"); goto done; } @@ -483,10 +452,12 @@ cli_validatev(clicon_handle h, cvec *vars, cvec *argv) { - int retval = -1; + int retval = -1; if ((retval = clicon_rpc_validate(h, "candidate")) < 0) - clicon_err(OE_CFG, 0, "Validate failed. Edit and try again or discard changes"); + goto done; + retval = 0; + done: return retval; } @@ -575,9 +546,9 @@ compare_dbsv(clicon_handle h, astext = cv_int32_get(cvec_i(argv, 0)); else astext = 0; - if (xmldb_get(h, "running", "/", &xc1, NULL, NULL) < 0) + if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0) goto done; - if (xmldb_get(h, "candidate", "/", &xc2, NULL, NULL) < 0) + if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0) goto done; if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */ goto done; @@ -620,7 +591,6 @@ load_config_filev(clicon_handle h, char *varstr; int fd = -1; cxobj *xt = NULL; - cxobj *xn; cxobj *x; cbuf *cbxml; @@ -663,20 +633,25 @@ load_config_filev(clicon_handle h, } if (clicon_xml_parse_file(fd, &xt, "") < 0) goto done; - if ((xn = xml_child_i(xt, 0)) != NULL){ + if (xt == NULL) + goto done; + /* Ensure top-level is "config", maybe this is too rough? */ + xml_name_set(xt, "config"); + // if ((xn = xml_child_i(xt, 0)) != NULL){ + if ((cbxml = cbuf_new()) == NULL) goto done; x = NULL; - while ((x = xml_child_each(xn, x, -1)) != NULL) + while ((x = xml_child_each(xt, x, -1)) != NULL) if (clicon_xml2cbuf(cbxml, x, 0, 0) < 0) goto done; - if (clicon_rpc_xmlput(h, "candidate", - replace?OP_REPLACE:OP_MERGE, - "", - cbuf_get(cbxml)) < 0) + if (clicon_rpc_edit_config(h, "candidate", + replace?OP_REPLACE:OP_MERGE, + "", + cbuf_get(cbxml)) < 0) goto done; cbuf_free(cbxml); - } + // } ret = 0; done: unchunk_group(__FUNCTION__); @@ -741,13 +716,13 @@ save_config_filev(clicon_handle h, goto done; } filename = vecp[0]; - if (xmldb_get(h, dbstr, "/", &xt, NULL, NULL) < 0) + if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0) goto done; if ((f = fopen(filename, "wb")) == NULL){ clicon_err(OE_CFG, errno, "Creating file %s", filename); goto done; } - if (xml_print(f, xt) < 0) + if (clicon_xml2file(f, xt, 0, 0) < 0) goto done; retval = 0; /* Fall through */ @@ -782,10 +757,8 @@ delete_allv(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); goto done; } - if (clicon_rpc_change(h, "candidate", - OP_REMOVE, - "/", "") < 0) - goto done; + if (clicon_rpc_delete_config(h, dbstr) < 0) + goto done; retval = 0; done: return retval; @@ -798,7 +771,7 @@ discard_changesv(clicon_handle h, cvec *cvv, cvec *argv) { - return clicon_rpc_copy(h, "running", "candidate"); + return clicon_rpc_copy_config(h, "running", "candidate"); } /*! Copy from one database to another, eg running->startup @@ -814,7 +787,7 @@ db_copy(clicon_handle h, db1 = cv_string_get(cvec_i(argv, 0)); db2 = cv_string_get(cvec_i(argv, 1)); - return clicon_rpc_copy(h, db1, db2); + return clicon_rpc_copy_config(h, db1, db2); } /* These are strings that can be used as 3rd argument to cli_setlog */ @@ -831,18 +804,18 @@ static int cli_notification_cb(int s, void *arg) { - struct clicon_msg *reply; + struct clicon_msg *reply = NULL; enum clicon_msg_type type; int eof; int retval = -1; char *eventstr = NULL; - int level; cxobj *xt = NULL; cxobj *xn; + cxobj *xe; char *format = (char*)arg; /* get msg (this is the reason this function is called) */ - if (clicon_msg_rcv(s, &reply, &eof, __FUNCTION__) < 0) + if (clicon_msg_rcv(s, &reply, &eof) < 0) goto done; if (eof){ clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__); @@ -855,11 +828,13 @@ cli_notification_cb(int s, goto done; type = ntohs(reply->op_type); switch (type){ - case CLICON_MSG_NOTIFY: - if (clicon_msg_notify_decode(reply, &level, &eventstr, __FUNCTION__) < 0) + 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", eventstr); + fprintf(stdout, "%s\n", eventstr); } else if (strcmp(format, SHOWAS_XML) == 0){ @@ -871,25 +846,20 @@ cli_notification_cb(int s, } else if (strcmp(format, SHOWAS_XML2TXT) == 0){ - if (clicon_xml_parse_string(&eventstr, &xt) < 0) - goto done; - if ((xn = xml_child_i(xt, 0)) != NULL) + if ((xn = xml_child_i(xe, 0)) != NULL) if (xml2txt(stdout, xn, 0) < 0) goto done; } else if (strcmp(format, SHOWAS_XML2JSON) == 0){ - if (clicon_xml_parse_string(&eventstr, &xt) < 0) - goto done; - if ((xn = xml_child_i(xt, 0)) != NULL){ + if ((xn = xml_child_i(xe, 0)) != NULL){ if (xml2json(stdout, xn, 0) < 0) goto done; } } break; default: - clicon_err(OE_PROTO, 0, "%s: unexpected reply: %d", - __FUNCTION__, type); + clicon_err(OE_PROTO, 0, "unexpected reply: %d", type); goto done; break; } @@ -897,15 +867,15 @@ cli_notification_cb(int s, done: if (xt) xml_free(xt); - unchunk_group(__FUNCTION__); /* event allocated by chunk */ + if (reply) + free(reply); return retval; - } /*! Make a notify subscription to backend and un/register callback for return messages. * * @param[in] h Clicon handle - * @param[in] cvv Not used + * @param[in] cvv Not used * @param[in] arg A string with [] * where is "0" or "1" * and is XXX @@ -943,7 +913,7 @@ cli_notifyv(clicon_handle h, "", status, cli_notification_cb, - (void*)formatstr) < 0) + (void*)strdup(formatstr)) < 0) goto done; retval = 0; @@ -951,6 +921,67 @@ cli_notifyv(clicon_handle h, return retval; } +/*! Lock database + * + * @param[in] h Clicon handle + * @param[in] cvv Not used + * @param[in] arg A string with + * @code + * lock("comment"), cli_lock("running"); + * @endcode + * XXX: format is a memory leak + */ +int +cli_lock(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + char *db; + int retval = -1; + + if (cvec_len(argv) != 1){ + clicon_err(OE_PLUGIN, 0, "Requires arguments: "); + goto done; + } + db = cv_string_get(cvec_i(argv, 0)); + if (clicon_rpc_lock(h, db) < 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Unlock database + * + * @param[in] h Clicon handle + * @param[in] cvv Not used + * @param[in] arg A string with + * @code + * lock("comment"), cli_lock("running"); + * @endcode + * XXX: format is a memory leak + */ +int +cli_unlock(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + char *db; + int retval = -1; + + if (cvec_len(argv) != 1){ + clicon_err(OE_PLUGIN, 0, "Requires arguments: "); + goto done; + } + db = cv_string_get(cvec_i(argv, 0)); + if (clicon_rpc_unlock(h, db) < 0) + goto done; + retval = 0; + done: + return retval; +} + + /* Here are backward compatible cligen callback functions used when * the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set. */ @@ -1076,8 +1107,6 @@ save_config_file(clicon_handle h, return retval; } - - int cli_notify(clicon_handle h, cvec *cvv, @@ -1126,15 +1155,15 @@ cli_notify(clicon_handle h, return retval; } -/* - * cli_debug - * set debug level on stderr (not syslog). +/*! set debug level on stderr (not syslog). * The level is either what is specified in arg as int argument. * _or_ if a 'level' variable is present in vars use that value instead. * XXX obsolete. Use cli_debug_cliv or cli_debug_backendv instead */ int -cli_debug(clicon_handle h, cvec *vars, cg_var *arg) +cli_debug(clicon_handle h, + cvec *vars, + cg_var *arg) { cg_var *cv; int level; diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 10fe40da..78cb7879 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -77,10 +77,10 @@ cli_terminate(clicon_handle h) { yang_spec *yspec; + clicon_rpc_close_session(h); if ((yspec = clicon_dbspec_yang(h)) != NULL) yspec_free(yspec); cli_plugin_finish(h); - exit_candidate_db(h); cli_handle_exit(h); return 0; } @@ -359,21 +359,12 @@ main(int argc, char **argv) goto done; } - /* A client does not have access to the candidate (and running) - databases if both these conditions are true: - 1. clicon_sock_family(h) == AF_INET[6] - */ - if (clicon_sock_family(h) == AF_UNIX) - if (init_candidate_db(h) < 0) - return -1; - if (logclisyntax) cli_logsyntax_set(h, logclisyntax); if (debug) clicon_option_dump(h, debug); - /* Join rest of argv to a single command */ restarg = clicon_strjoin(argc, argv, " ", __FUNCTION__); diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index f25eb2b4..3f80310a 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -142,12 +142,14 @@ expandv_dbvar(void *h, */ if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0) goto done; - if (xmldb_get(h, dbstr, xkpath, &xt, &xvec, &xlen) < 0) - goto done; + if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0) + goto done; /* One round to detect duplicates * XXX The code below would benefit from some cleanup */ j = 0; + if (xpath_vec(xt, xkpath, &xvec, &xlen) < 0) + goto done; for (i = 0; i < xlen; i++) { char *str; x = xvec[i]; @@ -187,7 +189,6 @@ expandv_dbvar(void *h, } retval = 0; done: - unchunk_group(__FUNCTION__); if (xvec) free(xvec); if (xt) @@ -474,7 +475,7 @@ show_confv_as(clicon_handle h, } else cprintf(cbx, "%s", xpath); - if (xmldb_get(h, db, cbuf_get(cbx), xt, NULL, NULL) < 0) + if (clicon_rpc_get_config(h, db, cbuf_get(cbx), xt) < 0) goto done; retval = 0; done: @@ -482,7 +483,6 @@ done: free(val); if (cbx) cbuf_free(cbx); - unchunk_group(__FUNCTION__); return retval; } @@ -597,7 +597,6 @@ show_confv_as_text1(clicon_handle h, done: if (xt) xml_free(xt); - unchunk_group(__FUNCTION__); return retval; } @@ -732,7 +731,9 @@ show_confv_xpath(clicon_handle h, } cv = cvec_find_var(cvv, "xpath"); xpath = cv_string_get(cv); - if (xmldb_get(h, str, xpath, &xt, &xv, &xlen) < 0) + if (clicon_rpc_get_config(h, str, xpath, &xt) < 0) + goto done; + if (xpath_vec(xt, xpath, &xv, &xlen) < 0) goto done; for (i=0; i - * @note Hardcoded that a variable in cvv is named "xpath" - */ -int -show_conf_xpath(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - int retval = -1; - char *str; - char *xpath; - cg_var *cv; - cxobj *xt = NULL; - cxobj **xv = NULL; - size_t xlen; - int i; - - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - /* Dont get attr here, take it from arg instead */ - if (strcmp(str, "running") != 0 && - strcmp(str, "candidate") != 0 && - strcmp(str, "startup") != 0){ - clicon_err(OE_PLUGIN, 0, "No such db name: %s", str); - goto done; - } - cv = cvec_find_var(cvv, "xpath"); - xpath = cv_string_get(cv); - if (xmldb_get(h, str, xpath, &xt, &xv, &xlen) < 0) - goto done; - for (i=0; i */ - cbuf *cb, /* Output xml stream. For reply */ - cbuf *cb_err, /* Error xml stream. For error reply */ - void *arg /* Argument given at netconf_register_callback() */ + cxobj *xn, /* Request: */ + cxobj **xret, /* Return xml tree, eg ... */ + void *arg /* Argument given at netconf_register_callback() */ ); /* @@ -57,25 +55,10 @@ typedef int (*netconf_cb_t)( */ int netconf_output(int s, cbuf *xf, char *msg); -int netconf_create_rpc_reply(cbuf *cb, /* msg buffer */ - cxobj *xr, /* orig request */ - char *body, - int ok); - int netconf_register_callback(clicon_handle h, netconf_cb_t cb, /* Callback called */ void *arg, /* Arg to send to callback */ char *tag); /* Xml tag when callback is made */ -int netconf_create_rpc_error(cbuf *xf, /* msg buffer */ - cxobj *xr, /* orig request */ - char *tag, - char *type, - char *severity, - char *message, - char *info); - -void netconf_ok_set(int ok); -int netconf_ok_get(void); int netconf_xpath(cxobj *xsearch, cxobj *xfilter, diff --git a/apps/netconf/netconf_filter.c b/apps/netconf/netconf_filter.c index 740507db..00b3232d 100644 --- a/apps/netconf/netconf_filter.c +++ b/apps/netconf/netconf_filter.c @@ -65,9 +65,7 @@ #include "netconf_lib.h" #include "netconf_filter.h" -/* - * xml_filter - * xf specifices a filter, and xn is an xml tree. +/* xf specifices a filter, and xn is an xml tree. * Select the part of xn that matches xf and return it. * Change xn destructively by removing the parts of the sub-tree that does * not match. @@ -107,9 +105,9 @@ leafstring(cxobj *x) * @retval -1 Error */ static int -xml_filter2(cxobj *xfilter, - cxobj *xparent, - int *remove_me) +xml_filter_recursive(cxobj *xfilter, + cxobj *xparent, + int *remove_me) { cxobj *s; cxobj *sprev; @@ -176,7 +174,7 @@ xml_filter2(cxobj *xfilter, } // XXX: s can be removed itself in the recursive call ! remove_s = 0; - if (xml_filter2(f, s, &remove_s) < 0) + if (xml_filter_recursive(f, s, &remove_s) < 0) return -1; if (remove_s){ xml_purge(s); @@ -207,9 +205,9 @@ xml_filter(cxobj *xfilter, int remove_s; /* Call recursive variant */ - retval = xml_filter2(xfilter, - xconfig, - &remove_s); + retval = xml_filter_recursive(xfilter, + xconfig, + &remove_s); return retval; } diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c index 3cd5e094..524be705 100644 --- a/apps/netconf/netconf_lib.c +++ b/apps/netconf/netconf_lib.c @@ -68,22 +68,8 @@ /* * Exported variables */ -enum transport_type transport = NETCONF_SSH; -int cc_closed = 0; - -static int cc_ok = 0; - -void -netconf_ok_set(int ok) -{ - cc_ok = ok; -} - -int -netconf_ok_get(void) -{ - return cc_ok; -} +enum transport_type transport = NETCONF_SSH; /* XXX Remove SOAP support */ +int cc_closed = 0; /* XXX Please remove (or at least hide in handle) this global variable */ int add_preamble(cbuf *xf) @@ -191,8 +177,7 @@ detect_endtag(char *tag, char ch, int *state) * @retval dbname Actual database file name */ char * -netconf_get_target(clicon_handle h, - cxobj *xn, +netconf_get_target(cxobj *xn, char *path) { cxobj *x; @@ -218,11 +203,13 @@ netconf_get_target(clicon_handle h, * @param[in] msg Only for debug */ int -netconf_output(int s, cbuf *xf, char *msg) +netconf_output(int s, + cbuf *xf, + char *msg) { char *buf = cbuf_get(xf); - int len = cbuf_len(xf); - int retval = -1; + int len = cbuf_len(xf); + int retval = -1; clicon_debug(1, "SEND %s", msg); if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */ diff --git a/apps/netconf/netconf_lib.h b/apps/netconf/netconf_lib.h index a942bb87..70077c1f 100644 --- a/apps/netconf/netconf_lib.h +++ b/apps/netconf/netconf_lib.h @@ -60,11 +60,6 @@ enum error_option{ /* edit-config */ CONTINUE_ON_ERROR }; -enum filter_option{ /* get-config/filter */ - FILTER_SUBTREE, - FILTER_XPATH -}; - /* * Variables */ @@ -74,14 +69,11 @@ extern int cc_closed; /* * Prototypes */ -void netconf_ok_set(int ok); -int netconf_ok_get(void); - int add_preamble(cbuf *xf); int add_postamble(cbuf *xf); int add_error_preamble(cbuf *xf, char *reason); int detect_endtag(char *tag, char ch, int *state); -char *netconf_get_target(clicon_handle h, cxobj *xn, char *path); +char *netconf_get_target(cxobj *xn, char *path); int add_error_postamble(cbuf *xf); int netconf_output(int s, cbuf *xf, char *msg); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 745bad9e..cfdda1b4 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -82,11 +82,12 @@ process_incoming_packet(clicon_handle h, { char *str; char *str0; - cxobj *xml_req = NULL; /* Request (in) */ + cxobj *xreq = NULL; /* Request (in) */ int isrpc = 0; /* either hello or rpc */ - cbuf *xf_out; - cbuf *xf_err; - cbuf *xf1; + cbuf *cbret = NULL; + cxobj *xret = NULL; /* Return (out) */ + cxobj *xrpc; + cxobj *xc; clicon_debug(1, "RECV"); clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb)); @@ -96,16 +97,15 @@ process_incoming_packet(clicon_handle h, } str = str0; /* Parse incoming XML message */ - if (clicon_xml_parse_string(&str, &xml_req) < 0){ - if ((cb = cbuf_new()) == NULL){ - netconf_create_rpc_error(cb, NULL, - "operation-failed", - "rpc", "error", - NULL, - NULL); - + if (clicon_xml_parse_string(&str, &xreq) < 0){ + if ((cbret = cbuf_new()) == NULL){ + cprintf(cbret, "" + "operation-failed" + "rpc" + "error" + "internal error" + ""); netconf_output(1, cb, "rpc-error"); - cbuf_free(cb); } else clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__); @@ -113,72 +113,56 @@ process_incoming_packet(clicon_handle h, goto done; } free(str0); - if (xpath_first(xml_req, "//rpc") != NULL){ + if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){ isrpc++; } else - if (xpath_first(xml_req, "//hello") != NULL) + if (xpath_first(xreq, "//hello") != NULL) ; else{ clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\ ed"); goto done; } - /* Initialize response buffers */ - if ((xf_out = cbuf_new()) == NULL){ - clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__); - goto done; + if (!isrpc){ /* hello */ + if (netconf_hello_dispatch(xreq) < 0) + goto done; } + else /* rpc */ + if (netconf_rpc_dispatch(h, xrpc, &xret) < 0){ + goto done; + } + else{ /* there is a return message in xret */ + cxobj *xa, *xa2; + assert(xret); - /* Create error buf */ - if ((xf_err = cbuf_new()) == NULL){ - clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__); - goto done; - } - netconf_ok_set(0); - if (isrpc){ - if (netconf_rpc_dispatch(h, - xml_req, - xpath_first(xml_req, "//rpc"), - xf_out, xf_err) < 0){ - assert(cbuf_len(xf_err)); - clicon_debug(1, "%s", cbuf_get(xf_err)); - if (isrpc){ - if (netconf_output(1, xf_err, "rpc-error") < 0) - goto done; + if ((cbret = cbuf_new()) != NULL){ + if ((xc = xml_child_i(xret,0))!=NULL){ + xa=NULL; + while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){ + if ((xa2 = xml_dup(xa)) ==NULL) + goto done; + if (xml_addsub(xc, xa2) < 0) + goto done; + } + add_preamble(cbret); + + clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0); + add_postamble(cbret); + if (netconf_output(1, cbret, "rpc-reply") < 0){ + cbuf_free(cbret); + goto done; + } + } } } - else{ - if ((xf1 = cbuf_new()) != NULL){ - if (netconf_create_rpc_reply(xf1, xml_req, cbuf_get(xf_out), netconf_ok_get()) < 0){ - cbuf_free(xf_out); - cbuf_free(xf_err); - cbuf_free(xf1); - goto done; - } - if (netconf_output(1, xf1, "rpc-reply") < 0){ - cbuf_reset(xf1); - netconf_create_rpc_error(xf1, xml_req, "operation-failed", - "protocol", "error", - NULL, cbuf_get(xf_err)); - netconf_output(1, xf1, "rpc-error"); - cbuf_free(xf_out); - cbuf_free(xf_err); - cbuf_free(xf1); - goto done; - } - cbuf_free(xf1); - } - } - } - else{ - netconf_hello_dispatch(xml_req); /* XXX: return-value */ - } - cbuf_free(xf_out); - cbuf_free(xf_err); done: - if (xml_req) - xml_free(xml_req); + if (xreq) + xml_free(xreq); + if (xret) + xml_free(xret); + if (cbret) + cbuf_free(cbret); return 0; } @@ -268,28 +252,12 @@ send_hello(int s) return retval; } -/*! Initialize candidate database */ static int -init_candidate_db(clicon_handle h) -{ - int retval = -1; - - /* init shared candidate */ - if (xmldb_exists(h, "candidate") != 1){ - if (xmldb_copy(h, "running", "candidate") < 0) - goto done; - } - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - -static int -terminate(clicon_handle h) +netconf_terminate(clicon_handle h) { yang_spec *yspec; + clicon_rpc_close_session(h); if ((yspec = clicon_dbspec_yang(h)) != NULL) yspec_free(yspec); clicon_handle_exit(h); @@ -402,8 +370,6 @@ main(int argc, char **argv) if (netconf_plugin_load(h) < 0) return -1; - if (init_candidate_db(h) < 0) - return -1; /* Call start function is all plugins before we go interactive */ tmp = *(argv-1); *(argv-1) = argv0; @@ -416,13 +382,11 @@ main(int argc, char **argv) goto done; if (debug) clicon_option_dump(h, debug); - if (event_loop() < 0) goto done; done: - netconf_plugin_unload(h); - terminate(h); + netconf_terminate(h); clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */ clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid()); return 0; diff --git a/apps/netconf/netconf_plugin.c b/apps/netconf/netconf_plugin.c index 856bc9f1..b18e5c61 100644 --- a/apps/netconf/netconf_plugin.c +++ b/apps/netconf/netconf_plugin.c @@ -66,6 +66,15 @@ #include "netconf_lib.h" #include "netconf_plugin.h" +/* Database dependency description */ +struct netconf_reg { + qelem_t nr_qelem; /* List header */ + netconf_cb_t nr_callback; /* Validation/Commit Callback */ + void *nr_arg; /* Application specific argument to cb */ + char *nr_tag; /* Xml tag when matched, callback called */ +}; +typedef struct netconf_reg netconf_reg_t; + /*! Unload a plugin */ static int @@ -245,10 +254,11 @@ catch: /*! See if there is any callback registered for this tag * - * @param xn Sub-tree (under xorig) at child of rpc: . - * @param xf Output xml stream. For reply - * @param xf_err Error xml stream. For error reply - * @param xorig Original request. + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at child of rpc: . + * @param[out] cb Output xml stream. For reply + * @param[out] cb_err Error xml stream. For error reply + * @param[out] xret Return XML, error or OK * * @retval -1 Error * @retval 0 OK, not found handler. @@ -256,31 +266,24 @@ catch: */ int netconf_plugin_callbacks(clicon_handle h, - cxobj *xn, - cbuf *xf, - cbuf *xf_err, - cxobj *xorig) + cxobj *xn, + cxobj **xret) { - netconf_reg_t *nr; + netconf_reg_t *nreg; int retval; if (deps == NULL) return 0; - nr = deps; + nreg = deps; do { - if (strcmp(nr->nr_tag, xml_name(xn)) == 0){ - if ((retval = nr->nr_callback(h, - xorig, - xn, - xf, - xf_err, - nr->nr_arg)) < 0) + if (strcmp(nreg->nr_tag, xml_name(xn)) == 0){ + if ((retval = nreg->nr_callback(h, xn, xret, nreg->nr_arg)) < 0) return -1; else return 1; /* handled */ } - nr = NEXTQ(netconf_reg_t *, nr); - } while (nr != deps); + nreg = NEXTQ(netconf_reg_t *, nreg); + } while (nreg != deps); return 0; } diff --git a/apps/netconf/netconf_plugin.h b/apps/netconf/netconf_plugin.h index 08dc3d1e..4b1f71b1 100644 --- a/apps/netconf/netconf_plugin.h +++ b/apps/netconf/netconf_plugin.h @@ -41,14 +41,6 @@ * Types */ -/* Database dependency description */ -struct netconf_reg { - qelem_t nr_qelem; /* List header */ - netconf_cb_t nr_callback; /* Validation/Commit Callback */ - void *nr_arg; /* Application specific argument to cb */ - char *nr_tag; /* Xml tag when matched, callback called */ -}; -typedef struct netconf_reg netconf_reg_t; /* * Prototypes @@ -59,12 +51,6 @@ int netconf_plugin_start(clicon_handle h, int argc, char **argv); int netconf_plugin_unload(clicon_handle h); - -int netconf_plugin_callbacks(clicon_handle h, -// dbspec_key *dbspec, - cxobj *xn, - cbuf *xf, - cbuf *xf_err, - cxobj *xt); +int netconf_plugin_callbacks(clicon_handle h, cxobj *xn, cxobj **xret); #endif /* _NETCONF_PLUGIN_H_ */ diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 92471ee8..4b9e6994 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -32,7 +32,7 @@ ***** END LICENSE BLOCK ***** * - * Code for handling netconf rpc messages + * Code for handling netconf rpc messages according to RFC 4741 and RFC 5277 *****************************************************************************/ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ @@ -74,18 +74,36 @@ */ -/*! get-config help function - * xfilter is a filter expression starting with - * only supported - * needs refactoring: move the lower part (after xfilter) up to get-config or - * sub-function., focus on xfilter part here. +/*! Get configuration * @param[in] h Clicon handle - * @param[in] xfilter Parsed xpath expression + * @param[in] xn Sub-tree (under xorig) at ... level. * @param[out] cb Output xml stream. For reply * @param[out] cb_err Error xml stream. For error reply - * @param[in] xorig Original request as xml tree. - * @param[in] target Database name - * @see netconf_get_config + * @param[out] xret Return XML, error or OK + * @note filter type subtree and xpath is supported, but xpath is preferred, and + * better performance and tested. Please use xpath. + * + * + * + * | + * + * + * + * + * + * | + * + * + * + * + * + * + * + * + * Example: + * + * + * ]]>]]> * Variants of the functions where x-axis is the variants of the clause * and y-axis is whether a or is present. * | no filter | filter subnet | filter xpath | @@ -102,222 +120,101 @@ * filter xpath + select all: ]]>]]> * filter subtree + config: - ]]>]]> + ]]>]]> * filter xpath + select: ]]>]]> */ - -/*! Variant for xmldb */ static int -netconf_filter_xmldb(clicon_handle h, - cxobj *xfilter, - enum filter_option foption, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig, - char *source) -{ - cxobj *xdb = NULL; - cxobj *xc; - cxobj *xfilterconf = NULL; - int retval = -1; - char *selector; - - /* Default subtree filter */ - switch (foption){ - case FILTER_SUBTREE: - /* Get the whole database as xml */ - if (xmldb_get(h, source, "/", &xdb, NULL, NULL) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "read-registry"); - goto done; - } - - /* Add under as reply */ - if ((xc = xml_insert(xdb, "configuration")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "filtering"); - goto done; - } - if (xfilter) - xfilterconf = xpath_first(xfilter, "//configuration"); - /* xml_filter removes parts of xml tree not matching */ - if (xfilterconf != NULL){ - if (xml_filter(xfilterconf, xc) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "filtering"); - goto done; - } - } - if (xml_child_nr(xc)) - clicon_xml2cbuf(cb, xc, 0, 1); - break; - case FILTER_XPATH: - if ((selector = xml_find_value(xfilter, "select")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-attribute", - "protocol", - "error", - NULL, - "select"); - goto done; - } - if (xmldb_get(h, source, selector, &xdb, NULL, NULL) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "read-registry"); - goto done; - } - if ((xc = xml_insert(xdb, "configuration")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "filtering"); - goto done; - } - if (xml_child_nr(xc)) - clicon_xml2cbuf(cb, xc, 0, 1); - break; - default: - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - "filter type not supported", - "type"); - goto done; - } - retval = 0; - done: - if (xdb) - xml_free(xdb); - return retval; -} - -/*! Get configuration - * @param[in] h Clicon handle - * @param[in] xorig Sub-tree (under xorig) at ... level. - * @param[out] cb Output xml stream. For reply - * @param[out] cb_err Error xml stream. For error reply - * @param[in] xorig Original request as xml tree. - * @note Only filter type="xpath" is supported - - - - | - - - - - - | - - - - - - - - Example: - - - ]]>]]> - */ -int netconf_get_config(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { - cxobj *xfilter; /* filter */ - int retval = -1; - char *source; - enum filter_option foption = FILTER_SUBTREE; - char *ftype = NULL; + cxobj *xfilter; /* filter */ + int retval = -1; + char *source; + char *ftype = NULL; + cxobj *xfilterconf; + cxobj *xconf; - if ((source = netconf_get_target(h, xn, "source")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "source"); - goto done; - } - /* ie ... */ - if ((xfilter = xpath_first(xn, "filter")) != NULL) { - if ((ftype = xml_find_value(xfilter, "type")) != NULL) - if (strcmp(ftype, "xpath")==0) - foption = FILTER_XPATH; - } - if (netconf_filter_xmldb(h, xfilter, foption, cb, cb_err, xorig, source) < 0) - goto done; + if ((source = netconf_get_target(xn, "source")) == NULL){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "source" + ""); + goto ok; + } + /* ie ... */ + if ((xfilter = xpath_first(xn, "filter")) != NULL) + ftype = xml_find_value(xfilter, "type"); + if (ftype == NULL || strcmp(ftype, "xpath")==0){ + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + } + else if (strcmp(ftype, "subtree")==0){ + /* Default rfc filter is subtree. I prefer xpath and use it internally. + Get whole subtree and then filter aftwerwards. This is suboptimal. + Therefore please use xpath. + */ + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + if (xfilter && + (xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL && + (xconf = xpath_first(*xret, "/rpc-reply/data/configuration")) != NULL){ + /* xml_filter removes parts of xml tree not matching */ + if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) || + xml_filter(xfilterconf, xconf) < 0){ + clicon_xml_parse(xret, "" + "operation-failed" + "applicatio" + "error" + "filtering" + ""); + } + } + } + else{ + clicon_xml_parse(xret, "" + "operation-failed" + "applicatio" + "error" + "filter type not supported" + "type" + ""); + } + ok: /* netconf error is not fatal */ retval = 0; - done: + done: return retval; } /*! Get options from netconf edit-config + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] op Operation type, eg merge,replace,... + * @param[out] testopt test option, eg set, test + * @param[out] erropt Error option, eg stop-on-error + * @retval -1 Fatal Error + * @retval 0 parameter error, xret returns error + * @retval 1 OK, op, testopt and erropt set + * @example * * ... * (merge | none | replace) * (stop-on-error | continue-on-error ) * (set | test-then-set | test-only) * - * - * */ static int get_edit_opts(cxobj *xn, - enum operation_type *op, enum test_option *testopt, enum error_option *erropt, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { int retval = -1; cxobj *x; char *optstr; - - if ((x = xpath_first(xn, "default-operation")) != NULL){ - if ((optstr = xml_body(x)) != NULL){ - if (strcmp(optstr, "replace") == 0) - *op = OP_REPLACE; - else - if (strcmp(optstr, "merge") == 0) - *op = OP_MERGE; - else - if (strcmp(optstr, "none") == 0) - *op = OP_NONE; - else{ - netconf_create_rpc_error(cb_err, xorig, - "invalid-value", - "protocol", - "error", - NULL, - NULL); - goto done; - } - } - } + if ((x = xpath_first(xn, "test-option")) != NULL){ if ((optstr = xml_body(x)) != NULL){ if (strcmp(optstr, "test-then-set") == 0) @@ -328,15 +225,8 @@ get_edit_opts(cxobj *xn, else if (strcmp(optstr, "test-only") == 0) *testopt = TEST_ONLY; - else{ - netconf_create_rpc_error(cb_err, xorig, - "invalid-value", - "protocol", - "error", - NULL, - NULL); - goto done; - } + else + goto parerr; } } if ((x = xpath_first(xn, "error-option")) != NULL){ @@ -346,23 +236,21 @@ get_edit_opts(cxobj *xn, else if (strcmp(optstr, "continue-on-error") == 0) *erropt = CONTINUE_ON_ERROR; - else{ - netconf_create_rpc_error(cb_err, xorig, - "invalid-value", - "protocol", - "error", - NULL, - NULL); - goto done; - } + else + goto parerr; } } - retval = 0; - done: - return retval; + retval = 1; /* hunky dory */ + return retval; + parerr: /* parameter error, xret set */ + clicon_xml_parse(xret, "" + "invalid-value" + "protocol" + "error" + ""); + return 0; } - /*! Netconf edit configuration Write the change on a tmp file, then load that into candidate configuration. @@ -396,84 +284,112 @@ get_edit_opts(cxobj *xn, (stop-on-error | continue-on-error ) (set | test-then-set | test-only) + +CLIXON addition: + + * + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK + * @retval 0 OK, xret points to valid return, either ok or rpc-error + * @retval -1 Error * only 'config' supported * error-option: only stop-on-error supported * test-option: not supported * - * @note delete/remove not implemented - * + * @note erropt, testopt only supports default */ -int +static int netconf_edit_config(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { int retval = -1; - int ret; + int optret; enum operation_type operation = OP_MERGE; - enum test_option testopt = TEST_THEN_SET; - enum error_option erropt = STOP_ON_ERROR; + enum test_option testopt = TEST_THEN_SET;/* only supports this */ + enum error_option erropt = STOP_ON_ERROR; /* only supports this */ cxobj *xc; /* config */ + cxobj *x; + cxobj *xfilter; + char *ftype = NULL; cxobj *xcc; /* child of config */ char *target; /* db */ cbuf *cbxml = NULL; - char *xmlstr; /* must have target, and it should be candidate */ - if ((target = netconf_get_target(h, xn, "target")) == NULL || + if ((target = netconf_get_target(xn, "target")) == NULL || strcmp(target, "candidate")){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "target"); - goto done; + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - if (get_edit_opts(xn, &operation, &testopt, &erropt, cb_err, xorig) < 0) + /* CLICON addition, eg /> */ + if ((xfilter = xpath_first(xn, "filter")) != NULL) { + if ((ftype = xml_find_value(xfilter, "type")) != NULL) + if (strcmp(ftype,"restconf")){ + clicon_xml_parse(xret, "" + "invalid-value" + "protocol" + "error" + ""); + goto ok; + } + } + if ((x = xpath_first(xn, "default-operation")) != NULL){ + if (xml_operation(xml_body(x), &operation) < 0){ + clicon_xml_parse(xret, "" + "invalid-value" + "protocol" + "error" + ""); + goto ok; + } + } + if ((optret = get_edit_opts(xn, &testopt, &erropt, xret)) < 0) goto done; + if (optret == 0) /* error in opt parameters */ + goto ok; + /* not supported opts */ + if (testopt!=TEST_THEN_SET || erropt!=STOP_ON_ERROR){ + clicon_xml_parse(xret, "" + "operation-not-supported" + "protocol" + "error" + ""); + goto ok; + } + /* operation is OP_REPLACE, OP_MERGE, or OP_NONE pass all to backend */ if ((xc = xpath_first(xn, "config")) != NULL){ +#if 0 /* application-specific code registers 'config' */ - if ((ret = netconf_plugin_callbacks(h, xc, cb, cb_err, xorig)) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", "error", - NULL, "Validation"); - goto done; + if ((ret = netconf_plugin_callbacks(h, xc, xret)) < 0){ + goto ok; } +#endif if ((cbxml = cbuf_new()) == NULL) goto done; if ((xcc = xml_child_i(xc, 0)) != NULL) if (clicon_xml2cbuf(cbxml, xcc, 0, 0) < 0) goto done; - xmlstr = cbuf_get(cbxml); - if (clicon_rpc_xmlput(h, target, - operation, - "", /* api-path */ - xmlstr) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "access-denied", - "protocol", - "error", - NULL, - "edit_config"); - goto done; - } + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; } - netconf_ok_set(1); + ok: retval = 0; - done: + done: if (cbxml) cbuf_free(cbxml); - unchunk_group(__FUNCTION__); return retval; } -/* +/*! Netconf copy configuration @@ -484,59 +400,42 @@ netconf_edit_config(clicon_handle h, + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int +static int netconf_copy_config(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { - char *source, *target; /* filenames */ - int retval = -1; + int retval = -1; + char *source; + char *target; /* filenames */ - if ((source = netconf_get_target(h, xn, "source")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "source"); - goto done; + if ((source = netconf_get_target(xn, "source")) == NULL){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "source" + ""); + goto ok; } - if ((target = netconf_get_target(h, xn, "target")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "source"); - goto done; + if ((target = netconf_get_target(xn, "target")) == NULL){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - -#ifdef notyet - /* If target is locked and not by this client, then return an error */ - if (target_locked(&client) > 0 && client != ce_nr){ - netconf_create_rpc_error(cb_err, xorig, - "access-denied", - "protocol", - "error", - NULL, - "lock"); - goto done; - } -#endif - if (clicon_rpc_copy(h, source, target) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", "error", - NULL, "Internal error"); - goto done; - } - netconf_ok_set(1); + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + ok: retval = 0; done: - unchunk_group(__FUNCTION__); return retval; } @@ -548,70 +447,36 @@ netconf_copy_config(clicon_handle h, Delete a configuration datastore. The configuration datastore cannot be deleted. + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int +static int netconf_delete_config(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { char *target; /* filenames */ int retval = -1; - if ((target = netconf_get_target(h, xn, "target")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "target"); - goto done; + if ((target = netconf_get_target(xn, "target")) == NULL || + strcmp(target, "running")==0){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - if (strcmp(target, "running") == 0){ - netconf_create_rpc_error(cb_err, xorig, - "bad-element", - "protocol", - "error", - NULL, - "target"); - goto done; - } - if (clicon_rpc_change(h, target, - OP_REMOVE, - "/", "") < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", "error", - NULL, "Internal error"); - goto done; - } - if (xmldb_init(h, target) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", "error", - NULL, "Internal error"); - goto done; - } - netconf_ok_set(1); + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + ok: retval = 0; done: return retval; } -/*! Close a (user) session - -*/ -int -netconf_close_session(cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) -{ - cc_closed++; - netconf_ok_set(1); - return 0; -} /*! Lock a database @@ -619,29 +484,30 @@ netconf_close_session(cxobj *xn, - XXX + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int +static int netconf_lock(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { - char *target; - int retval = -1; + int retval = -1; + char *target; - if ((target = netconf_get_target(h, xn, "target")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "source"); - goto done; + if ((target = netconf_get_target(xn, "target")) == NULL){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - - netconf_ok_set(1); + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + ok: retval = 0; done: return retval; @@ -654,29 +520,35 @@ netconf_lock(clicon_handle h, XXX + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int +static int netconf_unlock(clicon_handle h, - cxobj *xn, - cbuf *cb, cbuf *cb_err, - cxobj *xorig) + cxobj *xn, + cxobj **xret) +{ + return netconf_lock(h, xn, xret); +} + +/*! Close a (user) session + + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK +*/ +static int +netconf_close_session(clicon_handle h, + cxobj *xn, + cxobj **xret) { - char *target; int retval = -1; - if ((target = netconf_get_target(h, xn, "target")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "target"); + cc_closed++; + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; - } - /* XXX notyet */ - netconf_ok_set(1); retval = 0; - done: + done: return retval; } @@ -684,141 +556,108 @@ netconf_unlock(clicon_handle h, PID - XXX + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int -netconf_kill_session(cxobj *xn, cbuf *cb, cbuf *cb_err, cxobj *xorig) +static int +netconf_kill_session(clicon_handle h, + cxobj *xn, + cxobj **xret) { -#ifdef notyet - cxobj *xsessionid; - char *str, *ep; - long i; + int retval=-1; + cxobj *xs; - if ((xsessionid = xpath_first(xn, "//session-id")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "session-id"); - return -1; + if ((xs = xpath_first(xn, "//session-id")) == NULL){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "session-id" + ""); + goto ok; } - if (xml_find_value(xsessionid, "body") == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "session-id"); - return -1; + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + ok: + retval = 0; + done: + return retval; +} +/*! Check the semantic consistency of candidate + + :validate + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK + */ +static int +netconf_validate(clicon_handle h, + cxobj *xn, + cxobj **xret) +{ + int retval = -1; + char *target; + + if ((target = netconf_get_target(xn, "source")) == NULL){ + clicon_xml_parse(xret, "" + "missing-element" + "protocol" + "error" + "target" + ""); + goto ok; } - if ((str = xml_find_value(xsessionid, "body")) != NULL){ - i = strtol(str, &ep, 0); - if ((i == LONG_MIN || i == LONG_MAX) && errno) - return -1; - if ((ce = find_ce_bynr(i)) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", - "error", - NULL, - "No such client"); - return -1; - } - } -// ce_change_state(ce, CS_CLOSED, "Received close-session msg"); - netconf_ok_set(1); -#endif - return 0; + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) + goto done; + ok: + retval = 0; + done: + return retval; } /*! Commit candidate -> running :candidate + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int +static int netconf_commit(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { - int retval = -1; + int retval = -1; - if (clicon_rpc_commit(h, "candidate", "running") < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", "error", - NULL, "Internal error"); + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; - } - netconf_ok_set(1); retval = 0; done: - unchunk_group(__FUNCTION__); return retval; } /*! Discard all changes in candidate / revert to running :candidate + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK */ -int +static int netconf_discard_changes(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { - int retval = -1; + int retval = -1; - if (clicon_rpc_copy(h, "running", "candidate") < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "protocol", "error", - NULL, "Internal error"); + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) goto done; - } - netconf_ok_set(1); - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - -/*! Check the semantic consistency of candidate - - :validate - */ -int -netconf_validate(clicon_handle h, - cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) -{ - char *target; - int retval = -1; - - if ((target = netconf_get_target(h, xn, "source")) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "missing-element", - "protocol", - "error", - NULL, - "target"); - goto done; - } - /* Do validation */ - netconf_ok_set(1); retval = 0; done: return retval; } -/* - * netconf_notification_cb - * Called when a notification has happened on backend +/*! Called when a notification has happened on backend * and this session has registered for that event. * Filter it and forward it. @@ -838,13 +677,13 @@ netconf_notification_cb(int s, { cxobj *xfilter = (cxobj *)arg; char *selector; - struct clicon_msg *reply; + struct clicon_msg *reply = NULL; int eof; char *event = NULL; - int level; int retval = -1; cbuf *cb; cxobj *xe = NULL; /* event xml */ + cxobj *xt = NULL; /* top xml */ enum clicon_msg_type type; if (0){ @@ -852,7 +691,7 @@ netconf_notification_cb(int s, xml_print(stderr, xfilter); /* debug */ } /* get msg (this is the reason this function is called) */ - if (clicon_msg_rcv(s, &reply, &eof, __FUNCTION__) < 0) + if (clicon_msg_rcv(s, &reply, &eof) < 0) goto done; /* handle close from remote end: this will exit the client */ if (eof){ @@ -867,13 +706,14 @@ netconf_notification_cb(int s, /* multiplex on message type: we only expect notify */ type = ntohs(reply->op_type); switch (type){ - case CLICON_MSG_NOTIFY: - if (clicon_msg_notify_decode(reply, &level, &event, __FUNCTION__) < 0) + 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 */ - if (clicon_xml_parse_string(&event, &xe) < 0) - goto done; /* find and apply filter */ if ((selector = xml_find_value(xfilter, "select")) == NULL) goto done; @@ -888,17 +728,14 @@ netconf_notification_cb(int s, goto done; } add_preamble(cb); /* Make it well-formed netconf xml */ - cprintf(cb, ""); - cprintf(cb, ""); - cprintf(cb, "%s", event); - cprintf(cb, ""); - cprintf(cb, ""); + 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: @@ -909,11 +746,10 @@ netconf_notification_cb(int s, } retval = 0; done: - if (xe != NULL) - xml_free(xe); -// if (event) -// free(event); - unchunk_group(__FUNCTION__); + if (xt != NULL) + xml_free(xt); + if (reply) + free(reply); return retval; } @@ -926,213 +762,108 @@ netconf_notification_cb(int s, # only for replay (NYI) Dont support replay + * @param[in] h clicon handle + * @param[in] xn Sub-tree (under xorig) at ... level. + * @param[out] xret Return XML, error or OK + */ static int netconf_create_subscription(clicon_handle h, cxobj *xn, - cbuf *cb, - cbuf *cb_err, - cxobj *xorig) + cxobj **xret) { - cxobj *xstream; + int retval = -1; cxobj *xfilter; - cxobj *xfilter2 = NULL; - char *stream = NULL; int s; char *ftype; - int retval = -1; - if ((xstream = xpath_first(xn, "//stream")) != NULL) - stream = xml_find_value(xstream, "body"); - if (stream == NULL) - stream = "NETCONF"; if ((xfilter = xpath_first(xn, "//filter")) != NULL){ if ((ftype = xml_find_value(xfilter, "type")) != NULL){ if (strcmp(ftype, "xpath") != 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - "only xpath filter type supported", - "type"); - goto done; + clicon_xml_parse(xret, "" + "operation-failed" + "application" + "error" + "only xpath filter type supported" + "type" + ""); + goto ok; } } - /* xfilter2 is applied to netconf_notification_cb below - and is only freed on exit since no unreg is made. - */ - if ((xfilter2 = xml_dup(xfilter)) == NULL){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "Internal server error"); - goto done; - } - } - - if (clicon_rpc_subscription(h, 1, stream, MSG_NOTIFY_XML, "", &s) < 0){ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "application", - "error", - NULL, - "Internal protocol error"); + if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, &s) < 0) goto done; - } - if (event_reg_fd(s, netconf_notification_cb, xfilter2, "notification socket") < 0) + if (event_reg_fd(s, + netconf_notification_cb, + xfilter?xml_dup(xfilter):NULL, + "notification socket") < 0) goto done; - netconf_ok_set(1); + ok: retval = 0; done: return retval; } - /*! The central netconf rpc dispatcher. Look at first tag and dispach to sub-functions. * Call plugin handler if tag not found. If not handled by any handler, return * error. * @param[in] h clicon handle - * @param[in] xorig Original request as xml tree. * @param[in] xn Sub-tree (under xorig) at ... level. - * @param[out] cb Output xml stream. For reply - * @param[out] cb_err Error xml stream. For error reply + * @param[out] xret Return XML, error or OK */ int netconf_rpc_dispatch(clicon_handle h, - cxobj *xorig, cxobj *xn, - cbuf *cb, - cbuf *cb_err) + cxobj **xret) { cxobj *xe; int ret = 0; xe = NULL; while ((xe = xml_child_each(xn, xe, CX_ELMNT)) != NULL) { - if (strcmp(xml_name(xe), "close-session") == 0) - return netconf_close_session(xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "get-config") == 0) - return netconf_get_config(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "edit-config") == 0) - return netconf_edit_config(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "copy-config") == 0) - return netconf_copy_config(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "delete-config") == 0) - return netconf_delete_config(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "kill-session") == 0) /* TBD */ - return netconf_kill_session(xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "lock") == 0) /* TBD */ - return netconf_lock(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "unlock") == 0) /* TBD */ - return netconf_unlock(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "commit") == 0) - return netconf_commit(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "discard-changes") == 0) - return netconf_discard_changes(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "validate") == 0) - return netconf_validate(h, xe, cb, cb_err, xorig); - else - if (strcmp(xml_name(xe), "create-subscription") == 0) - return netconf_create_subscription(h, - xe, cb, cb_err, xorig); - else{ - if ((ret = netconf_plugin_callbacks(h, - xe, cb, cb_err, xorig)) < 0) + if (strcmp(xml_name(xe), "get-config") == 0) + return netconf_get_config(h, xe, xret); + else if (strcmp(xml_name(xe), "edit-config") == 0) + return netconf_edit_config(h, xe, xret); + else if (strcmp(xml_name(xe), "copy-config") == 0) + return netconf_copy_config(h, xe, xret); + else if (strcmp(xml_name(xe), "delete-config") == 0) + return netconf_delete_config(h, xe, xret); + else if (strcmp(xml_name(xe), "lock") == 0) + return netconf_lock(h, xe, xret); + else if (strcmp(xml_name(xe), "unlock") == 0) + return netconf_unlock(h, xe, xret); + else if (strcmp(xml_name(xe), "close-session") == 0) + return netconf_close_session(h, xe, xret); + else if (strcmp(xml_name(xe), "kill-session") == 0) + return netconf_kill_session(h, xe, xret); + /* Validate capability :validate */ + else if (strcmp(xml_name(xe), "validate") == 0) + return netconf_validate(h, xe, xret); + /* Candidate configuration capability :candidate */ + else if (strcmp(xml_name(xe), "commit") == 0) + return netconf_commit(h, xe, xret); + else if (strcmp(xml_name(xe), "discard-changes") == 0) + return netconf_discard_changes(h, xe, xret); + /* RFC 5277 :notification */ + else if (strcmp(xml_name(xe), "create-subscription") == 0) + return netconf_create_subscription(h, xe, xret); + /* Others */ + else{ + if ((ret = netconf_plugin_callbacks(h, xe, xret)) < 0) return -1; if (ret == 0){ /* not handled by callback */ - netconf_create_rpc_error(cb_err, xorig, - "operation-failed", - "rpc", "error", - xml_name(xe), "Not recognized"); - ret = -1; + clicon_xml_parse(xret, "" + "operation-failed" + "rpc" + "error" + "%s" + "Not recognized" + "", xml_name(xe)); + return 0; } } } return ret; } - -/* - * netconf_create_rpc_reply - * Create an rpc reply msg in cb - * Then send it using send_msg_to_client() - */ -int -netconf_create_rpc_reply(cbuf *cb, /* msg buffer */ - cxobj *xr, /* orig request */ - char *body, - int ok - ) -{ - cxobj *xn, *xa; - - add_preamble(cb); - cprintf(cb, ""); - if (ok) /* Just _maybe_ we should send data instead of ok if (if there is any) - even if ok is set? */ - cprintf(cb, ""); - else{ - cprintf(cb, ""); - cprintf(cb, "%s", body); - cprintf(cb, ""); - } - cprintf(cb, ""); - add_postamble(cb); - return 0; -} - - -/* - * netconf_create_rpc_error - * Create an rpc error msg in cb - * (Then it should be sent at a later stage using send_msg_to_client() ) - * Arguments: - * cb msg buffer to construct netconf message in - * xr original request including an "rpc" tag - * ... arguments to rpc-error reply message - */ -int -netconf_create_rpc_error(cbuf *cb, /* msg buffer */ - cxobj *xr, /* orig request */ - char *tag, - char *type, - char *severity, - char *message, - char *info) -{ - add_error_preamble(cb, tag); - cprintf(cb, ""); - cprintf(cb, ""); - if (tag) - cprintf(cb, "%s", tag); - cprintf(cb, "%s", type); - cprintf(cb, "%s", severity); - if (message) - cprintf(cb, "%s", message); - if (info) - cprintf(cb, "%s", info); - cprintf(cb, ""); - cprintf(cb, ""); - add_error_postamble(cb); - return 0; -} - diff --git a/apps/netconf/netconf_rpc.h b/apps/netconf/netconf_rpc.h index 631ad54a..bfc992d9 100644 --- a/apps/netconf/netconf_rpc.h +++ b/apps/netconf/netconf_rpc.h @@ -42,20 +42,7 @@ */ int netconf_rpc_dispatch(clicon_handle h, - cxobj *xorig, - cxobj *xn, - cbuf *xf, - cbuf *xf_err); - -int netconf_create_rpc_reply(cbuf *xf, /* msg buffer */ - cxobj *xr, /* orig request */ - char *body, int ok); -int netconf_create_rpc_error(cbuf *xf, /* msg buffer */ - cxobj *xr, /* orig request */ - char *tag, - char *type, - char *severity, - char *message, - char *info); + cxobj *xn, + cxobj **xret); #endif /* _NETCONF_RPC_H_ */ diff --git a/apps/restconf/README b/apps/restconf/README index 5ecfc1ef..5d3cd435 100644 --- a/apps/restconf/README +++ b/apps/restconf/README @@ -54,6 +54,9 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et } ] +curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data + + Debugging --------- Start the restconf programs with debug flag: diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 4306569a..514b0daf 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -141,16 +141,15 @@ api_data_get(clicon_handle h, int i; cbuf *path = NULL; cbuf *path1 = NULL; - cxobj *xt = NULL; cbuf *cbx = NULL; cxobj **vec = NULL; - size_t veclen; yang_spec *yspec; yang_stmt *y; yang_stmt *ykey; char *name; cvec *cvk = NULL; /* vector of index keys */ cg_var *cvi; + cxobj *xret = NULL; clicon_debug(1, "%s", __FUNCTION__); yspec = clicon_dbspec_yang(h); @@ -217,32 +216,34 @@ api_data_get(clicon_handle h, } } clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path)); - if (xmldb_get(h, "running", cbuf_get(path), &xt, &vec, &veclen) < 0) - goto done; - - if ((cbx = cbuf_new()) == NULL) - goto done; - if (veclen==0){ + if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){ notfound(r); goto done; } + if ((cbx = cbuf_new()) == NULL) + goto done; FCGX_SetExitStatus(200, r->out); /* OK */ FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n"); FCGX_FPrintF(r->out, "\r\n"); - if (xml2json_cbuf_vec(cbx, vec, veclen, 0) < 0) + clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret)); + vec = xml_childvec_get(xret); + if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0) goto done; + + clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx)); FCGX_FPrintF(r->out, "%s", cbuf_get(cbx)); FCGX_FPrintF(r->out, "\r\n\r\n"); retval = 0; done: + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); if (cbx) cbuf_free(cbx); - if (xt) - xml_free(xt); if (path) cbuf_free(path); if (path1) cbuf_free(path1); + if (xret) + xml_free(xret); return retval; } @@ -266,14 +267,14 @@ api_data_delete(clicon_handle h, clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); for (i=0; i") < 0){ + notfound(r); goto done; - if (clicon_rpc_commit(h, "candidate", "running") < 0) + } + if (clicon_rpc_commit(h) < 0) goto done; FCGX_SetExitStatus(201, r->out); FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); @@ -338,16 +339,23 @@ api_data_put(clicon_handle h, } if ((cbx = cbuf_new()) == NULL) goto done; + cprintf(cbx, ""); x = NULL; - while ((x = xml_child_each(xdata, x, -1)) != NULL) + while ((x = xml_child_each(xdata, x, -1)) != NULL) { if (clicon_xml2cbuf(cbx, x, 0, 0) < 0) goto done; - if (clicon_rpc_xmlput(h, "candidate", - OP_MERGE, - api_path, - cbuf_get(cbx)) < 0) + } + cprintf(cbx, ""); + clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); + if (clicon_rpc_edit_config(h, "candidate", + OP_MERGE, + api_path, + cbuf_get(cbx)) < 0){ + notfound(r); goto done; - if (clicon_rpc_commit(h, "candidate", "running") < 0) + } + + if (clicon_rpc_commit(h) < 0) goto done; FCGX_SetExitStatus(201, r->out); /* Created */ FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); @@ -437,7 +445,12 @@ request_process(clicon_handle h, clicon_debug(1, "DATA=%s", data); if (str2cvec(data, '&', '=', &dvec) < 0) goto done; - method = pvec[2]; + + if ((method = pvec[2]) == NULL){ + retval = notfound(r); + goto done; + } + retval = 0; test(r, 1); /* If present, check credentials */ @@ -448,6 +461,7 @@ request_process(clicon_handle h, if (auth == 0) goto done; clicon_debug(1, "%s credentials ok 2", __FUNCTION__); + clicon_debug(1, "%s credentials ok 3", __FUNCTION__); if (strcmp(method, "data") == 0) /* restconf, skip /api/data */ retval = api_data(h, r, path, pcvec, 2, qvec, data); @@ -469,6 +483,18 @@ request_process(clicon_handle h, return retval; } +static int +restconf_terminate(clicon_handle h) +{ + yang_spec *yspec; + + clicon_rpc_close_session(h); + if ((yspec = clicon_dbspec_yang(h)) != NULL) + yspec_free(yspec); + clicon_handle_exit(h); + return 0; +} + /*! Usage help routine * @param[in] argv0 command line * @param[in] h Clicon handle @@ -592,5 +618,6 @@ main(int argc, retval = 0; done: restconf_plugin_unload(h); + restconf_terminate(h); return retval; } diff --git a/apps/xmldb/Makefile.in b/apps/xmldb/Makefile.in deleted file mode 100644 index 581147a1..00000000 --- a/apps/xmldb/Makefile.in +++ /dev/null @@ -1,108 +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 ***** -# -VPATH = @srcdir@ -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ -CC = @CC@ -CFLAGS = @CFLAGS@ -LDFLAGS = @LDFLAGS@ - -prefix = @prefix@ -exec_prefix = @exec_prefix@ -bindir = @bindir@ -libexecdir = @libexecdir@ -localstatedir = @localstatedir@ -sysconfdir = @sysconfdir@ - -SH_SUFFIX = @SH_SUFFIX@ -CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ -CLIXON_MINOR = @CLIXON_VERSION_MINOR@ - -# Use this clixon lib for linking -CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR) - -# For dependency -LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) - -LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB) - -CPPFLAGS = @CPPFLAGS@ - -INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ - -SRC = - -OBJS = $(SRC:.c=.o) - -APPSRC = xmldb_main.c -APPOBJ = $(APPSRC:.c=.o) -APPL = clixon_xmldb - -all: $(APPL) - -clean: - rm -f $(OBJS) *.core $(APPL) $(APPOBJ) - -distclean: clean - rm -f Makefile *~ .depend - -# Put demon in bin -# Put other executables in libexec/ -# Also create a libexec/ directory for writeable/temporary files. -# Put config file in etc/ -install: $(APPL) - install -d $(DESTDIR)$(bindir) - install $(APPL) $(DESTDIR)$(bindir) - -install-include: - -uninstall: - rm -f $(bindir)/$(APPL) - -.SUFFIXES: -.SUFFIXES: .c .o - -.c.o: - $(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $< - -$(APPL) : $(APPOBJ) $(OBJS) $(LIBDEPS) - $(CC) $(LDFLAGS) $(APPOBJ) $(OBJS) $(LIBS) -o $@ - -TAGS: - find . -name '*.[chyl]' -print | etags - - -depend: - $(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) $(APPSRC) > .depend - -#include .depend - diff --git a/apps/xmldb/xmldb_main.c b/apps/xmldb/xmldb_main.c deleted file mode 100644 index 78a39b55..00000000 --- a/apps/xmldb/xmldb_main.c +++ /dev/null @@ -1,940 +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 ***** - - */ - -#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 -#include -#include -#include - -/* cligen */ -#include - -/* clicon */ -#include - -/* Command line options to be passed to getopt(3) */ -#define XMLDB_OPTS "hDSf:a:p:y:m:r:" - -#define DEFAULT_PORT 7878 -#define DEFAULT_ADDR "127.0.0.1" - -static int -xmldb_send_error(int s, - char *reason) -{ - int retval = -1; - cbuf *cb = NULL; /* Outgoing return message */ - - clicon_log(LOG_NOTICE, "%s", reason); - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - cprintf(cb, "%s", reason); - if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Process incoming xmldb get message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "get" - * example - * - * - * - * / - * # If set send back list of xpath hits not single tree - * - * - * @note restrictions on using only databases called candidate and running - * - */ -static int -xmldb_from_get(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - cxobj *x; - cbuf *cb = NULL; /* Outgoing return message */ - char *db; - char *xpath = "/"; - cxobj *xt = NULL; /* Top of return tree */ - cxobj *xc; /* Child */ - cxobj **xvec = NULL; - size_t xlen = 0; - int i; - - if (xpath_first(xr, "source/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "source/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Get request: Expected candidate or running as source"); - goto drop; - } - if ((x = xpath_first(xr, "xpath")) != NULL) - xpath = xml_body(x); - /* Actual get call */ - if (xmldb_get(h, db, xpath, &xt, &xvec, &xlen) < 0) - goto done; - xml_name_set(xt, "config"); - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - if (xvec){ - for (i=0; i - * - * - * merge|none|replace - * - * ... - * - * - * - * @note restrictions on using only databases called candidate and running - * @note key,val see xmldb_put_xkey - */ -static int -xmldb_from_put(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - cxobj *x; - char *db; - cxobj *xc; /* Child */ - char *opstr; - enum operation_type op = OP_REPLACE; - - if (xpath_first(xr, "target/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Put request: Expected candidate or running as source"); - goto drop; - } - if ((x = xpath_first(xr, "default-operation")) != NULL) - if ((opstr = xml_body(x)) != NULL){ - if (strcmp(opstr, "replace") == 0) - op = OP_REPLACE; - else - if (strcmp(opstr, "merge") == 0) - op = OP_MERGE; - else - if (strcmp(opstr, "none") == 0) - op = OP_NONE; - else{ - xmldb_send_error(s, "Put request: unrecognized default-operation"); - goto drop; - } - } - if ((xc = xpath_first(xr, "config")) != NULL){ - /* Actual put call */ - if (xmldb_put(h, db, xc, op) < 0) - goto done; - } - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - drop: - retval = 0; - done: - return retval; -} - -static int -xmldb_from_put_xkey(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - cxobj *x; - char *db; - char *xkey; - char *val; - char *opstr; - enum operation_type op = OP_REPLACE; - - if (xpath_first(xr, "target/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Put request: Expected candidate or running as source"); - goto drop; - } - if ((x = xpath_first(xr, "default-operation")) != NULL) - if ((opstr = xml_body(x)) != NULL){ - if (strcmp(opstr, "replace") == 0) - op = OP_REPLACE; - else - if (strcmp(opstr, "merge") == 0) - op = OP_MERGE; - else - if (strcmp(opstr, "none") == 0) - op = OP_NONE; - else{ - xmldb_send_error(s, "Put xkey request: unrecognized default-operation"); - goto drop; - } - } - if ((x = xpath_first(xr, "xkey")) == NULL){ - xmldb_send_error(s, "Put xkey request: no xkey"); - goto drop; - } - xkey = xml_body(x); - if ((x = xpath_first(xr, "value")) == NULL){ - xmldb_send_error(s, "Put xkey request: no value"); - goto drop; - } - val = xml_body(x); - if (xmldb_put_xkey(h, db, xkey, val, op) < 0) - goto done; - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - drop: - retval = 0; - done: - return retval; -} - -/*! Process incoming copy message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "exists" - * example - * - * - * - * - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_copy(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *source; - char *target; - - if (xpath_first(xr, "source/candidate") != NULL) - source = "candidate"; - else if (xpath_first(xr, "source/running") != NULL) - source = "running"; - else { - xmldb_send_error(s, "Copy request: Expected candidate or running as source"); - goto drop; - } - if (xpath_first(xr, "target/candidate") != NULL) - target = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - target = "running"; - else { - xmldb_send_error(s, "Copy request: Expected candidate or running as target"); - goto drop; - } - if (xmldb_copy(h, source, target) < 0) - goto done; - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - drop: - retval = 0; - done: - return retval; -} - -/*! Process incoming lock message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "exists" - * example - * - * - * - * 43 - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_lock(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *target; - char *idstr = NULL; - cxobj *x; - - if (xpath_first(xr, "target/candidate") != NULL) - target = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - target = "running"; - else { - xmldb_send_error(s, "Lock request: Expected candidate or running as target"); - goto drop; - } - if ((x = xpath_first(xr, "id")) != NULL){ - xmldb_send_error(s, "Lock request: mandatory id not found"); - goto drop; - } - idstr = xml_body(x); - if (xmldb_lock(h, target, atoi(idstr)) < 0) - goto done; - drop: - retval = 0; - done: - return retval; -} - - -/*! Process incoming unlock message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "exists" - * example - * - * - * - * 43 - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_unlock(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *target; - char *idstr = NULL; - cxobj *x; - - if (xpath_first(xr, "target/candidate") != NULL) - target = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - target = "running"; - else { - xmldb_send_error(s, "Unlock request: Expected candidate or running as target"); - goto drop; - } - if ((x = xpath_first(xr, "id")) != NULL){ - xmldb_send_error(s, "Unlock request: mandatory id not found"); - goto drop; - } - idstr = xml_body(x); - if (xmldb_unlock(h, target, atoi(idstr)) < 0) - goto done; - drop: - retval = 0; - done: - return retval; - -} - -/*! Process incoming islocked message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "exists" - * example - * - * - * - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_islocked(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *db; - int ret; - cbuf *cb = NULL; - - if (xpath_first(xr, "target/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Islocked request: Expected candidate or running as source"); - goto drop; - } - if ((ret = xmldb_islocked(h, db)) < 0) - goto done; - if (ret > 0){ - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_UNIX, errno, "cbuf_new"); - goto done; - } - cprintf(cb, "%u", ret); - if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - } - else{ - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - } - drop: - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - - -/*! Process incoming exists? message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "exists" - * example - * - * - * - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_exists(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *db; - - if (xpath_first(xr, "target/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Exists request: Expected candidate or running as source"); - goto drop; - } - /* XXX error and non-exist treated same */ - if (xmldb_exists(h, db) == 1){ - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - } - else{ - xmldb_send_error(s, "DB does not exist"); - goto drop; - } - drop: - retval = 0; - done: - return retval; -} - -/*! Process incoming xmldb delete message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "delete" - * example - * - * - * - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_delete(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *db; - - if (xpath_first(xr, "target/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Delete request: Expected candidate or running as source"); - goto drop; - } - if (xmldb_delete(h, db) < 0) - ; /* ignore */ - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - drop: - retval = 0; - done: - return retval; -} - - -/*! Process incoming xmldb init message - * @param[in] s Stream socket - * @param[in] cb Packet buffer - * @param[in] xr XML request node with root in "init" - * example - * - * - * - * - * - * @note restrictions on using only databases called candidate and running - */ -static int -xmldb_from_init(clicon_handle h, - int s, - cxobj *xr) -{ - int retval = -1; - char *db; - - if (xpath_first(xr, "target/candidate") != NULL) - db = "candidate"; - else if (xpath_first(xr, "target/running") != NULL) - db = "running"; - else { - xmldb_send_error(s, "Init request: Expected candidate or running as source"); - goto drop; - } - if (xmldb_init(h, db) < 0) - goto done; - if (write(s, "", strlen("")+1) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - drop: - retval = 0; - done: - return retval; -} - -/*! Process incoming xmldb packet - * @param[in] h Clicon handle - * @param[in] s Stream socket - * @param[in] cbin Incoming packet buffer - * example: ]]>]]> - */ -static int -xmldb_from_client(clicon_handle h, - int s, - cbuf *cbin) -{ - int retval = -1; - char *str; - cxobj *xrq = NULL; /* Request (in) */ - cxobj *xr; - cxobj *x; - cxobj *xt = NULL; - - clicon_debug(1, "xmldb message: \"%s\"", cbuf_get(cbin)); - str = cbuf_get(cbin); - str[strlen(str)-strlen("]]>]]>")] = '\0'; - /* Parse incoming XML message */ - if (clicon_xml_parse_string(&str, &xrq) < 0) - goto done; - if (debug) - xml_print(stderr, xrq); - if ((xr = xpath_first(xrq, "rpc")) != NULL){ - if ((x = xpath_first(xr, "get")) != NULL){ - if (xmldb_from_get(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "put")) != NULL){ - if (xmldb_from_put(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "put-xkey")) != NULL){ - if (xmldb_from_put_xkey(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "copy")) != NULL){ - if (xmldb_from_copy(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "lock")) != NULL){ - if (xmldb_from_lock(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "unlock")) != NULL){ - if (xmldb_from_unlock(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "islocked")) != NULL){ - if (xmldb_from_islocked(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "exists")) != NULL){ - if (xmldb_from_exists(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "init")) != NULL){ - if (xmldb_from_init(h, s, x) < 0) - goto done; - } - else if ((x = xpath_first(xr, "delete")) != NULL){ - if (xmldb_from_delete(h, s, x) < 0) - goto done; - } - } - else{ - xmldb_send_error(s, "Expected rpc as top xml msg"); - goto drop; - } - drop: - retval = 0; - done: - if (xrq) - xml_free(xrq); - if (xt) - xml_free(xt); - return retval; -} - -/*! stolen from netconf_lib.c */ -static int -detect_endtag(char *tag, char ch, int *state) -{ - int retval = 0; - - if (tag[*state] == ch){ - (*state)++; - if (*state == strlen(tag)){ - *state = 0; - retval = 1; - } - } - else - *state = 0; - return retval; -} - -/*! config_accept_client - */ -int -config_accept_client(int fd, - void *arg) -{ - int retval = -1; - clicon_handle h = (clicon_handle)arg; - int s = -1; - struct sockaddr_un from; - socklen_t slen; - ssize_t len; - unsigned char buf[BUFSIZ]; - int i; - cbuf *cb = NULL; - int xml_state = 0; - - clicon_debug(1, "Accepting client request"); - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "cbuf_new"); - return retval; - } - len = sizeof(from); - if ((s = accept(fd, (struct sockaddr*)&from, &slen)) < 0){ - clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__); - goto done; - } - if ((len = read(s, buf, sizeof(buf))) < 0){ - clicon_err(OE_UNIX, errno, "read"); - goto done; - } - for (i=0; i]]>", - buf[i], - &xml_state)) { - if (xmldb_from_client(h, s, cb) < 0){ - goto done; - } - cbuf_reset(cb); - } - } - retval = 0; - done: - if (cb) - cbuf_free(cb); - if (s != -1) - close(s); - return retval; -} - -/*! Create tcp server socket and register callback - */ -static int -server_socket(clicon_handle h, - char *ipv4addr, - uint16_t port) -{ - int retval = -1; - int s; - struct sockaddr_in addr; - - /* Open control socket */ - if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - clicon_err(OE_UNIX, errno, "socket"); - goto done; - } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if (inet_pton(addr.sin_family, ipv4addr, &addr.sin_addr) != 1){ - clicon_err(OE_UNIX, errno, "inet_pton: %s (Expected IPv4 address. Check settings of CLICON_SOCK_FAMILY and CLICON_SOCK)", ipv4addr); - goto done; - } - if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0){ - clicon_err(OE_UNIX, errno, "%s: bind", __FUNCTION__); - goto done; - } - clicon_debug(1, "Listen on server socket at %s:%hu", ipv4addr, port); - if (listen(s, 5) < 0){ - clicon_err(OE_UNIX, errno, "%s: listen", __FUNCTION__); - goto done; - } - if (event_reg_fd(s, config_accept_client, h, "server socket") < 0) { - close(s); - goto done; - } - retval = 0; - done: - return retval; -} - -/* - * usage - */ -static void -usage(char *argv0) -{ - fprintf(stderr, "usage:%s\n" - "where options are\n" - "\t-h\t\tHelp\n" - "\t-D\t\tDebug\n" - "\t-S\t\tLog on syslog\n" - "\t-f \tCLICON config file\n" - "\t-a \tIP address\n" - "\t-p \tTCP port\n" - "\t-y \tYang dir\n" - "\t-m \tYang main module name\n" - "\t-r \tYang module revision\n", - argv0 - ); - exit(0); -} - -int -main(int argc, char **argv) -{ - char c; - int use_syslog; - clicon_handle h; - uint16_t port; - char *addr = DEFAULT_ADDR; - - /* In the startup, logs to stderr & debug flag set later */ - clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR); - - /* Defaults */ - use_syslog = 0; - port = DEFAULT_PORT; - - if ((h = clicon_handle_init()) == NULL) - goto done; - /* getopt in two steps, first find config-file before over-riding options. */ - while ((c = getopt(argc, argv, XMLDB_OPTS)) != -1) - switch (c) { - case '?' : - case 'h' : /* help */ - usage(argv[0]); - break; - case 'D' : /* debug */ - debug = 1; - break; - case 'f': /* config file */ - if (!strlen(optarg)) - usage(argv[0]); - clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); - break; - case 'S': /* Log on syslog */ - use_syslog = 1; - break; - } - /* - * Logs, error and debug to stderr or syslog, set debug level - */ - clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, - use_syslog?CLICON_LOG_SYSLOG:CLICON_LOG_STDERR); - clicon_debug_init(debug, NULL); - - /* Find and read configfile */ - if (clicon_options_main(h) < 0){ - usage(argv[0]); - goto done; - } - - /* Now rest of options */ - optind = 1; - while ((c = getopt(argc, argv, XMLDB_OPTS)) != -1) - switch (c) { - case 'D': /* Processed earlier, ignore now. */ - case 'S': - case 'f': - break; - case 'a': /* address */ - clicon_option_str_set(h, "CLICON_XMLDB_ADDR", optarg); - break; - case 'p': /* port */ - clicon_option_str_set(h, "CLICON_XMLDB_PORT", optarg); - break; - case 'y': /* yang dir */ - clicon_option_str_set(h, "CLICON_YANG_DIR", optarg); - break; - case 'm': /* yang module */ - clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg); - break; - case 'r': /* yang revision */ - clicon_option_str_set(h, "CLICON_YANG_MODULE_REVISION", optarg); - break; - default: - usage(argv[0]); - break; - } - argc -= optind; - argv += optind; - clicon_option_str_set(h, "CLICON_XMLDB_RPC", "0"); - if (clicon_yang_dir(h) == NULL){ - clicon_err(OE_UNIX, errno, "yang dir not set"); - goto done; - } - if (clicon_yang_module_main(h) == NULL){ - clicon_err(OE_UNIX, errno, "yang main module not set"); - goto done; - } - if (yang_spec_main(h, NULL, 0) < 0) - goto done; - addr = clicon_xmldb_addr(h); - port = clicon_xmldb_port(h); - if (server_socket(h, addr, port) < 0) - goto done; - - if (event_loop() < 0) - goto done; - - done: - return 0; -} diff --git a/clixon.conf.cpp.cpp b/clixon.conf.cpp.cpp index dc801198..1dacc35a 100644 --- a/clixon.conf.cpp.cpp +++ b/clixon.conf.cpp.cpp @@ -121,11 +121,6 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile # Directory where "running", "candidate" and "startup" are placed CLICON_XMLDB_DIR localstatedir/APPNAME -# Set if xmldb runs in a separate process (clixon_xmldb). -# If set, also set xmldb_addr and xmldb_port below -# CLICON_XMLDB_RPC 0 - -# xmldb inet address (if CLICON_XMLDB_RPC) # CLICON_XMLDB_ADDR # xmldb tcp port (if CLICON_XMLDB_RPC) diff --git a/configure.ac b/configure.ac index 4589e06f..9b394fa3 100644 --- a/configure.ac +++ b/configure.ac @@ -189,7 +189,6 @@ AC_OUTPUT(Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile - apps/xmldb/Makefile include/Makefile etc/Makefile etc/clixonrc diff --git a/doc/Doxyfile.graphs b/doc/Doxyfile.graphs index 381b87d2..a264d0fc 100644 --- a/doc/Doxyfile.graphs +++ b/doc/Doxyfile.graphs @@ -743,7 +743,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl +INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/backend/ ../apps/restconf/ ../apps/netconf ../apps/dbctrl # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/doc/FAQ.txt b/doc/FAQ.txt index 6ce91d6a..79c38315 100644 --- a/doc/FAQ.txt +++ b/doc/FAQ.txt @@ -91,6 +91,7 @@ Q: How do you change the example? - routing_backend.c - Commit and validate functions. - routing_netconf.c - Modify semantics of netconf commands. + Q: How do you check what is in a database? ------------------------------------------ Use clixon_dbctrl. The name of the running or candidate databases are found in the @@ -169,9 +170,31 @@ The validation or commit will then be aborted. Q: How do you use netconf? -------------------------- -As an alternative to cli configuration, you can use netconf -directly. Easiest is to just pipe netconf commands to the -clixon_netconf application. +As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application. Example: echo "]]>]]>" | clixon_netconf -f /usr/local/etc/routing.conf +However, more useful is to run clixon_netconf as an SSH +subsystem. Register the subsystem in /etc/sshd_config: + + Subsystem netconf /usr/local/bin/clixon_netconf + +and then invoke it from a client using + ssh -s netconf + +Q: How do you use notifications? +-------------------------------- +The example has a prebuilt notification stream called "ROUTING" that triggers every 10s. +You enable the notification either via the cli or via netconf: +cli> notify +cli> Routing notification +Routing notification +... + +> clixon_netconf -qf /usr/local/etc/routing.conf +ROUTING]]>]]> +]]>]]> +Routing notification]]>]]> +Routing notification]]>]]> +... + diff --git a/example/README b/example/README index f6a0b221..e449d864 100644 --- a/example/README +++ b/example/README @@ -14,7 +14,6 @@ clixon_netconf -f /usr/local/etc/routing.conf 1. Setting data example using netconf ------------------------------------- - @@ -45,7 +44,38 @@ clixon_netconf -f /usr/local/etc/routing.conf ]]>]]> -3. Run as docker container +3. Creating notification +------------------------ +The example has an example notification triggering every 10s. To start a notification +stream in the session, create a subscription: +ROUTING]]>]]> +]]>]]> +Routing notification]]>]]> +Routing notification]]>]]> +... + +This can also be triggered via the CLI: +cli> notify +cli> Routing notification +Routing notification +... + +4. Downcall +----------- +Clixon has an extension mechanism which can be used to make extended internal +netconf messages to the backend configuration engine. You may need this to +make some special operation that is not covered by standard +netconf functions. The example has a simple "echo" downcall +mechanism that simply echoes what is sent down and is included for +reference. A more realistic downcall would perform some action, such as +reading some status. + +Example: +cli> downcall "This is a string" +This is a string +cli>p + +5. Run as docker container -------------------------- cd docker # look in README diff --git a/example/ietf-ip@2014-06-16.yang b/example/ietf-ip@2014-06-16.yang index f834f933..8e39326d 100644 --- a/example/ietf-ip@2014-06-16.yang +++ b/example/ietf-ip@2014-06-16.yang @@ -698,4 +698,4 @@ } } } - } +} diff --git a/example/routing.conf.local b/example/routing.conf.local index cebc9827..8d705aba 100644 --- a/example/routing.conf.local +++ b/example/routing.conf.local @@ -19,18 +19,11 @@ CLICON_CLI_GENMODEL_COMPLETION 1 # CLICON_CLI_GENMODEL_TYPE VARS CLICON_CLI_GENMODEL_TYPE VARS -# Set if xmldb runs in a separate process (clixon_xmldb). -# Also set addr and port below -CLICON_XMLDB_RPC 0 - -# xmldb inet address (if CLICON_XMLDB_RPC) -CLICON_XMLDB_ADDR 127.0.0.1 - -# xmldb tcp port (if CLICON_XMLDB_RPC) -CLICON_XMLDB_PORT 7878 - # Set if you want to use old obsolete cligen callback variable syntax # Migration: Set to 0 and change all user-defined cli callbacks in your cli spec files # E.g cmd, callback("single arg"); -> cmd, callback("two" "args"); # And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0 + +# Enabled uses "startup" configuration on boot +CLICON_USE_STARTUP_CONFIG 0 \ No newline at end of file diff --git a/example/routing_backend.c b/example/routing_backend.c index a957e7a5..59bac481 100644 --- a/example/routing_backend.c +++ b/example/routing_backend.c @@ -42,7 +42,7 @@ #include #include #include - +#include /* clicon */ #include @@ -53,6 +53,9 @@ /* These include signatures for plugin and transaction callbacks. */ #include +/* forward */ +static int notification_timer_setup(clicon_handle h); + /*! This is called on validate (and commit). Check validity of candidate */ int @@ -83,7 +86,47 @@ transaction_commit(clicon_handle h, return 0; } +/*! Routing example notifcation timer handler. Here is where the periodic action is + */ +static int +notification_timer(int fd, + void *arg) +{ + int retval = -1; + clicon_handle h = (clicon_handle)arg; + if (backend_notify(h, "ROUTING", 0, "Routing notification") < 0) + goto done; + if (notification_timer_setup(h) < 0) + goto done; + retval = 0; + done: + return retval; +} + +/*! Set up routing notifcation timer + */ +static int +notification_timer_setup(clicon_handle h) +{ + struct timeval t, t1; + + gettimeofday(&t, NULL); + t1.tv_sec = 10; t1.tv_usec = 0; + timeradd(&t, &t1, &t); + return event_reg_timeout(t, notification_timer, h, "notification timer"); +} + +static int +routing_downcall(clicon_handle h, + cxobj *xe, /* Request: */ + struct client_entry *ce, /* Client session */ + cbuf *cbret, /* Reply eg ... */ + void *arg) /* Argument given at register */ +{ + cprintf(cbret, "%s", xml_body(xe)); + return 0; +} /* * Plugin initialization */ @@ -92,8 +135,15 @@ plugin_init(clicon_handle h) { int retval = -1; + if (notification_timer_setup(h) < 0) + goto done; + if (backend_netconf_register_callback(h, routing_downcall, + NULL, + "myrouting"/* Xml tag when callback is made */ + ) < 0) + goto done; retval = 0; - // done: + done: return retval; } diff --git a/example/routing_cli.c b/example/routing_cli.c index eb3a3a9a..f2b1edd8 100644 --- a/example/routing_cli.c +++ b/example/routing_cli.c @@ -71,7 +71,7 @@ int mycallback(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; - cxobj *xt = NULL; + cxobj *xret = NULL; cg_var *myvar; /* Access cligen callback variables */ @@ -80,14 +80,51 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv) cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */ /* Show eth0 interfaces config using XPATH */ - if (xmldb_get(h, "candidate", - "/interfaces/interface[name=eth0]", - &xt, NULL, NULL) < 0) + if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]", + &xret) < 0) goto done; - xml_print(stdout, xt); + xml_print(stdout, xret); retval = 0; done: - if (xt) - xml_free(xt); + if (xret) + xml_free(xret); + return retval; +} + +/*! get argument and send as string to backend as RPC (which returns the string) + */ +int +downcall(clicon_handle h, + cvec *vars, + cvec *argv) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + char *str=""; + cg_var *cv; + cxobj *xret=NULL; + cxobj *xerr; + cxobj *xdata; + + if (cvec_len(vars)==2){ + if ((cv = cvec_i(vars, 1)) != NULL) + str = cv_string_get(cv); + } + if ((msg = clicon_msg_netconf_encode("%s", str)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + if ((xdata = xpath_first(xret, "//ok")) != NULL) + cli_output(stdout, "%s\n", xml_body(xdata)); + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); return retval; } diff --git a/example/routing_cli.cli b/example/routing_cli.cli index a96bd4e6..fe608b25 100644 --- a/example/routing_cli.cli +++ b/example/routing_cli.cli @@ -17,13 +17,14 @@ delete("Delete a configuration item") all("Delete whole candidate configuration 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_cliv(); + level("Set debug level: 1..n") ("Set debug level (0..n)"), cli_debug_backendv(); } discard("Discard edits (rollback 0)"), discard_changesv(); +compare("Compare running and candidate"), compare_dbsv((int32)1); show("Show a particular state of the system"){ - xpath("Show configuration") ("XPATH expression"), show_confv_xpath("candidate","/"); + xpath("Show configuration") ("XPATH expression"), show_confv_xpath("candidate"); compare("Compare candidate and running databases"), compare_dbsv((int32)0);{ xml("Show comparison in xml"), compare_dbsv((int32)0); text("Show comparison in text"), compare_dbsv((int32)1); @@ -43,3 +44,8 @@ load("Load configuration from XML file") ("Filename (local file 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"); +lock,cli_lock("candidate"); +unlock,cli_unlock("candidate"); \ No newline at end of file diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index 0d339514..a83b3aa7 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -109,9 +109,6 @@ int clicon_cli_varonly_set(clicon_handle h, int val); int clicon_cli_genmodel_completion(clicon_handle h); char *clicon_xmldb_dir(clicon_handle h); -int clicon_xmldb_rpc(clicon_handle h); -char *clicon_xmldb_addr(clicon_handle h); -uint16_t clicon_xmldb_port(clicon_handle h); char *clicon_quiet_mode(clicon_handle h); enum genmodel_type clicon_cli_genmodel_type(clicon_handle h); diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h index 485e0a12..b83bdf45 100644 --- a/lib/clixon/clixon_proto.h +++ b/lib/clixon/clixon_proto.h @@ -48,16 +48,10 @@ enum format_enum{ /* See also map_type2str in clicon_proto.c */ enum clicon_msg_type{ - CLICON_MSG_COMMIT = 1, /* Commit a configuration db->running_db - current state, set running_db. Body is: - 1. uint32: (1)snapshot while doing commit, (0) dont - 2. uint32: (1)save to startup-config, (0) dont - 3. string: name of 'from' database (eg "candidate") - 4. string: name of 'to' database (eg "running") + CLICON_MSG_NETCONF = 1, /* Generic netconf message (lock/unlock/..) can all + msgs go to this? + 1. string: netconf message */ - CLICON_MSG_VALIDATE, /* Validate settings in a database. Body is: - 1. string: name of database (eg "candidate") - */ CLICON_MSG_CHANGE, /* Change a (single) database entry: 1. uint32: operation: OP_MERGE/OP_REPLACE/OP_REMOVE 2. uint32: length of value string @@ -65,55 +59,6 @@ enum clicon_msg_type{ 4. string: key 5. string: value (can be NULL) */ - CLICON_MSG_XMLPUT, /* Send database entries as XML to backend daemon - 1. uint32: operation: LV_SET/LV_DELETE - 2. string: name of database to change (eg current) - 3. string: restconf api path - 4. string: XML data - */ - - CLICON_MSG_SAVE, /* Save config state from db to a file in backend. Body is: - 1. uint32: make snapshot (1), dont(0) - 2. string: name of database to save from (eg running) - 3. string: filename to write. If snapshot=1, then this - is empty. - */ - CLICON_MSG_LOAD, /* Load config state from file in backend to db via XML. Body is: - 1. uint32: whether to replace/initdb before load (1) or - merge (0). - 2. string: name of database to load into (eg running) - 3. string: filename to load from - - */ - CLICON_MSG_COPY, /* Copy from file to file in backend. Body is: - 1. string: filename to copy from - 2. string: filename to copy to - */ - CLICON_MSG_KILL, /* Kill (other) session: - 1. session-id - */ - CLICON_MSG_DEBUG, /* Debug - 1. session-id - */ - CLICON_MSG_CALL , /* Backend plugin call request. Body is: - 1. struct clicon_msg_call_req * - */ - CLICON_MSG_SUBSCRIPTION, /* Create a new notification subscription. - Body is: - 1. int: status off/on - 1. int: format (enum format_enum) - 2. string: name of notify stream - 3. string: filter, if format=xml: xpath, if text: fnmatch */ - CLICON_MSG_OK, /* server->client reply */ - CLICON_MSG_NOTIFY, /* Notification. Body is: - 1. int: loglevel - 2. event: log message. */ - CLICON_MSG_ERR /* server->client reply. - Body is: - 1. uint32: man error category - 2. uint32: sub-error - 3. string: reason - */ }; /* Protocol message header */ @@ -123,55 +68,37 @@ struct clicon_msg { char op_body[0]; /* rest of message, actual data */ }; -/* Generic clicon message. Either generic/internal message - or application-specific backend plugin downcall request */ -struct clicon_msg_call_req { - uint16_t cr_len; /* Length of total request */ - uint16_t cr_op; /* Generic application-defined operation */ - char *cr_plugin; /* Name of backend plugin, NULL -> internal - functions */ - char *cr_func; /* Function name in plugin (or internal) */ - uint16_t cr_arglen; /* App specific argument length */ - char *cr_arg; /* App specific argument */ - char cr_data[0]; /* Allocated data containng the above */ -}; - /* * Prototypes */ -#ifndef LIBCLICON_API int clicon_connect_unix(char *sockpath); -int clicon_rpc_connect_unix(struct clicon_msg *msg, - char *sockpath, - char **data, - uint16_t *datalen, - int *sock0, - const char *label); +int clicon_rpc_connect_unix(struct clicon_msg *msg, + char *sockpath, + char **ret, + int *sock0); -int clicon_rpc_connect_inet(struct clicon_msg *msg, - char *dst, - uint16_t port, - char **data, - uint16_t *datalen, - int *sock0, - const char *label); +int clicon_rpc_connect_inet(struct clicon_msg *msg, + char *dst, + uint16_t port, + char **ret, + int *sock0); -int clicon_rpc(int s, struct clicon_msg *msg, char **data, uint16_t *datalen, - const char *label); +int clicon_rpc(int s, struct clicon_msg *msg, char **xret); -#endif int clicon_msg_send(int s, struct clicon_msg *msg); -int clicon_msg_rcv(int s, struct clicon_msg **msg, - int *eof, const char *label); +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); +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, ...); + + #endif /* _CLIXON_PROTO_H_ */ diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index 1b6d0e09..985fdcce 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -40,27 +40,28 @@ #ifndef _CLIXON_PROTO_CLIENT_H_ #define _CLIXON_PROTO_CLIENT_H_ -int clicon_rpc_commit(clicon_handle h, char *from, char *to); +int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0, + int *sock0); +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); +int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op, + char *api_path, char *xml); +int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2); +int clicon_rpc_delete_config(clicon_handle h, char *db); +int clicon_rpc_lock(clicon_handle h, char *db); +int clicon_rpc_unlock(clicon_handle h, char *db); +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_create_subscription(clicon_handle h, char *stream, char *filter, + int *s); + int clicon_rpc_change(clicon_handle h, char *db, enum operation_type op, char *key, char *val); -int clicon_rpc_xmlput(clicon_handle h, char *db, enum operation_type op, - char *api_path, char *xml); -int clicon_rpc_dbitems(clicon_handle h, char *db, char *rx, - char *attr, char *val, - cvec ***cvv, size_t *cvvlen); -int clicon_rpc_save(clicon_handle h, char *dbname, int snapshot, char *filename); -int clicon_rpc_load(clicon_handle h, int replace, char *db, char *filename); -int clicon_rpc_copy(clicon_handle h, char *db1, char *db2); -int clicon_rpc_kill(clicon_handle h, int session_id); int clicon_rpc_debug(clicon_handle h, int level); -int clicon_rpc_call(clicon_handle h, uint16_t op, char *plugin, char *func, - void *param, uint16_t paramlen, - char **ret, uint16_t *retlen, - const void *label); -int clicon_rpc_subscription(clicon_handle h, int status, char *stream, - enum format_enum format, char *filter, int *s); - #endif /* _CLIXON_PROTO_CLIENT_H_ */ diff --git a/lib/clixon/clixon_proto_encode.h b/lib/clixon/clixon_proto_encode.h index 5d4f9e22..17eed052 100644 --- a/lib/clixon/clixon_proto_encode.h +++ b/lib/clixon/clixon_proto_encode.h @@ -42,27 +42,14 @@ /* * Prototypes */ -struct clicon_msg * -clicon_msg_commit_encode(char *dbsrc, char *dbdst, - const char *label); +struct clicon_msg *clicon_msg_netconf_encode(char *format, ...); +struct clicon_msg *clicon_msg_netconf_encode_xml(cxobj *xml); -int -clicon_msg_commit_decode(struct clicon_msg *msg, - char **dbsrc, char **dbdst, - const char *label); - -struct clicon_msg * -clicon_msg_validate_encode(char *db, - const char *label); - -int -clicon_msg_validate_decode(struct clicon_msg *msg, char **db, - const char *label); +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, - const char *label); + char *lvec, uint32_t lvec_len); int clicon_msg_change_decode(struct clicon_msg *msg, @@ -70,25 +57,9 @@ clicon_msg_change_decode(struct clicon_msg *msg, char **lvec, uint32_t *lvec_len, const char *label); -struct clicon_msg * -clicon_msg_xmlput_encode(char *db, - uint32_t op, - char *api_path, - char *xml, - const char *label); - -int -clicon_msg_xmlput_decode(struct clicon_msg *msg, - char **db, - uint32_t *op, - char **api_path, - char **xml, - const char *label); - struct clicon_msg * clicon_msg_dbitems_get_reply_encode(cvec **cvecv, - int cveclen, - const char *label); + int cveclen); int clicon_msg_dbitems_get_reply_decode(char *data, uint16_t datalen, @@ -96,82 +67,4 @@ clicon_msg_dbitems_get_reply_decode(char *data, size_t *cveclen, const char *label); -struct clicon_msg * -clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename, - const char *label); - -int -clicon_msg_save_decode(struct clicon_msg *msg, - char **db, uint32_t *snapshot, char **filename, - const char *label); - -struct clicon_msg * -clicon_msg_load_encode(int replace, char *db, char *filename, - const char *label); - -int -clicon_msg_load_decode(struct clicon_msg *msg, - int *replace, char **db, char **filename, - const char *label); - -struct clicon_msg * -clicon_msg_copy_encode(char *db_src, char *db_dst, - const char *label); - -int -clicon_msg_copy_decode(struct clicon_msg *msg, - char **db_src, char **db_dst, - const char *label); - -struct clicon_msg * -clicon_msg_kill_encode(uint32_t session_id, const char *label); - -int -clicon_msg_kill_decode(struct clicon_msg *msg, uint32_t *session_id, - const char *label); - -struct clicon_msg * -clicon_msg_debug_encode(uint32_t level, const char *label); - -int -clicon_msg_debug_decode(struct clicon_msg *msg, uint32_t *level, - const char *label); - -struct clicon_msg * -clicon_msg_call_encode(uint16_t op, char *plugin, char *func, - uint16_t arglen, void *arg, - const char *label); - -int -clicon_msg_call_decode(struct clicon_msg *msg, - struct clicon_msg_call_req **req, - const char *label); - -struct clicon_msg * -clicon_msg_subscription_encode(int status, - char *stream, - enum format_enum format, - char *filter, - const char *label); - -int clicon_msg_subscription_decode(struct clicon_msg *msg, - int *status, - char **stream, - enum format_enum *format, - char **filter, - const char *label); - -struct clicon_msg * -clicon_msg_notify_encode(int level, char *event, const char *label); - -int -clicon_msg_notify_decode(struct clicon_msg *msg, int *level, - char **event, const char *label); - -struct clicon_msg *clicon_msg_err_encode(uint32_t err, uint32_t suberr, - char *reason, const char *label); - -int clicon_msg_err_decode(struct clicon_msg *msg, uint32_t *err, uint32_t *suberr, - char **reason, const char *label); - #endif /* _CLIXON_PROTO_ENCODE_H_ */ diff --git a/lib/clixon/clixon_xml.h b/lib/clixon/clixon_xml.h index 98b6b097..91da69e5 100644 --- a/lib/clixon/clixon_xml.h +++ b/lib/clixon/clixon_xml.h @@ -40,7 +40,7 @@ * Types */ /* Netconf operation type */ -enum operation_type{ /* edit-config */ +enum operation_type{ /* edit-configo */ OP_MERGE, /* merge config-data */ OP_REPLACE,/* replace or create config-data */ OP_CREATE, /* create config data, error if exist */ @@ -98,6 +98,7 @@ cxobj *xml_child_i(cxobj *xn, int i); cxobj *xml_child_i_set(cxobj *xt, int i, cxobj *xc); cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type); +cxobj **xml_childvec_get(cxobj *x); int xml_childvec_set(cxobj *x, int len); cxobj *xml_new(char *name, cxobj *xn_parent); cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec); @@ -124,6 +125,7 @@ int clicon_xml_parse_file(int fd, cxobj **xml_top, char *endtag); /* XXX obsolete */ #define clicon_xml_parse_string(str, x) clicon_xml_parse_str((*str), x) int clicon_xml_parse_str(char *str, cxobj **xml_top); +int clicon_xml_parse(cxobj **cxtop, char *format, ...); int xml_copy(cxobj *x0, cxobj *x1); cxobj *xml_dup(cxobj *x0); @@ -137,5 +139,7 @@ int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg); int xml_body_parse(cxobj *xb, enum cv_type type, cg_var **cvp); int xml_body_int32(cxobj *xb, int32_t *val); int xml_body_uint32(cxobj *xb, uint32_t *val); +int xml_operation(char *opstr, enum operation_type *op); +char *xml_operation2str(enum operation_type op); #endif /* _CLIXON_XML_H */ diff --git a/lib/clixon/clixon_xml_db.h b/lib/clixon/clixon_xml_db.h index e1c6d474..f6c470b7 100644 --- a/lib/clixon/clixon_xml_db.h +++ b/lib/clixon/clixon_xml_db.h @@ -42,18 +42,19 @@ int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt); int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk); int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk); + 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, cxobj *xt, enum operation_type op); -int xmldb_put_tree(clicon_handle h, char *db, char *api_path, - cxobj *xt, enum operation_type op); -int xmldb_put_xkey(clicon_handle h, char *db, - char *xkey, char *val, - enum operation_type op); -int xmldb_dump_local(FILE *f, char *dbfilename, char *rxkey); +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); int xmldb_unlock(clicon_handle h, char *db, int pid); +int xmldb_unlock_all(clicon_handle h, int pid); int xmldb_islocked(clicon_handle h, char *db); int xmldb_exists(clicon_handle h, char *db); int xmldb_delete(clicon_handle h, char *db); diff --git a/lib/src/Makefile.in b/lib/src/Makefile.in index 5b0548f7..8fd6c3a7 100644 --- a/lib/src/Makefile.in +++ b/lib/src/Makefile.in @@ -69,7 +69,7 @@ SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \ clixon_yang.c clixon_yang_type.c \ clixon_hash.c clixon_options.c clixon_plugin.c \ clixon_proto.c clixon_proto_encode.c clixon_proto_client.c \ - clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_xml_db_rpc.c + clixon_xsl.c clixon_sha1.c clixon_xml_db.c YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \ lex.clixon_yang_parse.o clixon_yang_parse.tab.o \ diff --git a/lib/src/clixon_json_parse.y b/lib/src/clixon_json_parse.y index 036287a1..9471942b 100644 --- a/lib/src/clixon_json_parse.y +++ b/lib/src/clixon_json_parse.y @@ -224,7 +224,7 @@ json_current_body(struct clicon_json_yacc_arg *jy, */ /* top: json -> value is also possible */ -json : object J_EOF { clicon_debug(1,"json->object"); YYACCEPT; } +json : value J_EOF { clicon_debug(1,"json->object"); YYACCEPT; } ; value : J_TRUE { json_current_body(_JY, "true");} diff --git a/lib/src/clixon_options.c b/lib/src/clixon_options.c index f1432007..32df541e 100644 --- a/lib/src/clixon_options.c +++ b/lib/src/clixon_options.c @@ -196,10 +196,6 @@ clicon_option_default(clicon_hash_t *copt) if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0) goto catch; } - if (!hash_lookup(copt, "CLICON_XMLDB_RPC")){ - if (hash_add(copt, "CLICON_XMLDB_RPC", "0", strlen("0")+1) < 0) - goto catch; - } retval = 0; catch: unchunk_group(__FUNCTION__); @@ -616,35 +612,6 @@ clicon_xmldb_dir(clicon_handle h) return clicon_option_str(h, "CLICON_XMLDB_DIR"); } -/*! Set if xmldb runs in a separate process (clixon_xmldb). */ -int -clicon_xmldb_rpc(clicon_handle h) -{ - char *s; - - if ((s = clicon_option_str(h, "CLICON_XMLDB_RPC")) == NULL) - return 0; /* default 0 */ - return atoi(s); -} - -/*! Get xmldb inet address */ -char * -clicon_xmldb_addr(clicon_handle h) -{ - return clicon_option_str(h, "CLICON_XMLDB_ADDR"); -} - -/*! Get port for xmldb address in case of AF_INET or AF_INET6 */ -uint16_t -clicon_xmldb_port(clicon_handle h) -{ - char *s; - - if ((s = clicon_option_str(h, "CLICON_XMLDB_PORT")) == NULL) - return -1; - return atoi(s); -} - /*! Get YANG specification * Must use hash functions directly since they are not strings. */ diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 7e8bc392..7ecd32f8 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -68,6 +68,8 @@ #include "clixon_queue.h" #include "clixon_chunk.h" #include "clixon_sig.h" +#include "clixon_xml.h" +#include "clixon_xsl.h" #include "clixon_proto.h" #include "clixon_proto_encode.h" @@ -80,20 +82,8 @@ struct map_type2str{ /* Mapping between yang keyword string <--> clicon constants */ static const struct map_type2str msgmap[] = { - {CLICON_MSG_COMMIT, "commit"}, - {CLICON_MSG_VALIDATE, "validate"}, + {CLICON_MSG_NETCONF, "netconf"}, {CLICON_MSG_CHANGE, "change"}, - {CLICON_MSG_XMLPUT, "xmlput"}, - {CLICON_MSG_SAVE, "save"}, - {CLICON_MSG_LOAD, "load"}, - {CLICON_MSG_COPY, "copy"}, - {CLICON_MSG_KILL, "kill"}, - {CLICON_MSG_DEBUG, "debug"}, - {CLICON_MSG_CALL, "call"}, - {CLICON_MSG_SUBSCRIPTION, "subscription"}, - {CLICON_MSG_OK, "ok"}, - {CLICON_MSG_NOTIFY, "notify"}, - {CLICON_MSG_ERR, "err"}, {-1, NULL}, }; @@ -234,17 +224,14 @@ clicon_msg_send(int s, * Now, ^C will interrupt the whole process, and this may not be what you want. * * @param[in] s UNIX domain socket to communicate with backend - * @param[out] msg CLICON msg data reply structure. allocated using CLICON chunks, - * freed by caller with unchunk*(...,label) + * @param[out] msg CLICON msg data reply structure. Free with free() * @param[out] eof Set if eof encountered - * @param[in] label Label used in chunk allocation and deallocation. * Note: caller must ensure that s is closed if eof is set after call. */ int clicon_msg_rcv(int s, - struct clicon_msg **msg, - int *eof, - const char *label) + struct clicon_msg **msg, + int *eof) { int retval = -1; struct clicon_msg hdr; @@ -273,8 +260,8 @@ clicon_msg_rcv(int s, mlen = ntohs(hdr.op_len); clicon_debug(2, "%s: rcv msg seq=%d, len=%d", __FUNCTION__, ntohs(hdr.op_type), mlen); - if ((*msg = (struct clicon_msg *)chunk(mlen, label)) == NULL){ - clicon_err(OE_CFG, errno, "%s: chunk", __FUNCTION__); + if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){ + clicon_err(OE_CFG, errno, "malloc"); goto done; } memcpy(*msg, &hdr, hlen); @@ -296,17 +283,21 @@ clicon_msg_rcv(int s, } -/*! Connect to server, send an clicon_msg message and wait for result. - * Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close. - * NOTE: this is dependent on unix domain +/*! Connect to server, send a clicon_msg message and wait for result using unix socket + * + * @param[in] msg CLICON msg data structure. It has fixed header and variable body. + * @param[in] sockpath Unix domain file path + * @param[out] retdata Returned data as string netconf xml tree. + * @param[out] sock0 Return socket in case of asynchronous notify + * @retval 0 OK + * @retval -1 Error + * @see clicon_rpc But this is one-shot rpc: open, send, get reply and close. */ int clicon_rpc_connect_unix(struct clicon_msg *msg, char *sockpath, - char **data, - uint16_t *datalen, - int *sock0, - const char *label) + char **retdata, + int *sock0) { int retval = -1; int s = -1; @@ -325,7 +316,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg, } if ((s = clicon_connect_unix(sockpath)) < 0) goto done; - if (clicon_rpc(s, msg, data, datalen, label) < 0) + if (clicon_rpc(s, msg, retdata) < 0) goto done; if (sock0 != NULL) *sock0 = s; @@ -336,17 +327,23 @@ clicon_rpc_connect_unix(struct clicon_msg *msg, return retval; } -/*! Connect to server, send an clicon_msg message and wait for result using an inet socket - * Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close. +/*! Connect to server, send a clicon_msg message and wait for result using an inet socket + * This uses unix domain socket communication + * @param[in] msg CLICON msg data structure. It has fixed header and variable body. + * @param[in] dst IPv4 address + * @param[in] port TCP port + * @param[out] retdata Returned data as string netconf xml tree. + * @param[out] sock0 Return socket in case of asynchronous notify + * @retval 0 OK + * @retval -1 Error + * @see clicon_rpc But this is one-shot rpc: open, send, get reply and close. */ int clicon_rpc_connect_inet(struct clicon_msg *msg, char *dst, uint16_t port, - char **data, - uint16_t *datalen, - int *sock0, - const char *label) + char **retdata, + int *sock0) { int retval = -1; int s = -1; @@ -371,7 +368,7 @@ clicon_rpc_connect_inet(struct clicon_msg *msg, close(s); goto done; } - if (clicon_rpc(s, msg, data, datalen, label) < 0) + if (clicon_rpc(s, msg, retdata) < 0) goto done; if (sock0 != NULL) *sock0 = s; @@ -391,29 +388,25 @@ clicon_rpc_connect_inet(struct clicon_msg *msg, * * @param[in] s Socket to communicate with backend * @param[in] msg CLICON msg data structure. It has fixed header and variable body. - * @param[out] data Returned data as byte-strin exclusing header. - * Deallocate w unchunk...(..., label) - * @param[out] datalen Length of returned data - * @param[in] label Label used in chunk allocation. + * @param[out] xret Returned data as netconf xml tree. + * @retval 0 OK + * @retval -1 Error */ int -clicon_rpc(int s, - struct clicon_msg *msg, - char **data, - uint16_t *datalen, - const char *label) +clicon_rpc(int s, + struct clicon_msg *msg, + char **ret) { int retval = -1; struct clicon_msg *reply; int eof; - uint32_t err; - uint32_t suberr; - char *reason; enum clicon_msg_type type; + char *data = NULL; + cxobj *cx = NULL; if (clicon_msg_send(s, msg) < 0) goto done; - if (clicon_msg_rcv(s, &reply, &eof, label) < 0) + if (clicon_msg_rcv(s, &reply, &eof) < 0) goto done; if (eof){ clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__); @@ -423,32 +416,38 @@ clicon_rpc(int s, } type = ntohs(reply->op_type); switch (type){ - case CLICON_MSG_OK: - if (data != NULL) { - *data = reply->op_body; - *datalen = ntohs(reply->op_len) - sizeof(*reply); - } - break; - case CLICON_MSG_ERR: - if (clicon_msg_err_decode(reply, &err, &suberr, &reason, label) < 0) - goto done; - if (debug) - clicon_err(err, suberr, "%s msgtype:%hu", reason, ntohs(msg->op_type)); - else - clicon_err(err, suberr, "%s", reason); - goto done; + case CLICON_MSG_NETCONF: /* ok or rpc-error expected */ + data = reply->op_body; /* assume string */ break; default: - clicon_err(OE_PROTO, 0, "%s: unexpected reply: %hu", - __FUNCTION__, type); + clicon_err(OE_PROTO, 0, "%s: unexpected reply: %s", + __FUNCTION__, msg_type2str(type)); goto done; break; } + if (ret && data) + if ((*ret = strdup(data)) == NULL){ + clicon_err(OE_UNIX, errno, "strdup"); + goto done; + } retval = 0; done: + if (cx) + xml_free(cx); + if (reply) + free(reply); return retval; } +/*! Send a clicon_msg message as reply to a clicon rpc request + * + * @param[in] s Socket to communicate with client + * @param[in] type Clicon msg operation, see enum clicon_msg_type + * @param[in] data Returned data as byte-string. + * @param[in] datalen Length of returned data XXX may be unecessary if always string? + * @retval 0 OK + * @retval -1 Error + */ int send_msg_reply(int s, uint16_t type, @@ -475,57 +474,160 @@ send_msg_reply(int s, return retval; } -int -send_msg_ok(int s) -{ - return send_msg_reply(s, CLICON_MSG_OK, NULL, 0); -} - +/*! Send a clicon_msg NOTIFY message asynchronously to client + * + * @param[in] s Socket to communicate with client + * @param[in] level + * @param[in] event + * @retval 0 OK + * @retval -1 Error + */ int send_msg_notify(int s, int level, char *event) { - int retval = -1; - struct clicon_msg *msg; + int retval = -1; + struct clicon_msg *msg = NULL; - if ((msg=clicon_msg_notify_encode(level, event, __FUNCTION__)) == NULL) + if ((msg=clicon_msg_netconf_encode("%s", event)) == NULL) goto done; if (clicon_msg_send(s, msg) < 0) goto done; retval = 0; done: - unchunk_group(__FUNCTION__); + if (msg) + free(msg); return retval; } +/*! Send a clicon_msg OK message as reply to a clicon rpc request + * + * @param[in] s Socket to communicate with client + * @param[in] data Returned data as byte-string. + * @retval 0 OK + * @retval -1 Error + * @note send as netconf message XXX remove clicon header + */ int -send_msg_err(int s, int err, int suberr, char *format, ...) +send_msg_ok(int s, + char *data) +{ + int retval = -1; + cbuf *cb = NULL; + + if ((cb = cbuf_new()) == NULL) + goto done; + if (data) + cprintf(cb, "%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 *reason; - int len; - int retval = -1; - struct clicon_msg *msg; + char *info = NULL; + int len; + int retval = -1; + cbuf *cb = NULL; va_start(args, format); len = vsnprintf(NULL, 0, format, args) + 1; va_end(args); - if ((reason = (char *)chunk(len, __FUNCTION__)) == NULL) - return -1; - memset(reason, 0, len); + if ((info = malloc(len)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(info, 0, len); va_start(args, format); - vsnprintf(reason, len, format, args); + vsnprintf(info, len, format, args); va_end(args); - if ((msg=clicon_msg_err_encode(clicon_errno, clicon_suberrno, - reason, __FUNCTION__)) == NULL) + if ((cb = cbuf_new()) == NULL) goto done; - if (clicon_msg_send(s, msg) < 0) + cprintf(cb, "" + "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: - unchunk_group(__FUNCTION__); + if (cb) + cbuf_free(cb); + if (info) + free(info); return retval; } +/*! Send a clicon_msg Error message as reply to a clicon rpc request + * + * @param[in] s Socket to communicate with client + * @param[in] data Returned data as byte-string. + * @param[in] datalen Length of returned data + * @retval 0 OK + * @retval -1 Error + * @note send as netconf message XXX remove clicon header + * XXX: see clicon_xml_parse + */ +int +send_msg_netconf_reply(int s, + char *format, ...) +{ + va_list args; + char *str = NULL; + int len; + int retval = -1; + + va_start(args, format); + len = vsnprintf(NULL, 0, format, args) + 1; + va_end(args); + if ((str = malloc(len)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(str, 0, len); + va_start(args, format); + len = vsnprintf(str, len, format, args); + va_end(args); + if (send_msg_reply(s, CLICON_MSG_NETCONF, str, len) < 0){ + if (errno == ECONNRESET) + clicon_log(LOG_WARNING, "client rpc reset"); + goto done; + } + retval = 0; + done: + if (str) + free(str); + return retval; +} diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index ffc0ab79..fcbf6b24 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -49,6 +49,7 @@ #include #include #include +#include /* cligen */ #include @@ -56,38 +57,42 @@ /* clicon */ #include "clixon_queue.h" #include "clixon_chunk.h" +#include "clixon_log.h" #include "clixon_hash.h" #include "clixon_handle.h" #include "clixon_yang.h" #include "clixon_options.h" +#include "clixon_xml.h" +#include "clixon_xsl.h" #include "clixon_proto.h" #include "clixon_err.h" -#include "clixon_xml.h" #include "clixon_proto_encode.h" #include "clixon_proto_client.h" /*! Internal rpc function * @param[in] h CLICON handle - * @param[in] msg Encoded message - * @param[out] ret Return value from backend server (reply) - * @param[out] retlen Length of return value + * @param[in] msg Encoded message. Deallocate woth free + * @param[out] xret Return value from backend as netconf xml tree. Free w xml_free * @param[inout] sock0 If pointer exists, do not close socket to backend on success * and return it here. For keeping a notify socket open - * @param[in] label Chunk label for deallocating return values - * Deallocate with unchunk_group(label) * Note: sock0 is if connection should be persistent, like a notification/subscribe api */ -static int +int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, - char **ret, - uint16_t *retlen, - int *sock0, - const char *label) + cxobj **xret0, + int *sock0) { int retval = -1; char *sock; int port; + char *retdata = NULL; + cxobj *xret = NULL; + cxobj *x; + uint32_t err; + uint32_t suberr; + char *errmsg = NULL; + char *etype = NULL; if ((sock = clicon_sock(h)) == NULL){ clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set"); @@ -96,7 +101,7 @@ clicon_rpc_msg(clicon_handle h, /* What to do if inet socket? */ switch (clicon_sock_family(h)){ case AF_UNIX: - if (clicon_rpc_connect_unix(msg, sock, ret, retlen, sock0, label) < 0){ + if (clicon_rpc_connect_unix(msg, sock, &retdata, sock0) < 0){ #if 0 if (errno == ESHUTDOWN) /* Maybe could reconnect on a higher layer, but lets fail @@ -115,12 +120,428 @@ clicon_rpc_msg(clicon_handle h, clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set"); goto done; } - if (clicon_rpc_connect_inet(msg, sock, port, ret, retlen, sock0, label) < 0) + if (clicon_rpc_connect_inet(msg, sock, port, &retdata, sock0) < 0) goto done; break; } + +#if 1 /* Parse return data and intercept clixon errors */ + if (retdata) { + if (clicon_xml_parse_str(retdata, &xret) < 0) + goto done; + if (xpath_first(xret, "/rpc-reply/ok") != NULL || + xpath_first(xret, "/rpc-reply/data") != NULL) + ; + else if (xpath_first(xret, "/rpc-reply/rpc-error") != NULL){ + if ((x=xpath_first(xret, "//error-type"))!=NULL) + etype = xml_body(x); + if (etype&&strcmp(etype, "clixon")==0){ + if ((x=xpath_first(xret, "//error-tag"))!=NULL) + err = atoi(xml_body(x)); + if ((x=xpath_first(xret, "//error-message"))!=NULL) + suberr = atoi(xml_body(x)); + if ((x=xpath_first(xret, "//error-info"))!=NULL) + errmsg = xml_body(x); + if (debug) + clicon_err(err, suberr, "%s msgtype:%hu", errmsg, ntohs(msg->op_type)); + else + clicon_err(err, suberr, "%s", errmsg); + goto done; + } + } + else{ + clicon_err(OE_PROTO, 0, "%s: unexpected reply", + __FUNCTION__); + } + + } +#endif + if (xret0){ + *xret0 = xret; + xret = NULL; + } retval = 0; done: + if (retdata) + free(retdata); + if (xret) + xml_free(xret); + return retval; +} + +/*! Generic xml netconf clicon rpc + * Want to go over to use netconf directly between client and server,... + * @param[in] h clicon handle + * @param[in] xi XML netconf tree + * @param[out] xret Return XML, error or OK + * @param[out] sp Socket pointer for notification, otherwise NULL + */ +int +clicon_rpc_netconf_xml(clicon_handle h, + cxobj *xin, + cxobj **xret, + int *sp) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + + if ((msg = clicon_msg_netconf_encode_xml(xin)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, xret, sp) < 0) + goto done; + retval = 0; + done: + if (msg) + free(msg); + return retval; +} + +int +clicon_rpc_generate_error(cxobj *xerr) +{ + cxobj *x; + char *etype=""; + char *etag=""; + char *emsg=""; + char *einfo=""; + + if ((x=xpath_first(xerr, "error-type"))!=NULL) + etype = xml_body(x); + if ((x=xpath_first(xerr, "error-tag"))!=NULL) + etag = xml_body(x); + if ((x=xpath_first(xerr, "error-message"))!=NULL) + emsg = xml_body(x); + if ((x=xpath_first(xerr, "error-info"))!=NULL) + einfo = xml_body(x); + clicon_err(OE_XML, 0, "%s %s %s %s", etype, etag, emsg, einfo); + return 0; +} + +/*! Get database configuration + * Same as clicon_proto_change just with a cvec instead of lvec + * @param[in] h CLICON handle + * @param[in] db Name of database + * @param[in] xpath XPath (or "") + * @param[out] xt XML tree. must be freed by caller with xml_free + * @retval 0 OK + * @retval -1 Error, fatal or xml + * @code + * cxobj *xt = NULL; + * if (clicon_rpc_get_config(h, "running", "/", &xt) < 0) + * err; + * if (xt) + * xml_free(xt); + * @endcode + */ +int +clicon_rpc_get_config(clicon_handle h, + char *db, + char *xpath, + cxobj **xt) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cbuf *cb = NULL; + cxobj *xret = NULL; + cxobj *xerr; + cxobj *xd; + + if ((cb = cbuf_new()) == NULL) + goto done; + cprintf(cb, "<%s/>", db); + if (xpath && strlen(xpath)) + cprintf(cb, "", xpath); + cprintf(cb, ""); + if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + if ((xd = xpath_first(xret, "//data/config")) == NULL) + if ((xd = xml_new("config", NULL)) == NULL) + goto done; + if (xt){ + if (xml_rm(xd) < 0) + goto done; + *xt = xd; + } + retval = 0; + done: + if (cb) + cbuf_free(cb); + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + +/*! Send database entries as XML to backend daemon + * Same as clicon_proto_change just with a cvec instead of lvec + * @param[in] h CLICON handle + * @param[in] db Name of database + * @param[in] op Operation on database item: OP_MERGE, OP_REPLACE + * @param[in] api_path restconf API Path (or "") + * @param[in] xml XML string. Ex: ..... + * @retval 0 OK + * @retval -1 Error + * @code + * if (clicon_rpc_edit_config(h, "running", OP_MERGE, "/", + * "4") < 0) + * err; + * @endcode + */ +int +clicon_rpc_edit_config(clicon_handle h, + char *db, + enum operation_type op, + char *api_path, + char *xml) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cbuf *cb = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((cb = cbuf_new()) == NULL) + goto done; + cprintf(cb, "<%s/>", db); + cprintf(cb, "%s", + xml_operation2str(op)); + if (api_path && strlen(api_path)) + cprintf(cb, "", api_path); + if (xml) + cprintf(cb, "%s", xml); + cprintf(cb, ""); + if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (cb) + cbuf_free(cb); + if (msg) + free(msg); + return retval; +} + +/*! Send a request to backend to copy a file from one location to another + * Note this assumes the backend can access these files and (usually) assumes + * clients and servers have the access to the same filesystem. + * @param[in] h CLICON handle + * @param[in] db1 src database, eg "running" + * @param[in] db2 dst database, eg "startup" + * @code + * if (clicon_rpc_copy_config(h, "running", "startup") < 0) + * err; + * @endcode + */ +int +clicon_rpc_copy_config(clicon_handle h, + char *db1, + char *db2) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((msg = clicon_msg_netconf_encode("<%s/><%s/>", db1, db2)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + +/*! Send a request to backend to delete a config database + * @param[in] h CLICON handle + * @param[in] db database, eg "running" + * @code + * if (clicon_rpc_delete_config(h, "startup") < 0) + * err; + * @endcode + */ +int +clicon_rpc_delete_config(clicon_handle h, + char *db) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + +int +clicon_rpc_lock(clicon_handle h, + char *db) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + +int +clicon_rpc_unlock(clicon_handle h, + char *db) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + +int +clicon_rpc_close_session(clicon_handle h) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((msg = clicon_msg_netconf_encode("")) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + +int +clicon_rpc_kill_session(clicon_handle h, + int session_id) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + + if ((msg = clicon_msg_netconf_encode("%d", session_id)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } + retval = 0; + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); + return retval; +} + + +/*! Send validate request to backend daemon + * @param[in] h CLICON handle + * @param[in] db Name of database + * @retval 0 OK + */ +int +clicon_rpc_validate(clicon_handle h, + char *db) +{ + int retval = -1; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; + cxobj *x; + + if ((msg = clicon_msg_netconf_encode("<%s/>", db)) == NULL) + goto done; + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) + goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + x=xpath_first(xerr, "error-message"); + clicon_log(LOG_ERR, "Validate failed: \"%s\". Edit and try again or discard changes", x?xml_body(x):""); + goto done; + } + retval = 0; + done: + if (msg) + free(msg); + if (xret) + xml_free(xret); return retval; } @@ -128,45 +549,69 @@ clicon_rpc_msg(clicon_handle h, * @param[in] h CLICON handle * @param[in] from name of 'from' database (eg "candidate") * @param[in] db name of 'to' database (eg "running") - * @retval 0 Copy current->candidate + * @retval 0 OK */ int -clicon_rpc_commit(clicon_handle h, - char *from, - char *to) +clicon_rpc_commit(clicon_handle h) { int retval = -1; - struct clicon_msg *msg; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; - if ((msg=clicon_msg_commit_encode(from, to, __FUNCTION__)) == NULL) + if ((msg = clicon_msg_netconf_encode("")) == NULL) goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } retval = 0; done: - unchunk_group(__FUNCTION__); + if (xret) + xml_free(xret); + if (msg) + free(msg); return retval; } -/*! Send validate request to backend daemon - * @param[in] h CLICON handle - * @param[in] db Name of database - * @retval 0 +/*! Create a new notification subscription + * @param[in] h Clicon handle + * @param{in] stream name of notificatio/log stream (CLICON is predefined) + * @param{in] filter message filter, eg xpath for xml notifications + * @param[out] s0 socket returned where notification mesages will appear + * @note When using netconf create-subsrciption,status and format is not supported */ int -clicon_rpc_validate(clicon_handle h, - char *db) +clicon_rpc_create_subscription(clicon_handle h, + char *stream, + char *filter, + int *s0) { int retval = -1; - struct clicon_msg *msg; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; - if ((msg=clicon_msg_validate_encode(db, __FUNCTION__)) == NULL) + if ((msg = clicon_msg_netconf_encode("" + "%s" + "%s" + "", + stream?stream:"", filter?filter:"")) == NULL) goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) + if (clicon_rpc_msg(h, msg, &xret, s0) < 0) goto done; + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); + goto done; + } retval = 0; - done: - unchunk_group(__FUNCTION__); + done: + if (xret) + xml_free(xret); + if (msg) + free(msg); return retval; } @@ -189,160 +634,24 @@ clicon_rpc_change(clicon_handle h, char *val) { int retval = -1; - struct clicon_msg *msg; + struct clicon_msg *msg = NULL; if ((msg = clicon_msg_change_encode(db, op, key, val, - val?strlen(val)+1:0, - __FUNCTION__)) == NULL) + val?strlen(val)+1:0)) == NULL) goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) + if (clicon_rpc_msg(h, msg, NULL, NULL) < 0) goto done; retval = 0; done: - unchunk_group(__FUNCTION__); + if (msg) + free(msg); return retval; } -/*! Send database entries as XML to backend daemon - * Same as clicon_proto_change just with a cvec instead of lvec - * @param[in] h CLICON handle - * @param[in] db Name of database - * @param[in] op Operation on database item: OP_MERGE, OP_REPLACE - * @param[in] api_path restconf API Path (or "") - * @param[in] xml XML string. Ex: ..... - * @retval 0 OK - * @retval -1 Error - */ -int -clicon_rpc_xmlput(clicon_handle h, - char *db, - enum operation_type op, - char *api_path, - char *xml) -{ - int retval = -1; - struct clicon_msg *msg; - - if ((msg = clicon_msg_xmlput_encode(db, - (uint32_t)op, - api_path, - xml, - __FUNCTION__)) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) - goto done; - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - - - - -/*! Send database save request to backend daemon - * @param[in] h CLICON handle - * @param[in] db Name of database - * @param[in] snapshot Save to snapshot file - * @param[in] filename Save to file (backend file-system) - */ -int -clicon_rpc_save(clicon_handle h, - char *db, - int snapshot, - char *filename) -{ - int retval = -1; - struct clicon_msg *msg; - - if ((msg=clicon_msg_save_encode(db, snapshot, filename, - __FUNCTION__)) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) - goto done; - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - -/*! Send database load request to backend daemon - * @param[in] h CLICON handle - * @param[in] replace 0: merge with existing data, 1:replace completely - * @param[in] db Name of database - * @param[in] filename Load from file (backend file-system) - */ -int -clicon_rpc_load(clicon_handle h, - int replace, - char *db, - char *filename) -{ - int retval = -1; - struct clicon_msg *msg; - - if ((msg=clicon_msg_load_encode(replace, db, filename, - __FUNCTION__)) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) - goto done; - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - -/*! Send a request to backend to copy a file from one location to another - * Note this assumes the backend can access these files and (usually) assumes - * clients and servers have the access to the same filesystem. - * @param[in] h CLICON handle - * @param[in] db1 src database, eg "candidate" - * @param[in] db2 dst database, eg "running" - */ -int -clicon_rpc_copy(clicon_handle h, - char *db1, - char *db2) -{ - int retval = -1; - struct clicon_msg *msg; - - if ((msg=clicon_msg_copy_encode(db1, db2, __FUNCTION__)) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) - goto done; - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - - -/*! Send a kill session request to backend server - * @param[in] h CLICON handle - * @param[in] session_id Id of session to kill - */ -int -clicon_rpc_kill(clicon_handle h, - int session_id) -{ - int retval = -1; - struct clicon_msg *msg; - - if ((msg=clicon_msg_kill_encode(session_id, __FUNCTION__)) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) - goto done; - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - /*! Send a debug request to backend server * @param[in] h CLICON handle * @param[in] level Debug level @@ -352,107 +661,28 @@ clicon_rpc_debug(clicon_handle h, int level) { int retval = -1; - struct clicon_msg *msg; + struct clicon_msg *msg = NULL; + cxobj *xret = NULL; + cxobj *xerr; - if ((msg=clicon_msg_debug_encode(level, __FUNCTION__)) == NULL) + if ((msg = clicon_msg_netconf_encode("%d", level)) == NULL) goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0) + if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; - retval = 0; - done: - unchunk_group(__FUNCTION__); - return retval; -} - -/*! An rpc call from a frontend module to a function in a backend module - * - * A CLI/netconf frontend module can make a functional call to a backend - * module and get return value back. - * The backend module needs to be specified (XXX would be nice to avoid this) - * parameters can be sent, and value returned. - * A function (func) must be defined in the backend module (plugin) - * An example signature of such a downcall function is: - * @code - * char name[16]; - * char *ret; - * uint16_t retlen; - * clicon_rpc_call(h, 0, "my-backend-plugin", "my_fn", name, 16, - * &ret, &retlen, __FUNCTION__); - * unchunk_group(__FUNCTION__); # deallocate 'ret' - * @endcode - * Backend example function: - * @code -int -downcall(clicon_handle h, uint16_t op, uint16_t len, void *arg, - uint16_t *reply_data_len, void **reply_data) - * @endcode - * - * @param[in] h Clicon handle - * @param[in] op Generic application-defined operation - * @param[in] plugin Name of backend plugin (XXX look in backend plugin dir) - * @param[in] func Name of function i backend (ie downcall above) as string - * @param[in] param Input parameter given to function (void* arg in downcall) - * @param[in] paramlen Length of input parameter - * @param[out] ret Returned data as byte-string. Deallocate w unchunk...(..., label) - * @param[out] retlen Length of returned data - * @param[in] label Label used in chunk (de)allocation. Use: - * unchunk_group(label) to deallocate - */ -int -clicon_rpc_call(clicon_handle h, - uint16_t op, - char *plugin, - char *func, - void *param, - uint16_t paramlen, - char **ret, - uint16_t *retlen, - const void *label) -{ - int retval = -1; - struct clicon_msg *msg; - - if ((msg = clicon_msg_call_encode(op, plugin, func, - paramlen, param, - label)) == NULL) + if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){ + clicon_rpc_generate_error(xerr); goto done; - if (clicon_rpc_msg(h, msg, (char**)ret, retlen, NULL, label) < 0) + } + if (xpath_first(xret, "//rpc-reply/ok") == NULL){ + clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */ goto done; - retval = 0; -done: - return retval; -} - -/*! Create a new notification subscription - * @param[in] h Clicon handle - * @param[in] status 0: stop existing notification stream 1: start new stream. - * @param{in] stream name of notificatio/log stream (CLICON is predefined) - * @param{in] filter message filter, eg xpath for xml notifications - * @param[out] s0 socket returned where notification mesages will appear - */ -int -clicon_rpc_subscription(clicon_handle h, - int status, - char *stream, - enum format_enum format, - char *filter, - int *s0) -{ - struct clicon_msg *msg; - int retval = -1; - - if ((msg=clicon_msg_subscription_encode(status, stream, format, filter, - __FUNCTION__)) == NULL) - goto done; - if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0) - goto done; - if (status == 0 && s0){ - close(*s0); - *s0 = -1; } retval = 0; - done: - unchunk_group(__FUNCTION__); + done: + if (msg) + free(msg); + if (xret) + xml_free(xret); return retval; } diff --git a/lib/src/clixon_proto_encode.c b/lib/src/clixon_proto_encode.c index ee7fa34e..1fabf62c 100644 --- a/lib/src/clixon_proto_encode.c +++ b/lib/src/clixon_proto_encode.c @@ -68,107 +68,83 @@ #include "clixon_sig.h" #include "clixon_hash.h" #include "clixon_handle.h" +#include "clixon_xml.h" #include "clixon_proto.h" #include "clixon_proto_encode.h" struct clicon_msg * -clicon_msg_commit_encode(char *dbsrc, char *dbdst, - const char *label) +clicon_msg_netconf_encode(char *format, ...) { - struct clicon_msg *msg; - uint16_t len; + va_list args; + int xmllen; + int len; + struct clicon_msg *msg = NULL; int hdrlen = sizeof(*msg); - int p; - clicon_debug(2, "%s: dbsrc: %s dbdst: %s", - __FUNCTION__, - dbsrc, dbdst); - p = 0; - len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(dbsrc) + 1 + - strlen(dbdst) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); + va_start(args, format); + xmllen = vsnprintf(NULL, 0, format, args) + 1; + va_end(args); + + len = hdrlen + xmllen; + if ((msg = (struct clicon_msg *)malloc(len)) == NULL){ + clicon_err(OE_PROTO, errno, "malloc"); return NULL; } memset(msg, 0, len); /* hdr */ - msg->op_type = htons(CLICON_MSG_COMMIT); + msg->op_type = htons(CLICON_MSG_NETCONF); msg->op_len = htons(len); + /* body */ - strncpy(msg->op_body+p, dbsrc, len-p-hdrlen); - p += strlen(dbsrc)+1; - strncpy(msg->op_body+p, dbdst, len-p-hdrlen); - p += strlen(dbdst)+1; + va_start(args, format); + vsnprintf(msg->op_body, xmllen, format, args); + va_end(args); + return msg; } -int -clicon_msg_commit_decode(struct clicon_msg *msg, - char **dbsrc, char **dbdst, - const char *label) -{ - int p; - - p = 0; - /* body */ - if ((*dbsrc = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*dbsrc)+1; - if ((*dbdst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*dbdst)+1; - clicon_debug(2, "%s: dbsrc: %s dbdst: %s", __FUNCTION__, *dbsrc, *dbdst); - return 0; -} - struct clicon_msg * -clicon_msg_validate_encode(char *db, const char *label) +clicon_msg_netconf_encode_xml(cxobj *xml) { - struct clicon_msg *msg; - uint16_t len; - int hdrlen = sizeof(*msg); + struct clicon_msg *msg = NULL; + cbuf *cb = NULL; - clicon_debug(2, "%s: db: %s", __FUNCTION__, db); - len = sizeof(*msg) + strlen(db) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "cbuf_new"); + goto done; } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_VALIDATE); - msg->op_len = htons(len); - /* body */ - strncpy(msg->op_body, db, len-hdrlen); + if (clicon_xml2cbuf(cb, xml, 0, 0) < 0) + goto done; + if ((msg = clicon_msg_netconf_encode("%s", cbuf_get(cb))) < 0) + goto done; + done: + if (cb) + cbuf_free(cb); return msg; } int -clicon_msg_validate_decode(struct clicon_msg *msg, char **db, const char *label) +clicon_msg_netconf_decode(struct clicon_msg *msg, + cxobj **xml) { - /* body */ - if ((*db = chunk_sprintf(label, "%s", msg->op_body)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); - return -1; - } - clicon_debug(2, "%s: db: %s", __FUNCTION__, *db); - return 0; -} + int retval = -1; + char *xmlstr; + /* body */ + xmlstr = msg->op_body; + if (clicon_xml_parse_str(xmlstr, xml) < 0) + goto done; + retval = 0; + done: + return retval; +} struct clicon_msg * clicon_msg_change_encode(char *db, uint32_t op, char *key, char *str, - uint32_t str_len, - const char *label) + uint32_t str_len) { struct clicon_msg *msg; uint16_t len; @@ -184,8 +160,8 @@ clicon_msg_change_encode(char *db, strlen(key) + str_len; if (str_len) len++; /* if str not null add end of string */ - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); + if ((msg = (struct clicon_msg *)malloc(len)) == NULL){ + clicon_err(OE_PROTO, errno, "malloc"); return NULL; } memset(msg, 0, len); @@ -264,661 +240,5 @@ clicon_msg_change_decode(struct clicon_msg *msg, return 0; } -/*! Encode xmlput / edit of database content - * @param[in] db Name of database - * @param[in] op set|merge|delete. See lv_op_t - * @param[in] api_path restconf api path - * @param[in] xml XML data string - * @param[in] label Memory chunk label - * @retval msg Encoded message - * @retval NULL Error - */ -struct clicon_msg * -clicon_msg_xmlput_encode(char *db, - uint32_t op, - char *api_path, - char *xml, - const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int hdrlen = sizeof(*msg); - int p; - uint32_t tmp; - - clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s", - __FUNCTION__, - op, db, api_path, xml); - p = 0; - hdrlen = sizeof(*msg); - len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(api_path) + 1 +strlen(xml) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_XMLPUT); - msg->op_len = htons(len); - /* body */ - tmp = htonl(op); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - - strncpy(msg->op_body+p, db, len-p-hdrlen); - p += strlen(db)+1; - strncpy(msg->op_body+p, api_path, len-p-hdrlen); - p += strlen(api_path)+1; - strncpy(msg->op_body+p, xml, len-p-hdrlen); - p += strlen(xml)+1; - return msg; -} - -/*! Decode xmlput / edit of database content - * @param[in] msg Incoming message to be decoded - * @param[out] db Name of database - * @param[out] op set|merge|delete. See lv_op_t - * @param[out] api_path restconf api path - * @param[out] xml XML data string - * @param[in] label Memory chunk label - * @retval 0 OK - * @retval -1 Error - */ -int -clicon_msg_xmlput_decode(struct clicon_msg *msg, - char **db, - uint32_t *op, - char **api_path, - char **xml, - const char *label) -{ - int p; - uint32_t tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *op = ntohl(tmp); - p += sizeof(uint32_t); - - if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*db)+1; - if ((*api_path = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*api_path)+1; - if ((*xml = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*xml)+1; - clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s", - __FUNCTION__, - *op, *db, *api_path, *xml); - return 0; -} - -struct clicon_msg * -clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename, - const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int hdrlen = sizeof(*msg); - int p; - uint32_t tmp; - - clicon_debug(2, "%s: snapshot: %d db: %s filename: %s", - __FUNCTION__, - snapshot, db, filename); - p = 0; - hdrlen = sizeof(*msg); - len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1; - if (!snapshot) - len += strlen(filename) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_SAVE); - msg->op_len = htons(len); - /* body */ - tmp = htonl(snapshot); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - - strncpy(msg->op_body+p, db, len-p-hdrlen); - p += strlen(db)+1; - if (!snapshot){ - strncpy(msg->op_body+p, filename, len-p-hdrlen); - p += strlen(filename)+1; - } - return msg; -} - -int -clicon_msg_save_decode(struct clicon_msg *msg, - char **db, uint32_t *snapshot, char **filename, - const char *label) -{ - int p; - uint32_t tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *snapshot = ntohl(tmp); - p += sizeof(uint32_t); - - if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*db)+1; - if (*snapshot == 0){ - if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*filename)+1; - } - clicon_debug(2, "%s: snapshot: %d db: %s filename: %s", - __FUNCTION__, - *snapshot, *db, *filename); - return 0; -} - -struct clicon_msg * -clicon_msg_load_encode(int replace, char *db, char *filename, const char *label) -{ - struct clicon_msg *msg; - int hdrlen = sizeof(*msg); - uint16_t len; - uint32_t tmp; - int p; - - clicon_debug(2, "%s: replace: %d db: %s filename: %s", - __FUNCTION__, - replace, db, filename); - p = 0; - len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(filename) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_LOAD); - msg->op_len = htons(len); - /* body */ - tmp = htonl(replace); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - - strncpy(msg->op_body+p, db, len-p-hdrlen); - p += strlen(db)+1; - strncpy(msg->op_body+p, filename, len-p-hdrlen); - p += strlen(filename)+1; - return msg; -} - -int -clicon_msg_load_decode(struct clicon_msg *msg, - int *replace, - char **db, - char **filename, - const char *label) -{ - int p; - uint32_t tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *replace = ntohl(tmp); - p += sizeof(uint32_t); - if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*db)+1; - if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*filename)+1; - clicon_debug(2, "%s: %d db: %s filename: %s", - __FUNCTION__, - ntohs(msg->op_type), - *db, *filename); - return 0; -} - -struct clicon_msg * -clicon_msg_copy_encode(char *db_src, char *db_dst, - const char *label) -{ - struct clicon_msg *msg; - int hdrlen = sizeof(*msg); - uint16_t len; - int p; - - clicon_debug(2, "%s: db_src: %s db_dst: %s", - __FUNCTION__, - db_src, db_dst); - p = 0; - len = hdrlen + strlen(db_src) + 1 + strlen(db_dst) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_COPY); - msg->op_len = htons(len); - /* body */ - strncpy(msg->op_body+p, db_src, len-p-hdrlen); - p += strlen(db_src)+1; - strncpy(msg->op_body+p, db_dst, len-p-hdrlen); - p += strlen(db_dst)+1; - return msg; -} - -int -clicon_msg_copy_decode(struct clicon_msg *msg, - char **db_src, char **db_dst, - const char *label) -{ - int p; - - p = 0; - /* body */ - if ((*db_src = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*db_src)+1; - - if ((*db_dst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*db_dst)+1; - clicon_debug(2, "%s: db_src: %s db_dst: %s", - __FUNCTION__, - *db_src, *db_dst); - return 0; -} - -struct clicon_msg * -clicon_msg_kill_encode(uint32_t session_id, const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int p; - uint32_t tmp; - - clicon_debug(2, "%s: %d", __FUNCTION__, session_id); - p = 0; - len = sizeof(*msg) + sizeof(uint32_t); - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_KILL); - msg->op_len = htons(len); - /* body */ - tmp = htonl(session_id); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - return msg; - -} - -int -clicon_msg_kill_decode(struct clicon_msg *msg, - uint32_t *session_id, - const char *label) -{ - int p; - uint32_t tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *session_id = ntohl(tmp); - p += sizeof(uint32_t); - clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *session_id); - return 0; -} - -struct clicon_msg * -clicon_msg_debug_encode(uint32_t level, const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int p; - uint32_t tmp; - - clicon_debug(2, "%s: %d", __FUNCTION__, label); - p = 0; - len = sizeof(*msg) + sizeof(uint32_t); - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_DEBUG); - msg->op_len = htons(len); - /* body */ - tmp = htonl(level); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - return msg; - -} - -int -clicon_msg_debug_decode(struct clicon_msg *msg, - uint32_t *level, - const char *label) -{ - int p; - uint32_t tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *level = ntohl(tmp); - p += sizeof(uint32_t); - clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *level); - return 0; -} -struct clicon_msg * -clicon_msg_call_encode(uint16_t op, - char *plugin, - char *func, - uint16_t arglen, - void *arg, - const char *label) -{ - struct clicon_msg *msg; - struct clicon_msg_call_req *req; - int hdrlen = sizeof(*msg); - int len; - - clicon_debug(2, "%s: %d plugin: %s func: %s arglen: %d", - __FUNCTION__, op, plugin, func, arglen); - len = - hdrlen + - sizeof(struct clicon_msg_call_req) + - strlen(plugin) + 1 + - strlen(func) + 1 + - arglen; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_CALL); - msg->op_len = htons(len); - /* req */ - req = (struct clicon_msg_call_req *)msg->op_body; - req->cr_len = htons(len - hdrlen); - req->cr_op = htons(op); - req->cr_plugin = req->cr_data; - strncpy(req->cr_plugin, plugin, strlen(plugin)); - req->cr_func = req->cr_plugin + strlen(req->cr_plugin) + 1; - strncpy(req->cr_func, func, strlen(func)); - req->cr_arglen = htons(arglen); - req->cr_arg = req->cr_func + strlen(req->cr_func) + 1; - memcpy(req->cr_arg, arg, arglen); - - return msg; - -} - -int -clicon_msg_call_decode(struct clicon_msg *msg, - struct clicon_msg_call_req **req, - const char *label) -{ - uint16_t len; - struct clicon_msg_call_req *r; - - r = (struct clicon_msg_call_req *)msg->op_body; - len = ntohs(r->cr_len); - if ((*req = chunk(len, label)) == NULL) { - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return -1; - } - memcpy(*req, r, len); - (*req)->cr_len = ntohs(r->cr_len); - (*req)->cr_op = ntohs(r->cr_op); - (*req)->cr_arglen = ntohs(r->cr_arglen); - (*req)->cr_plugin = (*req)->cr_data; - (*req)->cr_func = (*req)->cr_plugin + strlen((*req)->cr_plugin) +1; - (*req)->cr_arg = (*req)->cr_func + strlen((*req)->cr_func) +1; - - return 0; -} - -struct clicon_msg * -clicon_msg_subscription_encode(int status, - char *stream, - enum format_enum format, - char *filter, - const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int hdrlen = sizeof(*msg); - int p; - int tmp; - - clicon_debug(2, "%s: %d %d %s %s", __FUNCTION__, status, format, stream, filter); - p = 0; - assert(filter); - len = hdrlen + sizeof(uint32_t) + sizeof(uint32_t) + strlen(stream) + 1 + strlen(filter) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_SUBSCRIPTION); - msg->op_len = htons(len); - /* body */ - tmp = htonl(status); - memcpy(msg->op_body+p, &tmp, sizeof(int)); - p += sizeof(int); - - tmp = htonl(format); - memcpy(msg->op_body+p, &tmp, sizeof(int)); - p += sizeof(int); - - strncpy(msg->op_body+p, stream, len-p-hdrlen); - p += strlen(stream)+1; - - strncpy(msg->op_body+p, filter, len-p-hdrlen); - p += strlen(filter)+1; - - return msg; -} - -int -clicon_msg_subscription_decode(struct clicon_msg *msg, - int *status, - char **stream, - enum format_enum *format, - char **filter, - const char *label) -{ - int p; - int tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(int)); - *status = ntohl(tmp); - p += sizeof(int); - - memcpy(&tmp, msg->op_body+p, sizeof(int)); - *format = ntohl(tmp); - p += sizeof(int); - - if ((*stream = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*stream)+1; - - if ((*filter = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*filter)+1; - clicon_debug(2, "%s: %d %s %d %s", __FUNCTION__, *status, *filter, *stream, *filter); - return 0; -} - -struct clicon_msg * -clicon_msg_notify_encode(int level, char *event, const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int hdrlen = sizeof(*msg); - int p; - int tmp; - - clicon_debug(2, "%s: %d %s", __FUNCTION__, level, event); - p = 0; - hdrlen = sizeof(*msg); - len = sizeof(*msg) + sizeof(uint32_t) + strlen(event) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_NOTIFY); - msg->op_len = htons(len); - /* body */ - tmp = htonl(level); - memcpy(msg->op_body+p, &tmp, sizeof(int)); - p += sizeof(int); - strncpy(msg->op_body+p, event, len-p-hdrlen); - p += strlen(event)+1; - - return msg; -} - -int -clicon_msg_notify_decode(struct clicon_msg *msg, - int *level, char **event, - const char *label) -{ - int p; - int tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(int)); - *level = ntohl(tmp); - p += sizeof(int); - if ((*event = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*event)+1; - clicon_debug(2, "%s: %d %s", __FUNCTION__, *level, *event); - return 0; -} - -struct clicon_msg * -clicon_msg_err_encode(uint32_t err, uint32_t suberr, char *reason, const char *label) -{ - struct clicon_msg *msg; - uint16_t len; - int hdrlen = sizeof(*msg); - int p; - uint32_t tmp; - - clicon_debug(2, "%s: %d %d %s", __FUNCTION__, err, suberr, reason); - p = 0; - hdrlen = sizeof(*msg); - len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(reason) + 1; - if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); - return NULL; - } - memset(msg, 0, len); - /* hdr */ - msg->op_type = htons(CLICON_MSG_ERR); - msg->op_len = htons(len); - /* body */ - tmp = htonl(err); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - tmp = htonl(suberr); - memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); - p += sizeof(uint32_t); - strncpy(msg->op_body+p, reason, len-p-hdrlen); - p += strlen(reason)+1; - - return msg; - -} - -int -clicon_msg_err_decode(struct clicon_msg *msg, - uint32_t *err, uint32_t *suberr, char **reason, - const char *label) -{ - int p; - uint32_t tmp; - - p = 0; - /* body */ - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *err = ntohl(tmp); - p += sizeof(uint32_t); - memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); - *suberr = ntohl(tmp); - p += sizeof(uint32_t); - if ((*reason = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ - clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", - __FUNCTION__); - return -1; - } - p += strlen(*reason)+1; - clicon_debug(2, "%s: %d %d %s", - __FUNCTION__, - *err, *suberr, *reason); - return 0; -} - diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 2a1aa8f9..77a11271 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -465,6 +465,12 @@ xml_childvec_set(cxobj *x, return 0; } +cxobj ** +xml_childvec_get(cxobj *x) +{ + return x->x_childvec; +} + /*! Create new xml node given a name and parent. Free it with xml_free(). * * @param[in] name Name of new @@ -913,24 +919,26 @@ clicon_xml2cbuf(cbuf *cb, cprintf(cb, "%s:", xml_namespace(cx)); cprintf(cb, "%s", xml_name(cx)); xc = NULL; - while ((xc = xml_child_each(cx, xc, -1)) != NULL) { - if (xml_type(xc) != CX_ATTR) - continue; + while ((xc = xml_child_each(cx, xc, CX_ATTR)) != NULL) clicon_xml2cbuf(cb, xc, level+1, prettyprint); + /* Check for special case instead of */ + if (xml_body(cx)==NULL && xml_child_nr(cx)==0) + cprintf(cb, "/>"); + else{ + cprintf(cb, ">"); + if (prettyprint && xml_body(cx)==NULL) + cprintf(cb, "\n"); + xc = NULL; + while ((xc = xml_child_each(cx, xc, -1)) != NULL) { + if (xml_type(xc) == CX_ATTR) + continue; + else + clicon_xml2cbuf(cb, xc, level+1, prettyprint); + } + if (prettyprint && xml_body(cx)==NULL) + cprintf(cb, "%*s", level*XML_INDENT, ""); + cprintf(cb, "", xml_name(cx)); } - cprintf(cb, ">"); - if (prettyprint && xml_body(cx)==NULL) - cprintf(cb, "\n"); - xc = NULL; - while ((xc = xml_child_each(cx, xc, -1)) != NULL) { - if (xml_type(xc) == CX_ATTR) - continue; - else - clicon_xml2cbuf(cb, xc, level+1, prettyprint); - } - if (prettyprint && xml_body(cx)==NULL) - cprintf(cb, "%*s", level*XML_INDENT, ""); - cprintf(cb, "", xml_name(cx)); if (prettyprint) cprintf(cb, "\n"); break; @@ -956,7 +964,7 @@ xml_parse(char *str, } ya.ya_xparent = x_up; if (clixon_xml_parsel_init(&ya) < 0) - goto done; + goto done; if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */ goto done; @@ -1079,7 +1087,6 @@ clicon_xml_parse_file(int fd, * @endcode * @see clicon_xml_parse_file * @note you need to free the xml parse tree after use, using xml_free() - * Update: with yacc parser I dont think it changes,.... */ int clicon_xml_parse_str(char *str, @@ -1090,6 +1097,57 @@ clicon_xml_parse_str(char *str, return xml_parse(str, *cxtop); } + +/*! Read XML definition from variable argument string and parse it into parse-tree. + * + * Utility function using stdarg instead of static string. + * @param[out] xml_top Top of XML parse tree. Will add extra top element called 'top'. + * you must free it after use, using xml_free() + * @param[in] format Pointer to string containing XML definition. + + * @retval 0 OK + * @retval -1 Error with clicon_err called + * + * @code + * cxobj *cx = NULL; + * if (clicon_xml_parse(&cx, "%s0 Id of locker @@ -462,6 +479,8 @@ db_islocked(char *db) return 0; } + + /*! Translate from symbolic database name to actual filename in file-system * @param[in] h Clicon handle * @param[in] db Symbolic database name, eg "candidate", "running" @@ -959,8 +978,8 @@ xml_order(cxobj *x, * cxobj **xvec; * size_t xlen; * yang_spec *yspec = clicon_dbspec_yang(h); - * if (xmldb_get_vec(dbname, "/interfaces/interface[name="eth"]", yspec, - * &xt, &xvec, &xlen) < 0) + * if (xmldb_get("running", "/interfaces/interface[name="eth"]", + * &xt, &xvec, &xlen) < 0) * err; * for (i=0; iys_argument); if (debug){ xml_print(stderr, xt); // yang_print(stderr, (yang_node*)ys, 0); } - if (get_operation(xt, &op) < 0) - goto done; + if ((opstr = xml_find_value(xt, "operation")) != NULL) + if (xml_operation(opstr, &op) < 0) + goto done; body = xml_body(xt); if ((cbxk = cbuf_new()) == NULL){ clicon_err(OE_UNIX, errno, "cbuf_new"); @@ -1240,95 +1183,31 @@ put(char *dbname, return retval; } - -/*! Local variant of xmldb_put */ -static int -xmldb_put_local(clicon_handle h, - char *db, - cxobj *xt, - enum operation_type op) -{ - int retval = -1; - cxobj *x = NULL; - yang_stmt *ys; - yang_spec *yspec; - char *dbfilename = NULL; - - yspec = clicon_dbspec_yang(h); - if (db2file(h, db, &dbfilename) < 0) - goto done; - if (op == OP_REPLACE){ - if (db_delete(dbfilename) < 0) - goto done; - if (db_init(dbfilename) < 0) - goto done; - } - while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){ - if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){ - clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x)); - goto done; - } - if (put(dbfilename, /* database name */ - x, /* xml root node */ - ys, /* yang statement of xml node */ - op, /* operation, eg merge/delete */ - "" /* aggregate xml key */ - ) < 0) - goto done; - } - retval = 0; - done: - if (dbfilename) - free(dbfilename); - return retval; -} - -/*! Modify database provided an xml tree and an operation - * @param[in] dbname Name of database to search in (filename including dir path) +/*! Modify database provided an xml tree, a restconf api_path and an operation + * * @param[in] h CLICON handle * @param[in] db running or candidate - * @param[in] xt xml-tree. Top-level symbol is dummy * @param[in] op OP_MERGE: just add it. * OP_REPLACE: first delete whole database * OP_NONE: operation attribute in xml determines operation + * @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13]) + * @param[in] xt xml-tree. Top-level symbol is dummy * @retval 0 OK * @retval -1 Error - * The xml may contain the "operation" attribute which defines the operation. - * @code - * cxobj *xt; - * if (clicon_xml_parse_str("17", &xt) < 0) - * err; - * if (xmldb_put(h, "running", xt, OP_MERGE) < 0) - * err; - * @endcode - * @see xmldb_put_xkey for single key - */ -int -xmldb_put(clicon_handle h, - char *db, - cxobj *xt, - enum operation_type op) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_put_rpc(h, db, xt, op); - else - return xmldb_put_local(h, db, xt, op); -} - -/*! XXX: replace xk with xt - * @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13]) - * e.g container top { - list list1 { - key "key1 key2 key3"; -* is referenced as - /restconf/data/top/list1=a,,foo + * example: + * container top { + * list list1 { + * key "key1 key2 key3"; + * is referenced as + * /restconf/data/top/list1=a,,foo + * @see xmldb_put */ static int -xmldb_put_tree_local(clicon_handle h, - char *db, - char *api_path, - cxobj *xt, - enum operation_type op) +xmldb_put_restconf_api_path(clicon_handle h, + char *db, + enum operation_type op, + char *api_path, + cxobj *xt) { int retval = -1; yang_stmt *y = NULL; @@ -1431,7 +1310,7 @@ xmldb_put_tree_local(clicon_handle h, clicon_err(OE_XML, errno, "List %s without argument", name); goto done; } - cprintf(ckey, "/%s", val2); + cprintf(ckey, "=%s", val2); cbuf_reset(csubkey); cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname); if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE) @@ -1515,41 +1394,98 @@ xmldb_put_tree_local(clicon_handle h, cvec_free(cvk); unchunk_group(__FUNCTION__); return retval; - } -/*! +/*! Modify database provided an xml tree and an operation + * + * @param[in] h CLICON handle + * @param[in] db running or candidate + * @param[in] xt xml-tree. Top-level symbol is dummy + * @param[in] op OP_MERGE: just add it. + * OP_REPLACE: first delete whole database + * OP_NONE: operation attribute in xml determines operation * @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13]) + * @retval 0 OK + * @retval -1 Error + * The xml may contain the "operation" attribute which defines the operation. + * @code + * cxobj *xt; + * if (clicon_xml_parse_str("17", &xt) < 0) + * err; + * if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0) + * err; + * @endcode + * @see xmldb_put_xkey for single key */ int -xmldb_put_tree(clicon_handle h, - char *db, - char *api_path, - cxobj *xt, - enum operation_type op) +xmldb_put(clicon_handle h, + char *db, + enum operation_type op, + char *api_path, + cxobj *xt) { - if (clicon_xmldb_rpc(h)) - return -1; /* XXX */ - else - return xmldb_put_tree_local(h, db, api_path, xt, op); + int retval = -1; + cxobj *x = NULL; + yang_stmt *ys; + yang_spec *yspec; + char *dbfilename = NULL; + + yspec = clicon_dbspec_yang(h); + if (db2file(h, db, &dbfilename) < 0) + goto done; + if (op == OP_REPLACE){ + if (db_delete(dbfilename) < 0) + goto done; + if (db_init(dbfilename) < 0) + goto done; + } + while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){ + if (api_path && strlen(api_path)){ + if (xmldb_put_restconf_api_path(h, db, op, api_path, x) < 0) + goto done; + continue; + } + if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){ + clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x)); + goto done; + } + if (put(dbfilename, /* database name */ + x, /* xml root node */ + ys, /* yang statement of xml node */ + op, /* operation, eg merge/delete */ + "" /* aggregate xml key */ + ) < 0) + goto done; + } + retval = 0; + done: + if (dbfilename) + free(dbfilename); + return retval; } /*! Modify database provided an XML database key and an operation * @param[in] h CLICON handle * @param[in] db Database name - * @param[in] xk XML Key, eg /aa/bb/17/name - * @param[in] val Key value, eg "17" * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc + * @param[in] xk XML Key, eg /aa/bb=17/name + * @param[in] val Key value, eg "17" + * @retval 0 OK * @retval -1 Error - * Local variant of xmldb_put_xkey + * @code + * if (xmldb_put_xkey(h, db, OP_MERGE, "/aa/bb=17/name", "17") < 0) + * err; + * @endcode + * @see xmldb_put with xml-tree, no path */ -static int -xmldb_put_xkey_local(clicon_handle h, - char *db, - char *xk, - char *val, - enum operation_type op) +int +xmldb_put_xkey(clicon_handle h, + char *db, + enum operation_type op, + char *xk, + char *val) + { int retval = -1; cxobj *x = NULL; @@ -1747,34 +1683,6 @@ xmldb_put_xkey_local(clicon_handle h, return retval; } - -/*! Modify database provided an XML database key and an operation - * @param[in] h CLICON handle - * @param[in] db Database name - * @param[in] xk XML Key, eg /aa/bb=17/name - * @param[in] val Key value, eg "17" - * @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc - * @retval 0 OK - * @retval -1 Error - * @code - * if (xmldb_put_xkey(h, db, "/aa/bb/17/name", "17", OP_MERGE) < 0) - * err; - * @endcode - * @see xmldb_put with xml-tree, no path - */ -int -xmldb_put_xkey(clicon_handle h, - char *db, - char *xk, - char *val, - enum operation_type op) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_put_xkey_rpc(h, db, xk, val, op); - else - return xmldb_put_xkey_local(h, db, xk, val, op); -} - /*! Raw dump of database, just keys and values, no xml interpretation * @param[in] f File * @param[in] dbfile File-name of database. This is a local file @@ -1782,7 +1690,7 @@ xmldb_put_xkey(clicon_handle h, * @note This function can only be called locally. */ int -xmldb_dump_local(FILE *f, +xmldb_dump(FILE *f, char *dbfilename, char *rxkey) { @@ -1807,10 +1715,15 @@ xmldb_dump_local(FILE *f, return retval; } - -/*! Local variant of xmldb_copy */ -static int -xmldb_copy_local(clicon_handle h, +/*! Copy database from db1 to db2 + * @param[in] h Clicon handle + * @param[in] from Source database copy + * @param[in] to Destination database + * @retval -1 Error + * @retval 0 OK + */ +int +xmldb_copy(clicon_handle h, char *from, char *to) { @@ -1834,27 +1747,15 @@ xmldb_copy_local(clicon_handle h, return retval; } -/*! Copy database - * @param[in] h Clicon handle - * @param[in] from Source database copy - * @param[in] to Destination database +/*! Lock database + * @param[in] h Clicon handle + * @param[in] db Database + * @param[in] pid Process id * @retval -1 Error * @retval 0 OK */ int -xmldb_copy(clicon_handle h, - char *from, - char *to) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_copy_rpc(h, from, to); - else - return xmldb_copy_local(h, from, to); -} - -/* Local variant of xmldb_lock */ -static int -xmldb_lock_local(clicon_handle h, +xmldb_lock(clicon_handle h, char *db, int pid) { @@ -1873,27 +1774,15 @@ xmldb_lock_local(clicon_handle h, return retval; } -/*! Lock database - * @param[in] h Clicon handle - * @param[in] db Database +/*! Unlock database + * @param[in] h Clicon handle + * @param[in] db Database * @param[in] pid Process id * @retval -1 Error * @retval 0 OK - */ + */ int -xmldb_lock(clicon_handle h, - char *db, - int pid) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_lock_rpc(h, db, pid); - else - return xmldb_lock_local(h, db, pid); -} - -/*! Local variant of xmldb_unlock */ -static int -xmldb_unlock_local(clicon_handle h, +xmldb_unlock(clicon_handle h, char *db, int pid) { @@ -1914,30 +1803,13 @@ xmldb_unlock_local(clicon_handle h, return retval; } -/*! Unlock database - * @param[in] h Clicon handle - * @param[in] db Database - * @param[in] pid Process id - * @retval -1 Error - * @retval 0 OK - */ +/*! Unlock all databases locked by pid (eg process dies) + */ int -xmldb_unlock(clicon_handle h, - char *db, - int pid) +xmldb_unlock_all(clicon_handle h, + int pid) { - if (clicon_xmldb_rpc(h)) - return xmldb_unlock_rpc(h, db, pid); - else - return xmldb_unlock_local(h, db, pid); -} - -/*! Local variant of xmldb_islocked */ -static int -xmldb_islocked_local(clicon_handle h, - char *db) -{ - return db_islocked(db); + return db_unlock_all(pid); } /*! Check if database is locked @@ -1951,16 +1823,19 @@ int xmldb_islocked(clicon_handle h, char *db) { - if (clicon_xmldb_rpc(h)) - return xmldb_islocked_rpc(h, db); - else - return xmldb_islocked_local(h, db); + return db_islocked(db); } -/*! Local variant of xmldb_exists */ -static int -xmldb_exists_local(clicon_handle h, - char *db) +/*! Check if db exists + * @param[in] h Clicon handle + * @param[in] db Database + * @retval -1 Error + * @retval 0 No it does not exist + * @retval 1 Yes it exists + */ +int +xmldb_exists(clicon_handle h, + char *db) { int retval = -1; char *filename = NULL; @@ -1978,25 +1853,15 @@ xmldb_exists_local(clicon_handle h, return retval; } -/*! Check if db exists +/*! Delete database. Remove file + * @param[in] h Clicon handle + * @param[in] db Database * @retval -1 Error - * @retval 0 No it does not exist - * @retval 1 Yes it exists - */ + * @retval 0 OK + */ int -xmldb_exists(clicon_handle h, +xmldb_delete(clicon_handle h, char *db) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_exists_rpc(h, db); - else - return xmldb_exists_local(h, db); -} - -/*! Local variant of xmldb_delete */ -static int -xmldb_delete_local(clicon_handle h, - char *db) { int retval = -1; char *filename = NULL; @@ -2012,23 +1877,15 @@ xmldb_delete_local(clicon_handle h, return retval; } -/*! Delete database. Remove file - * Should not be called from client. Use change("/", OP_REMOVE) instead. +/*! Initialize database + * @param[in] h Clicon handle + * @param[in] db Database + * @retval 0 OK + * @retval -1 Error */ int -xmldb_delete(clicon_handle h, - char *db) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_delete_rpc(h, db); - else - return xmldb_delete_local(h, db); -} - -/*! Local variant of xmldb_init */ -static int -xmldb_init_local(clicon_handle h, - char *db) +xmldb_init(clicon_handle h, + char *db) { int retval = -1; char *filename = NULL; @@ -2044,16 +1901,6 @@ xmldb_init_local(clicon_handle h, return retval; } -/*! Initialize database */ -int -xmldb_init(clicon_handle h, - char *db) -{ - if (clicon_xmldb_rpc(h)) - return xmldb_init_rpc(h, db); - else - return xmldb_init_local(h, db); -} #if 0 /* Test program */ /* @@ -2127,7 +1974,7 @@ main(int argc, char **argv) op = OP_REMOVE; else usage(argv[0]); - if (xmldb_put(h, db, xn, op) < 0) + if (xmldb_put(h, db, op, NULL, xn) < 0) goto done; } else diff --git a/lib/src/clixon_xml_db_rpc.c b/lib/src/clixon_xml_db_rpc.c deleted file mode 100644 index 65121549..00000000 --- a/lib/src/clixon_xml_db_rpc.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * - ***** BEGIN LICENSE BLOCK ***** - - Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren - - This file is part of CLIXON. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Alternatively, the contents of this file may be used under the terms of - the GNU General Public License Version 3 or later (the "GPL"), - in which case the provisions of the GPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of the GPL, and not to allow others to - use your version of this file under the terms of Apache License version 2, - indicate your decision by deleting the provisions above and replace them with - the notice and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the Apache License version 2 or the GPL. - - ***** END LICENSE BLOCK ***** - - * XML database - * TODO: xmldb_del: or dbxml_put_xkey delete - */ -#ifdef HAVE_CONFIG_H -#include "clixon_config.h" /* generated by config & autoconf */ -#endif - -#include -#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_string.h" -#include "clixon_chunk.h" -#include "clixon_hash.h" -#include "clixon_handle.h" -#include "clixon_qdb.h" -#include "clixon_yang.h" -#include "clixon_handle.h" -#include "clixon_yang.h" -#include "clixon_options.h" -#include "clixon_xml.h" -#include "clixon_xsl.h" -#include "clixon_xml_parse.h" -#include "clixon_xml_db.h" -#include "clixon_xml_db_rpc.h" - -/*! Make an rpc call to xmldb daemon - */ -static int -xmldb_rpc(clicon_handle h, - char *data, - size_t len, - char *retdata, - size_t *retlen - ) -{ - int retval = -1; - char *dst; - uint16_t port; - int s = -1; - struct sockaddr_in addr; - - if ((dst = clicon_xmldb_addr(h)) == NULL){ - clicon_err(OE_CFG, errno, "CLICON_XMLDB_ADDR option not set"); - goto done; - } - if ((port = clicon_xmldb_port(h)) == 0){ - clicon_err(OE_CFG, errno, "CLICON_XMLDB_PORT option not set"); - goto done; - } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if (inet_pton(addr.sin_family, dst, &addr.sin_addr) != 1) - goto done; /* Could check getaddrinfo */ - if ((s = socket(addr.sin_family, SOCK_STREAM, 0)) < 0) { - clicon_err(OE_CFG, errno, "socket"); - return -1; - } - if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0){ - clicon_err(OE_CFG, errno, "connecting socket inet4"); - close(s); - goto done; - } - if (write(s, data, len) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - if ((*retlen = read(s, retdata, *retlen)) < 0){ - clicon_err(OE_UNIX, errno, "write"); - goto done; - } - retval = 0; - if (debug > 1) - fprintf(stderr, "%s: \"%s\"\n", __FUNCTION__, retdata); - done: - if (s != -1) - close(s); - return retval; -} - -/*! Send put request to backend daemon - * @param[in] h CLICON handle - * @param[in] db running|candidate - * @retval 0 - */ -int -xmldb_put_rpc(clicon_handle h, - char *db, - cxobj *xt, - enum operation_type op) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - char *opstr; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - if (op){ - switch (op){ - case OP_REPLACE: - opstr = "replace"; - break; - case OP_MERGE: - opstr = "merge"; - break; - case OP_NONE: - default: - opstr = "none"; - break; - } - cprintf(cb, "%s", opstr); - } - cprintf(cb, ""); - if (clicon_xml2cbuf(cb, xt, 0, 1) < 0) - goto done; - cprintf(cb, ""); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Send put xkey request to backend daemon - * @param[in] h CLICON handle - * @param[in] db running|candidate - * @retval 0 - */ -int -xmldb_put_xkey_rpc(clicon_handle h, - char *db, - char *xk, - char *val, - enum operation_type op) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - char *opstr; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - if (op){ - switch (op){ - case OP_REPLACE: - opstr = "replace"; - break; - case OP_MERGE: - opstr = "merge"; - break; - case OP_NONE: - default: - opstr = "none"; - break; - } - cprintf(cb, "%s", opstr); - } - cprintf(cb, "%s", xk); - cprintf(cb, "%s", val); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Send get request to backend daemon - * @param[in] h CLICON handle - * @param[in] db running|candidate - * @retval 0 - */ -int -xmldb_get_rpc(clicon_handle h, - char *db, - char *xpath, - cxobj **xtop, - cxobj ***xvec, - size_t *xlen) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - cxobj *xt=NULL; - cxobj *xc; - int i; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - if (xpath) - cprintf(cb, "%s", xpath); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - if (clicon_xml_parse_str(rb, &xt) < 0) - goto done; - if (xvec){ - i=0; - if ((*xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) ==NULL){ - clicon_err(OE_UNIX, errno, "calloc"); - goto done; - } - xc = NULL; - while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) { - (*xvec)[i++] = xc; - } - *xlen = i; - *xtop = xt; - xt = NULL; - } - else{ - if (xml_rootchild(xt, 0, xtop) < 0) - goto done; - xt = NULL; - } - - retval = 0; - done: - if (xt) - xml_free(xt); - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Copy database - * @param[in] h Clicon handle - * @param[in] from Source database copy - * @param[in] to Destination database - * @retval -1 Error - * @retval 0 OK - */ -int -xmldb_copy_rpc(clicon_handle h, - char *from, - char *to) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - cxobj *xt = NULL; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", from); - cprintf(cb, "<%s/>", to); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - if (clicon_xml_parse_str(rb, &xt) < 0) - goto done; - if (xpath_first(xt, "//ok")) - retval = 0; - done: - if (xt) - xml_free(xt); - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Lock database - * @param[in] h Clicon handle - * @param[in] db Database - * @param[in] pid Process id - * @retval -1 Error - * @retval 0 OK - */ -int -xmldb_lock_rpc(clicon_handle h, - char *db, - int id) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - cxobj *xt = NULL; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - cprintf(cb, "<%u/>", id); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - if (clicon_xml_parse_str(rb, &xt) < 0) - goto done; - if (xpath_first(xt, "//ok")) - retval = 0; - done: - if (xt) - xml_free(xt); - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Unlock database - * @param[in] h Clicon handle - * @param[in] db Database - * @param[in] pid Process id - * @retval -1 Error - * @retval 0 OK - */ -int -xmldb_unlock_rpc(clicon_handle h, - char *db, - int id) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - cxobj *xt = NULL; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - cprintf(cb, "<%u/>", id); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - if (clicon_xml_parse_str(rb, &xt) < 0) - goto done; - if (xpath_first(xt, "//ok")) - retval = 0; - done: - if (xt) - xml_free(xt); - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Check if database is locked - * @param[in] h Clicon handle - * @param[in] db Database - * @retval -1 Error - * @retval pid Process id if locked - */ -int -xmldb_islocked_rpc(clicon_handle h, - char *db) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - cxobj *xt = NULL; - cxobj *x; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - if (clicon_xml_parse_str(rb, &xt) < 0) - goto done; - if (xpath_first(xt, "//unlocked")) - retval = 0; - else - if ((x=xpath_first(xt, "//locked")) != NULL) - retval = atoi(xml_body(x)); - done: - if (xt) - xml_free(xt); - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Send exists request to backend daemon - * @param[in] h CLICON handle - * @param[in] db running|candidate - * @retval -1 Error - * @retval 0 No it does not exist - * @retval 1 Yes it exists - */ -int -xmldb_exists_rpc(clicon_handle h, - char *db) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - cxobj *xt = NULL; - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - if (clicon_xml_parse_str(rb, &xt) < 0) - goto done; - if (xpath_first(xt, "//ok")) - retval = 1; - else - retval = 0; - done: - if (xt) - xml_free(xt); - if (cb) - cbuf_free(cb); - return retval; -} - - -/*! Send delete request to backend daemon - * @param[in] h CLICON handle - * @param[in] db running|candidate - * @retval 0 - */ -int -xmldb_delete_rpc(clicon_handle h, - char *db) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} - -/*! Send init request to backend daemon - * @param[in] h CLICON handle - * @param[in] db running|candidate - * @retval 0 - */ -int -xmldb_init_rpc(clicon_handle h, - char *db) -{ - int retval = -1; - cbuf *cb = NULL; - char retbuf[BUFSIZ]; - char *rb = retbuf; - size_t retlen = sizeof(retbuf); - - if ((cb = cbuf_new()) == NULL) - goto done; - cprintf(cb, ""); - cprintf(cb, "<%s/>", db); - cprintf(cb, "]]>]]>"); - if (xmldb_rpc(h, - cbuf_get(cb), - cbuf_len(cb)+1, - rb, &retlen) < 0) - goto done; - retval = 0; - done: - if (cb) - cbuf_free(cb); - return retval; -} diff --git a/lib/src/clixon_xml_db_rpc.h b/lib/src/clixon_xml_db_rpc.h deleted file mode 100644 index 561c4935..00000000 --- a/lib/src/clixon_xml_db_rpc.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * - ***** BEGIN LICENSE BLOCK ***** - - Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren - - This file is part of CLIXON. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Alternatively, the contents of this file may be used under the terms of - the GNU General Public License Version 3 or later (the "GPL"), - in which case the provisions of the GPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of the GPL, and not to allow others to - use your version of this file under the terms of Apache License version 2, - indicate your decision by deleting the provisions above and replace them with - the notice and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the Apache License version 2 or the GPL. - - ***** END LICENSE BLOCK ***** - - */ -#ifndef _CLIXON_XML_DB_RPC_H_ -#define _CLIXON_XML_DB_RPC_H_ - -/* - * Prototypes - */ -int xmldb_get_rpc(clicon_handle h, char *db, - char *xpath, - cxobj **xtop, cxobj ***xvec, size_t *xlen); -int xmldb_put_rpc(clicon_handle h, char *db, cxobj *xt, enum operation_type op); -int xmldb_put_xkey_rpc(clicon_handle h, char *db, char *xk, char *val, - enum operation_type op); -int xmldb_copy_rpc(clicon_handle h, char *from, char *to); -int xmldb_lock_rpc(clicon_handle h, char *db, int pid); -int xmldb_unlock_rpc(clicon_handle h, char *db, int pid); -int xmldb_islocked_rpc(clicon_handle h, char *db); - -int xmldb_exists_rpc(clicon_handle h, char *db); -int xmldb_delete_rpc(clicon_handle h, char *db); -int xmldb_init_rpc(clicon_handle h, char *db); - -#endif /* _CLIXON_XML_DB_RPC_H_ */ diff --git a/lib/src/clixon_xml_parse.h b/lib/src/clixon_xml_parse.h index 4211379f..05aeccdd 100644 --- a/lib/src/clixon_xml_parse.h +++ b/lib/src/clixon_xml_parse.h @@ -46,6 +46,7 @@ struct xml_parse_yacc_arg{ cxobj *ya_xelement; /* xml active element */ cxobj *ya_xparent; /* xml parent element*/ + int ya_skipspace; /* If set, translate successive space, \t \n with single space */ }; extern char *clixon_xml_parsetext; diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index d668f70c..c2923de4 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -100,7 +100,8 @@ xml_attr_new(struct xml_parse_yacc_arg *ya, there may also be some leakage here on NULL return */ static int -xml_parse_content(struct xml_parse_yacc_arg *ya, char *str) +xml_parse_content(struct xml_parse_yacc_arg *ya, + char *str) { int sz; char s0; @@ -112,14 +113,14 @@ xml_parse_content(struct xml_parse_yacc_arg *ya, char *str) s0 = str[0]; if (xn != NULL){ sz = strlen(xml_value(xn)); - if (s0 == ' ' || s0 == '\n' || s0 == '\t'){ + if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t')){ str[0] = ' '; if (xml_value(xn)[sz-1] == ' ') goto ok; } } else{ - if (s0 == ' ' || s0 == '\n' || s0 == '\t') + if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t')) goto ok; if ((xn = xml_new("body", xp)) == NULL) goto done; diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 87861328..48b14017 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -552,12 +552,6 @@ xpath_expr(char *predicate_expression, * @param[in] flags if != 0, only match xml nodes matching flags * @param[out] vec2 Result XML node vector * @param[out] vec2len Length of result vector. - * XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1 - * vec0 --> vec - * Det är nog bra om vec0 inte ändras, är input parameter - * Vid utgång ska vec1 innehålla resultatet. - * Internt då? - * XXX: hantering av (input)vec0-->vec-->vec2-->vec1 (resultat) */ static int xpath_find(struct xpath_element *xe, @@ -767,6 +761,7 @@ xpath_choice(cxobj *xtop, cxobj **vec0 = NULL; size_t vec0len = 0; + if ((s0 = strdup(xpath0)) == NULL){ clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); goto done; diff --git a/test/all.sh b/test/all.sh index 2409c238..233438ea 100755 --- a/test/all.sh +++ b/test/all.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # include err() and new() functions . ./lib.sh diff --git a/test/lib.sh b/test/lib.sh index 99040a00..f5ee47dc 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash testnr=0 testnname= @@ -32,7 +32,7 @@ expectfn(){ fi # grep extended grep match=`echo "$ret" | grep -Eo "$expect"` -# echo "ret:$ret" +# echo "ret:<$ret>" # echo "expect:$expect" # echo "match:$match" if [ -z "$match" ]; then @@ -57,7 +57,25 @@ EOF fi match=`echo "$ret" | grep -Eo "$expect"` if [ -z "$match" ]; then - err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\"" + err " fi } +# clicon_cli tester. First arg is command and second is expected outcome +expectwait(){ + cmd=$1 + input=$2 + expect=$3 + wait=$4 + +# Do while read stuff + sleep 10|cat <(echo $input) -| $cmd | while [ 1 ] ; do + read ret + match=$(echo "$ret" | grep -Eo "$expect"); + if [ -z "$match" ]; then + err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\"" + fi + break + done +} + diff --git a/test/test1.sh b/test/test1.sh index 9143732b..c5a1fbe7 100755 --- a/test/test1.sh +++ b/test/test1.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Test1: backend and cli basic functionality # Start backend server # Add an ethernet interface and an address @@ -18,7 +18,7 @@ if [ $? -ne 0 ]; then fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf -x 0 # -x 1 with xmldb proxy +sudo clixon_backend -If $clixon_cf if [ $? -ne 0 ]; then err fi @@ -30,7 +30,7 @@ expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name e interfaces interface enabled true$" new "cli failed validate" -expectfn "clixon_cli -1f $clixon_cf -l o validate" "Validate failed" +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" "" @@ -39,6 +39,25 @@ expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" "" new "cli commit" expectfn "clixon_cli -1f $clixon_cf -l o commit" "" +new "cli save" +expectfn "clixon_cli -1f $clixon_cf -l o save /tmp/foo" "" + +new "cli delete all" +expectfn "clixon_cli -1f $clixon_cf -l o delete all" "" + +new "cli load" +expectfn "clixon_cli -1f $clixon_cf -l o load /tmp/foo" "" + +new "cli check load" +expectfn "clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0 +interfaces interface enabled true$" + +new "cli debug" +expectfn "clixon_cli -1f $clixon_cf -l o debug level 1" "" + +new "cli downcall" +expectfn "clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$" + new "Kill backend" # Check if still alive pid=`pgrep clixon_backend` diff --git a/test/test2.sh b/test/test2.sh index c640c28d..93444e98 100755 --- a/test/test2.sh +++ b/test/test2.sh @@ -1,11 +1,5 @@ -#!/bin/sh -# Test1: backend and cli basic functionality -# Start backend server -# Add an ethernet interface and an address -# Show configuration -# Validate without a mandatory type -# Set the mandatory type -# Commit +#!/bin/bash +# Test2: backend and netconf basic functionality # include err() and new() functions . ./lib.sh @@ -18,15 +12,66 @@ if [ $? -ne 0 ]; then fi new "start backend" # start new backend -sudo clixon_backend -If $clixon_cf -x 0 # -x 1 with xmldb proxy +sudo clixon_backend -If $clixon_cf if [ $? -ne 0 ]; then err fi -new "netconf show config" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "]]>]]>" +new "netconf get empty config" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" -new "netconf lock" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "]]>]]>" +new "netconf edit config" +expecteof "clixon_netconf -qf $clixon_cf" "eth0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" + +new "netconf get config xpath" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^true]]>]]>$" + +new "netconf validate missing type" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^" + +new "netconf discard-changes" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf get empty config2" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf edit config patch" +expecteof "clixon_netconf -qf $clixon_cf" "eth1eth]]>]]>" "^]]>]]>$" + +new "netconf validate" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf commit" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf lock/unlock" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" + +new "netconf lock/lock" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf lock" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "close-session" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "kill-session" +expecteof "clixon_netconf -qf $clixon_cf" "44]]>]]>" "^]]>]]>$" + +new "copy startup" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf get startup" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^eth1ethtrue]]>]]>$" + +new "netconf delete startup" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf check empty startup" +expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + +new "netconf subscription" +expectwait "clixon_netconf -qf $clixon_cf" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 new "Kill backend" # Check if still alive @@ -39,4 +84,3 @@ sudo clixon_backend -zf $clixon_cf if [ $? -ne 0 ]; then err "kill backend" fi - diff --git a/test/test3.sh b/test/test3.sh new file mode 100755 index 00000000..b106bfdb --- /dev/null +++ b/test/test3.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Test3: backend and restconf basic functionality + +# include err() and new() functions +. ./lib.sh + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $clixon_cf +if [ $? -ne 0 ]; then + err +fi +new "start backend" +sudo clixon_backend -If $clixon_cf +if [ $? -ne 0 ]; then + err +fi + +new "kill old restconf daemon" +sudo pkill -u www-data clixon_restconf + +new "start restconf daemon" +sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df /usr/local/etc/routing.conf # -D + +sleep 1 + +new "restconf get empty config" +expectfn "curl -sG http://localhost/restconf/data" "^null $" + +new "restconf put config" +expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth1","type":"eth","enabled":"true"},{"name":"eth0","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' "" + +new "restconf get config" +expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth0","type": "eth","enabled": "true"}\]}} +$' + +new "restconf patch config" +expectfn 'curl -sX POST -d {"type":"type"} http://localhost/restconf/data/interfaces/interface=eth4' "" + +new "restconf delete config" +expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' "" + +new "restconf get config" +expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}} +$' + +new "Kill restconf daemon" +sudo pkill -u www-data clixon_restconf + +new "Kill backend" +# Check if still alive +pid=`pgrep clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -zf $clixon_cf +if [ $? -ne 0 ]; then + err "kill backend" +fi From eec5896797ad82e44758874ed582a2dc5dfb04b3 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sat, 25 Mar 2017 18:24:52 +0100 Subject: [PATCH 02/13] 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" "" From 19813a4d9c746c0f93465d8d50f7304eaf8e8138 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 26 Mar 2017 15:14:37 +0200 Subject: [PATCH 03/13] restconf post/put/head/patch, memtests --- apps/backend/backend_client.c | 2 - apps/backend/clixon_backend_handle.c | 2 +- apps/cli/cli_show.c | 2 - apps/netconf/netconf_main.c | 8 +- apps/restconf/Makefile.in | 1 + apps/restconf/restconf_lib.c | 33 ++ apps/restconf/restconf_lib.h | 35 +- apps/restconf/restconf_main.c | 343 +++--------------- apps/restconf/restconf_methods.c | 513 +++++++++++++++++++++++++++ apps/restconf/restconf_methods.h | 63 ++++ example/routing_backend.c | 12 +- lib/src/clixon_proto_client.c | 1 + lib/src/clixon_xml_db.c | 1 - lib/src/clixon_xsl.c | 26 +- test/test1.sh | 48 +-- test/test2.sh | 44 +-- test/test3.sh | 13 +- 17 files changed, 782 insertions(+), 365 deletions(-) create mode 100644 apps/restconf/restconf_methods.c create mode 100644 apps/restconf/restconf_methods.h diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index a4a3e651..41d36a86 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -85,7 +85,6 @@ client_subscription_add(struct client_entry *ce, { struct client_subscription *su = NULL; - fprintf(stderr, "%s stream:%s filter:%s\n", __FUNCTION__, stream, filter); if ((su = malloc(sizeof(*su))) == NULL){ clicon_err(OE_PLUGIN, errno, "malloc"); goto done; @@ -118,7 +117,6 @@ client_subscription_delete(struct client_entry *ce, struct client_subscription *su; struct client_subscription **su_prev; - fprintf(stderr, "%s stream:%s\n", __FUNCTION__, su0->su_stream); su_prev = &ce->ce_subscription; /* this points to stack and is not real backpointer */ for (su = *su_prev; su; su = su->su_next){ if (su == su0){ diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index e5308886..a795d7b0 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -441,7 +441,7 @@ backend_netconf_register_callback(clicon_handle h, memset (nr, 0, sizeof (*nr)); nr->nr_callback = cb; nr->nr_arg = arg; - nr->nr_tag = strdup(tag); /* strdup */ + nr->nr_tag = strdup(tag); /* XXX strdup memleak */ INSQ(nr, deps); return 0; catch: diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 3f80310a..561ed85e 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -614,8 +614,6 @@ show_confv_as_command(clicon_handle h, enum genmodel_type gt; int retval = -1; - if ((xt = xml_new("tmp", NULL)) == NULL) - goto done; if (show_confv_as(h, cvv, argv, &xt) < 0) goto done; xc = NULL; /* Dont print xt itself */ diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index cfdda1b4..e585a273 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -178,7 +178,8 @@ netconf_input_cb(int s, unsigned char buf[BUFSIZ]; int i; int len; - static cbuf *cb; /* XXX: should use ce state? */ + // static cbuf *cb; /* XXX: should use ce state? */ + cbuf *cb=NULL; /* XXX: should use ce state? */ int xml_state = 0; int retval = -1; @@ -221,7 +222,8 @@ netconf_input_cb(int s, } retval = 0; done: - // cbuf_free(cb); + if (cb) + cbuf_free(cb); if (cc_closed) retval = -1; return retval; @@ -257,9 +259,11 @@ netconf_terminate(clicon_handle h) { yang_spec *yspec; + clicon_rpc_close_session(h); if ((yspec = clicon_dbspec_yang(h)) != NULL) yspec_free(yspec); + event_exit(); clicon_handle_exit(h); return 0; } diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in index 54c80bcf..ddc845d0 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -62,6 +62,7 @@ CPPFLAGS = @CPPFLAGS@ INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ SRC = restconf_lib.c +SRC += restconf_methods.c OBJS = $(SRC:.c=.o) diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index 1aaa802b..adda4275 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -1,6 +1,39 @@ /* + * + ***** 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 ***** + */ + #include #include #include diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index 548e30e1..0a1bdd69 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -1,9 +1,36 @@ /* * - * $COPYRIGHTSTATEMENT$ - * - * $LICENSE$ - * This is backend headend sender code, ie communication with a pmagent + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + */ #ifndef _RESTCONF_LIB_H_ diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 514b0daf..043d6c63 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -66,7 +66,9 @@ /* clicon */ #include +/* restconf */ #include "restconf_lib.h" +#include "restconf_methods.h" /* Command line options to be passed to getopt(3) */ #define RESTCONF_OPTS "hDf:p:" @@ -75,301 +77,6 @@ resource ([RFC6415]) */ #define RESTCONF_API_ROOT "/restconf/" -/*! REST OPTIONS method - * According to restconf (Sec 3.5.1.1 in [draft]) - * @param[in] h Clixon handle - * @param[in] r Fastcgi request handle - * @param[in] pcvec Vector of path ie DOCUMENT_URI element - * @param[in] pi Offset, where path starts - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] head Set if HEAD request instead of GET - * @code - * curl -G http://localhost/restconf/data/interfaces/interface=eth0 - * @endcode - */ -static int -api_data_options(clicon_handle h, - FCGX_Request *r, - cvec *pcvec, - int pi, - cvec *qvec, - int head) -{ - int retval = -1; - - FCGX_SetExitStatus(200, r->out); /* OK */ - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - FCGX_FPrintF(r->out, "GET, HEAD, OPTIONS, PUT, POST, DELETE\r\n"); - retval = 0; - return retval; -} - -/*! Generic REST GET method - * According to restconf (Sec 3.5.1.1 in [draft]) - * @param[in] h Clixon handle - * @param[in] r Fastcgi request handle - * @param[in] pcvec Vector of path ie DOCUMENT_URI element - * @param[in] pi Offset, where path starts - * @param[in] qvec Vector of query string (QUERY_STRING) - * @code - * curl -G http://localhost/restconf/data/interfaces/interface=eth0 - * @endcode - * XXX: cant find a way to use Accept request field to choose Content-Type - * I would like to support both xml and json. - * Request may contain - * Accept: application/yang.data+json,application/yang.data+xml - * Response contains one of: - * Content-Type: application/yang.data+xml - * Content-Type: application/yang.data+json - * NOTE: If a retrieval request for a data resource representing a YANG leaf- - * list or list object identifies more than one instance, and XML - * encoding is used in the response, then an error response containing a - * "400 Bad Request" status-line MUST be returned by the server. - */ -static int -api_data_get(clicon_handle h, - FCGX_Request *r, - cvec *pcvec, - int pi, - cvec *qvec) -{ - int retval = -1; - cg_var *cv; - char *val; - char *v; - int i; - cbuf *path = NULL; - cbuf *path1 = NULL; - cbuf *cbx = NULL; - cxobj **vec = NULL; - yang_spec *yspec; - yang_stmt *y; - yang_stmt *ykey; - char *name; - cvec *cvk = NULL; /* vector of index keys */ - cg_var *cvi; - cxobj *xret = NULL; - - clicon_debug(1, "%s", __FUNCTION__); - yspec = clicon_dbspec_yang(h); - if ((path = cbuf_new()) == NULL) - goto done; - if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */ - goto done; - cv = NULL; - cprintf(path1, "/"); - /* translate eg a/b=c -> a/[b=c] */ - for (i=pi; iys_argument); - notfound(r); - goto done; - } - clicon_debug(1, "ykey:%s", ykey->ys_argument); - - /* The value is a list of keys: [ ]* */ - if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) - goto done; - cvi = NULL; - /* Iterate over individual yang keys */ - cprintf(path, "/%s", name); - v = val; - while ((cvi = cvec_each(cvk, cvi)) != NULL){ - cprintf(path, "[%s=%s]", cv_string_get(cvi), v); - v += strlen(v)+1; - } - if (val) - free(val); - } - else{ - cprintf(path, "%s%s", (i==pi?"":"/"), name); - cprintf(path1, "/%s", name); - } - } - clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path)); - if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){ - notfound(r); - goto done; - } - if ((cbx = cbuf_new()) == NULL) - goto done; - FCGX_SetExitStatus(200, r->out); /* OK */ - FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret)); - vec = xml_childvec_get(xret); - if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0) - goto done; - - clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx)); - FCGX_FPrintF(r->out, "%s", cbuf_get(cbx)); - FCGX_FPrintF(r->out, "\r\n\r\n"); - retval = 0; - done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - if (cbx) - cbuf_free(cbx); - if (path) - cbuf_free(path); - if (path1) - cbuf_free(path1); - if (xret) - xml_free(xret); - return retval; -} - -/*! Generic REST DELETE method - * @param[in] h CLIXON handle - * @param[in] r Fastcgi request handle - * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) - * @param[in] pi Offset, where path starts - * Example: - * curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0 - */ -static int -api_data_delete(clicon_handle h, - FCGX_Request *r, - char *api_path, - int pi) -{ - int retval = -1; - int i; - - clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); - for (i=0; i") < 0){ - notfound(r); - goto done; - } - if (clicon_rpc_commit(h) < 0) - goto done; - FCGX_SetExitStatus(201, r->out); - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - retval = 0; - done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - return retval; -} - -/*! Generic REST PUT method - * @param[in] h CLIXON handle - * @param[in] r Fastcgi request handle - * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) - * @param[in] pcvec Vector of path ie DOCUMENT_URI element - * @param[in] pi Offset, where to start pcvec - * @param[in] qvec Vector of query string (QUERY_STRING) - * @param[in] dvec Stream input data - * @param[in] post POST instead of PUT - * Example: - curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1 - * - PUT: - if the PUT request creates a new resource, - a "201 Created" status-line is returned. If an existing resource is - modified, a "204 No Content" status-line is returned. - - POST: - If the POST method succeeds, a "201 Created" status-line is returned - and there is no response message-body. A "Location" header - identifying the child resource that was created MUST be present in - the response in this case. - - If the data resource already exists, then the POST request MUST fail - and a "409 Conflict" status-line MUST be returned. - */ -static int -api_data_put(clicon_handle h, - FCGX_Request *r, - char *api_path, - cvec *pcvec, - int pi, - cvec *qvec, - char *data, - int post) -{ - int retval = -1; - int i; - cxobj *xdata = NULL; - cbuf *cbx = NULL; - cxobj *x; - - clicon_debug(1, "%s api_path:%s json:%s", - __FUNCTION__, - api_path, data); - for (i=0; i"); - x = NULL; - while ((x = xml_child_each(xdata, x, -1)) != NULL) { - if (clicon_xml2cbuf(cbx, x, 0, 0) < 0) - goto done; - } - cprintf(cbx, ""); - clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); - if (clicon_rpc_edit_config(h, "candidate", - OP_MERGE, - api_path, - cbuf_get(cbx)) < 0){ - notfound(r); - goto done; - } - - if (clicon_rpc_commit(h) < 0) - goto done; - FCGX_SetExitStatus(201, r->out); /* Created */ - FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); - FCGX_FPrintF(r->out, "\r\n"); - retval = 0; - done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); - if (xdata) - xml_free(xdata); - if (cbx) - cbuf_free(cbx); - return retval; -} - /*! Generic REST method, GET, PUT, DELETE * @param[in] h CLIXON handle * @param[in] r Fastcgi request handle @@ -393,14 +100,17 @@ api_data(clicon_handle h, clicon_debug(1, "%s", __FUNCTION__); request_method = FCGX_GetParam("REQUEST_METHOD", r->envp); + clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); if (strcmp(request_method, "OPTIONS")==0) - retval = api_data_options(h, r, pcvec, pi, qvec, 0); + retval = api_data_options(h, r); + else if (strcmp(request_method, "HEAD")==0) + retval = api_data_head(h, r, pcvec, pi, qvec); else if (strcmp(request_method, "GET")==0) retval = api_data_get(h, r, pcvec, pi, qvec); - else if (strcmp(request_method, "PUT")==0) - retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 0); else if (strcmp(request_method, "POST")==0) - retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 1); + retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data); + else if (strcmp(request_method, "PUT")==0) + retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data); else if (strcmp(request_method, "DELETE")==0) retval = api_data_delete(h, r, api_path, pi); else @@ -470,7 +180,7 @@ request_process(clicon_handle h, else retval = notfound(r); done: - clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + clicon_debug(1, "%s retval:%d K", __FUNCTION__, retval); if (dvec) cvec_free(dvec); if (qvec) @@ -488,6 +198,7 @@ restconf_terminate(clicon_handle h) { yang_spec *yspec; + clicon_debug(0, "%s", __FUNCTION__); clicon_rpc_close_session(h); if ((yspec = clicon_dbspec_yang(h)) != NULL) yspec_free(yspec); @@ -495,6 +206,27 @@ restconf_terminate(clicon_handle h) return 0; } +/* Need global variable to for signal handler */ +static clicon_handle _CLICON_HANDLE = NULL; + +/*! Signall terminates process + */ +static void +restconf_sig_term(int arg) +{ + static int i=0; + + if (i++ == 0) + clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d", + __PROGRAM__, __FUNCTION__, getpid(), arg); + else + exit(-1); + if (_CLICON_HANDLE) + restconf_terminate(_CLICON_HANDLE); + clicon_exit_set(); /* checked in event_loop() */ + exit(-1); +} + /*! Usage help routine * @param[in] argv0 command line * @param[in] h Clicon handle @@ -538,7 +270,7 @@ main(int argc, /* Create handle */ if ((h = clicon_handle_init()) == NULL) goto done; - + _CLICON_HANDLE = h; /* for termination handling */ while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1) switch (c) { case 'h': @@ -566,6 +298,15 @@ main(int argc, clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, CLICON_LOG_SYSLOG); clicon_debug_init(debug, NULL); + clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid()); + if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){ + clicon_err(OE_DEMON, errno, "Setting signal"); + goto done; + } + if (set_signal(SIGINT, restconf_sig_term, NULL) < 0){ + clicon_err(OE_DEMON, errno, "Setting signal"); + goto done; + } /* Find and read configfile */ if (clicon_options_main(h) < 0) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c new file mode 100644 index 00000000..90f4c6ff --- /dev/null +++ b/apps/restconf/restconf_methods.c @@ -0,0 +1,513 @@ +/* + * + ***** 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 ***** + + */ + +/* + * See draft-ietf-netconf-restconf-13.txt [draft] + * See draft-ietf-netconf-restconf-17.txt [draft] + + * sudo apt-get install libfcgi-dev + * gcc -o fastcgi fastcgi.c -lfcgi + + * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data + + * This is the interface: + * api/data/profile=/metric= PUT data:enable= + * api/test + +----------------------------+--------------------------------------+ + | 100 Continue | POST accepted, 201 should follow | + | 200 OK | Success with response message-body | + | 201 Created | POST to create a resource success | + | 204 No Content | Success without response message- | + | | body | + | 304 Not Modified | Conditional operation not done | + | 400 Bad Request | Invalid request message | + | 401 Unauthorized | Client cannot be authenticated | + | 403 Forbidden | Access to resource denied | + | 404 Not Found | Resource target or resource node not | + | | found | + | 405 Method Not Allowed | Method not allowed for target | + | | resource | + | 409 Conflict | Resource or lock in use | + | 412 Precondition Failed | Conditional method is false | + | 413 Request Entity Too | too-big error | + | Large | | + | 414 Request-URI Too Large | too-big error | + | 415 Unsupported Media Type | non RESTCONF media type | + | 500 Internal Server Error | operation-failed | + | 501 Not Implemented | unknown-operation | + | 503 Service Unavailable | Recoverable server error | + +----------------------------+--------------------------------------+ +Mapping netconf error-tag -> status code + +-------------------------+-------------+ + | | status code | + +-------------------------+-------------+ + | in-use | 409 | + | invalid-value | 400 | + | too-big | 413 | + | missing-attribute | 400 | + | bad-attribute | 400 | + | unknown-attribute | 400 | + | bad-element | 400 | + | unknown-element | 400 | + | unknown-namespace | 400 | + | access-denied | 403 | + | lock-denied | 409 | + | resource-denied | 409 | + | rollback-failed | 500 | + | data-exists | 409 | + | data-missing | 409 | + | operation-not-supported | 501 | + | operation-failed | 500 | + | partial-operation | 500 | + | malformed-message | 400 | + +-------------------------+-------------+ + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* cligen */ +#include + +/* clicon */ +#include + +#include "restconf_lib.h" +#include "restconf_methods.h" + +/*! REST OPTIONS method + * According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] h Clixon handle + * @param[in] r Fastcgi request handle + * @code + * curl -G http://localhost/restconf/data/interfaces/interface=eth0 + * @endcode + * Minimal support: + * 200 OK + * Allow: HEAD,GET,PUT,DELETE,OPTIONS + */ +int +api_data_options(clicon_handle h, + FCGX_Request *r) +{ + clicon_debug(1, "%s", __FUNCTION__); + FCGX_SetExitStatus(200, r->out); /* OK */ + FCGX_FPrintF(r->out, "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE\r\n"); + FCGX_FPrintF(r->out, "\r\n"); + return 0; +} + +/*! Generic GET (both HEAD and GET) + */ +static int +api_data_get_gen(clicon_handle h, + FCGX_Request *r, + cvec *pcvec, + int pi, + cvec *qvec, + int head) +{ + int retval = -1; + cg_var *cv; + char *val; + char *v; + int i; + cbuf *path = NULL; + cbuf *path1 = NULL; + cbuf *cbx = NULL; + cxobj **vec = NULL; + yang_spec *yspec; + yang_stmt *y; + yang_stmt *ykey; + char *name; + cvec *cvk = NULL; /* vector of index keys */ + cg_var *cvi; + cxobj *xret = NULL; + + clicon_debug(1, "%s", __FUNCTION__); + yspec = clicon_dbspec_yang(h); + if ((path = cbuf_new()) == NULL) + goto done; + if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */ + goto done; + cv = NULL; + cprintf(path1, "/"); + /* translate eg a/b=c -> a/[b=c] */ + for (i=pi; iys_argument); + notfound(r); + goto done; + } + clicon_debug(1, "ykey:%s", ykey->ys_argument); + + /* The value is a list of keys: [ ]* */ + if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) + goto done; + cvi = NULL; + /* Iterate over individual yang keys */ + cprintf(path, "/%s", name); + v = val; + while ((cvi = cvec_each(cvk, cvi)) != NULL){ + cprintf(path, "[%s=%s]", cv_string_get(cvi), v); + v += strlen(v)+1; + } + if (val) + free(val); + } + else{ + cprintf(path, "%s%s", (i==pi?"":"/"), name); + cprintf(path1, "/%s", name); + } + } + clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path)); + if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){ + notfound(r); + goto done; + } + { + cbuf *cb = cbuf_new(); + clicon_xml2cbuf(cb, xret, 0, 0); + clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb)); + cbuf_free(cb); + } + if ((cbx = cbuf_new()) == NULL) + goto done; + FCGX_SetExitStatus(200, r->out); /* OK */ + FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n"); + FCGX_FPrintF(r->out, "\r\n"); + if (head) + goto ok; + clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret)); + vec = xml_childvec_get(xret); + clicon_debug(1, "%s xretnr:%d", __FUNCTION__, xml_child_nr(xret)); + if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0) + goto done; + clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx)); + FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):""); + FCGX_FPrintF(r->out, "\r\n\r\n"); + ok: + retval = 0; + done: + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (cbx) + cbuf_free(cbx); + if (path) + cbuf_free(path); + if (path1) + cbuf_free(path1); + if (xret) + xml_free(xret); + return retval; +} + +/*! REST HEAD method + * @param[in] h Clixon handle + * @param[in] r Fastcgi request handle + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where path starts + * @param[in] qvec Vector of query string (QUERY_STRING) + The HEAD method is sent by the client to retrieve just the header fields + that would be returned for the comparable GET method, without the + response message-body. + * Relation to netconf: none + */ +int +api_data_head(clicon_handle h, + FCGX_Request *r, + cvec *pcvec, + int pi, + cvec *qvec) +{ + return api_data_get_gen(h, r, pcvec, pi, qvec, 1); +} + +/*! REST GET method + * According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] h Clixon handle + * @param[in] r Fastcgi request handle + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where path starts + * @param[in] qvec Vector of query string (QUERY_STRING) + * @code + * curl -G http://localhost/restconf/data/interfaces/interface=eth0 + * @endcode + * XXX: cant find a way to use Accept request field to choose Content-Type + * I would like to support both xml and json. + * Request may contain + * Accept: application/yang.data+json,application/yang.data+xml + * Response contains one of: + * Content-Type: application/yang.data+xml + * Content-Type: application/yang.data+json + * NOTE: If a retrieval request for a data resource representing a YANG leaf- + * list or list object identifies more than one instance, and XML + * encoding is used in the response, then an error response containing a + * "400 Bad Request" status-line MUST be returned by the server. + * Netconf: , + */ +int +api_data_get(clicon_handle h, + FCGX_Request *r, + cvec *pcvec, + int pi, + cvec *qvec) +{ + return api_data_get_gen(h, r, pcvec, pi, qvec, 0); +} + +/*! Generic edit-config method: PUT/POST/PATCH + */ +static int +api_data_edit(clicon_handle h, + FCGX_Request *r, + char *api_path, + cvec *pcvec, + int pi, + cvec *qvec, + char *data, + enum operation_type operation) +{ + int retval = -1; + int i; + cxobj *xdata = NULL; + cbuf *cbx = NULL; + cxobj *x; + + clicon_debug(1, "%s api_path:%s json:%s", + __FUNCTION__, + api_path, data); + for (i=0; i"); + x = NULL; + while ((x = xml_child_each(xdata, x, -1)) != NULL) { + if (clicon_xml2cbuf(cbx, x, 0, 0) < 0) + goto done; + } + cprintf(cbx, ""); + clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path); + if (clicon_rpc_edit_config(h, "candidate", + operation, + api_path, + cbuf_get(cbx)) < 0){ + notfound(r); + goto done; + } + + if (clicon_rpc_commit(h) < 0) + goto done; + FCGX_SetExitStatus(201, r->out); /* Created */ + FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); + FCGX_FPrintF(r->out, "\r\n"); + retval = 0; + done: + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + if (xdata) + xml_free(xdata); + if (cbx) + cbuf_free(cbx); + return retval; +} + + +/*! REST POST method + * @param[in] h CLIXON handle + * @param[in] r Fastcgi request handle + * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where to start pcvec + * @param[in] qvec Vector of query string (QUERY_STRING) + * @param[in] data Stream input data + POST: + If the POST method succeeds, a "201 Created" status-line is returned + and there is no response message-body. A "Location" header + identifying the child resource that was created MUST be present in + the response in this case. + + If the data resource already exists, then the POST request MUST fail + and a "409 Conflict" status-line MUST be returned. + * Netconf: (nc:operation="create") | invoke an RPC operation + */ +int +api_data_post(clicon_handle h, + FCGX_Request *r, + char *api_path, + cvec *pcvec, + int pi, + cvec *qvec, + char *data) +{ + return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_CREATE); +} + +/*! Generic REST PUT method + * @param[in] h CLIXON handle + * @param[in] r Fastcgi request handle + * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where to start pcvec + * @param[in] qvec Vector of query string (QUERY_STRING) + * @param[in] data Stream input data + * Example: + curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1 + * + PUT: + if the PUT request creates a new resource, + a "201 Created" status-line is returned. If an existing resource is + modified, a "204 No Content" status-line is returned. + + * Netconf: (nc:operation="create/replace") + */ +int +api_data_put(clicon_handle h, + FCGX_Request *r, + char *api_path, + cvec *pcvec, + int pi, + cvec *qvec, + char *data) +{ + /* XXX: OP_CREATE? */ + return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_REPLACE); +} + +/*! Generic REST PATCH method + * @param[in] h CLIXON handle + * @param[in] r Fastcgi request handle + * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] pcvec Vector of path ie DOCUMENT_URI element + * @param[in] pi Offset, where to start pcvec + * @param[in] qvec Vector of query string (QUERY_STRING) + * @param[in] data Stream input data + * Netconf: (nc:operation="merge") + */ +int +api_data_patch(clicon_handle h, + FCGX_Request *r, + char *api_path, + cvec *pcvec, + int pi, + cvec *qvec, + char *data) +{ + return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_MERGE); +} + +/*! Generic REST DELETE method + * @param[in] h CLIXON handle + * @param[in] r Fastcgi request handle + * @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft]) + * @param[in] pi Offset, where path starts + * Example: + * curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0 + * Netconf: (nc:operation="delete") + */ +int +api_data_delete(clicon_handle h, + FCGX_Request *r, + char *api_path, + int pi) +{ + int retval = -1; + int i; + + clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path); + for (i=0; i") < 0){ + notfound(r); + goto done; + } + if (clicon_rpc_commit(h) < 0) + goto done; + FCGX_SetExitStatus(201, r->out); + FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n"); + FCGX_FPrintF(r->out, "\r\n"); + retval = 0; + done: + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); + return retval; +} + diff --git a/apps/restconf/restconf_methods.h b/apps/restconf/restconf_methods.h new file mode 100644 index 00000000..f6946f8a --- /dev/null +++ b/apps/restconf/restconf_methods.h @@ -0,0 +1,63 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + */ + + +#ifndef _RESTCONF_METHODS_H_ +#define _RESTCONF_METHODS_H_ + +/* + * Constants + */ + +/* + * Prototypes + */ +int api_data_options(clicon_handle h, FCGX_Request *r); +int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi, + cvec *qvec); +int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi, + cvec *qvec); +int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path, + cvec *pcvec, int pi, + cvec *qvec, char *data); +int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path, + cvec *pcvec, int pi, + cvec *qvec, char *data); +int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path, + cvec *pcvec, int pi, + cvec *qvec, char *data); +int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi); + +#endif /* _RESTCONF_METHODS_H_ */ diff --git a/example/routing_backend.c b/example/routing_backend.c index 59bac481..34e6fc80 100644 --- a/example/routing_backend.c +++ b/example/routing_backend.c @@ -62,7 +62,7 @@ int transaction_validate(clicon_handle h, transaction_data td) { - transaction_print(stderr, td); + // transaction_print(stderr, td); return 0; } @@ -73,16 +73,18 @@ transaction_commit(clicon_handle h, transaction_data td) { cxobj *target = transaction_target(td); /* wanted XML tree */ - cxobj **vec; + cxobj **vec = NULL; int i; size_t len; /* Get all added i/fs */ if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0) return -1; - for (i=0; imydesc" +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" +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" "" +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" "" +expectfn "$clixon_cli -1f $clixon_cf -l o validate" "" new "cli commit" -expectfn "clixon_cli -1f $clixon_cf -l o commit" "" +expectfn "$clixon_cli -1f $clixon_cf -l o commit" "" new "cli save" -expectfn "clixon_cli -1f $clixon_cf -l o save /tmp/foo" "" +expectfn "$clixon_cli -1f $clixon_cf -l o save /tmp/foo" "" new "cli delete all" -expectfn "clixon_cli -1f $clixon_cf -l o delete all" "" +expectfn "$clixon_cli -1f $clixon_cf -l o delete all" "" new "cli load" -expectfn "clixon_cli -1f $clixon_cf -l o load /tmp/foo" "" +expectfn "$clixon_cli -1f $clixon_cf -l o load /tmp/foo" "" new "cli check load" -expectfn "clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0 +expectfn "$clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0 interfaces interface enabled true$" new "cli debug" -expectfn "clixon_cli -1f $clixon_cf -l o debug level 1" "" +expectfn "$clixon_cli -1f $clixon_cf -l o debug level 1" "" +# How to test this? +expectfn "$clixon_cli -1f $clixon_cf -l o debug level 0" "" new "cli downcall" -expectfn "clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$" +expectfn "$clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$" new "Kill backend" # Check if still alive diff --git a/test/test2.sh b/test/test2.sh index 93444e98..f23b070d 100755 --- a/test/test2.sh +++ b/test/test2.sh @@ -4,6 +4,10 @@ # include err() and new() functions . ./lib.sh +# For memcheck +# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" +clixon_netconf=clixon_netconf + # kill old backend (if any) new "kill old backend" sudo clixon_backend -zf $clixon_cf @@ -17,61 +21,61 @@ if [ $? -ne 0 ]; then err fi new "netconf get empty config" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "valgrind $clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf edit config" -expecteof "clixon_netconf -qf $clixon_cf" "eth0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "eth0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" new "netconf get config xpath" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^true]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^true]]>]]>$" new "netconf validate missing type" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^" new "netconf discard-changes" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf get empty config2" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" -new "netconf edit config patch" -expecteof "clixon_netconf -qf $clixon_cf" "eth1eth]]>]]>" "^]]>]]>$" +new "netconf edit config eth1" +expecteof "$clixon_netconf -qf $clixon_cf" "eth1eth]]>]]>" "^]]>]]>$" new "netconf validate" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf commit" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf lock/unlock" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" new "netconf lock/lock" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf lock" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "close-session" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "kill-session" -expecteof "clixon_netconf -qf $clixon_cf" "44]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "44]]>]]>" "^]]>]]>$" new "copy startup" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf get startup" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^eth1ethtrue]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^eth1ethtrue]]>]]>$" new "netconf delete startup" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf check empty startup" -expecteof "clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf subscription" -expectwait "clixon_netconf -qf $clixon_cf" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 +expectwait "$clixon_netconf -qf $clixon_cf" "ROUTING]]>]]>" "^]]>]]>Routing notification]]>]]>$" 30 new "Kill backend" # Check if still alive diff --git a/test/test3.sh b/test/test3.sh index b106bfdb..82f63b32 100755 --- a/test/test3.sh +++ b/test/test3.sh @@ -6,7 +6,7 @@ # kill old backend (if any) new "kill old backend" -sudo clixon_backend -zf $clixon_cf +#sudo clixon_backend -zf $clixon_cf if [ $? -ne 0 ]; then err fi @@ -24,6 +24,9 @@ sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c sleep 1 +new "restconf options" +expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE" + new "restconf get empty config" expectfn "curl -sG http://localhost/restconf/data" "^null $" @@ -34,8 +37,12 @@ new "restconf get config" expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth0","type": "eth","enabled": "true"}\]}} $' +new "restconf head" +expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json" + new "restconf patch config" -expectfn 'curl -sX POST -d {"type":"type"} http://localhost/restconf/data/interfaces/interface=eth4' "" +expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" +# XXX POST/PUT/PATCH new "restconf delete config" expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' "" @@ -45,7 +52,7 @@ expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": $' new "Kill restconf daemon" -sudo pkill -u www-data clixon_restconf +#sudo pkill -u www-data clixon_restconf new "Kill backend" # Check if still alive From fa20a9416b75a9342349054d156aa7ea7c748568 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 26 Mar 2017 15:16:36 +0200 Subject: [PATCH 04/13] site-specific constants --- apps/restconf/restconf_lib.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index 0a1bdd69..8eba97bb 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -39,13 +39,6 @@ /* * Constants */ -#define USER_COOKIE "c-user" /* connected user cookie */ -#define WWW_USER "root" -#define WWW_PASSWD "9rundpaj" // XXX -#define DEFAULT_TEMPLATE "nordunet" /* XXX Default sender template must be in conf */ -#define CONFIG_FILE "/usr/local/etc/grideye.conf" -#define NETCONF_BIN "/usr/local/bin/clixon_netconf" -#define NETCONF_OPTS "-qS" /* * Prototypes From 25d535703e26f5ed3f66aaba85567513eeb52a26 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 28 Mar 2017 21:01:57 +0200 Subject: [PATCH 05/13] testing --- CHANGELOG | 27 +++++++++++ apps/cli/cli_common.c | 2 +- apps/restconf/restconf_methods.c | 4 +- clixon.conf.cpp.cpp | 5 --- lib/clixon/clixon_proto_client.h | 4 -- lib/src/clixon_xml.c | 2 +- lib/src/clixon_xml_parse.h | 2 +- lib/src/clixon_xml_parse.y | 77 ++++++++++++++++++-------------- 8 files changed, 76 insertions(+), 47 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fffc0285..0e298e21 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,7 +29,34 @@ # # ***** END LICENSE BLOCK ***** +- The netconf support has been extended with lock/unlock +- clicon_rpc_call() has been removed and should be replaced by extending the + internal netconf protocol. + See downcall() function in example/routing_cli.c and + routing_downcall() in example/routing_backend.c +- Replace clicon_rpc_xmlput with clicon_rpc_edit_config +- Removed xmldb daemon. All xmldb acceses is made backend daemon. + No direct accesses by clients to xmldb API. + Instead use the rpc calls in clixon_proto_client.[ch] + In clients (eg cli/netconf) replace xmldb_get() in client code with + clicon_rpc_get_config(). + If you use the vector arguments of xmldb_get(), replace as follows: + xmldb_get(h, db, api_path, &xt, &xvec, &xlen); + with + clicon_rpc_get_config(h, dbstr, api_path, &xt); + xpath_vec(xt, api_path, &xvec, &xlen) + +- xmdlb_put_xkey() and xmldb_put_tree() have been folded into xmldb_put() + Replace xmldb_put_xkey with xmldb_put as follows: + xmldb_put_xkey(h, "candidate", cbuf_get(cb), str, OP_REPLACE); + with + clicon_xml_parse(&xml, "%s", str); + xmldb_put(h, "candidate", OP_REPLACE, cbuf_get(cb), xml); + xml_free(xml); + - Change internal protocol from clicon_proto.h to netconf. + This means that the internal protocol defined in clixon_proto.[ch] is removed + - Netconf startup configuration support. Set CLICON_USE_STARTUP_CONFIG to 1 to enable. Eg, if backend_main is started with -CIr startup will be copied to running. diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 1f6518c8..e4402680 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -733,7 +733,7 @@ save_config_filev(clicon_handle h, clicon_err(OE_CFG, errno, "Creating file %s", filename); goto done; } - if (clicon_xml2file(f, xt, 0, 0) < 0) + if (clicon_xml2file(f, xt, 0, 1) < 0) goto done; retval = 0; /* Fall through */ diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 90f4c6ff..0b2c5031 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -103,6 +103,7 @@ Mapping netconf error-tag -> status code #include #include #include +#include #include #include #include @@ -161,7 +162,7 @@ api_data_get_gen(clicon_handle h, cbuf *cbx = NULL; cxobj **vec = NULL; yang_spec *yspec; - yang_stmt *y; + yang_stmt *y = NULL; yang_stmt *ykey; char *name; cvec *cvk = NULL; /* vector of index keys */ @@ -189,6 +190,7 @@ api_data_get_gen(clicon_handle h, } } else{ + assert(y!=NULL); if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){ clicon_err(OE_UNIX, errno, "No yang node found: %s", name); goto done; diff --git a/clixon.conf.cpp.cpp b/clixon.conf.cpp.cpp index 1dacc35a..22d4262c 100644 --- a/clixon.conf.cpp.cpp +++ b/clixon.conf.cpp.cpp @@ -121,11 +121,6 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile # Directory where "running", "candidate" and "startup" are placed CLICON_XMLDB_DIR localstatedir/APPNAME -# CLICON_XMLDB_ADDR - -# xmldb tcp port (if CLICON_XMLDB_RPC) -# CLICON_XMLDB_PORT - # Dont include keys in cvec in cli vars callbacks, ie a & k in 'a k ' ignored # CLICON_CLI_VARONLY 1 diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h index 37420ca7..29ca939f 100644 --- a/lib/clixon/clixon_proto_client.h +++ b/lib/clixon/clixon_proto_client.h @@ -59,10 +59,6 @@ int clicon_rpc_commit(clicon_handle h); int clicon_rpc_discard_changes(clicon_handle h); int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter, int *s); - -int clicon_rpc_change(clicon_handle h, char *db, - enum operation_type op, char *key, char *val); - int clicon_rpc_debug(clicon_handle h, int level); #endif /* _CLIXON_PROTO_CLIENT_H_ */ diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 9249e33b..7a6f4698 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -963,11 +963,11 @@ xml_parse(char *str, return -1; } ya.ya_xparent = x_up; + ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */ if (clixon_xml_parsel_init(&ya) < 0) goto done; if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */ goto done; - retval = 0; done: clixon_xml_parsel_exit(&ya); diff --git a/lib/src/clixon_xml_parse.h b/lib/src/clixon_xml_parse.h index 05aeccdd..848bf55e 100644 --- a/lib/src/clixon_xml_parse.h +++ b/lib/src/clixon_xml_parse.h @@ -46,7 +46,7 @@ struct xml_parse_yacc_arg{ cxobj *ya_xelement; /* xml active element */ cxobj *ya_xparent; /* xml parent element*/ - int ya_skipspace; /* If set, translate successive space, \t \n with single space */ + int ya_skipspace; /* If set, remove all non-terminal bodies (strip prettyr-print) */ }; extern char *clixon_xml_parsetext; diff --git a/lib/src/clixon_xml_parse.y b/lib/src/clixon_xml_parse.y index c2923de4..ede62b8e 100644 --- a/lib/src/clixon_xml_parse.y +++ b/lib/src/clixon_xml_parse.y @@ -103,34 +103,19 @@ static int xml_parse_content(struct xml_parse_yacc_arg *ya, char *str) { - int sz; - char s0; cxobj *xn = ya->ya_xelement; cxobj *xp = ya->ya_xparent; int retval = -1; ya->ya_xelement = NULL; /* init */ - s0 = str[0]; - if (xn != NULL){ - sz = strlen(xml_value(xn)); - if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t')){ - str[0] = ' '; - if (xml_value(xn)[sz-1] == ' ') - goto ok; - } - } - else{ - if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t')) - goto ok; + if (xn == NULL){ if ((xn = xml_new("body", xp)) == NULL) goto done; xml_type_set(xn, CX_BODY); - sz = 0; } if (xml_value_append(xn, str)==NULL) goto done; ya->ya_xelement = xn; - ok: retval = 0; done: return retval; @@ -148,7 +133,6 @@ xml_parse_version(struct xml_parse_yacc_arg *ya, char *ver) return 0; } - static int xml_parse_id(struct xml_parse_yacc_arg *ya, char *name, char *namespace) { @@ -194,18 +178,31 @@ xml_parse_endslash_post(struct xml_parse_yacc_arg *ya) static int xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name) { - int retval = -1; + int retval = -1; + cxobj *x = ya->ya_xelement; + cxobj *xc; - if (strcmp(xml_name(ya->ya_xelement), name)){ + if (strcmp(xml_name(x), name)){ clicon_err(OE_XML, 0, "Sanity check failed: %s vs %s", - xml_name(ya->ya_xelement), name); + xml_name(x), name); goto done; } - if (xml_namespace(ya->ya_xelement)!=NULL){ + if (xml_namespace(x)!=NULL){ clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s\n", - xml_namespace(ya->ya_xelement), xml_name(ya->ya_xelement), name); + xml_namespace(x), xml_name(x), name); goto done; } + + /* remove all non-terminal bodies (strip pretty-print) */ + if (ya->ya_skipspace){ + if (xml_child_nr(x) == 1 && (xml_type(xml_child_i(x, 0))==CX_BODY)) + ; + else{ + xc = NULL; + while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) + xml_value_set(xc, ""); /* XXX remove */ + } + } retval = 0; done: free(name); @@ -215,25 +212,37 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name) static int xml_parse_bslash2(struct xml_parse_yacc_arg *ya, char *namespace, char *name) { - int retval = -1; + int retval = -1; + cxobj *x = ya->ya_xelement; + cxobj *xc; - if (strcmp(xml_name(ya->ya_xelement), name)){ + if (strcmp(xml_name(x), name)){ clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n", - xml_namespace(ya->ya_xelement), - xml_name(ya->ya_xelement), + xml_namespace(x), + xml_name(x), namespace, name); goto done; } - if (xml_namespace(ya->ya_xelement)==NULL || - strcmp(xml_namespace(ya->ya_xelement), namespace)){ + if (xml_namespace(x)==NULL || + strcmp(xml_namespace(x), namespace)){ clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n", - xml_namespace(ya->ya_xelement), - xml_name(ya->ya_xelement), + xml_namespace(x), + xml_name(x), namespace, name); goto done; } + /* remove all non-terminal bodies (strip pretty-print) */ + if (ya->ya_skipspace){ + if (xml_child_nr(x) == 1 && (xml_type(xml_child_i(x, 0))==CX_BODY)) + ; + else{ + xc = NULL; + while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) + xml_value_set(xc, ""); /* XXX remove */ + } + } retval = 0; done: free(name); @@ -301,7 +310,7 @@ emnt : '<' id attrs emnt1 ; id : NAME { if (xml_parse_id(_YA, $1, NULL) < 0) YYABORT; - clicon_debug(3, "id -> NAME");} + clicon_debug(3, "id -> NAME %s", $1);} | NAME ':' NAME { if (xml_parse_id(_YA, $3, $1) < 0) YYABORT; clicon_debug(3, "id -> NAME : NAME");} ; @@ -315,8 +324,8 @@ emnt1 : ESLASH {_YA->ya_xelement = NULL; ; etg : BSLASH NAME '>' - { if (xml_parse_bslash1(_YA, $2) < 0) YYABORT; - clicon_debug(3, "etg -> < "); } +{ clicon_debug(3, "etg -> < ", $2); if (xml_parse_bslash1(_YA, $2) < 0) YYABORT; } + | BSLASH NAME ':' NAME '>' { if (xml_parse_bslash2(_YA, $2, $4) < 0) YYABORT; clicon_debug(3, "etg -> < "); } @@ -329,7 +338,7 @@ list : list content { clicon_debug(3, "list -> list content"); } content : emnt { clicon_debug(3, "content -> emnt"); } | comment { clicon_debug(3, "content -> comment"); } | CHAR { if (xml_parse_content(_YA, $1) < 0) YYABORT; - clicon_debug(3, "content -> CHAR", $1); } + clicon_debug(3, "content -> CHAR %s", $1); } | { clicon_debug(3, "content -> "); } ; From 18093441a804cacf5115c5097c2e915d6d6d51a5 Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Mon, 3 Apr 2017 15:21:13 +0200 Subject: [PATCH 06/13] revert xsl for . and .. --- apps/backend/backend_client.c | 1 + apps/backend/backend_commit.c | 2 +- apps/backend/backend_socket.c | 8 +++---- apps/backend/clixon_backend_handle.c | 2 +- apps/cli/cli_common.c | 33 +++++++++++++++++++--------- lib/src/clixon_proto.c | 1 + lib/src/clixon_proto_client.c | 2 +- lib/src/clixon_xml_db.c | 6 ++--- lib/src/clixon_xsl.c | 13 ++++++++++- 9 files changed, 47 insertions(+), 21 deletions(-) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 41d36a86..f30467dd 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -870,6 +870,7 @@ from_client_msg(clicon_handle h, } reply: assert(cbuf_len(cbret)); + clicon_debug(1, "%s %s", __FUNCTION__, cbuf_get(cbret)); 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"); diff --git a/apps/backend/backend_commit.c b/apps/backend/backend_commit.c index 1f9ba7d7..95b15869 100644 --- a/apps/backend/backend_commit.c +++ b/apps/backend/backend_commit.c @@ -163,7 +163,7 @@ validate_common(clicon_handle h, &td->td_tcvec, /* changed: wanted values */ &td->td_clen) < 0) goto done; - if (debug) + if (debug>1) transaction_print(stderr, td); /* Mark as changed in tree */ for (i=0; itd_dlen; i++){ /* Also down */ diff --git a/apps/backend/backend_socket.c b/apps/backend/backend_socket.c index 6065cbf7..b83f7ce3 100644 --- a/apps/backend/backend_socket.c +++ b/apps/backend/backend_socket.c @@ -193,12 +193,12 @@ config_socket_init(clicon_handle h) return 0; } -/* - * config_accept_client +/*! Accept new socket client * XXX: credentials not properly implemented */ int -config_accept_client(int fd, void *arg) +config_accept_client(int fd, + void *arg) { int retval = -1; clicon_handle h = (clicon_handle)arg; @@ -218,7 +218,7 @@ config_accept_client(int fd, void *arg) char *mem; int i; - clicon_debug(1, "%s", __FUNCTION__); + clicon_debug(2, "%s", __FUNCTION__); len = sizeof(from); if ((s = accept(fd, (struct sockaddr*)&from, &len)) < 0){ clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__); diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index a795d7b0..84ecebbe 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -133,7 +133,7 @@ backend_notify(clicon_handle h, struct handle_subscription *hs; int retval = -1; - clicon_debug(1, "%s %s", __FUNCTION__, stream); + clicon_debug(2, "%s %s", __FUNCTION__, stream); /* First thru all clients(sessions), and all subscriptions and find matches */ for (ce = backend_client_list(h); ce; ce = ce_next){ ce_next = ce->ce_next; diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index e4402680..c41bf3e1 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -646,16 +646,18 @@ load_config_filev(clicon_handle h, goto done; if (xt == NULL) goto done; - /* Ensure top-level is "config", maybe this is too rough? */ - xml_name_set(xt, "config"); + // if ((xn = xml_child_i(xt, 0)) != NULL){ if ((cbxml = cbuf_new()) == NULL) goto done; x = NULL; - while ((x = xml_child_each(xt, x, -1)) != NULL) + while ((x = xml_child_each(xt, x, -1)) != NULL) { + /* Ensure top-level is "config", maybe this is too rough? */ + xml_name_set(x, "config"); if (clicon_xml2cbuf(cbxml, x, 0, 0) < 0) goto done; + } if (clicon_rpc_edit_config(h, "candidate", replace?OP_REPLACE:OP_MERGE, "", @@ -802,10 +804,12 @@ db_copy(clicon_handle h, } /* These are strings that can be used as 3rd argument to cli_setlog */ +#ifdef notused // broke in new version static const char *SHOWAS_TXT = "txt"; static const char *SHOWAS_XML = "xml"; static const char *SHOWAS_XML2TXT = "xml2txt"; static const char *SHOWAS_XML2JSON = "xml2json"; +#endif /*! This is the callback used by cli_setlog to print log message in CLI * param[in] s UNIX socket from backend where message should be read @@ -818,12 +822,14 @@ cli_notification_cb(int s, struct clicon_msg *reply = NULL; int eof; int retval = -1; - char *eventstr = NULL; cxobj *xt = NULL; - cxobj *xn; cxobj *xe; char *format = (char*)arg; - +#if 0 + char *eventstr = NULL; + cxobj *xn; +#endif + /* get msg (this is the reason this function is called) */ if (clicon_msg_rcv(s, &reply, &eof) < 0) goto done; @@ -838,8 +844,11 @@ cli_notification_cb(int s, goto done; if (clicon_msg_decode(reply, &xt) < 0) goto done; - if ((xe = xpath_first(xt, "//event")) != NULL) - eventstr = xml_body(xe); + if ((xe = xpath_first(xt, "//event")) != NULL){ + if (xml_print(stdout, xe) < 0) + goto done; + } +#ifdef notyet /* Broke in new version */ if (strcmp(format, SHOWAS_TXT) == 0){ fprintf(stdout, "%s\n", eventstr); } @@ -864,7 +873,7 @@ cli_notification_cb(int s, goto done; } } - +#endif retval = 0; done: if (xt) @@ -908,6 +917,10 @@ cli_notifyv(clicon_handle h, formatstr = cv_string_get(cvec_i(argv, 2)); if (strcmp(formatstr, "SHOWAS_TXT") != 0) format = MSG_NOTIFY_XML; + if ((formatstr = strdup(formatstr)) == NULL){ /* XXX */ + clicon_err(OE_PLUGIN, 0, "%s Requires arguments: []", __FUNCTION__); + goto done; + } } if (cli_notification_register(h, stream, @@ -915,7 +928,7 @@ cli_notifyv(clicon_handle h, "", status, cli_notification_cb, - (void*)strdup(formatstr)) < 0) + formatstr) < 0) goto done; retval = 0; diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 43816194..e74e28b1 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -119,6 +119,7 @@ clicon_msg_decode(struct clicon_msg *msg, /* body */ xmlstr = msg->op_body; + clicon_debug(1, "%s %s", __FUNCTION__, xmlstr); if (clicon_xml_parse_str(xmlstr, xml) < 0) goto done; retval = 0; diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index cc60ffb1..1ed8d5b9 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -606,7 +606,7 @@ clicon_rpc_discard_changes(clicon_handle h) cxobj *xret = NULL; cxobj *xerr; - if ((msg = clicon_msg_encode("")) == NULL) + if ((msg = clicon_msg_encode("")) == NULL) goto done; if (clicon_rpc_msg(h, msg, &xret, NULL) < 0) goto done; diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c index 8066c5fb..1dbd4a6a 100644 --- a/lib/src/clixon_xml_db.c +++ b/lib/src/clixon_xml_db.c @@ -1011,7 +1011,7 @@ xmldb_get(clicon_handle h, struct db_pair *pairs; cxobj *xt = NULL; - clicon_debug(1, "%s", __FUNCTION__); + clicon_debug(2, "%s", __FUNCTION__); if (db2file(h, db, &dbname) < 0) goto done; if (dbname==NULL){ @@ -1065,8 +1065,8 @@ xmldb_get(clicon_handle h, goto done; if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0) goto done; - if (debug) - clicon_xml2file(stdout, xt, 0, 1); + if (debug>1) + clicon_xml2file(stderr, xt, 0, 1); *xtop = xt; retval = 0; done: diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index c90bb290..441d15a4 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -199,7 +199,7 @@ xpath_parse_predicate(struct xpath_element *xe, int len; len = strlen(pred); - for (i=len-1; i>=0; i--){ /* -2 since we search for ][ */ + for (i=len-1; i>=0; i--){ /* -1 since we search for ][ */ s = &pred[i]; if (i==0 || (*(s)==']' && *(s+1)=='[')){ @@ -358,12 +358,23 @@ xpath_parse(char *xpath, else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){ xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext); } +#if 1 /* Problems with .[userid=1321] */ + else if (strncmp(s,".", strlen("."))==0) + xpath_element_new(A_SELF, s+strlen("."), &xpnext); +#else else if (strncmp(s,".", strlen(s))==0) /* abbreviatedstep */ xpath_element_new(A_SELF, NULL, &xpnext); +#endif + else if (strncmp(s,"self::", strlen("self::"))==0) xpath_element_new(A_SELF, s+strlen("self::"), &xpnext); +#if 1 + else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */ + xpath_element_new(A_PARENT, s+strlen(".."), &xpnext); +#else else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */ xpath_element_new(A_PARENT, NULL, &xpnext); +#endif else if (strncmp(s,"parent::", strlen("parent::"))==0) xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext); else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0) From 09cbea65c711daacf38b3505429bc046f6381d0a Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Mon, 3 Apr 2017 15:53:57 +0200 Subject: [PATCH 07/13] replace chunk code --- CHANGELOG | 7 +++++ apps/netconf/netconf_plugin.c | 2 -- apps/netconf/netconf_rpc.c | 2 -- lib/clixon/clixon_string.h | 1 + lib/src/clixon_string.c | 48 +++++++++++++++++++++++++++++++++-- lib/src/clixon_yang.c | 35 ++----------------------- 6 files changed, 56 insertions(+), 39 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0e298e21..82e6473d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,7 @@ # # ***** END LICENSE BLOCK ***** + - The netconf support has been extended with lock/unlock - clicon_rpc_call() has been removed and should be replaced by extending the internal netconf protocol. @@ -46,6 +47,12 @@ clicon_rpc_get_config(h, dbstr, api_path, &xt); xpath_vec(xt, api_path, &xvec, &xlen) +- clicon_rpc_change() is replaced with clicon_rpc_edit_config(). + Note modify argument 5: + clicon_rpc_change(h, db, op, apipath, "value") + to: + clicon_rpc_edit_config(h, db, op, apipath, "value") + - xmdlb_put_xkey() and xmldb_put_tree() have been folded into xmldb_put() Replace xmldb_put_xkey with xmldb_put as follows: xmldb_put_xkey(h, "candidate", cbuf_get(cb), str, OP_REPLACE); diff --git a/apps/netconf/netconf_plugin.c b/apps/netconf/netconf_plugin.c index b18e5c61..ce68c92b 100644 --- a/apps/netconf/netconf_plugin.c +++ b/apps/netconf/netconf_plugin.c @@ -256,8 +256,6 @@ catch: * * @param[in] h clicon handle * @param[in] xn Sub-tree (under xorig) at child of rpc: . - * @param[out] cb Output xml stream. For reply - * @param[out] cb_err Error xml stream. For error reply * @param[out] xret Return XML, error or OK * * @retval -1 Error diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 80a92834..b63af86c 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -77,8 +77,6 @@ /*! Get configuration * @param[in] h Clicon handle * @param[in] xn Sub-tree (under xorig) at ... level. - * @param[out] cb Output xml stream. For reply - * @param[out] cb_err Error xml stream. For error reply * @param[out] xret Return XML, error or OK * @note filter type subtree and xpath is supported, but xpath is preferred, and * better performance and tested. Please use xpath. diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 7537a799..55ec7a6f 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -56,6 +56,7 @@ static inline char * strdup4(char *str) */ char **clicon_sepsplit (char *string, char *delim, int *nvec, const char *label); char **clicon_strsplit (char *string, char *delim, int *nvec, const char *label); +char **clicon_strsep(char *string, char *delim, int *nvec0); char *clicon_strjoin (int argc, char **argv, char *delim, const char *label); char *clicon_strtrim(char *str, const char *label); int clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0); diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 611c4d27..139fdeff 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -69,6 +69,7 @@ * @retval NULL Error * @see clicon_strsplit Operates on full string delimiters rather than * individual character delimiters. + * @see clicon_strsep Use malloc instead of chunk */ char ** clicon_sepsplit (char *string, @@ -111,8 +112,6 @@ clicon_sepsplit (char *string, * the full delimiter string. The matched delimiters are not part of the * resulting vector. * - * See also clicon_sepsplit() which is similar - * * The vector returned is one single memory chunk that must be unchunked * by the caller * @@ -124,6 +123,7 @@ clicon_sepsplit (char *string, * @retval NULL Error * @see clicon_sepsplit Operates on individual character delimiters rather * than full string delimiter. + * @see clicon_strsep Use malloc instead of chunk */ char ** clicon_strsplit (char *string, @@ -165,6 +165,50 @@ clicon_strsplit (char *string, return vec; } +/*! Split string into a vector based on character delimiters. Using malloc + * + * The given string is split into a vector where the delimiter can be + * any of the characters in the specified delimiter string. + * + * The vector returned is one single memory block that must be freed + * by the caller + * + * @param[in] string String to be split + * @param[in] delim String of delimiter characters + * @param[out] nvec Number of entries in returned vector + * @retval vec Vector of strings. Free after use + * @retval NULL Error * + */ +char ** +clicon_strsep(char *string, + char *delim, + int *nvec0) +{ + char **vec = NULL; + char *ptr; + char *p; + int nvec = 1; + int i; + + for (i=0; i Date: Tue, 4 Apr 2017 17:11:21 +0200 Subject: [PATCH 08/13] cleaned up string functions --- apps/backend/backend_client.c | 2 +- apps/backend/backend_main.c | 5 +- apps/backend/clixon_backend_handle.c | 4 +- apps/cli/cli_common.c | 105 +++----- apps/cli/cli_main.c | 6 +- apps/cli/cli_plugin.c | 10 +- apps/cli/cli_show.c | 14 +- apps/restconf/restconf_main.c | 8 +- example/routing_cli.cli | 2 +- lib/clixon/clixon_proto.h | 10 +- lib/clixon/clixon_string.h | 9 +- lib/src/clixon_file.c | 33 ++- lib/src/clixon_proc.c | 11 +- lib/src/clixon_proto.c | 40 +++ lib/src/clixon_string.c | 362 ++++++--------------------- lib/src/clixon_xml_db.c | 36 ++- lib/src/clixon_yang.c | 13 +- test/test2.sh | 2 +- 18 files changed, 245 insertions(+), 427 deletions(-) diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index f30467dd..f347e6f9 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -694,7 +694,7 @@ from_client_create_subscription(clicon_handle h, } } } - if (client_subscription_add(ce, stream, MSG_NOTIFY_XML, filter) == NULL) + if (client_subscription_add(ce, stream, FORMAT_XML, filter) == NULL) goto done; cprintf(cbret, ""); ok: diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index 2832af7d..ce68fca5 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -72,7 +72,7 @@ #include "backend_handle.h" /* Command line options to be passed to getopt(3) */ -#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pt" +#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pty:" /*! Terminate. Cannot use h after this */ static int @@ -426,6 +426,9 @@ main(int argc, char **argv) case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */ printalt++; break; + case 'y' : /* yang module */ + clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg); + break; default: usage(argv[0], h); break; diff --git a/apps/backend/clixon_backend_handle.c b/apps/backend/clixon_backend_handle.c index 84ecebbe..720337e8 100644 --- a/apps/backend/clixon_backend_handle.c +++ b/apps/backend/clixon_backend_handle.c @@ -162,7 +162,7 @@ backend_notify(clicon_handle h, /* Then go thru all global (handle) subscriptions and find matches */ hs = NULL; while ((hs = subscription_each(h, hs)) != NULL){ - if (hs->hs_format != MSG_NOTIFY_TXT) + if (hs->hs_format != FORMAT_TEXT) continue; if (strcmp(hs->hs_stream, stream)) continue; @@ -240,7 +240,7 @@ backend_notify_xml(clicon_handle h, /* Then go thru all global (handle) subscriptions and find matches */ hs = NULL; while ((hs = subscription_each(h, hs)) != NULL){ - if (hs->hs_format != MSG_NOTIFY_XML) + if (hs->hs_format != FORMAT_XML) continue; if (strcmp(hs->hs_stream, stream)) continue; diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index c41bf3e1..0ea1383c 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -74,6 +74,7 @@ #include "cli_common.h" + /*! Register log notification stream * @param[in] h Clicon handle * @param[in] stream Event stream. CLICON is predefined, others are application-defined @@ -594,7 +595,7 @@ load_config_filev(clicon_handle h, { int ret = -1; struct stat st; - char **vecp; + char **vecp = NULL; char *filename; int replace; cg_var *cv; @@ -668,6 +669,13 @@ load_config_filev(clicon_handle h, ret = 0; done: unchunk_group(__FUNCTION__); + if (vecp){ + if (vecp[0]) + free(vecp[0]); + if (vecp[1]) + free(vecp[1]); + free(vecp); + } if (xt) xml_free(xt); if (fd != -1) @@ -803,14 +811,6 @@ db_copy(clicon_handle h, return clicon_rpc_copy_config(h, db1, db2); } -/* These are strings that can be used as 3rd argument to cli_setlog */ -#ifdef notused // broke in new version -static const char *SHOWAS_TXT = "txt"; -static const char *SHOWAS_XML = "xml"; -static const char *SHOWAS_XML2TXT = "xml2txt"; -static const char *SHOWAS_XML2JSON = "xml2json"; -#endif - /*! This is the callback used by cli_setlog to print log message in CLI * param[in] s UNIX socket from backend where message should be read * param[in] arg format: txt, xml, xml2txt, xml2json @@ -824,11 +824,7 @@ cli_notification_cb(int s, int retval = -1; cxobj *xt = NULL; cxobj *xe; - char *format = (char*)arg; -#if 0 - char *eventstr = NULL; - cxobj *xn; -#endif + enum format_enum format = (enum format_enum)arg; /* get msg (this is the reason this function is called) */ if (clicon_msg_rcv(s, &reply, &eof) < 0) @@ -840,40 +836,25 @@ cli_notification_cb(int s, event_unreg_fd(s, cli_notification_cb); goto done; } - if (format == NULL) - goto done; if (clicon_msg_decode(reply, &xt) < 0) goto done; - if ((xe = xpath_first(xt, "//event")) != NULL){ + xe = xpath_first(xt, "//event"); + switch (format){ + case FORMAT_XML: if (xml_print(stdout, xe) < 0) goto done; + break; + case FORMAT_TEXT: + if (xml2txt(stdout, xe, 0) < 0) + goto done; + break; + case FORMAT_JSON: + if (xml2json(stdout, xe, 0) < 0) + goto done; + break; + default: + break; } -#ifdef notyet /* Broke in new version */ - 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; - if ((xn = xml_child_i(xt, 0)) != NULL) - if (xml_print(stdout, xn) < 0) - goto done; - } - else - if (strcmp(format, SHOWAS_XML2TXT) == 0){ - if ((xn = xml_child_i(xe, 0)) != NULL) - if (xml2txt(stdout, xn, 0) < 0) - goto done; - } - else - if (strcmp(format, SHOWAS_XML2JSON) == 0){ - if ((xn = xml_child_i(xe, 0)) != NULL){ - if (xml2json(stdout, xn, 0) < 0) - goto done; - } - } -#endif retval = 0; done: if (xt) @@ -905,7 +886,7 @@ cli_notifyv(clicon_handle h, int retval = -1; int status; char *formatstr = NULL; - enum format_enum format = MSG_NOTIFY_TXT; + enum format_enum format = FORMAT_TEXT; if (cvec_len(argv) != 2 && cvec_len(argv) != 3){ clicon_err(OE_PLUGIN, 0, "%s Requires arguments: []", __FUNCTION__); @@ -915,12 +896,7 @@ cli_notifyv(clicon_handle h, status = atoi(cv_string_get(cvec_i(argv, 1))); if (cvec_len(argv) > 2){ formatstr = cv_string_get(cvec_i(argv, 2)); - if (strcmp(formatstr, "SHOWAS_TXT") != 0) - format = MSG_NOTIFY_XML; - if ((formatstr = strdup(formatstr)) == NULL){ /* XXX */ - clicon_err(OE_PLUGIN, 0, "%s Requires arguments: []", __FUNCTION__); - goto done; - } + format = format_str2int(formatstr); } if (cli_notification_register(h, stream, @@ -928,7 +904,7 @@ cli_notifyv(clicon_handle h, "", status, cli_notification_cb, - formatstr) < 0) + (void*)format) < 0) goto done; retval = 0; @@ -1046,7 +1022,7 @@ load_config_file(clicon_handle h, cvec *argv; cg_var *cv; char *str; - char **vec; + char **vec = NULL; int nvec; /* Split string into two parts and build a cvec of it and supply that to @@ -1055,10 +1031,8 @@ load_config_file(clicon_handle h, clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) goto done; - } if (nvec != 2){ clicon_err(OE_PLUGIN, 0, "Arg syntax is "); goto done; @@ -1076,7 +1050,8 @@ load_config_file(clicon_handle h, retval = load_config_filev(h, cvv, argv); done: - unchunk_group(__FUNCTION__); + if (vec) + free(vec); return retval; } int @@ -1088,7 +1063,7 @@ save_config_file(clicon_handle h, cvec *argv; cg_var *cv; char *str; - char **vec; + char **vec = NULL; int nvec; /* Split string into two parts and build a cvec of it and supply that to @@ -1097,10 +1072,8 @@ save_config_file(clicon_handle h, clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) goto done; - } if (nvec != 2){ clicon_err(OE_PLUGIN, 0, "Arg syntax is "); goto done; @@ -1118,7 +1091,8 @@ save_config_file(clicon_handle h, retval = save_config_filev(h, cvv, argv); done: - unchunk_group(__FUNCTION__); + if (vec) + free(vec); return retval; } @@ -1131,7 +1105,7 @@ cli_notify(clicon_handle h, cvec *argv; cg_var *cv; char *str; - char **vec; + char **vec = NULL; int nvec; /* Split string into two parts and build a cvec of it and supply that to @@ -1140,10 +1114,8 @@ cli_notify(clicon_handle h, clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) goto done; - } if (nvec != 2 && nvec != 3){ clicon_err(OE_PLUGIN, 0, "Arg syntax is []"); goto done; @@ -1166,7 +1138,8 @@ cli_notify(clicon_handle h, } retval = cli_notifyv(h, cvv, argv); done: - unchunk_group(__FUNCTION__); + if (vec) + free(vec); return retval; } diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 78cb7879..4a47ba78 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -174,7 +174,7 @@ main(int argc, char **argv) int help = 0; char *treename; int logdst = CLICON_LOG_STDERR; - char *restarg; /* what remains after options */ + char *restarg = NULL; /* what remains after options */ /* Defaults */ @@ -366,7 +366,7 @@ main(int argc, char **argv) clicon_option_dump(h, debug); /* Join rest of argv to a single command */ - restarg = clicon_strjoin(argc, argv, " ", __FUNCTION__); + restarg = clicon_strjoin(argc, argv, " "); /* If several cligen object variables match same preference, select first */ cligen_match_cgvar_same(1); @@ -389,6 +389,8 @@ main(int argc, char **argv) if (!once) cli_interactive(h); done: + if (restarg) + free(restarg); unchunk_group(__FUNCTION__); // Gets in your face if we log on stderr clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */ diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index d60c052f..4c3f75bf 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -398,9 +398,8 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir) clicon_err(OE_PLUGIN, 0, "No syntax mode specified in %s", filepath); goto done; } - if ((vec = clicon_strsplit(mode, ":", &nvec, __FUNCTION__)) == NULL) { + if ((vec = clicon_strsep(mode, ":", &nvec)) == NULL) goto done; - } for (i = 0; i < nvec; i++) { if (syntax_append(h, cli_syntax(h), vec[i], pt) < 0) { goto done; @@ -415,6 +414,8 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir) done: if (vr) cvec_free(vr); + if (vec) + free(vec); unchunk_group(__FUNCTION__); return retval; } @@ -1020,7 +1021,7 @@ cli_ptpush(clicon_handle h, char *mode, char *string, char *op) return 0; pt = &co_cmd->co_pt; /* vec is the command, eg 'edit policy_option' */ - if ((vec = clicon_strsplit(string, " ", &nvec, __FUNCTION__)) == NULL) + if ((vec = clicon_strsep(string, " ", &nvec)) == NULL) goto catch; co = NULL; found = 0; @@ -1050,7 +1051,8 @@ cli_ptpush(clicon_handle h, char *mode, char *string, char *op) co_up_set(cc, co_cmd); } catch: - unchunk_group(__FUNCTION__) ; + if (vec) + free(vec); return 0; } diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index 561ed85e..cdba9012 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -787,10 +787,8 @@ expand_dbvar(void *h, goto done; } /* In the example, str = "candidate /x/m1/%s/b" */ - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) goto done; - } dbstr = vec[0]; if (strcmp(dbstr, "running") != 0 && strcmp(dbstr, "candidate") != 0 && @@ -856,7 +854,8 @@ expand_dbvar(void *h, } retval = 0; done: - unchunk_group(__FUNCTION__); + if (vec) + free(vec); if (xvec) free(xvec); if (xt) @@ -905,10 +904,8 @@ show_conf_as(clicon_handle h, clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); goto done; } - if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_PLUGIN, errno, "clicon_strsplit"); + if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) goto done; - } if (nvec != 2 && nvec != 3){ clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected [] got %d arg", str, nvec); goto done; @@ -956,7 +953,8 @@ done: free(val); if (cbx) cbuf_free(cbx); - unchunk_group(__FUNCTION__); + if (vec) + free(vec); return retval; } diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 043d6c63..41619c31 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -129,7 +129,7 @@ request_process(clicon_handle h, char *path; char *query; char *method; - char **pvec; + char **pvec = NULL; int pn; cvec *qvec = NULL; cvec *dvec = NULL; @@ -141,7 +141,7 @@ request_process(clicon_handle h, clicon_debug(1, "%s", __FUNCTION__); path = FCGX_GetParam("DOCUMENT_URI", r->envp); query = FCGX_GetParam("QUERY_STRING", r->envp); - if ((pvec = clicon_strsplit(path, "/", &pn, __FUNCTION__)) == NULL) + if ((pvec = clicon_strsep(path, "/", &pn)) == NULL) goto done; if (str2cvec(query, '&', '=', &qvec) < 0) @@ -171,7 +171,6 @@ request_process(clicon_handle h, if (auth == 0) goto done; clicon_debug(1, "%s credentials ok 2", __FUNCTION__); - clicon_debug(1, "%s credentials ok 3", __FUNCTION__); if (strcmp(method, "data") == 0) /* restconf, skip /api/data */ retval = api_data(h, r, path, pcvec, 2, qvec, data); @@ -181,6 +180,8 @@ request_process(clicon_handle h, retval = notfound(r); done: clicon_debug(1, "%s retval:%d K", __FUNCTION__, retval); + if (pvec) + free(pvec); if (dvec) cvec_free(dvec); if (qvec) @@ -189,7 +190,6 @@ request_process(clicon_handle h, cvec_free(pcvec); if (cb) cbuf_free(cb); - unchunk_group(__FUNCTION__); return retval; } diff --git a/example/routing_cli.cli b/example/routing_cli.cli index b596ee22..4b0b2342 100644 --- a/example/routing_cli.cli +++ b/example/routing_cli.cli @@ -45,7 +45,7 @@ load("Load configuration from XML file") ("Filename (local file } 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"); +notify("Get notifications from backend"), cli_notifyv("ROUTING", "1", "text"); 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 e892788f..85342170 100644 --- a/lib/clixon/clixon_proto.h +++ b/lib/clixon/clixon_proto.h @@ -42,8 +42,11 @@ * Types */ enum format_enum{ - MSG_NOTIFY_TXT, /* means filter works on strings */ - MSG_NOTIFY_XML, /* means filter works on xml */ + FORMAT_XML, + FORMAT_JSON, + FORMAT_TEXT, + FORMAT_CLI, + FORMAT_NETCONF }; /* Protocol message header */ @@ -55,6 +58,9 @@ struct clicon_msg { /* * Prototypes */ +char *format_int2str(enum format_enum showas); +enum format_enum format_str2int(char *str); + struct clicon_msg *clicon_msg_encode(char *format, ...); int clicon_msg_decode(struct clicon_msg *msg, cxobj **xml); diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 55ec7a6f..86c22e6d 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -54,17 +54,10 @@ static inline char * strdup4(char *str) /* * Prototypes */ -char **clicon_sepsplit (char *string, char *delim, int *nvec, const char *label); -char **clicon_strsplit (char *string, char *delim, int *nvec, const char *label); char **clicon_strsep(char *string, char *delim, int *nvec0); -char *clicon_strjoin (int argc, char **argv, char *delim, const char *label); -char *clicon_strtrim(char *str, const char *label); -int clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0); +char *clicon_strjoin (int argc, char **argv, char *delim); #ifndef HAVE_STRNDUP char *clicon_strndup (const char *, size_t); #endif /* ! HAVE_STRNDUP */ -int clicon_strmatch(const char *str, const char *regexp, char **match); -char *clicon_strsub(char *str, char *from, char *to); - #endif /* _CLIXON_STRING_H_ */ diff --git a/lib/src/clixon_file.c b/lib/src/clixon_file.c index 3280c0c9..1b96ed08 100644 --- a/lib/src/clixon_file.c +++ b/lib/src/clixon_file.c @@ -62,19 +62,22 @@ #include "clixon_string.h" #include "clixon_file.h" -/* - * Resolve the real path of a given 'path', following symbolic links and '../'. +/*! Resolve the real path of a given 'path', following symbolic links and '../'. * If 'path' relative, it will be resolved based on the currnt working * directory 'cwd'. The response is a 2 entry vector of strings. The first * entry is the resolved path and the second is the part of the path which * actually exist. + * @retval vec */ char ** -clicon_realpath(const char *cwd, char *path, const char *label) +clicon_realpath(const char *cwd, + char *path, + const char *label) { char **ret = NULL; char *rest; - char **vec, **vec2; + char **vec = NULL; + char **vec2; int nvec, nvec2; char *p; char *rp = NULL; @@ -122,8 +125,10 @@ clicon_realpath(const char *cwd, char *path, const char *label) /* Split path based on '/'. Loop through vector from the end and copy each entry into a new vector, skipping '..' and it's previous directory as well as all '.' */ - vec = clicon_strsplit (p, "/", &nvec, __FUNCTION__); - vec2 = chunk(nvec * sizeof(char *), __FUNCTION__); + if ((vec = clicon_strsep(p, "/", &nvec)) == NULL) + goto catch; + if ((vec2 = malloc(nvec * sizeof(char *))) == NULL) + goto catch; nvec2 = i = nvec; while(--i >= 0) { if(strcmp(vec[i], "..") == 0) @@ -135,14 +140,14 @@ clicon_realpath(const char *cwd, char *path, const char *label) } /* Create resulting vector */ - if ((ret = chunk(sizeof(char *) * 2, label)) != NULL) { - if((ret[0] = clicon_strjoin(nvec-nvec2, &vec2[nvec2], "/", label)) == NULL) { - unchunk(ret); + if ((ret = malloc(sizeof(char *) * 2)) != NULL) { + if((ret[0] = clicon_strjoin(nvec-nvec2, &vec2[nvec2], "/")) == NULL) { + free(ret); ret = NULL; } - if ((ret[1] = chunkdup(rp, strlen(rp)+1, label)) == NULL) { - unchunk(ret[0]); - unchunk(ret); + if ((ret[1] = strdup(rp)) == NULL) { + free(ret[0]); + free(ret); ret = NULL; } } @@ -150,6 +155,10 @@ clicon_realpath(const char *cwd, char *path, const char *label) catch: if(rp) free(rp); + if(vec) + free(vec); + if(vec2) + free(vec2); unchunk_group(__FUNCTION__); return ret; } diff --git a/lib/src/clixon_proc.c b/lib/src/clixon_proc.c index 9d3d9472..41dd9f8b 100644 --- a/lib/src/clixon_proc.c +++ b/lib/src/clixon_proc.c @@ -121,13 +121,12 @@ clicon_proc_run (char *cmd, sigfn_t oldhandler = NULL; sigset_t oset; - argv = clicon_sepsplit (cmd, " \t", &argc, __FUNCTION__); + argv = clicon_strsep(cmd, " \t", &argc); if (!argv) return -1; if (pipe (outfd) == -1) goto done; - signal_get_mask(&oset); set_signal(SIGINT, clicon_proc_sigint, &oldhandler); @@ -194,7 +193,8 @@ clicon_proc_run (char *cmd, signal_set_mask (&oset); set_signal(SIGINT, oldhandler, NULL); - unchunk_group (__FUNCTION__); + if(argv) + free(argv); return retval; } @@ -216,7 +216,7 @@ clicon_proc_daemon (char *cmd) struct rlimit rlim; - argv = clicon_sepsplit (cmd, " \t", &argc, NULL); + argv = clicon_strsep(cmd, " \t", &argc); if (!argv) return -1; @@ -260,7 +260,8 @@ clicon_proc_daemon (char *cmd) retval = 0; done: - unchunk_group(__FUNCTION__); + if (argv) + free(argv); return (retval); } diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index e74e28b1..21c2b0ed 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -74,6 +74,46 @@ static int _atomicio_sig = 0; +/*! Formats (showas) derived from XML + */ +struct formatvec{ + char *fv_str; + int fv_int; +}; + +static struct formatvec _FORMATS[] = { + {"xml", FORMAT_XML}, + {"text", FORMAT_TEXT}, + {"json", FORMAT_JSON}, + {"cli", FORMAT_CLI}, + {"netconf", FORMAT_NETCONF}, + {NULL, -1} +}; + +/*! Translate from numeric error to string representation + */ +char * +format_int2str(enum format_enum showas) +{ + struct formatvec *fv; + + for (fv=_FORMATS; fv->fv_int != -1; fv++) + if (fv->fv_int == showas) + break; + return fv?(fv->fv_str?fv->fv_str:"unknown"):"unknown"; +} + +enum format_enum +format_str2int(char *str) +{ + struct formatvec *fv; + + for (fv=_FORMATS; fv->fv_int != -1; fv++) + if (strcmp(fv->fv_str, str) == 0) + break; + return fv?fv->fv_int:-1; +} + /*! 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() diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 139fdeff..ecf1431b 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -53,117 +53,6 @@ #include "clixon_string.h" #include "clixon_err.h" -/*! Split string into a vector based on character delimiters - * - * The given string is split into a vector where the delimiter can be - * any of the characters in the specified delimiter string. - * - * The vector returned is one single memory chunk that must be unchunked - * by the caller - * - * @param[in] string String to be split - * @param[in] delim String of delimiter characters - * @param[out] nvec Number of entries in returned vector - * @param[in] label Chunk label for returned vector - * @retval vec Vector of strings. Free with unchunk - * @retval NULL Error - * @see clicon_strsplit Operates on full string delimiters rather than - * individual character delimiters. - * @see clicon_strsep Use malloc instead of chunk - */ -char ** -clicon_sepsplit (char *string, - char *delim, - int *nvec, - const char *label) -{ - int idx; - size_t siz; - char *s, *s0; - char **vec, *vecp; - - *nvec = 0; - s0 = s = chunkdup (string, strlen(string)+1, __FUNCTION__); - while (strsep(&s, delim)) - (*nvec)++; - unchunk (s0); - - siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1; - vec = (char **) chunk (siz, label); - if (!vec) { - return NULL; - } - bzero (vec, siz); - - vecp = (char *)&vec[*nvec +1]; - bcopy (string, vecp, strlen (string)); - - for (idx = 0; idx < *nvec; idx++) { - vec[idx] = vecp; - strsep (&vecp, delim); - } - - return vec; -} - -/*! Split string into a vector based on a string delimiter - * - * The given string is split into a vector where the delimited by the - * the full delimiter string. The matched delimiters are not part of the - * resulting vector. - * - * The vector returned is one single memory chunk that must be unchunked - * by the caller - * - * @param[in] string String to be split - * @param[in] delim String of delimiter characters - * @param[out] nvec Number of entries in returned vector - * @param[in] label Chunk label for returned vector - * @retval vec Vector of strings. Free with unchunk - * @retval NULL Error - * @see clicon_sepsplit Operates on individual character delimiters rather - * than full string delimiter. - * @see clicon_strsep Use malloc instead of chunk - */ -char ** -clicon_strsplit (char *string, - char *delim, - int *nvec, - const char *label) -{ - int idx; - size_t siz; - char *s; - char **vec, *vecp; - - *nvec = 1; - s = string; - while ((s = strstr(s, delim))) { - s += strlen(delim); - (*nvec)++; - } - - siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1; - vec = (char **) chunk (siz, label); - if (!vec) { - return NULL; - } - bzero (vec, siz); - - vecp = (char *)&vec[*nvec +1]; - bcopy (string, vecp, strlen (string)); - - s = vecp; - for (idx = 0; idx < *nvec; idx++) { - vec[idx] = s; - if ((s = strstr(s, delim)) != NULL) { - *s = '\0'; - s += strlen(delim); - } - } - - return vec; -} /*! Split string into a vector based on character delimiters. Using malloc * @@ -189,14 +78,20 @@ clicon_strsep(char *string, char *p; int nvec = 1; int i; - - for (i=0; i str && isspace(*(end-1))) - end--; - if((new = chunkdup (start, end-start+1, label))) - new[end-start] = '\0'; - - return new; -} - -/*! Given a string s, on format: a[b], separate it into two parts: a and b - * [] are separators. - * alterative use: - * a/b -> a and b (where sep = "/") - * @param[in] label Chunk label for returned vector - */ -int -clicon_sep(char *s, - const char sep[2], - const char *label, - char **a0, - char **b0) -{ - char *a = NULL; - char *b = NULL; - char *ptr; + int i; int len; - int retval = -1; + char *str; - ptr = s; - /* move forward to last char of element name */ - while (*ptr && *ptr != sep[0] && *ptr != sep[1] ) - ptr++; - /* Copy first element name */ - len = ptr-s; - if ((a = chunkdup(s, len+1, label)) == NULL) - goto catch; - a[len] = '\0'; - /* Do we have an extended format? */ - if (*ptr == sep[0]) { - b = ++ptr; - /* move forward to end extension */ - while (*ptr && *ptr != sep[1]) - ptr++; - /* Copy extension */ - len = ptr-b; - if ((b = chunkdup(b, len+1, label)) == NULL) - goto catch; - b[len] = '\0'; + len = 0; + for (i = 0; i < argc; i++) + len += strlen(argv[i]); + if (delim) + len += (strlen(delim) * argc); + len += 1; /* '\0' */ + if ((str = malloc(len)) == NULL) + return NULL; + memset (str, '\0', len); + for (i = 0; i < argc; i++) { + if (i != 0) + strncat (str, delim, len - strlen(str)); + strncat (str, argv[i], len - strlen(str)); } - - *a0 = a; - *b0 = b; - retval = 0; - catch: - return retval; + return str; } @@ -346,82 +160,48 @@ clicon_strndup (const char *str, } #endif /* ! HAVE_STRNDUP */ -/*! Match string against regexp. - * - * If a match pointer is given, the matching substring - * will be allocated 'match' will be pointing to it. The match string must - * be free:ed by the application. - * @retval -1 Failure - * @retval 0 No match - * @retval >0 Match: Length of matching substring - */ +/* + * Turn this on for uni-test programs + * Usage: clixon_string join + * Example compile: + gcc -g -o clixon_string -I. -I../clixon ./clixon_string.c -lclixon -lcligen + * Example run: +*/ +#if 0 /* Test program */ + +static int +usage(char *argv0) +{ + fprintf(stderr, "usage:%s \n", argv0); + exit(0); +} + int -clicon_strmatch(const char *str, - const char *regexp, - char **match) +main(int argc, char **argv) { - size_t len; - int status; - regex_t re; - char rxerr[128]; - size_t nmatch = 1; - regmatch_t pmatch[1]; - - if (match) - *match = NULL; - - if ((status = regcomp(&re, regexp, REG_EXTENDED)) != 0) { - regerror(status, &re, rxerr, sizeof(rxerr)); - clicon_err(OE_REGEX, errno, "%s", rxerr); - return -1; - } - - status = regexec(&re, str, nmatch, pmatch, 0); - regfree(&re); - if (status != 0) - return 0; /* No match */ - - len = pmatch[0].rm_eo - pmatch[0].rm_so; -/* If we've specified a match pointer, allocate and populate it. */ - if (match) { - if ((*match = malloc(len + 1)) == NULL) { - clicon_err(OE_UNIX, errno, "Failed to allocate string"); - return -1; - } - memset(*match, '\0', len + 1); - strncpy(*match, str + pmatch[0].rm_so, len); - } - - return len; -} - -/*! Substitute pattern in string. - * @retval str Malloc:ed string on success, use free to deallocate - * @retval NULL Failure. - */ -char * -clicon_strsub(char *str, - char *from, - char *to) -{ - char **vec; int nvec; - char *new; - char *retval = NULL; + char **vec; + char *str0; + char *str1; + int i; - if ((vec = clicon_strsplit(str, from, &nvec, __FUNCTION__)) == NULL) { - clicon_err(OE_UNIX, errno, "Failed to split string"); - goto done; + if (argc != 2){ + usage(argv[0]); + return 0; } - - if ((new = clicon_strjoin (nvec, vec, to, __FUNCTION__)) == NULL) { - clicon_err(OE_UNIX, errno, "Failed to split string"); - goto done; - } - - retval = strdup(new); - - done: - unchunk_group(__FUNCTION__); - return retval; + str0 = argv[1]; + if ((vec = clicon_strsep("a b\tc", " \t", &nvec)) == NULL) + return -1; + fprintf(stderr, "nvec: %d\n", nvec); + for (i=0; i'"); goto done; } - if ((valvec = clicon_strsplit(restval, ",", &nvalvec, __FUNCTION__)) == NULL) + if (valvec) + free(valvec); + if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == NULL) goto done; if (cvec_len(cvk) != nvalvec){ @@ -1398,6 +1405,11 @@ xmldb_put_xkey(clicon_handle h, cbuf_free(crx); if (cvk) cvec_free(cvk); + if (vec) + free(vec); + if (valvec) + free(valvec); + unchunk_group(__FUNCTION__); return retval; } @@ -1431,7 +1443,7 @@ xmldb_put_restconf_api_path(clicon_handle h, int retval = -1; yang_stmt *y = NULL; yang_stmt *ykey; - char **vec; + char **vec = NULL; int nvec; int i; char *name; @@ -1466,7 +1478,7 @@ xmldb_put_restconf_api_path(clicon_handle h, clicon_err(OE_UNIX, errno, "cbuf_new"); goto done; } - if ((vec = clicon_strsplit(api_path, "/", &nvec, __FUNCTION__)) == NULL) + if ((vec = clicon_strsep(api_path, "/", &nvec)) == NULL) goto done; if (nvec < 2){ clicon_err(OE_XML, 0, "Malformed key: %s", api_path); @@ -1611,6 +1623,8 @@ xmldb_put_restconf_api_path(clicon_handle h, cbuf_free(crx); if (cvk) cvec_free(cvk); + if (vec) + free(vec); unchunk_group(__FUNCTION__); return retval; } diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index 6ab33816..7e0d0bf4 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -1695,11 +1695,9 @@ yang_xpath_abs(yang_node *yn, char *prefix = NULL; if ((vec = clicon_strsep(xpath, "/", &nvec)) == NULL){ - clicon_err(OE_YANG, errno, "%s: strsplit", __FUNCTION__); + clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__); return NULL; } - - /* Assume path looks like: "/prefix:id[/prefix:id]*" */ if (nvec < 2){ clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s", @@ -1955,16 +1953,14 @@ cvec * yang_arg2cvec(yang_stmt *ys, char *delim) { - char **vec; + char **vec = NULL; int i; int nvec; cvec *cvv = NULL; cg_var *cv; - if ((vec = clicon_strsplit(ys->ys_argument, " ", &nvec, __FUNCTION__)) == NULL){ - clicon_err(OE_YANG, errno, "clicon_strsplit"); + if ((vec = clicon_strsep(ys->ys_argument, " ", &nvec)) == NULL) goto done; - } if ((cvv = cvec_new(nvec)) == NULL){ clicon_err(OE_YANG, errno, "cvec_new"); goto done; @@ -1979,7 +1975,8 @@ yang_arg2cvec(yang_stmt *ys, } } done: - unchunk_group(__FUNCTION__); + if (vec) + free(vec); return cvv; } diff --git a/test/test2.sh b/test/test2.sh index f23b070d..ef16c0cb 100755 --- a/test/test2.sh +++ b/test/test2.sh @@ -21,7 +21,7 @@ if [ $? -ne 0 ]; then err fi new "netconf get empty config" -expecteof "valgrind $clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" new "netconf edit config" expecteof "$clixon_netconf -qf $clixon_cf" "eth0eth1true
9.2.3.424
]]>]]>" "^]]>]]>$" From c59869a44e6381d9c015a09b866fc82ad1d81fa6 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 4 Apr 2017 21:37:42 +0200 Subject: [PATCH 09/13] Alternative yang spec option -y added to all applications --- CHANGELOG | 2 + apps/backend/backend_main.c | 14 +++- apps/cli/cli_common.c | 25 +++---- apps/cli/cli_main.c | 15 ++++- apps/netconf/netconf_main.c | 15 ++++- apps/restconf/restconf_main.c | 19 +++++- lib/clixon/clixon_file.h | 2 - lib/src/clixon_file.c | 123 ---------------------------------- lib/src/clixon_string.c | 13 ++-- lib/src/clixon_xml_map.c | 25 ++++--- 10 files changed, 86 insertions(+), 167 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 82e6473d..053ce8e9 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -30,6 +30,8 @@ # ***** END LICENSE BLOCK ***** +- Alternative yang spec option -y added to all applications +- Many clicon special string functions have been removed - The netconf support has been extended with lock/unlock - clicon_rpc_call() has been removed and should be replaced by extending the internal netconf protocol. diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index ce68fca5..6ee74fc9 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -57,6 +57,7 @@ #include #include #include +#include /* cligen */ #include @@ -141,7 +142,8 @@ usage(char *argv0, clicon_handle h) " -r\t\tReload running database\n" " -p \t\tPrint database yang specification\n" " -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\n" - " -g \tClient membership required to this group (default: %s)\n", + " -g \tClient membership required to this group (default: %s)\n" + "\t-y \tOverride yang spec file (dont include .yang suffix)\n", argv0, plgdir ? plgdir : "none", confsock ? confsock : "none", @@ -426,9 +428,15 @@ main(int argc, char **argv) case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */ printalt++; break; - case 'y' : /* yang module */ - clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg); + case 'y' :{ /* yang module */ + /* Set revision to NULL, extract dir and module */ + char *str = strdup(optarg); + char *dir = dirname(str); + hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION"); + clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(optarg)); + clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir)); break; + } default: usage(argv[0], h); break; diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 0ea1383c..9493ed8c 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -595,8 +595,7 @@ load_config_filev(clicon_handle h, { int ret = -1; struct stat st; - char **vecp = NULL; - char *filename; + char *filename = NULL; int replace; cg_var *cv; char *opstr; @@ -628,11 +627,10 @@ load_config_filev(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } - if ((vecp = clicon_realpath(NULL, cv_string_get(cv), __FUNCTION__)) == NULL){ + if ((filename = realpath(cv_string_get(cv), NULL)) == NULL){ cli_output(stderr, "Failed to resolve filename\n"); goto done; } - filename = vecp[0]; if (stat(filename, &st) < 0){ clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s", filename, strerror(errno)); @@ -668,14 +666,8 @@ load_config_filev(clicon_handle h, // } ret = 0; done: - unchunk_group(__FUNCTION__); - if (vecp){ - if (vecp[0]) - free(vecp[0]); - if (vecp[1]) - free(vecp[1]); - free(vecp); - } + if (filename) + free(filename); if (xt) xml_free(xt); if (fd != -1) @@ -704,8 +696,7 @@ save_config_filev(clicon_handle h, cvec *argv) { int retval = -1; - char **vecp; - char *filename; + char *filename = NULL; cg_var *cv; char *dbstr; char *varstr; @@ -732,11 +723,10 @@ save_config_filev(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } - if ((vecp = clicon_realpath(NULL, cv_string_get(cv), __FUNCTION__)) == NULL){ + if ((filename = realpath(cv_string_get(cv), NULL)) == NULL){ cli_output(stderr, "Failed to resolve filename\n"); goto done; } - filename = vecp[0]; if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0) goto done; if ((f = fopen(filename, "wb")) == NULL){ @@ -748,7 +738,8 @@ save_config_filev(clicon_handle h, retval = 0; /* Fall through */ done: - unchunk_group(__FUNCTION__); + if (filename) + free(filename); if (xt) xml_free(xt); if (f != NULL) diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 4a47ba78..23e179ce 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -54,6 +54,7 @@ #include #include #include +#include /* cligen */ #include @@ -69,7 +70,7 @@ #include "cli_handle.h" /* Command line options to be passed to getopt(3) */ -#define CLI_OPTS "hD:f:F:1u:d:m:qpGLl:" +#define CLI_OPTS "hD:f:F:1u:d:m:qpGLl:y:" /*! terminate cli application */ static int @@ -150,7 +151,8 @@ usage(char *argv0, clicon_handle h) "\t-p \t\tPrint database yang specification\n" "\t-G \t\tPrint CLI syntax generated from dbspec (if CLICON_CLI_GENMODEL enabled)\n" "\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n" - "\t-l \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n", + "\t-l \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n" + "\t-y \tOverride yang spec file (dont include .yang suffix)\n", argv0, confsock ? confsock : "none", plgdir ? plgdir : "none" @@ -288,6 +290,15 @@ main(int argc, char **argv) case 'L' : /* Debug print dynamic CLI syntax */ logclisyntax++; break; + case 'y' :{ /* yang module */ + /* Set revision to NULL, extract dir and module */ + char *str = strdup(optarg); + char *dir = dirname(str); + hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION"); + clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(optarg)); + clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir)); + break; + } default: usage(argv[0], h); break; diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index e585a273..de90a128 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -56,6 +56,7 @@ #include #include #include +#include /* cligen */ #include @@ -70,7 +71,7 @@ #include "netconf_rpc.h" /* Command line options to be passed to getopt(3) */ -#define NETCONF_OPTS "hDqf:d:S" +#define NETCONF_OPTS "hDqf:d:Sy:" /*! Process incoming packet * @param[in] h Clicon handle @@ -286,7 +287,8 @@ usage(clicon_handle h, "\t-q\t\tQuiet: dont send hello prompt\n" "\t-f \tConfiguration file (mandatory)\n" "\t-d \tSpecify netconf plugin directory dir (default: %s)\n" - "\t-S\t\tLog on syslog\n", + "\t-S\t\tLog on syslog\n" + "\t-y \tOverride yang spec file (dont include .yang suffix)\n", argv0, netconfdir ); @@ -359,6 +361,15 @@ main(int argc, char **argv) usage(h, argv[0]); clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg); break; + case 'y' :{ /* yang module */ + /* Set revision to NULL, extract dir and module */ + char *str = strdup(optarg); + char *dir = dirname(str); + hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION"); + clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(optarg)); + clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir)); + break; + } default: usage(h, argv[0]); break; diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 41619c31..74642c82 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -59,6 +59,7 @@ #include #include #include +#include /* cligen */ #include @@ -71,7 +72,7 @@ #include "restconf_methods.h" /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hDf:p:" +#define RESTCONF_OPTS "hDf:p:y:" /* Should be discovered via "/.well-known/host-meta" resource ([RFC6415]) */ @@ -243,7 +244,8 @@ usage(clicon_handle h, "\t-h \t\tHelp\n" "\t-D \t\tDebug. Log to syslog\n" "\t-f \tConfiguration file (mandatory)\n" - "\t-d \tSpecify restconf plugin directory dir (default: %s)\n", + "\t-d \tSpecify restconf plugin directory dir (default: %s)\n" + "\t-y \tOverride yang spec file (dont include .yang suffix)\n", argv0, restconfdir ); @@ -264,6 +266,7 @@ main(int argc, char *sockpath; char *path; clicon_handle h; + char *yangspec=NULL; /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_SYSLOG); @@ -289,6 +292,9 @@ main(int argc, usage(h, argv[0]); clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg); break; + case 'y' : /* yang module */ + yangspec = optarg; + break; default: usage(h, argv[0]); break; @@ -312,6 +318,15 @@ main(int argc, if (clicon_options_main(h) < 0) goto done; + /* Overwrite yang module with -y option */ + if (yangspec){ + /* Set revision to NULL, extract dir and module */ + char *str = strdup(yangspec); + char *dir = dirname(str); + hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION"); + clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(yangspec)); + clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir)); + } /* Initialize plugins group */ if (restconf_plugin_load(h) < 0) return -1; diff --git a/lib/clixon/clixon_file.h b/lib/clixon/clixon_file.h index e3f807fc..7a57119b 100644 --- a/lib/clixon/clixon_file.h +++ b/lib/clixon/clixon_file.h @@ -37,8 +37,6 @@ #define _CLIXON_FILE_H_ -char **clicon_realpath(const char *cwd, char *path, const char *label); - int clicon_file_dirent(const char *dir, struct dirent **ent, const char *regexp, mode_t type, const char *label); diff --git a/lib/src/clixon_file.c b/lib/src/clixon_file.c index 1b96ed08..64ac8ee1 100644 --- a/lib/src/clixon_file.c +++ b/lib/src/clixon_file.c @@ -62,108 +62,6 @@ #include "clixon_string.h" #include "clixon_file.h" -/*! Resolve the real path of a given 'path', following symbolic links and '../'. - * If 'path' relative, it will be resolved based on the currnt working - * directory 'cwd'. The response is a 2 entry vector of strings. The first - * entry is the resolved path and the second is the part of the path which - * actually exist. - * @retval vec - */ -char ** -clicon_realpath(const char *cwd, - char *path, - const char *label) -{ - char **ret = NULL; - char *rest; - char **vec = NULL; - char **vec2; - int nvec, nvec2; - char *p; - char *rp = NULL; - char *ptr; - int i; - struct passwd *pwd; - char cwdbuf[PATH_MAX]; - - /* Prepend 'cwd' if not absolute */ - if (path[0] == '/') - p = path; - else { - if (cwd == NULL || strlen(cwd) == 0) - cwd = getcwd(cwdbuf, sizeof(cwdbuf)); - else if (cwd[0] == '~') { - if((pwd = getpwuid(getuid())) == NULL) - goto catch; - cwd = pwd->pw_dir; - } - p = chunk_sprintf(__FUNCTION__, "%s%s/%s", - (cwd[0]=='/' ? "" : "/"), cwd, path); - } - if (p == NULL) - goto catch; - - /* Make a local copy of 'path' */ - if ((path = chunkdup(p, strlen(p)+1, __FUNCTION__)) == NULL) - goto catch; - - /* Find the smallest portion of the path that exist and run realpath() */ - while(strlen(p) && ((rp = realpath(p, NULL)) == NULL)) { - if((ptr = strrchr(p, '/')) == NULL) - break; - *ptr = '\0'; - } - if(rp == NULL) - goto catch; - - /* Use the result of realpath() and the rest of 'path' untouched, to - form a new path */ - rest = path + strlen(p); - ptr = chunk_sprintf(__FUNCTION__, "%s%s", rp, rest); - p = ptr; - - /* Split path based on '/'. Loop through vector from the end and copy - each entry into a new vector, skipping '..' and it's previous directory - as well as all '.' */ - if ((vec = clicon_strsep(p, "/", &nvec)) == NULL) - goto catch; - if ((vec2 = malloc(nvec * sizeof(char *))) == NULL) - goto catch; - nvec2 = i = nvec; - while(--i >= 0) { - if(strcmp(vec[i], "..") == 0) - i--; /* Skip previous */ - else if(strcmp(vec[i], ".") == 0) - /* do nothing */ ; - else - vec2[--nvec2] = vec[i]; - } - - /* Create resulting vector */ - if ((ret = malloc(sizeof(char *) * 2)) != NULL) { - if((ret[0] = clicon_strjoin(nvec-nvec2, &vec2[nvec2], "/")) == NULL) { - free(ret); - ret = NULL; - } - if ((ret[1] = strdup(rp)) == NULL) { - free(ret[0]); - free(ret); - ret = NULL; - } - } - -catch: - if(rp) - free(rp); - if(vec) - free(vec); - if(vec2) - free(vec2); - unchunk_group(__FUNCTION__); - return ret; -} - - /* * qsort function */ @@ -354,24 +252,3 @@ clicon_file_copy(char *src, } -#ifdef NOTUSED -/* - * (un)lock a whole file. - * Arguments: - * fd - File descriptor - * cmd - F_GETLK, F_SETLK, F_SETLKW - * type - F_RDLCK, F_WRLCK, F_UNLCK - */ -int -file_lock(int fd, int cmd, int type) -{ - struct flock lock; - - lock.l_type = type; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - - return fcntl(fd, cmd, &lock); -} -#endif diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index ecf1431b..66c9c6b4 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -65,7 +65,7 @@ * @param[in] string String to be split * @param[in] delim String of delimiter characters * @param[out] nvec Number of entries in returned vector - * @retval vec Vector of strings. Free after use + * @retval vec Vector of strings. NULL terminated. Free after use * @retval NULL Error * */ char ** @@ -78,6 +78,7 @@ clicon_strsep(char *string, char *p; int nvec = 1; int i; + size_t siz; char *s; char *d; @@ -89,11 +90,13 @@ clicon_strsep(char *string, s++; } /* alloc vector and append copy of string */ - if ((vec = (char**)malloc(nvec* sizeof(char*) + strlen(string)+1)) == NULL){ + siz = (nvec+1)* sizeof(char*) + strlen(string)+1; + if ((vec = (char**)malloc(siz)) == NULL){ clicon_err(OE_UNIX, errno, "malloc"); goto done; } - ptr = (char*)vec + nvec* sizeof(char*); /* this is where ptr starts */ + memset(vec, 0, siz); + ptr = (char*)vec + (nvec+1)* sizeof(char*); /* this is where ptr starts */ strncpy(ptr, string, strlen(string)+1); i = 0; while ((p = strsep(&ptr, delim)) != NULL) @@ -190,10 +193,10 @@ main(int argc, char **argv) return 0; } str0 = argv[1]; - if ((vec = clicon_strsep("a b\tc", " \t", &nvec)) == NULL) + if ((vec = clicon_strsep(str0, " \t", &nvec)) == NULL) return -1; fprintf(stderr, "nvec: %d\n", nvec); - for (i=0; iys_argument, reason?reason:""); - if (reason) - free(reason); - goto done; + if ((body = xml_body(xt)) != NULL){ + if (cv_parse(body, cv) <0){ + clicon_err(OE_UNIX, errno, "cv_parse"); + goto done; + } + if ((ys_cv_validate(cv, ys, &reason)) != 1){ + clicon_err(OE_DB, 0, + "validation of %s failed %s", + ys->ys_argument, reason?reason:""); + if (reason) + free(reason); + goto done; + } } break; default: From 1e92304a52846bf7f946e409ceceb4d60b17ac4d Mon Sep 17 00:00:00 2001 From: Olof Hagsand Date: Wed, 5 Apr 2017 13:27:02 +0200 Subject: [PATCH 10/13] show_configuration and cli_copy_object added as generic cli commands --- CHANGELOG | 2 +- apps/cli/cli_common.c | 173 ++++++++++++++++++++++++++++++------ apps/cli/cli_show.c | 132 +++++++++++++++++++++++++-- apps/cli/clixon_cli_api.h | 3 + lib/clixon/clixon_options.h | 4 +- lib/clixon/clixon_xml_map.h | 2 +- lib/src/clixon_xml_map.c | 42 ++++----- 7 files changed, 297 insertions(+), 61 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 053ce8e9..4c2b8dd1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,7 +29,7 @@ # # ***** END LICENSE BLOCK ***** - +- show_configuration and cli_copy_object added as generic cli commands - Alternative yang spec option -y added to all applications - Many clicon special string functions have been removed - The netconf support has been extended with lock/unlock diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 9493ed8c..49496c97 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -627,10 +627,7 @@ load_config_filev(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } - if ((filename = realpath(cv_string_get(cv), NULL)) == NULL){ - cli_output(stderr, "Failed to resolve filename\n"); - goto done; - } + filename = cv_string_get(cv); if (stat(filename, &st) < 0){ clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s", filename, strerror(errno)); @@ -666,8 +663,6 @@ load_config_filev(clicon_handle h, // } ret = 0; done: - if (filename) - free(filename); if (xt) xml_free(xt); if (fd != -1) @@ -723,10 +718,7 @@ save_config_filev(clicon_handle h, clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr); goto done; } - if ((filename = realpath(cv_string_get(cv), NULL)) == NULL){ - cli_output(stderr, "Failed to resolve filename\n"); - goto done; - } + filename = cv_string_get(cv); if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0) goto done; if ((f = fopen(filename, "wb")) == NULL){ @@ -738,8 +730,6 @@ save_config_filev(clicon_handle h, retval = 0; /* Fall through */ done: - if (filename) - free(filename); if (xt) xml_free(xt); if (f != NULL) @@ -815,6 +805,7 @@ cli_notification_cb(int s, int retval = -1; cxobj *xt = NULL; cxobj *xe; + cxobj *x; enum format_enum format = (enum format_enum)arg; /* get msg (this is the reason this function is called) */ @@ -829,22 +820,26 @@ cli_notification_cb(int s, } if (clicon_msg_decode(reply, &xt) < 0) goto done; - xe = xpath_first(xt, "//event"); - switch (format){ - case FORMAT_XML: - if (xml_print(stdout, xe) < 0) - goto done; - break; - case FORMAT_TEXT: - if (xml2txt(stdout, xe, 0) < 0) - goto done; - break; - case FORMAT_JSON: - if (xml2json(stdout, xe, 0) < 0) - goto done; - break; - default: - break; + if ((xe = xpath_first(xt, "//event")) != NULL){ + x = NULL; + while ((x = xml_child_each(xe, x, -1)) != NULL) { + switch (format){ + case FORMAT_XML: + if (clicon_xml2file(stdout, x, 0, 1) < 0) + goto done; + break; + case FORMAT_TEXT: + if (xml2txt(stdout, x, 0) < 0) + goto done; + break; + case FORMAT_JSON: + if (xml2json(stdout, x, 1) < 0) + goto done; + break; + default: + break; + } + } } retval = 0; done: @@ -963,6 +958,127 @@ cli_unlock(clicon_handle h, return retval; } +/*! Copy one configuration object to antother + * + * Works for objects that are items ina yang list with a keyname, eg as: + * list sender{ + * key name; + * leaf name{... + * + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] argv Vector: , , , , + * Explanation of argv fields: + * db: Database name, eg candidate|tmp|startup + * xpath: XPATH expression with exactly two %s pointing to field and from name + * field: Name of list key, eg name + * fromvar:Name of variable containing name of object to copy from (given by xpath) + * tovar: Name of variable containing name of object to copy to. + * @code + * cli spec: + * copy snd to , copy_object("candidate", "/sender[%s=%s]", "from", "n1", "n2"); + * cli command: + * copy snd from to to + * @endcode + */ +int +cli_copy_object(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + int retval = -1; + char *db; + cxobj *x1 = NULL; + cxobj *x2 = NULL; + cxobj *x; + char *xpath; + int i; + int j; + cbuf *cb = NULL; + char *keyname; + char *fromvar; + cg_var *fromcv; + char *fromname = NULL; + char *tovar; + cg_var *tocv; + char *toname; + + if (cvec_len(argv) != 5){ + clicon_err(OE_PLUGIN, 0, "%s: Requires four elements: ", __FUNCTION__); + goto done; + } + /* First argv argument: Database */ + db = cv_string_get(cvec_i(argv, 0)); + /* Second argv argument: xpath */ + xpath = cv_string_get(cvec_i(argv, 1)); + /* Third argv argument: name of keyname */ + keyname = cv_string_get(cvec_i(argv, 2)); + /* Fourth argv argument: from variable */ + fromvar = cv_string_get(cvec_i(argv, 3)); + /* Fifth argv argument: to variable */ + tovar = cv_string_get(cvec_i(argv, 4)); + + /* Get from variable -> cv -> from name */ + if ((fromcv = cvec_find_var(cvv, fromvar)) == NULL){ + clicon_err(OE_PLUGIN, 0, "fromvar '%s' not found in cligen var list", fromvar); + goto done; + } + /* Get from name from cv */ + fromname = cv_string_get(fromcv); + /* Create xpath */ + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } + /* Sanity check that xpath contains exactly one %s */ + j = 0; + for (i=0; i cv -> to name */ + if ((tocv = cvec_find_var(cvv, tovar)) == NULL){ + clicon_err(OE_PLUGIN, 0, "tovar '%s' not found in cligen var list", tovar); + goto done; + } + toname = cv_string_get(tocv); + /* Create copy xml tree x2 */ + if ((x2 = xml_new("new", NULL)) == NULL) + goto done; + if (xml_copy(x1, x2) < 0) + goto done; + cprintf(cb, "/%s", keyname); + if ((x = xpath_first(x2, cbuf_get(cb))) == NULL){ + clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname); + goto done; + } + x = xml_find(x, "body"); + xml_value_set(x, toname); + /* resuse cb */ + cbuf_reset(cb); + /* create xml copy tree and merge it with database configuration */ + clicon_xml2cbuf(cb, x2, 0, 0); + if (clicon_rpc_edit_config(h, db, OP_MERGE, NULL, cbuf_get(cb)) < 0) + goto done; + retval = 0; + done: + if (cb) + cbuf_free(cb); + if (x1 != NULL) + xml_free(x1); + if (x2 != NULL) + xml_free(x2); + return retval; +} /* Here are backward compatible cligen callback functions used when * the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set. @@ -1045,6 +1161,7 @@ load_config_file(clicon_handle h, free(vec); return retval; } + int save_config_file(clicon_handle h, cvec *cvv, diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index cdba9012..fc0f2913 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -406,9 +406,9 @@ xml2csv(FILE *f, cxobj *x, cvec *cvv) * Utility function used by cligen spec file * @param[in] h CLICON handle * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] + * @param[in] argv A string: [] * @param[out] xt Configuration as xml tree. - * Format of arg: + * Format of argv: * "running", "candidate", "startup" * xpath expression * optional name of variable in cvv. If set, xpath must have a '%s' @@ -620,13 +620,12 @@ show_confv_as_command(clicon_handle h, while ((xc = xml_child_each(xt, xc, -1)) != NULL){ if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) goto done; - xml2cli(stdout, xc, prepend, gt, __FUNCTION__); /* cli syntax */ + xml2cli(stdout, xc, prepend, gt); /* cli syntax */ } retval = 0; done: if (xt) xml_free(xt); - unchunk_group(__FUNCTION__); return retval; } @@ -694,6 +693,129 @@ show_confv_as_csv(clicon_handle h, return show_confv_as_csv1(h, cvv, argv); } +/*! Generic show configuration CLIGEN callback + * Utility function used by cligen spec file + * @param[in] h CLICON handle + * @param[in] cvv Vector of variables from CLIgen command-line + * @param[in] argv String vector: [] + * Format of argv: + * "running"|"candidate"|"startup" + * "text"|"xml"|"json"|"cli"|"netconf" (see format_enum) + * xpath expression, that may contain one %, eg "/sender[name=%s]" + * optional name of variable in cvv. If set, xpath must have a '%s' + * @code + * show config id , show_conf_as("running","xml","iface[name=%s]","n"); + * @endcode + */ +int +show_configuration(clicon_handle h, + cvec *cvv, + cvec *argv) +{ + int retval = -1; + char *db; + char *formatstr; + char *xpath; + enum format_enum format; + cbuf *cbxpath = NULL; + char *attr = NULL; + int i; + int j; + cg_var *cvattr; + char *val = NULL; + cxobj *xt = NULL; + cxobj *xc; + enum genmodel_type gt; + + if (cvec_len(argv) != 3 && cvec_len(argv) != 4){ + clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,]", cvec_len(argv)); + + goto done; + } + /* First argv argument: Database */ + db = cv_string_get(cvec_i(argv, 0)); + /* Second argv argument: Format */ + formatstr = cv_string_get(cvec_i(argv, 1)); + if ((format = format_str2int(formatstr)) < 0){ + clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr); + goto done; + } + /* Third argv argument: xpath */ + xpath = cv_string_get(cvec_i(argv, 2)); + + /* Create XPATH variable string */ + if ((cbxpath = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } + /* Fourth argument is stdarg to xpath format string */ + if (cvec_len(argv) == 4){ + attr = cv_string_get(cvec_i(argv, 3)); + j = 0; + for (i=0; i\n"); + xc = NULL; /* Dont print xt itself */ + while ((xc = xml_child_each(xt, xc, -1)) != NULL) + clicon_xml2file(stdout, xc, 2, 1); + fprintf(stdout, "]]>]]>\n"); + break; + } + retval = 0; +done: + if (xt) + xml_free(xt); + if (val) + free(val); + if (cbxpath) + cbuf_free(cbxpath); + return retval; +} + /*! Show configuration as text given an xpath * Utility function used by cligen spec file * @param[in] h CLICON handle @@ -1092,7 +1214,7 @@ show_conf_as_command(clicon_handle h, cvec *cvv, cg_var *arg, char *prepend) while ((xc = xml_child_each(xt, xc, -1)) != NULL){ if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) goto done; - xml2cli(stdout, xc, prepend, gt, __FUNCTION__); /* cli syntax */ + xml2cli(stdout, xc, prepend, gt); /* cli syntax */ } retval = 0; done: diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 47891c2c..26afcef7 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -92,6 +92,7 @@ int discard_changesv(clicon_handle h, cvec *vars, cvec *argv); int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv); int cli_lock(clicon_handle h, cvec *cvv, cvec *argv); int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv); +int cli_copy_object(clicon_handle h, cvec *cvv, cvec *argv); /* cli_common.c: CLIgen old single arg callbacks */ int cli_set(clicon_handle h, cvec *vars, cg_var *arg); @@ -125,6 +126,8 @@ int show_confv_as_csv(clicon_handle h, cvec *vars, cvec *argv); int show_yangv(clicon_handle h, cvec *vars, cvec *argv); int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv); +int show_configuration(clicon_handle h, cvec *cvv, cvec *argv); + /* cli_show.c: CLIgen old single arg callbacks */ int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg); int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg); diff --git a/lib/clixon/clixon_options.h b/lib/clixon/clixon_options.h index a83b3aa7..a86f618f 100644 --- a/lib/clixon/clixon_options.h +++ b/lib/clixon/clixon_options.h @@ -50,9 +50,7 @@ * Types */ -/* - * enum gensyntx - * Controls how keywords a generated in CLI syntax / prints from obhect model +/*! Controls how keywords a generated in CLI syntax / prints from object model * Example syntax a.b[] $!x $y: * NONE: a b ; * VARS: a b y ; diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index f934c0c1..f90a8e71 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -53,7 +53,7 @@ enum { * Prototypes */ int xml2txt(FILE *f, cxobj *x, int level); -int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char *label); +int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt); int xml_yang_validate(cxobj *xt, yang_stmt *ys) ; int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0); int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index eb7e3499..3379beb9 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -197,23 +197,26 @@ xml2txt(FILE *f, cxobj *x, int level) * @param[in] x XML Parse-tree (to translate) * @param[in] prepend0 Print this text in front of all commands. * @param[in] gt option to steer cli syntax - * @param[in] label Memory chunk allocation label */ int xml2cli(FILE *f, cxobj *x, char *prepend0, - enum genmodel_type gt, - const char *label) + enum genmodel_type gt) { int retval = -1; cxobj *xe = NULL; char *term; - char *prepend; int bool; int nr; int i; + cbuf *cbpre; + /* Create prepend variable string */ + if ((cbpre = cbuf_new()) == NULL){ + clicon_err(OE_PLUGIN, errno, "cbuf_new"); + goto done; + } nr = xml_child_nr(x); if (!nr){ if (xml_type(x) == CX_BODY) @@ -226,10 +229,8 @@ xml2cli(FILE *f, retval = 0; goto done; } - prepend = ""; if (prepend0) - if ((prepend = chunk_sprintf(label, "%s%s", prepend, prepend0)) == NULL) - goto done; + cprintf(cbpre, "%s", prepend0); /* bool determines when to print a variable keyword: !leaf T for all (ie parameter) index GT_NONE F @@ -241,12 +242,11 @@ xml2cli(FILE *f, */ bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS && !xml_index(x)); // bool = (!x->xn_index || gt == GT_ALL); - if (bool && - (prepend = chunk_sprintf(label, "%s%s%s", - prepend, - strlen(prepend)?" ":"", - xml_name(x))) == NULL) - goto done; + if (bool){ + if (cbuf_len(cbpre)) + cprintf(cbpre, " "); + cprintf(cbpre, "%s", xml_name(x)); + } xe = NULL; /* First child is unique, then add that, before looping. */ i = 0; @@ -255,23 +255,19 @@ xml2cli(FILE *f, if (xml_index(xe) && i < nr-1) ; else - if (xml2cli(f, xe, prepend, gt, label) < 0) + if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0) goto done; if (xml_index(xe)){ /* assume index is first, otherwise need one more while */ - if (gt ==GT_ALL && (prepend = chunk_sprintf(label, "%s %s", - prepend, - xml_name(xe))) == NULL) - - goto done; - if ((prepend = chunk_sprintf(label, "%s %s", - prepend, - xml_value(xml_child_i(xe, 0)))) == NULL) - goto done; + if (gt == GT_ALL) + cprintf(cbpre, " %s", xml_name(xe)); + cprintf(cbpre, " %s", xml_value(xml_child_i(xe, 0))); } i++; } retval = 0; done: + if (cbpre) + cbuf_free(cbpre); return retval; } From 31c45e5c62e44fbcdb04accf7bb8efaa1f731aec Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Thu, 6 Apr 2017 10:26:10 +0200 Subject: [PATCH 11/13] removed cli single callback arg code --- CHANGELOG | 50 +- apps/cli/cli_common.c | 315 ++++-------- apps/cli/cli_generate.c | 8 +- apps/cli/cli_plugin.c | 25 +- apps/cli/cli_show.c | 827 ++----------------------------- apps/cli/clixon_cli_api.h | 79 +-- apps/restconf/restconf_main.c | 4 +- apps/restconf/restconf_methods.c | 4 +- clixon.conf.cpp.cpp | 12 - example/routing_cli.cli | 61 +-- lib/src/clixon_proto_client.c | 2 + lib/src/clixon_xml.c | 2 + test/test2.sh | 21 + test/test3.sh | 13 +- test/test4.sh | 65 +++ 15 files changed, 368 insertions(+), 1120 deletions(-) create mode 100755 test/test4.sh diff --git a/CHANGELOG b/CHANGELOG index 4c2b8dd1..3731f1f6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,7 +29,13 @@ # # ***** END LICENSE BLOCK ***** -- show_configuration and cli_copy_object added as generic cli commands +- cli_copy_config added as generic cli command +- cli_show_config added as generic cli command + Replace all show_confv*() and show_conf*() with cli_show_config() + Example: replace: + show_confv_as_json("candidate","/sender"); + with: + cli_show_config("candidate","json","/sender"); - Alternative yang spec option -y added to all applications - Many clicon special string functions have been removed - The netconf support has been extended with lock/unlock @@ -69,18 +75,40 @@ - Netconf startup configuration support. Set CLICON_USE_STARTUP_CONFIG to 1 to enable. Eg, if backend_main is started with -CIr startup will be copied to running. + - Added ".." as valid step in xpath + - Use restconf format for internal xmldb keys. Eg /a/b=3,4 -- List keys with special characters are RFC 3986 encoded. -- Changed example to use multiple cli callbacks -- Added cli multiple callback and expand support. Use options - CLICON_CLIGEN_CALLBACK_SINGLE_ARG and CLICON_CLIGEN_EXPAND_SINGLE_ARG - to control these. - The multiple support for expand callbacks is enabled but not for callbacks - since this causes problems for legacy applications. - If you change to multiple argument callbacks change all cli callback functions. - Library functions in clixon_cli_api.h (e.g cli_commit) is rewritten in new - for (eg cli_commitv). See clixon_cli_api.h for new names. + +- List keys with special characters RFC 3986 encoded. + +- Replaced cli expand functions with single to multiple args + This change is _not_ backward compatible + This effects all calls to expand_dbvar() or user-defined + expand callbacks + +- Replaced cli callback functions with single arg to multiple args + This change is _not_ backward compatible. + You are affected if you + (1) use system callbacks (i.e. in clixon_cli_api.h) + (2) write your own cli callbacks + + If you use cli callbacks, you need to rewrite cli callbacks from eg: + load("Comment") ,load_config_file("filename replace"); + to: + load("Comment") ,load_config_file("filename", "replace"); + + If you write your own, you need to change the callback signature from; + int cli_callback(clicon_handle h, cvec *vars, cg_var *arg) + to: + int cli_callback(clicon_handle h, cvec *vars, cvec *argv) + and rewrite the code to handle argv instead of arg. + These are the system functions affected: + cli_set, cli_merge, cli_del, cli_debug_backend, cli_set_mode, + cli_start_shell, cli_quit, cli_commit, cli_validate, compare_dbs, + load_config_file, save_config_file, delete_all, discard_changes, cli_notify, + show_yang, show_conf_xpath + - Added --with-cligen and --with-qdbm configure options - Added union type check for non-cli (eg xml) input - Empty yang type. Relaxed yang types for unions, eg two strings with different length. diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 49496c97..92882d5b 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -197,10 +197,10 @@ cli_signal_flush(clicon_handle h) * @see cli_callback_generate where arg is generated */ static int -cli_dbxmlv(clicon_handle h, - cvec *cvv, - cvec *argv, - enum operation_type op) +cli_dbxml(clicon_handle h, + cvec *cvv, + cvec *argv, + enum operation_type op) { int retval = -1; char *str = NULL; @@ -253,40 +253,54 @@ cli_dbxmlv(clicon_handle h, } int -cli_setv(clicon_handle h, cvec *cvv, cvec *argv) +cli_set(clicon_handle h, cvec *cvv, cvec *argv) { int retval = 1; - if (cli_dbxmlv(h, cvv, argv, OP_REPLACE) < 0) + if (cli_dbxml(h, cvv, argv, OP_REPLACE) < 0) goto done; retval = 0; done: return retval; } +int cli_setv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_set(h, vars, argv); +} int -cli_mergev(clicon_handle h, cvec *cvv, cvec *argv) +cli_merge(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; - if (cli_dbxmlv(h, cvv, argv, OP_MERGE) < 0) + if (cli_dbxml(h, cvv, argv, OP_MERGE) < 0) goto done; retval = 0; done: return retval; } +int cli_mergev(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_merge(h, vars, argv); +} + int -cli_delv(clicon_handle h, cvec *cvv, cvec *argv) +cli_del(clicon_handle h, cvec *cvv, cvec *argv) { int retval = -1; - if (cli_dbxmlv(h, cvv, argv, OP_REMOVE) < 0) + if (cli_dbxml(h, cvv, argv, OP_REMOVE) < 0) goto done; retval = 0; done: return retval; } +int cli_delv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_del(h, vars, argv); +} + /*! Set debug level on CLI client (not backend daemon) * @param[in] h Clicon handle @@ -296,7 +310,7 @@ cli_delv(clicon_handle h, cvec *cvv, cvec *argv) * _or_ if a 'level' variable is present in vars use that value instead. */ int -cli_debug_cliv(clicon_handle h, +cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv) { @@ -318,6 +332,10 @@ cli_debug_cliv(clicon_handle h, done: return retval; } +int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_debug_cli(h, vars, argv); +} /*! Set debug level on backend daemon (not CLI) * @param[in] h Clicon handle @@ -327,9 +345,9 @@ cli_debug_cliv(clicon_handle h, * _or_ if a 'level' variable is present in vars use that value instead. */ int -cli_debug_backendv(clicon_handle h, - cvec *vars, - cvec *argv) +cli_debug_backend(clicon_handle h, + cvec *vars, + cvec *argv) { int retval = -1; cg_var *cv; @@ -348,11 +366,15 @@ cli_debug_backendv(clicon_handle h, done: return retval; } +int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_debug_backend(h, vars, argv); +} /*! Set syntax mode */ int -cli_set_modev(clicon_handle h, +cli_set_mode(clicon_handle h, cvec *vars, cvec *argv) { @@ -369,14 +391,18 @@ cli_set_modev(clicon_handle h, done: return retval; } +int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_set_mode(h, vars, argv); +} /*! Start bash from cli callback * XXX Application specific?? */ int -cli_start_shellv(clicon_handle h, - cvec *vars, - cvec *argv) +cli_start_shell(clicon_handle h, + cvec *vars, + cvec *argv) { char *cmd; struct passwd *pw; @@ -426,23 +452,31 @@ cli_start_shellv(clicon_handle h, return 0; } +int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_start_shell(h, vars, argv); +} /*! Generic quit callback */ int -cli_quitv(clicon_handle h, +cli_quit(clicon_handle h, cvec *vars, cvec *argv) { cli_set_exiting(h, 1); return 0; } +int cli_quitv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_quit(h, vars, argv); +} /*! Generic commit callback * @param[in] argv No arguments expected */ int -cli_commitv(clicon_handle h, +cli_commit(clicon_handle h, cvec *vars, cvec *argv) { @@ -456,11 +490,15 @@ cli_commitv(clicon_handle h, done: return retval; } +int cli_commitv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_commit(h, vars, argv); +} /*! Generic validate callback */ int -cli_validatev(clicon_handle h, +cli_validate(clicon_handle h, cvec *vars, cvec *argv) { @@ -472,7 +510,10 @@ cli_validatev(clicon_handle h, done: return retval; } - +int cli_validatev(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_validate(h, vars, argv); +} /*! Compare two dbs using XML. Write to file and run diff */ @@ -541,7 +582,7 @@ compare_xmls(cxobj *xc1, * @param[in] arg arg: 0 as xml, 1: as text */ int -compare_dbsv(clicon_handle h, +compare_dbs(clicon_handle h, cvec *cvv, cvec *argv) { @@ -573,6 +614,10 @@ compare_dbsv(clicon_handle h, return retval; } +int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv) +{ + return compare_dbs(h, vars, argv); +} /*! Load a configuration file to candidate database * Utility function used by cligen spec file @@ -589,9 +634,9 @@ compare_dbsv(clicon_handle h, * @see save_config_file */ int -load_config_filev(clicon_handle h, - cvec *cvv, - cvec *argv) +load_config_file(clicon_handle h, + cvec *cvv, + cvec *argv) { int ret = -1; struct stat st; @@ -669,6 +714,10 @@ load_config_filev(clicon_handle h, close(fd); return ret; } +int load_config_filev(clicon_handle h, cvec *vars, cvec *argv) +{ + return load_config_file(h, vars, argv); +} /*! Copy database to local file * Utility function used by cligen spec file @@ -686,9 +735,9 @@ load_config_filev(clicon_handle h, * @see load_config_file */ int -save_config_filev(clicon_handle h, - cvec *cvv, - cvec *argv) +save_config_file(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; char *filename = NULL; @@ -736,14 +785,18 @@ save_config_filev(clicon_handle h, fclose(f); return retval; } +int save_config_filev(clicon_handle h, cvec *vars, cvec *argv) +{ + return save_config_file(h, vars, argv); +} /*! Delete all elements in a database * Utility function used by cligen spec file */ int -delete_allv(clicon_handle h, - cvec *cvv, - cvec *argv) +delete_all(clicon_handle h, + cvec *cvv, + cvec *argv) { char *dbstr; int retval = -1; @@ -765,16 +818,24 @@ delete_allv(clicon_handle h, done: return retval; } +int delete_allv(clicon_handle h, cvec *vars, cvec *argv) +{ + return delete_all(h, vars, argv); +} /*! Discard all changes in candidate and replace with running */ int -discard_changesv(clicon_handle h, - cvec *cvv, - cvec *argv) +discard_changes(clicon_handle h, + cvec *cvv, + cvec *argv) { return clicon_rpc_discard_changes(h); } +int discard_changesv(clicon_handle h, cvec *vars, cvec *argv) +{ + return discard_changes(h, vars, argv); +} /*! Copy from one database to another, eg running->startup * @param[in] argv a string: " " Copy from db1 to db2 @@ -864,9 +925,9 @@ cli_notification_cb(int s, * XXX: format is a memory leak */ int -cli_notifyv(clicon_handle h, - cvec *cvv, - cvec *argv) +cli_notify(clicon_handle h, + cvec *cvv, + cvec *argv) { char *stream = NULL; int retval = -1; @@ -897,6 +958,10 @@ cli_notifyv(clicon_handle h, done: return retval; } +int cli_notifyv(clicon_handle h, cvec *vars, cvec *argv) +{ + return cli_notify(h, vars, argv); +} /*! Lock database * @@ -976,13 +1041,13 @@ cli_unlock(clicon_handle h, * tovar: Name of variable containing name of object to copy to. * @code * cli spec: - * copy snd to , copy_object("candidate", "/sender[%s=%s]", "from", "n1", "n2"); + * copy snd to , cli_copy_config("candidate", "/sender[%s=%s]", "from", "n1", "n2"); * cli command: * copy snd from to to * @endcode */ int -cli_copy_object(clicon_handle h, +cli_copy_config(clicon_handle h, cvec *cvv, cvec *argv) { @@ -1080,176 +1145,6 @@ cli_copy_object(clicon_handle h, return retval; } -/* Here are backward compatible cligen callback functions used when - * the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set. - */ - -cb_single_arg(cli_set) -cb_single_arg(cli_merge) -cb_single_arg(cli_del) -cb_single_arg(cli_debug_cli) -cb_single_arg(cli_debug_backend) -cb_single_arg(cli_set_mode) -cb_single_arg(cli_start_shell) -cb_single_arg(cli_quit) -//cb_single_arg(cli_commit) -int cli_commit(clicon_handle h, cvec *cvv, cg_var *arg) -{ - int retval=-1; - cvec *argv = NULL; - - if (arg){ - if (cv_type_get(arg) > CGV_EMPTY){ - cligen_output(stderr, "%s: Illegal cvtype. This is most probably a single-argument cligen callback being used in a multi-argument setting. This can happen if option CLICON_CLIGEN_CALLBACK_SINGLE_ARG is 0 but you call a single argument callback (eg %s) from a .cli file. Please change to a multi-argument callback\n", __FUNCTION__, __FUNCTION__); - goto done; - } - if ((argv = cvec_from_var(arg)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_from_var"); - goto done; - } - } - retval = cli_commitv(h, cvv, argv); - done: - if (argv) cvec_free(argv); - return retval; -} - -cb_single_arg(cli_validate) -cb_single_arg(compare_dbs) -cb_single_arg(delete_all) -cb_single_arg(discard_changes) - -/* Follows some functions not covered by translation macro */ -int -load_config_file(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - int retval=-1; - cvec *argv; - cg_var *cv; - char *str; - char **vec = NULL; - int nvec; - - /* Split string into two parts and build a cvec of it and supply that to - the multi-arg callback */ - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) - goto done; - if (nvec != 2){ - clicon_err(OE_PLUGIN, 0, "Arg syntax is "); - goto done; - } - if ((argv = cvec_new(nvec)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_from_var"); - goto done; - } - cv = cvec_i(argv, 0); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[0]); - cv = cvec_i(argv, 1); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[1]); - - retval = load_config_filev(h, cvv, argv); - done: - if (vec) - free(vec); - return retval; -} - -int -save_config_file(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - int retval=-1; - cvec *argv; - cg_var *cv; - char *str; - char **vec = NULL; - int nvec; - - /* Split string into two parts and build a cvec of it and supply that to - the multi-arg callback */ - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) - goto done; - if (nvec != 2){ - clicon_err(OE_PLUGIN, 0, "Arg syntax is "); - goto done; - } - if ((argv = cvec_new(nvec)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_from_var"); - goto done; - } - cv = cvec_i(argv, 0); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[0]); - cv = cvec_i(argv, 1); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[1]); - - retval = save_config_filev(h, cvv, argv); - done: - if (vec) - free(vec); - return retval; -} - -int -cli_notify(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - int retval=-1; - cvec *argv; - cg_var *cv; - char *str; - char **vec = NULL; - int nvec; - - /* Split string into two parts and build a cvec of it and supply that to - the multi-arg callback */ - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) - goto done; - if (nvec != 2 && nvec != 3){ - clicon_err(OE_PLUGIN, 0, "Arg syntax is []"); - goto done; - } - if ((argv = cvec_new(nvec)) == NULL){ - clicon_err(OE_UNIX, errno, "cvec_from_var"); - goto done; - } - cv = cvec_i(argv, 0); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[0]); - - cv = cvec_i(argv, 1); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[1]); - if (nvec > 2){ - cv = cvec_i(argv, 2); - cv_type_set(cv, CGV_STRING); - cv_string_set(cv, vec[2]); - } - retval = cli_notifyv(h, cvv, argv); - done: - if (vec) - free(vec); - return retval; -} /*! set debug level on stderr (not syslog). * The level is either what is specified in arg as int argument. diff --git a/apps/cli/cli_generate.c b/apps/cli/cli_generate.c index bf7d9630..b37babad 100644 --- a/apps/cli/cli_generate.c +++ b/apps/cli/cli_generate.c @@ -65,10 +65,9 @@ /* This is the default callback function. But this is typically overwritten */ #define GENERATE_CALLBACK "cli_set" -#define GENERATE_CALLBACKV "cli_setv" /* variable expand function */ -#define GENERATE_EXPAND_XMLDB "expandv_dbvar" +#define GENERATE_EXPAND_XMLDB "expand_dbvar" /*===================================================================== * YANG generate CLI @@ -154,10 +153,7 @@ cli_callback_generate(clicon_handle h, if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0) goto done; - if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1) - cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt); - else - cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACKV, xkfmt); + cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt); retval = 0; done: if (xkfmt) diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 4c3f75bf..cbb68cbd 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -372,26 +372,13 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir) } /* Resolve callback names to function pointers. */ - if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1){ - if (cligen_callback_str2fn(pt, (cg_str2fn_t*)clixon_str2fn, handle) < 0){ - clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)", - filename, plgnam, plgnam); - goto done; - } - } - else - if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){ - clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)", - filename, plgnam, plgnam); - goto done; - } - if (clicon_option_int(h, "CLICON_CLIGEN_EXPAND_SINGLE_ARG")==1){ - if (cligen_expand_str2fn(pt, (expand_str2fn_t*)clixon_str2fn, handle) < 0) - goto done; + if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){ + clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)", + filename, plgnam, plgnam); + goto done; } - else - if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0) - goto done; + if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0) + goto done; /* Make sure we have a syntax mode specified */ if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */ diff --git a/apps/cli/cli_show.c b/apps/cli/cli_show.c index fc0f2913..392493f3 100644 --- a/apps/cli/cli_show.c +++ b/apps/cli/cli_show.c @@ -74,9 +74,6 @@ #include "clixon_cli_api.h" #include "cli_common.h" /* internal functions */ -static int xml2csv(FILE *f, cxobj *x, cvec *cvv); -//static int xml2csv_raw(FILE *f, cxobj *x); - /*! Completion callback intended for automatically generated data model * * Returns an expand-type list of commands as used by cligen 'expand' @@ -94,12 +91,12 @@ static int xml2csv(FILE *f, cxobj *x, cvec *cvv); * XXX: helptexts? */ int -expandv_dbvar(void *h, - char *name, - cvec *cvv, - cvec *argv, - cvec *commands, - cvec *helptexts) +expand_dbvar(void *h, + char *name, + cvec *cvv, + cvec *argv, + cvec *commands, + cvec *helptexts) { int retval = -1; char *xkfmt; @@ -197,13 +194,24 @@ expandv_dbvar(void *h, free(xkpath); return retval; } - - - +int +expandv_dbvar(void *h, + char *name, + cvec *cvv, + cvec *argv, + cvec *commands, + cvec *helptexts) +{ + return expand_dbvar(h, name, cvv, argv, commands, helptexts); +} /*! List files in a directory */ int -expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail) +expand_dir(char *dir, + int *nr, + char ***commands, + mode_t flags, + int detail) { DIR *dirp; struct dirent *dp; @@ -313,13 +321,11 @@ expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail) return retval; } - - /*! CLI callback show yang spec. If arg given matches yang argument string */ int -show_yangv(clicon_handle h, - cvec *cvv, - cvec *argv) +show_yang(clicon_handle h, + cvec *cvv, + cvec *argv) { yang_node *yn; char *str = NULL; @@ -335,362 +341,9 @@ show_yangv(clicon_handle h, yang_print(stdout, yn, 0); return 0; } - - -#ifdef notused -/*! XML to CSV raw variant - * @see xml2csv - */ -static int -xml2csv_raw(FILE *f, cxobj *x) +int show_yangv(clicon_handle h, cvec *vars, cvec *argv) { - cxobj *xc; - cxobj *xb; - int retval = -1; - int i = 0; - - xc = NULL; - while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) { - if (xml_child_nr(xc)){ - xb = xml_child_i(xc, 0); - if (xml_type(xb) == CX_BODY){ - if (i++) - fprintf(f, ";"); - fprintf(f, "%s", xml_value(xb)); - } - } - } - fprintf(f, "\n"); - retval = 0; - return retval; -} -#endif - -/*! Translate XML -> CSV commands - * Can only be made in a 'flat tree', ie on the form: - * B --> - * Type, A - * X, B - * @param[in] f Output file - * @param[in] x XML tree - * @param[in] cvv A vector of field names present in XML - * This means that only fields in x that are listed in cvv will be printed. - */ -static int -xml2csv(FILE *f, cxobj *x, cvec *cvv) -{ - cxobj *xe, *xb; - int retval = -1; - cg_var *vs; - - fprintf(f, "%s", xml_name(x)); - xe = NULL; - - vs = NULL; - while ((vs = cvec_each(cvv, vs))) { - if ((xe = xml_find(x, cv_name_get(vs))) == NULL){ - fprintf(f, ";"); - continue; - } - if (xml_child_nr(xe)){ - xb = xml_child_i(xe, 0); - fprintf(f, ";%s", xml_value(xb)); - } - } - fprintf(f, "\n"); - retval = 0; - return retval; -} - -/*! Generic function for showing configurations. - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] argv A string: [] - * @param[out] xt Configuration as xml tree. - * Format of argv: - * "running", "candidate", "startup" - * xpath expression - * optional name of variable in cvv. If set, xpath must have a '%s' - * @code - * show config id , show_conf_as("running interfaces/interface[name=%s] n"); - * @endcode - */ -static int -show_confv_as(clicon_handle h, - cvec *cvv, - cvec *argv, - cxobj **xt) /* top xml */ -{ - int retval = -1; - char *db; - char *xpath; - char *attr = NULL; - cbuf *cbx = NULL; - int i; - int j; - cg_var *cvattr; - char *val = NULL; - - if (cvec_len(argv) != 2 && cvec_len(argv) != 3){ - if (cvec_len(argv)==1) - clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \",[,]\"", cv_string_get(cvec_i(argv,0))); - else - clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,[,]", cvec_len(argv)); - - goto done; - } - /* Dont get attr here, take it from arg instead */ - db = cv_string_get(cvec_i(argv, 0)); - if (strcmp(db, "running") != 0 && - strcmp(db, "candidate") != 0 && - strcmp(db, "startup") != 0) { - clicon_err(OE_PLUGIN, 0, "No such db name: %s", db); - goto done; - } - xpath = cv_string_get(cvec_i(argv, 1)); - if ((cbx = cbuf_new()) == NULL){ - clicon_err(OE_PLUGIN, errno, "cbuf_new"); - goto done; - } - if (cvec_len(argv) == 3){ - attr = cv_string_get(cvec_i(argv, 2)); - j = 0; - for (i=0; i [] - * @param[in] netconf If set print as netconf edit-config, otherwise just xml - * @see show_conf_as the main function - */ -static int -show_confv_as_xml1(clicon_handle h, - cvec *cvv, - cvec *argv, - int netconf) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - - if (show_confv_as(h, cvv, argv, &xt) < 0) - goto done; - if (netconf) /* netconf prefix */ - fprintf(stdout, "\n"); - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - clicon_xml2file(stdout, xc, netconf?2:0, 1); - if (netconf) /* netconf postfix */ - fprintf(stdout, "]]>]]>\n"); - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; -} - -/*! Show configuration as prettyprinted xml - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] - * @see show_conf_as the main function - */ -int -show_confv_as_xml(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - return show_confv_as_xml1(h, cvv, argv, 0); -} - -/*! Show configuration as prettyprinted xml with netconf hdr/tail - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] - * @see show_conf_as the main function - */ -int -show_confv_as_netconf(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - return show_confv_as_xml1(h, cvv, argv, 1); -} - -/*! Show configuration as JSON - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] - * @see show_conf_as the main function - */ -int -show_confv_as_json(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - cxobj *xt = NULL; - int retval = -1; - - if (show_confv_as(h, cvv, argv, &xt) < 0) - goto done; - xml2json(stdout, xt, 1); - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; -} - - -/*! Show configuration as text - * Utility function used by cligen spec file - */ -static int -show_confv_as_text1(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - - if (show_confv_as(h, cvv, argv, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - xml2txt(stdout, xc, 0); /* tree-formed text */ - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; -} - - -/* Show configuration as commands, ie not tree format but as one-line commands - */ -static int -show_confv_as_command(clicon_handle h, - cvec *cvv, - cvec *argv, - char *prepend) -{ - cxobj *xt = NULL; - cxobj *xc; - enum genmodel_type gt; - int retval = -1; - - if (show_confv_as(h, cvv, argv, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL){ - if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) - goto done; - xml2cli(stdout, xc, prepend, gt); /* cli syntax */ - } - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; -} - -int -show_confv_as_text(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - return show_confv_as_text1(h, cvv, argv); -} - -int -show_confv_as_cli(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - return show_confv_as_command(h, cvv, argv, NULL); /* XXX: how to set prepend? */ -} - -static int -show_confv_as_csv1(clicon_handle h, - cvec *cvv0, - cvec *argv) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - cvec *cvv=NULL; - char *str; - - if (show_confv_as(h, cvv0, argv, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL){ - if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL) - goto done; -#ifdef NOTYET /* yang-spec? */ - if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){ - cg_var *vs; - fprintf(stdout, "Type"); - cvv = db_spec2cvec(ds); - vs = NULL; - while ((vs = cvec_each(cvv, vs))) - fprintf(stdout, ";%s", cv_name_get(vs)); - fprintf(stdout, "\n"); - } /* Now values just need to follow,... */ -#endif /* yang-spec? */ - if (cvv== NULL) - goto done; - xml2csv(stdout, xc, cvv); /* csv syntax */ - } - retval = 0; - done: - if (xt) - xml_free(xt); - unchunk_group(__FUNCTION__); - return retval; -} - -int -show_confv_as_csv(clicon_handle h, - cvec *cvv, - cvec *argv) -{ - return show_confv_as_csv1(h, cvv, argv); + return show_yang(h, vars, argv); } /*! Generic show configuration CLIGEN callback @@ -704,13 +357,13 @@ show_confv_as_csv(clicon_handle h, * xpath expression, that may contain one %, eg "/sender[name=%s]" * optional name of variable in cvv. If set, xpath must have a '%s' * @code - * show config id , show_conf_as("running","xml","iface[name=%s]","n"); + * show config id , cli_show_config("running","xml","iface[name=%s]","n"); * @endcode */ int -show_configuration(clicon_handle h, - cvec *cvv, - cvec *argv) +cli_show_config(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; char *db; @@ -824,9 +477,9 @@ done: * @note Hardcoded that a variable in cvv is named "xpath" */ int -show_confv_xpath(clicon_handle h, - cvec *cvv, - cvec *argv) +show_conf_xpath(clicon_handle h, + cvec *cvv, + cvec *argv) { int retval = -1; char *str; @@ -866,417 +519,7 @@ done: xml_free(xt); return retval; } - - -/*================================================================= - * Here are backward compatible cligen callback functions used when - * the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set. - */ - -cb_single_arg(show_yang) - -/*! This is obsolete version of expandv_dbvar - * If CLICON_CLIGEN_EXPAND_SINGLE_ARG is set -*/ -int -expand_dbvar(void *h, - char *name, - cvec *cvv, - cg_var *arg, - int *nr, - char ***commands, - char ***helptexts) +int show_confv_xpath(clicon_handle h, cvec *vars, cvec *argv) { - int nvec; - char **vec = NULL; - int retval = -1; - char *xkfmt; - char *str; - char *dbstr; - cxobj *xt = NULL; - char *xkpath = NULL; - cxobj **xvec = NULL; - size_t xlen = 0; - cxobj *x; - char *bodystr; - int i; - int j; - int k; - int i0; - - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - /* In the example, str = "candidate /x/m1/%s/b" */ - if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) - goto done; - dbstr = vec[0]; - if (strcmp(dbstr, "running") != 0 && - strcmp(dbstr, "candidate") != 0 && - strcmp(dbstr, "startup") != 0){ - clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr); - goto done; - } - xkfmt = vec[1]; - /* xkfmt = /interface=%s/address=%s - --> /interface=eth0/address=1.2.3.4 - */ - if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0) - goto done; - if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0) - goto done; - if (xpath_vec(xt, xkpath, &xvec, &xlen) < 0) - goto done; - /* One round to detect duplicates - * XXX The code below would benefit from some cleanup - */ - j = 0; - for (i = 0; i < xlen; i++) { - char *str; - x = xvec[i]; - if (xml_type(x) == CX_BODY) - bodystr = xml_value(x); - else - bodystr = xml_body(x); - if (bodystr == NULL){ - clicon_err(OE_CFG, 0, "No xml body"); - goto done; - } - /* detect duplicates */ - for (k=0; k [] - * @param[out] xt Configuration as xml tree. - * Format of arg: - * "running", "candidate", "startup" - * xpath expression - * optional name of variable in cvv. If set, xpath must have a '%s' - * @code - * show config id , show_conf_as("running interfaces/interface[name=%s] n"); - * @endcode - */ -static int -show_conf_as(clicon_handle h, - cvec *cvv, - cg_var *arg, - cxobj **xt) /* top xml */ -{ - int retval = -1; - char *db; - char **vec = NULL; - int nvec; - char *str; - char *xpath; - char *attr = NULL; - cbuf *cbx = NULL; - int i; - int j; - cg_var *cvattr; - char *val = NULL; - - if (arg == NULL || (str = cv_string_get(arg)) == NULL){ - clicon_err(OE_PLUGIN, 0, "%s: requires string argument", __FUNCTION__); - goto done; - } - if ((vec = clicon_strsep(str, " ", &nvec)) == NULL) - goto done; - if (nvec != 2 && nvec != 3){ - clicon_err(OE_PLUGIN, 0, "format error \"%s\" - expected [] got %d arg", str, nvec); - goto done; - } - /* Dont get attr here, take it from arg instead */ - db = vec[0]; - if (strcmp(db, "running") != 0 && - strcmp(db, "candidate") != 0 && - strcmp(db, "startup") != 0) { - clicon_err(OE_PLUGIN, 0, "No such db name: %s", db); - goto done; - } - xpath = vec[1]; - if ((cbx = cbuf_new()) == NULL){ - clicon_err(OE_PLUGIN, errno, "cbuf_new"); - goto done; - } - if (nvec == 3){ - attr = vec[2]; - j = 0; - for (i=0; i [] - * @param[in] netconf If set print as netconf edit-config, otherwise just xml - * @see show_conf_as the main function - */ -static int -show_conf_as_xml1(clicon_handle h, - cvec *cvv, - cg_var *arg, - int netconf) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - - if (show_conf_as(h, cvv, arg, &xt) < 0) - goto done; - if (netconf) /* netconf prefix */ - fprintf(stdout, "\n"); - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - clicon_xml2file(stdout, xc, netconf?2:0, 1); - if (netconf) /* netconf postfix */ - fprintf(stdout, "]]>]]>\n"); - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; - -} - -/*! Show configuration as prettyprinted xml - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] - * @see show_conf_as the main function - */ -int -show_conf_as_xml(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - return show_conf_as_xml1(h, cvv, arg, 0); -} - -/*! Show configuration as prettyprinted xml with netconf hdr/tail - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] - * @see show_conf_as the main function - */ -int -show_conf_as_netconf(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - return show_conf_as_xml1(h, cvv, arg, 1); -} - -/*! Show configuration as JSON - * Utility function used by cligen spec file - * @param[in] h CLICON handle - * @param[in] cvv Vector of variables from CLIgen command-line - * @param[in] arg A string: [] - * @see show_conf_as the main function - */ -int -show_conf_as_json(clicon_handle h, - cvec *cvv, - cg_var *arg) -{ - cxobj *xt = NULL; - int retval = -1; - - if (show_conf_as(h, cvv, arg, &xt) < 0) - goto done; - xml2json(stdout, xt, 1); - retval = 0; - done: - if (xt) - xml_free(xt); - return retval; -} - - -/*! Show configuration as text - * Utility function used by cligen spec file - */ -static int -show_conf_as_text1(clicon_handle h, cvec *cvv, cg_var *arg) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - - if (show_conf_as(h, cvv, arg, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL) - xml2txt(stdout, xc, 0); /* tree-formed text */ - retval = 0; - done: - if (xt) - xml_free(xt); - unchunk_group(__FUNCTION__); - return retval; -} - - -/* Show configuration as commands, ie not tree format but as one-line commands - */ -static int -show_conf_as_command(clicon_handle h, cvec *cvv, cg_var *arg, char *prepend) -{ - cxobj *xt = NULL; - cxobj *xc; - enum genmodel_type gt; - int retval = -1; - - if ((xt = xml_new("tmp", NULL)) == NULL) - goto done; - if (show_conf_as(h, cvv, arg, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL){ - if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR) - goto done; - xml2cli(stdout, xc, prepend, gt); /* cli syntax */ - } - retval = 0; - done: - if (xt) - xml_free(xt); - unchunk_group(__FUNCTION__); - return retval; -} - -int -show_conf_as_text(clicon_handle h, cvec *cvv, cg_var *arg) -{ - return show_conf_as_text1(h, cvv, arg); -} - -int -show_conf_as_cli(clicon_handle h, cvec *cvv, cg_var *arg) -{ - return show_conf_as_command(h, cvv, arg, NULL); /* XXX: how to set prepend? */ -} - -static int -show_conf_as_csv1(clicon_handle h, cvec *cvv0, cg_var *arg) -{ - cxobj *xt = NULL; - cxobj *xc; - int retval = -1; - cvec *cvv=NULL; - char *str; - - if (show_conf_as(h, cvv0, arg, &xt) < 0) - goto done; - xc = NULL; /* Dont print xt itself */ - while ((xc = xml_child_each(xt, xc, -1)) != NULL){ - if ((str = chunk_sprintf(__FUNCTION__, "%s[]", xml_name(xc))) == NULL) - goto done; -#ifdef NOTYET /* yang-spec? */ - if (ds==NULL && (ds = key2spec_key(dbspec, str)) != NULL){ - cg_var *vs; - fprintf(stdout, "Type"); - cvv = db_spec2cvec(ds); - vs = NULL; - while ((vs = cvec_each(cvv, vs))) - fprintf(stdout, ";%s", cv_name_get(vs)); - fprintf(stdout, "\n"); - } /* Now values just need to follow,... */ -#endif /* yang-spec? */ - if (cvv== NULL) - goto done; - xml2csv(stdout, xc, cvv); /* csv syntax */ - } - retval = 0; - done: - if (xt) - xml_free(xt); - unchunk_group(__FUNCTION__); - return retval; -} - -int -show_conf_as_csv(clicon_handle h, cvec *cvv, cg_var *arg) -{ - return show_conf_as_csv1(h, cvv, arg); -} - diff --git a/apps/cli/clixon_cli_api.h b/apps/cli/clixon_cli_api.h index 26afcef7..1426a670 100644 --- a/apps/cli/clixon_cli_api.h +++ b/apps/cli/clixon_cli_api.h @@ -74,67 +74,76 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo #define cli_output cligen_output /* cli_common.c: CLIgen new vector callbacks */ + + +int cli_set(clicon_handle h, cvec *vars, cvec *argv); int cli_setv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_merge(clicon_handle h, cvec *vars, cvec *argv); int cli_mergev(clicon_handle h, cvec *vars, cvec *argv); + +int cli_del(clicon_handle h, cvec *vars, cvec *argv); int cli_delv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv); int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv); int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv); int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv); + +int cli_start_shell(clicon_handle h, cvec *vars, cvec *argv); int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_quit(clicon_handle h, cvec *vars, cvec *argv); int cli_quitv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_commit(clicon_handle h, cvec *vars, cvec *argv); int cli_commitv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_validate(clicon_handle h, cvec *vars, cvec *argv); int cli_validatev(clicon_handle h, cvec *vars, cvec *argv); + +int compare_dbs(clicon_handle h, cvec *vars, cvec *argv); int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv); + +int load_config_file(clicon_handle h, cvec *vars, cvec *argv); int load_config_filev(clicon_handle h, cvec *vars, cvec *argv); + +int save_config_file(clicon_handle h, cvec *vars, cvec *argv); int save_config_filev(clicon_handle h, cvec *vars, cvec *argv); + +int delete_all(clicon_handle h, cvec *vars, cvec *argv); int delete_allv(clicon_handle h, cvec *vars, cvec *argv); + +int discard_changes(clicon_handle h, cvec *vars, cvec *argv); int discard_changesv(clicon_handle h, cvec *vars, cvec *argv); + +int cli_notify(clicon_handle h, cvec *cvv, cvec *argv); int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv); + +int db_copy(clicon_handle h, cvec *cvv, cvec *argv); + int cli_lock(clicon_handle h, cvec *cvv, cvec *argv); int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv); -int cli_copy_object(clicon_handle h, cvec *cvv, cvec *argv); - -/* cli_common.c: CLIgen old single arg callbacks */ -int cli_set(clicon_handle h, cvec *vars, cg_var *arg); -int cli_merge(clicon_handle h, cvec *vars, cg_var *arg); -int cli_del(clicon_handle h, cvec *vars, cg_var *arg); -int cli_debug_cli(clicon_handle h, cvec *vars, cg_var *arg); -int cli_debug_backend(clicon_handle h, cvec *vars, cg_var *argv); -int cli_set_mode(clicon_handle h, cvec *vars, cg_var *argv); -int cli_start_shell(clicon_handle h, cvec *vars, cg_var *argv); -int cli_quit(clicon_handle h, cvec *vars, cg_var *arg); -int cli_commit(clicon_handle h, cvec *vars, cg_var *arg); -int cli_validate(clicon_handle h, cvec *vars, cg_var *arg); -int compare_dbs(clicon_handle h, cvec *vars, cg_var *arg); -int load_config_file(clicon_handle h, cvec *vars, cg_var *arg); -int save_config_file(clicon_handle h, cvec *vars, cg_var *arg); -int delete_all(clicon_handle h, cvec *vars, cg_var *arg); -int discard_changes(clicon_handle h, cvec *vars, cg_var *arg); -int cli_notify(clicon_handle h, cvec *cvv, cg_var *arg); +int cli_copy_config(clicon_handle h, cvec *cvv, cvec *argv); /* In cli_show.c */ int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail); +int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv, + cvec *commands, cvec *helptexts); int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv, cvec *commands, cvec *helptexts); + /* cli_show.c: CLIgen new vector arg callbacks */ -int show_confv_as_xml(clicon_handle h, cvec *vars, cvec *argv); -int show_confv_as_netconf(clicon_handle h, cvec *vars, cvec *argv); -int show_confv_as_json(clicon_handle h, cvec *vars, cvec *argv); -int show_confv_as_text(clicon_handle h, cvec *vars, cvec *argv); -int show_confv_as_cli(clicon_handle h, cvec *vars, cvec *argv); -int show_confv_as_csv(clicon_handle h, cvec *vars, cvec *argv); +int show_yang(clicon_handle h, cvec *vars, cvec *argv); int show_yangv(clicon_handle h, cvec *vars, cvec *argv); + +int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv); int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv); -int show_configuration(clicon_handle h, cvec *cvv, cvec *argv); - -/* cli_show.c: CLIgen old single arg callbacks */ -int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg); -int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg); -int show_conf_as_json(clicon_handle h, cvec *vars, cg_var *arg); -int show_conf_as_text(clicon_handle h, cvec *vars, cg_var *arg); -int show_conf_as_cli(clicon_handle h, cvec *vars, cg_var *arg); -int show_conf_as_csv(clicon_handle h, cvec *vars, cg_var *arg); -int show_yang(clicon_handle h, cvec *vars, cg_var *arg); +int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv); #endif /* _CLIXON_CLI_API_H_ */ diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 74642c82..93936d18 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -112,6 +112,8 @@ api_data(clicon_handle h, retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data); else if (strcmp(request_method, "PUT")==0) retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data); + else if (strcmp(request_method, "PATCH")==0) + retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data); else if (strcmp(request_method, "DELETE")==0) retval = api_data_delete(h, r, api_path, pi); else @@ -180,7 +182,7 @@ request_process(clicon_handle h, else retval = notfound(r); done: - clicon_debug(1, "%s retval:%d K", __FUNCTION__, retval); + clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); if (pvec) free(pvec); if (dvec) diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c index 0b2c5031..5cdb1208 100644 --- a/apps/restconf/restconf_methods.c +++ b/apps/restconf/restconf_methods.c @@ -347,14 +347,14 @@ api_data_edit(clicon_handle h, cbuf *cbx = NULL; cxobj *x; - clicon_debug(1, "%s api_path:%s json:%s", + clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"", __FUNCTION__, api_path, data); for (i=0; iexpandv_dbvar("db","fmt") in all your cli spec files -CLICON_CLIGEN_EXPAND_SINGLE_ARG 0 - -# Set if you want to use old obsolete cligen callback variable syntax -# Migration: Set to 0 and change all user-defined cli callbacks in your cli spec files -# E.g cmd, callback("single arg"); -> cmd, callback("two" "args"); -# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files -CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1 - - diff --git a/example/routing_cli.cli b/example/routing_cli.cli index 4b0b2342..2b95fca9 100644 --- a/example/routing_cli.cli +++ b/example/routing_cli.cli @@ -4,48 +4,53 @@ CLICON_PROMPT="%U@%H> "; CLICON_PLUGIN="routing_cli"; # Note, when switching to PT, change datamodel to only @datamodel -set @datamodel:ietf-ip, cli_mergev(); +set @datamodel:ietf-ip, cli_merge(); #delete("Delete a configuration item") @datamodel:ietf-ipv4-unicast-routing, cli_del(); -delete("Delete a configuration item") @datamodel:ietf-ip, cli_delv(); +delete("Delete a configuration item") @datamodel:ietf-ip, cli_del(); -validate("Validate changes"), cli_validatev(); -commit("Commit the changes"), cli_commitv(); -quit("Quit Hello"), cli_quitv(); -delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_allv("candidate"); +validate("Validate changes"), cli_validate(); +commit("Commit the changes"), cli_commit(); +quit("Quit Hello"), cli_quit(); +delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_all("candidate"); 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(); +no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cli((int32)0); +debug("Debugging parts of the system"), cli_debug_cli((int32)1);{ + level("Set debug level: 1..n") ("Set debug level (0..n)"), cli_debug_backend(); } - -discard("Discard edits (rollback 0)"), discard_changesv(); -compare("Compare running and candidate"), compare_dbsv((int32)1); +copy("Copy and create a new object") { + interface("Copy interface"){ + ("name of interface to copy from") to("Copy to interface") ("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s=%s]","name","name","toname"); + } +} +discard("Discard edits (rollback 0)"), discard_changes(); +compare("Compare running and candidate"), compare_dbs((int32)1); +compare("Compare running and candidate"), compare_dbs((int32)1); show("Show a particular state of the system"){ - xpath("Show configuration") ("XPATH expression"), show_confv_xpath("candidate"); - compare("Compare candidate and running databases"), compare_dbsv((int32)0);{ - xml("Show comparison in xml"), compare_dbsv((int32)0); - text("Show comparison in text"), compare_dbsv((int32)1); + xpath("Show configuration") ("XPATH expression"), show_conf_xpath("candidate"); + compare("Compare candidate and running databases"), compare_dbs((int32)0);{ + xml("Show comparison in xml"), compare_dbs((int32)0); + text("Show comparison in text"), compare_dbs((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", "/"); - 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", "/"); + configuration("Show configuration"), cli_show_config("candidate", "text", "/");{ + xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/"); + netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/"); + text("Show configuration as text"), cli_show_config("candidate","text","/"); + cli("Show configuration as cli commands"), cli_show_config("candidate", "cli", "/"); + json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/"); } } -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"); +save("Save candidate configuration to XML file") ("Filename (local filename)"), save_config_file("candidate","filename"); +load("Load configuration from XML file") ("Filename (local filename)"),load_config_file("filename", "replace");{ + replace("Replace candidate with file contents"), load_config_file("filename", "replace"); + merge("Merge file with existent candidate"), load_config_file("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", "text"); -no("Negate") notify("Get notifications from backend"), cli_notifyv("ROUTING", "0", "xml"); +notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text"); +no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml"); lock,cli_lock("candidate"); unlock,cli_unlock("candidate"); \ No newline at end of file diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c index 1ed8d5b9..65bbb29e 100644 --- a/lib/src/clixon_proto_client.c +++ b/lib/src/clixon_proto_client.c @@ -220,6 +220,8 @@ clicon_rpc_generate_error(cxobj *xerr) clicon_err_fn("Clixon", 0, OE_XML, 0, "%s", cbuf_get(cb)); retval = 0; done: + if (cb) + cbuf_free(cb); return retval; } diff --git a/lib/src/clixon_xml.c b/lib/src/clixon_xml.c index 7a6f4698..f8134c0e 100644 --- a/lib/src/clixon_xml.c +++ b/lib/src/clixon_xml.c @@ -1479,6 +1479,8 @@ xml_operation(char *opstr, *op = OP_DELETE; else if (strcmp("remove", opstr) == 0) *op = OP_REMOVE; + else if (strcmp("none", opstr) == 0) + *op = OP_NONE; else{ clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr); return -1; diff --git a/test/test2.sh b/test/test2.sh index ef16c0cb..0267e87c 100755 --- a/test/test2.sh +++ b/test/test2.sh @@ -47,6 +47,27 @@ expecteof "$clixon_netconf -qf $clixon_cf" "< new "netconf commit" expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" +new "netconf edit config replace" +expecteof "$clixon_netconf -qf $clixon_cf" "eth2ethmerge ]]>]]>" "^]]>]]>$" + +new "netconf get replaced config" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^eth1ethtrueeth2ethtrue]]>]]>$" + +new "netconf edit config create" +expecteof "$clixon_netconf -qf $clixon_cf" 'eth3ethnone ]]>]]>' "^]]>]]>$" + +new "netconf edit config create 2nd" +expecteof "$clixon_netconf -qf $clixon_cf" 'eth3ethmerge ]]>]]>' "^" + +new "netconf edit config delete" +expecteof "$clixon_netconf -qf $clixon_cf" 'eth3ethnone ]]>]]>' "^]]>]]>$" + +new "netconf get delete config" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^eth1ethtrueeth2ethtrue]]>]]>$" + +new "netconf discard-changes" +expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>" "^]]>]]>$" + new "netconf lock/unlock" expecteof "$clixon_netconf -qf $clixon_cf" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$" diff --git a/test/test3.sh b/test/test3.sh index 82f63b32..0c04bdc6 100755 --- a/test/test3.sh +++ b/test/test3.sh @@ -6,7 +6,7 @@ # kill old backend (if any) new "kill old backend" -#sudo clixon_backend -zf $clixon_cf +sudo clixon_backend -zf $clixon_cf if [ $? -ne 0 ]; then err fi @@ -40,17 +40,22 @@ $' new "restconf head" expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json" -new "restconf patch config" +new "restconf POST config" expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" -# XXX POST/PUT/PATCH -new "restconf delete config" +new "restconf DELETE config" expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' "" new "restconf get config" expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}} $' +new "restconf PATCH config" +expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' "" + +new "restconf PUT" +expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' "" + new "Kill restconf daemon" #sudo pkill -u www-data clixon_restconf diff --git a/test/test4.sh b/test/test4.sh new file mode 100755 index 00000000..1c472fbb --- /dev/null +++ b/test/test4.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Test2: backend and netconf basic functionality + +# include err() and new() functions +. ./lib.sh + +# For memcheck +# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" +clixon_netconf=clixon_netconf + +cat < /tmp/test.yang +module ietf-ip{ + container x { + list y { + key "a b"; + leaf a { + type string; + } + leaf b { + type string; + } + leaf c { + type string; + } + } + leaf d { + type empty; + } + } +} +EOF + +# kill old backend (if any) +new "kill old backend" +sudo clixon_backend -zf $clixon_cf -y /tmp/test +if [ $? -ne 0 ]; then + err +fi + +new "start backend" +# start new backend +sudo clixon_backend -If $clixon_cf -y /tmp/test +if [ $? -ne 0 ]; then + err +fi +new "netconf edit config" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "125]]>]]>" "^]]>]]>$" + +new "netconf commit" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "]]>]]>" "^]]>]]>$" + +new "netconf get config xpath" +expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "]]>]]>" "^125]]>]]>$" + +new "Kill backend" +# Check if still alive +pid=`pgrep clixon_backend` +if [ -z "$pid" ]; then + err "backend already dead" +fi +# kill backend +sudo clixon_backend -zf $clixon_cf +if [ $? -ne 0 ]; then + err "kill backend" +fi From 7880b5d4982215fe6bc23e204b3649c605916633 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 7 Apr 2017 10:59:30 +0200 Subject: [PATCH 12/13] mem leak in lex for flex 2.6 --- apps/restconf/README | 41 ++++++++++++++++++++++--------------- lib/src/clixon_json_parse.l | 4 ---- lib/src/clixon_xml_parse.l | 4 ---- lib/src/clixon_yang_parse.l | 4 ---- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/apps/restconf/README b/apps/restconf/README index 5d3cd435..8d750288 100644 --- a/apps/restconf/README +++ b/apps/restconf/README @@ -1,19 +1,26 @@ +Clixon Restconf +=============== + +Contents: +1. Features +2. Installation using NGINX +3. Debugging + +1. FEATURES ++++++++++++ +Clixon restconf is a daemon based on FASTCGI. Instructions are available to +run with NGINX. +The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE. +and is based on draft-ietf-netconf-restconf-13. +There is currently (2017) a RFC 8040, many of those features are _not_ implemented, +including: +- query parameters (section 4.9) +- notifications (sec 6) +- only rudimentary error reporting exists (sec 7) + +2. INSTALLATION using NGINX ++++++++++++++++++++++++++++ -# Existing clixon installation. Using CLI: -olof@vandal> clixon_cli -f /usr/local/etc/routing.conf -olof@vandal> show configuration -interfaces { - interface { - name eth0; - type eth; - enabled true; - } - interface { - name eth9; - type eth; - enabled true; - } -} # Define nginx config file/etc/nginx/sites-available/default server { ... @@ -57,8 +64,8 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data -Debugging ---------- +3. DEBUGGING +++++++++++++ Start the restconf programs with debug flag: sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data diff --git a/lib/src/clixon_json_parse.l b/lib/src/clixon_json_parse.l index 86c46917..64ce04ae 100644 --- a/lib/src/clixon_json_parse.l +++ b/lib/src/clixon_json_parse.l @@ -131,11 +131,7 @@ int json_scan_exit(struct clicon_json_yacc_arg *jy) { yy_delete_buffer(jy->jy_lexbuf); -#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 clixon_json_parselex_destroy(); /* modern */ -#else - yy_init = 1; /* This does not quite free all buffers */ -#endif return 0; } diff --git a/lib/src/clixon_xml_parse.l b/lib/src/clixon_xml_parse.l index 4797b257..3192d035 100644 --- a/lib/src/clixon_xml_parse.l +++ b/lib/src/clixon_xml_parse.l @@ -141,11 +141,7 @@ int clixon_xml_parsel_exit(struct xml_parse_yacc_arg *ya) { yy_delete_buffer(ya->ya_lexbuf); -#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 clixon_xml_parselex_destroy(); /* modern */ -#else - yy_init = 1; /* This does not quite free all buffers */ -#endif return 0; } diff --git a/lib/src/clixon_yang_parse.l b/lib/src/clixon_yang_parse.l index 885cc708..a19328b5 100644 --- a/lib/src/clixon_yang_parse.l +++ b/lib/src/clixon_yang_parse.l @@ -248,11 +248,7 @@ int yang_scan_exit(struct clicon_yang_yacc_arg *yy) { yy_delete_buffer(yy->yy_lexbuf); -#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 clixon_yang_parselex_destroy(); /* modern */ -#else - yy_init = 1; /* This does not quite free all buffers */ -#endif return 0; } From 05edace630f295bc610a8ec9570c16d19114897c Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Fri, 7 Apr 2017 12:34:07 +0200 Subject: [PATCH 13/13] moved detect_endtag to clixon_proto --- apps/netconf/netconf_lib.c | 24 ------------- apps/netconf/netconf_lib.h | 1 - apps/netconf/netconf_main.c | 14 ++++---- lib/clixon/clixon_proto.h | 2 ++ lib/src/clixon_proto.c | 69 +++++++++++++++++++++++++++++++------ 5 files changed, 67 insertions(+), 43 deletions(-) diff --git a/apps/netconf/netconf_lib.c b/apps/netconf/netconf_lib.c index 524be705..e8854708 100644 --- a/apps/netconf/netconf_lib.c +++ b/apps/netconf/netconf_lib.c @@ -147,30 +147,6 @@ add_error_postamble(cbuf *xf) return 0; } -/*! Look for a text pattern in an input string, one char at a time - * @param[in] tag What to look for - * @param[in] ch New input character - * @param[in,out] state A state integer holding how far we have parsed. - * @retval 0 No, we havent detected end tag - * @retval 1 Yes, we have detected end tag! - * XXX: move to clicon_xml? - */ -int -detect_endtag(char *tag, char ch, int *state) -{ - int retval = 0; - - if (tag[*state] == ch){ - (*state)++; - if (*state == strlen(tag)){ - *state = 0; - retval = 1; - } - } - else - *state = 0; - return retval; -} /*! Get "target" attribute, return actual database given candidate or running * Caller must do error handling diff --git a/apps/netconf/netconf_lib.h b/apps/netconf/netconf_lib.h index 70077c1f..d47f7d7b 100644 --- a/apps/netconf/netconf_lib.h +++ b/apps/netconf/netconf_lib.h @@ -72,7 +72,6 @@ extern int cc_closed; int add_preamble(cbuf *xf); int add_postamble(cbuf *xf); int add_error_preamble(cbuf *xf, char *reason); -int detect_endtag(char *tag, char ch, int *state); char *netconf_get_target(cxobj *xn, char *path); int add_error_postamble(cbuf *xf); int netconf_output(int s, cbuf *xf, char *msg); diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index de90a128..5826393a 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -175,20 +175,18 @@ static int netconf_input_cb(int s, void *arg) { + int retval = -1; clicon_handle h = arg; unsigned char buf[BUFSIZ]; int i; int len; - // static cbuf *cb; /* XXX: should use ce state? */ - cbuf *cb=NULL; /* XXX: should use ce state? */ + cbuf *cb=NULL; int xml_state = 0; - int retval = -1; - if (cb == NULL) - if ((cb = cbuf_new()) == NULL){ - clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__); - return retval; - } + if ((cb = cbuf_new()) == NULL){ + clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__); + return retval; + } memset(buf, 0, sizeof(buf)); if ((len = read(s, buf, sizeof(buf))) < 0){ if (errno == ECONNRESET) diff --git a/lib/clixon/clixon_proto.h b/lib/clixon/clixon_proto.h index 85342170..711f835a 100644 --- a/lib/clixon/clixon_proto.h +++ b/lib/clixon/clixon_proto.h @@ -87,4 +87,6 @@ int send_msg_notify(int s, int level, char *event); int send_msg_reply(int s, char *data, uint16_t datalen); +int detect_endtag(char *tag, char ch, int *state); + #endif /* _CLIXON_PROTO_H_ */ diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 21c2b0ed..0e14eee2 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -90,7 +90,9 @@ static struct formatvec _FORMATS[] = { {NULL, -1} }; -/*! Translate from numeric error to string representation +/*! Translate from numeric format to string representation + * @param[in] showas Format value (see enum format_enum) + * @retval str String value */ char * format_int2str(enum format_enum showas) @@ -103,6 +105,10 @@ format_int2str(enum format_enum showas) return fv?(fv->fv_str?fv->fv_str:"unknown"):"unknown"; } +/*! Translate from string to numeric format representation + * @param[in] str String value + * @retval enum Format value (see enum format_enum) + */ enum format_enum format_str2int(char *str) { @@ -115,8 +121,8 @@ format_str2int(char *str) } /*! 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() + * @param[in] format 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, ...) @@ -149,10 +155,12 @@ clicon_msg_encode(char *format, ...) } /*! Decode a clicon netconf message + * @param[in] msg CLICON msg + * @param[out] xml XML parse tree */ int clicon_msg_decode(struct clicon_msg *msg, - cxobj **xml) + cxobj **xml) { int retval = -1; char *xmlstr; @@ -168,6 +176,9 @@ clicon_msg_decode(struct clicon_msg *msg, } /*! Open local connection using unix domain sockets + * @param[in] sockpath Unix domain file path + * @retval s socket + * @retval -1 error */ int clicon_connect_unix(char *sockpath) @@ -206,13 +217,19 @@ atomicio_sig_handler(int arg) _atomicio_sig++; } - /*! Ensure all of data on socket comes through. fn is either read or write + * @param[in] fn I/O function, ie read/write + * @param[in] fd File descriptor, eg socket + * @param[in] s0 Buffer to read to or write from + * @param[in] n Number of bytes to read/write, loop until done */ static ssize_t -atomicio(ssize_t (*fn) (int, void *, size_t), int fd, void *_s, size_t n) +atomicio(ssize_t (*fn) (int, void *, size_t), + int fd, + void *s0, + size_t n) { - char *s = _s; + char *s = s0; ssize_t res, pos = 0; while (n > pos) { @@ -236,6 +253,9 @@ atomicio(ssize_t (*fn) (int, void *, size_t), int fd, void *_s, size_t n) return (pos); } +/*! Print message on debug. Log if syslog, stderr if not + * @param[in] msg CLICON msg + */ static int msg_dump(struct clicon_msg *msg) { @@ -261,6 +281,10 @@ msg_dump(struct clicon_msg *msg) return 0; } +/*! Send a CLICON netconf message + * @param[in] s socket (unix or inet) to communicate with backend + * @param[out] msg CLICON msg data reply structure. Free with free() + */ int clicon_msg_send(int s, struct clicon_msg *msg) @@ -282,7 +306,7 @@ clicon_msg_send(int s, } -/*! Receive a CLICON message on a UNIX domain socket +/*! Receive a CLICON message * * XXX: timeout? and signals? * There is rudimentary code for turning on signals and handling them @@ -292,7 +316,7 @@ clicon_msg_send(int s, * behaviour. * Now, ^C will interrupt the whole process, and this may not be what you want. * - * @param[in] s UNIX domain socket to communicate with backend + * @param[in] s socket (unix or inet) to communicate with backend * @param[out] msg CLICON msg data reply structure. Free with free() * @param[out] eof Set if eof encountered * Note: caller must ensure that s is closed if eof is set after call. @@ -351,7 +375,6 @@ clicon_msg_rcv(int s, return retval; } - /*! Connect to server, send a clicon_msg message and wait for result using unix socket * * @param[in] msg CLICON msg data structure. It has fixed header and variable body. @@ -553,3 +576,29 @@ send_msg_notify(int s, free(msg); return retval; } + +/*! Look for a text pattern in an input string, one char at a time + * @param[in] tag What to look for + * @param[in] ch New input character + * @param[in,out] state A state integer holding how far we have parsed. + * @retval 0 No, we havent detected end tag + * @retval 1 Yes, we have detected end tag! + */ +int +detect_endtag(char *tag, + char ch, + int *state) +{ + int retval = 0; + + if (tag[*state] == ch){ + (*state)++; + if (*state == strlen(tag)){ + *state = 0; + retval = 1; + } + } + else + *state = 0; + return retval; +}