Merge branch 'develop'
This commit is contained in:
commit
9d8a2f73c2
85 changed files with 6664 additions and 3692 deletions
|
|
@ -1,34 +1,28 @@
|
|||
# ***** 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 *****
|
||||
# Clixon CHANGELOG
|
||||
|
||||
## 3.3.0
|
||||
|
||||
May 2017
|
||||
|
||||
- Datastore text module is now default.
|
||||
|
||||
- Refined netconf "none" semantics in tests and text datastore
|
||||
|
||||
- Moved apps/dbctrl to datastore/
|
||||
|
||||
- Added connect/disconnect/getopt/setopt and handle to xmldb API
|
||||
|
||||
- Added datastore 'text'
|
||||
|
||||
- Configure (autoconf) changes
|
||||
Removed libcurl dependency
|
||||
Disable restconf (and fastcgi) with configure --disable-restconf
|
||||
Disable keyvalue datastore (and qdbm) with configure --disable-keyvalue
|
||||
|
||||
- Created xmldb plugin api
|
||||
Moved qdbm, chunk and xmldb to datastore keyvalue directories
|
||||
Removed all other clixon dependency on chunk code
|
||||
|
||||
- cli_copy_config added as generic cli command
|
||||
- cli_show_config added as generic cli command
|
||||
Replace all show_confv*() and show_conf*() with cli_show_config()
|
||||
|
|
@ -52,7 +52,7 @@ LIBS = @LIBS@
|
|||
INCLUDES = -I. -I@srcdir@ @INCLUDES@
|
||||
SHELL = /bin/sh
|
||||
|
||||
SUBDIRS = lib apps include etc
|
||||
SUBDIRS = lib apps include etc datastore
|
||||
|
||||
.PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
/*! This is a small comment on one line
|
||||
*
|
||||
* This is a detailed description
|
||||
* spanning several lines.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* fn(a, &b);
|
||||
* @endcode
|
||||
*
|
||||
* @param[in] src This is a description of the first parameter
|
||||
* @param[in,out] dest This is a description of the second parameter
|
||||
* @retval TRUE This is a description of the return value
|
||||
* @retval FALSE This is a description of another return value
|
||||
* @see anotherfn()
|
||||
* @note This is just an example
|
||||
*/
|
||||
|
||||
/*! This is a documentation of a typedef or struct
|
||||
*
|
||||
* This is a detailed description
|
||||
* spanning several lines.
|
||||
*
|
||||
* Example usage:
|
||||
* @code
|
||||
* struct foo f;
|
||||
* @endcode
|
||||
*/
|
||||
typedef foo{
|
||||
int a; /**< This is the a field*/
|
||||
};
|
||||
102
README.md
102
README.md
|
|
@ -1,38 +1,96 @@
|
|||
CLIXON
|
||||
======
|
||||
# Clixon
|
||||
|
||||
CLIXON is an automatic configuration manager where you from a YANG
|
||||
specification generate interactive CLI, NETCONF, RESTCONF and embedded
|
||||
databases with transaction support.
|
||||
Clixon is an automatic configuration manager where you generate
|
||||
interactive CLI, NETCONF, RESTCONF and embedded databases with
|
||||
transaction support from a YANG specification.
|
||||
|
||||
CLIXON is a fork of CLICON where legacy key specification has been
|
||||
replaced completely by YANG. This means that legacy CLICON
|
||||
applications such as CLICON/ROST does not run on CLIXON.
|
||||
Table of contents
|
||||
=================
|
||||
* [Documentation](#documentation)
|
||||
* [Installation](#installation)
|
||||
* [Dependencies](#dependencies)
|
||||
* [Licenses](#licenses)
|
||||
* [Background](#background)
|
||||
* [Clixon SDK](#SDK)
|
||||
|
||||
Presentations and tutorial is found on the [CLICON project
|
||||
page](http://www.clicon.org)
|
||||
Documentation
|
||||
=============
|
||||
- [Frequently asked questions](doc/FAQ.md)
|
||||
- [XML datastore](datastore/README.md)
|
||||
- [Netconf support](apps/netconf/README.md)
|
||||
- [Restconf support](apps/restconf/README.md)
|
||||
- [Reference manual](http://www.clicon.org/doxygen/index.html) (Note the link may not be up-to-date. It is better to build your own: cd doc; make doc)
|
||||
- [Routing example](example/README.md)
|
||||
- [Clicon and Clixon project page](http://www.clicon.org)
|
||||
- [Tests](test/README.md)
|
||||
|
||||
Installation
|
||||
============
|
||||
A typical installation is as follows:
|
||||
```
|
||||
configure # Configure clixon to platform
|
||||
make # Compile
|
||||
sudo make install # Install libs, binaries, and config-files
|
||||
sudo make install-include # Install include files (for compiling)
|
||||
```
|
||||
|
||||
> configure # Configure clixon to platform
|
||||
> make # Compile
|
||||
> sudo make install # Install libs, binaries, and config-files
|
||||
> sudo make install-include # Install include files (for compiling)
|
||||
One [example application](example/README.md) is provided, a IETF IP YANG datamodel with
|
||||
generated CLI and configuration interface.
|
||||
|
||||
One example applications is provided, the IETF IP YANG datamodel with generated CLI and configuration interface. It all origins from work at
|
||||
[KTH](http://www.csc.kth.se/~olofh/10G_OSR)
|
||||
|
||||
[CLIgen](http://www.cligen.se) is required for building CLIXON. If you need
|
||||
Dependencies
|
||||
============
|
||||
Clixon is dependend on the following software packages, which need to exist on the target machine.
|
||||
- [CLIgen](http://www.cligen.se) is required for building Clixon. If you need
|
||||
to build and install CLIgen:
|
||||
|
||||
```
|
||||
git clone https://github.com/olofhagsand/cligen.git
|
||||
cd cligen; configure; make; make install
|
||||
```
|
||||
- Yacc/bison
|
||||
- Lex/Flex
|
||||
- Fcgi (if restconf is enabled)
|
||||
- Qdbm key-value store (if keyvalue datastore is enabled)
|
||||
|
||||
CLIXON is dual license. Either Apache License, Version 2.0 or GNU
|
||||
There is no yum/apt/ostree package for Clixon (please help?)
|
||||
|
||||
Licenses
|
||||
========
|
||||
Clixon is dual license. Either Apache License, Version 2.0 or GNU
|
||||
General Public License Version 2. You choose.
|
||||
|
||||
See LICENSE.md for license, CHANGELOG for recent changes.
|
||||
|
||||
See [LICENSE.md](LICENSE.md) for license, [CHANGELOG](CHANGELOG.md) for recent changes.
|
||||
|
||||
Background
|
||||
==========
|
||||
We implemented Clixon since we needed a generic configuration tool in
|
||||
several projects, including
|
||||
[KTH](http://www.csc.kth.se/~olofh/10G_OSR). Most of these projects
|
||||
were for embedded network and measuring-probe devices. We started with
|
||||
something called Clicon which was based on a key-value specification
|
||||
and data-store. But as time passed new standards evaolved and we
|
||||
started adapting it to XML, Yang and netconf. Finally we made Clixon
|
||||
where the legacy key specification has been replaced completely by
|
||||
YANG and using XML as configuration data. This means that legacy
|
||||
Clicon applications do not run on Clixon.
|
||||
|
||||
SDK
|
||||
===
|
||||
|
||||
<img src="doc/clixon_example_sdk.png" alt="clixon sdk" style="width: 200px;"/>
|
||||
|
||||
The figure shows the SDK runtime of Clixon.
|
||||
|
||||
YANG and XML is at the heart of Clixon. Yang modules are used as a
|
||||
specification for handling XML configuration data. The spec is also
|
||||
used to generate an interactive CLI client as well as provide
|
||||
[Netconf](apps/netconf/README.md) and
|
||||
[Restconf](apps/restconf/README.md) clients.
|
||||
|
||||
The [YANG RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) is implemented with the following exceptions:
|
||||
- object-references
|
||||
- if-feature
|
||||
- unique
|
||||
- rpc
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,10 +37,16 @@ CC = @CC@
|
|||
CFLAGS = @CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
with_restconf = @with_restconf@
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
SUBDIRS = cli backend dbctrl netconf restconf
|
||||
SUBDIRS = backend
|
||||
SUBDIRS += cli
|
||||
SUBDIRS += netconf
|
||||
ifeq ($(with_restconf),yes)
|
||||
SUBDIRS += restconf
|
||||
endif
|
||||
|
||||
.PHONY: all clean depend install $(SUBDIRS)
|
||||
|
||||
|
|
|
|||
|
|
@ -307,7 +307,6 @@ from_client_edit_config(clicon_handle h,
|
|||
}
|
||||
}
|
||||
if ((xc = xpath_first(xn, "config")) != NULL){
|
||||
/* XXX see from_client_xmlput() */
|
||||
if (xmldb_put(h, target, operation, api_path, xc) < 0){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
|
|
@ -438,7 +437,7 @@ from_client_unlock(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
else{
|
||||
xmldb_unlock(h, db, pid);
|
||||
xmldb_unlock(h, db);
|
||||
if (cprintf(cbret, "<rpc-reply><ok/></rpc-reply>") < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -496,7 +495,7 @@ from_client_kill_session(clicon_handle h,
|
|||
if (1 || (kill (pid, 0) != 0 && errno == ESRCH)){ /* Nothing there */
|
||||
/* clear from locks */
|
||||
if (xmldb_islocked(h, db) == pid)
|
||||
xmldb_unlock(h, db, pid);
|
||||
xmldb_unlock(h, db);
|
||||
}
|
||||
else{ /* failed to kill client */
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
|
|
@ -622,7 +621,6 @@ from_client_delete_config(clicon_handle h,
|
|||
piddb);
|
||||
goto ok;
|
||||
}
|
||||
|
||||
if (xmldb_delete(h, target) < 0){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
|
|
@ -633,7 +631,7 @@ from_client_delete_config(clicon_handle h,
|
|||
"</rpc-error></rpc-reply>", clicon_err_reason);
|
||||
goto ok;
|
||||
}
|
||||
if (xmldb_init(h, target) < 0){
|
||||
if (xmldb_create(h, target) < 0){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>protocol</error-type>"
|
||||
|
|
@ -872,9 +870,21 @@ from_client_msg(clicon_handle h,
|
|||
assert(cbuf_len(cbret));
|
||||
clicon_debug(1, "%s %s", __FUNCTION__, cbuf_get(cbret));
|
||||
if (send_msg_reply(ce->ce_s, cbuf_get(cbret), cbuf_len(cbret)+1) < 0){
|
||||
if (errno == ECONNRESET)
|
||||
switch (errno){
|
||||
case EPIPE:
|
||||
/* man (2) write:
|
||||
* EPIPE fd is connected to a pipe or socket whose reading end is
|
||||
* closed. When this happens the writing process will also receive
|
||||
* a SIGPIPE signal.
|
||||
* In Clixon this means a client, eg restconf, netconf or cli closes
|
||||
* the (UNIX domain) socket.
|
||||
*/
|
||||
case ECONNRESET:
|
||||
clicon_log(LOG_WARNING, "client rpc reset");
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
// ok:
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
* Prototypes
|
||||
* not exported.
|
||||
*/
|
||||
/* backend handles */
|
||||
/* backend handles. Defined in clixon_backend_handle.c */
|
||||
clicon_handle backend_handle_init(void);
|
||||
|
||||
int backend_handle_exit(clicon_handle h);
|
||||
|
|
|
|||
|
|
@ -73,11 +73,11 @@
|
|||
#include "backend_handle.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pty:"
|
||||
#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1IRCc:rg:py:x:"
|
||||
|
||||
/*! Terminate. Cannot use h after this */
|
||||
static int
|
||||
config_terminate(clicon_handle h)
|
||||
backend_terminate(clicon_handle h)
|
||||
{
|
||||
yang_spec *yspec;
|
||||
char *pidfile = clicon_backend_pidfile(h);
|
||||
|
|
@ -91,19 +91,18 @@ config_terminate(clicon_handle h)
|
|||
unlink(pidfile);
|
||||
if (sockpath)
|
||||
unlink(sockpath);
|
||||
xmldb_plugin_unload(h); /* unload storage plugin */
|
||||
backend_handle_exit(h); /* Cannot use h after this */
|
||||
event_exit();
|
||||
clicon_log_register_callback(NULL, NULL);
|
||||
clicon_debug(1, "%s done", __FUNCTION__);
|
||||
if (debug)
|
||||
chunk_check(stderr, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Unlink pidfile and quit
|
||||
*/
|
||||
static void
|
||||
config_sig_term(int arg)
|
||||
backend_sig_term(int arg)
|
||||
{
|
||||
static int i=0;
|
||||
|
||||
|
|
@ -130,6 +129,7 @@ usage(char *argv0, clicon_handle h)
|
|||
" -D <level>\tdebug\n"
|
||||
" -f <file>\tCLICON config file (mandatory)\n"
|
||||
" -d <dir>\tSpecify backend plugin directory (default: %s)\n"
|
||||
" -b <dir>\tSpecify XMLDB database directory\n"
|
||||
" -z\t\tKill other config daemon and exit\n"
|
||||
" -F\t\tforeground\n"
|
||||
" -1\t\tonce (dont wait for events)\n"
|
||||
|
|
@ -141,9 +141,9 @@ usage(char *argv0, clicon_handle h)
|
|||
" -c <file>\tLoad specified application config.\n"
|
||||
" -r\t\tReload running database\n"
|
||||
" -p \t\tPrint database yang specification\n"
|
||||
" -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\n"
|
||||
" -g <group>\tClient membership required to this group (default: %s)\n"
|
||||
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
|
||||
" -y <file>\tOverride yang spec file (dont include .yang suffix)\n"
|
||||
" -x <plugin>\tXMLDB plugin\n",
|
||||
argv0,
|
||||
plgdir ? plgdir : "none",
|
||||
confsock ? confsock : "none",
|
||||
|
|
@ -159,7 +159,7 @@ db_reset(clicon_handle h,
|
|||
{
|
||||
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
|
||||
return -1;
|
||||
if (xmldb_init(h, db) < 0)
|
||||
if (xmldb_create(h, db) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -181,7 +181,7 @@ rundb_main(clicon_handle h,
|
|||
cxobj *xt = NULL;
|
||||
cxobj *xn;
|
||||
|
||||
if (xmldb_init(h, "tmp") < 0)
|
||||
if (xmldb_create(h, "tmp") < 0)
|
||||
goto done;
|
||||
if (xmldb_copy(h, "running", "tmp") < 0){
|
||||
clicon_err(OE_UNIX, errno, "file copy");
|
||||
|
|
@ -206,7 +206,6 @@ done:
|
|||
xml_free(xt);
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -240,11 +239,11 @@ server_socket(clicon_handle h)
|
|||
int ss;
|
||||
|
||||
/* Open control socket */
|
||||
if ((ss = config_socket_init(h)) < 0)
|
||||
if ((ss = backend_socket_init(h)) < 0)
|
||||
return -1;
|
||||
/* ss is a server socket that the clients connect to. The callback
|
||||
therefore accepts clients on ss */
|
||||
if (event_reg_fd(ss, config_accept_client, h, "server socket") < 0) {
|
||||
if (event_reg_fd(ss, backend_accept_client, h, "server socket") < 0) {
|
||||
close(ss);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -256,19 +255,20 @@ server_socket(clicon_handle h)
|
|||
* log event.
|
||||
*/
|
||||
static int
|
||||
config_log_cb(int level, char *msg, void *arg)
|
||||
backend_log_cb(int level,
|
||||
char *msg,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
size_t n;
|
||||
char *ptr;
|
||||
char *nptr;
|
||||
char *newmsg = NULL;
|
||||
int retval = -1;
|
||||
char *ptr;
|
||||
char *nptr;
|
||||
char *newmsg = NULL;
|
||||
|
||||
/* backend_notify() will go through all clients and see if any has registered "CLICON",
|
||||
and if so make a clicon_proto notify message to those clients. */
|
||||
|
||||
|
||||
/* Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
|
||||
/* backend_notify() will go through all clients and see if any has
|
||||
registered "CLICON", and if so make a clicon_proto notify message to
|
||||
those clients.
|
||||
Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
|
||||
At this stage all formatting is already done */
|
||||
n = 0;
|
||||
for(ptr=msg; *ptr; ptr++)
|
||||
|
|
@ -283,7 +283,6 @@ config_log_cb(int level, char *msg, void *arg)
|
|||
if (*ptr == '%')
|
||||
*nptr++ = '%';
|
||||
}
|
||||
|
||||
retval = backend_notify(arg, "CLICON", level, newmsg);
|
||||
free(newmsg);
|
||||
|
||||
|
|
@ -309,11 +308,11 @@ main(int argc, char **argv)
|
|||
clicon_handle h;
|
||||
int help = 0;
|
||||
int printspec = 0;
|
||||
int printalt = 0;
|
||||
int pid;
|
||||
char *pidfile;
|
||||
char *sock;
|
||||
int sockfamily;
|
||||
char *xmldb_plugin;
|
||||
|
||||
/* In the startup, logs to stderr & syslog and debug flag set later */
|
||||
|
||||
|
|
@ -321,7 +320,7 @@ main(int argc, char **argv)
|
|||
/* Initiate CLICON handle */
|
||||
if ((h = backend_handle_init()) == NULL)
|
||||
return -1;
|
||||
if (config_plugin_init(h) != 0)
|
||||
if (backend_plugin_init(h) != 0)
|
||||
return -1;
|
||||
foreground = 0;
|
||||
once = 0;
|
||||
|
|
@ -387,6 +386,11 @@ main(int argc, char **argv)
|
|||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg);
|
||||
break;
|
||||
case 'b': /* XMLDB database directory */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_XMLDB_DIR", optarg);
|
||||
break;
|
||||
case 'F' : /* foreground */
|
||||
foreground = 1;
|
||||
break;
|
||||
|
|
@ -425,9 +429,6 @@ main(int argc, char **argv)
|
|||
case 'p' : /* Print spec */
|
||||
printspec++;
|
||||
break;
|
||||
case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */
|
||||
printalt++;
|
||||
break;
|
||||
case 'y' :{ /* yang module */
|
||||
/* Set revision to NULL, extract dir and module */
|
||||
char *str = strdup(optarg);
|
||||
|
|
@ -437,6 +438,10 @@ main(int argc, char **argv)
|
|||
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
||||
break;
|
||||
}
|
||||
case 'x' :{ /* xmldb plugin */
|
||||
clicon_option_str_set(h, "CLICON_XMLDB_PLUGIN", optarg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
usage(argv[0], h);
|
||||
break;
|
||||
|
|
@ -502,10 +507,25 @@ main(int argc, char **argv)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if ((xmldb_plugin = clicon_xmldb_plugin(h)) == NULL){
|
||||
clicon_log(LOG_ERR, "No xmldb plugin given (specify option CLICON_XMLDB_PLUGIN).\n");
|
||||
goto done;
|
||||
}
|
||||
if (xmldb_plugin_load(h, xmldb_plugin) < 0)
|
||||
goto done;
|
||||
/* Connect to plugin to get a handle */
|
||||
if (xmldb_connect(h) < 0)
|
||||
goto done;
|
||||
/* Parse db spec file */
|
||||
if (yang_spec_main(h, stdout, printspec) < 0)
|
||||
goto done;
|
||||
|
||||
/* Set options: database dir aqnd yangspec (could be hidden in connect?)*/
|
||||
if (xmldb_setopt(h, "dbdir", clicon_xmldb_dir(h)) < 0)
|
||||
goto done;
|
||||
if (xmldb_setopt(h, "yangspec", clicon_dbspec_yang(h)) < 0)
|
||||
goto done;
|
||||
|
||||
/* First check for startup config
|
||||
XXX the options below have become out-of-hand.
|
||||
Too complex, need to simplify*/
|
||||
|
|
@ -518,7 +538,7 @@ main(int argc, char **argv)
|
|||
else
|
||||
if (db_reset(h, "running") < 0)
|
||||
goto done;
|
||||
if (xmldb_init(h, "candidate") < 0)
|
||||
if (xmldb_create(h, "candidate") < 0)
|
||||
goto done;
|
||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||
goto done;
|
||||
|
|
@ -542,7 +562,7 @@ main(int argc, char **argv)
|
|||
}
|
||||
/* If candidate does not exist, create it from running */
|
||||
if (xmldb_exists(h, "candidate") != 1){
|
||||
if (xmldb_init(h, "candidate") < 0)
|
||||
if (xmldb_create(h, "candidate") < 0)
|
||||
goto done;
|
||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||
goto done;
|
||||
|
|
@ -605,14 +625,14 @@ main(int argc, char **argv)
|
|||
goto done;
|
||||
|
||||
/* Register log notifications */
|
||||
if (clicon_log_register_callback(config_log_cb, h) < 0)
|
||||
if (clicon_log_register_callback(backend_log_cb, h) < 0)
|
||||
goto done;
|
||||
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
|
||||
if (set_signal(SIGTERM, config_sig_term, NULL) < 0){
|
||||
if (set_signal(SIGTERM, backend_sig_term, NULL) < 0){
|
||||
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||
goto done;
|
||||
}
|
||||
if (set_signal(SIGINT, config_sig_term, NULL) < 0){
|
||||
if (set_signal(SIGINT, backend_sig_term, NULL) < 0){
|
||||
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -628,7 +648,7 @@ main(int argc, char **argv)
|
|||
goto done;
|
||||
done:
|
||||
clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
|
||||
config_terminate(h); /* Cannot use h after this */
|
||||
backend_terminate(h); /* Cannot use h after this */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ config_find_plugin(clicon_handle h,
|
|||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
config_plugin_init(clicon_handle h)
|
||||
backend_plugin_init(clicon_handle h)
|
||||
{
|
||||
find_plugin_t *fp = config_find_plugin;
|
||||
clicon_hash_t *data = clicon_data(h);
|
||||
|
|
@ -152,8 +152,8 @@ config_plugin_init(clicon_handle h)
|
|||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h,
|
||||
struct plugin *plg)
|
||||
backend_plugin_unload(clicon_handle h,
|
||||
struct plugin *plg)
|
||||
{
|
||||
char *error;
|
||||
|
||||
|
|
@ -178,44 +178,21 @@ plugin_unload(clicon_handle h,
|
|||
* @param[in] h Clicon handle
|
||||
* @param[in] file The plugin (.so) to load
|
||||
* @param[in] dlflags Arguments to dlopen(3)
|
||||
* @param[in] label Chunk label
|
||||
* @retval plugin Plugin struct
|
||||
* @retval NULL Error
|
||||
*/
|
||||
static struct plugin *
|
||||
plugin_load (clicon_handle h,
|
||||
char *file,
|
||||
int dlflags,
|
||||
const char *label)
|
||||
backend_plugin_load (clicon_handle h,
|
||||
char *file,
|
||||
int dlflags)
|
||||
{
|
||||
char *error;
|
||||
void *handle;
|
||||
char *name;
|
||||
struct plugin *new;
|
||||
plginit_t *initfun;
|
||||
struct plugin *new = NULL;
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_UNIX, 0, "dlopen: %s", error?error:"Unknown error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
initfun = dlsym(handle, PLUGIN_INIT);
|
||||
if ((error = (char*)dlerror()) != NULL) {
|
||||
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (initfun(h) != 0) {
|
||||
dlclose(handle);
|
||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||
clicon_err(OE_DB, 0, "Unknown error: %s: plugin_init does not make clicon_err call on error",
|
||||
file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((new = chunk(sizeof(*new), label)) == NULL) {
|
||||
if ((handle = plugin_load(h, file, dlflags)) == NULL)
|
||||
goto done;
|
||||
if ((new = malloc(sizeof(*new))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "dhunk: %s", strerror(errno));
|
||||
dlclose(handle);
|
||||
return NULL;
|
||||
|
|
@ -226,7 +203,6 @@ plugin_load (clicon_handle h,
|
|||
snprintf(new->p_name, sizeof(new->p_name), "%*s",
|
||||
(int)strlen(name)-2, name);
|
||||
new->p_handle = handle;
|
||||
new->p_init = initfun;
|
||||
if ((new->p_start = dlsym(handle, PLUGIN_START)) != NULL)
|
||||
clicon_debug(2, "%s callback registered.", PLUGIN_START);
|
||||
if ((new->p_exit = dlsym(handle, PLUGIN_EXIT)) != NULL)
|
||||
|
|
@ -246,7 +222,7 @@ plugin_load (clicon_handle h,
|
|||
if ((new->p_trans_abort = dlsym(handle, PLUGIN_TRANS_ABORT)) != NULL)
|
||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_ABORT);
|
||||
clicon_debug(2, "Plugin '%s' loaded.\n", name);
|
||||
|
||||
done:
|
||||
return new;
|
||||
}
|
||||
|
||||
|
|
@ -319,8 +295,8 @@ plugin_append(struct plugin *p)
|
|||
{
|
||||
struct plugin *new;
|
||||
|
||||
if ((new = rechunk(plugins, (nplugins+1) * sizeof (*p), NULL)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
if ((new = realloc(plugins, (nplugins+1) * sizeof (*p))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "realloc");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -340,7 +316,7 @@ plugin_append(struct plugin *p)
|
|||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
config_plugin_load_dir(clicon_handle h,
|
||||
backend_plugin_load_dir(clicon_handle h,
|
||||
const char *dir)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -348,11 +324,11 @@ config_plugin_load_dir(clicon_handle h,
|
|||
int np = 0;
|
||||
int ndp;
|
||||
struct stat st;
|
||||
char *filename;
|
||||
struct dirent *dp;
|
||||
char filename[MAXPATHLEN];
|
||||
struct dirent *dp = NULL;
|
||||
struct plugin *new;
|
||||
struct plugin *p = NULL;
|
||||
char *master;
|
||||
char master[MAXPATHLEN];
|
||||
char *master_plugin;
|
||||
|
||||
/* Format master plugin path */
|
||||
|
|
@ -360,50 +336,39 @@ config_plugin_load_dir(clicon_handle h,
|
|||
clicon_err(OE_PLUGIN, 0, "clicon_master_plugin option not set");
|
||||
goto quit;
|
||||
}
|
||||
master = chunk_sprintf(__FUNCTION__, "%s.so", master_plugin);
|
||||
if (master == NULL) {
|
||||
clicon_err(OE_PLUGIN, errno, "chunk_sprintf master plugin");
|
||||
goto quit;
|
||||
}
|
||||
snprintf(master, MAXPATHLEN-1, "%s.so", master_plugin);
|
||||
|
||||
/* Allocate plugin group object */
|
||||
/* Get plugin objects names from plugin directory */
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0)
|
||||
goto quit;
|
||||
|
||||
/* reset num plugins */
|
||||
np = 0;
|
||||
|
||||
/* Master plugin must be loaded first if it exists. */
|
||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, master);
|
||||
if (filename == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
goto quit;
|
||||
}
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, master);
|
||||
if (stat(filename, &st) == 0) {
|
||||
clicon_debug(1, "Loading master plugin '%.*s' ...",
|
||||
(int)strlen(filename), filename);
|
||||
|
||||
new = plugin_load(h, filename, RTLD_NOW|RTLD_GLOBAL, __FUNCTION__);
|
||||
new = backend_plugin_load(h, filename, RTLD_NOW|RTLD_GLOBAL);
|
||||
if (new == NULL)
|
||||
goto quit;
|
||||
if (plugin_append(new) < 0)
|
||||
goto quit;
|
||||
}
|
||||
|
||||
/* Now load the rest */
|
||||
/* Now load the rest. Note plugins is the global variable */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
if (strcmp(dp[i].d_name, master) == 0)
|
||||
continue; /* Skip master now */
|
||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "Loading plugin '%.*s' ...", (int)strlen(filename), filename);
|
||||
if (filename == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
goto quit;
|
||||
}
|
||||
new = plugin_load (h, filename, RTLD_NOW, __FUNCTION__);
|
||||
new = backend_plugin_load(h, filename, RTLD_NOW);
|
||||
if (new == NULL)
|
||||
goto quit;
|
||||
/* Append to 'plugins' */
|
||||
if (plugin_append(new) < 0)
|
||||
goto quit;
|
||||
}
|
||||
|
|
@ -413,13 +378,19 @@ config_plugin_load_dir(clicon_handle h,
|
|||
|
||||
quit:
|
||||
if (retval != 0) {
|
||||
if (p) {
|
||||
while (--np >= 0)
|
||||
plugin_unload (h, &p[np]);
|
||||
unchunk(p);
|
||||
/* XXX p is always NULL */
|
||||
if (plugins) {
|
||||
while (--np >= 0){
|
||||
if ((p = &plugins[np]) == NULL)
|
||||
continue;
|
||||
backend_plugin_unload(h, p);
|
||||
free(p);
|
||||
}
|
||||
free(plugins);
|
||||
}
|
||||
}
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -435,7 +406,7 @@ plugin_initiate(clicon_handle h)
|
|||
char *dir;
|
||||
|
||||
/* First load CLICON system plugins */
|
||||
if (config_plugin_load_dir(h, CLIXON_BACKEND_SYSDIR) < 0)
|
||||
if (backend_plugin_load_dir(h, CLIXON_BACKEND_SYSDIR) < 0)
|
||||
return -1;
|
||||
|
||||
/* Then load application plugins */
|
||||
|
|
@ -443,7 +414,7 @@ plugin_initiate(clicon_handle h)
|
|||
clicon_err(OE_PLUGIN, 0, "backend_dir not defined");
|
||||
return -1;
|
||||
}
|
||||
if (config_plugin_load_dir(h, dir) < 0)
|
||||
if (backend_plugin_load_dir(h, dir) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
|
|
@ -462,10 +433,12 @@ plugin_finish(clicon_handle h)
|
|||
|
||||
for (i = 0; i < nplugins; i++) {
|
||||
p = &plugins[i];
|
||||
plugin_unload(h, p);
|
||||
backend_plugin_unload(h, p);
|
||||
}
|
||||
if (plugins){
|
||||
free(plugins);
|
||||
plugins = NULL;
|
||||
}
|
||||
if (plugins)
|
||||
unchunk(plugins);
|
||||
nplugins = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ typedef struct {
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int config_plugin_init(clicon_handle h);
|
||||
int backend_plugin_init(clicon_handle h);
|
||||
int plugin_initiate(clicon_handle h);
|
||||
int plugin_finish(clicon_handle h);
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ config_socket_init_unix(clicon_handle h, char *sock)
|
|||
}
|
||||
|
||||
int
|
||||
config_socket_init(clicon_handle h)
|
||||
backend_socket_init(clicon_handle h)
|
||||
{
|
||||
char *sock;
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ config_socket_init(clicon_handle h)
|
|||
* XXX: credentials not properly implemented
|
||||
*/
|
||||
int
|
||||
config_accept_client(int fd,
|
||||
backend_accept_client(int fd,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int config_socket_init(clicon_handle h);
|
||||
int config_accept_client(int fd, void *arg);
|
||||
int backend_socket_init(clicon_handle h);
|
||||
int backend_accept_client(int fd, void *arg);
|
||||
|
||||
#endif /* _BACKEND_SOCKET_H_ */
|
||||
|
|
|
|||
|
|
@ -75,14 +75,20 @@
|
|||
* This file should only contain access functions for the _specific_
|
||||
* entries in the struct below.
|
||||
*/
|
||||
/*! Backend specific handle added to header CLICON handle
|
||||
* This file should only contain access functions for the _specific_
|
||||
* entries in the struct below.
|
||||
* @note The top part must be equivalent to struct clicon_handle in clixon_handle.c
|
||||
* @see struct clicon_handle, struct cli_handle
|
||||
*/
|
||||
struct backend_handle {
|
||||
int cb_magic; /* magic (HDR)*/
|
||||
clicon_hash_t *cb_copt; /* clicon option list (HDR) */
|
||||
clicon_hash_t *cb_data; /* internal clicon data (HDR) */
|
||||
int bh_magic; /* magic (HDR)*/
|
||||
clicon_hash_t *bh_copt; /* clicon option list (HDR) */
|
||||
clicon_hash_t *bh_data; /* internal clicon data (HDR) */
|
||||
/* ------ end of common handle ------ */
|
||||
struct client_entry *cb_ce_list; /* The client list */
|
||||
int cb_ce_nr; /* Number of clients, just increment */
|
||||
struct handle_subscription *cb_subscription; /* Event subscription list */
|
||||
struct client_entry *bh_ce_list; /* The client list */
|
||||
int bh_ce_nr; /* Number of clients, just increment */
|
||||
struct handle_subscription *bh_subscription; /* Event subscription list */
|
||||
};
|
||||
|
||||
/*! Creates and returns a clicon config handle for other CLICON API calls
|
||||
|
|
@ -141,7 +147,7 @@ backend_notify(clicon_handle h,
|
|||
if (strcmp(su->su_stream, stream) == 0){
|
||||
if (strlen(su->su_filter)==0 || fnmatch(su->su_filter, event, 0) == 0){
|
||||
if (send_msg_notify(ce->ce_s, level, event) < 0){
|
||||
if (errno == ECONNRESET){
|
||||
if (errno == ECONNRESET || errno == EPIPE){
|
||||
clicon_log(LOG_WARNING, "client %d reset", ce->ce_nr);
|
||||
#if 0
|
||||
/* We should remove here but removal is not possible
|
||||
|
|
@ -219,7 +225,7 @@ backend_notify_xml(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
if (send_msg_notify(ce->ce_s, level, cbuf_get(cb)) < 0){
|
||||
if (errno == ECONNRESET){
|
||||
if (errno == ECONNRESET || errno == EPIPE){
|
||||
clicon_log(LOG_WARNING, "client %d reset", ce->ce_nr);
|
||||
#if 0
|
||||
/* We should remove here but removal is not possible
|
||||
|
|
@ -257,11 +263,17 @@ backend_notify_xml(clicon_handle h,
|
|||
|
||||
}
|
||||
|
||||
/*! Add new client, typically frontend such as cli, netconf, restconf
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] addr Address of client
|
||||
* @retval ce Client entry
|
||||
* @retval NULL Error
|
||||
*/
|
||||
struct client_entry *
|
||||
backend_client_add(clicon_handle h,
|
||||
struct sockaddr *addr)
|
||||
{
|
||||
struct backend_handle *cb = handle(h);
|
||||
struct backend_handle *bh = handle(h);
|
||||
struct client_entry *ce;
|
||||
|
||||
if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){
|
||||
|
|
@ -269,24 +281,28 @@ backend_client_add(clicon_handle h,
|
|||
return NULL;
|
||||
}
|
||||
memset(ce, 0, sizeof(*ce));
|
||||
ce->ce_nr = cb->cb_ce_nr++;
|
||||
ce->ce_nr = bh->bh_ce_nr++;
|
||||
memcpy(&ce->ce_addr, addr, sizeof(*addr));
|
||||
ce->ce_next = cb->cb_ce_list;
|
||||
cb->cb_ce_list = ce;
|
||||
ce->ce_next = bh->bh_ce_list;
|
||||
bh->bh_ce_list = ce;
|
||||
return ce;
|
||||
}
|
||||
|
||||
/*! Return client list
|
||||
* @param[in] h Clicon handle
|
||||
* @retval ce_list Client entry list (all sessions)
|
||||
*/
|
||||
struct client_entry *
|
||||
backend_client_list(clicon_handle h)
|
||||
{
|
||||
struct backend_handle *cb = handle(h);
|
||||
struct backend_handle *bh = handle(h);
|
||||
|
||||
return cb->cb_ce_list;
|
||||
return bh->bh_ce_list;
|
||||
}
|
||||
|
||||
/*! Actually remove client from client list
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] ce Client hadnle
|
||||
* @param[in] ce Client handle
|
||||
* @see backend_client_rm which is more high-level
|
||||
*/
|
||||
int
|
||||
|
|
@ -295,9 +311,9 @@ backend_client_delete(clicon_handle h,
|
|||
{
|
||||
struct client_entry *c;
|
||||
struct client_entry **ce_prev;
|
||||
struct backend_handle *cb = handle(h);
|
||||
struct backend_handle *bh = handle(h);
|
||||
|
||||
ce_prev = &cb->cb_ce_list;
|
||||
ce_prev = &bh->bh_ce_list;
|
||||
for (c = *ce_prev; c; c = c->ce_next){
|
||||
if (c == ce){
|
||||
*ce_prev = c->ce_next;
|
||||
|
|
@ -328,7 +344,7 @@ subscription_add(clicon_handle h,
|
|||
subscription_fn_t fn,
|
||||
void *arg)
|
||||
{
|
||||
struct backend_handle *cb = handle(h);
|
||||
struct backend_handle *bh = handle(h);
|
||||
struct handle_subscription *hs = NULL;
|
||||
|
||||
if ((hs = malloc(sizeof(*hs))) == NULL){
|
||||
|
|
@ -339,10 +355,10 @@ subscription_add(clicon_handle h,
|
|||
hs->hs_stream = strdup(stream);
|
||||
hs->hs_format = format;
|
||||
hs->hs_filter = filter?strdup(filter):NULL;
|
||||
hs->hs_next = cb->cb_subscription;
|
||||
hs->hs_next = bh->bh_subscription;
|
||||
hs->hs_fn = fn;
|
||||
hs->hs_arg = arg;
|
||||
cb->cb_subscription = hs;
|
||||
bh->bh_subscription = hs;
|
||||
done:
|
||||
return hs;
|
||||
}
|
||||
|
|
@ -362,11 +378,11 @@ subscription_delete(clicon_handle h,
|
|||
subscription_fn_t fn,
|
||||
void *arg)
|
||||
{
|
||||
struct backend_handle *cb = handle(h);
|
||||
struct backend_handle *bh = handle(h);
|
||||
struct handle_subscription *hs;
|
||||
struct handle_subscription **hs_prev;
|
||||
|
||||
hs_prev = &cb->cb_subscription; /* this points to stack and is not real backpointer */
|
||||
hs_prev = &bh->bh_subscription; /* this points to stack and is not real backpointer */
|
||||
for (hs = *hs_prev; hs; hs = hs->hs_next){
|
||||
/* XXX arg == hs->hs_arg */
|
||||
if (strcmp(hs->hs_stream, stream)==0 && hs->hs_fn == fn){
|
||||
|
|
@ -404,15 +420,16 @@ struct handle_subscription *
|
|||
subscription_each(clicon_handle h,
|
||||
struct handle_subscription *hprev)
|
||||
{
|
||||
struct backend_handle *cb = handle(h);
|
||||
struct backend_handle *bh = handle(h);
|
||||
struct handle_subscription *hs = NULL;
|
||||
|
||||
if (hprev)
|
||||
hs = hprev->hs_next;
|
||||
else
|
||||
hs = cb->cb_subscription;
|
||||
hs = bh->bh_subscription;
|
||||
return hs;
|
||||
}
|
||||
|
||||
/* Database dependency description */
|
||||
struct backend_netconf_reg {
|
||||
qelem_t nr_qelem; /* List header */
|
||||
|
|
@ -456,10 +473,9 @@ catch:
|
|||
/*! See if there is any callback registered for this tag
|
||||
*
|
||||
* @param[in] h clicon handle
|
||||
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||
* @param[out] cb Output xml stream. For reply
|
||||
* @param[out] cb_err Error xml stream. For error reply
|
||||
* @param[out] xret Return XML, error or OK
|
||||
* @param[in] xe Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||
* @param[in] ce Client (session) entry
|
||||
* @param[out] cbret Return XML, error or OK as cbuf
|
||||
*
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK, not found handler.
|
||||
|
|
|
|||
|
|
@ -94,17 +94,19 @@ cli_notification_register(clicon_handle h,
|
|||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
char *logname;
|
||||
char *logname = NULL;
|
||||
void *p;
|
||||
int s;
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
size_t len;
|
||||
int s_exist = -1;
|
||||
|
||||
if ((logname = chunk_sprintf(__FUNCTION__, "log_socket_%s", stream)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "%s: chunk_sprintf", __FUNCTION__);
|
||||
len = strlen("log_socket_") + strlen(stream) + 1;
|
||||
if ((logname = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
snprintf(logname, len, "log_socket_%s", stream);
|
||||
if ((p = hash_value(cdat, logname, &len)) != NULL)
|
||||
s_exist = *(int*)p;
|
||||
|
||||
|
|
@ -132,7 +134,8 @@ cli_notification_register(clicon_handle h,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (logname)
|
||||
free(logname);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,12 +69,11 @@
|
|||
#define handle(h) (assert(clicon_handle_check(h)==0),(struct cli_handle *)(h))
|
||||
#define cligen(h) (handle(h)->cl_cligen)
|
||||
|
||||
/*
|
||||
* cli_handle
|
||||
* first part of this is header, same for clicon_handle and config_handle.
|
||||
* Access functions for common fields are found in clicon lib: clicon_options.[ch]
|
||||
/*! CLI specific handle added to header CLICON handle
|
||||
* This file should only contain access functions for the _specific_
|
||||
* entries in the struct below.
|
||||
* @note The top part must be equivalent to struct clicon_handle in clixon_handle.c
|
||||
* @see struct clicon_handle, struct backend_handle
|
||||
*/
|
||||
struct cli_handle {
|
||||
int cl_magic; /* magic (HDR)*/
|
||||
|
|
@ -112,21 +111,23 @@ cli_handle_init(void)
|
|||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
* cli_handle_exit
|
||||
* frees clicon handle
|
||||
/*! Free clicon handle
|
||||
*/
|
||||
int
|
||||
cli_handle_exit(clicon_handle h)
|
||||
{
|
||||
cligen_handle ch = cligen(h);
|
||||
struct cli_handle *cl = handle(h);
|
||||
|
||||
if (cl->cl_stx)
|
||||
free(cl->cl_stx);
|
||||
clicon_handle_exit(h); /* frees h and options */
|
||||
|
||||
cligen_exit(ch);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------
|
||||
* cli-specific handle access functions
|
||||
*----------------------------------------------------------*/
|
||||
|
|
|
|||
|
|
@ -174,7 +174,8 @@ main(int argc, char **argv)
|
|||
int printgen = 0;
|
||||
int logclisyntax = 0;
|
||||
int help = 0;
|
||||
char *treename;
|
||||
char *treename = NULL;
|
||||
int len;
|
||||
int logdst = CLICON_LOG_STDERR;
|
||||
char *restarg = NULL; /* what remains after options */
|
||||
|
||||
|
|
@ -343,8 +344,14 @@ main(int argc, char **argv)
|
|||
if (yang2cli(h, yspec, &pt, clicon_cli_genmodel_type(h)) < 0)
|
||||
goto done;
|
||||
|
||||
treename = chunk_sprintf(__FUNCTION__, "datamodel:%s", clicon_dbspec_name(h));
|
||||
len = strlen("datamodel:") + strlen(clicon_dbspec_name(h)) + 1;
|
||||
if ((treename = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
snprintf(treename, len, "datamodel:%s", clicon_dbspec_name(h));
|
||||
cli_tree_add(h, treename, pt);
|
||||
|
||||
if (printgen)
|
||||
cligen_print(stdout, pt, 1);
|
||||
}
|
||||
|
|
@ -400,9 +407,10 @@ main(int argc, char **argv)
|
|||
if (!once)
|
||||
cli_interactive(h);
|
||||
done:
|
||||
if (treename)
|
||||
free(treename);
|
||||
if (restarg)
|
||||
free(restarg);
|
||||
unchunk_group(__FUNCTION__);
|
||||
// Gets in your face if we log on stderr
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
||||
|
|
|
|||
|
|
@ -81,8 +81,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Find syntax mode named 'mode'. Create if specified
|
||||
/*! Find syntax mode named 'mode'. Create if specified
|
||||
*/
|
||||
static cli_syntaxmode_t *
|
||||
syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
|
||||
|
|
@ -101,8 +100,8 @@ syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
|
|||
if (create == 0)
|
||||
return NULL;
|
||||
|
||||
if ((m = chunk(sizeof(cli_syntaxmode_t), stx->stx_cnklbl)) == NULL) {
|
||||
perror("chunk");
|
||||
if ((m = malloc(sizeof(cli_syntaxmode_t))) == NULL) {
|
||||
perror("malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset (m, 0, sizeof (*m));
|
||||
|
|
@ -163,40 +162,16 @@ syntax_append(clicon_handle h,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unload a plugin
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h, void *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfun;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfun = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfun(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
cli_output (stderr, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unload all plugins in a group
|
||||
*/
|
||||
static int
|
||||
syntax_unload(clicon_handle h)
|
||||
cli_syntax_unload(clicon_handle h)
|
||||
{
|
||||
struct cli_plugin *p;
|
||||
cli_syntax_t *stx = cli_syntax(h);
|
||||
|
||||
cli_syntax_t *stx = cli_syntax(h);
|
||||
struct cli_plugin *p;
|
||||
cli_syntaxmode_t *m;
|
||||
|
||||
if (stx == NULL)
|
||||
return 0;
|
||||
|
||||
|
|
@ -204,15 +179,18 @@ syntax_unload(clicon_handle h)
|
|||
p = stx->stx_plugins;
|
||||
plugin_unload(h, p->cp_handle);
|
||||
clicon_debug(1, "DEBUG: Plugin '%s' unloaded.", p->cp_name);
|
||||
DELQ(stx->stx_plugins, stx->stx_plugins, struct cli_plugin *);
|
||||
DELQ(p, stx->stx_plugins, struct cli_plugin *);
|
||||
if (p)
|
||||
free(p);
|
||||
stx->stx_nplugins--;
|
||||
}
|
||||
while (stx->stx_nmodes > 0) {
|
||||
DELQ(stx->stx_modes, stx->stx_modes, cli_syntaxmode_t *);
|
||||
m = stx->stx_modes;
|
||||
DELQ(m, stx->stx_modes, cli_syntaxmode_t *);
|
||||
if (m)
|
||||
free(m);
|
||||
stx->stx_nmodes--;
|
||||
}
|
||||
|
||||
unchunk_group(stx->stx_cnklbl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -265,48 +243,31 @@ clixon_str2fn(char *name,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a dynamic plugin object and call it's init-function
|
||||
/*! Load a dynamic plugin object and call it's init-function
|
||||
* Note 'file' may be destructively modified
|
||||
* @retval plugin-handle should be freed after use
|
||||
*/
|
||||
static plghndl_t
|
||||
cli_plugin_load (clicon_handle h, char *file, int dlflags, const char *cnklbl)
|
||||
cli_plugin_load(clicon_handle h,
|
||||
char *file,
|
||||
int dlflags)
|
||||
{
|
||||
char *error;
|
||||
char *name;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfun;
|
||||
plghndl_t handle = NULL;
|
||||
struct cli_plugin *cp = NULL;
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
cli_output (stderr, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
if ((handle = plugin_load(h, file, dlflags)) == NULL)
|
||||
goto quit;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfun = dlsym(handle, PLUGIN_INIT)) != NULL) {
|
||||
if (initfun(h) != 0) {
|
||||
cli_output (stderr, "Failed to initiate %s\n", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
goto quit;
|
||||
}
|
||||
}
|
||||
|
||||
if ((cp = chunk(sizeof (struct cli_plugin), cnklbl)) == NULL) {
|
||||
perror("chunk");
|
||||
if ((cp = malloc(sizeof (struct cli_plugin))) == NULL) {
|
||||
perror("malloc");
|
||||
goto quit;
|
||||
}
|
||||
memset (cp, 0, sizeof(*cp));
|
||||
|
||||
name = basename(file);
|
||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%.*s", (int)strlen(name)-3, name);
|
||||
cp->cp_handle = handle;
|
||||
|
||||
quit:
|
||||
if (cp == NULL) {
|
||||
if (handle)
|
||||
dlclose(handle);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +284,7 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
|||
parse_tree pt = {0,};
|
||||
int retval = -1;
|
||||
FILE *f;
|
||||
char *filepath;
|
||||
char filepath[MAXPATHLEN];
|
||||
cvec *vr = NULL;
|
||||
char *prompt = NULL;
|
||||
char **vec = NULL;
|
||||
|
|
@ -331,12 +292,7 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
|||
char *plgnam;
|
||||
struct cli_plugin *p;
|
||||
|
||||
if ((filepath = chunk_sprintf(__FUNCTION__, "%s/%s",
|
||||
clispec_dir,
|
||||
filename)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "chunk");
|
||||
goto done;
|
||||
}
|
||||
snprintf(filepath, MAXPATHLEN-1, "%s/%s", clispec_dir, filename);
|
||||
if ((vr = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cvec_new");
|
||||
goto done;
|
||||
|
|
@ -403,7 +359,6 @@ done:
|
|||
cvec_free(vr);
|
||||
if (vec)
|
||||
free(vec);
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -415,10 +370,10 @@ cli_plugin_load_dir(clicon_handle h, char *dir, cli_syntax_t *stx)
|
|||
{
|
||||
int i;
|
||||
int ndp;
|
||||
struct dirent *dp;
|
||||
char *file;
|
||||
struct dirent *dp = NULL;
|
||||
char *master_plugin;
|
||||
char *master;
|
||||
char master[MAXPATHLEN];
|
||||
char filename[MAXPATHLEN];
|
||||
struct cli_plugin *cp;
|
||||
struct stat st;
|
||||
int retval = -1;
|
||||
|
|
@ -429,24 +384,18 @@ cli_plugin_load_dir(clicon_handle h, char *dir, cli_syntax_t *stx)
|
|||
clicon_err(OE_PLUGIN, 0, "clicon_master_plugin option not set");
|
||||
goto quit;
|
||||
}
|
||||
if ((master = chunk_sprintf(__FUNCTION__, "%s.so", master_plugin)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "chunk_sprintf master plugin");
|
||||
goto quit;
|
||||
}
|
||||
snprintf(master, MAXPATHLEN-1, "%s.so", master_plugin);
|
||||
|
||||
/* Get plugin objects names from plugin directory */
|
||||
ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__);
|
||||
ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG);
|
||||
if (ndp < 0)
|
||||
goto quit;
|
||||
|
||||
/* Load master plugin first */
|
||||
file = chunk_sprintf(__FUNCTION__, "%s/%s", dir, master);
|
||||
if (file == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk_sprintf dir");
|
||||
goto quit;
|
||||
}
|
||||
if (stat(file, &st) == 0) {
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, master);
|
||||
if (stat(filename, &st) == 0) {
|
||||
clicon_debug(1, "DEBUG: Loading master plugin '%s'", master);
|
||||
cp = cli_plugin_load(h, file, RTLD_NOW|RTLD_GLOBAL, stx->stx_cnklbl);
|
||||
cp = cli_plugin_load(h, filename, RTLD_NOW|RTLD_GLOBAL);
|
||||
if (cp == NULL)
|
||||
goto quit;
|
||||
/* Look up certain call-backs in master plugin */
|
||||
|
|
@ -459,33 +408,25 @@ cli_plugin_load_dir(clicon_handle h, char *dir, cli_syntax_t *stx)
|
|||
INSQ(cp, stx->stx_plugins);
|
||||
stx->stx_nplugins++;
|
||||
}
|
||||
unchunk (file);
|
||||
|
||||
/* Load the rest */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
if (strcmp (dp[i].d_name, master) == 0)
|
||||
continue; /* Skip master now */
|
||||
file = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
||||
if (file == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk_sprintf dir");
|
||||
goto quit;
|
||||
}
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "DEBUG: Loading plugin '%s'", dp[i].d_name);
|
||||
|
||||
if ((cp = cli_plugin_load (h, file, RTLD_NOW, stx->stx_cnklbl)) == NULL)
|
||||
if ((cp = cli_plugin_load (h, filename, RTLD_NOW)) == NULL)
|
||||
goto quit;
|
||||
INSQ(cp, stx->stx_plugins);
|
||||
stx->stx_nplugins++;
|
||||
unchunk (file);
|
||||
}
|
||||
if (dp)
|
||||
unchunk(dp);
|
||||
|
||||
retval = 0;
|
||||
|
||||
quit:
|
||||
unchunk_group(__FUNCTION__);
|
||||
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -501,8 +442,7 @@ cli_syntax_load (clicon_handle h)
|
|||
char *clispec_dir = NULL;
|
||||
int ndp;
|
||||
int i;
|
||||
char *cnklbl = "__CLICON_CLI_SYNTAX_CNK_LABEL__";
|
||||
struct dirent *dp;
|
||||
struct dirent *dp = NULL;
|
||||
cli_syntax_t *stx;
|
||||
cli_syntaxmode_t *m;
|
||||
|
||||
|
|
@ -521,13 +461,11 @@ cli_syntax_load (clicon_handle h)
|
|||
}
|
||||
|
||||
/* Allocate plugin group object */
|
||||
if ((stx = chunk(sizeof(*stx), cnklbl)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
if ((stx = malloc(sizeof(*stx))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto quit;
|
||||
}
|
||||
memset (stx, 0, sizeof (*stx)); /* Zero out all */
|
||||
/* populate name and chunk label */
|
||||
strncpy (stx->stx_cnklbl, cnklbl, sizeof(stx->stx_cnklbl)-1);
|
||||
|
||||
cli_syntax_set(h, stx);
|
||||
|
||||
|
|
@ -541,7 +479,7 @@ cli_syntax_load (clicon_handle h)
|
|||
goto quit;
|
||||
|
||||
/* load syntaxfiles */
|
||||
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG, __FUNCTION__)) < 0)
|
||||
if ((ndp = clicon_file_dirent(clispec_dir, &dp, "(.cli)$", S_IFREG)) < 0)
|
||||
goto quit;
|
||||
/* Load the rest */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
|
|
@ -550,9 +488,6 @@ cli_syntax_load (clicon_handle h)
|
|||
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
|
||||
goto quit;
|
||||
}
|
||||
if (dp)
|
||||
unchunk(dp);
|
||||
|
||||
|
||||
/* Did we successfully load any syntax modes? */
|
||||
if (stx->stx_nmodes <= 0) {
|
||||
|
|
@ -576,11 +511,11 @@ cli_syntax_load (clicon_handle h)
|
|||
|
||||
quit:
|
||||
if (retval != 0) {
|
||||
syntax_unload(h);
|
||||
unchunk_group(cnklbl);
|
||||
cli_syntax_unload(h);
|
||||
cli_syntax_set(h, NULL);
|
||||
}
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -614,7 +549,7 @@ cli_plugin_start(clicon_handle h, int argc, char **argv)
|
|||
int
|
||||
cli_plugin_finish(clicon_handle h)
|
||||
{
|
||||
syntax_unload(h);
|
||||
cli_syntax_unload(h);
|
||||
cli_syntax_set(h, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -663,39 +598,36 @@ clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* clicon_parse
|
||||
* Given a command string, parse and evaluate the string according to
|
||||
/*! Given a command string, parse and evaluate.
|
||||
* Parse and evaluate the string according to
|
||||
* the syntax parse tree of the syntax mode specified by *mode.
|
||||
* If there is no match in the tree for the command, the parse hook
|
||||
* will be called to see if another mode should be evaluated. If a
|
||||
* match is found in another mode, the mode variable is updated to point at
|
||||
* the new mode string.
|
||||
*
|
||||
* INPUT:
|
||||
* cmd The command string
|
||||
* match_obj Pointer to CLIgen match object
|
||||
* mode A pointer to the mode string pointer
|
||||
* OUTPUT:
|
||||
* kr Keyword vector
|
||||
* vr Variable vector
|
||||
* RETURNS:
|
||||
* -2 : on eof (shouldnt happen)
|
||||
* -1 : In parse error
|
||||
* >=0 : Number of matches
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] cmd The command string
|
||||
* @param[in,out] mode A pointer to the mode string pointer
|
||||
* @param[out] result -2 On eof (shouldnt happen)
|
||||
* -1 On parse error
|
||||
* >=0 Number of matches
|
||||
*/
|
||||
int
|
||||
clicon_parse(clicon_handle h, char *cmd, char **mode, int *result)
|
||||
clicon_parse(clicon_handle h,
|
||||
char *cmd,
|
||||
char **mode,
|
||||
int *result)
|
||||
{
|
||||
char *m, *msav;
|
||||
int res = -1;
|
||||
int r;
|
||||
cli_syntax_t *stx;
|
||||
char *m, *msav;
|
||||
int res = -1;
|
||||
int r;
|
||||
cli_syntax_t *stx = NULL;
|
||||
cli_syntaxmode_t *smode;
|
||||
char *treename;
|
||||
parse_tree *pt; /* Orig */
|
||||
cg_obj *match_obj;
|
||||
cvec *vr = NULL;
|
||||
cvec *cvv = NULL;
|
||||
|
||||
stx = cli_syntax(h);
|
||||
m = *mode;
|
||||
|
|
@ -719,11 +651,11 @@ clicon_parse(clicon_handle h, char *cmd, char **mode, int *result)
|
|||
fprintf(stderr, "No such parse-tree registered: %s\n", treename);
|
||||
goto done;;
|
||||
}
|
||||
if ((vr = cvec_new(0)) == NULL){
|
||||
fprintf(stderr, "%s: cvec_new: %s\n", __FUNCTION__, strerror(errno));
|
||||
if ((cvv = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;;
|
||||
}
|
||||
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, vr);
|
||||
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
|
||||
if (res != CG_MATCH)
|
||||
pt_expand_cleanup_1(pt);
|
||||
if (msav){
|
||||
|
|
@ -755,7 +687,7 @@ clicon_parse(clicon_handle h, char *cmd, char **mode, int *result)
|
|||
*mode = m;
|
||||
cli_set_syntax_mode(h, m);
|
||||
}
|
||||
if ((r = clicon_eval(h, cmd, match_obj, vr)) < 0)
|
||||
if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0)
|
||||
cli_handler_err(stdout);
|
||||
pt_expand_cleanup_1(pt);
|
||||
if (result)
|
||||
|
|
@ -769,8 +701,8 @@ clicon_parse(clicon_handle h, char *cmd, char **mode, int *result)
|
|||
}
|
||||
}
|
||||
done:
|
||||
if (vr)
|
||||
cvec_free(vr);
|
||||
if (cvv)
|
||||
cvec_free(cvv);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -891,9 +823,7 @@ cli_set_prompt(clicon_handle h, const char *name, const char *prompt)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Format prompt
|
||||
* XXX: HOST_NAME_MAX from sysconf()
|
||||
/*! Format prompt
|
||||
*/
|
||||
static int
|
||||
prompt_fmt (char *prompt, size_t plen, char *fmt, ...)
|
||||
|
|
@ -902,65 +832,56 @@ prompt_fmt (char *prompt, size_t plen, char *fmt, ...)
|
|||
char *s = fmt;
|
||||
char hname[1024];
|
||||
char tty[32];
|
||||
char *new;
|
||||
char *tmp;
|
||||
int ret = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Start with empty string */
|
||||
if((new = chunk_sprintf(__FUNCTION__, "%s", ""))==NULL)
|
||||
goto done;
|
||||
|
||||
cprintf(cb, "");
|
||||
while(*s) {
|
||||
if (*s == '%' && *++s) {
|
||||
switch(*s) {
|
||||
|
||||
case 'H': /* Hostname */
|
||||
if (gethostname (hname, sizeof (hname)) != 0)
|
||||
strncpy(hname, "unknown", sizeof(hname)-1);
|
||||
if((new = chunk_strncat(new, hname, 0, __FUNCTION__))==NULL)
|
||||
goto done;
|
||||
cprintf(cb, "%s", hname);
|
||||
break;
|
||||
|
||||
case 'U': /* Username */
|
||||
tmp = getenv("USER");
|
||||
if((new = chunk_strncat(new, (tmp ? tmp : "nobody"), 0, __FUNCTION__))==NULL)
|
||||
goto done;
|
||||
cprintf(cb, "%s", tmp?tmp:"nobody");
|
||||
break;
|
||||
|
||||
case 'T': /* TTY */
|
||||
if(ttyname_r(fileno(stdin), tty, sizeof(tty)-1) < 0)
|
||||
strcpy(tty, "notty");
|
||||
if((new = chunk_strncat(new, tty, strlen(tty), __FUNCTION__))==NULL)
|
||||
goto done;
|
||||
cprintf(cb, "%s", tty);
|
||||
break;
|
||||
|
||||
default:
|
||||
if((new = chunk_strncat(new, "%", 1, __FUNCTION__))==NULL ||
|
||||
(new = chunk_strncat(new, s, 1, __FUNCTION__)))
|
||||
goto done;
|
||||
cprintf(cb, "%%");
|
||||
cprintf(cb, "%c", *s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((new = chunk_strncat(new, s, 1, __FUNCTION__))==NULL)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
cprintf(cb, "%c", *s);
|
||||
s++;
|
||||
}
|
||||
|
||||
done:
|
||||
if (new)
|
||||
fmt = new;
|
||||
if (cb)
|
||||
fmt = cbuf_get(cb);
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(prompt, plen, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
unchunk_group(__FUNCTION__);
|
||||
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a formatted prompt string
|
||||
/*! Return a formatted prompt string
|
||||
*/
|
||||
char *
|
||||
cli_prompt(char *fmt)
|
||||
|
|
@ -1069,11 +990,10 @@ cli_ptpop(clicon_handle h, char *mode, char *op)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* clicon_valcb
|
||||
/*! Find a cli plugin based on name and resolve a function pointer in it.
|
||||
* Callback from clicon_dbvars_parse()
|
||||
* Find a cli plugin based on name if given and
|
||||
* use dlsym to resolve a function pointer in it.
|
||||
* Find a cli plugin based on name if given and use dlsym to resolve a
|
||||
* function pointer in it.
|
||||
* Call the resolved function to get the cgv populated
|
||||
*/
|
||||
int
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ struct cli_plugin {
|
|||
|
||||
/* Plugin group object */
|
||||
typedef struct {
|
||||
char stx_cnklbl[128]; /* Plugin group name */
|
||||
int stx_nplugins; /* Number of plugins */
|
||||
struct cli_plugin *stx_plugins; /* List of plugins */
|
||||
int stx_nmodes; /* Number of syntax modes */
|
||||
|
|
|
|||
|
|
@ -213,15 +213,15 @@ expand_dir(char *dir,
|
|||
mode_t flags,
|
||||
int detail)
|
||||
{
|
||||
DIR *dirp;
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
struct stat st;
|
||||
char *str;
|
||||
char *cmd;
|
||||
int len;
|
||||
int retval = -1;
|
||||
struct stat st;
|
||||
char *str;
|
||||
char *cmd;
|
||||
int len;
|
||||
int retval = -1;
|
||||
struct passwd *pw;
|
||||
char filename[MAXPATHLEN];
|
||||
char filename[MAXPATHLEN];
|
||||
|
||||
if ((dirp = opendir(dir)) == 0){
|
||||
fprintf(stderr, "expand_dir: opendir(%s) %s\n",
|
||||
|
|
|
|||
|
|
@ -1,241 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define DBCTRL_OPTS "hDSd:pbn:r:m:Zi"
|
||||
|
||||
/*
|
||||
* remove_entry
|
||||
*/
|
||||
static int
|
||||
remove_entry(char *dbname, char *key)
|
||||
{
|
||||
#ifdef NOTYET /* This assumes direct access to database */
|
||||
return db_del(dbname, key);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! usage
|
||||
*/
|
||||
static void
|
||||
usage(char *argv0)
|
||||
{
|
||||
fprintf(stderr, "usage:%s\n"
|
||||
"where options are\n"
|
||||
"\t-h\t\tHelp\n"
|
||||
"\t-D\t\tDebug\n"
|
||||
"\t-S\t\tLog on syslog\n"
|
||||
"\t-d <db>\t\tDatabase name (default: running)\n"
|
||||
"\t-p\t\tDump database on stdout\n"
|
||||
"\t-b\t\tBrief output, just print keys. Combine with -p or -m\n"
|
||||
"\t-n \"<key> <val>\" Add database entry\n"
|
||||
"\t-r <key>\tRemove database entry\n"
|
||||
"\t-m <regexp key>\tMatch regexp key in database\n"
|
||||
"\t-Z\t\tDelete database\n"
|
||||
"\t-i\t\tInit database\n",
|
||||
argv0
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
int zapdb;
|
||||
int initdb;
|
||||
int dumpdb;
|
||||
int addent;
|
||||
int rment;
|
||||
char *matchkey = NULL;
|
||||
char *addstr;
|
||||
char rmkey[MAXPATHLEN];
|
||||
int brief;
|
||||
char db[MAXPATHLEN] = {0,};
|
||||
int use_syslog;
|
||||
clicon_handle h;
|
||||
|
||||
/* In the startup, logs to stderr & debug flag set later */
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
|
||||
|
||||
/* Defaults */
|
||||
zapdb = 0;
|
||||
initdb = 0;
|
||||
dumpdb = 0;
|
||||
addent = 0;
|
||||
rment = 0;
|
||||
brief = 0;
|
||||
use_syslog = 0;
|
||||
addstr = NULL;
|
||||
memcpy(db, "running", strlen("running")+1);
|
||||
memset(rmkey, '\0', sizeof(rmkey));
|
||||
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
goto done;
|
||||
/* getopt in two steps, first find config-file before over-riding options. */
|
||||
while ((c = getopt(argc, argv, DBCTRL_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case '?' :
|
||||
case 'h' : /* help */
|
||||
usage(argv[0]);
|
||||
break;
|
||||
case 'D' : /* debug */
|
||||
debug = 1;
|
||||
break;
|
||||
case 'S': /* Log on syslog */
|
||||
use_syslog = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Logs, error and debug to stderr or syslog, set debug level
|
||||
*/
|
||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO,
|
||||
use_syslog?CLICON_LOG_SYSLOG:CLICON_LOG_STDERR);
|
||||
clicon_debug_init(debug, NULL);
|
||||
|
||||
|
||||
/* Now rest of options */
|
||||
optind = 1;
|
||||
while ((c = getopt(argc, argv, DBCTRL_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case 'Z': /* Zap database */
|
||||
zapdb++;
|
||||
break;
|
||||
case 'i': /* Init database */
|
||||
initdb++;
|
||||
break;
|
||||
case 'p': /* Dump/print database */
|
||||
dumpdb++;
|
||||
break;
|
||||
case 'b': /* Dump/print/match database brief (combone w -p or -m) */
|
||||
brief++;
|
||||
break;
|
||||
case 'd': /* db either db filename or symbolic: running|candidate */
|
||||
if (!optarg || sscanf(optarg, "%s", db) != 1)
|
||||
usage(argv[0]);
|
||||
break;
|
||||
case 'n': /* add database entry */
|
||||
if (!optarg || !strlen(optarg) || (addstr = strdup(optarg)) == NULL)
|
||||
usage(argv[0]);
|
||||
/* XXX addign both key and value, for now only key */
|
||||
addent++;
|
||||
break;
|
||||
case 'r':
|
||||
if (!optarg || sscanf(optarg, "%s", rmkey) != 1)
|
||||
usage(argv[0]);
|
||||
rment++;
|
||||
break;
|
||||
case 'm':
|
||||
if (!optarg || !strlen(optarg) || (matchkey = strdup(optarg)) == NULL)
|
||||
usage(argv[0]);
|
||||
dumpdb++;
|
||||
break;
|
||||
case 'D': /* Processed earlier, ignore now. */
|
||||
case 'S':
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
break;
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (*db == '\0'){
|
||||
clicon_err(OE_FATAL, 0, "database not specified (with -d <db>): %s");
|
||||
goto done;
|
||||
}
|
||||
if (dumpdb){
|
||||
/* Here db must be local file-path */
|
||||
if (xmldb_dump(stdout, db, matchkey)) {
|
||||
fprintf(stderr, "Match error\n");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (addent){ /* add entry */
|
||||
cxobj *xml = NULL;
|
||||
|
||||
if (clicon_xml_parse(&xml, "<config>%s</config>", addstr) < 0)
|
||||
goto done;
|
||||
if (xmldb_put(h, db, OP_REPLACE, NULL, xml) < 0)
|
||||
goto done;
|
||||
if (xml)
|
||||
xml_free(xml);
|
||||
|
||||
}
|
||||
if (rment)
|
||||
if (remove_entry(db, rmkey) < 0)
|
||||
goto done;
|
||||
if (zapdb) /* remove databases */
|
||||
if (xmldb_delete(h, db) < 0){
|
||||
clicon_err(OE_FATAL, errno, "delete %s", db);
|
||||
goto done;
|
||||
}
|
||||
if (initdb)
|
||||
if (xmldb_init(h, db) < 0)
|
||||
goto done;
|
||||
done:
|
||||
return 0;
|
||||
}
|
||||
16
apps/netconf/README.md
Normal file
16
apps/netconf/README.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Clixon Netconf
|
||||
|
||||
Clixon Netconf implements the following NETCONF proposals or standards:
|
||||
- [NETCONF Configuration Protocol](http://www.rfc-base.org/txt/rfc-4741.txt)
|
||||
- [Using the NETCONF Configuration Protocol over Secure SHell (SSH)](http://www.rfc-base.org/txt/rfc-4742.txt)
|
||||
- [NETCONF Event Notifications](http://www.rfc-base.org/txt/rfc-5277.txt)
|
||||
|
||||
However, it needs to be updated to RFC 6241 and RFC 6242.
|
||||
|
||||
Clixon NETCONF currently does not support the following features:
|
||||
|
||||
- :url capability
|
||||
- copy-config source config
|
||||
- edit-config testopts
|
||||
- edit-config erropts
|
||||
- edit-config config-text
|
||||
|
|
@ -75,60 +75,6 @@ struct netconf_reg {
|
|||
};
|
||||
typedef struct netconf_reg netconf_reg_t;
|
||||
|
||||
/*! Unload a plugin
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h, void *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfn;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfn = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfn(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Load a dynamic plugin object and call it's init-function
|
||||
* Note 'file' may be destructively modified
|
||||
*/
|
||||
static plghndl_t
|
||||
plugin_load (clicon_handle h, char *file, int dlflags, const char *cnklbl)
|
||||
{
|
||||
char *error;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfn;
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
goto quit;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfn = dlsym(handle, PLUGIN_INIT)) != NULL) {
|
||||
if (initfn(h) != 0) {
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s\n", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
goto quit;
|
||||
}
|
||||
}
|
||||
quit:
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static int nplugins = 0;
|
||||
static plghndl_t *plugins = NULL;
|
||||
static netconf_reg_t *deps = NULL;
|
||||
|
|
@ -141,9 +87,9 @@ netconf_plugin_load(clicon_handle h)
|
|||
int retval = -1;
|
||||
char *dir;
|
||||
int ndp;
|
||||
struct dirent *dp;
|
||||
struct dirent *dp = NULL;
|
||||
int i;
|
||||
char *filename;
|
||||
char filename[MAXPATHLEN];
|
||||
plghndl_t *handle;
|
||||
|
||||
if ((dir = clicon_netconf_dir(h)) == NULL){
|
||||
|
|
@ -152,30 +98,26 @@ netconf_plugin_load(clicon_handle h)
|
|||
}
|
||||
|
||||
/* Get plugin objects names from plugin directory */
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0)
|
||||
goto quit;
|
||||
|
||||
/* Load all plugins */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||
(int)strlen(filename), filename);
|
||||
if (filename == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
||||
goto quit;
|
||||
}
|
||||
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
|
||||
goto quit;
|
||||
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "realloc");
|
||||
goto quit;
|
||||
}
|
||||
plugins[nplugins++] = handle;
|
||||
unchunk (filename);
|
||||
}
|
||||
retval = 0;
|
||||
quit:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -194,8 +136,10 @@ netconf_plugin_unload(clicon_handle h)
|
|||
}
|
||||
for (i = 0; i < nplugins; i++)
|
||||
plugin_unload(h, plugins[i]);
|
||||
if (plugins)
|
||||
unchunk(plugins);
|
||||
if (plugins){
|
||||
free(plugins);
|
||||
plugins = NULL;
|
||||
}
|
||||
nplugins = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,20 @@
|
|||
Clixon Restconf
|
||||
===============
|
||||
# Clixon Restconf
|
||||
### Features
|
||||
|
||||
Contents:
|
||||
1. Features
|
||||
2. Installation using NGINX
|
||||
3. Debugging
|
||||
|
||||
1. FEATURES
|
||||
+++++++++++
|
||||
Clixon restconf is a daemon based on FASTCGI. Instructions are available to
|
||||
run with NGINX.
|
||||
The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE.
|
||||
and is based on draft-ietf-netconf-restconf-13.
|
||||
There is currently (2017) a RFC 8040, many of those features are _not_ implemented,
|
||||
There is currently (2017) a [RFC 8040: RESTCONF Protocol](https://tools.ietf.org/html/rfc8040), many of those features are _not_ implemented,
|
||||
including:
|
||||
- query parameters (section 4.9)
|
||||
- notifications (sec 6)
|
||||
- only rudimentary error reporting exists (sec 7)
|
||||
|
||||
2. INSTALLATION using NGINX
|
||||
+++++++++++++++++++++++++++
|
||||
### Installation using Nginx
|
||||
|
||||
# Define nginx config file/etc/nginx/sites-available/default
|
||||
Define nginx config file/etc/nginx/sites-available/default
|
||||
```
|
||||
server {
|
||||
...
|
||||
location /restconf {
|
||||
|
|
@ -30,13 +23,19 @@ server {
|
|||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
# Start nginx daemon
|
||||
```
|
||||
Start nginx daemon
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
# Make restconf calls with curl
|
||||
Make restconf calls with curl
|
||||
```
|
||||
olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces
|
||||
[
|
||||
{
|
||||
|
|
@ -62,16 +61,21 @@ olof@vandal> curl -G http://127.0.0.1/restconf/data/interfaces/interface/name=et
|
|||
]
|
||||
|
||||
curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
3. DEBUGGING
|
||||
++++++++++++
|
||||
Start the restconf programs with debug flag:
|
||||
sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data
|
||||
|
||||
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
|
||||
```
|
||||
Look at syslog:
|
||||
```
|
||||
tail -f /var/log/syslog | grep clixon_restconf
|
||||
```
|
||||
|
||||
Send command:
|
||||
```
|
||||
curl -G http://127.0.0.1/restconf/data/*
|
||||
|
||||
```
|
||||
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -46,9 +45,9 @@
|
|||
#include <fcgi_stdio.h>
|
||||
#include <signal.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -111,98 +110,6 @@ clicon_debug_xml(int dbglevel,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Split a string into a cligen variable vector using 1st and 2nd delimiter
|
||||
* Split a string first into elements delimited by delim1, then into
|
||||
* pairs delimited by delim2.
|
||||
* @param[in] string String to split
|
||||
* @param[in] delim1 First delimiter char that delimits between elements
|
||||
* @param[in] delim2 Second delimiter char for pairs within an element
|
||||
* @param[out] cvp Created cligen variable vector, NOTE: can be NULL
|
||||
* @retval 0 on OK
|
||||
* @retval -1 error
|
||||
*
|
||||
* Example, assuming delim1 = '&' and delim2 = '='
|
||||
* a=b&c=d -> [[a,"b"][c="d"]
|
||||
* kalle&c=d -> [[c="d"]] # Discard elements with no delim2
|
||||
* XXX differentiate between error and null cvec.
|
||||
*/
|
||||
int
|
||||
str2cvec(char *string,
|
||||
char delim1,
|
||||
char delim2,
|
||||
cvec **cvp)
|
||||
{
|
||||
int retval = -1;
|
||||
char *s;
|
||||
char *s0 = NULL;;
|
||||
char *val; /* value */
|
||||
char *valu; /* unescaped value */
|
||||
char *snext; /* next element in string */
|
||||
cvec *cvv = NULL;
|
||||
cg_var *cv;
|
||||
|
||||
clicon_debug(1, "%s %s", __FUNCTION__, string);
|
||||
if ((s0 = strdup(string)) == NULL){
|
||||
clicon_debug(1, "error strdup %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
s = s0;
|
||||
if ((cvv = cvec_new(0)) ==NULL){
|
||||
clicon_debug(1, "error cvec_new %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
while (s != NULL) {
|
||||
/*
|
||||
* In the pointer algorithm below:
|
||||
* name1=val1; name2=val2;
|
||||
* ^ ^ ^
|
||||
* | | |
|
||||
* s val snext
|
||||
*/
|
||||
if ((snext = index(s, delim1)) != NULL)
|
||||
*(snext++) = '\0';
|
||||
if ((val = index(s, delim2)) != NULL){
|
||||
*(val++) = '\0';
|
||||
if ((valu = curl_easy_unescape(NULL, val, 0, NULL)) == NULL){
|
||||
clicon_debug(1, "curl_easy_unescape %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
||||
clicon_debug(1, "error cvec_add %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
while ((strlen(s) > 0) && isblank(*s))
|
||||
s++;
|
||||
cv_name_set(cv, s);
|
||||
cv_string_set(cv, valu);
|
||||
free(valu);
|
||||
}
|
||||
else{
|
||||
if (strlen(s)){
|
||||
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
||||
clicon_debug(1, "error cvec_add %s", strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
cv_name_set(cv, s);
|
||||
cv_string_set(cv, "");
|
||||
}
|
||||
}
|
||||
s = snext;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
*cvp = cvv;
|
||||
if (s0)
|
||||
free(s0);
|
||||
return retval;
|
||||
err:
|
||||
if (cvv){
|
||||
cvec_free(cvv);
|
||||
cvv = NULL;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
|
|
@ -272,45 +179,6 @@ static int nplugins = 0;
|
|||
static plghndl_t *plugins = NULL;
|
||||
static credentials_t *p_credentials = NULL; /* Credentials callback */
|
||||
|
||||
/*! Load a dynamic plugin object and call it's init-function
|
||||
* Note 'file' may be destructively modified
|
||||
*/
|
||||
static plghndl_t
|
||||
plugin_load (clicon_handle h,
|
||||
char *file,
|
||||
int dlflags,
|
||||
const char *cnklbl)
|
||||
{
|
||||
char *error;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfn;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
goto done;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfn = dlsym(handle, PLUGIN_INIT)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to find plugin_init when loading restconf plugin %s", file);
|
||||
goto err;
|
||||
}
|
||||
if (initfn(h) != 0) {
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
goto err;
|
||||
}
|
||||
p_credentials = dlsym(handle, "restconf_credentials");
|
||||
done:
|
||||
return handle;
|
||||
err:
|
||||
if (handle)
|
||||
dlclose(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*! Load all plugins you can find in CLICON_RESTCONF_DIR
|
||||
*/
|
||||
int
|
||||
|
|
@ -319,65 +187,40 @@ restconf_plugin_load(clicon_handle h)
|
|||
int retval = -1;
|
||||
char *dir;
|
||||
int ndp;
|
||||
struct dirent *dp;
|
||||
struct dirent *dp = NULL;
|
||||
int i;
|
||||
char *filename;
|
||||
plghndl_t *handle;
|
||||
char filename[MAXPATHLEN];
|
||||
|
||||
if ((dir = clicon_restconf_dir(h)) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "clicon_restconf_dir not defined");
|
||||
goto quit;
|
||||
}
|
||||
/* Get plugin objects names from plugin directory */
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0)
|
||||
goto quit;
|
||||
|
||||
/* Load all plugins */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||
(int)strlen(filename), filename);
|
||||
if (filename == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
||||
goto quit;
|
||||
}
|
||||
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
|
||||
goto quit;
|
||||
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
p_credentials = dlsym(handle, "restconf_credentials");
|
||||
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "realloc");
|
||||
goto quit;
|
||||
}
|
||||
plugins[nplugins++] = handle;
|
||||
unchunk (filename);
|
||||
}
|
||||
retval = 0;
|
||||
quit:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Unload a plugin
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h, void *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfn;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfn = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfn(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Unload all restconf plugins */
|
||||
int
|
||||
|
|
@ -387,8 +230,10 @@ restconf_plugin_unload(clicon_handle h)
|
|||
|
||||
for (i = 0; i < nplugins; i++)
|
||||
plugin_unload(h, plugins[i]);
|
||||
if (plugins)
|
||||
unchunk(plugins);
|
||||
if (plugins){
|
||||
free(plugins);
|
||||
plugins = NULL;
|
||||
}
|
||||
nplugins = 0;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@
|
|||
int notfound(FCGX_Request *r);
|
||||
int badrequest(FCGX_Request *r);
|
||||
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
|
||||
int str2cvec(char *string, char delim1, char delim2, cvec **cvp);
|
||||
int test(FCGX_Request *r, int dbg);
|
||||
cbuf *readdata(FCGX_Request *r);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@
|
|||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <curl/curl.h>
|
||||
#include <libgen.h>
|
||||
|
||||
/* cligen */
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ Mapping netconf error-tag -> status code
|
|||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -153,86 +152,19 @@ api_data_get_gen(clicon_handle h,
|
|||
int head)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv;
|
||||
char *val;
|
||||
char *v;
|
||||
int i;
|
||||
cbuf *path = NULL;
|
||||
cbuf *path1 = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj **vec = NULL;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *y = NULL;
|
||||
yang_stmt *ykey;
|
||||
char *name;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
cg_var *cvi;
|
||||
cxobj *xret = NULL;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
if ((path = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */
|
||||
goto done;
|
||||
cv = NULL;
|
||||
cprintf(path1, "/");
|
||||
/* translate eg a/b=c -> a/[b=c] */
|
||||
for (i=pi; i<cvec_len(pcvec); i++){
|
||||
cv = cvec_i(pcvec, i);
|
||||
name = cv_name_get(cv);
|
||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||
if (i == pi){
|
||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
assert(y!=NULL);
|
||||
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Check if has value, means '=' */
|
||||
if (cv2str(cv, NULL, 0) > 0){
|
||||
if ((val = cv2str_dup(cv)) == NULL)
|
||||
goto done;
|
||||
v = val;
|
||||
/* XXX sync with yang */
|
||||
while((v=index(v, ',')) != NULL){
|
||||
*v = '\0';
|
||||
v++;
|
||||
}
|
||||
/* Find keys */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
||||
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
cprintf(path, "/%s", name);
|
||||
v = val;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
cprintf(path, "[%s=%s]", cv_string_get(cvi), v);
|
||||
v += strlen(v)+1;
|
||||
}
|
||||
if (val)
|
||||
free(val);
|
||||
}
|
||||
else{
|
||||
cprintf(path, "%s%s", (i==pi?"":"/"), name);
|
||||
cprintf(path1, "/%s", name);
|
||||
}
|
||||
if (api_path2xpath_cvv(yspec, pcvec, pi, path) < 0){
|
||||
notfound(r);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
||||
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
|
||||
|
|
@ -268,8 +200,6 @@ api_data_get_gen(clicon_handle h,
|
|||
cbuf_free(cbx);
|
||||
if (path)
|
||||
cbuf_free(path);
|
||||
if (path1)
|
||||
cbuf_free(path1);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
|
|
@ -366,7 +296,7 @@ api_data_edit(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
cprintf(cbx, "</config>");
|
||||
clicon_debug(1, "%s cbx: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
|
||||
clicon_debug(1, "%s xml: %s api_path:%s",__FUNCTION__, cbuf_get(cbx), api_path);
|
||||
if (clicon_rpc_edit_config(h, "candidate",
|
||||
operation,
|
||||
api_path,
|
||||
|
|
@ -407,7 +337,8 @@ api_data_edit(clicon_handle h,
|
|||
|
||||
If the data resource already exists, then the POST request MUST fail
|
||||
and a "409 Conflict" status-line MUST be returned.
|
||||
* Netconf: <edit-config> (nc:operation="create") | invoke an RPC operation
|
||||
* Netconf: <edit-config> (nc:operation="create") | invoke an RPC operation * @example
|
||||
|
||||
*/
|
||||
int
|
||||
api_data_post(clicon_handle h,
|
||||
|
|
@ -429,8 +360,8 @@ api_data_post(clicon_handle h,
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* Example:
|
||||
curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||
* @example
|
||||
curl -X PUT -d '{"enabled":"false"}' http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||
*
|
||||
PUT:
|
||||
if the PUT request creates a new resource,
|
||||
|
|
|
|||
|
|
@ -73,10 +73,6 @@ CLICON_CLI_DIR libdir/APPNAME/cli
|
|||
# Location of frontend .cli cligen spec files
|
||||
CLICON_CLISPEC_DIR libdir/APPNAME/clispec
|
||||
|
||||
# Directory where to save configuration commit history (in XML). Snapshots
|
||||
# are saved chronologically
|
||||
CLICON_ARCHIVE_DIR localstatedir/APPNAME/archive
|
||||
|
||||
# Enabled uses "startup" configuration on boot
|
||||
CLICON_USE_STARTUP_CONFIG 0
|
||||
|
||||
|
|
@ -121,6 +117,9 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
|
|||
# Directory where "running", "candidate" and "startup" are placed
|
||||
CLICON_XMLDB_DIR localstatedir/APPNAME
|
||||
|
||||
# XMLDB datastore plugin filename (see datastore/ and clixon_xml_db.[ch])
|
||||
CLICON_XMLDB_PLUGIN libdir/xmldb/text.so
|
||||
|
||||
# Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored
|
||||
# CLICON_CLI_VARONLY 1
|
||||
|
||||
|
|
|
|||
239
configure
vendored
239
configure
vendored
|
|
@ -632,6 +632,8 @@ CPP
|
|||
OBJEXT
|
||||
EXEEXT
|
||||
ac_ct_CC
|
||||
with_keyvalue
|
||||
with_restconf
|
||||
RANLIB
|
||||
AR
|
||||
EXE_SUFFIX
|
||||
|
|
@ -702,8 +704,9 @@ ac_subst_files=''
|
|||
ac_user_opts='
|
||||
enable_option_checking
|
||||
with_cligen
|
||||
with_restconf
|
||||
with_keyvalue
|
||||
with_qdbm
|
||||
enable_keycontent
|
||||
'
|
||||
ac_precious_vars='build_alias
|
||||
host_alias
|
||||
|
|
@ -1324,17 +1327,13 @@ if test -n "$ac_init_help"; then
|
|||
|
||||
cat <<\_ACEOF
|
||||
|
||||
Optional Features:
|
||||
--disable-option-checking ignore unrecognized --enable/--with options
|
||||
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
|
||||
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
|
||||
--disable-keycontent Disable reverse lookup content keys
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-cligen=dir Use CLIGEN here
|
||||
--with-qdbm=dir Use QDBM here
|
||||
--without-restconf disable support for restconf
|
||||
--without-keyvalue disable support for key-value xmldb datastore
|
||||
--with-qdbm=dir Use QDBM here, if keyvalue
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
|
|
@ -2136,7 +2135,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
|||
: ${CFLAGS="-O2"}
|
||||
|
||||
CLIXON_VERSION_MAJOR="3"
|
||||
CLIXON_VERSION_MINOR="2"
|
||||
CLIXON_VERSION_MINOR="3"
|
||||
CLIXON_VERSION_PATCH="0"
|
||||
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
||||
# Fix to specific version (eg 3.5) or head (3)
|
||||
|
|
@ -2327,6 +2326,8 @@ test -n "$target_alias" &&
|
|||
|
||||
|
||||
|
||||
# If yes, compile apps/restconf
|
||||
# If yes, compile datastore/keyvalue
|
||||
|
||||
#
|
||||
ac_ext=c
|
||||
|
|
@ -3541,7 +3542,6 @@ if test "${with_cligen}"; then
|
|||
fi
|
||||
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
|
||||
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
|
||||
if ${ac_cv_path_GREP+:} false; then :
|
||||
|
|
@ -3866,21 +3866,92 @@ else
|
|||
fi
|
||||
|
||||
|
||||
# This is for qdbm
|
||||
# This is for restconf (and fastcgi)
|
||||
|
||||
# Check whether --with-restconf was given.
|
||||
if test "${with_restconf+set}" = set; then :
|
||||
withval=$with_restconf;
|
||||
else
|
||||
with_restconf=yes
|
||||
fi
|
||||
|
||||
if test "x${with_restconf}" == xyes; then
|
||||
# Lives in libfcgi-dev
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FCGX_Init in -lfcgi" >&5
|
||||
$as_echo_n "checking for FCGX_Init in -lfcgi... " >&6; }
|
||||
if ${ac_cv_lib_fcgi_FCGX_Init+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lfcgi $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char FCGX_Init ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return FCGX_Init ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_fcgi_FCGX_Init=yes
|
||||
else
|
||||
ac_cv_lib_fcgi_FCGX_Init=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fcgi_FCGX_Init" >&5
|
||||
$as_echo "$ac_cv_lib_fcgi_FCGX_Init" >&6; }
|
||||
if test "x$ac_cv_lib_fcgi_FCGX_Init" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_LIBFCGI 1
|
||||
_ACEOF
|
||||
|
||||
LIBS="-lfcgi $LIBS"
|
||||
|
||||
else
|
||||
as_fn_error $? "libfcgi-dev missing" "$LINENO" 5
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# This is for keyvalue datastore (and qdbm)
|
||||
|
||||
# Check whether --with-keyvalue was given.
|
||||
if test "${with_keyvalue+set}" = set; then :
|
||||
withval=$with_keyvalue;
|
||||
else
|
||||
with_keyvalue=yes
|
||||
fi
|
||||
|
||||
|
||||
if test "x${with_keyvalue}" == xyes; then
|
||||
echo "yes keyvalue"
|
||||
# This is for qdbm
|
||||
|
||||
# Check whether --with-qdbm was given.
|
||||
if test "${with_qdbm+set}" = set; then :
|
||||
withval=$with_qdbm;
|
||||
fi
|
||||
|
||||
if test "${with_qdbm}"; then
|
||||
echo "Using QDBM here: ${with_qdbm}"
|
||||
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
||||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||
fi
|
||||
|
||||
# Problem: depot.h may be in qdbm/depot.h.
|
||||
for ac_header in depot.h
|
||||
if test "${with_qdbm}"; then
|
||||
echo "Using QDBM here: ${with_qdbm}"
|
||||
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
||||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||
fi
|
||||
# Problem: depot.h may be in qdbm/depot.h.
|
||||
for ac_header in depot.h
|
||||
do :
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "depot.h" "ac_cv_header_depot_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_depot_h" = xyes; then :
|
||||
|
|
@ -3907,7 +3978,7 @@ fi
|
|||
|
||||
done
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dpopen in -lqdbm" >&5
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dpopen in -lqdbm" >&5
|
||||
$as_echo_n "checking for dpopen in -lqdbm... " >&6; }
|
||||
if ${ac_cv_lib_qdbm_dpopen+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
|
|
@ -3954,6 +4025,7 @@ else
|
|||
as_fn_error $? "libqdbm-dev required" "$LINENO" 5
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
|
||||
$as_echo_n "checking for crypt in -lcrypt... " >&6; }
|
||||
|
|
@ -4179,55 +4251,6 @@ _ACEOF
|
|||
fi
|
||||
|
||||
|
||||
# restconf uses libcurl (I think?)
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for curl_global_init in -lcurl" >&5
|
||||
$as_echo_n "checking for curl_global_init in -lcurl... " >&6; }
|
||||
if ${ac_cv_lib_curl_curl_global_init+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lcurl $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char curl_global_init ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return curl_global_init ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_curl_curl_global_init=yes
|
||||
else
|
||||
ac_cv_lib_curl_curl_global_init=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curl_curl_global_init" >&5
|
||||
$as_echo "$ac_cv_lib_curl_curl_global_init" >&6; }
|
||||
if test "x$ac_cv_lib_curl_curl_global_init" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_LIBCURL 1
|
||||
_ACEOF
|
||||
|
||||
LIBS="-lcurl $LIBS"
|
||||
|
||||
else
|
||||
as_fn_error $? "libcurl missing" "$LINENO" 5
|
||||
fi
|
||||
|
||||
|
||||
for ac_func in inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp
|
||||
do :
|
||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||
|
|
@ -4241,81 +4264,9 @@ fi
|
|||
done
|
||||
|
||||
|
||||
# Lives in libfcgi-dev
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for FCGX_Init in -lfcgi" >&5
|
||||
$as_echo_n "checking for FCGX_Init in -lfcgi... " >&6; }
|
||||
if ${ac_cv_lib_fcgi_FCGX_Init+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lfcgi $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char FCGX_Init ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return FCGX_Init ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_fcgi_FCGX_Init=yes
|
||||
else
|
||||
ac_cv_lib_fcgi_FCGX_Init=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fcgi_FCGX_Init" >&5
|
||||
$as_echo "$ac_cv_lib_fcgi_FCGX_Init" >&6; }
|
||||
if test "x$ac_cv_lib_fcgi_FCGX_Init" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_LIBFCGI 1
|
||||
_ACEOF
|
||||
|
||||
LIBS="-lfcgi $LIBS"
|
||||
|
||||
else
|
||||
as_fn_error $? "libfcgi-dev missing" "$LINENO" 5
|
||||
fi
|
||||
|
||||
|
||||
# Check if extra keys inserted for database lists containing content. Eg A.n.foo = 3
|
||||
# means A.3 $!a=foo exists
|
||||
|
||||
# Check whether --enable-keycontent was given.
|
||||
if test "${enable_keycontent+set}" = set; then :
|
||||
enableval=$enable_keycontent;
|
||||
if test "$enableval" = no; then
|
||||
ac_enable_keycontent=no
|
||||
else
|
||||
ac_enable_keycontent=yes
|
||||
fi
|
||||
|
||||
else
|
||||
ac_enable_keycontent=yes
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if test "$ac_enable_keycontent" = "yes"; then
|
||||
$as_echo "#define DB_KEYCONTENT 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/dbctrl/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile doc/Makefile"
|
||||
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile 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"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
|
|
@ -5017,7 +4968,6 @@ do
|
|||
"apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;;
|
||||
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
|
||||
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
|
||||
"apps/dbctrl/Makefile") CONFIG_FILES="$CONFIG_FILES apps/dbctrl/Makefile" ;;
|
||||
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
|
||||
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
|
||||
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
|
||||
|
|
@ -5030,6 +4980,9 @@ do
|
|||
"docker/backend/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/backend/Dockerfile" ;;
|
||||
"docker/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Makefile" ;;
|
||||
"docker/netconf/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Dockerfile" ;;
|
||||
"datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;;
|
||||
"datastore/keyvalue/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/keyvalue/Makefile" ;;
|
||||
"datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;;
|
||||
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
|
||||
|
||||
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
|
||||
|
|
|
|||
69
configure.ac
69
configure.ac
|
|
@ -42,7 +42,7 @@ AC_INIT(lib/clixon/clixon.h.in)
|
|||
: ${CFLAGS="-O2"}
|
||||
|
||||
CLIXON_VERSION_MAJOR="3"
|
||||
CLIXON_VERSION_MINOR="2"
|
||||
CLIXON_VERSION_MINOR="3"
|
||||
CLIXON_VERSION_PATCH="0"
|
||||
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
||||
# Fix to specific version (eg 3.5) or head (3)
|
||||
|
|
@ -77,6 +77,8 @@ AC_SUBST(SH_SUFFIX)
|
|||
AC_SUBST(EXE_SUFFIX)
|
||||
AC_SUBST(AR)
|
||||
AC_SUBST(RANLIB)
|
||||
AC_SUBST(with_restconf) # If yes, compile apps/restconf
|
||||
AC_SUBST(with_keyvalue) # If yes, compile datastore/keyvalue
|
||||
|
||||
#
|
||||
AC_PROG_CC()
|
||||
|
|
@ -119,22 +121,39 @@ if test "${with_cligen}"; then
|
|||
LDFLAGS="-L${with_cligen}/lib ${LDFLAGS}"
|
||||
fi
|
||||
|
||||
|
||||
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]))
|
||||
|
||||
# This is for qdbm
|
||||
AC_ARG_WITH(qdbm, [ --with-qdbm=dir Use QDBM here ] )
|
||||
if test "${with_qdbm}"; then
|
||||
echo "Using QDBM here: ${with_qdbm}"
|
||||
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
||||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||
# This is for restconf (and fastcgi)
|
||||
AC_ARG_WITH([restconf],
|
||||
[AS_HELP_STRING([--without-restconf],[disable support for restconf])],
|
||||
[],
|
||||
[with_restconf=yes])
|
||||
if test "x${with_restconf}" == xyes; then
|
||||
# Lives in libfcgi-dev
|
||||
AC_CHECK_LIB(fcgi, FCGX_Init,, AC_MSG_ERROR([libfcgi-dev missing]))
|
||||
fi
|
||||
|
||||
# Problem: depot.h may be in qdbm/depot.h.
|
||||
AC_CHECK_HEADERS(depot.h,,[AC_CHECK_HEADERS(qdbm/depot.h,,AC_MSG_ERROR(libqdbm-dev required))])
|
||||
AC_CHECK_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required))
|
||||
# This is for keyvalue datastore (and qdbm)
|
||||
AC_ARG_WITH([keyvalue],
|
||||
[AS_HELP_STRING([--without-keyvalue],[disable support for key-value xmldb datastore])],
|
||||
[],
|
||||
[with_keyvalue=yes])
|
||||
|
||||
if test "x${with_keyvalue}" == xyes; then
|
||||
echo "yes keyvalue"
|
||||
# This is for qdbm
|
||||
AC_ARG_WITH(qdbm, [ --with-qdbm=dir Use QDBM here, if keyvalue ] )
|
||||
if test "${with_qdbm}"; then
|
||||
echo "Using QDBM here: ${with_qdbm}"
|
||||
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
||||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||
fi
|
||||
# Problem: depot.h may be in qdbm/depot.h.
|
||||
AC_CHECK_HEADERS(depot.h,,[AC_CHECK_HEADERS(qdbm/depot.h,,AC_MSG_ERROR(libqdbm-dev required))])
|
||||
AC_CHECK_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required))
|
||||
fi
|
||||
|
||||
AC_CHECK_LIB(crypt, crypt)
|
||||
AC_CHECK_HEADERS(crypt.h)
|
||||
|
|
@ -151,32 +170,8 @@ AC_CHECK_LIB(socket, socket)
|
|||
AC_CHECK_LIB(nsl, xdr_char)
|
||||
AC_CHECK_LIB(dl, dlopen)
|
||||
|
||||
# restconf uses libcurl (I think?)
|
||||
AC_CHECK_LIB(curl, curl_global_init,, AC_MSG_ERROR([libcurl missing]))
|
||||
|
||||
AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp)
|
||||
|
||||
# Lives in libfcgi-dev
|
||||
AC_CHECK_LIB(fcgi, FCGX_Init,, AC_MSG_ERROR([libfcgi-dev missing]))
|
||||
|
||||
# Check if extra keys inserted for database lists containing content. Eg A.n.foo = 3
|
||||
# means A.3 $!a=foo exists
|
||||
|
||||
AC_ARG_ENABLE(keycontent, [ --disable-keycontent Disable reverse lookup content keys],[
|
||||
if test "$enableval" = no; then
|
||||
ac_enable_keycontent=no
|
||||
else
|
||||
ac_enable_keycontent=yes
|
||||
fi
|
||||
],[ ac_enable_keycontent=yes])
|
||||
|
||||
AH_TEMPLATE([DB_KEYCONTENT],
|
||||
[ Check if extra keys inserted for database lists containing content.
|
||||
Eg A.n.foo = 3 means A.3 $!a=foo exists])
|
||||
if test "$ac_enable_keycontent" = "yes"; then
|
||||
AC_DEFINE(DB_KEYCONTENT)
|
||||
fi
|
||||
|
||||
AH_BOTTOM([#include <clixon_custom.h>])
|
||||
|
||||
AC_OUTPUT(Makefile
|
||||
|
|
@ -188,7 +183,6 @@ AC_OUTPUT(Makefile
|
|||
apps/backend/Makefile
|
||||
apps/netconf/Makefile
|
||||
apps/restconf/Makefile
|
||||
apps/dbctrl/Makefile
|
||||
include/Makefile
|
||||
etc/Makefile
|
||||
etc/clixonrc
|
||||
|
|
@ -201,6 +195,9 @@ AC_OUTPUT(Makefile
|
|||
docker/backend/Dockerfile
|
||||
docker/netconf/Makefile
|
||||
docker/netconf/Dockerfile
|
||||
datastore/Makefile
|
||||
datastore/keyvalue/Makefile
|
||||
datastore/text/Makefile
|
||||
doc/Makefile
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,18 +31,24 @@
|
|||
# ***** END LICENSE BLOCK *****
|
||||
#
|
||||
VPATH = @srcdir@
|
||||
prefix = @prefix@
|
||||
datarootdir = @datarootdir@
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
dbdir = @prefix@/db
|
||||
mandir = @mandir@
|
||||
libexecdir = @libexecdir@
|
||||
localstatedir = @localstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
with_restconf = @with_restconf@
|
||||
with_keyvalue = @with_keyvalue@
|
||||
|
||||
SH_SUFFIX = @SH_SUFFIX@
|
||||
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
|
||||
|
|
@ -60,34 +66,21 @@ CPPFLAGS = @CPPFLAGS@
|
|||
|
||||
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
||||
SRC =
|
||||
|
||||
OBJS = $(SRC:.c=.o)
|
||||
SUBDIRS = text
|
||||
ifeq ($(with_keyvalue),yes)
|
||||
SUBDIRS += keyvalue
|
||||
endif
|
||||
|
||||
APPSRC = dbctrl_main.c
|
||||
.PHONY: all clean depend install $(SUBDIRS)
|
||||
|
||||
APPSRC = datastore_client.c
|
||||
APPOBJ = $(APPSRC:.c=.o)
|
||||
APPL = clixon_dbctrl
|
||||
APPL = datastore_client
|
||||
|
||||
all: $(APPL)
|
||||
all: $(SUBDIRS) $(APPL)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) *.core $(APPL) $(APPOBJ)
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile *~ .depend
|
||||
|
||||
# Put demon in bin
|
||||
# Put other executables in libexec/
|
||||
# Also create a libexec/ directory for writeable/temporary files.
|
||||
# Put config file in etc/
|
||||
install: $(APPL)
|
||||
install -d $(DESTDIR)$(bindir)
|
||||
install $(APPL) $(DESTDIR)$(bindir)
|
||||
|
||||
install-include:
|
||||
|
||||
uninstall:
|
||||
rm -f $(bindir)/$(APPL)
|
||||
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
|
@ -95,14 +88,41 @@ uninstall:
|
|||
.c.o:
|
||||
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
|
||||
$(APPL) : $(APPOBJ) $(OBJS) $(LIBDEPS)
|
||||
$(CC) $(LDFLAGS) $(APPOBJ) $(OBJS) $(LIBS) -o $@
|
||||
$(APPL) : $(APPOBJ) $(LIBDEPS)
|
||||
$(CC) $(LDFLAGS) $(APPOBJ) $(LIBS) -o $@
|
||||
|
||||
TAGS:
|
||||
find . -name '*.[chyl]' -print | etags -
|
||||
|
||||
depend:
|
||||
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) $(APPSRC) > .depend
|
||||
for i in $(SUBDIRS); \
|
||||
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
|
||||
|
||||
#include .depend
|
||||
$(SUBDIRS):
|
||||
(cd $@; $(MAKE) $(MFLAGS) all)
|
||||
|
||||
install-include:
|
||||
for i in $(SUBDIRS); \
|
||||
do (cd $$i ; $(MAKE) $(MFLAGS) $@); done;
|
||||
|
||||
install: $(APPL)
|
||||
install -d $(DESTDIR)$(bindir)
|
||||
install $(APPL) $(DESTDIR)$(bindir)
|
||||
for i in $(SUBDIRS); \
|
||||
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
|
||||
|
||||
uninstall:
|
||||
rm -f $(bindir)/$(APPL)
|
||||
for i in $(SUBDIRS); \
|
||||
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
|
||||
|
||||
clean:
|
||||
rm -f *.core $(APPL) $(APPOBJ)
|
||||
for i in $(SUBDIRS); \
|
||||
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile *~ .depend
|
||||
for i in $(SUBDIRS); \
|
||||
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
|
||||
|
||||
tags:
|
||||
find $(srcdir) -name '*.[chyl]' -print | etags -
|
||||
94
datastore/README.md
Normal file
94
datastore/README.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Clixon datastore
|
||||
|
||||
The Clixon datastore is a stand-alone XML based datastore. The idea is
|
||||
to be able to use different datastores backends with the same
|
||||
API. There is currently a key-value plugin based on qdbm and a plain
|
||||
text-file datastore.
|
||||
|
||||
The datastore is primarily designed to be used by Clixon but can be used
|
||||
separately.
|
||||
|
||||
A datastore is a dynamic plugin that is loaded at runtime with a
|
||||
well-defined API. This means it is possible to create your own
|
||||
datastore and plug it in a Clixon backend at runtime.
|
||||
|
||||
### The functional API
|
||||
```
|
||||
int xmldb_plugin_load(clicon_handle h, char *filename);
|
||||
int xmldb_plugin_unload(clicon_handle h);
|
||||
int xmldb_connect(clicon_handle h);
|
||||
int xmldb_disconnect(clicon_handle h);
|
||||
int xmldb_getopt(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,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
|
||||
char *api_path, cxobj *xt);
|
||||
int xmldb_copy(clicon_handle h, char *from, char *to);
|
||||
int xmldb_lock(clicon_handle h, char *db, int pid);
|
||||
int xmldb_unlock(clicon_handle h, char *db);
|
||||
int xmldb_unlock_all(clicon_handle h, int pid);
|
||||
int xmldb_islocked(clicon_handle h, char *db);
|
||||
int xmldb_exists(clicon_handle h, char *db);
|
||||
int xmldb_delete(clicon_handle h, char *db);
|
||||
int xmldb_create(clicon_handle h, char *db);
|
||||
```
|
||||
|
||||
### Using the API
|
||||
|
||||
To use the API, a client needs the following:
|
||||
- A clicon handle.
|
||||
- A datastore plugin, such as a text.so or keyvalue.so. These are normally built and installed at Clixon make.
|
||||
- A directory where to store databases
|
||||
- A yang specification. This needs to be parsed using the Clixon yang_parse() method.
|
||||
|
||||
A client calling the API needs to:
|
||||
1. Load a plugin and
|
||||
2. Connect to a datastore.
|
||||
You can connect to several datastores, even concurrently,
|
||||
but in practice in Clixon, you connect to a single store.
|
||||
|
||||
After connecting to a datastore, you can create and modify databases
|
||||
within the datastore, and set and get options of the datastore itself.
|
||||
|
||||
When done, you disconnect from the datastore and unload the plugin.
|
||||
|
||||
Within a datastore, the following four databases may exist:
|
||||
- running
|
||||
- candidate
|
||||
- startup
|
||||
- tmp
|
||||
|
||||
Initially, a database does not exist but is created by
|
||||
xmldb_create(). It is deleted by xmldb_delete(). You may check for
|
||||
existence with xmldb_exists(). You need to create a database before
|
||||
you can perform any data access on it.
|
||||
|
||||
You may lock a database for exclusive modification according to
|
||||
Netconf semantics. You may also unlock a single dabase, unlock all frm
|
||||
a specific session.
|
||||
|
||||
You can read a database with xmldb_get() and modify a database with
|
||||
xmldb_put(), and xmldb_copy().
|
||||
|
||||
A typical datastore session can be as follows, see the source code of
|
||||
[datastore_client.c](datastore_client.c) for a more elaborate example.
|
||||
|
||||
```
|
||||
h = clicon_handle_init();
|
||||
xmldb_plugin_load(h, plugin);
|
||||
xmldb_connect(h);
|
||||
xmldb_setopt(h, "dbdir", dbdir);
|
||||
xmldb_setopt(h, "yangspec", yspec);
|
||||
/* From here databases in the datastore may be accessed */
|
||||
xmldb_create(h, "candidate");
|
||||
xmldb_copy(h, "running", "candidate");
|
||||
xmldb_lock(h, "candidate", 7878);
|
||||
xmldb_put(h, "candidate", OP_CREATE, "/interfaces/interface=eth0", xml);
|
||||
xmldb_unlock(h, "candidate");
|
||||
xmldb_get(h, "candidate", "/", &xml, &xvec, &xlen);
|
||||
xmldb_disconnect(h)
|
||||
xmdlb_plugin_unload(h);
|
||||
```
|
||||
|
||||
|
||||
309
datastore/datastore_client.c
Normal file
309
datastore/datastore_client.c
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
*
|
||||
***** 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 *****
|
||||
|
||||
* Examples:
|
||||
|
||||
./datastore_client -d candidate -b /usr/local/var/routing -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/routing/yang -m ietf-ip get /
|
||||
|
||||
sudo ./datastore_client -d candidate -b /usr/local/var/routing -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/routing/yang -m ietf-ip put merge /interfaces/interface=eth66 '<config>eth66</config>'
|
||||
|
||||
sudo ./datastore_client -d candidate -b /usr/local/var/routing -p /home/olof/src/clixon/datastore/keyvalue/keyvalue.so -y /usr/local/share/routing/yang -m ietf-ip put merge / '<config><interfaces><interface><name>eth0</name><enabled>true</enabled></interface></interfaces></config>'
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define DATASTORE_OPTS "hDd:p:b:y:m:"
|
||||
|
||||
/*! usage
|
||||
*/
|
||||
static void
|
||||
usage(char *argv0)
|
||||
{
|
||||
fprintf(stderr, "usage:%s <options>* [<command>]\n"
|
||||
"where options are\n"
|
||||
"\t-h\t\tHelp\n"
|
||||
"\t-D\t\tDebug\n"
|
||||
"\t-d <db>\t\tDatabase name. Default: running. Alt: candidate,startup\n"
|
||||
"\t-b <dir>\tDatabase directory. Mandatory\n"
|
||||
"\t-p <plugin>\tDatastore plugin. Mandatory\n"
|
||||
"\t-y <dir>\tYang directory (where modules are stored). Mandatory\n"
|
||||
"\t-m <module>\tYang module. Mandatory\n"
|
||||
"and command is either:\n"
|
||||
"\tget <xpath>\n"
|
||||
"\tput (merge|replace|create|delete|remove) <api_path> <xml>\n"
|
||||
"\tcopy <todb>\n"
|
||||
"\tlock <pid>\n"
|
||||
"\tunlock\n"
|
||||
"\tunlock_all <pid>\n"
|
||||
"\tislocked\n"
|
||||
"\texists\n"
|
||||
"\tdelete\n"
|
||||
"\tinit\n"
|
||||
,
|
||||
argv0
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
clicon_handle h;
|
||||
char *argv0;
|
||||
char *db = "running";
|
||||
char *plugin = NULL;
|
||||
char *cmd = NULL;
|
||||
yang_spec *yspec = NULL;
|
||||
char *yangdir = NULL;
|
||||
char *yangmodule = NULL;
|
||||
char *dbdir = NULL;
|
||||
int ret;
|
||||
int pid;
|
||||
enum operation_type op;
|
||||
cxobj *xt = NULL;
|
||||
|
||||
/* In the startup, logs to stderr & debug flag set later */
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
|
||||
|
||||
argv0 = argv[0];
|
||||
/* Defaults */
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
goto done;
|
||||
/* getopt in two steps, first find config-file before over-riding options. */
|
||||
while ((c = getopt(argc, argv, DATASTORE_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case '?' :
|
||||
case 'h' : /* help */
|
||||
usage(argv0);
|
||||
break;
|
||||
case 'D' : /* debug */
|
||||
debug = 1;
|
||||
break;
|
||||
case 'd': /* db symbolic: running|candidate|startup */
|
||||
if (!optarg)
|
||||
usage(argv0);
|
||||
db = optarg;
|
||||
break;
|
||||
case 'p': /* datastore plugin */
|
||||
if (!optarg)
|
||||
usage(argv0);
|
||||
plugin = optarg;
|
||||
break;
|
||||
case 'b': /* db directory */
|
||||
if (!optarg)
|
||||
usage(argv0);
|
||||
dbdir = optarg;
|
||||
break;
|
||||
case 'y': /* Yang directory */
|
||||
if (!optarg)
|
||||
usage(argv0);
|
||||
yangdir = optarg;
|
||||
break;
|
||||
case 'm': /* Yang module */
|
||||
if (!optarg)
|
||||
usage(argv0);
|
||||
yangmodule = optarg;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Logs, error and debug to stderr, set debug level
|
||||
*/
|
||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, CLICON_LOG_STDERR);
|
||||
clicon_debug_init(debug, NULL);
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc < 1)
|
||||
usage(argv0);
|
||||
cmd = argv[0];
|
||||
if (plugin == NULL){
|
||||
clicon_err(OE_DB, 0, "Missing plugin -p option");
|
||||
goto done;
|
||||
}
|
||||
if (dbdir == NULL){
|
||||
clicon_err(OE_DB, 0, "Missing dbdir -b option");
|
||||
goto done;
|
||||
}
|
||||
if (yangdir == NULL){
|
||||
clicon_err(OE_YANG, 0, "Missing yangdir -y option");
|
||||
goto done;
|
||||
}
|
||||
if (yangmodule == NULL){
|
||||
clicon_err(OE_YANG, 0, "Missing yang module -m option");
|
||||
goto done;
|
||||
}
|
||||
/* Load datastore plugin */
|
||||
if (xmldb_plugin_load(h, plugin) < 0)
|
||||
goto done;
|
||||
/* Connect to plugin to get a handle */
|
||||
if (xmldb_connect(h) < 0)
|
||||
goto done;
|
||||
/* Create yang spec */
|
||||
if ((yspec = yspec_new()) == NULL)
|
||||
goto done;
|
||||
/* Parse yang spec from given file */
|
||||
if (yang_parse(h, yangdir, yangmodule, NULL, yspec) < 0)
|
||||
goto done;
|
||||
/* Set database directory option */
|
||||
if (xmldb_setopt(h, "dbdir", dbdir) < 0)
|
||||
goto done;
|
||||
/* Set yang spec option */
|
||||
if (xmldb_setopt(h, "yangspec", yspec) < 0)
|
||||
goto done;
|
||||
if (strcmp(cmd, "get")==0){
|
||||
if (argc != 1 && argc != 2)
|
||||
usage(argv0);
|
||||
if (xmldb_get(h, db, argc==2?argv[1]:"/", &xt, NULL, 0) < 0)
|
||||
goto done;
|
||||
clicon_xml2file(stdout, xt, 0, 0);
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
else if (strcmp(cmd, "put")==0){
|
||||
if (argc != 3 && argc != 4){
|
||||
clicon_err(OE_DB, 0, "Unexpected nr of args: %d", argc);
|
||||
usage(argv0);
|
||||
}
|
||||
if (xml_operation(argv[1], &op) < 0){
|
||||
clicon_err(OE_DB, 0, "Unrecognized operation: %s", argv[1]);
|
||||
usage(argv0);
|
||||
}
|
||||
if (argc == 4){
|
||||
if (clicon_xml_parse_str(argv[3], &xt) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xmldb_put(h, db, op, argv[2], xt) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(cmd, "copy")==0){
|
||||
if (argc != 2)
|
||||
usage(argv0);
|
||||
if (xmldb_copy(h, db, argv[1]) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(cmd, "lock")==0){
|
||||
if (argc != 2)
|
||||
usage(argv0);
|
||||
pid = atoi(argv[1]);
|
||||
if (xmldb_lock(h, db, pid) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(cmd, "unlock")==0){
|
||||
if (argc != 1)
|
||||
usage(argv0);
|
||||
if (xmldb_unlock(h, db) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(cmd, "unlock_all")==0){
|
||||
if (argc != 2)
|
||||
usage(argv0);
|
||||
pid = atoi(argv[1]);
|
||||
if (xmldb_unlock_all(h, pid) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(cmd, "islocked")==0){
|
||||
if (argc != 1)
|
||||
usage(argv0);
|
||||
if ((ret = xmldb_islocked(h, db)) < 0)
|
||||
goto done;
|
||||
fprintf(stdout, "islocked: %d\n", ret);
|
||||
}
|
||||
else if (strcmp(cmd, "exists")==0){
|
||||
if (argc != 1)
|
||||
usage(argv0);
|
||||
if ((ret = xmldb_exists(h, db)) < 0)
|
||||
goto done;
|
||||
fprintf(stdout, "exists: %d\n", ret);
|
||||
}
|
||||
else if (strcmp(cmd, "delete")==0){
|
||||
if (argc != 1)
|
||||
usage(argv0);
|
||||
if (xmldb_delete(h, db) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(cmd, "init")==0){
|
||||
if (argc != 1)
|
||||
usage(argv0);
|
||||
if (xmldb_create(h, db) < 0)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_DB, 0, "Unrecognized command: %s", cmd);
|
||||
usage(argv0);
|
||||
}
|
||||
if (xmldb_disconnect(h) < 0)
|
||||
goto done;
|
||||
if (xmldb_plugin_unload(h) < 0)
|
||||
goto done;
|
||||
done:
|
||||
if (xt)
|
||||
xml_free(xt);
|
||||
if (h)
|
||||
clicon_handle_exit(h);
|
||||
if (yspec)
|
||||
yspec_free(yspec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
98
datastore/keyvalue/Makefile.in
Normal file
98
datastore/keyvalue/Makefile.in
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#
|
||||
# ***** 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@
|
||||
prefix = @prefix@
|
||||
datarootdir = @datarootdir@
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
dbdir = @prefix@/db
|
||||
mandir = @mandir@
|
||||
libexecdir = @libexecdir@
|
||||
localstatedir = @localstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
|
||||
VPATH = @srcdir@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ -rdynamic -fPIC
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
DATASTORE = keyvalue
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
|
||||
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
||||
PLUGIN = $(DATASTORE).so
|
||||
|
||||
SRC = clixon_keyvalue.c clixon_qdb.c clixon_chunk.c
|
||||
|
||||
OBJS = $(SRC:.c=.o)
|
||||
|
||||
all: $(PLUGIN)
|
||||
|
||||
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
|
||||
|
||||
$(PLUGIN): $(SRC)
|
||||
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) -shared -o $@ -lc $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(PLUGIN) $(OBJS) *.core
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile *~ .depend
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
.c.o: $(SRC)
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
|
||||
install: $(PLUGIN)
|
||||
install -d $(DESTDIR)$(clixon_LIBDIR)/xmldb
|
||||
install $(PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/xmldb;
|
||||
|
||||
install-include:
|
||||
|
||||
uninstall:
|
||||
rm -rf $(DESTDIR)$(clixon_LIBDIR)/xmldb/$(PLUGIN);
|
||||
|
||||
TAGS:
|
||||
find . -name '*.[chyl]' -print | etags -
|
||||
|
||||
depend:
|
||||
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
|
||||
|
||||
#include .depend
|
||||
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
ensure errno is set and return -1/NULL */
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
|
@ -45,7 +46,11 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_queue.h"
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_chunk.h"
|
||||
|
||||
/*
|
||||
1673
datastore/keyvalue/clixon_keyvalue.c
Normal file
1673
datastore/keyvalue/clixon_keyvalue.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -31,16 +31,26 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
Key-value store
|
||||
*/
|
||||
|
||||
#ifndef _CLIXON_PROC_H_
|
||||
#define _CLIXON_PROC_H_
|
||||
#ifndef _CLIXON_KEYVALUE_H
|
||||
#define _CLIXON_KEYVALUE_H
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int clicon_proc_run (char *, void (outcb)(char *), int doerr);
|
||||
int clicon_proc_daemon (char *);
|
||||
int group_name2gid(char *name, gid_t *gid);
|
||||
*/
|
||||
int kv_get(xmldb_handle h, char *db, char *xpath,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
int kv_put(xmldb_handle h, char *db, enum operation_type op,
|
||||
char *api_path, cxobj *xt);
|
||||
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||
int kv_copy(xmldb_handle h, char *from, char *to);
|
||||
int kv_lock(xmldb_handle h, char *db, int pid);
|
||||
int kv_unlock(xmldb_handle h, char *db);
|
||||
int kv_unlock_all(xmldb_handle h, int pid);
|
||||
int kv_islocked(xmldb_handle h, char *db);
|
||||
int kv_exists(xmldb_handle h, char *db);
|
||||
int kv_delete(xmldb_handle h, char *db);
|
||||
int kv_init(xmldb_handle h, char *db);
|
||||
|
||||
#endif /* _CLIXON_PROC_H_ */
|
||||
#endif /* _CLIXON_KEYVALUE_H */
|
||||
|
|
@ -78,9 +78,8 @@
|
|||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_queue.h"
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_qdb.h"
|
||||
|
||||
|
|
@ -425,7 +424,7 @@ db_regexp(char *file,
|
|||
/* Retrieve value if required */
|
||||
if ( ! noval) {
|
||||
if((val = dpget(iterdp, key, -1, 0, -1, &vlen)) == NULL) {
|
||||
clicon_log(OE_DB, "%s: dpget: %s", __FUNCTION__, dperrmsg(dpecode));
|
||||
clicon_log(LOG_WARNING, "%s: dpget: %s", __FUNCTION__, dperrmsg(dpecode));
|
||||
goto quit;
|
||||
}
|
||||
}
|
||||
97
datastore/text/Makefile.in
Normal file
97
datastore/text/Makefile.in
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#
|
||||
# ***** 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 *****
|
||||
#
|
||||
prefix = @prefix@
|
||||
datarootdir = @datarootdir@
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
dbdir = @prefix@/db
|
||||
mandir = @mandir@
|
||||
libexecdir = @libexecdir@
|
||||
localstatedir = @localstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
|
||||
VPATH = @srcdir@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@ -rdynamic -fPIC
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBS = @LIBS@
|
||||
DATASTORE = text
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
|
||||
INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
||||
PLUGIN = $(DATASTORE).so
|
||||
|
||||
SRC = clixon_xmldb_text.c
|
||||
|
||||
OBJS = $(SRC:.c=.o)
|
||||
|
||||
all: $(PLUGIN)
|
||||
|
||||
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
|
||||
|
||||
$(PLUGIN): $(SRC)
|
||||
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) -shared -o $@ -lc $^ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(PLUGIN) $(OBJS) *.core
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile *~ .depend
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
.c.o: $(SRC)
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
|
||||
install: $(PLUGIN)
|
||||
install -d $(DESTDIR)$(clixon_LIBDIR)/xmldb
|
||||
install $(PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/xmldb;
|
||||
|
||||
install-include:
|
||||
|
||||
uninstall:
|
||||
rm -rf $(DESTDIR)$(clixon_LIBDIR)/xmldb/$(PLUGIN);
|
||||
|
||||
TAGS:
|
||||
find . -name '*.[chyl]' -print | etags -
|
||||
|
||||
depend:
|
||||
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
|
||||
|
||||
#include .depend
|
||||
|
||||
1377
datastore/text/clixon_xmldb_text.c
Normal file
1377
datastore/text/clixon_xmldb_text.c
Normal file
File diff suppressed because it is too large
Load diff
56
datastore/text/clixon_xmldb_text.h
Normal file
56
datastore/text/clixon_xmldb_text.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
*
|
||||
***** 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 *****
|
||||
|
||||
Key-value store
|
||||
*/
|
||||
#ifndef _CLIXON_XMLDB_TEXT_H
|
||||
#define _CLIXON_XMLDB_TEXT_H
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int text_get(xmldb_handle h, char *db, char *xpath,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
int text_put(xmldb_handle h, char *db, enum operation_type op,
|
||||
char *api_path, cxobj *xt);
|
||||
int text_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||
int text_copy(xmldb_handle h, char *from, char *to);
|
||||
int text_lock(xmldb_handle h, char *db, int pid);
|
||||
int text_unlock(xmldb_handle h, char *db);
|
||||
int text_unlock_all(xmldb_handle h, int pid);
|
||||
int text_islocked(xmldb_handle h, char *db);
|
||||
int text_exists(xmldb_handle h, char *db);
|
||||
int text_delete(xmldb_handle h, char *db);
|
||||
int text_init(xmldb_handle h, char *db);
|
||||
|
||||
#endif /* _CLIXON_XMLDB_TEXT_H */
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
This README contains information for developers:
|
||||
# README for developers Clixon developers
|
||||
|
||||
1. How to document the code
|
||||
2. How to work in git (branching)
|
||||
3. How the meta-configure stuff works
|
||||
|
||||
1. How to document the code
|
||||
+++++++++++++++++++++++++++
|
||||
## How to document the code
|
||||
|
||||
```
|
||||
/*! This is a small comment on one line
|
||||
*
|
||||
* This is a detailed description
|
||||
|
|
@ -22,36 +23,17 @@ This README contains information for developers:
|
|||
* @retval FALSE This is a description of another return value
|
||||
* @see See also this function
|
||||
*/
|
||||
```
|
||||
|
||||
## How to work in git (branching)
|
||||
|
||||
2. How to work in git (branching)
|
||||
+++++++++++++++++++++++++++++++++
|
||||
Basically follows: http://nvie.com/posts/a-successful-git-branching-model/
|
||||
only somewhat simplified:
|
||||
|
||||
Do commits in develop branch. When done, merge with master.
|
||||
|
||||
$ git checkout develop
|
||||
Switch to branch develop
|
||||
$ git add ..
|
||||
$ git commit ..
|
||||
$ git push origin develop
|
||||
Add/commit stuff here (and push)
|
||||
|
||||
Ready for tagging
|
||||
-----------------
|
||||
(This is somewhat simplified - no release branch)
|
||||
$ ./bump-version.sh 3.6.0
|
||||
Files modified successfully, version bumped to 3.6.0
|
||||
$ git checkout master
|
||||
Switch to master
|
||||
$ git merge --no-ff develop
|
||||
Merge made by recursive.
|
||||
(Summary of changes)
|
||||
$ git tag -a 3.6.0
|
||||
|
||||
3. How the meta-configure stuff works
|
||||
+++++++++++++++++++++++++++++++++++++
|
||||
## How the meta-configure stuff works
|
||||
```
|
||||
configure.ac --.
|
||||
| .------> autoconf* -----> configure
|
||||
[aclocal.m4] --+---+
|
||||
|
|
@ -64,4 +46,4 @@ configure.ac --.
|
|||
[config.h.in] -. v .-> [config.h] -.
|
||||
+--> config.status* -+ +--> make*
|
||||
Makefile.in ---' `-> Makefile ---'
|
||||
|
||||
```
|
||||
|
|
@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
|
|||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = "CliXoN"
|
||||
PROJECT_NAME = "clixon"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
|
|
@ -743,7 +743,7 @@ WARN_LOGFILE =
|
|||
# spaces.
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl
|
||||
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl ../datastore
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
|
|||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = "CLICON"
|
||||
PROJECT_NAME = "clixon"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
|
|
@ -743,7 +743,7 @@ WARN_LOGFILE =
|
|||
# spaces.
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/backend/ ../apps/restconf/ ../apps/netconf ../apps/dbctrl
|
||||
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/backend/ ../apps/restconf/ ../apps/netconf ../apps/dbctrl ../datastore
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
|
|
|||
|
|
@ -1,112 +1,66 @@
|
|||
Frequently Asked Questions - CliXon
|
||||
===================================
|
||||
# Clixon FAQ
|
||||
|
||||
Q: What is CliXon?
|
||||
------------------
|
||||
CliXon is a configuration management tool including CLI generation,
|
||||
Yang parser, netconf interface and an embedded databases.
|
||||
## What is Clixon?
|
||||
|
||||
Q: Why should you use CliXon?
|
||||
-----------------------------
|
||||
If you want an easy-to-use config frontend based on yang with an open-source license.
|
||||
Typically for embedded devices requiring a config interface such as routers and switches.
|
||||
Clixon is a configuration management tool including a generated CLI ,
|
||||
Yang parser, netconf and restconf interface and an embedded databases.
|
||||
|
||||
Q: What license is available?
|
||||
-----------------------------
|
||||
The basic license is open-source, GPLv3. Contact authors for commercial license.
|
||||
## Why should I use Clixon?
|
||||
|
||||
Q: Is CliXon extendible?
|
||||
------------------------
|
||||
Yes. All application semantics is defined in plugins with well-defined APIs. There are currently three types of plugins: for CLI, for Netconf and for the backend.
|
||||
If you want an easy-to-use configuration frontend based on yang with an
|
||||
open-source license. Typically for embedded devices requiring a
|
||||
config interface such as routers and switches.
|
||||
|
||||
Q: Which language is CliXon implemented in?
|
||||
-------------------------------------------
|
||||
CliXon is written in C. The plugins are written in C. The CLI
|
||||
## What license is available?
|
||||
CLIXON is dual license. Either Apache License, Version 2.0 or GNU
|
||||
General Public License Version 2.
|
||||
|
||||
## Is Clixon extendible?
|
||||
Yes. All application semantics is defined in plugins with well-defined
|
||||
APIs. There are currently plugins for: CLI, Netconf, Restconf, the datastore and the backend.
|
||||
|
||||
## Which programming language is used?
|
||||
Clixon is written in C. The plugins are written in C. The CLI
|
||||
specification uses cligen (http://cligen.se)
|
||||
|
||||
There is a project for writing plugins in Python. It is reasonable
|
||||
simple to spawn an external script from a backend.
|
||||
|
||||
Q: Is CliXon different from Clicon?
|
||||
-----------------------------------
|
||||
CliXon is a fork of Clicon focussing on Yang specification and XML
|
||||
database. The yang support in clicon was grown out of a key-based
|
||||
scheme that became more and more complex since it had to translate
|
||||
between many different formats. CliXon uses only XML internally.
|
||||
## How to best understand Clixon?
|
||||
Run the ietf yang routing example, in the example directory.
|
||||
|
||||
The commit transaction mechanism has been simplified too. But CliXon
|
||||
is not backward compliant with key-based Clixon applications, such as
|
||||
Rost.
|
||||
|
||||
Q: How to best understand CliXon?
|
||||
---------------------------------
|
||||
Run the ietf yang routing example. It is used in many of the answers below.
|
||||
|
||||
Q: How do you build and install CliXon (and the example)?
|
||||
---------------------------------------------------------
|
||||
CliXon:
|
||||
## How do you build and install Clixon (and the example)?
|
||||
Clixon:
|
||||
```
|
||||
./configure;
|
||||
make;
|
||||
sudo make install;
|
||||
sudo make install-include
|
||||
|
||||
```
|
||||
The example:
|
||||
```
|
||||
cd example;
|
||||
make;
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Q: What about reference documentation?
|
||||
--------------------------------------
|
||||
CliXon uses Doxygen for reference documentation.
|
||||
## What about reference documentation?
|
||||
Clixon uses Doxygen for reference documentation.
|
||||
Build using 'make doc' and aim your browser at doc/html/index.html or
|
||||
use the web resource: http://clicon.org/ref/index.html
|
||||
|
||||
Q: 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 cli session: clixon_cli -f /usr/local/etc/routing.conf
|
||||
- Start a netconf session: clixon_netconf -f /usr/local/etc/routing.conf
|
||||
|
||||
Q: Can you run CliXon as docker containers?
|
||||
-------------------------------------------
|
||||
Yes, the example works as docker containers as well. backend and cli needs a
|
||||
common file-system so they need to run as a composed pair.
|
||||
cd example/docker
|
||||
make docker # Prepares /data as shared file-system mount
|
||||
run.sh # Starts an example backend and a cli
|
||||
## How is configuration data stored?
|
||||
Configuration data is stored in an XML datastore. The default is a
|
||||
text-based addatastore, but there also exists a key-value datastore
|
||||
using qdbm. In the example the datastore are regular files found in
|
||||
/usr/local/var/routing/.
|
||||
|
||||
The containers are by default downloaded from dockerhib, but you may
|
||||
build the containers locally:
|
||||
cd docker
|
||||
make docker
|
||||
|
||||
You may also push the containers with 'make push' but you may then consider changing the image name in the makefile.
|
||||
|
||||
Q: How do you change the example?
|
||||
---------------------------------
|
||||
- routing.conf.local - Override default settings
|
||||
- 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.c - Cli C-commands are placed here.
|
||||
- routing_backend.c - Commit and validate functions.
|
||||
- routing_netconf.c - Modify semantics of netconf commands.
|
||||
|
||||
|
||||
Q: How do you check what is in a database?
|
||||
------------------------------------------
|
||||
Use clixon_dbctrl. The name of the running or candidate databases are found in the
|
||||
configuration file.
|
||||
Example:
|
||||
> clixon_dbctrl -d /usr/local/var/routing/candidate_db / -p
|
||||
/interfaces/interface/eth0/ipv4
|
||||
/interfaces/interface/eth0/type bgp
|
||||
/interfaces/interface/eth0
|
||||
/interfaces/interface/eth0/name eth0
|
||||
Each line corresponds to a database entry (node in an XML tree).
|
||||
If the node is a leaf, the value appears as the second entry ('bgp' and 'eth0').
|
||||
|
||||
Q: What is validate and commit?
|
||||
-------------------------------
|
||||
## What is validate and commit?
|
||||
Clixon follows netconf in its validate and commit semantics.
|
||||
In short, you edit a 'candidate' configuration, which is first
|
||||
'validated' for consistency and then 'committed' to the 'running'
|
||||
|
|
@ -116,59 +70,31 @@ A clixon developer writes commit functions to incrementaly upgrade a
|
|||
system state based on configuration changes. Writing commit callbacks
|
||||
is the core functionality of a clixon system.
|
||||
|
||||
Q: How do you write a commit function?
|
||||
--------------------------------------
|
||||
You write a commit function in routing_backend.c.
|
||||
Every time a commit is made, transaction_commit() is called in the
|
||||
backend. It has a 'transaction_data td' argument which is used to fetch
|
||||
information on added, deleted and changed entries. You access this
|
||||
information using access functions as defined in clixon_backend_transaction.h
|
||||
## What is a Clixon configuration file?
|
||||
Clixon options are stored in a configuration file you must specify
|
||||
when you start a backend or client using -f. The example configuration
|
||||
file is /usr/local/etc/routing.conf.
|
||||
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.
|
||||
|
||||
Q: How do you check what has changed on commit?
|
||||
-----------------------------------------------
|
||||
You use XPATHs on the XML trees in the transaction commit callback.
|
||||
Suppose you want to print all added interfaces:
|
||||
cxobj *target = transaction_target(td); # wanted XML tree
|
||||
vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); /* Get added i/fs */
|
||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
||||
clicon_xml2file(stdout, vec[i], 0, 1); /* Print the added interface */
|
||||
You can look for added, deleted and changed entries in this way.
|
||||
## Can I run Clixon as docker containers?
|
||||
Yes, the example works as docker containers as well. backend and cli needs a
|
||||
common file-system so they need to run as a composed pair.
|
||||
```
|
||||
cd example/docker
|
||||
make docker # Prepares /data as shared file-system mount
|
||||
run.sh # Starts an example backend and a cli
|
||||
```
|
||||
The containers are by default downloaded from dockerhib, but you may
|
||||
build the containers locally:
|
||||
```
|
||||
cd docker
|
||||
make docker
|
||||
```
|
||||
You may also push the containers with 'make push' but you may then consider changing the image name in the makefile.
|
||||
|
||||
Q: How do I access the XML tree?
|
||||
--------------------------------
|
||||
Using XPATH, find and iteration functions defined in the XML library. Example library functions:
|
||||
xml_child_each(),
|
||||
xml_find(),
|
||||
xml_body(),
|
||||
clicon_xml2file(),
|
||||
xml_apply()
|
||||
More are found in the doxygen reference.
|
||||
|
||||
Q: How do you write a CLI callback function?
|
||||
--------------------------------------------
|
||||
(1) You add an entry in routing_cli.cli
|
||||
example("This is a comment") <var:int32>("This is a variable"), mycallback("myarg");
|
||||
(2) Then define a function in routing_cli.c
|
||||
mycallback(clicon_handle h, cvec *cvv, cg_var *arg)
|
||||
where 'cvv' contains the value of the variable and 'arg' contains the
|
||||
function parameter 'myarg'.
|
||||
|
||||
Q: What are cg_var and cvec used in CLI callbacks?
|
||||
--------------------------------------------------
|
||||
Those are 'CLIgen variables' and vector of CLIgen variables.
|
||||
They are documented in CLIgen documentation. Some examples on usage is found in the
|
||||
routing_cli.c
|
||||
|
||||
Q: How do you write a validation function?
|
||||
------------------------------------------
|
||||
Similar to a commit function, but instead write the transaction_validate() function.
|
||||
Check for inconsistencies in the XML trees and if they fail, make an clicon_err() call.
|
||||
clicon_err(OE_PLUGIN, 0, "Route %s lacks ipv4 addr", name);
|
||||
return -1;
|
||||
The validation or commit will then be aborted.
|
||||
|
||||
Q: How do you use netconf?
|
||||
--------------------------
|
||||
## How do I use netconf?
|
||||
|
||||
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
|
||||
Example:
|
||||
|
|
@ -176,25 +102,82 @@ Example:
|
|||
|
||||
However, more useful is to run clixon_netconf as an SSH
|
||||
subsystem. Register the subsystem in /etc/sshd_config:
|
||||
|
||||
```
|
||||
Subsystem netconf /usr/local/bin/clixon_netconf
|
||||
|
||||
```
|
||||
and then invoke it from a client using
|
||||
```
|
||||
ssh -s netconf <host>
|
||||
```
|
||||
|
||||
## How do I use notifications?
|
||||
|
||||
Q: How do you use notifications?
|
||||
--------------------------------
|
||||
The example has a prebuilt notification stream called "ROUTING" that triggers every 10s.
|
||||
You enable the notification either via the cli or via netconf:
|
||||
cli> notify
|
||||
cli> Routing notification
|
||||
Routing notification
|
||||
...
|
||||
|
||||
> clixon_netconf -qf /usr/local/etc/routing.conf
|
||||
```
|
||||
clixon_netconf -qf /usr/local/etc/routing.conf
|
||||
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
||||
<rpc-reply><ok/></rpc-reply>]]>]]>
|
||||
<notification><event>Routing notification</event></notification>]]>]]>
|
||||
<notification><event>Routing notification</event></notification>]]>]]>
|
||||
...
|
||||
```
|
||||
|
||||
## I want to program. How do I extend the example?
|
||||
- routing.conf.local - Override default settings
|
||||
- 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.c - Cli C-commands are placed here.
|
||||
- routing_backend.c - Commit and validate functions.
|
||||
- routing_netconf.c - Modify semantics of netconf commands.
|
||||
|
||||
## How do I write a commit function?
|
||||
You write a commit function in routing_backend.c.
|
||||
Every time a commit is made, transaction_commit() is called in the
|
||||
backend. It has a 'transaction_data td' argument which is used to fetch
|
||||
information on added, deleted and changed entries. You access this
|
||||
information using access functions as defined in clixon_backend_transaction.h
|
||||
|
||||
## How do i check what has changed on commit?
|
||||
You use XPATHs on the XML trees in the transaction commit callback.
|
||||
Suppose you want to print all added interfaces:
|
||||
```
|
||||
cxobj *target = transaction_target(td); # wanted XML tree
|
||||
vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); /* Get added i/fs */
|
||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
||||
clicon_xml2file(stdout, vec[i], 0, 1); /* Print the added interface */
|
||||
```
|
||||
You can look for added, deleted and changed entries in this way.
|
||||
|
||||
## How do I access the XML tree?
|
||||
Using XPATH, find and iteration functions defined in the XML library. Example library functions:
|
||||
```
|
||||
xml_child_each(),
|
||||
xml_find(),
|
||||
xml_body(),
|
||||
xml_print(),
|
||||
xml_apply()
|
||||
```
|
||||
More are found in the doxygen reference.
|
||||
|
||||
## How do I write a CLI callback function?
|
||||
|
||||
1. You add an entry in routing_cli.cli
|
||||
> example("This is a comment") <var:int32>("This is a variable"), mycallback("myarg");
|
||||
2. Then define a function in routing_cli.c
|
||||
> mycallback(clicon_handle h, cvec *cvv, cvec *arv)
|
||||
where 'cvv' contains the value of the variable 'var' and 'argv' contains the string "myarg".
|
||||
|
||||
The 'cvv' datatype is a 'CLIgen variable vector'.
|
||||
They are documented in [CLIgen tutorial](https://github.com/olofhagsand/cligen/blob/master/cligen_tutorial.pdf)
|
||||
|
||||
## How do I write a validation function?
|
||||
Similar to a commit function, but instead write the transaction_validate() function.
|
||||
Check for inconsistencies in the XML trees and if they fail, make an clicon_err() call.
|
||||
clicon_err(OE_PLUGIN, 0, "Route %s lacks ipv4 addr", name);
|
||||
return -1;
|
||||
The validation or commit will then be aborted.
|
||||
|
||||
BIN
doc/clixon_example_sdk.png
Normal file
BIN
doc/clixon_example_sdk.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 130 KiB |
|
|
@ -1,19 +1,25 @@
|
|||
Clixon yang routing example
|
||||
+++++++++++++++++++++++++++
|
||||
# Clixon yang routing example
|
||||
|
||||
0. Compile and run
|
||||
------------------
|
||||
cd example
|
||||
make && sudo make install
|
||||
# Start backend
|
||||
clixon_backend -f /usr/local/etc/routing.conf -I
|
||||
# Edit cli
|
||||
clixon_cli -f /usr/local/etc/routing.conf
|
||||
# Send netconf command
|
||||
clixon_netconf -f /usr/local/etc/routing.conf
|
||||
## Compile and run
|
||||
```
|
||||
cd example
|
||||
make && sudo make install
|
||||
```
|
||||
Start backend:
|
||||
```
|
||||
clixon_backend -f /usr/local/etc/routing.conf -I
|
||||
```
|
||||
Edit cli:
|
||||
```
|
||||
clixon_cli -f /usr/local/etc/routing.conf
|
||||
```
|
||||
Send netconf command:
|
||||
```
|
||||
clixon_netconf -f /usr/local/etc/routing.conf
|
||||
```
|
||||
|
||||
1. Setting data example using netconf
|
||||
-------------------------------------
|
||||
## Setting data example using netconf
|
||||
```
|
||||
<rpc><edit-config><target><candidate/></target><config>
|
||||
<interfaces>
|
||||
<interface>
|
||||
|
|
@ -28,40 +34,39 @@ clixon_netconf -f /usr/local/etc/routing.conf
|
|||
</interface>
|
||||
</interfaces>
|
||||
</config></edit-config></rpc>]]>]]>
|
||||
```
|
||||
|
||||
2. Getting data using netconf
|
||||
-----------------------------
|
||||
|
||||
## Getting data using netconf
|
||||
```
|
||||
<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>
|
||||
|
||||
<rpc><get-config><source><candidate/></source><filter/></get-config></rpc>]]>]]>
|
||||
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath"/></get-config></rpc>]]>]]>
|
||||
|
||||
<rpc><get-config><source><candidate/></source><filter type="subtree"><configuration><interfaces><interface><ipv4/></interface></interfaces></configuration></filter></get-config></rpc>]]>]]>
|
||||
|
||||
<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface/ipv4"/></get-config></rpc>]]>]]>
|
||||
|
||||
<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>
|
||||
```
|
||||
|
||||
## Creating notification
|
||||
|
||||
3. Creating notification
|
||||
------------------------
|
||||
The example has an example notification triggering every 10s. To start a notification
|
||||
stream in the session, create a subscription:
|
||||
```
|
||||
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
||||
<rpc-reply><ok/></rpc-reply>]]>]]>
|
||||
<notification><event>Routing notification</event></notification>]]>]]>
|
||||
<notification><event>Routing notification</event></notification>]]>]]>
|
||||
...
|
||||
|
||||
```
|
||||
This can also be triggered via the CLI:
|
||||
```
|
||||
cli> notify
|
||||
cli> Routing notification
|
||||
Routing notification
|
||||
...
|
||||
```
|
||||
|
||||
## Extending
|
||||
|
||||
4. Downcall
|
||||
-----------
|
||||
Clixon has an extension mechanism which can be used to make extended internal
|
||||
netconf messages to the backend configuration engine. You may need this to
|
||||
make some special operation that is not covered by standard
|
||||
|
|
@ -71,11 +76,13 @@ reference. A more realistic downcall would perform some action, such as
|
|||
reading some status.
|
||||
|
||||
Example:
|
||||
```
|
||||
cli> downcall "This is a string"
|
||||
This is a string
|
||||
cli>p
|
||||
```
|
||||
|
||||
5. Run as docker container
|
||||
--------------------------
|
||||
## Run as docker container
|
||||
```
|
||||
cd docker
|
||||
# look in README
|
||||
```
|
||||
|
|
@ -26,4 +26,8 @@ CLICON_CLI_GENMODEL_TYPE VARS
|
|||
CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0
|
||||
|
||||
# Enabled uses "startup" configuration on boot
|
||||
CLICON_USE_STARTUP_CONFIG 0
|
||||
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
|
||||
|
|
|
|||
|
|
@ -12,10 +12,6 @@
|
|||
/* Clixon version string */
|
||||
#undef CLIXON_VERSION_STRING
|
||||
|
||||
/* Check if extra keys inserted for database lists containing content. Eg
|
||||
A.n.foo = 3 means A.3 $!a=foo exists */
|
||||
#undef DB_KEYCONTENT
|
||||
|
||||
/* Define to 1 if you have the `alphasort' function. */
|
||||
#undef HAVE_ALPHASORT
|
||||
|
||||
|
|
@ -37,9 +33,6 @@
|
|||
/* Define to 1 if you have the `crypt' library (-lcrypt). */
|
||||
#undef HAVE_LIBCRYPT
|
||||
|
||||
/* Define to 1 if you have the `curl' library (-lcurl). */
|
||||
#undef HAVE_LIBCURL
|
||||
|
||||
/* Define to 1 if you have the `dl' library (-ldl). */
|
||||
#undef HAVE_LIBDL
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* NOTE: clixon.h is a GENERATED FILE and should not be edited.
|
||||
* clixon.h.in is the original
|
||||
*
|
||||
* Meta-include file that includes all sub-files in control-lib
|
||||
* Note: this include files is for external purposes. Do not include this
|
||||
* file in clicon lib-routines.
|
||||
|
|
@ -67,22 +70,18 @@
|
|||
#include <clixon/clixon_handle.h>
|
||||
#include <clixon/clixon_yang.h>
|
||||
#include <clixon/clixon_yang_type.h>
|
||||
#include <clixon/clixon_chunk.h>
|
||||
#include <clixon/clixon_event.h>
|
||||
#include <clixon/clixon_string.h>
|
||||
#include <clixon/clixon_file.h>
|
||||
#include <clixon/clixon_xml.h>
|
||||
#include <clixon/clixon_proto.h>
|
||||
#include <clixon/clixon_proto_encode.h>
|
||||
#include <clixon/clixon_proto_client.h>
|
||||
#include <clixon/clixon_proc.h>
|
||||
#include <clixon/clixon_plugin.h>
|
||||
#include <clixon/clixon_options.h>
|
||||
#include <clixon/clixon_xml_map.h>
|
||||
#include <clixon/clixon_xml_db.h>
|
||||
#include <clixon/clixon_xsl.h>
|
||||
#include <clixon/clixon_json.h>
|
||||
#include <clixon/clixon_plugin.h>
|
||||
#include <clixon/clixon_plugin.h>
|
||||
|
||||
/*
|
||||
* Global variables generated by Makefile
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@
|
|||
|
||||
|
||||
int clicon_file_dirent(const char *dir, struct dirent **ent,
|
||||
const char *regexp, mode_t type, const char *label);
|
||||
|
||||
char *clicon_tmpfile(const char *label);
|
||||
const char *regexp, mode_t type);
|
||||
|
||||
int clicon_file_copy(char *src, char *target);
|
||||
|
||||
int group_name2gid(char *name, gid_t *gid);
|
||||
|
||||
#endif /* _CLIXON_FILE_H_ */
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ char *clicon_cli_dir(clicon_handle h);
|
|||
char *clicon_clispec_dir(clicon_handle h);
|
||||
char *clicon_netconf_dir(clicon_handle h);
|
||||
char *clicon_restconf_dir(clicon_handle h);
|
||||
char *clicon_archive_dir(clicon_handle h);
|
||||
char *clicon_xmldb_plugin(clicon_handle h);
|
||||
int clicon_sock_family(clicon_handle h);
|
||||
char *clicon_sock(clicon_handle h);
|
||||
int clicon_sock_port(clicon_handle h);
|
||||
|
|
@ -120,4 +120,16 @@ int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
|
|||
char *clicon_dbspec_name(clicon_handle h);
|
||||
int clicon_dbspec_name_set(clicon_handle h, char *name);
|
||||
|
||||
int clicon_xmldb_plugin_set(clicon_handle h, plghndl_t handle);
|
||||
|
||||
plghndl_t clicon_xmldb_plugin_get(clicon_handle h);
|
||||
|
||||
int clicon_xmldb_api_set(clicon_handle h, void *xa_api);
|
||||
|
||||
void *clicon_xmldb_api_get(clicon_handle h);
|
||||
|
||||
int clicon_xmldb_handle_set(clicon_handle h, void *xh);
|
||||
|
||||
void *clicon_xmldb_handle_get(clicon_handle h);
|
||||
|
||||
#endif /* _CLIXON_OPTIONS_H_ */
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ typedef void *(find_plugin_t)(clicon_handle, char *);
|
|||
* Prototypes
|
||||
*/
|
||||
/* Common plugin function names, function types and signatures.
|
||||
* This set of plugins is extended in
|
||||
* This plugin code is exytended by backend, cli, netconf, restconf plugins
|
||||
* Cli see cli_plugin.c
|
||||
* Backend see config_plugin.c
|
||||
*/
|
||||
|
|
@ -77,4 +77,8 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
|
|||
/* Find a function in global namespace or a plugin. XXX clicon internal */
|
||||
void *clicon_find_func(clicon_handle h, char *plugin, char *func);
|
||||
|
||||
plghndl_t plugin_load (clicon_handle h, char *file, int dlflags);
|
||||
|
||||
int plugin_unload(clicon_handle h, plghndl_t *handle);
|
||||
|
||||
#endif /* _CLIXON_PLUGIN_H_ */
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ static inline char * strdup4(char *str)
|
|||
*/
|
||||
char **clicon_strsep(char *string, char *delim, int *nvec0);
|
||||
char *clicon_strjoin (int argc, char **argv, char *delim);
|
||||
int str2cvec(char *string, char delim1, char delim2, cvec **cvp);
|
||||
int percent_encode(char *str, char **escp);
|
||||
int percent_decode(char *esc, char **str);
|
||||
#ifndef HAVE_STRNDUP
|
||||
char *clicon_strndup (const char *, size_t);
|
||||
#endif /* ! HAVE_STRNDUP */
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
|
|||
#define XML_FLAG_ADD 0x02 /* Node is added (commits) or parent added rec*/
|
||||
#define XML_FLAG_DEL 0x04 /* Node is deleted (commits) or parent deleted rec */
|
||||
#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
|
||||
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
char *xml_type2str(enum cxobj_type type);
|
||||
char *xml_name(cxobj *xn);
|
||||
int xml_name_set(cxobj *xn, char *name);
|
||||
char *xml_namespace(cxobj *xn);
|
||||
|
|
@ -87,8 +89,6 @@ int xml_value_set(cxobj *xn, char *val);
|
|||
char *xml_value_append(cxobj *xn, char *val);
|
||||
enum cxobj_type xml_type(cxobj *xn);
|
||||
int xml_type_set(cxobj *xn, enum cxobj_type type);
|
||||
int xml_index(cxobj *xn);
|
||||
int xml_index_set(cxobj *xn, int index);
|
||||
|
||||
cg_var *xml_cv_get(cxobj *xn);
|
||||
int xml_cv_set(cxobj *xn, cg_var *cv);
|
||||
|
|
@ -103,6 +103,7 @@ int xml_childvec_set(cxobj *x, int len);
|
|||
cxobj *xml_new(char *name, cxobj *xn_parent);
|
||||
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
|
||||
void *xml_spec(cxobj *x);
|
||||
void *xml_spec_set(cxobj *x, void *spec);
|
||||
cxobj *xml_find(cxobj *xn_parent, char *name);
|
||||
|
||||
int xml_addsub(cxobj *xp, cxobj *xc);
|
||||
|
|
@ -113,6 +114,7 @@ int xml_rm(cxobj *xc);
|
|||
int xml_rootchild(cxobj *xp, int i, cxobj **xcp);
|
||||
|
||||
char *xml_body(cxobj *xn);
|
||||
cxobj *xml_body_get(cxobj *xn);
|
||||
char *xml_find_value(cxobj *xn_parent, char *name);
|
||||
char *xml_find_body(cxobj *xn, char *name);
|
||||
|
||||
|
|
@ -127,6 +129,7 @@ int clicon_xml_parse_file(int fd, cxobj **xml_top, char *endtag);
|
|||
int clicon_xml_parse_str(char *str, cxobj **xml_top);
|
||||
int clicon_xml_parse(cxobj **cxtop, char *format, ...);
|
||||
|
||||
int xmltree2cbuf(cbuf *cb, cxobj *x, int level);
|
||||
int xml_copy(cxobj *x0, cxobj *x1);
|
||||
cxobj *xml_dup(cxobj *x0);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,31 +30,126 @@
|
|||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* XML support functions.
|
||||
*/
|
||||
#ifndef _CLIXON_XML_DB_H
|
||||
#define _CLIXON_XML_DB_H
|
||||
|
||||
/* The XMLDB has a handle to keep connected state. To users of the API it is
|
||||
* a void* but it may have structure within a specific plugin.
|
||||
* The handle is independent from clicon so that (in principle) the datastore
|
||||
* can work in other contexts than clicon.
|
||||
* The connect API call sets the handle and disconnect resets it. In principle
|
||||
* there can be several handles at one time.
|
||||
*/
|
||||
#if 1 /* SANITY CHECK */
|
||||
typedef struct {int16_t a;} *xmldb_handle;
|
||||
#else
|
||||
typedef void *xmldb_handle;
|
||||
#endif
|
||||
|
||||
/* Version of clixon datastore plugin API. */
|
||||
#define XMLDB_API_VERSION 1
|
||||
|
||||
/* Magic to ensure plugin sanity. */
|
||||
#define XMLDB_API_MAGIC 0xf386f730
|
||||
|
||||
/* Name of plugin init function (must be called this) */
|
||||
#define XMLDB_PLUGIN_INIT_FN "clixon_xmldb_plugin_init"
|
||||
|
||||
/* Type of plugin init function */
|
||||
typedef void * (plugin_init_t)(int version);
|
||||
|
||||
/* Type of plugin exit function */
|
||||
typedef int (plugin_exit_t)(void);
|
||||
|
||||
/* Type of xmldb connect function */
|
||||
typedef xmldb_handle (xmldb_connect_t)(void);
|
||||
|
||||
/* Type of xmldb disconnect function */
|
||||
typedef int (xmldb_disconnect_t)(xmldb_handle xh);
|
||||
|
||||
/* Type of xmldb getopt function */
|
||||
typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
|
||||
|
||||
/* Type of xmldb setopt function */
|
||||
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
|
||||
|
||||
/* Type of xmldb get function */
|
||||
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
|
||||
/* Type of xmldb put function */
|
||||
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op,
|
||||
char *api_path, cxobj *xt);
|
||||
|
||||
/* Type of xmldb copy function */
|
||||
typedef int (xmldb_copy_t)(xmldb_handle xh, char *from, char *to);
|
||||
|
||||
/* Type of xmldb lock function */
|
||||
typedef int (xmldb_lock_t)(xmldb_handle xh, char *db, int pid);
|
||||
|
||||
/* Type of xmldb unlock function */
|
||||
typedef int (xmldb_unlock_t)(xmldb_handle xh, char *db);
|
||||
|
||||
/* Type of xmldb unlock_all function */
|
||||
typedef int (xmldb_unlock_all_t)(xmldb_handle xh, int pid);
|
||||
|
||||
/* Type of xmldb islocked function */
|
||||
typedef int (xmldb_islocked_t)(xmldb_handle xh, char *db);
|
||||
|
||||
/* Type of xmldb exists function */
|
||||
typedef int (xmldb_exists_t)(xmldb_handle xh, char *db);
|
||||
|
||||
/* Type of xmldb delete function */
|
||||
typedef int (xmldb_delete_t)(xmldb_handle xh, char *db);
|
||||
|
||||
/* Type of xmldb init function */
|
||||
typedef int (xmldb_create_t)(xmldb_handle xh, char *db);
|
||||
|
||||
/* plugin init struct for the api */
|
||||
struct xmldb_api{
|
||||
int xa_version;
|
||||
int xa_magic;
|
||||
plugin_init_t *xa_plugin_init_fn; /* XMLDB_PLUGIN_INIT_FN */
|
||||
plugin_exit_t *xa_plugin_exit_fn;
|
||||
xmldb_connect_t *xa_connect_fn;
|
||||
xmldb_disconnect_t *xa_disconnect_fn;
|
||||
xmldb_getopt_t *xa_getopt_fn;
|
||||
xmldb_setopt_t *xa_setopt_fn;
|
||||
xmldb_get_t *xa_get_fn;
|
||||
xmldb_put_t *xa_put_fn;
|
||||
xmldb_copy_t *xa_copy_fn;
|
||||
xmldb_lock_t *xa_lock_fn;
|
||||
xmldb_unlock_t *xa_unlock_fn;
|
||||
xmldb_unlock_all_t *xa_unlock_all_fn;
|
||||
xmldb_islocked_t *xa_islocked_fn;
|
||||
xmldb_exists_t *xa_exists_fn;
|
||||
xmldb_delete_t *xa_delete_fn;
|
||||
xmldb_create_t *xa_create_fn;
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
* API
|
||||
*/
|
||||
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
|
||||
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
|
||||
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
|
||||
int xmldb_plugin_load(clicon_handle h, char *filename);
|
||||
int xmldb_plugin_unload(clicon_handle h);
|
||||
|
||||
int xmldb_connect(clicon_handle h);
|
||||
int xmldb_disconnect(clicon_handle h);
|
||||
int xmldb_getopt(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,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
|
||||
char *api_path, cxobj *xt);
|
||||
int xmldb_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||
int xmldb_copy(clicon_handle h, char *from, char *to);
|
||||
int xmldb_lock(clicon_handle h, char *db, int pid);
|
||||
int xmldb_unlock(clicon_handle h, char *db, int pid);
|
||||
int xmldb_unlock(clicon_handle h, char *db);
|
||||
int xmldb_unlock_all(clicon_handle h, int pid);
|
||||
int xmldb_islocked(clicon_handle h, char *db);
|
||||
int xmldb_exists(clicon_handle h, char *db);
|
||||
int xmldb_delete(clicon_handle h, char *db);
|
||||
int xmldb_init(clicon_handle h, char *db);
|
||||
int xmldb_create(clicon_handle h, char *db);
|
||||
|
||||
#endif /* _CLIXON_XML_DB_H */
|
||||
|
|
|
|||
|
|
@ -61,5 +61,14 @@ int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
|
|||
cxobj ***first, size_t *firstlen,
|
||||
cxobj ***second, size_t *secondlen,
|
||||
cxobj ***changed1, cxobj ***changed2, size_t *changedlen);
|
||||
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
|
||||
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
|
||||
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
|
||||
int xml_tree_prune_flagged(cxobj *xt, int flag, int test, int *upmark);
|
||||
int xml_default(cxobj *x, void *arg);
|
||||
int xml_order(cxobj *x, void *arg);
|
||||
int xml_sanity(cxobj *x, void *arg);
|
||||
int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
||||
|
||||
#endif /* _CLIXON_XML_MAP_H_ */
|
||||
|
|
|
|||
|
|
@ -61,15 +61,13 @@ CPPFLAGS = @CPPFLAGS@
|
|||
|
||||
INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir)
|
||||
|
||||
SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
clixon_chunk.c clixon_proc.c \
|
||||
SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
||||
clixon_string.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c \
|
||||
clixon_yang.c clixon_yang_type.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c \
|
||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_client.c \
|
||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
|
||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c
|
||||
|
||||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@
|
|||
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_err.h"
|
||||
|
||||
/*
|
||||
|
|
@ -217,7 +216,7 @@ clicon_err_save(void)
|
|||
{
|
||||
struct err_state *es;
|
||||
|
||||
if ((es = chunk(sizeof(*es), NULL)) == NULL)
|
||||
if ((es = malloc(sizeof(*es))) == NULL)
|
||||
return NULL;
|
||||
es->es_errno = clicon_errno;
|
||||
es->es_suberrno = clicon_suberrno;
|
||||
|
|
@ -232,10 +231,11 @@ clicon_err_restore(void* handle)
|
|||
{
|
||||
struct err_state *es;
|
||||
|
||||
es = (struct err_state *)handle;
|
||||
clicon_errno = es->es_errno;
|
||||
clicon_suberrno = es->es_suberrno;
|
||||
strncpy(clicon_err_reason, es->es_reason, ERR_STRLEN-1);
|
||||
unchunk(es);
|
||||
if ((es = (struct err_state *)handle) != NULL){
|
||||
clicon_errno = es->es_errno;
|
||||
clicon_suberrno = es->es_suberrno;
|
||||
strncpy(clicon_err_reason, es->es_reason, ERR_STRLEN-1);
|
||||
free(es);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <grp.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -58,7 +59,6 @@
|
|||
/* clicon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_file.h"
|
||||
|
||||
|
|
@ -66,7 +66,8 @@
|
|||
* qsort function
|
||||
*/
|
||||
static int
|
||||
clicon_file_dirent_sort(const void* arg1, const void* arg2)
|
||||
clicon_file_dirent_sort(const void* arg1,
|
||||
const void* arg2)
|
||||
{
|
||||
struct dirent *d1 = (struct dirent *)arg1;
|
||||
struct dirent *d2 = (struct dirent *)arg2;
|
||||
|
|
@ -81,10 +82,10 @@ clicon_file_dirent_sort(const void* arg1, const void* arg2)
|
|||
|
||||
/*! Return sorted matching files from a directory
|
||||
* @param[in] dir Directory path
|
||||
* @param[out] ent Entries pointer, will be filled in with dir entries
|
||||
* @param[out] ent Entries pointer, will be filled in with dir entries. Free
|
||||
* after use
|
||||
* @param[in] regexp Regexp filename matching
|
||||
* @param[in] type File type matching, see stat(2)
|
||||
* @param[in] label Clicon Chunk label for memory handling, unchunk after use
|
||||
*
|
||||
* @retval n Number of matching files in directory
|
||||
* @retval -1 Error
|
||||
|
|
@ -92,34 +93,34 @@ clicon_file_dirent_sort(const void* arg1, const void* arg2)
|
|||
* @code
|
||||
* char *dir = "/root/fs";
|
||||
* struct dirent *dp;
|
||||
* if ((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__)) < 0)
|
||||
* if ((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG)) < 0)
|
||||
* return -1;
|
||||
* for (i = 0; i < ndp; i++)
|
||||
* do something with dp[i].d_name;
|
||||
* unchunk_group(__FUNCTION__);
|
||||
* free(dp);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
clicon_file_dirent(const char *dir,
|
||||
struct dirent **ent,
|
||||
const char *regexp,
|
||||
mode_t type,
|
||||
const char *label)
|
||||
mode_t type)
|
||||
{
|
||||
DIR *dirp;
|
||||
int retval = -1;
|
||||
int res;
|
||||
int nent;
|
||||
char *filename;
|
||||
regex_t re;
|
||||
char errbuf[128];
|
||||
struct stat st;
|
||||
struct dirent dent;
|
||||
int retval = -1;
|
||||
DIR *dirp;
|
||||
int res;
|
||||
int nent;
|
||||
regex_t re;
|
||||
char errbuf[128];
|
||||
char filename[MAXPATHLEN];
|
||||
struct stat st;
|
||||
struct dirent dent;
|
||||
struct dirent *dresp;
|
||||
struct dirent *tmp;
|
||||
struct dirent *new = NULL;
|
||||
struct dirent *dvecp = NULL;
|
||||
|
||||
|
||||
*ent = NULL;
|
||||
nent = 0;
|
||||
|
||||
|
|
@ -137,7 +138,9 @@ clicon_file_dirent(const char *dir,
|
|||
goto quit;
|
||||
}
|
||||
|
||||
for (res = readdir_r (dirp, &dent, &dresp); dresp; res = readdir_r (dirp, &dent, &dresp)) {
|
||||
for (res = readdir_r (dirp, &dent, &dresp);
|
||||
dresp;
|
||||
res = readdir_r (dirp, &dent, &dresp)) {
|
||||
if (res != 0) {
|
||||
clicon_err(OE_UNIX, 0, "readdir: %s", strerror(errno));
|
||||
goto quit;
|
||||
|
|
@ -150,12 +153,8 @@ clicon_file_dirent(const char *dir,
|
|||
}
|
||||
/* File type matching */
|
||||
if (type) {
|
||||
if ((filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dent.d_name)) == NULL) {
|
||||
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
|
||||
goto quit;
|
||||
}
|
||||
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dent.d_name);
|
||||
res = lstat(filename, &st);
|
||||
unchunk (filename);
|
||||
if (res != 0) {
|
||||
clicon_err(OE_UNIX, 0, "lstat: %s", strerror(errno));
|
||||
goto quit;
|
||||
|
|
@ -164,8 +163,8 @@ clicon_file_dirent(const char *dir,
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((tmp = rechunk(new, (nent+1)*sizeof(*dvecp), label)) == NULL) {
|
||||
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
|
||||
if ((tmp = realloc(new, (nent+1)*sizeof(*dvecp))) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "realloc");
|
||||
goto quit;
|
||||
}
|
||||
new = tmp;
|
||||
|
|
@ -183,30 +182,9 @@ quit:
|
|||
closedir(dirp);
|
||||
if (regexp)
|
||||
regfree(&re);
|
||||
unchunk_group(__FUNCTION__);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use mkstep() to create an empty temporary file, accessible only by this user.
|
||||
* A chunk:ed file name is returned so that caller can overwrite file safely.
|
||||
*/
|
||||
char *
|
||||
clicon_tmpfile(const char *label)
|
||||
{
|
||||
int fd;
|
||||
char file[] = "/tmp/.tmpXXXXXX";
|
||||
|
||||
if ((fd = mkstemp(file)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "mkstemp");
|
||||
return NULL;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return (char *)chunkdup(file, strlen(file)+1, label);
|
||||
}
|
||||
|
||||
/*! Make a copy of file src
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -252,3 +230,34 @@ clicon_file_copy(char *src,
|
|||
}
|
||||
|
||||
|
||||
/*! Translate group name to gid. Return -1 if error or not found.
|
||||
* @param[in] name Name of group
|
||||
* @param[out] gid Group id
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error. or not found
|
||||
*/
|
||||
int
|
||||
group_name2gid(char *name,
|
||||
gid_t *gid)
|
||||
{
|
||||
char buf[1024];
|
||||
struct group g0;
|
||||
struct group *gr = &g0;
|
||||
struct group *gtmp;
|
||||
|
||||
gr = &g0;
|
||||
/* This leaks memory in ubuntu */
|
||||
if (getgrnam_r(name, gr, buf, sizeof(buf), >mp) < 0){
|
||||
clicon_err(OE_UNIX, errno, "%s: getgrnam_r(%s): %s",
|
||||
__FUNCTION__, name, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (gtmp == NULL){
|
||||
clicon_err(OE_UNIX, 0, "%s: No such group: %s", __FUNCTION__, name);
|
||||
fprintf(stderr, "No such group %s\n", name);
|
||||
return -1;
|
||||
}
|
||||
if (gid)
|
||||
*gid = gr->gr_gid;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,16 +52,16 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
|
||||
#define CLICON_MAGIC 0x99aafabe
|
||||
|
||||
#define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h))
|
||||
|
||||
/*
|
||||
* clicon_handle
|
||||
* Internal structire of basic handle. Also header of all other handles.
|
||||
* see struct clicon_cli_handle, struct clicon_backend_handle, etc
|
||||
/*! Internal structure of basic handle. Also header of all other handles.
|
||||
* @note If you change here, you must also change the structs below:
|
||||
* @see struct cli_handle, struct backend_handle
|
||||
*/
|
||||
struct clicon_handle {
|
||||
int ch_magic; /* magic (HDR) */
|
||||
|
|
@ -69,7 +69,6 @@ struct clicon_handle {
|
|||
clicon_hash_t *ch_data; /* internal clicon data (HDR) */
|
||||
};
|
||||
|
||||
|
||||
/*! Internal call to allocate a CLICON handle.
|
||||
*
|
||||
* There may be different variants of handles with some common options.
|
||||
|
|
@ -166,3 +165,4 @@ clicon_data(clicon_handle h)
|
|||
|
||||
return ch->ch_data;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -200,8 +200,8 @@ hash_value(clicon_hash_t *hash,
|
|||
/*! Copy value and add hash entry.
|
||||
*
|
||||
* @param[in] hash Hash table
|
||||
* @param[in] key New variable name
|
||||
* @param[in] val New variable value
|
||||
* @param[in] key Variable name
|
||||
* @param[in] val Variable value
|
||||
* @param[in] vlen Length of variable value
|
||||
* @retval variable New hash structure on success
|
||||
* @retval NULL Failure
|
||||
|
|
@ -212,8 +212,9 @@ hash_add(clicon_hash_t *hash,
|
|||
void *val,
|
||||
size_t vlen)
|
||||
{
|
||||
void *newval;
|
||||
clicon_hash_t h, new = NULL;
|
||||
void *newval;
|
||||
clicon_hash_t h;
|
||||
clicon_hash_t new = NULL;
|
||||
|
||||
/* If variable exist, don't allocate a new. just replace value */
|
||||
h = hash_lookup (hash, key);
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@
|
|||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
|
||||
/*
|
||||
|
|
@ -198,7 +198,6 @@ clicon_option_default(clicon_hash_t *copt)
|
|||
}
|
||||
retval = 0;
|
||||
catch:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -233,8 +232,8 @@ clicon_option_sanity(clicon_hash_t *copt)
|
|||
clicon_err(OE_UNIX, 0, "CLICON_YANG_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_ARCHIVE_DIR")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_ARCHIVE_DIR not defined in config file");
|
||||
if (!hash_lookup(copt, "CLICON_XMLDB_DIR")){
|
||||
clicon_err(OE_UNIX, 0, "CLICON_XMLDB_DIR not defined in config file");
|
||||
goto done;
|
||||
}
|
||||
if (!hash_lookup(copt, "CLICON_SOCK")){
|
||||
|
|
@ -448,9 +447,9 @@ clicon_restconf_dir(clicon_handle h)
|
|||
}
|
||||
|
||||
char *
|
||||
clicon_archive_dir(clicon_handle h)
|
||||
clicon_xmldb_plugin(clicon_handle h)
|
||||
{
|
||||
return clicon_option_str(h, "CLICON_ARCHIVE_DIR");
|
||||
return clicon_option_str(h, "CLICON_XMLDB_PLUGIN");
|
||||
}
|
||||
|
||||
/* get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */
|
||||
|
|
@ -627,12 +626,12 @@ clicon_dbspec_yang(clicon_handle h)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set dbspec (YANG variant)
|
||||
/*! Set yang database specification
|
||||
* ys must be a malloced pointer
|
||||
*/
|
||||
int
|
||||
clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys)
|
||||
clicon_dbspec_yang_set(clicon_handle h,
|
||||
struct yang_spec *ys)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
|
||||
|
|
@ -644,8 +643,7 @@ clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get dbspec name as read from spec. Can be used in CLI '@' syntax.
|
||||
/*! Get dbspec name as read from spec. Can be used in CLI '@' syntax.
|
||||
* XXX: this we muśt change,...
|
||||
*/
|
||||
char *
|
||||
|
|
@ -656,11 +654,103 @@ clicon_dbspec_name(clicon_handle h)
|
|||
return clicon_option_str(h, "dbspec_name");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set dbspec name as read from spec. Can be used in CLI '@' syntax.
|
||||
/*! Set dbspec name as read from spec. Can be used in CLI '@' syntax.
|
||||
*/
|
||||
int
|
||||
clicon_dbspec_name_set(clicon_handle h, char *name)
|
||||
{
|
||||
return clicon_option_str_set(h, "dbspec_name", name);
|
||||
}
|
||||
|
||||
/*! Set xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
|
||||
int
|
||||
clicon_xmldb_plugin_set(clicon_handle h,
|
||||
plghndl_t handle)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
|
||||
if (hash_add(cdat, "xmldb_plugin", &handle, sizeof(void*)) == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get xmldb datastore plugin handle, as used by dlopen/dlsym/dlclose */
|
||||
plghndl_t
|
||||
clicon_xmldb_plugin_get(clicon_handle h)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
size_t len;
|
||||
void *p;
|
||||
|
||||
if ((p = hash_value(cdat, "xmldb_plugin", &len)) != NULL)
|
||||
return *(plghndl_t*)p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Set or reset XMLDB API struct pointer
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xa XMLDB API struct
|
||||
* @note xa is really of type struct xmldb_api*
|
||||
*/
|
||||
int
|
||||
clicon_xmldb_api_set(clicon_handle h,
|
||||
void *xa)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
|
||||
/* It is the pointer to xa_api that should be copied by hash,
|
||||
so we send a ptr to the ptr to indicate what to copy.
|
||||
*/
|
||||
if (hash_add(cdat, "xmldb_api", &xa, sizeof(void*)) == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get XMLDB API struct pointer
|
||||
* @param[in] h Clicon handle
|
||||
* @retval xa XMLDB API struct
|
||||
* @note xa is really of type struct xmldb_api*
|
||||
*/
|
||||
void *
|
||||
clicon_xmldb_api_get(clicon_handle h)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
size_t len;
|
||||
void *xa;
|
||||
|
||||
if ((xa = hash_value(cdat, "xmldb_api", &len)) != NULL)
|
||||
return *(void**)xa;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Set or reset XMLDB storage handle
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xh XMLDB storage handle. If NULL reset it
|
||||
* @note Just keep note of it, dont allocate it or so.
|
||||
*/
|
||||
int
|
||||
clicon_xmldb_handle_set(clicon_handle h,
|
||||
void *xh)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
|
||||
if (hash_add(cdat, "xmldb_handle", &xh, sizeof(void*)) == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get XMLDB storage handle
|
||||
* @param[in] h Clicon handle
|
||||
* @retval xh XMLDB storage handle. If not connected return NULL
|
||||
*/
|
||||
void *
|
||||
clicon_xmldb_handle_get(clicon_handle h)
|
||||
{
|
||||
clicon_hash_t *cdat = clicon_data(h);
|
||||
size_t len;
|
||||
void *xh;
|
||||
|
||||
if ((xh = hash_value(cdat, "xmldb_handle", &len)) != NULL)
|
||||
return *(void**)xh;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_plugin.h"
|
||||
|
||||
|
|
@ -84,3 +85,76 @@ clicon_find_func(clicon_handle h, char *plugin, char *func)
|
|||
|
||||
return dlsym(dlhandle, func);
|
||||
}
|
||||
|
||||
/*! Load a dynamic plugin object and call its init-function
|
||||
* Note 'file' may be destructively modified
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] file Which plugin to load
|
||||
* @param[in] dlflags See man(3) dlopen
|
||||
*/
|
||||
plghndl_t
|
||||
plugin_load(clicon_handle h,
|
||||
char *file,
|
||||
int dlflags)
|
||||
{
|
||||
char *error;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfn;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
goto done;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfn = dlsym(handle, PLUGIN_INIT)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to find plugin_init when loading restconf plugin %s", file);
|
||||
goto err;
|
||||
}
|
||||
if ((error = (char*)dlerror()) != NULL) {
|
||||
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
|
||||
goto done;
|
||||
}
|
||||
if (initfn(h) != 0) {
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
|
||||
clicon_err(OE_DB, 0, "Unknown error: %s: plugin_init does not make clicon_err call on error",
|
||||
file);
|
||||
goto err;
|
||||
}
|
||||
done:
|
||||
return handle;
|
||||
err:
|
||||
if (handle)
|
||||
dlclose(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*! Unload a plugin
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] handle Clicon handle
|
||||
*/
|
||||
int
|
||||
plugin_unload(clicon_handle h,
|
||||
plghndl_t *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfn;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfn = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfn(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,294 +0,0 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <grp.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_sig.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_proc.h"
|
||||
|
||||
/*
|
||||
* Macros
|
||||
*/
|
||||
#define signal_set_mask(set) sigprocmask(SIG_SETMASK, (set), NULL)
|
||||
#define signal_get_mask(set) sigprocmask (0, NULL, (set))
|
||||
|
||||
/*
|
||||
* Child process ID
|
||||
* XXX Really shouldn't be a global variable
|
||||
*/
|
||||
static int _clicon_proc_child = 0;
|
||||
|
||||
/*
|
||||
* Make sure child is killed by ctrl-C
|
||||
*/
|
||||
static void
|
||||
clicon_proc_sigint(int sig)
|
||||
{
|
||||
if (_clicon_proc_child > 0)
|
||||
kill (_clicon_proc_child, SIGINT);
|
||||
}
|
||||
|
||||
/*! Fork a child process, setup a pipe between parent and child.
|
||||
* Allowing parent to read the output of the child.
|
||||
* @param[in] doerr If non-zero, stderr will be directed to the pipe as well.
|
||||
* The pipe for the parent to write
|
||||
* to the child is closed and cannot be used.
|
||||
*
|
||||
* When child process is done with the pipe setup, execute the specified
|
||||
* command, execv(argv[0], argv).
|
||||
*
|
||||
* When parent is done with the pipe setup it will read output from the child
|
||||
* until eof. The read output will be sent to the specified output callback,
|
||||
* 'outcb' function.
|
||||
*
|
||||
* @retval number Matches (processes affected).
|
||||
* @retval -1 Error.
|
||||
*/
|
||||
int
|
||||
clicon_proc_run (char *cmd,
|
||||
void (outcb)(char *),
|
||||
int doerr)
|
||||
{
|
||||
char
|
||||
**argv,
|
||||
buf[512];
|
||||
int
|
||||
outfd[2] = { -1, -1 };
|
||||
int
|
||||
n,
|
||||
argc,
|
||||
status,
|
||||
retval = -1;
|
||||
pid_t
|
||||
child;
|
||||
sigfn_t oldhandler = NULL;
|
||||
sigset_t oset;
|
||||
|
||||
argv = clicon_strsep(cmd, " \t", &argc);
|
||||
if (!argv)
|
||||
return -1;
|
||||
|
||||
if (pipe (outfd) == -1)
|
||||
goto done;
|
||||
|
||||
signal_get_mask(&oset);
|
||||
set_signal(SIGINT, clicon_proc_sigint, &oldhandler);
|
||||
|
||||
|
||||
if ((child = fork ()) < 0) {
|
||||
retval = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (child == 0) { /* Child */
|
||||
|
||||
/* Unblock all signals except TSTP */
|
||||
clicon_signal_unblock (0);
|
||||
signal (SIGTSTP, SIG_IGN);
|
||||
|
||||
close (outfd[0]); /* Close unused read ends */
|
||||
outfd[0] = -1;
|
||||
|
||||
/* Divert stdout and stderr to pipes */
|
||||
dup2 (outfd[1], STDOUT_FILENO);
|
||||
if (doerr)
|
||||
dup2 (outfd[1], STDERR_FILENO);
|
||||
|
||||
execvp (argv[0], argv);
|
||||
perror("execvp");
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
/* Parent */
|
||||
|
||||
/* Close unused write ends */
|
||||
close (outfd[1]);
|
||||
outfd[1] = -1;
|
||||
|
||||
/* Read from pipe */
|
||||
while ((n = read (outfd[0], buf, sizeof (buf)-1)) != 0) {
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
buf[n] = '\0';
|
||||
/* Pass read data to callback function is defined */
|
||||
if (outcb)
|
||||
outcb (buf);
|
||||
}
|
||||
|
||||
/* Wait for child to finish */
|
||||
if(waitpid (child, &status, 0) == child)
|
||||
retval = WEXITSTATUS(status);
|
||||
else
|
||||
retval = -1;
|
||||
|
||||
done:
|
||||
|
||||
/* Clean up all pipes */
|
||||
if (outfd[0] != -1)
|
||||
close (outfd[0]);
|
||||
if (outfd[1] != -1)
|
||||
close (outfd[1]);
|
||||
|
||||
/* Restore sigmask and fn */
|
||||
signal_set_mask (&oset);
|
||||
set_signal(SIGINT, oldhandler, NULL);
|
||||
|
||||
if(argv)
|
||||
free(argv);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Spawn command and report exit status
|
||||
*/
|
||||
int
|
||||
clicon_proc_daemon (char *cmd)
|
||||
{
|
||||
char
|
||||
**argv;
|
||||
int
|
||||
i,
|
||||
argc,
|
||||
retval = -1,
|
||||
status, status2;
|
||||
pid_t
|
||||
child,
|
||||
pid;
|
||||
struct rlimit
|
||||
rlim;
|
||||
|
||||
argv = clicon_strsep(cmd, " \t", &argc);
|
||||
if (!argv)
|
||||
return -1;
|
||||
|
||||
if ((child = fork ()) < 0) {
|
||||
clicon_err(OE_UNIX, errno, "fork");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (child == 0) { /* Child */
|
||||
|
||||
clicon_signal_unblock (0);
|
||||
if ((pid = fork ()) < 0) {
|
||||
clicon_err(OE_UNIX, errno, "fork");
|
||||
return -1;
|
||||
_exit(1);
|
||||
}
|
||||
if (pid == 0) { /* Grandchild, create new session */
|
||||
setsid();
|
||||
if (chdir("/") < 0){
|
||||
clicon_err(OE_UNIX, errno, "chdirq");
|
||||
_exit(1);
|
||||
}
|
||||
/* Close open descriptors */
|
||||
if ( ! getrlimit (RLIMIT_NOFILE, &rlim))
|
||||
for (i = 0; i < rlim.rlim_cur; i++)
|
||||
close(i);
|
||||
|
||||
if (execv (argv[0], argv) < 0) {
|
||||
clicon_err(OE_UNIX, errno, "execv");
|
||||
_exit(1);
|
||||
}
|
||||
/* Not reached */
|
||||
}
|
||||
|
||||
waitpid (pid, &status2, 0);
|
||||
_exit(status2);
|
||||
|
||||
}
|
||||
|
||||
if (waitpid (child, &status, 0) > 0)
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
if (argv)
|
||||
free(argv);
|
||||
return (retval);
|
||||
}
|
||||
|
||||
|
||||
/*! Translate group name to gid. Return -1 if error or not found.
|
||||
*/
|
||||
int
|
||||
group_name2gid(char *name, gid_t *gid)
|
||||
{
|
||||
char buf[1024];
|
||||
struct group g0;
|
||||
struct group *gr = &g0;
|
||||
struct group *gtmp;
|
||||
|
||||
gr = &g0;
|
||||
/* This leaks memory in ubuntu */
|
||||
if (getgrnam_r(name, gr, buf, sizeof(buf), >mp) < 0){
|
||||
clicon_err(OE_UNIX, errno, "%s: getgrnam_r(%s): %s",
|
||||
__FUNCTION__, name, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (gtmp == NULL){
|
||||
clicon_err(OE_UNIX, 0, "%s: No such group: %s", __FUNCTION__, name);
|
||||
fprintf(stderr, "No such group %s\n", name);
|
||||
return -1;
|
||||
}
|
||||
if (gid)
|
||||
*gid = gr->gr_gid;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -66,7 +66,6 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_sig.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
|
|
@ -298,6 +297,8 @@ clicon_msg_send(int s,
|
|||
if (atomicio((ssize_t (*)(int, void *, size_t))write,
|
||||
s, msg, ntohs(msg->op_len)) < 0){
|
||||
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
|
||||
clicon_log(LOG_WARNING, "%s: write: %s len:%d msg:%s", __FUNCTION__,
|
||||
strerror(errno), ntohs(msg->op_len), msg->op_body);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -532,11 +533,11 @@ send_msg_reply(int s,
|
|||
uint16_t datalen)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_msg *reply;
|
||||
struct clicon_msg *reply = NULL;
|
||||
uint16_t len;
|
||||
|
||||
len = sizeof(*reply) + datalen;
|
||||
if ((reply = (struct clicon_msg *)chunk(len, __FUNCTION__)) == NULL)
|
||||
if ((reply = (struct clicon_msg *)malloc(len)) == NULL)
|
||||
goto done;
|
||||
memset(reply, 0, len);
|
||||
reply->op_len = htons(len);
|
||||
|
|
@ -546,7 +547,8 @@ send_msg_reply(int s,
|
|||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (reply)
|
||||
free(reply);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@
|
|||
|
||||
/* clicon */
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
|
|
|
|||
|
|
@ -47,9 +47,10 @@
|
|||
#include <regex.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_err.h"
|
||||
|
||||
|
|
@ -138,6 +139,200 @@ clicon_strjoin(int argc,
|
|||
return str;
|
||||
}
|
||||
|
||||
static int
|
||||
unreserved(unsigned char in)
|
||||
{
|
||||
switch(in) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
|
||||
case 'A': case 'B': case 'C': case 'D': case 'E':
|
||||
case 'F': case 'G': case 'H': case 'I': case 'J':
|
||||
case 'K': case 'L': case 'M': case 'N': case 'O':
|
||||
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
||||
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
||||
case '-': case '.': case '_': case '~':
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Percent encoding according to RFC 3896
|
||||
* @param[out] esc Deallocate with free()
|
||||
*/
|
||||
int
|
||||
percent_encode(char *str,
|
||||
char **escp)
|
||||
{
|
||||
int retval = -1;
|
||||
char *esc = NULL;
|
||||
int len;
|
||||
int i, j;
|
||||
|
||||
/* This is max */
|
||||
len = strlen(str)*3+1;
|
||||
if ((esc = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(esc, 0, len);
|
||||
j = 0;
|
||||
for (i=0; i<strlen(str); i++){
|
||||
if (unreserved(str[i]))
|
||||
esc[j++] = str[i];
|
||||
else{
|
||||
snprintf(&esc[j], 4, "%%%02X", str[i]&0xff);
|
||||
j += 3;
|
||||
}
|
||||
}
|
||||
*escp = esc;
|
||||
retval = 0;
|
||||
done:
|
||||
if (retval < 0 && esc)
|
||||
free(esc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Percent decoding according to RFC 3896
|
||||
* @param[out] str Deallocate with free()
|
||||
*/
|
||||
int
|
||||
percent_decode(char *esc,
|
||||
char **strp)
|
||||
{
|
||||
int retval = -1;
|
||||
char *str = NULL;
|
||||
int i, j;
|
||||
char hstr[3];
|
||||
int len;
|
||||
char *ptr;
|
||||
|
||||
/* This is max */
|
||||
len = strlen(esc)+1;
|
||||
if ((str = malloc(len)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(str, 0, len);
|
||||
j = 0;
|
||||
for (i=0; i<strlen(esc); i++){
|
||||
if (esc[i] == '%' && strlen(esc)-i > 2 &&
|
||||
isxdigit(esc[i+1]) && isxdigit(esc[i+2])){
|
||||
hstr[0] = esc[i+1];
|
||||
hstr[1] = esc[i+2];
|
||||
hstr[2] = 0;
|
||||
str[j] = strtoul(hstr, &ptr, 16);
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
str[j] = esc[i];
|
||||
j++;
|
||||
}
|
||||
str[j++] = '\0';
|
||||
*strp = str;
|
||||
retval = 0;
|
||||
done:
|
||||
if (retval < 0 && str)
|
||||
free(str);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Split a string into a cligen variable vector using 1st and 2nd delimiter
|
||||
* Split a string first into elements delimited by delim1, then into
|
||||
* pairs delimited by delim2.
|
||||
* @param[in] string String to split
|
||||
* @param[in] delim1 First delimiter char that delimits between elements
|
||||
* @param[in] delim2 Second delimiter char for pairs within an element
|
||||
* @param[out] cvp Created cligen variable vector, deallocate w cvec_free
|
||||
* @retval 0 on OK
|
||||
* @retval -1 error
|
||||
*
|
||||
* @example,
|
||||
* Assuming delim1 = '&' and delim2 = '='
|
||||
* a=b&c=d -> [[a,"b"][c="d"]
|
||||
* kalle&c=d -> [[c="d"]] # Discard elements with no delim2
|
||||
* XXX differentiate between error and null cvec.
|
||||
*/
|
||||
int
|
||||
str2cvec(char *string,
|
||||
char delim1,
|
||||
char delim2,
|
||||
cvec **cvp)
|
||||
{
|
||||
int retval = -1;
|
||||
char *s;
|
||||
char *s0 = NULL;;
|
||||
char *val; /* value */
|
||||
char *valu; /* unescaped value */
|
||||
char *snext; /* next element in string */
|
||||
cvec *cvv = NULL;
|
||||
cg_var *cv;
|
||||
|
||||
if ((s0 = strdup(string)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto err;
|
||||
}
|
||||
s = s0;
|
||||
if ((cvv = cvec_new(0)) ==NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto err;
|
||||
}
|
||||
while (s != NULL) {
|
||||
/*
|
||||
* In the pointer algorithm below:
|
||||
* name1=val1; name2=val2;
|
||||
* ^ ^ ^
|
||||
* | | |
|
||||
* s val snext
|
||||
*/
|
||||
if ((snext = index(s, delim1)) != NULL)
|
||||
*(snext++) = '\0';
|
||||
if ((val = index(s, delim2)) != NULL){
|
||||
*(val++) = '\0';
|
||||
if (percent_decode(val, &valu) < 0)
|
||||
goto err;
|
||||
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||
goto err;
|
||||
}
|
||||
while ((strlen(s) > 0) && isblank(*s))
|
||||
s++;
|
||||
cv_name_set(cv, s);
|
||||
cv_string_set(cv, valu);
|
||||
free(valu); valu = NULL;
|
||||
}
|
||||
else{
|
||||
if (strlen(s)){
|
||||
if ((cv = cvec_add(cvv, CGV_STRING)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||
goto err;
|
||||
}
|
||||
cv_name_set(cv, s);
|
||||
cv_string_set(cv, "");
|
||||
}
|
||||
}
|
||||
s = snext;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
*cvp = cvv;
|
||||
if (s0)
|
||||
free(s0);
|
||||
return retval;
|
||||
err:
|
||||
if (cvv){
|
||||
cvec_free(cvv);
|
||||
cvv = NULL;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/*! strndup() for systems without it, such as xBSD
|
||||
*/
|
||||
|
|
@ -163,6 +358,8 @@ clicon_strndup (const char *str,
|
|||
}
|
||||
#endif /* ! HAVE_STRNDUP */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Turn this on for uni-test programs
|
||||
* Usage: clixon_string join
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
|
||||
|
|
@ -76,7 +75,6 @@ struct xml{
|
|||
int x_childvec_len; /* length of vector */
|
||||
enum cxobj_type x_type; /* type of node: element, attribute, body */
|
||||
char *x_value; /* attribute and body nodes have values */
|
||||
int x_index; /* key node, cf sql index */
|
||||
int _x_vector_i; /* internal use: xml_child_each */
|
||||
int x_flags; /* Flags according to XML_FLAG_* above */
|
||||
void *x_spec; /* Pointer to specification, eg yang, by
|
||||
|
|
@ -84,6 +82,36 @@ struct xml{
|
|||
cg_var *x_cv; /* If body this contains the typed value */
|
||||
};
|
||||
|
||||
/* Type to string conversion */
|
||||
struct map_str2int{
|
||||
char *ms_str;
|
||||
enum cxobj_type ms_type;
|
||||
};
|
||||
|
||||
/* Mapping between xml type <--> string */
|
||||
static const struct map_str2int xsmap[] = {
|
||||
{"error", CX_ERROR},
|
||||
{"element", CX_ELMNT},
|
||||
{"attr", CX_ATTR},
|
||||
{"body", CX_BODY},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! Translate from xml type in enum form to string keyword
|
||||
* @param[in] type Xml type
|
||||
* @retval str String keyword
|
||||
*/
|
||||
char *
|
||||
xml_type2str(enum cxobj_type type)
|
||||
{
|
||||
const struct map_str2int *xs;
|
||||
|
||||
for (xs = &xsmap[0]; xs->ms_str; xs++)
|
||||
if (xs->ms_type == type)
|
||||
return xs->ms_str;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Access functions
|
||||
*/
|
||||
|
|
@ -223,7 +251,7 @@ xml_value(cxobj *xn)
|
|||
|
||||
/*! Set value of xml node, value is copied
|
||||
* @param[in] xn xml node
|
||||
* @param[in] val new value, null-terminated string, copied by function
|
||||
* @param[in] val new value, null-terminated string, copied by function
|
||||
* @retval -1 on error with clicon-err set
|
||||
* @retval 0 OK
|
||||
*/
|
||||
|
|
@ -294,33 +322,6 @@ xml_type_set(cxobj *xn,
|
|||
return old;
|
||||
}
|
||||
|
||||
/*! Get index/key of xnode
|
||||
* @param[in] xn xml node
|
||||
* @retval index of xml node
|
||||
* index/key is used in case of yang list constructs where one element is key
|
||||
*/
|
||||
int
|
||||
xml_index(cxobj *xn)
|
||||
{
|
||||
return xn->x_index;
|
||||
}
|
||||
|
||||
/*! Set index of xnode
|
||||
* @param[in] xn xml node
|
||||
* @param[in] index new index
|
||||
* @retval index old index
|
||||
* index/key is used in case of yang list constructs where one element is key
|
||||
*/
|
||||
int
|
||||
xml_index_set(cxobj *xn,
|
||||
int index)
|
||||
{
|
||||
int old = xn->x_index;
|
||||
|
||||
xn->x_index = index;
|
||||
return old;
|
||||
}
|
||||
|
||||
/*! Get cligen variable associated with node
|
||||
* @param[in] xn xml node
|
||||
* @retval cv Cligen variable if set
|
||||
|
|
@ -520,12 +521,21 @@ xml_new_spec(char *name,
|
|||
return x;
|
||||
}
|
||||
|
||||
|
||||
void *
|
||||
xml_spec(cxobj *x)
|
||||
{
|
||||
return x->x_spec;
|
||||
}
|
||||
|
||||
void *
|
||||
xml_spec_set(cxobj *x,
|
||||
void *spec)
|
||||
{
|
||||
x->x_spec = spec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Find an XML node matching name among a parent's children.
|
||||
*
|
||||
* Get first XML node directly under x_up in the xml hierarchy with
|
||||
|
|
@ -758,6 +768,16 @@ xml_body(cxobj *xn)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
cxobj *
|
||||
xml_body_get(cxobj *xn)
|
||||
{
|
||||
cxobj *xb = NULL;
|
||||
|
||||
while ((xb = xml_child_each(xn, xb, CX_BODY)) != NULL)
|
||||
return xb;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Find and return the value of a sub xml node
|
||||
*
|
||||
* The value can be of an attribute or body.
|
||||
|
|
@ -844,8 +864,8 @@ clicon_xml2file(FILE *f,
|
|||
int level,
|
||||
int prettyprint)
|
||||
{
|
||||
cbuf *cb;
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
|
|
@ -882,8 +902,8 @@ xml_print(FILE *f,
|
|||
/*! Print an XML tree structure to a cligen buffer
|
||||
*
|
||||
* @param[in,out] cb Cligen buffer to write to
|
||||
* @param[in] xn clicon xml tree
|
||||
* @param[in] level how many spaces to insert before each line
|
||||
* @param[in] xn Clicon xml tree
|
||||
* @param[in] level Indentation level
|
||||
* @param[in] prettyprint insert \n and spaces tomake the xml more readable.
|
||||
*
|
||||
* @code
|
||||
|
|
@ -897,47 +917,50 @@ xml_print(FILE *f,
|
|||
*/
|
||||
int
|
||||
clicon_xml2cbuf(cbuf *cb,
|
||||
cxobj *cx,
|
||||
cxobj *x,
|
||||
int level,
|
||||
int prettyprint)
|
||||
{
|
||||
cxobj *xc;
|
||||
char *name;
|
||||
|
||||
switch(xml_type(cx)){
|
||||
name = xml_name(x);
|
||||
switch(xml_type(x)){
|
||||
case CX_BODY:
|
||||
cprintf(cb, "%s", xml_value(cx));
|
||||
cprintf(cb, "%s", xml_value(x));
|
||||
break;
|
||||
case CX_ATTR:
|
||||
cprintf(cb, " ");
|
||||
if (xml_namespace(cx))
|
||||
cprintf(cb, "%s:", xml_namespace(cx));
|
||||
cprintf(cb, "%s=\"%s\"", xml_name(cx), xml_value(cx));
|
||||
if (xml_namespace(x))
|
||||
cprintf(cb, "%s:", xml_namespace(x));
|
||||
cprintf(cb, "%s=\"%s\"", name, xml_value(x));
|
||||
break;
|
||||
case CX_ELMNT:
|
||||
cprintf(cb, "%*s<", prettyprint?(level*XML_INDENT):0, "");
|
||||
if (xml_namespace(cx))
|
||||
cprintf(cb, "%s:", xml_namespace(cx));
|
||||
cprintf(cb, "%s", xml_name(cx));
|
||||
if (xml_namespace(x))
|
||||
cprintf(cb, "%s:", xml_namespace(x));
|
||||
cprintf(cb, "%s", name);
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(cx, xc, CX_ATTR)) != NULL)
|
||||
/* print attributes only */
|
||||
while ((xc = xml_child_each(x, xc, CX_ATTR)) != NULL)
|
||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
||||
/* Check for special case <a/> instead of <a></a> */
|
||||
if (xml_body(cx)==NULL && xml_child_nr(cx)==0)
|
||||
if (xml_body(x)==NULL && xml_child_nr(x)==0)
|
||||
cprintf(cb, "/>");
|
||||
else{
|
||||
cprintf(cb, ">");
|
||||
if (prettyprint && xml_body(cx)==NULL)
|
||||
if (prettyprint && xml_body(x)==NULL)
|
||||
cprintf(cb, "\n");
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(cx, xc, -1)) != NULL) {
|
||||
while ((xc = xml_child_each(x, xc, -1)) != NULL) {
|
||||
if (xml_type(xc) == CX_ATTR)
|
||||
continue;
|
||||
else
|
||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
||||
}
|
||||
if (prettyprint && xml_body(cx)==NULL)
|
||||
if (prettyprint && xml_body(x)==NULL)
|
||||
cprintf(cb, "%*s", level*XML_INDENT, "");
|
||||
cprintf(cb, "</%s>", xml_name(cx));
|
||||
cprintf(cb, "</%s>", name);
|
||||
}
|
||||
if (prettyprint)
|
||||
cprintf(cb, "\n");
|
||||
|
|
@ -976,6 +999,44 @@ xml_parse(char *str,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Print actual xml tree datastructures (not xml), mainly for debugging
|
||||
* @param[in,out] cb Cligen buffer to write to
|
||||
* @param[in] xn Clicon xml tree
|
||||
* @param[in] level Indentation level
|
||||
*/
|
||||
int
|
||||
xmltree2cbuf(cbuf *cb,
|
||||
cxobj *x,
|
||||
int level)
|
||||
{
|
||||
cxobj *xc;
|
||||
int i;
|
||||
|
||||
for (i=0; i<level*XML_INDENT; i++)
|
||||
cprintf(cb, " ");
|
||||
cprintf(cb, "%s", xml_type2str(xml_type(x)));
|
||||
if (xml_namespace(x)==NULL)
|
||||
cprintf(cb, " %s", xml_name(x));
|
||||
else
|
||||
cprintf(cb, " %s:%s", xml_namespace(x), xml_name(x));
|
||||
if (xml_value(x))
|
||||
cprintf(cb, " value:\"%s\"", xml_value(x));
|
||||
if (x->x_flags)
|
||||
cprintf(cb, " flags:0x%x", x->x_flags);
|
||||
if (xml_child_nr(x))
|
||||
cprintf(cb, " {");
|
||||
cprintf(cb, "\n");
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(x, xc, -1)) != NULL)
|
||||
xmltree2cbuf(cb, xc, level+1);
|
||||
if (xml_child_nr(x)){
|
||||
for (i=0; i<level*XML_INDENT; i++)
|
||||
cprintf(cb, " ");
|
||||
cprintf(cb, "}\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* FSM to detect a substring
|
||||
*/
|
||||
|
|
@ -1009,6 +1070,7 @@ FSM(char *tag,
|
|||
* Note, xt will add a top-level symbol called "top" meaning that <tree../> will look as:
|
||||
* <top><tree.../></tree>
|
||||
* XXX: There is a potential leak here on some return values.
|
||||
* XXX: What happens if endtag is different?
|
||||
* May block
|
||||
*/
|
||||
int
|
||||
|
|
@ -1016,59 +1078,69 @@ clicon_xml_parse_file(int fd,
|
|||
cxobj **cx,
|
||||
char *endtag)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
int len = 0;
|
||||
char ch;
|
||||
int retval;
|
||||
char *xmlbuf;
|
||||
char *xmlbuf = NULL;
|
||||
char *ptr;
|
||||
int maxbuf = BUFLEN;
|
||||
int endtaglen = strlen(endtag);
|
||||
int state = 0;
|
||||
int oldmaxbuf;
|
||||
|
||||
if (endtag == NULL){
|
||||
clicon_err(OE_XML, 0, "%s: endtag required\n", __FUNCTION__);
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
*cx = NULL;
|
||||
if ((xmlbuf = malloc(maxbuf)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
memset(xmlbuf, 0, maxbuf);
|
||||
ptr = xmlbuf;
|
||||
while (1){
|
||||
if ((retval = read(fd, &ch, 1)) < 0){
|
||||
if ((ret = read(fd, &ch, 1)) < 0){
|
||||
clicon_err(OE_XML, errno, "%s: read: [pid:%d]\n",
|
||||
__FUNCTION__,
|
||||
(int)getpid());
|
||||
break;
|
||||
}
|
||||
if (retval != 0){
|
||||
if (ret != 0){
|
||||
state = FSM(endtag, ch, state);
|
||||
xmlbuf[len++] = ch;
|
||||
}
|
||||
if (retval == 0 || state == endtaglen){
|
||||
if (ret == 0 || state == endtaglen){
|
||||
state = 0;
|
||||
if ((*cx = xml_new("top", NULL)) == NULL)
|
||||
break;
|
||||
if (xml_parse(ptr, *cx) < 0)
|
||||
return -1;
|
||||
if (xml_parse(ptr, *cx) < 0){
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (len>=maxbuf-1){ /* Space: one for the null character */
|
||||
int oldmaxbuf = maxbuf;
|
||||
|
||||
oldmaxbuf = maxbuf;
|
||||
maxbuf *= 2;
|
||||
if ((xmlbuf = realloc(xmlbuf, maxbuf)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
|
||||
return -1;
|
||||
goto done;
|
||||
}
|
||||
memset(xmlbuf+oldmaxbuf, 0, maxbuf-oldmaxbuf);
|
||||
ptr = xmlbuf;
|
||||
}
|
||||
} /* while */
|
||||
free(xmlbuf);
|
||||
return (*cx)?0:-1;
|
||||
retval = 0;
|
||||
done:
|
||||
if (retval < 0 && *cx){
|
||||
free(*cx);
|
||||
*cx = NULL;
|
||||
}
|
||||
if (xmlbuf)
|
||||
free(xmlbuf);
|
||||
return retval;
|
||||
// return (*cx)?0:-1;
|
||||
}
|
||||
|
||||
/*! Read an XML definition from string and parse it into a parse-tree.
|
||||
|
|
@ -1182,7 +1254,7 @@ copy_one(cxobj *xn0,
|
|||
* x1 should be a created placeholder. If x1 is non-empty,
|
||||
* the copied tree is appended to the existing tree.
|
||||
* @code
|
||||
* x1 = xml_new("new", xc);
|
||||
* x1 = xml_new("new", xparent);
|
||||
* xml_copy(x0, x1);
|
||||
* @endcode
|
||||
*/
|
||||
|
|
@ -1190,7 +1262,7 @@ int
|
|||
xml_copy(cxobj *x0,
|
||||
cxobj *x1)
|
||||
{
|
||||
int retval = -1;
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
cxobj *xcopy;
|
||||
|
||||
|
|
@ -1462,8 +1534,12 @@ xml_body_uint32(cxobj *xb,
|
|||
}
|
||||
|
||||
/*! Map xml operation from string to enumeration
|
||||
* @param[in] xn XML node
|
||||
* @param[out] op "operation" attribute may change operation
|
||||
* @param[in] opstr String, eg "merge"
|
||||
* @param[out] op Enumeration, eg OP_MERGE
|
||||
* @code
|
||||
* enum operation_type op;
|
||||
* xml_operation("replace", &op)
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_operation(char *opstr,
|
||||
|
|
@ -1488,6 +1564,14 @@ xml_operation(char *opstr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Map xml operation from enumeration to string
|
||||
* @param[in] op enumeration operation, eg OP_MERGE,...
|
||||
* @retval str String, eg "merge". Static string, no free necessary
|
||||
* @code
|
||||
* enum operation_type op;
|
||||
* xml_operation("replace", &op)
|
||||
* @endcode
|
||||
*/
|
||||
char *
|
||||
xml_operation2str(enum operation_type op)
|
||||
{
|
||||
|
|
@ -1511,3 +1595,52 @@ xml_operation2str(enum operation_type op)
|
|||
return "none";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn this on to get a xml parse and pretty print test program
|
||||
* Usage: xpath
|
||||
* read xml from input
|
||||
* Example compile:
|
||||
gcc -g -o xml -I. -I../clixon ./clixon_xml.c -lclixon -lcligen
|
||||
* Example run:
|
||||
echo "<a><b/></a>" | xml
|
||||
*/
|
||||
#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();
|
||||
|
||||
if (argc != 1){
|
||||
usage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (clicon_xml_parse_file(0, &xt, "</config>") < 0){
|
||||
fprintf(stderr, "parsing 2\n");
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* Test program */
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -75,10 +75,11 @@
|
|||
#include "clixon_string.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xsl.h"
|
||||
|
|
@ -89,9 +90,6 @@
|
|||
/* Something to do with reverse engineering of junos syntax? */
|
||||
#undef SPECIAL_TREATMENT_OF_NAME
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A node is a leaf if it contains a body.
|
||||
*/
|
||||
|
|
@ -127,8 +125,9 @@ xml2txt(FILE *f, cxobj *x, int level)
|
|||
{
|
||||
cxobj *xe = NULL;
|
||||
int children=0;
|
||||
char *term;
|
||||
char *term = NULL;
|
||||
int retval = -1;
|
||||
int encr=0;
|
||||
#ifdef SPECIAL_TREATMENT_OF_NAME
|
||||
cxobj *xname;
|
||||
#endif
|
||||
|
|
@ -140,15 +139,17 @@ xml2txt(FILE *f, cxobj *x, int level)
|
|||
if (xml_type(x) == CX_BODY){
|
||||
/* Kludge for escaping encrypted passwords */
|
||||
if (strcmp(xml_name(xml_parent(x)), "encrypted-password")==0)
|
||||
term = chunk_sprintf(__FUNCTION__, "\"%s\"", xml_value(x));
|
||||
else
|
||||
term = xml_value(x);
|
||||
encr++;
|
||||
term = xml_value(x);
|
||||
}
|
||||
else{
|
||||
fprintf(f, "%*s", 4*level, "");
|
||||
term = xml_name(x);
|
||||
}
|
||||
fprintf(f, "%s;\n", term);
|
||||
if (encr)
|
||||
fprintf(f, "\"%s\";\n", term);
|
||||
else
|
||||
fprintf(f, "%s;\n", term);
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -240,7 +241,7 @@ xml2cli(FILE *f,
|
|||
!index GT_VARS T
|
||||
!index GT_ALL T
|
||||
*/
|
||||
bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS && !xml_index(x));
|
||||
bool = !leaf(x) || gt == GT_ALL || (gt == GT_VARS);
|
||||
// bool = (!x->xn_index || gt == GT_ALL);
|
||||
if (bool){
|
||||
if (cbuf_len(cbpre))
|
||||
|
|
@ -252,12 +253,12 @@ xml2cli(FILE *f,
|
|||
i = 0;
|
||||
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
||||
/* Dont call this if it is index and there are other following */
|
||||
if (xml_index(xe) && i < nr-1)
|
||||
if (0 && i < nr-1)
|
||||
;
|
||||
else
|
||||
if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0)
|
||||
goto done;
|
||||
if (xml_index(xe)){ /* assume index is first, otherwise need one more while */
|
||||
if (0){ /* assume index is first, otherwise need one more while */
|
||||
if (gt == GT_ALL)
|
||||
cprintf(cbpre, " %s", xml_name(xe));
|
||||
cprintf(cbpre, " %s", xml_value(xml_child_i(xe, 0)));
|
||||
|
|
@ -791,3 +792,608 @@ xml_diff(yang_spec *yspec,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Construct an xml key format from yang statement using wildcards for keys
|
||||
* Recursively construct it to the top.
|
||||
* Example:
|
||||
* yang: container a -> list b -> key c -> leaf d
|
||||
* xpath: /a/b/%s/d
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] inclkey If inclkey then include key leaf (eg last leaf d in ex)
|
||||
* @param[out] cbuf keyfmt
|
||||
*/
|
||||
static int
|
||||
yang2xmlkeyfmt_1(yang_stmt *ys,
|
||||
int inclkey,
|
||||
cbuf *cb)
|
||||
{
|
||||
yang_node *yp; /* parent */
|
||||
yang_stmt *ykey;
|
||||
int i;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
int retval = -1;
|
||||
|
||||
yp = ys->ys_parent;
|
||||
if (yp != NULL &&
|
||||
yp->yn_keyword != Y_MODULE &&
|
||||
yp->yn_keyword != Y_SUBMODULE){
|
||||
if (yang2xmlkeyfmt_1((yang_stmt *)yp, 1, cb) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (inclkey){
|
||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||
cprintf(cb, "/%s", ys->ys_argument);
|
||||
}
|
||||
else{
|
||||
if (ys->ys_keyword == Y_LEAF && yp && yp->yn_keyword == Y_LIST){
|
||||
if (yang_key_match(yp, ys->ys_argument) == 0)
|
||||
cprintf(cb, "/%s", ys->ys_argument); /* Not if leaf and key */
|
||||
}
|
||||
else
|
||||
if (ys->ys_keyword != Y_CHOICE && ys->ys_keyword != Y_CASE)
|
||||
cprintf(cb, "/%s", ys->ys_argument);
|
||||
}
|
||||
|
||||
switch (ys->ys_keyword){
|
||||
case Y_LIST:
|
||||
if ((ykey = yang_find((yang_node*)ys, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
if (cvec_len(cvk))
|
||||
cprintf(cb, "=");
|
||||
/* Iterate over individual keys */
|
||||
for (i=0; i<cvec_len(cvk); i++){
|
||||
if (i)
|
||||
cprintf(cb, ",");
|
||||
cprintf(cb, "%%s");
|
||||
}
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
cprintf(cb, "=%%s");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} /* switch */
|
||||
retval = 0;
|
||||
done:
|
||||
if (cvk)
|
||||
cvec_free(cvk);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Construct an xml key format from yang statement using wildcards for keys
|
||||
* Recursively construct it to the top.
|
||||
* Example:
|
||||
* yang: container a -> list b -> key c -> leaf d
|
||||
* xpath: /a/b=%s/d
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] inclkey If !inclkey then dont include key leaf
|
||||
* @param[out] xkfmt XML key format. Needs to be freed after use.
|
||||
*/
|
||||
int
|
||||
yang2xmlkeyfmt(yang_stmt *ys,
|
||||
int inclkey,
|
||||
char **xkfmt)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (yang2xmlkeyfmt_1(ys, inclkey, cb) < 0)
|
||||
goto done;
|
||||
if ((*xkfmt = strdup(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Transform an xml key format and a vector of values to an XML key
|
||||
* Used for actual key, eg in clicon_rpc_change(), xmldb_put_xkey()
|
||||
* Example:
|
||||
* xmlkeyfmt: /aaa/%s
|
||||
* cvv: key=17
|
||||
* xmlkey: /aaa/17
|
||||
* @param[in] xkfmt XML key format, eg /aaa/%s
|
||||
* @param[in] cvv cligen variable vector, one for every wildchar in xkfmt
|
||||
* @param[out] xk XML key, eg /aaa/17. Free after use
|
||||
* @note first and last elements of cvv are not used,..
|
||||
* @see cli_dbxml where this function is called
|
||||
*/
|
||||
int
|
||||
xmlkeyfmt2key(char *xkfmt,
|
||||
cvec *cvv,
|
||||
char **xk)
|
||||
{
|
||||
int retval = -1;
|
||||
char c;
|
||||
int esc=0;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
int j;
|
||||
char *str;
|
||||
char *strenc=NULL;
|
||||
|
||||
|
||||
/* Sanity check */
|
||||
#if 1
|
||||
j = 0; /* Count % */
|
||||
for (i=0; i<strlen(xkfmt); i++)
|
||||
if (xkfmt[i] == '%')
|
||||
j++;
|
||||
if (j+2 < cvec_len(cvv)) {
|
||||
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
|
||||
xkfmt,
|
||||
j,
|
||||
cvec_len(cvv),
|
||||
cv_string_get(cvec_i(cvv, 0)));
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
j = 1; /* j==0 is cli string */
|
||||
for (i=0; i<strlen(xkfmt); i++){
|
||||
c = xkfmt[i];
|
||||
if (esc){
|
||||
esc = 0;
|
||||
if (c!='s')
|
||||
continue;
|
||||
if ((str = cv2str_dup(cvec_i(cvv, j++))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if (percent_encode(str, &strenc) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "%s", strenc);
|
||||
free(strenc); strenc = NULL;
|
||||
free(str); str = NULL;
|
||||
}
|
||||
else
|
||||
if (c == '%')
|
||||
esc++;
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
if ((*xk = strdup(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Transform an xml key format and a vector of values to an XML path
|
||||
* Used to input xmldb_get() or xmldb_get_vec
|
||||
* Add .* in last %s position.
|
||||
* Example:
|
||||
* xmlkeyfmt: /interface/%s/address/%s OLDXXX
|
||||
* xmlkeyfmt: /interface=%s/address=%s
|
||||
* cvv: name=eth0
|
||||
* xmlkey: /interface/[name=eth0]/address
|
||||
* Example2:
|
||||
* xmlkeyfmt: /ip/me/%s (if key)
|
||||
* cvv: -
|
||||
* xmlkey: /ipv4/me/a
|
||||
* @param[in] xkfmt XML key format
|
||||
* @param[in] cvv cligen variable vector, one for every wildchar in xkfmt
|
||||
* @param[out] xk XPATH
|
||||
*/
|
||||
int
|
||||
xmlkeyfmt2xpath(char *xkfmt,
|
||||
cvec *cvv,
|
||||
char **xk)
|
||||
{
|
||||
int retval = -1;
|
||||
char c;
|
||||
int esc=0;
|
||||
cbuf *cb = NULL;
|
||||
int i;
|
||||
int j;
|
||||
char *str;
|
||||
cg_var *cv;
|
||||
int skip = 0;
|
||||
|
||||
/* Sanity check: count '%' */
|
||||
#if 1
|
||||
j = 0; /* Count % */
|
||||
for (i=0; i<strlen(xkfmt); i++)
|
||||
if (xkfmt[i] == '%')
|
||||
j++;
|
||||
if (j < cvec_len(cvv)-1) {
|
||||
clicon_log(LOG_WARNING, "%s xmlkey format string mismatch(j=%d, cvec_len=%d): %s",
|
||||
xkfmt,
|
||||
j,
|
||||
cvec_len(cvv),
|
||||
cv_string_get(cvec_i(cvv, 0)));
|
||||
// goto done;
|
||||
}
|
||||
#endif
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
j = 1; /* j==0 is cli string */
|
||||
for (i=0; i<strlen(xkfmt); i++){
|
||||
c = xkfmt[i];
|
||||
if (esc){
|
||||
esc = 0;
|
||||
if (c!='s')
|
||||
continue;
|
||||
|
||||
if (j == cvec_len(cvv)) /* last element */
|
||||
//skip++;
|
||||
;
|
||||
else{
|
||||
cv = cvec_i(cvv, j++);
|
||||
if ((str = cv2str_dup(cv)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "[%s=%s]", cv_name_get(cv), str);
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
else /* regular char */
|
||||
if (c == '%')
|
||||
esc++;
|
||||
else{
|
||||
if (skip)
|
||||
skip=0;
|
||||
else
|
||||
if ((c == '=' || c == ',') && xkfmt[i+1]=='%')
|
||||
; /* skip */
|
||||
else
|
||||
cprintf(cb, "%c", c);
|
||||
}
|
||||
}
|
||||
if ((*xk = strdup4(cbuf_get(cb))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Prune everything that does not pass test
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] flag Which flag to test for
|
||||
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
||||
* @param[out] upmark Set if a child (recursively) has marked set.
|
||||
* The function removes all branches that does not a child that pass the test
|
||||
* Purge all nodes that dont have MARK flag set recursively.
|
||||
* Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
|
||||
* @code
|
||||
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_tree_prune_flagged(cxobj *xt,
|
||||
int flag,
|
||||
int test,
|
||||
int *upmark)
|
||||
{
|
||||
int retval = -1;
|
||||
int submark;
|
||||
int mark;
|
||||
cxobj *x;
|
||||
cxobj *xprev;
|
||||
|
||||
mark = 0;
|
||||
x = NULL;
|
||||
xprev = x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if (xml_flag(x, flag) == test?flag:0){
|
||||
mark++;
|
||||
xprev = x;
|
||||
continue; /* mark and stop here */
|
||||
}
|
||||
if (xml_tree_prune_flagged(x, flag, test, &submark) < 0)
|
||||
goto done;
|
||||
if (submark)
|
||||
mark++;
|
||||
else{ /* Safe with xml_child_each if last */
|
||||
if (xml_purge(x) < 0)
|
||||
goto done;
|
||||
x = xprev;
|
||||
}
|
||||
xprev = x;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (upmark)
|
||||
*upmark = mark;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Add default values (if not set)
|
||||
* @param[in] xt XML tree with some node marked
|
||||
*/
|
||||
int
|
||||
xml_default(cxobj *xt,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys;
|
||||
yang_stmt *y;
|
||||
int i;
|
||||
cxobj *xc;
|
||||
cxobj *xb;
|
||||
char *str;
|
||||
|
||||
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||
clicon_log(LOG_WARNING, "%s: no xml_spec(%s)", __FUNCTION__, xml_name(xt));
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
/* Check leaf defaults */
|
||||
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST){
|
||||
for (i=0; i<ys->ys_len; i++){
|
||||
y = ys->ys_stmt[i];
|
||||
if (y->ys_keyword != Y_LEAF)
|
||||
continue;
|
||||
assert(y->ys_cv);
|
||||
if (!cv_flag(y->ys_cv, V_UNSET)){ /* Default value exists */
|
||||
if (!xml_find(xt, y->ys_argument)){
|
||||
if ((xc = xml_new_spec(y->ys_argument, xt, y)) == NULL)
|
||||
goto done;
|
||||
if ((xb = xml_new("body", xc)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xb, CX_BODY);
|
||||
if ((str = cv2str_dup(y->ys_cv)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv2str_dup");
|
||||
goto done;
|
||||
}
|
||||
if (xml_value_set(xb, str) < 0)
|
||||
goto done;
|
||||
free(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Order XML children according to YANG
|
||||
* @param[in] xt XML top of tree
|
||||
*/
|
||||
int
|
||||
xml_order(cxobj *xt,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *y;
|
||||
yang_stmt *yc;
|
||||
int i;
|
||||
int j0;
|
||||
int j;
|
||||
cxobj *xc;
|
||||
cxobj *xj;
|
||||
char *yname; /* yang child name */
|
||||
char *xname; /* xml child name */
|
||||
|
||||
if ((y = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||
clicon_log(LOG_WARNING, "%s: no xml_spec(%s)", __FUNCTION__, xml_name(xt));
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
j0 = 0;
|
||||
/* Go through xml children and ensure they are same order as yspec children */
|
||||
for (i=0; i<y->ys_len; i++){
|
||||
yc = y->ys_stmt[i];
|
||||
if (!yang_is_syntax(yc))
|
||||
continue;
|
||||
yname = yc->ys_argument;
|
||||
/* First go thru xml children with same name */
|
||||
for (; j0<xml_child_nr(xt); j0++){
|
||||
xc = xml_child_i(xt, j0);
|
||||
if (xml_type(xc) != CX_ELMNT)
|
||||
continue;
|
||||
xname = xml_name(xc);
|
||||
if (strcmp(xname, yname))
|
||||
break;
|
||||
}
|
||||
/* Now we have children not with same name */
|
||||
for (j=j0; j<xml_child_nr(xt); j++){
|
||||
xc = xml_child_i(xt, j);
|
||||
if (xml_type(xc) != CX_ELMNT)
|
||||
continue;
|
||||
xname = xml_name(xc);
|
||||
if (strcmp(xname, yname))
|
||||
continue;
|
||||
/* reorder */
|
||||
xj = xml_child_i(xt, j0);
|
||||
xml_child_i_set(xt, j0, xc);
|
||||
xml_child_i_set(xt, j, xj);
|
||||
j0++;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Sanitize an xml tree: xml node has matching yang_stmt pointer
|
||||
* @param[in] xt XML top of tree
|
||||
*/
|
||||
int
|
||||
xml_sanity(cxobj *xt,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys;
|
||||
char *name;
|
||||
|
||||
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||
clicon_log(LOG_WARNING, "%s: no xml_spec(%s)", __FUNCTION__, xml_name(xt));
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
name = xml_name(xt);
|
||||
if (ys==NULL){
|
||||
clicon_err(OE_XML, 0, "No spec for xml node %s", name);
|
||||
goto done;
|
||||
}
|
||||
if (strstr(ys->ys_argument, name)==NULL){
|
||||
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
|
||||
name, ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate from restconf api-path in cvv form to xml xpath
|
||||
* eg a/b=c -> a/[b=c]
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] pcvec api-path as cvec
|
||||
* @param[in] pi Offset of cvec, where api-path starts
|
||||
* @param[out] path The xpath as cbuf variable string, must be initializeed
|
||||
* The api-path has some wierd encoding, use api_path2xpath() if you are
|
||||
* confused.
|
||||
* It works like this:
|
||||
* Assume origin incoming path is
|
||||
* "www.foo.com/restconf/a/b=c", pi is 2 and pcvec is:
|
||||
* ["www.foo.com" "restconf" "a" "b=c"]
|
||||
* which means the api-path is ["a" "b=c"] corresponding to "a/b=c"
|
||||
* @code
|
||||
* cbuf *xpath = cbuf_new();
|
||||
* if (api_path2xpath_cvv(yspec, cvv, i, xpath)
|
||||
* err;
|
||||
* ... access xpath as cbuf_get(xpath)
|
||||
* cbuf_free(xpath)
|
||||
* @endcode
|
||||
* @see api_path2xpath for string api-path argument
|
||||
*/
|
||||
int
|
||||
api_path2xpath_cvv(yang_spec *yspec,
|
||||
cvec *cvv,
|
||||
int offset,
|
||||
cbuf *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cg_var *cv;
|
||||
char *name;
|
||||
cvec *cvk = NULL; /* vector of index keys */
|
||||
yang_stmt *y = NULL;
|
||||
char *val;
|
||||
char *v;
|
||||
yang_stmt *ykey;
|
||||
cg_var *cvi;
|
||||
|
||||
for (i=offset; i<cvec_len(cvv); i++){
|
||||
cv = cvec_i(cvv, i);
|
||||
name = cv_name_get(cv);
|
||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||
if (i == offset){
|
||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
assert(y!=NULL);
|
||||
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Check if has value, means '=' */
|
||||
if (cv2str(cv, NULL, 0) > 0){
|
||||
if ((val = cv2str_dup(cv)) == NULL)
|
||||
goto done;
|
||||
v = val;
|
||||
/* XXX sync with yang */
|
||||
while((v=index(v, ',')) != NULL){
|
||||
*v = '\0';
|
||||
v++;
|
||||
}
|
||||
/* Find keys */
|
||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
||||
__FUNCTION__, y->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
||||
|
||||
/* The value is a list of keys: <key>[ <key>]* */
|
||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
||||
goto done;
|
||||
cvi = NULL;
|
||||
/* Iterate over individual yang keys */
|
||||
cprintf(xpath, "/%s", name);
|
||||
v = val;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
||||
cprintf(xpath, "[%s=%s]", cv_string_get(cvi), v);
|
||||
v += strlen(v)+1;
|
||||
}
|
||||
if (val)
|
||||
free(val);
|
||||
}
|
||||
else{
|
||||
cprintf(xpath, "%s%s", (i==offset?"":"/"), name);
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate from restconf api-path to xml xpath
|
||||
* eg a/b=c -> a/[b=c]
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] str API-path as string
|
||||
* @param[out] path The xpath as cbuf variable string, must be initializeed
|
||||
* @code
|
||||
* cbuf *xpath = cbuf_new();
|
||||
* if (api_path2xpath(yspec, "a/b=c", xpath)
|
||||
* err;
|
||||
* ... access xpath as cbuf_get(xpath)
|
||||
* cbuf_free(xpath)
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
api_path2xpath(yang_spec *yspec,
|
||||
char *api_path,
|
||||
cbuf *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
cvec *api_path_cvv = NULL;
|
||||
|
||||
/* rest url eg /album=ricky/foo */
|
||||
if (str2cvec(api_path, '/', '=', &api_path_cvv) < 0)
|
||||
goto done;
|
||||
if (api_path2xpath_cvv(yspec, api_path_cvv, 0, xpath) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (api_path_cvv)
|
||||
cvec_free(api_path_cvv);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,8 +175,10 @@ xml_parse_endslash_post(struct xml_parse_yacc_arg *ya)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Called at </name> */
|
||||
static int
|
||||
xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
||||
xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
|
||||
char *name)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x = ya->ya_xelement;
|
||||
|
|
@ -199,8 +201,10 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
|||
;
|
||||
else{
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL)
|
||||
xml_value_set(xc, ""); /* XXX remove */
|
||||
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) {
|
||||
xml_purge(xc);
|
||||
xc = NULL; /* reset iterator */
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -209,8 +213,11 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Called at </namespace:name> */
|
||||
static int
|
||||
xml_parse_bslash2(struct xml_parse_yacc_arg *ya, char *namespace, char *name)
|
||||
xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
|
||||
char *namespace,
|
||||
char *name)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x = ya->ya_xelement;
|
||||
|
|
|
|||
|
|
@ -358,6 +358,13 @@ xpath_parse(char *xpath,
|
|||
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
|
||||
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
|
||||
}
|
||||
#if 1
|
||||
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
|
||||
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
|
||||
#else
|
||||
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
|
||||
xpath_element_new(A_PARENT, NULL, &xpnext);
|
||||
#endif
|
||||
#if 1 /* Problems with .[userid=1321] */
|
||||
else if (strncmp(s,".", strlen("."))==0)
|
||||
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
|
||||
|
|
@ -368,13 +375,7 @@ xpath_parse(char *xpath,
|
|||
|
||||
else if (strncmp(s,"self::", strlen("self::"))==0)
|
||||
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
|
||||
#if 1
|
||||
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
|
||||
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
|
||||
#else
|
||||
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
|
||||
xpath_element_new(A_PARENT, NULL, &xpnext);
|
||||
#endif
|
||||
|
||||
else if (strncmp(s,"parent::", strlen("parent::"))==0)
|
||||
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
|
||||
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
|
||||
|
|
@ -1076,7 +1077,7 @@ int
|
|||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
cxobj **xv;
|
||||
cxobj **xv
|
||||
cxobj *x;
|
||||
cxobj *xn;
|
||||
size_t xlen = 0;
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
#include "clixon_file.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_yang_parse.h"
|
||||
|
|
@ -160,6 +160,10 @@ static const struct map_str2int ykmap[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
|
||||
/*! Create new yang specification
|
||||
* @retval yspec Free with yspec_free()
|
||||
* @retval NULL Error
|
||||
*/
|
||||
yang_spec *
|
||||
yspec_new(void)
|
||||
{
|
||||
|
|
@ -174,6 +178,10 @@ yspec_new(void)
|
|||
return yspec;
|
||||
}
|
||||
|
||||
/*! Create new yang node/statement
|
||||
* @retval ys Free with ys_free()
|
||||
* @retval NULL Error
|
||||
*/
|
||||
yang_stmt *
|
||||
ys_new(enum rfc_6020 keyw)
|
||||
{
|
||||
|
|
@ -1382,20 +1390,21 @@ yang_parse_file(clicon_handle h,
|
|||
* @retval 1 Match founbd, Most recent entry returned in fbuf
|
||||
* @retval 0 No matching entry found
|
||||
* @retval -1 Error
|
||||
*/static int
|
||||
*/
|
||||
static int
|
||||
yang_parse_find_match(clicon_handle h,
|
||||
const char *yang_dir,
|
||||
const char *module,
|
||||
cbuf *fbuf)
|
||||
{
|
||||
int retval = -1;
|
||||
struct dirent *dp;
|
||||
struct dirent *dp = NULL;
|
||||
int ndp;
|
||||
cbuf *regex = NULL;
|
||||
char *regexstr;
|
||||
|
||||
if ((regex = cbuf_new()) == NULL){
|
||||
clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
|
||||
clicon_err(OE_YANG, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(regex, "^%s.*(.yang)$", module);
|
||||
|
|
@ -1403,8 +1412,7 @@ yang_parse_find_match(clicon_handle h,
|
|||
if ((ndp = clicon_file_dirent(yang_dir,
|
||||
&dp,
|
||||
regexstr,
|
||||
S_IFREG,
|
||||
__FUNCTION__)) < 0)
|
||||
S_IFREG)) < 0)
|
||||
goto done;
|
||||
/* Entries are sorted, last entry should be most recent date */
|
||||
if (ndp != 0){
|
||||
|
|
@ -1416,7 +1424,8 @@ yang_parse_find_match(clicon_handle h,
|
|||
done:
|
||||
if (regex)
|
||||
cbuf_free(regex);
|
||||
unchunk_group(__FUNCTION__);
|
||||
if (dp)
|
||||
free(dp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1540,11 +1549,11 @@ yang_parse1(clicon_handle h,
|
|||
|
||||
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||
*
|
||||
* @param h CLICON handle
|
||||
* @param yang_dir Directory where all YANG module files reside
|
||||
* @param module Name of main YANG module. More modules may be parsed if imported
|
||||
* @param revision Optional module revision date
|
||||
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
||||
* @param[in] h CLICON handle
|
||||
* @param[in] yang_dir Directory where all YANG module files reside
|
||||
* @param[in] module Name of main YANG module. More modules may be parsed if imported
|
||||
* @param[in] revision Optional module revision date
|
||||
* @param[out] ysp Yang specification. Should ave been created by caller using yspec_new
|
||||
* @retval 0 Everything OK
|
||||
* @retval -1 Error encountered
|
||||
* The database symbols are inserted in alphabetical order.
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_chunk.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_type.h"
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
This directory contains testing code for clixon and the example
|
||||
routing application:
|
||||
clixon A top-level script clones clixon in /tmp and starts all.sh
|
||||
You can _copy_ this file (review it) 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.
|
||||
test1.sh First test
|
||||
test2.sh Second test,...
|
||||
11
test/README.md
Normal file
11
test/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Clixon tests
|
||||
|
||||
This directory contains testing code for clixon and the example
|
||||
routing application:
|
||||
- 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.
|
||||
- test1.sh CLI tests
|
||||
- test2.sh Netconf tests
|
||||
- test3.sh Restconf tests
|
||||
- test4.sh Yang tests
|
||||
- test5.sh Datastore tests
|
||||
|
|
@ -17,9 +17,8 @@ new(){
|
|||
# sleep 1
|
||||
}
|
||||
|
||||
# clicon_cli tester. First arg is command and second is expected outcome
|
||||
# clixon tester. First arg is command and second is expected outcome
|
||||
expectfn(){
|
||||
|
||||
cmd=$1
|
||||
expect=$2
|
||||
ret=`$cmd`
|
||||
|
|
@ -40,7 +39,8 @@ expectfn(){
|
|||
fi
|
||||
}
|
||||
|
||||
# clicon_cli tester. First arg is command and second is expected outcome
|
||||
# clixon tester. First arg is command second is stdin and
|
||||
# third is expected outcome
|
||||
expecteof(){
|
||||
cmd=$1
|
||||
input=$2
|
||||
|
|
@ -61,7 +61,8 @@ EOF
|
|||
fi
|
||||
}
|
||||
|
||||
# clicon_cli tester. First arg is command and second is expected outcome
|
||||
# clixon tester. First arg is command second is stdin and
|
||||
# third is expected outcome, fourth is how long to wait
|
||||
expectwait(){
|
||||
cmd=$1
|
||||
input=$2
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ sudo clixon_backend -If $clixon_cf
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "cli tests"
|
||||
|
||||
new "cli configure top"
|
||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""
|
||||
|
||||
|
|
|
|||
|
|
@ -20,14 +20,44 @@ sudo clixon_backend -If $clixon_cf
|
|||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
|
||||
new "netconf tests"
|
||||
|
||||
new "netconf get empty config"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc message-id=\"101\"><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply message-id=\"101\"><data/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "Add subtree eth0 using none which should not change anything"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><default-operation>none</default-operation><target><candidate/></target><config><interfaces><interface><name>eth0</name></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Check nothing added"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data/></rpc-reply>]]>]]>$'
|
||||
|
||||
new "Add subtree eth0 using none and create which should add eth0"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Check eth0 added using xpath"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth0]"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><config><interfaces><interface><name>eth0</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Re-create same eth0 which should generate error"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
||||
|
||||
new "Delete eth0 using none config"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Check deleted eth0"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc message-id="101"><get-config><source><candidate/></source></get-config></rpc>]]>]]>' '^<rpc-reply message-id="101"><data><config><interfaces/></config></data></rpc-reply>]]>]]>$'
|
||||
|
||||
new "Re-Delete eth0 using none should generate error"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth0</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
||||
|
||||
new "netconf edit config"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth0</name></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get config xpath"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/interfaces/interface[name=eth1]/enabled\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled"/></get-config></rpc>]]>]]>' "^<rpc-reply><data><config><interfaces><interface><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get config xpath parent"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface[name=eth1]/enabled/../.."/></get-config></rpc>]]>]]>' "^<rpc-reply><data><config><interfaces><interface><name>eth0</name><enabled>true</enabled></interface><interface><name>eth1</name><enabled>true</enabled><ipv4><enabled>true</enabled><forwarding>false</forwarding><address><ip>9.2.3.4</ip><prefix-length>24</prefix-length></address></ipv4></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf validate missing type"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
||||
|
|
@ -48,23 +78,11 @@ new "netconf commit"
|
|||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf edit config replace"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces><interface><name>eth2</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get replaced config"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf edit config create"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf edit config create 2nd"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="create"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>merge</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><rpc-error>"
|
||||
|
||||
new "netconf edit config delete"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" '<rpc><edit-config><target><candidate/></target><config><interfaces><interface operation="delete"><name>eth3</name><type>eth</type></interface></interfaces></config><default-operation>none</default-operation> </edit-config></rpc>]]>]]>' "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get delete config"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><interfaces><interface><name>eth1</name><type>eth</type><enabled>true</enabled></interface><interface><name>eth2</name><type>eth</type><enabled>true</enabled></interface></interfaces></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
|
|
|
|||
|
|
@ -24,37 +24,46 @@ sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c
|
|||
|
||||
sleep 1
|
||||
|
||||
new "restconf tests"
|
||||
|
||||
new "restconf options"
|
||||
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
||||
|
||||
new "restconf get empty config"
|
||||
expectfn "curl -sG http://localhost/restconf/data" "^null
$"
|
||||
|
||||
new "restconf put config"
|
||||
expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth1","type":"eth","enabled":"true"},{"name":"eth0","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' ""
|
||||
|
||||
new "restconf get config"
|
||||
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth0","type": "eth","enabled": "true"}\]}}
|
||||
$'
|
||||
|
||||
new "restconf head"
|
||||
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
|
||||
|
||||
new "restconf POST config"
|
||||
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||
new "restconf get empty config"
|
||||
expectfn "curl -sG http://localhost/restconf/data" "^null
$"
|
||||
|
||||
new "restconf DELETE config"
|
||||
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
|
||||
#
|
||||
new "Add subtree eth0,eth1 using POST"
|
||||
expectfn 'curl -sX POST -d {"interfaces":{"interface":[{"name":"eth0","type":"eth","enabled":"true"},{"name":"eth1","type":"eth","enabled":"true"}]}} http://localhost/restconf/data' ""
|
||||
|
||||
new "restconf get config"
|
||||
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}}
|
||||
new "Check eth0 added"
|
||||
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth0","type": "eth","enabled": "true"},{ "name": "eth1","type": "eth","enabled": "true"}\]}}
|
||||
$'
|
||||
|
||||
new "restconf PATCH config"
|
||||
expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||
new "Re-post eth0 which should generate error"
|
||||
expectfn 'curl -sX POST -d {"interfaces":{"interface":{"name":"eth0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' "Not Found"
|
||||
|
||||
new "restconf PUT"
|
||||
expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' ""
|
||||
new "delete eth0"
|
||||
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' ""
|
||||
|
||||
new "Check deleted eth0"
|
||||
expectfn 'curl -sG http://localhost/restconf/data' '{"interfaces": {"interface": {"name": "eth1","type": "eth","enabled": "true"}}}
|
||||
$'
|
||||
|
||||
new "Re-Delete eth0 using none should generate error"
|
||||
expectfn 'curl -sX DELETE http://localhost/restconf/data/interfaces/interface=eth0' "Not Found"
|
||||
|
||||
if false; then # XXX restconf dont support patch and put fully
|
||||
|
||||
new "restconf PATCH config"
|
||||
expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
||||
|
||||
new "restconf PUT"
|
||||
expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' ""
|
||||
fi
|
||||
|
||||
new "Kill restconf daemon"
|
||||
#sudo pkill -u www-data clixon_restconf
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/bash
|
||||
# Test2: backend and netconf basic functionality
|
||||
# Test4: Yang specifics: multi-keys and empty type
|
||||
|
||||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
|
@ -26,6 +26,19 @@ module ietf-ip{
|
|||
leaf d {
|
||||
type empty;
|
||||
}
|
||||
container f {
|
||||
leaf-list e {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf g {
|
||||
type string;
|
||||
}
|
||||
container h {
|
||||
leaf j {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
|
@ -52,6 +65,15 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><commit/></rpc>]]>
|
|||
new "netconf get config xpath"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/y[a=1][b=2]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y></x></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf edit leaf-list"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><x><f><e>hej</e><e>hopp</e></f></x></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get leaf-list"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f/e\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get leaf-list path"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Kill backend"
|
||||
# Check if still alive
|
||||
pid=`pgrep clixon_backend`
|
||||
|
|
|
|||
150
test/test5.sh
Executable file
150
test/test5.sh
Executable file
|
|
@ -0,0 +1,150 @@
|
|||
#!/bin/bash
|
||||
# Test5: datastore
|
||||
|
||||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
||||
datastore=datastore_client
|
||||
|
||||
|
||||
cat <<EOF > /tmp/ietf-ip.yang
|
||||
module ietf-ip{
|
||||
container x {
|
||||
list y {
|
||||
key "a b";
|
||||
leaf a {
|
||||
type string;
|
||||
}
|
||||
leaf b {
|
||||
type string;
|
||||
}
|
||||
leaf c {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf d {
|
||||
type empty;
|
||||
}
|
||||
container f {
|
||||
leaf-list e {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
leaf g {
|
||||
type string;
|
||||
}
|
||||
container h {
|
||||
leaf j {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
db='<config><x><y><a>1</a><b>2</b><c>first-entry</c></y><y><a>1</a><b>3</b><c>second-entry</c></y><y><a>2</a><b>3</b><c>third-entry</c></y><d/><f><e>a</e><e>b</e><e>c</e></f><g>astring</g></x></config>'
|
||||
|
||||
run(){
|
||||
name=$1
|
||||
dir=/tmp/$name
|
||||
|
||||
if [ ! -d $dir ]; then
|
||||
mkdir $dir
|
||||
fi
|
||||
rm -rf $dir/*
|
||||
|
||||
conf="-d candidate -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip"
|
||||
# echo "conf:$conf"
|
||||
new "datastore $name init"
|
||||
expectfn "$datastore $conf init" ""
|
||||
|
||||
# Whole tree operations
|
||||
new "datastore $name put all replace"
|
||||
expectfn "$datastore $conf put replace / $db" ""
|
||||
|
||||
new "datastore $name get"
|
||||
expectfn "$datastore $conf get /" "^$db$"
|
||||
|
||||
new "datastore $name put all remove"
|
||||
expectfn "$datastore $conf put remove /"
|
||||
|
||||
new "datastore $name get"
|
||||
expectfn "$datastore $conf get /" "^<config/>$"
|
||||
|
||||
new "datastore $name put all merge"
|
||||
expectfn "$datastore $conf put merge / $db" ""
|
||||
|
||||
new "datastore $name get"
|
||||
expectfn "$datastore $conf get /" "^$db$"
|
||||
|
||||
new "datastore $name put all delete"
|
||||
expectfn "$datastore $conf put remove /"
|
||||
|
||||
new "datastore $name get"
|
||||
expectfn "$datastore $conf get /" "^<config/>$"
|
||||
|
||||
new "datastore $name put all create"
|
||||
expectfn "$datastore $conf put create / $db" ""
|
||||
|
||||
new "datastore $name get"
|
||||
expectfn "$datastore $conf get /" "^$db$"
|
||||
|
||||
new "datastore $name put top create"
|
||||
expectfn "$datastore $conf put create / <config><x/></config>" "" # error
|
||||
|
||||
# Single key operations
|
||||
# leaf
|
||||
new "datastore $name put all delete"
|
||||
expectfn "$datastore $conf delete" ""
|
||||
|
||||
new "datastore $name init"
|
||||
expectfn "$datastore $conf init" ""
|
||||
|
||||
new "datastore $name create leaf"
|
||||
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
|
||||
|
||||
new "datastore $name create leaf"
|
||||
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
|
||||
|
||||
new "datastore $name delete leaf"
|
||||
expectfn "$datastore $conf put delete /x/y=1,3"
|
||||
|
||||
new "datastore $name replace leaf"
|
||||
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
|
||||
|
||||
new "datastore $name remove leaf"
|
||||
expectfn "$datastore $conf put remove /x/g"
|
||||
|
||||
new "datastore $name remove leaf"
|
||||
expectfn "$datastore $conf put remove /x/y=1,3/c"
|
||||
|
||||
new "datastore $name delete leaf"
|
||||
expectfn "$datastore $conf put delete /x/g"
|
||||
|
||||
new "datastore $name merge leaf"
|
||||
expectfn "$datastore $conf put merge /x/g <g>nalle</g>"
|
||||
|
||||
new "datastore $name replace leaf"
|
||||
expectfn "$datastore $conf put replace /x/g <g>nalle</g>"
|
||||
|
||||
new "datastore $name merge leaf"
|
||||
expectfn "$datastore $conf put merge /x/y=1,3/c <c>newentry</c>"
|
||||
|
||||
new "datastore $name replace leaf"
|
||||
expectfn "$datastore $conf put replace /x/y=1,3/c <c>newentry</c>"
|
||||
|
||||
new "datastore $name create leaf"
|
||||
expectfn "$datastore $conf put create /x/h <h><j>aaa</j></h>"
|
||||
|
||||
new "datastore $name create leaf"
|
||||
expectfn "$datastore $conf put create /x/y=1,3/c <c>newentry</c>"
|
||||
|
||||
#leaf-list
|
||||
|
||||
|
||||
rm -rf $dir
|
||||
}
|
||||
|
||||
#run keyvalue # cant get the put to work
|
||||
run text
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue