merged from develop branch: changed internal messages to netconf
This commit is contained in:
commit
ff66577151
86 changed files with 4773 additions and 7720 deletions
85
CHANGELOG
85
CHANGELOG
|
|
@ -29,21 +29,86 @@
|
||||||
#
|
#
|
||||||
# ***** END LICENSE BLOCK *****
|
# ***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
- cli_copy_config added as generic cli command
|
||||||
|
- cli_show_config added as generic cli command
|
||||||
|
Replace all show_confv*() and show_conf*() with cli_show_config()
|
||||||
|
Example: replace:
|
||||||
|
show_confv_as_json("candidate","/sender");
|
||||||
|
with:
|
||||||
|
cli_show_config("candidate","json","/sender");
|
||||||
|
- Alternative yang spec option -y added to all applications
|
||||||
|
- Many clicon special string functions have been removed
|
||||||
|
- The netconf support has been extended with lock/unlock
|
||||||
|
- clicon_rpc_call() has been removed and should be replaced by extending the
|
||||||
|
internal netconf protocol.
|
||||||
|
See downcall() function in example/routing_cli.c and
|
||||||
|
routing_downcall() in example/routing_backend.c
|
||||||
|
- Replace clicon_rpc_xmlput with clicon_rpc_edit_config
|
||||||
|
- Removed xmldb daemon. All xmldb acceses is made backend daemon.
|
||||||
|
No direct accesses by clients to xmldb API.
|
||||||
|
Instead use the rpc calls in clixon_proto_client.[ch]
|
||||||
|
In clients (eg cli/netconf) replace xmldb_get() in client code with
|
||||||
|
clicon_rpc_get_config().
|
||||||
|
If you use the vector arguments of xmldb_get(), replace as follows:
|
||||||
|
xmldb_get(h, db, api_path, &xt, &xvec, &xlen);
|
||||||
|
with
|
||||||
|
clicon_rpc_get_config(h, dbstr, api_path, &xt);
|
||||||
|
xpath_vec(xt, api_path, &xvec, &xlen)
|
||||||
|
|
||||||
|
- clicon_rpc_change() is replaced with clicon_rpc_edit_config().
|
||||||
|
Note modify argument 5:
|
||||||
|
clicon_rpc_change(h, db, op, apipath, "value")
|
||||||
|
to:
|
||||||
|
clicon_rpc_edit_config(h, db, op, apipath, "<config>value</config>")
|
||||||
|
|
||||||
|
- xmdlb_put_xkey() and xmldb_put_tree() have been folded into xmldb_put()
|
||||||
|
Replace xmldb_put_xkey with xmldb_put as follows:
|
||||||
|
xmldb_put_xkey(h, "candidate", cbuf_get(cb), str, OP_REPLACE);
|
||||||
|
with
|
||||||
|
clicon_xml_parse(&xml, "<config>%s</config>", str);
|
||||||
|
xmldb_put(h, "candidate", OP_REPLACE, cbuf_get(cb), xml);
|
||||||
|
xml_free(xml);
|
||||||
|
|
||||||
|
- Change internal protocol from clicon_proto.h to netconf.
|
||||||
|
This means that the internal protocol defined in clixon_proto.[ch] is removed
|
||||||
|
|
||||||
- Netconf startup configuration support. Set CLICON_USE_STARTUP_CONFIG to 1 to
|
- 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
|
enable. Eg, if backend_main is started with -CIr startup will be copied to
|
||||||
running.
|
running.
|
||||||
|
|
||||||
- Added ".." as valid step in xpath
|
- Added ".." as valid step in xpath
|
||||||
|
|
||||||
- Use restconf format for internal xmldb keys. Eg /a/b=3,4
|
- Use restconf format for internal xmldb keys. Eg /a/b=3,4
|
||||||
- List keys with special characters are RFC 3986 encoded.
|
|
||||||
- Changed example to use multiple cli callbacks
|
- List keys with special characters RFC 3986 encoded.
|
||||||
- Added cli multiple callback and expand support. Use options
|
|
||||||
CLICON_CLIGEN_CALLBACK_SINGLE_ARG and CLICON_CLIGEN_EXPAND_SINGLE_ARG
|
- Replaced cli expand functions with single to multiple args
|
||||||
to control these.
|
This change is _not_ backward compatible
|
||||||
The multiple support for expand callbacks is enabled but not for callbacks
|
This effects all calls to expand_dbvar() or user-defined
|
||||||
since this causes problems for legacy applications.
|
expand callbacks
|
||||||
If you change to multiple argument callbacks change all cli callback functions.
|
|
||||||
Library functions in clixon_cli_api.h (e.g cli_commit) is rewritten in new
|
- Replaced cli callback functions with single arg to multiple args
|
||||||
for (eg cli_commitv). See clixon_cli_api.h for new names.
|
This change is _not_ backward compatible.
|
||||||
|
You are affected if you
|
||||||
|
(1) use system callbacks (i.e. in clixon_cli_api.h)
|
||||||
|
(2) write your own cli callbacks
|
||||||
|
|
||||||
|
If you use cli callbacks, you need to rewrite cli callbacks from eg:
|
||||||
|
load("Comment") <filename:string>,load_config_file("filename replace");
|
||||||
|
to:
|
||||||
|
load("Comment") <filename:string>,load_config_file("filename", "replace");
|
||||||
|
|
||||||
|
If you write your own, you need to change the callback signature from;
|
||||||
|
int cli_callback(clicon_handle h, cvec *vars, cg_var *arg)
|
||||||
|
to:
|
||||||
|
int cli_callback(clicon_handle h, cvec *vars, cvec *argv)
|
||||||
|
and rewrite the code to handle argv instead of arg.
|
||||||
|
These are the system functions affected:
|
||||||
|
cli_set, cli_merge, cli_del, cli_debug_backend, cli_set_mode,
|
||||||
|
cli_start_shell, cli_quit, cli_commit, cli_validate, compare_dbs,
|
||||||
|
load_config_file, save_config_file, delete_all, discard_changes, cli_notify,
|
||||||
|
show_yang, show_conf_xpath
|
||||||
|
|
||||||
- Added --with-cligen and --with-qdbm configure options
|
- Added --with-cligen and --with-qdbm configure options
|
||||||
- Added union type check for non-cli (eg xml) input
|
- Added union type check for non-cli (eg xml) input
|
||||||
- Empty yang type. Relaxed yang types for unions, eg two strings with different length.
|
- Empty yang type. Relaxed yang types for unions, eg two strings with different length.
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,11 @@ This README contains information for developers:
|
||||||
|
|
||||||
2. How to work in git (branching)
|
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:
|
only somewhat simplified:
|
||||||
|
|
||||||
Do commits in develop branch. When done, merge with master.
|
Do commits in develop branch. When done, merge with master.
|
||||||
|
|
||||||
|
|
||||||
$ git checkout develop
|
$ git checkout develop
|
||||||
Switch to branch develop
|
Switch to branch develop
|
||||||
$ git add ..
|
$ git add ..
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ LIBS = @LIBS@
|
||||||
|
|
||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
|
|
||||||
SUBDIRS = cli backend dbctrl netconf xmldb restconf
|
SUBDIRS = cli backend dbctrl netconf restconf
|
||||||
|
|
||||||
.PHONY: all clean depend install $(SUBDIRS)
|
.PHONY: all clean depend install $(SUBDIRS)
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -70,8 +70,6 @@ struct client_subscription{
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int backend_client_rm(clicon_handle h, struct client_entry *ce);
|
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);
|
int from_client(int fd, void *arg);
|
||||||
|
|
||||||
#endif /* _BACKEND_CLIENT_H_ */
|
#endif /* _BACKEND_CLIENT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,10 @@ generic_validate(yang_spec *yspec,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Common code of candidate_validate and candidate_commit
|
/*! 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
|
static int
|
||||||
validate_common(clicon_handle h,
|
validate_common(clicon_handle h,
|
||||||
|
|
@ -137,7 +141,6 @@ validate_common(clicon_handle h,
|
||||||
int i;
|
int i;
|
||||||
cxobj *xn;
|
cxobj *xn;
|
||||||
|
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -160,7 +163,7 @@ validate_common(clicon_handle h,
|
||||||
&td->td_tcvec, /* changed: wanted values */
|
&td->td_tcvec, /* changed: wanted values */
|
||||||
&td->td_clen) < 0)
|
&td->td_clen) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (debug)
|
if (debug>1)
|
||||||
transaction_print(stderr, td);
|
transaction_print(stderr, td);
|
||||||
/* Mark as changed in tree */
|
/* Mark as changed in tree */
|
||||||
for (i=0; i<td->td_dlen; i++){ /* Also down */
|
for (i=0; i<td->td_dlen; i++){ /* Also down */
|
||||||
|
|
@ -253,126 +256,145 @@ candidate_commit(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Do a diff between candidate and running, then start a validate transaction
|
/*! Commit changes from candidate to running
|
||||||
*
|
* @param[in] h Clicon handle
|
||||||
* @param[in] h Clicon handle
|
* @param[out] cbret Return xml value cligen buffer
|
||||||
* @param[in] candidate: The candidate database. The wanted backend state
|
* @retval 0 OK. This may indicate both ok and err msg back to client
|
||||||
*/
|
* @retval -1 (Local) Error
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
candidate_validate(clicon_handle h,
|
from_client_commit(clicon_handle h,
|
||||||
char *candidate)
|
int mypid,
|
||||||
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
transaction_data_t *td = NULL;
|
int piddb;
|
||||||
|
|
||||||
|
/* Check if target locked by other client */
|
||||||
|
piddb = xmldb_islocked(h, "running");
|
||||||
|
if (piddb && mypid != piddb){
|
||||||
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
|
"<error-tag>lock-denied</error-tag>"
|
||||||
|
"<error-type>protocol</error-type>"
|
||||||
|
"<error-severity>error</error-severity>"
|
||||||
|
"<error-message>Operation failed, lock is already held</error-message>"
|
||||||
|
"<error-info><session-id>%d</session-id></error-info>"
|
||||||
|
"</rpc-error></rpc-reply>",
|
||||||
|
piddb);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate_commit(h, "candidate") < 0){
|
||||||
|
clicon_debug(1, "Commit candidate failed");
|
||||||
|
/* XXX: candidate_validate should have proper error handling */
|
||||||
|
cprintf(cbret, "<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:
|
||||||
|
return retval; /* may be zero if we ignoring errors from commit */
|
||||||
|
} /* from_client_commit */
|
||||||
|
|
||||||
|
/*! Discard all changes in candidate / revert to running
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] mypid Process/session id of calling client
|
||||||
|
* @param[out] cbret Return xml value cligen buffer
|
||||||
|
* @retval 0 OK. This may indicate both ok and err msg back to client
|
||||||
|
* @retval -1 (Local) Error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
from_client_discard_changes(clicon_handle h,
|
||||||
|
int mypid,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int piddb;
|
||||||
|
|
||||||
|
/* Check if target locked by other client */
|
||||||
|
piddb = xmldb_islocked(h, "candidate");
|
||||||
|
if (piddb && mypid != piddb){
|
||||||
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
|
"<error-tag>lock-denied</error-tag>"
|
||||||
|
"<error-type>protocol</error-type>"
|
||||||
|
"<error-severity>error</error-severity>"
|
||||||
|
"<error-message>Operation failed, lock is already held</error-message>"
|
||||||
|
"<error-info><session-id>%d</session-id></error-info>"
|
||||||
|
"</rpc-error></rpc-reply>",
|
||||||
|
piddb);
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
char *db,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
transaction_data_t *td = NULL;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
clicon_debug(1, "Validate %s", db);
|
||||||
|
|
||||||
/* 1. Start transaction */
|
/* 1. Start transaction */
|
||||||
if ((td = transaction_new()) == NULL)
|
if ((td = transaction_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Common steps (with commit) */
|
/* Common steps (with commit) */
|
||||||
if (validate_common(h, candidate, td) < 0)
|
if (validate_common(h, db, td) < 0){
|
||||||
goto done;
|
clicon_debug(1, "Validate %s failed", db);
|
||||||
|
/* XXX: candidate_validate should have proper error handling */
|
||||||
retval = 0;
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
done:
|
"<error-tag>missing-attribute</error-tag>"
|
||||||
/* In case of failure, call plugin transaction termination callbacks */
|
"<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)
|
if (retval < 0 && td)
|
||||||
plugin_transaction_abort(h, td);
|
plugin_transaction_abort(h, td);
|
||||||
if (td)
|
if (td)
|
||||||
transaction_free(td);
|
transaction_free(td);
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Handle an incoming commit message from a client.
|
|
||||||
* XXX: If commit succeeds and snapshot/startup fails, we have strange state:
|
|
||||||
* the commit has succeeded but an error message is returned.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
from_client_commit(clicon_handle h,
|
|
||||||
int s,
|
|
||||||
struct clicon_msg *msg,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
char *candidate;
|
|
||||||
char *running;
|
|
||||||
|
|
||||||
if (clicon_msg_commit_decode(msg,
|
|
||||||
&candidate,
|
|
||||||
&running,
|
|
||||||
label) < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (strcmp(candidate, "candidate") && strcmp(candidate, "tmp")){
|
|
||||||
clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (strcmp(running, "running")){
|
|
||||||
clicon_err(OE_PLUGIN, 0, "running db is not \"running\"");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (candidate_commit(h, "candidate") < 0){
|
|
||||||
clicon_debug(1, "Commit %s failed", candidate);
|
|
||||||
retval = 0; /* We ignore errors from commit, but maybe
|
|
||||||
we should fail on fatal errors? */
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
clicon_debug(1, "Commit %s", candidate);
|
|
||||||
retval = 0;
|
|
||||||
if (send_msg_ok(s) < 0)
|
|
||||||
goto done;
|
|
||||||
goto done;
|
|
||||||
err:
|
|
||||||
/* XXX: more elaborate errstring? */
|
|
||||||
if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
|
|
||||||
retval = -1;
|
|
||||||
done:
|
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
|
|
||||||
return retval; /* may be zero if we ignoring errors from commit */
|
|
||||||
} /* from_client_commit */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! Handle an incoming validate message from a client.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
from_client_validate(clicon_handle h,
|
|
||||||
int s,
|
|
||||||
struct clicon_msg *msg,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
char *candidate;
|
|
||||||
|
|
||||||
if (clicon_msg_validate_decode(msg,
|
|
||||||
&candidate,
|
|
||||||
label) < 0){
|
|
||||||
send_msg_err(s, clicon_errno, clicon_suberrno,
|
|
||||||
clicon_err_reason);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (strcmp(candidate, "candidate") != 0 && strcmp(candidate, "tmp") != 0){
|
|
||||||
clicon_err(OE_PLUGIN, 0, "candidate is not \"candidate\" or tmp");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
clicon_debug(1, "Validate %s", candidate);
|
|
||||||
if (candidate_validate(h, candidate) < 0){
|
|
||||||
clicon_debug(1, "Validate %s failed", candidate);
|
|
||||||
retval = 0; /* We ignore errors from commit, but maybe
|
|
||||||
we should fail on fatal errors? */
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
retval = 0;
|
|
||||||
if (send_msg_ok(s) < 0)
|
|
||||||
goto done;
|
|
||||||
goto done;
|
|
||||||
err:
|
|
||||||
/* XXX: more elaborate errstring? */
|
|
||||||
if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
|
|
||||||
retval = -1;
|
|
||||||
done:
|
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return retval;
|
return retval;
|
||||||
} /* from_client_validate */
|
} /* from_client_validate */
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,9 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int from_client_validate(clicon_handle h, int s, struct clicon_msg *msg, const char *label);
|
int from_client_validate(clicon_handle h, char *db, cbuf *cbret);
|
||||||
int from_client_commit(clicon_handle h, int s, struct clicon_msg *msg, const char *label);
|
int from_client_commit(clicon_handle h, int pid, cbuf *cbret);
|
||||||
int candidate_commit(clicon_handle h, char *candidate);
|
int from_client_discard_changes(clicon_handle h, int pid, cbuf *cbret);
|
||||||
|
int candidate_commit(clicon_handle h, char *db);
|
||||||
|
|
||||||
#endif /* _BACKEND_COMMIT_H_ */
|
#endif /* _BACKEND_COMMIT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@
|
||||||
#ifndef _BACKEND_HANDLE_H_
|
#ifndef _BACKEND_HANDLE_H_
|
||||||
#define _BACKEND_HANDLE_H_
|
#define _BACKEND_HANDLE_H_
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
* not exported.
|
* not exported.
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -72,7 +73,7 @@
|
||||||
#include "backend_handle.h"
|
#include "backend_handle.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:ptx:"
|
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pty:"
|
||||||
|
|
||||||
/*! Terminate. Cannot use h after this */
|
/*! Terminate. Cannot use h after this */
|
||||||
static int
|
static int
|
||||||
|
|
@ -142,7 +143,7 @@ usage(char *argv0, clicon_handle h)
|
||||||
" -p \t\tPrint database yang specification\n"
|
" -p \t\tPrint database yang specification\n"
|
||||||
" -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\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"
|
" -g <group>\tClient membership required to this group (default: %s)\n"
|
||||||
" -x <status>\tSet CLICON_XMLDB_RPC to 0 or 1.\n",
|
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
|
||||||
argv0,
|
argv0,
|
||||||
plgdir ? plgdir : "none",
|
plgdir ? plgdir : "none",
|
||||||
confsock ? confsock : "none",
|
confsock ? confsock : "none",
|
||||||
|
|
@ -153,11 +154,12 @@ usage(char *argv0, clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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;
|
return -1;
|
||||||
if (xmldb_init(h, "running") < 0)
|
if (xmldb_init(h, db) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +194,7 @@ rundb_main(clicon_handle h,
|
||||||
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
|
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xn = xml_child_i(xt, 0)) != NULL)
|
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;
|
goto done;
|
||||||
if (candidate_commit(h, "tmp") < 0)
|
if (candidate_commit(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -426,14 +428,15 @@ main(int argc, char **argv)
|
||||||
case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */
|
case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */
|
||||||
printalt++;
|
printalt++;
|
||||||
break;
|
break;
|
||||||
case 'x' : /* set xmldb rpc on */
|
case 'y' :{ /* yang module */
|
||||||
{
|
/* Set revision to NULL, extract dir and module */
|
||||||
int i;
|
char *str = strdup(optarg);
|
||||||
if (sscanf(optarg, "%d", &i) != 1)
|
char *dir = dirname(str);
|
||||||
usage(argv[0], h);
|
hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION");
|
||||||
clicon_option_int_set(h, "CLICON_XMLDB_RPC", i);
|
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(optarg));
|
||||||
}
|
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
usage(argv[0], h);
|
usage(argv[0], h);
|
||||||
break;
|
break;
|
||||||
|
|
@ -503,7 +506,7 @@ main(int argc, char **argv)
|
||||||
if (yang_spec_main(h, stdout, printspec) < 0)
|
if (yang_spec_main(h, stdout, printspec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* First check for starup config
|
/* First check for startup config
|
||||||
XXX the options below have become out-of-hand.
|
XXX the options below have become out-of-hand.
|
||||||
Too complex, need to simplify*/
|
Too complex, need to simplify*/
|
||||||
if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){
|
if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){
|
||||||
|
|
@ -513,8 +516,12 @@ main(int argc, char **argv)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (rundb_init(h) < 0)
|
if (db_reset(h, "running") < 0)
|
||||||
goto done;
|
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 running exists and reload_running set, make a copy to candidate */
|
||||||
if (reload_running){
|
if (reload_running){
|
||||||
|
|
@ -529,9 +536,17 @@ main(int argc, char **argv)
|
||||||
/* Init running db
|
/* Init running db
|
||||||
* -I or if it isnt there
|
* -I or if it isnt there
|
||||||
*/
|
*/
|
||||||
if (init_rundb || xmldb_exists(h, "running") != 1)
|
if (init_rundb || xmldb_exists(h, "running") != 1){
|
||||||
if (rundb_init(h) < 0)
|
if (db_reset(h, "running") < 0)
|
||||||
goto done;
|
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
|
/* Initialize plugins
|
||||||
(also calls plugin_init() and plugin_start(argc,argv) in each plugin */
|
(also calls plugin_init() and plugin_start(argc,argv) in each plugin */
|
||||||
|
|
|
||||||
|
|
@ -470,55 +470,6 @@ plugin_finish(clicon_handle h)
|
||||||
return 0;
|
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 */
|
/*! Create and initialize transaction */
|
||||||
transaction_data_t *
|
transaction_data_t *
|
||||||
transaction_new(void)
|
transaction_new(void)
|
||||||
|
|
@ -771,4 +722,3 @@ plugin_transaction_abort(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*! Transaction data
|
/*! Transaction data
|
||||||
* Clicon internal, presented as void* to app's callback in the 'transaction_data'
|
* Clicon internal, presented as void* to app's callback in the 'transaction_data'
|
||||||
* type in clicon_backend_api.h
|
* 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_reset_state(clicon_handle h, char *dbname);
|
||||||
int plugin_start_hooks(clicon_handle h, int argc, char **argv);
|
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);
|
transaction_data_t * transaction_new(void);
|
||||||
int transaction_free(transaction_data_t *);
|
int transaction_free(transaction_data_t *);
|
||||||
|
|
|
||||||
|
|
@ -193,12 +193,12 @@ config_socket_init(clicon_handle h)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Accept new socket client
|
||||||
* config_accept_client
|
|
||||||
* XXX: credentials not properly implemented
|
* XXX: credentials not properly implemented
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
config_accept_client(int fd, void *arg)
|
config_accept_client(int fd,
|
||||||
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
clicon_handle h = (clicon_handle)arg;
|
clicon_handle h = (clicon_handle)arg;
|
||||||
|
|
@ -218,7 +218,7 @@ config_accept_client(int fd, void *arg)
|
||||||
char *mem;
|
char *mem;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(2, "%s", __FUNCTION__);
|
||||||
len = sizeof(from);
|
len = sizeof(from);
|
||||||
if ((s = accept(fd, (struct sockaddr*)&from, &len)) < 0){
|
if ((s = accept(fd, (struct sockaddr*)&from, &len)) < 0){
|
||||||
clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__);
|
clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__);
|
||||||
|
|
@ -265,7 +265,7 @@ config_accept_client(int fd, void *arg)
|
||||||
/*
|
/*
|
||||||
* Here we register callbacks for actual data socket
|
* 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;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,7 @@ backend_notify(clicon_handle h,
|
||||||
struct handle_subscription *hs;
|
struct handle_subscription *hs;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
|
clicon_debug(2, "%s %s", __FUNCTION__, stream);
|
||||||
/* First thru all clients(sessions), and all subscriptions and find matches */
|
/* First thru all clients(sessions), and all subscriptions and find matches */
|
||||||
for (ce = backend_client_list(h); ce; ce = ce_next){
|
for (ce = backend_client_list(h); ce; ce = ce_next){
|
||||||
ce_next = ce->ce_next;
|
ce_next = ce->ce_next;
|
||||||
|
|
@ -161,7 +162,7 @@ backend_notify(clicon_handle h,
|
||||||
/* Then go thru all global (handle) subscriptions and find matches */
|
/* Then go thru all global (handle) subscriptions and find matches */
|
||||||
hs = NULL;
|
hs = NULL;
|
||||||
while ((hs = subscription_each(h, hs)) != NULL){
|
while ((hs = subscription_each(h, hs)) != NULL){
|
||||||
if (hs->hs_format != MSG_NOTIFY_TXT)
|
if (hs->hs_format != FORMAT_TEXT)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(hs->hs_stream, stream))
|
if (strcmp(hs->hs_stream, stream))
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -239,7 +240,7 @@ backend_notify_xml(clicon_handle h,
|
||||||
/* Then go thru all global (handle) subscriptions and find matches */
|
/* Then go thru all global (handle) subscriptions and find matches */
|
||||||
hs = NULL;
|
hs = NULL;
|
||||||
while ((hs = subscription_each(h, hs)) != NULL){
|
while ((hs = subscription_each(h, hs)) != NULL){
|
||||||
if (hs->hs_format != MSG_NOTIFY_XML)
|
if (hs->hs_format != FORMAT_XML)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(hs->hs_stream, stream))
|
if (strcmp(hs->hs_stream, stream))
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -257,7 +258,8 @@ backend_notify_xml(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct client_entry *
|
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 backend_handle *cb = handle(h);
|
||||||
struct client_entry *ce;
|
struct client_entry *ce;
|
||||||
|
|
@ -411,3 +413,79 @@ subscription_each(clicon_handle h,
|
||||||
hs = cb->cb_subscription;
|
hs = cb->cb_subscription;
|
||||||
return hs;
|
return hs;
|
||||||
}
|
}
|
||||||
|
/* Database dependency description */
|
||||||
|
struct backend_netconf_reg {
|
||||||
|
qelem_t nr_qelem; /* List header */
|
||||||
|
backend_netconf_cb_t nr_callback; /* Validation/Commit Callback */
|
||||||
|
void *nr_arg; /* Application specific argument to cb */
|
||||||
|
char *nr_tag; /* Xml tag when matched, callback called */
|
||||||
|
};
|
||||||
|
typedef struct backend_netconf_reg backend_netconf_reg_t;
|
||||||
|
|
||||||
|
static backend_netconf_reg_t *deps = NULL;
|
||||||
|
/*! Register netconf callback
|
||||||
|
* Called from plugin to register a callback for a specific netconf XML tag.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
backend_netconf_register_callback(clicon_handle h,
|
||||||
|
backend_netconf_cb_t cb, /* Callback called */
|
||||||
|
void *arg, /* Arg to send to callback */
|
||||||
|
char *tag) /* Xml tag when callback is made */
|
||||||
|
{
|
||||||
|
backend_netconf_reg_t *nr;
|
||||||
|
|
||||||
|
if ((nr = malloc(sizeof(backend_netconf_reg_t))) == NULL) {
|
||||||
|
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||||
|
goto catch;
|
||||||
|
}
|
||||||
|
memset (nr, 0, sizeof (*nr));
|
||||||
|
nr->nr_callback = cb;
|
||||||
|
nr->nr_arg = arg;
|
||||||
|
nr->nr_tag = strdup(tag); /* XXX strdup memleak */
|
||||||
|
INSQ(nr, deps);
|
||||||
|
return 0;
|
||||||
|
catch:
|
||||||
|
if (nr){
|
||||||
|
if (nr->nr_tag)
|
||||||
|
free(nr->nr_tag);
|
||||||
|
free(nr);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! See if there is any callback registered for this tag
|
||||||
|
*
|
||||||
|
* @param[in] h clicon handle
|
||||||
|
* @param[in] xn Sub-tree (under xorig) at child of rpc: <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
|
* 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.
|
/*! Generic downcall registration.
|
||||||
* Enables any function to be called from (cli) frontend
|
* 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 *subscription_each(clicon_handle h,
|
||||||
struct handle_subscription *hprev);
|
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_ */
|
#endif /* _CLIXON_BACKEND_HANDLE_H_ */
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -65,10 +65,9 @@
|
||||||
|
|
||||||
/* This is the default callback function. But this is typically overwritten */
|
/* This is the default callback function. But this is typically overwritten */
|
||||||
#define GENERATE_CALLBACK "cli_set"
|
#define GENERATE_CALLBACK "cli_set"
|
||||||
#define GENERATE_CALLBACKV "cli_setv"
|
|
||||||
|
|
||||||
/* variable expand function */
|
/* variable expand function */
|
||||||
#define GENERATE_EXPAND_XMLDB "expandv_dbvar"
|
#define GENERATE_EXPAND_XMLDB "expand_dbvar"
|
||||||
|
|
||||||
/*=====================================================================
|
/*=====================================================================
|
||||||
* YANG generate CLI
|
* YANG generate CLI
|
||||||
|
|
@ -154,10 +153,7 @@ cli_callback_generate(clicon_handle h,
|
||||||
|
|
||||||
if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0)
|
if (yang2xmlkeyfmt(ys, 0, &xkfmt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1)
|
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt);
|
||||||
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACK, xkfmt);
|
|
||||||
else
|
|
||||||
cprintf(cb0, ",%s(\"%s\")", GENERATE_CALLBACKV, xkfmt);
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (xkfmt)
|
if (xkfmt)
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -69,7 +70,7 @@
|
||||||
#include "cli_handle.h"
|
#include "cli_handle.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define CLI_OPTS "hD:f:F:1u:d:m:qpGLl:"
|
#define CLI_OPTS "hD:f:F:1u:d:m:qpGLl:y:"
|
||||||
|
|
||||||
/*! terminate cli application */
|
/*! terminate cli application */
|
||||||
static int
|
static int
|
||||||
|
|
@ -77,10 +78,10 @@ cli_terminate(clicon_handle h)
|
||||||
{
|
{
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
|
||||||
|
clicon_rpc_close_session(h);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
cli_plugin_finish(h);
|
cli_plugin_finish(h);
|
||||||
exit_candidate_db(h);
|
|
||||||
cli_handle_exit(h);
|
cli_handle_exit(h);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -150,7 +151,8 @@ usage(char *argv0, clicon_handle h)
|
||||||
"\t-p \t\tPrint database yang specification\n"
|
"\t-p \t\tPrint database yang specification\n"
|
||||||
"\t-G \t\tPrint CLI syntax generated from dbspec (if CLICON_CLI_GENMODEL enabled)\n"
|
"\t-G \t\tPrint CLI syntax generated from dbspec (if CLICON_CLI_GENMODEL enabled)\n"
|
||||||
"\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
|
"\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
|
||||||
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n",
|
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n"
|
||||||
|
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
|
||||||
argv0,
|
argv0,
|
||||||
confsock ? confsock : "none",
|
confsock ? confsock : "none",
|
||||||
plgdir ? plgdir : "none"
|
plgdir ? plgdir : "none"
|
||||||
|
|
@ -174,7 +176,7 @@ main(int argc, char **argv)
|
||||||
int help = 0;
|
int help = 0;
|
||||||
char *treename;
|
char *treename;
|
||||||
int logdst = CLICON_LOG_STDERR;
|
int logdst = CLICON_LOG_STDERR;
|
||||||
char *restarg; /* what remains after options */
|
char *restarg = NULL; /* what remains after options */
|
||||||
|
|
||||||
/* Defaults */
|
/* Defaults */
|
||||||
|
|
||||||
|
|
@ -288,6 +290,15 @@ main(int argc, char **argv)
|
||||||
case 'L' : /* Debug print dynamic CLI syntax */
|
case 'L' : /* Debug print dynamic CLI syntax */
|
||||||
logclisyntax++;
|
logclisyntax++;
|
||||||
break;
|
break;
|
||||||
|
case 'y' :{ /* yang module */
|
||||||
|
/* Set revision to NULL, extract dir and module */
|
||||||
|
char *str = strdup(optarg);
|
||||||
|
char *dir = dirname(str);
|
||||||
|
hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION");
|
||||||
|
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(optarg));
|
||||||
|
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
usage(argv[0], h);
|
usage(argv[0], h);
|
||||||
break;
|
break;
|
||||||
|
|
@ -359,23 +370,14 @@ main(int argc, char **argv)
|
||||||
goto done;
|
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)
|
if (logclisyntax)
|
||||||
cli_logsyntax_set(h, logclisyntax);
|
cli_logsyntax_set(h, logclisyntax);
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
clicon_option_dump(h, debug);
|
clicon_option_dump(h, debug);
|
||||||
|
|
||||||
|
|
||||||
/* Join rest of argv to a single command */
|
/* Join rest of argv to a single command */
|
||||||
restarg = clicon_strjoin(argc, argv, " ", __FUNCTION__);
|
restarg = clicon_strjoin(argc, argv, " ");
|
||||||
|
|
||||||
/* If several cligen object variables match same preference, select first */
|
/* If several cligen object variables match same preference, select first */
|
||||||
cligen_match_cgvar_same(1);
|
cligen_match_cgvar_same(1);
|
||||||
|
|
@ -398,6 +400,8 @@ main(int argc, char **argv)
|
||||||
if (!once)
|
if (!once)
|
||||||
cli_interactive(h);
|
cli_interactive(h);
|
||||||
done:
|
done:
|
||||||
|
if (restarg)
|
||||||
|
free(restarg);
|
||||||
unchunk_group(__FUNCTION__);
|
unchunk_group(__FUNCTION__);
|
||||||
// Gets in your face if we log on stderr
|
// Gets in your face if we log on stderr
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||||
|
|
|
||||||
|
|
@ -372,35 +372,21 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolve callback names to function pointers. */
|
/* Resolve callback names to function pointers. */
|
||||||
if (clicon_option_int(h, "CLICON_CLIGEN_CALLBACK_SINGLE_ARG")==1){
|
if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){
|
||||||
if (cligen_callback_str2fn(pt, (cg_str2fn_t*)clixon_str2fn, handle) < 0){
|
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
|
||||||
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
|
filename, plgnam, plgnam);
|
||||||
filename, plgnam, plgnam);
|
goto done;
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (cligen_callbackv_str2fn(pt, (cgv_str2fn_t*)clixon_str2fn, handle) < 0){
|
|
||||||
clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)",
|
|
||||||
filename, plgnam, plgnam);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (clicon_option_int(h, "CLICON_CLIGEN_EXPAND_SINGLE_ARG")==1){
|
|
||||||
if (cligen_expand_str2fn(pt, (expand_str2fn_t*)clixon_str2fn, handle) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
else
|
if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0)
|
||||||
if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0)
|
goto done;
|
||||||
goto done;
|
|
||||||
|
|
||||||
/* Make sure we have a syntax mode specified */
|
/* Make sure we have a syntax mode specified */
|
||||||
if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */
|
if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */
|
||||||
clicon_err(OE_PLUGIN, 0, "No syntax mode specified in %s", filepath);
|
clicon_err(OE_PLUGIN, 0, "No syntax mode specified in %s", filepath);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((vec = clicon_strsplit(mode, ":", &nvec, __FUNCTION__)) == NULL) {
|
if ((vec = clicon_strsep(mode, ":", &nvec)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
for (i = 0; i < nvec; i++) {
|
for (i = 0; i < nvec; i++) {
|
||||||
if (syntax_append(h, cli_syntax(h), vec[i], pt) < 0) {
|
if (syntax_append(h, cli_syntax(h), vec[i], pt) < 0) {
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -415,6 +401,8 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
||||||
done:
|
done:
|
||||||
if (vr)
|
if (vr)
|
||||||
cvec_free(vr);
|
cvec_free(vr);
|
||||||
|
if (vec)
|
||||||
|
free(vec);
|
||||||
unchunk_group(__FUNCTION__);
|
unchunk_group(__FUNCTION__);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
@ -1020,7 +1008,7 @@ cli_ptpush(clicon_handle h, char *mode, char *string, char *op)
|
||||||
return 0;
|
return 0;
|
||||||
pt = &co_cmd->co_pt;
|
pt = &co_cmd->co_pt;
|
||||||
/* vec is the command, eg 'edit policy_option' */
|
/* vec is the command, eg 'edit policy_option' */
|
||||||
if ((vec = clicon_strsplit(string, " ", &nvec, __FUNCTION__)) == NULL)
|
if ((vec = clicon_strsep(string, " ", &nvec)) == NULL)
|
||||||
goto catch;
|
goto catch;
|
||||||
co = NULL;
|
co = NULL;
|
||||||
found = 0;
|
found = 0;
|
||||||
|
|
@ -1050,7 +1038,8 @@ cli_ptpush(clicon_handle h, char *mode, char *string, char *op)
|
||||||
co_up_set(cc, co_cmd);
|
co_up_set(cc, co_cmd);
|
||||||
}
|
}
|
||||||
catch:
|
catch:
|
||||||
unchunk_group(__FUNCTION__) ;
|
if (vec)
|
||||||
|
free(vec);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -68,70 +68,82 @@ int cli_handle_exit(clicon_handle h);
|
||||||
cligen_handle cli_cligen(clicon_handle h);
|
cligen_handle cli_cligen(clicon_handle h);
|
||||||
|
|
||||||
/* cli_common.c */
|
/* 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,
|
int cli_notification_register(clicon_handle h, char *stream, enum format_enum format,
|
||||||
char *filter, int status,
|
char *filter, int status,
|
||||||
int (*fn)(int, void*), void *arg);
|
int (*fn)(int, void*), void *arg);
|
||||||
|
|
||||||
#define cli_output cligen_output
|
#define cli_output cligen_output
|
||||||
/* cli_common.c: CLIgen new vector callbacks */
|
/* cli_common.c: CLIgen new vector callbacks */
|
||||||
|
|
||||||
|
|
||||||
|
int cli_set(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_merge(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_del(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_start_shell(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_quit(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_commit(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_validate(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv);
|
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int compare_dbs(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv);
|
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int load_config_file(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv);
|
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int save_config_file(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
|
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int delete_all(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
|
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int discard_changes(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
|
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int cli_notify(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv);
|
int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
|
|
||||||
/* cli_common.c: CLIgen old single arg callbacks */
|
int db_copy(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int cli_set(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int cli_merge(clicon_handle h, cvec *vars, cg_var *arg);
|
int cli_lock(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int cli_del(clicon_handle h, cvec *vars, cg_var *arg);
|
int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int cli_debug_cli(clicon_handle h, cvec *vars, cg_var *arg);
|
int cli_copy_config(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int cli_debug_backend(clicon_handle h, cvec *vars, cg_var *argv);
|
|
||||||
int cli_set_mode(clicon_handle h, cvec *vars, cg_var *argv);
|
|
||||||
int cli_start_shell(clicon_handle h, cvec *vars, cg_var *argv);
|
|
||||||
int cli_quit(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int cli_commit(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int cli_validate(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int compare_dbs(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int load_config_file(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int save_config_file(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int delete_all(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int discard_changes(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int cli_notify(clicon_handle h, cvec *cvv, cg_var *arg);
|
|
||||||
|
|
||||||
/* In cli_show.c */
|
/* In cli_show.c */
|
||||||
int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail);
|
int expand_dir(char *dir, int *nr, char ***commands, mode_t flags, int detail);
|
||||||
|
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
||||||
|
cvec *commands, cvec *helptexts);
|
||||||
int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
|
||||||
cvec *commands, cvec *helptexts);
|
cvec *commands, cvec *helptexts);
|
||||||
|
|
||||||
/* cli_show.c: CLIgen new vector arg callbacks */
|
/* cli_show.c: CLIgen new vector arg callbacks */
|
||||||
int show_confv_as_xml(clicon_handle h, cvec *vars, cvec *argv);
|
int show_yang(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
int show_confv_as_netconf(clicon_handle h, cvec *vars, cvec *argv);
|
|
||||||
int show_confv_as_json(clicon_handle h, cvec *vars, cvec *argv);
|
|
||||||
int show_confv_as_text(clicon_handle h, cvec *vars, cvec *argv);
|
|
||||||
int show_confv_as_cli(clicon_handle h, cvec *vars, cvec *argv);
|
|
||||||
int show_confv_as_csv(clicon_handle h, cvec *vars, cvec *argv);
|
|
||||||
int show_yangv(clicon_handle h, cvec *vars, cvec *argv);
|
int show_yangv(clicon_handle h, cvec *vars, cvec *argv);
|
||||||
|
|
||||||
|
int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv);
|
int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
|
|
||||||
/* cli_show.c: CLIgen old single arg callbacks */
|
int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv);
|
||||||
int show_conf_as_xml(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int show_conf_as_netconf(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int show_conf_as_json(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int show_conf_as_text(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int show_conf_as_cli(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int show_conf_as_csv(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
int show_yang(clicon_handle h, cvec *vars, cg_var *arg);
|
|
||||||
|
|
||||||
#endif /* _CLIXON_CLI_API_H_ */
|
#endif /* _CLIXON_CLI_API_H_ */
|
||||||
|
|
|
||||||
|
|
@ -209,21 +209,28 @@ main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
if (dumpdb){
|
if (dumpdb){
|
||||||
/* Here db must be local file-path */
|
/* 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");
|
fprintf(stderr, "Match error\n");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (addent) /* add entry */
|
if (addent){ /* add entry */
|
||||||
if (xmldb_put_xkey(h, db, addstr, NULL, OP_REPLACE) < 0)
|
cxobj *xml = NULL;
|
||||||
|
|
||||||
|
if (clicon_xml_parse(&xml, "<config>%s</config>", addstr) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (xmldb_put(h, db, OP_REPLACE, NULL, xml) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml)
|
||||||
|
xml_free(xml);
|
||||||
|
|
||||||
|
}
|
||||||
if (rment)
|
if (rment)
|
||||||
if (remove_entry(db, rmkey) < 0)
|
if (remove_entry(db, rmkey) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (zapdb) /* remove databases */
|
if (zapdb) /* remove databases */
|
||||||
/* XXX This assumes direct access to database */
|
|
||||||
if (xmldb_delete(h, db) < 0){
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
if (initdb)
|
if (initdb)
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,9 @@
|
||||||
*/
|
*/
|
||||||
typedef int (*netconf_cb_t)(
|
typedef int (*netconf_cb_t)(
|
||||||
clicon_handle h,
|
clicon_handle h,
|
||||||
cxobj *xorig, /* Original request. */
|
cxobj *xn, /* Request: <rpc><xn></rpc> */
|
||||||
cxobj *xn, /* Sub-tree (under xorig) at child: <rpc><xn></rpc> */
|
cxobj **xret, /* Return xml tree, eg <rpc-reply>... */
|
||||||
cbuf *cb, /* Output xml stream. For reply */
|
void *arg /* Argument given at netconf_register_callback() */
|
||||||
cbuf *cb_err, /* Error xml stream. For error 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_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,
|
int netconf_register_callback(clicon_handle h,
|
||||||
netconf_cb_t cb, /* Callback called */
|
netconf_cb_t cb, /* Callback called */
|
||||||
void *arg, /* Arg to send to callback */
|
void *arg, /* Arg to send to callback */
|
||||||
char *tag); /* Xml tag when callback is made */
|
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,
|
int netconf_xpath(cxobj *xsearch,
|
||||||
cxobj *xfilter,
|
cxobj *xfilter,
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,7 @@
|
||||||
#include "netconf_lib.h"
|
#include "netconf_lib.h"
|
||||||
#include "netconf_filter.h"
|
#include "netconf_filter.h"
|
||||||
|
|
||||||
/*
|
/* xf specifices a filter, and xn is an xml tree.
|
||||||
* xml_filter
|
|
||||||
* xf specifices a filter, and xn is an xml tree.
|
|
||||||
* Select the part of xn that matches xf and return it.
|
* Select the part of xn that matches xf and return it.
|
||||||
* Change xn destructively by removing the parts of the sub-tree that does
|
* Change xn destructively by removing the parts of the sub-tree that does
|
||||||
* not match.
|
* not match.
|
||||||
|
|
@ -107,9 +105,9 @@ leafstring(cxobj *x)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml_filter2(cxobj *xfilter,
|
xml_filter_recursive(cxobj *xfilter,
|
||||||
cxobj *xparent,
|
cxobj *xparent,
|
||||||
int *remove_me)
|
int *remove_me)
|
||||||
{
|
{
|
||||||
cxobj *s;
|
cxobj *s;
|
||||||
cxobj *sprev;
|
cxobj *sprev;
|
||||||
|
|
@ -176,7 +174,7 @@ xml_filter2(cxobj *xfilter,
|
||||||
}
|
}
|
||||||
// XXX: s can be removed itself in the recursive call !
|
// XXX: s can be removed itself in the recursive call !
|
||||||
remove_s = 0;
|
remove_s = 0;
|
||||||
if (xml_filter2(f, s, &remove_s) < 0)
|
if (xml_filter_recursive(f, s, &remove_s) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (remove_s){
|
if (remove_s){
|
||||||
xml_purge(s);
|
xml_purge(s);
|
||||||
|
|
@ -207,9 +205,9 @@ xml_filter(cxobj *xfilter,
|
||||||
int remove_s;
|
int remove_s;
|
||||||
|
|
||||||
/* Call recursive variant */
|
/* Call recursive variant */
|
||||||
retval = xml_filter2(xfilter,
|
retval = xml_filter_recursive(xfilter,
|
||||||
xconfig,
|
xconfig,
|
||||||
&remove_s);
|
&remove_s);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,22 +68,8 @@
|
||||||
/*
|
/*
|
||||||
* Exported variables
|
* Exported variables
|
||||||
*/
|
*/
|
||||||
enum transport_type transport = NETCONF_SSH;
|
enum transport_type transport = NETCONF_SSH; /* XXX Remove SOAP support */
|
||||||
int cc_closed = 0;
|
int cc_closed = 0; /* XXX Please remove (or at least hide in handle) this global variable */
|
||||||
|
|
||||||
static int cc_ok = 0;
|
|
||||||
|
|
||||||
void
|
|
||||||
netconf_ok_set(int ok)
|
|
||||||
{
|
|
||||||
cc_ok = ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
netconf_ok_get(void)
|
|
||||||
{
|
|
||||||
return cc_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
add_preamble(cbuf *xf)
|
add_preamble(cbuf *xf)
|
||||||
|
|
@ -161,38 +147,13 @@ add_error_postamble(cbuf *xf)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Look for a text pattern in an input string, one char at a time
|
|
||||||
* @param[in] tag What to look for
|
|
||||||
* @param[in] ch New input character
|
|
||||||
* @param[in,out] state A state integer holding how far we have parsed.
|
|
||||||
* @retval 0 No, we havent detected end tag
|
|
||||||
* @retval 1 Yes, we have detected end tag!
|
|
||||||
* XXX: move to clicon_xml?
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
detect_endtag(char *tag, char ch, int *state)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
|
|
||||||
if (tag[*state] == ch){
|
|
||||||
(*state)++;
|
|
||||||
if (*state == strlen(tag)){
|
|
||||||
*state = 0;
|
|
||||||
retval = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*state = 0;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Get "target" attribute, return actual database given candidate or running
|
/*! Get "target" attribute, return actual database given candidate or running
|
||||||
* Caller must do error handling
|
* Caller must do error handling
|
||||||
* @retval dbname Actual database file name
|
* @retval dbname Actual database file name
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
netconf_get_target(clicon_handle h,
|
netconf_get_target(cxobj *xn,
|
||||||
cxobj *xn,
|
|
||||||
char *path)
|
char *path)
|
||||||
{
|
{
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
|
|
@ -218,11 +179,13 @@ netconf_get_target(clicon_handle h,
|
||||||
* @param[in] msg Only for debug
|
* @param[in] msg Only for debug
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
netconf_output(int s, cbuf *xf, char *msg)
|
netconf_output(int s,
|
||||||
|
cbuf *xf,
|
||||||
|
char *msg)
|
||||||
{
|
{
|
||||||
char *buf = cbuf_get(xf);
|
char *buf = cbuf_get(xf);
|
||||||
int len = cbuf_len(xf);
|
int len = cbuf_len(xf);
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
clicon_debug(1, "SEND %s", msg);
|
clicon_debug(1, "SEND %s", msg);
|
||||||
if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */
|
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
|
CONTINUE_ON_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
enum filter_option{ /* get-config/filter */
|
|
||||||
FILTER_SUBTREE,
|
|
||||||
FILTER_XPATH
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Variables
|
* Variables
|
||||||
*/
|
*/
|
||||||
|
|
@ -74,14 +69,10 @@ extern int cc_closed;
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
void netconf_ok_set(int ok);
|
|
||||||
int netconf_ok_get(void);
|
|
||||||
|
|
||||||
int add_preamble(cbuf *xf);
|
int add_preamble(cbuf *xf);
|
||||||
int add_postamble(cbuf *xf);
|
int add_postamble(cbuf *xf);
|
||||||
int add_error_preamble(cbuf *xf, char *reason);
|
int add_error_preamble(cbuf *xf, char *reason);
|
||||||
int detect_endtag(char *tag, char ch, int *state);
|
char *netconf_get_target(cxobj *xn, char *path);
|
||||||
char *netconf_get_target(clicon_handle h, cxobj *xn, char *path);
|
|
||||||
int add_error_postamble(cbuf *xf);
|
int add_error_postamble(cbuf *xf);
|
||||||
int netconf_output(int s, cbuf *xf, char *msg);
|
int netconf_output(int s, cbuf *xf, char *msg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -70,7 +71,7 @@
|
||||||
#include "netconf_rpc.h"
|
#include "netconf_rpc.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define NETCONF_OPTS "hDqf:d:S"
|
#define NETCONF_OPTS "hDqf:d:Sy:"
|
||||||
|
|
||||||
/*! Process incoming packet
|
/*! Process incoming packet
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
|
@ -82,11 +83,12 @@ process_incoming_packet(clicon_handle h,
|
||||||
{
|
{
|
||||||
char *str;
|
char *str;
|
||||||
char *str0;
|
char *str0;
|
||||||
cxobj *xml_req = NULL; /* Request (in) */
|
cxobj *xreq = NULL; /* Request (in) */
|
||||||
int isrpc = 0; /* either hello or rpc */
|
int isrpc = 0; /* either hello or rpc */
|
||||||
cbuf *xf_out;
|
cbuf *cbret = NULL;
|
||||||
cbuf *xf_err;
|
cxobj *xret = NULL; /* Return (out) */
|
||||||
cbuf *xf1;
|
cxobj *xrpc;
|
||||||
|
cxobj *xc;
|
||||||
|
|
||||||
clicon_debug(1, "RECV");
|
clicon_debug(1, "RECV");
|
||||||
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
|
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(cb));
|
||||||
|
|
@ -96,16 +98,15 @@ process_incoming_packet(clicon_handle h,
|
||||||
}
|
}
|
||||||
str = str0;
|
str = str0;
|
||||||
/* Parse incoming XML message */
|
/* Parse incoming XML message */
|
||||||
if (clicon_xml_parse_string(&str, &xml_req) < 0){
|
if (clicon_xml_parse_string(&str, &xreq) < 0){
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cbret = cbuf_new()) == NULL){
|
||||||
netconf_create_rpc_error(cb, NULL,
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"operation-failed",
|
"<error-tag>operation-failed</error-tag>"
|
||||||
"rpc", "error",
|
"<error-type>rpc</error-type>"
|
||||||
NULL,
|
"<error-severity>error</error-severity>"
|
||||||
NULL);
|
"<error-message>internal error</error-message>"
|
||||||
|
"</rpc-error></rpc-reply>");
|
||||||
netconf_output(1, cb, "rpc-error");
|
netconf_output(1, cb, "rpc-error");
|
||||||
cbuf_free(cb);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
||||||
|
|
@ -113,72 +114,56 @@ process_incoming_packet(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
free(str0);
|
free(str0);
|
||||||
if (xpath_first(xml_req, "//rpc") != NULL){
|
if ((xrpc=xpath_first(xreq, "//rpc")) != NULL){
|
||||||
isrpc++;
|
isrpc++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
if (xpath_first(xml_req, "//hello") != NULL)
|
if (xpath_first(xreq, "//hello") != NULL)
|
||||||
;
|
;
|
||||||
else{
|
else{
|
||||||
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\
|
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\
|
||||||
ed");
|
ed");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Initialize response buffers */
|
if (!isrpc){ /* hello */
|
||||||
if ((xf_out = cbuf_new()) == NULL){
|
if (netconf_hello_dispatch(xreq) < 0)
|
||||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
goto done;
|
||||||
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 ((cbret = cbuf_new()) != NULL){
|
||||||
if ((xf_err = cbuf_new()) == NULL){
|
if ((xc = xml_child_i(xret,0))!=NULL){
|
||||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
xa=NULL;
|
||||||
goto done;
|
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
|
||||||
}
|
if ((xa2 = xml_dup(xa)) ==NULL)
|
||||||
netconf_ok_set(0);
|
goto done;
|
||||||
if (isrpc){
|
if (xml_addsub(xc, xa2) < 0)
|
||||||
if (netconf_rpc_dispatch(h,
|
goto done;
|
||||||
xml_req,
|
}
|
||||||
xpath_first(xml_req, "//rpc"),
|
add_preamble(cbret);
|
||||||
xf_out, xf_err) < 0){
|
|
||||||
assert(cbuf_len(xf_err));
|
clicon_xml2cbuf(cbret, xml_child_i(xret,0), 0, 0);
|
||||||
clicon_debug(1, "%s", cbuf_get(xf_err));
|
add_postamble(cbret);
|
||||||
if (isrpc){
|
if (netconf_output(1, cbret, "rpc-reply") < 0){
|
||||||
if (netconf_output(1, xf_err, "rpc-error") < 0)
|
cbuf_free(cbret);
|
||||||
goto done;
|
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:
|
done:
|
||||||
if (xml_req)
|
if (xreq)
|
||||||
xml_free(xml_req);
|
xml_free(xreq);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
if (cbret)
|
||||||
|
cbuf_free(cbret);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,19 +175,18 @@ static int
|
||||||
netconf_input_cb(int s,
|
netconf_input_cb(int s,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
|
int retval = -1;
|
||||||
clicon_handle h = arg;
|
clicon_handle h = arg;
|
||||||
unsigned char buf[BUFSIZ];
|
unsigned char buf[BUFSIZ];
|
||||||
int i;
|
int i;
|
||||||
int len;
|
int len;
|
||||||
static cbuf *cb; /* XXX: should use ce state? */
|
cbuf *cb=NULL;
|
||||||
int xml_state = 0;
|
int xml_state = 0;
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
if (cb == NULL)
|
if ((cb = cbuf_new()) == NULL){
|
||||||
if ((cb = cbuf_new()) == NULL){
|
clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__);
|
||||||
clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__);
|
return retval;
|
||||||
return retval;
|
}
|
||||||
}
|
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
if ((len = read(s, buf, sizeof(buf))) < 0){
|
if ((len = read(s, buf, sizeof(buf))) < 0){
|
||||||
if (errno == ECONNRESET)
|
if (errno == ECONNRESET)
|
||||||
|
|
@ -237,7 +221,8 @@ netconf_input_cb(int s,
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
// cbuf_free(cb);
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
if (cc_closed)
|
if (cc_closed)
|
||||||
retval = -1;
|
retval = -1;
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -268,30 +253,16 @@ send_hello(int s)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Initialize candidate database */
|
|
||||||
static int
|
static int
|
||||||
init_candidate_db(clicon_handle h)
|
netconf_terminate(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)
|
|
||||||
{
|
{
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
|
||||||
|
|
||||||
|
clicon_rpc_close_session(h);
|
||||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
yspec_free(yspec);
|
yspec_free(yspec);
|
||||||
|
event_exit();
|
||||||
clicon_handle_exit(h);
|
clicon_handle_exit(h);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -314,7 +285,8 @@ usage(clicon_handle h,
|
||||||
"\t-q\t\tQuiet: dont send hello prompt\n"
|
"\t-q\t\tQuiet: dont send hello prompt\n"
|
||||||
"\t-f <file>\tConfiguration file (mandatory)\n"
|
"\t-f <file>\tConfiguration file (mandatory)\n"
|
||||||
"\t-d <dir>\tSpecify netconf plugin directory dir (default: %s)\n"
|
"\t-d <dir>\tSpecify netconf plugin directory dir (default: %s)\n"
|
||||||
"\t-S\t\tLog on syslog\n",
|
"\t-S\t\tLog on syslog\n"
|
||||||
|
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
|
||||||
argv0,
|
argv0,
|
||||||
netconfdir
|
netconfdir
|
||||||
);
|
);
|
||||||
|
|
@ -387,6 +359,15 @@ main(int argc, char **argv)
|
||||||
usage(h, argv[0]);
|
usage(h, argv[0]);
|
||||||
clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg);
|
clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'y' :{ /* yang module */
|
||||||
|
/* Set revision to NULL, extract dir and module */
|
||||||
|
char *str = strdup(optarg);
|
||||||
|
char *dir = dirname(str);
|
||||||
|
hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION");
|
||||||
|
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(optarg));
|
||||||
|
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
usage(h, argv[0]);
|
usage(h, argv[0]);
|
||||||
break;
|
break;
|
||||||
|
|
@ -402,8 +383,6 @@ main(int argc, char **argv)
|
||||||
if (netconf_plugin_load(h) < 0)
|
if (netconf_plugin_load(h) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (init_candidate_db(h) < 0)
|
|
||||||
return -1;
|
|
||||||
/* Call start function is all plugins before we go interactive */
|
/* Call start function is all plugins before we go interactive */
|
||||||
tmp = *(argv-1);
|
tmp = *(argv-1);
|
||||||
*(argv-1) = argv0;
|
*(argv-1) = argv0;
|
||||||
|
|
@ -416,13 +395,11 @@ main(int argc, char **argv)
|
||||||
goto done;
|
goto done;
|
||||||
if (debug)
|
if (debug)
|
||||||
clicon_option_dump(h, debug);
|
clicon_option_dump(h, debug);
|
||||||
|
|
||||||
if (event_loop() < 0)
|
if (event_loop() < 0)
|
||||||
goto done;
|
goto done;
|
||||||
done:
|
done:
|
||||||
|
|
||||||
netconf_plugin_unload(h);
|
netconf_plugin_unload(h);
|
||||||
terminate(h);
|
netconf_terminate(h);
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,15 @@
|
||||||
#include "netconf_lib.h"
|
#include "netconf_lib.h"
|
||||||
#include "netconf_plugin.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
|
/*! Unload a plugin
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -245,10 +254,9 @@ catch:
|
||||||
|
|
||||||
/*! See if there is any callback registered for this tag
|
/*! See if there is any callback registered for this tag
|
||||||
*
|
*
|
||||||
* @param xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
* @param[in] h clicon handle
|
||||||
* @param xf Output xml stream. For reply
|
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||||
* @param xf_err Error xml stream. For error reply
|
* @param[out] xret Return XML, error or OK
|
||||||
* @param xorig Original request.
|
|
||||||
*
|
*
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 OK, not found handler.
|
* @retval 0 OK, not found handler.
|
||||||
|
|
@ -256,31 +264,24 @@ catch:
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
netconf_plugin_callbacks(clicon_handle h,
|
netconf_plugin_callbacks(clicon_handle h,
|
||||||
cxobj *xn,
|
cxobj *xn,
|
||||||
cbuf *xf,
|
cxobj **xret)
|
||||||
cbuf *xf_err,
|
|
||||||
cxobj *xorig)
|
|
||||||
{
|
{
|
||||||
netconf_reg_t *nr;
|
netconf_reg_t *nreg;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (deps == NULL)
|
if (deps == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
nr = deps;
|
nreg = deps;
|
||||||
do {
|
do {
|
||||||
if (strcmp(nr->nr_tag, xml_name(xn)) == 0){
|
if (strcmp(nreg->nr_tag, xml_name(xn)) == 0){
|
||||||
if ((retval = nr->nr_callback(h,
|
if ((retval = nreg->nr_callback(h, xn, xret, nreg->nr_arg)) < 0)
|
||||||
xorig,
|
|
||||||
xn,
|
|
||||||
xf,
|
|
||||||
xf_err,
|
|
||||||
nr->nr_arg)) < 0)
|
|
||||||
return -1;
|
return -1;
|
||||||
else
|
else
|
||||||
return 1; /* handled */
|
return 1; /* handled */
|
||||||
}
|
}
|
||||||
nr = NEXTQ(netconf_reg_t *, nr);
|
nreg = NEXTQ(netconf_reg_t *, nreg);
|
||||||
} while (nr != deps);
|
} while (nreg != deps);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,14 +41,6 @@
|
||||||
* Types
|
* 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
|
* 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_unload(clicon_handle h);
|
||||||
|
|
||||||
|
int netconf_plugin_callbacks(clicon_handle h, cxobj *xn, cxobj **xret);
|
||||||
int netconf_plugin_callbacks(clicon_handle h,
|
|
||||||
// dbspec_key *dbspec,
|
|
||||||
cxobj *xn,
|
|
||||||
cbuf *xf,
|
|
||||||
cbuf *xf_err,
|
|
||||||
cxobj *xt);
|
|
||||||
|
|
||||||
#endif /* _NETCONF_PLUGIN_H_ */
|
#endif /* _NETCONF_PLUGIN_H_ */
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -42,20 +42,7 @@
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
netconf_rpc_dispatch(clicon_handle h,
|
netconf_rpc_dispatch(clicon_handle h,
|
||||||
cxobj *xorig,
|
cxobj *xn,
|
||||||
cxobj *xn,
|
cxobj **xret);
|
||||||
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);
|
|
||||||
|
|
||||||
#endif /* _NETCONF_RPC_H_ */
|
#endif /* _NETCONF_RPC_H_ */
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ CPPFLAGS = @CPPFLAGS@
|
||||||
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||||
|
|
||||||
SRC = restconf_lib.c
|
SRC = restconf_lib.c
|
||||||
|
SRC += restconf_methods.c
|
||||||
|
|
||||||
OBJS = $(SRC:.c=.o)
|
OBJS = $(SRC:.c=.o)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,26 @@
|
||||||
|
Clixon Restconf
|
||||||
|
===============
|
||||||
|
|
||||||
|
Contents:
|
||||||
|
1. Features
|
||||||
|
2. Installation using NGINX
|
||||||
|
3. Debugging
|
||||||
|
|
||||||
|
1. FEATURES
|
||||||
|
+++++++++++
|
||||||
|
Clixon restconf is a daemon based on FASTCGI. Instructions are available to
|
||||||
|
run with NGINX.
|
||||||
|
The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE.
|
||||||
|
and is based on draft-ietf-netconf-restconf-13.
|
||||||
|
There is currently (2017) a RFC 8040, many of those features are _not_ implemented,
|
||||||
|
including:
|
||||||
|
- query parameters (section 4.9)
|
||||||
|
- notifications (sec 6)
|
||||||
|
- only rudimentary error reporting exists (sec 7)
|
||||||
|
|
||||||
|
2. INSTALLATION using NGINX
|
||||||
|
+++++++++++++++++++++++++++
|
||||||
|
|
||||||
# Existing clixon installation. Using CLI:
|
|
||||||
olof@vandal> clixon_cli -f /usr/local/etc/routing.conf
|
|
||||||
olof@vandal> show configuration
|
|
||||||
interfaces {
|
|
||||||
interface {
|
|
||||||
name eth0;
|
|
||||||
type eth;
|
|
||||||
enabled true;
|
|
||||||
}
|
|
||||||
interface {
|
|
||||||
name eth9;
|
|
||||||
type eth;
|
|
||||||
enabled true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
# Define nginx config file/etc/nginx/sites-available/default
|
# Define nginx config file/etc/nginx/sites-available/default
|
||||||
server {
|
server {
|
||||||
...
|
...
|
||||||
|
|
@ -54,8 +61,11 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
Debugging
|
curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
|
||||||
---------
|
|
||||||
|
|
||||||
|
3. DEBUGGING
|
||||||
|
++++++++++++
|
||||||
Start the restconf programs with debug flag:
|
Start the restconf programs with debug flag:
|
||||||
sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data
|
sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,39 @@
|
||||||
/*
|
/*
|
||||||
|
*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||||
|
|
||||||
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
Alternatively, the contents of this file may be used under the terms of
|
||||||
|
the GNU General Public License Version 3 or later (the "GPL"),
|
||||||
|
in which case the provisions of the GPL are applicable instead
|
||||||
|
of those above. If you wish to allow use of your version of this file only
|
||||||
|
under the terms of the GPL, and not to allow others to
|
||||||
|
use your version of this file under the terms of Apache License version 2,
|
||||||
|
indicate your decision by deleting the provisions above and replace them with
|
||||||
|
the notice and other provisions required by the GPL. If you do not delete
|
||||||
|
the provisions above, a recipient may use your version of this file under
|
||||||
|
the terms of any one of the Apache License version 2 or the GPL.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,36 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* $COPYRIGHTSTATEMENT$
|
***** BEGIN LICENSE BLOCK *****
|
||||||
*
|
|
||||||
* $LICENSE$
|
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||||
* This is backend headend sender code, ie communication with a pmagent
|
|
||||||
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
Alternatively, the contents of this file may be used under the terms of
|
||||||
|
the GNU General Public License Version 3 or later (the "GPL"),
|
||||||
|
in which case the provisions of the GPL are applicable instead
|
||||||
|
of those above. If you wish to allow use of your version of this file only
|
||||||
|
under the terms of the GPL, and not to allow others to
|
||||||
|
use your version of this file under the terms of Apache License version 2,
|
||||||
|
indicate your decision by deleting the provisions above and replace them with
|
||||||
|
the notice and other provisions required by the GPL. If you do not delete
|
||||||
|
the provisions above, a recipient may use your version of this file under
|
||||||
|
the terms of any one of the Apache License version 2 or the GPL.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _RESTCONF_LIB_H_
|
#ifndef _RESTCONF_LIB_H_
|
||||||
|
|
@ -12,13 +39,6 @@
|
||||||
/*
|
/*
|
||||||
* Constants
|
* Constants
|
||||||
*/
|
*/
|
||||||
#define USER_COOKIE "c-user" /* connected user cookie */
|
|
||||||
#define WWW_USER "root"
|
|
||||||
#define WWW_PASSWD "9rundpaj" // XXX
|
|
||||||
#define DEFAULT_TEMPLATE "nordunet" /* XXX Default sender template must be in conf */
|
|
||||||
#define CONFIG_FILE "/usr/local/etc/grideye.conf"
|
|
||||||
#define NETCONF_BIN "/usr/local/bin/clixon_netconf"
|
|
||||||
#define NETCONF_OPTS "-qS"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -66,302 +67,17 @@
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include <clixon/clixon.h>
|
#include <clixon/clixon.h>
|
||||||
|
|
||||||
|
/* restconf */
|
||||||
#include "restconf_lib.h"
|
#include "restconf_lib.h"
|
||||||
|
#include "restconf_methods.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define RESTCONF_OPTS "hDf:p:"
|
#define RESTCONF_OPTS "hDf:p:y:"
|
||||||
|
|
||||||
/* Should be discovered via "/.well-known/host-meta"
|
/* Should be discovered via "/.well-known/host-meta"
|
||||||
resource ([RFC6415]) */
|
resource ([RFC6415]) */
|
||||||
#define RESTCONF_API_ROOT "/restconf/"
|
#define RESTCONF_API_ROOT "/restconf/"
|
||||||
|
|
||||||
/*! REST OPTIONS method
|
|
||||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
|
||||||
* @param[in] h Clixon handle
|
|
||||||
* @param[in] r Fastcgi request handle
|
|
||||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
|
||||||
* @param[in] pi Offset, where path starts
|
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
|
||||||
* @param[in] head Set if HEAD request instead of GET
|
|
||||||
* @code
|
|
||||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
|
||||||
* @endcode
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
api_data_options(clicon_handle h,
|
|
||||||
FCGX_Request *r,
|
|
||||||
cvec *pcvec,
|
|
||||||
int pi,
|
|
||||||
cvec *qvec,
|
|
||||||
int head)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "GET, HEAD, OPTIONS, PUT, POST, DELETE\r\n");
|
|
||||||
retval = 0;
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Generic REST GET method
|
|
||||||
* According to restconf (Sec 3.5.1.1 in [draft])
|
|
||||||
* @param[in] h Clixon handle
|
|
||||||
* @param[in] r Fastcgi request handle
|
|
||||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
|
||||||
* @param[in] pi Offset, where path starts
|
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
|
||||||
* @code
|
|
||||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
|
||||||
* @endcode
|
|
||||||
* XXX: cant find a way to use Accept request field to choose Content-Type
|
|
||||||
* I would like to support both xml and json.
|
|
||||||
* Request may contain
|
|
||||||
* Accept: application/yang.data+json,application/yang.data+xml
|
|
||||||
* Response contains one of:
|
|
||||||
* Content-Type: application/yang.data+xml
|
|
||||||
* Content-Type: application/yang.data+json
|
|
||||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
|
||||||
* list or list object identifies more than one instance, and XML
|
|
||||||
* encoding is used in the response, then an error response containing a
|
|
||||||
* "400 Bad Request" status-line MUST be returned by the server.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
api_data_get(clicon_handle h,
|
|
||||||
FCGX_Request *r,
|
|
||||||
cvec *pcvec,
|
|
||||||
int pi,
|
|
||||||
cvec *qvec)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
cg_var *cv;
|
|
||||||
char *val;
|
|
||||||
char *v;
|
|
||||||
int i;
|
|
||||||
cbuf *path = NULL;
|
|
||||||
cbuf *path1 = NULL;
|
|
||||||
cxobj *xt = NULL;
|
|
||||||
cbuf *cbx = NULL;
|
|
||||||
cxobj **vec = NULL;
|
|
||||||
size_t veclen;
|
|
||||||
yang_spec *yspec;
|
|
||||||
yang_stmt *y;
|
|
||||||
yang_stmt *ykey;
|
|
||||||
char *name;
|
|
||||||
cvec *cvk = NULL; /* vector of index keys */
|
|
||||||
cg_var *cvi;
|
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
|
||||||
yspec = clicon_dbspec_yang(h);
|
|
||||||
if ((path = cbuf_new()) == NULL)
|
|
||||||
goto done;
|
|
||||||
if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */
|
|
||||||
goto done;
|
|
||||||
cv = NULL;
|
|
||||||
cprintf(path1, "/");
|
|
||||||
/* translate eg a/b=c -> a/[b=c] */
|
|
||||||
for (i=pi; i<cvec_len(pcvec); i++){
|
|
||||||
cv = cvec_i(pcvec, i);
|
|
||||||
name = cv_name_get(cv);
|
|
||||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
|
||||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
|
||||||
if (i == pi){
|
|
||||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Check if has value, means '=' */
|
|
||||||
if (cv2str(cv, NULL, 0) > 0){
|
|
||||||
if ((val = cv2str_dup(cv)) == NULL)
|
|
||||||
goto done;
|
|
||||||
v = val;
|
|
||||||
/* XXX sync with yang */
|
|
||||||
while((v=index(v, ',')) != NULL){
|
|
||||||
*v = '\0';
|
|
||||||
v++;
|
|
||||||
}
|
|
||||||
/* Find keys */
|
|
||||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
|
||||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
|
||||||
__FUNCTION__, y->ys_argument);
|
|
||||||
notfound(r);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
|
||||||
|
|
||||||
/* The value is a list of keys: <key>[ <key>]* */
|
|
||||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
|
||||||
goto done;
|
|
||||||
cvi = NULL;
|
|
||||||
/* Iterate over individual yang keys */
|
|
||||||
cprintf(path, "/%s", name);
|
|
||||||
v = val;
|
|
||||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
|
||||||
cprintf(path, "[%s=%s]", cv_string_get(cvi), v);
|
|
||||||
v += strlen(v)+1;
|
|
||||||
}
|
|
||||||
if (val)
|
|
||||||
free(val);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
cprintf(path, "%s%s", (i==pi?"":"/"), name);
|
|
||||||
cprintf(path1, "/%s", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
|
||||||
if (xmldb_get(h, "running", cbuf_get(path), &xt, &vec, &veclen) < 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
|
||||||
goto done;
|
|
||||||
if (veclen==0){
|
|
||||||
notfound(r);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
|
||||||
if (xml2json_cbuf_vec(cbx, vec, veclen, 0) < 0)
|
|
||||||
goto done;
|
|
||||||
FCGX_FPrintF(r->out, "%s", cbuf_get(cbx));
|
|
||||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
if (cbx)
|
|
||||||
cbuf_free(cbx);
|
|
||||||
if (xt)
|
|
||||||
xml_free(xt);
|
|
||||||
if (path)
|
|
||||||
cbuf_free(path);
|
|
||||||
if (path1)
|
|
||||||
cbuf_free(path1);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Generic REST DELETE method
|
|
||||||
* @param[in] h CLIXON handle
|
|
||||||
* @param[in] r Fastcgi request handle
|
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
|
||||||
* @param[in] pi Offset, where path starts
|
|
||||||
* Example:
|
|
||||||
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
api_data_delete(clicon_handle h,
|
|
||||||
FCGX_Request *r,
|
|
||||||
char *api_path,
|
|
||||||
int pi)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
|
||||||
for (i=0; 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)
|
|
||||||
goto done;
|
|
||||||
if (clicon_rpc_commit(h, "candidate", "running") < 0)
|
|
||||||
goto done;
|
|
||||||
FCGX_SetExitStatus(201, r->out);
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Generic REST PUT method
|
|
||||||
* @param[in] h CLIXON handle
|
|
||||||
* @param[in] r Fastcgi request handle
|
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
|
||||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
|
||||||
* @param[in] pi Offset, where to start pcvec
|
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
|
||||||
* @param[in] dvec Stream input data
|
|
||||||
* @param[in] post POST instead of PUT
|
|
||||||
* Example:
|
|
||||||
curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
|
||||||
*
|
|
||||||
PUT:
|
|
||||||
if the PUT request creates a new resource,
|
|
||||||
a "201 Created" status-line is returned. If an existing resource is
|
|
||||||
modified, a "204 No Content" status-line is returned.
|
|
||||||
|
|
||||||
POST:
|
|
||||||
If the POST method succeeds, a "201 Created" status-line is returned
|
|
||||||
and there is no response message-body. A "Location" header
|
|
||||||
identifying the child resource that was created MUST be present in
|
|
||||||
the response in this case.
|
|
||||||
|
|
||||||
If the data resource already exists, then the POST request MUST fail
|
|
||||||
and a "409 Conflict" status-line MUST be returned.
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
api_data_put(clicon_handle h,
|
|
||||||
FCGX_Request *r,
|
|
||||||
char *api_path,
|
|
||||||
cvec *pcvec,
|
|
||||||
int pi,
|
|
||||||
cvec *qvec,
|
|
||||||
char *data,
|
|
||||||
int post)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
int i;
|
|
||||||
cxobj *xdata = NULL;
|
|
||||||
cbuf *cbx = NULL;
|
|
||||||
cxobj *x;
|
|
||||||
|
|
||||||
clicon_debug(1, "%s api_path:%s json:%s",
|
|
||||||
__FUNCTION__,
|
|
||||||
api_path, data);
|
|
||||||
for (i=0; i<pi; i++)
|
|
||||||
api_path = index(api_path+1, '/');
|
|
||||||
/* Parse input data as json into xml */
|
|
||||||
if (json_parse_str(data, &xdata) < 0){
|
|
||||||
clicon_debug(1, "%s json fail", __FUNCTION__);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
|
||||||
goto done;
|
|
||||||
x = 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)
|
|
||||||
goto done;
|
|
||||||
if (clicon_rpc_commit(h, "candidate", "running") < 0)
|
|
||||||
goto done;
|
|
||||||
FCGX_SetExitStatus(201, r->out); /* Created */
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
|
||||||
if (xdata)
|
|
||||||
xml_free(xdata);
|
|
||||||
if (cbx)
|
|
||||||
cbuf_free(cbx);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Generic REST method, GET, PUT, DELETE
|
/*! Generic REST method, GET, PUT, DELETE
|
||||||
* @param[in] h CLIXON handle
|
* @param[in] h CLIXON handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] r Fastcgi request handle
|
||||||
|
|
@ -385,14 +101,19 @@ api_data(clicon_handle h,
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||||
|
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||||
if (strcmp(request_method, "OPTIONS")==0)
|
if (strcmp(request_method, "OPTIONS")==0)
|
||||||
retval = api_data_options(h, r, pcvec, pi, qvec, 0);
|
retval = api_data_options(h, r);
|
||||||
|
else if (strcmp(request_method, "HEAD")==0)
|
||||||
|
retval = api_data_head(h, r, pcvec, pi, qvec);
|
||||||
else if (strcmp(request_method, "GET")==0)
|
else if (strcmp(request_method, "GET")==0)
|
||||||
retval = api_data_get(h, r, pcvec, pi, qvec);
|
retval = api_data_get(h, r, pcvec, pi, qvec);
|
||||||
else if (strcmp(request_method, "PUT")==0)
|
|
||||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 0);
|
|
||||||
else if (strcmp(request_method, "POST")==0)
|
else if (strcmp(request_method, "POST")==0)
|
||||||
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, 1);
|
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data);
|
||||||
|
else if (strcmp(request_method, "PUT")==0)
|
||||||
|
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data);
|
||||||
|
else if (strcmp(request_method, "PATCH")==0)
|
||||||
|
retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data);
|
||||||
else if (strcmp(request_method, "DELETE")==0)
|
else if (strcmp(request_method, "DELETE")==0)
|
||||||
retval = api_data_delete(h, r, api_path, pi);
|
retval = api_data_delete(h, r, api_path, pi);
|
||||||
else
|
else
|
||||||
|
|
@ -411,7 +132,7 @@ request_process(clicon_handle h,
|
||||||
char *path;
|
char *path;
|
||||||
char *query;
|
char *query;
|
||||||
char *method;
|
char *method;
|
||||||
char **pvec;
|
char **pvec = NULL;
|
||||||
int pn;
|
int pn;
|
||||||
cvec *qvec = NULL;
|
cvec *qvec = NULL;
|
||||||
cvec *dvec = NULL;
|
cvec *dvec = NULL;
|
||||||
|
|
@ -423,7 +144,7 @@ request_process(clicon_handle h,
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||||
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
||||||
if ((pvec = clicon_strsplit(path, "/", &pn, __FUNCTION__)) == NULL)
|
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if (str2cvec(query, '&', '=', &qvec) < 0)
|
if (str2cvec(query, '&', '=', &qvec) < 0)
|
||||||
|
|
@ -437,7 +158,12 @@ request_process(clicon_handle h,
|
||||||
clicon_debug(1, "DATA=%s", data);
|
clicon_debug(1, "DATA=%s", data);
|
||||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
method = pvec[2];
|
|
||||||
|
if ((method = pvec[2]) == NULL){
|
||||||
|
retval = notfound(r);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
test(r, 1);
|
test(r, 1);
|
||||||
/* If present, check credentials */
|
/* If present, check credentials */
|
||||||
|
|
@ -457,6 +183,8 @@ request_process(clicon_handle h,
|
||||||
retval = notfound(r);
|
retval = notfound(r);
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (pvec)
|
||||||
|
free(pvec);
|
||||||
if (dvec)
|
if (dvec)
|
||||||
cvec_free(dvec);
|
cvec_free(dvec);
|
||||||
if (qvec)
|
if (qvec)
|
||||||
|
|
@ -465,10 +193,43 @@ request_process(clicon_handle h,
|
||||||
cvec_free(pcvec);
|
cvec_free(pcvec);
|
||||||
if (cb)
|
if (cb)
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
restconf_terminate(clicon_handle h)
|
||||||
|
{
|
||||||
|
yang_spec *yspec;
|
||||||
|
|
||||||
|
clicon_debug(0, "%s", __FUNCTION__);
|
||||||
|
clicon_rpc_close_session(h);
|
||||||
|
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||||
|
yspec_free(yspec);
|
||||||
|
clicon_handle_exit(h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need global variable to for signal handler */
|
||||||
|
static clicon_handle _CLICON_HANDLE = NULL;
|
||||||
|
|
||||||
|
/*! Signall terminates process
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
restconf_sig_term(int arg)
|
||||||
|
{
|
||||||
|
static int i=0;
|
||||||
|
|
||||||
|
if (i++ == 0)
|
||||||
|
clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
|
||||||
|
__PROGRAM__, __FUNCTION__, getpid(), arg);
|
||||||
|
else
|
||||||
|
exit(-1);
|
||||||
|
if (_CLICON_HANDLE)
|
||||||
|
restconf_terminate(_CLICON_HANDLE);
|
||||||
|
clicon_exit_set(); /* checked in event_loop() */
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
/*! Usage help routine
|
/*! Usage help routine
|
||||||
* @param[in] argv0 command line
|
* @param[in] argv0 command line
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
|
@ -485,7 +246,8 @@ usage(clicon_handle h,
|
||||||
"\t-h \t\tHelp\n"
|
"\t-h \t\tHelp\n"
|
||||||
"\t-D \t\tDebug. Log to syslog\n"
|
"\t-D \t\tDebug. Log to syslog\n"
|
||||||
"\t-f <file>\tConfiguration file (mandatory)\n"
|
"\t-f <file>\tConfiguration file (mandatory)\n"
|
||||||
"\t-d <dir>\tSpecify restconf plugin directory dir (default: %s)\n",
|
"\t-d <dir>\tSpecify restconf plugin directory dir (default: %s)\n"
|
||||||
|
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
|
||||||
argv0,
|
argv0,
|
||||||
restconfdir
|
restconfdir
|
||||||
);
|
);
|
||||||
|
|
@ -506,13 +268,14 @@ main(int argc,
|
||||||
char *sockpath;
|
char *sockpath;
|
||||||
char *path;
|
char *path;
|
||||||
clicon_handle h;
|
clicon_handle h;
|
||||||
|
char *yangspec=NULL;
|
||||||
|
|
||||||
/* In the startup, logs to stderr & debug flag set later */
|
/* In the startup, logs to stderr & debug flag set later */
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_SYSLOG);
|
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_SYSLOG);
|
||||||
/* Create handle */
|
/* Create handle */
|
||||||
if ((h = clicon_handle_init()) == NULL)
|
if ((h = clicon_handle_init()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
_CLICON_HANDLE = h; /* for termination handling */
|
||||||
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
|
while ((c = getopt(argc, argv, RESTCONF_OPTS)) != -1)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
|
|
@ -531,6 +294,9 @@ main(int argc,
|
||||||
usage(h, argv[0]);
|
usage(h, argv[0]);
|
||||||
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
|
clicon_option_str_set(h, "CLICON_RESTCONF_DIR", optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'y' : /* yang module */
|
||||||
|
yangspec = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(h, argv[0]);
|
usage(h, argv[0]);
|
||||||
break;
|
break;
|
||||||
|
|
@ -540,11 +306,29 @@ main(int argc,
|
||||||
|
|
||||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, CLICON_LOG_SYSLOG);
|
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, CLICON_LOG_SYSLOG);
|
||||||
clicon_debug_init(debug, NULL);
|
clicon_debug_init(debug, NULL);
|
||||||
|
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
|
||||||
|
if (set_signal(SIGTERM, restconf_sig_term, NULL) < 0){
|
||||||
|
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (set_signal(SIGINT, restconf_sig_term, NULL) < 0){
|
||||||
|
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find and read configfile */
|
/* Find and read configfile */
|
||||||
if (clicon_options_main(h) < 0)
|
if (clicon_options_main(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
/* Overwrite yang module with -y option */
|
||||||
|
if (yangspec){
|
||||||
|
/* Set revision to NULL, extract dir and module */
|
||||||
|
char *str = strdup(yangspec);
|
||||||
|
char *dir = dirname(str);
|
||||||
|
hash_del(clicon_options(h), (char*)"CLICON_YANG_MODULE_REVISION");
|
||||||
|
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", basename(yangspec));
|
||||||
|
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
||||||
|
}
|
||||||
/* Initialize plugins group */
|
/* Initialize plugins group */
|
||||||
if (restconf_plugin_load(h) < 0)
|
if (restconf_plugin_load(h) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -592,5 +376,6 @@ main(int argc,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
restconf_plugin_unload(h);
|
restconf_plugin_unload(h);
|
||||||
|
restconf_terminate(h);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
515
apps/restconf/restconf_methods.c
Normal file
515
apps/restconf/restconf_methods.c
Normal file
|
|
@ -0,0 +1,515 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||||
|
|
||||||
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
Alternatively, the contents of this file may be used under the terms of
|
||||||
|
the GNU General Public License Version 3 or later (the "GPL"),
|
||||||
|
in which case the provisions of the GPL are applicable instead
|
||||||
|
of those above. If you wish to allow use of your version of this file only
|
||||||
|
under the terms of the GPL, and not to allow others to
|
||||||
|
use your version of this file under the terms of Apache License version 2,
|
||||||
|
indicate your decision by deleting the provisions above and replace them with
|
||||||
|
the notice and other provisions required by the GPL. If you do not delete
|
||||||
|
the provisions above, a recipient may use your version of this file under
|
||||||
|
the terms of any one of the Apache License version 2 or the GPL.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See draft-ietf-netconf-restconf-13.txt [draft]
|
||||||
|
* See draft-ietf-netconf-restconf-17.txt [draft]
|
||||||
|
|
||||||
|
* sudo apt-get install libfcgi-dev
|
||||||
|
* gcc -o fastcgi fastcgi.c -lfcgi
|
||||||
|
|
||||||
|
* sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data
|
||||||
|
|
||||||
|
* This is the interface:
|
||||||
|
* api/data/profile=<name>/metric=<name> PUT data:enable=<flag>
|
||||||
|
* api/test
|
||||||
|
+----------------------------+--------------------------------------+
|
||||||
|
| 100 Continue | POST accepted, 201 should follow |
|
||||||
|
| 200 OK | Success with response message-body |
|
||||||
|
| 201 Created | POST to create a resource success |
|
||||||
|
| 204 No Content | Success without response message- |
|
||||||
|
| | body |
|
||||||
|
| 304 Not Modified | Conditional operation not done |
|
||||||
|
| 400 Bad Request | Invalid request message |
|
||||||
|
| 401 Unauthorized | Client cannot be authenticated |
|
||||||
|
| 403 Forbidden | Access to resource denied |
|
||||||
|
| 404 Not Found | Resource target or resource node not |
|
||||||
|
| | found |
|
||||||
|
| 405 Method Not Allowed | Method not allowed for target |
|
||||||
|
| | resource |
|
||||||
|
| 409 Conflict | Resource or lock in use |
|
||||||
|
| 412 Precondition Failed | Conditional method is false |
|
||||||
|
| 413 Request Entity Too | too-big error |
|
||||||
|
| Large | |
|
||||||
|
| 414 Request-URI Too Large | too-big error |
|
||||||
|
| 415 Unsupported Media Type | non RESTCONF media type |
|
||||||
|
| 500 Internal Server Error | operation-failed |
|
||||||
|
| 501 Not Implemented | unknown-operation |
|
||||||
|
| 503 Service Unavailable | Recoverable server error |
|
||||||
|
+----------------------------+--------------------------------------+
|
||||||
|
Mapping netconf error-tag -> status code
|
||||||
|
+-------------------------+-------------+
|
||||||
|
| <error‑tag> | status code |
|
||||||
|
+-------------------------+-------------+
|
||||||
|
| in-use | 409 |
|
||||||
|
| invalid-value | 400 |
|
||||||
|
| too-big | 413 |
|
||||||
|
| missing-attribute | 400 |
|
||||||
|
| bad-attribute | 400 |
|
||||||
|
| unknown-attribute | 400 |
|
||||||
|
| bad-element | 400 |
|
||||||
|
| unknown-element | 400 |
|
||||||
|
| unknown-namespace | 400 |
|
||||||
|
| access-denied | 403 |
|
||||||
|
| lock-denied | 409 |
|
||||||
|
| resource-denied | 409 |
|
||||||
|
| rollback-failed | 500 |
|
||||||
|
| data-exists | 409 |
|
||||||
|
| data-missing | 409 |
|
||||||
|
| operation-not-supported | 501 |
|
||||||
|
| operation-failed | 500 |
|
||||||
|
| partial-operation | 500 |
|
||||||
|
| malformed-message | 400 |
|
||||||
|
+-------------------------+-------------+
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <syslog.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <fcgi_stdio.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
/* cligen */
|
||||||
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
|
/* clicon */
|
||||||
|
#include <clixon/clixon.h>
|
||||||
|
|
||||||
|
#include "restconf_lib.h"
|
||||||
|
#include "restconf_methods.h"
|
||||||
|
|
||||||
|
/*! REST OPTIONS method
|
||||||
|
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @code
|
||||||
|
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
|
* @endcode
|
||||||
|
* Minimal support:
|
||||||
|
* 200 OK
|
||||||
|
* Allow: HEAD,GET,PUT,DELETE,OPTIONS
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_options(clicon_handle h,
|
||||||
|
FCGX_Request *r)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||||
|
FCGX_FPrintF(r->out, "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Generic GET (both HEAD and GET)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
api_data_get_gen(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec,
|
||||||
|
int head)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cg_var *cv;
|
||||||
|
char *val;
|
||||||
|
char *v;
|
||||||
|
int i;
|
||||||
|
cbuf *path = NULL;
|
||||||
|
cbuf *path1 = NULL;
|
||||||
|
cbuf *cbx = NULL;
|
||||||
|
cxobj **vec = NULL;
|
||||||
|
yang_spec *yspec;
|
||||||
|
yang_stmt *y = NULL;
|
||||||
|
yang_stmt *ykey;
|
||||||
|
char *name;
|
||||||
|
cvec *cvk = NULL; /* vector of index keys */
|
||||||
|
cg_var *cvi;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
yspec = clicon_dbspec_yang(h);
|
||||||
|
if ((path = cbuf_new()) == NULL)
|
||||||
|
goto done;
|
||||||
|
if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */
|
||||||
|
goto done;
|
||||||
|
cv = NULL;
|
||||||
|
cprintf(path1, "/");
|
||||||
|
/* translate eg a/b=c -> a/[b=c] */
|
||||||
|
for (i=pi; i<cvec_len(pcvec); i++){
|
||||||
|
cv = cvec_i(pcvec, i);
|
||||||
|
name = cv_name_get(cv);
|
||||||
|
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||||
|
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||||
|
if (i == pi){
|
||||||
|
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
assert(y!=NULL);
|
||||||
|
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Check if has value, means '=' */
|
||||||
|
if (cv2str(cv, NULL, 0) > 0){
|
||||||
|
if ((val = cv2str_dup(cv)) == NULL)
|
||||||
|
goto done;
|
||||||
|
v = val;
|
||||||
|
/* XXX sync with yang */
|
||||||
|
while((v=index(v, ',')) != NULL){
|
||||||
|
*v = '\0';
|
||||||
|
v++;
|
||||||
|
}
|
||||||
|
/* Find keys */
|
||||||
|
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||||
|
__FUNCTION__, y->ys_argument);
|
||||||
|
notfound(r);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
||||||
|
|
||||||
|
/* The value is a list of keys: <key>[ <key>]* */
|
||||||
|
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||||
|
goto done;
|
||||||
|
cvi = NULL;
|
||||||
|
/* Iterate over individual yang keys */
|
||||||
|
cprintf(path, "/%s", name);
|
||||||
|
v = val;
|
||||||
|
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||||
|
cprintf(path, "[%s=%s]", cv_string_get(cvi), v);
|
||||||
|
v += strlen(v)+1;
|
||||||
|
}
|
||||||
|
if (val)
|
||||||
|
free(val);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
cprintf(path, "%s%s", (i==pi?"":"/"), name);
|
||||||
|
cprintf(path1, "/%s", name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
||||||
|
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
|
||||||
|
notfound(r);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
cbuf *cb = cbuf_new();
|
||||||
|
clicon_xml2cbuf(cb, xret, 0, 0);
|
||||||
|
clicon_debug(1, "%s xret:%s", __FUNCTION__, cbuf_get(cb));
|
||||||
|
cbuf_free(cb);
|
||||||
|
}
|
||||||
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
|
goto done;
|
||||||
|
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
if (head)
|
||||||
|
goto ok;
|
||||||
|
clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret));
|
||||||
|
vec = xml_childvec_get(xret);
|
||||||
|
clicon_debug(1, "%s xretnr:%d", __FUNCTION__, xml_child_nr(xret));
|
||||||
|
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||||
|
goto done;
|
||||||
|
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||||
|
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (cbx)
|
||||||
|
cbuf_free(cbx);
|
||||||
|
if (path)
|
||||||
|
cbuf_free(path);
|
||||||
|
if (path1)
|
||||||
|
cbuf_free(path1);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! REST HEAD method
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
|
* @param[in] pi Offset, where path starts
|
||||||
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
|
The HEAD method is sent by the client to retrieve just the header fields
|
||||||
|
that would be returned for the comparable GET method, without the
|
||||||
|
response message-body.
|
||||||
|
* Relation to netconf: none
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_head(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec)
|
||||||
|
{
|
||||||
|
return api_data_get_gen(h, r, pcvec, pi, qvec, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! REST GET method
|
||||||
|
* According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
|
* @param[in] pi Offset, where path starts
|
||||||
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
|
* @code
|
||||||
|
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
|
* @endcode
|
||||||
|
* XXX: cant find a way to use Accept request field to choose Content-Type
|
||||||
|
* I would like to support both xml and json.
|
||||||
|
* Request may contain
|
||||||
|
* Accept: application/yang.data+json,application/yang.data+xml
|
||||||
|
* Response contains one of:
|
||||||
|
* Content-Type: application/yang.data+xml
|
||||||
|
* Content-Type: application/yang.data+json
|
||||||
|
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||||
|
* list or list object identifies more than one instance, and XML
|
||||||
|
* encoding is used in the response, then an error response containing a
|
||||||
|
* "400 Bad Request" status-line MUST be returned by the server.
|
||||||
|
* Netconf: <get-config>, <get>
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_get(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec)
|
||||||
|
{
|
||||||
|
return api_data_get_gen(h, r, pcvec, pi, qvec, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Generic edit-config method: PUT/POST/PATCH
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
api_data_edit(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
char *api_path,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec,
|
||||||
|
char *data,
|
||||||
|
enum operation_type operation)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int i;
|
||||||
|
cxobj *xdata = NULL;
|
||||||
|
cbuf *cbx = NULL;
|
||||||
|
cxobj *x;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||||
|
__FUNCTION__,
|
||||||
|
api_path, data);
|
||||||
|
for (i=0; i<pi; i++)
|
||||||
|
api_path = index(api_path+1, '/');
|
||||||
|
/* Parse input data as json into xml */
|
||||||
|
if (json_parse_str(data, &xdata) < 0){
|
||||||
|
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
|
goto done;
|
||||||
|
cprintf(cbx, "<config>");
|
||||||
|
x = NULL;
|
||||||
|
while ((x = xml_child_each(xdata, x, -1)) != NULL) {
|
||||||
|
if (clicon_xml2cbuf(cbx, x, 0, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
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",
|
||||||
|
operation,
|
||||||
|
api_path,
|
||||||
|
cbuf_get(cbx)) < 0){
|
||||||
|
notfound(r);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clicon_rpc_commit(h) < 0)
|
||||||
|
goto done;
|
||||||
|
FCGX_SetExitStatus(201, r->out); /* Created */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (xdata)
|
||||||
|
xml_free(xdata);
|
||||||
|
if (cbx)
|
||||||
|
cbuf_free(cbx);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! REST POST method
|
||||||
|
* @param[in] h CLIXON handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
|
* @param[in] pi Offset, where to start pcvec
|
||||||
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
|
* @param[in] data Stream input data
|
||||||
|
POST:
|
||||||
|
If the POST method succeeds, a "201 Created" status-line is returned
|
||||||
|
and there is no response message-body. A "Location" header
|
||||||
|
identifying the child resource that was created MUST be present in
|
||||||
|
the response in this case.
|
||||||
|
|
||||||
|
If the data resource already exists, then the POST request MUST fail
|
||||||
|
and a "409 Conflict" status-line MUST be returned.
|
||||||
|
* Netconf: <edit-config> (nc:operation="create") | invoke an RPC operation
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_post(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
char *api_path,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec,
|
||||||
|
char *data)
|
||||||
|
{
|
||||||
|
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Generic REST PUT method
|
||||||
|
* @param[in] h CLIXON handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
|
* @param[in] pi Offset, where to start pcvec
|
||||||
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
|
* @param[in] data Stream input data
|
||||||
|
* Example:
|
||||||
|
curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||||
|
*
|
||||||
|
PUT:
|
||||||
|
if the PUT request creates a new resource,
|
||||||
|
a "201 Created" status-line is returned. If an existing resource is
|
||||||
|
modified, a "204 No Content" status-line is returned.
|
||||||
|
|
||||||
|
* Netconf: <edit-config> (nc:operation="create/replace")
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_put(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
char *api_path,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec,
|
||||||
|
char *data)
|
||||||
|
{
|
||||||
|
/* XXX: OP_CREATE? */
|
||||||
|
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_REPLACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Generic REST PATCH method
|
||||||
|
* @param[in] h CLIXON handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
|
* @param[in] pi Offset, where to start pcvec
|
||||||
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
|
* @param[in] data Stream input data
|
||||||
|
* Netconf: <edit-config> (nc:operation="merge")
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_patch(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
char *api_path,
|
||||||
|
cvec *pcvec,
|
||||||
|
int pi,
|
||||||
|
cvec *qvec,
|
||||||
|
char *data)
|
||||||
|
{
|
||||||
|
return api_data_edit(h, r, api_path, pcvec, pi, qvec, data, OP_MERGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Generic REST DELETE method
|
||||||
|
* @param[in] h CLIXON handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] pi Offset, where path starts
|
||||||
|
* Example:
|
||||||
|
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
||||||
|
* Netconf: <edit-config> (nc:operation="delete")
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_data_delete(clicon_handle h,
|
||||||
|
FCGX_Request *r,
|
||||||
|
char *api_path,
|
||||||
|
int pi)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||||
|
for (i=0; i<pi; i++)
|
||||||
|
api_path = index(api_path+1, '/');
|
||||||
|
if (clicon_rpc_edit_config(h, "candidate",
|
||||||
|
OP_DELETE,
|
||||||
|
api_path,
|
||||||
|
"<config/>") < 0){
|
||||||
|
notfound(r);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (clicon_rpc_commit(h) < 0)
|
||||||
|
goto done;
|
||||||
|
FCGX_SetExitStatus(201, r->out);
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/plain\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -30,27 +30,34 @@
|
||||||
the terms of any one of the Apache License version 2 or the GPL.
|
the terms of any one of the Apache License version 2 or the GPL.
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _RESTCONF_METHODS_H_
|
||||||
|
#define _RESTCONF_METHODS_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constants
|
||||||
*/
|
*/
|
||||||
#ifndef _CLIXON_XML_DB_RPC_H_
|
|
||||||
#define _CLIXON_XML_DB_RPC_H_
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int xmldb_get_rpc(clicon_handle h, char *db,
|
int api_data_options(clicon_handle h, FCGX_Request *r);
|
||||||
char *xpath,
|
int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
cvec *qvec);
|
||||||
int xmldb_put_rpc(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
|
int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
|
||||||
int xmldb_put_xkey_rpc(clicon_handle h, char *db, char *xk, char *val,
|
cvec *qvec);
|
||||||
enum operation_type op);
|
int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||||
int xmldb_copy_rpc(clicon_handle h, char *from, char *to);
|
cvec *pcvec, int pi,
|
||||||
int xmldb_lock_rpc(clicon_handle h, char *db, int pid);
|
cvec *qvec, char *data);
|
||||||
int xmldb_unlock_rpc(clicon_handle h, char *db, int pid);
|
int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||||
int xmldb_islocked_rpc(clicon_handle h, char *db);
|
cvec *pcvec, int pi,
|
||||||
|
cvec *qvec, char *data);
|
||||||
|
int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||||
|
cvec *pcvec, int pi,
|
||||||
|
cvec *qvec, char *data);
|
||||||
|
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi);
|
||||||
|
|
||||||
int xmldb_exists_rpc(clicon_handle h, char *db);
|
#endif /* _RESTCONF_METHODS_H_ */
|
||||||
int xmldb_delete_rpc(clicon_handle h, char *db);
|
|
||||||
int xmldb_init_rpc(clicon_handle h, char *db);
|
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_DB_RPC_H_ */
|
|
||||||
|
|
@ -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,16 +121,6 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
|
||||||
# Directory where "running", "candidate" and "startup" are placed
|
# Directory where "running", "candidate" and "startup" are placed
|
||||||
CLICON_XMLDB_DIR localstatedir/APPNAME
|
CLICON_XMLDB_DIR localstatedir/APPNAME
|
||||||
|
|
||||||
# Set if xmldb runs in a separate process (clixon_xmldb).
|
|
||||||
# If set, also set xmldb_addr and xmldb_port below
|
|
||||||
# CLICON_XMLDB_RPC 0
|
|
||||||
|
|
||||||
# xmldb inet address (if CLICON_XMLDB_RPC)
|
|
||||||
# CLICON_XMLDB_ADDR
|
|
||||||
|
|
||||||
# xmldb tcp port (if CLICON_XMLDB_RPC)
|
|
||||||
# CLICON_XMLDB_PORT
|
|
||||||
|
|
||||||
# Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored
|
# Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored
|
||||||
# CLICON_CLI_VARONLY 1
|
# CLICON_CLI_VARONLY 1
|
||||||
|
|
||||||
|
|
@ -138,15 +128,3 @@ CLICON_XMLDB_DIR localstatedir/APPNAME
|
||||||
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
|
# Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;
|
||||||
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
|
CLICON_RESTCONF_PATH /www-data/fastcgi_restconf.sock
|
||||||
|
|
||||||
# Set if you want to use old obsolete cligen expand variable syntax
|
|
||||||
# Migration: Set to 0 and change all user-defined cli completion callbacks
|
|
||||||
# E.g. expand_dbvar("db fmt") ->expandv_dbvar("db","fmt") in all your cli spec files
|
|
||||||
CLICON_CLIGEN_EXPAND_SINGLE_ARG 0
|
|
||||||
|
|
||||||
# Set if you want to use old obsolete cligen callback variable syntax
|
|
||||||
# Migration: Set to 0 and change all user-defined cli callbacks in your cli spec files
|
|
||||||
# E.g cmd, callback("single arg"); -> cmd, callback("two" "args");
|
|
||||||
# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
|
|
||||||
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
3
configure
vendored
3
configure
vendored
|
|
@ -4315,7 +4315,7 @@ fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile apps/xmldb/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile doc/Makefile"
|
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile doc/Makefile"
|
||||||
|
|
||||||
cat >confcache <<\_ACEOF
|
cat >confcache <<\_ACEOF
|
||||||
# This file is a shell script that caches the results of configure
|
# This file is a shell script that caches the results of configure
|
||||||
|
|
@ -5018,7 +5018,6 @@ do
|
||||||
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
|
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
|
||||||
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
|
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
|
||||||
"apps/dbctrl/Makefile") CONFIG_FILES="$CONFIG_FILES apps/dbctrl/Makefile" ;;
|
"apps/dbctrl/Makefile") CONFIG_FILES="$CONFIG_FILES apps/dbctrl/Makefile" ;;
|
||||||
"apps/xmldb/Makefile") CONFIG_FILES="$CONFIG_FILES apps/xmldb/Makefile" ;;
|
|
||||||
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
|
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
|
||||||
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
|
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
|
||||||
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
|
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,6 @@ AC_OUTPUT(Makefile
|
||||||
apps/netconf/Makefile
|
apps/netconf/Makefile
|
||||||
apps/restconf/Makefile
|
apps/restconf/Makefile
|
||||||
apps/dbctrl/Makefile
|
apps/dbctrl/Makefile
|
||||||
apps/xmldb/Makefile
|
|
||||||
include/Makefile
|
include/Makefile
|
||||||
etc/Makefile
|
etc/Makefile
|
||||||
etc/clixonrc
|
etc/clixonrc
|
||||||
|
|
|
||||||
|
|
@ -743,7 +743,7 @@ WARN_LOGFILE =
|
||||||
# spaces.
|
# spaces.
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# 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
|
# 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
|
# 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_backend.c - Commit and validate functions.
|
||||||
- routing_netconf.c - Modify semantics of netconf commands.
|
- routing_netconf.c - Modify semantics of netconf commands.
|
||||||
|
|
||||||
|
|
||||||
Q: How do you check what is in a database?
|
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
|
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?
|
Q: How do you use netconf?
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
As an alternative to cli configuration, you can use netconf
|
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
|
||||||
directly. Easiest is to just pipe netconf commands to the
|
|
||||||
clixon_netconf application.
|
|
||||||
Example:
|
Example:
|
||||||
echo "<rpc><get-config><source><candidate/></source><configuration/></get-config></rpc>]]>]]>" | clixon_netconf -f /usr/local/etc/routing.conf
|
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
|
1. Setting data example using netconf
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
<rpc><edit-config><target><candidate/></target><config>
|
<rpc><edit-config><target><candidate/></target><config>
|
||||||
<interfaces>
|
<interfaces>
|
||||||
<interface>
|
<interface>
|
||||||
|
|
@ -45,7 +44,38 @@ clixon_netconf -f /usr/local/etc/routing.conf
|
||||||
|
|
||||||
<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>
|
<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
|
cd docker
|
||||||
# look in README
|
# 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
|
||||||
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
|
# 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
|
# 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");
|
# E.g cmd, callback("single arg"); -> cmd, callback("two" "args");
|
||||||
# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
|
# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
|
||||||
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0
|
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 <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -53,13 +53,16 @@
|
||||||
/* These include signatures for plugin and transaction callbacks. */
|
/* These include signatures for plugin and transaction callbacks. */
|
||||||
#include <clixon/clixon_backend.h>
|
#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
|
/*! This is called on validate (and commit). Check validity of candidate
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
transaction_validate(clicon_handle h,
|
transaction_validate(clicon_handle h,
|
||||||
transaction_data td)
|
transaction_data td)
|
||||||
{
|
{
|
||||||
transaction_print(stderr, td);
|
// transaction_print(stderr, td);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,20 +73,62 @@ transaction_commit(clicon_handle h,
|
||||||
transaction_data td)
|
transaction_data td)
|
||||||
{
|
{
|
||||||
cxobj *target = transaction_target(td); /* wanted XML tree */
|
cxobj *target = transaction_target(td); /* wanted XML tree */
|
||||||
cxobj **vec;
|
cxobj **vec = NULL;
|
||||||
int i;
|
int i;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
/* Get all added i/fs */
|
/* Get all added i/fs */
|
||||||
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
if (xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &vec, &len) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
if (debug)
|
||||||
xml_print(stdout, vec[i]); /* Print the added interface */
|
for (i=0; i<len; i++) /* Loop over added i/fs */
|
||||||
|
xml_print(stdout, vec[i]); /* Print the added interface */
|
||||||
|
if (vec)
|
||||||
|
free(vec);
|
||||||
return 0;
|
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
|
* Plugin initialization
|
||||||
*/
|
*/
|
||||||
|
|
@ -92,8 +137,15 @@ plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
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;
|
retval = 0;
|
||||||
// done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ int
|
||||||
mycallback(clicon_handle h, cvec *cvv, cvec *argv)
|
mycallback(clicon_handle h, cvec *cvv, cvec *argv)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xt = NULL;
|
cxobj *xret = NULL;
|
||||||
cg_var *myvar;
|
cg_var *myvar;
|
||||||
|
|
||||||
/* Access cligen callback variables */
|
/* 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 */
|
cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */
|
||||||
|
|
||||||
/* Show eth0 interfaces config using XPATH */
|
/* Show eth0 interfaces config using XPATH */
|
||||||
if (xmldb_get(h, "candidate",
|
if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]",
|
||||||
"/interfaces/interface[name=eth0]",
|
&xret) < 0)
|
||||||
&xt, NULL, NULL) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
xml_print(stdout, xt);
|
xml_print(stdout, xret);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (xt)
|
if (xret)
|
||||||
xml_free(xt);
|
xml_free(xret);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! get argument and send as string to backend as RPC (which returns the string)
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
downcall(clicon_handle h,
|
||||||
|
cvec *vars,
|
||||||
|
cvec *argv)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
char *str="";
|
||||||
|
cg_var *cv;
|
||||||
|
cxobj *xret=NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
cxobj *xdata;
|
||||||
|
|
||||||
|
if (cvec_len(vars)==2){
|
||||||
|
if ((cv = cvec_i(vars, 1)) != NULL)
|
||||||
|
str = cv_string_get(cv);
|
||||||
|
}
|
||||||
|
if ((msg = clicon_msg_encode("<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;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,42 +4,53 @@ CLICON_PROMPT="%U@%H> ";
|
||||||
CLICON_PLUGIN="routing_cli";
|
CLICON_PLUGIN="routing_cli";
|
||||||
|
|
||||||
# Note, when switching to PT, change datamodel to only @datamodel
|
# Note, when switching to PT, change datamodel to only @datamodel
|
||||||
set @datamodel:ietf-ip, cli_mergev();
|
set @datamodel:ietf-ip, cli_merge();
|
||||||
|
|
||||||
#delete("Delete a configuration item") @datamodel:ietf-ipv4-unicast-routing, cli_del();
|
#delete("Delete a configuration item") @datamodel:ietf-ipv4-unicast-routing, cli_del();
|
||||||
delete("Delete a configuration item") @datamodel:ietf-ip, cli_delv();
|
delete("Delete a configuration item") @datamodel:ietf-ip, cli_del();
|
||||||
|
|
||||||
validate("Validate changes"), cli_validatev();
|
validate("Validate changes"), cli_validate();
|
||||||
commit("Commit the changes"), cli_commitv();
|
commit("Commit the changes"), cli_commit();
|
||||||
quit("Quit Hello"), cli_quitv();
|
quit("Quit Hello"), cli_quit();
|
||||||
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_allv("candidate");
|
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_all("candidate");
|
||||||
|
|
||||||
startup("Store running as startup config"), db_copy("running","startup");
|
startup("Store running as startup config"), db_copy("running", "startup");
|
||||||
no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cliv((int32)0);
|
no("Negate or remove") debug("Debugging parts of the system"), cli_debug_cli((int32)0);
|
||||||
debug("Debugging parts of the system"), cli_debug_cliv((int32)1);{
|
debug("Debugging parts of the system"), cli_debug_cli((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_backend();
|
||||||
}
|
}
|
||||||
|
copy("Copy and create a new object") {
|
||||||
discard("Discard edits (rollback 0)"), discard_changesv();
|
interface("Copy interface"){
|
||||||
|
<name:string expand_dbvar("candidate","/interfaces/interface/name")>("name of interface to copy from") to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s=%s]","name","name","toname");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
discard("Discard edits (rollback 0)"), discard_changes();
|
||||||
|
compare("Compare running and candidate"), compare_dbs((int32)1);
|
||||||
|
compare("Compare running and candidate"), compare_dbs((int32)1);
|
||||||
|
|
||||||
show("Show a particular state of the system"){
|
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_conf_xpath("candidate");
|
||||||
compare("Compare candidate and running databases"), compare_dbsv((int32)0);{
|
compare("Compare candidate and running databases"), compare_dbs((int32)0);{
|
||||||
xml("Show comparison in xml"), compare_dbsv((int32)0);
|
xml("Show comparison in xml"), compare_dbs((int32)0);
|
||||||
text("Show comparison in text"), compare_dbsv((int32)1);
|
text("Show comparison in text"), compare_dbs((int32)1);
|
||||||
}
|
}
|
||||||
configuration("Show configuration"), show_confv_as_text("candidate","/");{
|
configuration("Show configuration"), cli_show_config("candidate", "text", "/");{
|
||||||
xml("Show configuration as XML"), show_confv_as_xml("candidate","/");
|
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");
|
||||||
netconf("Show configuration as netconf edit-config operation"), show_confv_as_netconf("candidate","/");
|
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");
|
||||||
text("Show configuration as text"), show_confv_as_text("candidate","/");
|
text("Show configuration as text"), cli_show_config("candidate","text","/");
|
||||||
cli("Show configuration as cli commands"), show_confv_as_cli("candidate","/");
|
cli("Show configuration as cli commands"), cli_show_config("candidate", "cli", "/");
|
||||||
json("Show configuration as cli commands"), show_confv_as_json("candidate","/");
|
json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
save("Save candidate configuration to XML file") <filename:string>("Filename (local filename)"), save_config_filev("candidate","filename");
|
save("Save candidate configuration to XML file") <filename:string>("Filename (local filename)"), save_config_file("candidate","filename");
|
||||||
load("Load configuration from XML file") <filename:string>("Filename (local filename)"),load_config_filev("filename","replace");{
|
load("Load configuration from XML file") <filename:string>("Filename (local filename)"),load_config_file("filename", "replace");{
|
||||||
replace("Replace candidate with file contents"), load_config_filev("filename","replace");
|
replace("Replace candidate with file contents"), load_config_file("filename", "replace");
|
||||||
merge("Merge file with existent candidate"), load_config_filev("filename","merge");
|
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
|
||||||
}
|
}
|
||||||
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
|
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_notify("ROUTING", "1", "text");
|
||||||
|
no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml");
|
||||||
|
lock,cli_lock("candidate");
|
||||||
|
unlock,cli_unlock("candidate");
|
||||||
|
|
@ -37,8 +37,6 @@
|
||||||
#define _CLIXON_FILE_H_
|
#define _CLIXON_FILE_H_
|
||||||
|
|
||||||
|
|
||||||
char **clicon_realpath(const char *cwd, char *path, const char *label);
|
|
||||||
|
|
||||||
int clicon_file_dirent(const char *dir, struct dirent **ent,
|
int clicon_file_dirent(const char *dir, struct dirent **ent,
|
||||||
const char *regexp, mode_t type, const char *label);
|
const char *regexp, mode_t type, const char *label);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,7 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*! Controls how keywords a generated in CLI syntax / prints from object model
|
||||||
* enum gensyntx
|
|
||||||
* Controls how keywords a generated in CLI syntax / prints from obhect model
|
|
||||||
* Example syntax a.b[] $!x $y:
|
* Example syntax a.b[] $!x $y:
|
||||||
* NONE: a b <x> <y>;
|
* NONE: a b <x> <y>;
|
||||||
* VARS: a b <x> y <y>;
|
* VARS: a b <x> y <y>;
|
||||||
|
|
@ -109,9 +107,6 @@ int clicon_cli_varonly_set(clicon_handle h, int val);
|
||||||
int clicon_cli_genmodel_completion(clicon_handle h);
|
int clicon_cli_genmodel_completion(clicon_handle h);
|
||||||
|
|
||||||
char *clicon_xmldb_dir(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);
|
char *clicon_quiet_mode(clicon_handle h);
|
||||||
enum genmodel_type clicon_cli_genmodel_type(clicon_handle h);
|
enum genmodel_type clicon_cli_genmodel_type(clicon_handle h);
|
||||||
|
|
|
||||||
|
|
@ -42,136 +42,51 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
enum format_enum{
|
enum format_enum{
|
||||||
MSG_NOTIFY_TXT, /* means filter works on strings */
|
FORMAT_XML,
|
||||||
MSG_NOTIFY_XML, /* means filter works on xml */
|
FORMAT_JSON,
|
||||||
};
|
FORMAT_TEXT,
|
||||||
|
FORMAT_CLI,
|
||||||
/* See also map_type2str in clicon_proto.c */
|
FORMAT_NETCONF
|
||||||
enum clicon_msg_type{
|
|
||||||
CLICON_MSG_COMMIT = 1, /* Commit a configuration db->running_db
|
|
||||||
current state, set running_db. Body is:
|
|
||||||
1. uint32: (1)snapshot while doing commit, (0) dont
|
|
||||||
2. uint32: (1)save to startup-config, (0) dont
|
|
||||||
3. string: name of 'from' database (eg "candidate")
|
|
||||||
4. string: name of 'to' database (eg "running")
|
|
||||||
*/
|
|
||||||
CLICON_MSG_VALIDATE, /* Validate settings in a database. Body is:
|
|
||||||
1. string: name of database (eg "candidate")
|
|
||||||
*/
|
|
||||||
CLICON_MSG_CHANGE, /* Change a (single) database entry:
|
|
||||||
1. uint32: operation: OP_MERGE/OP_REPLACE/OP_REMOVE
|
|
||||||
2. uint32: length of value string
|
|
||||||
3. string: name of database to change (eg "running")
|
|
||||||
4. string: key
|
|
||||||
5. string: value (can be NULL)
|
|
||||||
*/
|
|
||||||
CLICON_MSG_XMLPUT, /* Send database entries as XML to backend daemon
|
|
||||||
1. uint32: operation: LV_SET/LV_DELETE
|
|
||||||
2. string: name of database to change (eg current)
|
|
||||||
3. string: restconf api path
|
|
||||||
4. string: XML data
|
|
||||||
*/
|
|
||||||
|
|
||||||
CLICON_MSG_SAVE, /* Save config state from db to a file in backend. Body is:
|
|
||||||
1. uint32: make snapshot (1), dont(0)
|
|
||||||
2. string: name of database to save from (eg running)
|
|
||||||
3. string: filename to write. If snapshot=1, then this
|
|
||||||
is empty.
|
|
||||||
*/
|
|
||||||
CLICON_MSG_LOAD, /* Load config state from file in backend to db via XML. Body is:
|
|
||||||
1. uint32: whether to replace/initdb before load (1) or
|
|
||||||
merge (0).
|
|
||||||
2. string: name of database to load into (eg running)
|
|
||||||
3. string: filename to load from
|
|
||||||
|
|
||||||
*/
|
|
||||||
CLICON_MSG_COPY, /* Copy from file to file in backend. Body is:
|
|
||||||
1. string: filename to copy from
|
|
||||||
2. string: filename to copy to
|
|
||||||
*/
|
|
||||||
CLICON_MSG_KILL, /* Kill (other) session:
|
|
||||||
1. session-id
|
|
||||||
*/
|
|
||||||
CLICON_MSG_DEBUG, /* Debug
|
|
||||||
1. session-id
|
|
||||||
*/
|
|
||||||
CLICON_MSG_CALL , /* Backend plugin call request. Body is:
|
|
||||||
1. struct clicon_msg_call_req *
|
|
||||||
*/
|
|
||||||
CLICON_MSG_SUBSCRIPTION, /* Create a new notification subscription.
|
|
||||||
Body is:
|
|
||||||
1. int: status off/on
|
|
||||||
1. int: format (enum format_enum)
|
|
||||||
2. string: name of notify stream
|
|
||||||
3. string: filter, if format=xml: xpath, if text: fnmatch */
|
|
||||||
CLICON_MSG_OK, /* server->client reply */
|
|
||||||
CLICON_MSG_NOTIFY, /* Notification. Body is:
|
|
||||||
1. int: loglevel
|
|
||||||
2. event: log message. */
|
|
||||||
CLICON_MSG_ERR /* server->client reply.
|
|
||||||
Body is:
|
|
||||||
1. uint32: man error category
|
|
||||||
2. uint32: sub-error
|
|
||||||
3. string: reason
|
|
||||||
*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Protocol message header */
|
/* Protocol message header */
|
||||||
struct clicon_msg {
|
struct clicon_msg {
|
||||||
uint16_t op_len; /* length of message. */
|
uint16_t op_len; /* length of message. */
|
||||||
uint16_t op_type; /* message type, see enum clicon_msg_type */
|
|
||||||
char op_body[0]; /* rest of message, actual data */
|
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
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
#ifndef LIBCLICON_API
|
char *format_int2str(enum format_enum showas);
|
||||||
|
enum format_enum format_str2int(char *str);
|
||||||
|
|
||||||
|
struct clicon_msg *clicon_msg_encode(char *format, ...);
|
||||||
|
int clicon_msg_decode(struct clicon_msg *msg, cxobj **xml);
|
||||||
|
|
||||||
int clicon_connect_unix(char *sockpath);
|
int clicon_connect_unix(char *sockpath);
|
||||||
|
|
||||||
int clicon_rpc_connect_unix(struct clicon_msg *msg,
|
int clicon_rpc_connect_unix(struct clicon_msg *msg,
|
||||||
char *sockpath,
|
char *sockpath,
|
||||||
char **data,
|
char **ret,
|
||||||
uint16_t *datalen,
|
int *sock0);
|
||||||
int *sock0,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int clicon_rpc_connect_inet(struct clicon_msg *msg,
|
int clicon_rpc_connect_inet(struct clicon_msg *msg,
|
||||||
char *dst,
|
char *dst,
|
||||||
uint16_t port,
|
uint16_t port,
|
||||||
char **data,
|
char **ret,
|
||||||
uint16_t *datalen,
|
int *sock0);
|
||||||
int *sock0,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int clicon_rpc(int s, struct clicon_msg *msg, char **data, uint16_t *datalen,
|
int clicon_rpc(int s, struct clicon_msg *msg, char **xret);
|
||||||
const char *label);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
int clicon_msg_send(int s, struct clicon_msg *msg);
|
int clicon_msg_send(int s, struct clicon_msg *msg);
|
||||||
|
|
||||||
int clicon_msg_rcv(int s, struct clicon_msg **msg,
|
int clicon_msg_rcv(int s, struct clicon_msg **msg, int *eof);
|
||||||
int *eof, const char *label);
|
|
||||||
|
|
||||||
int send_msg_notify(int s, int level, char *event);
|
int send_msg_notify(int s, int level, char *event);
|
||||||
|
|
||||||
int send_msg_reply(int s, uint16_t type, char *data, uint16_t datalen);
|
int send_msg_reply(int s, char *data, uint16_t datalen);
|
||||||
|
|
||||||
int send_msg_ok(int s);
|
int detect_endtag(char *tag, char ch, int *state);
|
||||||
|
|
||||||
int send_msg_err(int s, int err, int suberr, char *format, ...);
|
|
||||||
|
|
||||||
#endif /* _CLIXON_PROTO_H_ */
|
#endif /* _CLIXON_PROTO_H_ */
|
||||||
|
|
|
||||||
|
|
@ -40,27 +40,25 @@
|
||||||
#ifndef _CLIXON_PROTO_CLIENT_H_
|
#ifndef _CLIXON_PROTO_CLIENT_H_
|
||||||
#define _CLIXON_PROTO_CLIENT_H_
|
#define _CLIXON_PROTO_CLIENT_H_
|
||||||
|
|
||||||
int clicon_rpc_commit(clicon_handle h, char *from, char *to);
|
int clicon_rpc_msg(clicon_handle h, struct clicon_msg *msg, cxobj **xret0,
|
||||||
|
int *sock0);
|
||||||
|
int clicon_rpc_netconf(clicon_handle h, char *xmlst, cxobj **xret, int *sp);
|
||||||
|
int clicon_rpc_netconf_xml(clicon_handle h, cxobj *xml, cxobj **xret, int *sp);
|
||||||
|
int clicon_rpc_generate_error(cxobj *xerr);
|
||||||
|
int clicon_rpc_get_config(clicon_handle h, char *db, char *xpath, cxobj **xret);
|
||||||
|
int clicon_rpc_edit_config(clicon_handle h, char *db, enum operation_type op,
|
||||||
|
char *api_path, char *xml);
|
||||||
|
int clicon_rpc_copy_config(clicon_handle h, char *db1, char *db2);
|
||||||
|
int clicon_rpc_delete_config(clicon_handle h, char *db);
|
||||||
|
int clicon_rpc_lock(clicon_handle h, char *db);
|
||||||
|
int clicon_rpc_unlock(clicon_handle h, char *db);
|
||||||
|
int clicon_rpc_close_session(clicon_handle h);
|
||||||
|
int clicon_rpc_kill_session(clicon_handle h, int session_id);
|
||||||
int clicon_rpc_validate(clicon_handle h, char *db);
|
int clicon_rpc_validate(clicon_handle h, char *db);
|
||||||
int clicon_rpc_change(clicon_handle h, char *db,
|
int clicon_rpc_commit(clicon_handle h);
|
||||||
enum operation_type op, char *key, char *val);
|
int clicon_rpc_discard_changes(clicon_handle h);
|
||||||
|
int clicon_rpc_create_subscription(clicon_handle h, char *stream, char *filter,
|
||||||
int clicon_rpc_xmlput(clicon_handle h, char *db, enum operation_type op,
|
int *s);
|
||||||
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_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_ */
|
#endif /* _CLIXON_PROTO_CLIENT_H_ */
|
||||||
|
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
***** BEGIN LICENSE BLOCK *****
|
|
||||||
|
|
||||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
|
||||||
|
|
||||||
This file is part of CLIXON.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
the GNU General Public License Version 3 or later (the "GPL"),
|
|
||||||
in which case the provisions of the GPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of the GPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of Apache License version 2,
|
|
||||||
indicate your decision by deleting the provisions above and replace them with
|
|
||||||
the notice and other provisions required by the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the Apache License version 2 or the GPL.
|
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
|
||||||
|
|
||||||
*
|
|
||||||
* Protocol to communicate between clients (eg clixon_cli, clixon_netconf)
|
|
||||||
* and server (clicon_backend)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _CLIXON_PROTO_ENCODE_H_
|
|
||||||
#define _CLIXON_PROTO_ENCODE_H_
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prototypes
|
|
||||||
*/
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_commit_decode(struct clicon_msg *msg,
|
|
||||||
char **dbsrc, char **dbdst,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_validate_encode(char *db,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_validate_decode(struct clicon_msg *msg, char **db,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_change_encode(char *db, uint32_t op, char *key,
|
|
||||||
char *lvec, uint32_t lvec_len,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_change_decode(struct clicon_msg *msg,
|
|
||||||
char **db, uint32_t *op, char **key,
|
|
||||||
char **lvec, uint32_t *lvec_len,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_xmlput_encode(char *db,
|
|
||||||
uint32_t op,
|
|
||||||
char *api_path,
|
|
||||||
char *xml,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_xmlput_decode(struct clicon_msg *msg,
|
|
||||||
char **db,
|
|
||||||
uint32_t *op,
|
|
||||||
char **api_path,
|
|
||||||
char **xml,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_dbitems_get_reply_encode(cvec **cvecv,
|
|
||||||
int cveclen,
|
|
||||||
const char *label);
|
|
||||||
int
|
|
||||||
clicon_msg_dbitems_get_reply_decode(char *data,
|
|
||||||
uint16_t datalen,
|
|
||||||
cvec ***cvecv,
|
|
||||||
size_t *cveclen,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_save_decode(struct clicon_msg *msg,
|
|
||||||
char **db, uint32_t *snapshot, char **filename,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_load_encode(int replace, char *db, char *filename,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_load_decode(struct clicon_msg *msg,
|
|
||||||
int *replace, char **db, char **filename,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_copy_encode(char *db_src, char *db_dst,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_copy_decode(struct clicon_msg *msg,
|
|
||||||
char **db_src, char **db_dst,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_kill_encode(uint32_t session_id, const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_kill_decode(struct clicon_msg *msg, uint32_t *session_id,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_debug_encode(uint32_t level, const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_debug_decode(struct clicon_msg *msg, uint32_t *level,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_call_encode(uint16_t op, char *plugin, char *func,
|
|
||||||
uint16_t arglen, void *arg,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_call_decode(struct clicon_msg *msg,
|
|
||||||
struct clicon_msg_call_req **req,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_subscription_encode(int status,
|
|
||||||
char *stream,
|
|
||||||
enum format_enum format,
|
|
||||||
char *filter,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
int clicon_msg_subscription_decode(struct clicon_msg *msg,
|
|
||||||
int *status,
|
|
||||||
char **stream,
|
|
||||||
enum format_enum *format,
|
|
||||||
char **filter,
|
|
||||||
const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_notify_encode(int level, char *event, const char *label);
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_notify_decode(struct clicon_msg *msg, int *level,
|
|
||||||
char **event, const char *label);
|
|
||||||
|
|
||||||
struct clicon_msg *clicon_msg_err_encode(uint32_t err, uint32_t suberr,
|
|
||||||
char *reason, const char *label);
|
|
||||||
|
|
||||||
int clicon_msg_err_decode(struct clicon_msg *msg, uint32_t *err, uint32_t *suberr,
|
|
||||||
char **reason, const char *label);
|
|
||||||
|
|
||||||
#endif /* _CLIXON_PROTO_ENCODE_H_ */
|
|
||||||
|
|
@ -54,16 +54,10 @@ static inline char * strdup4(char *str)
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
char **clicon_sepsplit (char *string, char *delim, int *nvec, const char *label);
|
char **clicon_strsep(char *string, char *delim, int *nvec0);
|
||||||
char **clicon_strsplit (char *string, char *delim, int *nvec, const char *label);
|
char *clicon_strjoin (int argc, char **argv, char *delim);
|
||||||
char *clicon_strjoin (int argc, char **argv, char *delim, const char *label);
|
|
||||||
char *clicon_strtrim(char *str, const char *label);
|
|
||||||
int clicon_sep(char *s, const char sep[2], const char *label, char**a0, char **b0);
|
|
||||||
#ifndef HAVE_STRNDUP
|
#ifndef HAVE_STRNDUP
|
||||||
char *clicon_strndup (const char *, size_t);
|
char *clicon_strndup (const char *, size_t);
|
||||||
#endif /* ! HAVE_STRNDUP */
|
#endif /* ! HAVE_STRNDUP */
|
||||||
int clicon_strmatch(const char *str, const char *regexp, char **match);
|
|
||||||
char *clicon_strsub(char *str, char *from, char *to);
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _CLIXON_STRING_H_ */
|
#endif /* _CLIXON_STRING_H_ */
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
* Types
|
* Types
|
||||||
*/
|
*/
|
||||||
/* Netconf operation type */
|
/* Netconf operation type */
|
||||||
enum operation_type{ /* edit-config */
|
enum operation_type{ /* edit-configo */
|
||||||
OP_MERGE, /* merge config-data */
|
OP_MERGE, /* merge config-data */
|
||||||
OP_REPLACE,/* replace or create config-data */
|
OP_REPLACE,/* replace or create config-data */
|
||||||
OP_CREATE, /* create config data, error if exist */
|
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_i_set(cxobj *xt, int i, cxobj *xc);
|
||||||
cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type);
|
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);
|
int xml_childvec_set(cxobj *x, int len);
|
||||||
cxobj *xml_new(char *name, cxobj *xn_parent);
|
cxobj *xml_new(char *name, cxobj *xn_parent);
|
||||||
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
|
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 */
|
/* XXX obsolete */
|
||||||
#define clicon_xml_parse_string(str, x) clicon_xml_parse_str((*str), x)
|
#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_str(char *str, cxobj **xml_top);
|
||||||
|
int clicon_xml_parse(cxobj **cxtop, char *format, ...);
|
||||||
|
|
||||||
int xml_copy(cxobj *x0, cxobj *x1);
|
int xml_copy(cxobj *x0, cxobj *x1);
|
||||||
cxobj *xml_dup(cxobj *x0);
|
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_parse(cxobj *xb, enum cv_type type, cg_var **cvp);
|
||||||
int xml_body_int32(cxobj *xb, int32_t *val);
|
int xml_body_int32(cxobj *xb, int32_t *val);
|
||||||
int xml_body_uint32(cxobj *xb, uint32_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 */
|
#endif /* _CLIXON_XML_H */
|
||||||
|
|
|
||||||
|
|
@ -42,18 +42,16 @@
|
||||||
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
|
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
|
||||||
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
|
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
|
||||||
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
|
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
|
||||||
|
|
||||||
int xmldb_get(clicon_handle h, char *db, char *xpath,
|
int xmldb_get(clicon_handle h, char *db, char *xpath,
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||||
int xmldb_put(clicon_handle h, char *db, cxobj *xt, enum operation_type op);
|
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
|
||||||
int xmldb_put_tree(clicon_handle h, char *db, char *api_path,
|
char *api_path, cxobj *xt);
|
||||||
cxobj *xt, enum operation_type op);
|
int xmldb_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||||
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_copy(clicon_handle h, char *from, char *to);
|
int xmldb_copy(clicon_handle h, char *from, char *to);
|
||||||
int xmldb_lock(clicon_handle h, char *db, int pid);
|
int xmldb_lock(clicon_handle h, char *db, int pid);
|
||||||
int xmldb_unlock(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_islocked(clicon_handle h, char *db);
|
||||||
int xmldb_exists(clicon_handle h, char *db);
|
int xmldb_exists(clicon_handle h, char *db);
|
||||||
int xmldb_delete(clicon_handle h, char *db);
|
int xmldb_delete(clicon_handle h, char *db);
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ enum {
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int xml2txt(FILE *f, cxobj *x, int level);
|
int xml2txt(FILE *f, cxobj *x, int level);
|
||||||
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char *label);
|
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt);
|
||||||
int xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
|
int xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
|
||||||
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
||||||
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,8 @@ SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
|
||||||
clixon_json.c \
|
clixon_json.c \
|
||||||
clixon_yang.c clixon_yang_type.c \
|
clixon_yang.c clixon_yang_type.c \
|
||||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||||
clixon_proto.c clixon_proto_encode.c clixon_proto_client.c \
|
clixon_proto.c clixon_proto_client.c \
|
||||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c 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 \
|
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,7 @@ static struct errvec EV[] = {
|
||||||
{"UNIX error", OE_UNIX},
|
{"UNIX error", OE_UNIX},
|
||||||
{"Syslog error", OE_SYSLOG},
|
{"Syslog error", OE_SYSLOG},
|
||||||
{"Routing demon error", OE_ROUTING},
|
{"Routing demon error", OE_ROUTING},
|
||||||
|
{"XML error", OE_XML},
|
||||||
{"Plugins", OE_PLUGIN},
|
{"Plugins", OE_PLUGIN},
|
||||||
{"Yang error", OE_YANG},
|
{"Yang error", OE_YANG},
|
||||||
{"FATAL", OE_FATAL},
|
{"FATAL", OE_FATAL},
|
||||||
|
|
|
||||||
|
|
@ -62,99 +62,6 @@
|
||||||
#include "clixon_string.h"
|
#include "clixon_string.h"
|
||||||
#include "clixon_file.h"
|
#include "clixon_file.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* Resolve the real path of a given 'path', following symbolic links and '../'.
|
|
||||||
* If 'path' relative, it will be resolved based on the currnt working
|
|
||||||
* directory 'cwd'. The response is a 2 entry vector of strings. The first
|
|
||||||
* entry is the resolved path and the second is the part of the path which
|
|
||||||
* actually exist.
|
|
||||||
*/
|
|
||||||
char **
|
|
||||||
clicon_realpath(const char *cwd, char *path, const char *label)
|
|
||||||
{
|
|
||||||
char **ret = NULL;
|
|
||||||
char *rest;
|
|
||||||
char **vec, **vec2;
|
|
||||||
int nvec, nvec2;
|
|
||||||
char *p;
|
|
||||||
char *rp = NULL;
|
|
||||||
char *ptr;
|
|
||||||
int i;
|
|
||||||
struct passwd *pwd;
|
|
||||||
char cwdbuf[PATH_MAX];
|
|
||||||
|
|
||||||
/* Prepend 'cwd' if not absolute */
|
|
||||||
if (path[0] == '/')
|
|
||||||
p = path;
|
|
||||||
else {
|
|
||||||
if (cwd == NULL || strlen(cwd) == 0)
|
|
||||||
cwd = getcwd(cwdbuf, sizeof(cwdbuf));
|
|
||||||
else if (cwd[0] == '~') {
|
|
||||||
if((pwd = getpwuid(getuid())) == NULL)
|
|
||||||
goto catch;
|
|
||||||
cwd = pwd->pw_dir;
|
|
||||||
}
|
|
||||||
p = chunk_sprintf(__FUNCTION__, "%s%s/%s",
|
|
||||||
(cwd[0]=='/' ? "" : "/"), cwd, path);
|
|
||||||
}
|
|
||||||
if (p == NULL)
|
|
||||||
goto catch;
|
|
||||||
|
|
||||||
/* Make a local copy of 'path' */
|
|
||||||
if ((path = chunkdup(p, strlen(p)+1, __FUNCTION__)) == NULL)
|
|
||||||
goto catch;
|
|
||||||
|
|
||||||
/* Find the smallest portion of the path that exist and run realpath() */
|
|
||||||
while(strlen(p) && ((rp = realpath(p, NULL)) == NULL)) {
|
|
||||||
if((ptr = strrchr(p, '/')) == NULL)
|
|
||||||
break;
|
|
||||||
*ptr = '\0';
|
|
||||||
}
|
|
||||||
if(rp == NULL)
|
|
||||||
goto catch;
|
|
||||||
|
|
||||||
/* Use the result of realpath() and the rest of 'path' untouched, to
|
|
||||||
form a new path */
|
|
||||||
rest = path + strlen(p);
|
|
||||||
ptr = chunk_sprintf(__FUNCTION__, "%s%s", rp, rest);
|
|
||||||
p = ptr;
|
|
||||||
|
|
||||||
/* Split path based on '/'. Loop through vector from the end and copy
|
|
||||||
each entry into a new vector, skipping '..' and it's previous directory
|
|
||||||
as well as all '.' */
|
|
||||||
vec = clicon_strsplit (p, "/", &nvec, __FUNCTION__);
|
|
||||||
vec2 = chunk(nvec * sizeof(char *), __FUNCTION__);
|
|
||||||
nvec2 = i = nvec;
|
|
||||||
while(--i >= 0) {
|
|
||||||
if(strcmp(vec[i], "..") == 0)
|
|
||||||
i--; /* Skip previous */
|
|
||||||
else if(strcmp(vec[i], ".") == 0)
|
|
||||||
/* do nothing */ ;
|
|
||||||
else
|
|
||||||
vec2[--nvec2] = vec[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create resulting vector */
|
|
||||||
if ((ret = chunk(sizeof(char *) * 2, label)) != NULL) {
|
|
||||||
if((ret[0] = clicon_strjoin(nvec-nvec2, &vec2[nvec2], "/", label)) == NULL) {
|
|
||||||
unchunk(ret);
|
|
||||||
ret = NULL;
|
|
||||||
}
|
|
||||||
if ((ret[1] = chunkdup(rp, strlen(rp)+1, label)) == NULL) {
|
|
||||||
unchunk(ret[0]);
|
|
||||||
unchunk(ret);
|
|
||||||
ret = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch:
|
|
||||||
if(rp)
|
|
||||||
free(rp);
|
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* qsort function
|
* qsort function
|
||||||
*/
|
*/
|
||||||
|
|
@ -345,24 +252,3 @@ clicon_file_copy(char *src,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef NOTUSED
|
|
||||||
/*
|
|
||||||
* (un)lock a whole file.
|
|
||||||
* Arguments:
|
|
||||||
* fd - File descriptor
|
|
||||||
* cmd - F_GETLK, F_SETLK, F_SETLKW
|
|
||||||
* type - F_RDLCK, F_WRLCK, F_UNLCK
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
file_lock(int fd, int cmd, int type)
|
|
||||||
{
|
|
||||||
struct flock lock;
|
|
||||||
|
|
||||||
lock.l_type = type;
|
|
||||||
lock.l_whence = SEEK_SET;
|
|
||||||
lock.l_start = 0;
|
|
||||||
lock.l_len = 0;
|
|
||||||
|
|
||||||
return fcntl(fd, cmd, &lock);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
|
||||||
|
|
@ -131,11 +131,7 @@ int
|
||||||
json_scan_exit(struct clicon_json_yacc_arg *jy)
|
json_scan_exit(struct clicon_json_yacc_arg *jy)
|
||||||
{
|
{
|
||||||
yy_delete_buffer(jy->jy_lexbuf);
|
yy_delete_buffer(jy->jy_lexbuf);
|
||||||
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
|
|
||||||
clixon_json_parselex_destroy(); /* modern */
|
clixon_json_parselex_destroy(); /* modern */
|
||||||
#else
|
|
||||||
yy_init = 1; /* This does not quite free all buffers */
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -224,7 +224,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* top: json -> value is also possible */
|
/* 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");}
|
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)
|
if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0)
|
||||||
goto catch;
|
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;
|
retval = 0;
|
||||||
catch:
|
catch:
|
||||||
unchunk_group(__FUNCTION__);
|
unchunk_group(__FUNCTION__);
|
||||||
|
|
@ -616,35 +612,6 @@ clicon_xmldb_dir(clicon_handle h)
|
||||||
return clicon_option_str(h, "CLICON_XMLDB_DIR");
|
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
|
/*! Get YANG specification
|
||||||
* Must use hash functions directly since they are not strings.
|
* Must use hash functions directly since they are not strings.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -121,13 +121,12 @@ clicon_proc_run (char *cmd,
|
||||||
sigfn_t oldhandler = NULL;
|
sigfn_t oldhandler = NULL;
|
||||||
sigset_t oset;
|
sigset_t oset;
|
||||||
|
|
||||||
argv = clicon_sepsplit (cmd, " \t", &argc, __FUNCTION__);
|
argv = clicon_strsep(cmd, " \t", &argc);
|
||||||
if (!argv)
|
if (!argv)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (pipe (outfd) == -1)
|
if (pipe (outfd) == -1)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
|
||||||
signal_get_mask(&oset);
|
signal_get_mask(&oset);
|
||||||
set_signal(SIGINT, clicon_proc_sigint, &oldhandler);
|
set_signal(SIGINT, clicon_proc_sigint, &oldhandler);
|
||||||
|
|
@ -194,7 +193,8 @@ clicon_proc_run (char *cmd,
|
||||||
signal_set_mask (&oset);
|
signal_set_mask (&oset);
|
||||||
set_signal(SIGINT, oldhandler, NULL);
|
set_signal(SIGINT, oldhandler, NULL);
|
||||||
|
|
||||||
unchunk_group (__FUNCTION__);
|
if(argv)
|
||||||
|
free(argv);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ clicon_proc_daemon (char *cmd)
|
||||||
struct rlimit
|
struct rlimit
|
||||||
rlim;
|
rlim;
|
||||||
|
|
||||||
argv = clicon_sepsplit (cmd, " \t", &argc, NULL);
|
argv = clicon_strsep(cmd, " \t", &argc);
|
||||||
if (!argv)
|
if (!argv)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
@ -260,7 +260,8 @@ clicon_proc_daemon (char *cmd)
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (argv)
|
||||||
|
free(argv);
|
||||||
return (retval);
|
return (retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,47 +68,117 @@
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
#include "clixon_chunk.h"
|
||||||
#include "clixon_sig.h"
|
#include "clixon_sig.h"
|
||||||
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_xsl.h"
|
||||||
#include "clixon_proto.h"
|
#include "clixon_proto.h"
|
||||||
#include "clixon_proto_encode.h"
|
|
||||||
|
|
||||||
static int _atomicio_sig = 0;
|
static int _atomicio_sig = 0;
|
||||||
|
|
||||||
struct map_type2str{
|
/*! Formats (showas) derived from XML
|
||||||
enum clicon_msg_type mt_type;
|
*/
|
||||||
char *mt_str; /* string as in 4.2.4 in RFC 6020 */
|
struct formatvec{
|
||||||
|
char *fv_str;
|
||||||
|
int fv_int;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Mapping between yang keyword string <--> clicon constants */
|
static struct formatvec _FORMATS[] = {
|
||||||
static const struct map_type2str msgmap[] = {
|
{"xml", FORMAT_XML},
|
||||||
{CLICON_MSG_COMMIT, "commit"},
|
{"text", FORMAT_TEXT},
|
||||||
{CLICON_MSG_VALIDATE, "validate"},
|
{"json", FORMAT_JSON},
|
||||||
{CLICON_MSG_CHANGE, "change"},
|
{"cli", FORMAT_CLI},
|
||||||
{CLICON_MSG_XMLPUT, "xmlput"},
|
{"netconf", FORMAT_NETCONF},
|
||||||
{CLICON_MSG_SAVE, "save"},
|
{NULL, -1}
|
||||||
{CLICON_MSG_LOAD, "load"},
|
|
||||||
{CLICON_MSG_COPY, "copy"},
|
|
||||||
{CLICON_MSG_KILL, "kill"},
|
|
||||||
{CLICON_MSG_DEBUG, "debug"},
|
|
||||||
{CLICON_MSG_CALL, "call"},
|
|
||||||
{CLICON_MSG_SUBSCRIPTION, "subscription"},
|
|
||||||
{CLICON_MSG_OK, "ok"},
|
|
||||||
{CLICON_MSG_NOTIFY, "notify"},
|
|
||||||
{CLICON_MSG_ERR, "err"},
|
|
||||||
{-1, NULL},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *
|
/*! Translate from numeric format to string representation
|
||||||
msg_type2str(enum clicon_msg_type type)
|
* @param[in] showas Format value (see enum format_enum)
|
||||||
|
* @retval str String value
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
format_int2str(enum format_enum showas)
|
||||||
{
|
{
|
||||||
const struct map_type2str *mt;
|
struct formatvec *fv;
|
||||||
|
|
||||||
for (mt = &msgmap[0]; mt->mt_str; mt++)
|
for (fv=_FORMATS; fv->fv_int != -1; fv++)
|
||||||
if (mt->mt_type == type)
|
if (fv->fv_int == showas)
|
||||||
return mt->mt_str;
|
break;
|
||||||
return NULL;
|
return fv?(fv->fv_str?fv->fv_str:"unknown"):"unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Translate from string to numeric format representation
|
||||||
|
* @param[in] str String value
|
||||||
|
* @retval enum Format value (see enum format_enum)
|
||||||
|
*/
|
||||||
|
enum format_enum
|
||||||
|
format_str2int(char *str)
|
||||||
|
{
|
||||||
|
struct formatvec *fv;
|
||||||
|
|
||||||
|
for (fv=_FORMATS; fv->fv_int != -1; fv++)
|
||||||
|
if (strcmp(fv->fv_str, str) == 0)
|
||||||
|
break;
|
||||||
|
return fv?fv->fv_int:-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Encode a clicon netconf message
|
||||||
|
* @param[in] format Variable agrument list format an XML netconf string
|
||||||
|
* @retval msg Clicon message to send to eg clicon_msg_send()
|
||||||
|
*/
|
||||||
|
struct clicon_msg *
|
||||||
|
clicon_msg_encode(char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
int xmllen;
|
||||||
|
int len;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
int hdrlen = sizeof(*msg);
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
xmllen = vsnprintf(NULL, 0, format, args) + 1;
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
len = hdrlen + xmllen;
|
||||||
|
if ((msg = (struct clicon_msg *)malloc(len)) == NULL){
|
||||||
|
clicon_err(OE_PROTO, errno, "malloc");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(msg, 0, len);
|
||||||
|
/* hdr */
|
||||||
|
msg->op_len = htons(len);
|
||||||
|
|
||||||
|
/* body */
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(msg->op_body, xmllen, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Decode a clicon netconf message
|
||||||
|
* @param[in] msg CLICON msg
|
||||||
|
* @param[out] xml XML parse tree
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_msg_decode(struct clicon_msg *msg,
|
||||||
|
cxobj **xml)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *xmlstr;
|
||||||
|
|
||||||
|
/* body */
|
||||||
|
xmlstr = msg->op_body;
|
||||||
|
clicon_debug(1, "%s %s", __FUNCTION__, xmlstr);
|
||||||
|
if (clicon_xml_parse_str(xmlstr, xml) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Open local connection using unix domain sockets
|
/*! Open local connection using unix domain sockets
|
||||||
|
* @param[in] sockpath Unix domain file path
|
||||||
|
* @retval s socket
|
||||||
|
* @retval -1 error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_connect_unix(char *sockpath)
|
clicon_connect_unix(char *sockpath)
|
||||||
|
|
@ -147,13 +217,19 @@ atomicio_sig_handler(int arg)
|
||||||
_atomicio_sig++;
|
_atomicio_sig++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Ensure all of data on socket comes through. fn is either read or write
|
/*! Ensure all of data on socket comes through. fn is either read or write
|
||||||
|
* @param[in] fn I/O function, ie read/write
|
||||||
|
* @param[in] fd File descriptor, eg socket
|
||||||
|
* @param[in] s0 Buffer to read to or write from
|
||||||
|
* @param[in] n Number of bytes to read/write, loop until done
|
||||||
*/
|
*/
|
||||||
static ssize_t
|
static ssize_t
|
||||||
atomicio(ssize_t (*fn) (int, void *, size_t), int fd, void *_s, size_t n)
|
atomicio(ssize_t (*fn) (int, void *, size_t),
|
||||||
|
int fd,
|
||||||
|
void *s0,
|
||||||
|
size_t n)
|
||||||
{
|
{
|
||||||
char *s = _s;
|
char *s = s0;
|
||||||
ssize_t res, pos = 0;
|
ssize_t res, pos = 0;
|
||||||
|
|
||||||
while (n > pos) {
|
while (n > pos) {
|
||||||
|
|
@ -177,6 +253,9 @@ atomicio(ssize_t (*fn) (int, void *, size_t), int fd, void *_s, size_t n)
|
||||||
return (pos);
|
return (pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Print message on debug. Log if syslog, stderr if not
|
||||||
|
* @param[in] msg CLICON msg
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
msg_dump(struct clicon_msg *msg)
|
msg_dump(struct clicon_msg *msg)
|
||||||
{
|
{
|
||||||
|
|
@ -202,14 +281,18 @@ msg_dump(struct clicon_msg *msg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Send a CLICON netconf message
|
||||||
|
* @param[in] s socket (unix or inet) to communicate with backend
|
||||||
|
* @param[out] msg CLICON msg data reply structure. Free with free()
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
clicon_msg_send(int s,
|
clicon_msg_send(int s,
|
||||||
struct clicon_msg *msg)
|
struct clicon_msg *msg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
clicon_debug(2, "%s: send msg seq=%d len=%d",
|
clicon_debug(2, "%s: send msg len=%d",
|
||||||
__FUNCTION__, ntohs(msg->op_type), ntohs(msg->op_len));
|
__FUNCTION__, ntohs(msg->op_len));
|
||||||
if (debug > 2)
|
if (debug > 2)
|
||||||
msg_dump(msg);
|
msg_dump(msg);
|
||||||
if (atomicio((ssize_t (*)(int, void *, size_t))write,
|
if (atomicio((ssize_t (*)(int, void *, size_t))write,
|
||||||
|
|
@ -223,7 +306,7 @@ clicon_msg_send(int s,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Receive a CLICON message on a UNIX domain socket
|
/*! Receive a CLICON message
|
||||||
*
|
*
|
||||||
* XXX: timeout? and signals?
|
* XXX: timeout? and signals?
|
||||||
* There is rudimentary code for turning on signals and handling them
|
* There is rudimentary code for turning on signals and handling them
|
||||||
|
|
@ -233,18 +316,15 @@ clicon_msg_send(int s,
|
||||||
* behaviour.
|
* behaviour.
|
||||||
* Now, ^C will interrupt the whole process, and this may not be what you want.
|
* Now, ^C will interrupt the whole process, and this may not be what you want.
|
||||||
*
|
*
|
||||||
* @param[in] s UNIX domain socket to communicate with backend
|
* @param[in] s socket (unix or inet) to communicate with backend
|
||||||
* @param[out] msg CLICON msg data reply structure. allocated using CLICON chunks,
|
* @param[out] msg CLICON msg data reply structure. Free with free()
|
||||||
* freed by caller with unchunk*(...,label)
|
|
||||||
* @param[out] eof Set if eof encountered
|
* @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.
|
* Note: caller must ensure that s is closed if eof is set after call.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_msg_rcv(int s,
|
clicon_msg_rcv(int s,
|
||||||
struct clicon_msg **msg,
|
struct clicon_msg **msg,
|
||||||
int *eof,
|
int *eof)
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg hdr;
|
struct clicon_msg hdr;
|
||||||
|
|
@ -271,10 +351,10 @@ clicon_msg_rcv(int s,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
mlen = ntohs(hdr.op_len);
|
mlen = ntohs(hdr.op_len);
|
||||||
clicon_debug(2, "%s: rcv msg seq=%d, len=%d",
|
clicon_debug(2, "%s: rcv msg len=%d",
|
||||||
__FUNCTION__, ntohs(hdr.op_type), mlen);
|
__FUNCTION__, mlen);
|
||||||
if ((*msg = (struct clicon_msg *)chunk(mlen, label)) == NULL){
|
if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){
|
||||||
clicon_err(OE_CFG, errno, "%s: chunk", __FUNCTION__);
|
clicon_err(OE_CFG, errno, "malloc");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
memcpy(*msg, &hdr, hlen);
|
memcpy(*msg, &hdr, hlen);
|
||||||
|
|
@ -295,25 +375,27 @@ clicon_msg_rcv(int s,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Connect to server, send a clicon_msg message and wait for result using unix socket
|
||||||
/*! 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.
|
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
|
||||||
* NOTE: this is dependent on unix domain
|
* @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
|
int
|
||||||
clicon_rpc_connect_unix(struct clicon_msg *msg,
|
clicon_rpc_connect_unix(struct clicon_msg *msg,
|
||||||
char *sockpath,
|
char *sockpath,
|
||||||
char **data,
|
char **retdata,
|
||||||
uint16_t *datalen,
|
int *sock0)
|
||||||
int *sock0,
|
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int s = -1;
|
int s = -1;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
|
||||||
clicon_debug(1, "Send %s msg on %s",
|
clicon_debug(1, "Send msg on %s", sockpath);
|
||||||
msg_type2str(ntohs(msg->op_type)), sockpath);
|
|
||||||
/* special error handling to get understandable messages (otherwise ENOENT) */
|
/* special error handling to get understandable messages (otherwise ENOENT) */
|
||||||
if (stat(sockpath, &sb) < 0){
|
if (stat(sockpath, &sb) < 0){
|
||||||
clicon_err(OE_PROTO, errno, "%s: config daemon not running?", sockpath);
|
clicon_err(OE_PROTO, errno, "%s: config daemon not running?", sockpath);
|
||||||
|
|
@ -325,7 +407,7 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
|
||||||
}
|
}
|
||||||
if ((s = clicon_connect_unix(sockpath)) < 0)
|
if ((s = clicon_connect_unix(sockpath)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc(s, msg, data, datalen, label) < 0)
|
if (clicon_rpc(s, msg, retdata) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (sock0 != NULL)
|
if (sock0 != NULL)
|
||||||
*sock0 = s;
|
*sock0 = s;
|
||||||
|
|
@ -336,24 +418,29 @@ clicon_rpc_connect_unix(struct clicon_msg *msg,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Connect to server, send an clicon_msg message and wait for result using an inet socket
|
/*! Connect to server, send a 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.
|
* 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
|
int
|
||||||
clicon_rpc_connect_inet(struct clicon_msg *msg,
|
clicon_rpc_connect_inet(struct clicon_msg *msg,
|
||||||
char *dst,
|
char *dst,
|
||||||
uint16_t port,
|
uint16_t port,
|
||||||
char **data,
|
char **retdata,
|
||||||
uint16_t *datalen,
|
int *sock0)
|
||||||
int *sock0,
|
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int s = -1;
|
int s = -1;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
clicon_debug(1, "Send %s msg to %s:%hu",
|
clicon_debug(1, "Send msg to %s:%hu", dst, port);
|
||||||
msg_type2str(ntohs(msg->op_type)), dst, port);
|
|
||||||
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
|
|
@ -371,7 +458,7 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
|
||||||
close(s);
|
close(s);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (clicon_rpc(s, msg, data, datalen, label) < 0)
|
if (clicon_rpc(s, msg, retdata) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (sock0 != NULL)
|
if (sock0 != NULL)
|
||||||
*sock0 = s;
|
*sock0 = s;
|
||||||
|
|
@ -391,29 +478,24 @@ clicon_rpc_connect_inet(struct clicon_msg *msg,
|
||||||
*
|
*
|
||||||
* @param[in] s Socket to communicate with backend
|
* @param[in] s Socket to communicate with backend
|
||||||
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
|
* @param[in] msg CLICON msg data structure. It has fixed header and variable body.
|
||||||
* @param[out] data Returned data as byte-strin exclusing header.
|
* @param[out] xret Returned data as netconf xml tree.
|
||||||
* Deallocate w unchunk...(..., label)
|
* @retval 0 OK
|
||||||
* @param[out] datalen Length of returned data
|
* @retval -1 Error
|
||||||
* @param[in] label Label used in chunk allocation.
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc(int s,
|
clicon_rpc(int s,
|
||||||
struct clicon_msg *msg,
|
struct clicon_msg *msg,
|
||||||
char **data,
|
char **ret)
|
||||||
uint16_t *datalen,
|
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *reply;
|
struct clicon_msg *reply;
|
||||||
int eof;
|
int eof;
|
||||||
uint32_t err;
|
char *data = NULL;
|
||||||
uint32_t suberr;
|
cxobj *cx = NULL;
|
||||||
char *reason;
|
|
||||||
enum clicon_msg_type type;
|
|
||||||
|
|
||||||
if (clicon_msg_send(s, msg) < 0)
|
if (clicon_msg_send(s, msg) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_msg_rcv(s, &reply, &eof, label) < 0)
|
if (clicon_msg_rcv(s, &reply, &eof) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (eof){
|
if (eof){
|
||||||
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
|
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
|
||||||
|
|
@ -421,37 +503,31 @@ clicon_rpc(int s,
|
||||||
errno = ESHUTDOWN;
|
errno = ESHUTDOWN;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
type = ntohs(reply->op_type);
|
data = reply->op_body; /* assume string */
|
||||||
switch (type){
|
if (ret && data)
|
||||||
case CLICON_MSG_OK:
|
if ((*ret = strdup(data)) == NULL){
|
||||||
if (data != NULL) {
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
*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;
|
goto done;
|
||||||
if (debug)
|
}
|
||||||
clicon_err(err, suberr, "%s msgtype:%hu", reason, ntohs(msg->op_type));
|
|
||||||
else
|
|
||||||
clicon_err(err, suberr, "%s", reason);
|
|
||||||
goto done;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
clicon_err(OE_PROTO, 0, "%s: unexpected reply: %hu",
|
|
||||||
__FUNCTION__, type);
|
|
||||||
goto done;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (cx)
|
||||||
|
xml_free(cx);
|
||||||
|
if (reply)
|
||||||
|
free(reply);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Send a clicon_msg message as reply to a clicon rpc request
|
||||||
|
*
|
||||||
|
* @param[in] s Socket to communicate with client
|
||||||
|
* @param[in] data Returned data as byte-string.
|
||||||
|
* @param[in] datalen Length of returned data XXX may be unecessary if always string?
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
send_msg_reply(int s,
|
send_msg_reply(int s,
|
||||||
uint16_t type,
|
|
||||||
char *data,
|
char *data,
|
||||||
uint16_t datalen)
|
uint16_t datalen)
|
||||||
{
|
{
|
||||||
|
|
@ -463,7 +539,6 @@ send_msg_reply(int s,
|
||||||
if ((reply = (struct clicon_msg *)chunk(len, __FUNCTION__)) == NULL)
|
if ((reply = (struct clicon_msg *)chunk(len, __FUNCTION__)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
memset(reply, 0, len);
|
memset(reply, 0, len);
|
||||||
reply->op_type = htons(type);
|
|
||||||
reply->op_len = htons(len);
|
reply->op_len = htons(len);
|
||||||
if (datalen > 0)
|
if (datalen > 0)
|
||||||
memcpy(reply->op_body, data, datalen);
|
memcpy(reply->op_body, data, datalen);
|
||||||
|
|
@ -475,57 +550,55 @@ send_msg_reply(int s,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
/*! Send a clicon_msg NOTIFY message asynchronously to client
|
||||||
send_msg_ok(int s)
|
*
|
||||||
{
|
* @param[in] s Socket to communicate with client
|
||||||
return send_msg_reply(s, CLICON_MSG_OK, NULL, 0);
|
* @param[in] level
|
||||||
}
|
* @param[in] event
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
send_msg_notify(int s,
|
send_msg_notify(int s,
|
||||||
int level,
|
int level,
|
||||||
char *event)
|
char *event)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
struct clicon_msg *msg = NULL;
|
||||||
|
|
||||||
if ((msg=clicon_msg_notify_encode(level, event, __FUNCTION__)) == NULL)
|
if ((msg=clicon_msg_encode("<notification><event>%s</event></notification>", event)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_msg_send(s, msg) < 0)
|
if (clicon_msg_send(s, msg) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (msg)
|
||||||
|
free(msg);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Look for a text pattern in an input string, one char at a time
|
||||||
|
* @param[in] tag What to look for
|
||||||
|
* @param[in] ch New input character
|
||||||
|
* @param[in,out] state A state integer holding how far we have parsed.
|
||||||
|
* @retval 0 No, we havent detected end tag
|
||||||
|
* @retval 1 Yes, we have detected end tag!
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
send_msg_err(int s, int err, int suberr, char *format, ...)
|
detect_endtag(char *tag,
|
||||||
|
char ch,
|
||||||
|
int *state)
|
||||||
{
|
{
|
||||||
va_list args;
|
int retval = 0;
|
||||||
char *reason;
|
|
||||||
int len;
|
|
||||||
int retval = -1;
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
|
|
||||||
va_start(args, format);
|
if (tag[*state] == ch){
|
||||||
len = vsnprintf(NULL, 0, format, args) + 1;
|
(*state)++;
|
||||||
va_end(args);
|
if (*state == strlen(tag)){
|
||||||
if ((reason = (char *)chunk(len, __FUNCTION__)) == NULL)
|
*state = 0;
|
||||||
return -1;
|
retval = 1;
|
||||||
memset(reason, 0, len);
|
}
|
||||||
va_start(args, format);
|
}
|
||||||
vsnprintf(reason, len, format, args);
|
else
|
||||||
va_end(args);
|
*state = 0;
|
||||||
if ((msg=clicon_msg_err_encode(clicon_errno, clicon_suberrno,
|
|
||||||
reason, __FUNCTION__)) == NULL)
|
|
||||||
goto done;
|
|
||||||
if (clicon_msg_send(s, msg) < 0)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
retval = 0;
|
|
||||||
done:
|
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -56,38 +57,36 @@
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
#include "clixon_chunk.h"
|
||||||
|
#include "clixon_log.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
|
#include "clixon_xml.h"
|
||||||
|
#include "clixon_xsl.h"
|
||||||
#include "clixon_proto.h"
|
#include "clixon_proto.h"
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_xml.h"
|
|
||||||
#include "clixon_proto_encode.h"
|
|
||||||
#include "clixon_proto_client.h"
|
#include "clixon_proto_client.h"
|
||||||
|
|
||||||
/*! Internal rpc function
|
/*! Send internal netconf rpc from client to backend
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] msg Encoded message
|
* @param[in] msg Encoded message. Deallocate woth free
|
||||||
* @param[out] ret Return value from backend server (reply)
|
* @param[out] xret Return value from backend as netconf xml tree. Free w xml_free
|
||||||
* @param[out] retlen Length of return value
|
|
||||||
* @param[inout] sock0 If pointer exists, do not close socket to backend on success
|
* @param[inout] sock0 If pointer exists, do not close socket to backend on success
|
||||||
* and return it here. For keeping a notify socket open
|
* 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
|
* Note: sock0 is if connection should be persistent, like a notification/subscribe api
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
clicon_rpc_msg(clicon_handle h,
|
clicon_rpc_msg(clicon_handle h,
|
||||||
struct clicon_msg *msg,
|
struct clicon_msg *msg,
|
||||||
char **ret,
|
cxobj **xret0,
|
||||||
uint16_t *retlen,
|
int *sock0)
|
||||||
int *sock0,
|
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *sock;
|
char *sock;
|
||||||
int port;
|
int port;
|
||||||
|
char *retdata = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
if ((sock = clicon_sock(h)) == NULL){
|
if ((sock = clicon_sock(h)) == NULL){
|
||||||
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
|
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
|
||||||
|
|
@ -96,7 +95,7 @@ clicon_rpc_msg(clicon_handle h,
|
||||||
/* What to do if inet socket? */
|
/* What to do if inet socket? */
|
||||||
switch (clicon_sock_family(h)){
|
switch (clicon_sock_family(h)){
|
||||||
case AF_UNIX:
|
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 0
|
||||||
if (errno == ESHUTDOWN)
|
if (errno == ESHUTDOWN)
|
||||||
/* Maybe could reconnect on a higher layer, but lets fail
|
/* Maybe could reconnect on a higher layer, but lets fail
|
||||||
|
|
@ -115,231 +114,553 @@ clicon_rpc_msg(clicon_handle h,
|
||||||
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
|
clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
|
||||||
goto done;
|
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;
|
goto done;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata);
|
||||||
|
if (retdata &&
|
||||||
|
clicon_xml_parse_str(retdata, &xret) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xret0){
|
||||||
|
*xret0 = xret;
|
||||||
|
xret = NULL;
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (retdata)
|
||||||
|
free(retdata);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Commit changes send a commit request to backend daemon
|
/*! Generic xml netconf clicon rpc
|
||||||
* @param[in] h CLICON handle
|
* Want to go over to use netconf directly between client and server,...
|
||||||
* @param[in] from name of 'from' database (eg "candidate")
|
* @param[in] h clicon handle
|
||||||
* @param[in] db name of 'to' database (eg "running")
|
* @param[in] xmlstr XML netconf tree as string
|
||||||
* @retval 0 Copy current->candidate
|
* @param[out] xret Return XML netconf tree, error or OK
|
||||||
|
* @param[out] sp Socket pointer for notification, otherwise NULL
|
||||||
|
* @see clicon_rpc_netconf_xml xml as tree instead of string
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_commit(clicon_handle h,
|
clicon_rpc_netconf(clicon_handle h,
|
||||||
char *from,
|
char *xmlstr,
|
||||||
char *to)
|
cxobj **xret,
|
||||||
|
int *sp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
struct clicon_msg *msg = NULL;
|
||||||
|
|
||||||
if ((msg=clicon_msg_commit_encode(from, to, __FUNCTION__)) == NULL)
|
if ((msg = clicon_msg_encode("%s", xmlstr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
if (clicon_rpc_msg(h, msg, xret, sp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (msg)
|
||||||
|
free(msg);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Send validate request to backend daemon
|
/*! Generic xml netconf clicon rpc
|
||||||
* @param[in] h CLICON handle
|
* Want to go over to use netconf directly between client and server,...
|
||||||
* @param[in] db Name of database
|
* @param[in] h clicon handle
|
||||||
* @retval 0
|
* @param[in] xml XML netconf tree
|
||||||
|
* @param[out] xret Return XML netconf tree, error or OK
|
||||||
|
* @param[out] sp Socket pointer for notification, otherwise NULL
|
||||||
|
* @see clicon_rpc_netconf xml as string instead of tree
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_validate(clicon_handle h,
|
clicon_rpc_netconf_xml(clicon_handle h,
|
||||||
char *db)
|
cxobj *xml,
|
||||||
|
cxobj **xret,
|
||||||
|
int *sp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
cbuf *cb = NULL;
|
||||||
|
|
||||||
if ((msg=clicon_msg_validate_encode(db, __FUNCTION__)) == NULL)
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
}
|
||||||
|
if (clicon_xml2cbuf(cb, xml, 0, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
if (clicon_rpc_netconf(h, cbuf_get(cb), xret, sp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Send database change request to backend daemon, variant for xmldb
|
/*! Generate clicon error function call from Netconf error message
|
||||||
* Same as clicon_proto_change just with a string
|
* @param[in] xerr Netconf error message on the level: <rpc-reply><rpc-error>
|
||||||
* @param[in] h CLICON handle
|
|
||||||
* @param[in] db Name of database
|
|
||||||
* @param[in] op Operation on database item: set, delete, (merge?)
|
|
||||||
* @param[in] key Database key
|
|
||||||
* @param[in] value value as string
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
* @note special case: remove all: key:"/" op:OP_REMOVE
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_change(clicon_handle h,
|
clicon_rpc_generate_error(cxobj *xerr)
|
||||||
char *db,
|
|
||||||
enum operation_type op,
|
|
||||||
char *key,
|
|
||||||
char *val)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
cbuf *cb = NULL;
|
||||||
|
cxobj *x;
|
||||||
|
|
||||||
if ((msg = clicon_msg_change_encode(db,
|
if ((cb = cbuf_new()) ==NULL){
|
||||||
op,
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
key,
|
|
||||||
val,
|
|
||||||
val?strlen(val)+1:0,
|
|
||||||
__FUNCTION__)) == NULL)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
}
|
||||||
|
if ((x=xpath_first(xerr, "error-type"))!=NULL)
|
||||||
|
cprintf(cb, "%s ", xml_body(x));
|
||||||
|
if ((x=xpath_first(xerr, "error-tag"))!=NULL)
|
||||||
|
cprintf(cb, "%s ", xml_body(x));
|
||||||
|
if ((x=xpath_first(xerr, "error-message"))!=NULL)
|
||||||
|
cprintf(cb, "%s ", xml_body(x));
|
||||||
|
if ((x=xpath_first(xerr, "error-info"))!=NULL)
|
||||||
|
clicon_xml2cbuf(cb, xml_child_i(x,0), 0, 0);
|
||||||
|
clicon_err_fn("Clixon", 0, OE_XML, 0, "%s", cbuf_get(cb));
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Get database configuration
|
||||||
|
* Same as clicon_proto_change just with a cvec instead of lvec
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @param[in] db Name of database
|
||||||
|
* @param[in] xpath XPath (or "")
|
||||||
|
* @param[out] xt XML tree. must be freed by caller with xml_free
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error, fatal or xml
|
||||||
|
* @code
|
||||||
|
* cxobj *xt = NULL;
|
||||||
|
* if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
|
||||||
|
* err;
|
||||||
|
* if (xt)
|
||||||
|
* xml_free(xt);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_get_config(clicon_handle h,
|
||||||
|
char *db,
|
||||||
|
char *xpath,
|
||||||
|
cxobj **xt)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
cxobj *xd;
|
||||||
|
|
||||||
|
if ((cb = cbuf_new()) == NULL)
|
||||||
goto done;
|
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_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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
if (msg)
|
||||||
|
free(msg);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Send database entries as XML to backend daemon
|
/*! 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] h CLICON handle
|
||||||
* @param[in] db Name of database
|
* @param[in] db Name of database
|
||||||
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
|
* @param[in] op Operation on database item: OP_MERGE, OP_REPLACE
|
||||||
* @param[in] api_path restconf API Path (or "")
|
* @param[in] api_path restconf API Path (or "")
|
||||||
* @param[in] xml XML string. Ex: <a>..</a><b>...</b>
|
* @param[in] xml XML string. Ex: <config><a>..</a><b>...</b></config>
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
* @note xml arg need to have <config> as top element
|
||||||
|
* @code
|
||||||
|
* if (clicon_rpc_edit_config(h, "running", OP_MERGE, "/",
|
||||||
|
* "<config><a>4</a></config>") < 0)
|
||||||
|
* err;
|
||||||
|
* @endcode
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_xmlput(clicon_handle h,
|
clicon_rpc_edit_config(clicon_handle h,
|
||||||
char *db,
|
char *db,
|
||||||
enum operation_type op,
|
enum operation_type op,
|
||||||
char *api_path,
|
char *api_path,
|
||||||
char *xml)
|
char *xmlstr)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
struct clicon_msg *msg = NULL;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
if ((msg = clicon_msg_xmlput_encode(db,
|
if ((cb = cbuf_new()) == NULL)
|
||||||
(uint32_t)op,
|
|
||||||
api_path,
|
|
||||||
xml,
|
|
||||||
__FUNCTION__)) == NULL)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
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 (xmlstr)
|
||||||
|
cprintf(cb, "%s", xmlstr);
|
||||||
|
cprintf(cb, "</edit-config></rpc>");
|
||||||
|
if ((msg = clicon_msg_encode(cbuf_get(cb))) == NULL)
|
||||||
goto done;
|
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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (xret)
|
||||||
return retval;
|
xml_free(xret);
|
||||||
}
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
|
if (msg)
|
||||||
|
free(msg);
|
||||||
|
|
||||||
/*! 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;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Send a request to backend to copy a file from one location to another
|
/*! 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
|
* Note this assumes the backend can access these files and (usually) assumes
|
||||||
* clients and servers have the access to the same filesystem.
|
* clients and servers have the access to the same filesystem.
|
||||||
* @param[in] h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param[in] db1 src database, eg "candidate"
|
* @param[in] db1 src database, eg "running"
|
||||||
* @param[in] db2 dst database, eg "running"
|
* @param[in] db2 dst database, eg "startup"
|
||||||
|
* @code
|
||||||
|
* if (clicon_rpc_copy_config(h, "running", "startup") < 0)
|
||||||
|
* err;
|
||||||
|
* @endcode
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_copy(clicon_handle h,
|
clicon_rpc_copy_config(clicon_handle h,
|
||||||
char *db1,
|
char *db1,
|
||||||
char *db2)
|
char *db2)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
if ((msg=clicon_msg_copy_encode(db1, db2, __FUNCTION__)) == NULL)
|
if ((msg = clicon_msg_encode("<rpc><copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>", db1, db2)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
if (msg)
|
||||||
|
free(msg);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Send a request to backend to delete a config database
|
||||||
/*! Send a kill session request to backend server
|
* @param[in] h CLICON handle
|
||||||
* @param[in] h CLICON handle
|
* @param[in] db database, eg "running"
|
||||||
* @param[in] session_id Id of session to kill
|
* @code
|
||||||
|
* if (clicon_rpc_delete_config(h, "startup") < 0)
|
||||||
|
* err;
|
||||||
|
* @endcode
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_rpc_kill(clicon_handle h,
|
clicon_rpc_delete_config(clicon_handle h,
|
||||||
int session_id)
|
char *db)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
if ((msg=clicon_msg_kill_encode(session_id, __FUNCTION__)) == NULL)
|
if ((msg = clicon_msg_encode("<rpc><delete-config><target><%s/></target></delete-config></rpc>", db)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
if (msg)
|
||||||
|
free(msg);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Lock a database
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @param[in] db database, eg "running"
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_lock(clicon_handle h,
|
||||||
|
char *db)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Unlock a database
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @param[in] db database, eg "running"
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_unlock(clicon_handle h,
|
||||||
|
char *db)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Close a (user) session
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_close_session(clicon_handle h)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Kill other user sessions
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @param[in] session_id Session id of other user session
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_kill_session(clicon_handle h,
|
||||||
|
int session_id)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<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_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Commit changes send a commit request to backend daemon
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @retval 0 OK
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_commit(clicon_handle h)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<rpc><commit/></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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Discard all changes in candidate / revert to running
|
||||||
|
* @param[in] h CLICON handle
|
||||||
|
* @retval 0 OK
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_discard_changes(clicon_handle h)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<rpc><discard-changes/></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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Create a new notification subscription
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param{in] stream name of notificatio/log stream (CLICON is predefined)
|
||||||
|
* @param{in] filter message filter, eg xpath for xml notifications
|
||||||
|
* @param[out] s0 socket returned where notification mesages will appear
|
||||||
|
* @note When using netconf create-subsrciption,status and format is not supported
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
clicon_rpc_create_subscription(clicon_handle h,
|
||||||
|
char *stream,
|
||||||
|
char *filter,
|
||||||
|
int *s0)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
|
if ((msg = clicon_msg_encode("<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, &xret, s0) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||||
|
clicon_rpc_generate_error(xerr);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
|
if (msg)
|
||||||
|
free(msg);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,107 +673,28 @@ clicon_rpc_debug(clicon_handle h,
|
||||||
int level)
|
int level)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *msg;
|
struct clicon_msg *msg = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
if ((msg=clicon_msg_debug_encode(level, __FUNCTION__)) == NULL)
|
if ((msg = clicon_msg_encode("<rpc><debug><level>%d</level></debug></rpc>", level)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, NULL, NULL, NULL, __FUNCTION__) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||||
done:
|
clicon_rpc_generate_error(xerr);
|
||||||
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)
|
|
||||||
goto done;
|
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;
|
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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (msg)
|
||||||
|
free(msg);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,924 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
***** BEGIN LICENSE BLOCK *****
|
|
||||||
|
|
||||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
|
||||||
|
|
||||||
This file is part of CLIXON.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
|
|
||||||
Alternatively, the contents of this file may be used under the terms of
|
|
||||||
the GNU General Public License Version 3 or later (the "GPL"),
|
|
||||||
in which case the provisions of the GPL are applicable instead
|
|
||||||
of those above. If you wish to allow use of your version of this file only
|
|
||||||
under the terms of the GPL, and not to allow others to
|
|
||||||
use your version of this file under the terms of Apache License version 2,
|
|
||||||
indicate your decision by deleting the provisions above and replace them with
|
|
||||||
the notice and other provisions required by the GPL. If you do not delete
|
|
||||||
the provisions above, a recipient may use your version of this file under
|
|
||||||
the terms of any one of the Apache License version 2 or the GPL.
|
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
|
||||||
|
|
||||||
*
|
|
||||||
* Protocol to communicate between clients (eg clicon_cli, clicon_netconf)
|
|
||||||
* and server (clicon_backend)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "clixon_config.h" /* generated by config & autoconf */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <syslog.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.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_chunk.h"
|
|
||||||
#include "clixon_sig.h"
|
|
||||||
#include "clixon_hash.h"
|
|
||||||
#include "clixon_handle.h"
|
|
||||||
#include "clixon_proto.h"
|
|
||||||
#include "clixon_proto_encode.h"
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_commit_encode(char *dbsrc, char *dbdst,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: dbsrc: %s dbdst: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
dbsrc, dbdst);
|
|
||||||
p = 0;
|
|
||||||
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(dbsrc) + 1 +
|
|
||||||
strlen(dbdst) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_COMMIT);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
strncpy(msg->op_body+p, dbsrc, len-p-hdrlen);
|
|
||||||
p += strlen(dbsrc)+1;
|
|
||||||
strncpy(msg->op_body+p, dbdst, len-p-hdrlen);
|
|
||||||
p += strlen(dbdst)+1;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_commit_decode(struct clicon_msg *msg,
|
|
||||||
char **dbsrc, char **dbdst,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
if ((*dbsrc = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*dbsrc)+1;
|
|
||||||
if ((*dbdst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*dbdst)+1;
|
|
||||||
clicon_debug(2, "%s: dbsrc: %s dbdst: %s", __FUNCTION__, *dbsrc, *dbdst);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_validate_encode(char *db, const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, db);
|
|
||||||
len = sizeof(*msg) + strlen(db) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_VALIDATE);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
strncpy(msg->op_body, db, len-hdrlen);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_validate_decode(struct clicon_msg *msg, char **db, const char *label)
|
|
||||||
{
|
|
||||||
/* body */
|
|
||||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
clicon_debug(2, "%s: db: %s", __FUNCTION__, *db);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_change_encode(char *db,
|
|
||||||
uint32_t op,
|
|
||||||
char *key,
|
|
||||||
char *str,
|
|
||||||
uint32_t str_len,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: op: %d str_len: %d db: %s key: '%s'",
|
|
||||||
__FUNCTION__,
|
|
||||||
op, str_len, db, key);
|
|
||||||
p = 0;
|
|
||||||
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(db) + 1 +
|
|
||||||
strlen(key) + str_len;
|
|
||||||
if (str_len)
|
|
||||||
len++; /* if str not null add end of string */
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_CHANGE);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(op);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
tmp = htonl(str_len);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
|
||||||
p += strlen(db)+1;
|
|
||||||
strncpy(msg->op_body+p, key, len-p-hdrlen);
|
|
||||||
p += strlen(key)+1;
|
|
||||||
if (str_len){
|
|
||||||
memcpy(msg->op_body+p, str, str_len);
|
|
||||||
p += str_len;
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_change_decode(struct clicon_msg *msg,
|
|
||||||
char **db,
|
|
||||||
uint32_t *op,
|
|
||||||
char **key,
|
|
||||||
char **str,
|
|
||||||
uint32_t *str_len,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*op = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*str_len = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*db)+1;
|
|
||||||
if ((*key = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*key)+1;
|
|
||||||
|
|
||||||
if (*str_len){
|
|
||||||
if ((*str = chunk(*str_len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(*str, msg->op_body+p, *str_len);
|
|
||||||
p += *str_len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*str = NULL;
|
|
||||||
clicon_debug(2, "%s: op: %d str_len: %d db: %s key: '%s'",
|
|
||||||
__FUNCTION__,
|
|
||||||
*op, *str_len, *db, *key);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Encode xmlput / edit of database content
|
|
||||||
* @param[in] db Name of database
|
|
||||||
* @param[in] op set|merge|delete. See lv_op_t
|
|
||||||
* @param[in] api_path restconf api path
|
|
||||||
* @param[in] xml XML data string
|
|
||||||
* @param[in] label Memory chunk label
|
|
||||||
* @retval msg Encoded message
|
|
||||||
* @retval NULL Error
|
|
||||||
*/
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_xmlput_encode(char *db,
|
|
||||||
uint32_t op,
|
|
||||||
char *api_path,
|
|
||||||
char *xml,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
op, db, api_path, xml);
|
|
||||||
p = 0;
|
|
||||||
hdrlen = sizeof(*msg);
|
|
||||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(api_path) + 1 +strlen(xml) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_XMLPUT);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(op);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
|
||||||
p += strlen(db)+1;
|
|
||||||
strncpy(msg->op_body+p, api_path, len-p-hdrlen);
|
|
||||||
p += strlen(api_path)+1;
|
|
||||||
strncpy(msg->op_body+p, xml, len-p-hdrlen);
|
|
||||||
p += strlen(xml)+1;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Decode xmlput / edit of database content
|
|
||||||
* @param[in] msg Incoming message to be decoded
|
|
||||||
* @param[out] db Name of database
|
|
||||||
* @param[out] op set|merge|delete. See lv_op_t
|
|
||||||
* @param[out] api_path restconf api path
|
|
||||||
* @param[out] xml XML data string
|
|
||||||
* @param[in] label Memory chunk label
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
clicon_msg_xmlput_decode(struct clicon_msg *msg,
|
|
||||||
char **db,
|
|
||||||
uint32_t *op,
|
|
||||||
char **api_path,
|
|
||||||
char **xml,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*op = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*db)+1;
|
|
||||||
if ((*api_path = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*api_path)+1;
|
|
||||||
if ((*xml = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*xml)+1;
|
|
||||||
clicon_debug(2, "%s: op: %d db: %s api_path: %s xml: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
*op, *db, *api_path, *xml);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: snapshot: %d db: %s filename: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
snapshot, db, filename);
|
|
||||||
p = 0;
|
|
||||||
hdrlen = sizeof(*msg);
|
|
||||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1;
|
|
||||||
if (!snapshot)
|
|
||||||
len += strlen(filename) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_SAVE);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(snapshot);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
|
||||||
p += strlen(db)+1;
|
|
||||||
if (!snapshot){
|
|
||||||
strncpy(msg->op_body+p, filename, len-p-hdrlen);
|
|
||||||
p += strlen(filename)+1;
|
|
||||||
}
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_save_decode(struct clicon_msg *msg,
|
|
||||||
char **db, uint32_t *snapshot, char **filename,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*snapshot = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*db)+1;
|
|
||||||
if (*snapshot == 0){
|
|
||||||
if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*filename)+1;
|
|
||||||
}
|
|
||||||
clicon_debug(2, "%s: snapshot: %d db: %s filename: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
*snapshot, *db, *filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_load_encode(int replace, char *db, char *filename, const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
uint16_t len;
|
|
||||||
uint32_t tmp;
|
|
||||||
int p;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: replace: %d db: %s filename: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
replace, db, filename);
|
|
||||||
p = 0;
|
|
||||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(filename) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_LOAD);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(replace);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
|
|
||||||
strncpy(msg->op_body+p, db, len-p-hdrlen);
|
|
||||||
p += strlen(db)+1;
|
|
||||||
strncpy(msg->op_body+p, filename, len-p-hdrlen);
|
|
||||||
p += strlen(filename)+1;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_load_decode(struct clicon_msg *msg,
|
|
||||||
int *replace,
|
|
||||||
char **db,
|
|
||||||
char **filename,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*replace = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*db)+1;
|
|
||||||
if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*filename)+1;
|
|
||||||
clicon_debug(2, "%s: %d db: %s filename: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
ntohs(msg->op_type),
|
|
||||||
*db, *filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_copy_encode(char *db_src, char *db_dst,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
uint16_t len;
|
|
||||||
int p;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: db_src: %s db_dst: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
db_src, db_dst);
|
|
||||||
p = 0;
|
|
||||||
len = hdrlen + strlen(db_src) + 1 + strlen(db_dst) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_COPY);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
strncpy(msg->op_body+p, db_src, len-p-hdrlen);
|
|
||||||
p += strlen(db_src)+1;
|
|
||||||
strncpy(msg->op_body+p, db_dst, len-p-hdrlen);
|
|
||||||
p += strlen(db_dst)+1;
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_copy_decode(struct clicon_msg *msg,
|
|
||||||
char **db_src, char **db_dst,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
if ((*db_src = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*db_src)+1;
|
|
||||||
|
|
||||||
if ((*db_dst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*db_dst)+1;
|
|
||||||
clicon_debug(2, "%s: db_src: %s db_dst: %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
*db_src, *db_dst);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_kill_encode(uint32_t session_id, const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: %d", __FUNCTION__, session_id);
|
|
||||||
p = 0;
|
|
||||||
len = sizeof(*msg) + sizeof(uint32_t);
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_KILL);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(session_id);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
return msg;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_kill_decode(struct clicon_msg *msg,
|
|
||||||
uint32_t *session_id,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*session_id = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *session_id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_debug_encode(uint32_t level, const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: %d", __FUNCTION__, label);
|
|
||||||
p = 0;
|
|
||||||
len = sizeof(*msg) + sizeof(uint32_t);
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_DEBUG);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(level);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
return msg;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_debug_decode(struct clicon_msg *msg,
|
|
||||||
uint32_t *level,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*level = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *level);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_call_encode(uint16_t op,
|
|
||||||
char *plugin,
|
|
||||||
char *func,
|
|
||||||
uint16_t arglen,
|
|
||||||
void *arg,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
struct clicon_msg_call_req *req;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int len;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: %d plugin: %s func: %s arglen: %d",
|
|
||||||
__FUNCTION__, op, plugin, func, arglen);
|
|
||||||
len =
|
|
||||||
hdrlen +
|
|
||||||
sizeof(struct clicon_msg_call_req) +
|
|
||||||
strlen(plugin) + 1 +
|
|
||||||
strlen(func) + 1 +
|
|
||||||
arglen;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_CALL);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* req */
|
|
||||||
req = (struct clicon_msg_call_req *)msg->op_body;
|
|
||||||
req->cr_len = htons(len - hdrlen);
|
|
||||||
req->cr_op = htons(op);
|
|
||||||
req->cr_plugin = req->cr_data;
|
|
||||||
strncpy(req->cr_plugin, plugin, strlen(plugin));
|
|
||||||
req->cr_func = req->cr_plugin + strlen(req->cr_plugin) + 1;
|
|
||||||
strncpy(req->cr_func, func, strlen(func));
|
|
||||||
req->cr_arglen = htons(arglen);
|
|
||||||
req->cr_arg = req->cr_func + strlen(req->cr_func) + 1;
|
|
||||||
memcpy(req->cr_arg, arg, arglen);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_call_decode(struct clicon_msg *msg,
|
|
||||||
struct clicon_msg_call_req **req,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
uint16_t len;
|
|
||||||
struct clicon_msg_call_req *r;
|
|
||||||
|
|
||||||
r = (struct clicon_msg_call_req *)msg->op_body;
|
|
||||||
len = ntohs(r->cr_len);
|
|
||||||
if ((*req = chunk(len, label)) == NULL) {
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(*req, r, len);
|
|
||||||
(*req)->cr_len = ntohs(r->cr_len);
|
|
||||||
(*req)->cr_op = ntohs(r->cr_op);
|
|
||||||
(*req)->cr_arglen = ntohs(r->cr_arglen);
|
|
||||||
(*req)->cr_plugin = (*req)->cr_data;
|
|
||||||
(*req)->cr_func = (*req)->cr_plugin + strlen((*req)->cr_plugin) +1;
|
|
||||||
(*req)->cr_arg = (*req)->cr_func + strlen((*req)->cr_func) +1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_subscription_encode(int status,
|
|
||||||
char *stream,
|
|
||||||
enum format_enum format,
|
|
||||||
char *filter,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
int tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: %d %d %s %s", __FUNCTION__, status, format, stream, filter);
|
|
||||||
p = 0;
|
|
||||||
assert(filter);
|
|
||||||
len = hdrlen + sizeof(uint32_t) + sizeof(uint32_t) + strlen(stream) + 1 + strlen(filter) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_SUBSCRIPTION);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(status);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(int));
|
|
||||||
p += sizeof(int);
|
|
||||||
|
|
||||||
tmp = htonl(format);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(int));
|
|
||||||
p += sizeof(int);
|
|
||||||
|
|
||||||
strncpy(msg->op_body+p, stream, len-p-hdrlen);
|
|
||||||
p += strlen(stream)+1;
|
|
||||||
|
|
||||||
strncpy(msg->op_body+p, filter, len-p-hdrlen);
|
|
||||||
p += strlen(filter)+1;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_subscription_decode(struct clicon_msg *msg,
|
|
||||||
int *status,
|
|
||||||
char **stream,
|
|
||||||
enum format_enum *format,
|
|
||||||
char **filter,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
int tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(int));
|
|
||||||
*status = ntohl(tmp);
|
|
||||||
p += sizeof(int);
|
|
||||||
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(int));
|
|
||||||
*format = ntohl(tmp);
|
|
||||||
p += sizeof(int);
|
|
||||||
|
|
||||||
if ((*stream = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*stream)+1;
|
|
||||||
|
|
||||||
if ((*filter = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*filter)+1;
|
|
||||||
clicon_debug(2, "%s: %d %s %d %s", __FUNCTION__, *status, *filter, *stream, *filter);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_notify_encode(int level, char *event, const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
int tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: %d %s", __FUNCTION__, level, event);
|
|
||||||
p = 0;
|
|
||||||
hdrlen = sizeof(*msg);
|
|
||||||
len = sizeof(*msg) + sizeof(uint32_t) + strlen(event) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_NOTIFY);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(level);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(int));
|
|
||||||
p += sizeof(int);
|
|
||||||
strncpy(msg->op_body+p, event, len-p-hdrlen);
|
|
||||||
p += strlen(event)+1;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_notify_decode(struct clicon_msg *msg,
|
|
||||||
int *level, char **event,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
int tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(int));
|
|
||||||
*level = ntohl(tmp);
|
|
||||||
p += sizeof(int);
|
|
||||||
if ((*event = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*event)+1;
|
|
||||||
clicon_debug(2, "%s: %d %s", __FUNCTION__, *level, *event);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct clicon_msg *
|
|
||||||
clicon_msg_err_encode(uint32_t err, uint32_t suberr, char *reason, const char *label)
|
|
||||||
{
|
|
||||||
struct clicon_msg *msg;
|
|
||||||
uint16_t len;
|
|
||||||
int hdrlen = sizeof(*msg);
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
clicon_debug(2, "%s: %d %d %s", __FUNCTION__, err, suberr, reason);
|
|
||||||
p = 0;
|
|
||||||
hdrlen = sizeof(*msg);
|
|
||||||
len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(reason) + 1;
|
|
||||||
if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(msg, 0, len);
|
|
||||||
/* hdr */
|
|
||||||
msg->op_type = htons(CLICON_MSG_ERR);
|
|
||||||
msg->op_len = htons(len);
|
|
||||||
/* body */
|
|
||||||
tmp = htonl(err);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
tmp = htonl(suberr);
|
|
||||||
memcpy(msg->op_body+p, &tmp, sizeof(uint32_t));
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
strncpy(msg->op_body+p, reason, len-p-hdrlen);
|
|
||||||
p += strlen(reason)+1;
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
clicon_msg_err_decode(struct clicon_msg *msg,
|
|
||||||
uint32_t *err, uint32_t *suberr, char **reason,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int p;
|
|
||||||
uint32_t tmp;
|
|
||||||
|
|
||||||
p = 0;
|
|
||||||
/* body */
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*err = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
memcpy(&tmp, msg->op_body+p, sizeof(uint32_t));
|
|
||||||
*suberr = ntohl(tmp);
|
|
||||||
p += sizeof(uint32_t);
|
|
||||||
if ((*reason = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){
|
|
||||||
clicon_err(OE_PROTO, errno, "%s: chunk_sprintf",
|
|
||||||
__FUNCTION__);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
p += strlen(*reason)+1;
|
|
||||||
clicon_debug(2, "%s: %d %d %s",
|
|
||||||
__FUNCTION__,
|
|
||||||
*err, *suberr, *reason);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -53,228 +53,89 @@
|
||||||
#include "clixon_string.h"
|
#include "clixon_string.h"
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
|
|
||||||
/*! Split string into a vector based on character delimiters
|
|
||||||
|
/*! Split string into a vector based on character delimiters. Using malloc
|
||||||
*
|
*
|
||||||
* The given string is split into a vector where the delimiter can be
|
* The given string is split into a vector where the delimiter can be
|
||||||
* any of the characters in the specified delimiter string.
|
* any of the characters in the specified delimiter string.
|
||||||
*
|
*
|
||||||
* The vector returned is one single memory chunk that must be unchunked
|
* The vector returned is one single memory block that must be freed
|
||||||
* by the caller
|
* by the caller
|
||||||
*
|
*
|
||||||
* @param[in] string String to be split
|
* @param[in] string String to be split
|
||||||
* @param[in] delim String of delimiter characters
|
* @param[in] delim String of delimiter characters
|
||||||
* @param[out] nvec Number of entries in returned vector
|
* @param[out] nvec Number of entries in returned vector
|
||||||
* @param[in] label Chunk label for returned vector
|
* @retval vec Vector of strings. NULL terminated. Free after use
|
||||||
* @retval vec Vector of strings. Free with unchunk
|
* @retval NULL Error *
|
||||||
* @retval NULL Error
|
|
||||||
* @see clicon_strsplit Operates on full string delimiters rather than
|
|
||||||
* individual character delimiters.
|
|
||||||
*/
|
*/
|
||||||
char **
|
char **
|
||||||
clicon_sepsplit (char *string,
|
clicon_strsep(char *string,
|
||||||
char *delim,
|
char *delim,
|
||||||
int *nvec,
|
int *nvec0)
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int idx;
|
char **vec = NULL;
|
||||||
size_t siz;
|
char *ptr;
|
||||||
char *s, *s0;
|
char *p;
|
||||||
char **vec, *vecp;
|
int nvec = 1;
|
||||||
|
int i;
|
||||||
*nvec = 0;
|
|
||||||
s0 = s = chunkdup (string, strlen(string)+1, __FUNCTION__);
|
|
||||||
while (strsep(&s, delim))
|
|
||||||
(*nvec)++;
|
|
||||||
unchunk (s0);
|
|
||||||
|
|
||||||
siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1;
|
|
||||||
vec = (char **) chunk (siz, label);
|
|
||||||
if (!vec) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
bzero (vec, siz);
|
|
||||||
|
|
||||||
vecp = (char *)&vec[*nvec +1];
|
|
||||||
bcopy (string, vecp, strlen (string));
|
|
||||||
|
|
||||||
for (idx = 0; idx < *nvec; idx++) {
|
|
||||||
vec[idx] = vecp;
|
|
||||||
strsep (&vecp, delim);
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Split string into a vector based on a string delimiter
|
|
||||||
*
|
|
||||||
* The given string is split into a vector where the delimited by the
|
|
||||||
* the full delimiter string. The matched delimiters are not part of the
|
|
||||||
* resulting vector.
|
|
||||||
*
|
|
||||||
* See also clicon_sepsplit() which is similar
|
|
||||||
*
|
|
||||||
* The vector returned is one single memory chunk that must be unchunked
|
|
||||||
* by the caller
|
|
||||||
*
|
|
||||||
* @param[in] string String to be split
|
|
||||||
* @param[in] delim String of delimiter characters
|
|
||||||
* @param[out] nvec Number of entries in returned vector
|
|
||||||
* @param[in] label Chunk label for returned vector
|
|
||||||
* @retval vec Vector of strings. Free with unchunk
|
|
||||||
* @retval NULL Error
|
|
||||||
* @see clicon_sepsplit Operates on individual character delimiters rather
|
|
||||||
* than full string delimiter.
|
|
||||||
*/
|
|
||||||
char **
|
|
||||||
clicon_strsplit (char *string,
|
|
||||||
char *delim,
|
|
||||||
int *nvec,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
int idx;
|
|
||||||
size_t siz;
|
size_t siz;
|
||||||
char *s;
|
char *s;
|
||||||
char **vec, *vecp;
|
char *d;
|
||||||
|
|
||||||
*nvec = 1;
|
if ((s = string)==NULL)
|
||||||
s = string;
|
goto done;
|
||||||
while ((s = strstr(s, delim))) {
|
while (*s){
|
||||||
s += strlen(delim);
|
if ((d = index(delim, *s)) != NULL)
|
||||||
(*nvec)++;
|
nvec++;
|
||||||
|
s++;
|
||||||
}
|
}
|
||||||
|
/* alloc vector and append copy of string */
|
||||||
siz = ((*nvec +1) * sizeof (char *)) + strlen(string) + 1;
|
siz = (nvec+1)* sizeof(char*) + strlen(string)+1;
|
||||||
vec = (char **) chunk (siz, label);
|
if ((vec = (char**)malloc(siz)) == NULL){
|
||||||
if (!vec) {
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
return NULL;
|
goto done;
|
||||||
}
|
}
|
||||||
bzero (vec, siz);
|
memset(vec, 0, siz);
|
||||||
|
ptr = (char*)vec + (nvec+1)* sizeof(char*); /* this is where ptr starts */
|
||||||
vecp = (char *)&vec[*nvec +1];
|
strncpy(ptr, string, strlen(string)+1);
|
||||||
bcopy (string, vecp, strlen (string));
|
i = 0;
|
||||||
|
while ((p = strsep(&ptr, delim)) != NULL)
|
||||||
s = vecp;
|
vec[i++] = p;
|
||||||
for (idx = 0; idx < *nvec; idx++) {
|
*nvec0 = nvec;
|
||||||
vec[idx] = s;
|
done:
|
||||||
if ((s = strstr(s, delim)) != NULL) {
|
|
||||||
*s = '\0';
|
|
||||||
s += strlen(delim);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Concatenate elements of a string array into a string.
|
/*! Concatenate elements of a string array into a string.
|
||||||
* An optional delimiter string can be specified which will be inserted betwen
|
* An optional delimiter string can be specified which will be inserted betwen
|
||||||
* each element.
|
* each element.
|
||||||
* @param[in] label Chunk label for returned vector
|
* @retval str Joined string. Free after use.
|
||||||
* @retval str Joined string. Free with unchunk()
|
|
||||||
* @retval NULL Failure
|
* @retval NULL Failure
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
clicon_strjoin (int argc,
|
clicon_strjoin(int argc,
|
||||||
char **argv,
|
char **argv,
|
||||||
char *delim,
|
char *delim)
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int len;
|
|
||||||
char *str;
|
|
||||||
|
|
||||||
len = 0;
|
|
||||||
for (i = 0; i < argc; i++)
|
|
||||||
len += strlen(argv[i]);
|
|
||||||
if (delim)
|
|
||||||
len += (strlen(delim) * argc);
|
|
||||||
len += 1; /* '\0' */
|
|
||||||
|
|
||||||
if ((str = chunk (len, label)) == NULL)
|
|
||||||
return NULL;
|
|
||||||
memset (str, '\0', len);
|
|
||||||
|
|
||||||
for (i = 0; i < argc; i++) {
|
|
||||||
if (i != 0)
|
|
||||||
strncat (str, delim, len - strlen(str));
|
|
||||||
strncat (str, argv[i], len - strlen(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Trim whitespace in beginning and end of string.
|
|
||||||
*
|
|
||||||
* @param[in] label Chunk label for returned vector
|
|
||||||
* @retval str Trimmed string. Free with unchunk()
|
|
||||||
* @retval NULL Failure
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
clicon_strtrim(char *str,
|
|
||||||
const char *label)
|
|
||||||
{
|
|
||||||
char *start, *end, *new;
|
|
||||||
|
|
||||||
start = str;
|
|
||||||
while (*start != '\0' && isspace(*start))
|
|
||||||
start++;
|
|
||||||
if (!strlen(start))
|
|
||||||
return (char *)chunkdup("\0", 1, label);
|
|
||||||
|
|
||||||
end = str + strlen(str) ;
|
|
||||||
while (end > str && isspace(*(end-1)))
|
|
||||||
end--;
|
|
||||||
if((new = chunkdup (start, end-start+1, label)))
|
|
||||||
new[end-start] = '\0';
|
|
||||||
|
|
||||||
return new;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Given a string s, on format: a[b], separate it into two parts: a and b
|
|
||||||
* [] are separators.
|
|
||||||
* alterative use:
|
|
||||||
* a/b -> a and b (where sep = "/")
|
|
||||||
* @param[in] label Chunk label for returned vector
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
clicon_sep(char *s,
|
|
||||||
const char sep[2],
|
|
||||||
const char *label,
|
|
||||||
char **a0,
|
|
||||||
char **b0)
|
|
||||||
{
|
|
||||||
char *a = NULL;
|
|
||||||
char *b = NULL;
|
|
||||||
char *ptr;
|
|
||||||
int len;
|
int len;
|
||||||
int retval = -1;
|
char *str;
|
||||||
|
|
||||||
ptr = s;
|
len = 0;
|
||||||
/* move forward to last char of element name */
|
for (i = 0; i < argc; i++)
|
||||||
while (*ptr && *ptr != sep[0] && *ptr != sep[1] )
|
len += strlen(argv[i]);
|
||||||
ptr++;
|
if (delim)
|
||||||
/* Copy first element name */
|
len += (strlen(delim) * argc);
|
||||||
len = ptr-s;
|
len += 1; /* '\0' */
|
||||||
if ((a = chunkdup(s, len+1, label)) == NULL)
|
if ((str = malloc(len)) == NULL)
|
||||||
goto catch;
|
return NULL;
|
||||||
a[len] = '\0';
|
memset (str, '\0', len);
|
||||||
/* Do we have an extended format? */
|
for (i = 0; i < argc; i++) {
|
||||||
if (*ptr == sep[0]) {
|
if (i != 0)
|
||||||
b = ++ptr;
|
strncat (str, delim, len - strlen(str));
|
||||||
/* move forward to end extension */
|
strncat (str, argv[i], len - strlen(str));
|
||||||
while (*ptr && *ptr != sep[1])
|
|
||||||
ptr++;
|
|
||||||
/* Copy extension */
|
|
||||||
len = ptr-b;
|
|
||||||
if ((b = chunkdup(b, len+1, label)) == NULL)
|
|
||||||
goto catch;
|
|
||||||
b[len] = '\0';
|
|
||||||
}
|
}
|
||||||
|
return str;
|
||||||
*a0 = a;
|
|
||||||
*b0 = b;
|
|
||||||
retval = 0;
|
|
||||||
catch:
|
|
||||||
return retval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -302,82 +163,48 @@ clicon_strndup (const char *str,
|
||||||
}
|
}
|
||||||
#endif /* ! HAVE_STRNDUP */
|
#endif /* ! HAVE_STRNDUP */
|
||||||
|
|
||||||
/*! Match string against regexp.
|
/*
|
||||||
*
|
* Turn this on for uni-test programs
|
||||||
* If a match pointer is given, the matching substring
|
* Usage: clixon_string join
|
||||||
* will be allocated 'match' will be pointing to it. The match string must
|
* Example compile:
|
||||||
* be free:ed by the application.
|
gcc -g -o clixon_string -I. -I../clixon ./clixon_string.c -lclixon -lcligen
|
||||||
* @retval -1 Failure
|
* Example run:
|
||||||
* @retval 0 No match
|
*/
|
||||||
* @retval >0 Match: Length of matching substring
|
#if 0 /* Test program */
|
||||||
*/
|
|
||||||
|
static int
|
||||||
|
usage(char *argv0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "usage:%s <string>\n", argv0);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
clicon_strmatch(const char *str,
|
main(int argc, char **argv)
|
||||||
const char *regexp,
|
|
||||||
char **match)
|
|
||||||
{
|
{
|
||||||
size_t len;
|
|
||||||
int status;
|
|
||||||
regex_t re;
|
|
||||||
char rxerr[128];
|
|
||||||
size_t nmatch = 1;
|
|
||||||
regmatch_t pmatch[1];
|
|
||||||
|
|
||||||
if (match)
|
|
||||||
*match = NULL;
|
|
||||||
|
|
||||||
if ((status = regcomp(&re, regexp, REG_EXTENDED)) != 0) {
|
|
||||||
regerror(status, &re, rxerr, sizeof(rxerr));
|
|
||||||
clicon_err(OE_REGEX, errno, "%s", rxerr);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = regexec(&re, str, nmatch, pmatch, 0);
|
|
||||||
regfree(&re);
|
|
||||||
if (status != 0)
|
|
||||||
return 0; /* No match */
|
|
||||||
|
|
||||||
len = pmatch[0].rm_eo - pmatch[0].rm_so;
|
|
||||||
/* If we've specified a match pointer, allocate and populate it. */
|
|
||||||
if (match) {
|
|
||||||
if ((*match = malloc(len + 1)) == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "Failed to allocate string");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memset(*match, '\0', len + 1);
|
|
||||||
strncpy(*match, str + pmatch[0].rm_so, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Substitute pattern in string.
|
|
||||||
* @retval str Malloc:ed string on success, use free to deallocate
|
|
||||||
* @retval NULL Failure.
|
|
||||||
*/
|
|
||||||
char *
|
|
||||||
clicon_strsub(char *str,
|
|
||||||
char *from,
|
|
||||||
char *to)
|
|
||||||
{
|
|
||||||
char **vec;
|
|
||||||
int nvec;
|
int nvec;
|
||||||
char *new;
|
char **vec;
|
||||||
char *retval = NULL;
|
char *str0;
|
||||||
|
char *str1;
|
||||||
|
int i;
|
||||||
|
|
||||||
if ((vec = clicon_strsplit(str, from, &nvec, __FUNCTION__)) == NULL) {
|
if (argc != 2){
|
||||||
clicon_err(OE_UNIX, errno, "Failed to split string");
|
usage(argv[0]);
|
||||||
goto done;
|
return 0;
|
||||||
}
|
}
|
||||||
|
str0 = argv[1];
|
||||||
if ((new = clicon_strjoin (nvec, vec, to, __FUNCTION__)) == NULL) {
|
if ((vec = clicon_strsep(str0, " \t", &nvec)) == NULL)
|
||||||
clicon_err(OE_UNIX, errno, "Failed to split string");
|
return -1;
|
||||||
goto done;
|
fprintf(stderr, "nvec: %d\n", nvec);
|
||||||
}
|
for (i=0; i<nvec+1; i++)
|
||||||
|
fprintf(stderr, "vec[%d]: %s\n", i, vec[i]);
|
||||||
retval = strdup(new);
|
if ((str1 = clicon_strjoin(nvec, vec, " ")) == NULL)
|
||||||
|
return -1;
|
||||||
done:
|
fprintf(stderr, "join: %s\n", str1);
|
||||||
unchunk_group(__FUNCTION__);
|
free(vec);
|
||||||
return retval;
|
free(str1);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* Test program */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -465,6 +465,12 @@ xml_childvec_set(cxobj *x,
|
||||||
return 0;
|
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().
|
/*! Create new xml node given a name and parent. Free it with xml_free().
|
||||||
*
|
*
|
||||||
* @param[in] name Name of new
|
* @param[in] name Name of new
|
||||||
|
|
@ -913,24 +919,26 @@ clicon_xml2cbuf(cbuf *cb,
|
||||||
cprintf(cb, "%s:", xml_namespace(cx));
|
cprintf(cb, "%s:", xml_namespace(cx));
|
||||||
cprintf(cb, "%s", xml_name(cx));
|
cprintf(cb, "%s", xml_name(cx));
|
||||||
xc = NULL;
|
xc = NULL;
|
||||||
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
|
while ((xc = xml_child_each(cx, xc, CX_ATTR)) != NULL)
|
||||||
if (xml_type(xc) != CX_ATTR)
|
|
||||||
continue;
|
|
||||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
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)
|
if (prettyprint)
|
||||||
cprintf(cb, "\n");
|
cprintf(cb, "\n");
|
||||||
break;
|
break;
|
||||||
|
|
@ -955,11 +963,11 @@ xml_parse(char *str,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ya.ya_xparent = x_up;
|
ya.ya_xparent = x_up;
|
||||||
|
ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */
|
||||||
if (clixon_xml_parsel_init(&ya) < 0)
|
if (clixon_xml_parsel_init(&ya) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
|
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clixon_xml_parsel_exit(&ya);
|
clixon_xml_parsel_exit(&ya);
|
||||||
|
|
@ -1079,7 +1087,6 @@ clicon_xml_parse_file(int fd,
|
||||||
* @endcode
|
* @endcode
|
||||||
* @see clicon_xml_parse_file
|
* @see clicon_xml_parse_file
|
||||||
* @note you need to free the xml parse tree after use, using xml_free()
|
* @note you need to free the xml parse tree after use, using xml_free()
|
||||||
* Update: with yacc parser I dont think it changes,....
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_xml_parse_str(char *str,
|
clicon_xml_parse_str(char *str,
|
||||||
|
|
@ -1090,6 +1097,57 @@ clicon_xml_parse_str(char *str,
|
||||||
return xml_parse(str, *cxtop);
|
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>%d</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
|
/*! Copy single xml node without copying children
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -1402,3 +1460,54 @@ xml_body_uint32(cxobj *xb,
|
||||||
cv_free(cv);
|
cv_free(cv);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Map xml operation from string to enumeration
|
||||||
|
* @param[in] xn XML node
|
||||||
|
* @param[out] op "operation" attribute may change operation
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xml_operation(char *opstr,
|
||||||
|
enum operation_type *op)
|
||||||
|
{
|
||||||
|
if (strcmp("merge", opstr) == 0)
|
||||||
|
*op = OP_MERGE;
|
||||||
|
else if (strcmp("replace", opstr) == 0)
|
||||||
|
*op = OP_REPLACE;
|
||||||
|
else if (strcmp("create", opstr) == 0)
|
||||||
|
*op = OP_CREATE;
|
||||||
|
else if (strcmp("delete", opstr) == 0)
|
||||||
|
*op = OP_DELETE;
|
||||||
|
else if (strcmp("remove", opstr) == 0)
|
||||||
|
*op = OP_REMOVE;
|
||||||
|
else if (strcmp("none", opstr) == 0)
|
||||||
|
*op = OP_NONE;
|
||||||
|
else{
|
||||||
|
clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
xml_operation2str(enum operation_type op)
|
||||||
|
{
|
||||||
|
switch (op){
|
||||||
|
case OP_MERGE:
|
||||||
|
return "merge";
|
||||||
|
break;
|
||||||
|
case OP_REPLACE:
|
||||||
|
return "replace";
|
||||||
|
break;
|
||||||
|
case OP_CREATE:
|
||||||
|
return "create";
|
||||||
|
break;
|
||||||
|
case OP_DELETE:
|
||||||
|
return "delete";
|
||||||
|
break;
|
||||||
|
case OP_REMOVE:
|
||||||
|
return "remove";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -197,23 +197,26 @@ xml2txt(FILE *f, cxobj *x, int level)
|
||||||
* @param[in] x XML Parse-tree (to translate)
|
* @param[in] x XML Parse-tree (to translate)
|
||||||
* @param[in] prepend0 Print this text in front of all commands.
|
* @param[in] prepend0 Print this text in front of all commands.
|
||||||
* @param[in] gt option to steer cli syntax
|
* @param[in] gt option to steer cli syntax
|
||||||
* @param[in] label Memory chunk allocation label
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml2cli(FILE *f,
|
xml2cli(FILE *f,
|
||||||
cxobj *x,
|
cxobj *x,
|
||||||
char *prepend0,
|
char *prepend0,
|
||||||
enum genmodel_type gt,
|
enum genmodel_type gt)
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xe = NULL;
|
cxobj *xe = NULL;
|
||||||
char *term;
|
char *term;
|
||||||
char *prepend;
|
|
||||||
int bool;
|
int bool;
|
||||||
int nr;
|
int nr;
|
||||||
int i;
|
int i;
|
||||||
|
cbuf *cbpre;
|
||||||
|
|
||||||
|
/* Create prepend variable string */
|
||||||
|
if ((cbpre = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_PLUGIN, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
nr = xml_child_nr(x);
|
nr = xml_child_nr(x);
|
||||||
if (!nr){
|
if (!nr){
|
||||||
if (xml_type(x) == CX_BODY)
|
if (xml_type(x) == CX_BODY)
|
||||||
|
|
@ -226,10 +229,8 @@ xml2cli(FILE *f,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
prepend = "";
|
|
||||||
if (prepend0)
|
if (prepend0)
|
||||||
if ((prepend = chunk_sprintf(label, "%s%s", prepend, prepend0)) == NULL)
|
cprintf(cbpre, "%s", prepend0);
|
||||||
goto done;
|
|
||||||
/* bool determines when to print a variable keyword:
|
/* bool determines when to print a variable keyword:
|
||||||
!leaf T for all (ie parameter)
|
!leaf T for all (ie parameter)
|
||||||
index GT_NONE F
|
index GT_NONE F
|
||||||
|
|
@ -241,12 +242,11 @@ xml2cli(FILE *f,
|
||||||
*/
|
*/
|
||||||
bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS && !xml_index(x));
|
bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS && !xml_index(x));
|
||||||
// bool = (!x->xn_index || gt == GT_ALL);
|
// bool = (!x->xn_index || gt == GT_ALL);
|
||||||
if (bool &&
|
if (bool){
|
||||||
(prepend = chunk_sprintf(label, "%s%s%s",
|
if (cbuf_len(cbpre))
|
||||||
prepend,
|
cprintf(cbpre, " ");
|
||||||
strlen(prepend)?" ":"",
|
cprintf(cbpre, "%s", xml_name(x));
|
||||||
xml_name(x))) == NULL)
|
}
|
||||||
goto done;
|
|
||||||
xe = NULL;
|
xe = NULL;
|
||||||
/* First child is unique, then add that, before looping. */
|
/* First child is unique, then add that, before looping. */
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
@ -255,23 +255,19 @@ xml2cli(FILE *f,
|
||||||
if (xml_index(xe) && i < nr-1)
|
if (xml_index(xe) && i < nr-1)
|
||||||
;
|
;
|
||||||
else
|
else
|
||||||
if (xml2cli(f, xe, prepend, gt, label) < 0)
|
if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_index(xe)){ /* assume index is first, otherwise need one more while */
|
if (xml_index(xe)){ /* assume index is first, otherwise need one more while */
|
||||||
if (gt ==GT_ALL && (prepend = chunk_sprintf(label, "%s %s",
|
if (gt == GT_ALL)
|
||||||
prepend,
|
cprintf(cbpre, " %s", xml_name(xe));
|
||||||
xml_name(xe))) == NULL)
|
cprintf(cbpre, " %s", xml_value(xml_child_i(xe, 0)));
|
||||||
|
|
||||||
goto done;
|
|
||||||
if ((prepend = chunk_sprintf(label, "%s %s",
|
|
||||||
prepend,
|
|
||||||
xml_value(xml_child_i(xe, 0)))) == NULL)
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (cbpre)
|
||||||
|
cbuf_free(cbpre);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,6 +288,7 @@ xml_yang_validate(cxobj *xt,
|
||||||
yang_stmt *yc;
|
yang_stmt *yc;
|
||||||
int i;
|
int i;
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
|
char *body;
|
||||||
|
|
||||||
/* if not given by argument (overide) use default link */
|
/* if not given by argument (overide) use default link */
|
||||||
ys = ys0?ys0:xml_spec(xt);
|
ys = ys0?ys0:xml_spec(xt);
|
||||||
|
|
@ -321,17 +318,19 @@ xml_yang_validate(cxobj *xt,
|
||||||
/* In the union case, value is parsed as generic REST type,
|
/* In the union case, value is parsed as generic REST type,
|
||||||
* needs to be reparsed when concrete type is selected
|
* needs to be reparsed when concrete type is selected
|
||||||
*/
|
*/
|
||||||
if (cv_parse(xml_body(xt), cv) <0){
|
if ((body = xml_body(xt)) != NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cv_parse");
|
if (cv_parse(body, cv) <0){
|
||||||
goto done;
|
clicon_err(OE_UNIX, errno, "cv_parse");
|
||||||
}
|
goto done;
|
||||||
if ((ys_cv_validate(cv, ys, &reason)) != 1){
|
}
|
||||||
clicon_err(OE_DB, 0,
|
if ((ys_cv_validate(cv, ys, &reason)) != 1){
|
||||||
"validation of %s failed %s",
|
clicon_err(OE_DB, 0,
|
||||||
ys->ys_argument, reason?reason:"");
|
"validation of %s failed %s",
|
||||||
if (reason)
|
ys->ys_argument, reason?reason:"");
|
||||||
free(reason);
|
if (reason)
|
||||||
goto done;
|
free(reason);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ struct xml_parse_yacc_arg{
|
||||||
|
|
||||||
cxobj *ya_xelement; /* xml active element */
|
cxobj *ya_xelement; /* xml active element */
|
||||||
cxobj *ya_xparent; /* xml parent element*/
|
cxobj *ya_xparent; /* xml parent element*/
|
||||||
|
int ya_skipspace; /* If set, remove all non-terminal bodies (strip prettyr-print) */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern char *clixon_xml_parsetext;
|
extern char *clixon_xml_parsetext;
|
||||||
|
|
|
||||||
|
|
@ -141,11 +141,7 @@ int
|
||||||
clixon_xml_parsel_exit(struct xml_parse_yacc_arg *ya)
|
clixon_xml_parsel_exit(struct xml_parse_yacc_arg *ya)
|
||||||
{
|
{
|
||||||
yy_delete_buffer(ya->ya_lexbuf);
|
yy_delete_buffer(ya->ya_lexbuf);
|
||||||
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
|
|
||||||
clixon_xml_parselex_destroy(); /* modern */
|
clixon_xml_parselex_destroy(); /* modern */
|
||||||
#else
|
|
||||||
yy_init = 1; /* This does not quite free all buffers */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,36 +100,22 @@ xml_attr_new(struct xml_parse_yacc_arg *ya,
|
||||||
there may also be some leakage here on NULL return
|
there may also be some leakage here on NULL return
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml_parse_content(struct xml_parse_yacc_arg *ya, char *str)
|
xml_parse_content(struct xml_parse_yacc_arg *ya,
|
||||||
|
char *str)
|
||||||
{
|
{
|
||||||
int sz;
|
|
||||||
char s0;
|
|
||||||
cxobj *xn = ya->ya_xelement;
|
cxobj *xn = ya->ya_xelement;
|
||||||
cxobj *xp = ya->ya_xparent;
|
cxobj *xp = ya->ya_xparent;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
||||||
ya->ya_xelement = NULL; /* init */
|
ya->ya_xelement = NULL; /* init */
|
||||||
s0 = str[0];
|
if (xn == NULL){
|
||||||
if (xn != NULL){
|
|
||||||
sz = strlen(xml_value(xn));
|
|
||||||
if (s0 == ' ' || s0 == '\n' || s0 == '\t'){
|
|
||||||
str[0] = ' ';
|
|
||||||
if (xml_value(xn)[sz-1] == ' ')
|
|
||||||
goto ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if (s0 == ' ' || s0 == '\n' || s0 == '\t')
|
|
||||||
goto ok;
|
|
||||||
if ((xn = xml_new("body", xp)) == NULL)
|
if ((xn = xml_new("body", xp)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xml_type_set(xn, CX_BODY);
|
xml_type_set(xn, CX_BODY);
|
||||||
sz = 0;
|
|
||||||
}
|
}
|
||||||
if (xml_value_append(xn, str)==NULL)
|
if (xml_value_append(xn, str)==NULL)
|
||||||
goto done;
|
goto done;
|
||||||
ya->ya_xelement = xn;
|
ya->ya_xelement = xn;
|
||||||
ok:
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -147,7 +133,6 @@ xml_parse_version(struct xml_parse_yacc_arg *ya, char *ver)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
xml_parse_id(struct xml_parse_yacc_arg *ya, char *name, char *namespace)
|
xml_parse_id(struct xml_parse_yacc_arg *ya, char *name, char *namespace)
|
||||||
{
|
{
|
||||||
|
|
@ -193,18 +178,31 @@ xml_parse_endslash_post(struct xml_parse_yacc_arg *ya)
|
||||||
static int
|
static int
|
||||||
xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
cxobj *x = ya->ya_xelement;
|
||||||
|
cxobj *xc;
|
||||||
|
|
||||||
if (strcmp(xml_name(ya->ya_xelement), name)){
|
if (strcmp(xml_name(x), name)){
|
||||||
clicon_err(OE_XML, 0, "Sanity check failed: %s vs %s",
|
clicon_err(OE_XML, 0, "Sanity check failed: %s vs %s",
|
||||||
xml_name(ya->ya_xelement), name);
|
xml_name(x), name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml_namespace(ya->ya_xelement)!=NULL){
|
if (xml_namespace(x)!=NULL){
|
||||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s\n",
|
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s\n",
|
||||||
xml_namespace(ya->ya_xelement), xml_name(ya->ya_xelement), name);
|
xml_namespace(x), xml_name(x), name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* remove all non-terminal bodies (strip pretty-print) */
|
||||||
|
if (ya->ya_skipspace){
|
||||||
|
if (xml_child_nr(x) == 1 && (xml_type(xml_child_i(x, 0))==CX_BODY))
|
||||||
|
;
|
||||||
|
else{
|
||||||
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL)
|
||||||
|
xml_value_set(xc, ""); /* XXX remove */
|
||||||
|
}
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
free(name);
|
free(name);
|
||||||
|
|
@ -214,25 +212,37 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
||||||
static int
|
static int
|
||||||
xml_parse_bslash2(struct xml_parse_yacc_arg *ya, char *namespace, char *name)
|
xml_parse_bslash2(struct xml_parse_yacc_arg *ya, char *namespace, char *name)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
cxobj *x = ya->ya_xelement;
|
||||||
|
cxobj *xc;
|
||||||
|
|
||||||
if (strcmp(xml_name(ya->ya_xelement), name)){
|
if (strcmp(xml_name(x), name)){
|
||||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
|
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
|
||||||
xml_namespace(ya->ya_xelement),
|
xml_namespace(x),
|
||||||
xml_name(ya->ya_xelement),
|
xml_name(x),
|
||||||
namespace,
|
namespace,
|
||||||
name);
|
name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (xml_namespace(ya->ya_xelement)==NULL ||
|
if (xml_namespace(x)==NULL ||
|
||||||
strcmp(xml_namespace(ya->ya_xelement), namespace)){
|
strcmp(xml_namespace(x), namespace)){
|
||||||
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
|
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
|
||||||
xml_namespace(ya->ya_xelement),
|
xml_namespace(x),
|
||||||
xml_name(ya->ya_xelement),
|
xml_name(x),
|
||||||
namespace,
|
namespace,
|
||||||
name);
|
name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
/* remove all non-terminal bodies (strip pretty-print) */
|
||||||
|
if (ya->ya_skipspace){
|
||||||
|
if (xml_child_nr(x) == 1 && (xml_type(xml_child_i(x, 0))==CX_BODY))
|
||||||
|
;
|
||||||
|
else{
|
||||||
|
xc = NULL;
|
||||||
|
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL)
|
||||||
|
xml_value_set(xc, ""); /* XXX remove */
|
||||||
|
}
|
||||||
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
free(name);
|
free(name);
|
||||||
|
|
@ -300,7 +310,7 @@ emnt : '<' id attrs emnt1
|
||||||
;
|
;
|
||||||
|
|
||||||
id : NAME { if (xml_parse_id(_YA, $1, NULL) < 0) YYABORT;
|
id : NAME { if (xml_parse_id(_YA, $1, NULL) < 0) YYABORT;
|
||||||
clicon_debug(3, "id -> NAME");}
|
clicon_debug(3, "id -> NAME %s", $1);}
|
||||||
| NAME ':' NAME { if (xml_parse_id(_YA, $3, $1) < 0) YYABORT;
|
| NAME ':' NAME { if (xml_parse_id(_YA, $3, $1) < 0) YYABORT;
|
||||||
clicon_debug(3, "id -> NAME : NAME");}
|
clicon_debug(3, "id -> NAME : NAME");}
|
||||||
;
|
;
|
||||||
|
|
@ -314,8 +324,8 @@ emnt1 : ESLASH {_YA->ya_xelement = NULL;
|
||||||
;
|
;
|
||||||
|
|
||||||
etg : BSLASH NAME '>'
|
etg : BSLASH NAME '>'
|
||||||
{ if (xml_parse_bslash1(_YA, $2) < 0) YYABORT;
|
{ clicon_debug(3, "etg -> < </ NAME %s>", $2); if (xml_parse_bslash1(_YA, $2) < 0) YYABORT; }
|
||||||
clicon_debug(3, "etg -> < </ NAME >"); }
|
|
||||||
| BSLASH NAME ':' NAME '>'
|
| BSLASH NAME ':' NAME '>'
|
||||||
{ if (xml_parse_bslash2(_YA, $2, $4) < 0) YYABORT;
|
{ if (xml_parse_bslash2(_YA, $2, $4) < 0) YYABORT;
|
||||||
clicon_debug(3, "etg -> < </ NAME:NAME >"); }
|
clicon_debug(3, "etg -> < </ NAME:NAME >"); }
|
||||||
|
|
@ -328,7 +338,7 @@ list : list content { clicon_debug(3, "list -> list content"); }
|
||||||
content : emnt { clicon_debug(3, "content -> emnt"); }
|
content : emnt { clicon_debug(3, "content -> emnt"); }
|
||||||
| comment { clicon_debug(3, "content -> comment"); }
|
| comment { clicon_debug(3, "content -> comment"); }
|
||||||
| CHAR { if (xml_parse_content(_YA, $1) < 0) YYABORT;
|
| CHAR { if (xml_parse_content(_YA, $1) < 0) YYABORT;
|
||||||
clicon_debug(3, "content -> CHAR", $1); }
|
clicon_debug(3, "content -> CHAR %s", $1); }
|
||||||
| { clicon_debug(3, "content -> "); }
|
| { clicon_debug(3, "content -> "); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ xpath_parse_predicate(struct xpath_element *xe,
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = strlen(pred);
|
len = strlen(pred);
|
||||||
for (i=len-1; i>=0; i--){ /* -2 since we search for ][ */
|
for (i=len-1; i>=0; i--){ /* -1 since we search for ][ */
|
||||||
s = &pred[i];
|
s = &pred[i];
|
||||||
if (i==0 ||
|
if (i==0 ||
|
||||||
(*(s)==']' && *(s+1)=='[')){
|
(*(s)==']' && *(s+1)=='[')){
|
||||||
|
|
@ -358,12 +358,23 @@ xpath_parse(char *xpath,
|
||||||
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
|
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
|
||||||
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
|
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
|
||||||
}
|
}
|
||||||
|
#if 1 /* Problems with .[userid=1321] */
|
||||||
|
else if (strncmp(s,".", strlen("."))==0)
|
||||||
|
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
|
||||||
|
#else
|
||||||
else if (strncmp(s,".", strlen(s))==0) /* abbreviatedstep */
|
else if (strncmp(s,".", strlen(s))==0) /* abbreviatedstep */
|
||||||
xpath_element_new(A_SELF, NULL, &xpnext);
|
xpath_element_new(A_SELF, NULL, &xpnext);
|
||||||
|
#endif
|
||||||
|
|
||||||
else if (strncmp(s,"self::", strlen("self::"))==0)
|
else if (strncmp(s,"self::", strlen("self::"))==0)
|
||||||
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
|
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
|
||||||
|
#if 1
|
||||||
|
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
|
||||||
|
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
|
||||||
|
#else
|
||||||
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
|
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
|
||||||
xpath_element_new(A_PARENT, NULL, &xpnext);
|
xpath_element_new(A_PARENT, NULL, &xpnext);
|
||||||
|
#endif
|
||||||
else if (strncmp(s,"parent::", strlen("parent::"))==0)
|
else if (strncmp(s,"parent::", strlen("parent::"))==0)
|
||||||
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
|
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
|
||||||
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
|
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
|
||||||
|
|
@ -552,12 +563,6 @@ xpath_expr(char *predicate_expression,
|
||||||
* @param[in] flags if != 0, only match xml nodes matching flags
|
* @param[in] flags if != 0, only match xml nodes matching flags
|
||||||
* @param[out] vec2 Result XML node vector
|
* @param[out] vec2 Result XML node vector
|
||||||
* @param[out] vec2len Length of result 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
|
static int
|
||||||
xpath_find(struct xpath_element *xe,
|
xpath_find(struct xpath_element *xe,
|
||||||
|
|
@ -767,6 +772,7 @@ xpath_choice(cxobj *xtop,
|
||||||
cxobj **vec0 = NULL;
|
cxobj **vec0 = NULL;
|
||||||
size_t vec0len = 0;
|
size_t vec0len = 0;
|
||||||
|
|
||||||
|
|
||||||
if ((s0 = strdup(xpath0)) == NULL){
|
if ((s0 = strdup(xpath0)) == NULL){
|
||||||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -926,8 +932,8 @@ xpath_each(cxobj *cxtop,
|
||||||
|
|
||||||
/*! A restricted xpath that returns a vector of matches
|
/*! A restricted xpath that returns a vector of matches
|
||||||
*
|
*
|
||||||
* See xpath1() on details for subset.
|
* See xpath1() on details for subset
|
||||||
* @param[in] cxtop xml-tree where to search
|
. * @param[in] cxtop xml-tree where to search
|
||||||
* @param[in] xpath string with XPATH syntax
|
* @param[in] xpath string with XPATH syntax
|
||||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||||
* @param[out] veclen returns length of vector in return value
|
* @param[out] veclen returns length of vector in return value
|
||||||
|
|
@ -945,7 +951,7 @@ xpath_each(cxobj *cxtop,
|
||||||
* }
|
* }
|
||||||
* free(vec);
|
* free(vec);
|
||||||
* @endcode
|
* @endcode
|
||||||
* Note that although the returned vector must be freed after use, the returned xml
|
* @Note that although the returned vector must be freed after use, the returned xml
|
||||||
* trees need not be.
|
* trees need not be.
|
||||||
* @see also xpath_first, xpath_each.
|
* @see also xpath_first, xpath_each.
|
||||||
*/
|
*/
|
||||||
|
|
@ -987,7 +993,27 @@ xpath_vec(cxobj *cxtop,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
|
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
|
||||||
|
* @param[in] cxtop xml-tree where to search
|
||||||
|
* @param[in] xpath string with XPATH syntax
|
||||||
* @param[in] flags Set of flags that return nodes must match (0 if all)
|
* @param[in] flags Set of flags that return nodes must match (0 if all)
|
||||||
|
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||||
|
* @param[out] veclen returns length of vector in return value
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 error.
|
||||||
|
* @code
|
||||||
|
* cxobj **vec;
|
||||||
|
* size_t veclen;
|
||||||
|
* if (xpath_vec_flag(cxtop, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||||
|
* goto err;
|
||||||
|
* for (i=0; i<veclen; i++){
|
||||||
|
* xn = vec[i];
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* free(vec);
|
||||||
|
* @endcode
|
||||||
|
* @Note that although the returned vector must be freed after use, the returned xml
|
||||||
|
* trees need not be.
|
||||||
|
* @see also xpath_vec This is a specialized version.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xpath_vec_flag(cxobj *cxtop,
|
xpath_vec_flag(cxobj *cxtop,
|
||||||
|
|
|
||||||
|
|
@ -1676,37 +1676,6 @@ yang_xpath_vec(yang_node *yn,
|
||||||
return yret;
|
return yret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Alternative to clicon_strsplit using malloc. Note delim can only be one char
|
|
||||||
* Free return value after use
|
|
||||||
*/
|
|
||||||
static char **
|
|
||||||
clicon_strsplit_malloc(char *string,
|
|
||||||
char *delim,
|
|
||||||
int *nvec0)
|
|
||||||
{
|
|
||||||
char **vec = NULL;
|
|
||||||
char *ptr;
|
|
||||||
char *p;
|
|
||||||
int nvec = 1;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i=0; i<strlen(string); i++)
|
|
||||||
if (string[i]==*delim)
|
|
||||||
nvec++;
|
|
||||||
/* alloc vector and append copy of string */
|
|
||||||
if ((vec = (char**)malloc(nvec* sizeof(char*) + strlen(string)+1)) == NULL){
|
|
||||||
clicon_err(OE_YANG, errno, "malloc");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
ptr = (char*)vec + nvec* sizeof(char*); /* this is where ptr starts */
|
|
||||||
strncpy(ptr, string, strlen(string)+1);
|
|
||||||
i = 0;
|
|
||||||
while ((p = strsep(&ptr, delim)) != NULL)
|
|
||||||
vec[i++] = p;
|
|
||||||
*nvec0 = nvec;
|
|
||||||
err:
|
|
||||||
return vec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Given an absolute xpath (eg /a/b/c) find matching yang specification
|
/*! Given an absolute xpath (eg /a/b/c) find matching yang specification
|
||||||
* @param[in] yn Yang node
|
* @param[in] yn Yang node
|
||||||
|
|
@ -1725,12 +1694,10 @@ yang_xpath_abs(yang_node *yn,
|
||||||
char *id;
|
char *id;
|
||||||
char *prefix = NULL;
|
char *prefix = NULL;
|
||||||
|
|
||||||
if ((vec = clicon_strsplit_malloc(xpath, "/", &nvec)) == NULL){
|
if ((vec = clicon_strsep(xpath, "/", &nvec)) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "%s: strsplit", __FUNCTION__);
|
clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Assume path looks like: "/prefix:id[/prefix:id]*" */
|
/* Assume path looks like: "/prefix:id[/prefix:id]*" */
|
||||||
if (nvec < 2){
|
if (nvec < 2){
|
||||||
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
|
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
|
||||||
|
|
@ -1807,7 +1774,7 @@ yang_xpath(yang_node *yn,
|
||||||
/* check absolute path */
|
/* check absolute path */
|
||||||
if (xpath[0] == '/')
|
if (xpath[0] == '/')
|
||||||
return yang_xpath_abs(yn, xpath);
|
return yang_xpath_abs(yn, xpath);
|
||||||
if ((vec = clicon_strsplit_malloc(xpath, "/", &nvec)) == NULL)
|
if ((vec = clicon_strsep(xpath, "/", &nvec)) == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
ys = yang_xpath_vec((yang_node*)yn, vec, nvec);
|
ys = yang_xpath_vec((yang_node*)yn, vec, nvec);
|
||||||
err:
|
err:
|
||||||
|
|
@ -1986,16 +1953,14 @@ cvec *
|
||||||
yang_arg2cvec(yang_stmt *ys,
|
yang_arg2cvec(yang_stmt *ys,
|
||||||
char *delim)
|
char *delim)
|
||||||
{
|
{
|
||||||
char **vec;
|
char **vec = NULL;
|
||||||
int i;
|
int i;
|
||||||
int nvec;
|
int nvec;
|
||||||
cvec *cvv = NULL;
|
cvec *cvv = NULL;
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
|
|
||||||
if ((vec = clicon_strsplit(ys->ys_argument, " ", &nvec, __FUNCTION__)) == NULL){
|
if ((vec = clicon_strsep(ys->ys_argument, " ", &nvec)) == NULL)
|
||||||
clicon_err(OE_YANG, errno, "clicon_strsplit");
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
|
||||||
if ((cvv = cvec_new(nvec)) == NULL){
|
if ((cvv = cvec_new(nvec)) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "cvec_new");
|
clicon_err(OE_YANG, errno, "cvec_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -2010,7 +1975,8 @@ yang_arg2cvec(yang_stmt *ys,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (vec)
|
||||||
|
free(vec);
|
||||||
return cvv;
|
return cvv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -248,11 +248,7 @@ int
|
||||||
yang_scan_exit(struct clicon_yang_yacc_arg *yy)
|
yang_scan_exit(struct clicon_yang_yacc_arg *yy)
|
||||||
{
|
{
|
||||||
yy_delete_buffer(yy->yy_lexbuf);
|
yy_delete_buffer(yy->yy_lexbuf);
|
||||||
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
|
|
||||||
clixon_yang_parselex_destroy(); /* modern */
|
clixon_yang_parselex_destroy(); /* modern */
|
||||||
#else
|
|
||||||
yy_init = 1; /* This does not quite free all buffers */
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
# include err() and new() functions
|
# include err() and new() functions
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
|
|
|
||||||
22
test/lib.sh
22
test/lib.sh
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
testnr=0
|
testnr=0
|
||||||
testnname=
|
testnname=
|
||||||
|
|
@ -32,7 +32,7 @@ expectfn(){
|
||||||
fi
|
fi
|
||||||
# grep extended grep
|
# grep extended grep
|
||||||
match=`echo "$ret" | grep -Eo "$expect"`
|
match=`echo "$ret" | grep -Eo "$expect"`
|
||||||
# echo "ret:$ret"
|
# echo "ret:<$ret>"
|
||||||
# echo "expect:$expect"
|
# echo "expect:$expect"
|
||||||
# echo "match:$match"
|
# echo "match:$match"
|
||||||
if [ -z "$match" ]; then
|
if [ -z "$match" ]; then
|
||||||
|
|
@ -61,3 +61,21 @@ EOF
|
||||||
fi
|
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
|
# Test1: backend and cli basic functionality
|
||||||
# Start backend server
|
# Start backend server
|
||||||
# Add an ethernet interface and an address
|
# Add an ethernet interface and an address
|
||||||
|
|
@ -10,6 +10,10 @@
|
||||||
# include err() and new() functions
|
# include err() and new() functions
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
|
|
||||||
|
# For memcheck
|
||||||
|
#clixon_cli="valgrind --leak-check=full --show-leak-kinds=all clixon_cli"
|
||||||
|
clixon_cli=clixon_cli
|
||||||
|
|
||||||
# kill old backend (if any)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
sudo clixon_backend -zf $clixon_cf
|
sudo clixon_backend -zf $clixon_cf
|
||||||
|
|
@ -18,26 +22,72 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
new "start backend"
|
new "start backend"
|
||||||
# start new 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
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
|
new "cli configure top"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""
|
||||||
|
|
||||||
|
new "cli show configuration top"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces$"
|
||||||
|
|
||||||
|
new "cli configure delete top"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf delete interfaces" ""
|
||||||
|
|
||||||
|
new "cli show configuration delete top"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf show conf cli" ""
|
||||||
|
|
||||||
new "cli configure"
|
new "cli configure"
|
||||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0" ""
|
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0" ""
|
||||||
|
|
||||||
new "cli show configuration"
|
new "cli show configuration"
|
||||||
expectfn "clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth0
|
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth0
|
||||||
interfaces interface enabled true$"
|
interfaces interface enabled true$"
|
||||||
|
|
||||||
new "cli failed validate"
|
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"
|
new "cli configure more"
|
||||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 ipv4 address 1.2.3.4 prefix-length 24" ""
|
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0 ipv4 address 1.2.3.4 prefix-length 24" ""
|
||||||
expectfn "clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
|
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0 description mydesc" ""
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth0 type bgp" ""
|
||||||
|
|
||||||
|
new "cli show xpath description"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" "<description>mydesc</description>"
|
||||||
|
|
||||||
|
new "cli delete description"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o delete interfaces interface eth0 description mydesc"
|
||||||
|
|
||||||
|
new "cli show xpath no description"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o show xpath /interfaces/interface/description" ""
|
||||||
|
|
||||||
|
new "cli success validate"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o validate" ""
|
||||||
|
|
||||||
new "cli commit"
|
new "cli commit"
|
||||||
expectfn "clixon_cli -1f $clixon_cf -l o commit" ""
|
expectfn "$clixon_cli -1f $clixon_cf -l o commit" ""
|
||||||
|
|
||||||
|
new "cli save"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o save /tmp/foo" ""
|
||||||
|
|
||||||
|
new "cli delete all"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o delete all" ""
|
||||||
|
|
||||||
|
new "cli load"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o load /tmp/foo" ""
|
||||||
|
|
||||||
|
new "cli check load"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o show conf cli" "^interfaces interface name eth0
|
||||||
|
interfaces interface enabled true$"
|
||||||
|
|
||||||
|
new "cli debug"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o debug level 1" ""
|
||||||
|
# How to test this?
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o debug level 0" ""
|
||||||
|
|
||||||
|
new "cli downcall"
|
||||||
|
expectfn "$clixon_cli -1f $clixon_cf -l o downcall \"This is a test =====\"" "^\"This is a test =====\"$"
|
||||||
|
|
||||||
new "Kill backend"
|
new "Kill backend"
|
||||||
# Check if still alive
|
# Check if still alive
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
# Test1: backend and cli basic functionality
|
# Test2: backend and netconf basic functionality
|
||||||
# Start backend server
|
|
||||||
# Add an ethernet interface and an address
|
|
||||||
# Show configuration
|
|
||||||
# Validate without a mandatory type
|
|
||||||
# Set the mandatory type
|
|
||||||
# Commit
|
|
||||||
|
|
||||||
# include err() and new() functions
|
# include err() and new() functions
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
|
|
||||||
|
# For memcheck
|
||||||
|
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
||||||
|
clixon_netconf=clixon_netconf
|
||||||
|
|
||||||
# kill old backend (if any)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
sudo clixon_backend -zf $clixon_cf
|
sudo clixon_backend -zf $clixon_cf
|
||||||
|
|
@ -18,15 +16,87 @@ if [ $? -ne 0 ]; then
|
||||||
fi
|
fi
|
||||||
new "start backend"
|
new "start backend"
|
||||||
# start new 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
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
new "netconf show config"
|
new "netconf get empty config"
|
||||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "<rpc-reply><data></data></rpc-reply>]]>]]>"
|
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"
|
new "netconf edit config"
|
||||||
expecteof "clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]>" "<rpc-reply><ok/></rpc-reply>]]>]]>"
|
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 eth1"
|
||||||
|
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 edit config replace"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get replaced config"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf edit config create"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf edit config create 2nd"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
||||||
|
|
||||||
|
new "netconf edit config delete"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get delete config"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf discard-changes"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></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"
|
new "Kill backend"
|
||||||
# Check if still alive
|
# Check if still alive
|
||||||
|
|
@ -39,4 +109,3 @@ sudo clixon_backend -zf $clixon_cf
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err "kill backend"
|
err "kill backend"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
72
test/test3.sh
Executable file
72
test/test3.sh
Executable file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Test3: backend and restconf basic functionality
|
||||||
|
|
||||||
|
# include err() and new() functions
|
||||||
|
. ./lib.sh
|
||||||
|
|
||||||
|
# kill old backend (if any)
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $clixon_cf
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "start backend"
|
||||||
|
sudo clixon_backend -If $clixon_cf
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "kill old restconf daemon"
|
||||||
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
new "start restconf daemon"
|
||||||
|
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df /usr/local/etc/routing.conf # -D
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
new "restconf options"
|
||||||
|
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
||||||
|
|
||||||
|
new "restconf get empty config"
|
||||||
|
expectfn "curl -sG http://localhost/restconf/data" "^null
$"
|
||||||
|
|
||||||
|
new "restconf put config"
|
||||||
|
expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth1","type":"eth","enabled":"true"},{"name":"eth0","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' ""
|
||||||
|
|
||||||
|
new "restconf get config"
|
||||||
|
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth0","type": "eth","enabled": "true"}\]}}
|
||||||
|
$'
|
||||||
|
|
||||||
|
new "restconf head"
|
||||||
|
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
|
||||||
|
|
||||||
|
new "restconf POST config"
|
||||||
|
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||||
|
|
||||||
|
new "restconf DELETE config"
|
||||||
|
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
|
||||||
|
|
||||||
|
new "restconf get config"
|
||||||
|
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}}
|
||||||
|
$'
|
||||||
|
|
||||||
|
new "restconf PATCH config"
|
||||||
|
expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||||
|
|
||||||
|
new "restconf PUT"
|
||||||
|
expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' ""
|
||||||
|
|
||||||
|
new "Kill restconf daemon"
|
||||||
|
#sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if still alive
|
||||||
|
pid=`pgrep clixon_backend`
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
sudo clixon_backend -zf $clixon_cf
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err "kill backend"
|
||||||
|
fi
|
||||||
65
test/test4.sh
Executable file
65
test/test4.sh
Executable file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Test2: backend and netconf basic functionality
|
||||||
|
|
||||||
|
# include err() and new() functions
|
||||||
|
. ./lib.sh
|
||||||
|
|
||||||
|
# For memcheck
|
||||||
|
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
||||||
|
clixon_netconf=clixon_netconf
|
||||||
|
|
||||||
|
cat <<EOF > /tmp/test.yang
|
||||||
|
module ietf-ip{
|
||||||
|
container x {
|
||||||
|
list y {
|
||||||
|
key "a b";
|
||||||
|
leaf a {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf b {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf c {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf d {
|
||||||
|
type empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# kill old backend (if any)
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $clixon_cf -y /tmp/test
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "start backend"
|
||||||
|
# start new backend
|
||||||
|
sudo clixon_backend -If $clixon_cf -y /tmp/test
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
new "netconf edit config"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf commit"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "netconf get config xpath"
|
||||||
|
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y></x></config></data></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
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