Merge branch 'develop' for 3.7.0

This commit is contained in:
Olof hagsand 2018-07-22 20:52:02 +02:00
commit 728671944f
129 changed files with 8192 additions and 2282 deletions

View file

@ -1,5 +1,104 @@
# Clixon Changelog
## 3.7.0 (22 July 2018)
### Major New features
* YANG "must" and "when" Xpath-basedconstraints according to RFC 7950 Sec 7.5.3 and 7.21.5.
* Must and when Xpath constrained checked at validation/commit.
* YANG "identity" and "identityref", according to RFC 7950 Sec 7.18 and 9.10.
* Identities checked at validation/commit.
* CLI completion support of identity values.
* Example extended with iana-if-type RFC 7224 interface identities.
* Improved support for XPATH 1.0 according to https://www.w3.org/TR/xpath-10 using yacc/lex,
* Full suport of boolean constraints for "when"/"must", not only nodesets.
* See also API changes below.
* CDATA XML support (patch by David Cornejo, Netgate)
* Encode and decode (parsing) support
* Added cligen variable translation. Useful for processing input such as hashing, encryption.
* More info in example and FAQ.
* Example:
```
cli> translate value HAL
cli> show configuration
translate {
value IBM;
}
```
### API changes on existing features (you may need to change your code)
* YANG identity, identityref, must, and when validation support may cause applications that had not strictly enforced identities and constraints before.
* Restconf operations changed from prefix to module name.
* Proper specification for an operation is POST /restconf/operations/<module_name>:<rpc_procedure> HTTP/1.1
* See https://github.com/clicon/clixon/issues/31, https://github.com/clicon/clixon/pull/32 and https://github.com/clicon/clixon/issues/30
* Thanks David Cornejo and Dmitry Vakhrushev of Netgate for pointing this out.
* New XPATH 1.0 leads to some API changes and corrections
* Due to an error in the previous implementation, all XPATH calls on the form `x[a=str]` where `str` is a string (not a number or XML symbol), must be changed to: `x[a='str'] or x[a="str"]`
* This includes all calls to `xpath_vec, xpath_first`, etc.
* In CLI specs, calls to cli_copy_config() must change 2nd argument from `x[%s=%s]` to `x[%s='%s']`
* In CLI specs, calls to cli_show_config() may need to change third argument, eg
* `cli_show_config("running","text","/profile[name=%s]","name")` to `cli_show_config("running","text","/profile[name='%s']","name")`
* xpath_each() is removed
* The old API can be enabled by setting COMPAT_XSL in include/clixon_custom.h and recompile.
* Makefile change: Removed the make include file: clixon.mk and clixon.mk.in
* These generated the Makefile variables: clixon_DBSPECDIR, clixon_SYSCONFDIR, clixon_LOCALSTATEDIR, clixon_LIBDIR, clixon_DATADIR which have been replaced by generic autoconf variables instead.
* Removed cli callback vector functions. Set COMPAT_CLIV if you need to keep these functions in include/clixon_custom.h.
* Replace functions as follows in CLI SPEC files:
* cli_setv --> cli_set
* cli_mergev --> cli_merge
* cli_delv --> cli_del
* cli_debug_cliv --> cli_debug_cli
* cli_debug_backendv --> cli_debug_backend
* cli_set_modev --> cli_set_mode
* cli_start_shellv --> cli_start_shell
* cli_quitv --> cli_quit
* cli_commitv --> cli_commit
* cli_validatev --> cli_validate
* compare_dbsv --> compare_dbs
* load_config_filev --> load_config_file
* save_config_filev --> save_config_file
* delete_allv --> delete_all
* discard_changesv --> discard_changes
* cli_notifyv --> cli_notify
* show_yangv --> show_yang
* show_confv_xpath --> show_conf_xpath
* Changed `plugin_init()` backend return semantics: If returns NULL, _without_ calling clicon_err(), the module is disabled.
### Minor changes
* Clixon docker upgrade
* Updated the docker image build and example to a single clixon docker image.
* Example pushed to https://hub.docker.com/r/olofhagsand/clixon_example/
* Added systemd example files under example/systemd
* Added util subdir, with dedicated standalone xml, json, yang and xpath parser utility test programs.
* Validation of yang bits type space-separated list value
* Added -U <user> command line to clixon_cli and clixon_netconf for changing user.
* This is primarily for NACM pseudo-user tests
* Added a generated CLI show command that works on the generated parse tree with auto completion.
* A typical call is: show @datamodel:example, cli_show_auto("candidate", "json");
* The example contains a more elaborate example.
* Thanks ngashok for request, see https://github.com/clicon/clixon/issues/24
* Added XML namespace (xmlns) validation
* for eg <a xmlns:x="uri"><x:b/></a>
* ./configure extended with --enable-debug flag
* CFLAGS=-g ./configure deprecated
### Corrected Bugs
* Prefix of rpc was ignored (thanks Dmitri at netgate)
* https://github.com/clicon/clixon/issues/30
* Added cli return value also for single commands (eg -1)
* Fixed JSON unbalanced braces resulting in assert.
### Known issues
* Namespace name relabeling is not supported.
* Eg: if "des" is defined as prefix for an imported module, then a relabeling using xmlfns is not supported, such as:
```
<crypto xmlns:x="urn:example:des">x:des3</crypto>
```
## 3.6.1 (29 May 2018)
### Corrected Bugs
@ -452,7 +551,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
Instead use the rpc calls in clixon_proto_client.[ch]
In clients (eg cli/netconf) replace xmldb_get() in client code with
clicon_rpc_get_config().
pIf you use the vector arguments of xmldb_get(), replace as follows:
If you use the vector arguments of xmldb_get(), replace as follows:
xmldb_get(h, db, api_path, &xt, &xvec, &xlen);
with
clicon_rpc_get_config(h, dbstr, api_path, &xt);

View file

