Merge branch 'develop' into master for 3.3.3

This commit is contained in:
Olof hagsand 2017-11-19 19:05:03 +01:00
commit 75d1bd327a
79 changed files with 2776 additions and 1470 deletions

View file

@ -1,12 +1,61 @@
# Clixon CHANGELOG # Clixon Changelog
## 3.3.3 (Upcoming)
### Known issues
### Major changes:
* Clixon can now be compiled and run on Apple Darwin.
* Performance improvements
* Added xml hash lookup instead of linear search for better performance of large lists. To disable, undefine XML_CHILD_HASH in clixon_custom.h
* netconf client was limited to 8K byte messages. Now limit is 2^32 bytes
* XML and YANG-based configuration file.
* New configuration files have .xml suffix, old have .conf. Old config files till work for backward compatibility.
* The yang model is yang/clixon-config.yang.
* A migration utility is clixon_cli -x to print new format, eg:
```
clixon_cli -f /usr/local/etc/routing.conf -1x
```
* Introducing backend daemon startup modes. The flags -IRCr and option CLICON_USE_STARTUP_CONFIG are replaced with command-line option -s <mode> and option CLICON_STARTUP_MODE. You need to replace the starting of clixon_backend as follows:
* -I replace with -s "init" (or use of CLICON_STARTUP_MODE option)
* -CIr replace with -s "running"
* (no-option) replace with -s "none"
* CLICON_USE_STARTUP_CONFIG=1 replace with -s "startup"
Backward compatibility is enabled by defining BACKEND_STARTUP_BACKWARD_COMPAT in include/clixon_custom.h
### Minor changes:
* Disabled key-value datastore. Enable with --with-keyvalue
* Removed mandatory requirements for BACKEND, NETCONF, RESTCONF and CLI dirs.
* When user callbacks such as statedata() call returns -1, clixon_backend no * When user callbacks such as statedata() call returns -1, clixon_backend no
longer silently exits. Instead a log is printed and an RPC error is returned. longer silently exits. Instead a log is printed and an RPC error is returned.
* Added Floating point and negative number support to JSON
* Restconf: http cookie sent as attribute in rpc restconf_post operations to backend.
* Added option CLICON_CLISPEC_FILE as complement to CLICON_CLISPEC_DIR to
specify single CLI specification file, not only directory containing files.
* Replaced the following cli_ functions with their original cligen_functions:
cli_exiting, cli_set_exiting, cli_comment,
cli_set_comment, cli_tree_add, cli_tree_active,
cli_tree_active_set, cli_tree.
* Added a format parameter to clicon_rpc_generate_error() and changed error
printouts for backend errors, such as commit and validate. Example of the
new format:
```
> commit
Sep 27 18:11:58: Commit failed. Edit and try again or discard changes:
protocol invalid-value Missing mandatory variable: type
```
* Added event_poll function.
* Support for non-line scrolling in CLI, eg wrap lines. Set with: * Support for non-line scrolling in CLI, eg wrap lines. Set with:
CLICON_CLI_LINESCROLLING 0 CLICON_CLI_LINESCROLLING 0
## 3.3.2 Aug 27 2017 ## 3.3.2 (Aug 27 2017)
### Known issues ### Known issues
* Please use text datastore, key-value datastore no up-to-date * Please use text datastore, key-value datastore no up-to-date
@ -19,6 +68,7 @@
* If you use direct netconf get or get-config calls, you may need to handle the return XML differently. * If you use direct netconf get or get-config calls, you may need to handle the return XML differently.
* RESTCONF and CLI is not affected. * RESTCONF and CLI is not affected.
* Example: * Example:
``` ```
Query: Query:
<rpc><get/></rpc> <rpc><get/></rpc>
@ -42,6 +92,7 @@
* Added support for yang presence and no-presence containers. Previous default was "presence". * Added support for yang presence and no-presence containers. Previous default was "presence".
* Empty containers will be removed unless you have used the "presence" yang declaration. * Empty containers will be removed unless you have used the "presence" yang declaration.
* Example YANG without presence: * Example YANG without presence:
``` ```
container container
nopresence { nopresence {
@ -50,7 +101,9 @@
} }
} }
``` ```
If you submit "nopresence" without a leaf, it will automatically be removed: If you submit "nopresence" without a leaf, it will automatically be removed:
``` ```
<nopresence/> # removed <nopresence/> # removed
<nopresence> # not removed <nopresence> # not removed
@ -81,6 +134,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
* Validation for leafref forward and backward references; * Validation for leafref forward and backward references;
* CLI completion for generated cli leafrefs for both absolute and relative paths. * CLI completion for generated cli leafrefs for both absolute and relative paths.
* Example, relative path: * Example, relative path:
``` ```
leaf ifname { leaf ifname {
type leafref { type leafref {
@ -94,7 +148,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
* Restconf GET will return state data also, if defined. * Restconf GET will return state data also, if defined.
* You need to define state data in a backend callback. See the example and documentation for more details. * You need to define state data in a backend callback. See the example and documentation for more details.
### Minor changes: ### Minor Changes
* Added xpath support for predicate: current(), eg /interface[name=current()/../name] * Added xpath support for predicate: current(), eg /interface[name=current()/../name]
* Added prefix parsing of xpath, allowing eg /p:x/p:y, but prefix ignored. * Added prefix parsing of xpath, allowing eg /p:x/p:y, but prefix ignored.
* Corrected Yang union CLI generation and type validation. Recursive unions did not work. * Corrected Yang union CLI generation and type validation. Recursive unions did not work.
@ -110,7 +164,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
* Removed vector return values from xmldb_get() * Removed vector return values from xmldb_get()
* Generalized yang type resolution to all included (sub)modules not just the topmost * Generalized yang type resolution to all included (sub)modules not just the topmost
## 3.3.1 June 7 2017 ## 3.3.1 (June 7 2017)
* Fixed yang leafref cli completion for absolute paths. * Fixed yang leafref cli completion for absolute paths.
@ -118,9 +172,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
* Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000 * Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000
## 3.3.0 ## 3.3.0 (May 2017)
May 2017
* Datastore text module is now default. * Datastore text module is now default.
@ -161,7 +213,7 @@ May 2017
Instead use the rpc calls in clixon_proto_client.[ch] Instead use the rpc calls in clixon_proto_client.[ch]
In clients (eg cli/netconf) replace xmldb_get() in client code with In clients (eg cli/netconf) replace xmldb_get() in client code with
clicon_rpc_get_config(). clicon_rpc_get_config().
If you use the vector arguments of xmldb_get(), replace as follows: pIf you use the vector arguments of xmldb_get(), replace as follows:
xmldb_get(h, db, api_path, &xt, &xvec, &xlen); xmldb_get(h, db, api_path, &xt, &xvec, &xlen);
with with
clicon_rpc_get_config(h, dbstr, api_path, &xt); clicon_rpc_get_config(h, dbstr, api_path, &xt);
@ -211,9 +263,13 @@ May 2017
`load("Comment") <filename:string>,load_config_file("filename", "replace");` `load("Comment") <filename:string>,load_config_file("filename", "replace");`
If you write your own, you need to change the callback signature from; If you write your own, you need to change the callback signature from;
```
int cli_callback(clicon_handle h, cvec *vars, cg_var *arg) int cli_callback(clicon_handle h, cvec *vars, cg_var *arg)
```
to: to:
```
int cli_callback(clicon_handle h, cvec *vars, cvec *argv) int cli_callback(clicon_handle h, cvec *vars, cvec *argv)
```
and rewrite the code to handle argv instead of arg. and rewrite the code to handle argv instead of arg.
These are the system functions affected: These are the system functions affected:
cli_set, cli_merge, cli_del, cli_debug_backend, cli_set_mode, cli_set, cli_merge, cli_del, cli_debug_backend, cli_set_mode,
@ -225,7 +281,9 @@ May 2017
* 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.
Dec 2016: Dual license: both GPLv3 and APLv2 ## (Dec 2016)
* Dual license: both GPLv3 and APLv2
Feb 2016: Forked new clixon repository from clicon ## (Feb 2016)
* Forked new clixon repository from clicon

View file

@ -52,7 +52,7 @@ LIBS = @LIBS@
INCLUDES = -I. -I@srcdir@ @INCLUDES@ INCLUDES = -I. -I@srcdir@ @INCLUDES@
SHELL = /bin/sh SHELL = /bin/sh
SUBDIRS = lib apps include etc datastore SUBDIRS = lib apps include etc datastore yang
.PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker .PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker

View file

@ -47,6 +47,7 @@ libexecdir = @libexecdir@
localstatedir = @localstatedir@ localstatedir = @localstatedir@
sysconfdir = @sysconfdir@ sysconfdir = @sysconfdir@
includedir = @includedir@ includedir = @includedir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@ SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
@ -61,7 +62,7 @@ CLIXON_BACKEND_SYSDIR = $(libdir)/clixon/plugins/backend
# even though it may exist in $(libdir). But the new version may not have been installed yet. # even though it may exist in $(libdir). But the new version may not have been installed yet.
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB) -lpthread LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) -lpthread
CPPFLAGS = @CPPFLAGS@ -fPIC CPPFLAGS = @CPPFLAGS@ -fPIC
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@
@ -125,13 +126,17 @@ test.c :
echo "int main(){}" > $@ echo "int main(){}" > $@
test: test.c $(LIBOBJ) test: test.c $(LIBOBJ)
$(CC) $(INCLUDES) $(LDFLAGS) $< $(LIBOBJ) -L. -l:$(MYLIB) $(LIBS) -o $@ $(CC) $(INCLUDES) $(LDFLAGS) $< $(LIBOBJ) -L. $(MYLIB) $(LIBS) -o $@
$(APPL) : $(APPOBJ) $(MYLIBLINK) $(LIBDEPS) $(APPL) : $(APPOBJ) $(MYLIBLINK) $(LIBDEPS)
$(CC) $(LDFLAGS) $(APPOBJ) $(OBJS) -L. -l:$(MYLIB) $(LIBS) -o $@ $(CC) $(LDFLAGS) $(APPOBJ) $(OBJS) -L. $(MYLIB) $(LIBS) -o $@
$(MYLIB): $(LIBOBJ) $(MYLIB): $(LIBOBJ)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ -lc $(LIBOBJ)
else
$(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ -lc $(LIBOBJ) -Wl,-soname=$(MYLIBSO) $(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ -lc $(LIBOBJ) -Wl,-soname=$(MYLIBSO)
endif
# link-name is needed for application linking, eg for clixon_cli and clixon_backend # link-name is needed for application linking, eg for clixon_cli and clixon_backend
$(MYLIBLINK) : $(MYLIB) $(MYLIBLINK) : $(MYLIB)

View file

@ -1011,8 +1011,14 @@ from_client_msg(clicon_handle h,
} }
} }
reply: reply:
assert(cbuf_len(cbret)); if (cbuf_len(cbret) == 0)
clicon_debug(1, "%s %s", __FUNCTION__, cbuf_get(cbret)); cprintf(cbret, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>Internal error %s</error-message>"
"</rpc-error></rpc-reply>",clicon_err_reason);
clicon_debug(1, "%s cbret:%s", __FUNCTION__, cbuf_get(cbret));
if (send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1) < 0){ if (send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1) < 0){
switch (errno){ switch (errno){
case EPIPE: case EPIPE:
@ -1041,6 +1047,7 @@ from_client_msg(clicon_handle h,
if (retval < 0 && clicon_errno < 0) if (retval < 0 && clicon_errno < 0)
clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on error (message: %s)", clicon_log(LOG_NOTICE, "%s: Internal error: No clicon_err call on error (message: %s)",
__FUNCTION__, name?name:""); __FUNCTION__, name?name:"");
// clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
return retval;// -1 here terminates backend return retval;// -1 here terminates backend
} }
@ -1073,5 +1080,6 @@ from_client(int s,
done: done:
if (msg) if (msg)
free(msg); free(msg);
clicon_debug(1, "%s retval=%d", __FUNCTION__, retval);
return retval; /* -1 here terminates backend */ return retval; /* -1 here terminates backend */
} }

View file

