Merge branch 'develop'
This commit is contained in:
commit
9d8a2f73c2
85 changed files with 6664 additions and 3692 deletions
|
|
@ -1,33 +1,27 @@
|
||||||
# ***** BEGIN LICENSE BLOCK *****
|
# Clixon CHANGELOG
|
||||||
#
|
|
||||||
# Copyright (C) 2009-2017 Olof Hagsand and Benny Holmgren
|
## 3.3.0
|
||||||
#
|
|
||||||
# This file is part of CLIXON
|
May 2017
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
- Datastore text module is now default.
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
- Refined netconf "none" semantics in tests and text datastore
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
- Moved apps/dbctrl to datastore/
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
- Added connect/disconnect/getopt/setopt and handle to xmldb API
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
- Added datastore 'text'
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
- Configure (autoconf) changes
|
||||||
#
|
Removed libcurl dependency
|
||||||
# Alternatively, the contents of this file may be used under the terms of
|
Disable restconf (and fastcgi) with configure --disable-restconf
|
||||||
# the GNU General Public License Version 3 or later (the "GPL"),
|
Disable keyvalue datastore (and qdbm) with configure --disable-keyvalue
|
||||||
# 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
|
- Created xmldb plugin api
|
||||||
# under the terms of the GPL, and not to allow others to
|
Moved qdbm, chunk and xmldb to datastore keyvalue directories
|
||||||
# use your version of this file under the terms of Apache License version 2,
|
Removed all other clixon dependency on chunk code
|
||||||
# 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 *****
|
|
||||||
|
|
||||||
- cli_copy_config added as generic cli command
|
- cli_copy_config added as generic cli command
|
||||||
- cli_show_config added as generic cli command
|
- cli_show_config added as generic cli command
|
||||||
|
|
@ -52,7 +52,7 @@ LIBS = @LIBS@
|
||||||
INCLUDES = -I. -I@srcdir@ @INCLUDES@
|
INCLUDES = -I. -I@srcdir@ @INCLUDES@
|
||||||
SHELL = /bin/sh
|
SHELL = /bin/sh
|
||||||
|
|
||||||
SUBDIRS = lib apps include etc
|
SUBDIRS = lib apps include etc datastore
|
||||||
|
|
||||||
.PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker
|
.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
|
Clixon is an automatic configuration manager where you generate
|
||||||
specification generate interactive CLI, NETCONF, RESTCONF and embedded
|
interactive CLI, NETCONF, RESTCONF and embedded databases with
|
||||||
databases with transaction support.
|
transaction support from a YANG specification.
|
||||||
|
|
||||||
CLIXON is a fork of CLICON where legacy key specification has been
|
Table of contents
|
||||||
replaced completely by YANG. This means that legacy CLICON
|
=================
|
||||||
applications such as CLICON/ROST does not run on CLIXON.
|
* [Documentation](#documentation)
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Dependencies](#dependencies)
|
||||||
|
* [Licenses](#licenses)
|
||||||
|
* [Background](#background)
|
||||||
|
* [Clixon SDK](#SDK)
|
||||||
|
|
||||||
Presentations and tutorial is found on the [CLICON project
|
Documentation
|
||||||
page](http://www.clicon.org)
|
=============
|
||||||
|
- [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:
|
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
|
One [example application](example/README.md) is provided, a IETF IP YANG datamodel with
|
||||||
> make # Compile
|
generated CLI and configuration interface.
|
||||||
> sudo make install # Install libs, binaries, and config-files
|
|
||||||
> sudo make install-include # Install include files (for compiling)
|
|
||||||
|
|
||||||
One example applications is provided, the IETF IP YANG datamodel with generated CLI and configuration interface. It all origins from work at
|
Dependencies
|
||||||
[KTH](http://www.csc.kth.se/~olofh/10G_OSR)
|
============
|
||||||
|
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
|
- [CLIgen](http://www.cligen.se) is required for building Clixon. If you need
|
||||||
to build and install CLIgen:
|
to build and install CLIgen:
|
||||||
|
```
|
||||||
git clone https://github.com/olofhagsand/cligen.git
|
git clone https://github.com/olofhagsand/cligen.git
|
||||||
cd cligen; configure; make; make install
|
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.
|
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@
|
CFLAGS = @CFLAGS@
|
||||||
LDFLAGS = @LDFLAGS@
|
LDFLAGS = @LDFLAGS@
|
||||||
LIBS = @LIBS@
|
LIBS = @LIBS@
|
||||||
|
with_restconf = @with_restconf@
|
||||||
|
|
||||||
SHELL = /bin/sh
|
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)
|
.PHONY: all clean depend install $(SUBDIRS)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -307,7 +307,6 @@ from_client_edit_config(clicon_handle h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((xc = xpath_first(xn, "config")) != NULL){
|
if ((xc = xpath_first(xn, "config")) != NULL){
|
||||||
/* XXX see from_client_xmlput() */
|
|
||||||
if (xmldb_put(h, target, operation, api_path, xc) < 0){
|
if (xmldb_put(h, target, operation, api_path, xc) < 0){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>operation-failed</error-tag>"
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
|
@ -438,7 +437,7 @@ from_client_unlock(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
xmldb_unlock(h, db, pid);
|
xmldb_unlock(h, db);
|
||||||
if (cprintf(cbret, "<rpc-reply><ok/></rpc-reply>") < 0)
|
if (cprintf(cbret, "<rpc-reply><ok/></rpc-reply>") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -496,7 +495,7 @@ from_client_kill_session(clicon_handle h,
|
||||||
if (1 || (kill (pid, 0) != 0 && errno == ESRCH)){ /* Nothing there */
|
if (1 || (kill (pid, 0) != 0 && errno == ESRCH)){ /* Nothing there */
|
||||||
/* clear from locks */
|
/* clear from locks */
|
||||||
if (xmldb_islocked(h, db) == pid)
|
if (xmldb_islocked(h, db) == pid)
|
||||||
xmldb_unlock(h, db, pid);
|
xmldb_unlock(h, db);
|
||||||
}
|
}
|
||||||
else{ /* failed to kill client */
|
else{ /* failed to kill client */
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
|
|
@ -622,7 +621,6 @@ from_client_delete_config(clicon_handle h,
|
||||||
piddb);
|
piddb);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xmldb_delete(h, target) < 0){
|
if (xmldb_delete(h, target) < 0){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>operation-failed</error-tag>"
|
"<error-tag>operation-failed</error-tag>"
|
||||||
|
|
@ -633,7 +631,7 @@ from_client_delete_config(clicon_handle h,
|
||||||
"</rpc-error></rpc-reply>", clicon_err_reason);
|
"</rpc-error></rpc-reply>", clicon_err_reason);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (xmldb_init(h, target) < 0){
|
if (xmldb_create(h, target) < 0){
|
||||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||||
"<error-tag>operation-failed</error-tag>"
|
"<error-tag>operation-failed</error-tag>"
|
||||||
"<error-type>protocol</error-type>"
|
"<error-type>protocol</error-type>"
|
||||||
|
|
@ -872,9 +870,21 @@ from_client_msg(clicon_handle h,
|
||||||
assert(cbuf_len(cbret));
|
assert(cbuf_len(cbret));
|
||||||
clicon_debug(1, "%s %s", __FUNCTION__, cbuf_get(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 (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");
|
clicon_log(LOG_WARNING, "client rpc reset");
|
||||||
goto done;
|
break;
|
||||||
|
default:
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ok:
|
// ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
* Prototypes
|
* Prototypes
|
||||||
* not exported.
|
* not exported.
|
||||||
*/
|
*/
|
||||||
/* backend handles */
|
/* backend handles. Defined in clixon_backend_handle.c */
|
||||||
clicon_handle backend_handle_init(void);
|
clicon_handle backend_handle_init(void);
|
||||||
|
|
||||||
int backend_handle_exit(clicon_handle h);
|
int backend_handle_exit(clicon_handle h);
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,11 @@
|
||||||
#include "backend_handle.h"
|
#include "backend_handle.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define BACKEND_OPTS "hD:f:d:Fzu:P:1IRCc:rg:pty:"
|
#define BACKEND_OPTS "hD:f:d:b:Fzu:P:1IRCc:rg:py:x:"
|
||||||
|
|
||||||
/*! Terminate. Cannot use h after this */
|
/*! Terminate. Cannot use h after this */
|
||||||
static int
|
static int
|
||||||
config_terminate(clicon_handle h)
|
backend_terminate(clicon_handle h)
|
||||||
{
|
{
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
char *pidfile = clicon_backend_pidfile(h);
|
char *pidfile = clicon_backend_pidfile(h);
|
||||||
|
|
@ -91,19 +91,18 @@ config_terminate(clicon_handle h)
|
||||||
unlink(pidfile);
|
unlink(pidfile);
|
||||||
if (sockpath)
|
if (sockpath)
|
||||||
unlink(sockpath);
|
unlink(sockpath);
|
||||||
|
xmldb_plugin_unload(h); /* unload storage plugin */
|
||||||
backend_handle_exit(h); /* Cannot use h after this */
|
backend_handle_exit(h); /* Cannot use h after this */
|
||||||
event_exit();
|
event_exit();
|
||||||
clicon_log_register_callback(NULL, NULL);
|
clicon_log_register_callback(NULL, NULL);
|
||||||
clicon_debug(1, "%s done", __FUNCTION__);
|
clicon_debug(1, "%s done", __FUNCTION__);
|
||||||
if (debug)
|
|
||||||
chunk_check(stderr, NULL);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Unlink pidfile and quit
|
/*! Unlink pidfile and quit
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
config_sig_term(int arg)
|
backend_sig_term(int arg)
|
||||||
{
|
{
|
||||||
static int i=0;
|
static int i=0;
|
||||||
|
|
||||||
|
|
@ -130,6 +129,7 @@ usage(char *argv0, clicon_handle h)
|
||||||
" -D <level>\tdebug\n"
|
" -D <level>\tdebug\n"
|
||||||
" -f <file>\tCLICON config file (mandatory)\n"
|
" -f <file>\tCLICON config file (mandatory)\n"
|
||||||
" -d <dir>\tSpecify backend plugin directory (default: %s)\n"
|
" -d <dir>\tSpecify backend plugin directory (default: %s)\n"
|
||||||
|
" -b <dir>\tSpecify XMLDB database directory\n"
|
||||||
" -z\t\tKill other config daemon and exit\n"
|
" -z\t\tKill other config daemon and exit\n"
|
||||||
" -F\t\tforeground\n"
|
" -F\t\tforeground\n"
|
||||||
" -1\t\tonce (dont wait for events)\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"
|
" -c <file>\tLoad specified application config.\n"
|
||||||
" -r\t\tReload running database\n"
|
" -r\t\tReload running database\n"
|
||||||
" -p \t\tPrint database yang specification\n"
|
" -p \t\tPrint database yang specification\n"
|
||||||
" -t \t\tPrint alternate spec translation (eg if YANG print KEY, if KEY print YANG)\n"
|
|
||||||
" -g <group>\tClient membership required to this group (default: %s)\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,
|
argv0,
|
||||||
plgdir ? plgdir : "none",
|
plgdir ? plgdir : "none",
|
||||||
confsock ? confsock : "none",
|
confsock ? confsock : "none",
|
||||||
|
|
@ -159,7 +159,7 @@ db_reset(clicon_handle h,
|
||||||
{
|
{
|
||||||
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
|
if (xmldb_delete(h, db) != 0 && errno != ENOENT)
|
||||||
return -1;
|
return -1;
|
||||||
if (xmldb_init(h, db) < 0)
|
if (xmldb_create(h, db) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +181,7 @@ rundb_main(clicon_handle h,
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
cxobj *xn;
|
cxobj *xn;
|
||||||
|
|
||||||
if (xmldb_init(h, "tmp") < 0)
|
if (xmldb_create(h, "tmp") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xmldb_copy(h, "running", "tmp") < 0){
|
if (xmldb_copy(h, "running", "tmp") < 0){
|
||||||
clicon_err(OE_UNIX, errno, "file copy");
|
clicon_err(OE_UNIX, errno, "file copy");
|
||||||
|
|
@ -206,7 +206,6 @@ done:
|
||||||
xml_free(xt);
|
xml_free(xt);
|
||||||
if (fd != -1)
|
if (fd != -1)
|
||||||
close(fd);
|
close(fd);
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,11 +239,11 @@ server_socket(clicon_handle h)
|
||||||
int ss;
|
int ss;
|
||||||
|
|
||||||
/* Open control socket */
|
/* Open control socket */
|
||||||
if ((ss = config_socket_init(h)) < 0)
|
if ((ss = backend_socket_init(h)) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
/* ss is a server socket that the clients connect to. The callback
|
/* ss is a server socket that the clients connect to. The callback
|
||||||
therefore accepts clients on ss */
|
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);
|
close(ss);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -256,19 +255,20 @@ server_socket(clicon_handle h)
|
||||||
* log event.
|
* log event.
|
||||||
*/
|
*/
|
||||||
static int
|
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;
|
size_t n;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
char *nptr;
|
char *nptr;
|
||||||
char *newmsg = NULL;
|
char *newmsg = NULL;
|
||||||
int retval = -1;
|
|
||||||
|
|
||||||
/* backend_notify() will go through all clients and see if any has registered "CLICON",
|
/* backend_notify() will go through all clients and see if any has
|
||||||
and if so make a clicon_proto notify message to those clients. */
|
registered "CLICON", and if so make a clicon_proto notify message to
|
||||||
|
those clients.
|
||||||
|
Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
|
||||||
/* Sanitize '%' into "%%" to prevent segvfaults in vsnprintf later.
|
|
||||||
At this stage all formatting is already done */
|
At this stage all formatting is already done */
|
||||||
n = 0;
|
n = 0;
|
||||||
for(ptr=msg; *ptr; ptr++)
|
for(ptr=msg; *ptr; ptr++)
|
||||||
|
|
@ -283,7 +283,6 @@ config_log_cb(int level, char *msg, void *arg)
|
||||||
if (*ptr == '%')
|
if (*ptr == '%')
|
||||||
*nptr++ = '%';
|
*nptr++ = '%';
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = backend_notify(arg, "CLICON", level, newmsg);
|
retval = backend_notify(arg, "CLICON", level, newmsg);
|
||||||
free(newmsg);
|
free(newmsg);
|
||||||
|
|
||||||
|
|
@ -309,11 +308,11 @@ main(int argc, char **argv)
|
||||||
clicon_handle h;
|
clicon_handle h;
|
||||||
int help = 0;
|
int help = 0;
|
||||||
int printspec = 0;
|
int printspec = 0;
|
||||||
int printalt = 0;
|
|
||||||
int pid;
|
int pid;
|
||||||
char *pidfile;
|
char *pidfile;
|
||||||
char *sock;
|
char *sock;
|
||||||
int sockfamily;
|
int sockfamily;
|
||||||
|
char *xmldb_plugin;
|
||||||
|
|
||||||
/* In the startup, logs to stderr & syslog and debug flag set later */
|
/* In the startup, logs to stderr & syslog and debug flag set later */
|
||||||
|
|
||||||
|
|
@ -321,7 +320,7 @@ main(int argc, char **argv)
|
||||||
/* Initiate CLICON handle */
|
/* Initiate CLICON handle */
|
||||||
if ((h = backend_handle_init()) == NULL)
|
if ((h = backend_handle_init()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
if (config_plugin_init(h) != 0)
|
if (backend_plugin_init(h) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
foreground = 0;
|
foreground = 0;
|
||||||
once = 0;
|
once = 0;
|
||||||
|
|
@ -387,6 +386,11 @@ main(int argc, char **argv)
|
||||||
usage(argv[0], h);
|
usage(argv[0], h);
|
||||||
clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg);
|
clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg);
|
||||||
break;
|
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 */
|
case 'F' : /* foreground */
|
||||||
foreground = 1;
|
foreground = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -425,9 +429,6 @@ main(int argc, char **argv)
|
||||||
case 'p' : /* Print spec */
|
case 'p' : /* Print spec */
|
||||||
printspec++;
|
printspec++;
|
||||||
break;
|
break;
|
||||||
case 't' : /* Print alternative dbspec format (eg if YANG, print KEY) */
|
|
||||||
printalt++;
|
|
||||||
break;
|
|
||||||
case 'y' :{ /* yang module */
|
case 'y' :{ /* yang module */
|
||||||
/* Set revision to NULL, extract dir and module */
|
/* Set revision to NULL, extract dir and module */
|
||||||
char *str = strdup(optarg);
|
char *str = strdup(optarg);
|
||||||
|
|
@ -437,6 +438,10 @@ main(int argc, char **argv)
|
||||||
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
clicon_option_str_set(h, "CLICON_YANG_DIR", strdup(dir));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'x' :{ /* xmldb plugin */
|
||||||
|
clicon_option_str_set(h, "CLICON_XMLDB_PLUGIN", optarg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
usage(argv[0], h);
|
usage(argv[0], h);
|
||||||
break;
|
break;
|
||||||
|
|
@ -502,10 +507,25 @@ main(int argc, char **argv)
|
||||||
return -1;
|
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 */
|
/* Parse db spec file */
|
||||||
if (yang_spec_main(h, stdout, printspec) < 0)
|
if (yang_spec_main(h, stdout, printspec) < 0)
|
||||||
goto done;
|
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
|
/* First check for startup config
|
||||||
XXX the options below have become out-of-hand.
|
XXX the options below have become out-of-hand.
|
||||||
Too complex, need to simplify*/
|
Too complex, need to simplify*/
|
||||||
|
|
@ -518,7 +538,7 @@ main(int argc, char **argv)
|
||||||
else
|
else
|
||||||
if (db_reset(h, "running") < 0)
|
if (db_reset(h, "running") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xmldb_init(h, "candidate") < 0)
|
if (xmldb_create(h, "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -542,7 +562,7 @@ main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
/* If candidate does not exist, create it from running */
|
/* If candidate does not exist, create it from running */
|
||||||
if (xmldb_exists(h, "candidate") != 1){
|
if (xmldb_exists(h, "candidate") != 1){
|
||||||
if (xmldb_init(h, "candidate") < 0)
|
if (xmldb_create(h, "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -605,14 +625,14 @@ main(int argc, char **argv)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Register log notifications */
|
/* 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;
|
goto done;
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
|
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");
|
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||||
goto done;
|
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");
|
clicon_err(OE_DEMON, errno, "Setting signal");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -628,7 +648,7 @@ main(int argc, char **argv)
|
||||||
goto done;
|
goto done;
|
||||||
done:
|
done:
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,7 @@ config_find_plugin(clicon_handle h,
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
config_plugin_init(clicon_handle h)
|
backend_plugin_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
find_plugin_t *fp = config_find_plugin;
|
find_plugin_t *fp = config_find_plugin;
|
||||||
clicon_hash_t *data = clicon_data(h);
|
clicon_hash_t *data = clicon_data(h);
|
||||||
|
|
@ -152,8 +152,8 @@ config_plugin_init(clicon_handle h)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
plugin_unload(clicon_handle h,
|
backend_plugin_unload(clicon_handle h,
|
||||||
struct plugin *plg)
|
struct plugin *plg)
|
||||||
{
|
{
|
||||||
char *error;
|
char *error;
|
||||||
|
|
||||||
|
|
@ -178,44 +178,21 @@ plugin_unload(clicon_handle h,
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] file The plugin (.so) to load
|
* @param[in] file The plugin (.so) to load
|
||||||
* @param[in] dlflags Arguments to dlopen(3)
|
* @param[in] dlflags Arguments to dlopen(3)
|
||||||
* @param[in] label Chunk label
|
|
||||||
* @retval plugin Plugin struct
|
* @retval plugin Plugin struct
|
||||||
* @retval NULL Error
|
* @retval NULL Error
|
||||||
*/
|
*/
|
||||||
static struct plugin *
|
static struct plugin *
|
||||||
plugin_load (clicon_handle h,
|
backend_plugin_load (clicon_handle h,
|
||||||
char *file,
|
char *file,
|
||||||
int dlflags,
|
int dlflags)
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
char *error;
|
|
||||||
void *handle;
|
void *handle;
|
||||||
char *name;
|
char *name;
|
||||||
struct plugin *new;
|
struct plugin *new = NULL;
|
||||||
plginit_t *initfun;
|
|
||||||
|
|
||||||
dlerror(); /* Clear any existing error */
|
if ((handle = plugin_load(h, file, dlflags)) == NULL)
|
||||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
goto done;
|
||||||
error = (char*)dlerror();
|
if ((new = malloc(sizeof(*new))) == NULL) {
|
||||||
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) {
|
|
||||||
clicon_err(OE_UNIX, errno, "dhunk: %s", strerror(errno));
|
clicon_err(OE_UNIX, errno, "dhunk: %s", strerror(errno));
|
||||||
dlclose(handle);
|
dlclose(handle);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -226,7 +203,6 @@ plugin_load (clicon_handle h,
|
||||||
snprintf(new->p_name, sizeof(new->p_name), "%*s",
|
snprintf(new->p_name, sizeof(new->p_name), "%*s",
|
||||||
(int)strlen(name)-2, name);
|
(int)strlen(name)-2, name);
|
||||||
new->p_handle = handle;
|
new->p_handle = handle;
|
||||||
new->p_init = initfun;
|
|
||||||
if ((new->p_start = dlsym(handle, PLUGIN_START)) != NULL)
|
if ((new->p_start = dlsym(handle, PLUGIN_START)) != NULL)
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_START);
|
clicon_debug(2, "%s callback registered.", PLUGIN_START);
|
||||||
if ((new->p_exit = dlsym(handle, PLUGIN_EXIT)) != NULL)
|
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)
|
if ((new->p_trans_abort = dlsym(handle, PLUGIN_TRANS_ABORT)) != NULL)
|
||||||
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_ABORT);
|
clicon_debug(2, "%s callback registered.", PLUGIN_TRANS_ABORT);
|
||||||
clicon_debug(2, "Plugin '%s' loaded.\n", name);
|
clicon_debug(2, "Plugin '%s' loaded.\n", name);
|
||||||
|
done:
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,8 +295,8 @@ plugin_append(struct plugin *p)
|
||||||
{
|
{
|
||||||
struct plugin *new;
|
struct plugin *new;
|
||||||
|
|
||||||
if ((new = rechunk(plugins, (nplugins+1) * sizeof (*p), NULL)) == NULL) {
|
if ((new = realloc(plugins, (nplugins+1) * sizeof (*p))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,7 +316,7 @@ plugin_append(struct plugin *p)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
config_plugin_load_dir(clicon_handle h,
|
backend_plugin_load_dir(clicon_handle h,
|
||||||
const char *dir)
|
const char *dir)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -348,11 +324,11 @@ config_plugin_load_dir(clicon_handle h,
|
||||||
int np = 0;
|
int np = 0;
|
||||||
int ndp;
|
int ndp;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char *filename;
|
char filename[MAXPATHLEN];
|
||||||
struct dirent *dp;
|
struct dirent *dp = NULL;
|
||||||
struct plugin *new;
|
struct plugin *new;
|
||||||
struct plugin *p = NULL;
|
struct plugin *p = NULL;
|
||||||
char *master;
|
char master[MAXPATHLEN];
|
||||||
char *master_plugin;
|
char *master_plugin;
|
||||||
|
|
||||||
/* Format master plugin path */
|
/* 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");
|
clicon_err(OE_PLUGIN, 0, "clicon_master_plugin option not set");
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
master = chunk_sprintf(__FUNCTION__, "%s.so", master_plugin);
|
snprintf(master, MAXPATHLEN-1, "%s.so", master_plugin);
|
||||||
if (master == NULL) {
|
|
||||||
clicon_err(OE_PLUGIN, errno, "chunk_sprintf master plugin");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate plugin group object */
|
/* Allocate plugin group object */
|
||||||
/* Get plugin objects names from plugin directory */
|
/* 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;
|
goto quit;
|
||||||
|
|
||||||
/* reset num plugins */
|
/* reset num plugins */
|
||||||
np = 0;
|
np = 0;
|
||||||
|
|
||||||
/* Master plugin must be loaded first if it exists. */
|
/* Master plugin must be loaded first if it exists. */
|
||||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, master);
|
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, master);
|
||||||
if (filename == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
if (stat(filename, &st) == 0) {
|
if (stat(filename, &st) == 0) {
|
||||||
clicon_debug(1, "Loading master plugin '%.*s' ...",
|
clicon_debug(1, "Loading master plugin '%.*s' ...",
|
||||||
(int)strlen(filename), filename);
|
(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)
|
if (new == NULL)
|
||||||
goto quit;
|
goto quit;
|
||||||
if (plugin_append(new) < 0)
|
if (plugin_append(new) < 0)
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now load the rest */
|
/* Now load the rest. Note plugins is the global variable */
|
||||||
for (i = 0; i < ndp; i++) {
|
for (i = 0; i < ndp; i++) {
|
||||||
if (strcmp(dp[i].d_name, master) == 0)
|
if (strcmp(dp[i].d_name, master) == 0)
|
||||||
continue; /* Skip master now */
|
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);
|
clicon_debug(1, "Loading plugin '%.*s' ...", (int)strlen(filename), filename);
|
||||||
if (filename == NULL) {
|
new = backend_plugin_load(h, filename, RTLD_NOW);
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
new = plugin_load (h, filename, RTLD_NOW, __FUNCTION__);
|
|
||||||
if (new == NULL)
|
if (new == NULL)
|
||||||
goto quit;
|
goto quit;
|
||||||
|
/* Append to 'plugins' */
|
||||||
if (plugin_append(new) < 0)
|
if (plugin_append(new) < 0)
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
|
|
@ -413,13 +378,19 @@ config_plugin_load_dir(clicon_handle h,
|
||||||
|
|
||||||
quit:
|
quit:
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
if (p) {
|
/* XXX p is always NULL */
|
||||||
while (--np >= 0)
|
if (plugins) {
|
||||||
plugin_unload (h, &p[np]);
|
while (--np >= 0){
|
||||||
unchunk(p);
|
if ((p = &plugins[np]) == NULL)
|
||||||
|
continue;
|
||||||
|
backend_plugin_unload(h, p);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
free(plugins);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unchunk_group(__FUNCTION__);
|
if (dp)
|
||||||
|
free(dp);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -435,7 +406,7 @@ plugin_initiate(clicon_handle h)
|
||||||
char *dir;
|
char *dir;
|
||||||
|
|
||||||
/* First load CLICON system plugins */
|
/* 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;
|
return -1;
|
||||||
|
|
||||||
/* Then load application plugins */
|
/* Then load application plugins */
|
||||||
|
|
@ -443,7 +414,7 @@ plugin_initiate(clicon_handle h)
|
||||||
clicon_err(OE_PLUGIN, 0, "backend_dir not defined");
|
clicon_err(OE_PLUGIN, 0, "backend_dir not defined");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (config_plugin_load_dir(h, dir) < 0)
|
if (backend_plugin_load_dir(h, dir) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -462,10 +433,12 @@ plugin_finish(clicon_handle h)
|
||||||
|
|
||||||
for (i = 0; i < nplugins; i++) {
|
for (i = 0; i < nplugins; i++) {
|
||||||
p = &plugins[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;
|
nplugins = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ typedef struct {
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int config_plugin_init(clicon_handle h);
|
int backend_plugin_init(clicon_handle h);
|
||||||
int plugin_initiate(clicon_handle h);
|
int plugin_initiate(clicon_handle h);
|
||||||
int plugin_finish(clicon_handle h);
|
int plugin_finish(clicon_handle h);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ config_socket_init_unix(clicon_handle h, char *sock)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
config_socket_init(clicon_handle h)
|
backend_socket_init(clicon_handle h)
|
||||||
{
|
{
|
||||||
char *sock;
|
char *sock;
|
||||||
|
|
||||||
|
|
@ -197,7 +197,7 @@ config_socket_init(clicon_handle h)
|
||||||
* XXX: credentials not properly implemented
|
* XXX: credentials not properly implemented
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
config_accept_client(int fd,
|
backend_accept_client(int fd,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int config_socket_init(clicon_handle h);
|
int backend_socket_init(clicon_handle h);
|
||||||
int config_accept_client(int fd, void *arg);
|
int backend_accept_client(int fd, void *arg);
|
||||||
|
|
||||||
#endif /* _BACKEND_SOCKET_H_ */
|
#endif /* _BACKEND_SOCKET_H_ */
|
||||||
|
|
|
||||||
|
|
@ -75,14 +75,20 @@
|
||||||
* This file should only contain access functions for the _specific_
|
* This file should only contain access functions for the _specific_
|
||||||
* entries in the struct below.
|
* 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 {
|
struct backend_handle {
|
||||||
int cb_magic; /* magic (HDR)*/
|
int bh_magic; /* magic (HDR)*/
|
||||||
clicon_hash_t *cb_copt; /* clicon option list (HDR) */
|
clicon_hash_t *bh_copt; /* clicon option list (HDR) */
|
||||||
clicon_hash_t *cb_data; /* internal clicon data (HDR) */
|
clicon_hash_t *bh_data; /* internal clicon data (HDR) */
|
||||||
/* ------ end of common handle ------ */
|
/* ------ end of common handle ------ */
|
||||||
struct client_entry *cb_ce_list; /* The client list */
|
struct client_entry *bh_ce_list; /* The client list */
|
||||||
int cb_ce_nr; /* Number of clients, just increment */
|
int bh_ce_nr; /* Number of clients, just increment */
|
||||||
struct handle_subscription *cb_subscription; /* Event subscription list */
|
struct handle_subscription *bh_subscription; /* Event subscription list */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Creates and returns a clicon config handle for other CLICON API calls
|
/*! 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 (strcmp(su->su_stream, stream) == 0){
|
||||||
if (strlen(su->su_filter)==0 || fnmatch(su->su_filter, event, 0) == 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 (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);
|
clicon_log(LOG_WARNING, "client %d reset", ce->ce_nr);
|
||||||
#if 0
|
#if 0
|
||||||
/* We should remove here but removal is not possible
|
/* We should remove here but removal is not possible
|
||||||
|
|
@ -219,7 +225,7 @@ backend_notify_xml(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (send_msg_notify(ce->ce_s, level, cbuf_get(cb)) < 0){
|
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);
|
clicon_log(LOG_WARNING, "client %d reset", ce->ce_nr);
|
||||||
#if 0
|
#if 0
|
||||||
/* We should remove here but removal is not possible
|
/* 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 *
|
struct client_entry *
|
||||||
backend_client_add(clicon_handle h,
|
backend_client_add(clicon_handle h,
|
||||||
struct sockaddr *addr)
|
struct sockaddr *addr)
|
||||||
{
|
{
|
||||||
struct backend_handle *cb = handle(h);
|
struct backend_handle *bh = handle(h);
|
||||||
struct client_entry *ce;
|
struct client_entry *ce;
|
||||||
|
|
||||||
if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){
|
if ((ce = (struct client_entry *)malloc(sizeof(*ce))) == NULL){
|
||||||
|
|
@ -269,24 +281,28 @@ backend_client_add(clicon_handle h,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset(ce, 0, sizeof(*ce));
|
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));
|
memcpy(&ce->ce_addr, addr, sizeof(*addr));
|
||||||
ce->ce_next = cb->cb_ce_list;
|
ce->ce_next = bh->bh_ce_list;
|
||||||
cb->cb_ce_list = ce;
|
bh->bh_ce_list = ce;
|
||||||
return ce;
|
return ce;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Return client list
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @retval ce_list Client entry list (all sessions)
|
||||||
|
*/
|
||||||
struct client_entry *
|
struct client_entry *
|
||||||
backend_client_list(clicon_handle h)
|
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
|
/*! Actually remove client from client list
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] ce Client hadnle
|
* @param[in] ce Client handle
|
||||||
* @see backend_client_rm which is more high-level
|
* @see backend_client_rm which is more high-level
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -295,9 +311,9 @@ backend_client_delete(clicon_handle h,
|
||||||
{
|
{
|
||||||
struct client_entry *c;
|
struct client_entry *c;
|
||||||
struct client_entry **ce_prev;
|
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){
|
for (c = *ce_prev; c; c = c->ce_next){
|
||||||
if (c == ce){
|
if (c == ce){
|
||||||
*ce_prev = c->ce_next;
|
*ce_prev = c->ce_next;
|
||||||
|
|
@ -328,7 +344,7 @@ subscription_add(clicon_handle h,
|
||||||
subscription_fn_t fn,
|
subscription_fn_t fn,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct backend_handle *cb = handle(h);
|
struct backend_handle *bh = handle(h);
|
||||||
struct handle_subscription *hs = NULL;
|
struct handle_subscription *hs = NULL;
|
||||||
|
|
||||||
if ((hs = malloc(sizeof(*hs))) == NULL){
|
if ((hs = malloc(sizeof(*hs))) == NULL){
|
||||||
|
|
@ -339,10 +355,10 @@ subscription_add(clicon_handle h,
|
||||||
hs->hs_stream = strdup(stream);
|
hs->hs_stream = strdup(stream);
|
||||||
hs->hs_format = format;
|
hs->hs_format = format;
|
||||||
hs->hs_filter = filter?strdup(filter):NULL;
|
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_fn = fn;
|
||||||
hs->hs_arg = arg;
|
hs->hs_arg = arg;
|
||||||
cb->cb_subscription = hs;
|
bh->bh_subscription = hs;
|
||||||
done:
|
done:
|
||||||
return hs;
|
return hs;
|
||||||
}
|
}
|
||||||
|
|
@ -362,11 +378,11 @@ subscription_delete(clicon_handle h,
|
||||||
subscription_fn_t fn,
|
subscription_fn_t fn,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct backend_handle *cb = handle(h);
|
struct backend_handle *bh = handle(h);
|
||||||
struct handle_subscription *hs;
|
struct handle_subscription *hs;
|
||||||
struct handle_subscription **hs_prev;
|
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){
|
for (hs = *hs_prev; hs; hs = hs->hs_next){
|
||||||
/* XXX arg == hs->hs_arg */
|
/* XXX arg == hs->hs_arg */
|
||||||
if (strcmp(hs->hs_stream, stream)==0 && hs->hs_fn == fn){
|
if (strcmp(hs->hs_stream, stream)==0 && hs->hs_fn == fn){
|
||||||
|
|
@ -404,15 +420,16 @@ struct handle_subscription *
|
||||||
subscription_each(clicon_handle h,
|
subscription_each(clicon_handle h,
|
||||||
struct handle_subscription *hprev)
|
struct handle_subscription *hprev)
|
||||||
{
|
{
|
||||||
struct backend_handle *cb = handle(h);
|
struct backend_handle *bh = handle(h);
|
||||||
struct handle_subscription *hs = NULL;
|
struct handle_subscription *hs = NULL;
|
||||||
|
|
||||||
if (hprev)
|
if (hprev)
|
||||||
hs = hprev->hs_next;
|
hs = hprev->hs_next;
|
||||||
else
|
else
|
||||||
hs = cb->cb_subscription;
|
hs = bh->bh_subscription;
|
||||||
return hs;
|
return hs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Database dependency description */
|
/* Database dependency description */
|
||||||
struct backend_netconf_reg {
|
struct backend_netconf_reg {
|
||||||
qelem_t nr_qelem; /* List header */
|
qelem_t nr_qelem; /* List header */
|
||||||
|
|
@ -456,10 +473,9 @@ catch:
|
||||||
/*! See if there is any callback registered for this tag
|
/*! See if there is any callback registered for this tag
|
||||||
*
|
*
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
* @param[in] xe Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||||
* @param[out] cb Output xml stream. For reply
|
* @param[in] ce Client (session) entry
|
||||||
* @param[out] cb_err Error xml stream. For error reply
|
* @param[out] cbret Return XML, error or OK as cbuf
|
||||||
* @param[out] xret Return XML, error or OK
|
|
||||||
*
|
*
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 OK, not found handler.
|
* @retval 0 OK, not found handler.
|
||||||
|
|
|
||||||
|
|
@ -94,17 +94,19 @@ cli_notification_register(clicon_handle h,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *logname;
|
char *logname = NULL;
|
||||||
void *p;
|
void *p;
|
||||||
int s;
|
int s;
|
||||||
clicon_hash_t *cdat = clicon_data(h);
|
clicon_hash_t *cdat = clicon_data(h);
|
||||||
size_t len;
|
size_t len;
|
||||||
int s_exist = -1;
|
int s_exist = -1;
|
||||||
|
|
||||||
if ((logname = chunk_sprintf(__FUNCTION__, "log_socket_%s", stream)) == NULL){
|
len = strlen("log_socket_") + strlen(stream) + 1;
|
||||||
clicon_err(OE_PLUGIN, errno, "%s: chunk_sprintf", __FUNCTION__);
|
if ((logname = malloc(len)) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
snprintf(logname, len, "log_socket_%s", stream);
|
||||||
if ((p = hash_value(cdat, logname, &len)) != NULL)
|
if ((p = hash_value(cdat, logname, &len)) != NULL)
|
||||||
s_exist = *(int*)p;
|
s_exist = *(int*)p;
|
||||||
|
|
||||||
|
|
@ -132,7 +134,8 @@ cli_notification_register(clicon_handle h,
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (logname)
|
||||||
|
free(logname);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -69,12 +69,11 @@
|
||||||
#define handle(h) (assert(clicon_handle_check(h)==0),(struct cli_handle *)(h))
|
#define handle(h) (assert(clicon_handle_check(h)==0),(struct cli_handle *)(h))
|
||||||
#define cligen(h) (handle(h)->cl_cligen)
|
#define cligen(h) (handle(h)->cl_cligen)
|
||||||
|
|
||||||
/*
|
/*! CLI specific handle added to header CLICON handle
|
||||||
* 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]
|
|
||||||
* This file should only contain access functions for the _specific_
|
* This file should only contain access functions for the _specific_
|
||||||
* entries in the struct below.
|
* 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 {
|
struct cli_handle {
|
||||||
int cl_magic; /* magic (HDR)*/
|
int cl_magic; /* magic (HDR)*/
|
||||||
|
|
@ -112,21 +111,23 @@ cli_handle_init(void)
|
||||||
return h;
|
return h;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Free clicon handle
|
||||||
* cli_handle_exit
|
|
||||||
* frees clicon handle
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
cli_handle_exit(clicon_handle h)
|
cli_handle_exit(clicon_handle h)
|
||||||
{
|
{
|
||||||
cligen_handle ch = cligen(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 */
|
clicon_handle_exit(h); /* frees h and options */
|
||||||
|
|
||||||
cligen_exit(ch);
|
cligen_exit(ch);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*----------------------------------------------------------
|
/*----------------------------------------------------------
|
||||||
* cli-specific handle access functions
|
* cli-specific handle access functions
|
||||||
*----------------------------------------------------------*/
|
*----------------------------------------------------------*/
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,8 @@ main(int argc, char **argv)
|
||||||
int printgen = 0;
|
int printgen = 0;
|
||||||
int logclisyntax = 0;
|
int logclisyntax = 0;
|
||||||
int help = 0;
|
int help = 0;
|
||||||
char *treename;
|
char *treename = NULL;
|
||||||
|
int len;
|
||||||
int logdst = CLICON_LOG_STDERR;
|
int logdst = CLICON_LOG_STDERR;
|
||||||
char *restarg = NULL; /* what remains after options */
|
char *restarg = NULL; /* what remains after options */
|
||||||
|
|
||||||
|
|
@ -343,8 +344,14 @@ main(int argc, char **argv)
|
||||||
if (yang2cli(h, yspec, &pt, clicon_cli_genmodel_type(h)) < 0)
|
if (yang2cli(h, yspec, &pt, clicon_cli_genmodel_type(h)) < 0)
|
||||||
goto done;
|
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);
|
cli_tree_add(h, treename, pt);
|
||||||
|
|
||||||
if (printgen)
|
if (printgen)
|
||||||
cligen_print(stdout, pt, 1);
|
cligen_print(stdout, pt, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -400,9 +407,10 @@ main(int argc, char **argv)
|
||||||
if (!once)
|
if (!once)
|
||||||
cli_interactive(h);
|
cli_interactive(h);
|
||||||
done:
|
done:
|
||||||
|
if (treename)
|
||||||
|
free(treename);
|
||||||
if (restarg)
|
if (restarg)
|
||||||
free(restarg);
|
free(restarg);
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
// Gets in your face if we log on stderr
|
// Gets in your face if we log on stderr
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
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 *
|
static cli_syntaxmode_t *
|
||||||
syntax_mode_find(cli_syntax_t *stx, const char *mode, int create)
|
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)
|
if (create == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if ((m = chunk(sizeof(cli_syntaxmode_t), stx->stx_cnklbl)) == NULL) {
|
if ((m = malloc(sizeof(cli_syntaxmode_t))) == NULL) {
|
||||||
perror("chunk");
|
perror("malloc");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
memset (m, 0, sizeof (*m));
|
memset (m, 0, sizeof (*m));
|
||||||
|
|
@ -163,39 +162,15 @@ syntax_append(clicon_handle h,
|
||||||
return 0;
|
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
|
* Unload all plugins in a group
|
||||||
*/
|
*/
|
||||||
static int
|
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)
|
if (stx == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -204,15 +179,18 @@ syntax_unload(clicon_handle h)
|
||||||
p = stx->stx_plugins;
|
p = stx->stx_plugins;
|
||||||
plugin_unload(h, p->cp_handle);
|
plugin_unload(h, p->cp_handle);
|
||||||
clicon_debug(1, "DEBUG: Plugin '%s' unloaded.", p->cp_name);
|
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--;
|
stx->stx_nplugins--;
|
||||||
}
|
}
|
||||||
while (stx->stx_nmodes > 0) {
|
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--;
|
stx->stx_nmodes--;
|
||||||
}
|
}
|
||||||
|
|
||||||
unchunk_group(stx->stx_cnklbl);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,48 +243,31 @@ clixon_str2fn(char *name,
|
||||||
return NULL;
|
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
|
* Note 'file' may be destructively modified
|
||||||
|
* @retval plugin-handle should be freed after use
|
||||||
*/
|
*/
|
||||||
static plghndl_t
|
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;
|
char *name;
|
||||||
void *handle = NULL;
|
plghndl_t handle = NULL;
|
||||||
plginit_t *initfun;
|
|
||||||
struct cli_plugin *cp = NULL;
|
struct cli_plugin *cp = NULL;
|
||||||
|
|
||||||
dlerror(); /* Clear any existing error */
|
if ((handle = plugin_load(h, file, dlflags)) == NULL)
|
||||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
|
||||||
error = (char*)dlerror();
|
|
||||||
cli_output (stderr, "dlopen: %s\n", error ? error : "Unknown error");
|
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
if ((cp = malloc(sizeof (struct cli_plugin))) == NULL) {
|
||||||
/* call plugin_init() if defined */
|
perror("malloc");
|
||||||
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");
|
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
memset (cp, 0, sizeof(*cp));
|
memset (cp, 0, sizeof(*cp));
|
||||||
|
|
||||||
name = basename(file);
|
name = basename(file);
|
||||||
snprintf(cp->cp_name, sizeof(cp->cp_name), "%.*s", (int)strlen(name)-3, name);
|
snprintf(cp->cp_name, sizeof(cp->cp_name), "%.*s", (int)strlen(name)-3, name);
|
||||||
cp->cp_handle = handle;
|
cp->cp_handle = handle;
|
||||||
|
|
||||||
quit:
|
quit:
|
||||||
if (cp == NULL) {
|
|
||||||
if (handle)
|
|
||||||
dlclose(handle);
|
|
||||||
}
|
|
||||||
return cp;
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,7 +284,7 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
||||||
parse_tree pt = {0,};
|
parse_tree pt = {0,};
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
char *filepath;
|
char filepath[MAXPATHLEN];
|
||||||
cvec *vr = NULL;
|
cvec *vr = NULL;
|
||||||
char *prompt = NULL;
|
char *prompt = NULL;
|
||||||
char **vec = NULL;
|
char **vec = NULL;
|
||||||
|
|
@ -331,12 +292,7 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir)
|
||||||
char *plgnam;
|
char *plgnam;
|
||||||
struct cli_plugin *p;
|
struct cli_plugin *p;
|
||||||
|
|
||||||
if ((filepath = chunk_sprintf(__FUNCTION__, "%s/%s",
|
snprintf(filepath, MAXPATHLEN-1, "%s/%s", clispec_dir, filename);
|
||||||
clispec_dir,
|
|
||||||
filename)) == NULL){
|
|
||||||
clicon_err(OE_PLUGIN, errno, "chunk");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if ((vr = cvec_new(0)) == NULL){
|
if ((vr = cvec_new(0)) == NULL){
|
||||||
clicon_err(OE_PLUGIN, errno, "cvec_new");
|
clicon_err(OE_PLUGIN, errno, "cvec_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -403,7 +359,6 @@ done:
|
||||||
cvec_free(vr);
|
cvec_free(vr);
|
||||||
if (vec)
|
if (vec)
|
||||||
free(vec);
|
free(vec);
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,10 +370,10 @@ cli_plugin_load_dir(clicon_handle h, char *dir, cli_syntax_t *stx)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int ndp;
|
int ndp;
|
||||||
struct dirent *dp;
|
struct dirent *dp = NULL;
|
||||||
char *file;
|
|
||||||
char *master_plugin;
|
char *master_plugin;
|
||||||
char *master;
|
char master[MAXPATHLEN];
|
||||||
|
char filename[MAXPATHLEN];
|
||||||
struct cli_plugin *cp;
|
struct cli_plugin *cp;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int retval = -1;
|
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");
|
clicon_err(OE_PLUGIN, 0, "clicon_master_plugin option not set");
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
if ((master = chunk_sprintf(__FUNCTION__, "%s.so", master_plugin)) == NULL){
|
snprintf(master, MAXPATHLEN-1, "%s.so", master_plugin);
|
||||||
clicon_err(OE_PLUGIN, errno, "chunk_sprintf master plugin");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
/* Get plugin objects names from plugin directory */
|
/* 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)
|
if (ndp < 0)
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
||||||
/* Load master plugin first */
|
/* Load master plugin first */
|
||||||
file = chunk_sprintf(__FUNCTION__, "%s/%s", dir, master);
|
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, master);
|
||||||
if (file == NULL) {
|
if (stat(filename, &st) == 0) {
|
||||||
clicon_err(OE_UNIX, errno, "chunk_sprintf dir");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
if (stat(file, &st) == 0) {
|
|
||||||
clicon_debug(1, "DEBUG: Loading master plugin '%s'", master);
|
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)
|
if (cp == NULL)
|
||||||
goto quit;
|
goto quit;
|
||||||
/* Look up certain call-backs in master plugin */
|
/* 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);
|
INSQ(cp, stx->stx_plugins);
|
||||||
stx->stx_nplugins++;
|
stx->stx_nplugins++;
|
||||||
}
|
}
|
||||||
unchunk (file);
|
|
||||||
|
|
||||||
/* Load the rest */
|
/* Load the rest */
|
||||||
for (i = 0; i < ndp; i++) {
|
for (i = 0; i < ndp; i++) {
|
||||||
if (strcmp (dp[i].d_name, master) == 0)
|
if (strcmp (dp[i].d_name, master) == 0)
|
||||||
continue; /* Skip master now */
|
continue; /* Skip master now */
|
||||||
file = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
|
||||||
if (file == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "chunk_sprintf dir");
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
clicon_debug(1, "DEBUG: Loading plugin '%s'", 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;
|
goto quit;
|
||||||
INSQ(cp, stx->stx_plugins);
|
INSQ(cp, stx->stx_plugins);
|
||||||
stx->stx_nplugins++;
|
stx->stx_nplugins++;
|
||||||
unchunk (file);
|
|
||||||
}
|
}
|
||||||
if (dp)
|
|
||||||
unchunk(dp);
|
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
||||||
quit:
|
quit:
|
||||||
unchunk_group(__FUNCTION__);
|
if (dp)
|
||||||
|
free(dp);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -501,8 +442,7 @@ cli_syntax_load (clicon_handle h)
|
||||||
char *clispec_dir = NULL;
|
char *clispec_dir = NULL;
|
||||||
int ndp;
|
int ndp;
|
||||||
int i;
|
int i;
|
||||||
char *cnklbl = "__CLICON_CLI_SYNTAX_CNK_LABEL__";
|
struct dirent *dp = NULL;
|
||||||
struct dirent *dp;
|
|
||||||
cli_syntax_t *stx;
|
cli_syntax_t *stx;
|
||||||
cli_syntaxmode_t *m;
|
cli_syntaxmode_t *m;
|
||||||
|
|
||||||
|
|
@ -521,13 +461,11 @@ cli_syntax_load (clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate plugin group object */
|
/* Allocate plugin group object */
|
||||||
if ((stx = chunk(sizeof(*stx), cnklbl)) == NULL) {
|
if ((stx = malloc(sizeof(*stx))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
memset (stx, 0, sizeof (*stx)); /* Zero out all */
|
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);
|
cli_syntax_set(h, stx);
|
||||||
|
|
||||||
|
|
@ -541,7 +479,7 @@ cli_syntax_load (clicon_handle h)
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
||||||
/* load syntaxfiles */
|
/* 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;
|
goto quit;
|
||||||
/* Load the rest */
|
/* Load the rest */
|
||||||
for (i = 0; i < ndp; i++) {
|
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)
|
if (cli_load_syntax(h, dp[i].d_name, clispec_dir) < 0)
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
if (dp)
|
|
||||||
unchunk(dp);
|
|
||||||
|
|
||||||
|
|
||||||
/* Did we successfully load any syntax modes? */
|
/* Did we successfully load any syntax modes? */
|
||||||
if (stx->stx_nmodes <= 0) {
|
if (stx->stx_nmodes <= 0) {
|
||||||
|
|
@ -576,11 +511,11 @@ cli_syntax_load (clicon_handle h)
|
||||||
|
|
||||||
quit:
|
quit:
|
||||||
if (retval != 0) {
|
if (retval != 0) {
|
||||||
syntax_unload(h);
|
cli_syntax_unload(h);
|
||||||
unchunk_group(cnklbl);
|
|
||||||
cli_syntax_set(h, NULL);
|
cli_syntax_set(h, NULL);
|
||||||
}
|
}
|
||||||
unchunk_group(__FUNCTION__);
|
if (dp)
|
||||||
|
free(dp);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,7 +549,7 @@ cli_plugin_start(clicon_handle h, int argc, char **argv)
|
||||||
int
|
int
|
||||||
cli_plugin_finish(clicon_handle h)
|
cli_plugin_finish(clicon_handle h)
|
||||||
{
|
{
|
||||||
syntax_unload(h);
|
cli_syntax_unload(h);
|
||||||
cli_syntax_set(h, NULL);
|
cli_syntax_set(h, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -663,39 +598,36 @@ clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*! Given a command string, parse and evaluate.
|
||||||
* clicon_parse
|
* Parse and evaluate the string according to
|
||||||
* Given a command string, parse and evaluate the string according to
|
|
||||||
* the syntax parse tree of the syntax mode specified by *mode.
|
* 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
|
* 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
|
* 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
|
* match is found in another mode, the mode variable is updated to point at
|
||||||
* the new mode string.
|
* the new mode string.
|
||||||
*
|
*
|
||||||
* INPUT:
|
* @param[in] h Clicon handle
|
||||||
* cmd The command string
|
* @param[in] cmd The command string
|
||||||
* match_obj Pointer to CLIgen match object
|
* @param[in,out] mode A pointer to the mode string pointer
|
||||||
* mode A pointer to the mode string pointer
|
* @param[out] result -2 On eof (shouldnt happen)
|
||||||
* OUTPUT:
|
* -1 On parse error
|
||||||
* kr Keyword vector
|
* >=0 Number of matches
|
||||||
* vr Variable vector
|
|
||||||
* RETURNS:
|
|
||||||
* -2 : on eof (shouldnt happen)
|
|
||||||
* -1 : In parse error
|
|
||||||
* >=0 : Number of matches
|
|
||||||
*/
|
*/
|
||||||
int
|
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;
|
char *m, *msav;
|
||||||
int res = -1;
|
int res = -1;
|
||||||
int r;
|
int r;
|
||||||
cli_syntax_t *stx;
|
cli_syntax_t *stx = NULL;
|
||||||
cli_syntaxmode_t *smode;
|
cli_syntaxmode_t *smode;
|
||||||
char *treename;
|
char *treename;
|
||||||
parse_tree *pt; /* Orig */
|
parse_tree *pt; /* Orig */
|
||||||
cg_obj *match_obj;
|
cg_obj *match_obj;
|
||||||
cvec *vr = NULL;
|
cvec *cvv = NULL;
|
||||||
|
|
||||||
stx = cli_syntax(h);
|
stx = cli_syntax(h);
|
||||||
m = *mode;
|
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);
|
fprintf(stderr, "No such parse-tree registered: %s\n", treename);
|
||||||
goto done;;
|
goto done;;
|
||||||
}
|
}
|
||||||
if ((vr = cvec_new(0)) == NULL){
|
if ((cvv = cvec_new(0)) == NULL){
|
||||||
fprintf(stderr, "%s: cvec_new: %s\n", __FUNCTION__, strerror(errno));
|
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||||
goto done;;
|
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)
|
if (res != CG_MATCH)
|
||||||
pt_expand_cleanup_1(pt);
|
pt_expand_cleanup_1(pt);
|
||||||
if (msav){
|
if (msav){
|
||||||
|
|
@ -755,7 +687,7 @@ clicon_parse(clicon_handle h, char *cmd, char **mode, int *result)
|
||||||
*mode = m;
|
*mode = m;
|
||||||
cli_set_syntax_mode(h, 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);
|
cli_handler_err(stdout);
|
||||||
pt_expand_cleanup_1(pt);
|
pt_expand_cleanup_1(pt);
|
||||||
if (result)
|
if (result)
|
||||||
|
|
@ -769,8 +701,8 @@ clicon_parse(clicon_handle h, char *cmd, char **mode, int *result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
if (vr)
|
if (cvv)
|
||||||
cvec_free(vr);
|
cvec_free(cvv);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -891,9 +823,7 @@ cli_set_prompt(clicon_handle h, const char *name, const char *prompt)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Format prompt
|
||||||
* Format prompt
|
|
||||||
* XXX: HOST_NAME_MAX from sysconf()
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
prompt_fmt (char *prompt, size_t plen, char *fmt, ...)
|
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 *s = fmt;
|
||||||
char hname[1024];
|
char hname[1024];
|
||||||
char tty[32];
|
char tty[32];
|
||||||
char *new;
|
|
||||||
char *tmp;
|
char *tmp;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
|
||||||
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* Start with empty string */
|
/* Start with empty string */
|
||||||
if((new = chunk_sprintf(__FUNCTION__, "%s", ""))==NULL)
|
cprintf(cb, "");
|
||||||
goto done;
|
|
||||||
|
|
||||||
while(*s) {
|
while(*s) {
|
||||||
if (*s == '%' && *++s) {
|
if (*s == '%' && *++s) {
|
||||||
switch(*s) {
|
switch(*s) {
|
||||||
|
|
||||||
case 'H': /* Hostname */
|
case 'H': /* Hostname */
|
||||||
if (gethostname (hname, sizeof (hname)) != 0)
|
if (gethostname (hname, sizeof (hname)) != 0)
|
||||||
strncpy(hname, "unknown", sizeof(hname)-1);
|
strncpy(hname, "unknown", sizeof(hname)-1);
|
||||||
if((new = chunk_strncat(new, hname, 0, __FUNCTION__))==NULL)
|
cprintf(cb, "%s", hname);
|
||||||
goto done;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'U': /* Username */
|
case 'U': /* Username */
|
||||||
tmp = getenv("USER");
|
tmp = getenv("USER");
|
||||||
if((new = chunk_strncat(new, (tmp ? tmp : "nobody"), 0, __FUNCTION__))==NULL)
|
cprintf(cb, "%s", tmp?tmp:"nobody");
|
||||||
goto done;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'T': /* TTY */
|
case 'T': /* TTY */
|
||||||
if(ttyname_r(fileno(stdin), tty, sizeof(tty)-1) < 0)
|
if(ttyname_r(fileno(stdin), tty, sizeof(tty)-1) < 0)
|
||||||
strcpy(tty, "notty");
|
strcpy(tty, "notty");
|
||||||
if((new = chunk_strncat(new, tty, strlen(tty), __FUNCTION__))==NULL)
|
cprintf(cb, "%s", tty);
|
||||||
goto done;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if((new = chunk_strncat(new, "%", 1, __FUNCTION__))==NULL ||
|
cprintf(cb, "%%");
|
||||||
(new = chunk_strncat(new, s, 1, __FUNCTION__)))
|
cprintf(cb, "%c", *s);
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
if ((new = chunk_strncat(new, s, 1, __FUNCTION__))==NULL)
|
cprintf(cb, "%c", *s);
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
s++;
|
s++;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (new)
|
if (cb)
|
||||||
fmt = new;
|
fmt = cbuf_get(cb);
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
ret = vsnprintf(prompt, plen, fmt, ap);
|
ret = vsnprintf(prompt, plen, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
if (cb)
|
||||||
unchunk_group(__FUNCTION__);
|
cbuf_free(cb);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Return a formatted prompt string
|
||||||
* Return a formatted prompt string
|
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
cli_prompt(char *fmt)
|
cli_prompt(char *fmt)
|
||||||
|
|
@ -1069,11 +990,10 @@ cli_ptpop(clicon_handle h, char *mode, char *op)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*! Find a cli plugin based on name and resolve a function pointer in it.
|
||||||
* clicon_valcb
|
|
||||||
* Callback from clicon_dbvars_parse()
|
* Callback from clicon_dbvars_parse()
|
||||||
* Find a cli plugin based on name if given and
|
* Find a cli plugin based on name if given and use dlsym to resolve a
|
||||||
* use dlsym to resolve a function pointer in it.
|
* function pointer in it.
|
||||||
* Call the resolved function to get the cgv populated
|
* Call the resolved function to get the cgv populated
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,6 @@ struct cli_plugin {
|
||||||
|
|
||||||
/* Plugin group object */
|
/* Plugin group object */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char stx_cnklbl[128]; /* Plugin group name */
|
|
||||||
int stx_nplugins; /* Number of plugins */
|
int stx_nplugins; /* Number of plugins */
|
||||||
struct cli_plugin *stx_plugins; /* List of plugins */
|
struct cli_plugin *stx_plugins; /* List of plugins */
|
||||||
int stx_nmodes; /* Number of syntax modes */
|
int stx_nmodes; /* Number of syntax modes */
|
||||||
|
|
|
||||||
|
|
@ -213,15 +213,15 @@ expand_dir(char *dir,
|
||||||
mode_t flags,
|
mode_t flags,
|
||||||
int detail)
|
int detail)
|
||||||
{
|
{
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
char *str;
|
char *str;
|
||||||
char *cmd;
|
char *cmd;
|
||||||
int len;
|
int len;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
char filename[MAXPATHLEN];
|
char filename[MAXPATHLEN];
|
||||||
|
|
||||||
if ((dirp = opendir(dir)) == 0){
|
if ((dirp = opendir(dir)) == 0){
|
||||||
fprintf(stderr, "expand_dir: opendir(%s) %s\n",
|
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;
|
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 int nplugins = 0;
|
||||||
static plghndl_t *plugins = NULL;
|
static plghndl_t *plugins = NULL;
|
||||||
static netconf_reg_t *deps = NULL;
|
static netconf_reg_t *deps = NULL;
|
||||||
|
|
@ -141,9 +87,9 @@ netconf_plugin_load(clicon_handle h)
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *dir;
|
char *dir;
|
||||||
int ndp;
|
int ndp;
|
||||||
struct dirent *dp;
|
struct dirent *dp = NULL;
|
||||||
int i;
|
int i;
|
||||||
char *filename;
|
char filename[MAXPATHLEN];
|
||||||
plghndl_t *handle;
|
plghndl_t *handle;
|
||||||
|
|
||||||
if ((dir = clicon_netconf_dir(h)) == NULL){
|
if ((dir = clicon_netconf_dir(h)) == NULL){
|
||||||
|
|
@ -152,30 +98,26 @@ netconf_plugin_load(clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get plugin objects names from plugin directory */
|
/* 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;
|
goto quit;
|
||||||
|
|
||||||
/* Load all plugins */
|
/* Load all plugins */
|
||||||
for (i = 0; i < ndp; i++) {
|
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' ...",
|
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||||
(int)strlen(filename), filename);
|
(int)strlen(filename), filename);
|
||||||
if (filename == NULL) {
|
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
||||||
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
goto quit;
|
|
||||||
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
plugins[nplugins++] = handle;
|
plugins[nplugins++] = handle;
|
||||||
unchunk (filename);
|
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
quit:
|
quit:
|
||||||
unchunk_group(__FUNCTION__);
|
if (dp)
|
||||||
|
free(dp);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,8 +136,10 @@ netconf_plugin_unload(clicon_handle h)
|
||||||
}
|
}
|
||||||
for (i = 0; i < nplugins; i++)
|
for (i = 0; i < nplugins; i++)
|
||||||
plugin_unload(h, plugins[i]);
|
plugin_unload(h, plugins[i]);
|
||||||
if (plugins)
|
if (plugins){
|
||||||
unchunk(plugins);
|
free(plugins);
|
||||||
|
plugins = NULL;
|
||||||
|
}
|
||||||
nplugins = 0;
|
nplugins = 0;
|
||||||
return 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
|
Clixon restconf is a daemon based on FASTCGI. Instructions are available to
|
||||||
run with NGINX.
|
run with NGINX.
|
||||||
The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE.
|
The implementatation supports plain OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE.
|
||||||
and is based on draft-ietf-netconf-restconf-13.
|
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:
|
including:
|
||||||
- query parameters (section 4.9)
|
- query parameters (section 4.9)
|
||||||
- notifications (sec 6)
|
- notifications (sec 6)
|
||||||
- only rudimentary error reporting exists (sec 7)
|
- 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 {
|
server {
|
||||||
...
|
...
|
||||||
location /restconf {
|
location /restconf {
|
||||||
|
|
@ -30,13 +23,19 @@ server {
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Start nginx daemon
|
```
|
||||||
|
Start nginx daemon
|
||||||
|
```
|
||||||
sudo /etc/init.d nginx start
|
sudo /etc/init.d nginx start
|
||||||
|
```
|
||||||
|
|
||||||
# Start clixon restconf daemon
|
Start clixon restconf daemon
|
||||||
|
```
|
||||||
olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.conf " -s /bin/sh www-data
|
olof@vandal> sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/routing.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
|
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
|
curl -sX POST -d '{"clicon":{"interfaces":{"interface":{"name":"eth1","type":"eth","enabled":"true"}}}}' http://localhost/restconf/data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
3. DEBUGGING
|
Start the restconf fastcgi program with debug flag:
|
||||||
++++++++++++
|
```
|
||||||
Start the restconf programs with debug flag:
|
sudo su -c "/www-data/clixon_restconf -Df /usr/local/etc/routing.conf" -s /bin/sh www-
|
||||||
sudo su -c "/www-data/clixon_restconf -D" -s /bin/sh www-data
|
data
|
||||||
|
```
|
||||||
Look at syslog:
|
Look at syslog:
|
||||||
|
```
|
||||||
tail -f /var/log/syslog | grep clixon_restconf
|
tail -f /var/log/syslog | grep clixon_restconf
|
||||||
|
```
|
||||||
|
|
||||||
Send command:
|
Send command:
|
||||||
|
```
|
||||||
curl -G http://127.0.0.1/restconf/data/*
|
curl -G http://127.0.0.1/restconf/data/*
|
||||||
|
```
|
||||||
|
|
@ -33,7 +33,6 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -46,9 +45,9 @@
|
||||||
#include <fcgi_stdio.h>
|
#include <fcgi_stdio.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <sys/param.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -111,98 +110,6 @@ clicon_debug_xml(int dbglevel,
|
||||||
return retval;
|
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
|
* @param[in] r Fastcgi request handle
|
||||||
*/
|
*/
|
||||||
|
|
@ -272,45 +179,6 @@ static int nplugins = 0;
|
||||||
static plghndl_t *plugins = NULL;
|
static plghndl_t *plugins = NULL;
|
||||||
static credentials_t *p_credentials = NULL; /* Credentials callback */
|
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
|
/*! Load all plugins you can find in CLICON_RESTCONF_DIR
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -319,65 +187,40 @@ restconf_plugin_load(clicon_handle h)
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *dir;
|
char *dir;
|
||||||
int ndp;
|
int ndp;
|
||||||
struct dirent *dp;
|
struct dirent *dp = NULL;
|
||||||
int i;
|
int i;
|
||||||
char *filename;
|
|
||||||
plghndl_t *handle;
|
plghndl_t *handle;
|
||||||
|
char filename[MAXPATHLEN];
|
||||||
|
|
||||||
if ((dir = clicon_restconf_dir(h)) == NULL){
|
if ((dir = clicon_restconf_dir(h)) == NULL){
|
||||||
clicon_err(OE_PLUGIN, 0, "clicon_restconf_dir not defined");
|
clicon_err(OE_PLUGIN, 0, "clicon_restconf_dir not defined");
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
/* Get plugin objects names from plugin directory */
|
/* 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;
|
goto quit;
|
||||||
|
|
||||||
/* Load all plugins */
|
/* Load all plugins */
|
||||||
for (i = 0; i < ndp; i++) {
|
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' ...",
|
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||||
(int)strlen(filename), filename);
|
(int)strlen(filename), filename);
|
||||||
if (filename == NULL) {
|
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
p_credentials = dlsym(handle, "restconf_credentials");
|
||||||
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
|
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
||||||
goto quit;
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
|
|
||||||
clicon_err(OE_UNIX, errno, "chunk");
|
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
plugins[nplugins++] = handle;
|
plugins[nplugins++] = handle;
|
||||||
unchunk (filename);
|
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
quit:
|
quit:
|
||||||
unchunk_group(__FUNCTION__);
|
if (dp)
|
||||||
|
free(dp);
|
||||||
return retval;
|
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 */
|
/*! Unload all restconf plugins */
|
||||||
int
|
int
|
||||||
|
|
@ -387,8 +230,10 @@ restconf_plugin_unload(clicon_handle h)
|
||||||
|
|
||||||
for (i = 0; i < nplugins; i++)
|
for (i = 0; i < nplugins; i++)
|
||||||
plugin_unload(h, plugins[i]);
|
plugin_unload(h, plugins[i]);
|
||||||
if (plugins)
|
if (plugins){
|
||||||
unchunk(plugins);
|
free(plugins);
|
||||||
|
plugins = NULL;
|
||||||
|
}
|
||||||
nplugins = 0;
|
nplugins = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@
|
||||||
int notfound(FCGX_Request *r);
|
int notfound(FCGX_Request *r);
|
||||||
int badrequest(FCGX_Request *r);
|
int badrequest(FCGX_Request *r);
|
||||||
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
|
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);
|
int test(FCGX_Request *r, int dbg);
|
||||||
cbuf *readdata(FCGX_Request *r);
|
cbuf *readdata(FCGX_Request *r);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <curl/curl.h>
|
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,6 @@ Mapping netconf error-tag -> status code
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -153,86 +152,19 @@ api_data_get_gen(clicon_handle h,
|
||||||
int head)
|
int head)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cg_var *cv;
|
|
||||||
char *val;
|
|
||||||
char *v;
|
|
||||||
int i;
|
|
||||||
cbuf *path = NULL;
|
cbuf *path = NULL;
|
||||||
cbuf *path1 = NULL;
|
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
cxobj **vec = NULL;
|
cxobj **vec = NULL;
|
||||||
yang_spec *yspec;
|
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;
|
cxobj *xret = NULL;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
if ((path = cbuf_new()) == NULL)
|
if ((path = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if ((path1 = cbuf_new()) == NULL) /* without [] qualifiers */
|
if (api_path2xpath_cvv(yspec, pcvec, pi, path) < 0){
|
||||||
goto done;
|
notfound(r);
|
||||||
cv = NULL;
|
goto done;
|
||||||
cprintf(path1, "/");
|
|
||||||
/* translate eg a/b=c -> a/[b=c] */
|
|
||||||
for (i=pi; i<cvec_len(pcvec); i++){
|
|
||||||
cv = cvec_i(pcvec, i);
|
|
||||||
name = cv_name_get(cv);
|
|
||||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
|
||||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
|
||||||
if (i == pi){
|
|
||||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
assert(y!=NULL);
|
|
||||||
if ((y = yang_find_syntax((yang_node*)y, name)) == NULL){
|
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Check if has value, means '=' */
|
|
||||||
if (cv2str(cv, NULL, 0) > 0){
|
|
||||||
if ((val = cv2str_dup(cv)) == NULL)
|
|
||||||
goto done;
|
|
||||||
v = val;
|
|
||||||
/* XXX sync with yang */
|
|
||||||
while((v=index(v, ',')) != NULL){
|
|
||||||
*v = '\0';
|
|
||||||
v++;
|
|
||||||
}
|
|
||||||
/* Find keys */
|
|
||||||
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
|
|
||||||
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
|
|
||||||
__FUNCTION__, y->ys_argument);
|
|
||||||
notfound(r);
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
clicon_debug(1, "ykey:%s", ykey->ys_argument);
|
|
||||||
|
|
||||||
/* The value is a list of keys: <key>[ <key>]* */
|
|
||||||
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
|
|
||||||
goto done;
|
|
||||||
cvi = NULL;
|
|
||||||
/* Iterate over individual yang keys */
|
|
||||||
cprintf(path, "/%s", name);
|
|
||||||
v = val;
|
|
||||||
while ((cvi = cvec_each(cvk, cvi)) != NULL){
|
|
||||||
cprintf(path, "[%s=%s]", cv_string_get(cvi), v);
|
|
||||||
v += strlen(v)+1;
|
|
||||||
}
|
|
||||||
if (val)
|
|
||||||
free(val);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
cprintf(path, "%s%s", (i==pi?"":"/"), name);
|
|
||||||
cprintf(path1, "/%s", name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
clicon_debug(1, "%s path:%s", __FUNCTION__, cbuf_get(path));
|
||||||
if (clicon_rpc_get_config(h, "running", cbuf_get(path), &xret) < 0){
|
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);
|
cbuf_free(cbx);
|
||||||
if (path)
|
if (path)
|
||||||
cbuf_free(path);
|
cbuf_free(path);
|
||||||
if (path1)
|
|
||||||
cbuf_free(path1);
|
|
||||||
if (xret)
|
if (xret)
|
||||||
xml_free(xret);
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -366,7 +296,7 @@ api_data_edit(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cbx, "</config>");
|
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",
|
if (clicon_rpc_edit_config(h, "candidate",
|
||||||
operation,
|
operation,
|
||||||
api_path,
|
api_path,
|
||||||
|
|
@ -407,7 +337,8 @@ api_data_edit(clicon_handle h,
|
||||||
|
|
||||||
If the data resource already exists, then the POST request MUST fail
|
If the data resource already exists, then the POST request MUST fail
|
||||||
and a "409 Conflict" status-line MUST be returned.
|
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
|
int
|
||||||
api_data_post(clicon_handle h,
|
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] pi Offset, where to start pcvec
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] data Stream input data
|
* @param[in] data Stream input data
|
||||||
* Example:
|
* @example
|
||||||
curl -X PUT -d {\"enabled\":\"false\"} http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
curl -X PUT -d '{"enabled":"false"}' http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||||
*
|
*
|
||||||
PUT:
|
PUT:
|
||||||
if the PUT request creates a new resource,
|
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
|
# Location of frontend .cli cligen spec files
|
||||||
CLICON_CLISPEC_DIR libdir/APPNAME/clispec
|
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
|
# Enabled uses "startup" configuration on boot
|
||||||
CLICON_USE_STARTUP_CONFIG 0
|
CLICON_USE_STARTUP_CONFIG 0
|
||||||
|
|
||||||
|
|
@ -121,6 +117,9 @@ CLICON_BACKEND_PIDFILE localstatedir/APPNAME/APPNAME.pidfile
|
||||||
# Directory where "running", "candidate" and "startup" are placed
|
# Directory where "running", "candidate" and "startup" are placed
|
||||||
CLICON_XMLDB_DIR localstatedir/APPNAME
|
CLICON_XMLDB_DIR localstatedir/APPNAME
|
||||||
|
|
||||||
|
# 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
|
# Dont include keys in cvec in cli vars callbacks, ie a & k in 'a <b> k <c>' ignored
|
||||||
# CLICON_CLI_VARONLY 1
|
# CLICON_CLI_VARONLY 1
|
||||||
|
|
||||||
|
|
|
||||||
239
configure
vendored
239
configure
vendored
|
|
@ -632,6 +632,8 @@ CPP
|
||||||
OBJEXT
|
OBJEXT
|
||||||
EXEEXT
|
EXEEXT
|
||||||
ac_ct_CC
|
ac_ct_CC
|
||||||
|
with_keyvalue
|
||||||
|
with_restconf
|
||||||
RANLIB
|
RANLIB
|
||||||
AR
|
AR
|
||||||
EXE_SUFFIX
|
EXE_SUFFIX
|
||||||
|
|
@ -702,8 +704,9 @@ ac_subst_files=''
|
||||||
ac_user_opts='
|
ac_user_opts='
|
||||||
enable_option_checking
|
enable_option_checking
|
||||||
with_cligen
|
with_cligen
|
||||||
|
with_restconf
|
||||||
|
with_keyvalue
|
||||||
with_qdbm
|
with_qdbm
|
||||||
enable_keycontent
|
|
||||||
'
|
'
|
||||||
ac_precious_vars='build_alias
|
ac_precious_vars='build_alias
|
||||||
host_alias
|
host_alias
|
||||||
|
|
@ -1324,17 +1327,13 @@ if test -n "$ac_init_help"; then
|
||||||
|
|
||||||
cat <<\_ACEOF
|
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:
|
Optional Packages:
|
||||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||||
--with-cligen=dir Use CLIGEN here
|
--with-cligen=dir Use CLIGEN here
|
||||||
--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:
|
Some influential environment variables:
|
||||||
CC C compiler command
|
CC C compiler command
|
||||||
|
|
@ -2136,7 +2135,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||||
: ${CFLAGS="-O2"}
|
: ${CFLAGS="-O2"}
|
||||||
|
|
||||||
CLIXON_VERSION_MAJOR="3"
|
CLIXON_VERSION_MAJOR="3"
|
||||||
CLIXON_VERSION_MINOR="2"
|
CLIXON_VERSION_MINOR="3"
|
||||||
CLIXON_VERSION_PATCH="0"
|
CLIXON_VERSION_PATCH="0"
|
||||||
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
||||||
# Fix to specific version (eg 3.5) or head (3)
|
# Fix to specific version (eg 3.5) or head (3)
|
||||||
|
|
@ -2327,6 +2326,8 @@ test -n "$target_alias" &&
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# If yes, compile apps/restconf
|
||||||
|
# If yes, compile datastore/keyvalue
|
||||||
|
|
||||||
#
|
#
|
||||||
ac_ext=c
|
ac_ext=c
|
||||||
|
|
@ -3541,7 +3542,6 @@ if test "${with_cligen}"; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
|
{ $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; }
|
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
|
||||||
if ${ac_cv_path_GREP+:} false; then :
|
if ${ac_cv_path_GREP+:} false; then :
|
||||||
|
|
@ -3866,21 +3866,92 @@ else
|
||||||
fi
|
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.
|
# Check whether --with-qdbm was given.
|
||||||
if test "${with_qdbm+set}" = set; then :
|
if test "${with_qdbm+set}" = set; then :
|
||||||
withval=$with_qdbm;
|
withval=$with_qdbm;
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "${with_qdbm}"; then
|
if test "${with_qdbm}"; then
|
||||||
echo "Using QDBM here: ${with_qdbm}"
|
echo "Using QDBM here: ${with_qdbm}"
|
||||||
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
||||||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
||||||
fi
|
fi
|
||||||
|
# Problem: depot.h may be in qdbm/depot.h.
|
||||||
# Problem: depot.h may be in qdbm/depot.h.
|
for ac_header in depot.h
|
||||||
for ac_header in depot.h
|
|
||||||
do :
|
do :
|
||||||
ac_fn_c_check_header_mongrel "$LINENO" "depot.h" "ac_cv_header_depot_h" "$ac_includes_default"
|
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 :
|
if test "x$ac_cv_header_depot_h" = xyes; then :
|
||||||
|
|
@ -3907,7 +3978,7 @@ fi
|
||||||
|
|
||||||
done
|
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; }
|
$as_echo_n "checking for dpopen in -lqdbm... " >&6; }
|
||||||
if ${ac_cv_lib_qdbm_dpopen+:} false; then :
|
if ${ac_cv_lib_qdbm_dpopen+:} false; then :
|
||||||
$as_echo_n "(cached) " >&6
|
$as_echo_n "(cached) " >&6
|
||||||
|
|
@ -3954,6 +4025,7 @@ else
|
||||||
as_fn_error $? "libqdbm-dev required" "$LINENO" 5
|
as_fn_error $? "libqdbm-dev required" "$LINENO" 5
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
|
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
|
||||||
$as_echo_n "checking for crypt in -lcrypt... " >&6; }
|
$as_echo_n "checking for crypt in -lcrypt... " >&6; }
|
||||||
|
|
@ -4179,55 +4251,6 @@ _ACEOF
|
||||||
fi
|
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
|
for ac_func in inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp
|
||||||
do :
|
do :
|
||||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||||
|
|
@ -4241,81 +4264,9 @@ fi
|
||||||
done
|
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
|
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"
|
||||||
# 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"
|
|
||||||
|
|
||||||
cat >confcache <<\_ACEOF
|
cat >confcache <<\_ACEOF
|
||||||
# This file is a shell script that caches the results of configure
|
# This file is a shell script that caches the results of configure
|
||||||
|
|
@ -5017,7 +4968,6 @@ do
|
||||||
"apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;;
|
"apps/backend/Makefile") CONFIG_FILES="$CONFIG_FILES apps/backend/Makefile" ;;
|
||||||
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
|
"apps/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/netconf/Makefile" ;;
|
||||||
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
|
"apps/restconf/Makefile") CONFIG_FILES="$CONFIG_FILES apps/restconf/Makefile" ;;
|
||||||
"apps/dbctrl/Makefile") CONFIG_FILES="$CONFIG_FILES apps/dbctrl/Makefile" ;;
|
|
||||||
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
|
"include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;;
|
||||||
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
|
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
|
||||||
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
|
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
|
||||||
|
|
@ -5030,6 +4980,9 @@ do
|
||||||
"docker/backend/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/backend/Dockerfile" ;;
|
"docker/backend/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/backend/Dockerfile" ;;
|
||||||
"docker/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Makefile" ;;
|
"docker/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Makefile" ;;
|
||||||
"docker/netconf/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Dockerfile" ;;
|
"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" ;;
|
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
|
||||||
|
|
||||||
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
|
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
|
||||||
|
|
|
||||||
69
configure.ac
69
configure.ac
|
|
@ -42,7 +42,7 @@ AC_INIT(lib/clixon/clixon.h.in)
|
||||||
: ${CFLAGS="-O2"}
|
: ${CFLAGS="-O2"}
|
||||||
|
|
||||||
CLIXON_VERSION_MAJOR="3"
|
CLIXON_VERSION_MAJOR="3"
|
||||||
CLIXON_VERSION_MINOR="2"
|
CLIXON_VERSION_MINOR="3"
|
||||||
CLIXON_VERSION_PATCH="0"
|
CLIXON_VERSION_PATCH="0"
|
||||||
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
|
||||||
# Fix to specific version (eg 3.5) or head (3)
|
# Fix to specific version (eg 3.5) or head (3)
|
||||||
|
|
@ -77,6 +77,8 @@ AC_SUBST(SH_SUFFIX)
|
||||||
AC_SUBST(EXE_SUFFIX)
|
AC_SUBST(EXE_SUFFIX)
|
||||||
AC_SUBST(AR)
|
AC_SUBST(AR)
|
||||||
AC_SUBST(RANLIB)
|
AC_SUBST(RANLIB)
|
||||||
|
AC_SUBST(with_restconf) # If yes, compile apps/restconf
|
||||||
|
AC_SUBST(with_keyvalue) # If yes, compile datastore/keyvalue
|
||||||
|
|
||||||
#
|
#
|
||||||
AC_PROG_CC()
|
AC_PROG_CC()
|
||||||
|
|
@ -119,22 +121,39 @@ if test "${with_cligen}"; then
|
||||||
LDFLAGS="-L${with_cligen}/lib ${LDFLAGS}"
|
LDFLAGS="-L${with_cligen}/lib ${LDFLAGS}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
AC_CHECK_HEADERS(cligen/cligen.h,, AC_MSG_ERROR(cligen missing. Try: git clone https://github.com/olofhagsand/cligen.git))
|
AC_CHECK_HEADERS(cligen/cligen.h,, AC_MSG_ERROR(cligen missing. Try: git clone https://github.com/olofhagsand/cligen.git))
|
||||||
|
|
||||||
AC_CHECK_LIB(:libcligen.so.${CLIGEN_VERSION}, cligen_init,, AC_MSG_ERROR([CLIgen${CLIGEN_VERSION} missing. Try: git clone https://github.com/olofhagsand/cligen.git]))
|
AC_CHECK_LIB(: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
|
# This is for restconf (and fastcgi)
|
||||||
AC_ARG_WITH(qdbm, [ --with-qdbm=dir Use QDBM here ] )
|
AC_ARG_WITH([restconf],
|
||||||
if test "${with_qdbm}"; then
|
[AS_HELP_STRING([--without-restconf],[disable support for restconf])],
|
||||||
echo "Using QDBM here: ${with_qdbm}"
|
[],
|
||||||
CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}"
|
[with_restconf=yes])
|
||||||
LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}"
|
if test "x${with_restconf}" == xyes; then
|
||||||
|
# Lives in libfcgi-dev
|
||||||
|
AC_CHECK_LIB(fcgi, FCGX_Init,, AC_MSG_ERROR([libfcgi-dev missing]))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Problem: depot.h may be in qdbm/depot.h.
|
# This is for keyvalue datastore (and qdbm)
|
||||||
AC_CHECK_HEADERS(depot.h,,[AC_CHECK_HEADERS(qdbm/depot.h,,AC_MSG_ERROR(libqdbm-dev required))])
|
AC_ARG_WITH([keyvalue],
|
||||||
AC_CHECK_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required))
|
[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_LIB(crypt, crypt)
|
||||||
AC_CHECK_HEADERS(crypt.h)
|
AC_CHECK_HEADERS(crypt.h)
|
||||||
|
|
@ -151,32 +170,8 @@ AC_CHECK_LIB(socket, socket)
|
||||||
AC_CHECK_LIB(nsl, xdr_char)
|
AC_CHECK_LIB(nsl, xdr_char)
|
||||||
AC_CHECK_LIB(dl, dlopen)
|
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)
|
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>])
|
AH_BOTTOM([#include <clixon_custom.h>])
|
||||||
|
|
||||||
AC_OUTPUT(Makefile
|
AC_OUTPUT(Makefile
|
||||||
|
|
@ -188,7 +183,6 @@ AC_OUTPUT(Makefile
|
||||||
apps/backend/Makefile
|
apps/backend/Makefile
|
||||||
apps/netconf/Makefile
|
apps/netconf/Makefile
|
||||||
apps/restconf/Makefile
|
apps/restconf/Makefile
|
||||||
apps/dbctrl/Makefile
|
|
||||||
include/Makefile
|
include/Makefile
|
||||||
etc/Makefile
|
etc/Makefile
|
||||||
etc/clixonrc
|
etc/clixonrc
|
||||||
|
|
@ -201,6 +195,9 @@ AC_OUTPUT(Makefile
|
||||||
docker/backend/Dockerfile
|
docker/backend/Dockerfile
|
||||||
docker/netconf/Makefile
|
docker/netconf/Makefile
|
||||||
docker/netconf/Dockerfile
|
docker/netconf/Dockerfile
|
||||||
|
datastore/Makefile
|
||||||
|
datastore/keyvalue/Makefile
|
||||||
|
datastore/text/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,18 +31,24 @@
|
||||||
# ***** END LICENSE BLOCK *****
|
# ***** END LICENSE BLOCK *****
|
||||||
#
|
#
|
||||||
VPATH = @srcdir@
|
VPATH = @srcdir@
|
||||||
|
prefix = @prefix@
|
||||||
|
datarootdir = @datarootdir@
|
||||||
srcdir = @srcdir@
|
srcdir = @srcdir@
|
||||||
top_srcdir = @top_srcdir@
|
top_srcdir = @top_srcdir@
|
||||||
CC = @CC@
|
|
||||||
CFLAGS = @CFLAGS@
|
|
||||||
LDFLAGS = @LDFLAGS@
|
|
||||||
|
|
||||||
prefix = @prefix@
|
|
||||||
exec_prefix = @exec_prefix@
|
exec_prefix = @exec_prefix@
|
||||||
bindir = @bindir@
|
bindir = @bindir@
|
||||||
|
libdir = @libdir@
|
||||||
|
dbdir = @prefix@/db
|
||||||
|
mandir = @mandir@
|
||||||
libexecdir = @libexecdir@
|
libexecdir = @libexecdir@
|
||||||
localstatedir = @localstatedir@
|
localstatedir = @localstatedir@
|
||||||
sysconfdir = @sysconfdir@
|
sysconfdir = @sysconfdir@
|
||||||
|
CC = @CC@
|
||||||
|
CFLAGS = @CFLAGS@
|
||||||
|
LDFLAGS = @LDFLAGS@
|
||||||
|
LIBS = @LIBS@
|
||||||
|
with_restconf = @with_restconf@
|
||||||
|
with_keyvalue = @with_keyvalue@
|
||||||
|
|
||||||
SH_SUFFIX = @SH_SUFFIX@
|
SH_SUFFIX = @SH_SUFFIX@
|
||||||
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
|
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@
|
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)
|
APPOBJ = $(APPSRC:.c=.o)
|
||||||
APPL = clixon_dbctrl
|
APPL = datastore_client
|
||||||
|
|
||||||
all: $(APPL)
|
all: $(SUBDIRS) $(APPL)
|
||||||
|
|
||||||
clean:
|
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
|
||||||
rm -f $(OBJS) *.core $(APPL) $(APPOBJ)
|
|
||||||
|
|
||||||
distclean: clean
|
|
||||||
rm -f Makefile *~ .depend
|
|
||||||
|
|
||||||
# Put demon in bin
|
|
||||||
# Put other executables in libexec/
|
|
||||||
# Also create a libexec/ directory for writeable/temporary files.
|
|
||||||
# Put config file in etc/
|
|
||||||
install: $(APPL)
|
|
||||||
install -d $(DESTDIR)$(bindir)
|
|
||||||
install $(APPL) $(DESTDIR)$(bindir)
|
|
||||||
|
|
||||||
install-include:
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
rm -f $(bindir)/$(APPL)
|
|
||||||
|
|
||||||
.SUFFIXES:
|
.SUFFIXES:
|
||||||
.SUFFIXES: .c .o
|
.SUFFIXES: .c .o
|
||||||
|
|
@ -95,14 +88,41 @@ uninstall:
|
||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $<
|
$(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $<
|
||||||
|
|
||||||
$(APPL) : $(APPOBJ) $(OBJS) $(LIBDEPS)
|
$(APPL) : $(APPOBJ) $(LIBDEPS)
|
||||||
$(CC) $(LDFLAGS) $(APPOBJ) $(OBJS) $(LIBS) -o $@
|
$(CC) $(LDFLAGS) $(APPOBJ) $(LIBS) -o $@
|
||||||
|
|
||||||
TAGS:
|
|
||||||
find . -name '*.[chyl]' -print | etags -
|
|
||||||
|
|
||||||
depend:
|
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 */
|
ensure errno is set and return -1/NULL */
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
@ -45,7 +46,11 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_queue.h"
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
|
/* clicon */
|
||||||
|
#include <clixon/clixon.h>
|
||||||
|
|
||||||
#include "clixon_chunk.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 *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Key-value store
|
||||||
*/
|
*/
|
||||||
|
#ifndef _CLIXON_KEYVALUE_H
|
||||||
#ifndef _CLIXON_PROC_H_
|
#define _CLIXON_KEYVALUE_H
|
||||||
#define _CLIXON_PROC_H_
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int clicon_proc_run (char *, void (outcb)(char *), int doerr);
|
int kv_get(xmldb_handle h, char *db, char *xpath,
|
||||||
int clicon_proc_daemon (char *);
|
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||||
int group_name2gid(char *name, gid_t *gid);
|
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>
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_log.h"
|
#include <clixon/clixon.h>
|
||||||
#include "clixon_err.h"
|
|
||||||
#include "clixon_queue.h"
|
|
||||||
#include "clixon_chunk.h"
|
#include "clixon_chunk.h"
|
||||||
#include "clixon_qdb.h"
|
#include "clixon_qdb.h"
|
||||||
|
|
||||||
|
|
@ -425,7 +424,7 @@ db_regexp(char *file,
|
||||||
/* Retrieve value if required */
|
/* Retrieve value if required */
|
||||||
if ( ! noval) {
|
if ( ! noval) {
|
||||||
if((val = dpget(iterdp, key, -1, 0, -1, &vlen)) == NULL) {
|
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;
|
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
|
1. How to document the code
|
||||||
2. How to work in git (branching)
|
2. How to work in git (branching)
|
||||||
3. How the meta-configure stuff works
|
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 small comment on one line
|
||||||
*
|
*
|
||||||
* This is a detailed description
|
* 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
|
* @retval FALSE This is a description of another return value
|
||||||
* @see See also this function
|
* @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/
|
Basically follows: http://nvie.com/posts/a-successful-git-branching-model/
|
||||||
only somewhat simplified:
|
only somewhat simplified:
|
||||||
|
|
||||||
Do commits in develop branch. When done, merge with master.
|
Do commits in develop branch. When done, merge with master.
|
||||||
|
|
||||||
$ git checkout develop
|
## How the meta-configure stuff works
|
||||||
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
|
|
||||||
+++++++++++++++++++++++++++++++++++++
|
|
||||||
configure.ac --.
|
configure.ac --.
|
||||||
| .------> autoconf* -----> configure
|
| .------> autoconf* -----> configure
|
||||||
[aclocal.m4] --+---+
|
[aclocal.m4] --+---+
|
||||||
|
|
@ -64,4 +46,4 @@ configure.ac --.
|
||||||
[config.h.in] -. v .-> [config.h] -.
|
[config.h.in] -. v .-> [config.h] -.
|
||||||
+--> config.status* -+ +--> make*
|
+--> config.status* -+ +--> make*
|
||||||
Makefile.in ---' `-> Makefile ---'
|
Makefile.in ---' `-> Makefile ---'
|
||||||
|
```
|
||||||
|
|
@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||||
# title of most generated pages and in a few other places.
|
# title of most generated pages and in a few other places.
|
||||||
# The default value is: My Project.
|
# 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
|
# 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
|
# could be handy for archiving the generated documentation or if some version
|
||||||
|
|
@ -743,7 +743,7 @@ WARN_LOGFILE =
|
||||||
# spaces.
|
# spaces.
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl
|
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/config/ ../apps/netconf ../apps/dbctrl ../datastore
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||||
# title of most generated pages and in a few other places.
|
# title of most generated pages and in a few other places.
|
||||||
# The default value is: My Project.
|
# 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
|
# 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
|
# could be handy for archiving the generated documentation or if some version
|
||||||
|
|
@ -743,7 +743,7 @@ WARN_LOGFILE =
|
||||||
# spaces.
|
# spaces.
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = ../lib/src/ ../lib/clicon/ ../apps/cli/ ../apps/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
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||||
|
|
|
||||||
|
|
@ -1,112 +1,66 @@
|
||||||
Frequently Asked Questions - CliXon
|
# Clixon FAQ
|
||||||
===================================
|
|
||||||
|
|
||||||
Q: What is CliXon?
|
## What is Clixon?
|
||||||
------------------
|
|
||||||
CliXon is a configuration management tool including CLI generation,
|
|
||||||
Yang parser, netconf interface and an embedded databases.
|
|
||||||
|
|
||||||
Q: Why should you use CliXon?
|
Clixon is a configuration management tool including a generated CLI ,
|
||||||
-----------------------------
|
Yang parser, netconf and restconf interface and an embedded databases.
|
||||||
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.
|
|
||||||
|
|
||||||
Q: What license is available?
|
## Why should I use Clixon?
|
||||||
-----------------------------
|
|
||||||
The basic license is open-source, GPLv3. Contact authors for commercial license.
|
|
||||||
|
|
||||||
Q: Is CliXon extendible?
|
If you want an easy-to-use configuration frontend based on yang with an
|
||||||
------------------------
|
open-source license. Typically for embedded devices requiring a
|
||||||
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.
|
config interface such as routers and switches.
|
||||||
|
|
||||||
Q: Which language is CliXon implemented in?
|
## What license is available?
|
||||||
-------------------------------------------
|
CLIXON is dual license. Either Apache License, Version 2.0 or GNU
|
||||||
CliXon is written in C. The plugins are written in C. The CLI
|
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)
|
specification uses cligen (http://cligen.se)
|
||||||
|
|
||||||
There is a project for writing plugins in Python. It is reasonable
|
There is a project for writing plugins in Python. It is reasonable
|
||||||
simple to spawn an external script from a backend.
|
simple to spawn an external script from a backend.
|
||||||
|
|
||||||
Q: Is CliXon different from Clicon?
|
## How to best understand Clixon?
|
||||||
-----------------------------------
|
Run the ietf yang routing example, in the example directory.
|
||||||
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.
|
|
||||||
|
|
||||||
The commit transaction mechanism has been simplified too. But CliXon
|
## How do you build and install Clixon (and the example)?
|
||||||
is not backward compliant with key-based Clixon applications, such as
|
Clixon:
|
||||||
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:
|
|
||||||
./configure;
|
./configure;
|
||||||
make;
|
make;
|
||||||
sudo make install;
|
sudo make install;
|
||||||
sudo make install-include
|
sudo make install-include
|
||||||
|
```
|
||||||
The example:
|
The example:
|
||||||
|
```
|
||||||
cd example;
|
cd example;
|
||||||
make;
|
make;
|
||||||
sudo make install
|
sudo make install
|
||||||
|
```
|
||||||
|
|
||||||
Q: What about reference documentation?
|
## What about reference documentation?
|
||||||
--------------------------------------
|
Clixon uses Doxygen for reference documentation.
|
||||||
CliXon uses Doxygen for reference documentation.
|
|
||||||
Build using 'make doc' and aim your browser at doc/html/index.html or
|
Build using 'make doc' and aim your browser at doc/html/index.html or
|
||||||
use the web resource: http://clicon.org/ref/index.html
|
use the web resource: http://clicon.org/ref/index.html
|
||||||
|
|
||||||
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 backend server: 'clixon_backend -Ff /usr/local/etc/routing.conf'
|
||||||
- Start a cli session: clixon_cli -f /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
|
- Start a netconf session: clixon_netconf -f /usr/local/etc/routing.conf
|
||||||
|
|
||||||
Q: Can you run CliXon as docker containers?
|
## How is configuration data stored?
|
||||||
-------------------------------------------
|
Configuration data is stored in an XML datastore. The default is a
|
||||||
Yes, the example works as docker containers as well. backend and cli needs a
|
text-based addatastore, but there also exists a key-value datastore
|
||||||
common file-system so they need to run as a composed pair.
|
using qdbm. In the example the datastore are regular files found in
|
||||||
cd example/docker
|
/usr/local/var/routing/.
|
||||||
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
|
## What is validate and commit?
|
||||||
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?
|
|
||||||
-------------------------------
|
|
||||||
Clixon follows netconf in its validate and commit semantics.
|
Clixon follows netconf in its validate and commit semantics.
|
||||||
In short, you edit a 'candidate' configuration, which is first
|
In short, you edit a 'candidate' configuration, which is first
|
||||||
'validated' for consistency and then 'committed' to the 'running'
|
'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
|
system state based on configuration changes. Writing commit callbacks
|
||||||
is the core functionality of a clixon system.
|
is the core functionality of a clixon system.
|
||||||
|
|
||||||
Q: How do you write a commit function?
|
## What is a Clixon configuration file?
|
||||||
--------------------------------------
|
Clixon options are stored in a configuration file you must specify
|
||||||
You write a commit function in routing_backend.c.
|
when you start a backend or client using -f. The example configuration
|
||||||
Every time a commit is made, transaction_commit() is called in the
|
file is /usr/local/etc/routing.conf.
|
||||||
backend. It has a 'transaction_data td' argument which is used to fetch
|
This file is generated from the base source clixon.conf.cpp.cpp and
|
||||||
information on added, deleted and changed entries. You access this
|
is merged with local configuration files, such as routing.conf.local.
|
||||||
information using access functions as defined in clixon_backend_transaction.h
|
This is slightly confusing and could be improved.
|
||||||
|
|
||||||
Q: How do you check what has changed on commit?
|
## Can I run Clixon as docker containers?
|
||||||
-----------------------------------------------
|
Yes, the example works as docker containers as well. backend and cli needs a
|
||||||
You use XPATHs on the XML trees in the transaction commit callback.
|
common file-system so they need to run as a composed pair.
|
||||||
Suppose you want to print all added interfaces:
|
```
|
||||||
cxobj *target = transaction_target(td); # wanted XML tree
|
cd example/docker
|
||||||
vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); /* Get added i/fs */
|
make docker # Prepares /data as shared file-system mount
|
||||||
for (i=0; i<len; i++) /* Loop over added i/fs */
|
run.sh # Starts an example backend and a cli
|
||||||
clicon_xml2file(stdout, vec[i], 0, 1); /* Print the added interface */
|
```
|
||||||
You can look for added, deleted and changed entries in this way.
|
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?
|
## How do I use netconf?
|
||||||
--------------------------------
|
|
||||||
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?
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
|
As an alternative to cli configuration, you can use netconf. Easiest is to just pipe netconf commands to the clixon_netconf application.
|
||||||
Example:
|
Example:
|
||||||
|
|
@ -176,25 +102,82 @@ Example:
|
||||||
|
|
||||||
However, more useful is to run clixon_netconf as an SSH
|
However, more useful is to run clixon_netconf as an SSH
|
||||||
subsystem. Register the subsystem in /etc/sshd_config:
|
subsystem. Register the subsystem in /etc/sshd_config:
|
||||||
|
```
|
||||||
Subsystem netconf /usr/local/bin/clixon_netconf
|
Subsystem netconf /usr/local/bin/clixon_netconf
|
||||||
|
```
|
||||||
and then invoke it from a client using
|
and then invoke it from a client using
|
||||||
|
```
|
||||||
ssh -s netconf <host>
|
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.
|
The example has a prebuilt notification stream called "ROUTING" that triggers every 10s.
|
||||||
You enable the notification either via the cli or via netconf:
|
You enable the notification either via the cli or via netconf:
|
||||||
cli> notify
|
cli> notify
|
||||||
cli> Routing notification
|
cli> Routing notification
|
||||||
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><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
||||||
<rpc-reply><ok/></rpc-reply>]]>]]>
|
<rpc-reply><ok/></rpc-reply>]]>]]>
|
||||||
<notification><event>Routing notification</event></notification>]]>]]>
|
<notification><event>Routing notification</event></notification>]]>]]>
|
||||||
<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
|
## Compile and run
|
||||||
------------------
|
```
|
||||||
cd example
|
cd example
|
||||||
make && sudo make install
|
make && sudo make install
|
||||||
# Start backend
|
```
|
||||||
clixon_backend -f /usr/local/etc/routing.conf -I
|
Start backend:
|
||||||
# Edit cli
|
```
|
||||||
clixon_cli -f /usr/local/etc/routing.conf
|
clixon_backend -f /usr/local/etc/routing.conf -I
|
||||||
# Send netconf command
|
```
|
||||||
clixon_netconf -f /usr/local/etc/routing.conf
|
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>
|
<rpc><edit-config><target><candidate/></target><config>
|
||||||
<interfaces>
|
<interfaces>
|
||||||
<interface>
|
<interface>
|
||||||
|
|
@ -28,40 +34,39 @@ clixon_netconf -f /usr/local/etc/routing.conf
|
||||||
</interface>
|
</interface>
|
||||||
</interfaces>
|
</interfaces>
|
||||||
</config></edit-config></rpc>]]>]]>
|
</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></get-config></rpc>]]>]]>
|
||||||
|
|
||||||
<rpc><get-config><source><candidate/></source><filter/></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="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="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><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface/ipv4"/></get-config></rpc>]]>]]>
|
||||||
|
|
||||||
<rpc><validate><source><candidate/></source></validate></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
|
The example has an example notification triggering every 10s. To start a notification
|
||||||
stream in the session, create a subscription:
|
stream in the session, create a subscription:
|
||||||
|
```
|
||||||
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
<rpc><create-subscription><stream>ROUTING</stream></create-subscription></rpc>]]>]]>
|
||||||
<rpc-reply><ok/></rpc-reply>]]>]]>
|
<rpc-reply><ok/></rpc-reply>]]>]]>
|
||||||
<notification><event>Routing notification</event></notification>]]>]]>
|
<notification><event>Routing notification</event></notification>]]>]]>
|
||||||
<notification><event>Routing notification</event></notification>]]>]]>
|
<notification><event>Routing notification</event></notification>]]>]]>
|
||||||
...
|
...
|
||||||
|
```
|
||||||
This can also be triggered via the CLI:
|
This can also be triggered via the CLI:
|
||||||
|
```
|
||||||
cli> notify
|
cli> notify
|
||||||
cli> Routing notification
|
cli> Routing notification
|
||||||
Routing notification
|
Routing notification
|
||||||
...
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Extending
|
||||||
|
|
||||||
4. Downcall
|
|
||||||
-----------
|
|
||||||
Clixon has an extension mechanism which can be used to make extended internal
|
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
|
netconf messages to the backend configuration engine. You may need this to
|
||||||
make some special operation that is not covered by standard
|
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.
|
reading some status.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```
|
||||||
cli> downcall "This is a string"
|
cli> downcall "This is a string"
|
||||||
This is a string
|
This is a string
|
||||||
cli>p
|
```
|
||||||
|
|
||||||
5. Run as docker container
|
## Run as docker container
|
||||||
--------------------------
|
```
|
||||||
cd docker
|
cd docker
|
||||||
# look in README
|
# look in README
|
||||||
|
```
|
||||||
|
|
@ -27,3 +27,7 @@ CLICON_CLIGEN_CALLBACK_SINGLE_ARG 0
|
||||||
|
|
||||||
# Enabled uses "startup" configuration on boot
|
# 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 */
|
/* Clixon version string */
|
||||||
#undef 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. */
|
/* Define to 1 if you have the `alphasort' function. */
|
||||||
#undef HAVE_ALPHASORT
|
#undef HAVE_ALPHASORT
|
||||||
|
|
||||||
|
|
@ -37,9 +33,6 @@
|
||||||
/* Define to 1 if you have the `crypt' library (-lcrypt). */
|
/* Define to 1 if you have the `crypt' library (-lcrypt). */
|
||||||
#undef HAVE_LIBCRYPT
|
#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). */
|
/* Define to 1 if you have the `dl' library (-ldl). */
|
||||||
#undef HAVE_LIBDL
|
#undef HAVE_LIBDL
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** 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
|
* Meta-include file that includes all sub-files in control-lib
|
||||||
* Note: this include files is for external purposes. Do not include this
|
* Note: this include files is for external purposes. Do not include this
|
||||||
* file in clicon lib-routines.
|
* file in clicon lib-routines.
|
||||||
|
|
@ -67,22 +70,18 @@
|
||||||
#include <clixon/clixon_handle.h>
|
#include <clixon/clixon_handle.h>
|
||||||
#include <clixon/clixon_yang.h>
|
#include <clixon/clixon_yang.h>
|
||||||
#include <clixon/clixon_yang_type.h>
|
#include <clixon/clixon_yang_type.h>
|
||||||
#include <clixon/clixon_chunk.h>
|
|
||||||
#include <clixon/clixon_event.h>
|
#include <clixon/clixon_event.h>
|
||||||
#include <clixon/clixon_string.h>
|
#include <clixon/clixon_string.h>
|
||||||
#include <clixon/clixon_file.h>
|
#include <clixon/clixon_file.h>
|
||||||
#include <clixon/clixon_xml.h>
|
#include <clixon/clixon_xml.h>
|
||||||
#include <clixon/clixon_proto.h>
|
#include <clixon/clixon_proto.h>
|
||||||
#include <clixon/clixon_proto_encode.h>
|
|
||||||
#include <clixon/clixon_proto_client.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_options.h>
|
||||||
#include <clixon/clixon_xml_map.h>
|
#include <clixon/clixon_xml_map.h>
|
||||||
#include <clixon/clixon_xml_db.h>
|
#include <clixon/clixon_xml_db.h>
|
||||||
#include <clixon/clixon_xsl.h>
|
#include <clixon/clixon_xsl.h>
|
||||||
#include <clixon/clixon_json.h>
|
#include <clixon/clixon_json.h>
|
||||||
#include <clixon/clixon_plugin.h>
|
|
||||||
#include <clixon/clixon_plugin.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global variables generated by Makefile
|
* Global variables generated by Makefile
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,10 @@
|
||||||
|
|
||||||
|
|
||||||
int clicon_file_dirent(const char *dir, struct dirent **ent,
|
int clicon_file_dirent(const char *dir, struct dirent **ent,
|
||||||
const char *regexp, mode_t type, const char *label);
|
const char *regexp, mode_t type);
|
||||||
|
|
||||||
char *clicon_tmpfile(const char *label);
|
|
||||||
|
|
||||||
int clicon_file_copy(char *src, char *target);
|
int clicon_file_copy(char *src, char *target);
|
||||||
|
|
||||||
|
int group_name2gid(char *name, gid_t *gid);
|
||||||
|
|
||||||
#endif /* _CLIXON_FILE_H_ */
|
#endif /* _CLIXON_FILE_H_ */
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ char *clicon_cli_dir(clicon_handle h);
|
||||||
char *clicon_clispec_dir(clicon_handle h);
|
char *clicon_clispec_dir(clicon_handle h);
|
||||||
char *clicon_netconf_dir(clicon_handle h);
|
char *clicon_netconf_dir(clicon_handle h);
|
||||||
char *clicon_restconf_dir(clicon_handle h);
|
char *clicon_restconf_dir(clicon_handle h);
|
||||||
char *clicon_archive_dir(clicon_handle h);
|
char *clicon_xmldb_plugin(clicon_handle h);
|
||||||
int clicon_sock_family(clicon_handle h);
|
int clicon_sock_family(clicon_handle h);
|
||||||
char *clicon_sock(clicon_handle h);
|
char *clicon_sock(clicon_handle h);
|
||||||
int clicon_sock_port(clicon_handle h);
|
int clicon_sock_port(clicon_handle h);
|
||||||
|
|
@ -120,4 +120,16 @@ int clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys);
|
||||||
char *clicon_dbspec_name(clicon_handle h);
|
char *clicon_dbspec_name(clicon_handle h);
|
||||||
int clicon_dbspec_name_set(clicon_handle h, char *name);
|
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_ */
|
#endif /* _CLIXON_OPTIONS_H_ */
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ typedef void *(find_plugin_t)(clicon_handle, char *);
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
/* Common plugin function names, function types and signatures.
|
/* 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
|
* Cli see cli_plugin.c
|
||||||
* Backend see config_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 */
|
/* Find a function in global namespace or a plugin. XXX clicon internal */
|
||||||
void *clicon_find_func(clicon_handle h, char *plugin, char *func);
|
void *clicon_find_func(clicon_handle h, char *plugin, char *func);
|
||||||
|
|
||||||
|
plghndl_t plugin_load (clicon_handle h, char *file, int dlflags);
|
||||||
|
|
||||||
|
int plugin_unload(clicon_handle h, plghndl_t *handle);
|
||||||
|
|
||||||
#endif /* _CLIXON_PLUGIN_H_ */
|
#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_strsep(char *string, char *delim, int *nvec0);
|
||||||
char *clicon_strjoin (int argc, char **argv, char *delim);
|
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
|
#ifndef HAVE_STRNDUP
|
||||||
char *clicon_strndup (const char *, size_t);
|
char *clicon_strndup (const char *, size_t);
|
||||||
#endif /* ! HAVE_STRNDUP */
|
#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_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_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_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
|
||||||
|
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
char *xml_type2str(enum cxobj_type type);
|
||||||
char *xml_name(cxobj *xn);
|
char *xml_name(cxobj *xn);
|
||||||
int xml_name_set(cxobj *xn, char *name);
|
int xml_name_set(cxobj *xn, char *name);
|
||||||
char *xml_namespace(cxobj *xn);
|
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);
|
char *xml_value_append(cxobj *xn, char *val);
|
||||||
enum cxobj_type xml_type(cxobj *xn);
|
enum cxobj_type xml_type(cxobj *xn);
|
||||||
int xml_type_set(cxobj *xn, enum cxobj_type type);
|
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);
|
cg_var *xml_cv_get(cxobj *xn);
|
||||||
int xml_cv_set(cxobj *xn, cg_var *cv);
|
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(char *name, cxobj *xn_parent);
|
||||||
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
|
cxobj *xml_new_spec(char *name, cxobj *xn_parent, void *spec);
|
||||||
void *xml_spec(cxobj *x);
|
void *xml_spec(cxobj *x);
|
||||||
|
void *xml_spec_set(cxobj *x, void *spec);
|
||||||
cxobj *xml_find(cxobj *xn_parent, char *name);
|
cxobj *xml_find(cxobj *xn_parent, char *name);
|
||||||
|
|
||||||
int xml_addsub(cxobj *xp, cxobj *xc);
|
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);
|
int xml_rootchild(cxobj *xp, int i, cxobj **xcp);
|
||||||
|
|
||||||
char *xml_body(cxobj *xn);
|
char *xml_body(cxobj *xn);
|
||||||
|
cxobj *xml_body_get(cxobj *xn);
|
||||||
char *xml_find_value(cxobj *xn_parent, char *name);
|
char *xml_find_value(cxobj *xn_parent, char *name);
|
||||||
char *xml_find_body(cxobj *xn, char *name);
|
char *xml_find_body(cxobj *xn, char *name);
|
||||||
|
|
||||||
|
|
@ -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_str(char *str, cxobj **xml_top);
|
||||||
int clicon_xml_parse(cxobj **cxtop, char *format, ...);
|
int clicon_xml_parse(cxobj **cxtop, char *format, ...);
|
||||||
|
|
||||||
|
int xmltree2cbuf(cbuf *cb, cxobj *x, int level);
|
||||||
int xml_copy(cxobj *x0, cxobj *x1);
|
int xml_copy(cxobj *x0, cxobj *x1);
|
||||||
cxobj *xml_dup(cxobj *x0);
|
cxobj *xml_dup(cxobj *x0);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,31 +30,126 @@
|
||||||
the terms of any one of the Apache License version 2 or the GPL.
|
the terms of any one of the Apache License version 2 or the GPL.
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
* XML support functions.
|
|
||||||
*/
|
*/
|
||||||
#ifndef _CLIXON_XML_DB_H
|
#ifndef _CLIXON_XML_DB_H
|
||||||
#define _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
|
* Prototypes
|
||||||
|
* API
|
||||||
*/
|
*/
|
||||||
int yang2xmlkeyfmt(yang_stmt *ys, int inclkey, char **xkfmt);
|
int xmldb_plugin_load(clicon_handle h, char *filename);
|
||||||
int xmlkeyfmt2key(char *xkfmt, cvec *cvv, char **xk);
|
int xmldb_plugin_unload(clicon_handle h);
|
||||||
int xmlkeyfmt2xpath(char *xkfmt, cvec *cvv, char **xk);
|
|
||||||
|
|
||||||
|
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,
|
int xmldb_get(clicon_handle h, char *db, char *xpath,
|
||||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||||
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
|
int xmldb_put(clicon_handle h, char *db, enum operation_type op,
|
||||||
char *api_path, cxobj *xt);
|
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_copy(clicon_handle h, char *from, char *to);
|
||||||
int xmldb_lock(clicon_handle h, char *db, int pid);
|
int xmldb_lock(clicon_handle h, char *db, int pid);
|
||||||
int xmldb_unlock(clicon_handle h, char *db, int pid);
|
int xmldb_unlock(clicon_handle h, char *db);
|
||||||
int xmldb_unlock_all(clicon_handle h, int pid);
|
int xmldb_unlock_all(clicon_handle h, int pid);
|
||||||
int xmldb_islocked(clicon_handle h, char *db);
|
int xmldb_islocked(clicon_handle h, char *db);
|
||||||
int xmldb_exists(clicon_handle h, char *db);
|
int xmldb_exists(clicon_handle h, char *db);
|
||||||
int xmldb_delete(clicon_handle h, char *db);
|
int xmldb_delete(clicon_handle h, char *db);
|
||||||
int xmldb_init(clicon_handle h, char *db);
|
int xmldb_create(clicon_handle h, char *db);
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_DB_H */
|
#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 ***first, size_t *firstlen,
|
||||||
cxobj ***second, size_t *secondlen,
|
cxobj ***second, size_t *secondlen,
|
||||||
cxobj ***changed1, cxobj ***changed2, size_t *changedlen);
|
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_ */
|
#endif /* _CLIXON_XML_MAP_H_ */
|
||||||
|
|
|
||||||
|
|
@ -61,12 +61,10 @@ CPPFLAGS = @CPPFLAGS@
|
||||||
|
|
||||||
INCLUDES = -I. @INCLUDES@ -I$(top_srcdir)/lib/clixon -I$(top_srcdir)/include -I$(top_srcdir)
|
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 \
|
SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
|
||||||
clixon_chunk.c clixon_proc.c \
|
|
||||||
clixon_string.c clixon_handle.c \
|
clixon_string.c clixon_handle.c \
|
||||||
clixon_xml.c clixon_xml_map.c clixon_file.c \
|
clixon_xml.c clixon_xml_map.c clixon_file.c \
|
||||||
clixon_json.c \
|
clixon_json.c clixon_yang.c clixon_yang_type.c \
|
||||||
clixon_yang.c clixon_yang_type.c \
|
|
||||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||||
clixon_proto.c clixon_proto_client.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
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,6 @@
|
||||||
|
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -217,7 +216,7 @@ clicon_err_save(void)
|
||||||
{
|
{
|
||||||
struct err_state *es;
|
struct err_state *es;
|
||||||
|
|
||||||
if ((es = chunk(sizeof(*es), NULL)) == NULL)
|
if ((es = malloc(sizeof(*es))) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
es->es_errno = clicon_errno;
|
es->es_errno = clicon_errno;
|
||||||
es->es_suberrno = clicon_suberrno;
|
es->es_suberrno = clicon_suberrno;
|
||||||
|
|
@ -232,10 +231,11 @@ clicon_err_restore(void* handle)
|
||||||
{
|
{
|
||||||
struct err_state *es;
|
struct err_state *es;
|
||||||
|
|
||||||
es = (struct err_state *)handle;
|
if ((es = (struct err_state *)handle) != NULL){
|
||||||
clicon_errno = es->es_errno;
|
clicon_errno = es->es_errno;
|
||||||
clicon_suberrno = es->es_suberrno;
|
clicon_suberrno = es->es_suberrno;
|
||||||
strncpy(clicon_err_reason, es->es_reason, ERR_STRLEN-1);
|
strncpy(clicon_err_reason, es->es_reason, ERR_STRLEN-1);
|
||||||
unchunk(es);
|
free(es);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <grp.h>
|
||||||
|
|
||||||
/* cligen */
|
/* cligen */
|
||||||
#include <cligen/cligen.h>
|
#include <cligen/cligen.h>
|
||||||
|
|
@ -58,7 +59,6 @@
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_string.h"
|
#include "clixon_string.h"
|
||||||
#include "clixon_file.h"
|
#include "clixon_file.h"
|
||||||
|
|
||||||
|
|
@ -66,7 +66,8 @@
|
||||||
* qsort function
|
* qsort function
|
||||||
*/
|
*/
|
||||||
static int
|
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 *d1 = (struct dirent *)arg1;
|
||||||
struct dirent *d2 = (struct dirent *)arg2;
|
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
|
/*! Return sorted matching files from a directory
|
||||||
* @param[in] dir Directory path
|
* @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] regexp Regexp filename matching
|
||||||
* @param[in] type File type matching, see stat(2)
|
* @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 n Number of matching files in directory
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
@ -92,34 +93,34 @@ clicon_file_dirent_sort(const void* arg1, const void* arg2)
|
||||||
* @code
|
* @code
|
||||||
* char *dir = "/root/fs";
|
* char *dir = "/root/fs";
|
||||||
* struct dirent *dp;
|
* 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;
|
* return -1;
|
||||||
* for (i = 0; i < ndp; i++)
|
* for (i = 0; i < ndp; i++)
|
||||||
* do something with dp[i].d_name;
|
* do something with dp[i].d_name;
|
||||||
* unchunk_group(__FUNCTION__);
|
* free(dp);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_file_dirent(const char *dir,
|
clicon_file_dirent(const char *dir,
|
||||||
struct dirent **ent,
|
struct dirent **ent,
|
||||||
const char *regexp,
|
const char *regexp,
|
||||||
mode_t type,
|
mode_t type)
|
||||||
const char *label)
|
|
||||||
{
|
{
|
||||||
DIR *dirp;
|
int retval = -1;
|
||||||
int retval = -1;
|
DIR *dirp;
|
||||||
int res;
|
int res;
|
||||||
int nent;
|
int nent;
|
||||||
char *filename;
|
regex_t re;
|
||||||
regex_t re;
|
char errbuf[128];
|
||||||
char errbuf[128];
|
char filename[MAXPATHLEN];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
struct dirent dent;
|
struct dirent dent;
|
||||||
struct dirent *dresp;
|
struct dirent *dresp;
|
||||||
struct dirent *tmp;
|
struct dirent *tmp;
|
||||||
struct dirent *new = NULL;
|
struct dirent *new = NULL;
|
||||||
struct dirent *dvecp = NULL;
|
struct dirent *dvecp = NULL;
|
||||||
|
|
||||||
|
|
||||||
*ent = NULL;
|
*ent = NULL;
|
||||||
nent = 0;
|
nent = 0;
|
||||||
|
|
||||||
|
|
@ -137,7 +138,9 @@ clicon_file_dirent(const char *dir,
|
||||||
goto quit;
|
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) {
|
if (res != 0) {
|
||||||
clicon_err(OE_UNIX, 0, "readdir: %s", strerror(errno));
|
clicon_err(OE_UNIX, 0, "readdir: %s", strerror(errno));
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
@ -150,12 +153,8 @@ clicon_file_dirent(const char *dir,
|
||||||
}
|
}
|
||||||
/* File type matching */
|
/* File type matching */
|
||||||
if (type) {
|
if (type) {
|
||||||
if ((filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dent.d_name)) == NULL) {
|
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dent.d_name);
|
||||||
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
|
|
||||||
goto quit;
|
|
||||||
}
|
|
||||||
res = lstat(filename, &st);
|
res = lstat(filename, &st);
|
||||||
unchunk (filename);
|
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
clicon_err(OE_UNIX, 0, "lstat: %s", strerror(errno));
|
clicon_err(OE_UNIX, 0, "lstat: %s", strerror(errno));
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
@ -164,8 +163,8 @@ clicon_file_dirent(const char *dir,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((tmp = rechunk(new, (nent+1)*sizeof(*dvecp), label)) == NULL) {
|
if ((tmp = realloc(new, (nent+1)*sizeof(*dvecp))) == NULL) {
|
||||||
clicon_err(OE_UNIX, 0, "chunk: %s", strerror(errno));
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
new = tmp;
|
new = tmp;
|
||||||
|
|
@ -183,30 +182,9 @@ quit:
|
||||||
closedir(dirp);
|
closedir(dirp);
|
||||||
if (regexp)
|
if (regexp)
|
||||||
regfree(&re);
|
regfree(&re);
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
|
|
||||||
return retval;
|
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
|
/*! Make a copy of file src
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @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_handle.h"
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
|
#include "clixon_plugin.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
|
|
||||||
#define CLICON_MAGIC 0x99aafabe
|
#define CLICON_MAGIC 0x99aafabe
|
||||||
|
|
||||||
#define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h))
|
#define handle(h) (assert(clicon_handle_check(h)==0),(struct clicon_handle *)(h))
|
||||||
|
|
||||||
/*
|
/*! Internal structure of basic handle. Also header of all other handles.
|
||||||
* clicon_handle
|
* @note If you change here, you must also change the structs below:
|
||||||
* Internal structire of basic handle. Also header of all other handles.
|
* @see struct cli_handle, struct backend_handle
|
||||||
* see struct clicon_cli_handle, struct clicon_backend_handle, etc
|
|
||||||
*/
|
*/
|
||||||
struct clicon_handle {
|
struct clicon_handle {
|
||||||
int ch_magic; /* magic (HDR) */
|
int ch_magic; /* magic (HDR) */
|
||||||
|
|
@ -69,7 +69,6 @@ struct clicon_handle {
|
||||||
clicon_hash_t *ch_data; /* internal clicon data (HDR) */
|
clicon_hash_t *ch_data; /* internal clicon data (HDR) */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*! Internal call to allocate a CLICON handle.
|
/*! Internal call to allocate a CLICON handle.
|
||||||
*
|
*
|
||||||
* There may be different variants of handles with some common options.
|
* There may be different variants of handles with some common options.
|
||||||
|
|
@ -166,3 +165,4 @@ clicon_data(clicon_handle h)
|
||||||
|
|
||||||
return ch->ch_data;
|
return ch->ch_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,8 +200,8 @@ hash_value(clicon_hash_t *hash,
|
||||||
/*! Copy value and add hash entry.
|
/*! Copy value and add hash entry.
|
||||||
*
|
*
|
||||||
* @param[in] hash Hash table
|
* @param[in] hash Hash table
|
||||||
* @param[in] key New variable name
|
* @param[in] key Variable name
|
||||||
* @param[in] val New variable value
|
* @param[in] val Variable value
|
||||||
* @param[in] vlen Length of variable value
|
* @param[in] vlen Length of variable value
|
||||||
* @retval variable New hash structure on success
|
* @retval variable New hash structure on success
|
||||||
* @retval NULL Failure
|
* @retval NULL Failure
|
||||||
|
|
@ -212,8 +212,9 @@ hash_add(clicon_hash_t *hash,
|
||||||
void *val,
|
void *val,
|
||||||
size_t vlen)
|
size_t vlen)
|
||||||
{
|
{
|
||||||
void *newval;
|
void *newval;
|
||||||
clicon_hash_t h, new = NULL;
|
clicon_hash_t h;
|
||||||
|
clicon_hash_t new = NULL;
|
||||||
|
|
||||||
/* If variable exist, don't allocate a new. just replace value */
|
/* If variable exist, don't allocate a new. just replace value */
|
||||||
h = hash_lookup (hash, key);
|
h = hash_lookup (hash, key);
|
||||||
|
|
|
||||||
|
|
@ -61,9 +61,9 @@
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
|
#include "clixon_plugin.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -198,7 +198,6 @@ clicon_option_default(clicon_hash_t *copt)
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
catch:
|
catch:
|
||||||
unchunk_group(__FUNCTION__);
|
|
||||||
return retval;
|
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");
|
clicon_err(OE_UNIX, 0, "CLICON_YANG_DIR not defined in config file");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (!hash_lookup(copt, "CLICON_ARCHIVE_DIR")){
|
if (!hash_lookup(copt, "CLICON_XMLDB_DIR")){
|
||||||
clicon_err(OE_UNIX, 0, "CLICON_ARCHIVE_DIR not defined in config file");
|
clicon_err(OE_UNIX, 0, "CLICON_XMLDB_DIR not defined in config file");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (!hash_lookup(copt, "CLICON_SOCK")){
|
if (!hash_lookup(copt, "CLICON_SOCK")){
|
||||||
|
|
@ -448,9 +447,9 @@ clicon_restconf_dir(clicon_handle h)
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
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 */
|
/* get family of backend socket: AF_UNIX, AF_INET or AF_INET6 */
|
||||||
|
|
@ -627,12 +626,12 @@ clicon_dbspec_yang(clicon_handle h)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*! Set yang database specification
|
||||||
* Set dbspec (YANG variant)
|
|
||||||
* ys must be a malloced pointer
|
* ys must be a malloced pointer
|
||||||
*/
|
*/
|
||||||
int
|
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);
|
clicon_hash_t *cdat = clicon_data(h);
|
||||||
|
|
||||||
|
|
@ -644,8 +643,7 @@ clicon_dbspec_yang_set(clicon_handle h, struct yang_spec *ys)
|
||||||
return 0;
|
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,...
|
* XXX: this we muśt change,...
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
|
|
@ -656,11 +654,103 @@ clicon_dbspec_name(clicon_handle h)
|
||||||
return clicon_option_str(h, "dbspec_name");
|
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
|
int
|
||||||
clicon_dbspec_name_set(clicon_handle h, char *name)
|
clicon_dbspec_name_set(clicon_handle h, char *name)
|
||||||
{
|
{
|
||||||
return clicon_option_str_set(h, "dbspec_name", 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_err.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
|
#include "clixon_log.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_plugin.h"
|
#include "clixon_plugin.h"
|
||||||
|
|
||||||
|
|
@ -84,3 +85,76 @@ clicon_find_func(clicon_handle h, char *plugin, char *func)
|
||||||
|
|
||||||
return dlsym(dlhandle, 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_err.h"
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_sig.h"
|
#include "clixon_sig.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xsl.h"
|
#include "clixon_xsl.h"
|
||||||
|
|
@ -298,6 +297,8 @@ clicon_msg_send(int s,
|
||||||
if (atomicio((ssize_t (*)(int, void *, size_t))write,
|
if (atomicio((ssize_t (*)(int, void *, size_t))write,
|
||||||
s, msg, ntohs(msg->op_len)) < 0){
|
s, msg, ntohs(msg->op_len)) < 0){
|
||||||
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
|
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
|
||||||
|
clicon_log(LOG_WARNING, "%s: write: %s len:%d msg:%s", __FUNCTION__,
|
||||||
|
strerror(errno), ntohs(msg->op_len), msg->op_body);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -532,11 +533,11 @@ send_msg_reply(int s,
|
||||||
uint16_t datalen)
|
uint16_t datalen)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct clicon_msg *reply;
|
struct clicon_msg *reply = NULL;
|
||||||
uint16_t len;
|
uint16_t len;
|
||||||
|
|
||||||
len = sizeof(*reply) + datalen;
|
len = sizeof(*reply) + datalen;
|
||||||
if ((reply = (struct clicon_msg *)chunk(len, __FUNCTION__)) == NULL)
|
if ((reply = (struct clicon_msg *)malloc(len)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
memset(reply, 0, len);
|
memset(reply, 0, len);
|
||||||
reply->op_len = htons(len);
|
reply->op_len = htons(len);
|
||||||
|
|
@ -546,7 +547,8 @@ send_msg_reply(int s,
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
unchunk_group(__FUNCTION__);
|
if (reply)
|
||||||
|
free(reply);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,11 +56,11 @@
|
||||||
|
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
|
#include "clixon_plugin.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xsl.h"
|
#include "clixon_xsl.h"
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,10 @@
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <cligen/cligen.h>
|
||||||
|
|
||||||
/* clicon */
|
/* clicon */
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_string.h"
|
#include "clixon_string.h"
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
|
|
||||||
|
|
@ -138,6 +139,200 @@ clicon_strjoin(int argc,
|
||||||
return str;
|
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
|
/*! strndup() for systems without it, such as xBSD
|
||||||
*/
|
*/
|
||||||
|
|
@ -163,6 +358,8 @@ clicon_strndup (const char *str,
|
||||||
}
|
}
|
||||||
#endif /* ! HAVE_STRNDUP */
|
#endif /* ! HAVE_STRNDUP */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Turn this on for uni-test programs
|
* Turn this on for uni-test programs
|
||||||
* Usage: clixon_string join
|
* Usage: clixon_string join
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_log.h"
|
#include "clixon_log.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xml_parse.h"
|
#include "clixon_xml_parse.h"
|
||||||
|
|
||||||
|
|
@ -76,7 +75,6 @@ struct xml{
|
||||||
int x_childvec_len; /* length of vector */
|
int x_childvec_len; /* length of vector */
|
||||||
enum cxobj_type x_type; /* type of node: element, attribute, body */
|
enum cxobj_type x_type; /* type of node: element, attribute, body */
|
||||||
char *x_value; /* attribute and body nodes have values */
|
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_vector_i; /* internal use: xml_child_each */
|
||||||
int x_flags; /* Flags according to XML_FLAG_* above */
|
int x_flags; /* Flags according to XML_FLAG_* above */
|
||||||
void *x_spec; /* Pointer to specification, eg yang, by
|
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 */
|
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
|
* Access functions
|
||||||
*/
|
*/
|
||||||
|
|
@ -223,7 +251,7 @@ xml_value(cxobj *xn)
|
||||||
|
|
||||||
/*! Set value of xml node, value is copied
|
/*! Set value of xml node, value is copied
|
||||||
* @param[in] xn xml node
|
* @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 -1 on error with clicon-err set
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
*/
|
*/
|
||||||
|
|
@ -294,33 +322,6 @@ xml_type_set(cxobj *xn,
|
||||||
return old;
|
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
|
/*! Get cligen variable associated with node
|
||||||
* @param[in] xn xml node
|
* @param[in] xn xml node
|
||||||
* @retval cv Cligen variable if set
|
* @retval cv Cligen variable if set
|
||||||
|
|
@ -520,12 +521,21 @@ xml_new_spec(char *name,
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void *
|
void *
|
||||||
xml_spec(cxobj *x)
|
xml_spec(cxobj *x)
|
||||||
{
|
{
|
||||||
return x->x_spec;
|
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.
|
/*! Find an XML node matching name among a parent's children.
|
||||||
*
|
*
|
||||||
* Get first XML node directly under x_up in the xml hierarchy with
|
* Get first XML node directly under x_up in the xml hierarchy with
|
||||||
|
|
@ -758,6 +768,16 @@ xml_body(cxobj *xn)
|
||||||
return NULL;
|
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
|
/*! Find and return the value of a sub xml node
|
||||||
*
|
*
|
||||||
* The value can be of an attribute or body.
|
* The value can be of an attribute or body.
|
||||||
|
|
@ -844,8 +864,8 @@ clicon_xml2file(FILE *f,
|
||||||
int level,
|
int level,
|
||||||
int prettyprint)
|
int prettyprint)
|
||||||
{
|
{
|
||||||
cbuf *cb;
|
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
cbuf *cb = NULL;
|
||||||
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
|
@ -882,8 +902,8 @@ xml_print(FILE *f,
|
||||||
/*! Print an XML tree structure to a cligen buffer
|
/*! Print an XML tree structure to a cligen buffer
|
||||||
*
|
*
|
||||||
* @param[in,out] cb Cligen buffer to write to
|
* @param[in,out] cb Cligen buffer to write to
|
||||||
* @param[in] xn clicon xml tree
|
* @param[in] xn Clicon xml tree
|
||||||
* @param[in] level how many spaces to insert before each line
|
* @param[in] level Indentation level
|
||||||
* @param[in] prettyprint insert \n and spaces tomake the xml more readable.
|
* @param[in] prettyprint insert \n and spaces tomake the xml more readable.
|
||||||
*
|
*
|
||||||
* @code
|
* @code
|
||||||
|
|
@ -897,47 +917,50 @@ xml_print(FILE *f,
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_xml2cbuf(cbuf *cb,
|
clicon_xml2cbuf(cbuf *cb,
|
||||||
cxobj *cx,
|
cxobj *x,
|
||||||
int level,
|
int level,
|
||||||
int prettyprint)
|
int prettyprint)
|
||||||
{
|
{
|
||||||
cxobj *xc;
|
cxobj *xc;
|
||||||
|
char *name;
|
||||||
|
|
||||||
switch(xml_type(cx)){
|
name = xml_name(x);
|
||||||
|
switch(xml_type(x)){
|
||||||
case CX_BODY:
|
case CX_BODY:
|
||||||
cprintf(cb, "%s", xml_value(cx));
|
cprintf(cb, "%s", xml_value(x));
|
||||||
break;
|
break;
|
||||||
case CX_ATTR:
|
case CX_ATTR:
|
||||||
cprintf(cb, " ");
|
cprintf(cb, " ");
|
||||||
if (xml_namespace(cx))
|
if (xml_namespace(x))
|
||||||
cprintf(cb, "%s:", xml_namespace(cx));
|
cprintf(cb, "%s:", xml_namespace(x));
|
||||||
cprintf(cb, "%s=\"%s\"", xml_name(cx), xml_value(cx));
|
cprintf(cb, "%s=\"%s\"", name, xml_value(x));
|
||||||
break;
|
break;
|
||||||
case CX_ELMNT:
|
case CX_ELMNT:
|
||||||
cprintf(cb, "%*s<", prettyprint?(level*XML_INDENT):0, "");
|
cprintf(cb, "%*s<", prettyprint?(level*XML_INDENT):0, "");
|
||||||
if (xml_namespace(cx))
|
if (xml_namespace(x))
|
||||||
cprintf(cb, "%s:", xml_namespace(cx));
|
cprintf(cb, "%s:", xml_namespace(x));
|
||||||
cprintf(cb, "%s", xml_name(cx));
|
cprintf(cb, "%s", name);
|
||||||
xc = NULL;
|
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);
|
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
||||||
/* Check for special case <a/> instead of <a></a> */
|
/* 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, "/>");
|
cprintf(cb, "/>");
|
||||||
else{
|
else{
|
||||||
cprintf(cb, ">");
|
cprintf(cb, ">");
|
||||||
if (prettyprint && xml_body(cx)==NULL)
|
if (prettyprint && xml_body(x)==NULL)
|
||||||
cprintf(cb, "\n");
|
cprintf(cb, "\n");
|
||||||
xc = NULL;
|
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)
|
if (xml_type(xc) == CX_ATTR)
|
||||||
continue;
|
continue;
|
||||||
else
|
else
|
||||||
clicon_xml2cbuf(cb, xc, level+1, prettyprint);
|
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", level*XML_INDENT, "");
|
||||||
cprintf(cb, "</%s>", xml_name(cx));
|
cprintf(cb, "</%s>", name);
|
||||||
}
|
}
|
||||||
if (prettyprint)
|
if (prettyprint)
|
||||||
cprintf(cb, "\n");
|
cprintf(cb, "\n");
|
||||||
|
|
@ -976,6 +999,44 @@ xml_parse(char *str,
|
||||||
return retval;
|
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
|
* 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:
|
* Note, xt will add a top-level symbol called "top" meaning that <tree../> will look as:
|
||||||
* <top><tree.../></tree>
|
* <top><tree.../></tree>
|
||||||
* XXX: There is a potential leak here on some return values.
|
* XXX: There is a potential leak here on some return values.
|
||||||
|
* XXX: What happens if endtag is different?
|
||||||
* May block
|
* May block
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -1016,59 +1078,69 @@ clicon_xml_parse_file(int fd,
|
||||||
cxobj **cx,
|
cxobj **cx,
|
||||||
char *endtag)
|
char *endtag)
|
||||||
{
|
{
|
||||||
|
int retval = -1;
|
||||||
|
int ret;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
char ch;
|
char ch;
|
||||||
int retval;
|
char *xmlbuf = NULL;
|
||||||
char *xmlbuf;
|
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int maxbuf = BUFLEN;
|
int maxbuf = BUFLEN;
|
||||||
int endtaglen = strlen(endtag);
|
int endtaglen = strlen(endtag);
|
||||||
int state = 0;
|
int state = 0;
|
||||||
|
int oldmaxbuf;
|
||||||
|
|
||||||
if (endtag == NULL){
|
if (endtag == NULL){
|
||||||
clicon_err(OE_XML, 0, "%s: endtag required\n", __FUNCTION__);
|
clicon_err(OE_XML, 0, "%s: endtag required\n", __FUNCTION__);
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
*cx = NULL;
|
*cx = NULL;
|
||||||
if ((xmlbuf = malloc(maxbuf)) == NULL){
|
if ((xmlbuf = malloc(maxbuf)) == NULL){
|
||||||
clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
|
clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
memset(xmlbuf, 0, maxbuf);
|
memset(xmlbuf, 0, maxbuf);
|
||||||
ptr = xmlbuf;
|
ptr = xmlbuf;
|
||||||
while (1){
|
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",
|
clicon_err(OE_XML, errno, "%s: read: [pid:%d]\n",
|
||||||
__FUNCTION__,
|
__FUNCTION__,
|
||||||
(int)getpid());
|
(int)getpid());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (retval != 0){
|
if (ret != 0){
|
||||||
state = FSM(endtag, ch, state);
|
state = FSM(endtag, ch, state);
|
||||||
xmlbuf[len++] = ch;
|
xmlbuf[len++] = ch;
|
||||||
}
|
}
|
||||||
if (retval == 0 || state == endtaglen){
|
if (ret == 0 || state == endtaglen){
|
||||||
state = 0;
|
state = 0;
|
||||||
if ((*cx = xml_new("top", NULL)) == NULL)
|
if ((*cx = xml_new("top", NULL)) == NULL)
|
||||||
break;
|
break;
|
||||||
if (xml_parse(ptr, *cx) < 0)
|
if (xml_parse(ptr, *cx) < 0){
|
||||||
return -1;
|
goto done;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (len>=maxbuf-1){ /* Space: one for the null character */
|
if (len>=maxbuf-1){ /* Space: one for the null character */
|
||||||
int oldmaxbuf = maxbuf;
|
oldmaxbuf = maxbuf;
|
||||||
|
|
||||||
maxbuf *= 2;
|
maxbuf *= 2;
|
||||||
if ((xmlbuf = realloc(xmlbuf, maxbuf)) == NULL){
|
if ((xmlbuf = realloc(xmlbuf, maxbuf)) == NULL){
|
||||||
clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
|
clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
|
||||||
return -1;
|
goto done;
|
||||||
}
|
}
|
||||||
memset(xmlbuf+oldmaxbuf, 0, maxbuf-oldmaxbuf);
|
memset(xmlbuf+oldmaxbuf, 0, maxbuf-oldmaxbuf);
|
||||||
ptr = xmlbuf;
|
ptr = xmlbuf;
|
||||||
}
|
}
|
||||||
} /* while */
|
} /* while */
|
||||||
free(xmlbuf);
|
retval = 0;
|
||||||
return (*cx)?0:-1;
|
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.
|
/*! 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,
|
* x1 should be a created placeholder. If x1 is non-empty,
|
||||||
* the copied tree is appended to the existing tree.
|
* the copied tree is appended to the existing tree.
|
||||||
* @code
|
* @code
|
||||||
* x1 = xml_new("new", xc);
|
* x1 = xml_new("new", xparent);
|
||||||
* xml_copy(x0, x1);
|
* xml_copy(x0, x1);
|
||||||
* @endcode
|
* @endcode
|
||||||
*/
|
*/
|
||||||
|
|
@ -1190,7 +1262,7 @@ int
|
||||||
xml_copy(cxobj *x0,
|
xml_copy(cxobj *x0,
|
||||||
cxobj *x1)
|
cxobj *x1)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cxobj *xcopy;
|
cxobj *xcopy;
|
||||||
|
|
||||||
|
|
@ -1462,8 +1534,12 @@ xml_body_uint32(cxobj *xb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Map xml operation from string to enumeration
|
/*! Map xml operation from string to enumeration
|
||||||
* @param[in] xn XML node
|
* @param[in] opstr String, eg "merge"
|
||||||
* @param[out] op "operation" attribute may change operation
|
* @param[out] op Enumeration, eg OP_MERGE
|
||||||
|
* @code
|
||||||
|
* enum operation_type op;
|
||||||
|
* xml_operation("replace", &op)
|
||||||
|
* @endcode
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
xml_operation(char *opstr,
|
xml_operation(char *opstr,
|
||||||
|
|
@ -1488,6 +1564,14 @@ xml_operation(char *opstr,
|
||||||
return 0;
|
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 *
|
char *
|
||||||
xml_operation2str(enum operation_type op)
|
xml_operation2str(enum operation_type op)
|
||||||
{
|
{
|
||||||
|
|
@ -1511,3 +1595,52 @@ xml_operation2str(enum operation_type op)
|
||||||
return "none";
|
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_string.h"
|
||||||
#include "clixon_queue.h"
|
#include "clixon_queue.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_chunk.h"
|
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
|
#include "clixon_string.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
|
#include "clixon_plugin.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_xsl.h"
|
#include "clixon_xsl.h"
|
||||||
|
|
@ -89,9 +90,6 @@
|
||||||
/* Something to do with reverse engineering of junos syntax? */
|
/* Something to do with reverse engineering of junos syntax? */
|
||||||
#undef SPECIAL_TREATMENT_OF_NAME
|
#undef SPECIAL_TREATMENT_OF_NAME
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A node is a leaf if it contains a body.
|
* A node is a leaf if it contains a body.
|
||||||
*/
|
*/
|
||||||
|
|
@ -127,8 +125,9 @@ xml2txt(FILE *f, cxobj *x, int level)
|
||||||
{
|
{
|
||||||
cxobj *xe = NULL;
|
cxobj *xe = NULL;
|
||||||
int children=0;
|
int children=0;
|
||||||
char *term;
|
char *term = NULL;
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
int encr=0;
|
||||||
#ifdef SPECIAL_TREATMENT_OF_NAME
|
#ifdef SPECIAL_TREATMENT_OF_NAME
|
||||||
cxobj *xname;
|
cxobj *xname;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -140,15 +139,17 @@ xml2txt(FILE *f, cxobj *x, int level)
|
||||||
if (xml_type(x) == CX_BODY){
|
if (xml_type(x) == CX_BODY){
|
||||||
/* Kludge for escaping encrypted passwords */
|
/* Kludge for escaping encrypted passwords */
|
||||||
if (strcmp(xml_name(xml_parent(x)), "encrypted-password")==0)
|
if (strcmp(xml_name(xml_parent(x)), "encrypted-password")==0)
|
||||||
term = chunk_sprintf(__FUNCTION__, "\"%s\"", xml_value(x));
|
encr++;
|
||||||
else
|
term = xml_value(x);
|
||||||
term = xml_value(x);
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
fprintf(f, "%*s", 4*level, "");
|
fprintf(f, "%*s", 4*level, "");
|
||||||
term = xml_name(x);
|
term = xml_name(x);
|
||||||
}
|
}
|
||||||
fprintf(f, "%s;\n", term);
|
if (encr)
|
||||||
|
fprintf(f, "\"%s\";\n", term);
|
||||||
|
else
|
||||||
|
fprintf(f, "%s;\n", term);
|
||||||
retval = 0;
|
retval = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +241,7 @@ xml2cli(FILE *f,
|
||||||
!index GT_VARS T
|
!index GT_VARS T
|
||||||
!index GT_ALL 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);
|
// bool = (!x->xn_index || gt == GT_ALL);
|
||||||
if (bool){
|
if (bool){
|
||||||
if (cbuf_len(cbpre))
|
if (cbuf_len(cbpre))
|
||||||
|
|
@ -252,12 +253,12 @@ xml2cli(FILE *f,
|
||||||
i = 0;
|
i = 0;
|
||||||
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
while ((xe = xml_child_each(x, xe, -1)) != NULL){
|
||||||
/* Dont call this if it is index and there are other following */
|
/* 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
|
else
|
||||||
if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0)
|
if (xml2cli(f, xe, cbuf_get(cbpre), gt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_index(xe)){ /* assume index is first, otherwise need one more while */
|
if (0){ /* assume index is first, otherwise need one more while */
|
||||||
if (gt == GT_ALL)
|
if (gt == GT_ALL)
|
||||||
cprintf(cbpre, " %s", xml_name(xe));
|
cprintf(cbpre, " %s", xml_name(xe));
|
||||||
cprintf(cbpre, " %s", xml_value(xml_child_i(xe, 0)));
|
cprintf(cbpre, " %s", xml_value(xml_child_i(xe, 0)));
|
||||||
|
|
@ -791,3 +792,608 @@ xml_diff(yang_spec *yspec,
|
||||||
done:
|
done:
|
||||||
return retval;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Called at </name> */
|
||||||
static int
|
static int
|
||||||
xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
|
||||||
|
char *name)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x = ya->ya_xelement;
|
cxobj *x = ya->ya_xelement;
|
||||||
|
|
@ -199,8 +201,10 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
||||||
;
|
;
|
||||||
else{
|
else{
|
||||||
xc = NULL;
|
xc = NULL;
|
||||||
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL)
|
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) {
|
||||||
xml_value_set(xc, ""); /* XXX remove */
|
xml_purge(xc);
|
||||||
|
xc = NULL; /* reset iterator */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -209,8 +213,11 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya, char *name)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Called at </namespace:name> */
|
||||||
static int
|
static int
|
||||||
xml_parse_bslash2(struct xml_parse_yacc_arg *ya, char *namespace, char *name)
|
xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
|
||||||
|
char *namespace,
|
||||||
|
char *name)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *x = ya->ya_xelement;
|
cxobj *x = ya->ya_xelement;
|
||||||
|
|
|
||||||
|
|
@ -358,6 +358,13 @@ xpath_parse(char *xpath,
|
||||||
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
|
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
|
||||||
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
|
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
|
||||||
}
|
}
|
||||||
|
#if 1
|
||||||
|
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] */
|
#if 1 /* Problems with .[userid=1321] */
|
||||||
else if (strncmp(s,".", strlen("."))==0)
|
else if (strncmp(s,".", strlen("."))==0)
|
||||||
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
|
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
|
||||||
|
|
@ -368,13 +375,7 @@ xpath_parse(char *xpath,
|
||||||
|
|
||||||
else if (strncmp(s,"self::", strlen("self::"))==0)
|
else if (strncmp(s,"self::", strlen("self::"))==0)
|
||||||
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
|
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
|
||||||
#if 1
|
|
||||||
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
|
|
||||||
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
|
|
||||||
#else
|
|
||||||
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
|
|
||||||
xpath_element_new(A_PARENT, NULL, &xpnext);
|
|
||||||
#endif
|
|
||||||
else if (strncmp(s,"parent::", strlen("parent::"))==0)
|
else if (strncmp(s,"parent::", strlen("parent::"))==0)
|
||||||
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
|
xpath_element_new(A_PARENT, s+strlen("parent::"), &xpnext);
|
||||||
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
|
else if (strncmp(s,"ancestor::", strlen("ancestor::"))==0)
|
||||||
|
|
@ -1076,7 +1077,7 @@ int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
cxobj **xv;
|
cxobj **xv
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cxobj *xn;
|
cxobj *xn;
|
||||||
size_t xlen = 0;
|
size_t xlen = 0;
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
#include "clixon_file.h"
|
#include "clixon_file.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_chunk.h"
|
#include "clixon_plugin.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
#include "clixon_yang_type.h"
|
#include "clixon_yang_type.h"
|
||||||
#include "clixon_yang_parse.h"
|
#include "clixon_yang_parse.h"
|
||||||
|
|
@ -160,6 +160,10 @@ static const struct map_str2int ykmap[] = {
|
||||||
{NULL, -1}
|
{NULL, -1}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*! Create new yang specification
|
||||||
|
* @retval yspec Free with yspec_free()
|
||||||
|
* @retval NULL Error
|
||||||
|
*/
|
||||||
yang_spec *
|
yang_spec *
|
||||||
yspec_new(void)
|
yspec_new(void)
|
||||||
{
|
{
|
||||||
|
|
@ -174,6 +178,10 @@ yspec_new(void)
|
||||||
return yspec;
|
return yspec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create new yang node/statement
|
||||||
|
* @retval ys Free with ys_free()
|
||||||
|
* @retval NULL Error
|
||||||
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
ys_new(enum rfc_6020 keyw)
|
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 1 Match founbd, Most recent entry returned in fbuf
|
||||||
* @retval 0 No matching entry found
|
* @retval 0 No matching entry found
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/static int
|
*/
|
||||||
|
static int
|
||||||
yang_parse_find_match(clicon_handle h,
|
yang_parse_find_match(clicon_handle h,
|
||||||
const char *yang_dir,
|
const char *yang_dir,
|
||||||
const char *module,
|
const char *module,
|
||||||
cbuf *fbuf)
|
cbuf *fbuf)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
struct dirent *dp;
|
struct dirent *dp = NULL;
|
||||||
int ndp;
|
int ndp;
|
||||||
cbuf *regex = NULL;
|
cbuf *regex = NULL;
|
||||||
char *regexstr;
|
char *regexstr;
|
||||||
|
|
||||||
if ((regex = cbuf_new()) == NULL){
|
if ((regex = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
|
clicon_err(OE_YANG, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(regex, "^%s.*(.yang)$", module);
|
cprintf(regex, "^%s.*(.yang)$", module);
|
||||||
|
|
@ -1403,8 +1412,7 @@ yang_parse_find_match(clicon_handle h,
|
||||||
if ((ndp = clicon_file_dirent(yang_dir,
|
if ((ndp = clicon_file_dirent(yang_dir,
|
||||||
&dp,
|
&dp,
|
||||||
regexstr,
|
regexstr,
|
||||||
S_IFREG,
|
S_IFREG)) < 0)
|
||||||
__FUNCTION__)) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
/* Entries are sorted, last entry should be most recent date */
|
/* Entries are sorted, last entry should be most recent date */
|
||||||
if (ndp != 0){
|
if (ndp != 0){
|
||||||
|
|
@ -1416,7 +1424,8 @@ yang_parse_find_match(clicon_handle h,
|
||||||
done:
|
done:
|
||||||
if (regex)
|
if (regex)
|
||||||
cbuf_free(regex);
|
cbuf_free(regex);
|
||||||
unchunk_group(__FUNCTION__);
|
if (dp)
|
||||||
|
free(dp);
|
||||||
return retval;
|
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
|
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||||
*
|
*
|
||||||
* @param h CLICON handle
|
* @param[in] h CLICON handle
|
||||||
* @param yang_dir Directory where all YANG module files reside
|
* @param[in] yang_dir Directory where all YANG module files reside
|
||||||
* @param module Name of main YANG module. More modules may be parsed if imported
|
* @param[in] module Name of main YANG module. More modules may be parsed if imported
|
||||||
* @param revision Optional module revision date
|
* @param[in] revision Optional module revision date
|
||||||
* @param ysp Yang specification. Should ave been created by caller using yspec_new
|
* @param[out] ysp Yang specification. Should ave been created by caller using yspec_new
|
||||||
* @retval 0 Everything OK
|
* @retval 0 Everything OK
|
||||||
* @retval -1 Error encountered
|
* @retval -1 Error encountered
|
||||||
* The database symbols are inserted in alphabetical order.
|
* The database symbols are inserted in alphabetical order.
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_hash.h"
|
#include "clixon_hash.h"
|
||||||
#include "clixon_chunk.h"
|
#include "clixon_plugin.h"
|
||||||
#include "clixon_options.h"
|
#include "clixon_options.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
#include "clixon_yang_type.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
|
# 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(){
|
expectfn(){
|
||||||
|
|
||||||
cmd=$1
|
cmd=$1
|
||||||
expect=$2
|
expect=$2
|
||||||
ret=`$cmd`
|
ret=`$cmd`
|
||||||
|
|
@ -40,7 +39,8 @@ expectfn(){
|
||||||
fi
|
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(){
|
expecteof(){
|
||||||
cmd=$1
|
cmd=$1
|
||||||
input=$2
|
input=$2
|
||||||
|
|
@ -61,7 +61,8 @@ EOF
|
||||||
fi
|
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(){
|
expectwait(){
|
||||||
cmd=$1
|
cmd=$1
|
||||||
input=$2
|
input=$2
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ sudo clixon_backend -If $clixon_cf
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
|
new "cli tests"
|
||||||
|
|
||||||
new "cli configure top"
|
new "cli configure top"
|
||||||
expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""
|
expectfn "$clixon_cli -1f $clixon_cf set interfaces" ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,44 @@ sudo clixon_backend -If $clixon_cf
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
err
|
err
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
new "netconf tests"
|
||||||
|
|
||||||
new "netconf get empty config"
|
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"
|
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>]]>]]>$"
|
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"
|
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"
|
new "netconf validate missing type"
|
||||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply><rpc-error>"
|
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>]]>]]>$"
|
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new "netconf edit config replace"
|
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"
|
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>]]>]]>$"
|
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"
|
new "netconf discard-changes"
|
||||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
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
|
sleep 1
|
||||||
|
|
||||||
|
new "restconf tests"
|
||||||
|
|
||||||
new "restconf options"
|
new "restconf options"
|
||||||
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
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"
|
new "restconf head"
|
||||||
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
|
expectfn "curl -s -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
|
||||||
|
|
||||||
new "restconf POST config"
|
new "restconf get empty config"
|
||||||
expectfn 'curl -sX POST -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
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"
|
new "Check eth0 added"
|
||||||
expectfn "curl -sG http://localhost/restconf/data" '{"interfaces": {"interface": \[{"name": "eth1","type": "eth","enabled": "true"},{ "name": "eth4","type": "eth","enabled": "true"}\]}}
|
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"
|
new "Re-post eth0 which should generate error"
|
||||||
expectfn 'curl -sX PATCH -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth4' ""
|
expectfn 'curl -sX POST -d {"interfaces":{"interface":{"name":"eth0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' "Not Found"
|
||||||
|
|
||||||
new "restconf PUT"
|
new "delete eth0"
|
||||||
expectfn 'curl -sX PUT -d {"type":"eth"} http://localhost/restconf/data/interfaces/interface=eth5' ""
|
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"
|
new "Kill restconf daemon"
|
||||||
#sudo pkill -u www-data clixon_restconf
|
#sudo pkill -u www-data clixon_restconf
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Test2: backend and netconf basic functionality
|
# Test4: Yang specifics: multi-keys and empty type
|
||||||
|
|
||||||
# include err() and new() functions
|
# include err() and new() functions
|
||||||
. ./lib.sh
|
. ./lib.sh
|
||||||
|
|
@ -26,6 +26,19 @@ module ietf-ip{
|
||||||
leaf d {
|
leaf d {
|
||||||
type empty;
|
type empty;
|
||||||
}
|
}
|
||||||
|
container f {
|
||||||
|
leaf-list e {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leaf g {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
container h {
|
||||||
|
leaf j {
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -52,6 +65,15 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><commit/></rpc>]]>
|
||||||
new "netconf get config xpath"
|
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>]]>]]>$"
|
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"
|
new "Kill backend"
|
||||||
# Check if still alive
|
# Check if still alive
|
||||||
pid=`pgrep clixon_backend`
|
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