Change internal protocol from clicon_proto.h to netconf.

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

View file

@ -29,6 +29,7 @@
#
# ***** END LICENSE BLOCK *****
- Change internal protocol from clicon_proto.h to netconf.
- Netconf startup configuration support. Set CLICON_USE_STARTUP_CONFIG to 1 to
enable. Eg, if backend_main is started with -CIr startup will be copied to
running.

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -126,6 +126,10 @@ generic_validate(yang_spec *yspec,
}
/*! Common code of candidate_validate and candidate_commit
* @param[in] h Clicon handle
* @param[in] candidate The candidate database. The wanted backend state
* @retval 0 OK
* @retval -1 Fatal error or netconf error XXX Differentiate
*/
static int
validate_common(clicon_handle h,
@ -137,7 +141,6 @@ validate_common(clicon_handle h,
int i;
cxobj *xn;
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
@ -253,126 +256,117 @@ candidate_commit(clicon_handle h,
return retval;
}
/*! Do a diff between candidate and running, then start a validate transaction
*
* @param[in] h Clicon handle
* @param[in] candidate: The candidate database. The wanted backend state
*/
int
candidate_validate(clicon_handle h,
char *candidate)
{
int retval = -1;
transaction_data_t *td = NULL;
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
/* Common steps (with commit) */
if (validate_common(h, candidate, td) < 0)
goto done;
retval = 0;
done:
/* In case of failure, call plugin transaction termination callbacks */
if (retval < 0 && td)
plugin_transaction_abort(h, td);
if (td)
transaction_free(td);
return retval;
}
/*! Handle an incoming commit message from a client.
* XXX: If commit succeeds and snapshot/startup fails, we have strange state:
* the commit has succeeded but an error message is returned.
/*! Discard all changes in candidate / revert to running
* @param[in] h Clicon handle
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
* @retval -1 (Local) Error
*/
int
from_client_commit(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
from_client_commit(clicon_handle h,
cbuf *cbret)
{
int retval = -1;
char *candidate;
char *running;
if (clicon_msg_commit_decode(msg,
&candidate,
&running,
label) < 0)
goto err;
if (strcmp(candidate, "candidate") && strcmp(candidate, "tmp")){
clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp");
goto err;
}
if (strcmp(running, "running")){
clicon_err(OE_PLUGIN, 0, "running db is not \"running\"");
goto err;
}
if (candidate_commit(h, "candidate") < 0){
clicon_debug(1, "Commit %s failed", candidate);
retval = 0; /* We ignore errors from commit, but maybe
we should fail on fatal errors? */
goto err;
clicon_debug(1, "Commit candidate failed");
/* XXX: candidate_validate should have proper error handling */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>",
clicon_err_reason);
goto ok;
}
clicon_debug(1, "Commit %s", candidate);
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
if (send_msg_ok(s) < 0)
goto done;
goto done;
err:
/* XXX: more elaborate errstring? */
if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
retval = -1;
done:
unchunk_group(__FUNCTION__);
// done:
return retval; /* may be zero if we ignoring errors from commit */
} /* from_client_commit */
/*! Discard all changes in candidate / revert to running
* @param[in] h Clicon handle
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client
* @retval -1 (Local) Error
*/
int
from_client_discard_changes(clicon_handle h,
cbuf *cbret)
{
int retval = -1;
if (xmldb_copy(h, "running", "candidate") < 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>application</error-type>"
"<error-severity>error</error-severity>"
"<error-info>read-registry</error-info>"
"</rpc-error></rpc-reply>");
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
// done:
return retval; /* may be zero if we ignoring errors from commit */
}
/*! Handle an incoming validate message from a client.
* @param[in] h Clicon handle
* @param[in] db Database name
* @param[out] cbret Return xml value cligen buffer
* @retval 0 OK. This may indicate both ok and err msg back to client (eg invalid)
* @retval -1 (Local) Error
*/
int
from_client_validate(clicon_handle h,
int s,
struct clicon_msg *msg,
const char *label)
from_client_validate(clicon_handle h,
char *db,
cbuf *cbret)
{
int retval = -1;
char *candidate;
int retval = -1;
transaction_data_t *td = NULL;
if (clicon_msg_validate_decode(msg,
&candidate,
label) < 0){
send_msg_err(s, clicon_errno, clicon_suberrno,
clicon_err_reason);
goto err;
if (strcmp(db, "candidate") != 0 && strcmp(db, "tmp") != 0){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>invalid-value</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"</rpc-error></rpc-reply>");
goto ok;
}
if (strcmp(candidate, "candidate") != 0 && strcmp(candidate, "tmp") != 0){
clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp");
goto err;
}
clicon_debug(1, "Validate %s", candidate);
if (candidate_validate(h, candidate) < 0){
clicon_debug(1, "Validate %s failed", candidate);
retval = 0; /* We ignore errors from commit, but maybe
we should fail on fatal errors? */
goto err;
}
retval = 0;
if (send_msg_ok(s) < 0)
clicon_debug(1, "Validate %s", db);
/* 1. Start transaction */
if ((td = transaction_new()) == NULL)
goto done;
goto done;
err:
/* XXX: more elaborate errstring? */
if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
retval = -1;
done:
unchunk_group(__FUNCTION__);
/* Common steps (with commit) */
if (validate_common(h, db, td) < 0){
clicon_debug(1, "Validate %s failed", db);
/* XXX: candidate_validate should have proper error handling */
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>missing-attribute</error-tag>"
"<error-type>protocol</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"</rpc-error></rpc-reply>",
clicon_err_reason);
goto ok;
}
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
ok:
retval = 0;
done:
if (retval < 0 && td)
plugin_transaction_abort(h, td);
if (td)
transaction_free(td);
return retval;
} /* from_client_validate */

View file

@ -40,8 +40,9 @@
/*
* Prototypes
*/
int from_client_validate(clicon_handle h, int s, struct clicon_msg *msg, const char *label);
int from_client_commit(clicon_handle h, int s, struct clicon_msg *msg, const char *label);
int candidate_commit(clicon_handle h, char *candidate);
int from_client_validate(clicon_handle h, char *db, cbuf *cbret);
int from_client_commit(clicon_handle h, cbuf *cbret);
int from_client_discard_changes(clicon_handle h, cbuf *cbret);
int candidate_commit(clicon_handle h, char *db);
#endif /* _BACKEND_COMMIT_H_ */

View file

@ -37,7 +37,6 @@
#ifndef _BACKEND_HANDLE_H_
#define _BACKEND_HANDLE_H_
/*
* Prototypes
* not exported.

View file

@ -72,7 +72,7 @@
#include "backend_handle.h"
/* Command line options to be passed to getopt(3) */
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:ptx:"
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pt"
/*! Terminate. Cannot use h after this */
static int
@ -141,8 +141,7 @@ usage(char *argv0, clicon_handle h)
" -r\t\tReload running database\n"
" -p \t\tPrint database yang specification\n"
" -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\n"
" -g <group>\tClient membership required to this group (default: %s)\n"
" -x <status>\tSet CLICON_XMLDB_RPC to 0 or 1.\n",
" -g <group>\tClient membership required to this group (default: %s)\n",
argv0,
plgdir ? plgdir : "none",
confsock ? confsock : "none",
@ -153,11 +152,12 @@ usage(char *argv0, clicon_handle h)
}
static int
rundb_init(clicon_handle h)
db_reset(clicon_handle h,
char *db)
{
if (xmldb_delete(h, "running") != 0 && errno != ENOENT)
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
return -1;
if (xmldb_init(h, "running") < 0)
if (xmldb_init(h, db) < 0)
return -1;
return 0;
}
@ -192,7 +192,7 @@ rundb_main(clicon_handle h,
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL)
if (xmldb_put(h, "tmp", xn, OP_MERGE) < 0)
if (xmldb_put(h, "tmp", OP_MERGE, NULL, xn) < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;
@ -426,14 +426,6 @@ main(int argc, char **argv)
case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */
printalt++;
break;
case 'x' : /* set xmldb rpc on */
{
int i;
if (sscanf(optarg, "%d", &i) != 1)
usage(argv[0], h);
clicon_option_int_set(h, "CLICON_XMLDB_RPC", i);
}
break;
default:
usage(argv[0], h);
break;
@ -503,7 +495,7 @@ main(int argc, char **argv)
if (yang_spec_main(h, stdout, printspec) < 0)
goto done;
/* First check for starup config
/* First check for startup config
XXX the options below have become out-of-hand.
Too complex, need to simplify*/
if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){
@ -513,8 +505,12 @@ main(int argc, char **argv)
goto done;
}
else
if (rundb_init(h) < 0)
if (db_reset(h, "running") < 0)
goto done;
if (xmldb_init(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* If running exists and reload_running set, make a copy to candidate */
if (reload_running){
@ -529,9 +525,17 @@ main(int argc, char **argv)
/* Init running db
* -I or if it isnt there
*/
if (init_rundb || xmldb_exists(h, "running") != 1)
if (rundb_init(h) < 0)
if (init_rundb || xmldb_exists(h, "running") != 1){
if (db_reset(h, "running") < 0)
goto done;
}
/* If candidate does not exist, create it from running */
if (xmldb_exists(h, "candidate") != 1){
if (xmldb_init(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* Initialize plugins
(also calls plugin_init() and plugin_start(argc,argv) in each plugin */

View file

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

View file

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

View file

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

View file

@ -133,6 +133,7 @@ backend_notify(clicon_handle h,
struct handle_subscription *hs;
int retval = -1;
clicon_debug(1, "%s %s", __FUNCTION__, stream);
/* First thru all clients(sessions), and all subscriptions and find matches */
for (ce = backend_client_list(h); ce; ce = ce_next){
ce_next = ce->ce_next;
@ -257,7 +258,8 @@ backend_notify_xml(clicon_handle h,
}
struct client_entry *
backend_client_add(clicon_handle h, struct sockaddr *addr)
backend_client_add(clicon_handle h,
struct sockaddr *addr)
{
struct backend_handle *cb = handle(h);
struct client_entry *ce;
@ -411,3 +413,79 @@ subscription_each(clicon_handle h,
hs = cb->cb_subscription;
return hs;
}
/* Database dependency description */
struct backend_netconf_reg {
qelem_t nr_qelem; /* List header */
backend_netconf_cb_t nr_callback; /* Validation/Commit Callback */
void *nr_arg; /* Application specific argument to cb */
char *nr_tag; /* Xml tag when matched, callback called */
};
typedef struct backend_netconf_reg backend_netconf_reg_t;
static backend_netconf_reg_t *deps = NULL;
/*! Register netconf callback
* Called from plugin to register a callback for a specific netconf XML tag.
*/
int
backend_netconf_register_callback(clicon_handle h,
backend_netconf_cb_t cb, /* Callback called */
void *arg, /* Arg to send to callback */
char *tag) /* Xml tag when callback is made */
{
backend_netconf_reg_t *nr;
if ((nr = malloc(sizeof(backend_netconf_reg_t))) == NULL) {
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
goto catch;
}
memset (nr, 0, sizeof (*nr));
nr->nr_callback = cb;
nr->nr_arg = arg;
nr->nr_tag = strdup(tag); /* strdup */
INSQ(nr, deps);
return 0;
catch:
if (nr){
if (nr->nr_tag)
free(nr->nr_tag);
free(nr);
}
return -1;
}
/*! See if there is any callback registered for this tag
*
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></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;
}

View file

@ -43,6 +43,14 @@
/*
* Types
*/
struct client_entry;
typedef int (*backend_netconf_cb_t)(
clicon_handle h,
cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg <rpc-reply>... */
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_ */

View file

@ -74,36 +74,6 @@
#include "cli_common.h"
/*! Initialize candidate database
* We have implemented these:
* shared - all users share a common candidate db
*/
int
init_candidate_db(clicon_handle h)
{
int retval = -1;
if (xmldb_exists(h, "running") != 1){
clicon_err(OE_FATAL, 0, "Running db does not exist");
goto err;
}
if (xmldb_exists(h, "candidate") != 1)
if (clicon_rpc_copy(h, "running", "candidate") < 0)
goto err;
retval = 0;
err:
return retval;
}
/*! Exit candidate db
* (private canddidates should be removed?)
*/
int
exit_candidate_db(clicon_handle h)
{
return 0;
}
/*! Register log notification stream
* @param[in] h Clicon handle
* @param[in] stream Event stream. CLICON is predefined, others are application-defined
@ -114,13 +84,13 @@ exit_candidate_db(clicon_handle h)
* Note this calls cligen_regfd which may callback on cli command interpretator
*/
int
cli_notification_register(clicon_handle h,
char *stream,
cli_notification_register(clicon_handle h,
char *stream,
enum format_enum format,
char *filter,
int status,
int (*fn)(int, void*),
void *arg)
char *filter,
int status,
int (*fn)(int, void*),
void *arg)
{
int retval = -1;
char *logname;
@ -139,10 +109,10 @@ cli_notification_register(clicon_handle h,
if (status){ /* start */
if (s_exist!=-1){
clicon_err(OE_PLUGIN, 0, "%s: result log socket already exists", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Result log socket already exists");
goto done;
}
if (clicon_rpc_subscription(h, status, stream, format, filter, &s) < 0)
if (clicon_rpc_create_subscription(h, stream, filter, &s) < 0)
goto done;
if (cligen_regfd(s, fn, arg) < 0)
goto done;
@ -154,9 +124,10 @@ cli_notification_register(clicon_handle h,
cligen_unregfd(s_exist);
}
hash_del(cdat, logname);
if (clicon_rpc_subscription(h, status, stream, format, filter, NULL) < 0)
#if 0 /* cant turn off */
if (clicon_rpc_create_subscription(h, status, stream, format, filter, NULL) < 0)
goto done;
#endif
}
retval = 0;
done:
@ -214,7 +185,7 @@ cli_signal_flush(clicon_handle h)
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
* @param[in] op Operation to perform on database
* Cvv will contain forst the complete cli string, and then a set of optional
* Cvv will contain first the complete cli string, and then a set of optional
* instantiated variables.
* Example:
* cvv[0] = "set interfaces interface eth0 type bgp"
@ -257,7 +228,7 @@ cli_dbxmlv(clicon_handle h,
if (clicon_rpc_change(h, "candidate", op, xk, str) < 0)
goto done;
if (clicon_autocommit(h)) {
if (clicon_rpc_commit(h, "candidate", "running") < 0)
if (clicon_rpc_commit(h) < 0)
goto done;
}
retval = 0;
@ -465,9 +436,7 @@ cli_commitv(clicon_handle h,
{
int retval = -1;
if ((retval = clicon_rpc_commit(h,
"candidate",
"running")) < 0){ /* startup */
if ((retval = clicon_rpc_commit(h)) < 0){ /* startup */
cli_output(stderr, "Commit failed. Edit and try again or discard changes");
goto done;
}
@ -483,10 +452,12 @@ cli_validatev(clicon_handle h,
cvec *vars,
cvec *argv)
{
int retval = -1;
int retval = -1;
if ((retval = clicon_rpc_validate(h, "candidate")) < 0)
clicon_err(OE_CFG, 0, "Validate failed. Edit and try again or discard changes");
goto done;
retval = 0;
done:
return retval;
}
@ -575,9 +546,9 @@ compare_dbsv(clicon_handle h,
astext = cv_int32_get(cvec_i(argv, 0));
else
astext = 0;
if (xmldb_get(h, "running", "/", &xc1, NULL, NULL) < 0)
if (clicon_rpc_get_config(h, "running", "/", &xc1) < 0)
goto done;
if (xmldb_get(h, "candidate", "/", &xc2, NULL, NULL) < 0)
if (clicon_rpc_get_config(h, "candidate", "/", &xc2) < 0)
goto done;
if (compare_xmls(xc1, xc2, astext) < 0) /* astext? */
goto done;
@ -620,7 +591,6 @@ load_config_filev(clicon_handle h,
char *varstr;
int fd = -1;
cxobj *xt = NULL;
cxobj *xn;
cxobj *x;
cbuf *cbxml;
@ -663,20 +633,25 @@ load_config_filev(clicon_handle h,
}
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL){
if (xt == NULL)
goto done;
/* Ensure top-level is "config", maybe this is too rough? */
xml_name_set(xt, "config");
// if ((xn = xml_child_i(xt, 0)) != NULL){
if ((cbxml = cbuf_new()) == NULL)
goto done;
x = NULL;
while ((x = xml_child_each(xn, x, -1)) != NULL)
while ((x = xml_child_each(xt, x, -1)) != NULL)
if (clicon_xml2cbuf(cbxml, x, 0, 0) < 0)
goto done;
if (clicon_rpc_xmlput(h, "candidate",
replace?OP_REPLACE:OP_MERGE,
"",
cbuf_get(cbxml)) < 0)
if (clicon_rpc_edit_config(h, "candidate",
replace?OP_REPLACE:OP_MERGE,
"",
cbuf_get(cbxml)) < 0)
goto done;
cbuf_free(cbxml);
}
// }
ret = 0;
done:
unchunk_group(__FUNCTION__);
@ -741,13 +716,13 @@ save_config_filev(clicon_handle h,
goto done;
}
filename = vecp[0];
if (xmldb_get(h, dbstr, "/", &xt, NULL, NULL) < 0)
if (clicon_rpc_get_config(h, dbstr,"/", &xt) < 0)
goto done;
if ((f = fopen(filename, "wb")) == NULL){
clicon_err(OE_CFG, errno, "Creating file %s", filename);
goto done;
}
if (xml_print(f, xt) < 0)
if (clicon_xml2file(f, xt, 0, 0) < 0)
goto done;
retval = 0;
/* Fall through */
@ -782,10 +757,8 @@ delete_allv(clicon_handle h,
clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
goto done;
}
if (clicon_rpc_change(h, "candidate",
OP_REMOVE,
"/", "") < 0)
goto done;
if (clicon_rpc_delete_config(h, dbstr) < 0)
goto done;
retval = 0;
done:
return retval;
@ -798,7 +771,7 @@ discard_changesv(clicon_handle h,
cvec *cvv,
cvec *argv)
{
return clicon_rpc_copy(h, "running", "candidate");
return clicon_rpc_copy_config(h, "running", "candidate");
}
/*! Copy from one database to another, eg running->startup
@ -814,7 +787,7 @@ db_copy(clicon_handle h,
db1 = cv_string_get(cvec_i(argv, 0));
db2 = cv_string_get(cvec_i(argv, 1));
return clicon_rpc_copy(h, db1, db2);
return clicon_rpc_copy_config(h, db1, db2);
}
/* These are strings that can be used as 3rd argument to cli_setlog */
@ -831,18 +804,18 @@ static int
cli_notification_cb(int s,
void *arg)
{
struct clicon_msg *reply;
struct clicon_msg *reply = NULL;
enum clicon_msg_type type;
int eof;
int retval = -1;
char *eventstr = NULL;
int level;
cxobj *xt = NULL;
cxobj *xn;
cxobj *xe;
char *format = (char*)arg;
/* get msg (this is the reason this function is called) */
if (clicon_msg_rcv(s, &reply, &eof, __FUNCTION__) < 0)
if (clicon_msg_rcv(s, &reply, &eof) < 0)
goto done;
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
@ -855,11 +828,13 @@ cli_notification_cb(int s,
goto done;
type = ntohs(reply->op_type);
switch (type){
case CLICON_MSG_NOTIFY:
if (clicon_msg_notify_decode(reply, &level, &eventstr, __FUNCTION__) < 0)
case CLICON_MSG_NETCONF:
if (clicon_msg_netconf_decode(reply, &xt) < 0)
goto done;
if ((xe = xpath_first(xt, "//event")) != NULL)
eventstr = xml_body(xe);
if (strcmp(format, SHOWAS_TXT) == 0){
fprintf(stdout, "%s", eventstr);
fprintf(stdout, "%s\n", eventstr);
}
else
if (strcmp(format, SHOWAS_XML) == 0){
@ -871,25 +846,20 @@ cli_notification_cb(int s,
}
else
if (strcmp(format, SHOWAS_XML2TXT) == 0){
if (clicon_xml_parse_string(&eventstr, &xt) < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL)
if ((xn = xml_child_i(xe, 0)) != NULL)
if (xml2txt(stdout, xn, 0) < 0)
goto done;
}
else
if (strcmp(format, SHOWAS_XML2JSON) == 0){
if (clicon_xml_parse_string(&eventstr, &xt) < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL){
if ((xn = xml_child_i(xe, 0)) != NULL){
if (xml2json(stdout, xn, 0) < 0)
goto done;
}
}
break;
default:
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %d",
__FUNCTION__, type);
clicon_err(OE_PROTO, 0, "unexpected reply: %d", type);
goto done;
break;
}
@ -897,15 +867,15 @@ cli_notification_cb(int s,
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__); /* event allocated by chunk */
if (reply)
free(reply);
return retval;
}
/*! Make a notify subscription to backend and un/register callback for return messages.
*
* @param[in] h Clicon handle
* @param[in] cvv Not used
* @param[in] cvv Not used
* @param[in] arg A string with <log stream name> <stream status> [<format>]
* where <status> is "0" or "1"
* and <format> is XXX
@ -943,7 +913,7 @@ cli_notifyv(clicon_handle h,
"",
status,
cli_notification_cb,
(void*)formatstr) < 0)
(void*)strdup(formatstr)) < 0)
goto done;
retval = 0;
@ -951,6 +921,67 @@ cli_notifyv(clicon_handle h,
return retval;
}
/*! Lock database
*
* @param[in] h Clicon handle
* @param[in] cvv Not used
* @param[in] arg A string with <database>
* @code
* lock("comment"), cli_lock("running");
* @endcode
* XXX: format is a memory leak
*/
int
cli_lock(clicon_handle h,
cvec *cvv,
cvec *argv)
{
char *db;
int retval = -1;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "Requires arguments: <db>");
goto done;
}
db = cv_string_get(cvec_i(argv, 0));
if (clicon_rpc_lock(h, db) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Unlock database
*
* @param[in] h Clicon handle
* @param[in] cvv Not used
* @param[in] arg A string with <database>
* @code
* lock("comment"), cli_lock("running");
* @endcode
* XXX: format is a memory leak
*/
int
cli_unlock(clicon_handle h,
cvec *cvv,
cvec *argv)
{
char *db;
int retval = -1;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "Requires arguments: <db>");
goto done;
}
db = cv_string_get(cvec_i(argv, 0));
if (clicon_rpc_unlock(h, db) < 0)
goto done;
retval = 0;
done:
return retval;
}
/* Here are backward compatible cligen callback functions used when
* the option: CLICON_CLIGEN_CALLBACK_SINGLE_ARG is set.
*/
@ -1076,8 +1107,6 @@ save_config_file(clicon_handle h,
return retval;
}
int
cli_notify(clicon_handle h,
cvec *cvv,
@ -1126,15 +1155,15 @@ cli_notify(clicon_handle h,
return retval;
}
/*
* cli_debug
* set debug level on stderr (not syslog).
/*! set debug level on stderr (not syslog).
* The level is either what is specified in arg as int argument.
* _or_ if a 'level' variable is present in vars use that value instead.
* XXX obsolete. Use cli_debug_cliv or cli_debug_backendv instead
*/
int
cli_debug(clicon_handle h, cvec *vars, cg_var *arg)
cli_debug(clicon_handle h,
cvec *vars,
cg_var *arg)
{
cg_var *cv;
int level;

View file

@ -77,10 +77,10 @@ cli_terminate(clicon_handle h)
{
yang_spec *yspec;
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
cli_plugin_finish(h);
exit_candidate_db(h);
cli_handle_exit(h);
return 0;
}
@ -359,21 +359,12 @@ main(int argc, char **argv)
goto done;
}
/* A client does not have access to the candidate (and running)
databases if both these conditions are true:
1. clicon_sock_family(h) == AF_INET[6]
*/
if (clicon_sock_family(h) == AF_UNIX)
if (init_candidate_db(h) < 0)
return -1;
if (logclisyntax)
cli_logsyntax_set(h, logclisyntax);
if (debug)
clicon_option_dump(h, debug);
/* Join rest of argv to a single command */
restarg = clicon_strjoin(argc, argv, " ", __FUNCTION__);

View file

@ -142,12 +142,14 @@ expandv_dbvar(void *h,
*/
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
goto done;
if (xmldb_get(h, dbstr, xkpath, &xt, &xvec, &xlen) < 0)
goto done;
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
goto done;
/* One round to detect duplicates
* XXX The code below would benefit from some cleanup
*/
j = 0;
if (xpath_vec(xt, xkpath, &xvec, &xlen) < 0)
goto done;
for (i = 0; i < xlen; i++) {
char *str;
x = xvec[i];
@ -187,7 +189,6 @@ expandv_dbvar(void *h,
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
if (xvec)
free(xvec);
if (xt)
@ -474,7 +475,7 @@ show_confv_as(clicon_handle h,
}
else
cprintf(cbx, "%s", xpath);
if (xmldb_get(h, db, cbuf_get(cbx), xt, NULL, NULL) < 0)
if (clicon_rpc_get_config(h, db, cbuf_get(cbx), xt) < 0)
goto done;
retval = 0;
done:
@ -482,7 +483,6 @@ done:
free(val);
if (cbx)
cbuf_free(cbx);
unchunk_group(__FUNCTION__);
return retval;
}
@ -597,7 +597,6 @@ show_confv_as_text1(clicon_handle h,
done:
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
@ -732,7 +731,9 @@ show_confv_xpath(clicon_handle h,
}
cv = cvec_find_var(cvv, "xpath");
xpath = cv_string_get(cv);
if (xmldb_get(h, str, xpath, &xt, &xv, &xlen) < 0)
if (clicon_rpc_get_config(h, str, xpath, &xt) < 0)
goto done;
if (xpath_vec(xt, xpath, &xv, &xlen) < 0)
goto done;
for (i=0; i<xlen; i++)
xml_print(stdout, xv[i]);
@ -743,7 +744,6 @@ done:
free(xv);
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}
@ -806,7 +806,9 @@ expand_dbvar(void *h,
*/
if (xmlkeyfmt2xpath(xkfmt, cvv, &xkpath) < 0)
goto done;
if (xmldb_get(h, dbstr, xkpath, &xt, &xvec, &xlen) < 0)
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
goto done;
if (xpath_vec(xt, xkpath, &xvec, &xlen) < 0)
goto done;
/* One round to detect duplicates
* XXX The code below would benefit from some cleanup
@ -948,7 +950,7 @@ show_conf_as(clicon_handle h,
}
else
cprintf(cbx, "%s", xpath);
if (xmldb_get(h, db, cbuf_get(cbx), xt, NULL, NULL) < 0)
if (clicon_rpc_get_config(h, db, cbuf_get(cbx), xt) < 0)
goto done;
retval = 0;
done:
@ -1160,51 +1162,3 @@ 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: <dbname> <xpath>
* @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<xlen; i++)
xml_print(stdout, xv[i]);
retval = 0;
done:
if (xv)
free(xv);
if (xt)
xml_free(xt);
unchunk_group(__FUNCTION__);
return retval;
}

View file

@ -68,8 +68,6 @@ int cli_handle_exit(clicon_handle h);
cligen_handle cli_cligen(clicon_handle h);
/* cli_common.c */
int init_candidate_db(clicon_handle h);
int exit_candidate_db(clicon_handle h);
int cli_notification_register(clicon_handle h, char *stream, enum format_enum format,
char *filter, int status,
int (*fn)(int, void*), void *arg);
@ -92,6 +90,8 @@ int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv);
int cli_lock(clicon_handle h, cvec *cvv, cvec *argv);
int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv);
/* cli_common.c: CLIgen old single arg callbacks */
int cli_set(clicon_handle h, cvec *vars, cg_var *arg);

View file

@ -209,21 +209,20 @@ main(int argc, char **argv)
}
if (dumpdb){
/* Here db must be local file-path */
if (xmldb_dump_local(stdout, db, matchkey)) {
if (xmldb_dump(stdout, db, matchkey)) {
fprintf(stderr, "Match error\n");
goto done;
}
}
if (addent) /* add entry */
if (xmldb_put_xkey(h, db, addstr, NULL, OP_REPLACE) < 0)
if (xmldb_put_xkey(h, db, OP_REPLACE, NULL, addstr) < 0)
goto done;
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)

View file

@ -44,11 +44,9 @@
*/
typedef int (*netconf_cb_t)(
clicon_handle h,
cxobj *xorig, /* Original request. */
cxobj *xn, /* Sub-tree (under xorig) at child: <rpc><xn></rpc> */
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: <rpc><xn></rpc> */
cxobj **xret, /* Return xml tree, eg <rpc-reply>... */
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,

View file

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

View file

@ -68,22 +68,8 @@
/*
* Exported variables
*/
enum transport_type transport = NETCONF_SSH;
int cc_closed = 0;
static int cc_ok = 0;
void
netconf_ok_set(int ok)
{
cc_ok = ok;
}
int
netconf_ok_get(void)
{
return cc_ok;
}
enum transport_type transport = NETCONF_SSH; /* XXX Remove SOAP support */
int cc_closed = 0; /* XXX Please remove (or at least hide in handle) this global variable */
int
add_preamble(cbuf *xf)
@ -191,8 +177,7 @@ detect_endtag(char *tag, char ch, int *state)
* @retval dbname Actual database file name
*/
char *
netconf_get_target(clicon_handle h,
cxobj *xn,
netconf_get_target(cxobj *xn,
char *path)
{
cxobj *x;
@ -218,11 +203,13 @@ netconf_get_target(clicon_handle h,
* @param[in] msg Only for debug
*/
int
netconf_output(int s, cbuf *xf, char *msg)
netconf_output(int s,
cbuf *xf,
char *msg)
{
char *buf = cbuf_get(xf);
int len = cbuf_len(xf);
int retval = -1;
int len = cbuf_len(xf);
int retval = -1;
clicon_debug(1, "SEND %s", msg);
if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */

View file

@ -60,11 +60,6 @@ enum error_option{ /* edit-config */
CONTINUE_ON_ERROR
};
enum filter_option{ /* get-config/filter */
FILTER_SUBTREE,
FILTER_XPATH
};
/*
* Variables
*/
@ -74,14 +69,11 @@ extern int cc_closed;
/*
* Prototypes
*/
void netconf_ok_set(int ok);
int netconf_ok_get(void);
int add_preamble(cbuf *xf);
int add_postamble(cbuf *xf);
int add_error_preamble(cbuf *xf, char *reason);
int detect_endtag(char *tag, char ch, int *state);
char *netconf_get_target(clicon_handle h, cxobj *xn, char *path);
char *netconf_get_target(cxobj *xn, char *path);
int add_error_postamble(cbuf *xf);
int netconf_output(int s, cbuf *xf, char *msg);

View file

@ -82,11 +82,12 @@ process_incoming_packet(clicon_handle h,
{
char *str;
char *str0;
cxobj *xml_req = NULL; /* Request (in) */
cxobj *xreq = NULL; /* Request (in) */
int isrpc = 0; /* either hello or rpc */
cbuf *xf_out;
cbuf *xf_err;
cbuf *xf1;
cbuf *cbret = NULL;
cxobj *xret = NULL; /* Return (out) */
cxobj *xrpc;
cxobj *xc;
clicon_debug(1, "RECV");
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
@ -96,16 +97,15 @@ process_incoming_packet(clicon_handle h,
}
str = str0;
/* Parse incoming XML message */
if (clicon_xml_parse_string(&str, &xml_req) < 0){
if ((cb = cbuf_new()) == NULL){
netconf_create_rpc_error(cb, NULL,
"operation-failed",
"rpc", "error",
NULL,
NULL);
if (clicon_xml_parse_string(&str, &xreq) < 0){
if ((cbret = cbuf_new()) == NULL){
cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>internal error</error-message>"
"</rpc-error></rpc-reply>");
netconf_output(1, cb, "rpc-error");
cbuf_free(cb);
}
else
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
@ -113,72 +113,56 @@ process_incoming_packet(clicon_handle h,
goto done;
}
free(str0);
if (xpath_first(xml_req, "//rpc") != NULL){
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
isrpc++;
}
else
if (xpath_first(xml_req, "//hello") != NULL)
if (xpath_first(xreq, "//hello") != NULL)
;
else{
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\
ed");
goto done;
}
/* Initialize response buffers */
if ((xf_out = cbuf_new()) == NULL){
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
goto done;
if (!isrpc){ /* hello */
if (netconf_hello_dispatch(xreq) < 0)
goto done;
}
else /* rpc */
if (netconf_rpc_dispatch(h, xrpc, &xret) < 0){
goto done;
}
else{ /* there is a return message in xret */
cxobj *xa, *xa2;
assert(xret);
/* Create error buf */
if ((xf_err = cbuf_new()) == NULL){
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
goto done;
}
netconf_ok_set(0);
if (isrpc){
if (netconf_rpc_dispatch(h,
xml_req,
xpath_first(xml_req, "//rpc"),
xf_out, xf_err) < 0){
assert(cbuf_len(xf_err));
clicon_debug(1, "%s", cbuf_get(xf_err));
if (isrpc){
if (netconf_output(1, xf_err, "rpc-error") < 0)
goto done;
if ((cbret = cbuf_new()) != NULL){
if ((xc = xml_child_i(xret,0))!=NULL){
xa=NULL;
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
if (xml_addsub(xc, xa2) < 0)
goto done;
}
add_preamble(cbret);
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
add_postamble(cbret);
if (netconf_output(1, cbret, "rpc-reply") < 0){
cbuf_free(cbret);
goto done;
}
}
}
}
else{
if ((xf1 = cbuf_new()) != NULL){
if (netconf_create_rpc_reply(xf1, xml_req, cbuf_get(xf_out), netconf_ok_get()) < 0){
cbuf_free(xf_out);
cbuf_free(xf_err);
cbuf_free(xf1);
goto done;
}
if (netconf_output(1, xf1, "rpc-reply") < 0){
cbuf_reset(xf1);
netconf_create_rpc_error(xf1, xml_req, "operation-failed",
"protocol", "error",
NULL, cbuf_get(xf_err));
netconf_output(1, xf1, "rpc-error");
cbuf_free(xf_out);
cbuf_free(xf_err);
cbuf_free(xf1);
goto done;
}
cbuf_free(xf1);
}
}
}
else{
netconf_hello_dispatch(xml_req); /* XXX: return-value */
}
cbuf_free(xf_out);
cbuf_free(xf_err);
done:
if (xml_req)
xml_free(xml_req);
if (xreq)
xml_free(xreq);
if (xret)
xml_free(xret);
if (cbret)
cbuf_free(cbret);
return 0;
}
@ -268,28 +252,12 @@ send_hello(int s)
return retval;
}
/*! Initialize candidate database */
static int
init_candidate_db(clicon_handle h)
{
int retval = -1;
/* init shared candidate */
if (xmldb_exists(h, "candidate") != 1){
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
static int
terminate(clicon_handle h)
netconf_terminate(clicon_handle h)
{
yang_spec *yspec;
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
clicon_handle_exit(h);
@ -402,8 +370,6 @@ main(int argc, char **argv)
if (netconf_plugin_load(h) < 0)
return -1;
if (init_candidate_db(h) < 0)
return -1;
/* Call start function is all plugins before we go interactive */
tmp = *(argv-1);
*(argv-1) = argv0;
@ -416,13 +382,11 @@ main(int argc, char **argv)
goto done;
if (debug)
clicon_option_dump(h, debug);
if (event_loop() < 0)
goto done;
done:
netconf_plugin_unload(h);
terminate(h);
netconf_terminate(h);
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
return 0;

View file

@ -66,6 +66,15 @@
#include "netconf_lib.h"
#include "netconf_plugin.h"
/* Database dependency description */
struct netconf_reg {
qelem_t nr_qelem; /* List header */
netconf_cb_t nr_callback; /* Validation/Commit Callback */
void *nr_arg; /* Application specific argument to cb */
char *nr_tag; /* Xml tag when matched, callback called */
};
typedef struct netconf_reg netconf_reg_t;
/*! Unload a plugin
*/
static int
@ -245,10 +254,11 @@ catch:
/*! See if there is any callback registered for this tag
*
* @param xn Sub-tree (under xorig) at child of rpc: <rpc><xn></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: <rpc><xn></rpc>.
* @param[out] cb Output xml stream. For reply
* @param[out] cb_err Error xml stream. For error reply
* @param[out] xret Return XML, error or OK
*
* @retval -1 Error
* @retval 0 OK, not found handler.
@ -256,31 +266,24 @@ catch:
*/
int
netconf_plugin_callbacks(clicon_handle h,
cxobj *xn,
cbuf *xf,
cbuf *xf_err,
cxobj *xorig)
cxobj *xn,
cxobj **xret)
{
netconf_reg_t *nr;
netconf_reg_t *nreg;
int retval;
if (deps == NULL)
return 0;
nr = deps;
nreg = deps;
do {
if (strcmp(nr->nr_tag, xml_name(xn)) == 0){
if ((retval = nr->nr_callback(h,
xorig,
xn,
xf,
xf_err,
nr->nr_arg)) < 0)
if (strcmp(nreg->nr_tag, xml_name(xn)) == 0){
if ((retval = nreg->nr_callback(h, xn, xret, nreg->nr_arg)) < 0)
return -1;
else
return 1; /* handled */
}
nr = NEXTQ(netconf_reg_t *, nr);
} while (nr != deps);
nreg = NEXTQ(netconf_reg_t *, nreg);
} while (nreg != deps);
return 0;
}

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -54,6 +54,9 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et
}
]
curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
Debugging
---------
Start the restconf programs with debug flag:

View file

@ -141,16 +141,15 @@ api_data_get(clicon_handle h,
int i;
cbuf *path = NULL;
cbuf *path1 = NULL;
cxobj *xt = NULL;
cbuf *cbx = NULL;
cxobj **vec = NULL;
size_t veclen;
yang_spec *yspec;
yang_stmt *y;
yang_stmt *ykey;
char *name;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
cxobj *xret = NULL;
clicon_debug(1, "%s", __FUNCTION__);
yspec = clicon_dbspec_yang(h);
@ -217,32 +216,34 @@ api_data_get(clicon_handle h,
}
}
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
if (xmldb_get(h, "running", cbuf_get(path), &xt, &vec, &veclen) < 0)
goto done;
if ((cbx = cbuf_new()) == NULL)
goto done;
if (veclen==0){
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
notfound(r);
goto done;
}
if ((cbx = cbuf_new()) == NULL)
goto done;
FCGX_SetExitStatus(200, r->out); /* OK */
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
FCGX_FPrintF(r->out, "\r\n");
if (xml2json_cbuf_vec(cbx, vec, veclen, 0) < 0)
clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret));
vec = xml_childvec_get(xret);
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
goto done;
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
FCGX_FPrintF(r->out, "%s", cbuf_get(cbx));
FCGX_FPrintF(r->out, "\r\n\r\n");
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cbx)
cbuf_free(cbx);
if (xt)
xml_free(xt);
if (path)
cbuf_free(path);
if (path1)
cbuf_free(path1);
if (xret)
xml_free(xret);
return retval;
}
@ -266,14 +267,14 @@ api_data_delete(clicon_handle h,
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
/* Parse input data as json into xml */
if (clicon_rpc_xmlput(h, "candidate",
OP_REMOVE,
api_path,
"") < 0)
if (clicon_rpc_edit_config(h, "candidate",
OP_REMOVE,
api_path,
"<config/>") < 0){
notfound(r);
goto done;
if (clicon_rpc_commit(h, "candidate", "running") < 0)
}
if (clicon_rpc_commit(h) < 0)
goto done;
FCGX_SetExitStatus(201, r->out);
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
@ -338,16 +339,23 @@ api_data_put(clicon_handle h,
}
if ((cbx = cbuf_new()) == NULL)
goto done;
cprintf(cbx, "<config>");
x = NULL;
while ((x = xml_child_each(xdata, x, -1)) != NULL)
while ((x = xml_child_each(xdata, x, -1)) != NULL) {
if (clicon_xml2cbuf(cbx, x, 0, 0) < 0)
goto done;
if (clicon_rpc_xmlput(h, "candidate",
OP_MERGE,
api_path,
cbuf_get(cbx)) < 0)
}
cprintf(cbx, "</config>");
clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
if (clicon_rpc_edit_config(h, "candidate",
OP_MERGE,
api_path,
cbuf_get(cbx)) < 0){
notfound(r);
goto done;
if (clicon_rpc_commit(h, "candidate", "running") < 0)
}
if (clicon_rpc_commit(h) < 0)
goto done;
FCGX_SetExitStatus(201, r->out); /* Created */
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
@ -437,7 +445,12 @@ request_process(clicon_handle h,
clicon_debug(1, "DATA=%s", data);
if (str2cvec(data, '&', '=', &dvec) < 0)
goto done;
method = pvec[2];
if ((method = pvec[2]) == NULL){
retval = notfound(r);
goto done;
}
retval = 0;
test(r, 1);
/* If present, check credentials */
@ -448,6 +461,7 @@ request_process(clicon_handle h,
if (auth == 0)
goto done;
clicon_debug(1, "%s credentials ok 2", __FUNCTION__);
clicon_debug(1, "%s credentials ok 3", __FUNCTION__);
if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
retval = api_data(h, r, path, pcvec, 2, qvec, data);
@ -469,6 +483,18 @@ request_process(clicon_handle h,
return retval;
}
static int
restconf_terminate(clicon_handle h)
{
yang_spec *yspec;
clicon_rpc_close_session(h);
if ((yspec = clicon_dbspec_yang(h)) != NULL)
yspec_free(yspec);
clicon_handle_exit(h);
return 0;
}
/*! Usage help routine
* @param[in] argv0 command line
* @param[in] h Clicon handle
@ -592,5 +618,6 @@ main(int argc,
retval = 0;
done:
restconf_plugin_unload(h);
restconf_terminate(h);
return retval;
}

View file

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

View file

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include <clixon/clixon.h>
/* 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, "<error>%s</error>", 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
* <rpc>
* <get>
* <source><candidate/></source>
* <xpath>/</xpath>
* <vector/> # If set send back list of xpath hits not single tree
* </get>
* </rpc>
* @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<xlen; i++){
xc = xvec[i];
if (clicon_xml2cbuf(cb, xc, 0, 0) < 0)
goto done;
}
}
else{
if (clicon_xml2cbuf(cb, xt, 0, 0) < 0)
goto done;
}
if (debug)
fprintf(stderr, "%s: \"%s\" len:%d\n",
__FUNCTION__, cbuf_get(cb), cbuf_len(cb)+1);
if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
clicon_err(OE_UNIX, errno, "write");
goto done;
}
drop:
retval = 0;
done:
if (xt)
xml_free(xt);
if (xvec)
free(xvec);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Process incoming xmldb put message
* @param[in] s Stream socket
* @param[in] cb Packet buffer
* @param[in] xr XML request node with root in "put"
* example
* <rpc>
* <put>
* <target><candidate/></target>
* <default-operation>merge|none|replace</default-operation>
* <config>
* ...
* </config>
* </put>
* </rpc>
* @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, "<ok/>", strlen("<ok/>")+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, "<ok/>", strlen("<ok/>")+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
* <rpc>
* <copy>
* <source><candidate/></source>
* <target><running/></target>
* </copy>
* </rpc>
* @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, "<ok/>", strlen("<ok/>")+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
* <rpc>
* <exists>
* <target><candidate/></target>
* <id>43</id>
* </exists>
* </rpc>
* @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
* <rpc>
* <unlock>
* <target><candidate/></target>
* <id>43</id>
* </unlock>
* </rpc>
* @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
* <rpc>
* <islocked>
* <target><candidate/></target>
* </islocked>
* </rpc>
* @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, "<locked>%u</locked>", ret);
if (write(s, cbuf_get(cb), cbuf_len(cb)+1) < 0){
clicon_err(OE_UNIX, errno, "write");
goto done;
}
}
else{
if (write(s, "<unlocked/>", strlen("<unlocked/>")+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
* <rpc>
* <exists>
* <target><candidate/></target>
* </exists>
* </rpc>
* @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, "<ok/>", strlen("<ok/>")+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
* <rpc>
* <delete>
* <target><candidate/></target>
* </delete>
* </rpc>
* @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, "<ok/>", strlen("<ok/>")+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
* <rpc>
* <init>
* <target><candidate/></target>
* </init>
* </rpc>
* @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, "<ok/>", strlen("<ok/>")+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: <rpc><get></get></rpc>]]>]]>
*/
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<len; i++){
if (buf[i] == 0)
continue; /* Skip NULL chars (eg from terminals) */
cprintf(cb, "%c", buf[i]);
if (detect_endtag("]]>]]>",
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 <file>\tCLICON config file\n"
"\t-a <addr>\tIP address\n"
"\t-p <port>\tTCP port\n"
"\t-y <dir>\tYang dir\n"
"\t-m <mod>\tYang main module name\n"
"\t-r <rev>\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;
}

View file

@ -121,11 +121,6 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
# Directory where "running", "candidate" and "startup" are placed
CLICON_XMLDB_DIR localstatedir/APPNAME
# Set if xmldb runs in a separate process (clixon_xmldb).
# If set, also set xmldb_addr and xmldb_port below
# CLICON_XMLDB_RPC 0
# xmldb inet address (if CLICON_XMLDB_RPC)
# CLICON_XMLDB_ADDR
# xmldb tcp port (if CLICON_XMLDB_RPC)

View file

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

View file

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

View file

@ -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 "<rpc><get-config><source><candidate/></source><configuration/></get-config></rpc>]]>]]>" | 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 <host>
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
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
<rpc-reply><ok/></rpc-reply>]]>]]>
<notification><event>Routing notification</event></notification>]]>]]>
<notification><event>Routing notification</event></notification>]]>]]>
...

View file

@ -14,7 +14,6 @@ clixon_netconf -f /usr/local/etc/routing.conf
1. Setting data example using netconf
-------------------------------------
<rpc><edit-config><target><candidate/></target><config>
<interfaces>
<interface>
@ -45,7 +44,38 @@ clixon_netconf -f /usr/local/etc/routing.conf
<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>
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:
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
<rpc-reply><ok/></rpc-reply>]]>]]>
<notification><event>Routing notification</event></notification>]]>]]>
<notification><event>Routing notification</event></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

View file

@ -698,4 +698,4 @@
}
}
}
}
}

View file

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

View file

@ -42,7 +42,7 @@
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
/* clicon */
#include <cligen/cligen.h>
@ -53,6 +53,9 @@
/* These include signatures for plugin and transaction callbacks. */
#include <clixon/clixon_backend.h>
/* forward */
static int notification_timer_setup(clicon_handle h);
/*! This is called on validate (and commit). Check validity of candidate
*/
int
@ -83,7 +86,47 @@ transaction_commit(clicon_handle h,
return 0;
}
/*! Routing example notifcation timer handler. Here is where the periodic action is
*/
static int
notification_timer(int fd,
void *arg)
{
int retval = -1;
clicon_handle h = (clicon_handle)arg;
if (backend_notify(h, "ROUTING", 0, "Routing notification") < 0)
goto done;
if (notification_timer_setup(h) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Set up routing notifcation timer
*/
static int
notification_timer_setup(clicon_handle h)
{
struct timeval t, t1;
gettimeofday(&t, NULL);
t1.tv_sec = 10; t1.tv_usec = 0;
timeradd(&t, &t1, &t);
return event_reg_timeout(t, notification_timer, h, "notification timer");
}
static int
routing_downcall(clicon_handle h,
cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */
cbuf *cbret, /* Reply eg <rpc-reply>... */
void *arg) /* Argument given at register */
{
cprintf(cbret, "<rpc-reply><ok>%s</ok></rpc-reply>", xml_body(xe));
return 0;
}
/*
* Plugin initialization
*/
@ -92,8 +135,15 @@ plugin_init(clicon_handle h)
{
int retval = -1;
if (notification_timer_setup(h) < 0)
goto done;
if (backend_netconf_register_callback(h, routing_downcall,
NULL,
"myrouting"/* Xml tag when callback is made */
) < 0)
goto done;
retval = 0;
// done:
done:
return retval;
}

View file

@ -71,7 +71,7 @@ int
mycallback(clicon_handle h, cvec *cvv, cvec *argv)
{
int retval = -1;
cxobj *xt = NULL;
cxobj *xret = NULL;
cg_var *myvar;
/* Access cligen callback variables */
@ -80,14 +80,51 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */
/* Show eth0 interfaces config using XPATH */
if (xmldb_get(h, "candidate",
"/interfaces/interface[name=eth0]",
&xt, NULL, NULL) < 0)
if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]",
&xret) < 0)
goto done;
xml_print(stdout, xt);
xml_print(stdout, xret);
retval = 0;
done:
if (xt)
xml_free(xt);
if (xret)
xml_free(xret);
return retval;
}
/*! get argument and send as string to backend as RPC (which returns the string)
*/
int
downcall(clicon_handle h,
cvec *vars,
cvec *argv)
{
int retval = -1;
struct clicon_msg *msg = NULL;
char *str="";
cg_var *cv;
cxobj *xret=NULL;
cxobj *xerr;
cxobj *xdata;
if (cvec_len(vars)==2){
if ((cv = cvec_i(vars, 1)) != NULL)
str = cv_string_get(cv);
}
if ((msg = clicon_msg_netconf_encode("<rpc><myrouting>%s</myrouting></rpc>", 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;
}

View file

@ -17,13 +17,14 @@ delete("Delete a configuration item") all("Delete whole candidate configuration
startup("Store running as startup config"), db_copy("running","startup");
no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cliv((int32)0);
debug("Debugging parts of the system"), cli_debug_cliv((int32)1);{
level("Set debug level: 1..n") <level:int32>("Set debug level (0..n)"), cli_debug_cliv();
level("Set debug level: 1..n") <level:int32>("Set debug level (0..n)"), cli_debug_backendv();
}
discard("Discard edits (rollback 0)"), discard_changesv();
compare("Compare running and candidate"), compare_dbsv((int32)1);
show("Show a particular state of the system"){
xpath("Show configuration") <xpath:string>("XPATH expression"), show_confv_xpath("candidate","/");
xpath("Show configuration") <xpath:string>("XPATH expression"), show_confv_xpath("candidate");
compare("Compare candidate and running databases"), compare_dbsv((int32)0);{
xml("Show comparison in xml"), compare_dbsv((int32)0);
text("Show comparison in text"), compare_dbsv((int32)1);
@ -43,3 +44,8 @@ load("Load configuration from XML file") <filename:string>("Filename (local file
merge("Merge file with existent candidate"), load_config_filev("filename","merge");
}
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
downcall("This is a downcall") <str:rest>, downcall();
notify("Get notifications from backend"), cli_notifyv("ROUTING","1","txt");
no("Negate") notify("Get notifications from backend"), cli_notifyv("ROUTING","0","xml");
lock,cli_lock("candidate");
unlock,cli_unlock("candidate");

View file

@ -109,9 +109,6 @@ int clicon_cli_varonly_set(clicon_handle h, int val);
int clicon_cli_genmodel_completion(clicon_handle h);
char *clicon_xmldb_dir(clicon_handle h);
int clicon_xmldb_rpc(clicon_handle h);
char *clicon_xmldb_addr(clicon_handle h);
uint16_t clicon_xmldb_port(clicon_handle h);
char *clicon_quiet_mode(clicon_handle h);
enum genmodel_type clicon_cli_genmodel_type(clicon_handle h);

View file

@ -48,16 +48,10 @@ enum format_enum{
/* See also map_type2str in clicon_proto.c */
enum clicon_msg_type{
CLICON_MSG_COMMIT = 1, /* Commit a configuration db->running_db
current state, set running_db. Body is:
1. uint32: (1)snapshot while doing commit, (0) dont
2. uint32: (1)save to startup-config, (0) dont
3. string: name of 'from' database (eg "candidate")
4. string: name of 'to' database (eg "running")
CLICON_MSG_NETCONF = 1, /* Generic netconf message (lock/unlock/..) can all
msgs go to this?
1. string: netconf message
*/
CLICON_MSG_VALIDATE, /* Validate settings in a database. Body is:
1. string: name of database (eg "candidate")
*/
CLICON_MSG_CHANGE, /* Change a (single) database entry:
1. uint32: operation: OP_MERGE/OP_REPLACE/OP_REMOVE
2. uint32: length of value string
@ -65,55 +59,6 @@ enum clicon_msg_type{
4. string: key
5. string: value (can be NULL)
*/
CLICON_MSG_XMLPUT, /* Send database entries as XML to backend daemon
1. uint32: operation: LV_SET/LV_DELETE
2. string: name of database to change (eg current)
3. string: restconf api path
4. string: XML data
*/
CLICON_MSG_SAVE, /* Save config state from db to a file in backend. Body is:
1. uint32: make snapshot (1), dont(0)
2. string: name of database to save from (eg running)
3. string: filename to write. If snapshot=1, then this
is empty.
*/
CLICON_MSG_LOAD, /* Load config state from file in backend to db via XML. Body is:
1. uint32: whether to replace/initdb before load (1) or
merge (0).
2. string: name of database to load into (eg running)
3. string: filename to load from
*/
CLICON_MSG_COPY, /* Copy from file to file in backend. Body is:
1. string: filename to copy from
2. string: filename to copy to
*/
CLICON_MSG_KILL, /* Kill (other) session:
1. session-id
*/
CLICON_MSG_DEBUG, /* Debug
1. session-id
*/
CLICON_MSG_CALL , /* Backend plugin call request. Body is:
1. struct clicon_msg_call_req *
*/
CLICON_MSG_SUBSCRIPTION, /* Create a new notification subscription.
Body is:
1. int: status off/on
1. int: format (enum format_enum)
2. string: name of notify stream
3. string: filter, if format=xml: xpath, if text: fnmatch */
CLICON_MSG_OK, /* server->client reply */
CLICON_MSG_NOTIFY, /* Notification. Body is:
1. int: loglevel
2. event: log message. */
CLICON_MSG_ERR /* server->client reply.
Body is:
1. uint32: man error category
2. uint32: sub-error
3. string: reason
*/
};
/* Protocol message header */
@ -123,55 +68,37 @@ struct clicon_msg {
char op_body[0]; /* rest of message, actual data */
};
/* Generic clicon message. Either generic/internal message
or application-specific backend plugin downcall request */
struct clicon_msg_call_req {
uint16_t cr_len; /* Length of total request */
uint16_t cr_op; /* Generic application-defined operation */
char *cr_plugin; /* Name of backend plugin, NULL -> internal
functions */
char *cr_func; /* Function name in plugin (or internal) */
uint16_t cr_arglen; /* App specific argument length */
char *cr_arg; /* App specific argument */
char cr_data[0]; /* Allocated data containng the above */
};
/*
* Prototypes
*/
#ifndef LIBCLICON_API
int clicon_connect_unix(char *sockpath);
int clicon_rpc_connect_unix(struct clicon_msg *msg,
char *sockpath,
char **data,
uint16_t *datalen,
int *sock0,
const char *label);
int clicon_rpc_connect_unix(struct clicon_msg *msg,
char *sockpath,
char **ret,
int *sock0);
int clicon_rpc_connect_inet(struct clicon_msg *msg,
char *dst,
uint16_t port,
char **data,
uint16_t *datalen,
int *sock0,
const char *label);
int clicon_rpc_connect_inet(struct clicon_msg *msg,
char *dst,
uint16_t port,
char **ret,
int *sock0);
int clicon_rpc(int s, struct clicon_msg *msg, char **data, uint16_t *datalen,
const char *label);
int clicon_rpc(int s, struct clicon_msg *msg, char **xret);
#endif
int clicon_msg_send(int s, struct clicon_msg *msg);
int clicon_msg_rcv(int s, struct clicon_msg **msg,
int *eof, const char *label);
int clicon_msg_rcv(int s, struct clicon_msg **msg, int *eof);
int send_msg_notify(int s, int level, char *event);
int send_msg_reply(int s, uint16_t type, char *data, uint16_t datalen);
int send_msg_ok(int s);
int send_msg_ok(int s, char *data);
int send_msg_err(int s, int err, int suberr, char *format, ...);
int send_msg_netconf_reply(int s, char *format, ...);
#endif /* _CLIXON_PROTO_H_ */

View file

@ -40,27 +40,28 @@
#ifndef _CLIXON_PROTO_CLIENT_H_
#define _CLIXON_PROTO_CLIENT_H_
int clicon_rpc_commit(clicon_handle h, char *from, char *to);
int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
int *sock0);
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
int clicon_rpc_generate_error(cxobj *xerr);
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, cxobj **xret);
int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op,
char *api_path, char *xml);
int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2);
int clicon_rpc_delete_config(clicon_handle h, char *db);
int clicon_rpc_lock(clicon_handle h, char *db);
int clicon_rpc_unlock(clicon_handle h, char *db);
int clicon_rpc_close_session(clicon_handle h);
int clicon_rpc_kill_session(clicon_handle h, int session_id);
int clicon_rpc_validate(clicon_handle h, char *db);
int clicon_rpc_commit(clicon_handle h);
// discard-changes
int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter,
int *s);
int clicon_rpc_change(clicon_handle h, char *db,
enum operation_type op, char *key, char *val);
int clicon_rpc_xmlput(clicon_handle h, char *db, enum operation_type op,
char *api_path, char *xml);
int clicon_rpc_dbitems(clicon_handle h, char *db, char *rx,
char *attr, char *val,
cvec ***cvv, size_t *cvvlen);
int clicon_rpc_save(clicon_handle h, char *dbname, int snapshot, char *filename);
int clicon_rpc_load(clicon_handle h, int replace, char *db, char *filename);
int clicon_rpc_copy(clicon_handle h, char *db1, char *db2);
int clicon_rpc_kill(clicon_handle h, int session_id);
int clicon_rpc_debug(clicon_handle h, int level);
int clicon_rpc_call(clicon_handle h, uint16_t op, char *plugin, char *func,
void *param, uint16_t paramlen,
char **ret, uint16_t *retlen,
const void *label);
int clicon_rpc_subscription(clicon_handle h, int status, char *stream,
enum format_enum format, char *filter, int *s);
#endif /* _CLIXON_PROTO_CLIENT_H_ */

View file

@ -42,27 +42,14 @@
/*
* Prototypes
*/
struct clicon_msg *
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
const char *label);
struct clicon_msg *clicon_msg_netconf_encode(char *format, ...);
struct clicon_msg *clicon_msg_netconf_encode_xml(cxobj *xml);
int
clicon_msg_commit_decode(struct clicon_msg *msg,
char **dbsrc, char **dbdst,
const char *label);
struct clicon_msg *
clicon_msg_validate_encode(char *db,
const char *label);
int
clicon_msg_validate_decode(struct clicon_msg *msg, char **db,
const char *label);
int clicon_msg_netconf_decode(struct clicon_msg *msg, cxobj **xml);
struct clicon_msg *
clicon_msg_change_encode(char *db, uint32_t op, char *key,
char *lvec, uint32_t lvec_len,
const char *label);
char *lvec, uint32_t lvec_len);
int
clicon_msg_change_decode(struct clicon_msg *msg,
@ -70,25 +57,9 @@ clicon_msg_change_decode(struct clicon_msg *msg,
char **lvec, uint32_t *lvec_len,
const char *label);
struct clicon_msg *
clicon_msg_xmlput_encode(char *db,
uint32_t op,
char *api_path,
char *xml,
const char *label);
int
clicon_msg_xmlput_decode(struct clicon_msg *msg,
char **db,
uint32_t *op,
char **api_path,
char **xml,
const char *label);
struct clicon_msg *
clicon_msg_dbitems_get_reply_encode(cvec **cvecv,
int cveclen,
const char *label);
int cveclen);
int
clicon_msg_dbitems_get_reply_decode(char *data,
uint16_t datalen,
@ -96,82 +67,4 @@ clicon_msg_dbitems_get_reply_decode(char *data,
size_t *cveclen,
const char *label);
struct clicon_msg *
clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename,
const char *label);
int
clicon_msg_save_decode(struct clicon_msg *msg,
char **db, uint32_t *snapshot, char **filename,
const char *label);
struct clicon_msg *
clicon_msg_load_encode(int replace, char *db, char *filename,
const char *label);
int
clicon_msg_load_decode(struct clicon_msg *msg,
int *replace, char **db, char **filename,
const char *label);
struct clicon_msg *
clicon_msg_copy_encode(char *db_src, char *db_dst,
const char *label);
int
clicon_msg_copy_decode(struct clicon_msg *msg,
char **db_src, char **db_dst,
const char *label);
struct clicon_msg *
clicon_msg_kill_encode(uint32_t session_id, const char *label);
int
clicon_msg_kill_decode(struct clicon_msg *msg, uint32_t *session_id,
const char *label);
struct clicon_msg *
clicon_msg_debug_encode(uint32_t level, const char *label);
int
clicon_msg_debug_decode(struct clicon_msg *msg, uint32_t *level,
const char *label);
struct clicon_msg *
clicon_msg_call_encode(uint16_t op, char *plugin, char *func,
uint16_t arglen, void *arg,
const char *label);
int
clicon_msg_call_decode(struct clicon_msg *msg,
struct clicon_msg_call_req **req,
const char *label);
struct clicon_msg *
clicon_msg_subscription_encode(int status,
char *stream,
enum format_enum format,
char *filter,
const char *label);
int clicon_msg_subscription_decode(struct clicon_msg *msg,
int *status,
char **stream,
enum format_enum *format,
char **filter,
const char *label);
struct clicon_msg *
clicon_msg_notify_encode(int level, char *event, const char *label);
int
clicon_msg_notify_decode(struct clicon_msg *msg, int *level,
char **event, const char *label);
struct clicon_msg *clicon_msg_err_encode(uint32_t err, uint32_t suberr,
char *reason, const char *label);
int clicon_msg_err_decode(struct clicon_msg *msg, uint32_t *err, uint32_t *suberr,
char **reason, const char *label);
#endif /* _CLIXON_PROTO_ENCODE_H_ */

View file

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

View file

@ -42,18 +42,19 @@
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
int xmldb_get(clicon_handle h, char *db, char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
int xmldb_put_tree(clicon_handle h, char *db, char *api_path,
cxobj *xt, enum operation_type op);
int xmldb_put_xkey(clicon_handle h, char *db,
char *xkey, char *val,
enum operation_type op);
int xmldb_dump_local(FILE *f, char *dbfilename, char *rxkey);
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
char *api_path, cxobj *xt);
int xmldb_put_xkey(clicon_handle h, char *db, enum operation_type op,
char *xkey, char *val);
int xmldb_dump(FILE *f, char *dbfilename, char *rxkey);
int xmldb_copy(clicon_handle h, char *from, char *to);
int xmldb_lock(clicon_handle h, char *db, int pid);
int xmldb_unlock(clicon_handle h, char *db, int pid);
int xmldb_unlock_all(clicon_handle h, int pid);
int xmldb_islocked(clicon_handle h, char *db);
int xmldb_exists(clicon_handle h, char *db);
int xmldb_delete(clicon_handle h, char *db);

View file

@ -69,7 +69,7 @@ SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
clixon_yang.c clixon_yang_type.c \
clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_encode.c clixon_proto_client.c \
clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_xml_db_rpc.c
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \

View file

@ -224,7 +224,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
*/
/* top: json -> value is also possible */
json : object J_EOF { clicon_debug(1,"json->object"); YYACCEPT; }
json : value J_EOF { clicon_debug(1,"json->object"); YYACCEPT; }
;
value : J_TRUE { json_current_body(_JY, "true");}

View file

@ -196,10 +196,6 @@ clicon_option_default(clicon_hash_t *copt)
if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0)
goto catch;
}
if (!hash_lookup(copt, "CLICON_XMLDB_RPC")){
if (hash_add(copt, "CLICON_XMLDB_RPC", "0", strlen("0")+1) < 0)
goto catch;
}
retval = 0;
catch:
unchunk_group(__FUNCTION__);
@ -616,35 +612,6 @@ clicon_xmldb_dir(clicon_handle h)
return clicon_option_str(h, "CLICON_XMLDB_DIR");
}
/*! Set if xmldb runs in a separate process (clixon_xmldb). */
int
clicon_xmldb_rpc(clicon_handle h)
{
char *s;
if ((s = clicon_option_str(h, "CLICON_XMLDB_RPC")) == NULL)
return 0; /* default 0 */
return atoi(s);
}
/*! Get xmldb inet address */
char *
clicon_xmldb_addr(clicon_handle h)
{
return clicon_option_str(h, "CLICON_XMLDB_ADDR");
}
/*! Get port for xmldb address in case of AF_INET or AF_INET6 */
uint16_t
clicon_xmldb_port(clicon_handle h)
{
char *s;
if ((s = clicon_option_str(h, "CLICON_XMLDB_PORT")) == NULL)
return -1;
return atoi(s);
}
/*! Get YANG specification
* Must use hash functions directly since they are not strings.
*/