@ -73,7 +73,11 @@
#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:b:Fzu:P:1IRCc:rg:py:x:" #ifdef BACKEND_STARTUP_BACKWARD_COMPAT
#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1s:c:IRCrg:py:x:" /* substitute s: for IRCc:r */
#else
#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1s:c:g:py:x:" /* substitute s: for IRCc:r */
#endif
/*! Terminate. Cannot use h after this */ /*! Terminate. Cannot use h after this */
static int static int
@ -114,8 +118,7 @@ backend_sig_term(int arg)
clicon_exit_set(); /* checked in event_loop() */ clicon_exit_set(); /* checked in event_loop() */
} }
/* /*! usage
* usage
*/ */
static void static void
usage(char *argv0, clicon_handle h) usage(char *argv0, clicon_handle h)
@ -128,20 +131,23 @@ usage(char *argv0, clicon_handle h)
fprintf(stderr, "usage:%s\n" fprintf(stderr, "usage:%s\n"
"where options are\n" "where options are\n"
" -h\t\tHelp\n" " -h\t\tHelp\n"
" -D <level>\tdebug\n" " -D <level>\tDebug level\n"
" -f <file>\tCLICON config file (mandatory)\n" " -f <file>\tCLICON config file (mandatory)\n"
" -d <dir>\tSpecify backend plugin directory (default: %s)\n" " -d <dir>\tSpecify backend plugin directory (default: %s)\n"
" -b <dir>\tSpecify XMLDB database directory\n" " -b <dir>\tSpecify XMLDB database directory\n"
" -z\t\tKill other config daemon and exit\n" " -z\t\tKill other config daemon and exit\n"
" -F\t\tforeground\n" " -F\t\tRun in foreground, do not run as daemon\n"
" -1\t\tonce (dont wait for events)\n" " -1\t\tRun once and then quit (dont wait for events)\n"
" -u <path>\tconfig UNIX domain path / ip address (default: %s)\n" " -u <path>\tConfig UNIX domain path / ip address (default: %s)\n"
" -P <file>\tPid filename (default: %s)\n" " -P <file>\tPid filename (default: %s)\n"
" -s <mode>\tSpecify backend startup mode: none|startup|running|init (replaces -IRCr\n"
" -c <file>\tLoad extra xml configuration, but don't commit.\n"
#ifdef BACKEND_STARTUP_BACKWARD_COMPAT
" -I\t\tInitialize running state database\n" " -I\t\tInitialize running state database\n"
" -R\t\tCall plugin_reset() in plugins to reset system state in running db (use with -I)\n" " -R\t\tCall plugin_reset() in plugins to reset system state in running db (use with -I)\n"
" -C\t\tCall plugin_reset() in plugins to reset system state in candidate db (use with -I)\n" " -C\t\tCall plugin_reset() in plugins to reset system state in candidate db (use with -I)\n"
" -c <file>\tLoad specified application config.\n"
" -r\t\tReload running database\n" " -r\t\tReload running database\n"
#endif /* BACKEND_STARTUP_BACKWARD_COMPAT */
" -p \t\tPrint database yang specification\n" " -p \t\tPrint database yang specification\n"
" -g <group>\tClient membership required to this group (default: %s)\n" " -g <group>\tClient membership required to this group (default: %s)\n"
" -y <file>\tOverride yang spec file (dont include .yang suffix)\n" " -y <file>\tOverride yang spec file (dont include .yang suffix)\n"
@ -166,69 +172,26 @@ db_reset(clicon_handle h,
return 0; return 0;
} }
/*! Initialize running-config from file application configuration /*! Merge db1 into db2 without commit
*
* @param[in] h clicon handle
* @param[in] app_config_file clicon application configuration file
* @param[in] running_db Name of running db
* @retval 0 OK
* @retval -1 Error. clicon_err set
*/ */
static int static int
rundb_main(clicon_handle h, db_merge(clicon_handle h,
char *app_config_file) const char *db1,
const char *db2)
{ {
int retval = -1; int retval = -1;
int fd = -1; cxobj *xt = NULL;
cxobj *xt = NULL;
cxobj *xn;
if (xmldb_create(h, "tmp") < 0) /* Get data as xml from db1 */
if (xmldb_get(h, (char*)db1, NULL, 1, &xt) < 0)
goto done; goto done;
if (xmldb_copy(h, "running", "tmp") < 0){ /* Merge xml into db2. WIthout commit */
clicon_err(OE_UNIX, errno, "file copy"); if (xmldb_put(h, (char*)db2, OP_MERGE, xt) < 0)
goto done;
}
if ((fd = open(app_config_file, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", app_config_file);
goto done;
}
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL)
if (xmldb_put(h, "tmp", OP_MERGE, xn) < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;
if (xmldb_delete(h, "tmp") < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
if (xt) if (xt)
xml_free(xt); xml_free(xt);
if (fd != -1)
close(fd);
return retval;
}
static int
candb_reset(clicon_handle h)
{
int retval = -1;
if (xmldb_copy(h, "running", "tmp") < 0){
clicon_err(OE_UNIX, errno, "file copy");
goto done;
}
/* Request plugins to reset system state, eg initiate running from system
* -R
*/
if (plugin_reset_state(h, "tmp") < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;
retval = 0;
done:
return retval; return retval;
} }
@ -291,6 +254,337 @@ backend_log_cb(int level,
return retval; return retval;
} }
/*! Call plugin_start with -- user options */
static int
plugin_start_useroptions(clicon_handle h,
char *argv0,
int argc,
char **argv)
{
char *tmp;
tmp = *(argv-1);
*(argv-1) = argv0;
if (plugin_start_argv(h, argc+1, argv-1) < 0)
return -1;
*(argv-1) = tmp;
return 0;
}
#ifdef BACKEND_STARTUP_BACKWARD_COMPAT
/*! Initialize running-config from file application configuration
*
* @param[in] h clicon handle
* @param[in] extraxml_file clicon application configuration file
* @param[in] running_db Name of running db
* @retval 0 OK
* @retval -1 Error. clicon_err set
*/
static int
rundb_main(clicon_handle h,
char *extraxml_file)
{
int retval = -1;
int fd = -1;
cxobj *xt = NULL;
cxobj *xn;
if (xmldb_create(h, "tmp") < 0)
goto done;
if (xmldb_copy(h, "running", "tmp") < 0)
goto done;
if ((fd = open(extraxml_file, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", extraxml_file);
goto done;
}
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if ((xn = xml_child_i(xt, 0)) != NULL)
if (xmldb_put(h, "tmp", OP_MERGE, xn) < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;
if (xmldb_delete(h, "tmp") < 0)
goto done;
retval = 0;
done:
if (xt)
xml_free(xt);
if (fd != -1)
close(fd);
return retval;
}
static int
candb_reset(clicon_handle h)
{
int retval = -1;
if (xmldb_copy(h, "running", "tmp") < 0)
goto done;
/* Request plugins to reset system state, eg initiate running from system
* -R
*/
if (plugin_reset_state(h, "tmp") < 0)
goto done;
if (candidate_commit(h, "tmp") < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Legacy (old-style) startup mode where flags -IRCcr was used
*/
static int
fragmented_startup_mode(clicon_handle h,
char *argv0,
int argc,
char *argv[],
int reload_running,
int init_rundb,
int reset_state_candidate,
int reset_state_running,
char *extraxml_file)
{
int retval = -1;
/* First check for startup config */
if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){
if (xmldb_exists(h, "startup") == 1){
/* copy startup config -> running */
if (xmldb_copy(h, "startup", "running") < 0)
goto done;
}
else
if (db_reset(h, "running") < 0)
goto done;
if (xmldb_create(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* If running exists and reload_running set, make a copy to candidate */
if (reload_running){
if (xmldb_exists(h, "running") != 1){
clicon_log(LOG_NOTICE, "%s: -r (reload running) option given but no running_db found, proceeding without", __PROGRAM__);
reload_running = 0; /* void it, so we dont commit candidate below */
}
else
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* Init running db
* -I or if it isnt there
*/
if (init_rundb || xmldb_exists(h, "running") != 1){
if (db_reset(h, "running") < 0)
goto done;
}
/* If candidate does not exist, create it from running */
if (xmldb_exists(h, "candidate") != 1){
if (xmldb_create(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0)
goto done;
if (reset_state_candidate){
if (candb_reset(h) < 0)
goto done;
}
else
if (reset_state_running){
if (plugin_reset_state(h, "running") < 0)
goto done;
}
if (plugin_start_useroptions(h, argv0, argc, argv) <0)
goto done;
if (reload_running){
/* This could be a failed validation, and we should not fail for that */
(void)candidate_commit(h, "candidate");
}
/* Have we specified a config file to load? eg
* -c [<file>]
*/
if (extraxml_file)
if (rundb_main(h, extraxml_file) < 0)
goto done;
/* Initiate the shared candidate. */
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
retval = 0;
done:
return retval;
}
#endif /* BACKEND_STARTUP_BACKWARD_COMPAT */
/*! Merge xml in filename into database
*/
static int
load_extraxml(clicon_handle h,
char *filename,
const char *db)
{
int retval = -1;
cxobj *xt = NULL;
int fd = -1;
if (filename == NULL)
return 0;
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
}
if (clicon_xml_parse_file(fd, &xt, "</config>") < 0)
goto done;
/* Replace parent w first child */
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
if (xmldb_put(h, (char*)db, OP_MERGE, xt) < 0)
goto done;
retval = 0;
done:
if (fd != -1)
close(fd);
if (xt)
xml_free(xt);
return retval;
}
/*! Clixon none startup modes Do not touch running state
*/
static int
startup_mode_none(clicon_handle h)
{
int retval = -1;
/* If it is not there, create candidate from running */
if (xmldb_exists(h, "candidate") != 1)
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
/* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Clixon init startup modes Start with a completely clean running state
*/
static int
startup_mode_init(clicon_handle h)
{
int retval = -1;
/* Reset running, regardless */
if (db_reset(h, "running") < 0)
goto done;
/* If it is not there, create candidate from running */
if (xmldb_exists(h, "candidate") != 1)
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
/* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Clixon running startup mode: Commit running db configuration into running
*
copy reset commit merge
running----+ |--------------------+-----+------>
\ / /
candidate +--------------------+ /
/
tmp |-------+-----+---------+
reset extra file
*/
static int
startup_mode_running(clicon_handle h,
char *extraxml_file)
{
int retval = -1;
/* Stash original running to candidate for later commit */
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
/* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0)
goto done;
/* Clear tmp db */
if (db_reset(h, "tmp") < 0)
goto done;
/* Application may define extra xml in its reset function*/
if (plugin_reset_state(h, "tmp") < 0)
goto done;
/* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0)
goto done;
/* Commit original running */
if (candidate_commit(h, "candidate") < 0)
goto done;
/* Merge user reset state and extra xml file (no commit) */
if (db_merge(h, "tmp", "running") < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Clixon startup startup mode: Commit startup configuration into running state
reset commit merge
running |--------------------+-----+------>
/ /
startup --------------------+ /
/
tmp |-------+-----+---------+
reset extra file
*/
static int
startup_mode_startup(clicon_handle h,
char *extraxml_file)
{
int retval = -1;
/* If startup does not exist, clear it */
if (xmldb_exists(h, "startup") != 1) /* diff */
if (xmldb_create(h, "startup") < 0) /* diff */
return -1;
/* Load plugins and call plugin_init() */
if (plugin_initiate(h) != 0)
goto done;
/* Clear tmp db */
if (db_reset(h, "tmp") < 0)
goto done;
/* Application may define extra xml in its reset function*/
if (plugin_reset_state(h, "tmp") < 0)
goto done;
/* Get application extra xml from file */
if (load_extraxml(h, extraxml_file, "tmp") < 0)
goto done;
/* Commit startup */
if (candidate_commit(h, "startup") < 0) /* diff */
goto done;
/* Merge user reset state and extra xml file (no commit) */
if (db_merge(h, "tmp", "running") < 0)
goto done;
retval = 0;
done:
return retval;
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -298,14 +592,16 @@ main(int argc, char **argv)
int zap; int zap;
int foreground; int foreground;
int once; int once;
int init_rundb; enum startup_mode_t startup_mode;
int reload_running; #ifdef BACKEND_STARTUP_BACKWARD_COMPAT
int reset_state_running; int init_rundb = 0;
int reset_state_candidate; int reset_state_running = 0;
char *app_config_file = NULL; int reset_state_candidate = 0;
int reload_running = 0;
#endif
char *extraxml_file;
char *config_group; char *config_group;
char *argv0 = argv[0]; char *argv0 = argv[0];
char *tmp;
struct stat st; struct stat st;
clicon_handle h; clicon_handle h;
int help = 0; int help = 0;
@ -317,7 +613,6 @@ main(int argc, char **argv)
char *xmldb_plugin; char *xmldb_plugin;
/* In the startup, logs to stderr & syslog and debug flag set later */ /* In the startup, logs to stderr & syslog and debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR|CLICON_LOG_SYSLOG); clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR|CLICON_LOG_SYSLOG);
/* Initiate CLICON handle */ /* Initiate CLICON handle */
if ((h = backend_handle_init()) == NULL) if ((h = backend_handle_init()) == NULL)
@ -327,10 +622,7 @@ main(int argc, char **argv)
foreground = 0; foreground = 0;
once = 0; once = 0;
zap = 0; zap = 0;
init_rundb = 0; extraxml_file = NULL;
reload_running = 0;
reset_state_running = 0;
reset_state_candidate = 0;
/* /*
* Command-line options for help, debug, and config-file * Command-line options for help, debug, and config-file
@ -402,32 +694,41 @@ main(int argc, char **argv)
case 'z': /* Zap other process */ case 'z': /* Zap other process */
zap++; zap++;
break; break;
case 'u': /* config unix domain path / ip address */ case 'u': /* config unix domain path / ip address */
if (!strlen(optarg)) if (!strlen(optarg))
usage(argv[0], h); usage(argv[0], h);
clicon_option_str_set(h, "CLICON_SOCK", optarg); clicon_option_str_set(h, "CLICON_SOCK", optarg);
break; break;
case 'P': /* pidfile */ case 'P': /* pidfile */
clicon_option_str_set(h, "CLICON_BACKEND_PIDFILE", optarg); clicon_option_str_set(h, "CLICON_BACKEND_PIDFILE", optarg);
break; break;
case 'I': /* Initiate running db */ case 's' : /* startup mode */
init_rundb++; clicon_option_str_set(h, "CLICON_STARTUP_MODE", optarg);
break; if (clicon_startup_mode(h) < 0){
case 'R': /* Reset state directly into running */ fprintf(stderr, "Invalid startup mode: %s\n", optarg);
reset_state_running++; usage(argv[0], h);
break; }
case 'C': /* Reset state into candidate and then commit it */ break;
reset_state_candidate++; case 'c': /* Load application config */
break; extraxml_file = optarg;
case 'c': /* Load application config */ break;
app_config_file = optarg; #ifdef BACKEND_STARTUP_BACKWARD_COMPAT
break; case 'I': /* Initiate running db */
case 'r': /* Reload running */ init_rundb++;
reload_running++; break;
break; case 'R': /* Reset state directly into running */
case 'g': /* config socket group */ reset_state_running++;
clicon_option_str_set(h, "CLICON_SOCK_GROUP", optarg); break;
break; case 'C': /* Reset state into candidate and then commit it */
reset_state_candidate++;
break;
case 'r': /* Reload running */
reload_running++;
break;
#endif /* BACKEND_STARTUP_BACKWARD_COMPAT */
case 'g': /* config socket group */
clicon_option_str_set(h, "CLICON_SOCK_GROUP", optarg);
break;
case 'p' : /* Print spec */ case 'p' : /* Print spec */
printspec++; printspec++;
break; break;
@ -522,87 +823,53 @@ main(int argc, char **argv)
goto done; goto done;
if (xmldb_setopt(h, "yangspec", clicon_dbspec_yang(h)) < 0) if (xmldb_setopt(h, "yangspec", clicon_dbspec_yang(h)) < 0)
goto done; goto done;
/* If startup mode is not defined, eg via OPTION or -s, assume old method */
/* First check for startup config startup_mode = clicon_startup_mode(h);
XXX the options below have become out-of-hand. if (startup_mode == -1){ /* Old style, fragmented mode, phase out */
Too complex, need to simplify*/ #ifdef BACKEND_STARTUP_BACKWARD_COMPAT
if (clicon_option_int(h, "CLICON_USE_STARTUP_CONFIG") > 0){ if (fragmented_startup_mode(h,
if (xmldb_exists(h, "startup") == 1){ argv0, argc, argv,
/* copy startup config -> running */ reload_running, init_rundb,
if (xmldb_copy(h, "startup", "running") < 0) reset_state_candidate, reset_state_running,
goto done; extraxml_file
} ) < 0)
else
if (db_reset(h, "running") < 0)
goto done;
if (xmldb_create(h, "candidate") < 0)
goto done; goto done;
#else
clicon_log(LOG_ERR, "Startup mode undefined. Specify option CLICON_STARTUP_MODE or specify -s option to clicon_backend.\n");
goto done;
#endif
}
else {
/* Init running db if it is not there
*/
if (xmldb_exists(h, "running") != 1)
if (xmldb_create(h, "running") < 0)
return -1;
switch (startup_mode){
case SM_NONE:
if (startup_mode_none(h) < 0)
goto done;
break;
case SM_INIT: /* -I */
if (startup_mode_init(h) < 0)
goto done;
break;
case SM_RUNNING: /* -CIr */
if (startup_mode_running(h, extraxml_file) < 0)
goto done;
break;
case SM_STARTUP: /* startup configuration */
if (startup_mode_startup(h, extraxml_file) < 0)
goto done;
break;
}
/* Initiate the shared candidate. */
if (xmldb_copy(h, "running", "candidate") < 0) if (xmldb_copy(h, "running", "candidate") < 0)
goto done; goto done;
} /* Call plugin_start with user -- options */
/* If running exists and reload_running set, make a copy to candidate */ if (plugin_start_useroptions(h, argv0, argc, argv) <0)
if (reload_running){
if (xmldb_exists(h, "running") != 1){
clicon_log(LOG_NOTICE, "%s: -r (reload running) option given but no running_db found, proceeding without", __PROGRAM__);
reload_running = 0; /* void it, so we dont commit candidate below */
}
else
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* Init running db
* -I or if it isnt there
*/
if (init_rundb || xmldb_exists(h, "running") != 1){
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_create(h, "candidate") < 0)
goto done;
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
}
/* Initialize plugins
(also calls plugin_init() and plugin_start(argc,argv) in each plugin */
if (plugin_initiate(h) != 0)
goto done;
if (reset_state_candidate){
if (candb_reset(h) < 0)
goto done;
}
else
if (reset_state_running){
if (plugin_reset_state(h, "running") < 0)
goto done;
}
/* Call plugin_start */
tmp = *(argv-1);
*(argv-1) = argv0;
if (plugin_start_hooks(h, argc+1, argv-1) < 0)
goto done;
*(argv-1) = tmp;
if (reload_running){
/* This could be a failed validation, and we should not fail for that */
(void)candidate_commit(h, "candidate");
}
/* Have we specified a config file to load? eg
* -c [<file>]
*/
if (app_config_file)
if (rundb_main(h, app_config_file) < 0)
goto done;
/* Initiate the shared candidate. Maybe we should not do this?
* Too strict access
*/
if (xmldb_copy(h, "running", "candidate") < 0)
goto done;
if (once) if (once)
goto done; goto done;

View file

@ -71,7 +71,7 @@
* @note the following should match the prototypes in clixon_backend.h * @note the following should match the prototypes in clixon_backend.h
*/ */
#define PLUGIN_RESET "plugin_reset" #define PLUGIN_RESET "plugin_reset"
typedef int (plgreset_t)(clicon_handle h, char *dbname); /* Reset system status */ typedef int (plgreset_t)(clicon_handle h, const char *db); /* Reset system status */
/*! Plugin callback, if defined called to get state data from plugin /*! Plugin callback, if defined called to get state data from plugin
* @param[in] h Clicon handle * @param[in] h Clicon handle
@ -115,8 +115,8 @@ struct plugin {
/* /*
* Local variables * Local variables
*/ */
static int nplugins = 0; static int _nplugins = 0;
static struct plugin *plugins = NULL; static struct plugin *_plugins = NULL;
/*! Find a plugin by name and return the dlsym handl /*! Find a plugin by name and return the dlsym handl
* Used by libclicon code to find callback funcctions in plugins. * Used by libclicon code to find callback funcctions in plugins.
@ -132,8 +132,8 @@ config_find_plugin(clicon_handle h,
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++){ for (i = 0; i < _nplugins; i++){
p = &plugins[i]; p = &_plugins[i];
if (strcmp(p->p_name, name) == 0) if (strcmp(p->p_name, name) == 0)
return p->p_handle; return p->p_handle;
} }
@ -254,18 +254,17 @@ backend_plugin_load (clicon_handle h,
*/ */
int int
plugin_reset_state(clicon_handle h, plugin_reset_state(clicon_handle h,
char *dbname) const char *db)
{ {
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < _nplugins; i++) {
for (i = 0; i < nplugins; i++) { p = &_plugins[i];
p = &plugins[i];
if (p->p_reset) { if (p->p_reset) {
clicon_debug(1, "Calling plugin_reset() for %s\n", clicon_debug(1, "Calling plugin_reset() for %s\n",
p->p_name); p->p_name);
if (((p->p_reset)(h, dbname)) < 0) { if (((p->p_reset)(h, db)) < 0) {
clicon_err(OE_FATAL, 0, "plugin_reset() failed for %s\n", clicon_err(OE_FATAL, 0, "plugin_reset() failed for %s\n",
p->p_name); p->p_name);
return -1; return -1;
@ -283,15 +282,15 @@ plugin_reset_state(clicon_handle h,
* @retval -1 Error * @retval -1 Error
*/ */
int int
plugin_start_hooks(clicon_handle h, plugin_start_argv(clicon_handle h,
int argc, int argc,
char **argv) char **argv)
{ {
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++) { for (i = 0; i < _nplugins; i++) {
p = &plugins[i]; p = &_plugins[i];
if (p->p_start) { if (p->p_start) {
optind = 0; optind = 0;
if (((p->p_start)(h, argc, argv)) < 0) { if (((p->p_start)(h, argc, argv)) < 0) {
@ -314,15 +313,15 @@ plugin_append(struct plugin *p)
{ {
struct plugin *new; struct plugin *new;
if ((new = realloc(plugins, (nplugins+1) * sizeof (*p))) == NULL) { if ((new = realloc(_plugins, (_nplugins+1) * sizeof (*p))) == NULL) {
clicon_err(OE_UNIX, errno, "realloc"); clicon_err(OE_UNIX, errno, "realloc");
return -1; return -1;
} }
memset (&new[nplugins], 0, sizeof(new[nplugins])); memset (&new[_nplugins], 0, sizeof(new[_nplugins]));
memcpy (&new[nplugins], p, sizeof(new[nplugins])); memcpy (&new[_nplugins], p, sizeof(new[_nplugins]));
plugins = new; _plugins = new;
nplugins++; _nplugins++;
return 0; return 0;
} }
@ -400,14 +399,15 @@ backend_plugin_load_dir(clicon_handle h,
quit: quit:
if (retval != 0) { if (retval != 0) {
/* XXX p is always NULL */ /* XXX p is always NULL */
if (plugins) { if (_plugins) {
while (--np >= 0){ while (--np >= 0){
if ((p = &plugins[np]) == NULL) if ((p = &_plugins[np]) == NULL)
continue; continue;
backend_plugin_unload(h, p); backend_plugin_unload(h, p);
free(p); free(p);
} }
free(plugins); free(_plugins);
_plugins=0;
} }
} }
if (dp) if (dp)
@ -431,11 +431,9 @@ plugin_initiate(clicon_handle h)
return -1; return -1;
/* Then load application plugins */ /* Then load application plugins */
if ((dir = clicon_backend_dir(h)) == NULL){ dir = clicon_backend_dir(h);
clicon_err(OE_PLUGIN, 0, "backend_dir not defined"); /* If backend directory, load the backend plugisn */
return -1; if (dir && backend_plugin_load_dir(h, dir) < 0)
}
if (backend_plugin_load_dir(h, dir) < 0)
return -1; return -1;
return 0; return 0;
@ -452,15 +450,15 @@ plugin_finish(clicon_handle h)
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++) { for (i = 0; i < _nplugins; i++) {
p = &plugins[i]; p = &_plugins[i];
backend_plugin_unload(h, p); backend_plugin_unload(h, p);
} }
if (plugins){ if (_plugins){
free(plugins); free(_plugins);
plugins = NULL; _plugins = NULL;
} }
nplugins = 0; _nplugins = 0;
return 0; return 0;
} }
@ -516,8 +514,8 @@ plugin_transaction_begin(clicon_handle h,
int retval = 0; int retval = 0;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++) { for (i = 0; i < _nplugins; i++) {
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_begin) if (p->p_trans_begin)
if ((retval = (p->p_trans_begin)(h, (transaction_data)td)) < 0){ if ((retval = (p->p_trans_begin)(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
@ -546,8 +544,8 @@ plugin_transaction_validate(clicon_handle h,
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++){ for (i = 0; i < _nplugins; i++){
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_validate) if (p->p_trans_validate)
if ((retval = (p->p_trans_validate)(h, (transaction_data)td)) < 0){ if ((retval = (p->p_trans_validate)(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
@ -576,8 +574,8 @@ plugin_transaction_complete(clicon_handle h,
int retval = 0; int retval = 0;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++){ for (i = 0; i < _nplugins; i++){
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_complete) if (p->p_trans_complete)
if ((retval = (p->p_trans_complete)(h, (transaction_data)td)) < 0){ if ((retval = (p->p_trans_complete)(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
@ -623,7 +621,7 @@ plugin_transaction_revert(clicon_handle h,
tr.td_tcvec = td->td_scvec; tr.td_tcvec = td->td_scvec;
for (i = nr-1; i>=0; i--){ for (i = nr-1; i>=0; i--){
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_commit) if (p->p_trans_commit)
if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){ if ((p->p_trans_commit)(h, (transaction_data)&tr) < 0){
clicon_log(LOG_NOTICE, "Plugin '%s' %s revert callback failed", clicon_log(LOG_NOTICE, "Plugin '%s' %s revert callback failed",
@ -651,8 +649,8 @@ plugin_transaction_commit(clicon_handle h,
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++){ for (i = 0; i < _nplugins; i++){
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_commit) if (p->p_trans_commit)
if ((retval = (p->p_trans_commit)(h, (transaction_data)td)) < 0){ if ((retval = (p->p_trans_commit)(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
@ -680,8 +678,8 @@ plugin_transaction_end(clicon_handle h,
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++) { for (i = 0; i < _nplugins; i++) {
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_end) if (p->p_trans_end)
if ((retval = (p->p_trans_end)(h, (transaction_data)td)) < 0){ if ((retval = (p->p_trans_end)(h, (transaction_data)td)) < 0){
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */ if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
@ -708,8 +706,8 @@ plugin_transaction_abort(clicon_handle h,
int i; int i;
struct plugin *p; struct plugin *p;
for (i = 0; i < nplugins; i++) { for (i = 0; i < _nplugins; i++) {
p = &plugins[i]; p = &_plugins[i];
if (p->p_trans_abort) if (p->p_trans_abort)
(p->p_trans_abort)(h, (transaction_data)td); /* dont abort on error */ (p->p_trans_abort)(h, (transaction_data)td); /* dont abort on error */
} }
@ -750,8 +748,8 @@ backend_statedata_call(clicon_handle h,
clicon_err(OE_CFG, ENOENT, "XML tree expected"); clicon_err(OE_CFG, ENOENT, "XML tree expected");
goto done; goto done;
} }
for (i = 0; i < nplugins; i++) { for (i = 0; i < _nplugins; i++) {
p = &plugins[i]; p = &_plugins[i];
if (p->p_statedata) { if (p->p_statedata) {
if ((x = xml_new("config", NULL)) == NULL) if ((x = xml_new("config", NULL)) == NULL)
goto done; goto done;

View file

@ -66,8 +66,8 @@ int backend_plugin_init(clicon_handle h);
int plugin_initiate(clicon_handle h); int plugin_initiate(clicon_handle h);
int plugin_finish(clicon_handle h); int plugin_finish(clicon_handle h);
int plugin_reset_state(clicon_handle h, char *dbname); int plugin_reset_state(clicon_handle h, const char *db);
int plugin_start_hooks(clicon_handle h, int argc, char **argv); int plugin_start_argv(clicon_handle h, int argc, char **argv);
int backend_statedata_call(clicon_handle h, char *xpath, cxobj *xml); int backend_statedata_call(clicon_handle h, char *xpath, cxobj *xml);

View file

@ -70,7 +70,7 @@ int plugin_exit(clicon_handle h);
/*! Reset system state to original state. Eg at reboot before running thru config. /*! Reset system state to original state. Eg at reboot before running thru config.
* @see plgreset_t * @see plgreset_t
*/ */
int plugin_reset(clicon_handle h, char *dbname); int plugin_reset(clicon_handle h, const char *db);
/*! Retreive statedata, add statedata to XML tree /*! Retreive statedata, add statedata to XML tree
* @see plgstatedata_ t * @see plgstatedata_ t

View file

@ -45,7 +45,7 @@
*/ */
struct client_entry; struct client_entry;
typedef int (*backend_rpc_cb)( typedef int (*backend_rpc_cb)(
clicon_handle h, clicon_handle h, /* CLicon handle */
cxobj *xe, /* Request: <rpc><xn></rpc> */ cxobj *xe, /* Request: <rpc><xn></rpc> */
struct client_entry *ce, /* Client session */ struct client_entry *ce, /* Client session */
cbuf *cbret,/* Reply eg <rpc-reply>... */ cbuf *cbret,/* Reply eg <rpc-reply>... */

View file

@ -48,6 +48,7 @@ libexecdir = @libexecdir@
localstatedir = @localstatedir@ localstatedir = @localstatedir@
sysconfdir = @sysconfdir@ sysconfdir = @sysconfdir@
includedir = @includedir@ includedir = @includedir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@ SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
@ -62,7 +63,8 @@ CLIXON_CLI_SYSDIR = $(libdir)/clixon/plugins/cli
# even though it may exist in $(libdir). But the new version may not have been installed yet. # even though it may exist in $(libdir). But the new version may not have been installed yet.
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB) -lpthread LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) -lpthread
CPPFLAGS = @CPPFLAGS@ -fPIC CPPFLAGS = @CPPFLAGS@ -fPIC
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
@ -121,13 +123,17 @@ test.c :
echo "int main(){}" > $@ echo "int main(){}" > $@
test: test.c $(LIBOBJ) test: test.c $(LIBOBJ)
$(CC) $(INCLUDES) $(LDFLAGS) $< $(LIBOBJ) -L. -l:$(MYLIB) $(LIBS) -o $@ $(CC) $(INCLUDES) $(LDFLAGS) $< $(LIBOBJ) -L. $(MYLIB) $(LIBS) -o $@
$(APPL): $(OBJS) $(MYLIBLINK) $(LIBDEPS) $(APPL): $(OBJS) $(MYLIBLINK) $(LIBDEPS)
$(CC) $(LDFLAGS) $(OBJS) -L. -l:$(MYLIB) $(LIBS) -o $@ $(CC) $(LDFLAGS) $(OBJS) -L. $(MYLIB) $(LIBS) -o $@
$(MYLIB) : $(LIBOBJS) $(MYLIB) : $(LIBOBJS)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ $(LIBOBJS) $(LIBS)
else
$(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJS) $(LIBS) -Wl,-soname=$(MYLIBSO) $(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJS) $(LIBS) -Wl,-soname=$(MYLIBSO)
endif
# link-name is needed for application linking, eg for clixon_cli and clixon_config # link-name is needed for application linking, eg for clixon_cli and clixon_config
$(MYLIBLINK) : $(MYLIB) $(MYLIBLINK) : $(MYLIB)

View file

@ -520,7 +520,7 @@ cli_quit(clicon_handle h,
cvec *vars, cvec *vars,
cvec *argv) cvec *argv)
{ {
cli_set_exiting(h, 1); cligen_exiting_set(cli_cligen(h), 1);
return 0; return 0;
} }
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv) int cli_quitv(clicon_handle h, cvec *vars, cvec *argv)
@ -1243,3 +1243,13 @@ cli_debug(clicon_handle h,
done: done:
return 0; return 0;
} }
int
cli_help(clicon_handle h, cvec *vars, cvec *argv)
{
cligen_handle ch = cli_cligen(h);
parse_tree *pt;
pt = cligen_tree_active_get(ch);
return cligen_help(stdout, *pt);
}

View file

@ -86,9 +86,7 @@ struct cli_handle {
}; };
/* /*! Return a clicon handle for other CLICON API calls
* cli_handle_init
* returns a clicon handle for other CLICON API calls
*/ */
clicon_handle clicon_handle
cli_handle_init(void) cli_handle_init(void)
@ -153,85 +151,14 @@ cli_syntax_set(clicon_handle h,
return 0; return 0;
} }
/*----------------------------------------------------------
* cligen access functions /*! Return clicon handle */
*----------------------------------------------------------*/
cligen_handle cligen_handle
cli_cligen(clicon_handle h) cli_cligen(clicon_handle h)
{ {
return cligen(h); return cligen(h);
} }
/*
* cli_interactive and clicon_eval
*/
int
cli_exiting(clicon_handle h)
{
cligen_handle ch = cligen(h);
return cligen_exiting(ch);
}
/*
* cli_common.c: cli_quit
* cli_interactive()
*/
int
cli_set_exiting(clicon_handle h, int exiting)
{
cligen_handle ch = cligen(h);
return cligen_exiting_set(ch, exiting);
}
char
cli_comment(clicon_handle h)
{
cligen_handle ch = cligen(h);
return cligen_comment(ch);
}
char
cli_set_comment(clicon_handle h, char c)
{
cligen_handle ch = cligen(h);
return cligen_comment_set(ch, c);
}
char
cli_tree_add(clicon_handle h, char *tree, parse_tree pt)
{
cligen_handle ch = cligen(h);
return cligen_tree_add(ch, tree, pt);
}
char *
cli_tree_active(clicon_handle h)
{
cligen_handle ch = cligen(h);
return cligen_tree_active(ch);
}
int
cli_tree_active_set(clicon_handle h, char *treename)
{
cligen_handle ch = cligen(h);
return cligen_tree_active_set(ch, treename);
}
parse_tree *
cli_tree(clicon_handle h, char *name)
{
cligen_handle ch = cligen(h);
return cligen_tree(ch, name);
}
int int
cli_parse_file(clicon_handle h, cli_parse_file(clicon_handle h,
FILE *f, FILE *f,

View file

@ -41,20 +41,12 @@
* Prototypes * Prototypes
* Internal prototypes. For exported functions see clicon_cli_api.h * Internal prototypes. For exported functions see clicon_cli_api.h
*/ */
char cli_tree_add(clicon_handle h, char *tree, parse_tree pt);
int cli_parse_file(clicon_handle h, int cli_parse_file(clicon_handle h,
FILE *f, FILE *f,
char *name, /* just for errs */ char *name, /* just for errs */
parse_tree *pt, parse_tree *pt,
cvec *globals); cvec *globals);
char *cli_tree_active(clicon_handle h);
int cli_tree_active_set(clicon_handle h, char *treename);
parse_tree *cli_tree(clicon_handle h, char *name);
int cli_susp_hook(clicon_handle h, cli_susphook_t *fn); int cli_susp_hook(clicon_handle h, cli_susphook_t *fn);
char *cli_nomatch(clicon_handle h); char *cli_nomatch(clicon_handle h);

View file

@ -70,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:y:" #define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:"
/*! terminate cli application */ /*! terminate cli application */
static int static int
@ -109,25 +109,86 @@ cli_signal_init (clicon_handle h)
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @see cligen_loop * @see cligen_loop
*/ */
static void static int
cli_interactive(clicon_handle h) cli_interactive(clicon_handle h)
{ {
int retval = -1;
int res; int res;
char *cmd; char *cmd;
char *new_mode; char *new_mode;
int result; int result;
/* Loop through all commands */ /* Loop through all commands */
while(!cli_exiting(h)) { while(!cligen_exiting(cli_cligen(h))) {
// save_mode =
new_mode = cli_syntax_mode(h); new_mode = cli_syntax_mode(h);
if ((cmd = clicon_cliread(h)) == NULL) { if ((cmd = clicon_cliread(h)) == NULL) {
cli_set_exiting(h, 1); /* EOF */ cligen_exiting_set(cli_cligen(h), 1); /* EOF */
break; retval = -1;
goto done;
} }
if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0) if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0)
break; goto done;
} }
retval = 0;
done:
return retval;
}
/*! Read file as configuration file and print xml file for migrating to new fmt
* @see clicon_option_readfile_xml
*/
static int
dump_configfile_xml_fn(FILE *fout,
const char *filename)
{
struct stat st;
char opt[1024];
char val[1024];
char line[1024];
char *cp;
FILE *f = NULL;
int retval = -1;
char *suffix;
if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified");
goto done;
}
if ((suffix = rindex(filename, '.')) != NULL &&
strcmp((suffix+1), "xml") == 0){
clicon_err(OE_CFG, 0, "Configfile %s should not be XML", filename);
goto done;
}
if (stat(filename, &st) < 0){
clicon_err(OE_UNIX, errno, "%s", filename);
goto done;
}
if (!S_ISREG(st.st_mode)){
clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
goto done;
}
if ((f = fopen(filename, "r")) == NULL) {
clicon_err(OE_UNIX, errno, "configure file: %s", filename);
return -1;
}
clicon_debug(2, "Reading config file %s", __FUNCTION__, filename);
fprintf(fout, "<config>\n");
while (fgets(line, sizeof(line), f)) {
if ((cp = strchr(line, '\n')) != NULL) /* strip last \n */
*cp = '\0';
/* Trim out comments, strip whitespace, and remove CR */
if ((cp = strchr(line, '#')) != NULL)
memcpy(cp, "\n", 2);
if (sscanf(line, "%s %s", opt, val) < 2)
continue;
fprintf(fout, "\t<%s>%s</%s>\n", opt, val, opt);
}
fprintf(fout, "</config>\n");
retval = 0;
done:
if (f)
fclose(f);
return retval;
} }
static void static void
@ -142,6 +203,7 @@ usage(char *argv0, clicon_handle h)
"\t-h \t\tHelp\n" "\t-h \t\tHelp\n"
"\t-D <level> \tDebug\n" "\t-D <level> \tDebug\n"
"\t-f <file> \tConfig-file (mandatory)\n" "\t-f <file> \tConfig-file (mandatory)\n"
"\t-x\t\tDump configuration file as XML on stdout (migration utility)\n"
"\t-F <file> \tRead commands from file (default stdin)\n" "\t-F <file> \tRead commands from file (default stdin)\n"
"\t-1\t\tDo not enter interactive mode\n" "\t-1\t\tDo not enter interactive mode\n"
"\t-u <sockpath>\tconfig UNIX domain path (default: %s)\n" "\t-u <sockpath>\tconfig UNIX domain path (default: %s)\n"
@ -152,7 +214,8 @@ usage(char *argv0, clicon_handle h)
"\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", "\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n"
"\t-c <file>\tSpecify cli spec file.\n",
argv0, argv0,
confsock ? confsock : "none", confsock ? confsock : "none",
plgdir ? plgdir : "none" plgdir ? plgdir : "none"
@ -178,6 +241,7 @@ main(int argc, char **argv)
int len; int len;
int logdst = CLICON_LOG_STDERR; int logdst = CLICON_LOG_STDERR;
char *restarg = NULL; /* what remains after options */ char *restarg = NULL; /* what remains after options */
int dump_configfile_xml = 0;
/* Defaults */ /* Defaults */
@ -186,10 +250,11 @@ main(int argc, char **argv)
/* Initiate CLICON handle */ /* Initiate CLICON handle */
if ((h = cli_handle_init()) == NULL) if ((h = cli_handle_init()) == NULL)
goto done; goto done;
if (cli_plugin_init(h) != 0) if (cli_plugin_init(h) != 0)
goto done; goto done;
once = 0; once = 0;
cli_set_comment(h, '#'); /* Default to handle #! clicon_cli scripts */ cligen_comment_set(cli_cligen(h), '#'); /* Default to handle #! clicon_cli scripts */
/* /*
* First-step command-line options for help, debug, config-file and log, * First-step command-line options for help, debug, config-file and log,
@ -198,7 +263,6 @@ main(int argc, char **argv)
opterr = 0; opterr = 0;
while ((c = getopt(argc, argv, CLI_OPTS)) != -1) while ((c = getopt(argc, argv, CLI_OPTS)) != -1)
switch (c) { switch (c) {
case '?':
case 'h': case 'h':
/* Defer the call to usage() to later. Reason is that for helpful /* Defer the call to usage() to later. Reason is that for helpful
text messages, default dirs, etc, are not set until later. text messages, default dirs, etc, are not set until later.
@ -216,6 +280,9 @@ main(int argc, char **argv)
usage(argv[0], h); usage(argv[0], h);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg); clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break; break;
case 'x': /* dump config file as xml (migration from .conf file)*/
dump_configfile_xml++;
break;
case 'l': /* Log destination: s|e|o */ case 'l': /* Log destination: s|e|o */
switch (optarg[0]){ switch (optarg[0]){
case 's': case 's':
@ -239,6 +306,14 @@ main(int argc, char **argv)
clicon_debug_init(debug, NULL); clicon_debug_init(debug, NULL);
/* Use cli as util tool to dump config file as xml for migration */
if (dump_configfile_xml) {
clicon_hash_t *copt = clicon_options(h);
char *configfile = hash_value(copt, "CLICON_CONFIGFILE", NULL);
if (dump_configfile_xml_fn(stdout, configfile) < 0)
goto done;
}
/* Find and read configfile */ /* Find and read configfile */
if (clicon_options_main(h) < 0){ if (clicon_options_main(h) < 0){
if (help) if (help)
@ -253,6 +328,7 @@ main(int argc, char **argv)
switch (c) { switch (c) {
case 'D' : /* debug */ case 'D' : /* debug */
case 'f': /* config file */ case 'f': /* config file */
case 'x': /* dump config file as xml */
case 'l': /* Log destination */ case 'l': /* Log destination */
break; /* see above */ break; /* see above */
case 'F': /* read commands from file */ case 'F': /* read commands from file */
@ -295,6 +371,10 @@ main(int argc, char **argv)
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg); clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg);
break; break;
} }
case 'c' :{ /* Overwrite clispec with absolute filename */
clicon_option_str_set(h, "CLICON_CLISPEC_FILE", optarg);
break;
}
default: default:
usage(argv[0], h); usage(argv[0], h);
break; break;
@ -320,13 +400,9 @@ 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;
/* Check plugin directory */ /* Create tree generated from dataspec. If no other trees exists, this is
if (clicon_cli_dir(h) == NULL){ * the only one.
clicon_err(OE_PLUGIN, 0, "clicon_cli_dir not defined"); */
goto done;
}
/* Create tree generated from dataspec */
if (clicon_cli_genmodel(h)){ if (clicon_cli_genmodel(h)){
yang_spec *yspec; /* yang spec */ yang_spec *yspec; /* yang spec */
parse_tree pt = {0,}; /* cli parse tree */ parse_tree pt = {0,}; /* cli parse tree */
@ -345,7 +421,7 @@ main(int argc, char **argv)
goto done; goto done;
} }
snprintf(treename, len, "datamodel:%s", clicon_dbspec_name(h)); snprintf(treename, len, "datamodel:%s", clicon_dbspec_name(h));
cli_tree_add(h, treename, pt); cligen_tree_add(cli_cligen(h), treename, pt);
if (printgen) if (printgen)
cligen_print(stdout, pt, 1); cligen_print(stdout, pt, 1);
@ -367,10 +443,8 @@ main(int argc, char **argv)
fprintf (stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n"); fprintf (stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n");
goto done; goto done;
} }
if (cli_tree(h, cli_syntax_mode(h)) == NULL){ if (cligen_tree_find(cli_cligen(h), cli_syntax_mode(h)) == NULL)
fprintf (stderr, "FATAL: No such cli mode: %s\n", cli_syntax_mode(h)); clicon_log(LOG_WARNING, "No such cli mode: %s (Specify cli mode with CLICON_CLI_MODE in config file or -m <mode> on command line", cli_syntax_mode(h));
goto done;
}
if (logclisyntax) if (logclisyntax)
cli_logsyntax_set(h, logclisyntax); cli_logsyntax_set(h, logclisyntax);
@ -410,7 +484,8 @@ main(int argc, char **argv)
// 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 */
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid()); clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
cli_terminate(h); if (h)
cli_terminate(h);
return 0; return 0;
} }

View file

@ -67,9 +67,9 @@
#include "cli_handle.h" #include "cli_handle.h"
/* /*! Name of master plugin functions
* Name of plugin functions
* More in clicon_plugin.h * More in clicon_plugin.h
* @note not really used consider documenting or remove
*/ */
#define PLUGIN_PROMPT_HOOK "plugin_prompt_hook" #define PLUGIN_PROMPT_HOOK "plugin_prompt_hook"
#define PLUGIN_PARSE_HOOK "plugin_parse_hook" #define PLUGIN_PARSE_HOOK "plugin_parse_hook"
@ -113,8 +113,7 @@ syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
return m; return m;
} }
/* /*! Find plugin by name
* Find plugin by name
*/ */
static struct cli_plugin * static struct cli_plugin *
plugin_find_cli(cli_syntax_t *stx, char *plgnam) plugin_find_cli(cli_syntax_t *stx, char *plgnam)
@ -131,19 +130,17 @@ plugin_find_cli(cli_syntax_t *stx, char *plgnam)
return NULL; return NULL;
} }
/* /*! Generate parse tree for syntax mode
* Generate parse tree for syntax mode
*/ */
static int static int
gen_parse_tree(clicon_handle h, cli_syntaxmode_t *m) gen_parse_tree(clicon_handle h, cli_syntaxmode_t *m)
{ {
cli_tree_add(h, m->csm_name, m->csm_pt); cligen_tree_add(cli_cligen(h), m->csm_name, m->csm_pt);
return 0; return 0;
} }
/* /*! Append syntax
* Append syntax
*/ */
static int static int
syntax_append(clicon_handle h, syntax_append(clicon_handle h,
@ -162,8 +159,7 @@ syntax_append(clicon_handle h,
return 0; return 0;
} }
/* /*! Unload all plugins in a group
* Unload all plugins in a group
*/ */
static int static int
cli_syntax_unload(clicon_handle h) cli_syntax_unload(clicon_handle h)
@ -271,13 +267,15 @@ quit:
return cp; return cp;
} }
/* /*! Append to syntax mode from file
* Append to syntax mode from file * @param[in] h Clixon handle
* Arguments: * @param[in] filename Name of file where syntax is specified (in syntax-group dir)
* filename : Name of file where syntax is specified (in syntax-group dir) * @param[in] dir Name of dir, or NULL
*/ */
static int static int
cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir) cli_load_syntax(clicon_handle h,
const char *filename,
const char *dir)
{ {
void *handle = NULL; /* Handle to plugin .so module */ void *handle = NULL; /* Handle to plugin .so module */
char *mode = NULL; /* Name of syntax mode to append new syntax */ char *mode = NULL; /* Name of syntax mode to append new syntax */
@ -285,15 +283,18 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
int retval = -1; int retval = -1;
FILE *f; FILE *f;
char filepath[MAXPATHLEN]; char filepath[MAXPATHLEN];
cvec *vr = NULL; cvec *cvv = NULL;
char *prompt = NULL; char *prompt = NULL;
char **vec = NULL; char **vec = NULL;
int i, nvec; int i, nvec;
char *plgnam; char *plgnam;
struct cli_plugin *p; struct cli_plugin *p;
snprintf(filepath, MAXPATHLEN-1, "%s/%s", clispec_dir, filename); if (dir)
if ((vr = cvec_new(0)) == NULL){ snprintf(filepath, MAXPATHLEN-1, "%s/%s", dir, filename);
else
snprintf(filepath, MAXPATHLEN-1, "%s", filename);
if ((cvv = cvec_new(0)) == NULL){
clicon_err(OE_PLUGIN, errno, "cvec_new"); clicon_err(OE_PLUGIN, errno, "cvec_new");
goto done; goto done;
} }
@ -304,7 +305,7 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
} }
/* Assuming this plugin is first in queue */ /* Assuming this plugin is first in queue */
if (cli_parse_file(h, f, filepath, &pt, vr) < 0){ if (cli_parse_file(h, f, filepath, &pt, cvv) < 0){
clicon_err(OE_PLUGIN, 0, "failed to parse cli file %s", filepath); clicon_err(OE_PLUGIN, 0, "failed to parse cli file %s", filepath);
fclose(f); fclose(f);
goto done; goto done;
@ -312,9 +313,9 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
fclose(f); fclose(f);
/* Get CLICON specific global variables */ /* Get CLICON specific global variables */
prompt = cvec_find_str(vr, "CLICON_PROMPT"); prompt = cvec_find_str(cvv, "CLICON_PROMPT");
plgnam = cvec_find_str(vr, "CLICON_PLUGIN"); plgnam = cvec_find_str(cvv, "CLICON_PLUGIN");
mode = cvec_find_str(vr, "CLICON_MODE"); mode = cvec_find_str(cvv, "CLICON_MODE");
if (plgnam != NULL) { /* Find plugin for callback resolving */ if (plgnam != NULL) { /* Find plugin for callback resolving */
if ((p = plugin_find_cli (cli_syntax(h), plgnam)) != NULL) if ((p = plugin_find_cli (cli_syntax(h), plgnam)) != NULL)
@ -355,18 +356,19 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
retval = 0; retval = 0;
done: done:
if (vr) if (cvv)
cvec_free(vr); cvec_free(cvv);
if (vec) if (vec)
free(vec); free(vec);
return retval; return retval;
} }
/* /*! Load plugins within a directory
* Load plugins within a directory
*/ */
static int static int
cli_plugin_load_dir(clicon_handle h, char *dir, cli_syntax_t *stx) cli_plugin_load_dir(clicon_handle h,
char *dir,
cli_syntax_t *stx)
{ {
int i; int i;
int ndp; int ndp;
@ -431,8 +433,8 @@ cli_plugin_load_dir(clicon_handle h, char *dir, cli_syntax_t *stx)
} }
/* /*! Load a syntax group.
* Load a syntax group. * @param[in] h Clicon handle
*/ */
int int
cli_syntax_load (clicon_handle h) cli_syntax_load (clicon_handle h)
@ -440,6 +442,7 @@ cli_syntax_load (clicon_handle h)
int retval = -1; int retval = -1;
char *plugin_dir = NULL; char *plugin_dir = NULL;
char *clispec_dir = NULL; char *clispec_dir = NULL;
char *clispec_file = NULL;
int ndp; int ndp;
int i; int i;
struct dirent *dp = NULL; struct dirent *dp = NULL;
@ -451,14 +454,9 @@ cli_syntax_load (clicon_handle h)
return 0; return 0;
/* Format plugin directory path */ /* Format plugin directory path */
if ((plugin_dir = clicon_cli_dir(h)) == NULL){ plugin_dir = clicon_cli_dir(h);
clicon_err(OE_FATAL, 0, "clicon_cli_dir not set"); clispec_dir = clicon_clispec_dir(h);
goto quit; clispec_file = clicon_option_str(h, "CLICON_CLISPEC_FILE");
}
if ((clispec_dir = clicon_clispec_dir(h)) == NULL){
clicon_err(OE_FATAL, 0, "clicon_clispec_dir not set");
goto quit;
}
/* Allocate plugin group object */ /* Allocate plugin group object */
if ((stx = malloc(sizeof(*stx))) == NULL) { if ((stx = malloc(sizeof(*stx))) == NULL) {
@ -475,20 +473,25 @@ cli_syntax_load (clicon_handle h)
goto quit; goto quit;
/* Then load application plugins */ /* Then load application plugins */
if (cli_plugin_load_dir(h, plugin_dir, stx) < 0) if (plugin_dir && cli_plugin_load_dir(h, plugin_dir, stx) < 0)
goto quit; goto quit;
/* load syntaxfiles */ if (clispec_file){
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0) if (cli_load_syntax(h, clispec_file, NULL) < 0)
goto quit;
/* Load the rest */
for (i = 0; i < ndp; i++) {
clicon_debug(1, "DEBUG: Loading syntax '%.*s'",
(int)strlen(dp[i].d_name)-4, dp[i].d_name);
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
goto quit; goto quit;
} }
if (clispec_dir){
/* load syntaxfiles */
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0)
goto quit;
/* Load the rest */
for (i = 0; i < ndp; i++) {
clicon_debug(1, "DEBUG: Loading syntax '%.*s'",
(int)strlen(dp[i].d_name)-4, dp[i].d_name);
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
goto quit;
}
}
/* Did we successfully load any syntax modes? */ /* Did we successfully load any syntax modes? */
if (stx->stx_nmodes <= 0) { if (stx->stx_nmodes <= 0) {
retval = 0; retval = 0;
@ -519,8 +522,7 @@ quit:
return retval; return retval;
} }
/* /*! Call plugin_start() in all plugins
* Call plugin_start() in all plugins
*/ */
int int
cli_plugin_start(clicon_handle h, int argc, char **argv) cli_plugin_start(clicon_handle h, int argc, char **argv)
@ -544,7 +546,6 @@ cli_plugin_start(clicon_handle h, int argc, char **argv)
} }
/* /*
*/ */
int int
cli_plugin_finish(clicon_handle h) cli_plugin_finish(clicon_handle h)
@ -575,16 +576,20 @@ cli_handler_err(FILE *f)
} }
/* /*! Evaluate a matched command
* Evaluate a matched command * @param[in] h Clicon handle
* @param[in] cmd The command string
*/ */
int int
clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr) clicon_eval(clicon_handle h,
char *cmd,
cg_obj *match_obj,
cvec *cvv)
{ {
cli_output_reset(); cli_output_reset();
if (!cli_exiting(h)) { if (!cligen_exiting(cli_cligen(h))) {
clicon_err_reset(); clicon_err_reset();
if (cligen_eval(cli_cligen(h), match_obj, vr) < 0) { if (cligen_eval(cli_cligen(h), match_obj, cvv) < 0) {
#if 0 /* This is removed since we get two error messages on failure. #if 0 /* This is removed since we get two error messages on failure.
But maybe only sometime? But maybe only sometime?
Both a real log when clicon_err is called, and the here again. Both a real log when clicon_err is called, and the here again.
@ -606,24 +611,24 @@ clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr)
* the new mode string. * the new mode string.
* *
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] cmd The command string * @param[in] cmd Command string
* @param[in,out] mode A pointer to the mode string pointer * @param[in,out] modenamep Pointer to the mode string pointer
* @param[out] result -2 On eof (shouldnt happen) * @param[out] result -2 On eof (shouldnt happen)
* -1 On parse error * -1 On parse error
* >=0 Number of matches * >=0 Number of matches
*/ */
int int
clicon_parse(clicon_handle h, clicon_parse(clicon_handle h,
char *cmd, char *cmd,
char **mode, char **modenamep,
int *result) int *result)
{ {
char *m, *msav; char *modename;
char *modename0;
int res = -1; int res = -1;
int r; int r;
cli_syntax_t *stx = NULL; cli_syntax_t *stx = NULL;
cli_syntaxmode_t *smode; cli_syntaxmode_t *smode;
char *treename;
parse_tree *pt; /* Orig */ parse_tree *pt; /* Orig */
cg_obj *match_obj; cg_obj *match_obj;
cvec *cvv = NULL; cvec *cvv = NULL;
@ -634,26 +639,27 @@ clicon_parse(clicon_handle h,
else else
f = stderr; f = stderr;
stx = cli_syntax(h); stx = cli_syntax(h);
m = *mode; if ((modename = *modenamep) == NULL) {
if (m == NULL) {
smode = stx->stx_active_mode; smode = stx->stx_active_mode;
m = smode->csm_name; modename = smode->csm_name;
} }
else { else {
if ((smode = syntax_mode_find(stx, m, 0)) == NULL) { if ((smode = syntax_mode_find(stx, modename, 0)) == NULL) {
cli_output(f, "Can't find syntax mode '%s'\n", m); cli_output(f, "Can't find syntax mode '%s'\n", modename);
return -1; return -1;
} }
} }
msav = NULL;
while(smode) { while(smode) {
if (cli_tree_active(h)) modename0 = NULL;
msav = strdup(cli_tree_active(h)); if ((pt = cligen_tree_active_get(cli_cligen(h))) != NULL)
cli_tree_active_set(h, m); modename0 = pt->pt_name;
treename = cli_tree_active(h); if (cligen_tree_active_set(cli_cligen(h), modename) < 0){
if ((pt = cli_tree(h, treename)) == NULL){ fprintf(stderr, "No such parse-tree registered: %s\n", modename);
fprintf(stderr, "No such parse-tree registered: %s\n", treename); goto done;
goto done;; }
if ((pt = cligen_tree_active_get(cli_cligen(h))) == NULL){
fprintf(stderr, "No such parse-tree registered: %s\n", modename);
goto done;
} }
if ((cvv = cvec_new(0)) == NULL){ if ((cvv = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new"); clicon_err(OE_UNIX, errno, "cvec_new");
@ -662,12 +668,10 @@ clicon_parse(clicon_handle h,
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv); res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
if (res != CG_MATCH) if (res != CG_MATCH)
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */ pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
if (msav){ if (modename0){
cli_tree_active_set(h, msav); cligen_tree_active_set(cli_cligen(h), modename0);
free(msav); modename0 = NULL;
} }
msav = NULL;
switch (res) { switch (res) {
case CG_EOF: /* eof */ case CG_EOF: /* eof */
case CG_ERROR: case CG_ERROR:
@ -677,11 +681,11 @@ clicon_parse(clicon_handle h,
smode = NULL; smode = NULL;
if (stx->stx_parse_hook) { if (stx->stx_parse_hook) {
/* Try to find a match in upper modes, a'la IOS. */ /* Try to find a match in upper modes, a'la IOS. */
if ((m = stx->stx_parse_hook(h, cmd, m)) != NULL) { if ((modename = stx->stx_parse_hook(h, cmd, modename)) != NULL) {
if ((smode = syntax_mode_find(stx, m, 0)) != NULL) if ((smode = syntax_mode_find(stx, modename, 0)) != NULL)
continue; continue;
else else
cli_output(f, "Can't find syntax mode '%s'\n", m); cli_output(f, "Can't find syntax mode '%s'\n", modename);
} }
} }
/* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s", /* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s",
@ -690,9 +694,9 @@ clicon_parse(clicon_handle h,
cmd, cli_nomatch(h)); cmd, cli_nomatch(h));
break; break;
case CG_MATCH: case CG_MATCH:
if (m != *mode){ /* Command in different mode */ if (strcmp(modename, *modenamep)){ /* Command in different mode */
*mode = m; *modenamep = modename;
cli_set_syntax_mode(h, m); cli_set_syntax_mode(h, modename);
} }
if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0) if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0)
cli_handler_err(stdout); cli_handler_err(stdout);
@ -713,8 +717,9 @@ done:
return res; return res;
} }
/* /*! Read command from CLIgen's cliread() using current syntax mode.
* Read command from CLIgen's cliread() using current syntax mode. * @retval string char* buffer containing CLIgen command
* @retval NULL Fatal error
*/ */
char * char *
clicon_cliread(clicon_handle h) clicon_cliread(clicon_handle h)
@ -733,7 +738,7 @@ clicon_cliread(clicon_handle h)
cli_prompt_set(h, ""); cli_prompt_set(h, "");
else else
cli_prompt_set(h, cli_prompt(pfmt ? pfmt : mode->csm_prompt)); cli_prompt_set(h, cli_prompt(pfmt ? pfmt : mode->csm_prompt));
cli_tree_active_set(h, mode->csm_name); cligen_tree_active_set(cli_cligen(h), mode->csm_name);
ret = cliread(cli_cligen(h)); ret = cliread(cli_cligen(h));
if (pfmt) if (pfmt)
free(pfmt); free(pfmt);
@ -901,102 +906,6 @@ cli_prompt(char *fmt)
return prompt; return prompt;
} }
/*
* Run command in CLI engine
*/
int
cli_exec(clicon_handle h, char *cmd, char **mode, int *result)
{
return clicon_parse(h, cmd, mode, result);
}
/*
* push_one
* nifty code that 'pushes' a syntax one ore more levels
* op: eg "set"
* cmd: eg "edit policy-options"
*/
int
cli_ptpush(clicon_handle h, char *mode, char *string, char *op)
{
cg_obj *co, *co_cmd, *cc;
parse_tree *pt;
char **vec = NULL;
int i, j, nvec;
int found;
parse_tree pt_top;
cli_syntaxmode_t *m;
if ((m = syntax_mode_find(cli_syntax(h), mode, 0)) == NULL)
return 0;
pt_top = m->csm_pt;
if ((co_cmd = co_find_one(pt_top, op)) == NULL)
return 0;
pt = &co_cmd->co_pt;
/* vec is the command, eg 'edit policy_option' */
if ((vec = clicon_strsep(string, " ", &nvec)) == NULL)
goto catch;
co = NULL;
found = 0;
for (i=0; i<nvec; i++){
found = 0;
for (j=0; j<pt->pt_len; j++){
co = pt->pt_vec[j];
if (co && co->co_type == CO_COMMAND &&
(strcmp(co->co_command, vec[i])==0)){
pt = &co->co_pt;
found++;
break;
}
}
if (!found)
break;//not found on this level
}
if (found){ // match all levels
if (!co_cmd->co_pushed){
co_cmd->co_pt_push = co_cmd->co_pt;
co_cmd->co_pushed++;
}
co_cmd->co_pt = co->co_pt;
pt = &co_cmd->co_pt;
for (i=0; i<pt->pt_len; i++) /* set correct parent */
if ((cc = pt->pt_vec[i]) != NULL)
co_up_set(cc, co_cmd);
}
catch:
if (vec)
free(vec);
return 0;
}
int
cli_ptpop(clicon_handle h, char *mode, char *op)
{
cg_obj *co_cmd, *cc;
int i;
parse_tree *pt;
parse_tree pt_top;
cli_syntaxmode_t *m;
if ((m = syntax_mode_find(cli_syntax(h), mode, 0)) == NULL)
return 0;
pt_top = m->csm_pt;
if ((co_cmd = co_find_one(pt_top, op)) == NULL) //set
return 0;
if (!co_cmd->co_pushed)
return 0;
co_cmd->co_pushed = 0;
co_cmd->co_pt = co_cmd->co_pt_push;
pt = &co_cmd->co_pt;
for (i=0; i<pt->pt_len; i++) /* set correct parent */
if ((cc = pt->pt_vec[i]) != NULL)
co_up_set(cc, co_cmd);
return 0;
}
/*! Find a cli plugin based on name and resolve a function pointer in it. /*! Find a cli plugin based on name and resolve a function pointer in it.
* Callback from clicon_dbvars_parse() * Callback from clicon_dbvars_parse()
* Find a cli plugin based on name if given and use dlsym to resolve a * Find a cli plugin based on name if given and use dlsym to resolve a

View file

@ -99,7 +99,8 @@ expand_dbvar(void *h,
cvec *helptexts) cvec *helptexts)
{ {
int retval = -1; int retval = -1;
char *api_path; char *api_path_fmt;
char *api_path = NULL;
char *dbstr; char *dbstr;
cxobj *xt = NULL; cxobj *xt = NULL;
char *xpath = NULL; char *xpath = NULL;
@ -145,13 +146,16 @@ expand_dbvar(void *h,
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <api_path>"); clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <api_path>");
goto done; goto done;
} }
api_path = cv_string_get(cv); api_path_fmt = cv_string_get(cv);
/* api_path = /interface/%s/address/%s /* api_path_fmt = /interface/%s/address/%s
--> ^/interface/eth0/address/.*$ --> ^/interface/eth0/address/.*$
--> /interface/[name=eth0]/address --> /interface/[name=eth0]/address
*/ */
if (api_path_fmt2xpath(api_path, cvv, &xpath) < 0) if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0)
goto done; goto done;
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
goto done;
/* XXX read whole configuration, why not send xpath? */ /* XXX read whole configuration, why not send xpath? */
if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0) if (clicon_rpc_get_config(h, dbstr, "/", &xt) < 0)
goto done; goto done;
@ -165,7 +169,7 @@ expand_dbvar(void *h,
if ((xtop = xml_new("config", NULL)) == NULL) if ((xtop = xml_new("config", NULL)) == NULL)
goto done; goto done;
xbot = xtop; xbot = xtop;
/* This is primarily to get "y", XXX xbot can be broken (contains =%s) /* This is primarily to get "y",
* xpath2xml would have worked!! * xpath2xml would have worked!!
*/ */
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0) if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
@ -236,6 +240,8 @@ expand_dbvar(void *h,
} }
retval = 0; retval = 0;
done: done:
if (api_path)
free(api_path);
if (xvec) if (xvec)
free(xvec); free(xvec);
if (xtop) if (xtop)
@ -322,6 +328,7 @@ expand_dir(char *dir,
strerror(errno)); strerror(errno));
goto quit; goto quit;
} }
#ifndef __APPLE__
if (0 &&detail){ if (0 &&detail){
if ((pw = getpwuid(st.st_uid)) == NULL){ if ((pw = getpwuid(st.st_uid)) == NULL){
fprintf(stderr, "expand_dir: getpwuid(%d): %s\n", fprintf(stderr, "expand_dir: getpwuid(%d): %s\n",
@ -355,6 +362,7 @@ expand_dir(char *dir,
); );
cmd = str; cmd = str;
} }
#endif /* __APPLE__ */
if (((*commands) = if (((*commands) =
realloc(*commands, ((*nr)+1)*sizeof(char**))) == NULL){ realloc(*commands, ((*nr)+1)*sizeof(char**))) == NULL){
perror("expand_dir: realloc"); perror("expand_dir: realloc");

View file

@ -54,15 +54,10 @@ int cli_syntax_load(clicon_handle h);
int cli_handler_err(FILE *fd); int cli_handler_err(FILE *fd);
int cli_set_prompt(clicon_handle h, const char *mode, const char *prompt); int cli_set_prompt(clicon_handle h, const char *mode, const char *prompt);
char *cli_prompt(char *fmt); char *cli_prompt(char *fmt);
int cli_exec(clicon_handle h, char *cmd, char **mode, int *result);
int cli_ptpush(clicon_handle h, char *mode, char *string, char *op); int cli_ptpush(clicon_handle h, char *mode, char *string, char *op);
int cli_ptpop(clicon_handle h, char *mode, char *op); int cli_ptpop(clicon_handle h, char *mode, char *op);
/* cli_handle.c */ /* cli_handle.c */
char cli_set_comment(clicon_handle h, char c);
char cli_comment(clicon_handle h);
int cli_set_exiting(clicon_handle h, int exiting);
int cli_exiting(clicon_handle h);
clicon_handle cli_handle_init(void); clicon_handle cli_handle_init(void);
int cli_handle_exit(clicon_handle h); int cli_handle_exit(clicon_handle h);
cligen_handle cli_cligen(clicon_handle h); cligen_handle cli_cligen(clicon_handle h);
@ -133,6 +128,8 @@ int cli_lock(clicon_handle h, cvec *cvv, cvec *argv);
int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv); int cli_unlock(clicon_handle h, cvec *cvv, cvec *argv);
int cli_copy_config(clicon_handle h, cvec *cvv, cvec *argv); int cli_copy_config(clicon_handle h, cvec *cvv, cvec *argv);
int cli_help(clicon_handle h, cvec *vars, cvec *argv);
/* 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, int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,

View file

@ -47,6 +47,7 @@ libexecdir = @libexecdir@
localstatedir = @localstatedir@ localstatedir = @localstatedir@
sysconfdir = @sysconfdir@ sysconfdir = @sysconfdir@
includedir = @includedir@ includedir = @includedir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@ SH_SUFFIX = @SH_SUFFIX@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
@ -58,7 +59,8 @@ CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
# For dependency # For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB) LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB)
CPPFLAGS = @CPPFLAGS@ -fPIC CPPFLAGS = @CPPFLAGS@ -fPIC
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@
@ -112,10 +114,14 @@ uninstall:
$(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" $(CFLAGS) -c $< $(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" $(CFLAGS) -c $<
$(APPL) : $(OBJS) $(MYLIBLINK) $(LIBDEPS) $(APPL) : $(OBJS) $(MYLIBLINK) $(LIBDEPS)
$(CC) $(LDFLAGS) $(OBJS) -L. -l:$(MYLIB) $(LIBS) -o $@ $(CC) $(LDFLAGS) $(OBJS) -L. $(MYLIB) $(LIBS) -o $@
$(MYLIB) : $(LIBOBJS) $(MYLIB) : $(LIBOBJS)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ $(LIBOBJS) $(LIBS)
else
$(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJS) $(LIBS) -Wl,-soname=$(MYLIBSO) $(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJS) $(LIBS) -Wl,-soname=$(MYLIBSO)
endif
# link-name is needed for application linking, eg for clixon_cli and clixon_config # link-name is needed for application linking, eg for clixon_cli and clixon_config
$(MYLIBLINK) : $(MYLIB) $(MYLIBLINK) : $(MYLIB)

View file

@ -121,8 +121,7 @@ process_incoming_packet(clicon_handle h,
if (xpath_first(xreq, "//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: dropped");
ed");
goto done; goto done;
} }
if (!isrpc){ /* hello */ if (!isrpc){ /* hello */
@ -170,6 +169,9 @@ ed");
/*! Get netconf message: detect end-of-msg /*! Get netconf message: detect end-of-msg
* @param[in] s Socket where input arrived. read from this. * @param[in] s Socket where input arrived. read from this.
* @param[in] arg Clicon handle. * @param[in] arg Clicon handle.
* This routine continuously reads until no more data on s. There could
* be risk of starvation, but the netconf client does little else than
* read data so I do not see a danger of true starvation here.
*/ */
static int static int
netconf_input_cb(int s, netconf_input_cb(int s,
@ -182,43 +184,51 @@ netconf_input_cb(int s,
int len; int len;
cbuf *cb=NULL; cbuf *cb=NULL;
int xml_state = 0; int xml_state = 0;
int poll;
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){ while (1){
if (errno == ECONNRESET) if ((len = read(s, buf, sizeof(buf))) < 0){
len = 0; /* emulate EOF */ if (errno == ECONNRESET)
else{ len = 0; /* emulate EOF */
clicon_log(LOG_ERR, "%s: read: %s", __FUNCTION__, strerror(errno)); else{
goto done; clicon_log(LOG_ERR, "%s: read: %s", __FUNCTION__, strerror(errno));
}
} /* read */
if (len == 0){ /* EOF */
cc_closed++;
close(s);
retval = 0;
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)) {
/* OK, we have an xml string from a client */
if (process_incoming_packet(h, cb) < 0){
goto done; goto done;
} }
if (cc_closed) } /* read */
break; if (len == 0){ /* EOF */
cbuf_reset(cb); cc_closed++;
close(s);
retval = 0;
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)) {
/* OK, we have an xml string from a client */
if (process_incoming_packet(h, cb) < 0){
goto done;
}
if (cc_closed)
break;
cbuf_reset(cb);
}
}
/* poll==1 if more, poll==0 if none */
if ((poll = event_poll(s)) < 0)
goto done;
if (poll == 0)
break; /* No data to read */
} /* while */
retval = 0; retval = 0;
done: done:
if (cb) if (cb)
@ -276,8 +286,6 @@ static void
usage(clicon_handle h, usage(clicon_handle h,
char *argv0) char *argv0)
{ {
char *netconfdir = clicon_netconf_dir(h);
fprintf(stderr, "usage:%s\n" fprintf(stderr, "usage:%s\n"
"where options are\n" "where options are\n"
"\t-h\t\tHelp\n" "\t-h\t\tHelp\n"
@ -288,7 +296,7 @@ usage(clicon_handle h,
"\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", "\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
argv0, argv0,
netconfdir clicon_netconf_dir(h)
); );
exit(0); exit(0);
} }

View file

@ -92,8 +92,9 @@ netconf_plugin_load(clicon_handle h)
char filename[MAXPATHLEN]; char filename[MAXPATHLEN];
plghndl_t *handle; plghndl_t *handle;
/* If no DIR defined, then dont load plugins */
if ((dir = clicon_netconf_dir(h)) == NULL){ if ((dir = clicon_netconf_dir(h)) == NULL){
clicon_err(OE_PLUGIN, 0, "clicon_netconf_dir not defined"); retval = 0;
goto quit; goto quit;
} }

View file

@ -312,9 +312,7 @@ netconf_edit_config(clicon_handle h,
cxobj *x; cxobj *x;
cxobj *xfilter; cxobj *xfilter;
char *ftype = NULL; char *ftype = NULL;
cxobj *xcc; /* child of config */
char *target; /* db */ char *target; /* db */
cbuf *cbxml = NULL;
/* must have target, and it should be candidate */ /* must have target, and it should be candidate */
if ((target = netconf_get_target(xn, "target")) == NULL || if ((target = netconf_get_target(xn, "target")) == NULL ||
@ -371,19 +369,12 @@ netconf_edit_config(clicon_handle h,
goto ok; goto ok;
} }
#endif #endif
if ((cbxml = cbuf_new()) == NULL)
goto done;
if ((xcc = xml_child_i(xc, 0)) != NULL)
if (clicon_xml2cbuf(cbxml, xcc, 0, 0) < 0)
goto done;
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0) if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
goto done; goto done;
} }
ok: ok:
retval = 0; retval = 0;
done: done:
if (cbxml)
cbuf_free(cbxml);
return retval; return retval;
} }

View file

@ -31,7 +31,7 @@ sudo /etc/init.d nginx start
Start clixon restconf daemon Start clixon restconf daemon
``` ```
olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.conf " -s /bin/sh www-data olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.xml " -s /bin/sh www-data
``` ```
Make restconf calls with curl Make restconf calls with curl
@ -67,7 +67,7 @@ curl -sX POST -d '{"interfaces":{"interface":{"name":"eth1","type":"eth","enable
Start the restconf fastcgi program with debug flag: Start the restconf fastcgi program with debug flag:
``` ```
sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-data sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.xml" -s /bin/sh www-data
``` ```
Look at syslog: Look at syslog:
``` ```

View file

@ -316,7 +316,7 @@ restconf_plugin_load(clicon_handle h)
char filename[MAXPATHLEN]; char filename[MAXPATHLEN];
if ((dir = clicon_restconf_dir(h)) == NULL){ if ((dir = clicon_restconf_dir(h)) == NULL){
clicon_err(OE_PLUGIN, 0, "clicon_restconf_dir not defined"); retval = 0;
goto quit; goto quit;
} }
/* Get plugin objects names from plugin directory */ /* Get plugin objects names from plugin directory */
@ -330,7 +330,7 @@ restconf_plugin_load(clicon_handle h)
(int)strlen(filename), filename); (int)strlen(filename), filename);
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL) if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
goto quit; goto quit;
p_credentials = dlsym(handle, "restconf_credentials"); p_credentials = dlsym(handle, PLUGIN_CREDENTIALS);
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) { if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
clicon_err(OE_UNIX, errno, "realloc"); clicon_err(OE_UNIX, errno, "realloc");
goto quit; goto quit;
@ -406,3 +406,30 @@ plugin_credentials(clicon_handle h,
done: done:
return retval; return retval;
} }
/*! Parse a cookie string and return value of cookie attribute
* @param[in] cookiestr cookie string according to rfc6265 (modified)
* @param[in] attribute cookie attribute
* @param[out] val malloced cookie value, free with free()
*/
int
get_user_cookie(char *cookiestr,
char *attribute,
char **val)
{
int retval = -1;
cvec *cvv = NULL;
char *c;
if (str2cvec(cookiestr, ';', '=', &cvv) < 0)
goto done;
if ((c = cvec_find_str(cvv, attribute)) != NULL){
if ((*val = strdup(c)) == NULL)
goto done;
}
retval = 0;
done:
if (cvv)
cvec_free(cvv);
return retval;
}

View file

@ -53,10 +53,11 @@ int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
int test(FCGX_Request *r, int dbg); int test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r); cbuf *readdata(FCGX_Request *r);
int restconf_plugin_load(clicon_handle h); int restconf_plugin_load(clicon_handle h);
int restconf_plugin_start(clicon_handle h, int argc, char **argv); int restconf_plugin_start(clicon_handle h, int argc, char **argv);
int restconf_plugin_unload(clicon_handle h); int restconf_plugin_unload(clicon_handle h);
int plugin_credentials(clicon_handle h, FCGX_Request *r, int *auth); int plugin_credentials(clicon_handle h, FCGX_Request *r, int *auth);
int get_user_cookie(char *cookiestr, char *attribute, char **val);
#endif /* _RESTCONF_LIB_H_ */ #endif /* _RESTCONF_LIB_H_ */

View file

@ -39,7 +39,7 @@
* sudo apt-get install libfcgi-dev * sudo apt-get install libfcgi-dev
* gcc -o fastcgi fastcgi.c -lfcgi * gcc -o fastcgi fastcgi.c -lfcgi
* sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.xml " -s /bin/sh www-data
* This is the interface: * This is the interface:
* api/data/profile=<name>/metric=<name> PUT data:enable=<flag> * api/data/profile=<name>/metric=<name> PUT data:enable=<flag>
@ -205,9 +205,8 @@ request_process(clicon_handle h,
if (strcmp(method, "data") == 0) /* restconf, skip /api/data */ if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
retval = api_data(h, r, path, pcvec, 2, qvec, data); retval = api_data(h, r, path, pcvec, 2, qvec, data);
else else if (strcmp(method, "operations") == 0) /* rpc */
if (strcmp(method, "operations") == 0) /* rpc */ retval = api_operations(h, r, path, pcvec, 2, qvec, data);
retval = api_operations(h, r, path, pcvec, 2, qvec, data);
else if (strcmp(method, "test") == 0) else if (strcmp(method, "test") == 0)
retval = test(r, 0); retval = test(r, 0);
else else
@ -270,8 +269,6 @@ usage(clicon_handle h,
char *argv0) char *argv0)
{ {
char *restconfdir = clicon_restconf_dir(h);
fprintf(stderr, "usage:%s [options]\n" fprintf(stderr, "usage:%s [options]\n"
"where options are\n" "where options are\n"
"\t-h \t\tHelp\n" "\t-h \t\tHelp\n"
@ -280,7 +277,7 @@ usage(clicon_handle h,
"\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", "\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
argv0, argv0,
restconfdir clicon_restconf_dir(h)
); );
exit(0); exit(0);
} }

View file

@ -39,7 +39,7 @@
* sudo apt-get install libfcgi-dev * sudo apt-get install libfcgi-dev
* gcc -o fastcgi fastcgi.c -lfcgi * gcc -o fastcgi fastcgi.c -lfcgi
* sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf " -s /bin/sh www-data * sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.xml " -s /bin/sh www-data
* This is the interface: * This is the interface:
* api/data/profile=<name>/metric=<name> PUT data:enable=<flag> * api/data/profile=<name>/metric=<name> PUT data:enable=<flag>
@ -171,6 +171,7 @@ api_data_get_gen(clicon_handle h,
yspec = clicon_dbspec_yang(h); yspec = clicon_dbspec_yang(h);
if ((path = cbuf_new()) == NULL) if ((path = cbuf_new()) == NULL)
goto done; goto done;
cprintf(path, "/");
if (api_path2xpath_cvv(yspec, pcvec, pi, path) < 0){ if (api_path2xpath_cvv(yspec, pcvec, pi, path) < 0){
notfound(r); notfound(r);
goto done; goto done;
@ -691,13 +692,12 @@ api_operation_post(clicon_handle h,
char *media_accept; char *media_accept;
int use_xml = 0; /* By default return JSON */ int use_xml = 0; /* By default return JSON */
clicon_debug(1, "%s json:\"%s\"", __FUNCTION__, data); clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp); if ((media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp)) &&
if (strcmp(media_accept, "application/yang-data+xml")==0) strcmp(media_accept, "application/yang-data+xml")==0)
use_xml++; use_xml++;
if ((media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp)) &&
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp); strcmp(media_content_type, "application/yang-data+xml")==0)
if (strcmp(media_content_type, "application/yang-data+xml")==0)
parse_xml++; parse_xml++;
clicon_debug(1, "%s accept:\"%s\" content-type:\"%s\"", clicon_debug(1, "%s accept:\"%s\" content-type:\"%s\"",
__FUNCTION__, media_accept, media_content_type); __FUNCTION__, media_accept, media_content_type);
@ -707,6 +707,8 @@ api_operation_post(clicon_handle h,
} }
for (i=0; i<pi; i++) for (i=0; i<pi; i++)
oppath = index(oppath+1, '/'); oppath = index(oppath+1, '/');
clicon_debug(1, "%s oppath: %s", __FUNCTION__, oppath);
/* Find yang rpc statement, return yang rpc statement if found */ /* Find yang rpc statement, return yang rpc statement if found */
if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0) if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0)
goto done; goto done;
@ -724,7 +726,7 @@ api_operation_post(clicon_handle h,
xbot = xtop; xbot = xtop;
if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0) if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0)
goto done; goto done;
if (data){ if (data && strlen(data)){
/* Parse input data as json or xml into xml */ /* Parse input data as json or xml into xml */
if (parse_xml){ if (parse_xml){
if (clicon_xml_parse_str(data, &xdata) < 0){ if (clicon_xml_parse_str(data, &xdata) < 0){
@ -740,9 +742,11 @@ api_operation_post(clicon_handle h,
if ((xinput = xpath_first(xdata, "/input")) != NULL){ if ((xinput = xpath_first(xdata, "/input")) != NULL){
/* Add all input under <rpc>path */ /* Add all input under <rpc>path */
x = NULL; x = NULL;
while ((x = xml_child_each(xinput, x, -1)) != NULL) while (xml_child_nr(xinput)){
x = xml_child_i(xinput, 0);
if (xml_addsub(xbot, x) < 0) if (xml_addsub(xbot, x) < 0)
goto done; goto done;
}
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){ if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
xml_spec_set(xinput, yinput); /* needed for xml_spec_populate */ xml_spec_set(xinput, yinput); /* needed for xml_spec_populate */
if (xml_apply(xinput, CX_ELMNT, xml_spec_populate, yinput) < 0) if (xml_apply(xinput, CX_ELMNT, xml_spec_populate, yinput) < 0)
@ -755,6 +759,25 @@ api_operation_post(clicon_handle h,
} }
} }
} }
/* Non-standard: add cookie as attribute for backend
*/
{
cxobj *xa;
char *cookie;
char *cookieval = NULL;
if ((cookie = FCGX_GetParam("HTTP_COOKIE", r->envp)) != NULL &&
get_user_cookie(cookie, "c-user", &cookieval) ==0){
if ((xa = xml_new("id", xtop)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, cookieval) < 0)
goto done;
if (cookieval)
free(cookieval);
}
}
/* Send to backend */
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0) if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
goto done; goto done;
if ((cbx = cbuf_new()) == NULL) if ((cbx = cbuf_new()) == NULL)

View file

@ -105,7 +105,7 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
# CLICON_CLI_GENMODEL 1 # CLICON_CLI_GENMODEL 1
# Generate code for CLI completion of existing db symbols # Generate code for CLI completion of existing db symbols
# CLICON_CLI_GENMODEL_COMPLETION 0 # CLICON_CLI_GENMODEL_COMPLETION 1
# How to generate and show CLI syntax: VARS|ALL # How to generate and show CLI syntax: VARS|ALL
# CLICON_CLI_GENMODEL_TYPE VARS # CLICON_CLI_GENMODEL_TYPE VARS

58
configure vendored
View file

@ -681,6 +681,7 @@ infodir
docdir docdir
oldincludedir oldincludedir
includedir includedir
runstatedir
localstatedir localstatedir
sharedstatedir sharedstatedir
sysconfdir sysconfdir
@ -757,6 +758,7 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc' sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com' sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var' localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include' includedir='${prefix}/include'
oldincludedir='/usr/include' oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}' docdir='${datarootdir}/doc/${PACKAGE}'
@ -1009,6 +1011,15 @@ do
| -silent | --silent | --silen | --sile | --sil) | -silent | --silent | --silen | --sile | --sil)
silent=yes ;; silent=yes ;;
-runstatedir | --runstatedir | --runstatedi | --runstated \
| --runstate | --runstat | --runsta | --runst | --runs \
| --run | --ru | --r)
ac_prev=runstatedir ;;
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
| --run=* | --ru=* | --r=*)
runstatedir=$ac_optarg ;;
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;; ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@ -1146,7 +1157,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \ datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir libdir localedir mandir runstatedir
do do
eval ac_val=\$$ac_var eval ac_val=\$$ac_var
# Remove trailing slashes. # Remove trailing slashes.
@ -1299,6 +1310,7 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var] --localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib] --libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include] --includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include] --oldincludedir=DIR C header files for non-gcc [/usr/include]
@ -1332,7 +1344,7 @@ Optional Packages:
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-cligen=dir Use CLIGEN here --with-cligen=dir Use CLIGEN here
--without-restconf disable support for restconf --without-restconf disable support for restconf
--without-keyvalue disable support for key-value xmldb datastore --with-keyvalue enable support for key-value xmldb datastore
--with-qdbm=dir Use QDBM here, if keyvalue --with-qdbm=dir Use QDBM here, if keyvalue
Some influential environment variables: Some influential environment variables:
@ -2136,7 +2148,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
CLIXON_VERSION_MAJOR="3" CLIXON_VERSION_MAJOR="3"
CLIXON_VERSION_MINOR="3" CLIXON_VERSION_MINOR="3"
CLIXON_VERSION_PATCH="2" CLIXON_VERSION_PATCH="3"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
# Fix to specific version (eg 3.5) or head (3) # Fix to specific version (eg 3.5) or head (3)
CLIGEN_VERSION="3" CLIGEN_VERSION="3"
@ -2165,6 +2177,7 @@ cat >>confdefs.h <<_ACEOF
_ACEOF _ACEOF
# clixon versions spread to Makefile's (.so files) and variable in build.c # clixon versions spread to Makefile's (.so files) and variable in build.c
@ -3816,14 +3829,13 @@ fi
done done
as_ac_Lib=`$as_echo "ac_cv_lib_:libcligen.so.${CLIGEN_VERSION}''_cligen_init" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cligen_init in -lcligen" >&5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cligen_init in -l:libcligen.so.${CLIGEN_VERSION}" >&5 $as_echo_n "checking for cligen_init in -lcligen... " >&6; }
$as_echo_n "checking for cligen_init in -l:libcligen.so.${CLIGEN_VERSION}... " >&6; } if ${ac_cv_lib_cligen_cligen_init+:} false; then :
if eval \${$as_ac_Lib+:} false; then :
$as_echo_n "(cached) " >&6 $as_echo_n "(cached) " >&6
else else
ac_check_lib_save_LIBS=$LIBS ac_check_lib_save_LIBS=$LIBS
LIBS="-l:libcligen.so.${CLIGEN_VERSION} $LIBS" LIBS="-lcligen $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */ /* end confdefs.h. */
@ -3843,23 +3855,22 @@ return cligen_init ();
} }
_ACEOF _ACEOF
if ac_fn_c_try_link "$LINENO"; then : if ac_fn_c_try_link "$LINENO"; then :
eval "$as_ac_Lib=yes" ac_cv_lib_cligen_cligen_init=yes
else else
eval "$as_ac_Lib=no" ac_cv_lib_cligen_cligen_init=no
fi fi
rm -f core conftest.err conftest.$ac_objext \ rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS LIBS=$ac_check_lib_save_LIBS
fi fi
eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cligen_cligen_init" >&5
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_cv_lib_cligen_cligen_init" >&6; }
$as_echo "$ac_res" >&6; } if test "x$ac_cv_lib_cligen_cligen_init" = xyes; then :
if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_LIB:libcligen.so.${CLIGEN_VERSION}" | $as_tr_cpp` 1 #define HAVE_LIBCLIGEN 1
_ACEOF _ACEOF
LIBS="-l:libcligen.so.${CLIGEN_VERSION} $LIBS" LIBS="-lcligen $LIBS"
else else
as_fn_error $? "CLIgen${CLIGEN_VERSION} missing. Try: git clone https://github.com/olofhagsand/cligen.git" "$LINENO" 5 as_fn_error $? "CLIgen${CLIGEN_VERSION} missing. Try: git clone https://github.com/olofhagsand/cligen.git" "$LINENO" 5
@ -3932,10 +3943,10 @@ fi
if test "${with_keyvalue+set}" = set; then : if test "${with_keyvalue+set}" = set; then :
withval=$with_keyvalue; withval=$with_keyvalue;
else else
with_keyvalue=yes with_keyvalue=no
fi fi
echo "keyvalue:${with_keyvalue}"
if test "x${with_keyvalue}" == xyes; then if test "x${with_keyvalue}" == xyes; then
echo "yes keyvalue" echo "yes keyvalue"
# This is for qdbm # This is for qdbm
@ -4264,9 +4275,17 @@ fi
done done
# This is to find clixon system files in the source code.
# same as clixon_DATADIR defined in clixon.mk.cpp for Makefiles
cat >>confdefs.h <<_ACEOF
#define CLIXON_DATADIR "${prefix}/share/clixon"
_ACEOF
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 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 datastore/Makefile datastore/keyvalue/Makefile datastore/text/Makefile 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 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 datastore/Makefile datastore/keyvalue/Makefile datastore/text/Makefile yang/Makefile 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
@ -4983,6 +5002,7 @@ do
"datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;; "datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;;
"datastore/keyvalue/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/keyvalue/Makefile" ;; "datastore/keyvalue/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/keyvalue/Makefile" ;;
"datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;; "datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;;
"yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;;
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;

View file

@ -43,7 +43,7 @@ AC_INIT(lib/clixon/clixon.h.in)
CLIXON_VERSION_MAJOR="3" CLIXON_VERSION_MAJOR="3"
CLIXON_VERSION_MINOR="3" CLIXON_VERSION_MINOR="3"
CLIXON_VERSION_PATCH="2" CLIXON_VERSION_PATCH="3"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\"" CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
# Fix to specific version (eg 3.5) or head (3) # Fix to specific version (eg 3.5) or head (3)
CLIGEN_VERSION="3" CLIGEN_VERSION="3"
@ -55,6 +55,7 @@ AC_DEFINE_UNQUOTED(CLIXON_VERSION_MAJOR, $CLIXON_VERSION_MAJOR, [Clixon major re
AC_DEFINE_UNQUOTED(CLIXON_VERSION_MINOR, $CLIXON_VERSION_MINOR, [Clixon minor release]) AC_DEFINE_UNQUOTED(CLIXON_VERSION_MINOR, $CLIXON_VERSION_MINOR, [Clixon minor release])
AC_DEFINE_UNQUOTED(CLIXON_VERSION_PATCH, $CLIXON_VERSION_PATCH, [Clixon path version]) AC_DEFINE_UNQUOTED(CLIXON_VERSION_PATCH, $CLIXON_VERSION_PATCH, [Clixon path version])
# clixon versions spread to Makefile's (.so files) and variable in build.c # clixon versions spread to Makefile's (.so files) and variable in build.c
AC_SUBST(CLIXON_VERSION) AC_SUBST(CLIXON_VERSION)
AC_SUBST(CLIXON_VERSION_STRING) AC_SUBST(CLIXON_VERSION_STRING)
@ -123,7 +124,7 @@ fi
AC_CHECK_HEADERS(cligen/cligen.h,, AC_MSG_ERROR(cligen missing. Try: git clone https://github.com/olofhagsand/cligen.git)) AC_CHECK_HEADERS(cligen/cligen.h,, AC_MSG_ERROR(cligen missing. Try: git clone https://github.com/olofhagsand/cligen.git))
AC_CHECK_LIB(:libcligen.so.${CLIGEN_VERSION}, cligen_init,, AC_MSG_ERROR([CLIgen${CLIGEN_VERSION} missing. Try: git clone https://github.com/olofhagsand/cligen.git])) AC_CHECK_LIB(cligen, cligen_init,, AC_MSG_ERROR([CLIgen${CLIGEN_VERSION} missing. Try: git clone https://github.com/olofhagsand/cligen.git]))
# This is for restconf (and fastcgi) # This is for restconf (and fastcgi)
AC_ARG_WITH([restconf], AC_ARG_WITH([restconf],
@ -137,10 +138,10 @@ fi
# This is for keyvalue datastore (and qdbm) # This is for keyvalue datastore (and qdbm)
AC_ARG_WITH([keyvalue], AC_ARG_WITH([keyvalue],
[AS_HELP_STRING([--without-keyvalue],[disable support for key-value xmldb datastore])], [AS_HELP_STRING([--with-keyvalue],[enable support for key-value xmldb datastore])],
[], [],
[with_keyvalue=yes]) [with_keyvalue=no])
echo "keyvalue:${with_keyvalue}"
if test "x${with_keyvalue}" == xyes; then if test "x${with_keyvalue}" == xyes; then
echo "yes keyvalue" echo "yes keyvalue"
# This is for qdbm # This is for qdbm
@ -172,6 +173,10 @@ AC_CHECK_LIB(dl, dlopen)
AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp) AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp)
# This is to find clixon system files in the source code.
# same as clixon_DATADIR defined in clixon.mk.cpp for Makefiles
AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${prefix}/share/clixon", [Clixon data dir for system yang files etc])
AH_BOTTOM([#include <clixon_custom.h>]) AH_BOTTOM([#include <clixon_custom.h>])
AC_OUTPUT(Makefile AC_OUTPUT(Makefile
@ -198,6 +203,7 @@ AC_OUTPUT(Makefile
datastore/Makefile datastore/Makefile
datastore/keyvalue/Makefile datastore/keyvalue/Makefile
datastore/text/Makefile datastore/text/Makefile
yang/Makefile
doc/Makefile doc/Makefile
) )

View file

@ -60,7 +60,7 @@ CLIXON_LIB = libclixon.so.$(CLIXON_MAJOR).$(CLIXON_MINOR)
# For dependency # For dependency
LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB) LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB)
CPPFLAGS = @CPPFLAGS@ CPPFLAGS = @CPPFLAGS@

View file

@ -150,8 +150,8 @@ static int _startup_locked = 0;
*/ */
static int static int
kv_db2file(struct kv_handle *kh, kv_db2file(struct kv_handle *kh,
char *db, const char *db,
char **filename) char **filename)
{ {
int retval = -1; int retval = -1;
cbuf *cb; cbuf *cb;
@ -565,7 +565,7 @@ kv_setopt(xmldb_handle xh,
*/ */
int int
kv_get(xmldb_handle xh, kv_get(xmldb_handle xh,
char *db, const char *db,
char *xpath, char *xpath,
int config, int config,
cxobj **xtop) cxobj **xtop)
@ -783,7 +783,7 @@ put(char *dbfile,
*/ */
int int
kv_put(xmldb_handle xh, kv_put(xmldb_handle xh,
char *db, const char *db,
enum operation_type op, enum operation_type op,
cxobj *xt) cxobj *xt)
{ {
@ -836,8 +836,8 @@ kv_put(xmldb_handle xh,
*/ */
int int
kv_copy(xmldb_handle xh, kv_copy(xmldb_handle xh,
char *from, const char *from,
char *to) const char *to)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh); struct kv_handle *kh = handle(xh);
@ -869,8 +869,8 @@ kv_copy(xmldb_handle xh,
*/ */
int int
kv_lock(xmldb_handle xh, kv_lock(xmldb_handle xh,
char *db, const char *db,
int pid) int pid)
{ {
int retval = -1; int retval = -1;
// struct kv_handle *kh = handle(xh); // struct kv_handle *kh = handle(xh);
@ -900,7 +900,7 @@ kv_lock(xmldb_handle xh,
*/ */
int int
kv_unlock(xmldb_handle xh, kv_unlock(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
// struct kv_handle *kh = handle(xh); // struct kv_handle *kh = handle(xh);
@ -949,7 +949,7 @@ kv_unlock_all(xmldb_handle xh,
*/ */
int int
kv_islocked(xmldb_handle xh, kv_islocked(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
// struct kv_handle *kh = handle(xh); // struct kv_handle *kh = handle(xh);
@ -974,7 +974,7 @@ kv_islocked(xmldb_handle xh,
*/ */
int int
kv_exists(xmldb_handle xh, kv_exists(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh); struct kv_handle *kh = handle(xh);
@ -1001,7 +1001,7 @@ kv_exists(xmldb_handle xh,
*/ */
int int
kv_delete(xmldb_handle xh, kv_delete(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh); struct kv_handle *kh = handle(xh);
@ -1026,7 +1026,7 @@ kv_delete(xmldb_handle xh,
*/ */
int int
kv_create(xmldb_handle xh, kv_create(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
struct kv_handle *kh = handle(xh); struct kv_handle *kh = handle(xh);

View file

@ -39,16 +39,16 @@
/* /*
* Prototypes * Prototypes
*/ */
int kv_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop); int kv_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int kv_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt); int kv_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt);
int kv_dump(FILE *f, char *dbfilename, char *rxkey); int kv_dump(FILE *f, char *dbfilename, char *rxkey);
int kv_copy(xmldb_handle h, char *from, char *to); int kv_copy(xmldb_handle h, const char *from, const char *to);
int kv_lock(xmldb_handle h, char *db, int pid); int kv_lock(xmldb_handle h, const char *db, int pid);
int kv_unlock(xmldb_handle h, char *db); int kv_unlock(xmldb_handle h, const char *db);
int kv_unlock_all(xmldb_handle h, int pid); int kv_unlock_all(xmldb_handle h, int pid);
int kv_islocked(xmldb_handle h, char *db); int kv_islocked(xmldb_handle h, const char *db);
int kv_exists(xmldb_handle h, char *db); int kv_exists(xmldb_handle h, const char *db);
int kv_delete(xmldb_handle h, char *db); int kv_delete(xmldb_handle h, const char *db);
int kv_init(xmldb_handle h, char *db); int kv_init(xmldb_handle h, const char *db);
#endif /* _CLIXON_KEYVALUE_H */ #endif /* _CLIXON_KEYVALUE_H */

View file

@ -42,6 +42,7 @@ mandir = @mandir@
libexecdir = @libexecdir@ libexecdir = @libexecdir@
localstatedir = @localstatedir@ localstatedir = @localstatedir@
sysconfdir = @sysconfdir@ sysconfdir = @sysconfdir@
HOST_VENDOR = @host_vendor@
VPATH = @srcdir@ VPATH = @srcdir@
CC = @CC@ CC = @CC@
@ -64,7 +65,11 @@ all: $(PLUGIN)
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk -include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
$(PLUGIN): $(SRC) $(PLUGIN): $(SRC)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ -lc $^ $(LIBS)
else
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -o $@ -lc $^ $(LIBS) $(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -o $@ -lc $^ $(LIBS)
endif
clean: clean:
rm -f $(PLUGIN) $(OBJS) *.core rm -f $(PLUGIN) $(OBJS) *.core

View file

@ -99,7 +99,7 @@ text_handle_check(xmldb_handle xh)
*/ */
static int static int
text_db2file(struct text_handle *th, text_db2file(struct text_handle *th,
char *db, const char *db,
char **filename) char **filename)
{ {
int retval = -1; int retval = -1;
@ -281,7 +281,7 @@ singleconfigroot(cxobj *xt,
*/ */
int int
text_get(xmldb_handle xh, text_get(xmldb_handle xh,
char *db, const char *db,
char *xpath, char *xpath,
int config, int config,
cxobj **xtop) cxobj **xtop)
@ -363,6 +363,11 @@ text_get(xmldb_handle xh,
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0) if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
goto done; goto done;
#if (XML_CHILD_HASH==1)
/* Add hash */
if (xml_apply0(xt, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
if (debug>1) if (debug>1)
clicon_xml2file(stderr, xt, 0, 1); clicon_xml2file(stderr, xt, 0, 1);
*xtop = xt; *xtop = xt;
@ -380,83 +385,6 @@ text_get(xmldb_handle xh,
return retval; return retval;
} }
/*! Given a modification tree, check existing matching child in the base tree
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[out] x0cp Matching base tree child (if any)
*/
static int
match_base_child(cxobj *x0,
cxobj *x1c,
yang_stmt *yc,
cxobj **x0cp)
{
int retval = -1;
cxobj *x0c = NULL;
char *keyname;
cvec *cvk = NULL;
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *cname;
int ok;
char *x1bstr; /* body string */
cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
x1bstr = xml_body(x1c);
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(cname, xml_name(x0c)) == 0 &&
strcmp(xml_body(x0c), x1bstr)==0)
break;
}
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, yc->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x0c), cname))
continue;
cvi = NULL;
ok = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
ok = 1; /* if we come here */
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break;
ok = 2; /* and reaches here for all keynames, x0c is found. */
}
if (ok == 2)
break;
}
break;
default: /* Just match with name */
x0c = xml_find(x0, cname);
break;
}
*x0cp = x0c;
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Modify a base tree x0 with x1 with yang spec y according to operation op /*! Modify a base tree x0 with x1 with yang spec y according to operation op
* @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL * @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
@ -482,6 +410,8 @@ text_modify(cxobj *x0,
cxobj *x1c; /* mod child */ cxobj *x1c; /* mod child */
char *x1bstr; /* mod body string */ char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */ yang_stmt *yc; /* yang child */
cxobj **x0vec = NULL;
int i;
assert(x1 && xml_type(x1) == CX_ELMNT); assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0); assert(y0);
@ -529,6 +459,10 @@ text_modify(cxobj *x0,
if (xml_value_set(x0b, x1bstr) < 0) if (xml_value_set(x0b, x1bstr) < 0)
goto done; goto done;
} }
#if (XML_CHILD_HASH==1)
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
break; break;
case OP_DELETE: case OP_DELETE:
if (x0==NULL){ if (x0==NULL){
@ -536,8 +470,9 @@ text_modify(cxobj *x0,
goto done; goto done;
} }
case OP_REMOVE: /* fall thru */ case OP_REMOVE: /* fall thru */
if (x0) if (x0){
xml_purge(x0); xml_purge(x0);
}
break; break;
default: default:
break; break;
@ -562,8 +497,9 @@ text_modify(cxobj *x0,
if (y0->yn_keyword == Y_ANYXML){ if (y0->yn_keyword == Y_ANYXML){
if (op == OP_NONE) if (op == OP_NONE)
break; break;
if (x0) if (x0){
xml_purge(x0); xml_purge(x0);
}
if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL) if ((x0 = xml_new_spec(x1name, x0p, y0)) == NULL)
goto done; goto done;
if (xml_copy(x1, x0) < 0) if (xml_copy(x1, x0) < 0)
@ -576,8 +512,18 @@ text_modify(cxobj *x0,
if (op==OP_NONE) if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */ xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
} }
#if (XML_CHILD_HASH==1)
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
/* First pass: mark existing children in base */
/* Loop through children of the modification tree */ /* Loop through children of the modification tree */
if ((x0vec = calloc(xml_child_nr(x1), sizeof(x1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
x1c = NULL; x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) { while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c); x1cname = xml_name(x1c);
/* Get yang spec of the child */ /* Get yang spec of the child */
@ -587,9 +533,17 @@ text_modify(cxobj *x0,
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
x0c = NULL; x0c = NULL;
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0) if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0) x0vec[i++] = x0c;
}
/* Second pass: modify tree */
x1c = NULL;
i = 0;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
x1cname = xml_name(x1c);
yc = yang_find_datanode(y0, x1cname);
if (text_modify(x0vec[i++], (yang_node*)yc, x0, x1c, op) < 0)
goto done; goto done;
} }
break; break;
@ -609,6 +563,8 @@ text_modify(cxobj *x0,
// ok: // ok:
retval = 0; retval = 0;
done: done:
if (x0vec)
free(x0vec);
return retval; return retval;
} }
@ -668,7 +624,7 @@ text_modify_top(cxobj *x0,
goto done; goto done;
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, yc, &x0c) < 0) if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0) if (text_modify(x0c, (yang_node*)yc, x0, x1c, op) < 0)
goto done; goto done;
@ -725,7 +681,7 @@ xml_container_presence(cxobj *x,
*/ */
int int
text_put(xmldb_handle xh, text_put(xmldb_handle xh,
char *db, const char *db,
enum operation_type op, enum operation_type op,
cxobj *x1) cxobj *x1)
{ {
@ -787,6 +743,12 @@ text_put(xmldb_handle xh,
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0) if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done; goto done;
#if (XML_CHILD_HASH==1)
/* Add hash */
if (xml_apply0(x0, CX_ELMNT, xml_hash_op, (void*)1) < 0)
goto done;
#endif
/* /*
* Modify base tree x with modification x1 * Modify base tree x with modification x1
*/ */
@ -845,8 +807,8 @@ text_put(xmldb_handle xh,
*/ */
int int
text_copy(xmldb_handle xh, text_copy(xmldb_handle xh,
char *from, const char *from,
char *to) const char *to)
{ {
int retval = -1; int retval = -1;
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
@ -878,7 +840,7 @@ text_copy(xmldb_handle xh,
*/ */
int int
text_lock(xmldb_handle xh, text_lock(xmldb_handle xh,
char *db, const char *db,
int pid) int pid)
{ {
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
@ -898,7 +860,7 @@ text_lock(xmldb_handle xh,
*/ */
int int
text_unlock(xmldb_handle xh, text_unlock(xmldb_handle xh,
char *db) const char *db)
{ {
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
int zero = 0; int zero = 0;
@ -919,7 +881,7 @@ text_unlock_all(xmldb_handle xh,
int pid) int pid)
{ {
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
char **keys; char **keys = NULL;
size_t klen; size_t klen;
int i; int i;
int *val; int *val;
@ -931,6 +893,8 @@ text_unlock_all(xmldb_handle xh,
if ((val = hash_value(th->th_dbs, keys[i], &vlen)) != NULL && if ((val = hash_value(th->th_dbs, keys[i], &vlen)) != NULL &&
*val == pid) *val == pid)
hash_del(th->th_dbs, keys[i]); hash_del(th->th_dbs, keys[i]);
if (keys)
free(keys);
return 0; return 0;
} }
@ -943,7 +907,7 @@ text_unlock_all(xmldb_handle xh,
*/ */
int int
text_islocked(xmldb_handle xh, text_islocked(xmldb_handle xh,
char *db) const char *db)
{ {
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);
size_t vlen; size_t vlen;
@ -964,7 +928,7 @@ text_islocked(xmldb_handle xh,
*/ */
int int
text_exists(xmldb_handle xh, text_exists(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
@ -992,7 +956,7 @@ text_exists(xmldb_handle xh,
*/ */
int int
text_delete(xmldb_handle xh, text_delete(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
char *filename = NULL; char *filename = NULL;
@ -1012,6 +976,7 @@ text_delete(xmldb_handle xh,
} }
/*! Create / init database /*! Create / init database
* If it exists dont change.
* @param[in] xh XMLDB handle * @param[in] xh XMLDB handle
* @param[in] db Database * @param[in] db Database
* @retval 0 OK * @retval 0 OK
@ -1019,7 +984,7 @@ text_delete(xmldb_handle xh,
*/ */
int int
text_create(xmldb_handle xh, text_create(xmldb_handle xh,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
struct text_handle *th = handle(xh); struct text_handle *th = handle(xh);

View file

@ -39,16 +39,16 @@
/* /*
* Prototypes * Prototypes
*/ */
int text_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop); int text_get(xmldb_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int text_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt); int text_put(xmldb_handle h, const char *db, enum operation_type op, cxobj *xt);
int text_dump(FILE *f, char *dbfilename, char *rxkey); int text_dump(FILE *f, char *dbfilename, char *rxkey);
int text_copy(xmldb_handle h, char *from, char *to); int text_copy(xmldb_handle h, const char *from, const char *to);
int text_lock(xmldb_handle h, char *db, int pid); int text_lock(xmldb_handle h, const char *db, int pid);
int text_unlock(xmldb_handle h, char *db); int text_unlock(xmldb_handle h, const char *db);
int text_unlock_all(xmldb_handle h, int pid); int text_unlock_all(xmldb_handle h, int pid);
int text_islocked(xmldb_handle h, char *db); int text_islocked(xmldb_handle h, const char *db);
int text_exists(xmldb_handle h, char *db); int text_exists(xmldb_handle h, const char *db);
int text_delete(xmldb_handle h, char *db); int text_delete(xmldb_handle h, const char *db);
int text_init(xmldb_handle h, char *db); int text_init(xmldb_handle h, const char *db);
#endif /* _CLIXON_XMLDB_TEXT_H */ #endif /* _CLIXON_XMLDB_TEXT_H */

View file

@ -23,8 +23,8 @@ APIs. There are currently plugins for: CLI, Netconf, Restconf, the datastore an
Clixon is written in C. The plugins are written in C. The CLI Clixon is written in C. The plugins are written in C. The CLI
specification uses cligen (http://cligen.se) specification uses cligen (http://cligen.se)
There is a project for writing plugins in Python. It is reasonable It is possible ro write plugins in Python. It is reasonable
simple to spawn an external script from a backend. simple to spawn an external script from a backend (but needs to be done).
## How to best understand Clixon? ## How to best understand Clixon?
Run the ietf yang routing example, in the example directory. Run the ietf yang routing example, in the example directory.
@ -44,20 +44,28 @@ The example:
sudo make install sudo make install
``` ```
## Do I need to setup anything?
The config demon requires a valid group to create a server UNIX socket.
Define a valid CLICON_SOCK_GROUP in the config file or via the -g option
or create the group and add the user to it. The default group is 'clicon'.
On linux:
sudo groupadd clicon
sudo usermod -a -G clicon user
## What about reference documentation? ## What about reference documentation?
Clixon uses Doxygen for reference documentation. Clixon uses Doxygen for reference documentation.
Build using 'make doc' and aim your browser at doc/html/index.html or Build using 'make doc' and aim your browser at doc/html/index.html or
use the web resource: http://clicon.org/ref/index.html use the web resource: http://clicon.org/ref/index.html
## How do you run the example? ## How do you run the example?
- Start a backend server: 'clixon_backend -Ff /usr/local/etc/routing.conf' - Start a backend server: 'clixon_backend -Ff /usr/local/etc/routing.xml'
- Start a cli session: clixon_cli -f /usr/local/etc/routing.conf - Start a cli session: clixon_cli -f /usr/local/etc/routing.xml
- Start a netconf session: clixon_netconf -f /usr/local/etc/routing.conf - Start a netconf session: clixon_netconf -f /usr/local/etc/routing.xml
## How is configuration data stored? ## How is configuration data stored?
Configuration data is stored in an XML datastore. The default is a Configuration data is stored in an XML datastore. The default is a
text-based datastore, but there also exists a key-value datastore text-based datastore. In the example the datastore are regular files found in
using qdbm. In the example the datastore are regular files found in
/usr/local/var/routing/. /usr/local/var/routing/.
## What is validate and commit? ## What is validate and commit?
@ -73,10 +81,7 @@ is the core functionality of a clixon system.
## What is a Clixon configuration file? ## What is a Clixon configuration file?
Clixon options are stored in a configuration file you must specify Clixon options are stored in a configuration file you must specify
when you start a backend or client using -f. The example configuration when you start a backend or client using -f. The example configuration
file is /usr/local/etc/routing.conf. file is installed at /usr/local/etc/routing.xml.
This file is generated from the base source clixon.conf.cpp.cpp and
is merged with local configuration files, such as routing.conf.local.
This is slightly confusing and could be improved.
## Can I run Clixon as docker containers? ## Can I run Clixon as docker containers?
Yes, the example works as docker containers as well. backend and cli needs a Yes, the example works as docker containers as well. backend and cli needs a
@ -98,16 +103,16 @@ You may also push the containers with 'make push' but you may then consider chan
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application. As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
Example: 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.xml
However, more useful is to run clixon_netconf as an SSH However, more useful is to run clixon_netconf as an SSH
subsystem. Register the subsystem in /etc/sshd_config: subsystem. Register the subsystem in /etc/sshd_config:
``` ```
Subsystem netconf /usr/local/bin/clixon_netconf Subsystem netconf /usr/local/bin/clixon_netconf -f /usr/local/etc/routing.xml
``` ```
and then invoke it from a client using and then invoke it from a client using
``` ```
ssh -s netconf <host> ssh -s <host> netconf
``` ```
## How do I use restconf? ## How do I use restconf?
@ -138,7 +143,7 @@ cli>
``` ```
or via netconf: or via netconf:
``` ```
clixon_netconf -qf /usr/local/etc/routing.conf clixon_netconf -qf /usr/local/etc/routing.xml
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]> <rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
<rpc-reply><ok/></rpc-reply>]]>]]> <rpc-reply><ok/></rpc-reply>]]>]]>
<notification><event>Routing notification</event></notification>]]>]]> <notification><event>Routing notification</event></notification>]]>]]>
@ -146,8 +151,47 @@ clixon_netconf -qf /usr/local/etc/routing.conf
... ...
``` ```
## How should I start the backend daemon?
There are four different backend startup modes. There is differences in running state treatment, ie what state the machine is when you startthe daemon and how loading the configuration affects it:
- none - Do not touch running state. Typically after crash when running state and db are synched.
- init - Initialize running state. Start with a completely clean running state.
- running - Commit running db configuration into running state. Typically after reboot if a persistent running db exists.
- startup - Commit startup configuration into running state. After reboot when no persistent running db exists.
You use the -s to select the mode:
```
clixon_backend ... -s running
```
You may also add a default method in the configuration file:
```
<config>
...
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE
</config>
```
## How can I add extra XML?
There are two ways to add extra XML to running database after start. Note that this XML is not "committed" into running.
The first way is via a file. Assume you want to add this xml (the config tag is a necessary top-level tag):
```
<config>
<x>extra</x>
</config>
```
You add this via the -c option:
```
clixon_backend ... -c extra.xml
```
The second way is by programming the plugin_reset() in the backend
plugin. The example code contains an example on how to do this (see plugin_reset() in routing_backend.c).
## I want to program. How do I extend the example? ## I want to program. How do I extend the example?
- routing.conf.local - Override default settings - routing.xml - Change the configuration file
- The yang specifications - This is the central part. It changes the XML, database and the config cli. - The yang specifications - This is the central part. It changes the XML, database and the config cli.
- routing_cli.cli - Change the fixed part of the CLI commands - routing_cli.cli - Change the fixed part of the CLI commands
- routing_cli.c - Cli C-commands are placed here. - routing_cli.c - Cli C-commands are placed here.

View file

@ -50,7 +50,7 @@ NETCONF_PLUGIN = $(APPNAME)_netconf.so
PLUGINS = $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) PLUGINS = $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN)
all: $(PLUGINS) $(APPNAME).conf all: $(PLUGINS)
# Note: clixon.mk has a rule for: # Note: clixon.mk has a rule for:
# $(APPNAME.conf) # $(APPNAME.conf)
@ -91,16 +91,16 @@ SRC = $(BE_SRC) $(CLI_SRC) $(NETCONF_SRC)
OBJS = $(BE_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) OBJS = $(BE_OBJ) $(CLI_OBJ) $(NETCONF_OBJ)
clean: clean:
rm -f $(PLUGINS) $(OBJS) $(APPNAME).conf rm -f $(PLUGINS) $(OBJS)
(cd docker && $(MAKE) $(MFLAGS) $@) (cd docker && $(MAKE) $(MFLAGS) $@)
distclean: clean distclean: clean
rm -f Makefile *~ .depend rm -f Makefile *~ .depend
(cd docker && $(MAKE) $(MFLAGS) $@) (cd docker && $(MAKE) $(MFLAGS) $@)
install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(APPNAME).conf install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(APPNAME).xml
install -d $(DESTDIR)$(clixon_SYSCONFDIR) install -d $(DESTDIR)$(clixon_SYSCONFDIR)
install $(APPNAME).conf $(DESTDIR)$(clixon_SYSCONFDIR) install $(APPNAME).xml $(DESTDIR)$(clixon_SYSCONFDIR)
install -d $(DESTDIR)$(clixon_DBSPECDIR)/yang install -d $(DESTDIR)$(clixon_DBSPECDIR)/yang
install $(YANGSPECS) $(DESTDIR)$(clixon_DBSPECDIR)/yang install $(YANGSPECS) $(DESTDIR)$(clixon_DBSPECDIR)/yang
install -d $(DESTDIR)$(clixon_LIBDIR)/cli install -d $(DESTDIR)$(clixon_LIBDIR)/cli
@ -115,7 +115,7 @@ install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $
(cd docker && $(MAKE) $(MFLAGS) $@) (cd docker && $(MAKE) $(MFLAGS) $@)
uninstall: uninstall:
rm -rf $(DESTDIR)$(clixon_SYSCONFDIR)/$(APPNAME).conf rm -rf $(DESTDIR)$(clixon_SYSCONFDIR)/$(APPNAME).xml
rm -rf $(DESTDIR)$(clixon_DBSPECDIR) rm -rf $(DESTDIR)$(clixon_DBSPECDIR)
rm -rf $(DESTDIR)$(clixon_LOCALSTATEDIR) rm -rf $(DESTDIR)$(clixon_LOCALSTATEDIR)
rm -rf $(DESTDIR)$(clixon_LIBDIR) rm -rf $(DESTDIR)$(clixon_LIBDIR)

View file

@ -7,15 +7,15 @@
``` ```
Start backend: Start backend:
``` ```
clixon_backend -f /usr/local/etc/routing.conf -I clixon_backend -f /usr/local/etc/routing.xml -I
``` ```
Edit cli: Edit cli:
``` ```
clixon_cli -f /usr/local/etc/routing.conf clixon_cli -f /usr/local/etc/routing.xml
``` ```
Send netconf command: Send netconf command:
``` ```
clixon_netconf -f /usr/local/etc/routing.conf clixon_netconf -f /usr/local/etc/routing.xml
``` ```
## Setting data example using netconf ## Setting data example using netconf
@ -71,8 +71,7 @@ Clixon implements Yang RPC operations by an extension mechanism. The
extension mechanism enables you to add application-specific extension mechanism enables you to add application-specific
operations. It works by adding user-defined callbacks for added operations. It works by adding user-defined callbacks for added
netconf operations. It is possible to use the extension mechanism netconf operations. It is possible to use the extension mechanism
independent of the yang rpc construct, but it is recommended to use independent of the yang rpc construct, but it is recommended. The example includes an example:
that, and the example includes such an example:
Example: Example:
``` ```
@ -91,7 +90,7 @@ The example works by creating a netconf rpc call and sending it to the backend:
</rpc> </rpc>
``` ```
The backend in turn registers a callback (fib_route()) which handles the RPC. In the backend, a callback is registered (fib_route()) which handles the RPC.
``` ```
static int static int
fib_route(clicon_handle h, fib_route(clicon_handle h,
@ -122,7 +121,7 @@ To return state data, you need to write a backend state data callback
with the name "plugin_statedata" where you return an XML tree with with the name "plugin_statedata" where you return an XML tree with
state. This is then merged with config data by the system. state. This is then merged with config data by the system.
pA static example of returning state data is in the example. Note that A static example of returning state data is in the example. Note that
a real example would poll or get the interface counters via a system a real example would poll or get the interface counters via a system
call, as well as use the "xpath" argument to identify the requested call, as well as use the "xpath" argument to identify the requested
state data. state data.

View file

@ -12,27 +12,6 @@ CLICON_YANG_MODULE_MAIN example
# <module>[@<revision>] # <module>[@<revision>]
#CLICON_YANG_MODULE_REVISION 2014-06-16 #CLICON_YANG_MODULE_REVISION 2014-06-16
# Generate code for CLI completion of existing db symbols
# CLICON_CLI_GENMODEL_COMPLETION 0
CLICON_CLI_GENMODEL_COMPLETION 1
# How to generate and show CLI syntax: VARS|ALL
# CLICON_CLI_GENMODEL_TYPE VARS
CLICON_CLI_GENMODEL_TYPE VARS
# Set if you want to use old obsolete cligen callback variable syntax
# Migration: Set to 0 and change all user-defined cli callbacks in your cli spec files
# E.g cmd, callback("single arg"); -> cmd, callback("two" "args");
# And change predefined callbacks, eg cli_commit -> cli_commitv in all cli files
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0
# Enabled uses "startup" configuration on boot
CLICON_USE_STARTUP_CONFIG 0
# XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])
CLICON_XMLDB_PLUGIN /usr/local/lib/xmldb/text.so
#CLICON_XMLDB_PLUGIN /usr/local/lib/xmldb/keyvalue.so
# Set to 0 if you want CLI to wrap to next line. # Set to 0 if you want CLI to wrap to next line.
# Set to 1 if you want CLI to scroll sideways when approaching right margin # Set to 1 if you want CLI to scroll sideways when approaching right margin
CLICON_CLI_LINESCROLLING 0 CLICON_CLI_LINESCROLLING 0

18
example/routing.xml Normal file
View file

@ -0,0 +1,18 @@
<config>
<CLICON_CONFIGFILE>/usr/local/etc/routing.xml</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
<CLICON_CLI_MODE>routing</CLICON_CLI_MODE>
<CLICON_BACKEND_DIR>/usr/local/lib/routing/backend</CLICON_BACKEND_DIR>
<CLICON_NETCONF_DIR>/usr/local/lib/routing/netconf</CLICON_NETCONF_DIR>
<CLICON_RESTCONF_DIR>/usr/local/lib/routing/restconf</CLICON_RESTCONF_DIR>
<CLICON_CLI_DIR>/usr/local/lib/routing/cli</CLICON_CLI_DIR>
<CLICON_CLISPEC_DIR>/usr/local/lib/routing/clispec</CLICON_CLISPEC_DIR>
<CLICON_SOCK>/usr/local/var/routing/routing.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/routing/routing.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/routing</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
</config>

View file

@ -153,6 +153,8 @@ route_count(clicon_handle h,
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see xmldb_get * @see xmldb_get
* @note this example code returns a static statedata used in testing.
* Real code would poll state
*/ */
int int
plugin_statedata(clicon_handle h, plugin_statedata(clicon_handle h,
@ -163,14 +165,11 @@ plugin_statedata(clicon_handle h,
cxobj **xvec = NULL; cxobj **xvec = NULL;
/* Example of (static) statedata, real code would poll state */ /* Example of (static) statedata, real code would poll state */
if (0 && (xml_parse("<interfaces-state><interface>" if (xml_parse("<interfaces-state><interface>"
"<name>eth0</name>" "<name>eth0</name>"
"<type>eth</type>" "<type>eth</type>"
"<admin-status>up</admin-status>"
"<oper-status>up</oper-status>"
"<if-index>42</if-index>" "<if-index>42</if-index>"
"<speed>1000000000</speed>" "</interface></interfaces-state>", xstate) < 0)
"</interface></interfaces-state>", xstate)) < 0)
goto done; goto done;
retval = 0; retval = 0;
done: done:
@ -179,8 +178,10 @@ plugin_statedata(clicon_handle h,
return retval; return retval;
} }
/* /*! Plugin initialization. Create rpc callbacks
* Plugin initialization * plugin_init is called as soon as the plugin has been loaded and is
* assumed initialize the plugin's internal state if any as well as register
* any callbacks, configuration dependencies.
*/ */
int int
plugin_init(clicon_handle h) plugin_init(clicon_handle h)
@ -205,3 +206,58 @@ plugin_init(clicon_handle h)
return retval; return retval;
} }
/*! Plugin state reset. Add xml or set state in backend machine.
* Called in each backend plugin. plugin_reset is called after all plugins
* have been initialized. This give the application a chance to reset
* system state back to a base state.
* This is generally done when a system boots up to
* make sure the initial system state is well defined. This can be creating
* default configuration files for various daemons, set interface flags etc.
* @param[in] h Clicon handle
* @param[in] db Name of database. Not may be other than "running"
* In this example, a loopback interface is added
* @note This assumes example yang with interfaces/interface
*/
int
plugin_reset(clicon_handle h,
const char *db)
{
int retval = -1;
cxobj *xt = NULL;
if (clicon_xml_parse_str("<config><interfaces><interface>"
"<name>lo</name><type>local</type>"
"</interface></interfaces></config>", &xt) < 0)
goto done;
/* Replace parent w fiorst child */
if (xml_rootchild(xt, 0, &xt) < 0)
goto done;
/* Merge user reset state */
if (xmldb_put(h, (char*)db, OP_MERGE, xt) < 0)
goto done;
retval = 0;
done:
if (xt != NULL)
xml_free(xt);
return retval;
}
/*! Plugin start.
* @param[in] h Clicon handle
* @param[in] argc Argument vector length (args after -- to backend_main)
* @param[in] argv Argument vector
*
* plugin_start is called once everything has been initialized, right before
* the main event loop is entered. Command line options can be passed to the
* plugins by using "-- <args>" where <args> is any choice of
* options specific to the application. These options are passed to the
* plugin_start function via the argc and argv arguments which
* can be processed with the standard getopt(3).
*/
int
plugin_start(clicon_handle h,
int argc,
char **argv)
{
return 0;
}

View file

@ -11,7 +11,7 @@ delete("Delete a configuration item") @datamodel:example, cli_del();
validate("Validate changes"), cli_validate(); validate("Validate changes"), cli_validate();
commit("Commit the changes"), cli_commit(); commit("Commit the changes"), cli_commit();
quit("Quit Hello"), cli_quit(); quit("Quit"), cli_quit();
delete("Delete a configuration item") all("Delete whole candidate configuration"), delete_all("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");

View file

@ -1,5 +1,8 @@
/* include/clixon_config.h.in. Generated from configure.ac by autoheader. */ /* include/clixon_config.h.in. Generated from configure.ac by autoheader. */
/* Clixon data dir for system yang files etc */
#undef CLIXON_DATADIR
/* Clixon major release */ /* Clixon major release */
#undef CLIXON_VERSION_MAJOR #undef CLIXON_VERSION_MAJOR

View file

@ -43,3 +43,14 @@
int strverscmp (__const char *__s1, __const char *__s2); int strverscmp (__const char *__s1, __const char *__s2);
#endif #endif
/* Hash for XML trees list entries
* Experimental
*/
#define XML_CHILD_HASH 1
/* Backward compatible clixon backend daemon startup sequences
* This has been replaced with -s <mode> and CLICON_STARTUP_MODE
* define if enable backward compatible behaviour
* Remove in 3.3.4
*/
#undef BACKEND_STARTUP_BACKWARD_COMPAT

View file

@ -54,6 +54,8 @@ int event_reg_timeout(struct timeval t, int (*fn)(int, void*),
int event_unreg_timeout(int (*fn)(int, void*), void *arg); int event_unreg_timeout(int (*fn)(int, void*), void *arg);
int event_poll(int fd);
int event_loop(void); int event_loop(void);
int event_exit(void); int event_exit(void);

View file

@ -63,6 +63,13 @@ enum genmodel_type{
GT_ALL, /* Keywords on all variables */ GT_ALL, /* Keywords on all variables */
}; };
/*! See clixon-config.yang type startup_mode */
enum startup_mode_t{
SM_NONE=0,
SM_STARTUP,
SM_RUNNING,
SM_INIT
};
/* /*
* Prototypes * Prototypes
@ -93,6 +100,7 @@ char *clicon_clispec_dir(clicon_handle h);
char *clicon_netconf_dir(clicon_handle h); char *clicon_netconf_dir(clicon_handle h);
char *clicon_restconf_dir(clicon_handle h); char *clicon_restconf_dir(clicon_handle h);
char *clicon_xmldb_plugin(clicon_handle h); char *clicon_xmldb_plugin(clicon_handle h);
int clicon_startup_mode(clicon_handle h);
int clicon_sock_family(clicon_handle h); int clicon_sock_family(clicon_handle h);
char *clicon_sock(clicon_handle h); char *clicon_sock(clicon_handle h);
int clicon_sock_port(clicon_handle h); int clicon_sock_port(clicon_handle h);

View file

@ -69,11 +69,16 @@ typedef int (plginit_t)(clicon_handle); /* Plugin Init */
typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */ typedef int (plgstart_t)(clicon_handle, int, char **); /* Plugin start */
/* Called just before plugin unloaded. /* Called just before plugin unloaded.
* @see plgexit_t
*/ */
#define PLUGIN_EXIT "plugin_exit" #define PLUGIN_EXIT "plugin_exit"
typedef int (plgexit_t)(clicon_handle); /* Plugin exit */ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
/*! Called by restconf
* Returns 0 if credentials OK, -1 if failed
*/
#define PLUGIN_CREDENTIALS "plugin_credentials"
typedef int (plgcredentials_t)(clicon_handle, void *); /* Plugin credentials */
/* Find a function in global namespace or a plugin. XXX clicon internal */ /* Find a function in global namespace or a plugin. XXX clicon internal */
void *clicon_find_func(clicon_handle h, char *plugin, char *func); void *clicon_find_func(clicon_handle h, char *plugin, char *func);

View file

@ -51,7 +51,7 @@ enum format_enum{
/* Protocol message header */ /* Protocol message header */
struct clicon_msg { struct clicon_msg {
uint16_t op_len; /* length of message. */ uint32_t op_len; /* length of message. network byte order. */
char op_body[0]; /* rest of message, actual data */ char op_body[0]; /* rest of message, actual data */
}; };
@ -85,7 +85,7 @@ int clicon_msg_rcv(int s, struct clicon_msg **msg, int *eof);
int send_msg_notify(int s, int level, char *event); int send_msg_notify(int s, int level, char *event);
int send_msg_reply(int s, char *data, uint16_t datalen); int send_msg_reply(int s, char *data, uint32_t datalen);
int detect_endtag(char *tag, char ch, int *state); int detect_endtag(char *tag, char ch, int *state);

View file

@ -57,7 +57,12 @@ enum cxobj_type {CX_ERROR=-1,
typedef struct xml cxobj; /* struct defined in clicon_xml.c */ typedef struct xml cxobj; /* struct defined in clicon_xml.c */
/*! Callback function type for xml_apply */ /*! Callback function type for xml_apply
* @retval -1 Error, aborted at first error encounter
* @retval 0 OK, continue
* @retval 1 Abort, dont continue with others
* @retval 2 Locally, just abort this subtree, continue with others
*/
typedef int (xml_applyfn_t)(cxobj *yn, void *arg); typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
/* /*
@ -118,6 +123,7 @@ char *xml_body(cxobj *xn);
cxobj *xml_body_get(cxobj *xn); cxobj *xml_body_get(cxobj *xn);
char *xml_find_value(cxobj *xn_parent, char *name); char *xml_find_value(cxobj *xn_parent, char *name);
char *xml_find_body(cxobj *xn, char *name); char *xml_find_body(cxobj *xn, char *name);
cxobj *xml_find_body_obj(cxobj *xt, char *name, char *val);
int xml_free(cxobj *xn); int xml_free(cxobj *xn);
@ -146,5 +152,12 @@ 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); int xml_operation(char *opstr, enum operation_type *op);
char *xml_operation2str(enum operation_type op); char *xml_operation2str(enum operation_type op);
#if (XML_CHILD_HASH==1)
clicon_hash_t *xml_hash(cxobj *x);
int xml_hash_init(cxobj *x);
int xml_hash_rm(cxobj *x);
int xml_hash_key(cxobj *x, yang_stmt *y, cbuf *key);
int xml_hash_op(cxobj *x, void *arg);
#endif
#endif /* _CLIXON_XML_H */ #endif /* _CLIXON_XML_H */

View file

@ -75,34 +75,34 @@ typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value); typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
/* Type of xmldb get function */ /* Type of xmldb get function */
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath, int config, cxobj **xtop); typedef int (xmldb_get_t)(xmldb_handle xh, const char *db, char *xpath, int config, cxobj **xtop);
/* Type of xmldb put function */ /* Type of xmldb put function */
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op, cxobj *xt); typedef int (xmldb_put_t)(xmldb_handle xh, const char *db, enum operation_type op, cxobj *xt);
/* Type of xmldb copy function */ /* Type of xmldb copy function */
typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to); typedef int (xmldb_copy_t)(xmldb_handle xh, const char *from, const char *to);
/* Type of xmldb lock function */ /* Type of xmldb lock function */
typedef int (xmldb_lock_t)(xmldb_handle xh, char *db, int pid); typedef int (xmldb_lock_t)(xmldb_handle xh, const char *db, int pid);
/* Type of xmldb unlock function */ /* Type of xmldb unlock function */
typedef int (xmldb_unlock_t)(xmldb_handle xh, char *db); typedef int (xmldb_unlock_t)(xmldb_handle xh, const char *db);
/* Type of xmldb unlock_all function */ /* Type of xmldb unlock_all function */
typedef int (xmldb_unlock_all_t)(xmldb_handle xh, int pid); typedef int (xmldb_unlock_all_t)(xmldb_handle xh, int pid);
/* Type of xmldb islocked function */ /* Type of xmldb islocked function */
typedef int (xmldb_islocked_t)(xmldb_handle xh, char *db); typedef int (xmldb_islocked_t)(xmldb_handle xh, const char *db);
/* Type of xmldb exists function */ /* Type of xmldb exists function */
typedef int (xmldb_exists_t)(xmldb_handle xh, char *db); typedef int (xmldb_exists_t)(xmldb_handle xh, const char *db);
/* Type of xmldb delete function */ /* Type of xmldb delete function */
typedef int (xmldb_delete_t)(xmldb_handle xh, char *db); typedef int (xmldb_delete_t)(xmldb_handle xh, const char *db);
/* Type of xmldb init function */ /* Type of xmldb init function */
typedef int (xmldb_create_t)(xmldb_handle xh, char *db); typedef int (xmldb_create_t)(xmldb_handle xh, const char *db);
/* plugin init struct for the api */ /* plugin init struct for the api */
struct xmldb_api{ struct xmldb_api{
@ -133,20 +133,20 @@ struct xmldb_api{
int xmldb_plugin_load(clicon_handle h, char *filename); int xmldb_plugin_load(clicon_handle h, char *filename);
int xmldb_plugin_unload(clicon_handle h); int xmldb_plugin_unload(clicon_handle h);
int xmldb_validate_db(char *db); int xmldb_validate_db(const char *db);
int xmldb_connect(clicon_handle h); int xmldb_connect(clicon_handle h);
int xmldb_disconnect(clicon_handle h); int xmldb_disconnect(clicon_handle h);
int xmldb_getopt(clicon_handle h, char *optname, void **value); int xmldb_getopt(clicon_handle h, char *optname, void **value);
int xmldb_setopt(clicon_handle h, char *optname, void *value); int xmldb_setopt(clicon_handle h, char *optname, void *value);
int xmldb_get(clicon_handle h, char *db, char *xpath, int config, cxobj **xtop); int xmldb_get(clicon_handle h, const char *db, char *xpath, int config, cxobj **xtop);
int xmldb_put(clicon_handle h, char *db, enum operation_type op, cxobj *xt); int xmldb_put(clicon_handle h, const char *db, enum operation_type op, cxobj *xt);
int xmldb_copy(clicon_handle h, char *from, char *to); int xmldb_copy(clicon_handle h, const char *from, const char *to);
int xmldb_lock(clicon_handle h, char *db, int pid); int xmldb_lock(clicon_handle h, const char *db, int pid);
int xmldb_unlock(clicon_handle h, char *db); int xmldb_unlock(clicon_handle h, const char *db);
int xmldb_unlock_all(clicon_handle h, 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, const char *db);
int xmldb_exists(clicon_handle h, char *db); int xmldb_exists(clicon_handle h, const char *db);
int xmldb_delete(clicon_handle h, char *db); int xmldb_delete(clicon_handle h, const char *db);
int xmldb_create(clicon_handle h, char *db); int xmldb_create(clicon_handle h, const char *db);
#endif /* _CLIXON_XML_DB_H */ #endif /* _CLIXON_XML_DB_H */

View file

@ -65,6 +65,7 @@ int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath); int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
int schemanode, cxobj **xpathp, yang_node **ypathp); int schemanode, cxobj **xpathp, yang_node **ypathp);
int match_base_child(cxobj *x0, cxobj *x1c, cxobj **x0cp, yang_stmt *yc);
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec); int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
int yang_enum_int_value(cxobj *node, int32_t *val); int yang_enum_int_value(cxobj *node, int32_t *val);

View file

@ -42,6 +42,7 @@ mandir = @mandir@
libexecdir = @libexecdir@ libexecdir = @libexecdir@
localstatedir = @localstatedir@ localstatedir = @localstatedir@
sysconfdir = @sysconfdir@ sysconfdir = @sysconfdir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@ SH_SUFFIX = @SH_SUFFIX@
CLIXON_VERSION = @CLIXON_VERSION@ CLIXON_VERSION = @CLIXON_VERSION@
@ -162,7 +163,12 @@ build.c:
$(MYLIB) : $(GENOBJS) $(OBJS) $(MYLIB) : $(GENOBJS) $(OBJS)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(LDFLAGS) -shared -o $@ $(GENOBJS) $(OBJS) $(LIBS) -undefined dynamic_lookup -o $@
else
$(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(GENOBJS) $(OBJS) $(LIBS) -Wl,-soname=$(MYLIBSO) $(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(GENOBJS) $(OBJS) $(LIBS) -Wl,-soname=$(MYLIBSO)
endif
# link-name is needed for application linking, eg for clixon_cli and clixon_config # link-name is needed for application linking, eg for clixon_cli and clixon_config
$(MYLIBLINK) : $(MYLIB) $(MYLIBLINK) : $(MYLIB)
# ln -sf $(MYLIB) $@ # ln -sf $(MYLIB) $@

View file

@ -251,6 +251,26 @@ event_unreg_timeout(int (*fn)(int, void*),
return found?0:-1; return found?0:-1;
} }
/*! Poll to see if there is any data available on this file descriptor.
* @param[in] fd File descriptor
* @retval -1 Error
* @retval 0 Nothing to read/empty fd
* @retval 1 Something to read on fd
*/
int
event_poll(int fd)
{
int retval = -1;
fd_set fdset;
struct timeval tnull = {0,};
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
if ((retval = select(FD_SETSIZE, &fdset, NULL, NULL, &tnull)) < 0)
clicon_err(OE_EVENTS, errno, "%s select1: %s", __FUNCTION__, strerror(errno));
return retval;
}
/*! Dispatch file descriptor events (and timeouts) by invoking callbacks. /*! Dispatch file descriptor events (and timeouts) by invoking callbacks.
* There is an issue with fairness that timeouts may take over all events * There is an issue with fairness that timeouts may take over all events
* One could try to poll the file descriptors after a timeout? * One could try to poll the file descriptors after a timeout?
@ -258,11 +278,14 @@ event_unreg_timeout(int (*fn)(int, void*),
int int
event_loop(void) event_loop(void)
{ {
struct event_data *e, *e_next; struct event_data *e;
int n; struct event_data *e_next;
struct timeval t, t0, tnull={0,}; int n;
fd_set fdset; struct timeval t;
int retval = -1; struct timeval t0;
struct timeval tnull = {0,};
fd_set fdset;
int retval = -1;
while (!clicon_exit_get()){ while (!clicon_exit_get()){
FD_ZERO(&fdset); FD_ZERO(&fdset);
@ -286,7 +309,6 @@ event_loop(void)
clicon_debug(1, "%s select: %s", __FUNCTION__, strerror(errno)); clicon_debug(1, "%s select: %s", __FUNCTION__, strerror(errno));
clicon_err(OE_EVENTS, errno, "%s select1: %s", __FUNCTION__, strerror(errno)); clicon_err(OE_EVENTS, errno, "%s select1: %s", __FUNCTION__, strerror(errno));
retval = 0; retval = 0;
goto err;
} }
else else
clicon_err(OE_EVENTS, errno, "%s select2", __FUNCTION__); clicon_err(OE_EVENTS, errno, "%s select2", __FUNCTION__);
@ -311,8 +333,11 @@ event_loop(void)
if(e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset)){ if(e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset)){
clicon_debug(2, "%s: FD_ISSET: %s[%x]", clicon_debug(2, "%s: FD_ISSET: %s[%x]",
__FUNCTION__, e->e_string, e->e_arg); __FUNCTION__, e->e_string, e->e_arg);
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0) if ((*e->e_fn)(e->e_fd, e->e_arg) < 0){
clicon_debug(1, "%s Error in: %s", __FUNCTION__, e->e_string);
goto err; goto err;
}
if (_ee_unreg){ if (_ee_unreg){
_ee_unreg = 0; _ee_unreg = 0;
break; break;
@ -323,7 +348,7 @@ event_loop(void)
err: err:
break; break;
} }
clicon_debug(1, "%s done:", __FUNCTION__); clicon_debug(1, "%s done:%d", __FUNCTION__, retval);
return retval; return retval;
} }

View file

@ -185,7 +185,7 @@ quit:
return retval; return retval;
} }
/*! Make a copy of file src /*! Make a copy of file src. Overwrite existing
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
*/ */

View file

@ -94,6 +94,7 @@
#include "clixon_hash.h" #include "clixon_hash.h"
#define HASH_SIZE 1031 /* Number of hash buckets. Should be a prime */ #define HASH_SIZE 1031 /* Number of hash buckets. Should be a prime */
#define align4(s) (((s)/4)*4 + 4)
/*! A very simplistic algorithm to calculate a hash bucket index /*! A very simplistic algorithm to calculate a hash bucket index
*/ */
@ -235,7 +236,7 @@ hash_add(clicon_hash_t *hash,
} }
/* Make copy of lvalue */ /* Make copy of lvalue */
newval = malloc (vlen+3); /* XXX: qdbm needs aligned mallocs? */ newval = malloc(align4(vlen+3)); /* XXX: qdbm needs aligned mallocs? */
if (newval == NULL){ if (newval == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno)); clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch; goto catch;

View file

@ -624,3 +624,79 @@ json_parse_str(char *str,
return json_parse(str, "", *xt); return json_parse(str, "", *xt);
} }
/*
* Turn this on to get a json parse and pretty print test program
* Usage: xpath
* read xml from input
* Example compile:
gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen
* Example run:
echo '{"foo": -23}' | ./json
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s.\n\tInput on stdin\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
cxobj *xt;
cxobj *xc;
cbuf *cb = cbuf_new();
char *buf = NULL;
int i;
int c;
int len;
FILE *f = stdin;
if (argc != 1){
usage(argv[0]);
return 0;
}
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("malloc");
return -1;
}
memset(buf, 0, len);
i = 0; /* position in buf */
while (1){ /* read the whole file */
if ((c = fgetc(f)) == EOF)
break;
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
goto done;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
} /* read a line */
if (json_parse_str(buf, &xt) < 0)
return -1;
xc = NULL;
while ((xc = xml_child_each(xt, xc, -1)) != NULL) {
xmltree2cbuf(cb, xc, 0); /* dump data structures */
//clicon_xml2cbuf(cb, xc, 0, 1); /* print xml */
}
fprintf(stdout, "%s", cbuf_get(cb));
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
done:
return 0;
}
#endif /* Test program */

View file

@ -47,6 +47,10 @@
#include <cligen/cligen.h> #include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_json_parse.h" #include "clixon_json_parse.h"
@ -73,6 +77,11 @@ clixon_json_parsewrap(void)
%} %}
digit [0-9]
integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
exp ({integer}|{real})[eE][+-]{integer}
%x START %x START
%s STRING %s STRING
%s ESCAPE %s ESCAPE
@ -91,8 +100,7 @@ clixon_json_parsewrap(void)
<START>null { return J_NULL; } <START>null { return J_NULL; }
<START>false { return J_FALSE; } <START>false { return J_FALSE; }
<START>true { return J_TRUE; } <START>true { return J_TRUE; }
<START>[-+]?[0-9]+ { clixon_json_parselval.string = strdup(yytext); <START>\-?({integer}|{real}|{exp}) { clixon_json_parselval.string = strdup(yytext); return J_NUMBER;}
return J_NUMBER;}
<START>. { return -1; } <START>. { return -1; }
<STRING>\" { BEGIN(START); return J_DQ; } <STRING>\" { BEGIN(START); return J_DQ; }
<STRING>\\ { BEGIN(ESCAPE); } <STRING>\\ { BEGIN(ESCAPE); }

View file

@ -124,6 +124,10 @@ object.
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_json_parse.h" #include "clixon_json_parse.h"

View file

@ -58,6 +58,7 @@
/* clicon */ /* clicon */
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_hash.h" #include "clixon_hash.h"
#include "clixon_handle.h" #include "clixon_handle.h"
@ -65,13 +66,25 @@
#include "clixon_yang.h" #include "clixon_yang.h"
#include "clixon_plugin.h" #include "clixon_plugin.h"
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_xsl.h"
#include "clixon_xml_map.h"
/* /* Mapping between Clicon startup modes string <--> constants,
* clicon_option_dump see clixon-config.yang type startup_mode */
* Print registry on file. For debugging. static const map_str2int startup_mode_map[] = {
{"none", SM_NONE},
{"running", SM_RUNNING},
{"startup", SM_STARTUP},
{"init", SM_INIT},
{NULL, -1}
};
/*! Print registry on file. For debugging.
*/ */
void void
clicon_option_dump(clicon_handle h, int dbglevel) clicon_option_dump(clicon_handle h,
int dbglevel)
{ {
clicon_hash_t *hash = clicon_options(h); clicon_hash_t *hash = clicon_options(h);
int i; int i;
@ -101,29 +114,106 @@ clicon_option_dump(clicon_handle h, int dbglevel)
} }
/*! Read filename and set values to global options registry /*! Read filename and set values to global options registry. XML variant.
* @see clicon_option_readfile
*/ */
static int static int
clicon_option_readfile(clicon_hash_t *copt, const char *filename) clicon_option_readfile_xml(clicon_hash_t *copt,
const char *filename,
yang_spec *yspec)
{ {
struct stat st; struct stat st;
char opt[1024], val[1024]; FILE *f = NULL;
char line[1024], *cp; int retval = -1;
FILE *f; int fd;
int retval = -1; cxobj *xt = NULL;
cxobj *xc = NULL;
cxobj *x = NULL;
char *name;
char *body;
if (filename == NULL || !strlen(filename)){ if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified"); clicon_err(OE_UNIX, 0, "Not specified");
return -1; goto done;
} }
if (stat(filename, &st) < 0){ if (stat(filename, &st) < 0){
clicon_err(OE_UNIX, errno, "%s", filename); clicon_err(OE_UNIX, errno, "%s", filename);
return -1; goto done;
} }
if (!S_ISREG(st.st_mode)){ if (!S_ISREG(st.st_mode)){
clicon_err(OE_UNIX, 0, "%s is not a regular file", filename); clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
goto done;
}
if ((f = fopen(filename, "r")) == NULL) {
clicon_err(OE_UNIX, errno, "configure file: %s", filename);
return -1; return -1;
} }
clicon_debug(2, "Reading config file %s", __FUNCTION__, filename);
fd = fileno(f);
if (clicon_xml_parse_file(fd, &xt, "</clicon>") < 0)
goto done;
if (xml_child_nr(xt)==1 && xml_child_nr_type(xt, CX_BODY)==1){
clicon_err(OE_CFG, 0, "Config file %s: Expected XML but is probably old sh style", filename);
goto done;
}
if ((xc = xpath_first(xt, "config")) == NULL) {
clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"config\" element", filename);
goto done;
}
if (xml_apply0(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
goto done;
if (xml_apply0(xc, CX_ELMNT, xml_default, yspec) < 0)
goto done;
if (xml_apply0(xc, CX_ELMNT, xml_yang_validate_add, NULL) < 0)
goto done;
while ((x = xml_child_each(xc, x, CX_ELMNT)) != NULL) {
name = xml_name(x);
body = xml_body(x);
if (name && body &&
(hash_add(copt,
name,
body,
strlen(body)+1)) == NULL)
goto done;
}
retval = 0;
done:
if (xt)
xml_free(xt);
if (f)
fclose(f);
return retval;
}
/*! Read filename and set values to global options registry
* For legacy configuration file, ie not xml
* @see clicon_option_readfile_xml
*/
static int
clicon_option_readfile(clicon_hash_t *copt,
const char *filename)
{
struct stat st;
char opt[1024];
char val[1024];
char line[1024];
char *cp;
FILE *f = NULL;
int retval = -1;
if (filename == NULL || !strlen(filename)){
clicon_err(OE_UNIX, 0, "Not specified");
goto done;
}
if (stat(filename, &st) < 0){
clicon_err(OE_UNIX, errno, "%s", filename);
goto done;
}
if (!S_ISREG(st.st_mode)){
clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
goto done;
}
if ((f = fopen(filename, "r")) == NULL) { if ((f = fopen(filename, "r")) == NULL) {
clicon_err(OE_UNIX, errno, "configure file: %s", filename); clicon_err(OE_UNIX, errno, "configure file: %s", filename);
return -1; return -1;
@ -141,11 +231,12 @@ clicon_option_readfile(clicon_hash_t *copt, const char *filename)
opt, opt,
val, val,
strlen(val)+1)) == NULL) strlen(val)+1)) == NULL)
goto catch; goto done;
} }
retval = 0; retval = 0;
catch: done:
fclose(f); if (f)
fclose(f);
return retval; return retval;
} }
@ -159,50 +250,50 @@ clicon_option_default(clicon_hash_t *copt)
if (!hash_lookup(copt, "CLICON_YANG_MODULE_MAIN")){ if (!hash_lookup(copt, "CLICON_YANG_MODULE_MAIN")){
if (hash_add(copt, "CLICON_YANG_MODULE_MAIN", "clicon", strlen("clicon")+1) < 0) if (hash_add(copt, "CLICON_YANG_MODULE_MAIN", "clicon", strlen("clicon")+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_SOCK_GROUP")){ if (!hash_lookup(copt, "CLICON_SOCK_GROUP")){
val = CLICON_SOCK_GROUP; val = CLICON_SOCK_GROUP;
if (hash_add(copt, "CLICON_SOCK_GROUP", val, strlen(val)+1) < 0) if (hash_add(copt, "CLICON_SOCK_GROUP", val, strlen(val)+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_CLI_MODE")){ if (!hash_lookup(copt, "CLICON_CLI_MODE")){
if (hash_add(copt, "CLICON_CLI_MODE", "base", strlen("base")+1) < 0) if (hash_add(copt, "CLICON_CLI_MODE", "base", strlen("base")+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_MASTER_PLUGIN")){ if (!hash_lookup(copt, "CLICON_MASTER_PLUGIN")){
val = CLICON_MASTER_PLUGIN; val = CLICON_MASTER_PLUGIN;
if (hash_add(copt, "CLICON_MASTER_PLUGIN", val, strlen(val)+1) < 0) if (hash_add(copt, "CLICON_MASTER_PLUGIN", val, strlen(val)+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_CLI_GENMODEL")){ if (!hash_lookup(copt, "CLICON_CLI_GENMODEL")){
if (hash_add(copt, "CLICON_CLI_GENMODEL", "1", strlen("1")+1) < 0) if (hash_add(copt, "CLICON_CLI_GENMODEL", "1", strlen("1")+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_TYPE")){ if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_TYPE")){
if (hash_add(copt, "CLICON_CLI_GENMODEL_TYPE", "VARS", strlen("VARS")+1) < 0) if (hash_add(copt, "CLICON_CLI_GENMODEL_TYPE", "VARS", strlen("VARS")+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_AUTOCOMMIT")){ if (!hash_lookup(copt, "CLICON_AUTOCOMMIT")){
if (hash_add(copt, "CLICON_AUTOCOMMIT", "0", strlen("0")+1) < 0) if (hash_add(copt, "CLICON_AUTOCOMMIT", "0", strlen("0")+1) < 0)
goto catch; goto done;
} }
/* Legacy is 1 but default should really be 0. New apps should use 0 */ /* Legacy is 1 but default should really be 0. New apps should use 0 */
if (!hash_lookup(copt, "CLICON_CLI_VARONLY")){ if (!hash_lookup(copt, "CLICON_CLI_VARONLY")){
if (hash_add(copt, "CLICON_CLI_VARONLY", "1", strlen("1")+1) < 0) if (hash_add(copt, "CLICON_CLI_VARONLY", "1", strlen("1")+1) < 0)
goto catch; goto done;
} }
if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_COMPLETION")){ if (!hash_lookup(copt, "CLICON_CLI_GENMODEL_COMPLETION")){
if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "0", strlen("0")+1) < 0) if (hash_add(copt, "CLICON_CLI_GENMODEL_COMPLETION", "1", strlen("1")+1) < 0)
goto catch; goto done;
} }
/* Default is to use line-scrolling */ /* Default is to use line-scrolling */
if (!hash_lookup(copt, "CLICON_CLI_LINESCROLLING")){ if (!hash_lookup(copt, "CLICON_CLI_LINESCROLLING")){
if (hash_add(copt, "CLICON_CLI_LINESCROLLING", "1", strlen("1")+1) < 0) if (hash_add(copt, "CLICON_CLI_LINESCROLLING", "1", strlen("1")+1) < 0)
goto catch; goto done;
} }
retval = 0; retval = 0;
catch: done:
return retval; return retval;
} }
@ -222,7 +313,7 @@ clicon_option_sanity(clicon_hash_t *copt)
goto done; goto done;
} }
if (!hash_lookup(copt, "CLICON_BACKEND_DIR")){ if (!hash_lookup(copt, "CLICON_BACKEND_DIR")){
clicon_err(OE_UNIX, 0, "CLICON_BACKEND_PIDFILE not defined in config file"); clicon_err(OE_UNIX, 0, "CLICON_BACKEND_DIR not defined in config file");
goto done; goto done;
} }
if (!hash_lookup(copt, "CLICON_NETCONF_DIR")){ if (!hash_lookup(copt, "CLICON_NETCONF_DIR")){
@ -254,7 +345,6 @@ clicon_option_sanity(clicon_hash_t *copt)
return retval; return retval;
} }
/*! Initialize option values /*! Initialize option values
* *
* Set default options, Read config-file, Check that all values are set. * Set default options, Read config-file, Check that all values are set.
@ -266,6 +356,9 @@ clicon_options_main(clicon_handle h)
int retval = -1; int retval = -1;
char *configfile; char *configfile;
clicon_hash_t *copt = clicon_options(h); clicon_hash_t *copt = clicon_options(h);
char *suffix;
char xml = 0; /* Configfile is xml, otherwise legacy */
yang_spec *yspec = NULL;
/* /*
* Set configure file if not set by command-line above * Set configure file if not set by command-line above
@ -276,26 +369,44 @@ clicon_options_main(clicon_handle h)
} }
configfile = hash_value(copt, "CLICON_CONFIGFILE", NULL); configfile = hash_value(copt, "CLICON_CONFIGFILE", NULL);
clicon_debug(1, "CLICON_CONFIGFILE=%s", configfile); clicon_debug(1, "CLICON_CONFIGFILE=%s", configfile);
/* Set default options */ /* If file ends with .xml, assume it is new format */
if (clicon_option_default(copt) < 0) /* init registry from file */ if ((suffix = rindex(configfile, '.')) != NULL){
goto done; suffix++;
xml = strcmp(suffix,"xml") == 0;
/* Read configfile */ }
if (clicon_option_readfile(copt, configfile) < 0) if (xml){ /* Read clixon yang file */
goto done; if ((yspec = yspec_new()) == NULL)
goto done;
if (clicon_option_sanity(copt) < 0) if (yang_parse(h, CLIXON_DATADIR, "clixon-config", NULL, yspec) < 0)
goto done; goto done;
/* Read configfile */
if (clicon_option_readfile_xml(copt, configfile, yspec) < 0)
goto done;
if (yspec)
yspec_free(yspec);
}
else {
/* Set default options */
if (clicon_option_default(copt) < 0) /* init registry from file */
goto done;
/* Read configfile */
if (clicon_option_readfile(copt, configfile) < 0)
goto done;
if (clicon_option_sanity(copt) < 0)
goto done;
}
retval = 0; retval = 0;
done: done:
return retval; return retval;
} }
/*! Check if a clicon option has a value /*! Check if a clicon option has a value
* @param[in] h clicon_handle
* @param[in] name option name
*/ */
int int
clicon_option_exists(clicon_handle h, const char *name) clicon_option_exists(clicon_handle h,
const char *name)
{ {
clicon_hash_t *copt = clicon_options(h); clicon_hash_t *copt = clicon_options(h);
@ -339,7 +450,7 @@ clicon_option_str_set(clicon_handle h,
} }
/*! Get options as integer but stored as string /*! Get options as integer but stored as string
*
* @param h clicon handle * @param h clicon handle
* @param name name of option * @param name name of option
* @retval int An integer as aresult of atoi * @retval int An integer as aresult of atoi
@ -355,7 +466,8 @@ clicon_option_str_set(clicon_handle h,
* supply a defualt value as shown in the example. * supply a defualt value as shown in the example.
*/ */
int int
clicon_option_int(clicon_handle h, const char *name) clicon_option_int(clicon_handle h,
const char *name)
{ {
char *s; char *s;
@ -364,10 +476,12 @@ clicon_option_int(clicon_handle h, const char *name)
return atoi(s); return atoi(s);
} }
/*! set option given as int. /*! Set option given as int.
*/ */
int int
clicon_option_int_set(clicon_handle h, const char *name, int val) clicon_option_int_set(clicon_handle h,
const char *name,
int val)
{ {
char s[64]; char s[64];
@ -376,10 +490,11 @@ clicon_option_int_set(clicon_handle h, const char *name, int val)
return clicon_option_str_set(h, name, s); return clicon_option_str_set(h, name, s);
} }
/*! delete option /*! Delete option
*/ */
int int
clicon_option_del(clicon_handle h, const char *name) clicon_option_del(clicon_handle h,
const char *name)
{ {
clicon_hash_t *copt = clicon_options(h); clicon_hash_t *copt = clicon_options(h);
@ -418,6 +533,7 @@ clicon_yang_module_revision(clicon_handle h)
return clicon_option_str(h, "CLICON_YANG_MODULE_REVISION"); return clicon_option_str(h, "CLICON_YANG_MODULE_REVISION");
} }
/*! Directory of backend plugins. If null, no plugins are loaded */
char * char *
clicon_backend_dir(clicon_handle h) clicon_backend_dir(clicon_handle h)
{ {
@ -456,7 +572,16 @@ clicon_xmldb_plugin(clicon_handle h)
return clicon_option_str(h, "CLICON_XMLDB_PLUGIN"); return clicon_option_str(h, "CLICON_XMLDB_PLUGIN");
} }
/* get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */ int
clicon_startup_mode(clicon_handle h)
{
char *mode;
if ((mode = clicon_option_str(h, "CLICON_STARTUP_MODE")) == NULL)
return -1;
return clicon_str2int(startup_mode_map, mode);
}
/*! Get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */
int int
clicon_sock_family(clicon_handle h) clicon_sock_family(clicon_handle h)
{ {
@ -508,7 +633,7 @@ clicon_master_plugin(clicon_handle h)
return clicon_option_str(h, "CLICON_MASTER_PLUGIN"); return clicon_option_str(h, "CLICON_MASTER_PLUGIN");
} }
/* return initial clicon cli mode */ /*! Return initial clicon cli mode */
char * char *
clicon_cli_mode(clicon_handle h) clicon_cli_mode(clicon_handle h)
{ {
@ -529,7 +654,7 @@ clicon_cli_genmodel(clicon_handle h)
return 0; return 0;
} }
/* How to generate and show CLI syntax: VARS|ALL */ /*! How to generate and show CLI syntax: VARS|ALL */
enum genmodel_type enum genmodel_type
clicon_cli_genmodel_type(clicon_handle h) clicon_cli_genmodel_type(clicon_handle h)
{ {
@ -608,7 +733,7 @@ clicon_cli_genmodel_completion(clicon_handle h)
return 0; return 0;
} }
/* Where are "running" and "candidate" databases? */ /*! Where are "running" and "candidate" databases? */
char * char *
clicon_xmldb_dir(clicon_handle h) clicon_xmldb_dir(clicon_handle h)
{ {

View file

@ -66,6 +66,9 @@
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_sig.h" #include "clixon_sig.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xsl.h" #include "clixon_xsl.h"
@ -130,8 +133,8 @@ struct clicon_msg *
clicon_msg_encode(char *format, ...) clicon_msg_encode(char *format, ...)
{ {
va_list args; va_list args;
int xmllen; uint32_t xmllen;
int len; uint32_t len;
struct clicon_msg *msg = NULL; struct clicon_msg *msg = NULL;
int hdrlen = sizeof(*msg); int hdrlen = sizeof(*msg);
@ -146,7 +149,7 @@ clicon_msg_encode(char *format, ...)
} }
memset(msg, 0, len); memset(msg, 0, len);
/* hdr */ /* hdr */
msg->op_len = htons(len); msg->op_len = htonl(len);
/* body */ /* body */
va_start(args, format); va_start(args, format);
@ -267,7 +270,7 @@ msg_dump(struct clicon_msg *msg)
memset(buf2, 0, sizeof(buf2)); memset(buf2, 0, sizeof(buf2));
snprintf(buf2, sizeof(buf2), "%s:", __FUNCTION__); snprintf(buf2, sizeof(buf2), "%s:", __FUNCTION__);
for (i=0; i<ntohs(msg->op_len); i++){ for (i=0; i<ntohl(msg->op_len); i++){
snprintf(buf, sizeof(buf), "%s%02x", buf2, ((char*)msg)[i]&0xff); snprintf(buf, sizeof(buf), "%s%02x", buf2, ((char*)msg)[i]&0xff);
if ((i+1)%32==0){ if ((i+1)%32==0){
clicon_debug(2, buf); clicon_debug(2, buf);
@ -294,13 +297,13 @@ clicon_msg_send(int s,
int retval = -1; int retval = -1;
clicon_debug(2, "%s: send msg len=%d", clicon_debug(2, "%s: send msg len=%d",
__FUNCTION__, ntohs(msg->op_len)); __FUNCTION__, ntohl(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,
s, msg, ntohs(msg->op_len)) < 0){ s, msg, ntohl(msg->op_len)) < 0){
clicon_err(OE_CFG, errno, "%s", __FUNCTION__); clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
clicon_log(LOG_WARNING, "%s: write: %s len:%d msg:%s", __FUNCTION__, clicon_log(LOG_WARNING, "%s: write: %s len:%u msg:%s", __FUNCTION__,
strerror(errno), ntohs(msg->op_len), msg->op_body); strerror(errno), ntohs(msg->op_len), msg->op_body);
goto done; goto done;
} }
@ -309,7 +312,6 @@ clicon_msg_send(int s,
return retval; return retval;
} }
/*! Receive a CLICON message /*! Receive a CLICON message
* *
* XXX: timeout? and signals? * XXX: timeout? and signals?
@ -333,9 +335,9 @@ clicon_msg_rcv(int s,
int retval = -1; int retval = -1;
struct clicon_msg hdr; struct clicon_msg hdr;
int hlen; int hlen;
int len2; uint32_t len2;
sigfn_t oldhandler; sigfn_t oldhandler;
uint16_t mlen; uint32_t mlen;
*eof = 0; *eof = 0;
if (0) if (0)
@ -354,7 +356,7 @@ clicon_msg_rcv(int s,
clicon_err(OE_CFG, errno, "%s: header too short (%d)", __FUNCTION__, hlen); clicon_err(OE_CFG, errno, "%s: header too short (%d)", __FUNCTION__, hlen);
goto done; goto done;
} }
mlen = ntohs(hdr.op_len); mlen = ntohl(hdr.op_len);
clicon_debug(2, "%s: rcv msg len=%d", clicon_debug(2, "%s: rcv msg len=%d",
__FUNCTION__, mlen); __FUNCTION__, mlen);
if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){ if ((*msg = (struct clicon_msg *)malloc(mlen)) == NULL){
@ -362,8 +364,8 @@ clicon_msg_rcv(int s,
goto done; goto done;
} }
memcpy(*msg, &hdr, hlen); memcpy(*msg, &hdr, hlen);
if ((len2 = read(s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){ if ((len2 = atomicio(read, s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){
clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__); clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__);
goto done; goto done;
} }
if (len2 != mlen - sizeof(hdr)){ if (len2 != mlen - sizeof(hdr)){
@ -533,17 +535,17 @@ clicon_rpc(int s,
int int
send_msg_reply(int s, send_msg_reply(int s,
char *data, char *data,
uint16_t datalen) uint32_t datalen)
{ {
int retval = -1; int retval = -1;
struct clicon_msg *reply = NULL; struct clicon_msg *reply = NULL;
uint16_t len; uint32_t len;
len = sizeof(*reply) + datalen; len = sizeof(*reply) + datalen;
if ((reply = (struct clicon_msg *)malloc(len)) == NULL) if ((reply = (struct clicon_msg *)malloc(len)) == NULL)
goto done; goto done;
memset(reply, 0, len); memset(reply, 0, len);
reply->op_len = htons(len); reply->op_len = htonl(len);
if (datalen > 0) if (datalen > 0)
memcpy(reply->op_body, data, datalen); memcpy(reply->op_body, data, datalen);
if (clicon_msg_send(s, reply) < 0) if (clicon_msg_send(s, reply) < 0)

View file

@ -100,7 +100,7 @@ clicon_rpc_msg(clicon_handle h,
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
loud and proud */ loud and proud */
cli_set_exiting(1); cligen_exiting_set(cli_cligen(h), 1);
#endif #endif
goto done; goto done;
} }
@ -478,7 +478,6 @@ clicon_rpc_unlock(clicon_handle h,
} }
/*! Get database configuration and state data /*! Get database configuration and state data
* Same as clicon_proto_change just with a cvec instead of lvec
* @param[in] h CLICON handle * @param[in] h CLICON handle
* @param[in] xpath XPath (or "") * @param[in] xpath XPath (or "")
* @param[out] xt XML tree. Free with xml_free. * @param[out] xt XML tree. Free with xml_free.

View file

@ -34,6 +34,10 @@
* XML support functions. * XML support functions.
*/ */
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@ -51,7 +55,11 @@
#include "clixon_err.h" #include "clixon_err.h"
#include "clixon_log.h" #include "clixon_log.h"
#include "clixon_string.h" #include "clixon_string.h"
#include "clixon_queue.h" #include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_parse.h" #include "clixon_xml_parse.h"
@ -81,6 +89,9 @@ struct xml{
void *x_spec; /* Pointer to specification, eg yang, by void *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */ reference, dont free */
cg_var *x_cv; /* If body this contains the typed value */ cg_var *x_cv; /* If body this contains the typed value */
#if (XML_CHILD_HASH==1)
clicon_hash_t *x_hash; /* Hash of children */
#endif
}; };
/* Mapping between xml type <--> string */ /* Mapping between xml type <--> string */
@ -647,6 +658,9 @@ xml_purge(cxobj *xc)
int i; int i;
cxobj *xp; cxobj *xp;
#if (XML_CHILD_HASH==1)
xml_hash_op(xc, 0);
#endif
if ((xp = xml_parent(xc)) != NULL){ if ((xp = xml_parent(xc)) != NULL){
/* Find child order i in parent*/ /* Find child order i in parent*/
for (i=0; i<xml_child_nr(xp); i++) for (i=0; i<xml_child_nr(xp); i++)
@ -771,6 +785,9 @@ xml_rootchild(cxobj *xp,
* @retval NULL if no such node or no body in found node * @retval NULL if no such node or no body in found node
* Note, make a copy of the return value to use it properly * Note, make a copy of the return value to use it properly
* @see xml_find_body * @see xml_find_body
* Explaining picture:
* xt --> xb (x_type=CX_BODY)
* return xb.x_value
*/ */
char * char *
xml_body(cxobj *xn) xml_body(cxobj *xn)
@ -782,12 +799,18 @@ xml_body(cxobj *xn)
return NULL; return NULL;
} }
/*! Get (first) body of xml node, note could be many
* @param[in] xt xml tree node
* Explaining picture:
* xt --> xb (x_type=CX_BODY)
* return xb
*/
cxobj * cxobj *
xml_body_get(cxobj *xn) xml_body_get(cxobj *xt)
{ {
cxobj *xb = NULL; cxobj *xb = NULL;
while ((xb = xml_child_each(xn, xb, CX_BODY)) != NULL) while ((xb = xml_child_each(xt, xb, CX_BODY)) != NULL)
return xb; return xb;
return NULL; return NULL;
} }
@ -795,22 +818,27 @@ xml_body_get(cxobj *xn)
/*! Find and return the value of a sub xml node /*! Find and return the value of a sub xml node
* *
* The value can be of an attribute or body. * The value can be of an attribute or body.
* @param[in] xn xml tree node * @param[in] xt xml tree node
* @param[in] name name of xml tree nod (eg attr name or "body") * @param[in] name name of xml tree nod (eg attr name or "body")
* @retval The returned value as a pointer to the name string * @retval val Pointer to the name string
* @retval NULL if no such node or no value in found node * @retval NULL No such node or no value in node
* *
* Note, make a copy of the return value to use it properly * Note, make a copy of the return value to use it properly
* See also xml_find_body * See also xml_find_body
* Explaining picture:
* xt --> x
* x_name=name
* return x_value
*/ */
char * char *
xml_find_value(cxobj *x_up, xml_find_value(cxobj *xt,
char *name) char *name)
{ {
cxobj *x; cxobj *x = NULL;
if ((x = xml_find(x_up, name)) != NULL) while ((x = xml_child_each(xt, x, -1)) != NULL)
return xml_value(x); if (strcmp(name, xml_name(x)) == 0)
return xml_value(x);
return NULL; return NULL;
} }
@ -821,18 +849,56 @@ xml_find_value(cxobj *x_up,
* @retval NULL if no such node or no body in found node * @retval NULL if no such node or no body in found node
* @note, make a copy of the return value to use it properly * @note, make a copy of the return value to use it properly
* @see xml_find_value * @see xml_find_value
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name return x_value
*/ */
char * char *
xml_find_body(cxobj *xn, xml_find_body(cxobj *xt,
char *name) char *name)
{ {
cxobj *x; cxobj *x=NULL;
if ((x = xml_find(xn, name)) != NULL) while ((x = xml_child_each(xt, x, -1)) != NULL)
return xml_body(x); if (strcmp(name, xml_name(x)) == 0)
return xml_body(x);
return NULL; return NULL;
} }
/*! Find xml object with matching name and value.
*
* This can be useful if x is a leaf-list with many subs with same name,
* but you need to pick the object with a specific value
* @param[in] xt XML tree
* @param[in] name Name of child (there can be many with same name)
* @param[in] val Value. Must be equal to body of child.
* @retval x Child with matching name and body
*
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name x_value=val
* return x
*/
cxobj *
xml_find_body_obj(cxobj *xt,
char *name,
char *val)
{
cxobj *x = NULL;
char *bstr;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(x)))
continue;
if ((bstr = xml_body(x)) == NULL)
continue;
if (strcmp(bstr, val) == 0)
break; /* x is returned */
}
return x;
}
/*! Free an xl sub-tree recursively, but do not remove it from parent /*! Free an xl sub-tree recursively, but do not remove it from parent
* @param[in] x the xml tree to be freed. * @param[in] x the xml tree to be freed.
* @see xml_purge where x is also removed from parent * @see xml_purge where x is also removed from parent
@ -852,10 +918,15 @@ xml_free(cxobj *x)
if (x->x_cv) if (x->x_cv)
cv_free(x->x_cv); cv_free(x->x_cv);
for (i=0; i<x->x_childvec_len; i++){ for (i=0; i<x->x_childvec_len; i++){
xc = x->x_childvec[i]; if ((xc = x->x_childvec[i]) != NULL){
xml_free(xc); xml_free(xc);
x->x_childvec[i] = NULL; x->x_childvec[i] = NULL;
}
} }
#if (XML_CHILD_HASH==1)
if (x->x_hash)
hash_free(x->x_hash);
#endif
if (x->x_childvec) if (x->x_childvec)
free(x->x_childvec); free(x->x_childvec);
free(x); free(x);
@ -995,7 +1066,7 @@ clicon_xml2cbuf(cbuf *cb,
*/ */
int int
xml_parse(char *str, xml_parse(char *str,
cxobj *x_up) cxobj *xt)
{ {
int retval = -1; int retval = -1;
struct xml_parse_yacc_arg ya = {0,}; struct xml_parse_yacc_arg ya = {0,};
@ -1004,7 +1075,7 @@ xml_parse(char *str,
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__); clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
return -1; return -1;
} }
ya.ya_xparent = x_up; ya.ya_xparent = xt;
ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */ 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;
@ -1033,7 +1104,8 @@ xmltree2cbuf(cbuf *cb,
for (i=0; i<level*XML_INDENT; i++) for (i=0; i<level*XML_INDENT; i++)
cprintf(cb, " "); cprintf(cb, " ");
cprintf(cb, "%s", xml_type2str(xml_type(x))); if (xml_type(x) != CX_BODY)
cprintf(cb, "%s", xml_type2str(xml_type(x)));
if (xml_namespace(x)==NULL) if (xml_namespace(x)==NULL)
cprintf(cb, " %s", xml_name(x)); cprintf(cb, " %s", xml_name(x));
else else
@ -1080,7 +1152,7 @@ FSM(char *tag,
* @retval -1 Error with clicon_err called * @retval -1 Error with clicon_err called
* *
* @code * @code
* cxobj *xt; * cxobj *xt = NULL;
* clicon_xml_parse_file(0, &xt, "</clicon>"); * clicon_xml_parse_file(0, &xt, "</clicon>");
* xml_free(xt); * xml_free(xt);
* @endcode * @endcode
@ -1375,8 +1447,8 @@ cxvec_append(cxobj *x,
* @param[in] fn Callback * @param[in] fn Callback
* @param[in] arg Argument * @param[in] arg Argument
* @retval -1 Error, aborted at first error encounter * @retval -1 Error, aborted at first error encounter
* @retval 0 OK, all nodes traversed * @retval 0 OK, all nodes traversed (subparts may have been skipped)
* @retval n OK, aborted at first encounter of first match * @retval 1 OK, aborted on first fn returned 1
* *
* @code * @code
* int x_fn(cxobj *x, void *arg) * int x_fn(cxobj *x, void *arg)
@ -1401,12 +1473,18 @@ xml_apply(cxobj *xn,
x = NULL; x = NULL;
while ((x = xml_child_each(xn, x, type)) != NULL) { while ((x = xml_child_each(xn, x, type)) != NULL) {
if (fn(x, arg) < 0) if ((ret = fn(x, arg)) < 0)
goto done; goto done;
if (ret == 2)
continue; /* Abort this node, dont recurse */
else if (ret == 1){
retval = 1;
goto done;
}
if ((ret = xml_apply(x, type, fn, arg)) < 0) if ((ret = xml_apply(x, type, fn, arg)) < 0)
goto done; goto done;
if (ret > 0){ if (ret == 1){
retval = ret; retval = 1;
goto done; goto done;
} }
} }
@ -1417,10 +1495,9 @@ xml_apply(cxobj *xn,
/*! Apply a function call on top object and all xml node children recursively /*! Apply a function call on top object and all xml node children recursively
* @retval -1 Error, aborted at first error encounter * @retval -1 Error, aborted at first error encounter
* @retval 0 OK, all nodes traversed * @retval 0 OK, all nodes traversed (subparts may have been skipped)
* @retval n OK, aborted at first encounter of first match * @retval 1 OK, aborted on first fn returned 1
* @see xml_apply not including top object * @see xml_apply not including top object
*/ */
int int
xml_apply0(cxobj *xn, xml_apply0(cxobj *xn,
@ -1431,11 +1508,13 @@ xml_apply0(cxobj *xn,
int retval = -1; int retval = -1;
int ret; int ret;
if ((ret = fn(xn, arg)) < 0) if ((ret = fn(xn, arg)) < 0) /* -1, 0, 1, 2 */
goto done; goto done;
if (ret > 0) if (ret == 1)
retval = ret; retval = 1;
else else if (ret > 1)
retval = 0;
else /* 0 */
retval = xml_apply(xn, type, fn, arg); retval = xml_apply(xn, type, fn, arg);
done: done:
return retval; return retval;
@ -1641,6 +1720,160 @@ xml_operation2str(enum operation_type op)
} }
} }
#if (XML_CHILD_HASH==1)
/*! Return yang hash
* Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml.
*/
clicon_hash_t *
xml_hash(cxobj *x)
{
return x->x_hash;
}
int
xml_hash_init(cxobj *x)
{
if ((x->x_hash = hash_init()) < 0)
return -1;
return 0;
}
int
xml_hash_rm(cxobj *x)
{
if (x && x->x_hash){
hash_free(x->x_hash);
x->x_hash = NULL;
}
return 0;
}
/* Compute hash key for xml entry
* @param[in] x
* @param[in] y
* @param[out] key
* key: yangtype+x1name
* LEAFLIST: b0
* LIST: b2vec+b0 -> x0c
*/
int
xml_hash_key(cxobj *x,
yang_stmt *y,
cbuf *key)
{
int retval = -1;
yang_stmt *ykey;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
char *b;
char *str;
switch (y->ys_keyword){
case Y_CONTAINER: str = "c"; break;
case Y_LEAF: str = "e"; break;
case Y_LEAF_LIST: str = "l"; break;
case Y_LIST: str = "i"; break;
default:
str = "xx"; break;
break;
}
cprintf(key, "%s%s", str, xml_name(x));
switch (y->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
if ((b = xml_body(x)) == NULL){
cbuf_reset(key);
goto ok;
}
cprintf(key, "%s", xml_body(x));
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
keyname = cv_string_get(cvi);
if ((b = xml_find_body(x, keyname)) == NULL){
cbuf_reset(key);
goto ok;
}
cprintf(key, "/%s", b);
}
break;
default:
break;
}
ok:
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! XML hash add. Create hash and add key/value to parent
*
* @param[in] arg -1: rm only hash 0: rm entry, 1: add
* Typically called for a whole tree.
*/
int
xml_hash_op(cxobj *x,
void *arg)
{
int retval = -1;
cxobj *xp;
clicon_hash_t *ph;
yang_stmt *y;
cbuf *key = NULL; /* cligen buffer hash key */
int op = (intptr_t)arg;
if (xml_hash(x)==NULL){
if (op==1)
xml_hash_init(x);
}
else if (op==-1|| op==0)
xml_hash_rm(x);
if (op==-1)
goto ok;
if ((xp = xml_parent(x)) == NULL)
goto ok;
if ((ph = xml_hash(xp))==NULL)
goto ok;
if ((y = xml_spec(x)) == NULL)
goto ok;
if ((key = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xml_hash_key(x, y, key) < 0)
goto done;
if (cbuf_len(key)){
// fprintf(stderr, "%s add %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x);
if (op == 1){
if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL)
goto done;
}
else
if (hash_del(ph, cbuf_get(key)) < 0)
goto done;
}
ok:
retval = 0;
done:
if (key)
cbuf_free(key);
return retval;
}
#endif
/* /*
* Turn this on to get a xml parse and pretty print test program * Turn this on to get a xml parse and pretty print test program
* Usage: xpath * Usage: xpath

View file

@ -42,7 +42,6 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <fcntl.h> #include <fcntl.h>
#include <time.h> #include <time.h>
#include <fcgi_stdio.h>
#include <signal.h> #include <signal.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -176,7 +175,7 @@ xmldb_plugin_unload(clicon_handle h)
* @retval -1 Failed validate, xret set to error * @retval -1 Failed validate, xret set to error
*/ */
int int
xmldb_validate_db(char *db) xmldb_validate_db(const char *db)
{ {
if (strcmp(db, "running") != 0 && if (strcmp(db, "running") != 0 &&
strcmp(db, "candidate") != 0 && strcmp(db, "candidate") != 0 &&
@ -337,7 +336,7 @@ xmldb_setopt(clicon_handle h,
*/ */
int int
xmldb_get(clicon_handle h, xmldb_get(clicon_handle h,
char *db, const char *db,
char *xpath, char *xpath,
int config, int config,
cxobj **xtop) cxobj **xtop)
@ -394,7 +393,7 @@ xmldb_get(clicon_handle h,
*/ */
int int
xmldb_put(clicon_handle h, xmldb_put(clicon_handle h,
char *db, const char *db,
enum operation_type op, enum operation_type op,
cxobj *xt) cxobj *xt)
{ {
@ -440,8 +439,8 @@ xmldb_put(clicon_handle h,
*/ */
int int
xmldb_copy(clicon_handle h, xmldb_copy(clicon_handle h,
char *from, const char *from,
char *to) const char *to)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh; xmldb_handle xh;
@ -473,7 +472,7 @@ xmldb_copy(clicon_handle h,
*/ */
int int
xmldb_lock(clicon_handle h, xmldb_lock(clicon_handle h,
char *db, const char *db,
int pid) int pid)
{ {
int retval = -1; int retval = -1;
@ -507,7 +506,7 @@ xmldb_lock(clicon_handle h,
*/ */
int int
xmldb_unlock(clicon_handle h, xmldb_unlock(clicon_handle h,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh; xmldb_handle xh;
@ -556,7 +555,7 @@ xmldb_unlock_all(clicon_handle h,
clicon_err(OE_DB, 0, "Not connected to datastore plugin"); clicon_err(OE_DB, 0, "Not connected to datastore plugin");
goto done; goto done;
} }
retval =xa->xa_unlock_all_fn(xh, pid); retval = xa->xa_unlock_all_fn(xh, pid);
done: done:
return retval; return retval;
} }
@ -570,7 +569,7 @@ xmldb_unlock_all(clicon_handle h,
*/ */
int int
xmldb_islocked(clicon_handle h, xmldb_islocked(clicon_handle h,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh; xmldb_handle xh;
@ -602,7 +601,7 @@ xmldb_islocked(clicon_handle h,
*/ */
int int
xmldb_exists(clicon_handle h, xmldb_exists(clicon_handle h,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh; xmldb_handle xh;
@ -633,7 +632,7 @@ xmldb_exists(clicon_handle h,
*/ */
int int
xmldb_delete(clicon_handle h, xmldb_delete(clicon_handle h,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh; xmldb_handle xh;
@ -664,7 +663,7 @@ xmldb_delete(clicon_handle h,
*/ */
int int
xmldb_create(clicon_handle h, xmldb_create(clicon_handle h,
char *db) const char *db)
{ {
int retval = -1; int retval = -1;
xmldb_handle xh; xmldb_handle xh;

View file

@ -464,14 +464,11 @@ xml2cvec(cxobj *xt,
char *body; char *body;
char *reason = NULL; char *reason = NULL;
int ret; int ret;
int i = 0;
int len = 0;
char *name; char *name;
xc = NULL; xc = NULL;
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) /* Tried to allocate whole cvv here,but some cg_vars may be invalid */
len++; if ((cvv = cvec_new(0)) == NULL){
if ((cvv = cvec_new(len)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new"); clicon_err(OE_UNIX, errno, "cvec_new");
goto err; goto err;
} }
@ -483,8 +480,10 @@ xml2cvec(cxobj *xt,
clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s", clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s",
__FUNCTION__, name, yt->ys_argument); __FUNCTION__, name, yt->ys_argument);
if ((body = xml_body(xc)) != NULL){ if ((body = xml_body(xc)) != NULL){
cv = cvec_i(cvv, i++); if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
cv_type_set(cv, CGV_STRING); clicon_err(OE_PLUGIN, errno, "cvec_add");
goto err;
}
cv_name_set(cv, name); cv_name_set(cv, name);
if ((ret = cv_parse1(body, cv, &reason)) < 0){ if ((ret = cv_parse1(body, cv, &reason)) < 0){
clicon_err(OE_PLUGIN, errno, "cv_parse"); clicon_err(OE_PLUGIN, errno, "cv_parse");
@ -498,11 +497,13 @@ xml2cvec(cxobj *xt,
} }
} }
} }
else else if ((ycv = ys->ys_cv) != NULL){
if ((ycv = ys->ys_cv) != NULL){
if ((body = xml_body(xc)) != NULL){ if ((body = xml_body(xc)) != NULL){
/* XXX: cvec_add uses realloc, can we avoid that? */ /* XXX: cvec_add uses realloc, can we avoid that? */
cv = cvec_i(cvv, i++); if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
clicon_err(OE_PLUGIN, errno, "cvec_add");
goto err;
}
if (cv_cp(cv, ycv) < 0){ if (cv_cp(cv, ycv) < 0){
clicon_err(OE_PLUGIN, errno, "cv_cp"); clicon_err(OE_PLUGIN, errno, "cv_cp");
goto err; goto err;
@ -567,6 +568,8 @@ cvec2xml_1(cvec *cvv,
cv = NULL; cv = NULL;
i = 0; i = 0;
while ((cv = cvec_each(cvv, cv)) != NULL) { while ((cv = cvec_each(cvv, cv)) != NULL) {
if (cv_type_get(cv)==CGV_ERR || cv_name_get(cv) == NULL)
continue;
if ((xn = xml_new(cv_name_get(cv), NULL)) == NULL) /* this leaks */ if ((xn = xml_new(cv_name_get(cv), NULL)) == NULL) /* this leaks */
goto err; goto err;
xml_parent_set(xn, xt); xml_parent_set(xn, xt);
@ -587,271 +590,262 @@ cvec2xml_1(cvec *cvv,
return retval; return retval;
} }
/*! Return 1 if value is a body of one of the named children of xt */ /*! Given child tree x1c, find matching child in base tree x0
static int * param[in] x0 Base tree node
xml_is_body(cxobj *xt, * param[in] x1c Modification tree child
char *name, * param[in] yc Yang spec of tree child
char *val) * param[out] x0cp Matching base tree child (if any)
{ * @note XXX: room for optimization? on 1K calls we have 1M body calls and
cxobj *x; 500K xml_child_each/cvec_each calls.
char *bx; The outer loop is large for large lists
The inner loop is small
x = NULL; Major time in xml_find_body()
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) { Can one do a binary search in the x0 list?
if (strcmp(name, xml_name(x))) */
continue; int
if ((bx = xml_body(x)) == NULL) match_base_child(cxobj *x0,
continue; cxobj *x1c,
if (strcmp(xml_body(x), val) == 0) cxobj **x0cp,
return 1; yang_stmt *yc)
}
return 0;
}
/*! Recursive help function to compute differences between two xml trees
* @see dbdiff_vector.
*/
static int
xml_diff1(yang_stmt *ys,
cxobj *xt1,
cxobj *xt2,
cxobj ***first,
size_t *firstlen,
cxobj ***second,
size_t *secondlen,
cxobj ***changed1,
cxobj ***changed2,
size_t *changedlen)
{ {
int retval = -1; int retval = -1;
cxobj *x1 = NULL; char *x1cname;
cxobj *x2 = NULL; cxobj *x0c = NULL; /* x0 child */
yang_stmt *y;
yang_stmt *ykey;
char *name;
cg_var *cvi;
cvec *cvk = NULL; /* vector of index keys */ cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *keyname; char *keyname;
int equal; int equal;
char *body1; char **b1vec = NULL;
char *body2; int i;
#if (XML_CHILD_HASH==1)
cxobj **p;
cbuf *key = NULL; /* cligen buffer hash key */
size_t vlen;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec"); *x0cp = NULL; /* return value */
/* Check nodes present in xt1 and xt2 + nodes only in xt1 if (xml_hash(x0) == NULL)
* Loop over xt1 goto nohash;
*/ if ((key = cbuf_new()) == NULL){
x1 = NULL; clicon_err(OE_XML, errno, "cbuf_new");
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){ goto done1;
name = xml_name(x1); }
if (ys->ys_keyword == Y_SPEC) if (xml_hash_key(x1c, yc, key) < 0)
y = yang_find_topnode((yang_spec*)ys, name, 0); goto done;
else x0c = NULL;
y = yang_find_datanode((yang_node*)ys, name); if (cbuf_len(key))
if (y == NULL){ if ((p = hash_value(xml_hash(x0), cbuf_get(key), &vlen)) != NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name); assert(vlen == sizeof(x0c));
x0c = *p;
}
// fprintf(stderr, "%s get %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x0c);
*x0cp = x0c;
retval = 0;
done1:
if (key)
cbuf_free(key);
return retval;
nohash:
#endif /* XML_CHILD_HASH */
*x0cp = NULL; /* return value */
x1cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_CONTAINER: /* Equal regardless */
case Y_LEAF: /* Equal regardless */
x0c = xml_find(x0, x1cname);
break;
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1c)) == NULL)
goto ok;
x0c = xml_find_body_obj(x0, x1cname, b1);
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, yc->ys_argument);
goto done; goto done;
} }
switch (y->ys_keyword){ /* The value is a list of keys: <key>[ <key>]* */
case Y_LIST: if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){ goto done;
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key", cvi = NULL; i = 0;
__FUNCTION__, y->ys_argument); while ((cvi = cvec_each(cvk, cvi)) != NULL)
i++;
if ((b1vec = calloc(i, sizeof(b1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((b1 = xml_find_body(x1c, keyname)) == NULL){
clicon_err(OE_UNIX, errno, "key %s not found", keyname);
goto done; goto done;
} }
/* The value is a list of keys: <key>[ <key>]* */ b1vec[i++] = b1;
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL) }
goto done; /* Iterate over x0 tree to (1) find a child that matches name
/* Iterate over xt2 tree to (1) find a child that matches name (2) that have keys that matches */
(2) that have keys that matches */ x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){
equal = 0; equal = 0;
x2 = NULL; if (strcmp(xml_name(x0c), x1cname))
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x2), name))
continue;
cvi = NULL;
equal = 0;
/* (2) Match keys between x1 and x2 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((body1 = xml_find_body(x1, keyname)) == NULL)
continue; /* may be error */
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body1, body2)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
if (equal){
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
}
else
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
case Y_LEAF:
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
body1 = xml_body(x1);
body2 = xml_body(x2);
if (body1 == NULL || body2 == NULL) /* empty type */
break;
if (strcmp(xml_body(x1), xml_body(x2))){
if (cxvec_append(x1, changed1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2, changed2, changedlen) < 0)
goto done;
}
break;
case Y_LEAF_LIST:
if ((body1 = xml_body(x1)) == NULL)
continue; continue;
if (!xml_is_body(xt2, name, body1)) /* where body is */ /* Must be inner loop */
if (cxvec_append(x1, first, firstlen) < 0) cvi = NULL; i = 0;
goto done; while ((cvi = cvec_each(cvk, cvi)) != NULL) {
break; b1 = b1vec[i++];
default:
break;
}
} /* while xt1 */
/* Check nodes present only in xt2
* Loop over xt2
*/
x2 = NULL;
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
name = xml_name(x2);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name, 0);
else
y = yang_find_datanode((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){
case Y_LIST:
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
/* Iterate over xt1 tree to (1) find a child that matches name
(2) that have keys that matches */
equal = 0;
x1 = NULL;
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x1), name))
continue;
cvi = NULL;
equal = 0; equal = 0;
/* (2) Match keys between x2 and x1 */ keyname = cv_string_get(cvi);
while ((cvi = cvec_each(cvk, cvi)) != NULL) { if ((b0 = xml_find_body(x0c, keyname)) == NULL)
keyname = cv_string_get(cvi); break; /* error case */
if ((body2 = xml_find_body(x2, keyname)) == NULL) if (strcmp(b0, b1))
continue; /* may be error */ break; /* stop as soon as inequal key found */
if ((body1 = xml_find_body(x1, keyname)) == NULL) equal=1; /* reaches here for all keynames, x0c is found. */
continue; /* may be error */
if (strcmp(body2, body1)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
} }
if (cvk){ if (equal) /* x0c and x1c equal, otherwise look for other */
cvec_free(cvk); break;
cvk = NULL; } /* while x0c */
} break;
if (!equal) default:
if (cxvec_append(x2, second, secondlen) < 0) break;
goto done; }
break; ok:
case Y_CONTAINER: *x0cp = x0c;
/* Equal regardless */
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF:
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF_LIST:
body2 = xml_body(x2);
if (!xml_is_body(xt1, name, body2)) /* where body is */
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
default:
break;
}
} /* while xt1 */
retval = 0; retval = 0;
done: done:
if (b1vec)
free(b1vec);
if (cvk) if (cvk)
cvec_free(cvk); cvec_free(cvk);
return retval; return retval;
} }
/*! Find next yang node, either start from yang_spec or some yang-node
* @param[in] y Node spec or sny yang-node
* @param[in] name Name of childnode to find
* @retval ys yang statement
* @retval NULL Error: no node found
*/
static yang_stmt *
yang_next(yang_node *y,
char *name)
{
yang_stmt *ys;
if (y->yn_keyword == Y_SPEC)
ys = yang_find_topnode((yang_spec*)y, name, 0);
else
ys = yang_find_datanode(y, name);
if (ys == NULL)
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
return ys;
}
/*! Recursive help function to compute differences between two xml trees
* @param[in] x1 First XML tree
* @param[in] x2 Second XML tree
* @param[out] x1vec Pointervector to XML nodes existing in only first tree
* @param[out] x1veclen Length of first vector
* @param[out] x2vec Pointervector to XML nodes existing in only second tree
* @param[out] x2veclen Length of x2vec vector
* @param[out] changed_x1 Pointervector to XML nodes changed orig value
* @param[out] changed_x2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
*/
static int
xml_diff1(yang_stmt *ys,
cxobj *x1,
cxobj *x2,
cxobj ***x1vec,
size_t *x1veclen,
cxobj ***x2vec,
size_t *x2veclen,
cxobj ***changed_x1,
cxobj ***changed_x2,
size_t *changedlen)
{
int retval = -1;
cxobj *x1c = NULL; /* x1 child */
cxobj *x2c = NULL; /* x2 child */
yang_stmt *yc;
char *b1;
char *b2;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
/* Check nodes present in x1 and x2 + nodes only in x1
* Loop over x1
* XXX: room for improvement. Compare with match_base_child()
*/
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
if ((yc = yang_next((yang_node*)ys, xml_name(x1c))) == NULL)
goto done;
if (match_base_child(x2, x1c, &x2c, yc) < 0)
goto done;
if (x2c == NULL){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
}
else{
if (yc->ys_keyword == Y_LEAF){
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
break;
if ((b2 = xml_body(x2c)) == NULL) /* empty type */
break;
if (strcmp(b1, b2)){
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2c, changed_x2, changedlen) < 0)
goto done;
}
}
if (xml_diff1(yc, x1c, x2c,
x1vec, x1veclen,
x2vec, x2veclen,
changed_x1, changed_x2, changedlen)< 0)
goto done;
}
} /* while x1 */
/* Check nodes present only in x2
* Loop over x2
*/
x2c = NULL;
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
if ((yc = yang_next((yang_node*)ys, xml_name(x2c))) == NULL)
goto done;
if (match_base_child(x1, x2c, &x1c, yc) < 0)
goto done;
if (x1c == NULL)
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
goto done;
} /* while x1 */
retval = 0;
done:
return retval;
}
/*! Compute differences between two xml trees /*! Compute differences between two xml trees
* @param[in] yspec Yang specification * @param[in] yspec Yang specification
* @param[in] xt1 First XML tree * @param[in] x1 First XML tree
* @param[in] xt2 Second XML tree * @param[in] x2 Second XML tree
* @param[out] first Pointervector to XML nodes existing in only first tree * @param[out] first Pointervector to XML nodes existing in only first tree
* @param[out] firstlen Length of first vector * @param[out] firstlen Length of first vector
* @param[out] second Pointervector to XML nodes existing in only second tree * @param[out] second Pointervector to XML nodes existing in only second tree
* @param[out] secondlen Length of second vector * @param[out] secondlen Length of second vector
* @param[out] changed1 Pointervector to XML nodes changed value * @param[out] changed1 Pointervector to XML nodes changed orig value
* @param[out] changed2 Pointervector to XML nodes changed value * @param[out] changed2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector * @param[out] changedlen Length of changed vector
* All xml vectors should be freed after use. * All xml vectors should be freed after use.
* Bot xml trees should be freed with xml_free() * Bot xml trees should be freed with xml_free()
*/ */
int int
xml_diff(yang_spec *yspec, xml_diff(yang_spec *yspec,
cxobj *xt1, cxobj *x1,
cxobj *xt2, cxobj *x2,
cxobj ***first, cxobj ***first,
size_t *firstlen, size_t *firstlen,
cxobj ***second, cxobj ***second,
@ -865,19 +859,19 @@ xml_diff(yang_spec *yspec,
*firstlen = 0; *firstlen = 0;
*secondlen = 0; *secondlen = 0;
*changedlen = 0; *changedlen = 0;
if (xt1 == NULL && xt2 == NULL) if (x1 == NULL && x2 == NULL)
return 0; return 0;
if (xt2 == NULL){ if (x2 == NULL){
if (cxvec_append(xt1, first, firstlen) < 0) if (cxvec_append(x1, first, firstlen) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if (xt1 == NULL){ if (x1 == NULL){
if (cxvec_append(xt1, second, secondlen) < 0) if (cxvec_append(x1, second, secondlen) < 0)
goto done; goto done;
goto ok; goto ok;
} }
if (xml_diff1((yang_stmt*)yspec, xt1, xt2, if (xml_diff1((yang_stmt*)yspec, x1, x2,
first, firstlen, first, firstlen,
second, secondlen, second, secondlen,
changed1, changed2, changedlen) < 0) changed1, changed2, changedlen) < 0)
@ -1002,15 +996,21 @@ yang2api_path_fmt(yang_stmt *ys,
/*! Transform an xml key format and a vector of values to an XML key /*! Transform an xml key format and a vector of values to an XML key
* Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey() * Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey()
* Example: * Example:
* xmlkeyfmt: /aaa/%s/name * xmlkeyfmt: /interfaces/interface=%s/ipv4/address=%s
* cvv: key=17 * cvv: 0 : set interfaces interface e ipv4 address 1.2.3.4
* xmlkey: /aaa/17/name * 1 : name = "e"
* 2 : ip = "1.2.3.4"
* api_path: /interfaces/interface=e/ipv4/address=1.2.3.4
* @param[in] api_path_fmt XML key format, eg /aaa/%s/name * @param[in] api_path_fmt XML key format, eg /aaa/%s/name
* @param[in] cvv cligen variable vector, one for every wildchar in api_path_fmt * @param[in] cvv cligen variable vector, one for every wildchar in
* api_path_fmt
* @param[out] api_path api_path, eg /aaa/17. Free after use * @param[out] api_path api_path, eg /aaa/17. Free after use
* @param[out] yang_arg yang-stmt argument name. Free after use * @param[out] yang_arg yang-stmt argument name. Free after use
* @note first and last elements of cvv are not used,.. * @note first and last elements of cvv are not used,..
* @see cli_dbxml where this function is called * @see api_path_fmt2xpath
*
* /interfaces/interface=%s/name --> /interfaces/interface/name
* /interfaces/interface=%s/ipv4/address=%s --> /interfaces/interface=e/ipv4/address
*/ */
int int
api_path_fmt2api_path(char *api_path_fmt, api_path_fmt2api_path(char *api_path_fmt,
@ -1025,19 +1025,19 @@ api_path_fmt2api_path(char *api_path_fmt,
int j; int j;
char *str; char *str;
char *strenc=NULL; char *strenc=NULL;
cg_var *cv;
#if 1
/* Sanity check */ /* Sanity check */
#if 0
j = 0; /* Count % */ j = 0; /* Count % */
for (i=0; i<strlen(api_path_fmt); i++) for (i=0; i<strlen(api_path_fmt); i++)
if (api_path_fmt[i] == '%') if (api_path_fmt[i] == '%')
j++; j++;
if (j+2 < cvec_len(cvv)) { if (j > cvec_len(cvv)) { //cvec_len can be longer
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s", clicon_log(LOG_WARNING, "%s api_path_fmt number of %% is %d, does not match number of cvv entries %d",
api_path_fmt, api_path_fmt,
j, j,
cvec_len(cvv), cvec_len(cvv));
cv_string_get(cvec_i(cvv, 0)));
goto done; goto done;
} }
#endif #endif
@ -1052,24 +1052,30 @@ api_path_fmt2api_path(char *api_path_fmt,
esc = 0; esc = 0;
if (c!='s') if (c!='s')
continue; continue;
if ((str = cv2str_dup(cvec_i(cvv, j++))) == NULL){ if (j == cvec_len(cvv)) /* last element */
clicon_err(OE_UNIX, errno, "strdup"); ;
goto done; else{
cv = cvec_i(cvv, j++);
if ((str = cv2str_dup(cv)) == NULL){
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
if (percent_encode(str, &strenc) < 0)
goto done;
cprintf(cb, "%s", strenc);
free(strenc); strenc = NULL;
free(str); str = NULL;
} }
if (percent_encode(str, &strenc) < 0)
goto done;
cprintf(cb, "%s", strenc);
free(strenc); strenc = NULL;
free(str); str = NULL;
} }
else else
if (c == '%') if (c == '%')
esc++; esc++;
else if (c == '/'){ else{
cprintf(cb, "%c", c); if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%' && j == cvec_len(cvv))
; /* skip */
else
cprintf(cb, "%c", c);
} }
else
cprintf(cb, "%c", c);
} }
if ((*api_path = strdup(cbuf_get(cb))) == NULL){ if ((*api_path = strdup(cbuf_get(cb))) == NULL){
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
@ -1082,12 +1088,12 @@ api_path_fmt2api_path(char *api_path_fmt,
return retval; return retval;
} }
/*! Transform an xml key format and a vector of values to an XML path /*! Transform an xml key format and a vector of values to an XML path
* Used to input xmldb_get() or xmldb_get_vec * Used to input xmldb_get() or xmldb_get_vec
* Add .* in last %s position. * Add .* in last %s position.
* Example: * Example:
* xmlkeyfmt: /interface/%s/address/%s OLDXXX * api_path_fmt: /interface/%s/address/%s
* xmlkeyfmt: /interface=%s/address=%s
* cvv: name=eth0 * cvv: name=eth0
* xmlkey: /interface/[name=eth0]/address * xmlkey: /interface/[name=eth0]/address
* Example2: * Example2:
@ -1111,21 +1117,20 @@ api_path_fmt2xpath(char *api_path_fmt,
int j; int j;
char *str; char *str;
cg_var *cv; cg_var *cv;
int skip = 0;
/* Sanity check: count '%' */ /* Sanity check: count '%' */
#if 0 #if 1
j = 0; /* Count % */ j = 0; /* Count % */
for (i=0; i<strlen(api_path_fmt); i++) for (i=0; i<strlen(api_path_fmt); i++)
if (api_path_fmt[i] == '%') if (api_path_fmt[i] == '%')
j++; j++;
if (j < cvec_len(cvv)-1) { if (j > cvec_len(cvv)) {
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s", clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
api_path_fmt, api_path_fmt,
j, j,
cvec_len(cvv), cvec_len(cvv),
cv_string_get(cvec_i(cvv, 0))); cv_string_get(cvec_i(cvv, 0)));
// goto done; goto done;
} }
#endif #endif
if ((cb = cbuf_new()) == NULL){ if ((cb = cbuf_new()) == NULL){
@ -1139,9 +1144,7 @@ api_path_fmt2xpath(char *api_path_fmt,
esc = 0; esc = 0;
if (c!='s') if (c!='s')
continue; continue;
if (j == cvec_len(cvv)) /* last element */ if (j == cvec_len(cvv)) /* last element */
//skip++;
; ;
else{ else{
cv = cvec_i(cvv, j++); cv = cvec_i(cvv, j++);
@ -1157,13 +1160,10 @@ api_path_fmt2xpath(char *api_path_fmt,
if (c == '%') if (c == '%')
esc++; esc++;
else{ else{
if (skip) if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%')
skip=0; ; /* skip */
else else
if ((c == '=' || c == ',') && api_path_fmt[i+1]=='%') cprintf(cb, "%c", c);
; /* skip */
else
cprintf(cb, "%c", c);
} }
} }
if ((*xpath = strdup4(cbuf_get(cb))) == NULL){ if ((*xpath = strdup4(cbuf_get(cb))) == NULL){
@ -1460,8 +1460,10 @@ xml_non_config_data(cxobj *xt,
/*! Add yang specification backpoint to XML node /*! Add yang specification backpoint to XML node
* @param[in] xt XML tree node * @param[in] xt XML tree node
* @note This should really be unnecessary since yspec should be set on creation * @param[in] arg Yang spec
* @note This may be unnecessary if yspec us set on creation
* @note For subs to anyxml nodes will not have spec set * @note For subs to anyxml nodes will not have spec set
* @note No validation is done,... XXX
* @code * @code
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec) * xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
* @endcode * @endcode
@ -1483,14 +1485,6 @@ xml_spec_populate(cxobj *x,
y = yang_find_datanode((yang_node*)yp, xml_name(x)); y = yang_find_datanode((yang_node*)yp, xml_name(x));
else else
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */ y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
#ifdef XXX_OBSOLETE /* Add validate elsewhere */
if (y==NULL){
clicon_err(OE_XML, EBADF, "yang spec not found for xml node '%s' xml parent name: '%s' yangspec:'%s']",
name,
xp?xml_name(xp):"", yp?yp->ys_argument:"");
goto done;
}
#endif
if (y) if (y)
xml_spec_set(x, y); xml_spec_set(x, y);
retval = 0; retval = 0;
@ -1702,7 +1696,7 @@ api_path2xml_vec(char **vec,
} }
switch (y->ys_keyword){ switch (y->ys_keyword){
case Y_LEAF_LIST: case Y_LEAF_LIST:
if (restval==NULL){ if (0 && restval==NULL){
clicon_err(OE_XML, 0, "malformed key, expected '=<restval>'"); clicon_err(OE_XML, 0, "malformed key, expected '=<restval>'");
goto done; goto done;
} }
@ -1712,7 +1706,7 @@ api_path2xml_vec(char **vec,
if ((xb = xml_new("body", x)) == NULL) if ((xb = xml_new("body", x)) == NULL)
goto done; goto done;
xml_type_set(xb, CX_BODY); xml_type_set(xb, CX_BODY);
if (xml_value_set(xb, restval) < 0) if (restval && xml_value_set(xb, restval) < 0)
goto done; goto done;
break; break;
case Y_LIST: case Y_LIST:
@ -1788,11 +1782,11 @@ api_path2xml_vec(char **vec,
} }
/*! Create xml tree from api-path /*! Create xml tree from api-path
* @param[in] api_path API-path as defined in RFC 8040 * @param[in] api_path API-path as defined in RFC 8040
* @param[in] yspec Yang spec * @param[in] yspec Yang spec
* @param[in] schemanode If set use schema nodes otherwise data nodes. * @param[in] schemanode If set use schema nodes otherwise data nodes.
* @param[out] xpathp Resulting xml tree * @param[out] xpathp Resulting xml tree
* @param[out] ypathp Yang spec matching xpathp * @param[out] ypathp Yang spec matching xpathp
* @see api_path2xml_vec * @see api_path2xml_vec
*/ */
int int
@ -1833,82 +1827,6 @@ api_path2xml(char *api_path,
return retval; return retval;
} }
/*! Given a modification tree, check existing matching child in the base tree
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[out] x0cp Matching base tree child (if any)
*/
static int
match_base_child(cxobj *x0,
cxobj *x1c,
yang_stmt *yc,
cxobj **x0cp)
{
int retval = -1;
cxobj *x0c = NULL;
char *keyname;
cvec *cvk = NULL;
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *cname;
int ok;
char *x1bstr; /* body string */
cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
x1bstr = xml_body(x1c);
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(cname, xml_name(x0c)) == 0 &&
strcmp(xml_body(x0c), x1bstr)==0)
break;
}
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, yc->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x0c), cname))
continue;
cvi = NULL;
ok = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
ok = 1; /* if we come here */
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break;
ok = 2; /* and reaches here for all keynames, x0c is found. */
}
if (ok == 2)
break;
}
break;
default: /* Just match with name */
x0c = xml_find(x0, cname);
break;
}
*x0cp = x0c;
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Merge a base tree x0 with x1 with yang spec y /*! Merge a base tree x0 with x1 with yang spec y
* @param[in] x0 Base xml tree (can be NULL in add scenarios) * @param[in] x0 Base xml tree (can be NULL in add scenarios)
@ -1975,7 +1893,7 @@ xml_merge1(cxobj *x0,
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
x0c = NULL; x0c = NULL;
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0) if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0) if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
goto done; goto done;
@ -2012,7 +1930,7 @@ xml_merge(cxobj *x0,
goto done; goto done;
} }
/* See if there is a corresponding node in the base tree */ /* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, yc, &x0c) < 0) if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done; goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0) if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
goto done; goto done;
@ -2074,3 +1992,70 @@ done:
return retval; return retval;
} }
/*
* Turn this on for uni-test programs
* Usage: clixon_string join
* Example compile:
gcc -g -o clixon_xml_map -I. -I../clixon ./clixon_xml_map.c -lclixon -lcligen
* Example run:
/interfaces/interface=%s/name --> interfaces/interface/name
/interfaces/interface=%s/ipv4/address=%s e --> /interfaces/interface=e/ipv4/address
/interfaces/interface=%s,%s/ipv4/address=%s e f --> /interfaces/interface=e,f/ipv4/address
/interfaces/interface=%s/ipv4/address=%s,%s e f --> /interfaces/interface=e/ipv4/address=f
/interfaces/interface=%s/ipv4/address=%s/prefix-length eth 1.2.3.4 -->
/interfaces/interface=eth/ipv4/address=1.2.3.4/prefix-length
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s <api_path_fmt> <cv0>, <cv1>,...\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
int nvec;
char **vec;
char *str0;
char *str1;
int i;
char *api_path_fmt;
cg_var *cv;
cvec *cvv;
char *api_path=NULL;
clicon_log_init(__FILE__, LOG_INFO, CLICON_LOG_STDERR);
if (argc < 2){
usage(argv[0]);
return 0;
}
api_path_fmt = argv[1];
if ((cvv = cvec_new(0)) == NULL){
perror("cvec_new");
return -1;
}
cv = cv_new(CGV_STRING);
cv_string_set(cv, "CLI base command");
cvec_append_var(cvv, cv);
for (i=2; i<argc; i++){
cv = cv_new(CGV_STRING);
if (cv_parse(argv[i], cv) < 0){
perror("cv_parse");
return -1;
}
cvec_append_var(cvv, cv);
}
if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
return -1;
printf("%s\n", api_path);
return 0;
}
#endif /* Test program */

View file

@ -48,6 +48,10 @@
#include <cligen/cligen.h> #include <cligen/cligen.h>
/* clicon */ /* clicon */
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h" #include "clixon_xml.h"
#include "clixon_xml_parse.h" #include "clixon_xml_parse.h"

View file

@ -93,6 +93,7 @@ in
#include <fnmatch.h> #include <fnmatch.h>
#include <stdint.h> #include <stdint.h>
#include <assert.h> #include <assert.h>
#include <syslog.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -107,7 +108,6 @@ in
/* Constants */ /* Constants */
#define XPATH_VEC_START 128 #define XPATH_VEC_START 128
/* /*
* Types * Types
*/ */
@ -422,10 +422,12 @@ recursive_find(cxobj *xn,
cxobj *xsub; cxobj *xsub;
cxobj **vec = *vec0; cxobj **vec = *vec0;
size_t veclen = *vec0len; size_t veclen = *vec0len;
char *name;
xsub = NULL; xsub = NULL;
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) { while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
if (fnmatch(pattern, xml_name(xsub), 0) == 0){ name = xml_name(xsub);
if (fnmatch(pattern, name, 0) == 0){
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags)); clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
if (flags==0x0 || xml_flag(xsub, flags)) if (flags==0x0 || xml_flag(xsub, flags))
if (cxvec_append(xsub, &vec, &veclen) < 0) if (cxvec_append(xsub, &vec, &veclen) < 0)
@ -449,8 +451,8 @@ xpath_exec(cxobj *xcur, char *xpath, cxobj **vec0, size_t vec0len,
/*! XPath predicate expression check /*! XPath predicate expression check
* @param[in] xcur xml-tree where to search * @param[in] xcur xml-tree where to search
* @param[in] predicate_expression xpath expression as a string * @param[in] predicate_expression xpath expression as a string
* @param[in] flags Extra xml flag checks that must match (apart from predicate) * @param[in] flags Extra xml flag checks that must match (apart from predicate)
* @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered * @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered
* @param[in,out] vec0len Length of vector or matches * @param[in,out] vec0len Length of vector or matches
* On input, vec0 contains a list of xml nodes to match. * On input, vec0 contains a list of xml nodes to match.
@ -483,6 +485,7 @@ xpath_expr(cxobj *xcur,
char *val; char *val;
char *e0; char *e0;
char *e; char *e;
char *name;
if ((e0 = strdup(predicate_expression)) == NULL){ if ((e0 = strdup(predicate_expression)) == NULL){
clicon_err(OE_UNIX, errno, "strdup"); clicon_err(OE_UNIX, errno, "strdup");
@ -569,7 +572,8 @@ xpath_expr(cxobj *xcur,
xv = (*vec0)[i]; xv = (*vec0)[i];
x = NULL; x = NULL;
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
if (strcmp(tag, xml_name(x)) != 0) name = xml_name(x);
if (name==NULL || strcmp(tag, name) != 0)
continue; continue;
if ((val = xml_body(x)) != NULL && if ((val = xml_body(x)) != NULL &&
strcmp(val, ebody) == 0){ strcmp(val, ebody) == 0){
@ -588,7 +592,8 @@ xpath_expr(cxobj *xcur,
/* Check if more may match,... */ /* Check if more may match,... */
x = NULL; x = NULL;
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) { while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
if (strcmp(tag, xml_name(x)) != 0) name = xml_name(x);
if (name==NULL || strcmp(tag, name) != 0)
continue; continue;
if ((val = xml_body(x)) != NULL && if ((val = xml_body(x)) != NULL &&
strcmp(val, e) == 0){ strcmp(val, e) == 0){
@ -644,12 +649,11 @@ xpath_find(cxobj *xcur,
cxobj *xparent; cxobj *xparent;
size_t vec1len = 0; size_t vec1len = 0;
struct xpath_predicate *xp; struct xpath_predicate *xp;
char *name;
if (xe == NULL){ if (xe == NULL){
/* append */
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
if (flags==0x0 || xml_flag(xv, flags)) if (flags==0x0 || xml_flag(xv, flags))
cxvec_append(xv, vec2, vec2len); cxvec_append(xv, vec2, vec2len);
} }
@ -693,19 +697,21 @@ xpath_find(cxobj *xcur,
goto done; goto done;
} }
} }
else else{
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
x = NULL; x = NULL;
while ((x = xml_child_each(xv, x, -1)) != NULL) { while ((x = xml_child_each(xv, x, -1)) != NULL) {
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){ name = xml_name(x);
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags)); if (name && fnmatch(xe->xe_str, name, 0) == 0) {
if (flags==0x0 || xml_flag(x, flags)) clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags));
if (cxvec_append(x, &vec1, &vec1len) < 0) if (flags==0x0 || xml_flag(x, flags))
goto done; if (cxvec_append(x, &vec1, &vec1len) < 0)
} goto done;
}
} }
} }
}
free(vec0); free(vec0);
vec0 = vec1; vec0 = vec1;
vec0len = vec1len; vec0len = vec1len;
@ -718,20 +724,24 @@ xpath_find(cxobj *xcur,
default: default:
break; break;
} }
/* remove duplicates */ /* remove duplicates
for (i=0; i<vec0len; i++){ * This is cycle-heavy and I dont know when it is needed?
for (j=i+1; j<vec0len; j++){ */
if (vec0[i] == vec0[j]){ if (0)
memmove(vec0[j], vec0[j+1], (vec0len-j)*sizeof(cxobj*)); for (i=0; i<vec0len; i++){
vec0len--; for (j=i+1; j<vec0len; j++){
if (vec0[i] == vec0[j]){
memmove(vec0[j], vec0[j+1], (vec0len-j)*sizeof(cxobj*));
vec0len--;
}
} }
} }
}
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){ for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0) if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done; goto done;
} }
if (xpath_find(xcur, xe->xe_next, descendants, if (xpath_find(xcur, xe->xe_next, descendants,
vec0, vec0len, flags, vec0, vec0len, flags,
vec2, vec2len) < 0) vec2, vec2len) < 0)
@ -910,8 +920,8 @@ xpath_first0(cxobj *xcur,
* ... * ...
* } * }
* @endcode * @endcode
* Note that the returned pointer points into the original tree so should not be freed * @note the returned pointer points into the original tree so should not be freed after use.
* after use. * @note return value does not see difference between error and not found
* @see also xpath_vec. * @see also xpath_vec.
*/ */
cxobj * cxobj *
@ -978,8 +988,10 @@ xpath_each(cxobj *xcur,
int i; int i;
if (xprev == NULL){ if (xprev == NULL){
if (vec1) // XXX if (vec1) {
free(vec1); // XXX free(vec1);
vec1 = NULL;
}
vec1len = 0; vec1len = 0;
if (xpath_choice(xcur, xpath, 0, &vec1, &vec1len) < 0) if (xpath_choice(xcur, xpath, 0, &vec1, &vec1len) < 0)
goto done; goto done;
@ -1022,10 +1034,10 @@ xpath_each(cxobj *xcur,
* xn = xvec[i]; * xn = xvec[i];
* ... * ...
* } * }
* free(vec); * free(xvec);
* @endcode * @endcode
* @Note that although the returned vector must be freed after use, the returned xml * @note Although the returned vector must be freed after use,
* trees need not be. * the returned xml trees should not.
* @see also xpath_first, xpath_each. * @see also xpath_first, xpath_each.
*/ */
int int
@ -1035,7 +1047,7 @@ xpath_vec(cxobj *xcur,
size_t *veclen, size_t *veclen,
...) ...)
{ {
int retval = -1; int retval = -1;
va_list ap; va_list ap;
size_t len; size_t len;
char *xpath; char *xpath;

View file

@ -1504,7 +1504,7 @@ yang_parse_recurse(clicon_handle h,
yang_spec *ysp) yang_spec *ysp)
{ {
yang_stmt *yi = NULL; /* import */ yang_stmt *yi = NULL; /* import */
yang_stmt *ymod; yang_stmt *ymod = NULL;
yang_stmt *yrev; yang_stmt *yrev;
char *modname; char *modname;
char *subrevision; char *subrevision;
@ -1527,7 +1527,7 @@ yang_parse_recurse(clicon_handle h,
if ((nr = yang_parse_find_match(h, yang_dir, module, fbuf)) < 0) if ((nr = yang_parse_find_match(h, yang_dir, module, fbuf)) < 0)
goto done; goto done;
if (nr == 0){ if (nr == 0){
clicon_err(OE_YANG, errno, "No matching %s yang files found", module); clicon_err(OE_YANG, errno, "No matching %s yang files found (expected modulenameor absolute filename)", module);
goto done; goto done;
} }
} }
@ -1779,7 +1779,7 @@ schema_nodeid_vec(yang_node *yn,
} }
} }
if (!match){ if (!match){
clicon_debug(1, "%s not found", nodeid); clicon_debug(1, "%s: %s not found", __FUNCTION__, nodeid);
goto ok; goto ok;
} }
ynext = (yang_node*)ys; ynext = (yang_node*)ys;

View file

@ -1,7 +1,7 @@
# Clixon tests # Clixon tests
This directory contains testing code for clixon and the example This directory contains testing code for clixon and the example
routing application: routing application. Assumes setup of http daemon as describe under apps/restonf
- clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script - clixon A top-level script clones clixon in /tmp and starts all.sh. You can copy this file (review it first) and place as cron script
- all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically. - all.sh Run through all tests named 'test*.sh' in this directory. Therefore, if you place a test in this directory matching 'test*.sh' it will be run automatically.
- test_cli.sh CLI tests - test_cli.sh CLI tests

View file

@ -2,7 +2,7 @@
testnr=0 testnr=0
testnname= testnname=
clixon_cf=/usr/local/etc/routing.conf clixon_cf=/usr/local/etc/routing.xml
# error and exit, arg is optional extra errmsg # error and exit, arg is optional extra errmsg
err(){ err(){
echo "Error in Test$testnr [$testname]:" echo "Error in Test$testnr [$testname]:"
@ -15,7 +15,7 @@ err(){
new(){ new(){
testnr=`expr $testnr + 1` testnr=`expr $testnr + 1`
testname=$1 testname=$1
echo "Test$testnr [$1]" >&2 echo "Test$testnr [$1]"
# sleep 1 # sleep 1
} }
@ -80,6 +80,24 @@ EOF
fi fi
} }
# clixon tester read from file for large tests
expecteof_file(){
cmd=$1
file=$2
expect=$3
# Do while read stuff
ret=$($cmd<$file)
# Match if both are empty string
if [ -z "$ret" -a -z "$expect" ]; then
return
fi
match=`echo "$ret" | grep -Eo "$expect"`
if [ -z "$match" ]; then
err "$expect" "$ret"
fi
}
# clixon tester. First arg is command second is stdin and # clixon tester. First arg is command second is stdin and
# third is expected outcome, fourth is how long to wait # third is expected outcome, fourth is how long to wait
expectwait(){ expectwait(){

View file

@ -22,7 +22,7 @@ if [ $? -ne 0 ]; then
fi fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -If $clixon_cf sudo clixon_backend -s init -f $clixon_cf
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
@ -46,8 +46,6 @@ expectfn "$clixon_cli -1f $clixon_cf set interfaces interface eth/0/0" "^$"
new "cli show configuration" new "cli show configuration"
expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$" expectfn "$clixon_cli -1f $clixon_cf show conf cli" "^interfaces interface name eth/0/0" "interfaces interface enabled true$"
new "cli failed validate" new "cli failed validate"
expectfn "$clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable" expectfn "$clixon_cli -1f $clixon_cf -l o validate" "Missing mandatory variable"

View file

@ -32,6 +32,17 @@ module example{
} }
} }
} }
list sender{
key name;
leaf name{
type string;
}
leaf template{
type leafref{
path "/sender/name";
}
}
}
} }
EOF EOF
@ -44,14 +55,16 @@ fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -If $clixon_cf -y /tmp/leafref.yang sudo clixon_backend -s init -f $clixon_cf -y /tmp/leafref.yang
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
new "leafref base config" new "leafref base config"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><interfaces> expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/leafref.yang" "<rpc><edit-config><target><candidate/></target><config><interfaces>
<interface><name>eth0</name> <type>eth</type> <ipv4><address><ip>192.0.2.1</ip></address></ipv4> <ipv4><address><ip>192.0.2.2</ip></address></ipv4></interface><interface><name>lo</name><type>lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" <interface><name>eth0</name> <type>eth</type> <ipv4><address><ip>192.0.2.1</ip></address><address><ip>192.0.2.2</ip></address></ipv4></interface>
<interface><name>lo</name><type>lo</type><ipv4><address><ip>127.0.0.1</ip></address></ipv4></interface>
</interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "leafref get config" new "leafref get config"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>' expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply><data><interfaces><interface><name>eth0</name>'
@ -94,6 +107,12 @@ expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set default-addre
new "cli leafref validate" new "cli leafref validate"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o validate" "^$" expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o validate" "^$"
new "cli sender"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set sender a" "^$"
new "cli sender template"
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/leafref.yang -l o set sender b template a" "^$"
new "Kill backend" new "Kill backend"
# Check if still alive # Check if still alive
pid=`pgrep clixon_backend` pid=`pgrep clixon_backend`

View file

@ -16,7 +16,7 @@ if [ $? -ne 0 ]; then
fi fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -If $clixon_cf sudo clixon_backend -s init -f $clixon_cf
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
@ -83,7 +83,7 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/><
new "netconf commit" new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit config replace" new "netconf edit config replace XXX is merge?"
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>]]>]]>$" 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" new "netconf get replaced config"
@ -96,7 +96,7 @@ new "netconf edit state operation should fail"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces-state><interface><name>eth1</name><type>eth</type></interface></interfaces-state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag>" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces-state><interface><name>eth1</name><type>eth</type></interface></interfaces-state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value</error-tag>"
new "netconf get state operation" new "netconf get state operation"
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get><filter type=\"xpath\" select=\"/interfaces-state\"/></get></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$" expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get><filter type=\"xpath\" select=\"/interfaces-state\"/></get></rpc>]]>]]>" "^<rpc-reply><data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data></rpc-reply>]]>]]>$"
new "netconf lock/unlock" 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>]]>]]>$" 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>]]>]]>$"

119
test/test_perf.sh Executable file
View file

@ -0,0 +1,119 @@
#!/bin/bash
# Scaling test
if [ $# = 0 ]; then
number=1000
elif [ $# = 1 ]; then
number=$1
else
echo "Usage: $0 [<number>]"
exit 1
fi
fyang=/tmp/scaling.yang
fconfig=/tmp/config
# include err() and new() functions
. ./lib.sh
# For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
# clixon_netconf="valgrind --tool=callgrind clixon_netconf
clixon_netconf=clixon_netconf
cat <<EOF > $fyang
module ietf-ip{
container x {
list y {
key "a";
leaf a {
type string;
}
leaf b {
type string;
}
}
leaf-list c {
type string;
}
}
}
EOF
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $clixon_cf -y $fyang
if [ $? -ne 0 ]; then
err
fi
new "start backend"
# start new backend
sudo clixon_backend -s init -f $clixon_cf -y $fyang
if [ $? -ne 0 ]; then
err
fi
new "generate large list config"
echo -n "<rpc><edit-config><target><candidate/></target><config><x>" > $fconfig
for (( i=0; i<$number; i++ )); do
echo -n "<y><a>$i</a><b>$i</b></y>" >> $fconfig
done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
new "netconf edit large config"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf edit large config again"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
rm $fconfig
new "netconf commit large config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf add small config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><y><a>x</a><b>y</b></y></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit small config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get large config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><y><a>0</a><b>0</b></y><y><a>1</a><b>1</b>"
new "generate large leaf-list config"
echo -n "<rpc><edit-config><target><candidate/></target><default-operation>replace</default-operation><config><x>" > $fconfig
for (( i=0; i<$number; i++ )); do
echo -n "<c>$i</c>" >> $fconfig
done
echo "</x></config></edit-config></rpc>]]>]]>" >> $fconfig
new "netconf replace large list-leaf config"
expecteof_file "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "$fconfig" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
rm $fconfig
new "netconf commit large leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf add small leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><edit-config><target><candidate/></target><config><x><c>x</c></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf commit small leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><commit><source><candidate/></source></commit></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "netconf get large leaf-list config"
expecteof "time -p $clixon_netconf -qf $clixon_cf -y $fyang" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><x><c>0</c><c>1</c>"
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

View file

@ -1,9 +1,13 @@
#!/bin/bash #!/bin/bash
# Test3: backend and restconf basic functionality # Restconf basic functionality
# Assume http server setup, such as nginx described in apps/restconf/README.md
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
# This is a fixed 'state' implemented in routing_backend. It is always there
state='{"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}'
# 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
@ -11,7 +15,7 @@ if [ $? -ne 0 ]; then
err err
fi fi
new "start backend" new "start backend"
sudo clixon_backend -If $clixon_cf sudo clixon_backend -s init -f $clixon_cf
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi
@ -20,7 +24,7 @@ new "kill old restconf daemon"
sudo pkill -u www-data clixon_restconf sudo pkill -u www-data clixon_restconf
new "start restconf daemon" 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 sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -Df /usr/local/etc/routing.xml # -D
sleep 1 sleep 1
@ -33,27 +37,30 @@ new "restconf head"
expectfn "curl -sS -I http://localhost/restconf/data" "HTTP/1.1 200 OK" expectfn "curl -sS -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
#Content-Type: application/yang-data+json" #Content-Type: application/yang-data+json"
new "restconf get empty config" new "restconf get empty config + state"
expectfn "curl -sSG http://localhost/restconf/data" "null" expectfn "curl -sSG http://localhost/restconf/data" $state
new "restconf get state operation"
expectfn "curl -sS -G http://localhost/restconf/data/interfaces-state" $state
new "restconf Add subtree to datastore using POST" new "restconf Add subtree to datastore using POST"
expectfn 'curl -sS -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' "" expectfn 'curl -sS -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' ""
new "restconf Check interfaces eth/0/0 added" new "restconf Check interfaces eth/0/0 added"
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}} expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}},"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}
$' $'
new "restconf delete interfaces" new "restconf delete interfaces"
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces' "" expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces' ""
new "restconf Check empty config" new "restconf Check empty config"
expectfn "curl -sSG http://localhost/restconf/data" "null" expectfn "curl -sSG http://localhost/restconf/data" $state
new "restconf Add interfaces subtree eth/0/0 using POST" new "restconf Add interfaces subtree eth/0/0 using POST"
expectfn 'curl -sS -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' "" expectfn 'curl -sS -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' ""
new "restconf Check eth/0/0 added" new "restconf Check eth/0/0 added"
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}} expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}},"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}
$' $'
new "restconf Re-post eth/0/0 which should generate error" new "restconf Re-post eth/0/0 which should generate error"
@ -70,7 +77,7 @@ new "restconf delete eth/0/0"
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "" expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
new "Check deleted eth/0/0" new "Check deleted eth/0/0"
expectfn 'curl -sS -G http://localhost/restconf/data' "null" expectfn 'curl -sS -G http://localhost/restconf/data' $state
new "restconf Re-Delete eth/0/0 using none should generate error" new "restconf Re-Delete eth/0/0 using none should generate error"
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "Not Found" expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "Not Found"
@ -79,10 +86,10 @@ new "restconf Add subtree eth/0/0 using PUT"
expectfn 'curl -sS -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "" expectfn 'curl -sS -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
new "restconf get subtree" new "restconf get subtree"
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}} expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}},"interfaces-state": {"interface": {"name": "eth0","type": "eth","if-index": "42"}}}
$' $'
new "restconf rpc using POST json" new "restconf operation rpc using POST json"
expectfn 'curl -sS -X POST -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '{ "output": { "route": { "address-family": "ipv4", "next-hop": { "next-hop-list": "2.3.4.5" } } } } ' expectfn 'curl -sS -X POST -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '{ "output": { "route": { "address-family": "ipv4", "next-hop": { "next-hop-list": "2.3.4.5" } } } } '
new "restconf rpc using POST xml" new "restconf rpc using POST xml"

90
test/test_startup.sh Executable file
View file

@ -0,0 +1,90 @@
#!/bin/bash
# Startup test: Start clicon daemon in the (four) different startup modes
# and the dbs and files are setup as follows:
# - The example reset_state callback adds "lo" interface
# - An extra xml configuration file starts with an "extra" interface
# - running db starts with a "run" interface
# - startup db starts with a "start" interface
# 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
clixon_cli=clixon_cli
run(){
mode=$1
expect=$2
cat <<EOF > /tmp/db
<config>
<interfaces>
<interface>
<name>run</name>
<type>eth</type>
</interface>
</interfaces>
</config>
EOF
sudo mv /tmp/db /usr/local/var/routing/running_db
cat <<EOF > /tmp/db
<config>
<interfaces>
<interface>
<name>startup</name>
<type>eth</type>
</interface>
</interfaces>
</config>
EOF
sudo mv /tmp/db /usr/local/var/routing/startup_db
cat <<EOF > /tmp/config
<config>
<interfaces>
<interface>
<name>extra</name>
<type>eth</type>
</interface>
</interfaces>
</config>
EOF
# kill old backend (if any)
new "kill old backend"
sudo clixon_backend -zf $clixon_cf
if [ $? -ne 0 ]; then
err
fi
new "start backend"
# start new backend
sudo clixon_backend -f $clixon_cf -s $mode -c /tmp/config
if [ $? -ne 0 ]; then
err
fi
new "Check $mode"
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><running/></source></get-config></rpc>]]>]]>' "^<rpc-reply>$expect</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
}
run init '<data/>'
run none '<data><interfaces><interface><name>run</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>'
run running '<data><interfaces><interface><name>run</name><type>eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>local</type><enabled>true</enabled></interface><interface><name>extra</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>'
run startup '<data><interfaces><interface><name>startup</name><type>eth</type><enabled>true</enabled></interface><interface><name>lo</name><type>local</type><enabled>true</enabled></interface><interface><name>extra</name><type>eth</type><enabled>true</enabled></interface></interfaces></data>'

View file

@ -69,7 +69,7 @@ if [ $? -ne 0 ]; then
fi fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -If $clixon_cf -y /tmp/type.yang sudo clixon_backend -s init -f $clixon_cf -y /tmp/type.yang
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi

View file

@ -3,12 +3,29 @@
# include err() and new() functions # include err() and new() functions
. ./lib.sh . ./lib.sh
clixon_cf=/tmp/conf_yang.xml
# For memcheck # For memcheck
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf" # clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
clixon_netconf=clixon_netconf clixon_netconf=clixon_netconf
clixon_cli=clixon_cli clixon_cli=clixon_cli
cat <<EOF > /tmp/conf_yang.xml
<config>
<CLICON_CONFIGFILE>/tmp/test_yang.xml</CLICON_CONFIGFILE>
<CLICON_YANG_DIR>/usr/local/share/routing/yang</CLICON_YANG_DIR>
<CLICON_YANG_MODULE_MAIN>example</CLICON_YANG_MODULE_MAIN>
<CLICON_CLISPEC_DIR>/usr/local/lib/routing/clispec</CLICON_CLISPEC_DIR>
<CLICON_CLI_DIR>/usr/local/lib/routing/cli</CLICON_CLI_DIR>
<CLICON_CLI_MODE>routing</CLICON_CLI_MODE>
<CLICON_SOCK>/usr/local/var/routing/routing.sock</CLICON_SOCK>
<CLICON_BACKEND_PIDFILE>/usr/local/var/routing/routing.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
<CLICON_XMLDB_DIR>/usr/local/var/routing</CLICON_XMLDB_DIR>
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
</config>
EOF
cat <<EOF > /tmp/test.yang cat <<EOF > /tmp/test.yang
module example{ module example{
container x { container x {
@ -70,7 +87,7 @@ fi
new "start backend" new "start backend"
# start new backend # start new backend
sudo clixon_backend -If $clixon_cf -y /tmp/test.yang sudo clixon_backend -s init -f $clixon_cf -y /tmp/test.yang
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
err err
fi fi

69
yang/Makefile.in Normal file
View file

@ -0,0 +1,69 @@
#
# ***** 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@
prefix = @prefix@
bindir = @bindir@
includedir = @includedir@
datarootdir = @datarootdir@
YANGSPECS = clixon-config@2017-07-02.yang
YANGSPECS += ietf-netconf@2011-06-01.yang
APPNAME = clixon # subdir ehere these files are installed
all:
# Note: clixon.mk has a rule for:
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
clean:
distclean: clean
rm -f Makefile *~ .depend
install: $(YANGSPECS)
install -d $(DESTDIR)/yang
install $(YANGSPECS) $(DESTDIR)$(clixon_DATADIR)
uninstall:
(cd $(DESTDIR)$(clixon_DATADIR); rm -rf $(YANGSPECS))
install-include:
depend:
#include .depend

View file

@ -38,18 +38,41 @@
***** END LICENSE BLOCK *****"; ***** END LICENSE BLOCK *****";
revision 2017-07-02 { revision 2017-11-12 {
description description
"Initial revision"; "Added startup config";
} }
typedef startup_mode{
description "Which method to boot/start clicon backend.
The methods differ in how they reach a running state
Which source database to commit from, if any.";
type enumeration{
enum none{
description "Do not touch running state
Typically after crash when running state and db are synched";
}
enum init{
description "Initialize running state.
Start with a completely clean running state";
}
enum running{
description "Commit running db configuration into running state
After reboot if a persistent running db exists";
}
enum startup{
description "Commit startup configuration into running state
After reboot when no persistent running db exists";
}
}
}
container config {
leaf CLICON_CONFIGFILE{ leaf CLICON_CONFIGFILE{
type string; type string;
default "sysconfdir/$APPNAME.conf";
description "Location of configuration-file for default values (this file)"; description "Location of configuration-file for default values (this file)";
} }
leaf CLICON_YANG_DIR { leaf CLICON_YANG_DIR {
type string; type string;
default "prefix/share/$APPNAME/yang"; mandatory true;
description "Location of YANG module and submodule files. Only if CLICON_DBSPEC_TYPE is YANG"; description "Location of YANG module and submodule files. Only if CLICON_DBSPEC_TYPE is YANG";
} }
leaf CLICON_YANG_MODULE_MAIN { leaf CLICON_YANG_MODULE_MAIN {
@ -66,34 +89,73 @@
} }
leaf CLICON_BACKEND_DIR { leaf CLICON_BACKEND_DIR {
type string; type string;
default "libdir/$APPNAME/backend"; description "Location of backend .so plugins. Load all .so
description "Location of backend .so plugins"; plugins in this dir as backend plugins";
} }
leaf CLICON_NETCONF_DIR { leaf CLICON_NETCONF_DIR {
type string; type string;
default "libdir/$APPNAME/netconf";
description "Location of netconf (frontend) .so plugins"; description "Location of netconf (frontend) .so plugins";
} }
leaf CLICON_RESTCONF_DIR { leaf CLICON_RESTCONF_DIR {
type string; type string;
default "libdir/$APPNAME/restconf"; description "Location of restconf (frontend) .so plugins. Load all .so
description "Location of restconf (frontend) .so plugins"; plugins in this dir as restconf code plugins";
}
leaf CLICON_RESTCONF_PATH {
type string;
default "/www-data/fastcgi_restconf.sock";
description "FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock";
} }
leaf CLICON_CLI_DIR { leaf CLICON_CLI_DIR {
type string; type string;
default "libdir/$APPNAME/cli"; description "Location of cli frontend .so plugins. Load all .so
description "Location of cli frontend .so plugins"; plugins in this dir as CLI object plugins";
} }
leaf CLICON_CLISPEC_DIR { leaf CLICON_CLISPEC_DIR {
type string; type string;
default "libdir/$APPNAME/clispec"; description "Location of frontend .cli cligen spec files. Load all .cli
description "Location of frontend .cli cligen spec files"; files in this dir as CLI specification files";
} }
leaf CLICON_USE_STARTUP_CONFIG { leaf CLICON_CLISPEC_FILE {
type string;
description "Specific frontend .cli cligen spec file.";
}
leaf CLICON_CLI_MODE {
type string;
default "base";
description "Startup CLI mode. This should match a CLICON_MODE set in
one of the clispec files";
}
leaf CLICON_CLI_GENMODEL {
type int32; type int32;
default 0; default 1;
description "Enabled uses \"startup\" configuration on boot"; description "Generate code for CLI completion of existing db symbols.
Example: Add name=\"myspec\" in datamodel spec and reference
as @myspec";
} }
leaf CLICON_CLI_GENMODEL_COMPLETION {
type int32;
default 1;
description "Generate code for CLI completion of existing db symbols";
}
leaf CLICON_CLI_GENMODEL_TYPE {
type string;
default "VARS";
description "How to generate and show CLI syntax: VARS|ALL";
}
leaf CLICON_CLI_VARONLY {
type int32;
default 1;
description "Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored";
}
leaf CLICON_CLI_LINESCROLLING {
type int32;
default 1;
description "Set to 0 if you want CLI to wrap to next line.
Set to 1 if you want CLI to scroll sideways when approaching right margin";
}
leaf CLICON_SOCK_FAMILY { leaf CLICON_SOCK_FAMILY {
type string; type string;
default "UNIX"; default "UNIX";
@ -101,25 +163,25 @@
} }
leaf CLICON_SOCK { leaf CLICON_SOCK {
type string; type string;
default "localstatedir/$APPNAME/$APPNAME.sock"; mandatory true;
description "If family above is AF_UNIX: Unix socket for communicating with description "If family above is AF_UNIX: Unix socket for communicating
clixon_backend. If family above is AF_INET: IPv4 address"; with clixon_backend. If family is AF_INET: IPv4 address";
} }
leaf CLICON_SOCK_PORT { leaf CLICON_SOCK_PORT {
type int32; type int32;
default 4535; default 4535;
description "Inet socket port for communicating with clixon_backend (only IPv4|IPv6)"; description "Inet socket port for communicating with clixon_backend (only IPv4|IPv6)";
} }
leaf CLICON_BACKEND_PIDFILE {
type string;
default "localstatedir/$APPNAME/$APPNAME.pidfile";
description "Process-id file";
}
leaf CLICON_SOCK_GROUP { leaf CLICON_SOCK_GROUP {
type string; type string;
default "clicon"; default "clicon";
description "Group membership to access clixon_backend unix socket"; description "Group membership to access clixon_backend unix socket";
} }
leaf CLICON_BACKEND_PIDFILE {
type string;
mandatory true;
description "Process-id file";
}
leaf CLICON_AUTOCOMMIT { leaf CLICON_AUTOCOMMIT {
type int32; type int32;
default 0; default 0;
@ -131,49 +193,28 @@
default "master"; default "master";
description "Name of master plugin (both frontend and backend). description "Name of master plugin (both frontend and backend).
Master plugin has special callbacks for frontends. Master plugin has special callbacks for frontends.
See clicon user manual for more info."; See clicon user manual for more info. (Obsolete)";
}
leaf CLICON_CLI_MODE {
type string;
default "base";
description "Startup CLI mode. This should match the CLICON_MODE in your startup clispec file";
}
leaf CLICON_CLI_GENMODEL {
type int32;
default 1;
description "Generate code for CLI completion of existing db symbols.
Add name=\"myspec\" in datamodel spec and reference as @myspec";
}
leaf CLICON_CLI_GENMODEL_COMPLETION {
type int32;
default 0;
description "Generate code for CLI completion of existing db symbols";
}
leaf CLICON_CLI_GENMODEL_TYPE {
type string;
default "VARS";
description "How to generate and show CLI syntax: VARS|ALL";
} }
leaf CLICON_XMLDB_DIR { leaf CLICON_XMLDB_DIR {
type string; type string;
default "localstatedir/$APPNAME"; mandatory true;
description "Directory where \"running\", \"candidate\" and \"startup\" are placed"; description "Directory where \"running\", \"candidate\" and \"startup\" are placed";
} }
leaf CLICON_USE_STARTUP_CONFIG {
type int32;
default 0;
description "Enabled uses \"startup\" configuration on boot. It is called
startup_db and exists in XMLDB_DIR. NOTE: Obsolete with CLICON_STARTUP_MODE";
}
leaf CLICON_XMLDB_PLUGIN { leaf CLICON_XMLDB_PLUGIN {
type string; type string;
default "libdir/xmldb/text.so"; mandatory true;
description "XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])"; description "XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])";
} }
leaf CLICON_CLI_VARONLY { leaf CLICON_STARTUP_MODE {
type int32; type startup_mode;
default 1; description "Which method to boot/start clicon backend";
description "Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored";
} }
leaf CLICON_RESTCONF_PATH { }
type string;
default "/www-data/fastcgi_restconf.sock";
description "FastCGI unix socket. Should be specified in webserver
Eg in nginx: fastcgi_pass unix:/www-data/clicon_restconf.sock;";
}
} }