Inital commit
This commit is contained in:
parent
edc5e091bb
commit
d6e393ea58
145 changed files with 58117 additions and 0 deletions
122
apps/netconf/Makefile.in
Normal file
122
apps/netconf/Makefile.in
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
#
|
||||
# Makefile
|
||||
#
|
||||
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
#
|
||||
# This file is part of CLICON.
|
||||
#
|
||||
# CLICON is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# CLICON is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with CLICON; see the file COPYING. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
#
|
||||
VPATH = @srcdir@
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
CC = @CC@
|
||||
CFLAGS = @CFLAGS@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
prefix = @prefix@
|
||||
datarootdir = @datarootdir@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
libdir = @libdir@
|
||||
mandir = @mandir@
|
||||
libexecdir = @libexecdir@
|
||||
localstatedir = @localstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
includedir = @includedir@
|
||||
|
||||
SH_SUFFIX = @SH_SUFFIX@
|
||||
CLICON_MAJOR = @CLICON_VERSION_MAJOR@
|
||||
CLICON_MINOR = @CLICON_VERSION_MINOR@
|
||||
|
||||
# Use this clicon lib for linking
|
||||
CLICON_LIB = libclicon.so.$(CLICON_MAJOR).$(CLICON_MINOR)
|
||||
|
||||
# For dependency
|
||||
LIBDEPS = $(top_srcdir)/lib/src/$(CLICON_LIB)
|
||||
|
||||
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLICON_LIB)
|
||||
CPPFLAGS = @CPPFLAGS@ -fPIC
|
||||
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@
|
||||
|
||||
APPL = clicon_netconf
|
||||
SRC = netconf_main.c
|
||||
OBJS = $(SRC:.c=.o)
|
||||
|
||||
MYNAME = clicon_netconf
|
||||
MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX)
|
||||
MYLIB = $(MYLIBLINK).$(CLICON_MAJOR).$(CLICON_MINOR)
|
||||
MYLIBSO = $(MYLIBLINK).$(CLICON_MAJOR)
|
||||
|
||||
LIBSRC = netconf_hello.c netconf_rpc.c netconf_filter.c netconf_lib.c netconf_plugin.c
|
||||
LIBOBJS = $(LIBSRC:.c=.o)
|
||||
|
||||
all: $(MYLIB) $(APPL)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(LIBOBJS) *.core $(APPL) $(MYLIB) $(MYLIBSO) $(MYLIBLINK)
|
||||
|
||||
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: install-lib $(APPL)
|
||||
install -d $(DESTDIR)$(bindir)
|
||||
install $(APPL) $(DESTDIR)$(bindir)
|
||||
|
||||
install-lib: $(MYLIB)
|
||||
install -d $(DESTDIR)$(libdir)
|
||||
install $(MYLIB) $(DESTDIR)$(libdir)
|
||||
ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclicon_netconf.so.2
|
||||
ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclicon_netconf.so
|
||||
|
||||
install-include: clicon_netconf.h
|
||||
install -d $(DESTDIR)$(includedir)/clicon
|
||||
install -m 644 $^ $(DESTDIR)$(includedir)/clicon
|
||||
|
||||
uninstall:
|
||||
rm -f $(bindir)/$(APPL)
|
||||
rm -f $(libdir)/$(MYLIB)
|
||||
rm -f $(includedir)/clicon/*
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
.c.o:
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) -D__PROGRAM__=\"$(APPL)\" $(CFLAGS) -c $<
|
||||
|
||||
$(APPL) : $(OBJS) $(MYLIBLINK) $(LIBDEPS)
|
||||
$(CC) $(LDFLAGS) $(OBJS) -L. -l:$(MYLIB) $(LIBS) -o $@
|
||||
|
||||
$(MYLIB) : $(LIBOBJS)
|
||||
$(CC) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJS) $(LIBS) -Wl,-soname=$(MYLIBSO)
|
||||
|
||||
# link-name is needed for application linking, eg for clicon_cli and clicon_config
|
||||
$(MYLIBLINK) : $(MYLIB)
|
||||
# ln -sf $(MYLIB) $(MYLIBSO)
|
||||
# ln -sf $(MYLIB) $@
|
||||
|
||||
TAGS:
|
||||
find . -name '*.[chyl]' -print | etags -
|
||||
|
||||
depend:
|
||||
$(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(SRC) $(APPSRC) > .depend
|
||||
|
||||
#include .depend
|
||||
|
||||
73
apps/netconf/clicon_netconf.h
Normal file
73
apps/netconf/clicon_netconf.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* The exported interface to plugins. External apps (eg frontend netconf plugins)
|
||||
* should only include this file (not the netconf_*.h)
|
||||
*/
|
||||
|
||||
#ifndef _CLICON_NETCONF_H_
|
||||
#define _CLICON_NETCONF_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
typedef int (*netconf_cb_t)(
|
||||
clicon_handle h,
|
||||
cxobj *xorig, /* Original request. */
|
||||
cxobj *xn, /* Sub-tree (under xorig) at child: <rpc><xn></rpc> */
|
||||
cbuf *cb, /* Output xml stream. For reply */
|
||||
cbuf *cb_err, /* Error xml stream. For error reply */
|
||||
void *arg /* Argument given at netconf_register_callback() */
|
||||
);
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
* (Duplicated. Also in netconf_*.h)
|
||||
*/
|
||||
int netconf_output(int s, cbuf *xf, char *msg);
|
||||
|
||||
int netconf_create_rpc_reply(cbuf *cb, /* msg buffer */
|
||||
cxobj *xr, /* orig request */
|
||||
char *body,
|
||||
int ok);
|
||||
|
||||
int netconf_register_callback(clicon_handle h,
|
||||
netconf_cb_t cb, /* Callback called */
|
||||
void *arg, /* Arg to send to callback */
|
||||
char *tag); /* Xml tag when callback is made */
|
||||
int netconf_create_rpc_error(cbuf *xf, /* msg buffer */
|
||||
cxobj *xr, /* orig request */
|
||||
char *tag,
|
||||
char *type,
|
||||
char *severity,
|
||||
char *message,
|
||||
char *info);
|
||||
|
||||
void netconf_ok_set(int ok);
|
||||
int netconf_ok_get(void);
|
||||
|
||||
int netconf_xpath(cxobj *xsearch,
|
||||
cxobj *xfilter,
|
||||
cbuf *xf, cbuf *xf_err,
|
||||
cxobj *xt);
|
||||
|
||||
|
||||
#endif /* _CLICON_NETCONF_H_ */
|
||||
633
apps/netconf/netconf_filter.c
Normal file
633
apps/netconf/netconf_filter.c
Normal file
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* netconf match & selection: get and edit operations
|
||||
*****************************************************************************/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clicon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clicon/clicon.h>
|
||||
|
||||
#include "netconf_rpc.h"
|
||||
#include "netconf_lib.h"
|
||||
#include "netconf_filter.h"
|
||||
|
||||
/*
|
||||
* xml_filter
|
||||
* xf specifices a filter, and xn is an xml tree.
|
||||
* Select the part of xn that matches xf and return it.
|
||||
* Change xn destructively by removing the parts of the sub-tree that does
|
||||
* not match.
|
||||
* Match according to Section 6 of RFC 4741.
|
||||
NO_FILTER, select all
|
||||
EMPTY_FILTER, select nothing
|
||||
ATTRIBUTE_MATCH, select if attribute match
|
||||
SELECTION, select this node
|
||||
CONTENT_MATCH, select all siblings with matching content
|
||||
CONTAINMENT select
|
||||
*/
|
||||
|
||||
/* return a string containing leafs value, NULL if no leaf or no value */
|
||||
static char*
|
||||
leafstring(cxobj *x)
|
||||
{
|
||||
cxobj *c;
|
||||
|
||||
if (xml_type(x) != CX_ELMNT)
|
||||
return NULL;
|
||||
if (xml_child_nr(x) != 1)
|
||||
return NULL;
|
||||
c = xml_child_i(x, 0);
|
||||
if (xml_child_nr(c) != 0)
|
||||
return NULL;
|
||||
if (xml_type(c) != CX_BODY)
|
||||
return NULL;
|
||||
return xml_value(c);
|
||||
}
|
||||
|
||||
/*! Internal recursive part where configuration xml tree is pruned frim filter
|
||||
* assume parent has been selected and filter match (same name) as parent
|
||||
* parent is pruned according to selection.
|
||||
* @param[in] xfilter Filter xml
|
||||
* @param[out] xconf Configuration xml
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
xml_filter2(cxobj *xfilter,
|
||||
cxobj *xparent,
|
||||
int *remove_me)
|
||||
{
|
||||
cxobj *s;
|
||||
cxobj *sprev;
|
||||
cxobj *f;
|
||||
cxobj *attr;
|
||||
char *an;
|
||||
char *af;
|
||||
char *fstr;
|
||||
char *sstr;
|
||||
int containments;
|
||||
int remove_s;
|
||||
|
||||
*remove_me = 0;
|
||||
assert(xfilter && xparent && strcmp(xml_name(xfilter), xml_name(xparent))==0);
|
||||
/* 1. Check selection */
|
||||
if (xml_child_nr(xfilter) == 0)
|
||||
goto match;
|
||||
|
||||
/* Count containment/selection nodes in filter */
|
||||
f = NULL;
|
||||
containments = 0;
|
||||
while ((f = xml_child_each(xfilter, f, CX_ELMNT)) != NULL) {
|
||||
if (leafstring(f))
|
||||
continue;
|
||||
containments++;
|
||||
}
|
||||
|
||||
/* 2. Check attribute match */
|
||||
attr = NULL;
|
||||
while ((attr = xml_child_each(xfilter, attr, CX_ATTR)) != NULL) {
|
||||
af = xml_value(attr);
|
||||
an = xml_find_value(xfilter, xml_name(attr));
|
||||
if (af && an && strcmp(af, an)==0)
|
||||
; // match
|
||||
else
|
||||
goto nomatch;
|
||||
}
|
||||
/* 3. Check content match */
|
||||
f = NULL;
|
||||
while ((f = xml_child_each(xfilter, f, CX_ELMNT)) != NULL) {
|
||||
if ((fstr = leafstring(f)) == NULL)
|
||||
continue;
|
||||
if ((s = xml_find(xparent, xml_name(f))) == NULL)
|
||||
goto nomatch;
|
||||
if ((sstr = leafstring(s)) == NULL)
|
||||
continue;
|
||||
if (strcmp(fstr, sstr))
|
||||
goto nomatch;
|
||||
}
|
||||
/* If filter has no further specifiers, accept */
|
||||
if (!containments)
|
||||
goto match;
|
||||
/* Check recursively the rest of the siblings */
|
||||
sprev = s = NULL;
|
||||
while ((s = xml_child_each(xparent, s, CX_ELMNT)) != NULL) {
|
||||
if ((f = xml_find(xfilter, xml_name(s))) == NULL){
|
||||
xml_prune(xparent, s, 1);
|
||||
s = sprev;
|
||||
continue;
|
||||
}
|
||||
if (leafstring(f)){
|
||||
sprev = s;
|
||||
continue; // unsure?sk=lf
|
||||
}
|
||||
// XXX: s can be removed itself in the recursive call !
|
||||
remove_s = 0;
|
||||
if (xml_filter2(f, s, &remove_s) < 0)
|
||||
return -1;
|
||||
if (remove_s){
|
||||
xml_prune(xparent, s, 1);
|
||||
s = sprev;
|
||||
}
|
||||
sprev = s;
|
||||
}
|
||||
|
||||
match:
|
||||
return 0;
|
||||
nomatch: /* prune this parent node (maybe only children?) */
|
||||
*remove_me = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Remove parts of configuration xml tree that does not match filter xml tree
|
||||
* @param[in] xfilter Filter xml
|
||||
* @param[out] xconf Configuration xml
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* This is the top-level function, calls a recursive variant.
|
||||
*/
|
||||
int
|
||||
xml_filter(cxobj *xfilter,
|
||||
cxobj *xconfig)
|
||||
{
|
||||
int retval;
|
||||
int remove_s;
|
||||
|
||||
/* Call recursive variant */
|
||||
retval = xml_filter2(xfilter,
|
||||
xconfig,
|
||||
&remove_s);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* netconf_xpath
|
||||
* @param[in] xsearch where you search for xpath, grouped by a single top node which
|
||||
* is not significant and will not be returned in any result.
|
||||
* @param[in] xfilter the xml sub-tree, eg:
|
||||
* <filter type="xpath" select="/t:top/t:users/t:user[t:name='fred']"/>
|
||||
* @param[out] cb Output xml stream. For reply
|
||||
* @param[out] cb_err Error xml stream. For error reply
|
||||
* @param[in] xorig Original tree
|
||||
*
|
||||
*/
|
||||
int
|
||||
netconf_xpath(cxobj *xsearch,
|
||||
cxobj *xfilter,
|
||||
cbuf *cb,
|
||||
cbuf *cb_err,
|
||||
cxobj *xorig)
|
||||
{
|
||||
cxobj *x;
|
||||
int retval = -1;
|
||||
char *selector;
|
||||
cxobj **xv;
|
||||
int xlen;
|
||||
int i;
|
||||
|
||||
if ((selector = xml_find_value(xfilter, "select")) == NULL){
|
||||
netconf_create_rpc_error(cb_err, xorig,
|
||||
"operation-failed",
|
||||
"application",
|
||||
"error",
|
||||
NULL,
|
||||
"select");
|
||||
goto done;
|
||||
}
|
||||
|
||||
x = NULL;
|
||||
|
||||
clicon_errno = 0;
|
||||
if ((xv = xpath_vec(xsearch, selector, &xlen)) != NULL) {
|
||||
for (i=0; i<xlen; i++){
|
||||
x = xv[i];
|
||||
clicon_xml2cbuf(cb, x, 0, 1);
|
||||
}
|
||||
free(xv);
|
||||
}
|
||||
/* XXX: NULL means error sometimes */
|
||||
if (clicon_errno){
|
||||
netconf_create_rpc_error(cb_err, xorig,
|
||||
"operation-failed",
|
||||
"application",
|
||||
"error",
|
||||
clicon_err_reason,
|
||||
"select");
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
#ifdef NOTUSED
|
||||
/*
|
||||
* in edit_config, we copy a tree to the config. But some wthings shouldbe
|
||||
* cleaned:
|
||||
* - operation attribute
|
||||
*/
|
||||
static int
|
||||
netconf_clean(cxobj *xn)
|
||||
{
|
||||
cxobj *xa;
|
||||
cxobj *x;
|
||||
|
||||
if ((xa = xml_find(xn, "operation")) != NULL &&
|
||||
xml_type(xa) == CX_ATTR)
|
||||
xml_prune(xn, xa, 1);
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL)
|
||||
netconf_clean(x);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_operation
|
||||
* get the value of the "operation" attribute and change op if given
|
||||
*/
|
||||
static int
|
||||
get_operation(cxobj *xn,
|
||||
enum operation_type *op,
|
||||
cbuf *xf_err,
|
||||
cxobj *xorig)
|
||||
{
|
||||
char *opstr;
|
||||
|
||||
if ((opstr = xml_find_value(xn, "operation")) != NULL){
|
||||
if (strcmp("merge", opstr) == 0)
|
||||
*op = OP_MERGE;
|
||||
else
|
||||
if (strcmp("replace", opstr) == 0)
|
||||
*op = OP_REPLACE;
|
||||
else
|
||||
if (strcmp("create", opstr) == 0)
|
||||
*op = OP_CREATE;
|
||||
else
|
||||
if (strcmp("delete", opstr) == 0)
|
||||
*op = OP_DELETE;
|
||||
else
|
||||
if (strcmp("remove", opstr) == 0)
|
||||
*op = OP_REMOVE;
|
||||
else{
|
||||
netconf_create_rpc_error(xf_err, xorig,
|
||||
"bad-attribute",
|
||||
"protocol",
|
||||
"error",
|
||||
NULL,
|
||||
"<bad-attribute>operation</bad-attribute>");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* forward */
|
||||
static int
|
||||
xml_edit(cxobj *filter,
|
||||
cxobj *parent,
|
||||
enum operation_type op,
|
||||
cbuf *xf_err,
|
||||
cxobj *xorig);
|
||||
|
||||
|
||||
/*! Merge two XML trees according to RFC4741/Junos
|
||||
* 1. in configuration(parent) but not in new(filter) -> remain in configuration
|
||||
* 2. not in configuration but in new -> add to configuration
|
||||
* 3. Both in configuration and new: Do 1.2.3 with children.
|
||||
* A key is: the configuration data identified by the element
|
||||
*/
|
||||
static int
|
||||
edit_selection(cxobj *filter,
|
||||
cxobj *parent,
|
||||
enum operation_type op,
|
||||
cbuf *xf_err,
|
||||
cxobj *xorig)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
assert(filter && parent && strcmp(xml_name(filter), xml_name(parent))==0);
|
||||
fprintf(stderr, "%s: %s\n", __FUNCTION__, xml_name(filter));
|
||||
switch (op){
|
||||
case OP_MERGE:
|
||||
break;
|
||||
case OP_REPLACE:
|
||||
xml_prune(xml_parent(parent), parent, 1);
|
||||
break;
|
||||
case OP_CREATE:
|
||||
netconf_create_rpc_error(xf_err, xorig,
|
||||
NULL,
|
||||
"protocol",
|
||||
"error",
|
||||
"statement creation failed",
|
||||
"<bad-element></bad-element>");
|
||||
goto done;
|
||||
break;
|
||||
case OP_DELETE:
|
||||
fprintf(stderr, "%s: %s DELETE\n", __FUNCTION__, xml_name(filter));
|
||||
if (xml_child_nr(parent) == 0){
|
||||
fprintf(stderr, "%s: %s ERROR\n", __FUNCTION__, xml_name(filter));
|
||||
netconf_create_rpc_error(xf_err, xorig,
|
||||
NULL,
|
||||
"protocol",
|
||||
"error",
|
||||
"statement not found",
|
||||
"<bad-element></bad-element>");
|
||||
goto done;
|
||||
}
|
||||
/* fall through */
|
||||
case OP_REMOVE:
|
||||
fprintf(stderr, "%s: %s REMOVE\n", __FUNCTION__, xml_name(filter));
|
||||
xml_prune(xml_parent(parent), parent, 1);
|
||||
break;
|
||||
case OP_NONE:
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
netconf_ok_set(1); /* maybe cc_ok shouldnt be set if we continue? */
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: not called from external?
|
||||
*/
|
||||
static int
|
||||
edit_match(cxobj *filter,
|
||||
cxobj *parent,
|
||||
enum operation_type op,
|
||||
cbuf *xf_err,
|
||||
cxobj *xorig,
|
||||
int match
|
||||
)
|
||||
{
|
||||
cxobj *f;
|
||||
cxobj *s;
|
||||
cxobj *copy;
|
||||
int retval = -1;
|
||||
|
||||
clicon_debug(1, "%s: %s op:%d", __FUNCTION__, xml_name(filter), op);
|
||||
if (match)
|
||||
switch (op){
|
||||
case OP_REPLACE:
|
||||
case OP_CREATE:
|
||||
s = NULL;
|
||||
while ((s = xml_child_each(parent, s, -1)) != NULL){
|
||||
xml_prune(parent, s, 1);
|
||||
s = NULL;
|
||||
}
|
||||
if (xml_copy(filter, parent) < 0)
|
||||
goto done;
|
||||
netconf_clean(parent);
|
||||
retval = 0;
|
||||
netconf_ok_set(1);
|
||||
goto done;
|
||||
break;
|
||||
case OP_DELETE:
|
||||
case OP_REMOVE:
|
||||
xml_prune(xml_parent(parent), parent, 1);
|
||||
netconf_ok_set(1);
|
||||
goto done;
|
||||
break;
|
||||
case OP_MERGE:
|
||||
case OP_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
f = NULL;
|
||||
while ((f = xml_child_each(filter, f, CX_ELMNT)) != NULL) {
|
||||
s = xml_find(parent, xml_name(f));
|
||||
switch (op){
|
||||
case OP_MERGE:
|
||||
/* things in filter:
|
||||
not in conf should be added
|
||||
in conf go down recursive
|
||||
*/
|
||||
if (s == NULL && match){
|
||||
if ((copy = xml_new(xml_name(f), parent)) == NULL)
|
||||
goto done;
|
||||
if (xml_copy(f, copy) < 0)
|
||||
goto done;
|
||||
netconf_clean(copy);
|
||||
}
|
||||
else{
|
||||
s = NULL;
|
||||
while ((s = xml_child_each(parent, s, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(f), xml_name(s)))
|
||||
continue;
|
||||
if ((retval = xml_edit(f, s, op, xf_err, xorig)) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OP_REPLACE:
|
||||
#if 1
|
||||
/* things in filter
|
||||
in conf: remove from conf and
|
||||
add to conf
|
||||
*/
|
||||
// if (!match)
|
||||
// break;
|
||||
if (s != NULL)
|
||||
xml_prune(parent, s, 1);
|
||||
if ((copy = xml_new(xml_name(f), parent)) == NULL)
|
||||
goto done;
|
||||
if (xml_copy(f, copy) < 0)
|
||||
goto done;
|
||||
netconf_clean(copy);
|
||||
#endif
|
||||
break;
|
||||
case OP_CREATE:
|
||||
#if 0
|
||||
/* things in filter
|
||||
in conf: error
|
||||
else add to conf
|
||||
*/
|
||||
if (!match)
|
||||
break;
|
||||
if (s != NULL){
|
||||
netconf_create_rpc_error(xf_err, xorig,
|
||||
NULL,
|
||||
"protocol",
|
||||
"error",
|
||||
"statement creation failed",
|
||||
"<bad-element></bad-element>");
|
||||
goto done;
|
||||
}
|
||||
if ((copy = xml_new(xml_name(f), parent)) == NULL)
|
||||
goto done;
|
||||
if (xml_copy(f, copy) < 0)
|
||||
goto done;
|
||||
netconf_clean(copy);
|
||||
#endif
|
||||
break;
|
||||
case OP_DELETE:
|
||||
/* things in filter
|
||||
if not in conf: error
|
||||
else remove from conf
|
||||
*/
|
||||
#if 0
|
||||
if (!match)
|
||||
break;
|
||||
if (s == NULL){
|
||||
netconf_create_rpc_error(xf_err, xorig,
|
||||
NULL,
|
||||
"protocol",
|
||||
"error",
|
||||
"statement not found",
|
||||
"<bad-element></bad-element>");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
/* fall through */
|
||||
case OP_REMOVE:
|
||||
/* things in filter
|
||||
remove from conf
|
||||
*/
|
||||
#if 0
|
||||
if (!match)
|
||||
break;
|
||||
xml_prune(parent, s, 1);
|
||||
#endif
|
||||
break;
|
||||
case OP_NONE:
|
||||
/* recursive descent */
|
||||
s = NULL;
|
||||
while ((s = xml_child_each(parent, s, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(xml_name(f), xml_name(s)))
|
||||
continue;
|
||||
if ((retval = xml_edit(f, s, op, xf_err, xorig)) < 0)
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} /* while f */
|
||||
retval = 0;
|
||||
netconf_ok_set(1); /* maybe cc_ok shouldnt be set if we continue? */
|
||||
done:
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* xml_edit
|
||||
* merge filter into parent
|
||||
* XXX: not called from external?
|
||||
*/
|
||||
static int
|
||||
xml_edit(cxobj *filter,
|
||||
cxobj *parent,
|
||||
enum operation_type op,
|
||||
cbuf *xf_err,
|
||||
cxobj *xorig)
|
||||
{
|
||||
cxobj *attr;
|
||||
cxobj *f;
|
||||
cxobj *s;
|
||||
int retval = -1;
|
||||
char *an, *af;
|
||||
char *fstr, *sstr;
|
||||
int keymatch = 0;
|
||||
|
||||
get_operation(filter, &op, xf_err, xorig);
|
||||
/* 1. First try selection: filter is empty */
|
||||
if (xml_child_nr(filter) == 0){ /* no elements? */
|
||||
retval = edit_selection(filter, parent, op, xf_err, xorig);
|
||||
goto done;
|
||||
}
|
||||
if (xml_child_nr(filter) == 1 && /* same as above */
|
||||
xpath_first(filter, "/[@operation]")){
|
||||
retval = edit_selection(filter, parent, op, xf_err, xorig);
|
||||
goto done;
|
||||
}
|
||||
/* 2. Check attribute match */
|
||||
attr = NULL;
|
||||
while ((attr = xml_child_each(filter, attr, CX_ATTR)) != NULL) {
|
||||
af = xml_value(attr);
|
||||
an = xml_find_value(filter, xml_name(attr));
|
||||
if (af && an && strcmp(af, an)==0)
|
||||
; // match
|
||||
else
|
||||
goto nomatch;
|
||||
}
|
||||
/* 3. Check content match */
|
||||
/*
|
||||
* For content-match we do a somewhat strange thing, we find
|
||||
* a match in first content-node and assume that is unique
|
||||
* and then we remove/replace that
|
||||
* For merge we just continue
|
||||
*/
|
||||
f = NULL;
|
||||
while ((f = xml_child_each(filter, f, CX_ELMNT)) != NULL) {
|
||||
if ((fstr = leafstring(f)) == NULL)
|
||||
continue;
|
||||
/* we found a filter leaf-match: no return we say it should match*/
|
||||
if ((s = xml_find(parent, xml_name(f))) == NULL)
|
||||
goto nomatch;
|
||||
if ((sstr = leafstring(s)) == NULL)
|
||||
goto nomatch;
|
||||
if (strcmp(fstr, sstr))
|
||||
goto nomatch;
|
||||
keymatch++;
|
||||
break; /* match */
|
||||
}
|
||||
|
||||
if (debug && keymatch){
|
||||
fprintf(stderr, "%s: match\n", __FUNCTION__);
|
||||
fprintf(stderr, "%s: filter:\n", __FUNCTION__);
|
||||
clicon_xml2file(stdout, filter, 0, 1);
|
||||
fprintf(stderr, "%s: config:\n", __FUNCTION__);
|
||||
clicon_xml2file(stdout, parent, 0, 1);
|
||||
}
|
||||
|
||||
retval = edit_match(filter, parent, op, xf_err, xorig, keymatch);
|
||||
/* match */
|
||||
netconf_ok_set(1);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
nomatch:
|
||||
return 0;
|
||||
}
|
||||
#endif /* NOTUSED */
|
||||
36
apps/netconf/netconf_filter.h
Normal file
36
apps/netconf/netconf_filter.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* netconf match & selection: get and edit operations
|
||||
*****************************************************************************/
|
||||
#ifndef _NETCONF_FILTER_H_
|
||||
#define _NETCONF_FILTER_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int xml_filter(cxobj *xf, cxobj *xn);
|
||||
int netconf_xpath(cxobj *xsearch,
|
||||
cxobj *xfilter,
|
||||
cbuf *xf, cbuf *xf_err,
|
||||
cxobj *xt);
|
||||
|
||||
#endif /* _NETCONF_FILTER_H_ */
|
||||
118
apps/netconf/netconf_hello.c
Normal file
118
apps/netconf/netconf_hello.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* Code for handling netconf hello messages
|
||||
*****************************************************************************/
|
||||
/*
|
||||
Capabilities are advertised in messages sent by each peer during
|
||||
session establishment. When the NETCONF session is opened, each peer
|
||||
(both client and server) MUST send a <hello> element containing a
|
||||
list of that peer's capabilities. Each peer MUST send at least the
|
||||
base NETCONF capability, "urn:ietf:params:netconf:base:1.0".
|
||||
<hello>
|
||||
<capabilities>
|
||||
<capability>URI</capability>
|
||||
</capabilities>
|
||||
|
||||
</hello>
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clicon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clicon/clicon.h>
|
||||
|
||||
#include "netconf_lib.h"
|
||||
#include "netconf_hello.h"
|
||||
|
||||
static int
|
||||
netconf_hello(cxobj *xn)
|
||||
{
|
||||
cxobj *x;
|
||||
|
||||
x = NULL;
|
||||
while ((x = xpath_each(xn, "//capability", x)) != NULL) {
|
||||
//fprintf(stderr, "cap: %s\n", xml_body(x));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
netconf_hello_dispatch(cxobj *xn)
|
||||
{
|
||||
cxobj *xp;
|
||||
int retval = -1;
|
||||
|
||||
if ((xp = xpath_first(xn, "//hello")) != NULL)
|
||||
retval = netconf_hello(xp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* netconf_create_hello
|
||||
* create capability string (once)
|
||||
*/
|
||||
int
|
||||
netconf_create_hello(cbuf *xf, /* msg buffer */
|
||||
int session_id)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
add_preamble(xf);
|
||||
cprintf(xf, "<hello>");
|
||||
cprintf(xf, "<capabilities>");
|
||||
cprintf(xf, "<capability>urn:ietf:params:xml:ns:netconf:base:1.0</capability>\n");
|
||||
cprintf(xf, "<capability>urn:ietf:params:xml:ns:netconf:capability:candidate:1:0</capability>\n");
|
||||
cprintf(xf, "<capability>urn:ietf:params:xml:ns:netconf:capability:validate:1.0</capability>\n");
|
||||
cprintf(xf, "<capability>urn:ietf:params:netconf:capability:xpath:1.0</capability>\n");
|
||||
cprintf(xf, "<capability>urn:ietf:params:netconf:capability:notification:1.0</capability>\n");
|
||||
|
||||
|
||||
// cprintf(xf, "<capability>urn:rnr:rnrapi:1:0</capability>");
|
||||
cprintf(xf, "</capabilities>");
|
||||
cprintf(xf, "<session-id>%lu</session-id>", 42+session_id);
|
||||
cprintf(xf, "</hello>");
|
||||
add_postamble(xf);
|
||||
return retval;
|
||||
}
|
||||
34
apps/netconf/netconf_hello.h
Normal file
34
apps/netconf/netconf_hello.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* Code for handling netconf hello messages
|
||||
*****************************************************************************/
|
||||
#ifndef _NETCONF_HELLO_H_
|
||||
#define _NETCONF_HELLO_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int netconf_create_hello(cbuf *xf, int session_id);
|
||||
|
||||
int netconf_hello_dispatch(cxobj *xn);
|
||||
|
||||
#endif /* _NETCONF_HELLO_H_ */
|
||||
261
apps/netconf/netconf_lib.c
Normal file
261
apps/netconf/netconf_lib.c
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* netconf lib
|
||||
*****************************************************************************/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clicon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <syslog.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/param.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clicon/clicon.h>
|
||||
|
||||
#include "netconf_rpc.h"
|
||||
#include "netconf_lib.h"
|
||||
|
||||
/*
|
||||
* Exported variables
|
||||
*/
|
||||
enum transport_type transport = NETCONF_SSH;
|
||||
int cc_closed = 0;
|
||||
|
||||
static int cc_ok = 0;
|
||||
|
||||
void
|
||||
netconf_ok_set(int ok)
|
||||
{
|
||||
cc_ok = ok;
|
||||
}
|
||||
|
||||
int
|
||||
netconf_ok_get(void)
|
||||
{
|
||||
return cc_ok;
|
||||
}
|
||||
|
||||
int
|
||||
add_preamble(cbuf *xf)
|
||||
{
|
||||
if (transport == NETCONF_SOAP)
|
||||
cprintf(xf, "\n<soapenv:Envelope\n xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\">\n"
|
||||
"<soapenv:Body>");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_postamble
|
||||
* add netconf xml postamble of message. That is, xml after the body of the message.
|
||||
* for soap this is the envelope stuff, for ssh this is ]]>]]>
|
||||
*/
|
||||
int
|
||||
add_postamble(cbuf *xf)
|
||||
{
|
||||
switch (transport){
|
||||
case NETCONF_SSH:
|
||||
cprintf(xf, "]]>]]>"); /* Add RFC4742 end-of-message marker */
|
||||
break;
|
||||
case NETCONF_SOAP:
|
||||
cprintf(xf, "\n</soapenv:Body>" "</soapenv:Envelope>");
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_error_preamble
|
||||
* compared to regular messages (see add_preamble), error message differ in some
|
||||
* protocols (eg soap) by adding a longer and deeper header.
|
||||
*/
|
||||
int
|
||||
add_error_preamble(cbuf *xf, char *reason)
|
||||
{
|
||||
switch (transport){
|
||||
case NETCONF_SOAP:
|
||||
cprintf(xf, "<soapenv:Envelope xmlns:soapenv=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:xml=\"http://www.w3.org/XML/1998/namespace\">"
|
||||
"<soapenv:Body>"
|
||||
"<soapenv:Fault>"
|
||||
"<soapenv:Code>"
|
||||
"<soapenv:Value>env:Receiver</soapenv:Value>"
|
||||
"</soapenv:Code>"
|
||||
"<soapenv:Reason>"
|
||||
"<soapenv:Text xml:lang=\"en\">%s</soapenv:Text>"
|
||||
"</soapenv:Reason>"
|
||||
"<detail>", reason);
|
||||
break;
|
||||
default:
|
||||
if (add_preamble(xf) < 0)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_error_postamble
|
||||
* compared to regular messages (see add_postamble), error message differ in some
|
||||
* protocols (eg soap) by adding a longer and deeper header.
|
||||
*/
|
||||
int
|
||||
add_error_postamble(cbuf *xf)
|
||||
{
|
||||
switch (transport){
|
||||
case NETCONF_SOAP:
|
||||
cprintf(xf, "</detail>" "</soapenv:Fault>");
|
||||
default: /* fall through */
|
||||
if (add_postamble(xf) < 0)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Look for a text pattern in an input string, one char at a time
|
||||
* @param[in] tag What to look for
|
||||
* @param[in] ch New input character
|
||||
* @param[in,out] state A state integer holding how far we have parsed.
|
||||
* @retval 0 No, we havent detected end tag
|
||||
* @retval 1 Yes, we have detected end tag!
|
||||
* XXX: move to clicon_xml?
|
||||
*/
|
||||
int
|
||||
detect_endtag(char *tag, char ch, int *state)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
if (tag[*state] == ch){
|
||||
(*state)++;
|
||||
if (*state == strlen(tag)){
|
||||
*state = 0;
|
||||
retval = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
*state = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* target_locked
|
||||
* return 1 if locked, 0 if not.
|
||||
* return clientid if locked.
|
||||
*/
|
||||
int
|
||||
target_locked(enum target_type target, int *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* unlock_target
|
||||
*/
|
||||
int
|
||||
unlock_target(enum target_type target)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lock_target(enum target_type target)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get "target" attribute, return actual database given candidate or running
|
||||
* Caller must do error handling
|
||||
* @retval dbname Actual database file name
|
||||
*/
|
||||
char *
|
||||
netconf_get_target(clicon_handle h, cxobj *xn, char *path)
|
||||
{
|
||||
cxobj *x;
|
||||
char *target = NULL;
|
||||
char *running_db;
|
||||
char *candidate_db;
|
||||
|
||||
if ((running_db = clicon_running_db(h)) == NULL)
|
||||
goto done;
|
||||
if ((candidate_db = clicon_candidate_db(h)) == NULL)
|
||||
goto done;
|
||||
if ((x = xpath_first(xn, path)) != NULL){
|
||||
if (xpath_first(x, "candidate") != NULL)
|
||||
target = candidate_db;
|
||||
else
|
||||
if (xpath_first(x, "running") != NULL)
|
||||
target = running_db;
|
||||
}
|
||||
done:
|
||||
return target;
|
||||
|
||||
}
|
||||
|
||||
/*! Send netconf message from cbuf on socket
|
||||
* @param[in] s
|
||||
* @param[in] cb Cligen buffer that contains the XML message
|
||||
* @param[in] msg Only for debug
|
||||
*/
|
||||
int
|
||||
netconf_output(int s, cbuf *xf, char *msg)
|
||||
{
|
||||
char *buf = cbuf_get(xf);
|
||||
int len = cbuf_len(xf);
|
||||
int retval = -1;
|
||||
|
||||
clicon_debug(1, "SEND %s", msg);
|
||||
if (debug > 1){ /* XXX: below only works to stderr, clicon_debug may log to syslog */
|
||||
cxobj *xt = NULL;
|
||||
if (clicon_xml_parse_string(&buf, &xt) == 0){
|
||||
clicon_xml2file(stderr, xml_child_i(xt, 0), 0, 0);
|
||||
fprintf(stderr, "\n");
|
||||
xml_free(xt);
|
||||
}
|
||||
}
|
||||
if (write(s, buf, len) < 0){
|
||||
if (errno == EPIPE)
|
||||
;
|
||||
else
|
||||
clicon_log(LOG_ERR, "%s: write: %s", __FUNCTION__, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
75
apps/netconf/netconf_lib.h
Normal file
75
apps/netconf/netconf_lib.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* Netconf lib
|
||||
*****************************************************************************/
|
||||
#ifndef _NETCONF_LIB_H_
|
||||
#define _NETCONF_LIB_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
enum target_type{ /* netconf */
|
||||
RUNNING,
|
||||
CANDIDATE
|
||||
};
|
||||
enum transport_type{
|
||||
NETCONF_SSH, /* RFC 4742 */
|
||||
NETCONF_SOAP, /* RFC 4743 */
|
||||
};
|
||||
|
||||
enum test_option{ /* edit-config */
|
||||
SET,
|
||||
TEST_THEN_SET,
|
||||
TEST_ONLY
|
||||
};
|
||||
|
||||
enum error_option{ /* edit-config */
|
||||
STOP_ON_ERROR,
|
||||
CONTINUE_ON_ERROR
|
||||
};
|
||||
|
||||
enum filter_option{ /* get-config/filter */
|
||||
FILTER_SUBTREE,
|
||||
FILTER_XPATH
|
||||
};
|
||||
|
||||
/*
|
||||
* Variables
|
||||
*/
|
||||
extern enum transport_type transport;
|
||||
extern int cc_closed;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
void netconf_ok_set(int ok);
|
||||
int netconf_ok_get(void);
|
||||
|
||||
int add_preamble(cbuf *xf);
|
||||
int add_postamble(cbuf *xf);
|
||||
int add_error_preamble(cbuf *xf, char *reason);
|
||||
int detect_endtag(char *tag, char ch, int *state);
|
||||
char *netconf_get_target(clicon_handle h, cxobj *xn, char *path);
|
||||
int add_error_postamble(cbuf *xf);
|
||||
int netconf_output(int s, cbuf *xf, char *msg);
|
||||
|
||||
#endif /* _NETCONF_LIB_H_ */
|
||||
425
apps/netconf/netconf_main.c
Normal file
425
apps/netconf/netconf_main.c
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clicon_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 <assert.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clicon/clicon.h>
|
||||
|
||||
#include "clicon_netconf.h"
|
||||
#include "netconf_lib.h"
|
||||
#include "netconf_hello.h"
|
||||
#include "netconf_plugin.h"
|
||||
#include "netconf_rpc.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define NETCONF_OPTS "hDqf:d:S"
|
||||
|
||||
/*! Process incoming packet
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xf Packet buffer
|
||||
*/
|
||||
static int
|
||||
process_incoming_packet(clicon_handle h,
|
||||
cbuf *xf)
|
||||
{
|
||||
char *str;
|
||||
char *str0;
|
||||
cxobj *xml_req = NULL; /* Request (in) */
|
||||
int isrpc = 0; /* either hello or rpc */
|
||||
cbuf *xf_out;
|
||||
cbuf *xf_err;
|
||||
cbuf *xf1;
|
||||
|
||||
clicon_debug(1, "RECV");
|
||||
clicon_debug(2, "%s: RCV: \"%s\"", __FUNCTION__, cbuf_get(xf));
|
||||
if ((str0 = strdup(cbuf_get(xf))) == NULL){
|
||||
clicon_log(LOG_ERR, "%s: strdup: %s", __FUNCTION__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
str = str0;
|
||||
/* Parse incoming XML message */
|
||||
if (clicon_xml_parse_string(&str, &xml_req) < 0){
|
||||
if ((xf = cbuf_new()) == NULL){
|
||||
netconf_create_rpc_error(xf, NULL,
|
||||
"operation-failed",
|
||||
"rpc", "error",
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
netconf_output(1, xf, "rpc-error");
|
||||
cbuf_free(xf);
|
||||
}
|
||||
else
|
||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
||||
free(str0);
|
||||
goto done;
|
||||
}
|
||||
free(str0);
|
||||
if (xpath_first(xml_req, "//rpc") != NULL){
|
||||
isrpc++;
|
||||
}
|
||||
else
|
||||
if (xpath_first(xml_req, "//hello") != NULL)
|
||||
;
|
||||
else{
|
||||
clicon_log(LOG_WARNING, "Invalid netconf msg: neither rpc or hello: dropp\
|
||||
ed");
|
||||
goto done;
|
||||
}
|
||||
/* Initialize response buffers */
|
||||
if ((xf_out = cbuf_new()) == NULL){
|
||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Create error buf */
|
||||
if ((xf_err = cbuf_new()) == NULL){
|
||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
netconf_ok_set(0);
|
||||
if (isrpc){
|
||||
if (netconf_rpc_dispatch(h,
|
||||
xml_req,
|
||||
xpath_first(xml_req, "//rpc"),
|
||||
xf_out, xf_err) < 0){
|
||||
assert(cbuf_len(xf_err));
|
||||
clicon_debug(1, "%s", cbuf_get(xf_err));
|
||||
if (isrpc){
|
||||
if (netconf_output(1, xf_err, "rpc-error") < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if ((xf1 = cbuf_new()) != NULL){
|
||||
if (netconf_create_rpc_reply(xf1, xml_req, cbuf_get(xf_out), netconf_ok_get()) < 0){
|
||||
cbuf_free(xf_out);
|
||||
cbuf_free(xf_err);
|
||||
cbuf_free(xf1);
|
||||
goto done;
|
||||
}
|
||||
if (netconf_output(1, xf1, "rpc-reply") < 0){
|
||||
cbuf_reset(xf1);
|
||||
netconf_create_rpc_error(xf1, xml_req, "operation-failed",
|
||||
"protocol", "error",
|
||||
NULL, cbuf_get(xf_err));
|
||||
netconf_output(1, xf1, "rpc-error");
|
||||
cbuf_free(xf_out);
|
||||
cbuf_free(xf_err);
|
||||
cbuf_free(xf1);
|
||||
goto done;
|
||||
}
|
||||
cbuf_free(xf1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
netconf_hello_dispatch(xml_req); /* XXX: return-value */
|
||||
}
|
||||
cbuf_free(xf_out);
|
||||
cbuf_free(xf_err);
|
||||
done:
|
||||
if (xml_req)
|
||||
xml_free(xml_req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Get netconf message: detect end-of-msg
|
||||
* @param[in] s Socket where input arrived. read from this.
|
||||
* @param[in] arg Clicon handle.
|
||||
*/
|
||||
static int
|
||||
netconf_input_cb(int s,
|
||||
void *arg)
|
||||
{
|
||||
clicon_handle h = arg;
|
||||
unsigned char buf[BUFSIZ];
|
||||
int i;
|
||||
int len;
|
||||
static cbuf *xf; /* XXX: should use ce state? */
|
||||
int xml_state = 0;
|
||||
int retval = -1;
|
||||
|
||||
if (xf == NULL)
|
||||
if ((xf = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if ((len = read(s, buf, sizeof(buf))) < 0){
|
||||
if (errno == ECONNRESET)
|
||||
len = 0; /* emulate EOF */
|
||||
else{
|
||||
clicon_log(LOG_ERR, "%s: read: %s", __FUNCTION__, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
} /* read */
|
||||
if (len == 0){ /* EOF */
|
||||
cc_closed++;
|
||||
close(s);
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i=0; i<len; i++){
|
||||
if (buf[i] == 0)
|
||||
continue; /* Skip NULL chars (eg from terminals) */
|
||||
cprintf(xf, "%c", buf[i]);
|
||||
if (detect_endtag("]]>]]>",
|
||||
buf[i],
|
||||
&xml_state)) {
|
||||
/* OK, we have an xml string from a client */
|
||||
if (process_incoming_packet(h, xf) < 0){
|
||||
goto done;
|
||||
}
|
||||
if (cc_closed)
|
||||
break;
|
||||
cbuf_reset(xf);
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
// cbuf_free(xf);
|
||||
if (cc_closed)
|
||||
retval = -1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* send_hello
|
||||
* args: s file descriptor to write on (eg 1 - stdout)
|
||||
*/
|
||||
static int
|
||||
send_hello(int s)
|
||||
{
|
||||
cbuf *xf;
|
||||
int retval = -1;
|
||||
|
||||
if ((xf = cbuf_new()) == NULL){
|
||||
clicon_log(LOG_ERR, "%s: cbuf_new", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
if (netconf_create_hello(xf, getpid()) < 0)
|
||||
goto done;
|
||||
if (netconf_output(s, xf, "hello") < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xf)
|
||||
cbuf_free(xf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* from init_candidate_db() and clicon_rpc_copy() */
|
||||
static int
|
||||
init_candidate_db(clicon_handle h, char *running_db, char *candidate_db)
|
||||
{
|
||||
struct stat sb;
|
||||
int retval = -1;
|
||||
|
||||
/* init shared candidate */
|
||||
if (lstat(candidate_db, &sb) < 0){
|
||||
if (clicon_rpc_copy(h, running_db, candidate_db) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
terminate(clicon_handle h)
|
||||
{
|
||||
yang_spec *yspec;
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||
yspec_free(yspec);
|
||||
clicon_handle_exit(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* usage
|
||||
*/
|
||||
static void
|
||||
usage(char *argv0, clicon_handle h)
|
||||
{
|
||||
char *netconfdir = clicon_netconf_dir(h);
|
||||
|
||||
fprintf(stderr, "usage:%s\n"
|
||||
"where options are\n"
|
||||
"\t-h\t\tHelp\n"
|
||||
"\t-D\t\tDebug\n"
|
||||
"\t-q\t\tQuiet: dont send hello prompt\n"
|
||||
"\t-f <file>\tConfiguration file (mandatory)\n"
|
||||
"\t-d <dir>\tSpecify netconf plugin directory dir (default: %s)\n"
|
||||
"\t-S\t\tLog on syslog\n",
|
||||
argv0,
|
||||
netconfdir
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
char *tmp;
|
||||
char *argv0 = argv[0];
|
||||
int quiet = 0;
|
||||
clicon_handle h;
|
||||
int use_syslog;
|
||||
char *running_db;
|
||||
char *candidate_db;
|
||||
|
||||
/* Defaults */
|
||||
use_syslog = 0;
|
||||
|
||||
/* In the startup, logs to stderr & debug flag set later */
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
|
||||
/* Create handle */
|
||||
if ((h = clicon_handle_init()) == NULL)
|
||||
return -1;
|
||||
|
||||
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case 'h' : /* help */
|
||||
usage(argv[0], h);
|
||||
break;
|
||||
case 'D' : /* debug */
|
||||
debug = 1;
|
||||
break;
|
||||
case 'f': /* override config file */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
|
||||
break;
|
||||
case 'S': /* Log on syslog */
|
||||
use_syslog = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Logs, error and debug to stderr or syslog, set debug level
|
||||
*/
|
||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO,
|
||||
use_syslog?CLICON_LOG_SYSLOG:CLICON_LOG_STDERR);
|
||||
clicon_debug_init(debug, NULL);
|
||||
|
||||
/* Find and read configfile */
|
||||
if (clicon_options_main(h) < 0)
|
||||
return -1;
|
||||
|
||||
/* Now rest of options */
|
||||
optind = 1;
|
||||
opterr = 0;
|
||||
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case 'h' : /* help */
|
||||
case 'D' : /* debug */
|
||||
case 'f': /* config file */
|
||||
case 'S': /* Log on syslog */
|
||||
break; /* see above */
|
||||
case 'q': /* quiet: dont write hello */
|
||||
quiet++;
|
||||
break;
|
||||
case 'd': /* Plugin directory */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_NETCONF_DIR", optarg);
|
||||
break;
|
||||
default:
|
||||
usage(argv[0], h);
|
||||
break;
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* Parse db spec file */
|
||||
if (yang_spec_main(h, stdout, 0) < 0)
|
||||
goto done;
|
||||
|
||||
/* Initialize plugins group */
|
||||
if (netconf_plugin_load(h) < 0)
|
||||
return -1;
|
||||
|
||||
if ((running_db = clicon_running_db(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "running db not set");
|
||||
goto done;
|
||||
}
|
||||
if ((candidate_db = clicon_candidate_db(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "candidate db not set");
|
||||
goto done;
|
||||
}
|
||||
if (init_candidate_db(h, running_db, candidate_db) < 0)
|
||||
return -1;
|
||||
/* Call start function is all plugins before we go interactive */
|
||||
tmp = *(argv-1);
|
||||
*(argv-1) = argv0;
|
||||
netconf_plugin_start(h, argc+1, argv-1);
|
||||
*(argv-1) = tmp;
|
||||
|
||||
if (!quiet)
|
||||
send_hello(1);
|
||||
if (event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0)
|
||||
goto done;
|
||||
if (debug)
|
||||
clicon_option_dump(h, debug);
|
||||
|
||||
if (event_loop() < 0)
|
||||
goto done;
|
||||
done:
|
||||
|
||||
netconf_plugin_unload(h);
|
||||
terminate(h);
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
||||
return 0;
|
||||
}
|
||||
277
apps/netconf/netconf_plugin.c
Normal file
277
apps/netconf/netconf_plugin.c
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* handling netconf plugins
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clicon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <grp.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clicon/clicon.h>
|
||||
|
||||
/* clicon netconf*/
|
||||
#include "clicon_netconf.h"
|
||||
#include "netconf_lib.h"
|
||||
#include "netconf_plugin.h"
|
||||
|
||||
/*
|
||||
* Unload a plugin
|
||||
*/
|
||||
static int
|
||||
plugin_unload(clicon_handle h, void *handle)
|
||||
{
|
||||
int retval = 0;
|
||||
char *error;
|
||||
plgexit_t *exitfn;
|
||||
|
||||
/* Call exit function is it exists */
|
||||
exitfn = dlsym(handle, PLUGIN_EXIT);
|
||||
if (dlerror() == NULL)
|
||||
exitfn(h);
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if (dlclose(handle) != 0) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
|
||||
/* Just report */
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Load a dynamic plugin object and call it's init-function
|
||||
* Note 'file' may be destructively modified
|
||||
*/
|
||||
static plghndl_t
|
||||
plugin_load (clicon_handle h, char *file, int dlflags, const char *cnklbl)
|
||||
{
|
||||
char *error;
|
||||
void *handle = NULL;
|
||||
plginit_t *initfn;
|
||||
|
||||
dlerror(); /* Clear any existing error */
|
||||
if ((handle = dlopen (file, dlflags)) == NULL) {
|
||||
error = (char*)dlerror();
|
||||
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
|
||||
goto quit;
|
||||
}
|
||||
/* call plugin_init() if defined */
|
||||
if ((initfn = dlsym(handle, PLUGIN_INIT)) != NULL) {
|
||||
if (initfn(h) != 0) {
|
||||
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s\n", strrchr(file,'/')?strchr(file, '/'):file);
|
||||
goto quit;
|
||||
}
|
||||
}
|
||||
quit:
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
static int nplugins = 0;
|
||||
static plghndl_t *plugins = NULL;
|
||||
static netconf_reg_t *deps = NULL;
|
||||
|
||||
/*
|
||||
* netconf_plugin_load
|
||||
* Load allplugins you can find in CLICON_NETCONF_DIR
|
||||
*/
|
||||
int
|
||||
netconf_plugin_load(clicon_handle h)
|
||||
{
|
||||
int retval = -1;
|
||||
char *dir;
|
||||
int ndp;
|
||||
struct dirent *dp;
|
||||
int i;
|
||||
char *filename;
|
||||
plghndl_t *handle;
|
||||
|
||||
if ((dir = clicon_netconf_dir(h)) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "clicon_netconf_dir not defined");
|
||||
goto quit;
|
||||
}
|
||||
|
||||
/* Get plugin objects names from plugin directory */
|
||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
|
||||
goto quit;
|
||||
|
||||
/* Load all plugins */
|
||||
for (i = 0; i < ndp; i++) {
|
||||
filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
|
||||
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
|
||||
(int)strlen(filename), filename);
|
||||
if (filename == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
goto quit;
|
||||
}
|
||||
if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
|
||||
goto quit;
|
||||
if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "chunk");
|
||||
goto quit;
|
||||
}
|
||||
plugins[nplugins++] = handle;
|
||||
unchunk (filename);
|
||||
}
|
||||
retval = 0;
|
||||
quit:
|
||||
unchunk_group(__FUNCTION__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
netconf_plugin_unload(clicon_handle h)
|
||||
{
|
||||
int i;
|
||||
netconf_reg_t *nr;
|
||||
|
||||
while((nr = deps) != NULL) {
|
||||
DELQ(nr, deps, netconf_reg_t *);
|
||||
if (nr->nr_tag)
|
||||
free(nr->nr_tag);
|
||||
free(nr);
|
||||
}
|
||||
for (i = 0; i < nplugins; i++)
|
||||
plugin_unload(h, plugins[i]);
|
||||
if (plugins)
|
||||
unchunk(plugins);
|
||||
nplugins = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call plugin_start in all plugins
|
||||
*/
|
||||
int
|
||||
netconf_plugin_start(clicon_handle h, int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
plgstart_t *startfn;
|
||||
|
||||
for (i = 0; i < nplugins; i++) {
|
||||
/* Call exit function is it exists */
|
||||
if ((startfn = dlsym(plugins[i], PLUGIN_START)) == NULL)
|
||||
break;
|
||||
optind = 0;
|
||||
if (startfn(h, argc, argv) < 0) {
|
||||
clicon_debug(1, "plugin_start() failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* netconf_register_callback
|
||||
* Called from plugin to register a callback for a specific netconf XML tag.
|
||||
*/
|
||||
int
|
||||
netconf_register_callback(clicon_handle h,
|
||||
netconf_cb_t cb, /* Callback called */
|
||||
void *arg, /* Arg to send to callback */
|
||||
char *tag) /* Xml tag when callback is made */
|
||||
{
|
||||
netconf_reg_t *nr;
|
||||
|
||||
if ((nr = malloc(sizeof(netconf_reg_t))) == NULL) {
|
||||
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
|
||||
goto catch;
|
||||
}
|
||||
memset (nr, 0, sizeof (*nr));
|
||||
nr->nr_callback = cb;
|
||||
nr->nr_arg = arg;
|
||||
nr->nr_tag = strdup(tag); /* strdup */
|
||||
INSQ(nr, deps);
|
||||
return 0;
|
||||
catch:
|
||||
if (nr){
|
||||
if (nr->nr_tag)
|
||||
free(nr->nr_tag);
|
||||
free(nr);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*! See if there is any callback registered for this tag
|
||||
*
|
||||
* @param xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
|
||||
* @param xf Output xml stream. For reply
|
||||
* @param xf_err Error xml stream. For error reply
|
||||
* @param xorig Original request.
|
||||
*
|
||||
* @retval -1 Error
|
||||
* @retval 0 OK, not found handler.
|
||||
* @retval 1 OK, handler called
|
||||
*/
|
||||
int
|
||||
netconf_plugin_callbacks(clicon_handle h,
|
||||
cxobj *xn,
|
||||
cbuf *xf,
|
||||
cbuf *xf_err,
|
||||
cxobj *xorig)
|
||||
{
|
||||
netconf_reg_t *nr;
|
||||
int retval;
|
||||
|
||||
if (deps == NULL)
|
||||
return 0;
|
||||
nr = deps;
|
||||
do {
|
||||
if (strcmp(nr->nr_tag, xml_name(xn)) == 0){
|
||||
if ((retval = nr->nr_callback(h,
|
||||
xorig,
|
||||
xn,
|
||||
xf,
|
||||
xf_err,
|
||||
nr->nr_arg)) < 0)
|
||||
return -1;
|
||||
else
|
||||
return 1; /* handled */
|
||||
}
|
||||
nr = NEXTQ(netconf_reg_t *, nr);
|
||||
} while (nr != deps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
57
apps/netconf/netconf_plugin.h
Normal file
57
apps/netconf/netconf_plugin.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* handling netconf plugins
|
||||
*****************************************************************************/
|
||||
#ifndef _NETCONF_PLUGIN_H_
|
||||
#define _NETCONF_PLUGIN_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
|
||||
/* Database dependency description */
|
||||
struct netconf_reg {
|
||||
qelem_t nr_qelem; /* List header */
|
||||
netconf_cb_t nr_callback; /* Validation/Commit Callback */
|
||||
void *nr_arg; /* Application specific argument to cb */
|
||||
char *nr_tag; /* Xml tag when matched, callback called */
|
||||
};
|
||||
typedef struct netconf_reg netconf_reg_t;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int netconf_plugin_load(clicon_handle h);
|
||||
|
||||
int netconf_plugin_start(clicon_handle h, int argc, char **argv);
|
||||
|
||||
int netconf_plugin_unload(clicon_handle h);
|
||||
|
||||
|
||||
int netconf_plugin_callbacks(clicon_handle h,
|
||||
// dbspec_key *dbspec,
|
||||
cxobj *xn,
|
||||
cbuf *xf,
|
||||
cbuf *xf_err,
|
||||
cxobj *xt);
|
||||
|
||||
#endif /* _NETCONF_PLUGIN_H_ */
|
||||
1258
apps/netconf/netconf_rpc.c
Normal file
1258
apps/netconf/netconf_rpc.c
Normal file
File diff suppressed because it is too large
Load diff
48
apps/netconf/netconf_rpc.h
Normal file
48
apps/netconf/netconf_rpc.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
* Code for handling netconf rpc messages
|
||||
*****************************************************************************/
|
||||
#ifndef _NETCONF_RPC_H_
|
||||
#define _NETCONF_RPC_H_
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int
|
||||
netconf_rpc_dispatch(clicon_handle h,
|
||||
cxobj *xorig,
|
||||
cxobj *xn,
|
||||
cbuf *xf,
|
||||
cbuf *xf_err);
|
||||
|
||||
int netconf_create_rpc_reply(cbuf *xf, /* msg buffer */
|
||||
cxobj *xr, /* orig request */
|
||||
char *body, int ok);
|
||||
int netconf_create_rpc_error(cbuf *xf, /* msg buffer */
|
||||
cxobj *xr, /* orig request */
|
||||
char *tag,
|
||||
char *type,
|
||||
char *severity,
|
||||
char *message,
|
||||
char *info);
|
||||
|
||||
#endif /* _NETCONF_RPC_H_ */
|
||||
Loading…
Add table
Add a link
Reference in a new issue