View file

@ -68,6 +68,8 @@
#include "clixon_queue.h"
#include "clixon_chunk.h"
#include "clixon_sig.h"
#include "clixon_xml.h"
#include "clixon_xsl.h"
#include "clixon_proto.h"
#include "clixon_proto_encode.h"
@ -80,20 +82,8 @@ struct map_type2str{
/* Mapping between yang keyword string <--> clicon constants */
static const struct map_type2str msgmap[] = {
{CLICON_MSG_COMMIT, "commit"},
{CLICON_MSG_VALIDATE, "validate"},
{CLICON_MSG_NETCONF, "netconf"},
{CLICON_MSG_CHANGE, "change"},
{CLICON_MSG_XMLPUT, "xmlput"},
{CLICON_MSG_SAVE, "save"},
{CLICON_MSG_LOAD, "load"},
{CLICON_MSG_COPY, "copy"},
{CLICON_MSG_KILL, "kill"},
{CLICON_MSG_DEBUG, "debug"},
{CLICON_MSG_CALL, "call"},
{CLICON_MSG_SUBSCRIPTION, "subscription"},
{CLICON_MSG_OK, "ok"},
{CLICON_MSG_NOTIFY, "notify"},
{CLICON_MSG_ERR, "err"},
{-1, NULL},
};
@ -234,17 +224,14 @@ clicon_msg_send(int s,
* Now, ^C will interrupt the whole process, and this may not be what you want.
*
* @param[in] s UNIX domain socket to communicate with backend
* @param[out] msg CLICON msg data reply structure. allocated using CLICON chunks,
* freed by caller with unchunk*(...,label)
* @param[out] msg CLICON msg data reply structure. Free with free()
* @param[out] eof Set if eof encountered
* @param[in] label Label used in chunk allocation and deallocation.
* Note: caller must ensure that s is closed if eof is set after call.
*/
int
clicon_msg_rcv(int s,
struct clicon_msg **msg,
int *eof,
const char *label)
struct clicon_msg **msg,
int *eof)
{
int retval = -1;
struct clicon_msg hdr;
@ -273,8 +260,8 @@ clicon_msg_rcv(int s,
mlen = ntohs(hdr.op_len);
clicon_debug(2, "%s: rcv msg seq=%d, len=%d",
__FUNCTION__, ntohs(hdr.op_type), mlen);
if ((*msg = (struct clicon_msg *)chunk(mlen, label)) == NULL){
clicon_err(OE_CFG, errno, "%s: chunk", __FUNCTION__);
if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){
clicon_err(OE_CFG, errno, "malloc");
goto done;
}
memcpy(*msg, &hdr, hlen);
@ -296,17 +283,21 @@ clicon_msg_rcv(int s,
}
/*! Connect to server, send an clicon_msg message and wait for result.
* Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close.
* NOTE: this is dependent on unix domain
/*! Connect to server, send a clicon_msg message and wait for result using unix socket
*
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[in] sockpath Unix domain file path
* @param[out] retdata Returned data as string netconf xml tree.
* @param[out] sock0 Return socket in case of asynchronous notify
* @retval 0 OK
* @retval -1 Error
* @see clicon_rpc But this is one-shot rpc: open, send, get reply and close.
*/
int
clicon_rpc_connect_unix(struct clicon_msg *msg,
char *sockpath,
char **data,
uint16_t *datalen,
int *sock0,
const char *label)
char **retdata,
int *sock0)
{
int retval = -1;
int s = -1;
@ -325,7 +316,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
}
if ((s = clicon_connect_unix(sockpath)) < 0)
goto done;
if (clicon_rpc(s, msg, data, datalen, label) < 0)
if (clicon_rpc(s, msg, retdata) < 0)
goto done;
if (sock0 != NULL)
*sock0 = s;
@ -336,17 +327,23 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
return retval;
}
/*! Connect to server, send an clicon_msg message and wait for result using an inet socket
* Compared to clicon_rpc, this is a one-shot rpc: open, send, get reply and close.
/*! Connect to server, send a clicon_msg message and wait for result using an inet socket
* This uses unix domain socket communication
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[in] dst IPv4 address
* @param[in] port TCP port
* @param[out] retdata Returned data as string netconf xml tree.
* @param[out] sock0 Return socket in case of asynchronous notify
* @retval 0 OK
* @retval -1 Error
* @see clicon_rpc But this is one-shot rpc: open, send, get reply and close.
*/
int
clicon_rpc_connect_inet(struct clicon_msg *msg,
char *dst,
uint16_t port,
char **data,
uint16_t *datalen,
int *sock0,
const char *label)
char **retdata,
int *sock0)
{
int retval = -1;
int s = -1;
@ -371,7 +368,7 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
close(s);
goto done;
}
if (clicon_rpc(s, msg, data, datalen, label) < 0)
if (clicon_rpc(s, msg, retdata) < 0)
goto done;
if (sock0 != NULL)
*sock0 = s;
@ -391,29 +388,25 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
*
* @param[in] s Socket to communicate with backend
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
* @param[out] data Returned data as byte-strin exclusing header.
* Deallocate w unchunk...(..., label)
* @param[out] datalen Length of returned data
* @param[in] label Label used in chunk allocation.
* @param[out] xret Returned data as netconf xml tree.
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_rpc(int s,
struct clicon_msg *msg,
char **data,
uint16_t *datalen,
const char *label)
clicon_rpc(int s,
struct clicon_msg *msg,
char **ret)
{
int retval = -1;
struct clicon_msg *reply;
int eof;
uint32_t err;
uint32_t suberr;
char *reason;
enum clicon_msg_type type;
char *data = NULL;
cxobj *cx = NULL;
if (clicon_msg_send(s, msg) < 0)
goto done;
if (clicon_msg_rcv(s, &reply, &eof, label) < 0)
if (clicon_msg_rcv(s, &reply, &eof) < 0)
goto done;
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
@ -423,32 +416,38 @@ clicon_rpc(int s,
}
type = ntohs(reply->op_type);
switch (type){
case CLICON_MSG_OK:
if (data != NULL) {
*data = reply->op_body;
*datalen = ntohs(reply->op_len) - sizeof(*reply);
}
break;
case CLICON_MSG_ERR:
if (clicon_msg_err_decode(reply, &err, &suberr, &reason, label) < 0)
goto done;
if (debug)
clicon_err(err, suberr, "%s msgtype:%hu", reason, ntohs(msg->op_type));
else
clicon_err(err, suberr, "%s", reason);
goto done;
case CLICON_MSG_NETCONF: /* ok or rpc-error expected */
data = reply->op_body; /* assume string */
break;
default:
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %hu",
__FUNCTION__, type);
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %s",
__FUNCTION__, msg_type2str(type));
goto done;
break;
}
if (ret && data)
if ((*ret = strdup(data)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
retval = 0;
done:
if (cx)
xml_free(cx);
if (reply)
free(reply);
return retval;
}
/*! Send a clicon_msg message as reply to a clicon rpc request
*
* @param[in] s Socket to communicate with client
* @param[in] type Clicon msg operation, see enum clicon_msg_type
* @param[in] data Returned data as byte-string.
* @param[in] datalen Length of returned data XXX may be unecessary if always string?
* @retval 0 OK
* @retval -1 Error
*/
int
send_msg_reply(int s,
uint16_t type,
@ -475,57 +474,160 @@ send_msg_reply(int s,
return retval;
}
int
send_msg_ok(int s)
{
return send_msg_reply(s, CLICON_MSG_OK, NULL, 0);
}
/*! Send a clicon_msg NOTIFY message asynchronously to client
*
* @param[in] s Socket to communicate with client
* @param[in] level
* @param[in] event
* @retval 0 OK
* @retval -1 Error
*/
int
send_msg_notify(int s,
int level,
char *event)
{
int retval = -1;
struct clicon_msg *msg;
int retval = -1;
struct clicon_msg *msg = NULL;
if ((msg=clicon_msg_notify_encode(level, event, __FUNCTION__)) == NULL)
if ((msg=clicon_msg_netconf_encode("<notification><event>%s</event></notification>", event)) == NULL)
goto done;
if (clicon_msg_send(s, msg) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
if (msg)
free(msg);
return retval;
}
/*! Send a clicon_msg OK message as reply to a clicon rpc request
*
* @param[in] s Socket to communicate with client
* @param[in] data Returned data as byte-string.
* @retval 0 OK
* @retval -1 Error
* @note send as netconf message XXX remove clicon header
*/
int
send_msg_err(int s, int err, int suberr, char *format, ...)
send_msg_ok(int s,
char *data)
{
int retval = -1;
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL)
goto done;
if (data)
cprintf(cb, "<rpc-reply><ok>%s</ok></rpc-reply>", data);
else
cprintf(cb, "<rpc-reply><ok/></rpc-reply>");
if (send_msg_reply(s, CLICON_MSG_NETCONF, cbuf_get(cb), cbuf_len(cb)+1) < 0){
if (errno == ECONNRESET)
clicon_log(LOG_WARNING, "client rpc reset");
goto done;
}
retval=0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Send a clicon_msg Error message as reply to a clicon rpc request
*
* @param[in] s Socket to communicate with client
* @param[in] data Returned data as byte-string.
* @param[in] datalen Length of returned data
* @retval 0 OK
* @retval -1 Error
* @note send as netconf message XXX remove clicon header
*/
int
send_msg_err(int s,
int err,
int suberr,
char *format, ...)
{
va_list args;
char *reason;
int len;
int retval = -1;
struct clicon_msg *msg;
char *info = NULL;
int len;
int retval = -1;
cbuf *cb = NULL;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
if ((reason = (char *)chunk(len, __FUNCTION__)) == NULL)
return -1;
memset(reason, 0, len);
if ((info = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(info, 0, len);
va_start(args, format);
vsnprintf(reason, len, format, args);
vsnprintf(info, len, format, args);
va_end(args);
if ((msg=clicon_msg_err_encode(clicon_errno, clicon_suberrno,
reason, __FUNCTION__)) == NULL)
if ((cb = cbuf_new()) == NULL)
goto done;
if (clicon_msg_send(s, msg) < 0)
cprintf(cb, "<rpc-reply><rpc-error>"
"<error-type>clixon</error-type>"
"<error-tag>%d</error-tag>"
"<error-message>%d</error-message>"
"<error-info>%s</error-info>"
"</rpc-error></rpc-reply>",
err, suberr, info);
if (send_msg_reply(s, CLICON_MSG_NETCONF, cbuf_get(cb), cbuf_len(cb)+1) < 0){
if (errno == ECONNRESET)
clicon_log(LOG_WARNING, "client rpc reset");
goto done;
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
if (cb)
cbuf_free(cb);
if (info)
free(info);
return retval;
}
/*! Send a clicon_msg Error message as reply to a clicon rpc request
*
* @param[in] s Socket to communicate with client
* @param[in] data Returned data as byte-string.
* @param[in] datalen Length of returned data
* @retval 0 OK
* @retval -1 Error
* @note send as netconf message XXX remove clicon header
* XXX: see clicon_xml_parse
*/
int
send_msg_netconf_reply(int s,
char *format, ...)
{
va_list args;
char *str = NULL;
int len;
int retval = -1;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
if ((str = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(str, 0, len);
va_start(args, format);
len = vsnprintf(str, len, format, args);
va_end(args);
if (send_msg_reply(s, CLICON_MSG_NETCONF, str, len) < 0){
if (errno == ECONNRESET)
clicon_log(LOG_WARNING, "client rpc reset");
goto done;
}
retval = 0;
done:
if (str)
free(str);
return retval;
}

View file

@ -49,6 +49,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/syslog.h>
/* cligen */
#include <cligen/cligen.h>
@ -56,38 +57,42 @@
/* clicon */
#include "clixon_queue.h"
#include "clixon_chunk.h"
#include "clixon_log.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_xsl.h"
#include "clixon_proto.h"
#include "clixon_err.h"
#include "clixon_xml.h"
#include "clixon_proto_encode.h"
#include "clixon_proto_client.h"
/*! Internal rpc function
* @param[in] h CLICON handle
* @param[in] msg Encoded message
* @param[out] ret Return value from backend server (reply)
* @param[out] retlen Length of return value
* @param[in] msg Encoded message. Deallocate woth free
* @param[out] xret Return value from backend as netconf xml tree. Free w xml_free
* @param[inout] sock0 If pointer exists, do not close socket to backend on success
* and return it here. For keeping a notify socket open
* @param[in] label Chunk label for deallocating return values
* Deallocate with unchunk_group(label)
* Note: sock0 is if connection should be persistent, like a notification/subscribe api
*/
static int
int
clicon_rpc_msg(clicon_handle h,
struct clicon_msg *msg,
char **ret,
uint16_t *retlen,
int *sock0,
const char *label)
cxobj **xret0,
int *sock0)
{
int retval = -1;
char *sock;
int port;
char *retdata = NULL;
cxobj *xret = NULL;
cxobj *x;
uint32_t err;
uint32_t suberr;
char *errmsg = NULL;
char *etype = NULL;
if ((sock = clicon_sock(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
@ -96,7 +101,7 @@ clicon_rpc_msg(clicon_handle h,
/* What to do if inet socket? */
switch (clicon_sock_family(h)){
case AF_UNIX:
if (clicon_rpc_connect_unix(msg, sock, ret, retlen, sock0, label) < 0){
if (clicon_rpc_connect_unix(msg, sock, &retdata, sock0) < 0){
#if 0
if (errno == ESHUTDOWN)
/* Maybe could reconnect on a higher layer, but lets fail
@ -115,12 +120,428 @@ clicon_rpc_msg(clicon_handle h,
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
goto done;
}
if (clicon_rpc_connect_inet(msg, sock, port, ret, retlen, sock0, label) < 0)
if (clicon_rpc_connect_inet(msg, sock, port, &retdata, sock0) < 0)
goto done;
break;
}
#if 1 /* Parse return data and intercept clixon errors */
if (retdata) {
if (clicon_xml_parse_str(retdata, &xret) < 0)
goto done;
if (xpath_first(xret, "/rpc-reply/ok") != NULL ||
xpath_first(xret, "/rpc-reply/data") != NULL)
;
else if (xpath_first(xret, "/rpc-reply/rpc-error") != NULL){
if ((x=xpath_first(xret, "//error-type"))!=NULL)
etype = xml_body(x);
if (etype&&strcmp(etype, "clixon")==0){
if ((x=xpath_first(xret, "//error-tag"))!=NULL)
err = atoi(xml_body(x));
if ((x=xpath_first(xret, "//error-message"))!=NULL)
suberr = atoi(xml_body(x));
if ((x=xpath_first(xret, "//error-info"))!=NULL)
errmsg = xml_body(x);
if (debug)
clicon_err(err, suberr, "%s msgtype:%hu", errmsg, ntohs(msg->op_type));
else
clicon_err(err, suberr, "%s", errmsg);
goto done;
}
}
else{
clicon_err(OE_PROTO, 0, "%s: unexpected reply",
__FUNCTION__);
}
}
#endif
if (xret0){
*xret0 = xret;
xret = NULL;
}
retval = 0;
done:
if (retdata)
free(retdata);
if (xret)
xml_free(xret);
return retval;
}
/*! Generic xml netconf clicon rpc
* Want to go over to use netconf directly between client and server,...
* @param[in] h clicon handle
* @param[in] xi XML netconf tree
* @param[out] xret Return XML, error or OK
* @param[out] sp Socket pointer for notification, otherwise NULL
*/
int
clicon_rpc_netconf_xml(clicon_handle h,
cxobj *xin,
cxobj **xret,
int *sp)
{
int retval = -1;
struct clicon_msg *msg = NULL;
if ((msg = clicon_msg_netconf_encode_xml(xin)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, xret, sp) < 0)
goto done;
retval = 0;
done:
if (msg)
free(msg);
return retval;
}
int
clicon_rpc_generate_error(cxobj *xerr)
{
cxobj *x;
char *etype="";
char *etag="";
char *emsg="";
char *einfo="";
if ((x=xpath_first(xerr, "error-type"))!=NULL)
etype = xml_body(x);
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
etag = xml_body(x);
if ((x=xpath_first(xerr, "error-message"))!=NULL)
emsg = xml_body(x);
if ((x=xpath_first(xerr, "error-info"))!=NULL)
einfo = xml_body(x);
clicon_err(OE_XML, 0, "%s %s %s %s", etype, etag, emsg, einfo);
return 0;
}
/*! Get database configuration
* Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] xpath XPath (or "")
* @param[out] xt XML tree. must be freed by caller with xml_free
* @retval 0 OK
* @retval -1 Error, fatal or xml
* @code
* cxobj *xt = NULL;
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
* err;
* if (xt)
* xml_free(xt);
* @endcode
*/
int
clicon_rpc_get_config(clicon_handle h,
char *db,
char *xpath,
cxobj **xt)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cbuf *cb = NULL;
cxobj *xret = NULL;
cxobj *xerr;
cxobj *xd;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><get-config><source><%s/></source>", db);
if (xpath && strlen(xpath))
cprintf(cb, "<filter type=\"xpath\" select=\"%s\"/>", xpath);
cprintf(cb, "</get-config></rpc>");
if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
if ((xd = xpath_first(xret, "//data/config")) == NULL)
if ((xd = xml_new("config", NULL)) == NULL)
goto done;
if (xt){
if (xml_rm(xd) < 0)
goto done;
*xt = xd;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
/*! Send database entries as XML to backend daemon
* Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
* @param[in] api_path restconf API Path (or "")
* @param[in] xml XML string. Ex: <a>..</a><b>...</b>
* @retval 0 OK
* @retval -1 Error
* @code
* if (clicon_rpc_edit_config(h, "running", OP_MERGE, "/",
* "<config><a>4</a></config>") < 0)
* err;
* @endcode
*/
int
clicon_rpc_edit_config(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
char *xml)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cbuf *cb = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><edit-config><target><%s/></target>", db);
cprintf(cb, "<default-operation>%s</default-operation>",
xml_operation2str(op));
if (api_path && strlen(api_path))
cprintf(cb, "<filter type=\"restconf\" select=\"%s\"/>", api_path);
if (xml)
cprintf(cb, "%s", xml);
cprintf(cb, "</edit-config></rpc>");
if ((msg = clicon_msg_netconf_encode(cbuf_get(cb))) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (cb)
cbuf_free(cb);
if (msg)
free(msg);
return retval;
}
/*! Send a request to backend to copy a file from one location to another
* Note this assumes the backend can access these files and (usually) assumes
* clients and servers have the access to the same filesystem.
* @param[in] h CLICON handle
* @param[in] db1 src database, eg "running"
* @param[in] db2 dst database, eg "startup"
* @code
* if (clicon_rpc_copy_config(h, "running", "startup") < 0)
* err;
* @endcode
*/
int
clicon_rpc_copy_config(clicon_handle h,
char *db1,
char *db2)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg = clicon_msg_netconf_encode("<rpc><copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>", db1, db2)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
/*! Send a request to backend to delete a config database
* @param[in] h CLICON handle
* @param[in] db database, eg "running"
* @code
* if (clicon_rpc_delete_config(h, "startup") < 0)
* err;
* @endcode
*/
int
clicon_rpc_delete_config(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg = clicon_msg_netconf_encode("<rpc><delete-config><target><%s/></target></delete-config></rpc>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
int
clicon_rpc_lock(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg = clicon_msg_netconf_encode("<rpc><lock><target><%s/></target></lock></rpc>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
int
clicon_rpc_unlock(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg = clicon_msg_netconf_encode("<rpc><unlock><target><%s/></target></unlock></rpc>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
int
clicon_rpc_close_session(clicon_handle h)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg = clicon_msg_netconf_encode("<rpc><close-session/></rpc>")) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
int
clicon_rpc_kill_session(clicon_handle h,
int session_id)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg = clicon_msg_netconf_encode("<rpc><kill-session><session-id>%d</session-id></kill-session></rpc>", session_id)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
/*! Send validate request to backend daemon
* @param[in] h CLICON handle
* @param[in] db Name of database
* @retval 0 OK
*/
int
clicon_rpc_validate(clicon_handle h,
char *db)
{
int retval = -1;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
cxobj *x;
if ((msg = clicon_msg_netconf_encode("<rpc><validate><source><%s/></source></validate></rpc>", db)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
x=xpath_first(xerr, "error-message");
clicon_log(LOG_ERR, "Validate failed: \"%s\". Edit and try again or discard changes", x?xml_body(x):"");
goto done;
}
retval = 0;
done:
if (msg)
free(msg);
if (xret)
xml_free(xret);
return retval;
}
@ -128,45 +549,69 @@ clicon_rpc_msg(clicon_handle h,
* @param[in] h CLICON handle
* @param[in] from name of 'from' database (eg "candidate")
* @param[in] db name of 'to' database (eg "running")
* @retval 0 Copy current->candidate
* @retval 0 OK
*/
int
clicon_rpc_commit(clicon_handle h,
char *from,
char *to)
clicon_rpc_commit(clicon_handle h)
{
int retval = -1;
struct clicon_msg *msg;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg=clicon_msg_commit_encode(from, to, __FUNCTION__)) == NULL)
if ((msg = clicon_msg_netconf_encode("<rpc><commit/></rpc>")) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
/*! Send validate request to backend daemon
* @param[in] h CLICON handle
* @param[in] db Name of database
* @retval 0
/*! Create a new notification subscription
* @param[in] h Clicon handle
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
* @param{in] filter message filter, eg xpath for xml notifications
* @param[out] s0 socket returned where notification mesages will appear
* @note When using netconf create-subsrciption,status and format is not supported
*/
int
clicon_rpc_validate(clicon_handle h,
char *db)
clicon_rpc_create_subscription(clicon_handle h,
char *stream,
char *filter,
int *s0)
{
int retval = -1;
struct clicon_msg *msg;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg=clicon_msg_validate_encode(db, __FUNCTION__)) == NULL)
if ((msg = clicon_msg_netconf_encode("<rpc><create-subscription>"
"<stream>%s</stream>"
"<filter>%s</filter>"
"</create-subscription></rpc>",
stream?stream:"", filter?filter:"")) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
done:
if (xret)
xml_free(xret);
if (msg)
free(msg);
return retval;
}
@ -189,160 +634,24 @@ clicon_rpc_change(clicon_handle h,
char *val)
{
int retval = -1;
struct clicon_msg *msg;
struct clicon_msg *msg = NULL;
if ((msg = clicon_msg_change_encode(db,
op,
key,
val,
val?strlen(val)+1:0,
__FUNCTION__)) == NULL)
val?strlen(val)+1:0)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
if (clicon_rpc_msg(h, msg, NULL, NULL) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
if (msg)
free(msg);
return retval;
}
/*! Send database entries as XML to backend daemon
* Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
* @param[in] api_path restconf API Path (or "")
* @param[in] xml XML string. Ex: <a>..</a><b>...</b>
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_rpc_xmlput(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
char *xml)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg = clicon_msg_xmlput_encode(db,
(uint32_t)op,
api_path,
xml,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send database save request to backend daemon
* @param[in] h CLICON handle
* @param[in] db Name of database
* @param[in] snapshot Save to snapshot file
* @param[in] filename Save to file (backend file-system)
*/
int
clicon_rpc_save(clicon_handle h,
char *db,
int snapshot,
char *filename)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_save_encode(db, snapshot, filename,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send database load request to backend daemon
* @param[in] h CLICON handle
* @param[in] replace 0: merge with existing data, 1:replace completely
* @param[in] db Name of database
* @param[in] filename Load from file (backend file-system)
*/
int
clicon_rpc_load(clicon_handle h,
int replace,
char *db,
char *filename)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_load_encode(replace, db, filename,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a request to backend to copy a file from one location to another
* Note this assumes the backend can access these files and (usually) assumes
* clients and servers have the access to the same filesystem.
* @param[in] h CLICON handle
* @param[in] db1 src database, eg "candidate"
* @param[in] db2 dst database, eg "running"
*/
int
clicon_rpc_copy(clicon_handle h,
char *db1,
char *db2)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_copy_encode(db1, db2, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a kill session request to backend server
* @param[in] h CLICON handle
* @param[in] session_id Id of session to kill
*/
int
clicon_rpc_kill(clicon_handle h,
int session_id)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg=clicon_msg_kill_encode(session_id, __FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! Send a debug request to backend server
* @param[in] h CLICON handle
* @param[in] level Debug level
@ -352,107 +661,28 @@ clicon_rpc_debug(clicon_handle h,
int level)
{
int retval = -1;
struct clicon_msg *msg;
struct clicon_msg *msg = NULL;
cxobj *xret = NULL;
cxobj *xerr;
if ((msg=clicon_msg_debug_encode(level, __FUNCTION__)) == NULL)
if ((msg = clicon_msg_netconf_encode("<rpc><debug><level>%d</level></debug></rpc>", level)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
goto done;
retval = 0;
done:
unchunk_group(__FUNCTION__);
return retval;
}
/*! An rpc call from a frontend module to a function in a backend module
*
* A CLI/netconf frontend module can make a functional call to a backend
* module and get return value back.
* The backend module needs to be specified (XXX would be nice to avoid this)
* parameters can be sent, and value returned.
* A function (func) must be defined in the backend module (plugin)
* An example signature of such a downcall function is:
* @code
* char name[16];
* char *ret;
* uint16_t retlen;
* clicon_rpc_call(h, 0, "my-backend-plugin", "my_fn", name, 16,
* &ret, &retlen, __FUNCTION__);
* unchunk_group(__FUNCTION__); # deallocate 'ret'
* @endcode
* Backend example function:
* @code
int
downcall(clicon_handle h, uint16_t op, uint16_t len, void *arg,
uint16_t *reply_data_len, void **reply_data)
* @endcode
*
* @param[in] h Clicon handle
* @param[in] op Generic application-defined operation
* @param[in] plugin Name of backend plugin (XXX look in backend plugin dir)
* @param[in] func Name of function i backend (ie downcall above) as string
* @param[in] param Input parameter given to function (void* arg in downcall)
* @param[in] paramlen Length of input parameter
* @param[out] ret Returned data as byte-string. Deallocate w unchunk...(..., label)
* @param[out] retlen Length of returned data
* @param[in] label Label used in chunk (de)allocation. Use:
* unchunk_group(label) to deallocate
*/
int
clicon_rpc_call(clicon_handle h,
uint16_t op,
char *plugin,
char *func,
void *param,
uint16_t paramlen,
char **ret,
uint16_t *retlen,
const void *label)
{
int retval = -1;
struct clicon_msg *msg;
if ((msg = clicon_msg_call_encode(op, plugin, func,
paramlen, param,
label)) == NULL)
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error(xerr);
goto done;
if (clicon_rpc_msg(h, msg, (char**)ret, retlen, NULL, label) < 0)
}
if (xpath_first(xret, "//rpc-reply/ok") == NULL){
clicon_err(OE_XML, 0, "rpc error"); /* XXX extract info from rpc-error */
goto done;
retval = 0;
done:
return retval;
}
/*! Create a new notification subscription
* @param[in] h Clicon handle
* @param[in] status 0: stop existing notification stream 1: start new stream.
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
* @param{in] filter message filter, eg xpath for xml notifications
* @param[out] s0 socket returned where notification mesages will appear
*/
int
clicon_rpc_subscription(clicon_handle h,
int status,
char *stream,
enum format_enum format,
char *filter,
int *s0)
{
struct clicon_msg *msg;
int retval = -1;
if ((msg=clicon_msg_subscription_encode(status, stream, format, filter,
__FUNCTION__)) == NULL)
goto done;
if (clicon_rpc_msg(h, msg, NULL, NULL, s0, __FUNCTION__) < 0)
goto done;
if (status == 0 && s0){
close(*s0);
*s0 = -1;
}
retval = 0;
done:
unchunk_group(__FUNCTION__);
done:
if (msg)
free(msg);
if (xret)
xml_free(xret);
return retval;
}

View file

@ -68,107 +68,83 @@
#include "clixon_sig.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_xml.h"
#include "clixon_proto.h"
#include "clixon_proto_encode.h"
struct clicon_msg *
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
const char *label)
clicon_msg_netconf_encode(char *format, ...)
{
struct clicon_msg *msg;
uint16_t len;
va_list args;
int xmllen;
int len;
struct clicon_msg *msg = NULL;
int hdrlen = sizeof(*msg);
int p;
clicon_debug(2, "%s: dbsrc: %s dbdst: %s",
__FUNCTION__,
dbsrc, dbdst);
p = 0;
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(dbsrc) + 1 +
strlen(dbdst) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
va_start(args, format);
xmllen = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
len = hdrlen + xmllen;
if ((msg = (struct clicon_msg *)malloc(len)) == NULL){
clicon_err(OE_PROTO, errno, "malloc");
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_COMMIT);
msg->op_type = htons(CLICON_MSG_NETCONF);
msg->op_len = htons(len);
/* body */
strncpy(msg->op_body+p, dbsrc, len-p-hdrlen);
p += strlen(dbsrc)+1;
strncpy(msg->op_body+p, dbdst, len-p-hdrlen);
p += strlen(dbdst)+1;
va_start(args, format);
vsnprintf(msg->op_body, xmllen, format, args);
va_end(args);
return msg;
}
int
clicon_msg_commit_decode(struct clicon_msg *msg,
char **dbsrc, char **dbdst,
const char *label)
{
int p;
p = 0;
/* body */
if ((*dbsrc = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*dbsrc)+1;
if ((*dbdst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*dbdst)+1;
clicon_debug(2, "%s: dbsrc: %s dbdst: %s", __FUNCTION__, *dbsrc, *dbdst);
return 0;
}
struct clicon_msg *
clicon_msg_validate_encode(char *db, const char *label)
clicon_msg_netconf_encode_xml(cxobj *xml)
{
struct clicon_msg *msg;
uint16_t len;
int hdrlen = sizeof(*msg);
struct clicon_msg *msg = NULL;
cbuf *cb = NULL;
clicon_debug(2, "%s: db: %s", __FUNCTION__, db);
len = sizeof(*msg) + strlen(db) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_VALIDATE);
msg->op_len = htons(len);
/* body */
strncpy(msg->op_body, db, len-hdrlen);
if (clicon_xml2cbuf(cb, xml, 0, 0) < 0)
goto done;
if ((msg = clicon_msg_netconf_encode("%s", cbuf_get(cb))) < 0)
goto done;
done:
if (cb)
cbuf_free(cb);
return msg;
}
int
clicon_msg_validate_decode(struct clicon_msg *msg, char **db, const char *label)
clicon_msg_netconf_decode(struct clicon_msg *msg,
cxobj **xml)
{
/* body */
if ((*db = chunk_sprintf(label, "%s", msg->op_body)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__);
return -1;
}
clicon_debug(2, "%s: db: %s", __FUNCTION__, *db);
return 0;
}
int retval = -1;
char *xmlstr;
/* body */
xmlstr = msg->op_body;
if (clicon_xml_parse_str(xmlstr, xml) < 0)
goto done;
retval = 0;
done:
return retval;
}
struct clicon_msg *
clicon_msg_change_encode(char *db,
uint32_t op,
char *key,
char *str,
uint32_t str_len,
const char *label)
uint32_t str_len)
{
struct clicon_msg *msg;
uint16_t len;
@ -184,8 +160,8 @@ clicon_msg_change_encode(char *db,
strlen(key) + str_len;
if (str_len)
len++; /* if str not null add end of string */
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
if ((msg = (struct clicon_msg *)malloc(len)) == NULL){
clicon_err(OE_PROTO, errno, "malloc");
return NULL;
}
memset(msg, 0, len);
@ -264,661 +240,5 @@ clicon_msg_change_decode(struct clicon_msg *msg,
return 0;
}
/*! Encode xmlput / edit of database content
* @param[in] db Name of database
* @param[in] op set|merge|delete. See lv_op_t
* @param[in] api_path restconf api path
* @param[in] xml XML data string
* @param[in] label Memory chunk label
* @retval msg Encoded message
* @retval NULL Error
*/
struct clicon_msg *
clicon_msg_xmlput_encode(char *db,
uint32_t op,
char *api_path,
char *xml,
const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int hdrlen = sizeof(*msg);
int p;
uint32_t tmp;
clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s",
__FUNCTION__,
op, db, api_path, xml);
p = 0;
hdrlen = sizeof(*msg);
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(api_path) + 1 +strlen(xml) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_XMLPUT);
msg->op_len = htons(len);
/* body */
tmp = htonl(op);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
strncpy(msg->op_body+p, db, len-p-hdrlen);
p += strlen(db)+1;
strncpy(msg->op_body+p, api_path, len-p-hdrlen);
p += strlen(api_path)+1;
strncpy(msg->op_body+p, xml, len-p-hdrlen);
p += strlen(xml)+1;
return msg;
}
/*! Decode xmlput / edit of database content
* @param[in] msg Incoming message to be decoded
* @param[out] db Name of database
* @param[out] op set|merge|delete. See lv_op_t
* @param[out] api_path restconf api path
* @param[out] xml XML data string
* @param[in] label Memory chunk label
* @retval 0 OK
* @retval -1 Error
*/
int
clicon_msg_xmlput_decode(struct clicon_msg *msg,
char **db,
uint32_t *op,
char **api_path,
char **xml,
const char *label)
{
int p;
uint32_t tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*op = ntohl(tmp);
p += sizeof(uint32_t);
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db)+1;
if ((*api_path = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*api_path)+1;
if ((*xml = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*xml)+1;
clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s",
__FUNCTION__,
*op, *db, *api_path, *xml);
return 0;
}
struct clicon_msg *
clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename,
const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int hdrlen = sizeof(*msg);
int p;
uint32_t tmp;
clicon_debug(2, "%s: snapshot: %d db: %s filename: %s",
__FUNCTION__,
snapshot, db, filename);
p = 0;
hdrlen = sizeof(*msg);
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1;
if (!snapshot)
len += strlen(filename) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_SAVE);
msg->op_len = htons(len);
/* body */
tmp = htonl(snapshot);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
strncpy(msg->op_body+p, db, len-p-hdrlen);
p += strlen(db)+1;
if (!snapshot){
strncpy(msg->op_body+p, filename, len-p-hdrlen);
p += strlen(filename)+1;
}
return msg;
}
int
clicon_msg_save_decode(struct clicon_msg *msg,
char **db, uint32_t *snapshot, char **filename,
const char *label)
{
int p;
uint32_t tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*snapshot = ntohl(tmp);
p += sizeof(uint32_t);
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db)+1;
if (*snapshot == 0){
if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*filename)+1;
}
clicon_debug(2, "%s: snapshot: %d db: %s filename: %s",
__FUNCTION__,
*snapshot, *db, *filename);
return 0;
}
struct clicon_msg *
clicon_msg_load_encode(int replace, char *db, char *filename, const char *label)
{
struct clicon_msg *msg;
int hdrlen = sizeof(*msg);
uint16_t len;
uint32_t tmp;
int p;
clicon_debug(2, "%s: replace: %d db: %s filename: %s",
__FUNCTION__,
replace, db, filename);
p = 0;
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(filename) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_LOAD);
msg->op_len = htons(len);
/* body */
tmp = htonl(replace);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
strncpy(msg->op_body+p, db, len-p-hdrlen);
p += strlen(db)+1;
strncpy(msg->op_body+p, filename, len-p-hdrlen);
p += strlen(filename)+1;
return msg;
}
int
clicon_msg_load_decode(struct clicon_msg *msg,
int *replace,
char **db,
char **filename,
const char *label)
{
int p;
uint32_t tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*replace = ntohl(tmp);
p += sizeof(uint32_t);
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db)+1;
if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*filename)+1;
clicon_debug(2, "%s: %d db: %s filename: %s",
__FUNCTION__,
ntohs(msg->op_type),
*db, *filename);
return 0;
}
struct clicon_msg *
clicon_msg_copy_encode(char *db_src, char *db_dst,
const char *label)
{
struct clicon_msg *msg;
int hdrlen = sizeof(*msg);
uint16_t len;
int p;
clicon_debug(2, "%s: db_src: %s db_dst: %s",
__FUNCTION__,
db_src, db_dst);
p = 0;
len = hdrlen + strlen(db_src) + 1 + strlen(db_dst) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_COPY);
msg->op_len = htons(len);
/* body */
strncpy(msg->op_body+p, db_src, len-p-hdrlen);
p += strlen(db_src)+1;
strncpy(msg->op_body+p, db_dst, len-p-hdrlen);
p += strlen(db_dst)+1;
return msg;
}
int
clicon_msg_copy_decode(struct clicon_msg *msg,
char **db_src, char **db_dst,
const char *label)
{
int p;
p = 0;
/* body */
if ((*db_src = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db_src)+1;
if ((*db_dst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*db_dst)+1;
clicon_debug(2, "%s: db_src: %s db_dst: %s",
__FUNCTION__,
*db_src, *db_dst);
return 0;
}
struct clicon_msg *
clicon_msg_kill_encode(uint32_t session_id, const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int p;
uint32_t tmp;
clicon_debug(2, "%s: %d", __FUNCTION__, session_id);
p = 0;
len = sizeof(*msg) + sizeof(uint32_t);
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_KILL);
msg->op_len = htons(len);
/* body */
tmp = htonl(session_id);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
return msg;
}
int
clicon_msg_kill_decode(struct clicon_msg *msg,
uint32_t *session_id,
const char *label)
{
int p;
uint32_t tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*session_id = ntohl(tmp);
p += sizeof(uint32_t);
clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *session_id);
return 0;
}
struct clicon_msg *
clicon_msg_debug_encode(uint32_t level, const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int p;
uint32_t tmp;
clicon_debug(2, "%s: %d", __FUNCTION__, label);
p = 0;
len = sizeof(*msg) + sizeof(uint32_t);
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_DEBUG);
msg->op_len = htons(len);
/* body */
tmp = htonl(level);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
return msg;
}
int
clicon_msg_debug_decode(struct clicon_msg *msg,
uint32_t *level,
const char *label)
{
int p;
uint32_t tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*level = ntohl(tmp);
p += sizeof(uint32_t);
clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *level);
return 0;
}
struct clicon_msg *
clicon_msg_call_encode(uint16_t op,
char *plugin,
char *func,
uint16_t arglen,
void *arg,
const char *label)
{
struct clicon_msg *msg;
struct clicon_msg_call_req *req;
int hdrlen = sizeof(*msg);
int len;
clicon_debug(2, "%s: %d plugin: %s func: %s arglen: %d",
__FUNCTION__, op, plugin, func, arglen);
len =
hdrlen +
sizeof(struct clicon_msg_call_req) +
strlen(plugin) + 1 +
strlen(func) + 1 +
arglen;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_CALL);
msg->op_len = htons(len);
/* req */
req = (struct clicon_msg_call_req *)msg->op_body;
req->cr_len = htons(len - hdrlen);
req->cr_op = htons(op);
req->cr_plugin = req->cr_data;
strncpy(req->cr_plugin, plugin, strlen(plugin));
req->cr_func = req->cr_plugin + strlen(req->cr_plugin) + 1;
strncpy(req->cr_func, func, strlen(func));
req->cr_arglen = htons(arglen);
req->cr_arg = req->cr_func + strlen(req->cr_func) + 1;
memcpy(req->cr_arg, arg, arglen);
return msg;
}
int
clicon_msg_call_decode(struct clicon_msg *msg,
struct clicon_msg_call_req **req,
const char *label)
{
uint16_t len;
struct clicon_msg_call_req *r;
r = (struct clicon_msg_call_req *)msg->op_body;
len = ntohs(r->cr_len);
if ((*req = chunk(len, label)) == NULL) {
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return -1;
}
memcpy(*req, r, len);
(*req)->cr_len = ntohs(r->cr_len);
(*req)->cr_op = ntohs(r->cr_op);
(*req)->cr_arglen = ntohs(r->cr_arglen);
(*req)->cr_plugin = (*req)->cr_data;
(*req)->cr_func = (*req)->cr_plugin + strlen((*req)->cr_plugin) +1;
(*req)->cr_arg = (*req)->cr_func + strlen((*req)->cr_func) +1;
return 0;
}
struct clicon_msg *
clicon_msg_subscription_encode(int status,
char *stream,
enum format_enum format,
char *filter,
const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int hdrlen = sizeof(*msg);
int p;
int tmp;
clicon_debug(2, "%s: %d %d %s %s", __FUNCTION__, status, format, stream, filter);
p = 0;
assert(filter);
len = hdrlen + sizeof(uint32_t) + sizeof(uint32_t) + strlen(stream) + 1 + strlen(filter) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_SUBSCRIPTION);
msg->op_len = htons(len);
/* body */
tmp = htonl(status);
memcpy(msg->op_body+p, &tmp, sizeof(int));
p += sizeof(int);
tmp = htonl(format);
memcpy(msg->op_body+p, &tmp, sizeof(int));
p += sizeof(int);
strncpy(msg->op_body+p, stream, len-p-hdrlen);
p += strlen(stream)+1;
strncpy(msg->op_body+p, filter, len-p-hdrlen);
p += strlen(filter)+1;
return msg;
}
int
clicon_msg_subscription_decode(struct clicon_msg *msg,
int *status,
char **stream,
enum format_enum *format,
char **filter,
const char *label)
{
int p;
int tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(int));
*status = ntohl(tmp);
p += sizeof(int);
memcpy(&tmp, msg->op_body+p, sizeof(int));
*format = ntohl(tmp);
p += sizeof(int);
if ((*stream = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*stream)+1;
if ((*filter = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*filter)+1;
clicon_debug(2, "%s: %d %s %d %s", __FUNCTION__, *status, *filter, *stream, *filter);
return 0;
}
struct clicon_msg *
clicon_msg_notify_encode(int level, char *event, const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int hdrlen = sizeof(*msg);
int p;
int tmp;
clicon_debug(2, "%s: %d %s", __FUNCTION__, level, event);
p = 0;
hdrlen = sizeof(*msg);
len = sizeof(*msg) + sizeof(uint32_t) + strlen(event) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_NOTIFY);
msg->op_len = htons(len);
/* body */
tmp = htonl(level);
memcpy(msg->op_body+p, &tmp, sizeof(int));
p += sizeof(int);
strncpy(msg->op_body+p, event, len-p-hdrlen);
p += strlen(event)+1;
return msg;
}
int
clicon_msg_notify_decode(struct clicon_msg *msg,
int *level, char **event,
const char *label)
{
int p;
int tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(int));
*level = ntohl(tmp);
p += sizeof(int);
if ((*event = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*event)+1;
clicon_debug(2, "%s: %d %s", __FUNCTION__, *level, *event);
return 0;
}
struct clicon_msg *
clicon_msg_err_encode(uint32_t err, uint32_t suberr, char *reason, const char *label)
{
struct clicon_msg *msg;
uint16_t len;
int hdrlen = sizeof(*msg);
int p;
uint32_t tmp;
clicon_debug(2, "%s: %d %d %s", __FUNCTION__, err, suberr, reason);
p = 0;
hdrlen = sizeof(*msg);
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(reason) + 1;
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
return NULL;
}
memset(msg, 0, len);
/* hdr */
msg->op_type = htons(CLICON_MSG_ERR);
msg->op_len = htons(len);
/* body */
tmp = htonl(err);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
tmp = htonl(suberr);
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
p += sizeof(uint32_t);
strncpy(msg->op_body+p, reason, len-p-hdrlen);
p += strlen(reason)+1;
return msg;
}
int
clicon_msg_err_decode(struct clicon_msg *msg,
uint32_t *err, uint32_t *suberr, char **reason,
const char *label)
{
int p;
uint32_t tmp;
p = 0;
/* body */
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*err = ntohl(tmp);
p += sizeof(uint32_t);
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
*suberr = ntohl(tmp);
p += sizeof(uint32_t);
if ((*reason = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
__FUNCTION__);
return -1;
}
p += strlen(*reason)+1;
clicon_debug(2, "%s: %d %d %s",
__FUNCTION__,
*err, *suberr, *reason);
return 0;
}

View file

@ -465,6 +465,12 @@ xml_childvec_set(cxobj *x,
return 0;
}
cxobj **
xml_childvec_get(cxobj *x)
{
return x->x_childvec;
}
/*! Create new xml node given a name and parent. Free it with xml_free().
*
* @param[in] name Name of new
@ -913,24 +919,26 @@ clicon_xml2cbuf(cbuf *cb,
cprintf(cb, "%s:", xml_namespace(cx));
cprintf(cb, "%s", xml_name(cx));
xc = NULL;
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
if (xml_type(xc) != CX_ATTR)
continue;
while ((xc = xml_child_each(cx, xc, CX_ATTR)) != NULL)
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
/* Check for special case <a/> instead of <a></a> */
if (xml_body(cx)==NULL && xml_child_nr(cx)==0)
cprintf(cb, "/>");
else{
cprintf(cb, ">");
if (prettyprint && xml_body(cx)==NULL)
cprintf(cb, "\n");
xc = NULL;
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
if (xml_type(xc) == CX_ATTR)
continue;
else
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
}
if (prettyprint && xml_body(cx)==NULL)
cprintf(cb, "%*s", level*XML_INDENT, "");
cprintf(cb, "</%s>", xml_name(cx));
}
cprintf(cb, ">");
if (prettyprint && xml_body(cx)==NULL)
cprintf(cb, "\n");
xc = NULL;
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
if (xml_type(xc) == CX_ATTR)
continue;
else
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
}
if (prettyprint && xml_body(cx)==NULL)
cprintf(cb, "%*s", level*XML_INDENT, "");
cprintf(cb, "</%s>", xml_name(cx));
if (prettyprint)
cprintf(cb, "\n");
break;
@ -956,7 +964,7 @@ xml_parse(char *str,
}
ya.ya_xparent = x_up;
if (clixon_xml_parsel_init(&ya) < 0)
goto done;
goto done;
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
goto done;
@ -1079,7 +1087,6 @@ clicon_xml_parse_file(int fd,
* @endcode
* @see clicon_xml_parse_file
* @note you need to free the xml parse tree after use, using xml_free()
* Update: with yacc parser I dont think it changes,....
*/
int
clicon_xml_parse_str(char *str,
@ -1090,6 +1097,57 @@ clicon_xml_parse_str(char *str,
return xml_parse(str, *cxtop);
}
/*! Read XML definition from variable argument string and parse it into parse-tree.
*
* Utility function using stdarg instead of static string.
* @param[out] xml_top Top of XML parse tree. Will add extra top element called 'top'.
* you must free it after use, using xml_free()
* @param[in] format Pointer to string containing XML definition.
* @retval 0 OK
* @retval -1 Error with clicon_err called
*
* @code
* cxobj *cx = NULL;
* if (clicon_xml_parse(&cx, "<xml>%s</xml", 22) < 0)
* err;
* xml_free(cx);
* @endcode
* @see clicon_xml_parse_str
* @note you need to free the xml parse tree after use, using xml_free()
*/
int
clicon_xml_parse(cxobj **cxtop,
char *format, ...)
{
int retval = -1;
va_list args;
char *str = NULL;
int len;
va_start(args, format);
len = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
if ((str = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(str, 0, len);
va_start(args, format);
len = vsnprintf(str, len, format, args) + 1;
va_end(args);
if ((*cxtop = xml_new("top", NULL)) == NULL)
return -1;
if (xml_parse(str, *cxtop) < 0)
goto done;
retval = 0;
done:
if (str)
free(str);
return retval;
}
/*! Copy single xml node without copying children
*/
static int
@ -1402,3 +1460,52 @@ xml_body_uint32(cxobj *xb,
cv_free(cv);
return 0;
}
/*! Map xml operation from string to enumeration
* @param[in] xn XML node
* @param[out] op "operation" attribute may change operation
*/
int
xml_operation(char *opstr,
enum operation_type *op)
{
if (strcmp("merge", opstr) == 0)
*op = OP_MERGE;
else if (strcmp("replace", opstr) == 0)
*op = OP_REPLACE;
else if (strcmp("create", opstr) == 0)
*op = OP_CREATE;
else if (strcmp("delete", opstr) == 0)
*op = OP_DELETE;
else if (strcmp("remove", opstr) == 0)
*op = OP_REMOVE;
else{
clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
return -1;
}
return 0;
}
char *
xml_operation2str(enum operation_type op)
{
switch (op){
case OP_MERGE:
return "merge";
break;
case OP_REPLACE:
return "replace";
break;
case OP_CREATE:
return "create";
break;
case OP_DELETE:
return "delete";
break;
case OP_REMOVE:
return "remove";
break;
default:
return "none";
}
}

View file

@ -121,7 +121,6 @@
#include "clixon_options.h"
#include "clixon_xsl.h"
#include "clixon_xml_parse.h"
#include "clixon_xml_db_rpc.h"
#include "clixon_xml_db.h"
/*! Construct an xml key format from yang statement using wildcards for keys
@ -417,6 +416,8 @@ static int _candidate_locked = 0;
static int _startup_locked = 0;
/*! Lock database
* @param[in] db Database name
* @param[in] pid process/session-id
*/
static int
db_lock(char *db,
@ -433,6 +434,7 @@ db_lock(char *db,
}
/*! Unlock database
* @param[in] db Database name
*/
static int
db_unlock(char *db)
@ -446,6 +448,21 @@ db_unlock(char *db)
return 0;
}
/*! Unlock all databases locked by pid (eg process dies)
* @param[in] pid process/session-id
*/
static int
db_unlock_all(int pid)
{
if (_running_locked == pid)
_running_locked = 0;
if (_candidate_locked == pid)
_candidate_locked = 0;
if (_startup_locked == pid)
_startup_locked = 0;
return 0;
}
/*! returns id of locker
* @retval 0 Not locked
* @retval >0 Id of locker
@ -462,6 +479,8 @@ db_islocked(char *db)
return 0;
}
/*! Translate from symbolic database name to actual filename in file-system
* @param[in] h Clicon handle
* @param[in] db Symbolic database name, eg "candidate", "running"
@ -959,8 +978,8 @@ xml_order(cxobj *x,
* cxobj **xvec;
* size_t xlen;
* yang_spec *yspec = clicon_dbspec_yang(h);
* if (xmldb_get_vec(dbname, "/interfaces/interface[name="eth"]", yspec,
* &xt, &xvec, &xlen) < 0)
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
@ -973,13 +992,13 @@ xml_order(cxobj *x,
* @see xpath_vec
* @see xmldb_get
*/
static int
xmldb_get_local(clicon_handle h,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec0,
size_t *xlen0)
int
xmldb_get(clicon_handle h,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec0,
size_t *xlen0)
{
int retval = -1;
yang_spec *yspec;
@ -1021,9 +1040,10 @@ xmldb_get_local(clicon_handle h,
/* If vectors are specified then filter out everything else,
* otherwise return complete tree.
*/
if (xvec != NULL)
if (xvec != NULL){
for (i=0; i<xlen; i++)
xml_flag_set(xvec[i], XML_FLAG_MARK);
}
/* Top is special case */
if (!xml_flag(xt, XML_FLAG_MARK))
if (xml_tree_prune_unmarked(xt, NULL) < 0)
@ -1036,6 +1056,7 @@ xmldb_get_local(clicon_handle h,
*xlen0 = xlen;
xlen = 0;
}
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
goto done;
/* XXX does not work for top-level */
@ -1045,6 +1066,7 @@ xmldb_get_local(clicon_handle h,
goto done;
if (debug)
clicon_xml2file(stdout, xt, 0, 1);
*xtop = xt;
retval = 0;
done:
@ -1057,87 +1079,6 @@ xmldb_get_local(clicon_handle h,
}
/*! Get content of database using path.
* The function returns a minimal tree that includes all sub-trees that match
* xpath.
* @param[in] h Clicon handle
* @param[in] db running | candidate
* @param[in] xpath String with XPATH syntax
* @param[in] vector If set, return list of results in xvec, else single tree.
* @param[out] xtop XML tree. Freed by xml_free()
* @param[out] xvec Vector of xml trees. Free after use
* @param[out] xlen Length of vector.
* @retval 0 OK
* @retval -1 Error
* @code
* cxobj *xt;
* cxobj **xvec;
* size_t xlen;
*
* if (xmldb_get("running", "/interfaces/interface[name="eth"]",
* &xt, &xvec, &xlen) < 0)
* err;
* for (i=0; i<xlen; i++){
* xn = xv[i];
* ...
* }
* xml_free(xt);
* free(xvec);
* @endcode
* @see xpath_vec
*/
int
xmldb_get(clicon_handle h,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
clicon_debug(1, "%s", __FUNCTION__);
if (clicon_xmldb_rpc(h))
retval = xmldb_get_rpc(h, db, xpath, xtop, xvec, xlen);
else
retval = xmldb_get_local(h, db, xpath, xtop, xvec, xlen);
return retval;
}
/*! Get value of the "operation" attribute and change op if given
* @param[in] xn XML node
* @param[out] op "operation" attribute may change operation
*/
static int
get_operation(cxobj *xn,
enum operation_type *op)
{
char *opstr;
if ((opstr = xml_find_value(xn, "operation")) != NULL){
if (strcmp("merge", opstr) == 0)
*op = OP_MERGE;
else
if (strcmp("replace", opstr) == 0)
*op = OP_REPLACE;
else
if (strcmp("create", opstr) == 0)
*op = OP_CREATE;
else
if (strcmp("delete", opstr) == 0)
*op = OP_DELETE;
else
if (strcmp("remove", opstr) == 0)
*op = OP_REMOVE;
else{
clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
return -1;
}
}
return 0;
}
/*! Add data to database internal recursive function
* @param[in] dbname Name of database to search in (filename incl dir path)
* @param[in] xt xml-node.
@ -1163,14 +1104,16 @@ put(char *dbname,
yang_stmt *y;
int exists;
char *bodyenc=NULL;
char *opstr;
clicon_debug(1, "%s xk0:%s ys:%s", __FUNCTION__, xk0, ys->ys_argument);
if (debug){
xml_print(stderr, xt);
// yang_print(stderr, (yang_node*)ys, 0);
}
if (get_operation(xt, &op) < 0)
goto done;
if ((opstr = xml_find_value(xt, "operation")) != NULL)
if (xml_operation(opstr, &op) < 0)
goto done;
body = xml_body(xt);
if ((cbxk = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
@ -1240,95 +1183,31 @@ put(char *dbname,
return retval;
}
/*! Local variant of xmldb_put */
static int
xmldb_put_local(clicon_handle h,
char *db,
cxobj *xt,
enum operation_type op)
{
int retval = -1;
cxobj *x = NULL;
yang_stmt *ys;
yang_spec *yspec;
char *dbfilename = NULL;
yspec = clicon_dbspec_yang(h);
if (db2file(h, db, &dbfilename) < 0)
goto done;
if (op == OP_REPLACE){
if (db_delete(dbfilename) < 0)
goto done;
if (db_init(dbfilename) < 0)
goto done;
}
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
goto done;
}
if (put(dbfilename, /* database name */
x, /* xml root node */
ys, /* yang statement of xml node */
op, /* operation, eg merge/delete */
"" /* aggregate xml key */
) < 0)
goto done;
}
retval = 0;
done:
if (dbfilename)
free(dbfilename);
return retval;
}
/*! Modify database provided an xml tree and an operation
* @param[in] dbname Name of database to search in (filename including dir path)
/*! Modify database provided an xml tree, a restconf api_path and an operation
*
* @param[in] h CLICON handle
* @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
* @param[in] xt xml-tree. Top-level symbol is dummy
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
* err;
* if (xmldb_put(h, "running", xt, OP_MERGE) < 0)
* err;
* @endcode
* @see xmldb_put_xkey for single key
*/
int
xmldb_put(clicon_handle h,
char *db,
cxobj *xt,
enum operation_type op)
{
if (clicon_xmldb_rpc(h))
return xmldb_put_rpc(h, db, xt, op);
else
return xmldb_put_local(h, db, xt, op);
}
/*! XXX: replace xk with xt
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
* e.g container top {
list list1 {
key "key1 key2 key3";
* is referenced as
/restconf/data/top/list1=a,,foo
* example:
* container top {
* list list1 {
* key "key1 key2 key3";
* is referenced as
* /restconf/data/top/list1=a,,foo
* @see xmldb_put
*/
static int
xmldb_put_tree_local(clicon_handle h,
char *db,
char *api_path,
cxobj *xt,
enum operation_type op)
xmldb_put_restconf_api_path(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
cxobj *xt)
{
int retval = -1;
yang_stmt *y = NULL;
@ -1431,7 +1310,7 @@ xmldb_put_tree_local(clicon_handle h,
clicon_err(OE_XML, errno, "List %s without argument", name);
goto done;
}
cprintf(ckey, "/%s", val2);
cprintf(ckey, "=%s", val2);
cbuf_reset(csubkey);
cprintf(csubkey, "%s/%s", cbuf_get(ckey), keyname);
if (op == OP_MERGE || op == OP_REPLACE || op == OP_CREATE)
@ -1515,41 +1394,98 @@ xmldb_put_tree_local(clicon_handle h,
cvec_free(cvk);
unchunk_group(__FUNCTION__);
return retval;
}
/*!
/*! Modify database provided an xml tree and an operation
*
* @param[in] h CLICON handle
* @param[in] db running or candidate
* @param[in] xt xml-tree. Top-level symbol is dummy
* @param[in] op OP_MERGE: just add it.
* OP_REPLACE: first delete whole database
* OP_NONE: operation attribute in xml determines operation
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [restconf-draft 13])
* @retval 0 OK
* @retval -1 Error
* The xml may contain the "operation" attribute which defines the operation.
* @code
* cxobj *xt;
* if (clicon_xml_parse_str("<a>17</a>", &xt) < 0)
* err;
* if (xmldb_put(h, "running", OP_MERGE, NULL, xt) < 0)
* err;
* @endcode
* @see xmldb_put_xkey for single key
*/
int
xmldb_put_tree(clicon_handle h,
char *db,
char *api_path,
cxobj *xt,
enum operation_type op)
xmldb_put(clicon_handle h,
char *db,
enum operation_type op,
char *api_path,
cxobj *xt)
{
if (clicon_xmldb_rpc(h))
return -1; /* XXX */
else
return xmldb_put_tree_local(h, db, api_path, xt, op);
int retval = -1;
cxobj *x = NULL;
yang_stmt *ys;
yang_spec *yspec;
char *dbfilename = NULL;
yspec = clicon_dbspec_yang(h);
if (db2file(h, db, &dbfilename) < 0)
goto done;
if (op == OP_REPLACE){
if (db_delete(dbfilename) < 0)
goto done;
if (db_init(dbfilename) < 0)
goto done;
}
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
if (api_path && strlen(api_path)){
if (xmldb_put_restconf_api_path(h, db, op, api_path, x) < 0)
goto done;
continue;
}
if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
goto done;
}
if (put(dbfilename, /* database name */
x, /* xml root node */
ys, /* yang statement of xml node */
op, /* operation, eg merge/delete */
"" /* aggregate xml key */
) < 0)
goto done;
}
retval = 0;
done:
if (dbfilename)
free(dbfilename);
return retval;
}
/*! Modify database provided an XML database key and an operation
* @param[in] h CLICON handle
* @param[in] db Database name
* @param[in] xk XML Key, eg /aa/bb/17/name
* @param[in] val Key value, eg "17"
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @param[in] xk XML Key, eg /aa/bb=17/name
* @param[in] val Key value, eg "17"
* @retval 0 OK
* @retval -1 Error
* Local variant of xmldb_put_xkey
* @code
* if (xmldb_put_xkey(h, db, OP_MERGE, "/aa/bb=17/name", "17") < 0)
* err;
* @endcode
* @see xmldb_put with xml-tree, no path
*/
static int
xmldb_put_xkey_local(clicon_handle h,
char *db,
char *xk,
char *val,
enum operation_type op)
int
xmldb_put_xkey(clicon_handle h,
char *db,
enum operation_type op,
char *xk,
char *val)
{
int retval = -1;
cxobj *x = NULL;
@ -1747,34 +1683,6 @@ xmldb_put_xkey_local(clicon_handle h,
return retval;
}
/*! Modify database provided an XML database key and an operation
* @param[in] h CLICON handle
* @param[in] db Database name
* @param[in] xk XML Key, eg /aa/bb=17/name
* @param[in] val Key value, eg "17"
* @param[in] op OP_MERGE, OP_REPLACE, OP_REMOVE, etc
* @retval 0 OK
* @retval -1 Error
* @code
* if (xmldb_put_xkey(h, db, "/aa/bb/17/name", "17", OP_MERGE) < 0)
* err;
* @endcode
* @see xmldb_put with xml-tree, no path
*/
int
xmldb_put_xkey(clicon_handle h,
char *db,
char *xk,
char *val,
enum operation_type op)
{
if (clicon_xmldb_rpc(h))
return xmldb_put_xkey_rpc(h, db, xk, val, op);
else
return xmldb_put_xkey_local(h, db, xk, val, op);
}
/*! Raw dump of database, just keys and values, no xml interpretation
* @param[in] f File
* @param[in] dbfile File-name of database. This is a local file
@ -1782,7 +1690,7 @@ xmldb_put_xkey(clicon_handle h,
* @note This function can only be called locally.
*/
int
xmldb_dump_local(FILE *f,
xmldb_dump(FILE *f,
char *dbfilename,
char *rxkey)
{
@ -1807,10 +1715,15 @@ xmldb_dump_local(FILE *f,
return retval;
}
/*! Local variant of xmldb_copy */
static int
xmldb_copy_local(clicon_handle h,
/*! Copy database from db1 to db2
* @param[in] h Clicon handle
* @param[in] from Source database copy
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_copy(clicon_handle h,
char *from,
char *to)
{
@ -1834,27 +1747,15 @@ xmldb_copy_local(clicon_handle h,
return retval;
}
/*! Copy database
* @param[in] h Clicon handle
* @param[in] from Source database copy
* @param[in] to Destination database
/*! Lock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_copy(clicon_handle h,
char *from,
char *to)
{
if (clicon_xmldb_rpc(h))
return xmldb_copy_rpc(h, from, to);
else
return xmldb_copy_local(h, from, to);
}
/* Local variant of xmldb_lock */
static int
xmldb_lock_local(clicon_handle h,
xmldb_lock(clicon_handle h,
char *db,
int pid)
{
@ -1873,27 +1774,15 @@ xmldb_lock_local(clicon_handle h,
return retval;
}
/*! Lock database
* @param[in] h Clicon handle
* @param[in] db Database
/*! Unlock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
*/
int
xmldb_lock(clicon_handle h,
char *db,
int pid)
{
if (clicon_xmldb_rpc(h))
return xmldb_lock_rpc(h, db, pid);
else
return xmldb_lock_local(h, db, pid);
}
/*! Local variant of xmldb_unlock */
static int
xmldb_unlock_local(clicon_handle h,
xmldb_unlock(clicon_handle h,
char *db,
int pid)
{
@ -1914,30 +1803,13 @@ xmldb_unlock_local(clicon_handle h,
return retval;
}
/*! Unlock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
/*! Unlock all databases locked by pid (eg process dies)
*/
int
xmldb_unlock(clicon_handle h,
char *db,
int pid)
xmldb_unlock_all(clicon_handle h,
int pid)
{
if (clicon_xmldb_rpc(h))
return xmldb_unlock_rpc(h, db, pid);
else
return xmldb_unlock_local(h, db, pid);
}
/*! Local variant of xmldb_islocked */
static int
xmldb_islocked_local(clicon_handle h,
char *db)
{
return db_islocked(db);
return db_unlock_all(pid);
}
/*! Check if database is locked
@ -1951,16 +1823,19 @@ int
xmldb_islocked(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_islocked_rpc(h, db);
else
return xmldb_islocked_local(h, db);
return db_islocked(db);
}
/*! Local variant of xmldb_exists */
static int
xmldb_exists_local(clicon_handle h,
char *db)
/*! Check if db exists
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
xmldb_exists(clicon_handle h,
char *db)
{
int retval = -1;
char *filename = NULL;
@ -1978,25 +1853,15 @@ xmldb_exists_local(clicon_handle h,
return retval;
}
/*! Check if db exists
/*! Delete database. Remove file
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
* @retval 0 OK
*/
int
xmldb_exists(clicon_handle h,
xmldb_delete(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_exists_rpc(h, db);
else
return xmldb_exists_local(h, db);
}
/*! Local variant of xmldb_delete */
static int
xmldb_delete_local(clicon_handle h,
char *db)
{
int retval = -1;
char *filename = NULL;
@ -2012,23 +1877,15 @@ xmldb_delete_local(clicon_handle h,
return retval;
}
/*! Delete database. Remove file
* Should not be called from client. Use change("/", OP_REMOVE) instead.
/*! Initialize database
* @param[in] h Clicon handle
* @param[in] db Database
* @retval 0 OK
* @retval -1 Error
*/
int
xmldb_delete(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_delete_rpc(h, db);
else
return xmldb_delete_local(h, db);
}
/*! Local variant of xmldb_init */
static int
xmldb_init_local(clicon_handle h,
char *db)
xmldb_init(clicon_handle h,
char *db)
{
int retval = -1;
char *filename = NULL;
@ -2044,16 +1901,6 @@ xmldb_init_local(clicon_handle h,
return retval;
}
/*! Initialize database */
int
xmldb_init(clicon_handle h,
char *db)
{
if (clicon_xmldb_rpc(h))
return xmldb_init_rpc(h, db);
else
return xmldb_init_local(h, db);
}
#if 0 /* Test program */
/*
@ -2127,7 +1974,7 @@ main(int argc, char **argv)
op = OP_REMOVE;
else
usage(argv[0]);
if (xmldb_put(h, db, xn, op) < 0)
if (xmldb_put(h, db, op, NULL, xn) < 0)
goto done;
}
else

View file

@ -1,576 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* XML database
* TODO: xmldb_del: or dbxml_put_xkey delete
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <arpa/inet.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_chunk.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_qdb.h"
#include "clixon_yang.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_xsl.h"
#include "clixon_xml_parse.h"
#include "clixon_xml_db.h"
#include "clixon_xml_db_rpc.h"
/*! Make an rpc call to xmldb daemon
*/
static int
xmldb_rpc(clicon_handle h,
char *data,
size_t len,
char *retdata,
size_t *retlen
)
{
int retval = -1;
char *dst;
uint16_t port;
int s = -1;
struct sockaddr_in addr;
if ((dst = clicon_xmldb_addr(h)) == NULL){
clicon_err(OE_CFG, errno, "CLICON_XMLDB_ADDR option not set");
goto done;
}
if ((port = clicon_xmldb_port(h)) == 0){
clicon_err(OE_CFG, errno, "CLICON_XMLDB_PORT option not set");
goto done;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (inet_pton(addr.sin_family, dst, &addr.sin_addr) != 1)
goto done; /* Could check getaddrinfo */
if ((s = socket(addr.sin_family, SOCK_STREAM, 0)) < 0) {
clicon_err(OE_CFG, errno, "socket");
return -1;
}
if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) < 0){
clicon_err(OE_CFG, errno, "connecting socket inet4");
close(s);
goto done;
}
if (write(s, data, len) < 0){
clicon_err(OE_UNIX, errno, "write");
goto done;
}
if ((*retlen = read(s, retdata, *retlen)) < 0){
clicon_err(OE_UNIX, errno, "write");
goto done;
}
retval = 0;
if (debug > 1)
fprintf(stderr, "%s: \"%s\"\n", __FUNCTION__, retdata);
done:
if (s != -1)
close(s);
return retval;
}
/*! Send put request to backend daemon
* @param[in] h CLICON handle
* @param[in] db running|candidate
* @retval 0
*/
int
xmldb_put_rpc(clicon_handle h,
char *db,
cxobj *xt,
enum operation_type op)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
char *opstr;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><put>");
cprintf(cb, "<target><%s/></target>", db);
if (op){
switch (op){
case OP_REPLACE:
opstr = "replace";
break;
case OP_MERGE:
opstr = "merge";
break;
case OP_NONE:
default:
opstr = "none";
break;
}
cprintf(cb, "<default-operation>%s</default-operation>", opstr);
}
cprintf(cb, "<config>");
if (clicon_xml2cbuf(cb, xt, 0, 1) < 0)
goto done;
cprintf(cb, "</config>");
cprintf(cb, "</put></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Send put xkey request to backend daemon
* @param[in] h CLICON handle
* @param[in] db running|candidate
* @retval 0
*/
int
xmldb_put_xkey_rpc(clicon_handle h,
char *db,
char *xk,
char *val,
enum operation_type op)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
char *opstr;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><put-xkey>");
cprintf(cb, "<target><%s/></target>", db);
if (op){
switch (op){
case OP_REPLACE:
opstr = "replace";
break;
case OP_MERGE:
opstr = "merge";
break;
case OP_NONE:
default:
opstr = "none";
break;
}
cprintf(cb, "<default-operation>%s</default-operation>", opstr);
}
cprintf(cb, "<xkey>%s</xkey>", xk);
cprintf(cb, "<value>%s</value>", val);
cprintf(cb, "</put-xkey></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Send get request to backend daemon
* @param[in] h CLICON handle
* @param[in] db running|candidate
* @retval 0
*/
int
xmldb_get_rpc(clicon_handle h,
char *db,
char *xpath,
cxobj **xtop,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
cxobj *xt=NULL;
cxobj *xc;
int i;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><get>");
cprintf(cb, "<source><%s/></source>", db);
if (xpath)
cprintf(cb, "<xpath>%s</xpath>", xpath);
cprintf(cb, "</get></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
if (clicon_xml_parse_str(rb, &xt) < 0)
goto done;
if (xvec){
i=0;
if ((*xvec = calloc(xml_child_nr(xt), sizeof(cxobj*))) ==NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
xc = NULL;
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
(*xvec)[i++] = xc;
}
*xlen = i;
*xtop = xt;
xt = NULL;
}
else{
if (xml_rootchild(xt, 0, xtop) < 0)
goto done;
xt = NULL;
}
retval = 0;
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Copy database
* @param[in] h Clicon handle
* @param[in] from Source database copy
* @param[in] to Destination database
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_copy_rpc(clicon_handle h,
char *from,
char *to)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
cxobj *xt = NULL;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><copy>");
cprintf(cb, "<source><%s/></source>", from);
cprintf(cb, "<target><%s/></target>", to);
cprintf(cb, "</copy></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
if (clicon_xml_parse_str(rb, &xt) < 0)
goto done;
if (xpath_first(xt, "//ok"))
retval = 0;
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Lock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_lock_rpc(clicon_handle h,
char *db,
int id)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
cxobj *xt = NULL;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><lock>");
cprintf(cb, "<target><%s/></target>", db);
cprintf(cb, "<id><%u/></id>", id);
cprintf(cb, "</lock></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
if (clicon_xml_parse_str(rb, &xt) < 0)
goto done;
if (xpath_first(xt, "//ok"))
retval = 0;
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Unlock database
* @param[in] h Clicon handle
* @param[in] db Database
* @param[in] pid Process id
* @retval -1 Error
* @retval 0 OK
*/
int
xmldb_unlock_rpc(clicon_handle h,
char *db,
int id)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
cxobj *xt = NULL;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><unlock>");
cprintf(cb, "<target><%s/></target>", db);
cprintf(cb, "<id><%u/></id>", id);
cprintf(cb, "</unlock></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
if (clicon_xml_parse_str(rb, &xt) < 0)
goto done;
if (xpath_first(xt, "//ok"))
retval = 0;
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Check if database is locked
* @param[in] h Clicon handle
* @param[in] db Database
* @retval -1 Error
* @retval pid Process id if locked
*/
int
xmldb_islocked_rpc(clicon_handle h,
char *db)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
cxobj *xt = NULL;
cxobj *x;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><islocked>");
cprintf(cb, "<target><%s/></target>", db);
cprintf(cb, "</islocked></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
if (clicon_xml_parse_str(rb, &xt) < 0)
goto done;
if (xpath_first(xt, "//unlocked"))
retval = 0;
else
if ((x=xpath_first(xt, "//locked")) != NULL)
retval = atoi(xml_body(x));
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Send exists request to backend daemon
* @param[in] h CLICON handle
* @param[in] db running|candidate
* @retval -1 Error
* @retval 0 No it does not exist
* @retval 1 Yes it exists
*/
int
xmldb_exists_rpc(clicon_handle h,
char *db)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
cxobj *xt = NULL;
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><exists>");
cprintf(cb, "<target><%s/></target>", db);
cprintf(cb, "</exists></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
if (clicon_xml_parse_str(rb, &xt) < 0)
goto done;
if (xpath_first(xt, "//ok"))
retval = 1;
else
retval = 0;
done:
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Send delete request to backend daemon
* @param[in] h CLICON handle
* @param[in] db running|candidate
* @retval 0
*/
int
xmldb_delete_rpc(clicon_handle h,
char *db)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><delete>");
cprintf(cb, "<target><%s/></target>", db);
cprintf(cb, "</delete></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Send init request to backend daemon
* @param[in] h CLICON handle
* @param[in] db running|candidate
* @retval 0
*/
int
xmldb_init_rpc(clicon_handle h,
char *db)
{
int retval = -1;
cbuf *cb = NULL;
char retbuf[BUFSIZ];
char *rb = retbuf;
size_t retlen = sizeof(retbuf);
if ((cb = cbuf_new()) == NULL)
goto done;
cprintf(cb, "<rpc><init>");
cprintf(cb, "<target><%s/></target>", db);
cprintf(cb, "</init></rpc>]]>]]>");
if (xmldb_rpc(h,
cbuf_get(cb),
cbuf_len(cb)+1,
rb, &retlen) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}

View file

@ -1,56 +0,0 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
#ifndef _CLIXON_XML_DB_RPC_H_
#define _CLIXON_XML_DB_RPC_H_
/*
* Prototypes
*/
int xmldb_get_rpc(clicon_handle h, char *db,
char *xpath,
cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put_rpc(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
int xmldb_put_xkey_rpc(clicon_handle h, char *db, char *xk, char *val,
enum operation_type op);
int xmldb_copy_rpc(clicon_handle h, char *from, char *to);
int xmldb_lock_rpc(clicon_handle h, char *db, int pid);
int xmldb_unlock_rpc(clicon_handle h, char *db, int pid);
int xmldb_islocked_rpc(clicon_handle h, char *db);
int xmldb_exists_rpc(clicon_handle h, char *db);
int xmldb_delete_rpc(clicon_handle h, char *db);
int xmldb_init_rpc(clicon_handle h, char *db);
#endif /* _CLIXON_XML_DB_RPC_H_ */

View file

@ -46,6 +46,7 @@ struct xml_parse_yacc_arg{
cxobj *ya_xelement; /* xml active element */
cxobj *ya_xparent; /* xml parent element*/
int ya_skipspace; /* If set, translate successive space, \t \n with single space */
};
extern char *clixon_xml_parsetext;

View file

@ -100,7 +100,8 @@ xml_attr_new(struct xml_parse_yacc_arg *ya,
there may also be some leakage here on NULL return
*/
static int
xml_parse_content(struct xml_parse_yacc_arg *ya, char *str)
xml_parse_content(struct xml_parse_yacc_arg *ya,
char *str)
{
int sz;
char s0;
@ -112,14 +113,14 @@ xml_parse_content(struct xml_parse_yacc_arg *ya, char *str)
s0 = str[0];
if (xn != NULL){
sz = strlen(xml_value(xn));
if (s0 == ' ' || s0 == '\n' || s0 == '\t'){
if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t')){
str[0] = ' ';
if (xml_value(xn)[sz-1] == ' ')
goto ok;
}
}
else{
if (s0 == ' ' || s0 == '\n' || s0 == '\t')
if (ya->ya_skipspace && (s0 == ' ' || s0 == '\n' || s0 == '\t'))
goto ok;
if ((xn = xml_new("body", xp)) == NULL)
goto done;

View file

@ -552,12 +552,6 @@ xpath_expr(char *predicate_expression,
* @param[in] flags if != 0, only match xml nodes matching flags
* @param[out] vec2 Result XML node vector
* @param[out] vec2len Length of result vector.
* XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1
* vec0 --> vec
* Det är nog bra om vec0 inte ändras, är input parameter
* Vid utgång ska vec1 innehålla resultatet.
* Internt ?
* XXX: hantering av (input)vec0-->vec-->vec2-->vec1 (resultat)
*/
static int
xpath_find(struct xpath_element *xe,
@ -767,6 +761,7 @@ xpath_choice(cxobj *xtop,
cxobj **vec0 = NULL;
size_t vec0len = 0;
if ((s0 = strdup(xpath0)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# include err() and new() functions
. ./lib.sh

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
testnr=0
testnname=
@ -32,7 +32,7 @@ expectfn(){
fi
# grep extended grep
match=`echo "$ret" | grep -Eo "$expect"`
# echo "ret:$ret"
# echo "ret:<$ret>"
# echo "expect:$expect"
# echo "match:$match"
if [ -z "$match" ]; then
@ -57,7 +57,25 @@ EOF
fi
match=`echo "$ret" | grep -Eo "$expect"`
if [ -z "$match" ]; then
err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\""
err "
fi
}
# clicon_cli tester. First arg is command and second is expected outcome
expectwait(){
cmd=$1
input=$2
expect=$3
wait=$4
# Do while read stuff
sleep 10|cat <(echo $input) -| $cmd | while [ 1 ] ; do
read ret
match=$(echo "$ret" | grep -Eo "$expect");
if [ -z "$match" ]; then
err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\""
fi
break
done
}

View file

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# Test1: backend and cli basic functionality
# Start backend server
# Add an ethernet interface and an address
@ -18,7 +18,7 @@ if [ $? -ne 0 ]; then
fi
new "start backend"
# start new backend
sudo clixon_backend -If $clixon_cf -x 0 # -x 1 with xmldb proxy
sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
@ -30,7 +30,7 @@ expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name e
interfaces interface enabled true$"
new "cli failed validate"
expectfn "clixon_cli -1f $clixon_cf -l o validate" "Validate failed"
expectfn "clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"
new "cli configure more"
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 ipv4 address 1.2.3.4 prefix-length 24" ""
@ -39,6 +39,25 @@ expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
new "cli commit"
expectfn "clixon_cli -1f $clixon_cf -l o commit" ""
new "cli save"
expectfn "clixon_cli -1f $clixon_cf -l o save /tmp/foo" ""
new "cli delete all"
expectfn "clixon_cli -1f $clixon_cf -l o delete all" ""
new "cli load"
expectfn "clixon_cli -1f $clixon_cf -l o load /tmp/foo" ""
new "cli check load"
expectfn "clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0
interfaces interface enabled true$"
new "cli debug"
expectfn "clixon_cli -1f $clixon_cf -l o debug level 1" ""
new "cli downcall"
expectfn "clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$"
new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`

View file

@ -1,11 +1,5 @@
#!/bin/sh
# Test1: backend and cli basic functionality
# Start backend server
# Add an ethernet interface and an address
# Show configuration
# Validate without a mandatory type
# Set the mandatory type
# Commit
#!/bin/bash
# Test2: backend and netconf basic functionality
# include err() and new() functions
. ./lib.sh
@ -18,15 +12,66 @@ if [ $? -ne 0 ]; then
fi
new "start backend"
# start new backend
sudo clixon_backend -If $clixon_cf -x 0 # -x 1 with xmldb proxy
sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "netconf show config"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "<rpc-reply><data></data></rpc-reply>]]>]]>"
new "netconf get empty config"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc message-id=\"101\"><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply message-id=\"101\"><data/></rpc-reply>]]>]]>$"
new "netconf lock"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "<rpc-reply><ok/></rpc-reply>]]>]]>"
new "netconf edit config"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get config xpath"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/interfaces/interface[name=eth1]/enabled\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf validate missing type"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
new "netconf discard-changes"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get empty config2"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
new "netconf edit config patch"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth1</name><type>eth</type></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf validate"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf lock/unlock"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf lock/lock"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf lock"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "close-session"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><close-session/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "kill-session"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><kill-session><session-id>44</session-id></kill-session></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "copy startup"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><copy-config><target><startup/></target><source><candidate/></source></copy-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get startup"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
new "netconf delete startup"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><delete-config><target><startup/></target></delete-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf check empty startup"
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
new "netconf subscription"
expectwait "clixon_netconf -qf $clixon_cf" "<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><notification><event>Routing notification</event></notification>]]>]]>$" 30
new "Kill backend"
# Check if still alive
@ -39,4 +84,3 @@ sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err "kill backend"
fi

60
test/test3.sh Executable file
View file

@ -0,0 +1,60 @@
#!/bin/bash
# Test3: backend and restconf basic functionality
# include err() and new() functions
. ./lib.sh
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "start backend"
sudo clixon_backend -If $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf
new "start restconf daemon"
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df /usr/local/etc/routing.conf # -D
sleep 1
new "restconf get empty config"
expectfn "curl -sG http://localhost/restconf/data" "^null $"
new "restconf put config"
expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth1","type":"eth","enabled":"true"},{"name":"eth0","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' ""
new "restconf get config"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth0","type": "eth","enabled": "true"}\]}}
$'
new "restconf patch config"
expectfn 'curl -sX POST -d {"type":"type"} http://localhost/restconf/data/interfaces/interface=eth4' ""
new "restconf delete config"
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
new "restconf get config"
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}}
$'
new "Kill restconf daemon"
sudo pkill -u www-data clixon_restconf
new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`
if [ -z "$pid" ]; then
err "backend already dead"
fi
# kill backend
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err "kill backend"
fi