Change internal protocol from clicon_proto.h to netconf.
This commit is contained in:
parent
2e09f54d12
commit
2fcefda831
66 changed files with 3012 additions and 5141 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 ..
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@
|
|||
#ifndef _BACKEND_HANDLE_H_
|
||||
#define _BACKEND_HANDLE_H_
|
||||
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
* not exported.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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__);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
29
doc/FAQ.txt
29
doc/FAQ.txt
|
|
@ -91,6 +91,7 @@ Q: How do you change the example?
|
|||
- routing_backend.c - Commit and validate functions.
|
||||
- routing_netconf.c - Modify semantics of netconf commands.
|
||||
|
||||
|
||||
Q: How do you check what is in a database?
|
||||
------------------------------------------
|
||||
Use clixon_dbctrl. The name of the running or candidate databases are found in the
|
||||
|
|
@ -169,9 +170,31 @@ The validation or commit will then be aborted.
|
|||
Q: How do you use netconf?
|
||||
--------------------------
|
||||
|
||||
As an alternative to cli configuration, you can use netconf
|
||||
directly. Easiest is to just pipe netconf commands to the
|
||||
clixon_netconf application.
|
||||
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
|
||||
Example:
|
||||
echo "<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>]]>]]>
|
||||
...
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -698,4 +698,4 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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");}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -552,12 +552,6 @@ xpath_expr(char *predicate_expression,
|
|||
* @param[in] flags if != 0, only match xml nodes matching flags
|
||||
* @param[out] vec2 Result XML node vector
|
||||
* @param[out] vec2len Length of result vector.
|
||||
* XXX: Kommer in i funktionen med vec0, resultatet appendas i vec1
|
||||
* vec0 --> vec
|
||||
* Det är nog bra om vec0 inte ändras, är input parameter
|
||||
* Vid utgång ska vec1 innehålla resultatet.
|
||||
* Internt då?
|
||||
* XXX: hantering av (input)vec0-->vec-->vec2-->vec1 (resultat)
|
||||
*/
|
||||
static int
|
||||
xpath_find(struct xpath_element *xe,
|
||||
|
|
@ -767,6 +761,7 @@ xpath_choice(cxobj *xtop,
|
|||
cxobj **vec0 = NULL;
|
||||
size_t vec0len = 0;
|
||||
|
||||
|
||||
if ((s0 = strdup(xpath0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
|
|
|||
24
test/lib.sh
24
test/lib.sh
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
testnr=0
|
||||
testnname=
|
||||
|
|
@ -32,7 +32,7 @@ expectfn(){
|
|||
fi
|
||||
# grep extended grep
|
||||
match=`echo "$ret" | grep -Eo "$expect"`
|
||||
# echo "ret:$ret"
|
||||
# echo "ret:<$ret>"
|
||||
# echo "expect:$expect"
|
||||
# echo "match:$match"
|
||||
if [ -z "$match" ]; then
|
||||
|
|
@ -57,7 +57,25 @@ EOF
|
|||
fi
|
||||
match=`echo "$ret" | grep -Eo "$expect"`
|
||||
if [ -z "$match" ]; then
|
||||
err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\""
|
||||
err "
|
||||
fi
|
||||
}
|
||||
|
||||
# clicon_cli tester. First arg is command and second is expected outcome
|
||||
expectwait(){
|
||||
cmd=$1
|
||||
input=$2
|
||||
expect=$3
|
||||
wait=$4
|
||||
|
||||
# Do while read stuff
|
||||
sleep 10|cat <(echo $input) -| $cmd | while [ 1 ] ; do
|
||||
read ret
|
||||
match=$(echo "$ret" | grep -Eo "$expect");
|
||||
if [ -z "$match" ]; then
|
||||
err "\nExpected:\t\"$expect\"\nGot:\t\"$ret\""
|
||||
fi
|
||||
break
|
||||
done
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
60
test/test3.sh
Executable 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue