diff --git a/CHANGELOG b/CHANGELOG
index 2aa49340..3731f1f6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -29,21 +29,86 @@
#
# ***** END LICENSE BLOCK *****
+- 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
+- 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)
+
+- 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);
+ 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.
+
- 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/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..f347e6f9 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,6 @@ client_subscription_add(struct client_entry *ce,
{
struct client_subscription *su = NULL;
- clicon_debug(1, "%s stream:%s filter:%s", __FUNCTION__, stream, filter);
if ((su = malloc(sizeof(*su))) == NULL){
clicon_err(OE_PLUGIN, errno, "malloc");
goto done;
@@ -92,7 +92,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:
@@ -132,6 +132,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 +144,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 +160,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,436 +180,310 @@ 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.
- */
-static int
-from_client_change(clicon_handle h,
- int s,
- int pid,
- struct clicon_msg *msg,
- const char *label)
+/*! FInd target/source in netconf request. Assume sanity made so not finding is error */
+static char*
+netconf_db_find(cxobj *xn,
+ char *name)
{
- int retval = -1;
- uint32_t len;
- char *xk;
- char *db;
- enum operation_type op;
- char *str = NULL;
- char *val=NULL;
- int piddb;
+ cxobj *xs; /* source */
+ cxobj *xi;
+ char *db = NULL;
- if (clicon_msg_change_decode(msg,
- &db,
- &op,
- &xk,
- &val,
- &len,
- label) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
+ if ((xs = xml_find(xn, name)) == NULL)
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;
- }
- }
- /* Update database */
- if (xmldb_put_xkey(h, db, xk, val, op) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
+ if ((xi = xml_child_i(xs, 0)) == NULL)
goto done;
- }
- if (send_msg_ok(s) < 0)
- goto done;
- retval = 0;
- done:
- if (str)
- free(str);
- return retval;
+ db = xml_name(xi);
+ done:
+ return db;
}
-/*! 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.
+/*! 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_xmlput(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;
- 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);
+ 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;
}
- 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_tree(h, db, api_path, x, op) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- }
- }
- else
- if (xmldb_put_tree(h, db, api_path, NULL, op) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
+ 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;
+ }
}
- else if (xmldb_put(h, db, xt, op) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
- }
- if (send_msg_ok(s) < 0)
- goto done;
- retval = 0;
- done:
- if (str)
- free(str);
- if (cvv)
- cvec_free (cvv);
- if (xt)
- 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;
+ cprintf(cbret, "");
+ ok:
retval = 0;
done:
- if (f != NULL)
- fclose(f);
- if (xn)
- xml_free(xn);
+ if (xret)
+ xml_free(xret);
return retval;
}
-
-/*! 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[in] mypid Process/session id of calling client
+ * @param[out] cbret Return xml value cligen buffer
+ * 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,
+ int mypid,
+ 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;
+ int piddb;
- 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;
+ /* 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 (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 (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] xe Netconf request xml tree
+ * @param[in] pid Unix process id
+ * @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,
+ cxobj *xe,
+ int pid,
+ 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] xe Netconf request xml tree
+ * @param[in] pid Unix process id
+ * @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,
+ cxobj *xe,
+ int pid,
+ 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 +499,205 @@ 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[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 *source;
+ char *target;
+ int retval = -1;
+ int piddb;
+
+ if ((source = netconf_db_find(xe, "source")) == NULL){
+ cprintf(cbret, ""
+ "missing-element"
+ "protocol"
+ "error"
+ "source"
+ "");
+ goto ok;
+ }
+ if ((target = netconf_db_find(xe, "target")) == NULL){
+ cprintf(cbret, ""
+ "missing-element"
+ "protocol"
+ "error"
+ "target"
+ "");
+ 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_copy(h, source, target) < 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[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)
+{
+ int retval = -1;
+ char *target;
+ int piddb;
+
+ if ((target = netconf_db_find(xe, "target")) == NULL||
+ strcmp(target, "running")==0){
+ cprintf(cbret, ""
+ "missing-element"
+ "protocol"
+ "error"
+ "target"
+ "");
+ 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"
+ "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, FORMAT_XML, filter) == NULL)
goto done;
+ cprintf(cbret, "");
+ ok:
retval = 0;
done:
return retval;
@@ -635,140 +705,38 @@ 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] msg Message
- * @param[in] label Memory chunk
+ * @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,
- int s,
- struct clicon_msg *msg,
- const char *label)
+ cxobj *xe,
+ cbuf *cbret)
{
- int retval = -1;
+ int retval = -1;
uint32_t level;
-
- if (clicon_msg_debug_decode(msg,
- &level,
- label) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto done;
+ 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 */
-
- if (send_msg_ok(s) < 0)
- goto done;
clicon_log(LOG_NOTICE, "%s debug:%d", __FUNCTION__, debug);
+ cprintf(cbret, "");
+ ok:
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) {
- 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);
- 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:
+ //done:
return retval;
}
@@ -779,78 +747,173 @@ from_client_subscription(clicon_handle h,
* @retval -1 Error Terminates backend and is never called). Instead errors are
* propagated back to client.
*/
-int
-from_client(int s, void* arg)
+static int
+from_client_msg(clicon_handle h,
+ struct client_entry *ce,
+ struct clicon_msg *msg)
{
- struct client_entry *ce = (struct client_entry *)arg;
- clicon_handle h = ce->ce_handle;
- struct clicon_msg *msg;
- enum clicon_msg_type type;
- int eof;
+ int retval = -1;
+ cxobj *xt = NULL;
+ cxobj *x;
+ cxobj *xe;
+ char *name;
+ char *db;
+ cbuf *cbret = NULL; /* return message */
+ int pid;
+ int ret;
- assert(s == ce->ce_s);
- if (clicon_msg_rcv(ce->ce_s, &msg, &eof, __FUNCTION__) < 0)
- goto done;
- if (eof){
- backend_client_rm(h, ce);
+ 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;
}
- 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)
- 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;
- 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);
+ if (clicon_msg_decode(msg, &xt) < 0){
+ cprintf(cbret, ""
+ "operation-failed"
+ "rpc"
+ "error"
+ "rpc expected"
+ "Not recognized"
+ "");
+ goto reply;
+ }
+ if ((x = xpath_first(xt, "/rpc")) == NULL){
+ cprintf(cbret, ""
+ "operation-failed"
+ "rpc"
+ "error"
+ "rpc expected"
+ "Not recognized"
+ "");
+ goto reply;
+ }
+ 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, pid, cbret) <0)
+ goto done;
+ }
+ else if (strcmp(name, "copy-config") == 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, pid, cbret) <0)
+ goto done;
+ }
+ else if (strcmp(name, "lock") == 0){
+ if (from_client_lock(h, xe, pid, cbret) < 0)
+ goto done;
+ }
+ else if (strcmp(name, "unlock") == 0){
+ if (from_client_unlock(h, xe, pid, 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 reply;
+ }
+ if (from_client_validate(h, db, cbret) < 0)
+ goto done;
+ }
+ else if (strcmp(name, "commit") == 0){
+ if (from_client_commit(h, pid, cbret) < 0)
+ goto done;
+ }
+ else if (strcmp(name, "discard-changes") == 0){
+ if (from_client_discard_changes(h, pid, 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);
+ }
+ }
+ 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");
goto done;
}
-// retval = 0;
+ // ok:
+ retval = 0;
done:
- unchunk_group(__FUNCTION__);
-// return retval;
- return 0; // -1 here terminates
+ if (xt)
+ xml_free(xt);
+ if (cbret)
+ cbuf_free(cbret);
+ return retval;// -1 here terminates backend
}
+/*! 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.
+ */
+int
+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;
+ int eof;
+
+ // assert(s == ce->ce_s);
+ if (clicon_msg_rcv(ce->ce_s, &msg, &eof) < 0)
+ goto done;
+ if (eof)
+ backend_client_rm(h, ce);
+ else
+ if (from_client_msg(h, ce, msg) < 0)
+ goto done;
+ retval = 0;
+ done:
+ if (msg)
+ free(msg);
+ return retval;
+}
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..95b15869 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;
@@ -160,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 */
@@ -253,126 +256,145 @@ 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
-*/
+/*! 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
+ * @retval -1 (Local) Error
+ */
int
-candidate_validate(clicon_handle h,
- char *candidate)
+from_client_commit(clicon_handle h,
+ int mypid,
+ cbuf *cbret)
{
- int retval = -1;
- transaction_data_t *td = NULL;
+ 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");
+ /* 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:
+ 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[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
+ */
+int
+from_client_discard_changes(clicon_handle h,
+ int mypid,
+ cbuf *cbret)
+{
+ 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"
+ "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,
+ char *db,
+ cbuf *cbret)
+{
+ int retval = -1;
+ transaction_data_t *td = NULL;
+
+ if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
+ cprintf(cbret, ""
+ "invalid-value"
+ "protocol"
+ "error"
+ "");
+ goto ok;
+ }
+ clicon_debug(1, "Validate %s", db);
/* 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 (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;
- }
-
-
-/*! 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.
- */
-int
-from_client_commit(clicon_handle h,
- int s,
- struct clicon_msg *msg,
- const char *label)
-{
- 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 %s", candidate);
- 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__);
-
- return retval; /* may be zero if we ignoring errors from commit */
-} /* from_client_commit */
-
-
-
-/*! Handle an incoming validate message from a client.
- */
-int
-from_client_validate(clicon_handle h,
- int s,
- struct clicon_msg *msg,
- const char *label)
-{
- int retval = -1;
- char *candidate;
-
- if (clicon_msg_validate_decode(msg,
- &candidate,
- label) < 0){
- send_msg_err(s, clicon_errno, clicon_suberrno,
- clicon_err_reason);
- goto err;
- }
- 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)
- 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__);
return retval;
} /* from_client_validate */
diff --git a/apps/backend/backend_commit.h b/apps/backend/backend_commit.h
index e790b4e8..98b76f74 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, 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/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..6ee74fc9 100644
--- a/apps/backend/backend_main.c
+++ b/apps/backend/backend_main.c
@@ -57,6 +57,7 @@
#include
#include
#include
+#include
/* cligen */
#include
@@ -72,7 +73,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:pty:"
/*! Terminate. Cannot use h after this */
static int
@@ -142,7 +143,7 @@ usage(char *argv0, clicon_handle h)
" -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",
+ "\t-y \tOverride yang spec file (dont include .yang suffix)\n",
argv0,
plgdir ? plgdir : "none",
confsock ? confsock : "none",
@@ -153,11 +154,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 +194,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 +428,15 @@ 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);
- }
+ 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;
@@ -503,7 +506,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 +516,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 +536,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..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__);
@@ -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..720337e8 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(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;
@@ -161,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;
@@ -239,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;
@@ -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); /* XXX strdup memleak */
+ 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..92882d5b 100644
--- a/apps/cli/cli_common.c
+++ b/apps/cli/cli_common.c
@@ -74,35 +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
@@ -114,13 +85,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 +110,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 +125,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 +186,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"
@@ -225,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;
@@ -237,6 +209,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__);
@@ -254,14 +227,24 @@ 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, "candidate", "running") < 0)
+ if (clicon_rpc_commit(h) < 0)
goto done;
}
retval = 0;
done:
+ if (cb)
+ cbuf_free(cb);
if (str)
free(str);
if (xk)
@@ -270,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
@@ -313,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)
{
@@ -335,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
@@ -344,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;
@@ -365,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)
{
@@ -386,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;
@@ -443,31 +452,37 @@ 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)
{
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;
}
@@ -475,21 +490,30 @@ 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)
{
- 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;
}
-
+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
*/
@@ -558,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)
{
@@ -575,9 +599,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;
@@ -590,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
@@ -606,21 +634,19 @@ 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;
- char **vecp;
- char *filename;
+ char *filename = NULL;
int replace;
cg_var *cv;
char *opstr;
char *varstr;
int fd = -1;
cxobj *xt = NULL;
- cxobj *xn;
cxobj *x;
cbuf *cbxml;
@@ -646,11 +672,7 @@ 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){
- cli_output(stderr, "Failed to resolve filename\n");
- goto done;
- }
- filename = vecp[0];
+ filename = cv_string_get(cv);
if (stat(filename, &st) < 0){
clicon_err(OE_UNIX, 0, "load_config: stat(%s): %s",
filename, strerror(errno));
@@ -663,29 +685,39 @@ 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;
+
+ // 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) {
+ /* 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_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__);
if (xt)
xml_free(xt);
if (fd != -1)
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
@@ -703,13 +735,12 @@ 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 **vecp;
- char *filename;
+ char *filename = NULL;
cg_var *cv;
char *dbstr;
char *varstr;
@@ -736,37 +767,36 @@ 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){
- cli_output(stderr, "Failed to resolve filename\n");
- goto done;
- }
- filename = vecp[0];
- if (xmldb_get(h, dbstr, "/", &xt, NULL, NULL) < 0)
+ filename = cv_string_get(cv);
+ 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, 1) < 0)
goto done;
retval = 0;
/* Fall through */
done:
- unchunk_group(__FUNCTION__);
if (xt)
xml_free(xt);
if (f != NULL)
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;
@@ -782,23 +812,29 @@ 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;
}
+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_copy(h, "running", "candidate");
+ 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
@@ -814,15 +850,9 @@ 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 */
-static const char *SHOWAS_TXT = "txt";
-static const char *SHOWAS_XML = "xml";
-static const char *SHOWAS_XML2TXT = "xml2txt";
-static const char *SHOWAS_XML2JSON = "xml2json";
-
/*! 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
@@ -831,18 +861,16 @@ static int
cli_notification_cb(int s,
void *arg)
{
- struct clicon_msg *reply;
- enum clicon_msg_type type;
+ struct clicon_msg *reply = NULL;
int eof;
int retval = -1;
- char *eventstr = NULL;
- int level;
cxobj *xt = NULL;
- cxobj *xn;
- char *format = (char*)arg;
-
+ cxobj *xe;
+ cxobj *x;
+ 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, __FUNCTION__) < 0)
+ if (clicon_msg_rcv(s, &reply, &eof) < 0)
goto done;
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
@@ -851,61 +879,42 @@ cli_notification_cb(int s,
event_unreg_fd(s, cli_notification_cb);
goto done;
}
- if (format == NULL)
+ if (clicon_msg_decode(reply, &xt) < 0)
goto done;
- type = ntohs(reply->op_type);
- switch (type){
- case CLICON_MSG_NOTIFY:
- if (clicon_msg_notify_decode(reply, &level, &eventstr, __FUNCTION__) < 0)
- goto done;
- if (strcmp(format, SHOWAS_TXT) == 0){
- fprintf(stdout, "%s", 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)
+ 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;
- }
- 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 (xml2txt(stdout, xn, 0) < 0)
+ break;
+ case FORMAT_TEXT:
+ if (xml2txt(stdout, x, 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 (xml2json(stdout, xn, 0) < 0)
+ break;
+ case FORMAT_JSON:
+ if (xml2json(stdout, x, 1) < 0)
goto done;
+ break;
+ default:
+ break;
}
}
- break;
- default:
- clicon_err(OE_PROTO, 0, "%s: unexpected reply: %d",
- __FUNCTION__, type);
- goto done;
- break;
}
retval = 0;
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
@@ -916,15 +925,15 @@ 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;
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__);
@@ -934,8 +943,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;
+ format = format_str2int(formatstr);
}
if (cli_notification_register(h,
stream,
@@ -943,198 +951,210 @@ cli_notifyv(clicon_handle h,
"",
status,
cli_notification_cb,
- (void*)formatstr) < 0)
+ (void*)format) < 0)
goto done;
retval = 0;
done:
return retval;
}
+int cli_notifyv(clicon_handle h, cvec *vars, cvec *argv)
+{
+ return cli_notify(h, vars, argv);
+}
-/* Here are backward compatible cligen callback functions used when
- * the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set.
+/*! 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;
-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;
- 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__);
+ if (cvec_len(argv) != 1){
+ clicon_err(OE_PLUGIN, 0, "Requires arguments: ");
goto done;
}
- if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){
- clicon_err(OE_PLUGIN, errno, "clicon_strsplit");
+ db = cv_string_get(cvec_i(argv, 0));
+ if (clicon_rpc_lock(h, db) < 0)
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:
- unchunk_group(__FUNCTION__);
- 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;
- 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_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){
- clicon_err(OE_PLUGIN, errno, "clicon_strsplit");
- 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:
- unchunk_group(__FUNCTION__);
+ retval = 0;
+ done:
return retval;
}
-
-
-int
-cli_notify(clicon_handle h,
+/*! 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,
- cg_var *arg)
-{
- int retval=-1;
- cvec *argv;
- cg_var *cv;
- char *str;
- char **vec;
- int nvec;
+ cvec *argv)
+{
+ char *db;
+ int retval = -1;
- /* 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__);
+ if (cvec_len(argv) != 1){
+ clicon_err(OE_PLUGIN, 0, "Requires arguments: ");
goto done;
}
- if ((vec = clicon_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){
- clicon_err(OE_PLUGIN, errno, "clicon_strsplit");
+ db = cv_string_get(cvec_i(argv, 0));
+ if (clicon_rpc_unlock(h, db) < 0)
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:
- unchunk_group(__FUNCTION__);
+ retval = 0;
+ done:
return retval;
}
-/*
- * cli_debug
- * set debug level on stderr (not syslog).
+/*! 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 , cli_copy_config("candidate", "/sender[%s=%s]", "from", "n1", "n2");
+ * cli command:
+ * copy snd from to to
+ * @endcode
+ */
+int
+cli_copy_config(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;
+}
+
+
+/*! 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_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_main.c b/apps/cli/cli_main.c
index 10fe40da..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
@@ -77,10 +78,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;
}
@@ -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"
@@ -174,7 +176,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 */
@@ -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;
@@ -359,23 +370,14 @@ 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__);
+ restarg = clicon_strjoin(argc, argv, " ");
/* If several cligen object variables match same preference, select first */
cligen_match_cgvar_same(1);
@@ -398,6 +400,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..cbb68cbd 100644
--- a/apps/cli/cli_plugin.c
+++ b/apps/cli/cli_plugin.c
@@ -372,35 +372,21 @@ 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 */
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 +401,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 +1008,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 +1038,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 f25eb2b4..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;
@@ -142,12 +139,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 +186,6 @@ expandv_dbvar(void *h,
}
retval = 0;
done:
- unchunk_group(__FUNCTION__);
if (xvec)
free(xvec);
if (xt)
@@ -196,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;
@@ -312,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;
@@ -334,126 +341,69 @@ 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;
+ return show_yang(h, vars, argv);
}
-/*! Generic function for showing configurations.
+/*! 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] arg A string: []
- * @param[out] xt Configuration as xml tree.
- * Format of arg:
- * "running", "candidate", "startup"
- * xpath expression
+ * @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 interfaces/interface[name=%s] n");
+ * show config id , cli_show_config("running","xml","iface[name=%s]","n");
* @endcode
*/
-static int
-show_confv_as(clicon_handle h,
- cvec *cvv,
- cvec *argv,
- cxobj **xt) /* top xml */
+int
+cli_show_config(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;
- cbuf *cbx = NULL;
int i;
int j;
cg_var *cvattr;
char *val = NULL;
+ cxobj *xt = NULL;
+ cxobj *xc;
+ enum genmodel_type gt;
- 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));
+ if (cvec_len(argv) != 3 && cvec_len(argv) != 4){
+ clicon_err(OE_PLUGIN, 0, "Got %d arguments. Expected: ,,[,]", cvec_len(argv));
goto done;
}
- /* Dont get attr here, take it from arg instead */
+ /* First argv argument: Database */
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);
+ /* 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;
}
- xpath = cv_string_get(cvec_i(argv, 1));
- if ((cbx = cbuf_new()) == NULL){
+ /* 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;
}
- if (cvec_len(argv) == 3){
- attr = cv_string_get(cvec_i(argv, 2));
+ /* 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 (cbx)
- cbuf_free(cbx);
- unchunk_group(__FUNCTION__);
+ if (cbxpath)
+ cbuf_free(cbxpath);
return retval;
}
-/*! Show a configuration database on stdout using XML format
- * 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] 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);
- unchunk_group(__FUNCTION__);
- 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 ((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 */
- 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 */
- }
- retval = 0;
- done:
- if (xt)
- xml_free(xt);
- unchunk_group(__FUNCTION__);
- 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);
-}
-
/*! Show configuration as text given an xpath
* Utility function used by cligen spec file
* @param[in] h CLICON handle
@@ -705,9 +477,9 @@ show_confv_as_csv(clicon_handle h,
* @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;
@@ -732,7 +504,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 /interface=eth0/address=1.2.3.4
- */
- if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
- goto done;
- if (xmldb_get(h, dbstr, xkpath, &xt, &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_strsplit(str, " ", &nvec, __FUNCTION__)) == NULL){
- clicon_err(OE_PLUGIN, errno, "clicon_strsplit");
- 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, __FUNCTION__); /* 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);
-}
-
-/*! Show configuration as text given an xpath
- * 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:
- * @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%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;
if (zapdb) /* remove databases */
- /* XXX This assumes direct access to database */
if (xmldb_delete(h, db) < 0){
- clicon_err(OE_FATAL, errno, "xmldb_delete %s", db);
+ clicon_err(OE_FATAL, errno, "delete %s", db);
goto done;
}
if (initdb)
diff --git a/apps/netconf/clixon_netconf.h b/apps/netconf/clixon_netconf.h
index 3b495c58..616f511b 100644
--- a/apps/netconf/clixon_netconf.h
+++ b/apps/netconf/clixon_netconf.h
@@ -44,11 +44,9 @@
*/
typedef int (*netconf_cb_t)(
clicon_handle h,
- cxobj *xorig, /* Original request. */
- cxobj *xn, /* Sub-tree (under xorig) at child: */
- 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..e8854708 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)
@@ -161,38 +147,13 @@ 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
* @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 +179,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..d47f7d7b 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,10 @@ 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..5826393a 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
@@ -82,11 +83,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 +98,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 +114,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;
}
@@ -190,19 +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;
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)
@@ -237,7 +221,8 @@ netconf_input_cb(int s,
}
retval = 0;
done:
- // cbuf_free(cb);
+ if (cb)
+ cbuf_free(cb);
if (cc_closed)
retval = -1;
return retval;
@@ -268,30 +253,16 @@ 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);
+ event_exit();
clicon_handle_exit(h);
return 0;
}
@@ -314,7 +285,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
);
@@ -387,6 +359,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;
@@ -402,8 +383,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 +395,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..ce68c92b 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,9 @@ 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] xret Return XML, error or OK
*
* @retval -1 Error
* @retval 0 OK, not found handler.
@@ -256,31 +264,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..b63af86c 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,34 @@
*/
-/*! 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[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[in] xn Sub-tree (under xorig) at ... level.
+ * @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 +118,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 +223,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 +234,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 +282,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 +398,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 +445,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 +482,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 +518,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 +554,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,21 +675,20 @@ 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 */
- enum clicon_msg_type type;
+ cxobj *xt = NULL; /* top xml */
if (0){
fprintf(stderr, "%s\n", __FUNCTION__); /* debug */
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){
@@ -864,56 +700,41 @@ 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_NOTIFY:
- if (clicon_msg_notify_decode(reply, &level, &event, __FUNCTION__) < 0)
- goto done;
- /* 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;
- 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, "");
- cprintf(cb, "");
- cprintf(cb, "%s", event);
- cprintf(cb, "");
- cprintf(cb, "");
- add_postamble(cb);
- /* Send it to listening client on stdout */
- if (netconf_output(1, cb, "notification") < 0){
- cbuf_free(cb);
- goto done;
- }
- 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 (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 +747,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/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/README b/apps/restconf/README
index 5ecfc1ef..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 {
...
@@ -54,8 +61,11 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et
}
]
-Debugging
----------
+curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
+
+
+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/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..8eba97bb 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_
@@ -12,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
diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c
index 4306569a..93936d18 100644
--- a/apps/restconf/restconf_main.c
+++ b/apps/restconf/restconf_main.c
@@ -59,6 +59,7 @@
#include
#include
#include
+#include
/* cligen */
#include
@@ -66,302 +67,17 @@
/* 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:"
+#define RESTCONF_OPTS "hDf:p:y:"
/* Should be discovered via "/.well-known/host-meta"
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;
- 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;
-
- 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; i 0){
- if ((val = cv2str_dup(cv)) == NULL)
- goto done;
- v = val;
- /* XXX sync with yang */
- while((v=index(v, ',')) != NULL){
- *v = '\0';
- v++;
- }
- /* Find keys */
- 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);
- 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 (xmldb_get(h, "running", cbuf_get(path), &xt, &vec, &veclen) < 0)
- goto done;
-
- if ((cbx = cbuf_new()) == NULL)
- goto done;
- if (veclen==0){
- notfound(r);
- 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)
- goto done;
- FCGX_FPrintF(r->out, "%s", cbuf_get(cbx));
- FCGX_FPrintF(r->out, "\r\n\r\n");
- retval = 0;
- done:
- if (cbx)
- cbuf_free(cbx);
- if (xt)
- xml_free(xt);
- if (path)
- cbuf_free(path);
- if (path1)
- cbuf_free(path1);
- 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; iout);
- 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; iout); /* 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
@@ -385,14 +101,19 @@ 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, "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
@@ -411,7 +132,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;
@@ -423,7 +144,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)
@@ -437,7 +158,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 */
@@ -457,6 +183,8 @@ request_process(clicon_handle h,
retval = notfound(r);
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
+ if (pvec)
+ free(pvec);
if (dvec)
cvec_free(dvec);
if (qvec)
@@ -465,10 +193,43 @@ request_process(clicon_handle h,
cvec_free(pcvec);
if (cb)
cbuf_free(cb);
- unchunk_group(__FUNCTION__);
return retval;
}
+static int
+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);
+ clicon_handle_exit(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
@@ -485,7 +246,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
);
@@ -506,13 +268,14 @@ 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);
/* 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':
@@ -531,6 +294,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;
@@ -540,11 +306,29 @@ 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)
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;
@@ -592,5 +376,6 @@ main(int argc,
retval = 0;
done:
restconf_plugin_unload(h);
+ restconf_terminate(h);
return retval;
}
diff --git a/apps/restconf/restconf_methods.c b/apps/restconf/restconf_methods.c
new file mode 100644
index 00000000..5cdb1208
--- /dev/null
+++ b/apps/restconf/restconf_methods.c
@@ -0,0 +1,515 @@
+/*
+ *
+ ***** 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
+#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 = NULL;
+ 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; i 0){
+ if ((val = cv2str_dup(cv)) == NULL)
+ goto done;
+ v = val;
+ /* XXX sync with yang */
+ while((v=index(v, ',')) != NULL){
+ *v = '\0';
+ v++;
+ }
+ /* Find keys */
+ 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);
+ 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/lib/src/clixon_xml_db_rpc.h b/apps/restconf/restconf_methods.h
similarity index 64%
rename from lib/src/clixon_xml_db_rpc.h
rename to apps/restconf/restconf_methods.h
index 561c4935..f6946f8a 100644
--- a/lib/src/clixon_xml_db_rpc.h
+++ b/apps/restconf/restconf_methods.h
@@ -30,27 +30,34 @@
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
*/
-#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 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);
-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_ */
+#endif /* _RESTCONF_METHODS_H_ */
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..57923b95 100644
--- a/clixon.conf.cpp.cpp
+++ b/clixon.conf.cpp.cpp
@@ -121,16 +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)
-# CLICON_XMLDB_PORT
-
# Dont include keys in cvec in cli vars callbacks, ie a & k in 'a k ' ignored
# CLICON_CLI_VARONLY 1
@@ -138,15 +128,3 @@ CLICON_XMLDB_DIR localstatedir/APPNAME
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
-# Set if you want to use old obsolete cligen expand variable syntax
-# Migration: Set to 0 and change all user-defined cli completion callbacks
-# E.g. expand_dbvar("db fmt") ->expandv_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/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/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..34e6fc80 100644
--- a/example/routing_backend.c
+++ b/example/routing_backend.c
@@ -42,7 +42,7 @@
#include
#include
#include
-
+#include
/* clicon */
#include
@@ -53,13 +53,16 @@
/* 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
transaction_validate(clicon_handle h,
transaction_data td)
{
- transaction_print(stderr, td);
+ // transaction_print(stderr, td);
return 0;
}
@@ -70,20 +73,62 @@ 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; i */
+ 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 +137,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..3515408a 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_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..2b95fca9 100644
--- a/example/routing_cli.cli
+++ b/example/routing_cli.cli
@@ -4,42 +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_cliv();
+startup("Store running as startup config"), db_copy("running", "startup");
+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();
+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_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/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/clixon/clixon_options.h b/lib/clixon/clixon_options.h
index 0d339514..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 ;
@@ -109,9 +107,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..711f835a 100644
--- a/lib/clixon/clixon_proto.h
+++ b/lib/clixon/clixon_proto.h
@@ -42,136 +42,51 @@
* Types
*/
enum format_enum{
- MSG_NOTIFY_TXT, /* means filter works on strings */
- MSG_NOTIFY_XML, /* means filter works on xml */
-};
-
-/* 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_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
- 3. string: name of database to change (eg "running")
- 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
- */
+ FORMAT_XML,
+ FORMAT_JSON,
+ FORMAT_TEXT,
+ FORMAT_CLI,
+ FORMAT_NETCONF
};
/* 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 */
};
-/* 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
+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);
+
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_reply(int s, char *data, uint16_t datalen);
-int send_msg_ok(int s);
-
-int send_msg_err(int s, int err, int suberr, char *format, ...);
+int detect_endtag(char *tag, char ch, int *state);
#endif /* _CLIXON_PROTO_H_ */
diff --git a/lib/clixon/clixon_proto_client.h b/lib/clixon/clixon_proto_client.h
index 1b6d0e09..29ca939f 100644
--- a/lib/clixon/clixon_proto_client.h
+++ b/lib/clixon/clixon_proto_client.h
@@ -40,27 +40,25 @@
#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(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);
+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_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_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_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
deleted file mode 100644
index 5d4f9e22..00000000
--- a/lib/clixon/clixon_proto_encode.h
+++ /dev/null
@@ -1,177 +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_commit_encode(char *dbsrc, char *dbdst,
- const char *label);
-
-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);
-
-struct clicon_msg *
-clicon_msg_change_encode(char *db, uint32_t op, char *key,
- char *lvec, uint32_t lvec_len,
- const char *label);
-
-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_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
-clicon_msg_dbitems_get_reply_decode(char *data,
- uint16_t datalen,
- cvec ***cvecv,
- 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_string.h b/lib/clixon/clixon_string.h
index 7537a799..86c22e6d 100644
--- a/lib/clixon/clixon_string.h
+++ b/lib/clixon/clixon_string.h
@@ -54,16 +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_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_strsep(char *string, char *delim, int *nvec0);
+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/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..8889a8d8 100644
--- a/lib/clixon/clixon_xml_db.h
+++ b/lib/clixon/clixon_xml_db.h
@@ -42,18 +42,16 @@
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_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/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/Makefile.in b/lib/src/Makefile.in
index 5b0548f7..ac869185 100644
--- a/lib/src/Makefile.in
+++ b/lib/src/Makefile.in
@@ -68,8 +68,8 @@ 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_xsl.c clixon_sha1.c clixon_xml_db.c clixon_xml_db_rpc.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 \
lex.clixon_yang_parse.o clixon_yang_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_file.c b/lib/src/clixon_file.c
index 3280c0c9..64ac8ee1 100644
--- a/lib/src/clixon_file.c
+++ b/lib/src/clixon_file.c
@@ -62,99 +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.
- */
-char **
-clicon_realpath(const char *cwd, char *path, const char *label)
-{
- char **ret = NULL;
- char *rest;
- char **vec, **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 '.' */
- vec = clicon_strsplit (p, "/", &nvec, __FUNCTION__);
- vec2 = chunk(nvec * sizeof(char *), __FUNCTION__);
- 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 = chunk(sizeof(char *) * 2, label)) != NULL) {
- if((ret[0] = clicon_strjoin(nvec-nvec2, &vec2[nvec2], "/", label)) == NULL) {
- unchunk(ret);
- ret = NULL;
- }
- if ((ret[1] = chunkdup(rp, strlen(rp)+1, label)) == NULL) {
- unchunk(ret[0]);
- unchunk(ret);
- ret = NULL;
- }
- }
-
-catch:
- if(rp)
- free(rp);
- unchunk_group(__FUNCTION__);
- return ret;
-}
-
-
/*
* qsort function
*/
@@ -345,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_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_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_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 7e8bc392..0e14eee2 100644
--- a/lib/src/clixon_proto.c
+++ b/lib/src/clixon_proto.c
@@ -68,47 +68,117 @@
#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"
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 */
+/*! Formats (showas) derived from XML
+ */
+struct formatvec{
+ char *fv_str;
+ int fv_int;
};
-/* Mapping between yang keyword string <--> clicon constants */
-static const struct map_type2str msgmap[] = {
- {CLICON_MSG_COMMIT, "commit"},
- {CLICON_MSG_VALIDATE, "validate"},
- {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},
+static struct formatvec _FORMATS[] = {
+ {"xml", FORMAT_XML},
+ {"text", FORMAT_TEXT},
+ {"json", FORMAT_JSON},
+ {"cli", FORMAT_CLI},
+ {"netconf", FORMAT_NETCONF},
+ {NULL, -1}
};
-static char *
-msg_type2str(enum clicon_msg_type type)
+/*! 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)
{
- const struct map_type2str *mt;
+ struct formatvec *fv;
- for (mt = &msgmap[0]; mt->mt_str; mt++)
- if (mt->mt_type == type)
- return mt->mt_str;
- return NULL;
+ for (fv=_FORMATS; fv->fv_int != -1; fv++)
+ if (fv->fv_int == showas)
+ break;
+ 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)
+{
+ 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] 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, ...)
+{
+ 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_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
+ * @param[in] msg CLICON msg
+ * @param[out] xml XML parse tree
+ */
+int
+clicon_msg_decode(struct clicon_msg *msg,
+ cxobj **xml)
+{
+ int retval = -1;
+ char *xmlstr;
+
+ /* body */
+ xmlstr = msg->op_body;
+ clicon_debug(1, "%s %s", __FUNCTION__, xmlstr);
+ if (clicon_xml_parse_str(xmlstr, xml) < 0)
+ goto done;
+ retval = 0;
+ done:
+ return retval;
}
/*! 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)
@@ -147,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) {
@@ -177,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)
{
@@ -202,14 +281,18 @@ 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)
{
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,
@@ -223,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
@@ -233,18 +316,15 @@ 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[out] msg CLICON msg data reply structure. allocated using CLICON chunks,
- * freed by caller with unchunk*(...,label)
+ * @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
- * @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;
@@ -271,10 +351,10 @@ 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);
- if ((*msg = (struct clicon_msg *)chunk(mlen, label)) == NULL){
- clicon_err(OE_CFG, errno, "%s: chunk", __FUNCTION__);
+ 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;
}
memcpy(*msg, &hdr, hlen);
@@ -295,25 +375,27 @@ clicon_msg_rcv(int s,
return retval;
}
-
-/*! 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;
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);
@@ -325,7 +407,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,24 +418,29 @@ 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;
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;
@@ -371,7 +458,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 +478,24 @@ 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__);
@@ -421,37 +503,31 @@ clicon_rpc(int s,
errno = ESHUTDOWN;
goto done;
}
- 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)
+ data = reply->op_body; /* assume string */
+ if (ret && data)
+ if ((*ret = strdup(data)) == NULL){
+ clicon_err(OE_UNIX, errno, "strdup");
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;
- break;
- default:
- clicon_err(OE_PROTO, 0, "%s: unexpected reply: %hu",
- __FUNCTION__, type);
- goto done;
- break;
- }
+ }
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] 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,
char *data,
uint16_t datalen)
{
@@ -463,7 +539,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);
@@ -475,57 +550,55 @@ 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_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;
}
+/*! 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
-send_msg_err(int s, int err, int suberr, char *format, ...)
+detect_endtag(char *tag,
+ char ch,
+ int *state)
{
- va_list args;
- char *reason;
- int len;
- int retval = -1;
- struct clicon_msg *msg;
+ int retval = 0;
- 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);
- va_start(args, format);
- vsnprintf(reason, len, format, args);
- va_end(args);
- if ((msg=clicon_msg_err_encode(clicon_errno, clicon_suberrno,
- reason, __FUNCTION__)) == NULL)
- goto done;
- if (clicon_msg_send(s, msg) < 0)
- goto done;
-
- retval = 0;
- done:
- unchunk_group(__FUNCTION__);
+ if (tag[*state] == ch){
+ (*state)++;
+ if (*state == strlen(tag)){
+ *state = 0;
+ retval = 1;
+ }
+ }
+ else
+ *state = 0;
return retval;
}
-
diff --git a/lib/src/clixon_proto_client.c b/lib/src/clixon_proto_client.c
index ffc0ab79..65bbb29e 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,36 @@
/* 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
+/*! Send internal netconf rpc from client to backend
* @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;
if ((sock = clicon_sock(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
@@ -96,7 +95,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,231 +114,553 @@ 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;
}
+ clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata);
+ if (retdata &&
+ clicon_xml_parse_str(retdata, &xret) < 0)
+ goto done;
+ if (xret0){
+ *xret0 = xret;
+ xret = NULL;
+ }
retval = 0;
done:
+ if (retdata)
+ free(retdata);
+ if (xret)
+ xml_free(xret);
return retval;
}
-/*! 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 Copy current->candidate
+/*! Generic xml netconf clicon rpc
+ * Want to go over to use netconf directly between client and server,...
+ * @param[in] h clicon handle
+ * @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_commit(clicon_handle h,
- char *from,
- char *to)
+clicon_rpc_netconf(clicon_handle h,
+ char *xmlstr,
+ cxobj **xret,
+ int *sp)
{
int retval = -1;
- struct clicon_msg *msg;
+ struct clicon_msg *msg = NULL;
- if ((msg=clicon_msg_commit_encode(from, to, __FUNCTION__)) == NULL)
+ if ((msg = clicon_msg_encode("%s", xmlstr)) < 0)
goto done;
- if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
+ if (clicon_rpc_msg(h, msg, xret, sp) < 0)
goto done;
retval = 0;
done:
- unchunk_group(__FUNCTION__);
+ 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
+/*! 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_validate(clicon_handle h,
- char *db)
+clicon_rpc_netconf_xml(clicon_handle h,
+ cxobj *xml,
+ cxobj **xret,
+ int *sp)
{
int retval = -1;
- struct clicon_msg *msg;
+ cbuf *cb = NULL;
- if ((msg=clicon_msg_validate_encode(db, __FUNCTION__)) == NULL)
+ if ((cb = cbuf_new()) == NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
- if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
+ }
+ 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:
- unchunk_group(__FUNCTION__);
+ if (cb)
+ cbuf_free(cb);
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
+/*! Generate clicon error function call from Netconf error message
+ * @param[in] xerr Netconf error message on the level:
*/
int
-clicon_rpc_change(clicon_handle h,
- char *db,
- enum operation_type op,
- char *key,
- char *val)
+clicon_rpc_generate_error(cxobj *xerr)
{
- int retval = -1;
- struct clicon_msg *msg;
+ int retval = -1;
+ cbuf *cb = NULL;
+ cxobj *x;
- if ((msg = clicon_msg_change_encode(db,
- op,
- key,
- val,
- val?strlen(val)+1:0,
- __FUNCTION__)) == NULL)
+ if ((cb = cbuf_new()) ==NULL){
+ clicon_err(OE_XML, errno, "cbuf_new");
goto done;
- if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
+ }
+ if ((x=xpath_first(xerr, "error-type"))!=NULL)
+ cprintf(cb, "%s ", xml_body(x));
+ if ((x=xpath_first(xerr, "error-tag"))!=NULL)
+ cprintf(cb, "%s ", xml_body(x));
+ if ((x=xpath_first(xerr, "error-message"))!=NULL)
+ cprintf(cb, "%s ", xml_body(x));
+ if ((x=xpath_first(xerr, "error-info"))!=NULL)
+ 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:
+ if (cb)
+ cbuf_free(cb);
+ return retval;
+}
+
+/*! 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_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:
- unchunk_group(__FUNCTION__);
+ 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: .....
+ * @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)
+ * err;
+ * @endcode
*/
int
-clicon_rpc_xmlput(clicon_handle h,
- char *db,
- enum operation_type op,
- char *api_path,
- char *xml)
+clicon_rpc_edit_config(clicon_handle h,
+ char *db,
+ enum operation_type op,
+ char *api_path,
+ char *xmlstr)
{
int retval = -1;
- struct clicon_msg *msg;
+ struct clicon_msg *msg = NULL;
+ cbuf *cb = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr;
- if ((msg = clicon_msg_xmlput_encode(db,
- (uint32_t)op,
- api_path,
- xml,
- __FUNCTION__)) == NULL)
+ if ((cb = cbuf_new()) == NULL)
goto done;
- if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
+ cprintf(cb, "<%s/>", db);
+ cprintf(cb, "%s",
+ xml_operation2str(op));
+ if (api_path && strlen(api_path))
+ cprintf(cb, "", api_path);
+ if (xmlstr)
+ cprintf(cb, "%s", xmlstr);
+ cprintf(cb, "");
+ if ((msg = clicon_msg_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:
- 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__);
+ 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 "candidate"
- * @param[in] db2 dst database, eg "running"
+ * @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(clicon_handle h,
- char *db1,
- char *db2)
+clicon_rpc_copy_config(clicon_handle h,
+ char *db1,
+ char *db2)
{
int retval = -1;
- struct clicon_msg *msg;
+ struct clicon_msg *msg = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr;
- if ((msg=clicon_msg_copy_encode(db1, db2, __FUNCTION__)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/><%s/>", db1, db2)) == 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 a kill session request to backend server
- * @param[in] h CLICON handle
- * @param[in] session_id Id of session to kill
+/*! 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_kill(clicon_handle h,
- int session_id)
+clicon_rpc_delete_config(clicon_handle h,
+ char *db)
{
int retval = -1;
- struct clicon_msg *msg;
+ struct clicon_msg *msg = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr;
- if ((msg=clicon_msg_kill_encode(session_id, __FUNCTION__)) == NULL)
+ if ((msg = clicon_msg_encode("<%s/>", db)) == 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;
+}
+
+/*! Lock a database
+ * @param[in] h CLICON handle
+ * @param[in] db database, eg "running"
+ */
+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_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;
+}
+
+/*! Unlock a database
+ * @param[in] h CLICON handle
+ * @param[in] db database, eg "running"
+ */
+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_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;
+}
+
+/*! Close a (user) session
+ * @param[in] h CLICON handle
+ */
+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_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;
+}
+
+/*! 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)
+{
+ int retval = -1;
+ struct clicon_msg *msg = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr;
+
+ if ((msg = clicon_msg_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_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;
+}
+
+/*! Commit changes send a commit request to backend daemon
+ * @param[in] h CLICON handle
+ * @retval 0 OK
+ */
+int
+clicon_rpc_commit(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;
+ 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;
+ 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;
+}
+
+/*! 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_create_subscription(clicon_handle h,
+ char *stream,
+ char *filter,
+ int *s0)
+{
+ int retval = -1;
+ struct clicon_msg *msg = NULL;
+ cxobj *xret = NULL;
+ cxobj *xerr;
+
+ 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;
+ 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;
}
@@ -352,107 +673,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_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
deleted file mode 100644
index ee7fa34e..00000000
--- a/lib/src/clixon_proto_encode.c
+++ /dev/null
@@ -1,924 +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_proto.h"
-#include "clixon_proto_encode.h"
-
-struct clicon_msg *
-clicon_msg_commit_encode(char *dbsrc, char *dbdst,
- const char *label)
-{
- struct clicon_msg *msg;
- uint16_t len;
- 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__);
- return NULL;
- }
- memset(msg, 0, len);
- /* hdr */
- msg->op_type = htons(CLICON_MSG_COMMIT);
- 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;
- 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)
-{
- struct clicon_msg *msg;
- uint16_t len;
- int hdrlen = sizeof(*msg);
-
- 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;
- }
- 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);
- return msg;
-}
-
-int
-clicon_msg_validate_decode(struct clicon_msg *msg, char **db, const char *label)
-{
- /* 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;
-}
-
-
-struct clicon_msg *
-clicon_msg_change_encode(char *db,
- uint32_t op,
- char *key,
- char *str,
- uint32_t str_len,
- 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 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 *)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_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;
-}
-
-/*! 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_string.c b/lib/src/clixon_string.c
index 611c4d27..66c9c6b4 100644
--- a/lib/src/clixon_string.c
+++ b/lib/src/clixon_string.c
@@ -53,228 +53,89 @@
#include "clixon_string.h"
#include "clixon_err.h"
-/*! Split string into a vector based on character delimiters
+
+/*! 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 chunk that must be unchunked
+ * 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
- * @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.
+ * @retval vec Vector of strings. NULL terminated. Free after use
+ * @retval NULL Error *
*/
char **
-clicon_sepsplit (char *string,
- char *delim,
- int *nvec,
- const char *label)
+clicon_strsep(char *string,
+ char *delim,
+ int *nvec0)
{
- 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.
- *
- * See also clicon_sepsplit() which is similar
- *
- * 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.
- */
-char **
-clicon_strsplit (char *string,
- char *delim,
- int *nvec,
- const char *label)
-{
- int idx;
+ char **vec = NULL;
+ char *ptr;
+ char *p;
+ int nvec = 1;
+ int i;
size_t siz;
char *s;
- char **vec, *vecp;
-
- *nvec = 1;
- s = string;
- while ((s = strstr(s, delim))) {
- s += strlen(delim);
- (*nvec)++;
+ char *d;
+
+ if ((s = string)==NULL)
+ goto done;
+ while (*s){
+ if ((d = index(delim, *s)) != NULL)
+ nvec++;
+ s++;
}
-
- 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);
- }
- }
-
+ /* alloc vector and append copy of string */
+ siz = (nvec+1)* sizeof(char*) + strlen(string)+1;
+ if ((vec = (char**)malloc(siz)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ 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)
+ vec[i++] = p;
+ *nvec0 = nvec;
+ done:
return vec;
}
/*! Concatenate elements of a string array into a string.
* An optional delimiter string can be specified which will be inserted betwen
* each element.
- * @param[in] label Chunk label for returned vector
- * @retval str Joined string. Free with unchunk()
+ * @retval str Joined string. Free after use.
* @retval NULL Failure
*/
char *
-clicon_strjoin (int argc,
- char **argv,
- char *delim,
- const char *label)
+clicon_strjoin(int argc,
+ char **argv,
+ char *delim)
{
- int i;
- int len;
- char *str;
-
- len = 0;
- for (i = 0; i < argc; i++)
- len += strlen(argv[i]);
- if (delim)
- len += (strlen(delim) * argc);
- len += 1; /* '\0' */
-
- if ((str = chunk (len, label)) == 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));
- }
-
- return str;
-}
-
-/*! Trim whitespace in beginning and end of string.
- *
- * @param[in] label Chunk label for returned vector
- * @retval str Trimmed string. Free with unchunk()
- * @retval NULL Failure
- */
-char *
-clicon_strtrim(char *str,
- const char *label)
-{
- char *start, *end, *new;
-
- start = str;
- while (*start != '\0' && isspace(*start))
- start++;
- if (!strlen(start))
- return (char *)chunkdup("\0", 1, label);
-
- end = str + strlen(str) ;
- while (end > 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;
}
@@ -302,82 +163,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(str0, " \t", &nvec)) == NULL)
+ return -1;
+ fprintf(stderr, "nvec: %d\n", nvec);
+ for (i=0; ix_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, "%s>", xml_name(cx));
}
- cprintf(cb, ">");
- if (prettyprint && xml_body(cx)==NULL)
- cprintf(cb, "\n");
- xc = NULL;
- while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
- if (xml_type(xc) == CX_ATTR)
- continue;
- else
- clicon_xml2cbuf(cb, xc, level+1, prettyprint);
- }
- if (prettyprint && xml_body(cx)==NULL)
- cprintf(cb, "%*s", level*XML_INDENT, "");
- cprintf(cb, "%s>", xml_name(cx));
if (prettyprint)
cprintf(cb, "\n");
break;
@@ -955,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;
+ goto done;
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
goto done;
-
retval = 0;
done:
clixon_xml_parsel_exit(&ya);
@@ -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, "%d", 22) < 0)
+ * err;
+ * xml_free(cx);
+ * @endcode
+ * @see clicon_xml_parse_str
+ * @note you need to free the xml parse tree after use, using xml_free()
+ */
+int
+clicon_xml_parse(cxobj **cxtop,
+ char *format, ...)
+{
+ int retval = -1;
+ va_list args;
+ char *str = NULL;
+ int len;
+
+ va_start(args, format);
+ len = vsnprintf(NULL, 0, format, args) + 1;
+ va_end(args);
+ if ((str = malloc(len)) == NULL){
+ clicon_err(OE_UNIX, errno, "malloc");
+ goto done;
+ }
+ memset(str, 0, len);
+ va_start(args, format);
+ len = vsnprintf(str, len, format, args) + 1;
+ va_end(args);
+ if ((*cxtop = xml_new("top", NULL)) == NULL)
+ return -1;
+ if (xml_parse(str, *cxtop) < 0)
+ goto done;
+ retval = 0;
+ done:
+ if (str)
+ free(str);
+ return retval;
+}
+
/*! Copy single xml node without copying children
*/
static int
@@ -1402,3 +1460,54 @@ xml_body_uint32(cxobj *xb,
cv_free(cv);
return 0;
}
+
+/*! Map xml operation from string to enumeration
+ * @param[in] xn XML node
+ * @param[out] op "operation" attribute may change operation
+ */
+int
+xml_operation(char *opstr,
+ enum operation_type *op)
+{
+ if (strcmp("merge", opstr) == 0)
+ *op = OP_MERGE;
+ else if (strcmp("replace", opstr) == 0)
+ *op = OP_REPLACE;
+ else if (strcmp("create", opstr) == 0)
+ *op = OP_CREATE;
+ else if (strcmp("delete", opstr) == 0)
+ *op = OP_DELETE;
+ else if (strcmp("remove", opstr) == 0)
+ *op = OP_REMOVE;
+ else if (strcmp("none", opstr) == 0)
+ *op = OP_NONE;
+ else{
+ clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
+ return -1;
+ }
+ return 0;
+}
+
+char *
+xml_operation2str(enum operation_type op)
+{
+ switch (op){
+ case OP_MERGE:
+ return "merge";
+ break;
+ case OP_REPLACE:
+ return "replace";
+ break;
+ case OP_CREATE:
+ return "create";
+ break;
+ case OP_DELETE:
+ return "delete";
+ break;
+ case OP_REMOVE:
+ return "remove";
+ break;
+ default:
+ return "none";
+ }
+}
diff --git a/lib/src/clixon_xml_db.c b/lib/src/clixon_xml_db.c
index e85d5f58..d79a93ff 100644
--- a/lib/src/clixon_xml_db.c
+++ b/lib/src/clixon_xml_db.c
@@ -121,7 +121,6 @@
#include "clixon_options.h"
#include "clixon_xsl.h"
#include "clixon_xml_parse.h"
-#include "clixon_xml_db_rpc.h"
#include "clixon_xml_db.h"
/*! Construct an xml key format from yang statement using wildcards for keys
@@ -231,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:
@@ -417,6 +417,8 @@ static int _candidate_locked = 0;
static int _startup_locked = 0;
/*! Lock database
+ * @param[in] db Database name
+ * @param[in] pid process/session-id
*/
static int
db_lock(char *db,
@@ -433,6 +435,7 @@ db_lock(char *db,
}
/*! Unlock database
+ * @param[in] db Database name
*/
static int
db_unlock(char *db)
@@ -446,6 +449,21 @@ db_unlock(char *db)
return 0;
}
+/*! Unlock all databases locked by pid (eg process dies)
+ * @param[in] pid process/session-id
+ */
+static int
+db_unlock_all(int pid)
+{
+ if (_running_locked == pid)
+ _running_locked = 0;
+ if (_candidate_locked == pid)
+ _candidate_locked = 0;
+ if (_startup_locked == pid)
+ _startup_locked = 0;
+ return 0;
+}
+
/*! returns id of locker
* @retval 0 Not locked
* @retval >0 Id of locker
@@ -462,6 +480,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"
@@ -645,9 +665,9 @@ get(char *dbname,
cxobj *xt)
{
int retval = -1;
- char **vec;
+ char **vec = NULL;
int nvec;
- char **valvec;
+ char **valvec = NULL;
int nvalvec;
int i;
int j;
@@ -671,7 +691,7 @@ get(char *dbname,
clicon_err(OE_DB, 0, "Invalid key: %s", xk);
goto done;
}
- if ((vec = clicon_strsplit(xk, "/", &nvec, __FUNCTION__)) == NULL)
+ if ((vec = clicon_strsep(xk, "/", &nvec)) == NULL)
goto done;
/* Element 0 is NULL '/',
Element 1 is top symbol and needs to find subs in all modules:
@@ -739,7 +759,9 @@ get(char *dbname,
cvi = NULL;
/* Iterate over individual yang keys */
cprintf(cb, "%s", name);
- 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){
retval = 0;
@@ -814,7 +836,10 @@ get(char *dbname,
}
retval = 0;
done:
- unchunk_group(__FUNCTION__);
+ if (vec)
+ free(vec);
+ if (valvec)
+ free(valvec);
if (cvk)
cvec_free(cvk);
return retval;
@@ -959,8 +984,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; i1)
+ clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt;
retval = 0;
done:
@@ -1057,87 +1084,6 @@ xmldb_get_local(clicon_handle h,
}
-/*! Get content of database using path.
-
- * The function returns a minimal tree that includes all sub-trees that match
- * xpath.
- * @param[in] h Clicon handle
- * @param[in] db running | candidate
- * @param[in] xpath String with XPATH syntax
- * @param[in] vector If set, return list of results in xvec, else single tree.
- * @param[out] xtop XML tree. Freed by xml_free()
- * @param[out] xvec Vector of xml trees. Free after use
- * @param[out] xlen Length of vector.
- * @retval 0 OK
- * @retval -1 Error
- * @code
- * cxobj *xt;
- * cxobj **xvec;
- * size_t xlen;
- *
- * if (xmldb_get("running", "/interfaces/interface[name="eth"]",
- * &xt, &xvec, &xlen) < 0)
- * err;
- * for (i=0; 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,100 +1188,262 @@ 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"
-/*! Local variant of xmldb_put */
+ * @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_local(clicon_handle h,
- char *db,
- cxobj *xt,
- enum operation_type op)
+xmldb_put_xkey(clicon_handle h,
+ char *db,
+ enum operation_type op,
+ char *xk,
+ char *val)
+
{
int retval = -1;
cxobj *x = NULL;
- yang_stmt *ys;
+ yang_stmt *y = NULL;
+ yang_stmt *ykey;
+ char **vec = NULL;
+ int nvec;
+ char **valvec = NULL;
+ 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 *dbfilename = NULL;
+ char *filename = NULL;
yspec = clicon_dbspec_yang(h);
- if (db2file(h, db, &dbfilename) < 0)
+ if (db2file(h, db, &filename) < 0)
+ goto done;
+ if (xk == NULL || *xk!='/'){
+ clicon_err(OE_DB, 0, "Invalid key: %s", xk);
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));
+ 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_strsep(xk, "/", &nvec)) == 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)
+ free(valvec);
+ if ((valvec = clicon_strsep(restval, ",", &nvalvec)) == 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;
}
- 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;
+ 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 (dbfilename)
- free(dbfilename);
+ if (filename)
+ free(filename);
+ if (ckey)
+ cbuf_free(ckey);
+ if (csubkey)
+ cbuf_free(csubkey);
+ if (crx)
+ cbuf_free(crx);
+ if (cvk)
+ cvec_free(cvk);
+ if (vec)
+ free(vec);
+ if (valvec)
+ free(valvec);
+
+ unchunk_group(__FUNCTION__);
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;
yang_stmt *ykey;
- char **vec;
+ char **vec = NULL;
int nvec;
int i;
char *name;
@@ -1368,7 +1478,7 @@ xmldb_put_tree_local(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);
@@ -1513,268 +1623,83 @@ xmldb_put_tree_local(clicon_handle h,
cbuf_free(crx);
if (cvk)
cvec_free(cvk);
+ if (vec)
+ free(vec);
unchunk_group(__FUNCTION__);
return retval;
-
}
-/*!
- * @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
- */
-int
-xmldb_put_tree(clicon_handle h,
- char *db,
- char *api_path,
- cxobj *xt,
- enum operation_type op)
-{
- if (clicon_xmldb_rpc(h))
- return -1; /* XXX */
- else
- return xmldb_put_tree_local(h, db, api_path, xt, op);
-}
-
-/*! Modify database provided an XML database key and an operation
+/*! Modify database provided an xml tree 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] 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
- * Local variant of xmldb_put_xkey
+ * 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
*/
-static int
-xmldb_put_xkey_local(clicon_handle h,
- char *db,
- char *xk,
- char *val,
- enum operation_type op)
+int
+xmldb_put(clicon_handle h,
+ char *db,
+ enum operation_type op,
+ char *api_path,
+ cxobj *xt)
{
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_stmt *ys;
yang_spec *yspec;
- char *filename = NULL;
+ 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, &filename) < 0)
- goto done;
- if (xk == NULL || *xk!='/'){
- clicon_err(OE_DB, 0, "Invalid key: %s", xk);
+ 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;
}
- 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++;
+ 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 (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);
+ 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;
}
- 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;
+ 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 (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__);
+ 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
- * @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 +1707,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 +1732,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 +1764,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 +1791,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 +1820,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 +1840,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 +1870,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 +1894,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 +1918,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 +1991,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_map.c b/lib/src/clixon_xml_map.c
index 3f962e1d..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;
}
@@ -292,6 +288,7 @@ xml_yang_validate(cxobj *xt,
yang_stmt *yc;
int i;
yang_stmt *ys;
+ char *body;
/* if not given by argument (overide) use default link */
ys = ys0?ys0:xml_spec(xt);
@@ -321,17 +318,19 @@ xml_yang_validate(cxobj *xt,
/* In the union case, value is parsed as generic REST type,
* needs to be reparsed when concrete type is selected
*/
- if (cv_parse(xml_body(xt), 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;
+ 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:
diff --git a/lib/src/clixon_xml_parse.h b/lib/src/clixon_xml_parse.h
index 4211379f..848bf55e 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, remove all non-terminal bodies (strip prettyr-print) */
};
extern char *clixon_xml_parsetext;
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_xml_parse.y b/lib/src/clixon_xml_parse.y
index d668f70c..ede62b8e 100644
--- a/lib/src/clixon_xml_parse.y
+++ b/lib/src/clixon_xml_parse.y
@@ -100,36 +100,22 @@ 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;
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 (s0 == ' ' || s0 == '\n' || s0 == '\t'){
- str[0] = ' ';
- if (xml_value(xn)[sz-1] == ' ')
- goto ok;
- }
- }
- else{
- if (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;
@@ -147,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)
{
@@ -193,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);
@@ -214,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);
@@ -300,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");}
;
@@ -314,8 +324,8 @@ emnt1 : ESLASH {_YA->ya_xelement = NULL;
;
etg : BSLASH NAME '>'
- { if (xml_parse_bslash1(_YA, $2) < 0) YYABORT;
- clicon_debug(3, "etg -> < NAME >"); }
+{ clicon_debug(3, "etg -> < NAME %s>", $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 -> < NAME:NAME >"); }
@@ -328,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 -> "); }
;
diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c
index 87861328..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)
@@ -552,12 +563,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 +772,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;
@@ -926,8 +932,8 @@ xpath_each(cxobj *cxtop,
/*! A restricted xpath that returns a vector of matches
*
- * See xpath1() on details for subset.
- * @param[in] cxtop xml-tree where to search
+ * See xpath1() on details for subset
+. * @param[in] cxtop xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value
@@ -945,7 +951,7 @@ xpath_each(cxobj *cxtop,
* }
* free(vec);
* @endcode
- * Note that although the returned vector must be freed after use, the returned xml
+ * @Note that although the returned vector must be freed after use, the returned xml
* trees need not be.
* @see also xpath_first, xpath_each.
*/
@@ -987,7 +993,27 @@ xpath_vec(cxobj *cxtop,
}
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
+ * @param[in] cxtop xml-tree where to search
+ * @param[in] xpath string with XPATH syntax
* @param[in] flags Set of flags that return nodes must match (0 if all)
+ * @param[out] vec vector of xml-trees. Vector must be free():d after use
+ * @param[out] veclen returns length of vector in return value
+ * @retval 0 OK
+ * @retval -1 error.
+ * @code
+ * cxobj **vec;
+ * size_t veclen;
+ * if (xpath_vec_flag(cxtop, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
+ * goto err;
+ * for (i=0; iys_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;
@@ -2010,7 +1975,8 @@ yang_arg2cvec(yang_stmt *ys,
}
}
done:
- unchunk_group(__FUNCTION__);
+ if (vec)
+ free(vec);
return cvv;
}
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;
}
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..ba9672a4 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
@@ -61,3 +61,21 @@ EOF
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..24d30a9f 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
@@ -10,6 +10,10 @@
# include err() and new() functions
. ./lib.sh
+# For memcheck
+#clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli"
+clixon_cli=clixon_cli
+
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $clixon_cf
@@ -18,26 +22,72 @@ 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 "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" ""
+expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0" ""
new "cli show configuration"
-expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth0
+expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth0
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" ""
-expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
+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" ""
+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" ""
+# 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 =====\"$"
new "Kill backend"
# Check if still alive
diff --git a/test/test2.sh b/test/test2.sh
index c640c28d..0267e87c 100755
--- a/test/test2.sh
+++ b/test/test2.sh
@@ -1,15 +1,13 @@
-#!/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
+# 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
@@ -18,15 +16,87 @@ 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" "eth0eth1true9.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 eth1"
+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 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" "]]>]]>]]>]]>" "^]]>]]>]]>]]>$"
+
+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 +109,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..0c04bdc6
--- /dev/null
+++ b/test/test3.sh
@@ -0,0 +1,72 @@
+#!/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 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
$"
+
+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 head"
+expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
+
+new "restconf POST config"
+expectfn 'curl -sX POST -d {"type":"eth"} 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 "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
+
+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
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