@ -52,11 +52,11 @@ INSTALL = @INSTALL@
INCLUDES = -I. -I@srcdir@ @INCLUDES@
SHELL = /bin/sh
SUBDIRS = lib apps include etc datastore yang
SUBDIRS = lib apps include etc datastore util yang
.PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status docker
all: $(SUBDIRS) clixon.mk
all: $(SUBDIRS)
$(SUBDIRS):
(cd $@ && $(MAKE) $(MFLAGS) all)
@ -65,12 +65,7 @@ depend:
for i in $(SUBDIRS) doc example docker; \
do (cd $$i && $(MAKE) $(MFLAGS) depend); done
clixon.mk: clixon.mk.cpp
$(CPP) -P -traditional-cpp -x assembler-with-cpp -Dprefix=$(prefix) -Dlocalstatedir=$(localstatedir) -Dsysconfdir=$(sysconfdir) -Ddatadir=$(datadir) -Dlibdir=$(libdir) $< > $@
install: clixon.mk
install -d -m 0755 $(DESTDIR)$(datadir)/clixon
install -m 0644 clixon.mk $(DESTDIR)$(datadir)/clixon
install:
for i in $(SUBDIRS) doc; \
do (cd $$i; $(MAKE) $(MFLAGS) $@)||exit 1; done; \
echo "Install for compilation by: make install-include"
@ -83,7 +78,6 @@ install-include:
uninstall:
for i in $(SUBDIRS) doc example docker; \
do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done;
rm -f $(DESTDIR)$(datadir)/clixon/clixon.mk
doc:
cd $@; $(MAKE) $(MFLAGS) $@
@ -100,7 +94,7 @@ clean:
distclean:
rm -f Makefile TAGS config.status config.log *~ .depend
rm -rf autom4te.cache clixon.mk build-root/rpmbuild
rm -rf autom4te.cache build-root/rpmbuild
rm -f build-root/*.tar.xz build-root/*.rpm extras/rpm/Makefile
for i in $(SUBDIRS) doc example docker; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done

View file

@ -11,6 +11,7 @@ transaction support from a YANG specification.
* [Support](#support)
* [Dependencies](#dependencies)
* [Extending](#extending)
* [XML and XPATH](#xml)
* [Yang](#yang)
* [Netconf](#netconf)
* [Restconf](#restconf)
@ -21,13 +22,14 @@ transaction support from a YANG specification.
* [Runtime](#runtime)
* [Clixon project page](http://www.clicon.org)
* [Tests](test/)
* [Docker](docker/)
* [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`)
Background
==========
Clixon was implemented to provide an open-source generic configuration
tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, whilke clixon is a system with config-db, xml and rest interfaces. Most of the projects using clixon are for embedded network and measuring devices. But Clixon is more generic than that.
tool. The existing [CLIgen](http://www.cligen.se) tool was for command-lines only, while clixon is a system with configuration database, xml and rest interfaces. Most of the projects using clixon are for embedded network and measuring devices. But Clixon is more generic than that.
Users of clixon currently include:
* [Netgate](https://www.netgate.com)
@ -89,9 +91,17 @@ are also available.
Plugins are written in C and easiest is to look at
[example](example/README.md) or consulting the [FAQ](doc/FAQ.md).
XML
===
Clixon has its own implementation of XML and XPATH implementation.
The standards covered include:
- [XML](https://www.w3.org/TR/2008/REC-xml-20081126)
- [Namespaces](https://www.w3.org/TR/2009/REC-xml-names-20091208)
- [XPATH](https://www.w3.org/TR/xpath-10)
Yang
====
YANG and XML is at the heart of Clixon. Yang modules are used as a
specification for handling XML configuration data. The YANG spec is
used to generate an interactive CLI, netconf and restconf clients. It
@ -99,8 +109,9 @@ also manages an XML datastore.
Clixon mainly follows [YANG 1.0 RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) with some exceptions:
- conformance: feature, if-feature, deviation
- identity, base, identityref
- list features: min/max-elements, unique
- action statements
- notifications
The aim is also to cover new features in YANG 1.1 [YANG RFC 7950](https://www.rfc-editor.org/rfc/rfc7950.txt)

View file

@ -531,6 +531,8 @@ from_client_unlock(clicon_handle h,
ok:
retval = 0;
done:
if (cbx)
cbuf_free(cbx);
return retval;
}
@ -803,12 +805,13 @@ from_client_debug(clicon_handle h,
return retval;
}
/*! Match nacm access operations according to RFC8321 3.4.4.
/*! Match nacm access operations according to RFC8341 3.4.4.
* Incoming RPC Message Validation Step 7 (c)
* The rule's "access-operations" leaf has the "exec" bit set or
* has the special value "*".
* @retval 0 No match
* @retval 1 Match
* XXX access_operations is bit-fields
*/
static int
nacm_match_access(char *access_operations,
@ -818,7 +821,7 @@ nacm_match_access(char *access_operations,
return 0;
if (strcmp(access_operations,"*")==0)
return 1;
if (strstr(mode, access_operations)!=NULL)
if (strstr(access_operations, mode)!=NULL)
return 1;
return 0;
}
@ -832,7 +835,7 @@ nacm_match_access(char *access_operations,
* @retval 0 Matching rule AND Not access and cbret set
* @retval 1 Matchung rule AND Access
* @retval 2 No matching rule Goto step 10
* From RFC8321 3.4.4. Incoming RPC Message Validation
* From RFC8341 3.4.4. Incoming RPC Message Validation
+---------+-----------------+---------------------+-----------------+
| Method | Resource class | NETCONF operation | Access |
| | | | operation |
@ -874,6 +877,7 @@ nacm_match_rule(clicon_handle h,
module_name = xml_find_body(xrule, "module-name");
rpc_name = xml_find_body(xrule, "rpc-name");
/* XXX access_operations can be a set of bits */
access_operations = xml_find_body(xrule, "access-operations");
action = xml_find_body(xrule, "action");
clicon_debug(1, "%s: %s %s %s %s", __FUNCTION__,
@ -911,7 +915,7 @@ nacm_match_rule(clicon_handle h,
* @retval -1 Error
* @retval 0 Not access and cbret set
* @retval 1 Access
* From RFC8321 3.4.4. Incoming RPC Message Validation
* From RFC8341 3.4.4. Incoming RPC Message Validation
*/
static int
nacm_access(clicon_handle h,
@ -983,7 +987,13 @@ nacm_access(clicon_handle h,
if (username == NULL)
goto step10;
/* User's group */
if (xpath_vec(xacm, "groups/group[user-name=%s]", &gvec, &glen, username) < 0)
if (xpath_vec(xacm,
#ifdef COMPAT_XSL
"groups/group[user-name=%s]",
#else
"groups/group[user-name='%s']",
#endif
&gvec, &glen, username) < 0)
goto done;
/* 5. If no groups are found, continue with step 10. */
if (glen == 0)
@ -1000,7 +1010,13 @@ nacm_access(clicon_handle h,
for (j=0; j<glen; j++){
char *gname;
gname = xml_find_body(gvec[j], "name");
if (xpath_first(xrlist,".[group=%s]", gname)!=NULL)
if (xpath_first(xrlist,
#ifdef COMPAT_XSL
".[group=%s]",
#else
".[group='%s']",
#endif
gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
@ -1202,7 +1218,7 @@ from_client_msg(clicon_handle h,
}
reply:
if (cbuf_len(cbret) == 0)
if (netconf_operation_failed(cbret, "application", clicon_err_reason)< 0)
if (netconf_operation_failed(cbret, "application", clicon_errno?clicon_err_reason:"unknown")< 0)
goto done;
clicon_debug(1, "%s cbret:%s", __FUNCTION__, cbuf_get(cbret));
/* XXX problem here is that cbret has not been parsed so may contain

View file

@ -79,8 +79,8 @@
* string regexp checked.
* See also db_lv_set() where defaults are also filled in. The case here for defaults
* are if code comes via XML/NETCONF.
* @param yspec Yang spec
* @param td Transaction data
* @param[in] yspec Yang spec
* @param[in] td Transaction data
*/
static int
generic_validate(yang_spec *yspec,

View file

@ -117,7 +117,8 @@ backend_sig_term(int arg)
/*! usage
*/
static void
usage(char *argv0, clicon_handle h)
usage(clicon_handle h,
char *argv0)
{
char *plgdir = clicon_backend_dir(h);
char *confsock = clicon_sock(h);
@ -581,11 +582,11 @@ main(int argc,
break;
case 'D' : /* debug */
if (sscanf(optarg, "%d", &debug) != 1)
usage(argv[0], h);
usage(h, argv[0]);
break;
case 'f': /* config file */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
}
@ -602,7 +603,7 @@ main(int argc,
/* Find and read configfile */
if (clicon_options_main(h) < 0){
if (help)
usage(argv[0], h);
usage(h, argv[0]);
return -1;
}
/* External NACM file? */
@ -621,12 +622,12 @@ main(int argc,
break; /* see above */
case 'd': /* Plugin directory */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_BACKEND_DIR", optarg);
break;
case 'b': /* XMLDB database directory */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_XMLDB_DIR", optarg);
break;
case 'F' : /* foreground */
@ -640,7 +641,7 @@ main(int argc,
break;
case 'u': /* config unix domain path / ip address */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'P': /* pidfile */
@ -650,7 +651,7 @@ main(int argc,
clicon_option_str_set(h, "CLICON_STARTUP_MODE", optarg);
if (clicon_startup_mode(h) < 0){
fprintf(stderr, "Invalid startup mode: %s\n", optarg);
usage(argv[0], h);
usage(h, argv[0]);
}
break;
case 'c': /* Load application config */
@ -668,7 +669,7 @@ main(int argc,
break;
}
default:
usage(argv[0], h);
usage(h, argv[0]);
break;
}
@ -677,7 +678,7 @@ main(int argc,
/* Defer: Wait to the last minute to print help message */
if (help)
usage(argv[0], h);
usage(h, argv[0]);
/* Check pid-file, if zap kil the old daemon, else return here */
if ((pidfile = clicon_backend_pidfile(h)) == NULL){

View file

@ -157,6 +157,8 @@ clixon_plugin_statedata(clicon_handle h,
if (reason){
while ((xc = xml_child_i(*xtop, 0)) != NULL)
xml_purge(xc);
clicon_log(LOG_NOTICE, "%s: Plugin '%s' state callback failed",
__FUNCTION__, cp->cp_name);
if (netconf_operation_failed_xml(xtop, "rpc", reason)< 0)
goto done;
goto ok;
@ -167,7 +169,7 @@ clixon_plugin_statedata(clicon_handle h,
}
}
/* Code complex to filter out anything that is outside of xpath */
if (xpath_vec(*xtop, xpath?xpath:"/", &xvec, &xlen) < 0)
if (xpath_vec(*xtop, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found and

View file

@ -96,12 +96,12 @@ config_socket_init_ipv4(clicon_handle h, char *dst)
goto err; /* Could check getaddrinfo */
}
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0){
clicon_err(OE_UNIX, errno, "%s: bind", __FUNCTION__);
clicon_err(OE_UNIX, errno, "bind");
goto err;
}
clicon_debug(1, "Listen on server socket at %s:%hu", dst, port);
if (listen(s, 5) < 0){
clicon_err(OE_UNIX, errno, "%s: listen", __FUNCTION__);
clicon_err(OE_UNIX, errno, "listen");
goto err;
}
return s;
@ -126,7 +126,7 @@ config_socket_init_unix(clicon_handle h, char *sock)
struct stat st;
if (lstat(sock, &st) == 0 && unlink(sock) < 0){
clicon_err(OE_UNIX, errno, "%s: unlink(%s)", __FUNCTION__, sock);
clicon_err(OE_UNIX, errno, "unlink(%s)", sock);
return -1;
}
/* then find configuration group (for clients) and find its groupid */
@ -142,7 +142,7 @@ config_socket_init_unix(clicon_handle h, char *sock)
#endif
/* create unix socket */
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
clicon_err(OE_UNIX, errno, "%s: socket", __FUNCTION__);
clicon_err(OE_UNIX, errno, "socket");
return -1;
}
// setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
@ -151,20 +151,19 @@ config_socket_init_unix(clicon_handle h, char *sock)
strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);
old_mask = umask(S_IRWXO | S_IXGRP | S_IXUSR);
if (bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){
clicon_err(OE_UNIX, errno, "%s: bind", __FUNCTION__);
clicon_err(OE_UNIX, errno, "bind");
umask(old_mask);
goto err;
}
umask(old_mask);
/* change socket path file group */
if (lchown(sock, -1, gid) < 0){
clicon_err(OE_UNIX, errno, "%s: lchown(%s, %s)", __FUNCTION__,
sock, config_group);
clicon_err(OE_UNIX, errno, "lchown(%s, %s)", sock, config_group);
goto err;
}
clicon_debug(1, "Listen on server socket at %s", addr.sun_path);
if (listen(s, 5) < 0){
clicon_err(OE_UNIX, errno, "%s: listen", __FUNCTION__);
clicon_err(OE_UNIX, errno, "listen");
goto err;
}
return s;
@ -221,14 +220,14 @@ backend_accept_client(int fd,
clicon_debug(2, "%s", __FUNCTION__);
len = sizeof(from);
if ((s = accept(fd, (struct sockaddr*)&from, &len)) < 0){
clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__);
clicon_err(OE_UNIX, errno, "accept");
goto done;
}
#if defined(SO_PEERCRED)
/* fill in the user data structure */
clen = sizeof(credentials);
if(getsockopt(s, SOL_SOCKET, SO_PEERCRED/* XXX finns ej i freebsd*/, &credentials, &clen)){
clicon_err(OE_UNIX, errno, "%s: getsockopt", __FUNCTION__);
clicon_err(OE_UNIX, errno, "getsockopt");
goto done;
}
#endif

View file

@ -220,7 +220,7 @@ backend_notify_xml(clicon_handle h,
ce_next = ce->ce_next;
for (su = ce->ce_subscription; su; su = su->su_next)
if (strcmp(su->su_stream, stream) == 0){
if (strlen(su->su_filter)==0 || xpath_first(x, su->su_filter) != NULL){
if (strlen(su->su_filter)==0 || xpath_first(x, "%s", su->su_filter) != NULL){
if (cb==NULL){
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new");
@ -255,7 +255,7 @@ backend_notify_xml(clicon_handle h,
continue;
if (strcmp(hs->hs_stream, stream))
continue;
if (strlen(hs->hs_filter)==0 || xpath_first(x, hs->hs_filter) != NULL){
if (strlen(hs->hs_filter)==0 || xpath_first(x, "%s", hs->hs_filter) != NULL){
if ((*hs->hs_fn)(h, x, hs->hs_arg) < 0)
goto done;
}

View file

@ -78,7 +78,7 @@
/*! Register log notification stream
* @param[in] h Clicon handle
* @param[in] stream Event stream. CLICON is predefined, others are application-defined
* @param[in] filter Filter. For xml notification ie xpath: .[name=kalle]
* @param[in] filter Filter. For xml notification ie xpath: .[name="kalle"]
* @param[in] status 0 for stop, 1 to start
* @param[in] fn Callback function called when notification occurs
* @param[in] arg Argument to function note
@ -184,7 +184,7 @@ cli_signal_flush(clicon_handle h)
cli_signal_block (h);
}
/*! Modify xml database from a callback using xml key format strings
/*! Modify xml datastore from a callback using xml key format strings
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
@ -221,7 +221,7 @@ cli_dbxml(clicon_handle h,
cxobj *xb; /* body */
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element to be xml key format string", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires one element to be xml key format string");
goto done;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -283,8 +283,15 @@ cli_dbxml(clicon_handle h,
return retval;
}
/*! Set datastore xml entry
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
*/
int
cli_set(clicon_handle h, cvec *cvv, cvec *argv)
cli_set(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = 1;
@ -294,13 +301,16 @@ cli_set(clicon_handle h, cvec *cvv, cvec *argv)
done:
return retval;
}
int cli_setv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_set(h, vars, argv);
}
/*! Merge datastore xml entry
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
*/
int
cli_merge(clicon_handle h, cvec *cvv, cvec *argv)
cli_merge(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
@ -310,13 +320,16 @@ cli_merge(clicon_handle h, cvec *cvv, cvec *argv)
done:
return retval;
}
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_merge(h, vars, argv);
}
/*! Create datastore xml entry
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
*/
int
cli_create(clicon_handle h, cvec *cvv, cvec *argv)
cli_create(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
@ -326,11 +339,16 @@ cli_create(clicon_handle h, cvec *cvv, cvec *argv)
done:
return retval;
}
/*!
/*! Remove datastore xml entry
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
* @see cli_del
*/
int
cli_remove(clicon_handle h, cvec *cvv, cvec *argv)
cli_remove(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
@ -341,8 +359,15 @@ cli_remove(clicon_handle h, cvec *cvv, cvec *argv)
return retval;
}
/*! Delete datastore xml
* @param[in] h Clicon handle
* @param[in] cvv Vector of cli string and instantiated variables
* @param[in] argv Vector. First element xml key format string, eg "/aaa/%s"
*/
int
cli_del(clicon_handle h, cvec *cvv, cvec *argv)
cli_del(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = -1;
@ -352,11 +377,6 @@ cli_del(clicon_handle h, cvec *cvv, cvec *argv)
done:
return retval;
}
int cli_delv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_del(h, vars, argv);
}
/*! Set debug level on CLI client (not backend daemon)
* @param[in] h Clicon handle
@ -374,9 +394,9 @@ cli_debug_cli(clicon_handle h,
cg_var *cv;
int level;
if ((cv = cvec_find_var(vars, "level")) == NULL){
if ((cv = cvec_find(vars, "level")) == NULL){
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires either label var or single arg: 0|1", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires either label var or single arg: 0|1");
goto done;
}
cv = cvec_i(argv, 0);
@ -388,10 +408,6 @@ cli_debug_cli(clicon_handle h,
done:
return retval;
}
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_debug_cli(h, vars, argv);
}
/*! Set debug level on backend daemon (not CLI)
* @param[in] h Clicon handle
@ -409,9 +425,9 @@ cli_debug_backend(clicon_handle h,
cg_var *cv;
int level;
if ((cv = cvec_find_var(vars, "level")) == NULL){
if ((cv = cvec_find(vars, "level")) == NULL){
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires either label var or single arg: 0|1", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires either label var or single arg: 0|1");
goto done;
}
cv = cvec_i(argv, 0);
@ -422,11 +438,39 @@ cli_debug_backend(clicon_handle h,
done:
return retval;
}
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv)
/*! Set debug level on restconf daemon
* @param[in] h Clicon handle
* @param[in] vars If variable "level" exists, its integer value is used
* @param[in] arg Else use the integer value of argument
* @note The level is either what is specified in arg as int argument.
* _or_ if a 'level' variable is present in vars use that value instead.
*/
int
cli_debug_restconf(clicon_handle h,
cvec *vars,
cvec *argv)
{
return cli_debug_backend(h, vars, argv);
int retval = -1;
cg_var *cv;
int level;
if ((cv = cvec_find(vars, "level")) == NULL){
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "Requires either label var or single arg: 0|1");
goto done;
}
cv = cvec_i(argv, 0);
}
level = cv_int32_get(cv);
/* restconf daemon */
if (0) /* XXX notyet */
retval = clicon_rpc_debug(h, level);
done:
return retval;
}
/*! Set syntax mode
*/
int
@ -438,7 +482,7 @@ cli_set_mode(clicon_handle h,
char *str = NULL;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element to be cli mode", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires one element to be cli mode");
goto done;
}
str = cv_string_get(cvec_i(argv, 0));
@ -447,13 +491,10 @@ cli_set_mode(clicon_handle h,
done:
return retval;
}
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_set_mode(h, vars, argv);
}
/*! Start bash from cli callback
* XXX Application specific??
* XXX replace fprintf with clicon_err?
*/
int
cli_start_shell(clicon_handle h,
@ -470,7 +511,7 @@ cli_start_shell(clicon_handle h,
if ((pw = getpwuid(getuid())) == NULL){
fprintf(stderr, "%s: getpwuid: %s\n",
__FUNCTION__, strerror(errno));
__FUNCTION__, strerror(errno));
return -1;
}
if (chdir(pw->pw_dir) < 0){
@ -508,10 +549,6 @@ cli_start_shell(clicon_handle h,
return 0;
}
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_start_shell(h, vars, argv);
}
/*! Generic quit callback
*/
@ -523,10 +560,6 @@ cli_quit(clicon_handle h,
cligen_exiting_set(cli_cligen(h), 1);
return 0;
}
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_quit(h, vars, argv);
}
/*! Generic commit callback
* @param[in] argv No arguments expected
@ -544,10 +577,6 @@ cli_commit(clicon_handle h,
done:
return retval;
}
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_commit(h, vars, argv);
}
/*! Generic validate callback
*/
@ -564,10 +593,6 @@ cli_validate(clicon_handle h,
done:
return retval;
}
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_validate(h, vars, argv);
}
/*! Compare two dbs using XML. Write to file and run diff
*/
@ -647,7 +672,7 @@ compare_dbs(clicon_handle h,
int astext;
if (cvec_len(argv) > 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires 0 or 1 element. If given: astext flag 0|1", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires 0 or 1 element. If given: astext flag 0|1");
goto done;
}
if (cvec_len(argv))
@ -677,10 +702,6 @@ compare_dbs(clicon_handle h,
return retval;
}
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv)
{
return compare_dbs(h, vars, argv);
}
/*! Load a configuration file to candidate database
* Utility function used by cligen spec file
@ -692,7 +713,7 @@ int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv)
* @note file is assumed to have a dummy top-tag, eg <clicon></clicon>
* @code
* # cligen spec
* load file <name2:string>, load_config_filev("name2","merge");
* load file <name2:string>, load_config_file("name2","merge");
* @endcode
* @see save_config_file
*/
@ -730,7 +751,7 @@ load_config_file(clicon_handle h,
clicon_err(OE_PLUGIN, 0, "No such op: %s, expected merge or replace", opstr);
goto done;
}
if ((cv = cvec_find_var(cvv, varstr)) == NULL){
if ((cv = cvec_find(cvv, varstr)) == NULL){
clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr);
goto done;
}
@ -742,7 +763,7 @@ load_config_file(clicon_handle h,
}
/* Open and parse local file into xml */
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_UNIX, errno, "%s: open(%s)", __FUNCTION__, filename);
clicon_err(OE_UNIX, errno, "open(%s)", filename);
goto done;
}
if (xml_parse_file(fd, "</clicon>", NULL, &xt) < 0)
@ -772,10 +793,6 @@ load_config_file(clicon_handle h,
close(fd);
return ret;
}
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv)
{
return load_config_file(h, vars, argv);
}
/*! Copy database to local file
* Utility function used by cligen spec file
@ -808,9 +825,11 @@ save_config_file(clicon_handle h,
if (cvec_len(argv) != 2){
if (cvec_len(argv)==1)
clicon_err(OE_PLUGIN, 0, "%s: Got single argument:\"%s\". Expected \"<dbname>,<varname>\"", cv_string_get(cvec_i(argv,0)));
clicon_err(OE_PLUGIN, 0, "Got single argument:\"%s\". Expected \"<dbname>,<varname>\"",
cv_string_get(cvec_i(argv,0)));
else
clicon_err(OE_PLUGIN, 0, "%s: Got %d arguments. Expected: <dbname>,<varname>", cvec_len(argv));
clicon_err(OE_PLUGIN, 0, " Got %d arguments. Expected: <dbname>,<varname>",
cvec_len(argv));
goto done;
}
@ -822,7 +841,7 @@ save_config_file(clicon_handle h,
clicon_err(OE_PLUGIN, 0, "No such db name: %s", dbstr);
goto done;
}
if ((cv = cvec_find_var(cvv, varstr)) == NULL){
if ((cv = cvec_find(cvv, varstr)) == NULL){
clicon_err(OE_PLUGIN, 0, "No such var name: %s", varstr);
goto done;
}
@ -857,10 +876,6 @@ save_config_file(clicon_handle h,
fclose(f);
return retval;
}
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv)
{
return save_config_file(h, vars, argv);
}
/*! Delete all elements in a database
* Utility function used by cligen spec file
@ -874,7 +889,7 @@ delete_all(clicon_handle h,
int retval = -1;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element: dbname", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires one element: dbname");
goto done;
}
dbstr = cv_string_get(cvec_i(argv, 0));
@ -890,10 +905,6 @@ delete_all(clicon_handle h,
done:
return retval;
}
int delete_allv(clicon_handle h, cvec *vars, cvec *argv)
{
return delete_all(h, vars, argv);
}
/*! Discard all changes in candidate and replace with running
*/
@ -903,12 +914,8 @@ discard_changes(clicon_handle h,
cvec *argv)
{
return clicon_rpc_discard_changes(h);
}
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv)
{
return discard_changes(h, vars, argv);
}
}
/*! Copy from one database to another, eg running->startup
* @param[in] argv a string: "<db1> <db2>" Copy from db1 to db2
*/
@ -945,7 +952,7 @@ cli_notification_cb(int s,
if (clicon_msg_rcv(s, &reply, &eof) < 0)
goto done;
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
clicon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close");
close(s);
errno = ESHUTDOWN;
event_unreg_fd(s, cli_notification_cb);
@ -992,7 +999,7 @@ cli_notification_cb(int s,
* and <format> is XXX
* Example code: Start logging of mystream and show logs as xml
* @code
* cmd("comment"), cli_notifyv("mystream","1","xml");
* cmd("comment"), cli_notify("mystream","1","xml");
* @endcode
* XXX: format is a memory leak
*/
@ -1008,7 +1015,7 @@ cli_notify(clicon_handle h,
enum format_enum format = FORMAT_TEXT;
if (cvec_len(argv) != 2 && cvec_len(argv) != 3){
clicon_err(OE_PLUGIN, 0, "%s Requires arguments: <logstream> <status> [<format>]", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires arguments: <logstream> <status> [<format>]");
goto done;
}
stream = cv_string_get(cvec_i(argv, 0));
@ -1030,10 +1037,79 @@ cli_notify(clicon_handle h,
done:
return retval;
}
/* Backward compatible Set if you want to enable "v" cli callback functions,
* such as cli_setv()
* This was obsoleted in 3.7.
* @see include/clixon_custom.h
*/
#ifdef COMPAT_CLIV
int cli_setv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_set(h, vars, argv);
}
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_merge(h, vars, argv);
}
int cli_delv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_del(h, vars, argv);
}
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_debug_cli(h, vars, argv);
}
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_debug_backend(h, vars, argv);
}
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_set_mode(h, vars, argv);
}
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_start_shell(h, vars, argv);
}
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_quit(h, vars, argv);
}
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_commit(h, vars, argv);
}
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_validate(h, vars, argv);
}
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv)
{
return compare_dbs(h, vars, argv);
}
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv)
{
return load_config_file(h, vars, argv);
}
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv)
{
return save_config_file(h, vars, argv);
}
int delete_allv(clicon_handle h, cvec *vars, cvec *argv)
{
return delete_all(h, vars, argv);
}
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv)
{
return discard_changes(h, vars, argv);
}
int cli_notifyv(clicon_handle h, cvec *vars, cvec *argv)
{
return cli_notify(h, vars, argv);
}
#endif /* COMPAT_CLIV */
/*! Lock database
*
@ -1113,7 +1189,7 @@ cli_unlock(clicon_handle h,
* tovar: Name of variable containing name of object to copy to.
* @code
* cli spec:
* copy snd <n1:string> to <n2:string>, cli_copy_config("candidate", "/sender[%s=%s]", "from", "n1", "n2");
* copy snd <n1:string> to <n2:string>, cli_copy_config("candidate", "/sender[%s='%s']", "from", "n1", "n2");
* cli command:
* copy snd from to to
* @endcode
@ -1142,7 +1218,7 @@ cli_copy_config(clicon_handle h,
cxobj *xerr;
if (cvec_len(argv) != 5){
clicon_err(OE_PLUGIN, 0, "%s: Requires four elements: <db> <xpath> <keyname> <from> <to>", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires four elements: <db> <xpath> <keyname> <from> <to>");
goto done;
}
/* First argv argument: Database */
@ -1157,7 +1233,7 @@ cli_copy_config(clicon_handle h,
tovar = cv_string_get(cvec_i(argv, 4));
/* Get from variable -> cv -> from name */
if ((fromcv = cvec_find_var(cvv, fromvar)) == NULL){
if ((fromcv = cvec_find(cvv, fromvar)) == NULL){
clicon_err(OE_PLUGIN, 0, "fromvar '%s' not found in cligen var list", fromvar);
goto done;
}
@ -1168,17 +1244,34 @@ cli_copy_config(clicon_handle h,
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
/* Sanity check that xpath contains exactly one %s */
/* Sanity check that xpath contains exactly two %s, ie [%s='%s'] */
j = 0;
for (i=0; i<strlen(xpath); i++)
for (i=0; i<strlen(xpath); i++){
if (xpath[i] == '%')
j++;
#ifdef COMPAT_XSL
/* This is a horrible kludge due to:
* (1) old xpath implementation wrongly did: a[b=x] instead of a[b='x']
* (2) cli_copy_config has as 2nd argument such an xpath provided by user.
*/
if (j==2){
int k;
if ((xpath[i-1] == '\'' || xpath[i-1] == '\"') &&
(xpath[i+2] == '\'' || xpath[i+2] == '\"')){
for (k=i-1;k<i+2;k++)
xpath[k] = xpath[k+1];
for (k=i+1;k<strlen(xpath)+1;k++)
xpath[k] = xpath[k+2];
i-=1;
}
}
#endif
}
if (j != 2){
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have two '%%'");
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have two '%%'", xpath);
goto done;
}
cprintf(cb, xpath, keyname, fromname);
/* Get from object configuration and store in x1 */
if (clicon_rpc_get_config(h, db, cbuf_get(cb), &x1) < 0)
goto done;
@ -1188,7 +1281,7 @@ cli_copy_config(clicon_handle h,
}
/* Get to variable -> cv -> to name */
if ((tocv = cvec_find_var(cvv, tovar)) == NULL){
if ((tocv = cvec_find(cvv, tovar)) == NULL){
clicon_err(OE_PLUGIN, 0, "tovar '%s' not found in cligen var list", tovar);
goto done;
}
@ -1200,7 +1293,7 @@ cli_copy_config(clicon_handle h,
goto done;
xml_name_set(x2, "config");
cprintf(cb, "/%s", keyname);
if ((x = xpath_first(x2, cbuf_get(cb))) == NULL){
if ((x = xpath_first(x2, "%s", cbuf_get(cb))) == NULL){
clicon_err(OE_PLUGIN, 0, "Field %s not found in copy tree", keyname);
goto done;
}
@ -1237,7 +1330,7 @@ cli_debug(clicon_handle h,
cg_var *cv;
int level;
if ((cv = cvec_find_var(vars, "level")) == NULL)
if ((cv = cvec_find(vars, "level")) == NULL)
cv = arg;
level = cv_int32_get(cv);
/* cli */

View file

@ -141,6 +141,7 @@ cli_expand_var_generate(clicon_handle h,
* @param[in] ys yang_stmt of the node at hand
* @param[in] cb0 The string where the result format string is inserted.
* @see cli_dbxml This is where the xmlkeyfmt string is used
* @see pt_callback_reference in CLIgen where the actual callback overwrites the template
*/
static int
cli_callback_generate(clicon_handle h,
@ -204,20 +205,51 @@ yang2cli_var_sub(clicon_handle h,
}
type = ytype?ytype->ys_argument:NULL;
cvtypestr = cv_type2str(cvtype);
if (type && strcmp(type, "identityref") == 0)
cprintf(cb, "(");
cprintf(cb, "<%s:%s", ys->ys_argument, cvtypestr);
/* enumeration special case completion */
if (type && (strcmp(type, "enumeration") == 0 || strcmp(type, "bits") == 0)){
cprintf(cb, " choice:");
i = 0;
while ((yi = yn_each((yang_node*)ytype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT)
continue;
if (i)
cprintf(cb, "|");
cprintf(cb, "%s", yi->ys_argument);
i++;
if (type){
if (strcmp(type, "enumeration") == 0 || strcmp(type, "bits") == 0){
cprintf(cb, " choice:");
i = 0;
while ((yi = yn_each((yang_node*)ytype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT)
continue;
if (i)
cprintf(cb, "|");
cprintf(cb, "%s", yi->ys_argument);
i++;
}
}
else if (strcmp(type, "identityref") == 0){
yang_stmt *ybaseref;
yang_stmt *ybaseid;
cg_var *cv = NULL;
char *name;
char *id;
/* Add a wildchar string first -let validate take it for default prefix */
cprintf(cb, ">");
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
cprintf(cb, "|<%s:%s choice:", ys->ys_argument, cvtypestr);
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) != NULL &&
(ybaseid = yang_find_identity(ys, ybaseref->ys_argument)) != NULL){
i = 0;
while ((cv = cvec_each(ybaseid->ys_cvec, cv)) != NULL){
if (i++)
cprintf(cb, "|");
name = strdup(cv_name_get(cv));
if ((id=strchr(name, ':')) != NULL)
*id = '\0';
cprintf(cb, "%s:%s", name, id+1);
if (name)
free(name);
}
}
}
}
if (options & YANG_OPTIONS_FRACTION_DIGITS)
cprintf(cb, " fraction-digits:%u", fraction_digits);
if (options & (YANG_OPTIONS_RANGE|YANG_OPTIONS_LENGTH)){
@ -253,16 +285,18 @@ yang2cli_var_sub(clicon_handle h,
goto done;
}
}
cprintf(cb, "%s]", r);
cprintf(cb, "%s]", r); /* range */
free(r);
r = NULL;
}
if (options & YANG_OPTIONS_PATTERN)
cprintf(cb, " regexp:\"%s\"", pattern);
cprintf(cb, ">");
if (helptext)
cprintf(cb, "(\"%s\")", helptext);
if (type && strcmp(type, "identityref") == 0)
cprintf(cb, ")");
retval = 0;
done:
return retval;
@ -385,6 +419,7 @@ yang2cli_var(clicon_handle h,
enum cv_type cvtype;
int options = 0;
int completionp;
char *type;
if (yang_type_get(ys, &origtype, &yrestype,
&options, &mincv, &maxcv, &pattern, &fraction_digits) < 0)
@ -413,11 +448,11 @@ yang2cli_var(clicon_handle h,
cprintf(cb, ")");
}
else{
char *type;
type = yrestype?yrestype->ys_argument:NULL;
if (type)
completionp = clicon_cli_genmodel_completion(h) &&
strcmp(type, "enumeration") != 0 &&
strcmp(type, "identityref") != 0 &&
strcmp(type, "bits") != 0;
else
completionp = clicon_cli_genmodel_completion(h);
@ -748,7 +783,7 @@ yang2cli(clicon_handle h,
cvec *globals; /* global variables from syntax */
if ((cbuf = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
/* Traverse YANG specification: loop through statements */

View file

@ -71,7 +71,7 @@
#include "cli_handle.h"
/* Command line options to be passed to getopt(3) */
#define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:"
#define CLI_OPTS "hD:f:xl:F:1u:d:m:qpGLy:c:U:"
/*! terminate cli application */
static int
@ -108,6 +108,8 @@ cli_signal_init (clicon_handle h)
/*! Interactive CLI command loop
* @param[in] h CLICON handle
* @retval 0
* @retval -1
* @see cligen_loop
*/
static int
@ -117,17 +119,16 @@ cli_interactive(clicon_handle h)
int res;
char *cmd;
char *new_mode;
int result;
int eval;
/* Loop through all commands */
while(!cligen_exiting(cli_cligen(h))) {
new_mode = cli_syntax_mode(h);
if ((cmd = clicon_cliread(h)) == NULL) {
cligen_exiting_set(cli_cligen(h), 1); /* EOF */
retval = -1;
goto done;
}
if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0)
if ((res = clicon_parse(h, cmd, &new_mode, &eval)) < 0)
goto done;
}
retval = 0;
@ -172,7 +173,7 @@ dump_configfile_xml_fn(FILE *fout,
clicon_err(OE_UNIX, errno, "configure file: %s", filename);
return -1;
}
clicon_debug(2, "Reading config file %s", __FUNCTION__, filename);
clicon_debug(2, "%s: Reading config file %s", __FUNCTION__, filename);
fprintf(fout, "<config>\n");
while (fgets(line, sizeof(line), f)) {
if ((cp = strchr(line, '\n')) != NULL) /* strip last \n */
@ -193,7 +194,8 @@ dump_configfile_xml_fn(FILE *fout,
}
static void
usage(char *argv0, clicon_handle h)
usage(clicon_handle h,
char *argv0)
{
char *confsock = clicon_sock(h);
char *plgdir = clicon_cli_dir(h);
@ -216,7 +218,8 @@ usage(char *argv0, clicon_handle h)
"\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n"
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n"
"\t-c <file>\tSpecify cli spec file.\n",
"\t-c <file>\tSpecify cli spec file.\n"
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n",
argv0,
confsock ? confsock : "none",
plgdir ? plgdir : "none"
@ -229,6 +232,7 @@ usage(char *argv0, clicon_handle h)
int
main(int argc, char **argv)
{
int retval = -1;
char c;
int once;
char *tmp;
@ -254,7 +258,9 @@ main(int argc, char **argv)
/* Initiate CLICON handle */
if ((h = cli_handle_init()) == NULL)
goto done;
/* Set username to clicon handle. Use in all communication to backend */
/* Set username to clicon handle. Use in all communication to backend
* Note, can be overridden by -U
*/
if ((pw = getpwuid(getuid())) == NULL){
clicon_err(OE_UNIX, errno, "getpwuid");
goto done;
@ -281,11 +287,11 @@ main(int argc, char **argv)
break;
case 'D' : /* debug */
if (sscanf(optarg, "%d", &debug) != 1)
usage(argv[0], h);
usage(h, argv[0]);
break;
case 'f': /* config file */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'x': /* dump config file as xml (migration from .conf file)*/
@ -303,9 +309,9 @@ main(int argc, char **argv)
logdst = CLICON_LOG_STDOUT;
break;
default:
usage(argv[0], h);
usage(h, argv[0]);
}
break;
break;
}
/*
* Logs, error and debug to stderr or syslog, set debug level
@ -325,7 +331,7 @@ main(int argc, char **argv)
/* Find and read configfile */
if (clicon_options_main(h) < 0){
if (help)
usage(argv[0], h);
usage(h, argv[0]);
return -1;
}
@ -350,17 +356,17 @@ main(int argc, char **argv)
break;
case 'u': /* config unix domain path/ ip host */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'd': /* Plugin directory: overrides configfile */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CLI_DIR", optarg);
break;
case 'm': /* CLI syntax mode */
if (!strlen(optarg))
usage(argv[0], h);
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CLI_MODE", optarg);
break;
case 'q' : /* Quiet mode */
@ -383,8 +389,14 @@ main(int argc, char **argv)
clicon_option_str_set(h, "CLICON_CLISPEC_FILE", optarg);
break;
}
case 'U': /* Clixon 'pseudo' user */
if (!strlen(optarg))
usage(h, argv[0]);
if (clicon_username_set(h, optarg) < 0)
goto done;
break;
default:
usage(argv[0], h);
usage(h, argv[0]);
break;
}
}
@ -393,7 +405,7 @@ main(int argc, char **argv)
/* Defer: Wait to the last minute to print help message */
if (help)
usage(argv[0], h);
usage(h, argv[0]);
/* Setup signal handlers */
cli_signal_init(h);
@ -445,7 +457,7 @@ main(int argc, char **argv)
}
if (!cli_syntax_mode(h)){
fprintf (stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n");
fprintf(stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n");
goto done;
}
if (cligen_tree_find(cli_cligen(h), cli_syntax_mode(h)) == NULL)
@ -476,11 +488,19 @@ main(int argc, char **argv)
if (restarg != NULL && strlen(restarg)){
char *mode = cli_syntax_mode(h);
int result;
clicon_parse(h, restarg, &mode, &result);
/* */
if (clicon_parse(h, restarg, &mode, &result) != 1){
goto done;
}
if (result < 0)
goto done;
}
/* Go into event-loop unless -1 command-line */
if (!once)
cli_interactive(h);
retval = cli_interactive(h);
else
retval = 0;
done:
if (treename)
free(treename);
@ -491,6 +511,5 @@ main(int argc, char **argv)
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
if (h)
cli_terminate(h);
return 0;
return retval;
}

View file

@ -97,7 +97,7 @@ syntax_mode_find(cli_syntax_t *stx,
perror("malloc");
return NULL;
}
memset (m, 0, sizeof (*m));
memset(m, 0, sizeof(*m));
strncpy(m->csm_name, mode, sizeof(m->csm_name)-1);
strncpy(m->csm_prompt, CLI_DEFAULT_PROMPT, sizeof(m->csm_prompt)-1);
INSQ(m, stx->stx_modes);
@ -263,7 +263,7 @@ cli_load_syntax(clicon_handle h,
if ((cp = clixon_plugin_find(h, plgnam)) != NULL)
handle = cp->cp_handle;
if (handle == NULL){
clicon_err(OE_PLUGIN, 0, "CLICON_PLUGIN set to '%s' in %s but plugin %s.so not found in %s\n",
clicon_err(OE_PLUGIN, 0, "CLICON_PLUGIN set to '%s' in %s but plugin %s.so not found in %s",
plgnam, filename, plgnam,
clicon_cli_dir(h));
goto done;
@ -277,6 +277,9 @@ cli_load_syntax(clicon_handle h,
}
if (cligen_expandv_str2fn(pt, (expandv_str2fn_t*)clixon_str2fn, handle) < 0)
goto done;
/* Variable translation functions */
if (cligen_translate_str2fn(pt, (translate_str2fn_t*)clixon_str2fn, handle) < 0)
goto done;
/* Make sure we have a syntax mode specified */
if (mode == NULL || strlen(mode) < 1) { /* may be null if not given in file */
@ -311,7 +314,7 @@ done:
* @param[in] h Clicon handle
*/
int
cli_syntax_load (clicon_handle h)
cli_syntax_load(clicon_handle h)
{
int retval = -1;
char *plugin_dir = NULL;
@ -340,7 +343,7 @@ cli_syntax_load (clicon_handle h)
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset (stx, 0, sizeof (*stx)); /* Zero out all */
memset(stx, 0, sizeof(*stx)); /* Zero out all */
cli_syntax_set(h, stx);
@ -441,6 +444,8 @@ cli_handler_err(FILE *f)
/*! Evaluate a matched command
* @param[in] h Clicon handle
* @param[in] cmd The command string
* @retval int If there is a callback, the return value of the callback is returned,
* @retval 0 otherwise
*/
int
clicon_eval(clicon_handle h,
@ -448,10 +453,12 @@ clicon_eval(clicon_handle h,
cg_obj *match_obj,
cvec *cvv)
{
int retval = 0;
cli_output_reset();
if (!cligen_exiting(cli_cligen(h))) {
clicon_err_reset();
if (cligen_eval(cli_cligen(h), match_obj, cvv) < 0) {
if ((retval = cligen_eval(cli_cligen(h), match_obj, cvv)) < 0) {
#if 0 /* This is removed since we get two error messages on failure.
But maybe only sometime?
Both a real log when clicon_err is called, and the here again.
@ -460,11 +467,10 @@ clicon_eval(clicon_handle h,
#endif
}
}
return 0;
return retval;
}
/*! Given a command string, parse and evaluate.
/*! Given a command string, parse and if match single command, eval it.
* Parse and evaluate the string according to
* the syntax parse tree of the syntax mode specified by *mode.
* If there is no match in the tree for the command, the parse hook
@ -475,19 +481,25 @@ clicon_eval(clicon_handle h,
* @param[in] h Clicon handle
* @param[in] cmd Command string
* @param[in,out] modenamep Pointer to the mode string pointer
* @param[out] result -2 On eof (shouldnt happen)
* @param[out] evalres Evaluation result if retval=1
* -2 On eof (shouldnt happen)
* -1 On parse error
* >=0 Number of matches
* @retval -2 Eof CG_EOF
* @retval -1 Error CG_ERROR
* @retval 0 No match CG_NOMATCH
* @retval 1 Exactly one match CG_MATCH
* @retval 2+ Multiple matches
*/
int
clicon_parse(clicon_handle h,
char *cmd,
char **modenamep,
int *result)
int *evalres)
{
int retval = -1;
char *modename;
char *modename0;
int res = -1;
int r;
cli_syntax_t *stx = NULL;
cli_syntaxmode_t *smode;
@ -508,10 +520,10 @@ clicon_parse(clicon_handle h,
else {
if ((smode = syntax_mode_find(stx, modename, 0)) == NULL) {
cli_output(f, "Can't find syntax mode '%s'\n", modename);
return -1;
goto done;
}
}
while(smode) {
if (smode){
modename0 = NULL;
if ((pt = cligen_tree_active_get(cli_cligen(h))) != NULL)
modename0 = pt->pt_name;
@ -527,20 +539,19 @@ clicon_parse(clicon_handle h,
clicon_err(OE_UNIX, errno, "cvec_new");
goto done;;
}
res = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
if (res != CG_MATCH)
retval = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
if (retval != CG_MATCH)
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
if (modename0){
cligen_tree_active_set(cli_cligen(h), modename0);
modename0 = NULL;
}
switch (res) {
switch (retval) {
case CG_EOF: /* eof */
case CG_ERROR:
cli_output(f, "CLI parse error: %s\n", cmd);
goto done;
break;
case CG_NOMATCH: /* no match */
smode = NULL;
/* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s",
cmd, cli_nomatch(h));*/
cli_output(f, "CLI syntax error: \"%s\": %s\n",
@ -554,20 +565,18 @@ clicon_parse(clicon_handle h,
if ((r = clicon_eval(h, cmd, match_obj, cvv)) < 0)
cli_handler_err(stdout);
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
if (result)
*result = r;
goto done;
if (evalres)
*evalres = r;
break;
default:
cli_output(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd);
goto done;
break;
}
} /* switch retval */
}
done:
if (cvv)
cvec_free(cvv);
return res;
return retval;
}
/*! Read command from CLIgen's cliread() using current syntax mode.
@ -667,9 +676,9 @@ cli_set_prompt(clicon_handle h,
* @param[in] fmt Stdarg fmt string
*/
static int
prompt_fmt (char *prompt,
size_t plen,
char *fmt, ...)
prompt_fmt(char *prompt,
size_t plen,
char *fmt, ...)
{
va_list ap;
char *s = fmt;
@ -689,7 +698,7 @@ prompt_fmt (char *prompt,
if (*s == '%' && *++s) {
switch(*s) {
case 'H': /* Hostname */
if (gethostname (hname, sizeof (hname)) != 0)
if (gethostname(hname, sizeof(hname)) != 0)
strncpy(hname, "unknown", sizeof(hname)-1);
cprintf(cb, "%s", hname);
break;

View file

@ -123,8 +123,7 @@ expand_dbvar(void *h,
char *reason = NULL;
if (argv == NULL || cvec_len(argv) != 2){
clicon_err(OE_PLUGIN, 0, "%s: requires arguments: <db> <xmlkeyfmt>",
__FUNCTION__);
clicon_err(OE_PLUGIN, 0, "requires arguments: <db> <xmlkeyfmt>");
goto done;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -132,7 +131,7 @@ expand_dbvar(void *h,
goto done;
}
if ((cv = cvec_i(argv, 0)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <db>");
clicon_err(OE_PLUGIN, 0, "Error when accessing argument <db>");
goto done;
}
dbstr = cv_string_get(cv);
@ -143,13 +142,13 @@ expand_dbvar(void *h,
goto done;
}
if ((cv = cvec_i(argv, 1)) == NULL){
clicon_err(OE_PLUGIN, 0, "%s: Error when accessing argument <api_path>");
clicon_err(OE_PLUGIN, 0, "Error when accessing argument <api_path>");
goto done;
}
api_path_fmt = cv_string_get(cv);
/* api_path_fmt = /interface/%s/address/%s
--> ^/interface/eth0/address/.*$
--> /interface/[name=eth0]/address
--> /interface/[name="eth0"]/address
*/
if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0)
goto done;
@ -161,7 +160,7 @@ expand_dbvar(void *h,
goto done;
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
goto ok;
}
xcur = xt; /* default top-of-tree */
xpathcur = xpath;
@ -197,7 +196,7 @@ expand_dbvar(void *h,
cli_output(stderr, "%s\n", reason);
goto done;
}
if ((xcur = xpath_first(xt, xpath)) == NULL){
if ((xcur = xpath_first(xt, "%s", xpath)) == NULL){
clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath);
goto done;
}
@ -205,7 +204,7 @@ expand_dbvar(void *h,
/* One round to detect duplicates
*/
j = 0;
if (xpath_vec(xcur, xpathcur, &xvec, &xlen) < 0)
if (xpath_vec(xcur, "%s", &xvec, &xlen, xpathcur) < 0)
goto done;
for (i = 0; i < xlen; i++) {
char *str;
@ -409,10 +408,6 @@ show_yang(clicon_handle h,
yang_print(stdout, yn);
return 0;
}
int show_yangv(clicon_handle h, cvec *vars, cvec *argv)
{
return show_yang(h, vars, argv);
}
/*! Generic show configuration CLIGEN callback
* Utility function used by cligen spec file
@ -421,11 +416,11 @@ int show_yangv(clicon_handle h, cvec *vars, cvec *argv)
* @param[in] argv String vector: <dbname> <format> <xpath> [<varname>]
* Format of argv:
* <dbname> "running"|"candidate"|"startup"
* <dbname> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
* <xpath> xpath expression, that may contain one %, eg "/sender[name=%s]"
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
* <xpath> xpath expression, that may contain one %, eg "/sender[name="%s"]"
* <varname> optional name of variable in cvv. If set, xpath must have a '%s'
* @code
* show config id <n:string>, cli_show_config("running","xml","iface[name=%s]","n");
* show config id <n:string>, cli_show_config("running","xml","iface[name="%s"]","n");
* @endcode
*/
int
@ -478,10 +473,11 @@ cli_show_config(clicon_handle h,
if (xpath[i] == '%')
j++;
if (j != 1){
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have a single '%%'");
clicon_err(OE_PLUGIN, 0, "xpath '%s' does not have a single '%%'",
xpath);
goto done;
}
if ((cvattr = cvec_find_var(cvv, attr)) == NULL){
if ((cvattr = cvec_find(cvv, attr)) == NULL){
clicon_err(OE_PLUGIN, 0, "attr '%s' not found in cligen var list", attr);
goto done;
}
@ -565,7 +561,7 @@ show_conf_xpath(clicon_handle h,
int i;
if (cvec_len(argv) != 1){
clicon_err(OE_PLUGIN, 0, "%s: Requires one element to be <dbname>", __FUNCTION__);
clicon_err(OE_PLUGIN, 0, "Requires one element to be <dbname>");
goto done;
}
str = cv_string_get(cvec_i(argv, 0));
@ -576,7 +572,7 @@ show_conf_xpath(clicon_handle h,
clicon_err(OE_PLUGIN, 0, "No such db name: %s", str);
goto done;
}
cv = cvec_find_var(cvv, "xpath");
cv = cvec_find(cvv, "xpath");
xpath = cv_string_get(cv);
if (clicon_rpc_get_config(h, str, xpath, &xt) < 0)
goto done;
@ -584,7 +580,7 @@ show_conf_xpath(clicon_handle h,
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
if (xpath_vec(xt, xpath, &xv, &xlen) < 0)
if (xpath_vec(xt, "%s", &xv, &xlen, xpath) < 0)
goto done;
for (i=0; i<xlen; i++)
xml_print(stdout, xv[i]);
@ -598,13 +594,105 @@ done:
return retval;
}
int show_confv_xpath(clicon_handle h, cvec *vars, cvec *argv)
{
return show_conf_xpath(h, vars, argv);
}
int cli_show_version(clicon_handle h, cvec *vars, cvec *argv)
{
cli_output(stdout, "%s\n", CLIXON_VERSION_STRING);
return 0;
}
/*! Generic show configuration CLIGEN callback using generated CLI syntax
* Format of argv:
* <api_path_fmt> Generated API PATH
* <dbname> "running"|"candidate"|"startup"
* <format> "text"|"xml"|"json"|"cli"|"netconf" (see format_enum)
*/
int
cli_show_auto(clicon_handle h,
cvec *cvv,
cvec *argv)
{
int retval = 1;
yang_spec *yspec;
char *api_path_fmt; /* xml key format */
// char *api_path = NULL; /* xml key */
char *db;
char *xpath;
char *formatstr;
enum format_enum format = FORMAT_XML;
cxobj *xt = NULL;
cxobj *xp;
cxobj *xerr;
enum genmodel_type gt;
if (cvec_len(argv) != 3){
clicon_err(OE_PLUGIN, 0, "Usage: <api-path-fmt>* <database> <format>. (*) generated.");
goto done;
}
/* First argv argument: API_path format */
api_path_fmt = cv_string_get(cvec_i(argv, 0));
/* Second argv argument: Database */
db = cv_string_get(cvec_i(argv, 1));
/* Third format: output format */
formatstr = cv_string_get(cvec_i(argv, 2));
if ((format = format_str2int(formatstr)) < 0){
clicon_err(OE_PLUGIN, 0, "Not valid format: %s", formatstr);
goto done;
}
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
goto done;
}
// if (api_path_fmt2api_path(api_path_fmt, cvv, &api_path) < 0)
// goto done;
if (api_path_fmt2xpath(api_path_fmt, cvv, &xpath) < 0)
goto done;
/* Get configuration from database */
if (clicon_rpc_get_config(h, db, xpath, &xt) < 0)
goto done;
if ((xerr = xpath_first(xt, "/rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
if ((xp = xpath_first(xt, "%s", xpath)) != NULL)
/* Print configuration according to format */
switch (format){
case FORMAT_XML:
clicon_xml2file(stdout, xp, 0, 1);
break;
case FORMAT_JSON:
xml2json(stdout, xp, 1);
break;
case FORMAT_TEXT:
xml2txt(stdout, xp, 0); /* tree-formed text */
break;
case FORMAT_CLI:
if ((gt = clicon_cli_genmodel_type(h)) == GT_ERR)
goto done;
xml2cli(stdout, xp, NULL, gt); /* cli syntax */
break;
case FORMAT_NETCONF:
fprintf(stdout, "<rpc><edit-config><target><candidate/></target><config>\n");
clicon_xml2file(stdout, xp, 2, 1);
fprintf(stdout, "</config></edit-config></rpc>]]>]]>\n");
break;
default: /* see cli_show_config() */
break;
}
retval = 0;
done:
if (xt)
xml_free(xt);
return retval;
}
#ifdef COMPAT_CLIV
int show_yangv(clicon_handle h, cvec *vars, cvec *argv)
{
return show_yang(h, vars, argv);
}
int show_confv_xpath(clicon_handle h, cvec *vars, cvec *argv)
{
return show_conf_xpath(h, vars, argv);
}
#endif /* COMPAT_CLIV */

View file

@ -72,55 +72,51 @@ int cli_notification_register(clicon_handle h, char *stream, enum format_enum fo
int cli_set(clicon_handle h, cvec *vars, cvec *argv);
int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
int cli_merge(clicon_handle h, cvec *vars, cvec *argv);
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
int cli_create(clicon_handle h, cvec *vars, cvec *argv);
int cli_remove(clicon_handle h, cvec *vars, cvec *argv);
int cli_del(clicon_handle h, cvec *vars, cvec *argv);
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cli(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_backend(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_restconf(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_mode(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv);
int cli_start_shell(clicon_handle h, cvec *vars, cvec *argv);
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv);
int cli_quit(clicon_handle h, cvec *vars, cvec *argv);
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_commit(clicon_handle h, cvec *vars, cvec *argv);
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_validate(clicon_handle h, cvec *vars, cvec *argv);
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv);
int compare_dbs(clicon_handle h, cvec *vars, cvec *argv);
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv);
int load_config_file(clicon_handle h, cvec *vars, cvec *argv);
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int save_config_file(clicon_handle h, cvec *vars, cvec *argv);
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int delete_all(clicon_handle h, cvec *vars, cvec *argv);
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
int discard_changes(clicon_handle h, cvec *vars, cvec *argv);
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
int cli_notify(clicon_handle h, cvec *cvv, cvec *argv);
int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv);
int db_copy(clicon_handle h, cvec *cvv, cvec *argv);
@ -139,11 +135,34 @@ int expandv_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
/* cli_show.c: CLIgen new vector arg callbacks */
int show_yang(clicon_handle h, cvec *vars, cvec *argv);
int show_yangv(clicon_handle h, cvec *vars, cvec *argv);
int show_conf_xpath(clicon_handle h, cvec *cvv, cvec *argv);
int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv);
int cli_show_config(clicon_handle h, cvec *cvv, cvec *argv);
int cli_show_auto(clicon_handle h, cvec *cvv, cvec *argv);
#ifdef COMPAT_CLIV
int cli_setv(clicon_handle h, cvec *vars, cvec *argv);
int cli_mergev(clicon_handle h, cvec *vars, cvec *argv);
int cli_delv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_cliv(clicon_handle h, cvec *vars, cvec *argv);
int cli_debug_backendv(clicon_handle h, cvec *vars, cvec *argv);
int cli_set_modev(clicon_handle h, cvec *vars, cvec *argv);
int cli_start_shellv(clicon_handle h, cvec *vars, cvec *argv);
int cli_quitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_commitv(clicon_handle h, cvec *vars, cvec *argv);
int cli_validatev(clicon_handle h, cvec *vars, cvec *argv);
int compare_dbsv(clicon_handle h, cvec *vars, cvec *argv);
int load_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int save_config_filev(clicon_handle h, cvec *vars, cvec *argv);
int delete_allv(clicon_handle h, cvec *vars, cvec *argv);
int discard_changesv(clicon_handle h, cvec *vars, cvec *argv);
int cli_notifyv(clicon_handle h, cvec *cvv, cvec *argv);
int show_yangv(clicon_handle h, cvec *vars, cvec *argv);
int show_confv_xpath(clicon_handle h, cvec *cvv, cvec *argv);
#endif /* COMPAT_CLIV */
#endif /* _CLIXON_CLI_API_H_ */

View file

@ -82,12 +82,15 @@
static int
netconf_hello(cxobj *xn)
{
#ifdef nyi
cxobj *x;
x = NULL;
while ((x = xpath_each(xn, "//capability", x)) != NULL) {
//fprintf(stderr, "cap: %s\n", xml_body(x));
}
#endif
return 0;
}

View file

@ -164,7 +164,7 @@ netconf_get_target(cxobj *xn,
cxobj *x;
char *target = NULL;
if ((x = xpath_first(xn, path)) != NULL){
if ((x = xpath_first(xn, "%s", path)) != NULL){
if (xpath_first(x, "candidate") != NULL)
target = "candidate";
else

View file

@ -71,7 +71,7 @@
#include "netconf_rpc.h"
/* Command line options to be passed to getopt(3) */
#define NETCONF_OPTS "hDqf:d:Sy:"
#define NETCONF_OPTS "hDqf:d:Sy:U:"
/*! Process incoming packet
* @param[in] h Clicon handle
@ -134,6 +134,12 @@ process_incoming_packet(clicon_handle h,
if ((cbret = cbuf_new()) != NULL){
if ((xc = xml_child_i(xret,0))!=NULL){
xa=NULL;
/* Copy message-id attribute from incoming to reply.
* RFC 6241:
* If additional attributes are present in an <rpc> element, a NETCONF
* peer MUST return them unmodified in the <rpc-reply> element. This
* includes any "xmlns" attributes.
*/
while ((xa = xml_child_each(xrpc, xa, CX_ATTR)) != NULL){
if ((xa2 = xml_dup(xa)) ==NULL)
goto done;
@ -182,7 +188,7 @@ netconf_input_cb(int s,
int poll;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_XML, errno, "cbuf_new");
return retval;
}
memset(buf, 0, sizeof(buf));
@ -292,7 +298,8 @@ usage(clicon_handle h,
"\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"
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n",
"\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n"
"\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n",
argv0,
clicon_netconf_dir(h)
);
@ -379,6 +386,12 @@ main(int argc,
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", optarg);
break;
}
case 'U': /* Clixon 'pseudo' user */
if (!strlen(optarg))
usage(h, argv[0]);
if (clicon_username_set(h, optarg) < 0)
goto done;
break;
default:
usage(h, argv[0]);
break;

View file

@ -752,7 +752,7 @@ netconf_notification_cb(int s,
goto done;
/* handle close from remote end: this will exit the client */
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
clicon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close");
close(s);
errno = ESHUTDOWN;
event_unreg_fd(s, netconf_notification_cb);
@ -770,13 +770,13 @@ netconf_notification_cb(int s,
/* find and apply filter */
if ((selector = xml_find_value(xfilter, "select")) == NULL)
goto done;
if (xpath_first(xe, selector) == NULL) {
if (xpath_first(xe, "%s", selector) == NULL) {
fprintf(stderr, "%s no match\n", __FUNCTION__); /* debug */
}
}
/* create netconf message */
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
add_preamble(cb); /* Make it well-formed netconf xml */
@ -888,12 +888,19 @@ netconf_application_rpc(clicon_handle h,
goto done;
}
cbuf_reset(cb);
// if (xml_namespace(xn))
cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn));
// else
// cprintf(cb, "/%s", xml_name(xn)); /* XXX not accepdted by below */
if (xml_namespace(xn) == NULL){
xml_parse_va(xret, NULL, "<rpc-reply><rpc-error>"
"<error-tag>operation-failed</error-tag>"
"<error-type>rpc</error-type>"
"<error-severity>error</error-severity>"
"<error-message>%s</error-message>"
"<error-info>Not recognized</error-info>"
"</rpc-error></rpc-reply>", xml_name(xn));
goto ok;
}
cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn));
/* Find yang rpc statement, return yang rpc statement if found */
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), Y_RPC, &yrpc) < 0)
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), Y_RPC, &yrpc) < 0)
goto done;
/* Check if found */
if (yrpc != NULL){
@ -937,6 +944,7 @@ netconf_application_rpc(clicon_handle h,
retval = 1; /* handled by callback */
goto done;
}
ok:
retval = 0;
done:
if (cb)
@ -964,12 +972,26 @@ netconf_rpc_dispatch(clicon_handle h,
int retval = -1;
cxobj *xe;
yang_spec *yspec = NULL;
char *username;
cxobj *xa;
/* Check incoming RPC against system / netconf RPC:s */
if ((yspec = clicon_netconf_yang(h)) == NULL){
clicon_err(OE_YANG, ENOENT, "No netconf yang spec");
goto done;
}
/* Tag username on all incoming requests in case they are forwarded as internal messages
* This may be unecesary since not all are forwarded.
* It may even be wrong if something else is done with the incoming message?
*/
if ((username = clicon_username_get(h)) != NULL){
if ((xa = xml_new("username", xn, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (xml_value_set(xa, username) < 0)
goto done;
}
xe = NULL;
while ((xe = xml_child_each(xn, xe, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(xe), "get-config") == 0){
@ -1047,5 +1069,8 @@ netconf_rpc_dispatch(clicon_handle h,
}
retval = 0;
done:
/* Username attribute added at top - otherwise it is returned to sender */
if ((xa = xml_find(xn, "username")) != NULL)
xml_purge(xa);
return retval;
}

View file

@ -2,7 +2,12 @@
### Installation using Nginx
Define nginx config file/etc/nginx/sites-available/default
Ensure www-data is member of the CLICON_SOCK_GROUP (default clicon). If not, add it:
```
sudo usermod -a -G clicon www-data
```
Define nginx config file: /etc/nginx/sites-available/default
```
server {
...
@ -17,6 +22,11 @@ Start nginx daemon
```
sudo /etc/init.d nginx start
```
Alternatively, start it via systemd:
```
sudo /etc/init.d/nginx start
sudo systemctl start start.service
```
Start clixon restconf daemon
```

View file

@ -95,6 +95,9 @@ Mapping netconf error-tag -> status code
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -252,9 +255,9 @@ api_data_get2(clicon_handle h,
}
}
else{
if (xpath_vec(xret, path, &xvec, &xlen) < 0)
if (xpath_vec(xret, "%s", &xvec, &xlen, path) < 0)
goto done;
clicon_debug(1, "%s: xpath:%s xlen:%d", __FUNCTION__, path, xlen);
clicon_debug(1, "%s: xpath:%s xlen:%d", __FUNCTION__, path, (int)xlen);
if (use_xml){
for (i=0; i<xlen; i++){
x = xvec[i];
@ -925,8 +928,7 @@ api_operations_get(clicon_handle h,
yang_spec *yspec;
yang_stmt *ym;
yang_stmt *yc;
yang_stmt *yprefix;
char *prefix;
char *modname;
cbuf *cbx = NULL;
cxobj *xt = NULL;
@ -937,15 +939,12 @@ api_operations_get(clicon_handle h,
cprintf(cbx, "<operations>");
ym = NULL;
while ((ym = yn_each((yang_node*)yspec, ym)) != NULL) {
if ((yprefix = yang_find((yang_node*)ym, Y_PREFIX, NULL)) != NULL)
prefix = yprefix->ys_argument;
else
continue;
modname = ym->ys_argument;
yc = NULL;
while ((yc = yn_each((yang_node*)ym, yc)) != NULL) {
if (yc->ys_keyword != Y_RPC)
continue;
cprintf(cbx, "<%s:%s />", prefix, yc->ys_argument);
cprintf(cbx, "<%s:%s />", modname, yc->ys_argument);
}
}
cprintf(cbx, "</operations>");
@ -1030,6 +1029,9 @@ api_operations_post(clicon_handle h,
char *username;
cbuf *cbret = NULL;
int ret = 0;
char *prefix = NULL;
char *id = NULL;
yang_stmt *ys = NULL;
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -1047,9 +1049,28 @@ api_operations_post(clicon_handle h,
}
clicon_debug(1, "%s oppath: %s", __FUNCTION__, oppath);
/* Find yang rpc statement, return yang rpc statement if found */
if (yang_abs_schema_nodeid(yspec, oppath, Y_RPC, &yrpc) < 0)
/* Find yang rpc statement, return yang rpc statement if found
* POST {+restconf}/operations/<operation>
*
* The <operation> field identifies the module name and rpc identifier
* string for the desired operation.
*/
if (yang_nodeid_split(oppath+1, &prefix, &id) < 0) /* +1 skip / */
goto done;
if ((ys = yang_find((yang_node*)yspec, Y_MODULE, prefix)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang module not found") < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
if ((yrpc = yang_find((yang_node*)ys, Y_RPC, id)) == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
goto done;
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
goto done;
goto ok;
}
if (yrpc == NULL){
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
goto done;
@ -1224,6 +1245,10 @@ api_operations_post(clicon_handle h,
retval = 0;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (prefix)
free(prefix);
if (id)
free(id);
if (xdata)
xml_free(xdata);
if (xtop)

View file

@ -1,46 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the Apache License version 2 or the GPL.
#
# ***** END LICENSE BLOCK *****
#
#
# Include this file in your application Makefile using eg:
# -include $(datarootdir)/clixon/clixon.mk
# then you can use the DIRS below in your install rules.
# You also get rules for the application configure file.
# NOTE: APPNAME must be defined in the local Makefile
clixon_DBSPECDIR=prefix/share/$(APPNAME)
clixon_SYSCONFDIR=sysconfdir
clixon_LOCALSTATEDIR=localstatedir/$(APPNAME)
clixon_LIBDIR=libdir/$(APPNAME)
clixon_DATADIR=datadir/clixon # for system yang files

164
configure vendored
View file

@ -621,6 +621,7 @@ ac_includes_default="\
ac_subst_vars='LTLIBOBJS
LIBOBJS
CLIXON_DATADIR
EGREP
GREP
LEXLIB
@ -634,12 +635,12 @@ EXEEXT
ac_ct_CC
with_restconf
RANLIB
AR
EXE_SUFFIX
SH_SUFFIX
AR_SUFFIX
OBJ_SUFFIX
INSTALLFLAGS
INSTALL
INSTALL_DATA
INSTALL_SCRIPT
INSTALL_PROGRAM
CPPFLAGS
INCLUDES
LDFLAGS
@ -705,6 +706,7 @@ SHELL'
ac_subst_files=''
ac_user_opts='
enable_option_checking
enable_debug
with_cligen
with_restconf
with_configfile
@ -1339,6 +1341,12 @@ if test -n "$ac_init_help"; then
cat <<\_ACEOF
Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-debug Build with debug symbols, default: no
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
@ -2147,10 +2155,11 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="3"
CLIXON_VERSION_MINOR="6"
CLIXON_VERSION_PATCH="1"
CLIXON_VERSION_MINOR="7"
CLIXON_VERSION_PATCH="0"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
# Fix to specific version (eg 3.5) or head (3)
# Fix to specific CLIgen version (eg 3.5) or head (3)
CLIGEN_VERSION="3"
if test "$prefix" = "NONE"; then
CLIGEN_PREFIX="$ac_default_prefix"
@ -2339,6 +2348,99 @@ test -n "$target_alias" &&
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
# SysV /etc/install, /usr/sbin/install
# SunOS /usr/etc/install
# IRIX /sbin/install
# AIX /bin/install
# AmigaOS /C/install, which installs bootblocks on floppy discs
# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
# AFS /usr/afsws/bin/install, which mishandles nonexistent args
# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
# OS/2's system install, which has a completely different semantic
# ./install, which can be erroneously created by make from ./install.sh.
# Reject install programs that cannot install multiple files.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
$as_echo_n "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
if ${ac_cv_path_install+:} false; then :
$as_echo_n "(cached) " >&6
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
# Account for people who put trailing slashes in PATH elements.
case $as_dir/ in #((
./ | .// | /[cC]/* | \
/etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
/usr/ucb/* ) ;;
*)
# OSF1 and SCO ODT 3.0 have their own names for install.
# Don't use installbsd from OSF since it installs stuff as root
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
if test $ac_prog = install &&
grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
:
elif test $ac_prog = install &&
grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# program-specific install script used by HP pwplus--don't use.
:
else
rm -rf conftest.one conftest.two conftest.dir
echo one > conftest.one
echo two > conftest.two
mkdir conftest.dir
if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
test -s conftest.one && test -s conftest.two &&
test -s conftest.dir/conftest.one &&
test -s conftest.dir/conftest.two
then
ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
break 3
fi
fi
fi
done
done
;;
esac
done
IFS=$as_save_IFS
rm -rf conftest.one conftest.two conftest.dir
fi
if test "${ac_cv_path_install+set}" = set; then
INSTALL=$ac_cv_path_install
else
# As a last resort, use the slow shell script. Don't cache a
# value for INSTALL within a source directory, because that will
# break other packages using the cache if that directory is
# removed, or if the value is a relative name.
INSTALL=$ac_install_sh
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
$as_echo "$INSTALL" >&6; }
# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
# It thinks the first close brace ends the variable substitution.
test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
@ -3279,6 +3381,27 @@ CPPFLAGS="-DHAVE_CONFIG_H ${CPPFLAGS}"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: compiler is $CC" >&5
$as_echo "compiler is $CC" >&6; }
# Debug flag
# Check whether --enable-debug was given.
if test "${enable_debug+set}" = set; then :
enableval=$enable_debug;
if test "$enableval" = no; then
ac_enable_debug=no
else
ac_enable_debug=yes
fi
else
ac_enable_debug=no
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug is $ac_enable_debug" >&5
$as_echo "debug is $ac_enable_debug" >&6; }
if test "$ac_enable_debug" = "yes"; then
CFLAGS="-g -Wall"
INSTALLFLAGS=""
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CPPFLAGS is $CPPFLAGS" >&5
$as_echo "CPPFLAGS is $CPPFLAGS" >&6; }
@ -3540,11 +3663,7 @@ _ACEOF
fi
EXE_SUFFIX=""
OBJ_SUFFIX=".o"
AR_SUFFIX=".a"
SH_SUFFIX=".so"
AR="ar"
# This is for cligen
@ -4190,11 +4309,12 @@ fi
done
# This is to find clixon system files in the source code.
# same as clixon_DATADIR defined in clixon.mk.cpp for Makefiles
# This is to find clixon system files in the source code and Makefile
CLIXON_DATADIR="${prefix}/share/clixon"
cat >>confdefs.h <<_ACEOF
#define CLIXON_DATADIR "${prefix}/share/clixon"
#define CLIXON_DATADIR "${CLIXON_DATADIR}"
_ACEOF
@ -4207,7 +4327,7 @@ _ACEOF
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/docker/Makefile extras/rpm/Makefile docker/Makefile docker/cli/Makefile docker/cli/Dockerfile docker/backend/Makefile docker/backend/Dockerfile docker/netconf/Makefile docker/netconf/Dockerfile datastore/Makefile datastore/text/Makefile yang/Makefile doc/Makefile"
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile extras/rpm/Makefile docker/Makefile datastore/Makefile datastore/text/Makefile util/Makefile yang/Makefile doc/Makefile"
cat >confcache <<\_ACEOF
# This file is a shell script that caches the results of configure
@ -4787,6 +4907,7 @@ gives unlimited permission to copy, distribute and modify it."
ac_pwd='$ac_pwd'
srcdir='$srcdir'
INSTALL='$INSTALL'
test -n "\$AWK" || AWK=awk
_ACEOF
@ -4913,17 +5034,11 @@ do
"etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;;
"etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;;
"example/Makefile") CONFIG_FILES="$CONFIG_FILES example/Makefile" ;;
"example/docker/Makefile") CONFIG_FILES="$CONFIG_FILES example/docker/Makefile" ;;
"extras/rpm/Makefile") CONFIG_FILES="$CONFIG_FILES extras/rpm/Makefile" ;;
"docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;;
"docker/cli/Makefile") CONFIG_FILES="$CONFIG_FILES docker/cli/Makefile" ;;
"docker/cli/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/cli/Dockerfile" ;;
"docker/backend/Makefile") CONFIG_FILES="$CONFIG_FILES docker/backend/Makefile" ;;
"docker/backend/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/backend/Dockerfile" ;;
"docker/netconf/Makefile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Makefile" ;;
"docker/netconf/Dockerfile") CONFIG_FILES="$CONFIG_FILES docker/netconf/Dockerfile" ;;
"datastore/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/Makefile" ;;
"datastore/text/Makefile") CONFIG_FILES="$CONFIG_FILES datastore/text/Makefile" ;;
"util/Makefile") CONFIG_FILES="$CONFIG_FILES util/Makefile" ;;
"yang/Makefile") CONFIG_FILES="$CONFIG_FILES yang/Makefile" ;;
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
@ -5369,6 +5484,10 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
# CONFIG_FILE
#
case $INSTALL in
[\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
*) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
esac
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
@ -5422,6 +5541,7 @@ s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
s&@builddir@&$ac_builddir&;t t
s&@abs_builddir@&$ac_abs_builddir&;t t
s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
s&@INSTALL@&$ac_INSTALL&;t t
$ac_datarootdir_hack
"
eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \

View file

@ -43,10 +43,11 @@ AC_INIT(lib/clixon/clixon.h.in)
: ${INSTALLFLAGS="-s"}
CLIXON_VERSION_MAJOR="3"
CLIXON_VERSION_MINOR="6"
CLIXON_VERSION_PATCH="1"
CLIXON_VERSION_MINOR="7"
CLIXON_VERSION_PATCH="0"
CLIXON_VERSION="\"${CLIXON_VERSION_MAJOR}.${CLIXON_VERSION_MINOR}.${CLIXON_VERSION_PATCH}\""
# Fix to specific version (eg 3.5) or head (3)
# Fix to specific CLIgen version (eg 3.5) or head (3)
CLIGEN_VERSION="3"
if test "$prefix" = "NONE"; then
CLIGEN_PREFIX="$ac_default_prefix"
@ -73,18 +74,19 @@ AC_SUBST(CLIGEN_PREFIX)
AC_MSG_RESULT(CLIXON version is ${CLIXON_VERSION})
AC_CANONICAL_TARGET
AC_SUBST(CC)
AC_SUBST(CFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(INCLUDES)
AC_SUBST(CPPFLAGS)
AC_PROG_INSTALL
AC_SUBST(INSTALL)
AC_SUBST(INSTALL_DATA)
AC_SUBST(INSTALL_PROGRAM)
AC_SUBST(INSTALLFLAGS)
AC_SUBST(LIBS)
AC_SUBST(OBJ_SUFFIX)
AC_SUBST(AR_SUFFIX)
AC_SUBST(SH_SUFFIX)
AC_SUBST(EXE_SUFFIX)
AC_SUBST(AR)
AC_SUBST(RANLIB)
AC_SUBST(with_restconf) # If yes, compile apps/restconf
#
@ -94,6 +96,21 @@ AC_PROG_CPP
CPPFLAGS="-DHAVE_CONFIG_H ${CPPFLAGS}"
AC_MSG_RESULT(compiler is $CC)
# Debug flag
AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug],[Build with debug symbols, default: no]),[
if test "$enableval" = no; then
ac_enable_debug=no
else
ac_enable_debug=yes
fi
],
[ ac_enable_debug=no])
AC_MSG_RESULT(debug is $ac_enable_debug)
if test "$ac_enable_debug" = "yes"; then
CFLAGS="-g -Wall"
INSTALLFLAGS=""
fi
AC_MSG_RESULT(CPPFLAGS is $CPPFLAGS)
AC_MSG_RESULT(CFLAGS is $CFLAGS)
@ -113,11 +130,7 @@ if test "$prefix" = "NONE"; then
fi
AC_CHECK_LIB(m, main)
EXE_SUFFIX=""
OBJ_SUFFIX=".o"
AR_SUFFIX=".a"
SH_SUFFIX=".so"
AR="ar"
# This is for cligen
AC_ARG_WITH(cligen, [ --with-cligen=dir Use CLIGEN here ] )
@ -165,9 +178,10 @@ AC_CHECK_LIB(dl, dlopen)
AC_CHECK_FUNCS(inet_aton sigaction sigvec strlcpy strsep strndup alphasort versionsort strverscmp)
# This is to find clixon system files in the source code.
# same as clixon_DATADIR defined in clixon.mk.cpp for Makefiles
AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${prefix}/share/clixon", [Clixon data dir for system yang files etc])
# This is to find clixon system files in the source code and Makefile
AC_SUBST(CLIXON_DATADIR)
CLIXON_DATADIR="${prefix}/share/clixon"
AC_DEFINE_UNQUOTED(CLIXON_DATADIR, "${CLIXON_DATADIR}", [Clixon data dir for system yang files etc])
# Default location for config file
AC_DEFINE_UNQUOTED(CLIXON_DEFAULT_CONFIG,"${DEFAULT_CONFIG}",[Location for apps to find default config file])
@ -187,17 +201,11 @@ AC_OUTPUT(Makefile
etc/Makefile
etc/clixonrc
example/Makefile
example/docker/Makefile
extras/rpm/Makefile
docker/Makefile
docker/cli/Makefile
docker/cli/Dockerfile
docker/backend/Makefile
docker/backend/Dockerfile
docker/netconf/Makefile
docker/netconf/Dockerfile
datastore/Makefile
datastore/text/Makefile
util/Makefile
yang/Makefile
doc/Makefile
)

View file

@ -78,8 +78,6 @@ APPL = datastore_client
all: $(SUBDIRS) $(APPL)
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
.SUFFIXES:
.SUFFIXES: .c .o
@ -101,14 +99,11 @@ install-include:
for i in $(SUBDIRS); \
do (cd $$i ; $(MAKE) $(MFLAGS) $@)||exit 1; done;
install: $(APPL)
install -d -m 0755 $(DESTDIR)$(bindir)
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(bindir)
install:
for i in $(SUBDIRS); \
do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done
uninstall:
rm -f $(DESTDIR)$(bindir)/$(APPL)
for i in $(SUBDIRS); \
do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done

View file

@ -63,7 +63,6 @@ OBJS = $(SRC:.c=.o)
all: $(PLUGIN)
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
$(PLUGIN): $(SRC)
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -o $@ -lc $^ $(LIBS)

View file

@ -92,7 +92,7 @@ chunk_initialize ()
chunk_pagesz = getpagesize();
bzero (&chunk_heads, sizeof (chunk_heads));
bzero (&chunk_heads, sizeof(chunk_heads));
for (idx = 0; idx < CHUNK_HEADS; idx++) {
chunk_head_t *chead = &chunk_heads[idx];
@ -121,7 +121,7 @@ chunk_initialize ()
*/
chead->ch_size =
(chead->ch_blksz / chead->ch_nchkperblk)
- sizeof (chunk_t);
- sizeof(chunk_t);
}
@ -150,22 +150,22 @@ chunk_new_block (chunk_head_t *chead)
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (blk == MAP_FAILED)
return -1;
memset ((void *)blk, 0, sizeof(*blk));
memset((void *)blk, 0, sizeof(*blk));
/* Allocate chunk block */
blk->cb_blk = (void *)
mmap(NULL, chead->ch_blksz,
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
if (blk->cb_blk == MAP_FAILED) {
munmap (blk, chead->ch_blksz);
munmap(blk, chead->ch_blksz);
return -1;
}
memset (blk->cb_blk, 0, chead->ch_blksz);
memset(blk->cb_blk, 0, chead->ch_blksz);
/* Initialize chunk header */
blk->cb_head = chead;
INSQ (blk, chead->ch_blks);
INSQ(blk, chead->ch_blks);
chead->ch_nblks++;
/* Initialize chunks */
@ -174,10 +174,10 @@ chunk_new_block (chunk_head_t *chead)
cnk = (chunk_t *)c;
cnk->c_blk = blk;
INSQ (cnk, chead->ch_free);
INSQ(cnk, chead->ch_free);
chead->ch_nfree++;
c += (chead->ch_size + sizeof (chunk_t));
c += (chead->ch_size + sizeof(chunk_t));
}
@ -188,7 +188,7 @@ chunk_new_block (chunk_head_t *chead)
* chunk_release_block() - Unqueue a block, it's chunks and free mem
*/
static void
chunk_release_block (chunk_block_t *cblk)
chunk_release_block(chunk_block_t *cblk)
{
int idx;
char *c;
@ -201,7 +201,7 @@ chunk_release_block (chunk_block_t *cblk)
/*
* Dequeue block
*/
DELQ (cblk, chead->ch_blks, chunk_block_t *);
DELQ(cblk, chead->ch_blks, chunk_block_t *);
chead->ch_nblks--;
/*
@ -211,17 +211,17 @@ chunk_release_block (chunk_block_t *cblk)
for (idx = 0; idx < chead->ch_nchkperblk; idx++) {
cnk = (chunk_t *)c;
DELQ (cnk, chead->ch_free, chunk_t *);
DELQ(cnk, chead->ch_free, chunk_t *);
chead->ch_nfree--;
c += (chead->ch_size + sizeof (chunk_t));
c += (chead->ch_size + sizeof(chunk_t));
}
/*
* Free block
*/
munmap ((void *)cblk->cb_blk, chead->ch_blksz);
munmap ((void *)cblk, sizeof(*cblk));
munmap((void *)cblk->cb_blk, chead->ch_blksz);
munmap((void *)cblk, sizeof(*cblk));
}
@ -230,7 +230,7 @@ chunk_release_block (chunk_block_t *cblk)
* chunk_alloc() - Map new chunk of memory
*/
static void *
chunk_alloc (size_t len)
chunk_alloc(size_t len)
{
register int idx;
chunk_head_t *chead;
@ -258,21 +258,21 @@ chunk_alloc (size_t len)
/* Get new block if necessary */
if (!chead->ch_nfree)
if (chunk_new_block (chead))
if (chunk_new_block(chead))
return (void *)NULL;
/* Move a free chunk to the in-use list */
cnk = chead->ch_free;
DELQ (cnk, chead->ch_free, chunk_t *);
DELQ(cnk, chead->ch_free, chunk_t *);
chead->ch_nfree--;
INSQ (cnk, chead->ch_cnks);
INSQ(cnk, chead->ch_cnks);
/* Add reference to the corresponding block */
cnk->c_blk->cb_ref++;
#ifdef CHUNK_DIAG
/* Clear diag info */
bzero ((void *)&cnk->c_diag, sizeof (cnk->c_diag));
bzero((void *)&cnk->c_diag, sizeof(cnk->c_diag));
#endif /* CHUNK_DIAG */
/* Return pointer to first byte after the chunk header */
@ -285,9 +285,9 @@ chunk_alloc (size_t len)
*/
void *
#ifdef CHUNK_DIAG
_chunk (size_t len, const char *name, const char *file, int line)
_chunk(size_t len, const char *name, const char *file, int line)
#else
chunk (size_t len, const char *name)
chunk(size_t len, const char *name)
#endif
{
int newgrp = 0;
@ -306,10 +306,10 @@ chunk (size_t len, const char *name)
/* Get actual chunk
*/
ptr = chunk_alloc (len);
ptr = chunk_alloc(len);
if (!ptr)
goto error;
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
#ifdef CHUNK_DIAG
/* Store who reuested us
@ -329,7 +329,7 @@ chunk (size_t len, const char *name)
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp (tmp->cg_name, name)) {
if (!strcmp(tmp->cg_name, name)) {
grp = tmp;
break;
}
@ -343,26 +343,26 @@ chunk (size_t len, const char *name)
*/
if ( !grp ) {
grp = (chunk_group_t *) chunk_alloc (sizeof (chunk_group_t));
grp = (chunk_group_t *) chunk_alloc(sizeof(chunk_group_t));
if (!grp)
goto error;
bzero (grp, sizeof (chunk_group_t));
bzero(grp, sizeof(chunk_group_t));
grp->cg_name = (char *) chunk_alloc (strlen (name) + 1);
grp->cg_name = (char *) chunk_alloc(strlen(name) + 1);
if (!grp->cg_name)
goto error;
bcopy (name, grp->cg_name, strlen(name)+1);
bcopy(name, grp->cg_name, strlen(name)+1);
newgrp = 1;
}
/* Get new entry.
*/
ent = (chunk_grpent_t *) chunk_alloc (sizeof (chunk_grpent_t));
ent = (chunk_grpent_t *) chunk_alloc(sizeof(chunk_grpent_t));
if (!ent)
goto error;
bzero (ent, sizeof (chunk_grpent_t));
bzero(ent, sizeof(chunk_grpent_t));
/* Now put everything together
*/
@ -371,22 +371,22 @@ chunk (size_t len, const char *name)
ent->ce_cnk = cnk;
ent->ce_grp = grp;
INSQ (ent, grp->cg_ent);
INSQ(ent, grp->cg_ent);
if (newgrp)
INSQ (grp, chunk_grp);
INSQ(grp, chunk_grp);
return (ptr);
error:
if (grp && newgrp) {
if (grp->cg_name)
unchunk (grp->cg_name);
unchunk (grp);
unchunk(grp->cg_name);
unchunk(grp);
}
if (ent)
unchunk (ent);
unchunk(ent);
if (ptr)
unchunk (ptr);
unchunk(ptr);
return (void *) NULL;
}
@ -397,9 +397,9 @@ chunk (size_t len, const char *name)
*/
void *
#ifdef CHUNK_DIAG
_rechunk (void *ptr, size_t len, const char *name, const char *file, int line)
_rechunk(void *ptr, size_t len, const char *name, const char *file, int line)
#else
rechunk (void *ptr, size_t len, const char *name)
rechunk(void *ptr, size_t len, const char *name)
#endif
{
int idx;
@ -414,22 +414,22 @@ rechunk (void *ptr, size_t len, const char *name)
*/
if (!ptr) {
#ifdef CHUNK_DIAG
return _chunk (len, name, file, line);
return _chunk(len, name, file, line);
#else
return chunk (len, name);
return chunk(len, name);
#endif
}
/* Zero length, free chunk
*/
if (len == 0) {
unchunk (ptr);
unchunk(ptr);
return (void *) NULL;
}
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
chead = cnk->c_blk->cb_head;
/* Find sufficient sized block head
@ -451,22 +451,22 @@ rechunk (void *ptr, size_t len, const char *name)
/* Get new chunk
*/
#ifdef CHUNK_DIAG
new = _chunk (len, name, file, line);
new = _chunk(len, name, file, line);
#else
new = chunk (len, name);
new = chunk(len, name);
#endif
if (!new)
return (void *) NULL;
newcnk = (chunk_t *) (((char *)new) - sizeof (chunk_t));
newcnk = (chunk_t *) (((char *)new) - sizeof(chunk_t));
newchead = newcnk->c_blk->cb_head;
/* Copy contents to new chunk
*/
bcopy (ptr, new, MIN(newchead->ch_size, chead->ch_size));
bcopy(ptr, new, MIN(newchead->ch_size, chead->ch_size));
/* Free old chunk
*/
unchunk (ptr);
unchunk(ptr);
return (new);
@ -476,7 +476,7 @@ rechunk (void *ptr, size_t len, const char *name)
* unchunk() - Release chunk
*/
void
unchunk (void *ptr)
unchunk(void *ptr)
{
chunk_t *cnk;
chunk_head_t *chead;
@ -489,14 +489,14 @@ unchunk (void *ptr)
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
cblk = cnk->c_blk;
chead = cblk->cb_head;
/* Move chunk back to free list
*/
DELQ (cnk, chead->ch_cnks, chunk_t *);
INSQ (cnk, chead->ch_free);
DELQ(cnk, chead->ch_cnks, chunk_t *);
INSQ(cnk, chead->ch_free);
chead->ch_nfree++;
/* If chunk is grouped, remove from group.
@ -504,13 +504,13 @@ unchunk (void *ptr)
ent = cnk->c_grpent;
if (ent) {
grp = ent->ce_grp;
DELQ (ent, grp->cg_ent, chunk_grpent_t *);
unchunk (ent);
DELQ(ent, grp->cg_ent, chunk_grpent_t *);
unchunk(ent);
cnk->c_grpent = NULL;
/* Group empty? */
if (!dont_unchunk_group && !grp->cg_ent) {
DELQ (grp, chunk_grp, chunk_group_t *);
DELQ(grp, chunk_grp, chunk_group_t *);
unchunk(grp->cg_name);
unchunk(grp);
}
@ -528,7 +528,7 @@ unchunk (void *ptr)
* unchunk_group() - Release all group chunks.
*/
void
unchunk_group (const char *name)
unchunk_group(const char *name)
{
chunk_group_t *tmp;
chunk_group_t *grp = NULL;
@ -542,7 +542,7 @@ unchunk_group (const char *name)
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp (tmp->cg_name, name)) {
if (!strcmp(tmp->cg_name, name)) {
grp = tmp;
break;
}
@ -560,16 +560,16 @@ unchunk_group (const char *name)
dont_unchunk_group = 1;
while (grp->cg_ent) {
cnk = grp->cg_ent->ce_cnk;
unchunk ((chunk_t *)(((char *)cnk) + sizeof (chunk_t)));
unchunk((chunk_t *)(((char *)cnk) + sizeof(chunk_t)));
}
dont_unchunk_group = 0;
/* Remove group from list and free it
*/
DELQ (grp, chunk_grp, chunk_group_t *);
unchunk (grp->cg_name);
unchunk (grp);
DELQ(grp, chunk_grp, chunk_group_t *);
unchunk(grp->cg_name);
unchunk(grp);
}
/*
@ -577,9 +577,9 @@ unchunk_group (const char *name)
*/
void *
#ifdef CHUNK_DIAG
_chunkdup (const void *ptr, size_t len, const char *name, const char *file, int line)
_chunkdup(const void *ptr, size_t len, const char *name, const char *file, int line)
#else
chunkdup (const void *ptr, size_t len, const char *name)
chunkdup(const void *ptr, size_t len, const char *name)
#endif
{
void *new;
@ -592,9 +592,9 @@ chunkdup (const void *ptr, size_t len, const char *name)
/* Get new chunk
*/
#ifdef CHUNK_DIAG
new = _chunk (len, name, file, line);
new = _chunk(len, name, file, line);
#else
new = chunk (len, name);
new = chunk(len, name);
#endif
if (!new)
return (void *)NULL;
@ -610,7 +610,7 @@ chunkdup (const void *ptr, size_t len, const char *name)
* chunksize() - Return size of memory chunk.
*/
size_t
chunksize (void *ptr)
chunksize(void *ptr)
{
chunk_t *cnk;
chunk_head_t *chead;
@ -621,7 +621,7 @@ chunksize (void *ptr)
/* Rewind pointer to beginning of chunk header
*/
cnk = (chunk_t *) (((char *)ptr) - sizeof (chunk_t));
cnk = (chunk_t *) (((char *)ptr) - sizeof(chunk_t));
cblk = cnk->c_blk;
chead = cblk->cb_head;
@ -635,10 +635,10 @@ chunksize (void *ptr)
*/
char *
#ifdef CHUNK_DIAG
_chunk_strncat (const char *dst, const char *src, size_t n, const char *name,
_chunk_strncat(const char *dst, const char *src, size_t n, const char *name,
const char *file, int line)
#else
chunk_strncat (const char *dst, const char *src, size_t n, const char *name)
chunk_strncat(const char *dst, const char *src, size_t n, const char *name)
#endif
{
size_t len;
@ -651,7 +651,7 @@ chunk_strncat (const char *dst, const char *src, size_t n, const char *name)
#ifdef CHUNK_DIAG
ptr = _rechunk(ptr, len, name, file, line);
#else
ptr = rechunk (ptr, len, name);
ptr = rechunk(ptr, len, name);
#endif
if (ptr == NULL)
return NULL;
@ -670,10 +670,10 @@ chunk_strncat (const char *dst, const char *src, size_t n, const char *name)
*/
char *
#ifdef CHUNK_DIAG
_chunk_sprintf (const char *name, const char *file,
_chunk_sprintf(const char *name, const char *file,
int line, const char *fmt, ...)
#else
chunk_sprintf (const char *name, char *fmt, ...)
chunk_sprintf(const char *name, char *fmt, ...)
#endif
{
size_t len;
@ -683,13 +683,13 @@ chunk_sprintf (const char *name, char *fmt, ...)
/* Calculate formatted string length */
va_start(args, fmt);
len = vsnprintf(NULL, 0, fmt, args) + 1;
va_end (args);
va_end(args);
/* get chunk */
#ifdef CHUNK_DIAG
str = _chunk (len, name, file, line);
str = _chunk(len, name, file, line);
#else
str = chunk (len, name);
str = chunk(len, name);
#endif
if (str == NULL)
return NULL;
@ -697,7 +697,7 @@ chunk_sprintf (const char *name, char *fmt, ...)
/* Format string */
va_start(args, fmt);
len = vsnprintf(str, len, fmt, args);
va_end (args);
va_end(args);
return str;
}
@ -756,7 +756,7 @@ chunk_check(FILE *fout, const char *name)
if (chunk_grp) {
tmp = chunk_grp;
do {
if (!strcmp (tmp->cg_name, name)) {
if (!strcmp(tmp->cg_name, name)) {
grp = tmp;
break;
}

View file

@ -64,7 +64,7 @@
*
* Alternative for xmlkeyfmt would be eg:
* RESTCONF: /interfaces/interface=%s/ipv4/address/ip=%s (used)
* XPATH: /interfaces/interface[name=%s]/ipv4/address/[ip=%s]
* XPATH: /interfaces/interface[name='%s']/ipv4/address/[ip'=%s']
*
* Paths through the code (for coverage)
* cli_callback_generate +----------------+
@ -375,7 +375,7 @@ get(char *dbname,
arg = valvec[j++];
if (uri_percent_decode(arg, &argdec) < 0)
goto done;
cprintf(cb, "[%s=%s]", cv_string_get(cvi), argdec);
cprintf(cb, "[%s='%s']", cv_string_get(cvi), argdec);
free(argdec);
argdec=NULL;
}

View file

@ -436,7 +436,7 @@ db_regexp(char *file,
goto quit;
}
pair = &newpairs[npairs];
memset (pair, 0, sizeof(*pair));
memset(pair, 0, sizeof(*pair));
pair->dp_key = chunk_sprintf(label, "%s", key);
if (regexp)
@ -451,7 +451,7 @@ db_regexp(char *file,
}
if ( ! noval) {
if (vlen){
pair->dp_val = chunkdup (val, vlen, label);
pair->dp_val = chunkdup(val, vlen, label);
if (pair->dp_val == NULL) {
clicon_err(OE_DB, errno, "%s: chunkdup", __FUNCTION__);
goto quit;

View file

@ -63,8 +63,6 @@ OBJS = $(SRC:.c=.o)
all: $(PLUGIN)
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
$(PLUGIN): $(SRC)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(CPPFLAGS) $(INCLUDES) $(CFLAGS) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ -lc $^ $(LIBS)

View file

@ -281,7 +281,8 @@ text_setopt(xmldb_handle xh,
else if (strcmp(value,"json")==0)
th->th_format = "json";
else{
clicon_err(OE_PLUGIN, 0, "Option %s unrecognized format: %s", optname, value);
clicon_err(OE_PLUGIN, 0, "Option %s unrecognized format: %s",
optname, (char*)value);
goto done;
}
}
@ -473,7 +474,7 @@ text_get(xmldb_handle xh,
} /* xt == NULL */
/* Here xt looks like: <config>...</config> */
if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
if (xpath_vec(xt, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* If vectors are specified then mark the nodes found with all ancestors
@ -806,7 +807,8 @@ text_modify_top(cxobj *x0,
x1cname = xml_name(x1c);
/* Get yang spec of the child */
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
clicon_err(OE_YANG, ENOENT, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", x1, x1cname);
clicon_err(OE_YANG, ENOENT, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?",
xml_name(x1), x1cname);
goto done;
}
/* See if there is a corresponding node in the base tree */

View file

@ -1,4 +1,4 @@
i# Clixon FAQ
# Clixon FAQ
## What is Clixon?
@ -24,7 +24,7 @@ Clixon is written in C. The plugins are written in C. The CLI
specification uses cligen (http://cligen.se)
## How to best understand Clixon?
Run the Clixon example, in the example directory.
Run the Clixon example, in the [example](../example) directory.
## How do you build and install Clixon (and the example)?
Clixon:
@ -41,14 +41,25 @@ The example:
sudo make install
```
## Do I need to setup anything?
## Do I need to setup anything? (IMPORTANT)
The config demon requires a valid group to create a server UNIX socket.
Define a valid CLICON_SOCK_GROUP in the config file or via the -g option
or create the group and add the user to it. The default group is 'clicon'.
Add yourself and www-data, if you intend to use restconf.
On linux:
```
sudo groupadd clicon
sudo usermod -a -G clicon user
sudo usermod -a -G clicon <user>
sudo usermod -a -G clicon www-data
```
Verify:
```
grep clicon /etc/group
clicon:x:1001:<user>,www-data
```
## What about reference documentation?
Clixon uses Doxygen for reference documentation.
@ -90,20 +101,14 @@ You can change where CLixon looks for the configuration FILE as follows:
- FILE is /usr/local/etc/clixon.xml
## Can I run Clixon as docker containers?
Yes, the example works as docker containers as well. backend and cli needs a
common file-system so they need to run as a composed pair.
Yes, the example works as docker containers as well. There should be a
prepared container in docker hib for the example where the backend and
CLI is bundled.
```
cd example/docker
make docker # Prepares /data as shared file-system mount
run.sh # Starts an example backend and a cli
sudo docker run -ti --rm olofhagsand/clixon_example
```
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.
Look in the example documentation for more info.
## How do I use netconf?
@ -127,7 +132,25 @@ You can access clixon via REST API using restconf, such as using
curl. GET, PUT, POST are supported.
You need a web-server, such as nginx, and start a restconf fcgi
daemon, clixon_restconf. Read more in the restconf docs.
daemon, clixon_restconf.
For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default:
```
server {
...
location /restconf {
root /usr/share/nginx/html/restconf;
fastcgi_pass unix:/www-data/fastcgi_restconf.sock;
include fastcgi_params;
}
}
```
Start nginx daemon
```
sudo /etc/init.d/nginx start
```
Read more in the restconf docs.
Example:
```
@ -178,6 +201,11 @@ You may also add a default method in the configuration file:
</config>
```
## Can I use systemd with Clixon?
Yes. Systemd example files are provide for the backend and the
restconf daemon as part of the [example](../example/systemd).
## How can I add extra XML?
There are two ways to add extra XML to running database after start. Note that this XML is not "committed" into running.
@ -342,5 +370,35 @@ plugin_credentials(clicon_handle h,
To authenticate, the callback needs to return the value 1 and supply a username.
See [../apps/example/example_restconf.c] plugin_credentials() for
See [../apps/example/example_restconf.c] example_restconf_credentials() for
an example of HTTP basic auth.
## How do I write a CLI translator function?
The CLI can perform variable translation. This is useful if you want to
prcess the input, such as hashing, encrypting or in other way
translate the input.
Yang example:
```
list translate{
leaf value{
type string;
}
}
```
CLI specification:
```
translate value (<value:string translate:incstr()>),cli_set("/translate/value");
```
If you run this example using the `incstr()` function which increments the characters in the input, you get this result:
```
cli> translate value HAL
cli> show configuration
translate {
value IBM;
}
```
You can perform translation on any type, not only strings.

View file

@ -31,19 +31,41 @@
# ***** END LICENSE BLOCK *****
#
FROM ubuntu:14.04
# 12.04
FROM debian
MAINTAINER Olof Hagsand <olof@hagsand.se>
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y libqdbm-dev
RUN apt-get update && apt-get install -y \
git make gcc flex bison \
libfcgi-dev \
libcurl4-openssl-dev
RUN groupadd clicon
COPY libcligen.so.@CLIGEN_VERSION@ /usr/lib/
COPY libclixon.so.@CLIXON_VERSION_MAJOR@ /usr/lib/
COPY libclixon_cli.so.@CLIXON_VERSION_MAJOR@ /usr/lib/
COPY clixon_cli /usr/bin/
# Create a directory to hold source-code, dependencies etc
RUN mkdir /clixon
WORKDIR /clixon
# Clone cligen and clixon
RUN git clone https://github.com/olofhagsand/cligen.git
RUN git clone https://github.com/clicon/clixon.git
# Build cligen
WORKDIR /clixon/cligen
RUN ./configure
RUN make
RUN make install
# Build clixon
WORKDIR /clixon/clixon
RUN git checkout -b develop origin/develop
RUN ./configure
RUN make
RUN make install
RUN make install-include
RUN ldconfig
CMD ["/usr/bin/clixon_cli", "-f", "/data/clixon.conf"]

View file

@ -38,46 +38,33 @@ CFLAGS = @CFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
# Change this
IMAGE = olofhagsand/clixon
SHELL = /bin/sh
SUBDIRS = cli backend netconf
.PHONY: all clean depend install $(SUBDIRS) docker push
.PHONY: all clean depend install docker push
all: $(SUBDIRS)
depend:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
$(SUBDIRS):
(cd $@; $(MAKE) $(MFLAGS) all)
install-include:
for i in $(SUBDIRS); \
do (cd $$i ; $(MAKE) $(MFLAGS) $@); done;
install:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
uninstall:
for i in $(SUBDIRS); \
do (cd $$i; $(MAKE) $(MFLAGS) $@); done
all:
@echo "Run make docker to build docker image"
clean:
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
docker:
for i in $(SUBDIRS); \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done
sudo docker build -t $(IMAGE) .
push:
for i in $(SUBDIRS); \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done
sudo docker push $(IMAGE)
depend:
install-include:
install:
uninstall:

View file

@ -1,12 +0,0 @@
This dir is not updated
This dir contains docker code - how to build clixon as docker containers
cli Build olofhagsand/clixon_cli container
backend Build olofhagsand/clixon_backend container
netconf Build olofhagsand/clixon_netconf container
Perform the build by 'make docker'.
You may also do 'make push' if you want to push the image, but you may then consider changing the image name (in the makefile:s).
You may run the container directly by going directly to example and
the docker runtime scripts there

19
docker/README.md Normal file
View file

@ -0,0 +1,19 @@
# Clixon base docker image
This directory contains code for building and pushing a Clixon docker
container. By default it is pushed to olofhagsand/clixon, but you can change
the IMAGE in Makefile.in and push it to another name.
The clixon docker image is a base image that can be used to build
clixon applications. It has all the whole code for a clixon release
which it downloads from git - it does not use local code.
See example/docker for how to build a docker application using the base image.
Build and push
==============
Perform the build by 'make docker'.
You may also do 'make push' if you want to push the image, but you may then consider changing the image name (in the makefile:s).
You may run the container directly by going directly to example and
the docker runtime scripts there

View file

@ -1,6 +0,0 @@
Ensure that cligen and clixon has been built and installed.
sudo make docker
make push
Then go to example and run the example as a docker container

View file

@ -1,76 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2018 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@
exec_prefix = @exec_prefix@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
prefix = @prefix@
bindir = @bindir@
sbindir = @sbindir@
libdir = @libdir@
includedir = @includedir@
datarootdir = @datarootdir@
# You may consider changing this
image = olofhagsand/clixon_cli
all:
@echo "Run make docker to build docker image"
clean:
distclean: clean
rm -f Makefile *~ .depend libcligen* libclixon* clixon_cli Dockerfile
# Kind of reverse install, could have copied from src dir,...
.PHONY: docker push
docker:
cp $(DESTDIR)$(libdir)/libcligen.so.@CLIGEN_VERSION@ .
cp $(DESTDIR)$(libdir)/libclixon.so.@CLIXON_VERSION_MAJOR@ .
cp $(DESTDIR)$(libdir)/libclixon_cli.so.@CLIXON_VERSION_MAJOR@ .
cp $(DESTDIR)$(bindir)/clixon_cli .
sudo docker build -t $(image) .
push:
sudo docker push $(image)
install:
uninstall:
install-include:
depend:
# $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
#include .depend

View file

@ -1,6 +0,0 @@
Ensure that cligen and clixon has been built and installed.
sudo make docker
make push
Then go to example and run the example as a docker container

View file

@ -1,52 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2018 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 *****
#
FROM ubuntu:14.04
# 12.04
MAINTAINER Olof Hagsand <olof@hagsand.se>
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y libqdbm-dev
RUN groupadd clicon
COPY libcligen.so.@CLIGEN_VERSION@ /usr/lib/
COPY libclixon.so.@CLIXON_VERSION_MAJOR@ /usr/lib/
COPY libclixon_netconf.so.@CLIXON_VERSION_MAJOR@ /usr/lib/
COPY clixon_netconf /usr/bin/
RUN ldconfig
CMD ["/usr/bin/clixon_netconf", "-f", "/data/clixon.conf"]

View file

@ -1,76 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2018 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@
exec_prefix = @exec_prefix@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
prefix = @prefix@
bindir = @bindir@
sbindir = @sbindir@
libdir = @libdir@
includedir = @includedir@
datarootdir = @datarootdir@
# You may consider changing this
image = olofhagsand/clixon_netconf
all:
@echo "Run make docker to build docker image"
clean:
distclean: clean
rm -f Makefile *~ .depend libcligen* libclixon* clixon_netconf Dockerfile
# Kind of reverse install, could have copied from src dir,...
.PHONY: docker push
docker:
cp $(DESTDIR)$(libdir)/libcligen.so.@CLIGEN_VERSION@ .
cp $(DESTDIR)$(libdir)/libclixon.so.@CLIXON_VERSION_MAJOR@ .
cp $(DESTDIR)$(libdir)/libclixon_netconf.so.@CLIXON_VERSION_MAJOR@ .
cp $(DESTDIR)$(bindir)/clixon_netconf .
sudo docker build -t $(image) .
push:
sudo docker push $(image)
install:
uninstall:
install-include:
depend:
# $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
#include .depend

View file

@ -1,6 +0,0 @@
Ensure that cligen and clixon has been built and installed.
sudo make docker
make push
Then go to example and run the example as a docker container

View file

@ -31,21 +31,29 @@
# ***** END LICENSE BLOCK *****
#
FROM ubuntu:14.04
# 12.04
FROM olofhagsand/clixon
MAINTAINER Olof Hagsand <olof@hagsand.se>
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y libqdbm-dev
RUN groupadd clicon
COPY libcligen.so.@CLIGEN_VERSION@ /usr/lib/
COPY libclixon.so.@CLIXON_VERSION_MAJOR@ /usr/lib/
COPY libclixon_backend.so.@CLIXON_VERSION_MAJOR@ /usr/lib/
COPY clixon_backend /usr/sbin/
RUN ldconfig
RUN sudo groupadd clixon
CMD ["/usr/sbin/clixon_backend", "-F", "-f", "/data/clixon.conf"]
RUN apt-get update && apt-get install -y \
procps # ps for debugging
# Create a directory to hold source-code, dependencies etc
RUN mkdir /example
WORKDIR /example
# Clone clixon (again) since example application is there.
# Replace this with your application
RUN git clone https://github.com/clicon/clixon.git
# Build clixon
WORKDIR /example/clixon
RUN git checkout -b develop origin/develop
RUN ./configure
WORKDIR /example/clixon/example
RUN make
RUN make install
RUN install example.xml /usr/local/etc/clixon.xml
CMD /usr/local/sbin/clixon_backend && /usr/local/bin/clixon_cli

View file

@ -37,8 +37,13 @@ prefix = @prefix@
bindir = @bindir@
includedir = @includedir@
datarootdir = @datarootdir@
sysconfdir = @sysconfdir@
datarootdir = @datarootdir@
localstatedir = @localstatedir@
libdir = @exec_prefix@/lib
APPNAME = example
CC = @CC@
CFLAGS = @CFLAGS@ -rdynamic -fPIC
INSTALLFLAGS = @INSTALLFLAGS@
@ -51,12 +56,14 @@ CLI_PLUGIN = $(APPNAME)_cli.so
NETCONF_PLUGIN = $(APPNAME)_netconf.so
RESTCONF_PLUGIN = $(APPNAME)_restconf.so
# Example docker image. PLEASE CHANGE THIS
IMAGE = olofhagsand/clixon_example
PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN)
all: $(PLUGINS)
.PHONY: all clean depend install docker push
# Note: clixon.mk has rules for clixon_DBSPECDIR, clixon_SYSCONFDIR, etc used below
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
all: $(PLUGINS)
CLISPECS = $(APPNAME)_cli.cli
@ -69,6 +76,7 @@ YANGSPECS += ietf-routing@2014-10-26.yang
YANGSPECS += ietf-ipv4-unicast-routing@2014-10-26.yang
YANGSPECS += ietf-ipv6-unicast-routing@2014-10-26.yang
YANGSPECS += ietf-ipsec@2016-03-09.yang
YANGSPECS += iana-if-type@2014-05-08.yang
# Backend plugin
BE_SRC = $(APPNAME)_backend.c
@ -105,42 +113,45 @@ OBJS = $(BE_OBJ) $(BE2_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) $(RESTCONF_OBJ)
clean:
rm -f $(PLUGINS) $(OBJS)
(cd docker && $(MAKE) $(MFLAGS) $@)
distclean: clean
rm -f Makefile *~ .depend
(cd docker && $(MAKE) $(MFLAGS) $@)
install: $(YANGSPECS) $(CLISPECS) $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) $(RESTCONF_PLUGIN) $(APPNAME).xml
install -d -m 0755 $(DESTDIR)$(clixon_SYSCONFDIR)
install -m 0644 $(APPNAME).xml $(DESTDIR)$(clixon_SYSCONFDIR)
install -d -m 0755 $(DESTDIR)$(clixon_DBSPECDIR)/yang
install -m 0644 $(YANGSPECS) $(DESTDIR)$(clixon_DBSPECDIR)/yang
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/cli
install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/cli
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/backend
install -m 0644 $(INSTALLFLAGS) $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/backend
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/netconf
install -m 0644 $(INSTALLFLAGS) $(NETCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/netconf
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/restconf
install -m 0644 $(INSTALLFLAGS) $(RESTCONF_PLUGIN) $(DESTDIR)$(clixon_LIBDIR)/restconf
install -d -m 0755 $(DESTDIR)$(clixon_LIBDIR)/clispec
install -m 0644 $(CLISPECS) $(DESTDIR)$(clixon_LIBDIR)/clispec
install -d -m 0755 $(DESTDIR)$(clixon_LOCALSTATEDIR)
(cd docker && $(MAKE) $(MFLAGS) $@)
install -d -m 0755 $(DESTDIR)$(sysconfdir)
install -m 0644 $(APPNAME).xml $(DESTDIR)$(sysconfdir)
install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang
install -m 0644 $(YANGSPECS) $(DESTDIR)$(datarootdir)/$(APPNAME)/yang
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/cli
install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/cli
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/backend
install -m 0644 $(INSTALLFLAGS) $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/backend
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/netconf
install -m 0644 $(INSTALLFLAGS) $(NETCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/netconf
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/restconf
install -m 0644 $(INSTALLFLAGS) $(RESTCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/restconf
install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/clispec
install -m 0644 $(CLISPECS) $(DESTDIR)$(libdir)/$(APPNAME)/clispec
install -d -m 0755 $(DESTDIR)$(localstatedir)/$(APPNAME)
docker:
sudo docker build -t $(IMAGE) .
push:
sudo docker push $(IMAGE)
uninstall:
rm -rf $(DESTDIR)$(clixon_SYSCONFDIR)/$(APPNAME).xml
rm -rf $(DESTDIR)$(clixon_DBSPECDIR)
rm -rf $(DESTDIR)$(clixon_LOCALSTATEDIR)
rm -rf $(DESTDIR)$(clixon_LIBDIR)
(cd docker && $(MAKE) $(MFLAGS) $@)
echo "libdir:$(libdir)"
echo "libdir2:$(libdir2)"
rm -rf $(DESTDIR)$(sysconfdir)/$(APPNAME).xml
rm -rf $(DESTDIR)$(datarootdir)/$(APPNAME)
rm -rf $(DESTDIR)$(localstatedir)/$(APPNAME)
rm -rf $(DESTDIR)$(libdir)/$(APPNAME)
install-include:
depend:
$(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
(cd docker && $(MAKE) $(MFLAGS) $@)
#include .depend

View file

@ -16,14 +16,20 @@ routing example. It contains the following files:
* example_netconf.c Netconf callback plugin
* Makefile.in Example makefile where plugins are built and installed
## Compile and run
Before you start,
* Make [group setup](../doc/FAQ.md#do-i-need-to-setup-anything-important)
* Setup [restconf](../doc/FAQ.md#how-do-i-use-restconf)
```
cd example
make && sudo make install
```
Start backend:
```
clixon_backend -f /usr/local/etc/example.xml -I
sudo clixon_backend -f /usr/local/etc/example.xml -s init
```
Edit cli:
```
@ -188,6 +194,10 @@ The example contains some stubs for authorization according to [RFC8341(NACM)](h
* A NACM backend plugin reporting the mandatory NACM state variables.
## Systemd files
Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example.
## Run as docker container
(Note not updated)
@ -196,5 +206,19 @@ cd docker
# look in README
```
## Docker
Run the example as a docker container as follows:
```
sudo docker run -ti --rm olofhagsand/clixon_example
```
Build the container and push yourself: First change the IMAGE variable in Makefile (eg to "you/clixon_example). Then build and push:
```
make docker
make push
sudo docker run -ti --rm you/clixon_example
```

View file

@ -1,80 +0,0 @@
#
# ***** BEGIN LICENSE BLOCK *****
#
# Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
#
# This file is part of CLIXON
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Alternatively, the contents of this file may be used under the terms of
# the GNU General Public License Version 3 or later (the "GPL"),
# in which case the provisions of the GPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of the GPL, and not to allow others to
# use your version of this file under the terms of Apache License version 2,
# indicate your decision by deleting the provisions above and replace them with
# the notice and other provisions required by the GPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the Apache License version 2 or the GPL.
#
# ***** END LICENSE BLOCK *****
#
VPATH = @srcdir@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
prefix = @prefix@
bindir = @bindir@
includedir = @includedir@
datarootdir = @datarootdir@
APPNAME = routing
all: $(APPNAME).conf
-include $(DESTDIR)$(datarootdir)/clixon/clixon.mk
# Kind of reverse install, could have copied from src dir,...
.PHONY: docker push
docker: $(APPNAME).conf
install -d data
install -d data/yang
install -d data/backend
install -d data/cli
install -d data/netconf
install -d data/clispec
install $(APPNAME).conf data/clixon.conf # docker image assumes /data/clixon.conf
install ../*.yang data/yang/
install ../routing_cli.so data/cli/
install ../routing_backend.so data/backend/
install ../routing_netconf.so data/netconf/
install ../*.cli data/clispec
clean:
rm -f $(APPNAME).conf
distclean: clean
rm -f Makefile *~ .depend
rm -rf data
install:
uninstall:
install-include:
depend:
$(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend
#include .depend

View file

@ -1,7 +0,0 @@
Run the ietf routing example as docker container.
Use the dockerhub container, or alternatively, build clicon as docker images
by doing make docker in the top builddir.
(cd ..; make) # Make example
make docker # Create config file and shared file system
run.sh # Run a backend and a cli container

View file

@ -1,66 +0,0 @@
# Main YANG module first parsed by parser (in CLICON_YANG_DIR). eg clicon.yang.
# Location of configuration-file for default values (this file)
CLICON_CONFIGFILE /data/clixon.conf
# Location of YANG module and submodule files. Only if CLICON_DBSPEC_TYPE is YANG
CLICON_YANG_DIR /data/yang
# Option used to construct initial yang file:
# <module>[@<revision>]
# This option is only relevant if CLICON_DBSPEC_TYPE is YANG
# CLICON_YANG_MODULE_MAIN clicon
# Option used to construct initial yang file:
# <module>[@<revision>]
# This option is only relevant if CLICON_DBSPEC_TYPE is YANG
CLICON_YANG_MODULE_REVISION
# Candidate qdbm database
CLICON_CANDIDATE_DB /data/candidate_db
# Running qdbm database
CLICON_RUNNING_DB /data/running_db
# Location of backend .so plugins
CLICON_BACKEND_DIR /data/backend
# Location of netconf (frontend) .so plugins
CLICON_NETCONF_DIR /data/netconf
# Location of cli frontend .so plugins
CLICON_CLI_DIR /data/cli
# Location of frontend .cli cligen spec files
CLICON_CLISPEC_DIR /data/clispec
# Directory where to save configuration commit history (in XML). Snapshots
# are saved chronologically
CLICON_ARCHIVE_DIR /data
# XXX Name of startup configuration file (in XML)
CLICON_STARTUP_CONFIG /data/startup-config
# Address family for communicating with clixon_backend (UNIX|IPv4|IPv6)
CLICON_SOCK_FAMILY UNIX
# If family above is AF_UNIX: Unix socket for communicating with clixon_backend
# If family above is AF_INET: IPv4 address
CLICON_SOCK /data/routing.sock
# Inet socket port for communicating with clixon_backend (only IPv4|IPv6)
CLICON_SOCK_PORT 4535
# Process-id file
CLICON_BACKEND_PIDFILE /data/routing.pidfile
# Save values as XML in database instead of lvec:s.
# This is optimized for yang specified applications
# But not compatible with key-based application (eg Rost)
CLICON_DB_XML 1
# Startup CLI mode. This should match the CLICON_MODE in your startup clispec file
CLICON_CLI_MODE routing
# Option used to construct initial yang file:
# <module>[@<revision>]
# This option is only relevant if CLICON_DBSPEC_TYPE is YANG
CLICON_YANG_MODULE_MAIN ietf-ip
# Option used to construct initial yang file:
# <module>[@<revision>]
# This option is only relevant if CLICON_DBSPEC_TYPE is YANG
CLICON_YANG_MODULE_REVISION 2014-06-16
# Generate code for CLI completion of existing db symbols
# CLICON_CLI_GENMODEL_COMPLETION 0
CLICON_CLI_GENMODEL_COMPLETION 1
# How to generate and show CLI syntax: VARS|ALL
# CLICON_CLI_GENMODEL_TYPE VARS
CLICON_CLI_GENMODEL_TYPE VARS

View file

@ -1,8 +0,0 @@
#!/bin/bash
# Start daemon and a cli docker containers .
# Note that they have a common file-system at /data
#
sudo docker run -td --net host -v $(pwd)/data:/data olofhagsand/clixon_backend
sudo docker run -ti --rm --net host -v $(pwd)/data:/data olofhagsand/clixon_cli

View file

@ -1,13 +1,33 @@
module example {
prefix ex;
import ietf-interfaces {
prefix if;
}
import ietf-ip {
prefix ip;
}
import ietf-routing {
prefix rt;
}
import iana-if-type {
prefix ianaift;
}
description
"Example code that includes ietf-ip and ietf-routing";
/* Example interface type for tests, local callbacks, etc */
identity eth {
base if:interface-type;
}
identity loopback {
base if:interface-type;
}
/* Translation function example - See also example_cli */
list translate{
leaf value{
type string;
}
}
rpc client-rpc {
description "Example local client-side RPC that is processed by the
the netconf/restconf and not sent to the backend.

View file

@ -180,7 +180,7 @@ empty(clicon_handle h, /* Clicon handle */
* Real code would poll state
*/
int
plugin_statedata(clicon_handle h,
example_statedata(clicon_handle h,
char *xpath,
cxobj *xstate)
{
@ -190,7 +190,7 @@ plugin_statedata(clicon_handle h,
/* Example of (static) statedata, real code would poll state */
if (xml_parse_string("<interfaces-state><interface>"
"<name>eth0</name>"
"<type>eth</type>"
"<type>ex:eth</type>"
"<if-index>42</if-index>"
"</interface></interfaces-state>", NULL, &xstate) < 0)
goto done;
@ -214,14 +214,14 @@ plugin_statedata(clicon_handle h,
* @note This assumes example yang with interfaces/interface
*/
int
plugin_reset(clicon_handle h,
const char *db)
example_reset(clicon_handle h,
const char *db)
{
int retval = -1;
cxobj *xt = NULL;
if (xml_parse_string("<config><interfaces><interface>"
"<name>lo</name><type>local</type>"
"<name>lo</name><type>ex:loopback</type>"
"</interface></interfaces></config>", NULL, &xt) < 0)
goto done;
/* Replace parent w fiorst child */
@ -250,7 +250,7 @@ plugin_reset(clicon_handle h,
* can be processed with the standard getopt(3).
*/
int
plugin_start(clicon_handle h,
example_start(clicon_handle h,
int argc,
char **argv)
{
@ -261,11 +261,11 @@ clixon_plugin_api *clixon_plugin_init(clicon_handle h);
static clixon_plugin_api api = {
"example", /* name */
clixon_plugin_init, /* init */
plugin_start, /* start */
clixon_plugin_init, /* init - must be called clixon_plugin_init */
example_start, /* start */
NULL, /* exit */
.ca_reset=plugin_reset, /* reset */
.ca_statedata=plugin_statedata, /* statedata */
.ca_reset=example_reset, /* reset */
.ca_statedata=example_statedata, /* statedata */
.ca_trans_begin=NULL, /* trans begin */
.ca_trans_validate=transaction_validate,/* trans validate */
.ca_trans_complete=NULL, /* trans complete */

View file

@ -114,6 +114,13 @@ static clixon_plugin_api api = {
clixon_plugin_api *
clixon_plugin_init(clicon_handle h)
{
char *nacm_mode;
clicon_debug(1, "%s backend nacm", __FUNCTION__);
nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
if (nacm_mode==NULL || strcmp(nacm_mode, "disabled") == 0){
clicon_debug(1, "%s CLICON_NACM_MODE not enabled: example nacm module disabled", __FUNCTION__);
return NULL;
}
return &api;
}

View file

@ -67,7 +67,12 @@ mycallback(clicon_handle h, cvec *cvv, cvec *argv)
cli_output(stderr, "arg = %s\n", cv_string_get(cvec_i(argv,0))); /* get string value */
/* Show eth0 interfaces config using XPATH */
if (clicon_rpc_get_config(h, "running","/interfaces/interface[name=eth0]",
if (clicon_rpc_get_config(h, "running",
#ifdef COMPAT_XSL
"/interfaces/interface[name=eth0]",
#else
"/interfaces/interface[name='eth0']",
#endif
&xret) < 0)
goto done;
@ -90,17 +95,24 @@ fib_route_rpc(clicon_handle h,
cxobj *xtop = NULL;
cxobj *xrpc;
cxobj *xret = NULL;
cxobj *xerr;
/* User supplied variable in CLI command */
instance = cvec_find(cvv, "instance"); /* get a cligen variable from vector */
/* Create XML for fib-route netconf RPC */
if (xml_parse_va(&xtop, NULL, "<rpc><fib-route><routing-instance-name>%s</routing-instance-name></fib-route></rpc>", cv_string_get(instance)) < 0)
if (xml_parse_va(&xtop, NULL, "<rpc username=\"%s\"><fib-route><routing-instance-name>%s</routing-instance-name></fib-route></rpc>",
clicon_username_get(h),
cv_string_get(instance)) < 0)
goto done;
/* Skip top-level */
xrpc = xml_child_i(xtop, 0);
/* Send to backend */
if (clicon_rpc_netconf_xml(h, xrpc, &xret, NULL) < 0)
goto done;
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
clicon_rpc_generate_error("Get configuration", xerr);
goto done;
}
/* Print result */
xml_print(stdout, xml_child_i(xret, 0));
retval = 0;
@ -137,3 +149,21 @@ clixon_plugin_init(clicon_handle h)
return &api;
}
/*! Translate function from an original value to a new.
* In this case, assume string and increment characters, eg HAL->IBM
*/
int
incstr(cligen_handle h,
cg_var *cv)
{
char *str;
int i;
if (cv_type_get(cv) != CGV_STRING)
return 0;
str = cv_string_get(cv);
for (i=0; i<strlen(str); i++)
str[i]++;
return 0;
}

View file

@ -3,6 +3,9 @@ CLICON_MODE="example";
CLICON_PROMPT="%U@%H> ";
CLICON_PLUGIN="example_cli";
# Translate variable "value" by incrementing its characters
translate value (<value:string translate:incstr()>),cli_set("/translate/value");
# Note, when switching to PT, change datamodel to only @datamodel
set @datamodel:example, cli_set();
merge @datamodel:example, cli_merge();
@ -21,7 +24,7 @@ debug("Debugging parts of the system"), cli_debug_cli((int32)1);{
}
copy("Copy and create a new object") {
interface("Copy interface"){
<name:string expand_dbvar("candidate","/interfaces/interface=%s/name")>("name of interface to copy from") to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s=%s]","name","name","toname");
<name:string expand_dbvar("candidate","/interfaces/interface=%s/name")>("name of interface to copy from") to("Copy to interface") <toname:string>("Name of interface to copy to"), cli_copy_config("candidate","//interface[%s='%s']","name","name","toname");
}
}
discard("Discard edits (rollback 0)"), discard_changes();
@ -36,11 +39,21 @@ show("Show a particular state of the system"){
text("Show comparison in text"), compare_dbs((int32)1);
}
configuration("Show configuration"), cli_show_config("candidate", "text", "/");{
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");
text("Show configuration as text"), cli_show_config("candidate","text","/");
cli("Show configuration as cli commands"), cli_show_config("candidate", "cli", "/");
json("Show configuration as cli commands"), cli_show_config("candidate", "json", "/");
xml("Show configuration as XML"), cli_show_config("candidate", "xml", "/");{
@datamodel:example, cli_show_auto("candidate", "text");
}
cli("Show configuration as CLI commands"), cli_show_config("candidate", "cli", "/");{
@datamodel:example, cli_show_auto("candidate", "cli");
}
netconf("Show configuration as netconf edit-config operation"), cli_show_config("candidate", "netconf", "/");{
@datamodel:example, cli_show_auto("candidate", "netconf");
}
text("Show configuration as text"), cli_show_config("candidate","text","/");{
@datamodel:example, cli_show_auto("candidate", "text");
}
json("Show configuration as JSON"), cli_show_config("candidate", "json", "/");{
@datamodel:example, cli_show_auto("candidate", "json");
}
}
}
@ -50,7 +63,7 @@ load("Load configuration from XML file") <filename:string>("Filename (local file
merge("Merge file with existent candidate"), load_config_file("filename", "merge");
}
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");
rpc("fib-route rpc") <instance:string>("routing instance"), fib_route_rpc("myarg");
rpc("ex:fib-route rpc") <instance:string>("routing instance"), fib_route_rpc("myarg");
notify("Get notifications from backend"), cli_notify("ROUTING", "1", "text");
no("Negate") notify("Get notifications from backend"), cli_notify("ROUTING", "0", "xml");
lock,cli_lock("candidate");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
[Unit]
Description=Starts and stops a clixon example service on this system
Wants=example_restconf.service
[Service]
Type=forking
User=root
RestartSec=60
Restart=on-failure
ExecStart=/usr/local/sbin/clixon_backend -s running -f /usr/local/etc/example.xml
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,14 @@
[Unit]
Description=Starts and stops an example clixon restconf service on this system
Wants=example.service
After=example.service
[Service]
Type=simple
User=www-data
WorkingDirectory=/www-data
Restart=on-failure
ExecStart=/www-data/clixon_restconf -f /usr/local/etc/example.xml
[Install]
WantedBy=multi-user.target

View file

@ -1,142 +0,0 @@
/* include/clixon_config.h.in. Generated from configure.ac by autoheader. */
/* Clixon data dir for system yang files etc */
#undef CLIXON_DATADIR
/* Location for apps to find default config file */
#undef CLIXON_DEFAULT_CONFIG
/* Clixon major release */
#undef CLIXON_VERSION_MAJOR
/* Clixon minor release */
#undef CLIXON_VERSION_MINOR
/* Clixon path version */
#undef CLIXON_VERSION_PATCH
/* Clixon version string */
#undef CLIXON_VERSION_STRING
/* Define to 1 if you have the `alphasort' function. */
#undef HAVE_ALPHASORT
/* Define to 1 if you have the <cligen/cligen.h> header file. */
#undef HAVE_CLIGEN_CLIGEN_H
/* Define to 1 if you have the <crypt.h> header file. */
#undef HAVE_CRYPT_H
/* Define to 1 if you have the <depot.h> header file. */
#undef HAVE_DEPOT_H
/* Define to 1 if you have the `inet_aton' function. */
#undef HAVE_INET_ATON
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `cligen' library (-lcligen). */
#undef HAVE_LIBCLIGEN
/* Define to 1 if you have the `crypt' library (-lcrypt). */
#undef HAVE_LIBCRYPT
/* Define to 1 if you have the `dl' library (-ldl). */
#undef HAVE_LIBDL
/* Define to 1 if you have the `fcgi' library (-lfcgi). */
#undef HAVE_LIBFCGI
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define to 1 if you have the `nsl' library (-lnsl). */
#undef HAVE_LIBNSL
/* Define to 1 if you have the `qdbm' library (-lqdbm). */
#undef HAVE_LIBQDBM
/* Define to 1 if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
/* Define to 1 if you have the <linux/if_vlan.h> header file. */
#undef HAVE_LINUX_IF_VLAN_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <qdbm/depot.h> header file. */
#undef HAVE_QDBM_DEPOT_H
/* Define to 1 if you have the `sigaction' function. */
#undef HAVE_SIGACTION
/* Define to 1 if you have the `sigvec' function. */
#undef HAVE_SIGVEC
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the `strlcpy' function. */
#undef HAVE_STRLCPY
/* Define to 1 if you have the `strndup' function. */
#undef HAVE_STRNDUP
/* Define to 1 if you have the `strsep' function. */
#undef HAVE_STRSEP
/* Define to 1 if you have the `strverscmp' function. */
#undef HAVE_STRVERSCMP
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <sys/ucred.h> header file. */
#undef HAVE_SYS_UCRED_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `versionsort' function. */
#undef HAVE_VERSIONSORT
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
#undef YYTEXT_POINTER
#include <clixon_custom.h>

View file

@ -1,134 +0,0 @@
/* include/clixon_config.h. Generated from clixon_config.h.in by configure. */
/* include/clixon_config.h.in. Generated from configure.ac by autoheader. */
/* Clixon major release */
#define CLIXON_VERSION_MAJOR 3
/* Clixon minor release */
#define CLIXON_VERSION_MINOR 3
/* Clixon path version */
#define CLIXON_VERSION_PATCH 2
/* Clixon version string */
#define CLIXON_VERSION_STRING "3.3.2"
/* Define to 1 if you have the `alphasort' function. */
#define HAVE_ALPHASORT 1
/* Define to 1 if you have the <cligen/cligen.h> header file. */
#define HAVE_CLIGEN_CLIGEN_H 1
/* Define to 1 if you have the <crypt.h> header file. */
#define HAVE_CRYPT_H 1
/* Define to 1 if you have the <depot.h> header file. */
/* #undef HAVE_DEPOT_H */
/* Define to 1 if you have the `inet_aton' function. */
#define HAVE_INET_ATON 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `crypt' library (-lcrypt). */
#define HAVE_LIBCRYPT 1
/* Define to 1 if you have the `dl' library (-ldl). */
#define HAVE_LIBDL 1
/* Define to 1 if you have the `fcgi' library (-lfcgi). */
#define HAVE_LIBFCGI 1
/* Define to 1 if you have the `m' library (-lm). */
#define HAVE_LIBM 1
/* Define to 1 if you have the `nsl' library (-lnsl). */
#define HAVE_LIBNSL 1
/* Define to 1 if you have the `qdbm' library (-lqdbm). */
#define HAVE_LIBQDBM 1
/* Define to 1 if you have the `socket' library (-lsocket). */
/* #undef HAVE_LIBSOCKET */
/* Define to 1 if you have the <linux/if_vlan.h> header file. */
#define HAVE_LINUX_IF_VLAN_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <qdbm/depot.h> header file. */
#define HAVE_QDBM_DEPOT_H 1
/* Define to 1 if you have the `sigaction' function. */
#define HAVE_SIGACTION 1
/* Define to 1 if you have the `sigvec' function. */
#define HAVE_SIGVEC 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strlcpy' function. */
/* #undef HAVE_STRLCPY */
/* Define to 1 if you have the `strndup' function. */
#define HAVE_STRNDUP 1
/* Define to 1 if you have the `strsep' function. */
#define HAVE_STRSEP 1
/* Define to 1 if you have the `strverscmp' function. */
#define HAVE_STRVERSCMP 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/ucred.h> header file. */
/* #undef HAVE_SYS_UCRED_H */
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the `versionsort' function. */
#define HAVE_VERSIONSORT 1
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME ""
/* Define to the full name and version of this package. */
#define PACKAGE_STRING ""
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME ""
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION ""
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
#define YYTEXT_POINTER 1
#include <clixon_custom.h>

View file

@ -43,6 +43,26 @@
int strverscmp (__const char *__s1, __const char *__s2);
#endif
/* Set if you want to enable "v" cli callback functions, such as cli_setv()
* This was obsoleted in 3.7
*/
#undef COMPAT_CLIV
/* Set if you want to assert that all rpc messages have set username
*/
#undef RPC_USERNAME_ASSERT
/* Full xmlns validation check is made only if XML has associated YANG spec
*/
#define XMLNS_YANG_ONLY 1
/* Set if you want to enable old xpath functions in clixon_xsl.* instead of the
* the new xpath functions in clixon_xpath.*
* Note that when changing from old xpath code to new, calls on the form
* `x[a=str]` where `str` is a string (not a number or XML symbol),
* must be changed to: `x[a='str'] or x[a="str"]`
* Enabling COMPAT_XSL may make sense if you have written a lot of user code that
* relieson the error above. Or if a bug appears in the newimplementation.
* @see test/lib.sh
*/
#undef COMPAT_XSL

View file

@ -82,6 +82,8 @@
#include <clixon/clixon_xml_map.h>
#include <clixon/clixon_xml_db.h>
#include <clixon/clixon_xsl.h>
#include <clixon/clixon_xpath_ctx.h>
#include <clixon/clixon_xpath.h>
#include <clixon/clixon_json.h>
#include <clixon/clixon_netconf_lib.h>

View file

@ -85,7 +85,11 @@ extern char clicon_err_reason[ERR_STRLEN];
* Prototypes
*/
int clicon_err_reset(void);
#if defined(__GNUC__) && __GNUC__ >= 3
int clicon_err_fn(const char *fn, const int line, int level, int err, char *format, ...) __attribute__ ((format (printf, 5, 6)));
#else
int clicon_err_fn(const char *fn, const int line, int level, int err, char *format, ...);
#endif
char *clicon_strerror(int err);
void *clicon_err_save(void);
int clicon_err_restore(void *handle);

View file

@ -62,10 +62,16 @@ extern int debug;
int clicon_log_init(char *ident, int upto, int flags);
int clicon_get_logflags(void);
int clicon_log_str(int level, char *msg);
#if defined(__GNUC__) && __GNUC__ >= 3
int clicon_log(int level, char *format, ...) __attribute__ ((format (printf, 2, 3)));
int clicon_debug(int dbglevel, char *format, ...) __attribute__ ((format (printf, 2, 3)));
#else
int clicon_log(int level, char *format, ...);
int clicon_debug(int dbglevel, char *format, ...);
#endif
clicon_log_notify_t *clicon_log_register_callback(clicon_log_notify_t *cb, void *arg);
int clicon_debug_init(int dbglevel, FILE *f);
int clicon_debug(int dbglevel, char *format, ...);
char *mon2name(int md);
#endif /* _CLIXON_LOG_H_ */

View file

@ -41,7 +41,9 @@
/*
* Constants
*/
/* Hardcoded plugin symbol. Must exist in all plugins to kickstart */
/* Hardcoded plugin symbol. Must exist in all plugins to kickstart
* @see clixon_plugin_init
*/
#define CLIXON_PLUGIN_INIT "clixon_plugin_init"
/*
@ -91,8 +93,9 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
/* Plugin authorization. Set username option (or not)
* @param[in] Clicon handle
* @param[in] void*, eg Fastcgihandle request restconf
* @retval 0 if credentials OK
* @retval -1 credentials not OK
* @retval -1 Fatal error
* @retval 0 Credential not OK
* @retval 1 Credential OK
*/
typedef int (plgauth_t)(clicon_handle, void *);
@ -180,6 +183,7 @@ typedef struct clixon_plugin clixon_plugin;
/*! Plugin initialization function. Must appear in all plugins
* @param[in] h Clixon handle
* @retval api Pointer to API struct
* @retval NULL Failure (if clixon_err() called), module disabled otherwise.
* @see CLIXON_PLUGIN_INIT default symbol
*/
clixon_plugin_api *clixon_plugin_init(clicon_handle h);

View file

@ -61,7 +61,11 @@ struct clicon_msg {
char *format_int2str(enum format_enum showas);
enum format_enum format_str2int(char *str);
#if defined(__GNUC__) && __GNUC__ >= 3
struct clicon_msg *clicon_msg_encode(char *format, ...) __attribute__ ((format (printf, 1, 2)));
#else
struct clicon_msg *clicon_msg_encode(char *format, ...);
#endif
int clicon_msg_decode(struct clicon_msg *msg, cxobj **xml);
int clicon_connect_unix(char *sockpath);

View file

@ -46,6 +46,8 @@ static const map_str2int atmap[] = {
{NULL, -1}
};
* @endcode
* @see clicon_int2str
* @see clicon_str2int
*/
struct map_str2int{
char *ms_str;

View file

@ -69,7 +69,7 @@ typedef struct xml cxobj; /* struct defined in clicon_xml.c */
* @retval 1 Abort, dont continue with others
* @retval 2 Locally, just abort this subtree, continue with others
*/
typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
typedef int (xml_applyfn_t)(cxobj *x, void *arg);
/*
* xml_flag() flags:
@ -80,6 +80,7 @@ typedef int (xml_applyfn_t)(cxobj *yn, void *arg);
#define XML_FLAG_CHANGE 0x08 /* Node is changed (commits) or child changed rec */
#define XML_FLAG_NONE 0x10 /* Node is added as NONE */
/* Sort and binary search of XML children
* Experimental
*/
@ -106,9 +107,6 @@ char *xml_value_append(cxobj *xn, char *val);
enum cxobj_type xml_type(cxobj *xn);
int xml_type_set(cxobj *xn, enum cxobj_type type);
cg_var *xml_cv_get(cxobj *xn);
int xml_cv_set(cxobj *xn, cg_var *cv);
int xml_child_nr(cxobj *xn);
int xml_child_nr_type(cxobj *xn, enum cxobj_type type);
cxobj *xml_child_i(cxobj *xn, int i);

103
lib/clixon/clixon_xpath.h Normal file
View file

@ -0,0 +1,103 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
*/
#ifndef _CLIXON_XPATH_H
#define _CLIXON_XPATH_H
/*
* Types
*/
enum xp_op{
XO_AND,
XO_OR,
XO_DIV,
XO_MOD,
XO_ADD,
XO_MULT,
XO_SUB,
XO_EQ,
XO_NE,
XO_GE,
XO_LE,
XO_LT,
XO_GT,
XO_UNION,
};
/* Axis specifiers according to https://www.w3.org/TR/xpath-10/#NT-AxisName */
enum axis_type{
A_NAN = 0, /* Not set */
A_ANCESTOR,
A_ANCESTOR_OR_SELF,
A_ATTRIBUTE,
A_CHILD,
A_DESCENDANT,
A_DESCENDANT_OR_SELF,
A_FOLLOWING,
A_FOLLOWING_SIBLING,
A_NAMESPACE,
A_PARENT,
A_PRECEEDING,
A_PRECEEDING_SIBLING,
A_SELF,
A_ROOT /* XXX Not in https://www.w3.org/TR/xpath-10 */
};
/*
* Variables
*/
extern const map_str2int xpopmap[];
/*
* Prototypes
*/
#if defined(__GNUC__) && __GNUC__ >= 3
int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 5)));
int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags,
cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 2, 6)));
cxobj *xpath_first(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3)));
int xpath_vec_bool(cxobj *xcur, char *format, ...) __attribute__ ((format (printf, 2, 3)));
#else
int xpath_vec(cxobj *xcur, char *format, cxobj ***vec, size_t *veclen, ...);
int xpath_vec_flag(cxobj *xcur, char *format, uint16_t flags,
cxobj ***vec, size_t *veclen, ...);
cxobj *xpath_first(cxobj *xcur, char *format, ...);
int xpath_vec_bool(cxobj *xcur, char *format, ...);
#endif
int xpath_vec_ctx(cxobj *xcur, char *xpath, xp_ctx **xrp);
#endif /* _CLIXON_XPATH_H */

View file

@ -0,0 +1,103 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
* This file defines XPATH contexts using in traversing the XPATH parse tree.
*/
#ifndef _CLIXON_XPATH_CTX_H
#define _CLIXON_XPATH_CTX_H
/*
* Types
*/
/*! XPATH expression type
* An expression is evaluated to yield an object, which has one of the following four basic types:
* node-set (an unordered collection of nodes without duplicates)
* boolean (true or false)
* number (a floating-point number)
* string (a sequence of UCS characters)
*/
enum xp_objtype{
XT_NODESET,
XT_BOOL,
XT_NUMBER,
XT_STRING
};
/* Expression evaluation occurs with respect to a context. XSLT and XPointer specify how the context is
* determined for XPath expressions used in XSLT and XPointer respectively. The context consists of:
* a node (the context node)
* a pair of non-zero positive integers (the context position and the context size)
* a set of variable bindings
* a function library
* the set of namespace declarations in scope for the expression
* For each node in the node-set to be filtered, the PredicateExpr is
* evaluated with that node as the context node, with the number of nodes
* in the node-set as the context size, and with the proximity position
* of the node in the node-set with respect to the axis as the context
* position; if PredicateExpr evaluates to true for that node, the node
* is included in the new node-set; otherwise, it is not included.
*/
struct xp_ctx{
enum xp_objtype xc_type;
cxobj **xc_nodeset; /* if type XT_NODESET */
size_t xc_size; /* Length of nodeset */
int xc_bool; /* if xc_type XT_BOOL */
double xc_number; /* if xc_type XT_NUMBER */
char *xc_string; /* if xc_type XT_STRING */
cxobj *xc_node; /* Node in nodeset XXX maybe not needed*/
cxobj *xc_initial; /* RFC 7960 10.1.1 extension: for current() */
int xc_descendant; /* // */
/* NYI: a set of variable bindings, set of namespace declarations */
};
typedef struct xp_ctx xp_ctx;
/*
* Variables
*/
extern const map_str2int ctxmap[];
/*
* Prototypes
*/
int ctx_free(xp_ctx *xc);
xp_ctx *ctx_dup(xp_ctx *xc);
int ctx_nodeset_replace(xp_ctx *xc, cxobj **vec, size_t veclen);
int ctx_print(cbuf *cb, int id, xp_ctx *xc, char *str);
int ctx2boolean(xp_ctx *xc);
int ctx2string(xp_ctx *xc, char **str0);
int ctx2number(xp_ctx *xc, double *n0);
#endif /* _CLIXON_XPATH_CTX_H */

View file

@ -39,10 +39,12 @@
/*
* Prototypes
*/
cxobj *xpath_first(cxobj *cxtop, char *format, ...);
int xpath_vec_xsl(cxobj *cxtop, char *xpath, cxobj ***vec, size_t *veclen);
int xpath_vec_flag_xsl(cxobj *cxtop, char *xpath, uint16_t flags,
cxobj ***vec, size_t *veclen);
cxobj *xpath_first_xsl(cxobj *cxtop, char *xpath);
#ifdef COMPAT_XSL
cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev);
int xpath_vec(cxobj *cxtop, char *format, cxobj ***vec, size_t *veclen, ...);
int xpath_vec_flag(cxobj *cxtop, char *xpath, uint16_t flags,
cxobj ***vec, size_t *veclen, ...);
#endif
#endif /* _CLIXON_XSL_H */

View file

@ -202,6 +202,7 @@ struct yang_stmt{
cvec *ys_cvec; /* List of stmt-specific variables
Y_RANGE: range_min, range_max
Y_LIST: vector of keys
Y_TYPE & identity: store all derived types
*/
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
};
@ -245,6 +246,7 @@ yang_stmt *yn_each(yang_node *yn, yang_stmt *ys);
char *yang_key2str(int keyword);
char *yarg_prefix(yang_stmt *ys);
char *yarg_id(yang_stmt *ys);
int yang_nodeid_split(char *nodeid, char **prefix, char **id);
yang_stmt *ys_module(yang_stmt *ys);
yang_spec *ys_spec(yang_stmt *ys);
yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
@ -252,9 +254,11 @@ yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, yang_class class);
char *yang_find_myprefix(yang_stmt *ys);
int yang_order(yang_stmt *y);
int yang_print(FILE *f, yang_node *yn);
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
yang_stmt *yang_parse_file(int fd, const char *name, yang_spec *ysp);
int yang_parse(clicon_handle h, const char *yang_dir,
const char *module, const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,

View file

@ -45,6 +45,7 @@ sysconfdir = @sysconfdir@
HOST_VENDOR = @host_vendor@
SH_SUFFIX = @SH_SUFFIX@
CLIXON_VERSION = @CLIXON_VERSION@
CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@
CLIXON_MINOR = @CLIXON_VERSION_MINOR@
@ -52,6 +53,8 @@ CLIXON_MINOR = @CLIXON_VERSION_MINOR@
VPATH = @srcdir@
CC = @CC@
CFLAGS = -fPIC @CFLAGS@
INSTALL = @INSTALL@
INSTALL_LIB = @INSTALL@
INSTALLFLAGS = @INSTALLFLAGS@
LDFLAGS = @LDFLAGS@
LIBS = @LIBS@
@ -69,12 +72,13 @@ SRC = clixon_sig.c clixon_log.c clixon_err.c clixon_event.c \
clixon_json.c clixon_yang.c clixon_yang_type.c \
clixon_hash.c clixon_options.c clixon_plugin.c \
clixon_proto.c clixon_proto_client.c \
clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_netconf_lib.c
clixon_xsl.c clixon_xpath.c clixon_xpath_ctx.c clixon_sha1.c \
clixon_xml_db.c clixon_netconf_lib.c
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
lex.clixon_json_parse.o clixon_json_parse.tab.o
lex.clixon_json_parse.o clixon_json_parse.tab.o \
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o
# Generated src
GENSRC = build.c
@ -96,9 +100,11 @@ clean:
rm -f clixon_xml_parse.tab.[ch] clixon_xml_parse.yy.[co]
rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co]
rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co]
rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co]
rm -f lex.clixon_xml_parse.c
rm -f lex.clixon_yang_parse.c
rm -f lex.clixon_json_parse.c
rm -f lex.clixon_xpath_parse.c
#############################################################################
# Implicit rules for lex and yacc.
@ -148,6 +154,18 @@ clixon_json_parse.tab.c clixon_json_parse.tab.h: clixon_json_parse.y
lex.clixon_json_parse.o : lex.clixon_json_parse.c clixon_json_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# xpath parser
lex.clixon_xpath_parse.c : clixon_xpath_parse.l clixon_xpath_parse.tab.h
$(LEX) -Pclixon_xpath_parse clixon_xpath_parse.l # -d is debug
clixon_xpath_parse.tab.c clixon_xpath_parse.tab.h: clixon_xpath_parse.y
$(YACC) -l -d -p clixon_xpath_parse clixon_xpath_parse.y # -t is debug
mv y.tab.c clixon_xpath_parse.tab.c
mv y.tab.h clixon_xpath_parse.tab.h
lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
distclean: clean
rm -f Makefile *~ .depend
@ -162,7 +180,6 @@ build.c:
date +"const char CLIXON_BUILDSTR[64]=\"%Y.%m.%d %H:%M by `whoami` on `hostname`"\"\; > build.c;
echo "const char CLIXON_VERSION[64]=\"$(CLIXON_VERSION)\""\; >> build.c;
$(MYLIB) : $(GENOBJS) $(OBJS)
ifeq ($(HOST_VENDOR),apple)
$(CC) $(LDFLAGS) -shared -o $@ $(GENOBJS) $(OBJS) $(LIBS) -undefined dynamic_lookup -o $@
@ -182,8 +199,8 @@ install: install-lib
install-include:
install-lib: $(MYLIB)
install -m 0755 -d $(DESTDIR)$(libdir)
install -m 0644 $(INSTALLFLAGS) $(MYLIB) $(DESTDIR)$(libdir)
$(INSTALL) -m 0755 -d $(DESTDIR)$(libdir)
$(INSTALL_LIB) -m 0644 $(INSTALLFLAGS) $(MYLIB) $(DESTDIR)$(libdir)
ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclixon.so.3
ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclixon.so

View file

@ -102,7 +102,8 @@ static struct errvec EV[] = {
};
static char *
clicon_strerror1(int err, struct errvec vec[])
clicon_strerror1(int err,
struct errvec vec[])
{
struct errvec *ev;
@ -134,18 +135,18 @@ clicon_err_reset(void)
* - Set global reason string clicon_err_reason
* @note: err direction (syslog and/or stderr) controlled by clicon_log_init()
*
* @param fn Inline function name (when called from clicon_err() macro)
* @param line Inline file line number (when called from clicon_err() macro)
* @param err Error number, typically errno
* @param suberr Sub-error number
* @param reason Error string, format with argv
* @param[in] fn Inline function name (when called from clicon_err() macro)
* @param[in] line Inline file line number (when called from clicon_err() macro)
* @param[in] err Error number, typically errno
* @param[in] suberr Sub-error number
* @param[in] reason Error string, format with argv
*/
int
clicon_err_fn(const char *fn,
const int line,
int category,
int suberr,
char *reason, ...)
const int line,
int category,
int suberr,
char *reason, ...)
{
va_list args;
int len;

View file

@ -267,7 +267,7 @@ event_poll(int fd)
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
if ((retval = select(FD_SETSIZE, &fdset, NULL, NULL, &tnull)) < 0)
clicon_err(OE_EVENTS, errno, "%s select1: %s", __FUNCTION__, strerror(errno));
clicon_err(OE_EVENTS, errno, "select");
return retval;
}
@ -307,18 +307,17 @@ event_loop(void)
if (n == -1) {
if (errno == EINTR){
clicon_debug(1, "%s select: %s", __FUNCTION__, strerror(errno));
clicon_err(OE_EVENTS, errno, "%s select1: %s", __FUNCTION__, strerror(errno));
clicon_err(OE_EVENTS, errno, "select");
retval = 0;
}
else
clicon_err(OE_EVENTS, errno, "%s select2", __FUNCTION__);
clicon_err(OE_EVENTS, errno, "select");
goto err;
}
if (n==0){ /* Timeout */
e = ee_timers;
ee_timers = ee_timers->e_next;
clicon_debug(2, "%s timeout: %s[%x]",
__FUNCTION__, e->e_string, e->e_arg);
clicon_debug(2, "%s timeout: %s", __FUNCTION__, e->e_string);
if ((*e->e_fn)(0, e->e_arg) < 0){
free(e);
goto err;
@ -331,8 +330,7 @@ event_loop(void)
break;
e_next = e->e_next;
if(e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset)){
clicon_debug(2, "%s: FD_ISSET: %s[%x]",
__FUNCTION__, e->e_string, e->e_arg);
clicon_debug(2, "%s: FD_ISSET: %s", __FUNCTION__, e->e_string);
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0){
clicon_debug(1, "%s Error in: %s", __FUNCTION__, e->e_string);
goto err;

View file

@ -241,12 +241,11 @@ group_name2gid(char *name,
gr = &g0;
/* This leaks memory in ubuntu */
if (getgrnam_r(name, gr, buf, sizeof(buf), &gtmp) < 0){
clicon_err(OE_UNIX, errno, "%s: getgrnam_r(%s): %s",
__FUNCTION__, name, strerror(errno));
clicon_err(OE_UNIX, errno, "getgrnam_r(%s)", name);
return -1;
}
if (gtmp == NULL){
clicon_err(OE_UNIX, 0, "%s: No such group: %s", __FUNCTION__, name);
clicon_err(OE_UNIX, 0, "No such group: %s", name);
fprintf(stderr, "No such group %s\n", name);
return -1;
}

View file

@ -52,24 +52,24 @@
* clicon_hash_t *hash = hash_init();
*
* n = 234;
* hash_add (hash, "APA", &n, sizeof(n));
* hash_add(hash, "APA", &n, sizeof(n));
* hash_dump(hash, stdout);
*
* puts("----");
*
* hash_add (hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1);
* hash_add(hash, "BEPA", "hoppla Polle!", strlen("hoppla Polle!")+1);
* puts((char *)hash_value(hash, "BEPA", NULL));
* hash_dump(hash, stdout);
*
* puts("----");
*
* n = 33;
* hash_add (hash, "CEPA", &n, sizeof(n));
* hash_add(hash, "CEPA", &n, sizeof(n));
* hash_dump(hash, stdout);
*
* puts("----");
*
* hash_del (hash, "APA");
* hash_del(hash, "APA");
* hash_dump(hash, stdout);
*
* hash_free(hash);
@ -118,11 +118,11 @@ hash_init (void)
{
clicon_hash_t *hash;
if ((hash = (clicon_hash_t *)malloc (sizeof (clicon_hash_t) * HASH_SIZE)) == NULL){
if ((hash = (clicon_hash_t *)malloc(sizeof(clicon_hash_t) * HASH_SIZE)) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
return NULL;
}
memset (hash, 0, sizeof(clicon_hash_t)*HASH_SIZE);
memset(hash, 0, sizeof(clicon_hash_t)*HASH_SIZE);
return hash;
}
@ -167,7 +167,7 @@ hash_lookup(clicon_hash_t *hash,
h = hash[bkt];
if (h) {
do {
if (!strcmp (h->h_key, key))
if (!strcmp(h->h_key, key))
return h;
h = NEXTQ(clicon_hash_t, h);
} while (h != hash[bkt]);
@ -218,15 +218,15 @@ hash_add(clicon_hash_t *hash,
clicon_hash_t new = NULL;
/* If variable exist, don't allocate a new. just replace value */
h = hash_lookup (hash, key);
h = hash_lookup(hash, key);
if (h == NULL) {
if ((new = (clicon_hash_t)malloc (sizeof (*new))) == NULL){
if ((new = (clicon_hash_t)malloc(sizeof(*new))) == NULL){
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch;
}
memset (new, 0, sizeof (*new));
memset(new, 0, sizeof(*new));
new->h_key = strdup (key);
new->h_key = strdup(key);
if (new->h_key == NULL){
clicon_err(OE_UNIX, errno, "strdup: %s", strerror(errno));
goto catch;
@ -241,11 +241,11 @@ hash_add(clicon_hash_t *hash,
clicon_err(OE_UNIX, errno, "malloc: %s", strerror(errno));
goto catch;
}
memcpy (newval, val, vlen);
memcpy(newval, val, vlen);
/* Free old value if existing variable */
if (h->h_val)
free (h->h_val);
free(h->h_val);
h->h_val = newval;
h->h_vlen = vlen;
@ -258,8 +258,8 @@ hash_add(clicon_hash_t *hash,
catch:
if (new) {
if (new->h_key)
free (new->h_key);
free (new);
free(new->h_key);
free(new);
}
return NULL;
@ -279,15 +279,15 @@ hash_del(clicon_hash_t *hash,
{
clicon_hash_t h;
h = hash_lookup (hash, key);
h = hash_lookup(hash, key);
if (h == NULL)
return -1;
DELQ(h, hash[hash_bucket(key)], clicon_hash_t);
free (h->h_key);
free (h->h_val);
free (h);
free(h->h_key);
free(h->h_val);
free(h);
return 0;
}

View file

@ -691,8 +691,9 @@ int
json_parse_str(char *str,
cxobj **xt)
{
if ((*xt = xml_new("top", NULL, NULL)) == NULL)
return -1;
if (*xt == NULL)
if ((*xt = xml_new("top", NULL, NULL)) == NULL)
return -1;
return json_parse(str, "", *xt);
}
@ -729,16 +730,14 @@ json_parse_file(int fd,
int len = 0;
if ((jsonbuf = malloc(jsonbuflen)) == NULL){
clicon_err(OE_XML, errno, "%s: malloc", __FUNCTION__);
clicon_err(OE_XML, errno, "malloc");
goto done;
}
memset(jsonbuf, 0, jsonbuflen);
ptr = jsonbuf;
while (1){
if ((ret = read(fd, &ch, 1)) < 0){
clicon_err(OE_XML, errno, "%s: read: [pid:%d]\n",
__FUNCTION__,
(int)getpid());
clicon_err(OE_XML, errno, "read");
break;
}
if (ret != 0)
@ -755,7 +754,7 @@ json_parse_file(int fd,
oldjsonbuflen = jsonbuflen;
jsonbuflen *= 2;
if ((jsonbuf = realloc(jsonbuf, jsonbuflen)) == NULL){
clicon_err(OE_XML, errno, "%s: realloc", __FUNCTION__);
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memset(jsonbuf+oldjsonbuflen, 0, jsonbuflen-oldjsonbuflen);
@ -775,7 +774,7 @@ json_parse_file(int fd,
/*
* Turn this on to get a json parse and pretty print test program
* Usage: xpath
* Usage: json
* read json from input
* Example compile:
gcc -g -o json -I. -I../clixon ./clixon_json.c -lclixon -lcligen
@ -792,7 +791,8 @@ usage(char *argv0)
}
int
main(int argc, char **argv)
main(int argc,
char **argv)
{
cxobj *xt;
cxobj *xc;

View file

@ -101,7 +101,7 @@ object.
/* typecast macro */
#define _JY ((struct clicon_json_yacc_arg *)_jy)
#define _YYERROR(msg) {clicon_debug(2, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
/* add _yy to error paramaters */
#define YY_(msgid) msgid
@ -139,7 +139,8 @@ extern int clixon_json_parseget_lineno (void);
*/
void
clixon_json_parseerror(void *_jy, char *s)
clixon_json_parseerror(void *_jy,
char *s)
{
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
_JY->jy_name,
@ -192,7 +193,10 @@ json_current_clone(struct clicon_json_yacc_arg *jy)
{
cxobj *xn;
assert(xn = jy->jy_current);
if (jy->jy_current == NULL){
return -1;
}
xn = jy->jy_current;
json_current_pop(jy);
if (jy->jy_current)
json_current_new(jy, xml_name(xn));
@ -258,7 +262,7 @@ array : '[' ']'
;
valuelist : value
| valuelist { json_current_clone(_JY);} ',' value
| valuelist { if (json_current_clone(_JY)< 0) _YYERROR("stack?");} ',' value
;
/* quoted string */

View file

@ -157,13 +157,12 @@ slogtime(void)
/*! Make a logging call to syslog (or stderr).
*
* This is the _only_ place the actual syslog (or stderr) logging is made in clicon,..
* @note syslog makes itw own filtering, but if log to stderr we do it here
* @see clicon_debug()
*
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER
* @param[in] msg Message to print as argv.
* This is the _only_ place the actual syslog (or stderr) logging is made in clicon,..
* @note syslog makes itw own filtering, but if log to stderr we do it here
* @see clicon_debug
*/
int
clicon_log_str(int level,
@ -214,13 +213,13 @@ clicon_log_str(int level,
/*! Make a logging call to syslog using variable arg syntax.
*
* See also clicon_log_init() and clicon_log_str()
*
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. This
* is OR:d with facility == LOG_USER
* @param[in] format Message to print as argv.
* @code
clicon_log(LOG_NOTICE, "%s: dump to dtd not supported", __PROGRAM__);
* @endcode
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER
* @param[in] format Message to print as argv.
* @see cicon_log_init and clicon_log_str
*/
int
clicon_log(int level,

View file

@ -68,6 +68,8 @@
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_xsl.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xml_map.h"
/* Mapping between Clicon startup modes string <--> constants,
@ -148,7 +150,7 @@ clicon_option_readfile_xml(clicon_hash_t *copt,
clicon_err(OE_UNIX, errno, "configure file: %s", filename);
return -1;
}
clicon_debug(2, "Reading config file %s", __FUNCTION__, filename);
clicon_debug(2, "%s: Reading config file %s", __FUNCTION__, filename);
fd = fileno(f);
if (xml_parse_file(fd, "</clicon>", yspec, &xt) < 0)
goto done;
@ -186,6 +188,7 @@ clicon_option_readfile_xml(clicon_hash_t *copt,
/*! Initialize option values
*
* Set default options, Read config-file, Check that all values are set.
* Read clixon system config files
* @param[in] h clicon handle
*/
int
@ -289,10 +292,10 @@ clicon_option_str_set(clicon_handle h,
/*! Get options as integer but stored as string
*
* @param h clicon handle
* @param name name of option
* @retval int An integer as aresult of atoi
* @retval -1 If option does not exist
* @param[in] h clicon handle
* @param[in] name name of option
* @retval int An integer as aresult of atoi
* @retval -1 If option does not exist
* @code
* if (clicon_option_exists(h, "X")
* return clicon_option_int(h, "X");
@ -330,10 +333,10 @@ clicon_option_int_set(clicon_handle h,
/*! Get options as bool but stored as string
*
* @param h clicon handle
* @param name name of option
* @retval 0 false, or does not exist, or does not have a boolean value
* @retval 1 true
* @param[in] h clicon handle
* @param[in] name name of option
* @retval 0 false, or does not exist, or does not have a boolean value
* @retval 1 true
* @code
* if (clicon_option_exists(h, "X")
* return clicon_option_bool(h, "X");

View file

@ -43,6 +43,7 @@
#include <errno.h>
#include <dlfcn.h>
#include <dirent.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/param.h>
@ -126,7 +127,7 @@ clixon_plugin_each_revert(clicon_handle h,
int nr)
{
int i;
clixon_plugin *cp;
clixon_plugin *cp = NULL;
clixon_plugin *cpnext = NULL;
if (cpprev == NULL)
@ -192,7 +193,7 @@ plugin_load_one(clicon_handle h,
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");
clicon_err(OE_PLUGIN, errno, "dlopen: %s", error ? error : "Unknown error");
goto done;
}
/* call plugin_init() if defined, eg CLIXON_PLUGIN_INIT or CLIXON_BACKEND_INIT */
@ -204,12 +205,16 @@ plugin_load_one(clicon_handle h,
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
goto done;
}
clicon_err_reset();
if ((api = initfn(h)) == NULL) {
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;
if (!clicon_errno){ /* if clicon_err() is not called then log and continue */
clicon_log(LOG_WARNING, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
dlclose(handle);
}
else{
clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
goto err;
}
}
/* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
if ((cp = (clixon_plugin *)malloc(sizeof(struct clixon_plugin))) == NULL){
@ -228,7 +233,8 @@ plugin_load_one(clicon_handle h,
snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
(int)strlen(name), name);
cp->cp_api = *api;
if (api)
cp->cp_api = *api;
clicon_debug(1, "%s", __FUNCTION__);
done:
return cp;
@ -342,7 +348,7 @@ clixon_plugin_exit(clicon_handle h)
}
if (dlclose(cp->cp_handle) != 0) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlclose: %s\n", error ? error : "Unknown error");
clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error");
}
}
if (_clixon_plugins){
@ -430,7 +436,7 @@ rpc_callback_register(clicon_handle h,
clicon_err(OE_DB, errno, "malloc: %s", strerror(errno));
goto done;
}
memset (rc, 0, sizeof (*rc));
memset(rc, 0, sizeof(*rc));
rc->rc_callback = cb;
rc->rc_arg = arg;
rc->rc_tag = strdup(tag); /* XXX strdup memleak */
@ -466,7 +472,7 @@ rpc_callback_delete_all(void)
* @param[in] h clicon handle
* @param[in] xn Sub-tree (under xorig) at child of rpc: <rpc><xn></rpc>.
* @param[out] xret Return XML, error or OK
* @param[in] arg Domain-speific arg (eg client_entry)
* @param[in] arg Domain-speific arg (eg client_entry)
*
* @retval -1 Error
* @retval 0 OK, not found handler.

View file

@ -203,7 +203,7 @@ clicon_connect_unix(char *sockpath)
clicon_debug(2, "%s: connecting to %s", __FUNCTION__, addr.sun_path);
if (connect(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){
if (errno == EACCES)
clicon_err(OE_CFG, errno, "connecting unix socket: %s.\n"
clicon_err(OE_CFG, errno, "connecting unix socket: %s."
"Client should be member of group $CLICON_SOCK_GROUP: ",
sockpath);
else
@ -273,7 +273,7 @@ msg_dump(struct clicon_msg *msg)
for (i=0; i<ntohl(msg->op_len); i++){
snprintf(buf, sizeof(buf), "%s%02x", buf2, ((char*)msg)[i]&0xff);
if ((i+1)%32==0){
clicon_debug(2, buf);
clicon_debug(2, "%s", buf);
snprintf(buf, sizeof(buf), "%s:", __FUNCTION__);
}
else
@ -282,7 +282,7 @@ msg_dump(struct clicon_msg *msg)
strncpy(buf2, buf, sizeof(buf2));
}
if (i%32)
clicon_debug(2, buf);
clicon_debug(2, "%s", buf);
return 0;
}
@ -302,7 +302,7 @@ clicon_msg_send(int s,
msg_dump(msg);
if (atomicio((ssize_t (*)(int, void *, size_t))write,
s, msg, ntohl(msg->op_len)) < 0){
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
clicon_err(OE_CFG, errno, "atomicio");
clicon_log(LOG_WARNING, "%s: write: %s len:%u msg:%s", __FUNCTION__,
strerror(errno), ntohs(msg->op_len), msg->op_body);
goto done;
@ -344,7 +344,7 @@ clicon_msg_rcv(int s,
set_signal(SIGINT, atomicio_sig_handler, &oldhandler);
if ((hlen = atomicio(read, s, &hdr, sizeof(hdr))) < 0){
clicon_err(OE_CFG, errno, "%s", __FUNCTION__);
clicon_err(OE_CFG, errno, "atomicio");
goto done;
}
if (hlen == 0){
@ -353,7 +353,7 @@ clicon_msg_rcv(int s,
goto done;
}
if (hlen != sizeof(hdr)){
clicon_err(OE_CFG, errno, "%s: header too short (%d)", __FUNCTION__, hlen);
clicon_err(OE_CFG, errno, "header too short (%d)", hlen);
goto done;
}
mlen = ntohl(hdr.op_len);
@ -365,11 +365,11 @@ clicon_msg_rcv(int s,
}
memcpy(*msg, &hdr, hlen);
if ((len2 = atomicio(read, s, (*msg)->op_body, mlen - sizeof(hdr))) < 0){
clicon_err(OE_CFG, errno, "%s: read", __FUNCTION__);
clicon_err(OE_CFG, errno, "read");
goto done;
}
if (len2 != mlen - sizeof(hdr)){
clicon_err(OE_CFG, errno, "%s: body too short", __FUNCTION__);
clicon_err(OE_CFG, errno, "body too short");
goto done;
}
if (debug > 1)
@ -504,7 +504,7 @@ clicon_rpc(int s,
if (clicon_msg_rcv(s, &reply, &eof) < 0)
goto done;
if (eof){
clicon_err(OE_PROTO, ESHUTDOWN, "%s: Socket unexpected close", __FUNCTION__);
clicon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close");
close(s);
errno = ESHUTDOWN;
goto done;

View file

@ -65,6 +65,9 @@
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_xsl.h"
#include "clixon_string.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_proto.h"
#include "clixon_err.h"
#include "clixon_proto_client.h"
@ -91,6 +94,9 @@ clicon_rpc_msg(clicon_handle h,
cxobj *xret = NULL;
yang_spec *yspec;
#ifdef RPC_USERNAME_ASSERT
assert(strstr(msg->op_body, "username")!=NULL); /* XXX */
#endif
clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body);
if ((sock = clicon_sock(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");

View file

@ -54,7 +54,6 @@
#include "clixon_string.h"
#include "clixon_err.h"
/*! Split string into a vector based on character delimiters. Using malloc
*
* The given string is split into a vector where the delimiter can be
@ -63,6 +62,17 @@
* The vector returned is one single memory block that must be freed
* by the caller
*
* @code
* char **vec = NULL;
* int nvec;
* if ((vec = clicon_strsep("/home/user/src/clixon", "/", &nvec)) == NULL)
* err;
* for (i=0; i<nvec; i++){
* v = vec[i++];
* ...
* }
* free(vec);
* @endcode
* @param[in] string String to be split
* @param[in] delim String of delimiter characters
* @param[out] nvec Number of entries in returned vector
@ -130,11 +140,11 @@ clicon_strjoin(int argc,
len += 1; /* '\0' */
if ((str = malloc(len)) == NULL)
return NULL;
memset (str, '\0', len);
memset(str, '\0', len);
for (i = 0; i < argc; i++) {
if (i != 0)
strncat (str, delim, len - strlen(str));
strncat (str, argv[i], len - strlen(str));
strncat(str, delim, len - strlen(str));
strncat(str, argv[i], len - strlen(str));
}
return str;
}
@ -287,31 +297,56 @@ xml_chardata_encode(char *str,
int l;
int len;
int i, j;
int cdata; /* when set, skip encoding */
len = 0;
/* First compute length (do nothing) */
len = 0; cdata = 0;
for (i=0; i<strlen(str); i++){
switch (str[i]){
case '&':
len += strlen("&amp; ");
break;
case '<':
len += strlen("&lt; ");
break;
case '>':
len += strlen("&gt; ");
break;
default:
if (cdata){
if (strncmp(&str[i], "]]>", strlen("]]>")) == 0)
cdata = 0;
len++;
}
else
switch (str[i]){
case '&':
len += strlen("&amp; ");
break;
case '<':
if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
len++;
cdata++;
}
else
len += strlen("&lt; ");
break;
case '>':
len += strlen("&gt; ");
break;
default:
len++;
}
}
len++; /* trailing \0 */
/* We know length, allocate encoding buffer */
if ((esc = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(esc, 0, len);
j = 0;
/* Same code again, but now actually encode into output buffer */
j = 0; cdata = 0;
for (i=0; i<strlen(str); i++){
if (cdata){
if (strncmp(&str[i], "]]>", strlen("]]>")) == 0){
cdata = 0;
esc[j++] = str[i++];
esc[j++] = str[i++];
}
esc[j++] = str[i];
}
else
switch (str[i]){
case '&':
if ((l=snprintf(&esc[j], 7, "&amp; ")) < 0){
@ -321,6 +356,11 @@ xml_chardata_encode(char *str,
j += l;
break;
case '<':
if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
esc[j++] = str[i];
cdata++;
break;
}
if ((l=snprintf(&esc[j], 6, "&lt; ")) < 0){
clicon_err(OE_UNIX, errno, "snprintf");
goto done;
@ -478,21 +518,21 @@ clicon_str2int(const map_str2int *mstab,
*/
#ifndef HAVE_STRNDUP
char *
clicon_strndup (const char *str,
size_t len)
clicon_strndup(const char *str,
size_t len)
{
char *new;
size_t slen;
slen = strlen (str);
slen = strlen(str);
len = (len < slen ? len : slen);
new = malloc (len + 1);
new = malloc(len + 1);
if (new == NULL)
return NULL;
new[len] = '\0';
memcpy (new, str, len);
memcpy(new, str, len);
return new;
}

View file

@ -109,10 +109,9 @@ struct xml{
enum cxobj_type x_type; /* type of node: element, attribute, body */
char *x_value; /* attribute and body nodes have values */
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_* */
yang_stmt *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */
cg_var *x_cv; /* If body this contains the typed value */
};
/* Mapping between xml type <--> string */
@ -181,7 +180,7 @@ xml_namespace(cxobj *xn)
return xn->x_namespace;
}
/*! Set name of xnode, name is copied
/*! Set name space of xnode, namespace is copied
* @param[in] xn xml node
* @param[in] namespace new namespace, null-terminated string, copied by function
* @retval -1 on error with clicon-err set
@ -204,6 +203,74 @@ xml_namespace_set(cxobj *xn,
return 0;
}
/*! See if xmlns:<namespace>=<uri> exists, if so return <uri>
*
* @param[in] xn XML node
* @param[in] nsn Namespace name
* @retval URI return associated URI if found
* @retval NULL No namespace name binding found for nsn
*/
static char *
xmlns_check(cxobj *xn,
char *nsn)
{
cxobj *x = NULL;
char *xns;
while ((x = xml_child_each(xn, x, -1)) != NULL)
if ((xns = xml_namespace(x)) && strcmp(xns, "xmlns")==0 &&
strcmp(xml_name(x), nsn) == 0)
return xml_value(x);
return NULL;
}
/*! Check namespace of xml node by searhing recursively among ancestors
* @param[in] xn xml node
* @param[in] namespace check validity of namespace
* @retval 0 Found / validated or no yang spec
* @retval -1 Not found
* @note This function is grossly inefficient
*/
static int
xml_namespace_check(cxobj *xn,
void *arg)
{
cxobj *xp = NULL;
char *nsn;
char *n;
yang_stmt *ys = xml_spec(xn);
/* No namespace name - comply */
if ((nsn = xml_namespace(xn)) == NULL)
return 0;
/* Check if NSN defined in same node */
if (xmlns_check(xn, nsn) != NULL)
return 0;
/* Check if NSN defined in some ancestor */
while ((xp = xml_parent(xn)) != NULL) {
if (xmlns_check(xp, nsn) != NULL)
return 0;
xn = xp;
}
#ifdef XMLNS_YANG_ONLY
if (ys == NULL)
return 0; /* If no yang spec */
else
#endif
{
/* Check if my namespace */
if ((n = yang_find_myprefix(ys)) != NULL && strcmp(nsn,n)==0)
return 0;
/* Check if any imported module */
if (yang_find_module_by_prefix(ys, nsn) != NULL)
return 0;
}
/* Not found, error */
clicon_err(OE_XML, ENOENT, "Namespace name %s in %s:%s not found",
nsn, nsn, xml_name(xn));
return -1;
}
/*! Get parent of xnode
* @param[in] xn xml node
* @retval parent xml node
@ -345,35 +412,6 @@ xml_type_set(cxobj *xn,
return old;
}
/*! Get cligen variable associated with node
* @param[in] xn xml node
* @retval cv Cligen variable if set
* @retval NULL If not set, or not applicable
*/
cg_var *
xml_cv_get(cxobj *xn)
{
if (xn->x_cv)
return xn->x_cv;
else
return NULL;
}
/*! Set cligen variable associated with node
* @param[in] xn xml node
* @param[in] cv Cligen variable or NULL
* @retval 0 if OK
*/
int
xml_cv_set(cxobj *xn,
cg_var *cv)
{
if (xn->x_cv)
free(xn->x_cv);
xn->x_cv = cv;
return 0;
}
/*! Get number of children
* @param[in] xn xml node
* @retval number of children in XML tree
@ -869,7 +907,6 @@ xml_find_value(cxobj *xt,
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name return x_value
*/
char *
xml_find_body(cxobj *xt,
@ -932,8 +969,6 @@ xml_free(cxobj *x)
free(x->x_value);
if (x->x_namespace)
free(x->x_namespace);
if (x->x_cv)
cv_free(x->x_cv);
for (i=0; i<x->x_childvec_len; i++){
if ((xc = x->x_childvec[i]) != NULL){
xml_free(xc);
@ -1247,6 +1282,9 @@ _xml_parse(const char *str,
goto done;
if (clixon_xml_parseparse(&ya) != 0) /* yacc returns 1 on error */
goto done;
/* Verify namespaces after parsing */
if (xml_apply0(xt, CX_ELMNT, xml_namespace_check, NULL) < 0)
goto done;
/* Sort the complete tree after parsing */
if (yspec){
if (xml_apply0(xt, CX_ELMNT, xml_sort, NULL) < 0)
@ -1321,7 +1359,7 @@ xml_parse_file(int fd,
ptr = xmlbuf;
while (1){
if ((ret = read(fd, &ch, 1)) < 0){
clicon_err(OE_XML, errno, "read: [pid:%d]\n",
clicon_err(OE_XML, errno, "read: [pid:%d]",
(int)getpid());
break;
}
@ -1449,8 +1487,6 @@ int
xml_copy_one(cxobj *x0,
cxobj *x1)
{
cg_var *cv1;
xml_type_set(x1, xml_type(x0));
if (xml_value(x0)){ /* malloced string */
if ((x1->x_value = strdup(x0->x_value)) == NULL){
@ -1461,14 +1497,6 @@ xml_copy_one(cxobj *x0,
if (xml_name(x0)) /* malloced string */
if ((xml_name_set(x1, xml_name(x0))) < 0)
return -1;
if (xml_cv_get(x0)){
if ((cv1 = cv_dup(xml_cv_get(x0))) == NULL){
clicon_err(OE_XML, errno, "cv_dup");
return -1;
}
if ((xml_cv_set(x1, cv1)) < 0)
return -1;
}
return 0;
}
@ -1658,7 +1686,6 @@ xml_apply0(cxobj *xn,
return retval;
}
/*! Apply a function call recursively on all ancestors
* Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an
@ -1737,7 +1764,7 @@ xml_body_parse(cxobj *xb,
goto done;
}
if (cvret == 0){ /* parsing failed */
clicon_err(OE_XML, errno, "Parsing CV: %s", &reason);
clicon_err(OE_XML, errno, "Parsing CV: %s", reason);
if (reason)
free(reason);
}
@ -1861,7 +1888,6 @@ xml_operation2str(enum operation_type op)
}
}
/*
* Turn this on to get a xml parse and pretty print test program
* Usage: xpath
@ -1871,7 +1897,7 @@ xml_operation2str(enum operation_type op)
* Example run:
echo "<a><b/></a>" | xml
*/
#if 0 /* Test program */
#if 1 /* Test program */
static int
usage(char *argv0)
@ -1883,7 +1909,7 @@ usage(char *argv0)
int
main(int argc, char **argv)
{
cxobj *xt;
cxobj *xt = NULL;
cxobj *xc;
cbuf *cb = cbuf_new();
@ -1891,8 +1917,8 @@ main(int argc, char **argv)
usage(argv[0]);
return 0;
}
if (xml_parse_file(0, "</config>", NULL,&xt) < 0){
fprintf(stderr, "parsing 2\n");
if (xml_parse_file(0, "</config>", NULL, &xt) < 0){
fprintf(stderr, "xml parse error %s\n", clicon_err_reason);
return -1;
}
xc = NULL;

View file

@ -87,7 +87,7 @@ xmldb_plugin_load(clicon_handle h,
dlerror(); /* Clear any existing error */
if ((handle = dlopen(filename, RTLD_NOW|RTLD_GLOBAL)) == NULL) {
error = (char*)dlerror();
clicon_err(OE_PLUGIN, errno, "dlopen: %s\n", error ? error : "Unknown error");
clicon_err(OE_PLUGIN, errno, "dlopen: %s", error ? error : "Unknown error");
goto done;
}
/* Try v1 */
@ -162,7 +162,7 @@ xmldb_plugin_unload(clicon_handle 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");
clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error");
/* Just report no -1 return*/
}
retval = 0;

View file

@ -83,6 +83,8 @@
#include "clixon_options.h"
#include "clixon_xml.h"
#include "clixon_plugin.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xsl.h"
#include "clixon_log.h"
#include "clixon_err.h"
@ -226,7 +228,7 @@ xml2cli(FILE *f,
return retval;
}
/*! Validate an xml node of type leafref, ensure the value is one of that path's reference
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
* @param[in] xt XML leaf node of type leafref
* @param[in] ytype Yang type statement belonging to the XML node
*/
@ -249,7 +251,7 @@ validate_leafref(cxobj *xt,
clicon_err(OE_DB, 0, "Leafref %s requires path statement", ytype->ys_argument);
goto done;
}
if (xpath_vec(xt, ypath->ys_argument, &xvec, &xlen) < 0)
if (xpath_vec(xt, "%s", &xvec, &xlen, ypath->ys_argument) < 0)
goto done;
for (i = 0; i < xlen; i++) {
x = xvec[i];
@ -270,6 +272,72 @@ validate_leafref(cxobj *xt,
return retval;
}
/*! Validate xml node of type identityref, ensure value is a defined identity
* Check if a given node has value derived from base identity. This is
* a run-time check necessary when validating eg netconf.
* Valid values for an identityref are any identities derived from all
* the identityref's base identities.
* Example:
* b0 --> b1 --> b2 (b1 & b2 are derived)
* identityref b2
* base b0;
* This function does: derived_from(b2, b0);
* @param[in] xt XML leaf node of type identityref
* @param[in] ys Yang spec of leaf
* @param[in] ytype Yang type field of type identityref
* @see ys_populate_identity where the derived types are set
* @see RFC7950 Sec 9.10.2:
*/
static int
validate_identityref(cxobj *xt,
yang_stmt *ys,
yang_stmt *ytype)
{
int retval = -1;
char *node;
yang_stmt *ybaseref; /* This is the type's base reference */
yang_stmt *ybaseid;
char *prefix = NULL;
cbuf *cb = NULL;
/* Get idref value. Then see if this value is derived from ytype.
* Always add default prefix because derived identifiers are stored with
* prefixes in the base identifiers derived-list.
*/
if ((node = xml_body(xt)) == NULL)
return 0;
if (strchr(node, ':') == NULL){
prefix = yang_find_myprefix(ys);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "%s:%s", prefix, node);
node = cbuf_get(cb);
}
/* This is the type's base reference */
if ((ybaseref = yang_find((yang_node*)ytype, Y_BASE, NULL)) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, no base");
goto done;
}
/* This is the actual base identity */
if ((ybaseid = yang_find_identity(ybaseref, ybaseref->ys_argument)) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, no base identity");
goto done;
}
/* Here check if node is in the derived node list of the base identity */
if (cvec_find(ybaseid->ys_cvec, node) == NULL){
clicon_err(OE_DB, 0, "Identityref validation failed, %s not derived from %s", node, ybaseid->ys_argument);
goto done;
}
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Validate a single XML node with yang specification for added entry
* 1. Check if mandatory leafs present as subs.
* 2. Check leaf values, eg int ranges and string regexps.
@ -360,28 +428,91 @@ xml_yang_validate_all(cxobj *xt,
void *arg)
{
int retval = -1;
yang_stmt *ys;
yang_stmt *ytype;
yang_stmt *ys; /* yang node */
yang_stmt *yc; /* yang child */
yang_stmt *ye; /* yang must error-message */
char *xpath;
int nr;
/* if not given by argument (overide) use default link
and !Node has a config sub-statement and it is false */
if ((ys = xml_spec(xt)) != NULL &&
yang_config(ys) != 0){
/* Node-specific validation */
switch (ys->ys_keyword){
case Y_LEAF:
/* fall thru */
case Y_LEAF_LIST:
/* Special case if leaf is leafref, then first check against
current xml tree
*/
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL &&
strcmp(ytype->ys_argument, "leafref") == 0)
if (validate_leafref(xt, ytype) < 0)
goto done;
*/
if ((yc = yang_find((yang_node*)ys, Y_TYPE, NULL)) != NULL){
if (strcmp(yc->ys_argument, "leafref") == 0){
if (validate_leafref(xt, yc) < 0)
goto done;
}
else if (strcmp(yc->ys_argument, "identityref") == 0){
if (validate_identityref(xt, ys, yc) < 0)
goto done;
}
}
if ((yc = yang_find((yang_node*)ys, Y_MIN_ELEMENTS, NULL)) != NULL){
/* The behavior of the constraint depends on the type of the
* leaf-list's or list's closest ancestor node in the schema tree
* that is not a non-presence container (see Section 7.5.1):
* o If no such ancestor exists in the schema tree, the constraint
* is enforced.
* o Otherwise, if this ancestor is a case node, the constraint is
* enforced if any other node from the case exists.
* o Otherwise, it is enforced if the ancestor node exists.
*/
#if 0
cxobj *xp;
cxobj *x;
int i;
if ((xp = xml_parent(xt)) != NULL){
nr = atoi(yc->ys_argument);
x = NULL;
i = 0;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL)
i++;
}
#endif
}
if ((yc = yang_find((yang_node*)ys, Y_MAX_ELEMENTS, NULL)) != NULL){
}
break;
default:
break;
}
/* must sub-node RFC 7950 Sec 7.5.3. Can be several. */
yc = NULL;
while ((yc = yn_each((yang_node*)ys, yc)) != NULL) {
if (yc->ys_keyword != Y_MUST)
continue;
xpath = yc->ys_argument; /* "must" has xpath argument */
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done;
if (!nr){
if ((ye = yang_find((yang_node*)yc, Y_ERROR_MESSAGE, NULL)) != NULL)
clicon_err(OE_DB, 0, "%s", ye->ys_argument);
else
clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
goto done;
}
}
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
if ((yc = yang_find((yang_node*)ys, Y_WHEN, NULL)) != NULL){
xpath = yc->ys_argument; /* "when" has xpath argument */
if ((nr = xpath_vec_bool(xt, "%s", xpath)) < 0)
goto done;
if (!nr){
clicon_err(OE_DB, 0, "xpath %s validation failed", xml_name(xt));
goto done;
}
}
}
retval = 0;
done:
@ -394,6 +525,7 @@ xml_yang_validate_all(cxobj *xt,
* @param[out] cvv CLIgen variable vector. Should be freed by cvec_free()
* @retval 0 Everything OK, cvv allocated and set
* @retval -1 Something wrong, clicon_err() called to set error. No cvv returned
* @note cvv Should be freed by cvec_free() after use.
* 'Not recursive' means that only one level of XML bodies is translated to cvec:s.
* If range is wriong (eg 1000 for uint8) a warning is logged, the value is
* skipped, and continues.
@ -426,7 +558,7 @@ xml2cvec(cxobj *xt,
char *name;
xc = NULL;
/* Tried to allocate whole cvv here,but some cg_vars may be invalid */
/* Tried to allocate whole cvv here, but some cg_vars may be invalid */
if ((cvv = cvec_new(0)) == NULL){
clicon_err(OE_UNIX, errno, "cvec_new");
goto err;
@ -913,7 +1045,7 @@ api_path_fmt2api_path(char *api_path_fmt,
* @example
* api_path_fmt: /interface/%s/address/%s
* cvv: name=eth0
* xpath: /interface/[name=eth0]/address
* xpath: /interface/[name='eth0']/address
* @example
* api_path_fmt: /ip/me/%s (if key)
* cvv: -
@ -956,7 +1088,13 @@ api_path_fmt2xpath(char *api_path_fmt,
clicon_err(OE_UNIX, errno, "cv2str_dup");
goto done;
}
cprintf(cb, "[%s=%s]", cv_name_get(cv), str);
cprintf(cb,
#ifdef COMPAT_XSL
"[%s=%s]",
#else
"[%s='%s']",
#endif
cv_name_get(cv), str);
free(str);
}
}
@ -1301,7 +1439,6 @@ xml_spec_populate(cxobj *x,
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
@ -1374,7 +1511,13 @@ api_path2xpath_cvv(yang_spec *yspec,
cprintf(xpath, "/%s", name);
v = val;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
cprintf(xpath, "[%s=%s]", cv_string_get(cvi), v);
cprintf(xpath,
#ifdef COMPAT_XSL
"[%s=%s]",
#else
"[%s='%s']",
#endif
cv_string_get(cvi), v);
v += strlen(v)+1;
}
if (val)
@ -1644,6 +1787,7 @@ xml_merge1(cxobj *x0,
cxobj *x1c; /* mod child */
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cbuf *cbr = NULL; /* Reason buffer */
assert(x1 && xml_type(x1) == CX_ELMNT);
assert(y0);
@ -1682,9 +1826,16 @@ xml_merge1(cxobj *x0,
x1cname = xml_name(x1c);
/* Get yang spec of the child */
if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
if (reason){
if ((cbr = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname);
if ((*reason = strdup(cbuf_get(cbr))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
break;
}
@ -1701,6 +1852,8 @@ xml_merge1(cxobj *x0,
ok:
retval = 0;
done:
if (cbr)
cbuf_free(cbr);
return retval;
}
@ -1708,7 +1861,7 @@ xml_merge1(cxobj *x0,
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] x1 xml tree which modifies base
* @param[in] yspec Yang spec
* @param[out] reason If retval=0 a malloced string. Needs to be freed by caller
* @param[out] reason If retval=0, reason is set. Malloced. Needs to be freed by caller
* @retval 0 OK. If reason is set, Yang error
* @retval -1 Error
* @note both x0 and x1 need to be top-level trees
@ -1726,6 +1879,7 @@ xml_merge(cxobj *x0,
cxobj *x0c; /* base child */
cxobj *x1c; /* mod child */
yang_stmt *yc;
cbuf *cbr = NULL; /* Reason buffer */
/* Loop through children of the modification tree */
x1c = NULL;
@ -1733,9 +1887,16 @@ xml_merge(cxobj *x0,
x1cname = xml_name(x1c);
/* Get yang spec of the child */
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
if (reason){
if ((cbr = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
cprintf(cbr, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", xml_name(x1), x1cname);
if ((*reason = strdup(cbuf_get(cbr))) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
break;
}
@ -1749,6 +1910,8 @@ xml_merge(cxobj *x0,
}
retval = 0; /* OK */
done:
if (cbr)
cbuf_free(cbr);
return retval;
}

View file

@ -77,6 +77,7 @@ int clixon_xml_parsewrap(void)
%x START
%s STATEA
%s AMPERSAND
%s CDATA
%s CMNT
%s STR
%s TEXTDECL
@ -103,17 +104,23 @@ int clixon_xml_parsewrap(void)
<STATEA>"</" { BEGIN(START); return BSLASH; }
<STATEA>"<!--" { BEGIN(CMNT); return BCOMMENT; }
<STATEA>"<![CDATA[" { BEGIN(CDATA); _YA->ya_lex_state = STATEA; clixon_xml_parselval.string = yytext; return CHARDATA;}
<STATEA>\< { BEGIN(START); return *clixon_xml_parsetext; }
<STATEA>& { _YA->ya_lex_state =STATEA;BEGIN(AMPERSAND);}
<STATEA>\n { clixon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHARDATA);}
<STATEA>. { clixon_xml_parselval.string = yytext; return CHARDATA; /*XXX:optimize*/}
<STATEA>. { clixon_xml_parselval.string = yytext; return CHARDATA; /*XXX:optimize*/}
/* @see xml_chardata_encode */
<AMPERSAND>"amp; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "&"; return CHARDATA;}
<AMPERSAND>"lt; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "<"; return CHARDATA;}
<AMPERSAND>"gt; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = ">"; return CHARDATA;}
<AMPERSAND>"apos; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;}
<AMPERSAND>"aquot; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "'"; return CHARDATA;}
<AMPERSAND>"quot; " {BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = "\""; return CHARDATA;}
<CDATA>. { clixon_xml_parselval.string = yytext; return CHARDATA;}
<CDATA>\n { clixon_xml_parselval.string = yytext;_YA->ya_linenum++; return (CHARDATA);}
<CDATA>"]]>" { BEGIN(_YA->ya_lex_state); clixon_xml_parselval.string = yytext; return CHARDATA;}
<CMNT>"-->" { BEGIN(START); return ECOMMENT; }
<CMNT>\n _YA->ya_linenum++;

View file

@ -47,8 +47,7 @@
%token BTEXT ETEXT
%token BCOMMENT ECOMMENT
%type <string> attvalue attqname
%type <string> attvalue
%lex-param {void *_ya} /* Add this argument to parse() and lex() function */
%parse-param {void *_ya}
@ -117,7 +116,7 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
char *ver)
{
if(strcmp(ver, "1.0")){
clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0\n", ver);
clicon_err(OE_XML, errno, "Wrong XML version %s expected 1.0", ver);
free(ver);
return -1;
}
@ -125,15 +124,41 @@ xml_parse_version(struct xml_parse_yacc_arg *ya,
return 0;
}
/*! Parse Qualified name
/*! Parse Qualified name --> Unprefixed name
* @param[in] ya XML parser yacc handler struct
* @param[in] prefix Prefix, namespace, or NULL
* @param[in] localpart Name
*/
static int
xml_parse_qname(struct xml_parse_yacc_arg *ya,
char *prefix,
char *name)
xml_parse_unprefixed_name(struct xml_parse_yacc_arg *ya,
char *name)
{
int retval = -1;
cxobj *x;
yang_stmt *y = NULL; /* yang node */
cxobj *xp; /* xml parent */
xp = ya->ya_xparent;
if (xml_child_spec(name, xp, ya->ya_yspec, &y) < 0)
goto done;
if ((x = xml_new(name, xp, y)) == NULL)
goto done;
ya->ya_xelement = x;
retval = 0;
done:
free(name);
return retval;
}
/*! Parse Qualified name -> PrefixedName
* @param[in] ya XML parser yacc handler struct
* @param[in] prefix Prefix, namespace, or NULL
* @param[in] localpart Name
*/
static int
xml_parse_prefixed_name(struct xml_parse_yacc_arg *ya,
char *prefix,
char *name)
{
int retval = -1;
cxobj *x;
@ -197,7 +222,7 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
goto done;
}
if (xml_namespace(x)!=NULL){
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s\n",
clicon_err(OE_XML, 0, "XML parse sanity check failed: %s:%s vs %s",
xml_namespace(x), xml_name(x), name);
goto done;
}
@ -235,7 +260,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
cxobj *xc;
if (strcmp(xml_name(x), name)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
xml_namespace(x),
xml_name(x),
namespace,
@ -244,7 +269,7 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
}
if (xml_namespace(x)==NULL ||
strcmp(xml_namespace(x), namespace)){
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s\n",
clicon_err(OE_XML, 0, "Sanity check failed: %s:%s vs %s:%s",
xml_namespace(x),
xml_name(x),
namespace,
@ -276,44 +301,29 @@ xml_parse_bslash2(struct xml_parse_yacc_arg *ya,
static int
xml_parse_attr(struct xml_parse_yacc_arg *ya,
char *qname,
char *prefix,
char *name,
char *attval)
{
int retval = -1;
cxobj *xa;
if ((xa = xml_new(qname, ya->ya_xelement, NULL)) == NULL)
if ((xa = xml_new(name, ya->ya_xelement, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);
if (prefix && xml_namespace_set(xa, prefix) < 0)
goto done;
if (xml_value_set(xa, attval) < 0)
goto done;
retval = 0;
done:
free(qname);
free(name);
if (prefix)
free(prefix);
free(attval);
return retval;
}
/*! Parse Attribue Qualified name, Just transform prefix:name into a new string
*
*/
static char*
xml_merge_attqname(struct xml_parse_yacc_arg *ya,
char *prefix,
char *name)
{
char *str;
int len = strlen(prefix)+strlen(name)+2;
if ((str=malloc(len)) == NULL)
return NULL;
snprintf(str, len, "%s:%s", prefix, name);
free(prefix);
free(name);
return str;
}
%}
%%
@ -344,9 +354,9 @@ element : '<' qname attrs element1
{ clicon_debug(3, "element -> < qname attrs element1"); }
;
qname : NAME { if (xml_parse_qname(_YA, NULL, $1) < 0) YYABORT;
qname : NAME { if (xml_parse_unprefixed_name(_YA, $1) < 0) YYABORT;
clicon_debug(3, "qname -> NAME %s", $1);}
| NAME ':' NAME { if (xml_parse_qname(_YA, $1, $3) < 0) YYABORT;
| NAME ':' NAME { if (xml_parse_prefixed_name(_YA, $1, $3) < 0) YYABORT;
clicon_debug(3, "qname -> NAME : NAME");}
;
@ -385,15 +395,10 @@ attrs : attrs attr
|
;
attr : attqname '=' attvalue { if (xml_parse_attr(_YA, $1, $3) < 0) YYABORT; }
attr : NAME '=' attvalue { if (xml_parse_attr(_YA, NULL, $1, $3) < 0) YYABORT; }
| NAME ':' NAME '=' attvalue { if (xml_parse_attr(_YA, $1, $3, $5) < 0) YYABORT; }
;
attqname : NAME {$$ = $1;}
| NAME ':' NAME
{ if (($$ = xml_merge_attqname(_YA, $1, $3)) == NULL) YYABORT; }
;
attvalue : '\"' CHARDATA '\"' { $$=$2; /* $2 must be consumed */}
| '\"' '\"' { $$=strdup(""); /* $2 must be consumed */}
;

1364
lib/src/clixon_xpath.c Normal file

File diff suppressed because it is too large Load diff

294
lib/src/clixon_xpath_ctx.c Normal file
View file

@ -0,0 +1,294 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2, indicate
your decision by deleting the provisions above and replace them with the
notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
* This file defines XPATH contexts using in traversing the XPATH parse tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <fnmatch.h>
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xpath_parse.h"
#include "clixon_xpath_ctx.h"
/*
* Variables
*/
const map_str2int ctxmap[] = {
{"nodeset", XT_NODESET},
{"bool", XT_BOOL},
{"number", XT_NUMBER},
{"string", XT_STRING},
{NULL, -1}
};
/*! Free xpath context */
int
ctx_free(xp_ctx *xc)
{
if (xc->xc_nodeset)
free(xc->xc_nodeset);
if (xc->xc_string)
free(xc->xc_string);
free(xc);
return 0;
}
/*! Duplicate xpath context */
xp_ctx *
ctx_dup(xp_ctx *xc0)
{
static xp_ctx *xc = NULL;
if ((xc = malloc(sizeof(*xc))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(xc, 0, sizeof(*xc));
*xc = *xc0;
if (xc0->xc_size){
if ((xc->xc_nodeset = calloc(xc0->xc_size, sizeof(cxobj*))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
memcpy(xc->xc_nodeset, xc0->xc_nodeset, xc->xc_size*sizeof(cxobj*));
}
if (xc0->xc_string)
if ((xc->xc_string = strdup(xc0->xc_string)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
done:
return xc;
}
/*! Print XPATH context */
int
ctx_print(cbuf *cb,
int id,
xp_ctx *xc,
char *str)
{
static int ident = 0;
int i;
if (id<0)
ident += id;
cprintf(cb, "%*s%s ", ident, "", str?str:"");
if (id>0)
ident += id;
if (xc){
cprintf(cb, "%s: ", (char*)clicon_int2str(ctxmap, xc->xc_type));
switch (xc->xc_type){
case XT_NODESET:
for (i=0; i<xc->xc_size; i++)
cprintf(cb, "%s ", xml_name(xc->xc_nodeset[i]));
break;
case XT_BOOL:
cprintf(cb, "%s", xc->xc_bool?"true":"false");
break;
case XT_NUMBER:
cprintf(cb, "%lf", xc->xc_number);
break;
case XT_STRING:
cprintf(cb, "%s", xc->xc_string);
break;
}
}
return 0;
}
/*! Convert xpath context to boolean according to boolean() function in XPATH spec
* @param[in] xc XPATH context
* @retval 0 False
* @retval 1 True
* a number is true if and only if it is neither positive or negative zero nor NaN
* a node-set is true if and only if it is non-empty
* a string is true if and only if its length is non-zero
* an object of a type other than the four basic types is converted to a boolean
* in a way that is dependent on that type
*/
int
ctx2boolean(xp_ctx *xc)
{
int b = -1;
switch (xc->xc_type){
case XT_NODESET:
b = (xc->xc_size != 0);
break;
case XT_BOOL:
b = xc->xc_bool;
break;
case XT_NUMBER:
b = (xc->xc_number != 0.0 && xc->xc_number != NAN);
break;
case XT_STRING:
b = (xc->xc_string && strlen(xc->xc_string));
break;
}
return b;
}
/*! Convert xpath context to string according to string() function in XPATH spec
* @param[in] xc XPATH context
* @param[out] str0 Malloced result string
* @retval 0 OK
* @retval -1 Error
* @note string malloced.
*/
int
ctx2string(xp_ctx *xc,
char **str0)
{
int retval = -1;
char *str = NULL;
int len;
char *b;
switch (xc->xc_type){
case XT_NODESET:
if (xc->xc_size && (b = xml_body(xc->xc_nodeset[0]))){
if ((str = strdup(b)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
}
else
if ((str = strdup("")) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
break;
case XT_BOOL:
if ((str = strdup(xc->xc_bool == 0?"false":"true")) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
break;
case XT_NUMBER:
len = snprintf(NULL, 0, "%0lf", xc->xc_number);
len++;
if ((str = malloc(len)) == NULL){
clicon_err(OE_XML, errno, "malloc");
goto done;
}
snprintf(str, len, "%0lf", xc->xc_number);
break;
case XT_STRING:
if ((str = strdup(xc->xc_string)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
break;
}
*str0 = str;
retval = 0;
done:
return retval;
}
/*! Convert xpath context to number according to number() function in XPATH spec
* @param[in] xc XPATH context
* @param[out] n0 Floating point or NAN
* @retval 0 OK
* @retval -1 Error
*/
int
ctx2number(xp_ctx *xc,
double *n0)
{
int retval = -1;
char *str = NULL;
double n;
switch (xc->xc_type){
case XT_NODESET:
if (ctx2string(xc, &str) < 0)
goto done;
if (sscanf(str, "%lf",&n) != 1)
n = NAN;
break;
case XT_BOOL:
n = (double)xc->xc_bool;
break;
case XT_NUMBER:
n = xc->xc_number;
break;
case XT_STRING:
if (sscanf(xc->xc_string, "%lf",&n) != 1)
n = NAN;
break;
}
*n0 = n;
retval = 0;
done:
if (str)
free(str);
return retval;
}
/*! Replace a nodeset of a XPATH context with a new nodeset
*/
int
ctx_nodeset_replace(xp_ctx *xc,
cxobj **vec,
size_t veclen)
{
if (xc->xc_nodeset)
free(xc->xc_nodeset);
xc->xc_nodeset = vec;
xc->xc_size = veclen;
return 0;
}

View file

@ -0,0 +1,101 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
*/
#ifndef _CLIXON_XPATH_PARSE_H_
#define _CLIXON_XPATH_PARSE_H_
/*
* Types
*/
/* used as non-terminal type in yacc rules */
enum xp_type{
XP_EXP,
XP_AND,
XP_RELEX,
XP_ADD,
XP_UNION,
XP_PATHEXPR,
XP_LOCPATH,
XP_ABSPATH,
XP_RELLOCPATH,
XP_STEP,
XP_NODE,
XP_NODE_FN,
XP_PRED,
XP_PRI0,
XP_PRIME_NR,
XP_PRIME_STR,
XP_PRIME_FN,
};
/*! XPATH Parsing generates a tree of nodes that is later traversed
*/
struct xpath_tree{
enum xp_type xs_type;
int xs_int;
double xs_double;
char *xs_s0;
char *xs_s1;
struct xpath_tree *xs_c0; /* child 0 */
struct xpath_tree *xs_c1; /* child 1 */
};
typedef struct xpath_tree xpath_tree;
struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */
const char *xy_name; /* Name of syntax (for error string) */
int xy_linenum; /* Number of \n in parsed buffer */
char *xy_parse_string; /* original (copy of) parse string */
void *xy_lexbuf; /* internal parse buffer from lex */
xpath_tree *xy_top;
};
/*
* Variables
*/
extern char *clixon_xpath_parsetext;
/*
* Prototypes
*/
int xpath_scan_init(struct clicon_xpath_yacc_arg *jy);
int xpath_scan_exit(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_init(struct clicon_xpath_yacc_arg *jy);
int xpath_parse_exit(struct clicon_xpath_yacc_arg *jy);
int clixon_xpath_parselex(void *);
int clixon_xpath_parseparse(void *);
void clixon_xpath_parseerror(void *, char*);
#endif /* _CLIXON_XPATH_PARSE_H_ */

View file

@ -0,0 +1,174 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 Olof Hagsand and Benny Holmgren
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
*/
%{
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <netinet/in.h>
#include "clixon_xpath_parse.tab.h" /* generated */
#include <cligen/cligen.h>
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
#define YY_DECL int clixon_xpath_parselex(void *_yy)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _XY ((struct clicon_xpath_yacc_arg *)_yy)
#define MAXBUF 4*4*64*1024
#undef clixon_xpath_parsewrap
int
clixon_xpath_parsewrap(void)
{
return 1;
}
%}
digit [0-9]
integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
%x TOKEN
%s QLITERAL
%s ALITERAL
%%
<TOKEN>[ \t]
<TOKEN>\n { _XY->xy_linenum++; }
<TOKEN>\r { }
<TOKEN><<EOF>> { return X_EOF; }
<TOKEN>".." { return DOUBLEDOT; }
<TOKEN>[()\[\]\.@,/:|] { return *yytext; }
<TOKEN>"::" { return DOUBLECOLON; }
<TOKEN>and { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>or { clixon_xpath_parselval.intval = clicon_str2int(xpopmap, yytext); return LOGOP; }
<TOKEN>div { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>mod { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>[+*\-] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return ADDOP; }
<TOKEN>\? { return *yytext; }
<TOKEN>"//" { return DOUBLESLASH; }
<TOKEN>"!=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext); return RELOP; }
<TOKEN>">=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>"<=" { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>[<>=] { clixon_xpath_parselval.intval = clicon_str2int(xpopmap,yytext);return RELOP; }
<TOKEN>last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
<TOKEN>position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
<TOKEN>count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
<TOKEN>ancestor { clixon_xpath_parselval.intval = A_ANCESTOR; return AXISNAME; }
<TOKEN>ancestor-or-self { clixon_xpath_parselval.intval = A_ANCESTOR_OR_SELF; return AXISNAME; }
<TOKEN>attribute { clixon_xpath_parselval.intval = A_ATTRIBUTE; return AXISNAME; }
<TOKEN>child { clixon_xpath_parselval.intval = A_CHILD; return AXISNAME; }
<TOKEN>descendant { clixon_xpath_parselval.intval = A_DESCENDANT; return AXISNAME; }
<TOKEN>descendant-or-self { clixon_xpath_parselval.intval = A_DESCENDANT_OR_SELF; return AXISNAME; }
<TOKEN>following { clixon_xpath_parselval.intval = A_FOLLOWING; return AXISNAME; }
<TOKEN>following-sibling { clixon_xpath_parselval.intval = A_FOLLOWING_SIBLING; return AXISNAME; }
<TOKEN>namespace { clixon_xpath_parselval.intval = A_NAMESPACE; return AXISNAME; }
<TOKEN>parent { clixon_xpath_parselval.intval = A_PARENT; return AXISNAME; }
<TOKEN>preceding { clixon_xpath_parselval.intval = A_PRECEEDING; return AXISNAME; }
<TOKEN>preceding-sibling { clixon_xpath_parselval.intval = A_PRECEEDING_SIBLING; return AXISNAME; }
<TOKEN>self { clixon_xpath_parselval.intval = A_SELF; return AXISNAME; }
<TOKEN>current { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>comment { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>text { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>processing-instructions { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>node { clixon_xpath_parselval.string = strdup(yytext); return NODETYPE; }
<TOKEN>\" { BEGIN(QLITERAL); return QUOTE; }
<TOKEN>\' { BEGIN(ALITERAL); return APOST; }
<TOKEN>\-?({integer}|{real}) { sscanf(yytext,"%lf",&clixon_xpath_parselval.dval); return NUMBER;}
<TOKEN>[0-9A-Za-z_\-]+ { clixon_xpath_parselval.string = strdup(yytext);
return NAME; /* rather be catch-all */
}
<TOKEN>. { fprintf(stderr,"LEXICAL ERROR\n"); return -1; }
<QLITERAL>\" { BEGIN(TOKEN); return QUOTE; }
<QLITERAL>. { clixon_xpath_parselval.string = strdup(yytext);
return CHAR;}
<ALITERAL>\' { BEGIN(TOKEN); return APOST; }
<ALITERAL>. { clixon_xpath_parselval.string = strdup(yytext);
return CHAR;}
%%
/*! Initialize scanner.
*/
int
xpath_scan_init(struct clicon_xpath_yacc_arg *xy)
{
BEGIN(TOKEN);
xy->xy_lexbuf = yy_scan_string (xy->xy_parse_string);
#if 1 /* XXX: just to use unput to avoid warning */
if (0)
yyunput(0, "");
#endif
return 0;
}
/*
* free buffers
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
*/
int
xpath_scan_exit(struct clicon_xpath_yacc_arg *xy)
{
yy_delete_buffer(xy->xy_lexbuf);
clixon_xpath_parselex_destroy(); /* modern */
return 0;
}

View file

@ -0,0 +1,289 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2018 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 *****
* XPATH Parser
* From https://www.w3.org/TR/xpath-10/
* The primary syntactic construct in XPath is the expression.
* An expression matches the production Expr
* see https://www.w3.org/TR/xpath-10/#NT-Expr)
* Lexical structure is defined by ExprToken, see
* see https://www.w3.org/TR/xpath-10/#exprlex
*/
%start start
%union {
int intval;
double dval;
char *string;
void *stack; /* xpath_tree */
}
%token <intval> AXISNAME
%token <intval> LOGOP
%token <intval> ADDOP
%token <intval> RELOP
%token <dval> NUMBER
%token <string> X_EOF
%token <string> QUOTE
%token <string> APOST
%token <string> CHAR
%token <string> NAME
%token <string> NODETYPE
%token <string> DOUBLEDOT
%token <string> DOUBLECOLON
%token <string> DOUBLESLASH
%token <string> FUNCTIONNAME
%type <intval> axisspec
%type <string> string
%type <stack> expr
%type <stack> andexpr
%type <stack> relexpr
%type <stack> addexpr
%type <stack> unionexpr
%type <stack> pathexpr
%type <stack> locationpath
%type <stack> abslocpath
%type <stack> rellocpath
%type <stack> step
%type <stack> nodetest
%type <stack> predicates
%type <stack> primaryexpr
%lex-param {void *_xy} /* Add this argument to parse() and lex() function */
%parse-param {void *_xy}
%{
/* Here starts user C-code */
/* typecast macro */
#define _XY ((struct clicon_xpath_yacc_arg *)_xy)
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_xpath_parsetext, _XY->xy_linenum); YYERROR;}
/* add _yy to error paramaters */
#define YY_(msgid) msgid
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <fnmatch.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <cligen/cligen.h>
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xpath_parse.h"
extern int clixon_xpath_parseget_lineno (void);
/*
also called from yacc generated code *
*/
void
clixon_xpath_parseerror(void *_xy,
char *s)
{
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
_XY->xy_name,
_XY->xy_linenum ,
s,
clixon_xpath_parsetext);
return;
}
int
xpath_parse_init(struct clicon_xpath_yacc_arg *xy)
{
// clicon_debug_init(2, NULL);
return 0;
}
int
xpath_parse_exit(struct clicon_xpath_yacc_arg *xy)
{
return 0;
}
static xpath_tree *
xp_new(enum xp_type type,
int i0,
double d0,
char *s0,
char *s1,
xpath_tree *c0,
xpath_tree *c1)
{
xpath_tree *xs = NULL;
if ((xs = malloc(sizeof(xpath_tree))) == NULL){
clicon_err(OE_XML, errno, "malloc");
goto done;
}
memset(xs, 0, sizeof(*xs));
xs->xs_type = type;
xs->xs_int = i0;
xs->xs_double = d0;
xs->xs_s0 = s0;
xs->xs_s1 = s1;
xs->xs_c0 = c0;
xs->xs_c1 = c1;
done:
return xs;
}
%}
%%
/*
*/
start : expr X_EOF { _XY->xy_top=$1;clicon_debug(1,"start->expr"); YYACCEPT; }
| locationpath X_EOF { _XY->xy_top=$1;clicon_debug(1,"start->locationpath"); YYACCEPT; }
;
expr : expr LOGOP andexpr { $$=xp_new(XP_EXP,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"expr->expr or andexpr"); }
| andexpr { $$=xp_new(XP_EXP,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"expr-> andexpr"); }
;
andexpr : andexpr LOGOP relexpr { $$=xp_new(XP_AND,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"andexpr-> andexpr and relexpr"); }
| relexpr { $$=xp_new(XP_AND,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"andexpr-> relexpr"); }
;
relexpr : relexpr RELOP addexpr { $$=xp_new(XP_RELEX,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"relexpr-> relexpr relop addexpr"); }
| addexpr { $$=xp_new(XP_RELEX,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"relexpr-> addexpr"); }
;
addexpr : addexpr ADDOP unionexpr { $$=xp_new(XP_ADD,$2,0.0,NULL,NULL,$1, $3);clicon_debug(1,"addexpr-> addexpr ADDOP unionexpr"); }
| unionexpr { $$=xp_new(XP_ADD,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"addexpr-> unionexpr"); }
;
/* node-set */
unionexpr : unionexpr '|' pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(1,"unionexpr-> unionexpr | pathexpr"); }
| pathexpr { $$=xp_new(XP_UNION,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"unionexpr-> pathexpr"); }
;
pathexpr : locationpath { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"pathexpr-> locationpath"); }
| primaryexpr { $$=xp_new(XP_PATHEXPR,A_NAN,0.0,NULL,NULL,$1, NULL);clicon_debug(1,"pathexpr-> primaryexpr"); }
;
/* location path returns a node-set */
locationpath : rellocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"locationpath-> rellocpath"); }
| abslocpath { $$=xp_new(XP_LOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"locationpath-> abslocpath"); }
;
abslocpath : '/' { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,NULL, NULL);clicon_debug(1,"abslocpath-> /"); }
| '/' rellocpath { $$=xp_new(XP_ABSPATH,A_ROOT,0.0,NULL,NULL,$2, NULL);clicon_debug(1,"abslocpath->/ rellocpath");}
/* // is short for /descendant-or-self::node()/ */
| DOUBLESLASH rellocpath {$$=xp_new(XP_ABSPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$2, NULL); clicon_debug(1,"abslocpath-> // rellocpath"); }
;
rellocpath : step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, NULL); clicon_debug(1,"rellocpath-> step"); }
| rellocpath '/' step { $$=xp_new(XP_RELLOCPATH,A_NAN,0.0,NULL,NULL,$1, $3);clicon_debug(1,"rellocpath-> rellocpath / step"); }
| rellocpath DOUBLESLASH step { $$=xp_new(XP_RELLOCPATH,A_DESCENDANT_OR_SELF,0.0,NULL,NULL,$1, $3); clicon_debug(1,"rellocpath-> rellocpath // step"); }
;
step : axisspec nodetest predicates {$$=xp_new(XP_STEP,$1,0.0, NULL, NULL, $2, $3);clicon_debug(1,"step->axisspec(%d) nodetest", $1); }
| '.' predicates { $$=xp_new(XP_STEP,A_SELF, 0.0,NULL, NULL, NULL, $2); clicon_debug(1,"step-> ."); }
| DOUBLEDOT predicates { $$=xp_new(XP_STEP, A_PARENT, 0.0,NULL, NULL, NULL, $2); clicon_debug(1,"step-> .."); }
;
axisspec : AXISNAME DOUBLECOLON { clicon_debug(1,"axisspec-> AXISNAME(%d) ::", $1); $$=$1;}
| '@' { $$=A_ATTRIBUTE; clicon_debug(1,"axisspec-> @"); }
| { clicon_debug(1,"axisspec-> "); $$=A_CHILD;}
;
nodetest : '*' { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(1,"nodetest-> *"); }
| NAME { $$=xp_new(XP_NODE,A_NAN,0.0, NULL, $1, NULL, NULL); clicon_debug(1,"nodetest-> name(%s)",$1); }
| NAME ':' NAME { $$=xp_new(XP_NODE,A_NAN,0.0, $1, $3, NULL, NULL);clicon_debug(1,"nodetest-> name(%s) : name(%s)", $1, $3); }
| NAME ':' '*' { $$=xp_new(XP_NODE,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(1,"nodetest-> name(%s) : *", $1); }
| NODETYPE '(' ')' { $$=xp_new(XP_NODE_FN,A_NAN,0.0, $1, NULL, NULL, NULL); clicon_debug(1,"nodetest-> nodetype()"); }
;
/* evaluates to boolean */
predicates : predicates '[' expr ']' { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, $1, $3); clicon_debug(1,"predicates-> [ expr ]"); }
| { $$=xp_new(XP_PRED,A_NAN,0.0, NULL, NULL, NULL, NULL); clicon_debug(1,"predicates->"); }
;
primaryexpr : '(' expr ')' { $$=xp_new(XP_PRI0,A_NAN,0.0, NULL, NULL, $2, NULL); clicon_debug(1,"primaryexpr-> ( expr )"); }
| NUMBER { $$=xp_new(XP_PRIME_NR,A_NAN, $1, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> NUMBER(%lf)", $1); }
| QUOTE string QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> \" string \""); }
| QUOTE QUOTE { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> \" \""); }
| APOST string APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, $2, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> ' string '"); }
| APOST APOST { $$=xp_new(XP_PRIME_STR,A_NAN,0.0, NULL, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> ' '"); }
| FUNCTIONNAME '(' ')' { $$=xp_new(XP_PRIME_FN,A_NAN,0.0, $1, NULL, NULL, NULL);clicon_debug(1,"primaryexpr-> functionname ( arguments )"); }
;
/* XXX Adding this between FUNCTIONNAME() breaks parser,..
arguments : arguments expr { clicon_debug(1,"arguments-> arguments expr"); }
| { clicon_debug(1,"arguments-> "); }
;
*/
string : string CHAR {
int len = strlen($1);
$$ = realloc($1, len+strlen($2) + 1);
sprintf($$+len, "%s", $2);
free($2);
clicon_debug(1,"string-> string CHAR");
}
| CHAR { clicon_debug(1,"string-> "); }
;
%%

View file

@ -35,55 +35,17 @@
* NOTE: there is a main function at the end of this file where you can test out
* different xpath expressions.
* Look at the end of the file for a test unit program
The code is implemented according to XPATH 1.0:
https://www.w3.org/TR/xpath-10/
The primary syntactic construct in XPath is the expression. An expression matches
the production Expr (see https://www.w3.org/TR/xpath-10/#NT-Expr)
*/
/*
See https://www.w3.org/TR/xpath/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
Implementation of a limited xslt xpath syntax. Some examples. Given the following
xml tree:
<aaa>
<bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd>
</aaa>
With the following xpath examples. There are some diffs and many limitations compared
to the xml standards:
/ whole tree <aaa>...</aaa>
/bbb
/aaa/bbb <bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
//bbb as above
//b?b as above
//b\* as above
//b\*\/ccc <ccc>42</ccc>
<ccc>99</ccc>
//\*\/ccc <ccc>42</ccc>
<ccc>99</ccc>
<ccc>22</ccc>
-- //bbb@x x="hello"
//bbb[@x] <bbb x="hello"><ccc>42</ccc></bbb>
<bbb x="bye"><ccc>99</ccc></bbb>
//bbb[@x=hello] <bbb x="hello"><ccc>42</ccc></bbb>
//bbb[@x="hello"] as above
//bbb[0] <bbb x="hello"><ccc>42</ccc></bbb>
//bbb[ccc=99] <bbb x="bye"><ccc>99</ccc></bbb>
--- //\*\/[ccc=99] same as above
'//bbb | //ddd' <bbb><ccc>42</ccc></bbb>
<bbb x="hello"><ccc>99</ccc></bbb>
<ddd><ccc>22</ccc></ddd> (NB spaces)
etc
For xpath v1.0 see http://www.w3.org/TR/xpath/
record[name=c][time=d]
in
<record>
<name>c</name>
<time>d</time>
<userid>45654df4-2292-45d3-9ca5-ee72452568a8</userid>
</record>
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -94,6 +56,8 @@ in
#include <stdint.h>
#include <assert.h>
#include <syslog.h>
#include <fcntl.h>
#include <math.h>
/* cligen */
#include <cligen/cligen.h>
@ -107,14 +71,18 @@ in
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_xsl.h"
/* Constants */
#define XPATH_VEC_START 128
/*
* Types
*/
struct searchvec{
cxobj **sv_v0; /* here is result */
int sv_v0len;
@ -126,13 +94,22 @@ typedef struct searchvec searchvec;
/* Local types
*/
enum axis_type{
A_SELF,
A_CHILD,
A_PARENT,
A_ROOT,
A_ANCESTOR,
A_DESCENDANT_OR_SELF, /* actually descendant-or-self */
struct xpath_predicate{
struct xpath_predicate *xp_next;
char *xp_expr;
};
/* XPATH Axis according to https://www.w3.org/TR/xpath-10/#NT-Step
* Axis ::= AxisSpecifier NodeTest Predicate*
* Eg "child::
*/
struct xpath_element{
struct xpath_element *xe_next;
enum axis_type xe_type;
char *xe_prefix; /* eg for namespaces */
char *xe_str; /* eg for child */
struct xpath_predicate *xe_predicate; /* eg within [] */
};
/* Mapping between axis type string <--> int */
@ -146,23 +123,12 @@ static const map_str2int axismap[] = {
{NULL, -1}
};
struct xpath_predicate{
struct xpath_predicate *xp_next;
char *xp_expr;
};
struct xpath_element{
struct xpath_element *xe_next;
enum axis_type xe_type;
char *xe_prefix; /* eg for namespaces */
char *xe_str; /* eg for child */
struct xpath_predicate *xe_predicate; /* eg within [] */
};
static int xpath_split(char *xpathstr, char **pathexpr);
/*! Print xpath structure for debug */
static int
xpath_print(FILE *f, struct xpath_element *xplist)
xpath_print(FILE *f,
struct xpath_element *xplist)
{
struct xpath_element *xe;
struct xpath_predicate *xp;
@ -204,7 +170,7 @@ xpath_parse_predicate(struct xpath_element *xe,
}
memset(xp, 0, sizeof(*xp));
if ((xp->xp_expr = strdup(s)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
xp->xp_next = xe->xe_predicate;
@ -216,6 +182,11 @@ xpath_parse_predicate(struct xpath_element *xe,
return retval;
}
/*! XPATH parse, create new child element
* @param[in] atype Axis type, see https://www.w3.org/TR/xpath-10/#axes
* @param[in] str
* @param[out] xpnext
*/
static int
xpath_element_new(enum axis_type atype,
char *str,
@ -235,7 +206,7 @@ xpath_element_new(enum axis_type atype,
xe->xe_type = atype;
if (str){
if ((str1 = strdup(str)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
if (xpath_split(str1, &pred) < 0) /* Can be more predicates */
@ -246,20 +217,20 @@ xpath_element_new(enum axis_type atype,
*local = '\0';
local++;
if ((xe->xe_prefix = strdup(str1)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
}
else
local = str1;
if ((xe->xe_str = strdup(local)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
}
else{
if ((xe->xe_str = strdup("*")) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
}
@ -308,8 +279,17 @@ xpath_free(struct xpath_element *xplist)
return 0;
}
/*
* // is short for /descendant-or-self::node()/
/*! Parse xpath to xpath_element structure
*
* [1] LocationPath ::= RelativeLocationPath
* | AbsoluteLocationPath
* [2] AbsoluteLocationPath ::= '/' RelativeLocationPath?
* | AbbreviatedAbsoluteLocationPath
* [3] RelativeLocationPath ::= Step
| RelativeLocationPath '/' Step
| AbbreviatedRelativeLocationPath
* @see https://www.w3.org/TR/xpath-10/#NT-LocationPath
*/
static int
xpath_parse(char *xpath,
@ -325,7 +305,7 @@ xpath_parse(char *xpath,
int esc = 0;
if ((s0 = strdup(xpath)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
s = s0;
@ -354,6 +334,7 @@ xpath_parse(char *xpath,
}
s++;
}
/* Iterate through steps (s), see https://www.w3.org/TR/xpath-10/#NT-Step */
s = s0;
for (i=0; i<nvec; i++){
if ((i==0 && strcmp(s,"")==0)) /* Initial / or // */
@ -363,21 +344,10 @@ xpath_parse(char *xpath,
else if (strncmp(s,"descendant-or-self::", strlen("descendant-or-self::"))==0){
xpath_element_new(A_DESCENDANT_OR_SELF, s+strlen("descendant-or-self::"), &xpnext);
}
#if 1
else if (strncmp(s,"..", strlen(".."))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, s+strlen(".."), &xpnext);
#else
else if (strncmp(s,"..", strlen(s))==0) /* abbreviatedstep */
xpath_element_new(A_PARENT, NULL, &xpnext);
#endif
#if 1 /* Problems with .[userid=1321] */
else if (strncmp(s,".", strlen("."))==0)
xpath_element_new(A_SELF, s+strlen("."), &xpnext);
#else
else if (strncmp(s,".", strlen(s))==0) /* abbreviatedstep */
xpath_element_new(A_SELF, NULL, &xpnext);
#endif
else if (strncmp(s,"self::", strlen("self::"))==0)
xpath_element_new(A_SELF, s+strlen("self::"), &xpnext);
@ -404,23 +374,21 @@ xpath_parse(char *xpath,
*
* The xv_* arguments are filled in nodes found earlier.
* args:
* @param[in] xn_parent Base XML object
* @param[in] name shell wildcard pattern to match with node name
* @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY
* @param[in,out] vec1 internal buffers with results
* @param[in,out] vec0 internal buffers with results
* @param[in,out] vec_len internal buffers with length of vec0,vec1
* @param[in,out] vec_max internal buffers with max of vec0,vec1
* @param[in] xn_parent Base XML object
* @param[in] pattern Shell wildcard pattern to match with node name
* @param[in] node_type CX_ELMNT, CX_ATTR or CX_BODY
* @param[in,out] vec0 Internal buffers with results
* @param[in,out] vec0len Internal buffers with length of vec0
* returns:
* 0 on OK, -1 on error
*/
static int
recursive_find(cxobj *xn,
char *pattern,
int node_type,
uint16_t flags,
cxobj ***vec0,
size_t *vec0len)
xpath_recursive_find(cxobj *xn,
char *pattern,
int node_type,
uint16_t flags,
cxobj ***vec0,
size_t *vec0len)
{
int retval = -1;
cxobj *xsub;
@ -438,7 +406,7 @@ recursive_find(cxobj *xn,
goto done;
// continue; /* Dont go deeper */
}
if (recursive_find(xsub, pattern, node_type, flags, &vec, &veclen) < 0)
if (xpath_recursive_find(xsub, pattern, node_type, flags, &vec, &veclen) < 0)
goto done;
}
retval = 0;
@ -501,8 +469,7 @@ xpath_expr(cxobj *xcur,
e_v=e;
e_a = strsep(&e_v, "=");
if (e_a == NULL){
clicon_err(OE_XML, errno, "%s: malformed expression: [@%s]",
__FUNCTION__, e);
clicon_err(OE_XML, errno, "malformed expression: [@%s]", e);
goto done;
}
for (i=0; i<*vec0len; i++){
@ -533,15 +500,13 @@ xpath_expr(cxobj *xcur,
}
}
else{
clicon_err(OE_XML, errno, "%s: malformed expression: [%s]",
__FUNCTION__, e);
clicon_err(OE_XML, errno, "malformed expression: [%s]", e);
goto done;
}
}
else{ /* name = expr */
if ((tag = strsep(&e, "=")) == NULL){
clicon_err(OE_XML, errno, "%s: malformed expression: [%s]",
__FUNCTION__, e);
clicon_err(OE_XML, errno, "malformed expression: [%s]", e);
goto done;
}
/* Strip trailing spaces */
@ -697,7 +662,7 @@ xpath_find(cxobj *xcur,
if (descendants0){
for (i=0; i<vec0len; i++){
xv = vec0[i];
if (recursive_find(xv, xe->xe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0)
if (xpath_recursive_find(xv, xe->xe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0)
goto done;
}
}
@ -740,7 +705,6 @@ xpath_find(cxobj *xcur,
}
}
}
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done;
@ -781,7 +745,7 @@ xpath_split(char *xpathstr,
}
}
if (pe==NULL){
clicon_err(OE_XML, errno, "%s: mismatched []: %s", __FUNCTION__, xpathstr);
clicon_err(OE_XML, errno, "mismatched []: %s", xpathstr);
goto done;
}
}
@ -800,6 +764,7 @@ xpath_split(char *xpathstr,
* @param[in] flags if != 0, only match xml nodes matching flags
* @param[out] vec2 Result XML node vector
* @param[out] vec2len Length of result vector.
* @see https://www.w3.org/TR/xpath-10/#NT-LocationPath
*/
static int
xpath_exec(cxobj *xcur,
@ -833,14 +798,20 @@ xpath_exec(cxobj *xcur,
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] flags if != 0, only match xml nodes matching flags
* @param[in] vec1 vector of XML trees
* @param[in] vec1len length of XML trees
* @param[out] vec1 vector of XML trees
* @param[out] vec1len length of XML trees
* For example: xpath = //a | //b.
* xpath_first+ splits xpath up in several subcalls
* (eg xpath=//a and xpath=//b) and collects the results.
* Note: if a match is found in both, two (or more) same results will be
* returned.
* Note, this could be 'folded' into xpath1 but I judged it too complex.
* @see https://www.w3.org/TR/xpath-10/#NT-Expr
* An 'Expr' is composed of compositions of and, or, =, +, -, down to:
* PathExpr ::= LocationPath
* | FilterExpr
* | FilterExpr '/' RelativeLocationPath
* | FilterExpr '//' RelativeLocationPath
*/
static int
xpath_choice(cxobj *xcur,
@ -850,7 +821,7 @@ xpath_choice(cxobj *xcur,
size_t *vec1len)
{
int retval = -1;
char *s0;
char *s0 = NULL;
char *s1;
char *s2;
char *xpath;
@ -858,7 +829,7 @@ xpath_choice(cxobj *xcur,
size_t vec0len = 0;
if ((s0 = strdup(xpath0)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_XML, errno, "strdup");
goto done;
}
s2 = s1 = s0;
@ -880,7 +851,7 @@ xpath_choice(cxobj *xcur,
goto done;
}
retval = 0;
done:
done:
if (s0)
free(s0);
if (vec0)
@ -888,11 +859,26 @@ xpath_choice(cxobj *xcur,
return retval;
}
/*! Help function to xpath_first
/*! Xpath nodeset function where only the first matching entry is returned
* args:
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @retval xml-tree of first match
* @retval NULL Error or not found
*
* @code
* cxobj *x;
* if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) {
* ...
* }
* @endcode
* @note the returned pointer points into the original tree so should not be freed fter use.
* @note return value does not see difference between error and not found
* @see xpath_first. This is obsolete and only enabled with COMPAT_XSL
*/
static cxobj *
xpath_first0(cxobj *xcur,
char *xpath)
cxobj *
xpath_first_xsl(cxobj *xcur,
char *xpath)
{
cxobj **vec1 = NULL;
size_t vec1len = 0;
@ -910,57 +896,7 @@ xpath_first0(cxobj *xcur,
return xn;
}
/*! A restricted xpath function where the first matching entry is returned
* See xpath1() on details for subset.
* args:
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @retval xml-tree of first match
* @retval NULL Error or not found
*
* @code
* cxobj *x;
* if ((x = xpath_first(xtop, "//symbol/foo")) != NULL) {
* ...
* }
* @endcode
* @note the returned pointer points into the original tree so should not be freed after use.
* @note return value does not see difference between error and not found
* @see also xpath_vec.
*/
cxobj *
xpath_first(cxobj *xcur,
char *format,
...)
{
cxobj *retval = NULL;
va_list ap;
size_t len;
char *xpath;
va_start(ap, format);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, format);
if (vsnprintf(xpath, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
retval = xpath_first0(xcur, xpath);
done:
if (xpath)
free(xpath);
return retval;
}
#ifdef COMPAT_XSL
/*! A restricted xpath iterator that loops over all matching entries. Dont use.
*
* See xpath1() on details for subset.
@ -976,10 +912,9 @@ xpath_first(cxobj *xcur,
* }
* @endcode
*
* Note that the returned pointer points into the original tree so should not be freed
* @note The returned pointer points into the original tree so should not be freed
* after use.
* @see also xpath, xpath_vec.
* NOTE: uses a static variable: consider replacing with xpath_vec() instead
* @note obsolete. Dont use
*/
cxobj *
xpath_each(cxobj *xcur,
@ -1018,11 +953,12 @@ xpath_each(cxobj *xcur,
done:
return xn;
}
#endif
/*! A restricted xpath that returns a vector of matches
*
* See xpath1() on details for subset
. * @param[in] xcur xml-tree where to search
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value
@ -1043,41 +979,22 @@ xpath_each(cxobj *xcur,
* @note Although the returned vector must be freed after use,
* the returned xml trees should not.
* @see also xpath_first, xpath_each.
* @see xpath_vec. This is obsolete and only enabled with COMPAT_XSL
*/
int
xpath_vec(cxobj *xcur,
char *format,
cxobj ***vec,
size_t *veclen,
...)
xpath_vec_xsl(cxobj *xcur,
char *xpath,
cxobj ***vec,
size_t *veclen)
{
int retval = -1;
va_list ap;
size_t len;
char *xpath;
va_start(ap, veclen);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, veclen);
if (vsnprintf(xpath, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
*vec = NULL;
*veclen = 0;
retval = xpath_choice(xcur, xpath, 0x0, vec, veclen);
if (xpath_choice(xcur, xpath, 0x0, vec, veclen) < 0)
goto done;
retval = 0;
done:
if (xpath)
free(xpath);
return retval;
}
@ -1102,99 +1019,23 @@ xpath_vec(cxobj *xcur,
* @endcode
* @Note that although the returned vector must be freed after use, the returned xml
* trees need not be.
* @see also xpath_vec This is a specialized version.
* @see xpath_vec_flag. This is obsolete and only enabled with COMPAT_XSL
*/
int
xpath_vec_flag(cxobj *xcur,
char *format,
uint16_t flags,
cxobj ***vec,
size_t *veclen,
...)
xpath_vec_flag_xsl(cxobj *xcur,
char *xpath,
uint16_t flags,
cxobj ***vec,
size_t *veclen)
{
int retval = -1;
va_list ap;
size_t len;
char *xpath;
va_start(ap, veclen);
len = vsnprintf(NULL, 0, format, ap);
va_end(ap);
/* allocate a message string exactly fitting the message length */
if ((xpath = malloc(len+1)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
/* second round: compute write message from reason and args */
va_start(ap, veclen);
if (vsnprintf(xpath, len+1, format, ap) < 0){
clicon_err(OE_UNIX, errno, "vsnprintf");
va_end(ap);
goto done;
}
va_end(ap);
*vec=NULL;
*veclen = 0;
retval = xpath_choice(xcur, xpath, flags, vec, veclen);
if (xpath_choice(xcur, xpath, flags, vec, veclen) < 0)
goto done;
retval = 0;
done:
if (xpath)
free(xpath);
return retval;
}
/*
* Turn this on to get an xpath test program
* Usage: xpath [<xpath>]
* read xml from input
* Example compile:
gcc -g -o xpath -I. -I../clixon ./clixon_xsl.c -lclixon -lcligen
* Example run:
echo "<a><b/></a>" | xpath "a"
*/
#if 0 /* Test program */
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s <xpath>.\n\tInput on stdin\n", argv0);
exit(0);
}
int
main(int argc, char **argv)
{
int i;
cxobj **xv
cxobj *x;
cxobj *xn;
size_t xlen = 0;
if (argc != 2){
usage(argv[0]);
return 0;
}
if (xml_parse_file(0, &x, "</clicon>") < 0){
fprintf(stderr, "parsing 2\n");
return -1;
}
printf("\n");
if (xpath_vec(x, argv[1], &xv, &xlen) < 0)
return -1;
if (xv){
for (i=0; i<xlen; i++){
xn = xv[i];
fprintf(stdout, "[%d]:\n", i);
clicon_xml2file(stdout, xn, 0, 1);
}
free(xv);
}
if (x)
xml_free(x);
return 0;
}
#endif /* Test program */

View file

@ -45,11 +45,14 @@
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <unistd.h>
#define __USE_GNU /* strverscmp */
#include <string.h>
#include <arpa/inet.h>
#include <regex.h>
#include <dirent.h>
#include <sys/types.h>
#include <fcntl.h>
#include <syslog.h>
#include <assert.h>
#include <sys/stat.h>
@ -74,6 +77,8 @@
#include "clixon_yang_type.h"
#include "clixon_yang_parse.h"
/* Size of json read buffer when reading from file*/
#define BUFLEN 1024
/* Mapping between yang keyword string <--> clicon constants */
static const map_str2int ykmap[] = {
@ -158,7 +163,7 @@ yspec_new(void)
yang_spec *yspec;
if ((yspec = malloc(sizeof(*yspec))) == NULL){
clicon_err(OE_YANG, errno, "%s: malloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "malloc");
return NULL;
}
memset(yspec, 0, sizeof(*yspec));
@ -176,7 +181,7 @@ ys_new(enum rfc_6020 keyw)
yang_stmt *ys;
if ((ys = malloc(sizeof(*ys))) == NULL){
clicon_err(OE_YANG, errno, "%s: malloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "malloc");
return NULL;
}
memset(ys, 0, sizeof(*ys));
@ -184,7 +189,7 @@ ys_new(enum rfc_6020 keyw)
/* The cvec contains stmt-specific variables. Only few stmts need variables so the
cvec could be lazily created to save some heap and cycles. */
if ((ys->ys_cvec = cvec_new(0)) == NULL){
clicon_err(OE_YANG, errno, "%s: cvec_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cvec_new");
return NULL;
}
return ys;
@ -247,7 +252,7 @@ yn_realloc(yang_node *yn)
yn->yn_len++;
if ((yn->yn_stmt = realloc(yn->yn_stmt, (yn->yn_len)*sizeof(yang_stmt *))) == 0){
clicon_err(OE_YANG, errno, "%s: realloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "realloc");
return -1;
}
yn->yn_stmt[yn->yn_len - 1] = NULL; /* init field */
@ -255,6 +260,13 @@ yn_realloc(yang_node *yn)
}
/*! Copy yang statement recursively from old to new
* @param[in] ynew New empty (but created) yang statement (to)
* @param[in] yold Old existing yang statement (from)
* @code
* yang_stmt *new = ys_new(Y_LEAF);
* if (ys_cp(new, old) < 0)
* err;
* @endcode
*/
int
ys_cp(yang_stmt *ynew,
@ -269,22 +281,22 @@ ys_cp(yang_stmt *ynew,
ynew->ys_parent = NULL;
if (yold->ys_stmt)
if ((ynew->ys_stmt = calloc(yold->ys_len, sizeof(yang_stmt *))) == NULL){
clicon_err(OE_YANG, errno, "%s: calloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "calloc");
goto done;
}
if (yold->ys_argument)
if ((ynew->ys_argument = strdup(yold->ys_argument)) == NULL){
clicon_err(OE_YANG, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
if (yold->ys_cv)
if ((ynew->ys_cv = cv_dup(yold->ys_cv)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_dup", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_dup");
goto done;
}
if (yold->ys_cvec)
if ((ynew->ys_cvec = cvec_dup(yold->ys_cvec)) == NULL){
clicon_err(OE_YANG, errno, "%s: cvec_dup", __FUNCTION__);
clicon_err(OE_YANG, errno, "cvec_dup");
goto done;
}
if (yold->ys_typecache){
@ -304,8 +316,10 @@ ys_cp(yang_stmt *ynew,
return retval;
}
/*! Create a new yang node and copy the contents recursively from the original.
*
/*! Create a new yang node and copy the contents recursively from the original. *
* @param[in] old Old existing yang statement (from)
* @retval NULL Error
* @retval new New created yang statement
* This may involve duplicating strings, etc.
* The new yang tree needs to be freed by ys_free().
* The parent of new is NULL, it needs to be explicityl inserted somewhere
@ -328,7 +342,6 @@ ys_dup(yang_stmt *old)
return new;
}
/*! Insert yang statement as child of a parent yang_statement, last in list
*
* Also add parent to child as up-pointer
@ -598,8 +611,8 @@ yang_find_schemanode(yang_node *yn,
/*! Find first matching data node in all (sub)modules in a yang spec
*
* @param[in] ysp Yang specification
* @param[in] argument if NULL, match any(first) argument. XXX is that really a case?
* @param[in] schemanode If set look for schema nodes, otherwise only data nodes
* @param[in] argument Name of node. If NULL match first
* @param[in] class See yang_class for class of yang nodes
* A yang specification has modules as children which in turn can have
* syntax-nodes as children. This function goes through all the modules to
* look for nodes. Note that if a child to a module is a choice,
@ -790,7 +803,7 @@ yarg_id(yang_stmt *ys)
return id;
}
/* Assume argument is id on the type: <[prefix:]id>, return 'prefix'
/*! Assume argument is id on the type: <[prefix:]id>, return 'prefix'
* @param[in] ys A yang statement
* @retval NULL No prefix
* @retval prefix Malloced string that needs to be freed by caller.
@ -810,6 +823,44 @@ yarg_prefix(yang_stmt *ys)
}
/*! Split yang node identifier into prefix and identifer.
* @param[in] node-id
* @param[out] prefix Malloced string. May be NULL.
* @param[out] id Malloced identifier.
* @retval 0 OK
* @retval -1 Error
* @note caller need to free id and prefix after use
*/
int
yang_nodeid_split(char *nodeid,
char **prefix,
char **id)
{
int retval = -1;
char *str;
if ((str = strchr(nodeid, ':')) == NULL){
if ((*id = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
}
else{
if ((*prefix = strdup(nodeid)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
(*prefix)[str-nodeid] = '\0';
str++;
if ((*id = strdup(str)) == NULL){
clicon_err(OE_YANG, errno, "strdup");
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Given a yang statement and a prefix, return yang module to that prefix
* Note, not the other module but the proxy import statement only
@ -896,7 +947,7 @@ yang_print(FILE *f,
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cbuf_new");
goto done;
}
if (yang_print_cbuf(cb, yn, 0) < 0)
@ -992,14 +1043,14 @@ ys_populate_leaf(yang_stmt *ys,
goto done;
/* 2. Create the CV using cvtype and name it */
if ((cv = cv_new(cvtype)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */
cv_dec64_n_set(cv, fraction_digits);
if (cv_name_set(cv, ys->ys_argument) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new_set", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new_set");
goto done;
}
/* 3. Check if default value. Here we parse the string in the default-stmt
@ -1077,7 +1128,7 @@ ys_populate_range(yang_stmt *ys,
yparent = ys->ys_parent; /* Find parent: type */
if (yparent->yn_keyword != Y_TYPE){
clicon_err(OE_YANG, 0, "%s: parent should be type", __FUNCTION__);
clicon_err(OE_YANG, 0, "parent should be type");
goto done;
}
if (yang_type_resolve(ys, (yang_stmt*)yparent, &yrestype,
@ -1097,7 +1148,8 @@ ys_populate_range(yang_stmt *ys,
}
if ((maxstr = strstr(minstr, "..")) != NULL){
if (strlen(maxstr) < 2){
clicon_err(OE_YANG, 0, "range statement: %s not on the form: <int>..<int>");
clicon_err(OE_YANG, 0, "range statement: %s not on the form: <int>..<int>",
ys->ys_argument);
goto done;
}
minstr[maxstr-minstr] = '\0';
@ -1160,6 +1212,8 @@ ys_populate_range(yang_stmt *ys,
/*! Sanity check yang type statement
* XXX: Replace with generic parent/child type-check
* @param[in] ys The yang statement (type) to populate.
* @
*/
static int
ys_populate_type(yang_stmt *ys,
@ -1191,28 +1245,86 @@ ys_populate_type(yang_stmt *ys,
return retval;
}
/*! Sanity check yang type statement
/*! Sanity check yang identity statement recursively
*
* Find base identities if any and add this identity to derived list.
* Do this recursively
* @param[in] ys The yang identity to populate.
* @param[in] arg If set contains a derived identifier
* @see validate_identityref which in runtime validates actual valoues
*/
static int
ys_populate_identity(yang_stmt *ys,
void *arg)
{
int retval = -1;
yang_stmt *ybase;
yang_stmt *yc = NULL;
yang_stmt *ybaseid;
cg_var *cv;
char *derid;
char *baseid;
char *prefix = NULL;
cbuf *cb = NULL;
char *idref = (char*)arg;
char *p;
if ((ybase = yang_find((yang_node*)ys, Y_BASE, NULL)) == NULL)
return 0;
if ((yang_find_identity(ys, ybase->ys_argument)) == NULL){
clicon_err(OE_YANG, 0, "Identity %s not found (base type of %s)",
ybase->ys_argument, ys->ys_argument);
goto done;
if (idref == NULL){
/* Create derived identity through prefix:id if not recursively called*/
derid = ys->ys_argument; /* derived id */
if ((prefix = yarg_prefix(ys)) == NULL){
if ((p = yang_find_myprefix(ys)) != NULL)
prefix = strdup(yang_find_myprefix(ys));
}
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (prefix)
cprintf(cb, "%s:%s", prefix, derid);
else
cprintf(cb, "%s", derid);
idref = cbuf_get(cb);
}
/* Iterate through all base statements and check the base identity exists
* AND populate the base identity recursively
*/
while ((yc = yn_each((yang_node*)ys, yc)) != NULL) {
if (yc->ys_keyword != Y_BASE)
continue;
baseid = yc->ys_argument;
if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){
clicon_err(OE_YANG, 0, "No such identity: %s", baseid);
goto done;
}
// continue; /* root identity */
/* Check if derived id is already in base identifier */
if (cvec_find(ybaseid->ys_cvec, idref) != NULL)
continue;
/* Add derived id to ybaseid */
if ((cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_UNIX, errno, "cv_new");
goto done;
}
/* add prefix */
cv_name_set(cv, idref);
cvec_append_var(ybaseid->ys_cvec, cv); /* cv copied */
if (cv){
cv_free(cv);
cv = NULL;
}
/* Transitive to the root */
if (ys_populate_identity(ybaseid, idref) < 0)
goto done;
}
retval = 0;
done:
if (prefix)
free(prefix);
if (cb)
cbuf_free(cb);
return retval;
}
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
*
* We do this in 2nd pass after complete parsing to be sure to have a complete parse-tree
@ -1433,7 +1545,7 @@ yang_expand(yang_node *yn)
size = (yn->yn_len - i - 1)*sizeof(struct yang_stmt *);
yn->yn_len += glen - 1;
if (glen && (yn->yn_stmt = realloc(yn->yn_stmt, (yn->yn_len)*sizeof(yang_stmt *))) == 0){
clicon_err(OE_YANG, errno, "%s: realloc", __FUNCTION__);
clicon_err(OE_YANG, errno, "realloc");
return -1;
}
/* Then move all existing elements up from i+1 (not uses-stmt) */
@ -1474,10 +1586,10 @@ yang_expand(yang_node *yn)
* Syntax parsing. A string is input and a syntax-tree is returned (or error).
* A variable record is also returned containing a list of (global) variable values.
* (cloned from cligen)
* @param h CLICON handle
* @param str String of yang statements
* @param name Log string, typically filename
* @param ysp Yang specification. Should ave been created by caller using yspec_new
* @param[in] h CLICON handle
* @param[in] str String of yang statements
* @param[in] name Log string, typically filename
* @param[in] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
* Calling order:
@ -1488,21 +1600,22 @@ yang_expand(yang_node *yn)
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_str(clicon_handle h,
char *str,
yang_parse_str(char *str,
const char *name, /* just for errs */
yang_spec *yspec)
{
struct clicon_yang_yacc_arg yy = {0,};
yang_stmt *ymod = NULL;
yy.yy_handle = h;
if (yspec == NULL){
clicon_err(OE_YANG, 0, "Yang parse need top level yang spec");
goto done;
}
yy.yy_name = (char*)name;
yy.yy_linenum = 1;
yy.yy_parse_string = str;
yy.yy_stack = NULL;
yy.yy_module = NULL; /* this is the return value - the module/sub-module */
if (ystack_push(&yy, (yang_node*)yspec) == NULL)
goto done;
if (strlen(str)){ /* Not empty */
@ -1526,18 +1639,71 @@ yang_parse_str(clicon_handle h,
ymod = yy.yy_module;
done:
ystack_pop(&yy);
if (yy.yy_stack)
free (yy.yy_stack);
return ymod; /* top-level (sub)module */
}
/*! Read an opened file into a string and call yang string parsing
/*! Parse yang spec from an open file descriptor
* @param[in] fd File descriptor containing the YANG file as ASCII characters
* @param[in] name For debug, eg filename
* @param[in] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error
*/
yang_stmt *
yang_parse_file(int fd,
const char *name,
yang_spec *ysp)
{
char *buf = NULL;
int i;
int c;
int len;
yang_stmt *ymod = NULL;
int ret;
len = BUFLEN; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return NULL;
}
memset(buf, 0, len);
i = 0; /* position in buf */
while (1){ /* read the whole file */
if ((ret = read(fd, &c, 1)) < 0){
clicon_err(OE_XML, errno, "read");
break;
}
if (ret == 0)
break; /* eof */
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
} /* read a line */
if ((ymod = yang_parse_str(buf, name, ysp)) < 0)
goto done;
done:
if (buf)
free(buf);
return ymod; /* top-level (sub)module */
}
/*! Open a file, read into a string and invoke yang parsing
*
* Similar to clicon_yang_str(), just read a file first
* (cloned from cligen)
* @param h CLICON handle
* @param filename Name of file
* @param ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
* @param[in] h CLICON handle
* @param[in] filename Name of file
* @param[in] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
* The database symbols are inserted in alphabetical order.
* Calling order:
@ -1548,57 +1714,27 @@ yang_parse_str(clicon_handle h,
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_file(clicon_handle h,
const char *filename,
yang_spec *ysp
)
yang_parse_filename(const char *filename,
yang_spec *ysp)
{
char *buf = NULL;
int i;
int c;
int len;
yang_stmt *ymod = NULL;
FILE *f = NULL;
struct stat st;
int fd = -1;
struct stat st;
clicon_log(LOG_DEBUG, "Parsing yang file: %s", filename);
if (stat(filename, &st) < 0){
clicon_err(OE_YANG, errno, "%s not found", filename);
goto done;
}
if ((f = fopen(filename, "r")) == NULL){
clicon_err(OE_UNIX, errno, "fopen(%s)", filename);
if ((fd = open(filename, O_RDONLY)) < 0){
clicon_err(OE_YANG, errno, "open(%s)", filename);
goto done;
}
len = 1024; /* any number is fine */
if ((buf = malloc(len)) == NULL){
perror("pt_file malloc");
return NULL;
}
memset(buf, 0, len);
i = 0; /* position in buf */
while (1){ /* read the whole file */
if ((c = fgetc(f)) == EOF)
break;
if (len==i){
if ((buf = realloc(buf, 2*len)) == NULL){
fprintf(stderr, "%s: realloc: %s\n", __FUNCTION__, strerror(errno));
goto done;
}
memset(buf+len, 0, len);
len *= 2;
}
buf[i++] = (char)(c&0xff);
} /* read a line */
if ((ymod = yang_parse_str(h, buf, filename, ysp)) < 0)
if ((ymod = yang_parse_file(fd, filename, ysp)) < 0)
goto done;
done:
if (f)
fclose(f);
if (buf)
free(buf);
if (fd != -1)
close(fd);
return ymod; /* top-level (sub)module */
}
@ -1613,8 +1749,7 @@ yang_parse_file(clicon_handle h,
* @retval -1 Error
*/
static int
yang_parse_find_match(clicon_handle h,
const char *yang_dir,
yang_parse_find_match(const char *yang_dir,
const char *module,
cbuf *fbuf)
{
@ -1658,7 +1793,7 @@ yang_parse_find_match(clicon_handle h,
* @param[in] h CLICON handle
* @param[in] yang_dir Directory where all YANG module files reside
* @param[in] module Name of main YANG module. Or absolute file name.
* @param[in] revision Optional module revision date
* @param[in] revision Module revision date or NULL
* @param[in] ysp Yang specification. Should have been created by caller using yspec_new
* @retval ymod Top-level yang (sub)module
* @retval NULL Error encountered
@ -1671,8 +1806,7 @@ yang_parse_find_match(clicon_handle h,
* clixon_yang_parseparse # Actual yang parsing using yacc
*/
static yang_stmt *
yang_parse_recurse(clicon_handle h,
const char *yang_dir,
yang_parse_recurse(const char *yang_dir,
const char *module,
const char *revision,
yang_spec *ysp)
@ -1686,26 +1820,26 @@ yang_parse_recurse(clicon_handle h,
int nr;
if (module[0] == '/'){
if ((ymod = yang_parse_file(h, module, ysp)) == NULL)
if ((ymod = yang_parse_filename(module, ysp)) == NULL)
goto done;
}
else {
if ((fbuf = cbuf_new()) == NULL){
clicon_err(OE_YANG, errno, "%s: cbuf_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cbuf_new");
goto done;
}
if (revision)
cprintf(fbuf, "%s/%s@%s.yang", yang_dir, module, revision);
else{
/* No specific revision, Match a yang file */
if ((nr = yang_parse_find_match(h, yang_dir, module, fbuf)) < 0)
if ((nr = yang_parse_find_match(yang_dir, module, fbuf)) < 0)
goto done;
if (nr == 0){
clicon_err(OE_YANG, errno, "No matching %s yang files found (expected module name or absolute filename)", module);
clicon_err(OE_YANG, errno, "No matching %s yang files found in %s (expected module name or absolute filename)", module, yang_dir);
goto done;
}
}
if ((ymod = yang_parse_file(h, cbuf_get(fbuf), ysp)) == NULL)
if ((ymod = yang_parse_filename(cbuf_get(fbuf), ysp)) == NULL)
goto done;
}
@ -1720,7 +1854,7 @@ yang_parse_recurse(clicon_handle h,
subrevision = NULL;
if (yang_find((yang_node*)ysp, Y_MODULE, modname) == NULL)
/* recursive call */
if (yang_parse_recurse(h, yang_dir, modname, subrevision, ysp) == NULL){
if (yang_parse_recurse(yang_dir, modname, subrevision, ysp) == NULL){
ymod = NULL;
goto done;
}
@ -1779,7 +1913,7 @@ ys_schemanode_check(yang_stmt *ys,
* @param[in] h CLICON handle
* @param[in] yang_dir Directory where all YANG module files reside (except mainfile)
* @param[in] mainmod Name of main YANG module. Or absolute file name.
* @param[in] revision Optional main module revision date.
* @param[in] revision Main module revision date string or NULL
* @param[out] ysp Yang specification. Should ave been created by caller using yspec_new
* @retval 0 Everything OK
* @retval -1 Error encountered
@ -1788,7 +1922,8 @@ ys_schemanode_check(yang_stmt *ys,
* @note if mainmod is filename, revision is not considered.
* Calling order:
* yang_parse # Parse top-level yang module. Expand and populate yang tree
* yang_parse_recurse # Parse one yang module, go through its (sub)modules, parse them and then recursively parse them
* yang_parse_recurse # Parse one yang module, go through its (sub)modules,
* parse them and then recursively parse them
* yang_parse_file # Read yang file into a string
* yang_parse_str # Set up yacc parser and call it given a string
* clixon_yang_parseparse # Actual yang parsing using yacc
@ -1804,7 +1939,7 @@ yang_parse(clicon_handle h,
yang_stmt *ymod; /* Top-level yang (sub)module */
/* Step 1: parse from text to yang parse-tree. */
if ((ymod = yang_parse_recurse(h, yang_dir, mainmodule, revision, ysp)) == NULL)
if ((ymod = yang_parse_recurse(yang_dir, mainmodule, revision, ysp)) == NULL)
goto done;
/* Add top module name as dbspec-name */
clicon_dbspec_name_set(h, ymod->ys_argument);
@ -1988,16 +2123,16 @@ schema_nodeid_vec(yang_node *yn,
* Assume schema nodeid:s have prefixes, (actually the first).
* @see yang_desc_schema_nodeid
* @see RFC7950 6.5
o schema node: A node in the schema tree. One of action, container,
leaf, leaf-list, list, choice, case, rpc, input, output,
notification, anydata, and anyxml.
* o schema node: A node in the schema tree. One of action, container,
* leaf, leaf-list, list, choice, case, rpc, input, output,
* notification, anydata, and anyxml.
* Used in yang: deviation, top-level augment
*/
int
yang_abs_schema_nodeid(yang_spec *yspec,
char *schema_nodeid,
yang_abs_schema_nodeid(yang_spec *yspec,
char *schema_nodeid,
enum rfc_6020 keyword,
yang_stmt **yres)
yang_stmt **yres)
{
int retval = -1;
char **vec = NULL;
@ -2014,13 +2149,13 @@ yang_abs_schema_nodeid(yang_spec *yspec,
goto done;
}
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){
clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__);
clicon_err(OE_YANG, errno, "strsep");
goto done;
}
/* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */
if (nvec < 2){
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
__FUNCTION__, schema_nodeid);
clicon_err(OE_YANG, 0, "NULL or truncated path: %s",
schema_nodeid);
goto done;
}
/* split <prefix>:<id> */
@ -2029,7 +2164,7 @@ yang_abs_schema_nodeid(yang_spec *yspec,
goto ok;
}
if ((prefix = strdup(vec[1])) == NULL){
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
prefix[id-vec[1]] = '\0';
@ -2042,7 +2177,6 @@ yang_abs_schema_nodeid(yang_spec *yspec,
}
}
if (ymod == NULL){ /* Try with topnode */
if ((ys = yang_find_topnode(yspec, id, YC_SCHEMANODE)) == NULL){
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id);
goto done;
@ -2051,6 +2185,11 @@ yang_abs_schema_nodeid(yang_spec *yspec,
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found2", prefix,id);
goto done;
}
if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL &&
strcmp(yprefix->ys_argument, prefix) != 0){
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id);
goto done;
}
}
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, keyword, yres) < 0)
goto done;
@ -2117,7 +2256,7 @@ ys_parse(yang_stmt *ys,
assert(ys->ys_cv == NULL); /* Cv:s are parsed in different places, difficult to separate */
if ((ys->ys_cv = cv_new(cvtype)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if ((cvret = cv_parse1(ys->ys_argument, ys->ys_cv, &reason)) < 0){ /* error */
@ -2143,7 +2282,7 @@ ys_parse(yang_stmt *ys,
* That is, siblings, etc, may not be there. Complete checks are made in
* ys_populate instead.
* @param[in] ys yang statement
* @param[in] extra Yang extra for cornercases (unknown/extension)
* @param[in] extra Yang extra for cornercases (unknown/extension).
*
* The cv:s created in parse-tree as follows:
* fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass)
@ -2186,7 +2325,7 @@ ys_parse_sub(yang_stmt *ys,
goto done;
}
if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
clicon_err(OE_YANG, errno, "%s: cv_new", __FUNCTION__);
clicon_err(OE_YANG, errno, "cv_new");
goto done;
}
if ((cvret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */
@ -2197,12 +2336,11 @@ ys_parse_sub(yang_stmt *ys,
clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
goto done;
}
free(extra);
break;
default:
break;
}
ok:
ok:
retval = 0;
done:
if (prefix)
@ -2268,9 +2406,9 @@ yang_spec_netconf(clicon_handle h)
}
/*! Read, parse and save application yang specification as option
* @param h clicon handle
* @param f file to print to (if printspec enabled)
* @param printspec print database (YANG) specification as read from file
* @param[in] h clicon handle
* @param[in] f file to print to (if printspec enabled)
* @param[in] printspec print database (YANG) specification as read from file
*/
yang_spec*
yang_spec_main(clicon_handle h)
@ -2280,7 +2418,7 @@ yang_spec_main(clicon_handle h)
char *yang_module;
char *yang_revision;
if ((yang_dir = clicon_yang_dir(h)) == NULL){
if ((yang_dir = clicon_yang_dir(h)) == NULL){
clicon_err(OE_FATAL, 0, "CLICON_YANG_DIR option not set");
goto done;
}

View file

@ -51,7 +51,6 @@ struct ys_stack{
};
struct clicon_yang_yacc_arg{ /* XXX: mostly unrelevant */
clicon_handle yy_handle; /* cligen_handle */
char *yy_name; /* Name of syntax (for error string) */
int yy_linenum; /* Number of \n in parsed buffer */
char *yy_parse_string; /* original (copy of) parse string */

View file

@ -260,7 +260,7 @@ ysp_add(struct clicon_yang_yacc_arg *yy,
clicon_err(OE_YANG, errno, "No stack");
goto err;
}
yn = ystack->ys_node;
assert(yn = ystack->ys_node);
if ((ys = ys_new(keyword)) == NULL)
goto err;
/* NOTE: does not make a copy of string, ie argument is 'consumed' here */
@ -343,8 +343,9 @@ unknown_stmt : ustring ':' ustring ';'
}
| ustring ':' ustring ' ' string ';'
{ char *id; if ((id=prefix_id_join($1, $3)) == NULL) _YYERROR("0");
if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL) _YYERROR("0");
if (ysp_add(_yy, Y_UNKNOWN, id, $5) == NULL){ _YYERROR("0"); }
clicon_debug(2,"unknown-stmt -> ustring : ustring string");
if ($5) free($5);
}
;

View file

@ -41,6 +41,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <ctype.h>
#define __USE_GNU /* strverscmp */
@ -354,7 +355,7 @@ clicon_type2cv(char *origtype,
if (restype != NULL){
yang2cv_type(restype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_DB, 0, "%s: \"%s\" type not translated", __FUNCTION__, restype);
clicon_err(OE_DB, 0, "\"%s\" type not translated", restype);
goto done;
}
}
@ -365,7 +366,7 @@ clicon_type2cv(char *origtype,
*/
yang2cv_type(origtype, cvtype);
if (*cvtype == CGV_ERR){
clicon_err(OE_DB, 0, "%s: \"%s\": type not resolved", __FUNCTION__, origtype);
clicon_err(OE_DB, 0, "\"%s\": type not resolved", origtype);
goto done;
}
}
@ -380,7 +381,7 @@ clicon_type2cv(char *origtype,
(rmax && (i) > cv_##type##_get(rmax)))
/*!
/*! Validate CLIgen variable
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
@ -399,9 +400,13 @@ cv_validate1(cg_var *cv,
int retval = 1; /* OK */
int retval2;
yang_stmt *yi = NULL;
uint64_t u = 0;
int64_t i = 0;
unsigned int u = 0;
int i = 0;
char *str;
int found;
char **vec = NULL;
int nvec;
char *v;
if (reason && *reason){
free(*reason);
@ -413,7 +418,7 @@ cv_validate1(cg_var *cv,
i = cv_int8_get(cv);
if (range_check(i, range_min, range_max, int8)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
*reason = cligen_reason("Number out of range: %d", i);
retval = 0;
break;
}
@ -424,7 +429,7 @@ cv_validate1(cg_var *cv,
i = cv_int16_get(cv);
if (range_check(i, range_min, range_max, int16)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
*reason = cligen_reason("Number out of range: %d", i);
retval = 0;
break;
}
@ -435,29 +440,31 @@ cv_validate1(cg_var *cv,
i = cv_int32_get(cv);
if (range_check(i, range_min, range_max, int32)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
*reason = cligen_reason("Number out of range: %d", i);
retval = 0;
break;
}
}
break;
case CGV_INT64:
case CGV_INT64:{
int64_t i64;
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int64_get(cv);
i64 = cv_int64_get(cv);
if (range_check(i, range_min, range_max, int64)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
*reason = cligen_reason("Number out of range: %" PRId64, i64);
retval = 0;
break;
}
}
break;
}
case CGV_UINT8:
if ((options & YANG_OPTIONS_RANGE) != 0){
u = cv_uint8_get(cv);
if (range_check(u, range_min, range_max, uint8)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
*reason = cligen_reason("Number out of range: %u", u);
retval = 0;
break;
}
@ -468,7 +475,7 @@ cv_validate1(cg_var *cv,
u = cv_uint16_get(cv);
if (range_check(u, range_min, range_max, uint16)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
*reason = cligen_reason("Number out of range: %u", u);
retval = 0;
break;
}
@ -479,29 +486,31 @@ cv_validate1(cg_var *cv,
u = cv_uint32_get(cv);
if (range_check(u, range_min, range_max, uint32)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
*reason = cligen_reason("Number out of range: %u", u);
retval = 0;
break;
}
}
break;
case CGV_UINT64:
case CGV_UINT64:{
uint64_t u64;
if ((options & YANG_OPTIONS_RANGE) != 0){
u = cv_uint64_get(cv);
u64 = cv_uint64_get(cv);
if (range_check(u, range_min, range_max, uint64)){
if (reason)
*reason = cligen_reason("Number out of range: %lu", u);
*reason = cligen_reason("Number out of range: %" PRIu64, u64);
retval = 0;
break;
}
}
break;
}
case CGV_DEC64:
if ((options & YANG_OPTIONS_RANGE) != 0){
i = cv_int64_get(cv);
if (range_check(i, range_min, range_max, int64)){
if (reason)
*reason = cligen_reason("Number out of range: %ld", i);
*reason = cligen_reason("Number out of range: %d", i);
retval = 0;
break;
}
@ -510,29 +519,57 @@ cv_validate1(cg_var *cv,
case CGV_STRING:
case CGV_REST:
str = cv_string_get(cv);
if (restype &&
(strcmp(restype, "enumeration") == 0 || strcmp(restype, "bits") == 0)){
int found = 0;
while ((yi = yn_each((yang_node*)yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM && yi->ys_keyword != Y_BIT)
continue;
if (strcmp(yi->ys_argument, str) == 0){
found++;
if (restype){
if (strcmp(restype, "enumeration") == 0){
found = 0;
while ((yi = yn_each((yang_node*)yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_ENUM)
continue;
if (strcmp(yi->ys_argument, str) == 0){
found++;
break;
}
}
if (!found){
if (reason)
*reason = cligen_reason("'%s' does not match enumeration", str);
retval = 0;
break;
}
}
if (!found){
if (reason)
*reason = cligen_reason("'%s' does not match enumeration", str);
retval = 0;
break;
if (strcmp(restype, "bits") == 0){
/* The lexical representation of the bits type is a space-separated list
* of the names of the bits that are set. A zero-length string thus
* represents a value where no bits are set.
*/
if ((vec = clicon_strsep(str, " \t", &nvec)) == NULL)
goto done;
for (i=0; i<nvec; i++){
if ((v = vec[i]) == NULL || !strlen(v))
continue;
found = 0;
while ((yi = yn_each((yang_node*)yrestype, yi)) != NULL){
if (yi->ys_keyword != Y_BIT)
continue;
if (strcmp(yi->ys_argument, v) == 0){
found++;
break;
}
}
if (!found){
if (reason)
*reason = cligen_reason("'%s' does not match enumeration", v);
retval = 0;
break;
}
}
}
}
if ((options & YANG_OPTIONS_LENGTH) != 0){
u = strlen(str);
if (range_check(u, range_min, range_max, uint64)){
if (reason)
*reason = cligen_reason("string length out of range: %lu", u);
*reason = cligen_reason("string length out of range: %u", u);
retval = 0;
break;
}
@ -571,9 +608,11 @@ cv_validate1(cg_var *cv,
case CGV_EMPTY: /* XXX */
break;
}
if (reason && *reason)
assert(retval == 0); /* validation failed with error reason */
done:
if (vec)
free(vec);
return retval;
}
@ -713,8 +752,8 @@ ys_cv_validate(cg_var *cv,
if (cvtype == CGV_STRING && cv_type_get(ycv) == CGV_REST)
;
else {
clicon_err(OE_DB, 0, "%s: Type mismatch data:%s != yang:%s",
__FUNCTION__, cv_type2str(cvtype), cv_type2str(cv_type_get(ycv)));
clicon_err(OE_DB, 0, "Type mismatch data:%s != yang:%s",
cv_type2str(cvtype), cv_type2str(cv_type_get(ycv)));
goto done;
}
}
@ -728,7 +767,7 @@ ys_cv_validate(cg_var *cv,
}
else
if ((retval = cv_validate1(cv, cvtype, options, range_min, range_max, pattern,
yrestype, restype, reason)) < 0)
yrestype, restype, reason)) < 0)
goto done;
done:
if (cvt)
@ -764,9 +803,9 @@ ys_typedef_up(yang_stmt *ys)
return (yang_stmt*)ys;
}
/*! Return yang-stmt of identity
/*! Find identity yang-stmt
This is a sanity check of base identity of identity-ref and for identity
statements.
statements when parsing.
Return true if node is identityref and is derived from identity_name
The derived-from() function returns true if the (first) node (in
@ -779,12 +818,17 @@ ys_typedef_up(yang_stmt *ys)
identityref's base identity.
1. (base) identity must exist (be found). This is a sanity check
of the specification and also necessary for identity statements.
(This is what is dine here)
2. Check if a given node has value derived from base identity. This is
a run-time check necessary when validating eg netconf.
(This is validation)
3. Find all valid derived identities from a identityref base identity.
This is for cli generation.
vad är det denna function ska göra? Svar: 1
*/
(This is for cli generation)
* @param[in] ys Yang spec of id statement
* @param[in] identity Identity string -check if it exists
* @retval 0 OK
* @see validate_identityref for (2) above
*/
yang_stmt *
yang_find_identity(yang_stmt *ys,
char *identity)
@ -864,14 +908,14 @@ resolve_restrictions(yang_stmt *yrange,
}
/*! Recursively resolve a yang type to built-in type with optional restrictions
* @param [in] ys yang-stmt from where the current search is based
* @param [in] ytype yang-stmt object containing currently resolving type
* @param [out] yrestype resolved type. return built-in type or NULL. mandatory
* @param [out] options pointer to flags field of optional values. optional
* @param [out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE
* @param [out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE
* @param [out] pattern pointer to static string of yang string pattern. optional
* @param [out] fraction for decimal64, how many digits after period
* @param[in] ys yang-stmt from where the current search is based
* @param[in] ytype yang-stmt object containing currently resolving type
* @param[out] yrestype resolved type. return built-in type or NULL. mandatory
* @param[out] options pointer to flags field of optional values. optional
* @param[out] mincv pointer to cv with min range or length. If options&YANG_OPTIONS_RANGE
* @param[out] maxcv pointer to cv with max range or length. If options&YANG_OPTIONS_RANGE
* @param[out] pattern pointer to static string of yang string pattern. optional
* @param[out] fraction for decimal64, how many digits after period
* @retval 0 OK. Note yrestype may still be NULL.
* @retval -1 Error, clicon_err handles errors
* The setting of the options argument has the following semantics:
@ -956,7 +1000,7 @@ yang_type_resolve(yang_stmt *ys,
if (rytypedef != NULL){ /* We have found a typedef */
/* Find associated type statement */
if ((rytype = yang_find((yang_node*)rytypedef, Y_TYPE, NULL)) == NULL){
clicon_err(OE_DB, 0, "%s: mandatory type object is not found", __FUNCTION__);
clicon_err(OE_DB, 0, "mandatory type object is not found");
goto done;
}
/* recursively resolve this new type */
@ -994,14 +1038,14 @@ yang_type_resolve(yang_stmt *ys,
* if (options & YANG_OPTIONS_PATTERN != 0)
* printf("regexp: %s\n", pattern);
* @endcode
* @param [in] ys yang-stmt, leaf or leaf-list
* @param [out] origtype original type may be derived or built-in
* @param [out] yrestype pointer to resolved type stmt. should be built-in or NULL
* @param [out] options pointer to flags field of optional values
* @param [out] mincv pointer to cv of min range or length. optional
* @param [out] maxcv pointer to cv of max range or length. optional
* @param [out] pattern pointer to static string of yang string pattern. optional
* @param [out] fraction for decimal64, how many digits after period
* @param[in] ys yang-stmt, leaf or leaf-list
* @param[out] origtype original type may be derived or built-in
* @param[out] yrestype pointer to resolved type stmt. should be built-in or NULL
* @param[out] options pointer to flags field of optional values
* @param[out] mincv pointer to cv of min range or length. optional
* @param[out] maxcv pointer to cv of max range or length. optional
* @param[out] pattern pointer to static string of yang string pattern. optional
* @param[out] fraction for decimal64, how many digits after period
* @retval 0 OK, but note that restype==NULL means not resolved.
* @retval -1 Error, clicon_err handles errors
* The setting of the options argument has the following semantics:
@ -1031,7 +1075,7 @@ yang_type_get(yang_stmt *ys,
*options = 0x0;
/* Find mandatory type */
if ((ytype = yang_find((yang_node*)ys, Y_TYPE, NULL)) == NULL){
clicon_err(OE_DB, 0, "%s: mandatory type object is not found", __FUNCTION__);
clicon_err(OE_DB, 0, "mandatory type object is not found");
goto done;
}
/* XXX: here we seem to have some problems if type is union */

36
test/Jenkinsfile vendored Normal file
View file

@ -0,0 +1,36 @@
node {
stage('Checkout') {
git url: 'https://github.com/clicon/clixon.git'
checkout scm
}
stage('Configure') {
/* `make check` returns non-zero on test failures,
* using `true` to allow the Pipeline to continue nonetheless
*/
sh 'make clean'
sh './configure'
}
stage('Make') {
/* `make check` returns non-zero on test failures,
* using `true` to allow the Pipeline to continue nonetheless
*/
sh 'make'
}
stage('Make install') {
sh 'sudo make install'
}
stage('Make install-include') {
sh 'sudo make install-include'
}
stage('Make Example') {
sh 'cd example'
sh 'make'
sh 'sudo make install'
}
stage('Testing') {
sh 'cd test'
sh './all.sh'
}
}

Some files were not shown because too many files have changed in this diff Show more