Merge branch 'develop' for 3.7.0
This commit is contained in:
commit
728671944f
129 changed files with 8192 additions and 2282 deletions
101
CHANGELOG.md
101
CHANGELOG.md
|
|
@ -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);
|
||||
|
|
|
|||
14
Makefile.in
14
Makefile.in
|
|
@ -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
|
||||
|
|
|
|||
17
README.md
17
README.md
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
164
configure
vendored
|
|
@ -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" \
|
||||
|
|
|
|||
50
configure.ac
50
configure.ac
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
94
doc/FAQ.md
94
doc/FAQ.md
|
|
@ -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.
|
||||
|
|
@ -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"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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
19
docker/README.md
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
1506
example/iana-if-type@2014-05-08.yang
Normal file
1506
example/iana-if-type@2014-05-08.yang
Normal file
File diff suppressed because it is too large
Load diff
13
example/systemd/example.service
Normal file
13
example/systemd/example.service
Normal 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
|
||||
14
example/systemd/example_restconf.service
Normal file
14
example/systemd/example_restconf.service
Normal 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
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ static const map_str2int atmap[] = {
|
|||
{NULL, -1}
|
||||
};
|
||||
* @endcode
|
||||
* @see clicon_int2str
|
||||
* @see clicon_str2int
|
||||
*/
|
||||
struct map_str2int{
|
||||
char *ms_str;
|
||||
|
|
|
|||
|
|
@ -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
103
lib/clixon/clixon_xpath.h
Normal 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 */
|
||||
103
lib/clixon/clixon_xpath_ctx.h
Normal file
103
lib/clixon/clixon_xpath_ctx.h
Normal 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 */
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -241,12 +241,11 @@ group_name2gid(char *name,
|
|||
gr = &g0;
|
||||
/* This leaks memory in ubuntu */
|
||||
if (getgrnam_r(name, gr, buf, sizeof(buf), >mp) < 0){
|
||||
clicon_err(OE_UNIX, errno, "%s: getgrnam_r(%s): %s",
|
||||
__FUNCTION__, name, strerror(errno));
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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("& ");
|
||||
break;
|
||||
case '<':
|
||||
len += strlen("< ");
|
||||
break;
|
||||
case '>':
|
||||
len += strlen("> ");
|
||||
break;
|
||||
default:
|
||||
if (cdata){
|
||||
if (strncmp(&str[i], "]]>", strlen("]]>")) == 0)
|
||||
cdata = 0;
|
||||
len++;
|
||||
}
|
||||
else
|
||||
switch (str[i]){
|
||||
case '&':
|
||||
len += strlen("& ");
|
||||
break;
|
||||
case '<':
|
||||
if (strncmp(&str[i], "<![CDATA[", strlen("<![CDATA[")) == 0){
|
||||
len++;
|
||||
cdata++;
|
||||
}
|
||||
else
|
||||
len += strlen("< ");
|
||||
break;
|
||||
case '>':
|
||||
len += strlen("> ");
|
||||
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, "& ")) < 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, "< ")) < 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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
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
294
lib/src/clixon_xpath_ctx.c
Normal 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;
|
||||
}
|
||||
|
||||
101
lib/src/clixon_xpath_parse.h
Normal file
101
lib/src/clixon_xpath_parse.h
Normal 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_ */
|
||||
174
lib/src/clixon_xpath_parse.l
Normal file
174
lib/src/clixon_xpath_parse.l
Normal 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;
|
||||
}
|
||||
|
||||
289
lib/src/clixon_xpath_parse.y
Normal file
289
lib/src/clixon_xpath_parse.y
Normal 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-> "); }
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
|
||||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
Så 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
36
test/Jenkinsfile
vendored
Normal 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
Loading…
Add table
Add a link
Reference in a new issue